Skip to content

Commit 8e4e5db

Browse files
committed
initial commit
0 parents  commit 8e4e5db

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+6381
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* text=auto
2+

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Files generated by invoking Julia with --code-coverage
2+
*.jl.cov
3+
*.jl.*.cov
4+
5+
# Files generated by invoking Julia with --track-allocation
6+
*.jl.mem
7+
8+
# System-specific files and directories generated by the BinaryProvider and BinDeps packages
9+
# They contain absolute paths specific to the host computer, and so should not be committed
10+
deps/deps.jl
11+
deps/build.log
12+
deps/downloads/
13+
deps/usr/
14+
deps/src/
15+
16+
# Build artifacts for creating documentation generated by the Documenter package
17+
docs/build/
18+
docs/site/
19+
20+
# File generated by Pkg, the package manager, based on a corresponding Project.toml
21+
# It records a fixed state of all packages used by the project. As such, it should not be
22+
# committed for packages, but should be committed for applications that require a static
23+
# environment.
24+
Manifest.toml
25+
26+
.env
27+
results/
28+
package-lock.json
29+
dist/
30+
node_modules/
31+
32+
.vscode/
33+

Examples.md

Lines changed: 437 additions & 0 deletions
Large diffs are not rendered by default.

Project.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name = "SchroedingerNumerics"
2+
uuid = "537d3920-04f8-45d5-b939-67e14d5b2f3b"
3+
authors = ["cnoelle <[email protected]>"]
4+
version = "0.1.0"
5+
6+
[deps]
7+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
8+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
9+
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"

README.md

Lines changed: 385 additions & 0 deletions
Large diffs are not rendered by default.

examples/freeParticle/gaussian.json

Lines changed: 17 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "classical",
3+
"scheme": {"id": "SymplecticEuler" , "mass": 1, "deltaX": 0.001},
4+
"V_coefficients": [0,0,1],
5+
"mass": 1.0,
6+
"deltaT": 0.006283185307179587,
7+
"t": 0.0,
8+
"point": [1.0,0.0]
9+
}

examples/harmonicOscillator/coherentStateQm.json

Lines changed: 18 additions & 0 deletions
Large diffs are not rendered by default.

examples/harmonicOscillator/coherentStateQmResidual.json

Lines changed: 21 additions & 0 deletions
Large diffs are not rendered by default.

examples/harmonicOscillator/groundStateQm.json

Lines changed: 18 additions & 0 deletions
Large diffs are not rendered by default.

src/ClassicalSystem.jl

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
struct ClassicalSystem
2+
initialState::AbstractPoint
3+
hamiltonian::AbstractObservable
4+
propagator::ClassicalPropagator
5+
scheme::ClassicalNumericsScheme
6+
deltaT::Real
7+
# state
8+
currentTime::Real
9+
currentState::AbstractPoint
10+
function ClassicalSystem(point::AbstractPoint, hamiltonian::AbstractObservable, deltaT::Real;
11+
scheme::ClassicalNumericsScheme=SymplecticEuler(), t0::Real=0.)
12+
propagator::ClassicalPropagator = incarnate(hamiltonian, scheme, deltaT)
13+
return new(point, hamiltonian, propagator, scheme, deltaT, t0, point)
14+
end # ClassicalSystem
15+
function ClassicalSystem(other::ClassicalSystem, t::Real, point::AbstractPoint)
16+
return new(other.initialState, other.hamiltonian, other.propagator,
17+
other.scheme, other.deltaT, t, point)
18+
end # ClassicalSystem
19+
end # ClassicalSystem
20+
21+
function scheme(system::ClassicalSystem)::NumericsScheme
22+
return system.scheme
23+
end
24+
25+
function hamiltonian(system::ClassicalSystem)::AbstractObservable
26+
return system.hamiltonian
27+
end
28+
29+
function deltaT(system::ClassicalSystem)::Real
30+
return system.deltaT
31+
end
32+
33+
function propagate(system::ClassicalSystem, timeSteps::Int=1)::ClassicalSystem
34+
for _ in 1:timeSteps
35+
point::AbstractPoint = propagateSingleTimestep(system.currentState, system.propagator)
36+
system = ClassicalSystem(system, system.currentTime + system.deltaT, point)
37+
end # for k
38+
return system
39+
end # p
40+
41+
function _writeSettings(system::ClassicalSystem, file::IO)
42+
indent::String = " "
43+
write(file, "{\n")
44+
write(file, indent, "\"type\": \"classical\",\n")
45+
write(file, indent, "\"deltaT\": $(system.deltaT),\n")
46+
write(file, indent, "\"scheme\": $(_schemeToJson(system.scheme))")
47+
try
48+
_writePotentialJson(file, projectX(system.hamiltonian),
49+
indent=length(indent), startWithComma=true)
50+
catch
51+
end
52+
write(file, "\n}\n")
53+
end # _writeSettings
54+
55+
56+
function trace(system::ClassicalSystem, timeSteps::Int=1000;
57+
folder::String = "./results") # io::IO
58+
Base.Filesystem.mkpath(folder)
59+
settingsFile::String = joinpath(folder, "settings.json")
60+
pointsFile::String = joinpath(folder, "points.csv")
61+
open(settingsFile, "w") do settingsFile1
62+
_writeSettings(system, settingsFile1)
63+
end # settingsFile
64+
V::Any = nothing
65+
try
66+
V = projectX(system.hamiltonian) # we expect this to be a function of x
67+
catch
68+
end
69+
open(pointsFile, "w") do fileObservables
70+
write(fileObservables, "x, p, E\n")
71+
for _ in 1:timeSteps
72+
# write expectation values
73+
point::Point = pin(system.currentState)
74+
xVal::Real = point.q
75+
pVal::Real = point.p
76+
energy::Real = system.hamiltonian(point)
77+
write(fileObservables, "$(xVal), $(pVal), $(energy)\n")
78+
system = propagate(system, 1)
79+
end # for k
80+
end # open fileObservables
81+
return system
82+
83+
end # trace
84+
85+
export ClassicalSystem
86+
export propagate
87+
export trace
88+

