Compare commits

..

32 Commits
jacobi ... main

Author SHA1 Message Date
Nuwan Yapa 7545ebf5b5 Update README.md 2026-02-18 01:09:14 +00:00
Nuwan Yapa 840efad148 First version of README.md 2026-02-14 01:02:21 +00:00
Nuwan Yapa cf9de201a9 Julia package dependencies 2026-02-13 23:53:42 +00:00
Nuwan Yapa 37acda8fac Ignore VS Code configuration files 2026-02-13 15:32:27 -05:00
Nuwan Yapa 45b43728fb R2R EC implemented 2024-07-12 18:09:36 -04:00
Nuwan Yapa 850447a507 Merge remote-tracking branch 'origin/calculations' 2024-07-12 14:52:13 -04:00
Nuwan Yapa df0b5820ea Move calculations into folder 2024-07-12 14:48:43 -04:00
Nuwan Yapa 1f4a648d52 Simple 3-body bound system 2024-04-09 18:07:17 -04:00
Nuwan Yapa 8ee4057f7e .gitignore HPC stuff 2024-02-26 10:26:48 -05:00
ysyapa d3a25b2ccf New ComplexScaling-FV calculation 2023-10-21 14:57:45 -04:00
ysyapa 922807eca1 n_imag=1 2023-09-05 16:18:19 +00:00
ysyapa 7cc20a9c27 Fixed GPU related bug 2023-08-27 23:02:51 -04:00
ysyapa 547be9fa98 Changed parameters 2023-08-25 22:09:41 +00:00
ysyapa 7a74745f7e Merge branch 'master' into calculations 2023-08-25 21:21:32 +00:00
ysyapa 3d29c323d0 New calculation for paper 2023-08-25 21:03:25 +00:00
ysyapa 7fdb5947a1 Merge branch 'master' into calculations 2023-08-25 21:02:53 +00:00
Nuwan Yapa 0bcb7b3d16 Ignore calculation outputs 2023-08-14 10:46:22 -04:00
Nuwan Yapa 3f0d7566fa Merge branch 'master' into calculations 2023-08-10 05:45:10 -04:00
Nuwan Yapa 1195e63f51 Merge branch 'master' into calculations 2023-08-10 05:37:12 -04:00
ysyapa b05fb757b1 Merge branch 'master' into calculations 2023-08-10 09:11:19 +00:00
ysyapa 3b6a57c512 New parameters 2023-08-10 09:09:56 +00:00
ysyapa 490e94262b Increase N 2023-08-09 18:22:05 +00:00
ysyapa e6f32295f5 Export .dat file 2023-08-09 18:19:55 +00:00
ysyapa cbd0460600 Chose potential for new paper 2023-08-07 19:46:19 +00:00
ysyapa 911143a9ca Minor fix 2023-05-24 00:46:21 -04:00
ysyapa 8ce0f749b7 Use CPU 2023-05-23 19:13:58 +00:00
ysyapa 700d2b4308 Seperate .jl files instead of notebook 2023-05-23 19:11:36 +00:00
ysyapa d3726c725b Adopt to new interface 2023-05-23 19:08:09 +00:00
ysyapa c831c52f78 Merge branch 'master' into calculations 2023-05-23 18:43:54 +00:00
ysyapa 77eea9319c Not worrying about memory leak 2023-04-20 16:28:08 -04:00
ysyapa 7879f047f2 Merge branch 'master' into calculations 2023-04-20 16:27:33 -04:00
ysyapa 0c3fb156ae 3-body resonance calculation 2023-04-09 20:08:00 -04:00
14 changed files with 323 additions and 93 deletions

6
.gitignore vendored
View File

@ -1,3 +1,9 @@
# VSCode
.vscode/
# HPC scripts and logs
hpc/
# Calculation outputs # Calculation outputs
*.dat *.dat
*.csv *.csv

View File

