-
Notifications
You must be signed in to change notification settings - Fork 7
Add FroehlichPolaron_nD structure and implementation #355
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
base: develop
Are you sure you want to change the base?
Conversation
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.
Looks great! I haven't tested the code yet, but I didn't notice any potential issues, so I only have some style comments for now. Generally, we try to adhere to something close to this style guide https://github.com/JuliaDiff/BlueStyle
The PR also needs some tests. Add a few examples of interface tests (see first test set in test/Hamiltonians.jl
). Other things you could test for is that this code is equivalent to FroelichPolaron
for 1D and some general property tests (you can use the FroelichPolaron
tests for inspiration there).
function FroehlichPolaron_nD( | ||
address::OccupationNumberFS{M,AT}; | ||
D::Int = 2, | ||
geometry::CubicGrid = PeriodicBoundaries(ntuple(_ -> Int(round(num_modes(address)^(1/D))), D)), |
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.
geometry::CubicGrid = PeriodicBoundaries(ntuple(_ -> Int(round(num_modes(address)^(1/D))), D)), | |
geometry::CubicGrid = PeriodicBoundaries(ntuple(Returns(round(Int, M^(1/D))), D)), |
A bit more compact, but equivalent
) where {M, AT} | ||
|
||
# Check positive l | ||
if length(l) != D || any(x -> x <= 0, l) |
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.
Feel free to ignore this comment, but I would write it like this
if length(l) != D || any(x -> x <= 0, l) | |
if length(l) != D || any(<=(0), l) |
end | ||
|
||
#check m_lin is possible | ||
if abs(M^(1/D) - round(M^(1/D))) >0.01 |
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 abs(M^(1/D) - round(M^(1/D))) >0.01 | |
if abs(M^(1/D) - round(M^(1/D))) > 0.01 |
Try to put spaces on both sides of operators, except in very long expressions where having some terms close together makes it more readable.
for idx_mode in 1:M | ||
idx = Tuple(geometry[idx_mode]) | ||
kv = ntuple(d -> begin | ||
m = idx[d] - 1 - div(round(M^(1/D)),2) | ||
(2π / l[d]) * m | ||
end, D) | ||
ks_tmp[idx_mode] = SVector{D,Float64}(kv) | ||
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.
for idx_mode in 1:M | |
idx = Tuple(geometry[idx_mode]) | |
kv = ntuple(d -> begin | |
m = idx[d] - 1 - div(round(M^(1/D)),2) | |
(2π / l[d]) * m | |
end, D) | |
ks_tmp[idx_mode] = SVector{D,Float64}(kv) | |
end | |
for idx_mode in 1:M | |
idx = Tuple(geometry[idx_mode]) | |
ntuple(D) do d | |
m = idx[d] - 1 - div(round(M^(1/D)), 2) | |
(2π / l[d]) * m | |
end | |
ks_tmp[idx_mode] = SVector{D,Float64}(kv) | |
end |
Please make sure the code is indented properly! The do notation is also helpful here.
Pph = zero(h.ks[1]) | ||
Nph = 0 |
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.
It's not clear what these are. Try to use more descriptive variable names.
using SpecialFunctions | ||
|
||
#structure | ||
struct FroehlichPolaron_nD{ |
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.
We usually avoid underscores in struct names. Maybe FroelichPolaronND
?
Since this supports 1D as well, do we still need the regular FroelichPolaron
? If this is just as efficient and can be used as a drop-in replacement, I think the old Hamiltonian should be removed.
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.
Thanks, @yosrabi for the PR! The code looks nice and I'm looking forward to having the multi-dimensional Froehlich model represented in Rimu.
For now I have just a few comments supporting what @mtsch has already said:
It is common in Julia to use 'CamelCase' for structs and 'snake_case' for functions, and we follow this convention (you can google the terms).
I would also strongly prefer to not have two different Froehlich polaron Hamiltonians in Rimu, in particular if they have intersecting functionality. What I had in mind for the extension to 2 and 3 dimensions was an implementation that just adds new features to the existing FroehlichPolaron
by adding new keyword arguments (e.g. for dimension
with default set to 1) and reusing the old code, user interface, and tests where possible and extending or replacing them only where necessary. Please consider whether this is still feasible! Otherwise we could go with Matija's suggestion to let this new implementation supersede the old one. This would make sense if the new code was faster/more efficient/more elegant or in some other way superior to the old one. As a minimum it should not be any worse.
Ideally, the call signature should be compatible such that replacing the old with the new Hamiltonian would be a non-breaking change (i.e. all previous scripts making use of the documented features of the old FroehlichPolaron
would still run without errors). If there is a good reason to change the behaviour or call signature such that a breaking change is required, we can discuss that as well, of course.
Adding tests for the new code and features that run automatically is important (and a hard requirement for any new code to be added to the codebase). These test are all in the test/
folder. The entry point is test/runtests.jl
but your test should be added to test/Hamiltonians.jl
. I suggest that you look into that file and search for FroelichPolaron
to see what tests are already being done for the existing implementation. Ideally, the tests should run on the smallest units (functions) of the new code, check for the correctness of the results, and cover all (feasible) use cases. The minimum requirement is that all (meaningful) parts of the new code are run at least once during the tests.
Finally, I see that GitHub has detected conflicts of your branch with the main develop
branch. The reason for this to happen is likely that we recently merged a few other PRs that may be conflicting with yours. The best way to deal with this is to (first fetch and then) merge develop
into your branch locally on your computer, and resolve any merge conflicts with a code editor (e.g. vscode) before committing the changes and pushing the local branch back to the repo. There should not be any serious merge conficts as you've mostly edited a new file. You'll simplify your life a lot if you do this before acting on any other comments from this code review.
function operator_column(h::FroehlichPolaron_nD, address) | ||
M = num_modes(address) | ||
return FroehlichPolaron_nDColumn(h, address, 2M) | ||
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.
I would move this function next to the struct definition since it acts like a constructor.
offdiagonals(col::FroehlichPolaron_nDColumn) = FroehlichPolaron_nDOffdiagonals(col.address, col.hamiltonian, col.num_offdiagonals) | ||
|
||
Base.size(ods::FroehlichPolaron_nDOffdiagonals) = (ods.num_offdiagonals,) | ||
Base.eltype(::FroehlichPolaron_nDOffdiagonals{A}) where {A} = Pair{A,Float64} |
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.
This is not necessary, a default definition is created when you make something a subtype of AbstractVector
.
end | ||
|
||
|
||
#diagonal elements calculator |
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.
Try avoiding comments that simply state what the code does. They should be used to explain how/why something is done in a particular way or to break up long chunks of code.
#calculate V_k for a specific k. Edge case of D = 1. | ||
|
||
|
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.
Here, a docstring would be more useful than a comment.
Sorry i think I accidentally tried to merge the branch. The requested changes have been implemented and the tests updated. |
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.
This is looking great! Just a few small comments.
function operator_column(h::FroehlichPolaronND, address) | ||
M = num_modes(address) | ||
return FroehlichPolaronNDColumn(h, address, 2M) | ||
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.
This should be under the definition of the FroehlichPolaronNDColumn
struct.
function Base.iterate(ods::FroehlichPolaronNDOffdiagonals, state::Int=1) | ||
state > ods.num_offdiagonals && return nothing | ||
return ods[state], state + 1 | ||
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.
This is not needed, since the offdiagonals struct is an AbstractVector
.
end | ||
|
||
|
||
if h.momentum_cutoff != nothing |
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 h.momentum_cutoff != nothing | |
if !isnothing(h.momentum_cutoff) |
This is slightly more efficient.
@inline function calc_vk(h::FroehlichPolaronND, kidx::Int) | ||
knorm = sqrt(dot(h.ks[kidx], h.ks[kidx])) | ||
vol = prod(h.l) | ||
if h.d == 1 | ||
return (2 * h.alpha /(h.l[1]))^0.5 | ||
else | ||
if knorm == 0.0 | ||
return 0.0 | ||
else | ||
return (-1) * sqrt((gamma(((h.d-1)/2)) * h.alpha * 2^(h.d-(3/2)) * pi^((h.d-1)/2) * h.omega^2) / (sqrt(h.mass * h.omega) * (knorm^(h.d-1) * vol))) | ||
end | ||
end | ||
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.
Most of this doesn't need to be calculated over and over again; I'd suggest moving the constant part into the Hamiltonian as h.v
, and then this function simplifies to something like
@inline function calc_vk(h::FroehlichPolaronND, kidx::Int)
if h.d == 1
return h.v
else
knorm = sqrt(dot(h.ks[kidx], h.ks[kidx]))
if knorm == 0.0
return 0.0
else
return h.v / sqrt(knorm^(h.d - 1))
end
end
end
if abs(M^(1/D) - round(M^(1/D))) > 0.01 | ||
throw(ArgumentError("M = $M is not a perfect $D th power")) | ||
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.
if abs(M^(1/D) - round(M^(1/D))) > 0.01 | |
throw(ArgumentError("M = $M is not a perfect $D th power")) | |
end | |
if D > 1 && !isinteger(log(D, M)) | |
throw(ArgumentError("M = $M is not a perfect power of $D")) | |
end |
momentum(HubbardMom1D(BoseFS(0, 1, 5, 1, 0))), | ||
Rimu.FirstOrderTransitionOperator(HubbardRealSpace(BoseFS(1,1,1,1)), -5.0, 0.01), | ||
HubbardReal1D(BoseFS(2,0,0); u=1.0im) * ExtendedHubbardReal1D(BoseFS(2,0,0)), | ||
FroehlichPolaronND(OccupationNumberFS(0,0,0,0)) |
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.
FroehlichPolaronND(OccupationNumberFS(0,0,0,0)) | |
FroehlichPolaronND(OccupationNumberFS(0,0,0,0)), |
@test get_offdiagonal(f6, addr5, 1) == get_offdiagonal(f7, addr5, 1) | ||
end | ||
|
||
@testset "FroehlichPolaronND" begin |
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.
These tests look good, we should probably have some checking that it works as expected for higher dimensions as well.
end | ||
|
||
|
||
function Base.show(io::IO, h::FroehlichPolaronND) |
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.
The result of this should be able to be pasted back into the REPL.
@@ -0,0 +1,235 @@ | |||
using SpecialFunctions |
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.
This is already done in Hamiltonians.jl, so we don't need it here.
using SpecialFunctions | ||
|
||
|
||
struct FroehlichPolaronND{ |
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.
This should have a docstring; see the other Hamiltonians for examples.
Hi, After some testing I think this is working as intended. All feedback and improvement ideas are welcome.