src/CrankNicolson.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
raw"Implements the Crank-Nicolson integration scheme for the Schrödinger equation, i.e.
2+
(1+i/(2\hbar) \hat H) \psi(t+1) = (1-i/(2\hbar) \hat H)\psi(t) "
3+
struct CrankNicolson <: QmNumericsScheme
4+
end # CrankNicolson
5+
6+
function schemeId(::CrankNicolson)::String
7+
return "CrankNicolson"
8+
end
9+
10+
struct CNMatrix <: QuantumPropagator
11+
representation::QmRepresentation
12+
config::SchroedingerConfig
13+
raw"(1+i*deltaT/(2\hbar) \hat H)"
14+
A::AbstractArray{<:Complex, 2}
15+
raw"(1-i*deltaT/(2\hbar) \hat H)"
16+
B::AbstractArray{<:Complex, 2}
17+
raw"The time evolution matrix (1+i/(2\hbar) \hat H)^(-1) * (1-i/(2\hbar) \hat H)"
18+
AinvB::Union{AbstractArray{<:Complex, 2}, Nothing}
19+
end # CNMatrix
20+
21+
"""
22+
Find the concrete matrix representation of the Hamiltonian for the selected grid
23+
"""
24+
function incarnate(hamiltonian::AbstractObservable, representation::QmRepresentation,
25+
scheme::CrankNicolson, deltaT::Real, invertMatrix::Bool=false)::QuantumPropagator
26+
cfg = qmConfig(representation)
27+
H = asOperator(hamiltonian, representation)
28+
hbar = cfg.hbar
29+
A = (LinearAlgebra.I + (im*deltaT/2/hbar) * H)
30+
B = (LinearAlgebra.I - (im*deltaT/2/hbar) * H)
31+
AinvB = invertMatrix ? LinearAlgebra.inv(A) * B : nothing
32+
return CNMatrix(representation, cfg, A, B, AinvB)
33+
end # incarnate
34+
35+
36+
function propagateSingleTimestep(psi::AbstractWaveFunction, propagator::CNMatrix)::AbstractWaveFunction
37+
valuesOld::AbstractArray{<:Complex, 1} = values(psi, propagator.representation)
38+
valuesNew::AbstractArray{<:Complex, 1} = isnothing(propagator.AinvB) ? propagator.A\(propagator.B * valuesOld) : propagator.AinvB * valuesOld
39+
return asWavefunction(valuesNew, propagator.representation)
40+
end # propagateSingleTimestep
41+
42+
export CrankNicolson

