Skip to content

Commit

Permalink
Good defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
kbarros committed Sep 17, 2024
1 parent b4c857e commit b6cc2b9
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 51 deletions.
8 changes: 4 additions & 4 deletions examples/09_Disordered_spectrum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ plot_spins(sys; color=[s[3] for s in sys.dipoles], ndims=2)
qs = [[0, 0, 0], [1/3, 1/3, 0], [1/2, 0, 0], [0, 0, 0]]
labels = ["Γ", "K", "M", "Γ"]
path = q_space_path(cryst, qs, 150; labels)
kernel = gaussian(fwhm=0.4);
kernel = lorentzian(fwhm=0.4);

# Perform a traditional spin wave calculation. The spectrum shows sharp modes
# associated with coherent excitations about the K-point ordering wavevector,
Expand Down Expand Up @@ -67,13 +67,13 @@ plot_spins(sys_inhom; color=[s[3] for s in sys_inhom.dipoles], ndims=2)
# [`SpinWaveTheory`](@ref). Using KPM, the cost of an [`intensities`](@ref)
# calculation becomes linear in system size and scales inversely with the width
# of the line broadening `kernel`. Error is controllable through the
# dimensionless `tol` parameter. Reasonable choices are 0.1 (faster) or 0.01
# (more accurate).
# dimensionless `tol` parameter. A relatively small value is needed to resolve
# the large intensities near the Goldstone mode.
#
# Observe that disorder in the nearest-neighbor exchange serves to broaden the
# discrete excitation bands into a continuum.

swt = SpinWaveTheoryKPM(sys_inhom; measure=ssf_perp(sys_inhom), tol=0.1)
swt = SpinWaveTheoryKPM(sys_inhom; measure=ssf_perp(sys_inhom), tol=0.01)
res = intensities(swt, path; energies, kernel)
plot_intensities(res)

Expand Down
64 changes: 29 additions & 35 deletions src/KPM/SpinWaveTheoryKPM.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ and the specified error tolerance `tol`. Specifically, for each wavevector,
``M`` scales like the spectral bandwidth of excitations, divided by the energy
resolution of the broadening kernel, times the negative logarithm of `tol`.
Reasonable choices of the error tolerance `tol` are `1e-1` for a faster
calculation, or `1e-2` for a more accurate calculation.
The error tolerance `tol` should be tuned empirically for each calculation.
Reasonable starting points are `1e-1` (more speed) or `1e-2` (more accuracy).
References:
Expand All @@ -24,33 +24,14 @@ References:
struct SpinWaveTheoryKPM
swt :: SpinWaveTheory
tol :: Float64
lanczos_iters :: Int

function SpinWaveTheoryKPM(sys::System; measure::Union{Nothing, MeasureSpec}, regularization=1e-8, tol)
return new(SpinWaveTheory(sys; measure, regularization), tol)
function SpinWaveTheoryKPM(sys::System; measure::Union{Nothing, MeasureSpec}, regularization=1e-8, tol, lanczos_iters=15)
return new(SpinWaveTheory(sys; measure, regularization), tol, lanczos_iters)
end
end


# Smooth approximation to a Heaviside step function. The original idea was to
# construct an convolution kernel that is smooth everywhere, avoiding the need
# for a Jackson kernel. Although this would work, it also has the effect of
# masking intensities at small energy. TODO: Ideally, one would instead
# dynamically extend the polynomial order M if it is detected that there is
# large intensity near zero energy. An alternative (currently implemented) is to
# leave the Jackson kernel on at all times and use a sharp step function for
# this regularization. The Jackson kernel makes KPM easier to use, and mitigates
# intensity masking, but sacrifices significant accuracy. (Error decreases
# linearly with polynomial order rather than exponentially.)
function regularization_function(y)
if y < 0
return 0.0
elseif 0 y 1
return (4 - 3y) * y^3
else
return 1.0
end
end

function mul_Ĩ!(y, x)
L = size(y, 2) ÷ 2
view(y, :, 1:L) .= .+view(x, :, 1:L)
Expand All @@ -71,10 +52,11 @@ function set_moments!(moments, measure, u, α)
end


function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::AbstractBroadening, kT=0.0, verbose=false, with_jackson=true)
function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::AbstractBroadening, kT=0.0, verbose=false)
iszero(kT) || error("KPM does not yet support finite kT")
qpts = convert(AbstractQPoints, qpts)

(; swt, tol) = swt_kpm
(; swt, tol, lanczos_iters) = swt_kpm
(; sys, measure) = swt
cryst = orig_crystal(sys)

Expand All @@ -87,7 +69,6 @@ function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::
Ncells = Na / natoms(cryst)
Nf = nflavors(swt)
L = Nf*Na
n_lancozs_iters = 10
Avec_pref = zeros(ComplexF64, Na) # initialize array of some prefactors