@ -6,23 +6,27 @@ using TensorOperations, KrylovKit, LinearAlgebra, CUDA, cuTENSOR, NVTX
"A Hamiltonian that can be applied to a vector" "A Hamiltonian that can be applied to a vector"
struct Hamiltonian{T} struct Hamiltonian{T}
s::system{T} s::system{T}
K::Union{CuTensor{Complex{T}}, Matrix{Complex{T}}} K_partial::Matrix{Complex{T}}
Vs::Union{Array{Complex{T}}, CuArray{Complex{T}}} K_diag::Union{CuTensor{Complex{T}},Nothing}
K_mixed::Union{CuTensor{Complex{T}},Nothing}
Vs::Union{Array{Complex{T}},CuArray{Complex{T}}}
hermitian::Bool hermitian::Bool
mode::Hamiltonian_backend mode::Hamiltonian_backend
function Hamiltonian{T}(s::system{T}, V_twobody::Function, ϕ::Real, n_image::Int, mode::Hamiltonian_backend) where {T<:Float} function Hamiltonian{T}(s::system{T}, V_twobody::Function, ϕ::Real, n_image::Int, mode::Hamiltonian_backend) where {T<:Float}
@assert mode != gpu_cutensor || CUDA.functional() && CUDA.has_cuda() && CUDA.has_cuda_gpu() "CUDA not available" @assert mode != gpu_cutensor || CUDA.functional() && CUDA.has_cuda() && CUDA.has_cuda_gpu() "CUDA not available"
hermitian = ϕ == 0.0
Vs = calculate_Vs(s, V_twobody, convert(T, ϕ), n_image)
k = -s.N÷2:s.N÷2-1 k = -s.N÷2:s.N÷2-1
= ∂_1DOF.(Ref(s), k, k') Vs = calculate_Vs(s, V_twobody, convert(T, ϕ), n_image)
K = exp(-2im * convert(T, ϕ)) .* ( * ) # TODO: Calculate K matrix elements directly hermitian = ϕ == 0.0
K_partial = (exp(-im * convert(T, ϕ)) * im / sqrt(2 * s.μ)) .* ∂_1DOF.(Ref(s), k, k')
K_diag = nothing
K_mixed = nothing
if mode == gpu_cutensor if mode == gpu_cutensor
K = CuTensor(CuArray(K), ['a', 'A']) K_diag = CuTensor(CuArray(K_partial * K_partial), ['a', 'A'])
K_mixed = CuTensor(CuArray(K_partial), ['a', 'A']) * CuTensor(CuArray(K_partial), ['b', 'B'])
Vs = CuArray(Vs) Vs = CuArray(Vs)
end end
return new{T}(s, K, Vs, hermitian, mode) return new{T}(s, K_partial, K_diag, K_mixed, Vs, hermitian, mode)
end end
end end
@ -41,22 +45,30 @@ function LinearAlgebra.mul!(out::Array{Complex{T}}, H::Hamiltonian{T}, v::Array{
coords = H.s.n - 1 coords = H.s.n - 1
nconList_v_template = -collect(1:H.s.d*(coords)) nconList_v_template = -collect(1:H.s.d*(coords))
for dim = 1:H.s.d for dim = 1:H.s.d
for coord = 1:coords for coord1 = 1:coords
i = which_index(H.s, dim, coord) for coord2 = 1:coord1
nconList_K = [-i, 1] i1 = which_index(H.s, dim, coord1)
nconList_v = copy(nconList_v_template) i2 = which_index(H.s, dim, coord2)
nconList_v[i] = 1 nconList_1 = [-i1, 1]
v_new = @ncon((H.K, v), (nconList_K, nconList_v)) nconList_2 = [-i2, 2]
coeff = -1 / (2 * H.s.μs[coord]) nconList_v = copy(nconList_v_template)
out = axpy!(coeff, v_new, out) if i1 == i2
nconList_2[1] = 1
else
nconList_v[i1] = 1
end
nconList_v[i2] = 2
v_new = @ncon((H.K_partial, H.K_partial, v), (nconList_1, nconList_2, nconList_v))
out = axpy!(1, v_new, out)
end
end end
end end
return out return out
end end
"cuTENSOR contraction and accumulation (C = A * B + C)" "cuTENSOR contraction and accumulation (C = A * B + C)"
function contract_accumulate!(alpha::Number, C::CuTensor, A::CuTensor, B::CuTensor)::CuTensor function contract_accumulate!(C::CuTensor, A::CuTensor, B::CuTensor)::CuTensor
cuTENSOR.contraction!(alpha, A.data, A.inds, cuTENSOR.CUTENSOR_OP_IDENTITY, B.data, B.inds, cuTENSOR.CUTENSOR_OP_IDENTITY, cuTENSOR.contraction!(one(eltype(C)), A.data, A.inds, cuTENSOR.CUTENSOR_OP_IDENTITY, B.data, B.inds, cuTENSOR.CUTENSOR_OP_IDENTITY,
one(eltype(C)), C.data, C.inds, cuTENSOR.CUTENSOR_OP_IDENTITY, cuTENSOR.CUTENSOR_OP_IDENTITY) one(eltype(C)), C.data, C.inds, cuTENSOR.CUTENSOR_OP_IDENTITY, cuTENSOR.CUTENSOR_OP_IDENTITY)
return C return C
end end
@ -74,16 +86,31 @@ function LinearAlgebra.mul!(out::CuArray{Complex{T}}, H::Hamiltonian{T}, v::CuAr
v_t = CuTensor(v, copy(inds_template)) v_t = CuTensor(v, copy(inds_template))
out_t = CuTensor(out, copy(inds_template)) out_t = CuTensor(out, copy(inds_template))
for dim = 1:H.s.d for dim = 1:H.s.d
for coord = 1:coords for coord1 = 1:coords
i = which_index(H.s, dim, coord) for coord2 = 1:coord1
@assert v_t.inds == inds_template "v indices permuted" i1 = which_index(H.s, dim, coord1)
@assert H.K.inds[2] == 'A' "K_diag indices permuted" i2 = which_index(H.s, dim, coord2)
H.K.inds[1] = 'a' - 1 + i @assert v_t.inds == inds_template "v indices permuted"
v_t.inds[i] = 'A' if i1 == i2
#synchronize(ctx) @assert H.K_diag.inds[2] == 'A' "K_diag indices permuted"
coeff = -1 / (2 * H.s.μs[coord]) H.K_diag.inds[1] = 'a' - 1 + i1
NVTX.@range "K" out_t = contract_accumulate!(coeff, out_t, H.K, v_t) v_t.inds[i1] = 'A'
v_t.inds[i] = 'a' - 1 + i #synchronize(ctx)
NVTX.@range "K-diag" out_t = contract_accumulate!(out_t, H.K_diag, v_t)
v_t.inds[i1] = 'a' - 1 + i1
else
@assert H.K_mixed.inds[2] == 'A' && H.K_mixed.inds[4] == 'B' "K_mixed indices permuted"
H.K_mixed.inds[1] = 'a' - 1 + i1
H.K_mixed.inds[3] = 'a' - 1 + i2
# OPTIMIZE: A and B can be swapped
v_t.inds[i1] = 'A'
v_t.inds[i2] = 'B'
#synchronize(ctx)
NVTX.@range "K-mixed" out_t = contract_accumulate!(out_t, H.K_mixed, v_t)
v_t.inds[i1] = 'a' - 1 + i1
v_t.inds[i2] = 'a' - 1 + i2
end
end
end end
end end
@assert out_t.inds == inds_template "out indices permuted" @assert out_t.inds == inds_template "out indices permuted"

2
LocalPreferences.toml Normal file
View File

@ -0,0 +1,2 @@
[TensorOperations]
precompile_workload = true

7
Project.toml Normal file
View File

@ -0,0 +1,7 @@
[deps]
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77"
NVTX = "5da4648a-3479-48b8-97b9-01cb529c0a1f"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2"
cuTENSOR = "011b41b2-24ef-40a8-b3eb-fa098493e9e1"

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# DVR-jl
Solves the quantum $n$-body problem in finite volume (lattice) with periodic boundary conditions. Uses discrete variable representation (DVR) with optional support for complex scaling to study resonances. All details can be found in [H. Yu, N. Yapa, and S. König, Complex scaling in finite volume, Phys. Rev. C 109, 014316 (2024)](https://doi.org/10.1103/PhysRevC.109.014316).
Written in Julia with optional CUDA GPU acceleration (experimental).
## Installation
Make sure you have Julia installed. Required packages can be installed with a single command:
```bash
julia --project=. -e 'import Pkg; Pkg.instantiate()'
```
## Usage
See `calculations/3b_bound.jl` for an example on a 3-body bound state.
See `calculations/3b_res_from_paper.jl` for an example of a 3-body resonance via complex scaling.
## Planned features
- [ ] Spin and isospin degrees of freedom for nuclear calculations
- [ ] Multi-node HPC support
- [ ] Parity and cubic symmetries ($S_4$)
## Acknowledgments
The author gratefully acknowledges the guidance from Sebastian König.

19
calculations/3b_bound.jl Normal file
View File

@ -0,0 +1,19 @@
include("../Hamiltonian.jl")
mode = cpu_tensor
T = Float32
V_gauss(r2) = -2 * exp(-r2 / 4)
d = 3
n = 3
N = 20
L = 15
n_imag = 1
ϕ = 0
s = system{T}(d, n, N, L)
H = Hamiltonian{T}(s, V_gauss, ϕ, n_imag, mode)
@time evals, _, info = eig(H, 5)
print(info.numops, " operations")
display(evals)

View File

@ -0,0 +1,36 @@
# 10.1007/s00601-020-01550-8
# Fig. 7
# E_R = 4.18(8)
#./En.run -d 3 -n 3 -N 16 -c pot=v_shifted_gauss,v0=2.0,r=1.5,a=3.0 -c n_eig=20 -c which=li -c tol=1e-6 -L 16 -c phi=0.3 -v
include("../Hamiltonian.jl")
mode = cpu_tensor
T = Float32 # single-precision mode
using Plots
V_gauss(r2) =
2 * exp(-((sqrt(r2) - 3) / 1.5) ^ 2)
d = 3
n = 3
N = 16
L = 16
n_imag = 0
for ϕ::T in 0.2:0.05:0.4
s = system{T}(d, n, N, L)
H = Hamiltonian{T}(s, V_gauss, ϕ, n_imag, mode)
@time evals, _, info = eig(H, 20)
print(info.numops, " operations")
display(evals)
scatter(real.(evals), imag.(evals); legend=false)
xlabel!("Re E")
ylabel!("Im E")
xlims!(0, 6)
ylims!(-0.6, 0)
savefig("temp/phi$(Int(round(ϕ * 100))).png")
end

View File

@ -0,0 +1,24 @@
include("../Hamiltonian.jl")
mode = cpu_tensor
T = Float32 # single-precision mode
V_gauss(r2) =
-10 * exp(-(sqrt(r2)) ^ 2)
d = 3
n = 2
N = 96
ϕ = pi/6
n_imag = 1
open("ComplexScaling-FV-P-res.dat", "w") do f
for L = range(20, 35, length=16)
println("Calculating L=", L)
s = system{T}(d, n, N, L)
H = Hamiltonian{T}(s, V_gauss, ϕ, n_imag, mode)
@time evals, _, info = eig(H, 40)
dataline = vcat([L], hcat(real.(evals), imag.(evals))'[:])
println(f, join(dataline, '\t'))
end
end

View File

@ -0,0 +1,24 @@
include("../Hamiltonian.jl")
mode = cpu_tensor
T = Float32 # single-precision mode
V_gauss(r2) =
-10 * exp(-(sqrt(r2)) ^ 2)
d = 3
n = 2
N = 30
L = 6
n_imag = 1
open("ComplexScaling-FV-S-bound-phi.dat", "w") do f
for ϕ = range(0.0, 0.5, length=11)
println("Calculating ϕ=", ϕ)
s = system{T}(d, n, N, L)
H = Hamiltonian{T}(s, V_gauss, ϕ, n_imag, mode)
@time evals, _, info = eig(H, 10, resonances = false)
dataline = vcat([ϕ], hcat(real.(evals), imag.(evals))'[:])
println(f, join(dataline, '\t'))
end
end

View File

@ -0,0 +1,24 @@
include("../Hamiltonian.jl")
mode = cpu_tensor
T = Float32 # single-precision mode
V_gauss(r2) =
2 * exp(- ((sqrt(r2)-3)/1.5) ^ 2)
d = 3
n = 2
N = 96
L = 30
n_imag = 1
open("ComplexScaling-FV-S-res-phi.dat", "w") do f
for ϕ = range(0.1, 0.6, length=26)
println("Calculating ϕ=", ϕ)
s = system{T}(d, n, N, L)
H = Hamiltonian{T}(s, V_gauss, ϕ, n_imag, mode)
@time evals, _, info = eig(H, 40, resonances = true)
dataline = vcat([ϕ], hcat(real.(evals), imag.(evals))'[:])
println(f, join(dataline, '\t'))
end
end

67
calculations/EC-R2R-S.jl Normal file
View File

@ -0,0 +1,67 @@
using Plots, Arpack
include("../helper.jl")
include("../Hamiltonian.jl")
mode = cpu_tensor
T = Float32 # single-precision mode
V_r2(c) = r2 -> c * (-5 * exp(-r2/3) + 2 * exp(-r2/10))
d = 3
n = 2
N = 48
L = 30
ϕ = pi/6
n_imag = 1
s = system{T}(d, n, N, L)
train_cs = range(0.78, 0.45, length=5)
train_ref = reverse([0.05387926313545913-0.008900278182520881im,
0.11254295298924327-0.020515067379548786im,
0.16060154707503538-0.03716539208626717im,
0.19741353362674618-0.05994519982799412im,
0.2219100763497223-0.08959449893439568im])
extrapolate_cs = range(0.38, 0.22, length=5)
extrapolate_ref = reverse([0.23165109150003316-0.12052751440975719im,
0.23190549514995962-0.1406687118589838im,
0.22763660218046278-0.1626190970863793im,
0.21807104244164865-0.18635600686249373im,
0.2020979906072586-0.21180157628258728im])
training_E = ComplexF64[]
training_vec = Array[]
exact_E = ComplexF64[]
extrapolated_E = ComplexF64[]
for c in train_cs
println("Training c=", c)
H = Hamiltonian{T}(s, V_r2(c), ϕ, n_imag, mode)
@time evals, evecs, info = eig(H, 20, resonances = true)
i = nearestIndex(evals, pop!(train_ref))
push!(training_E, evals[i])
push!(training_vec, evecs[i])
end
N_EC = [sum(x .* y) for (x, y) in Iterators.product(training_vec, training_vec)]
for c in extrapolate_cs
println("Extrapolating c=", c)
H = Hamiltonian{T}(s, V_r2(c), ϕ, n_imag, mode)
@time evals, _, info = eig(H, 40, resonances = true)
nearestE = nearest(evals, pop!(extrapolate_ref))
push!(exact_E, nearestE)
# EC extrapolation
H_training_vec = H.(training_vec)
H_EC = [sum(x .* y) for (x, y) in Iterators.product(training_vec, H_training_vec)]
evals = eigvals(H_EC, N_EC)
push!(extrapolated_E, nearestE)
end
scatter(real.(training_E), imag.(training_E), label="training")
scatter!(real.(exact_E), imag.(exact_E), label="exact")
scatter!(real.(extrapolated_E), imag.(extrapolated_E), label="extrapolated")
savefig("temp/EC-R2R-S.pdf")

View File

@ -1,38 +1,18 @@
Float = Union{Float32,Float64} Float = Union{Float32,Float64}
norm_square(x) = sum(x .* x)
reducedMass(m1, m2) = 1 / (1/m1 + 1/m2)
"A few-body system defined by its physical parameters" "A few-body system defined by its physical parameters"
struct system{T} struct system{T}
d::Int d::Int
n::Int n::Int
N::Int N::Int
L::T L::T
μs::Vector{T} μ::T
invU::Matrix{T}
function system{T}(d::Int, n::Int, N::Int, L::Real) where {T<:Float} system{T}(d::Int, n::Int, N::Int, L::Real, μ::Real=0.5) where {T<:Float} = new{T}(d, n, N, convert(T, L), convert(T, μ))
μs = [1/((coord + 1)^2 * reducedMass(coord, 1)) for coord in 1:(n - 1)]
# TODO: Optimize
U = Matrix{T}(undef, n, n)
for i in CartesianIndices(U)
if i[1] + 1 == i[2]
U[i] = -i[1]
elseif i[1] >= i[2]
U[i] = 1
else
U[i] = 0
end
end
U[n, :] .= 1/n
invU = inv(U)[:, 1:(n - 1)]
return new{T}(d, n, N, convert(T, L), μs, invU)
end
end end
norm_square(x::Array{Int})::Int = sum(x .* x)
"Eq (46): Partial derivative matrix element for 1 degree of freedom" "Eq (46): Partial derivative matrix element for 1 degree of freedom"
function ∂_1DOF(s::system{T}, k::Int, l::Int)::Complex{T} where {T<:Float} function ∂_1DOF(s::system{T}, k::Int, l::Int)::Complex{T} where {T<:Float}
if k == l if k == l
@ -42,20 +22,19 @@ function ∂_1DOF(s::system{T}, k::Int, l::Int)::Complex{T} where {T<:Float}
end end
end end
"Which index (dimension of the multidimensional array) corresponds to spatial dimension 'dim' of coordinate 'coord'?" "Which index (dimension of the multidimensional array) corresponds to spatial dimension 'dim' and particle 'p'?"
which_index(s::system, dim::Int, coord::Int)::Int = (dim - 1) * (s.n - 1) + coord which_index(s::system, dim::Int, p::Int)::Int = (dim - 1) * (s.n - 1) + p
"Get the distance to the nearest image of the particle" "Δk (distance in terms of lattice paramter) between two particles along the given dimension"
function nearest(s::system, Δk) function get_Δk(s::system, i::CartesianIndex, dim::Int, p1::Int, p2::Int)::Int
# TODO: Optimize if p1 == p2
while true return 0
if Δk >= s.N ÷ 2 elseif p1 == s.n
Δk -= s.N return -(i[which_index(s, dim, p2)] - s.N ÷ 2 - 1)
elseif Δk < -s.N ÷ 2 elseif p2 == s.n
Δk += s.N return i[which_index(s, dim, p1)] - s.N ÷ 2 - 1
else else
return Δk return i[which_index(s, dim, p1)] - i[which_index(s, dim, p2)]
end
end end
end end
@ -65,17 +44,21 @@ function calculate_Vs(s::system{T}, V_twobody::Function, ϕ::T, n_image::Int)::A
images = collect.(Iterators.product(fill(-n_image:n_image, s.d)...)) # TODO: Learn how to use tuples instead of vectors images = collect.(Iterators.product(fill(-n_image:n_image, s.d)...)) # TODO: Learn how to use tuples instead of vectors
Vs = zeros(Complex{T}, fill(s.N, s.d * (s.n - 1))...) Vs = zeros(Complex{T}, fill(s.N, s.d * (s.n - 1))...)
Threads.@threads for i in CartesianIndices(Vs) Threads.@threads for i in CartesianIndices(Vs)
xs = reshape(collect(Tuple(i)), s.n - 1, s.d) .- (s.N ÷ 2 + 1)
rs = s.invU * xs
for p1 in 1:s.n for p1 in 1:s.n
for p2 in 1:(p1 - 1) for p2 in (p1 + 1):s.n
Δk = Array{T}(undef, s.d) min_Δk = Array{Int}(undef, s.d)
for dim in 1:s.d for dim in 1:s.d
Δk_temp = rs[p1, dim] - rs[p2, dim] Δk = get_Δk(s, i, dim, p1, p2)
Δk[dim] = nearest(s, Δk_temp) if Δk > s.N ÷ 2
min_Δk[dim] = Δk - s.N
elseif Δk < -s.N ÷ 2
min_Δk[dim] = Δk + s.N
else
min_Δk[dim] = Δk
end
end end
for image in images for image in images
Δk² = norm_square(Δk .- (s.N .* image)) Δk² = norm_square(min_Δk .- (s.N .* image))
Vs[i] += V_twobody(Δk² * coeff²) Vs[i] += V_twobody(Δk² * coeff²)
end end
end end

5
helper.jl Normal file
View File

@ -0,0 +1,5 @@
"Index of the nearest value in a list to a given reference point"
nearestIndex(list::Array, ref) = argmin(norm.(list .- ref))
"Nearest value in a list to a given reference point"
nearest(list::Array, ref) = list[nearestIndex(list, ref)]

View File

@ -1,21 +0,0 @@
include("Hamiltonian.jl")
T=Float32
function V_test(r2)
return -4*exp(-r2/4)
end
for (n,N) in [(2,16), (3,8)]
println("\n$n-body system with N=$N")
n_image=0
for L::T in 5.0:9.0
print("L=$L: ")
s=system{T}(3,n,N,L)
print("Constructing H...")
H=Hamiltonian{T}(s,V_test,0.0,n_image,cpu_tensor)
print("Diagonalizing...")
evals,_,_ = eig(H,5)
println(real.(evals))
end
end