src/DifferentiationUtils.jl

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# below: different types of operators associated to V, and helpers
2+
"
3+
Differentiation as a matrix operator
4+
5+
* 1: forward/Newton (default)
6+
* 0: symmetric/midpoint
7+
* -1: backward
8+
"
9+
function _diffOperator(gridPoints::AbstractRange{<:Real}, order::Int;
10+
diffMethod::Int=1)::AbstractArray{<:Real, 2}
11+
if order == 0
12+
return LinearAlgebra.I
13+
end # if
14+
# In principle we can give a formula that works for all orders, see
15+
# https://en.wikipedia.org/wiki/Numerical_differentiation#Higher_derivatives
16+
# It is rather tricky, however, to take into account the boundary conditions, so we do it case by case
17+
numPoints::Int = length(gridPoints)
18+
deltaX::Real = step(gridPoints)
19+
if (order === 1) # the first derivative may be calculated in different ways
20+
# FIXME use ints?
21+
if (diffMethod === 1)
22+
diag1 = fill(-1., numPoints)
23+
diag1[numPoints] = 0.
24+
M1::AbstractArray{<:Real, 2} = LinearAlgebra.Bidiagonal(diag1, ones(numPoints-1), :U)
25+
return M1/deltaX
26+
elseif (diffMethod === 0)
27+
upper = fill(1., numPoints-1)
28+
lower = fill(-1., numPoints-1)
29+
upper[1] = 2.
30+
lower[numPoints-1] = -2.
31+
diag2 = fill(0., numPoints)
32+
diag2[1] = -2.
33+
diag2[numPoints] = 2.
34+
M2::AbstractArray{<:Real, 2} = LinearAlgebra.Tridiagonal(lower, diag2, upper)
35+
return M2/deltaX/2
36+
elseif (diffMethod === -1)
37+
diag3 = fill(1., numPoints)
38+
diag3[0] = 0.
39+
M3::AbstractArray{<:Real, 2} = LinearAlgebra.Bidiagonal(-ones(numPoints), diag3, :L)
40+
return M3/deltaX
41+
end
42+
elseif (order === 2)
43+
diagonal::AbstractArray{Float64, 1} = fill(-2., numPoints)
44+
diagonal[1] = -1.
45+
diagonal[numPoints] = -1.
46+
Msquared::AbstractArray{Float64, 2} = LinearAlgebra.Tridiagonal(ones(numPoints-1), diagonal, ones(numPoints-1))
47+
return Msquared/(deltaX^2)
48+
end # if
49+
squares::Int = convert(Int, floor(order / 2))
50+
M::AbstractArray{<:Real, 2} = _diffOperator(gridPoints, 2, diffMethod=diffMethod)^squares
51+
return order % 2 === 0 ? M : M * _diffOperator(gridPoints, 1, diffMethod=diffMethod)
52+
end # _diffOperator
53+
54+
"Differentiation as a matrix operator",
55+
function _diffOperator(gridPoints::AbstractArray{<:Real, 1}, order::Int; diffMethod::Int=1)::AbstractArray{<:Real, 2}
56+
if order == 0
57+
return LinearAlgebra.I
58+
end # if
59+
# this is the case that gridPoints is not equally spaced (is not a range)
60+
# In principle we can give a formula that works for all orders, see
61+
# https://en.wikipedia.org/wiki/Numerical_differentiation#Higher_derivatives
62+
# It is rather tricky, however, to take into account the boundary conditions, so we do it case by case
63+
numPoints::Int = length(gridPoints)
64+
if (order === 1) # the first derivative may be calculated in different ways
65+
if (diffMethod === 1)
66+
upper1 = map(idx -> 1/(gridPoints[idx+1] - gridPoints[idx]), 1:(numPoints-1))
67+
diag1 = -copy(upper1)
68+
push!(diag1, 0.)
69+
return LinearAlgebra.Bidiagonal(diag1, upper1, :U)
70+
elseif (diffMethod === 0)
71+
upper2 = map(idx -> 1/(gridPoints[idx+1] - gridPoints[idx-1]), 2:(numPoints-1))
72+
lower2 = -copy(upper2)
73+
diag2 = fill(0., numPoints-2)
74+
firstUpper::Float64 = 1/(gridPoints[2] - gridPoints[1])
75+
prepend!(upper2, firstUpper)
76+
prepend!(diag2, -firstUpper)
77+
lastLower::Float64 = 1/(gridPoints[numPoints] - gridPoints[numPoints-1])
78+
push!(lower2, -lastLower)
79+
push!(diag2, lastLower)
80+
return LinearAlgebra.Tridiagonal(lower2, diag2, upper2)
81+
elseif (diffMethod === -1)
82+
diag3 = map(idx -> 1/(gridPoints[idx] - gridPoints[idx-1]), 2:numPoints)
83+
lower3 = -copy(diag3)
84+
prepend!(diag3, 0.)
85+
return LinearAlgebra.Bidiagonal(diag3, lower3, :L)
86+
end
87+
elseif (order === 2)
88+
upperSquare = map(idx -> 1/(gridPoints[idx+1] - gridPoints[idx])/(gridPoints[idx] - gridPoints[idx-1]), 2:(numPoints-1))
89+
lowerSquare = copy(upperSquare)
90+
diagSquare = -2 * copy(upperSquare)
91+
firstUpperSquare::Real = 1/(gridPoints[2] - gridPoints[1])^2
92+
prepend!(upperSquare, firstUpperSquare)
93+
prepend!(diagSquare, -firstUpperSquare)
94+
lastLowerSquare::Real = 1/(gridPoints[numPoints] - gridPoints[numPoints-1])^2
95+
push!(lowerSquare, lastLowerSquare)
96+
push!(diagSquare, -lastLowerSquare)
97+
return LinearAlgebra.Tridiagonal(lowerSquare, diagSquare, upperSquare)
98+
end # if
99+
squares::Int = convert(Int, floor(order / 2))
100+
M::AbstractArray{<:Real, 2} = _diffOperator(gridPoints, 2, diffMethod=diffMethod)^squares
101+
return order % 2 === 0 ? M : M * _diffOperator(gridPoints, 1, diffMethod=diffMethod)
102+
end # _diffOperator