Nobs = size(measure.observables, 1)
Expand Down Expand Up @@ -140,10 +121,10 @@ function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::
# Bound eigenvalue magnitudes and determine order of polynomial
# expansion

lo, hi = eigbounds(swt, q_reshaped, n_lancozs_iters)
lo, hi = eigbounds(swt, q_reshaped, lanczos_iters)
γ = 1.1 * max(abs(lo), hi)
factor = max(-3*log10(tol), 1)
M = round(Int, factor * 2γ / kernel.fwhm)
accuracy_factor = max(-3*log10(tol), 1)
M = round(Int, accuracy_factor * 2γ / kernel.fwhm)
resize!(moments, Ncorr, M)

if verbose
Expand All @@ -170,10 +151,23 @@ function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::
plan = FFTW.plan_r2r!(buf, FFTW.REDFT10)

for (iω, ω) in enumerate(energies)
ωcut = with_jackson ? 0.0 : kernel.fwhm
f(x) = regularization_function(x / ωcut) * kernel(x, ω) * thermal_prefactor(x; kT)
# Ideally we would use thermal_prefactor instead of
# thermal_prefactor_zero to allow for finite temperature effects.
# Unfortunately, the Bose function's 1/x singularity introduces
# divergence of the Chebyshev expansion integrals, and is tricky to
# regularize. At kT=0, the occupation is a Heaviside step function.
# To mitigate ringing artifacts associated with truncated Chebyshev
# approximation, introduce smoothing on the energy scale σ. This is
# the polynomial resolution scale times a prefactor that grows like
# sqrt(accuracy) to reduce lingering ringing artifacts. See "AFM
# KPM" for a test case where the smoothing degrades accuracy, and
# "Disordered spectrum with KPM" for an illustration of how
# smoothing affects intensities near the Goldstone mode.
σ = sqrt(accuracy_factor) */ M)
thermal_prefactor_zero(x) = (tanh(x / σ) + 1) / 2
f(x) = kernel(x, ω) * thermal_prefactor_zero(x)
coefs = cheb_coefs!(M, f, (-γ, γ); buf, plan)
with_jackson && apply_jackson_kernel!(coefs)
# apply_jackson_kernel!(coefs)
for i in 1:Ncorr
corrbuf[i] = dot(coefs, view(moments, i, :)) / Ncells
end
Expand All @@ -184,8 +178,8 @@ function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::
return Intensities(cryst, qpts, collect(energies), data)
end

function intensities(swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::AbstractBroadening, kT=0.0, verbose=false, with_jackson=true)
function intensities(swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::AbstractBroadening, kT=0.0, verbose=false)
qpts = convert(AbstractQPoints, qpts)
data = zeros(eltype(swt_kpm.swt.measure), length(energies), length(qpts.qs))
return intensities!(data, swt_kpm, qpts; energies, kernel, kT, verbose, with_jackson)
return intensities!(data, swt_kpm, qpts; energies, kernel, kT, verbose)
end
9 changes: 3 additions & 6 deletions src/SpinWaveTheory/DispersionAndIntensities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,9 @@ end

# Returns |1 + nB(ω)| where nB(ω) = 1 / (exp(βω) - 1) is the Bose function.
function thermal_prefactor(ω; kT)
if iszero(kT)
return iszero(ω) ? 1/2 :> 0 ? 1 : 0)
else
@assert kT > 0
return abs(1 / (1 - exp(-ω/kT)))
end
@assert kT >= 0
iszero(ω) && return Inf
return abs(1 / (1 - exp(-ω/kT)))
end


Expand Down
11 changes: 5 additions & 6 deletions test/test_kpm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,16 @@ end
formfactors = [1 => FormFactor("Fe2")]
measure = ssf_perp(sys; formfactors)
swt = SpinWaveTheory(sys; measure)
swt_kpm = SpinWaveTheoryKPM(sys; measure, tol=1e-6)
swt_kpm = SpinWaveTheoryKPM(sys; measure, tol=1e-8)

kernel = lorentzian(fwhm=0.1)
energies = range(0, 6, 100)
kT = 0.2
kT = 0.0
res1 = intensities(swt, [q]; energies, kernel, kT)
res2 = intensities(swt_kpm, [q]; energies, kernel, kT, with_jackson=false)
res2 = intensities(swt_kpm, [q]; energies, kernel, kT)

# TODO: Accuracy is very poor with Jackson kernel enabled (decreases
# inversely with polynomial order). But it's difficult to disable the
# Jackson kernel while avoiding "Gibbs ringing" at Goldstone modes.
# Note that KPM accuracy is currently limited by Gibbs ringing
# introduced in the thermal occupancy (Heaviside step) function.
@test isapprox(res1.data, res2.data, rtol=1e-5)
end
end

0 comments on commit b6cc2b9

Please sign in to comment.