From 8cdd7b432afd366500dbbf28fcf36c8562cae639 Mon Sep 17 00:00:00 2001 From: Pablo San-Jose Date: Mon, 7 Oct 2024 17:47:16 +0200 Subject: [PATCH 1/3] integration path override to densitymatrix and josephson --- src/docstrings.jl | 42 +++++++++++++++++++++++++------------- src/observables.jl | 39 +++++++++++++++++++++++++---------- test/test_greenfunction.jl | 4 ++++ 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/docstrings.jl b/src/docstrings.jl index 4571bb9f..f2e81088 100644 --- a/src/docstrings.jl +++ b/src/docstrings.jl @@ -1976,9 +1976,10 @@ said algorithm. As above, but using a generic algorithm that relies on numerical integration along a contour in the complex plane, between points `(ωmin, ωmax)` (or along a polygonal path connecting -`ωpoints`, a collection of numbers), which should be chosen so as to encompass the full system bandwidth. Keywords -`quadgk_opts` are passed to the `QuadGK.quadgk` integration routine. See below for -additiona `opts`. +`ωpoints`, a collection of numbers), which should be chosen so as to encompass the full +system bandwidth. When the `ωpoints` are all real, an extra point is added at `ω = µ` to the +integration path, to better integrate the step in the Fermi function. Keywords `quadgk_opts` +are passed to the `QuadGK.quadgk` integration routine. See below for additiona `opts`. densitymatrix(gs::GreenSlice, ωmax::Number; opts...) @@ -1990,9 +1991,16 @@ As above with `ωmin = -ωmax`. Evaluate the density matrix at chemical potential `μ` and temperature `kBT` (in the same units as the Hamiltonian) for the given `g` parameters `params`, if any. The result is given -as an `OrbitalSliceMatrix`, see its docstring for further details. When the `ωpoints` are -all real, an extra point is added at `ω = µ` to the integration path, to better integrate -the step in the Fermi function. +as an `OrbitalSliceMatrix`, see its docstring for further details. + +If the generic integration algorithm is used with complex `ωpoints`, the following form is +also available: + + ρ(μ = 0, kBT = 0, override = missing; params...) + +Here `override` can be a collection of `ωpoints` that will replace the original ones +provided when defining `ρ`. Alternatively, it can be a function that will be applied to the +original `ωpoints`. This may be useful when the integration path must depend on `params`. ## Algorithms and keywords @@ -2035,10 +2043,13 @@ densitymatrix For a `gs = g[i::Integer]` slice of the `g::GreenFunction` of a hybrid junction, build a `J::Josephson` object representing the equilibrium (static) Josephson current `I_J` flowing into `g` through contact `i`, integrated along a polygonal contour connecting `ωpoints` (a -collection of numbers) in the complex plane. The result of `I_J` is given in units of `qe/h` -(`q` is the dimensionless carrier charge). `I_J` can be written as ``I_J = Re ∫ dω f(ω) -j(ω)``, where ``j(ω) = (qe/h) × 2Tr[(ΣʳᵢGʳ - GʳΣʳᵢ)τz]``. Here `f(ω)` is the Fermi function -with `µ = 0`. +collection of numbers) in the complex plane. When the `ωpoints` are all real, an extra point +is added at `ω = 0` to the integration path, to better integrate the step in the Fermi +function. + +The result of `I_J` is given in units of `qe/h` (`q` is the dimensionless carrier charge). +`I_J` can be written as ``I_J = Re ∫ dω f(ω) j(ω)``, where ``j(ω) = (qe/h) × 2Tr[(ΣʳᵢGʳ - +GʳΣʳᵢ)τz]``. Here `f(ω)` is the Fermi function with `µ = 0`. josephson(gs::GreenSlice, ωmax::Real; kw...) @@ -2046,12 +2057,15 @@ As above, but with `ωpoints = (-ωmax, ωmax)`. ## Full evaluation - J(kBT = 0; params...) # where J::Josephson + J(kBT = 0, override = missing; params...) # where J::Josephson Evaluate the current `I_J` at chemical potemtial `µ = 0` and temperature `kBT` (in the same -units as the Hamiltonian) for the given `g` parameters `params`, if any. When the `ωpoints` -are all real, an extra point is added at `ω = 0` to the integration path, to better -integrate the step in the Fermi function. +units as the Hamiltonian) for the given `g` parameters `params`, if any. + +It's possible to `override` a complex integration path at evaluation time. In this case +`override` can be a collection of `ωpoints` that will replace the original ones provided +when defining `J`. Alternatively, it can be a function that will be applied to the original +`ωpoints`. This may be useful when the integration path must depend on `params`. ## Keywords diff --git a/src/observables.jl b/src/observables.jl index 5e9f4561..39f560b0 100644 --- a/src/observables.jl +++ b/src/observables.jl @@ -482,13 +482,13 @@ end #region ## Constructors ## # generic fallback (for other solvers) -(ρ::DensityMatrix)(mu = 0, kBT = 0; params...) = ρ.solver(mu, kBT; params...) |> - maybe_OrbitalSliceArray(axes(ρ.gs)) +(ρ::DensityMatrix)(mu = 0, kBT = 0; params...) = + ρ.solver(mu, kBT; params...) |> maybe_OrbitalSliceArray(axes(ρ.gs)) # special case for integrator solver -(ρ::DensityMatrix{<:DensityMatrixIntegratorSolver})(mu = 0, kBT = 0; params...) = ρ.solver(mu, kBT)(; params...) |> - maybe_OrbitalSliceArray(axes(ρ.gs)) +(ρ::DensityMatrix{<:DensityMatrixIntegratorSolver})(mu = 0, kBT = 0, override = missing; params...) = + ρ.solver(mu, kBT, override)(; params...) |> maybe_OrbitalSliceArray(axes(ρ.gs)) -(s::DensityMatrixIntegratorSolver)(mu, kBT) = s.ifunc(mu, kBT); +(s::DensityMatrixIntegratorSolver)(mu, kBT, override = missing) = s.ifunc(mu, kBT, override); # redirects to specialized method densitymatrix(gs::GreenSlice; kw...) = @@ -506,8 +506,12 @@ function densitymatrix(gs::GreenSlice{T}, ωpoints; omegamap = Returns((;)), ims result = similar_Matrix(gs) opts´ = (; imshift, slope = 1, post = gf_to_rho!, atol, opts...) ωpoints_vec = collect(promote_type(T, typeof.(ωpoints)...), ωpoints) - ifunc(mu, kBT) = Integrator(result, DensityMatrixIntegrand(gs, T(mu), T(kBT), omegamap), - maybe_insert_mu!(ωpoints_vec, ωpoints, mu, kBT); opts´...) + function ifunc(mu, kBT, override) + ωpoints´ = override_path!(override, ωpoints_vec, ωpoints) + ρd = DensityMatrixIntegrand(gs, T(mu), T(kBT), omegamap) + pts = maybe_insert_mu!(ωpoints_vec, ωpoints´, zero(T), kBT) + return Integrator(result, ρd, pts; opts´...) + end return DensityMatrix(DensityMatrixIntegratorSolver(ifunc), gs) end @@ -535,6 +539,17 @@ end maybe_insert_mu!(pts, mu, kBT) = pts +override_path!(::Missing, ptsvec::Vector{<:Complex}, pts) = pts +override_path!(f::Function, ptsvec::Vector{<:Complex}, pts) = f.(pts) + +function override_path!(pts´, ptsvec::Vector{<:Complex}, pts) + resize!(ptsvec, length(pts)) + return pts´ +end + +override_path!(override, ptsvec, pts) = + argerror("Override of real ωpoints not supported, use a complex path upon construction") + #endregion #region ## API ## @@ -608,9 +623,10 @@ end # generic fallback (for other solvers) (j::Josephson)(kBT = 0; params...) = j.solver(kBT; params...) # special case for integrator solver -(j::Josephson{<:JosephsonIntegratorSolver})(kBT = 0; params...) = j.solver(kBT)(; params...) +(j::Josephson{<:JosephsonIntegratorSolver})(kBT = 0, override = missing; params...) = + j.solver(kBT, override)(; params...) -(s::JosephsonIntegratorSolver)(kBT) = s.ifunc(kBT) +(s::JosephsonIntegratorSolver)(kBT, override = missing) = s.ifunc(kBT, override) josephson(gs::GreenSlice{T}, ωmax::Number; kw...) where {T} = josephson(gs, (-ωmax, ωmax); kw...) @@ -627,10 +643,11 @@ function josephson(gs::GreenSlice{T}, ωpoints; omegamap = Returns((;)), phases phases´, traces = sanitize_phases_traces(phases, T) opts´ = (; imshift, slope = 1, post = real, atol, opts...) ωpoints_vec = collect(promote_type(T, typeof.(ωpoints)...), ωpoints) - function ifunc(kBT) + function ifunc(kBT, override) + ωpoints´ = override_path!(override, ωpoints_vec, ωpoints) jd = JosephsonIntegrand(g, T(kBT), contact, tauz, phases´, omegamap, traces, Σfull, Σ, similar(Σ), similar(Σ), similar(Σ), similar(tauz, Complex{T})) - pts = maybe_insert_mu!(ωpoints_vec, ωpoints, zero(T), kBT) + pts = maybe_insert_mu!(ωpoints_vec, ωpoints´, zero(T), kBT) return Integrator(traces, jd, pts; opts´...) end return Josephson(JosephsonIntegratorSolver(ifunc), gs) diff --git a/test/test_greenfunction.jl b/test/test_greenfunction.jl index f25aa433..065015bb 100644 --- a/test/test_greenfunction.jl +++ b/test/test_greenfunction.jl @@ -355,6 +355,10 @@ function testjosephson(g0) @test all(((j1, j2) -> ≈(j1, j2, atol = 0.000001)).(j1, J3())) @test all(((j1, j2) -> ≈(j1, j2, atol = 0.000001)).(j1, J4())) @test all(((j1, j2) -> ≈(j1, j2, atol = 0.000001)).(j1, J5())) + # path override + j5 = J5(0.2) + j5´ = J5(0.2, (-4,-2+0.1im,0) .+ sqrt(eps(Float64))*im) + @test j5 != j5 && j5 ≈ j5´ j = Quantica.integrand(J1) @test Quantica.call!(j, 0.2+0.3im) isa Vector @test typeof.(Quantica.path.((J1, J2, J3, J4, J5))) == From 867d1e4fe94ccc259d73283089bf9bb3e588e5ef Mon Sep 17 00:00:00 2001 From: Pablo San-Jose Date: Mon, 7 Oct 2024 19:12:37 +0200 Subject: [PATCH 2/3] test fix better error message fix --- src/observables.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/observables.jl b/src/observables.jl index 39f560b0..ea85d527 100644 --- a/src/observables.jl +++ b/src/observables.jl @@ -539,7 +539,7 @@ end maybe_insert_mu!(pts, mu, kBT) = pts -override_path!(::Missing, ptsvec::Vector{<:Complex}, pts) = pts +override_path!(::Missing, ptsvec, pts) = pts override_path!(f::Function, ptsvec::Vector{<:Complex}, pts) = f.(pts) function override_path!(pts´, ptsvec::Vector{<:Complex}, pts) @@ -548,7 +548,7 @@ function override_path!(pts´, ptsvec::Vector{<:Complex}, pts) end override_path!(override, ptsvec, pts) = - argerror("Override of real ωpoints not supported, use a complex path upon construction") + argerror("Override not recognized, or real ωpoints used upon construction (only complex paths can be overriden)") #endregion From db5011b24fc7f75e085d6c89d2276594c7e15121 Mon Sep 17 00:00:00 2001 From: Pablo San-Jose Date: Mon, 7 Oct 2024 21:46:11 +0200 Subject: [PATCH 3/3] ambiguity fix --- src/observables.jl | 3 ++- test/test_greenfunction.jl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/observables.jl b/src/observables.jl index ea85d527..4678a013 100644 --- a/src/observables.jl +++ b/src/observables.jl @@ -540,6 +540,7 @@ end maybe_insert_mu!(pts, mu, kBT) = pts override_path!(::Missing, ptsvec, pts) = pts +override_path!(::Missing, ptsvec::Vector{<:Complex}, pts) = pts override_path!(f::Function, ptsvec::Vector{<:Complex}, pts) = f.(pts) function override_path!(pts´, ptsvec::Vector{<:Complex}, pts) @@ -548,7 +549,7 @@ function override_path!(pts´, ptsvec::Vector{<:Complex}, pts) end override_path!(override, ptsvec, pts) = - argerror("Override not recognized, or real ωpoints used upon construction (only complex paths can be overriden)") + argerror("Override of real ωpoints not supported, use complex ωpoints upon construction") #endregion diff --git a/test/test_greenfunction.jl b/test/test_greenfunction.jl index 065015bb..a608c964 100644 --- a/test/test_greenfunction.jl +++ b/test/test_greenfunction.jl @@ -358,7 +358,7 @@ function testjosephson(g0) # path override j5 = J5(0.2) j5´ = J5(0.2, (-4,-2+0.1im,0) .+ sqrt(eps(Float64))*im) - @test j5 != j5 && j5 ≈ j5´ + @test j5 != j5´ && j5 ≈ j5´ j = Quantica.integrand(J1) @test Quantica.call!(j, 0.2+0.3im) isa Vector @test typeof.(Quantica.path.((J1, J2, J3, J4, J5))) ==