src/ExactQuantumPropagation.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"Get the next value of an exactly known wave function"
2+
struct ExactPropagation <: QmNumericsScheme
3+
end # ExactPropagation
4+
5+
function schemeId(::ExactPropagation)::String
6+
return "ExactQmPropagation"
7+
end
8+
9+
struct ExactPropagator <: QuantumPropagator
10+
deltaT::Real
11+
end # ExactPropagator
12+
13+
function incarnate(hamiltonian::AbstractObservable, representation::QmRepresentation,
14+
scheme::ExactPropagation, deltaT::Real)::QuantumPropagator
15+
return ExactPropagator(deltaT)
16+
end #specialize
17+
18+
# propagate time step (qm)
19+
function propagateSingleTimestep(psi::ExactWaveFunction, propagator::ExactPropagator)::ExactWaveFunction
20+
return ExactWaveFunction(psi.f, psi.timestamp + propagator.deltaT)
21+
end # propagateSingleTimestep
22+
23+
function propagateSingleTimestep(psi::ExactSampledWaveFunction, propagator::ExactPropagator)::ExactSampledWaveFunction
24+
return ExactSampledWaveFunction(psi.psi0, psi.f, psi.timestamp + propagator.deltaT)
25+
end # propagateSingleTimestep
26+
27+
function propagateSingleTimestep(psi::TranslatedExactWaveFunction, propagator::ExactPropagator)::TranslatedExactWaveFunction
28+
return TranslatedExactWaveFunction(psi.trajectory, propagateSingleTimestep(psi.psi, propagator), psi.hbar)
29+
end # propagateSingleTimestep
30+
31+
export ExactPropagation

src/ExactTrajectory.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
struct ExactTrajectory <: ClassicalNumericsScheme
2+
# A function Real -> Point (time -> phase space)
3+
f::Any
4+
end # struct
5+
6+
function schemeId(scheme::ExactTrajectory)::String
7+
return "ExactTrajectory"
8+
end
9+
10+
struct ExactTrajectoryPropagator <: ClassicalPropagator
11+
scheme::ExactTrajectory
12+
deltaT::Real
13+
end # ExactTrajectoryPropagator
14+
15+
function incarnate(hamiltonian::AbstractObservable,
16+
scheme::ExactTrajectory, deltaT::Real)::ExactTrajectoryPropagator
17+
return ExactTrajectoryPropagator(scheme, deltaT)
18+
end #specialize
19+
20+
struct TimedPoint <: AbstractPoint
21+
q::Real
22+
p::Real
23+
t::Real
24+
end # TimedPoint
25+
26+
function pin(point::TimedPoint)::Point
27+
return Point(point.q, point.p)
28+
end # pin
29+
30+
# propagate time step (classical)
31+
function propagateSingleTimestep(point::AbstractPoint, propagator::ExactTrajectoryPropagator)::TimedPoint
32+
if !(point isa TimedPoint)
33+
p = pin(point)
34+
point = TimedPoint(p.q, p.p, 0.)
35+
end # if
36+
newT::Real = point.t + propagator.deltaT
37+
newPoint::Point = propagator.scheme.f(newT)
38+
return TimedPoint(newPoint.q, newPoint.p, newT)
39+
end # propagateSingleTimestep
40+
41+
export ExactTrajectory

0 commit comments

Comments
 (0)