-
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
RFC: MeshSpec and linearmesh, with support for bandcuts (i.e. lifting) #70
Conversation
The last commit includes a fix in This however has broken some tests with ParametricHamiltonian. Looking into ways to fix that. [EDIT: fixed, hopefully] |
Codecov Report
@@ Coverage Diff @@
## master #70 +/- ##
==========================================
+ Coverage 56.23% 60.88% +4.65%
==========================================
Files 15 15
Lines 2287 2572 +285
==========================================
+ Hits 1286 1566 +280
- Misses 1001 1006 +5
Continue to review full report at Codecov.
|
Updated the OP with more details. |
fix tests MeshSpec fix anyoldmatrix fix tests degeneracy fix + closed kwarg fix finite-difference Parametric + BZpoints fix test docstring BZpoint typo
I think the general API
|
I think I agree. Let's settle on
This is problematic, because we want the provided nodes to be included in the mesh. If
This is again problematic. The lift function depends on the dimensions of the hamiltonian, so it cannot be an intrinsic part of the mesh. It is literally the mapping between the mesh and the Hamiltonian parameter space, so it needs to go outside.
Because in Julia we cannot know the number of arguments
Thanks for looking into the docs! Internally almost everything (including the vertices of a mesh) are StaticArrays. It is just that, for convenience, we accept tuples that we convert later (they are much easier to type) |
That is true. But we can work around that by defining a function that distributes the number of points has equally as possible. Something like:
Then we would just need to modify:
and I think everything should work.
The
or
with lift stored in a field
I understand this, but I do not see how the picture is different if we just provide the mesh, instead of mesh+lift. How do we know if each vertex in the mesh provided by a user has the correct number of elements that are taken as an argument by Now these two points are not very important. You have already made the functionality available. I was just trying to understand the rational for the way you implemented it. (I always learn new Julia things everytime I look at your code!) EDIT: fiex typos in |
I found a possible problem. If I try:
I would expect the mesh generated from the spec to have 5 + 2 + 8 = 15 points
|
src/mesh.jl
Outdated
end | ||
|
||
buildmesh(s::LinearMeshSpec{N,L,T}) where {N,L,T} = buildmesh(s, SMatrix{L,L,T}(I)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want the case with resolution = number
to specify the total number of points in the path, we can define a function:
function points_per_segment(s::LinearMeshSpec{N,L,T,R}, br) where {N, L, T, R<:Integer} | |
nverts = length(s.vertices) | |
npts = only(s.resolution) | |
seg_lengths = segment_lengths(s, br) # lengths are nomalized to sum(seg_lengths) == 1 | |
seg_npts = [floor(Int, (npts-1)*seg_lengths[n]) for n in 1:(nverts-1)] # underestimate number of points per segment | |
seg_remainder = [npts*seg_lengths[n] - seg_npts[n] for n in 1:(nverts-1)] # how many points can we still fit in each segment | |
perm = sortperm(seg_remainder, rev = true) # sort segments by the number of points we can still fit in them | |
r = npts - 1 - sum(seg_npts) # number of points that haven't been distributed yet | |
p = 1 | |
while r>0 | |
seg_npts[perm[p]] += 1 | |
r -= 1 | |
p += 1 | |
end | |
# for convinince in buildmesh: sum(npts) == number of desired points - 1. Ensures the correct numbed of points | |
return ntuple(i->seg_npts[i], Val(N-1)) | |
end | |
function points_per_segment(s::Quantica.LinearMeshSpec{N,L,T,R}, br) where {N, L, T, R} | |
npts = [n for n in s.resolution] | |
npts[end] -= 1 # for convinince in buildmesh. sum(npts) == number of desired points - 1. Ensures the correct numbed of points | |
return ntuple(i->npts[i], Val(N-1)) | |
end |
src/mesh.jl
Outdated
function buildmesh(s::LinearMeshSpec{N}, br) where {N} | ||
ranges = ((i, r) -> range(i, i+1, length = r)).(ntuple(identity, Val(N-1)), s.points) | ||
verts = SVector.(first(ranges)) | ||
for r in Base.tail(ranges) | ||
pop!(verts) | ||
append!(verts, SVector.(r)) | ||
end | ||
s.closed && pop!(verts) | ||
nv = length(verts) | ||
nodefunc = idx_to_node(s, br) | ||
verts .= nodefunc.(verts) | ||
adjmat = sparse(vcat(1:nv-1, 2:nv), vcat(2:nv, 1:nv-1), true, nv, nv) | ||
s.closed && (adjmat[end, 1] = adjmat[1, end] = true) | ||
return Mesh(verts, adjmat) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the previous definition of points_per_segment
, buildmesh
can be written as (such that the number of vertices in the returned mesh ==sum(resolution)
):
function buildmesh(s::LinearMeshSpec{N}, br) where {N} | |
ranges = ((i, r) -> range(i, i+1, length = r)).(ntuple(identity, Val(N-1)), s.points) | |
verts = SVector.(first(ranges)) | |
for r in Base.tail(ranges) | |
pop!(verts) | |
append!(verts, SVector.(r)) | |
end | |
s.closed && pop!(verts) | |
nv = length(verts) | |
nodefunc = idx_to_node(s, br) | |
verts .= nodefunc.(verts) | |
adjmat = sparse(vcat(1:nv-1, 2:nv), vcat(2:nv, 1:nv-1), true, nv, nv) | |
s.closed && (adjmat[end, 1] = adjmat[1, end] = true) | |
return Mesh(verts, adjmat) | |
end | |
function buildmesh(s::Quantica.LinearMeshSpec{N}, br) where {N} | |
nsegs = length(s.vertices)-1 | |
seg_npts = points_per_segment(s, br) | |
nodes =linearmesh_nodes(s, br) | |
verts = [SVector(nodes[1])] | |
for n in 1:nsegs | |
for i in 1:seg_npts[n] | |
push!(verts, SVector(nodes[n] + (nodes[n+1]-nodes[n])*i/seg_npts[n])) | |
end | |
end | |
nv = length(verts) | |
adjmat = sparse(vcat(1:nv-1, 2:nv), vcat(2:nv, 1:nv-1), true, nv, nv) | |
s.closed && (adjmat[end, 1] = adjmat[1, end] = true) | |
return Mesh(verts, adjmat) | |
end |
I'm afraid I see that as trying to be too clever for our own good :-D. I'd rather opt for behaviour that is more predictable and easier to maintain and understand
It is possible, but it is not conceptually cleaner. Again, why would you want to mix the concepts of a mesh with the mapping of that mesh to a Hamiltonian? In fact, if you do that, how can a
You've got a point. I need to think about this again, as I feel there was a more powerful reason for this decision (it's been a while)
Note that 5 is the points of a segment including endpoints (see docs). Hence, it is actually 4+1+8 = 13. The code is working as intended. |
Fair enough!
It is just that right now, for a
Oh sorry, I missed that in the docs! |
But it is not different, really. All
You are right about lifts and |
What I think we must definitely disallow is the method |
One more stylistic tweak: since I plan to merge this soon. Any more suggestions? |
Ok, this convinced me. So the idea is that each Regarding
So maybe that was your initial reasoning? |
To be precise, one MeshSpec has one default lift function for a given Hamiltonian, but yes. Note that a
EDIT: I had misunderstood your comment. Yes, precisely, that's what's done in commit 4aa3647 |
Closes: #64
This implements the proposal by @BacAmorim in #64 that unifies
marchingmesh
andlinearmesh
to both construct subtypes ofMeshSpec
. This kind of type holds information necessary to build aMesh
and a lift function, onceh
is provided. Multiple dispatch on functionsbuildmesh(spec, br)
andbuildlift(spec, br)
take care of that, wherebr
is the Bravais matrix ofh
(which is actually the only thing required to build the mesh from the spec).We thus now have a new API for bandstructures, which is the one a typical user is likely to use, based on MeshSpec objects
where we currently have two types of
MeshSpec
,MarchingMeshSpec
andLinearMeshSpec
, constructed usingmarchingmesh(ranges; resolution, axes)
andlinearmesh(nodes; resolution, samelength, closed)
functions.The
linearmesh
function implements named points in the Brillouin zone as nodes for polygonal cuts. A Γ-K-M-Γ cut, for example, would be done likeor alternatively specifying one or more of the points as
phi
tuples,Note that we can specify different resolutions per segment in the cut, or an equal resolution with a single integer.
The updated
marchingmesh::MeshSpec
API has a syntax based on tuples, instead of ranges, in keeping with the aboveThe old API
bandmesh(h, mesh; lift = ..., kw...)
is still available. TheMeshSpec
API above actually callsbandmesh(h, mesh; lift = ..., kw...)
under the hood after computing themesh
and thelift
.