Skip to content

Commit

Permalink
Rename :dipole_large_S to :dipole_uncorrected (#321)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbarros authored Sep 20, 2024
1 parent 05af30b commit ff453e3
Show file tree
Hide file tree
Showing 20 changed files with 73 additions and 69 deletions.
33 changes: 17 additions & 16 deletions docs/src/renormalization.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,11 @@ same form, but now applied to the expected dipole. Classical Stevens functions
are constructed as homogeneous polynomials of order $k$, because lower-order
terms would vanish in the limit $s \to \infty$.

In a real magnetic compound, however, the spin magnitude $s$ is not necessarily
large. To obtain a better approximation, one should avoid the formal limit $s
\to \infty$. Our approach is to start with the full dynamics of SU(_N_) coherent
states, and then constrain it to the space of pure dipole states
$|\boldsymbol{\Omega}\rangle$. The latter are defined as any states where the
expected dipole 3-vector,
For real compounds with finite quantum spin-$s$, one can obtain a better
approximation by avoiding the formal $s \to \infty$ limit. Corrections can be
derived by starting from the full dynamics of SU(_N_) coherent states and then
constraining to the space of pure dipole states $|\boldsymbol{\Omega}\rangle$.
The latter are defined as any states where the expected dipole 3-vector,
```math
\boldsymbol{\Omega} ≡ \langle \boldsymbol{\Omega}| \hat{\mathbf{S}} | \boldsymbol{\Omega}\rangle,
```
Expand Down Expand Up @@ -128,8 +127,8 @@ interpreted as a correction to the traditional large-$s$ classical limit.

Renormalization also applies to the coupling between different sites. In Sunny,
couplings will often be expressed as a polynomial of spin operators using
[`set_pair_coupling!`](@ref), but any such coupling can be decomposed as sum of
tensor products of Stevens operators. Without loss of generality, consider a
[`set_pair_coupling!`](@ref), but any such coupling can be decomposed as a sum
of tensor products of Stevens operators. Without loss of generality, consider a
single coupling between two Stevens operators
$\hat{\mathcal{H}}_\mathrm{coupling} = \hat{\mathcal{O}}_{k,q} \otimes
\hat{\mathcal{O}}_{k',q'}$ along a bond connecting sites $i$ and $j$. Upon
Expand All @@ -139,16 +138,18 @@ $E_\mathrm{coupling} = c_k c_k' \mathcal{O}_{k,q}(\boldsymbol{\Omega}_i)
\mathcal{O}_{k',q'}(\boldsymbol{\Omega}_j)$, which now involves a product of
renormalized Stevens functions.

## Use `:dipole_large_s` mode to disable renormalization
## Use `:dipole_uncorrected` mode to disable renormalization

Although we generally recommend the above renormalization procedure, there are
circumstances where it is not desirable. Examples include reproducing a
model-system study, or describing a micromagnetic system for which the
$s\to\infty$ limit is a good approximation. To simulate dipoles without
interaction strength renormalization, construct a [`System`](@ref) using the
mode `:dipole_large_s` instead of `:dipole`. Symbolic operators in the large-$s$
limit can be constructed by passing `Inf` to either [`spin_matrices`](@ref) or
[`stevens_matrices`](@ref).
cases where it is not desirable. Examples include reproducing a legacy study, or
modeling a micromagnetic system for which the $s\to\infty$ limit is a good
approximation. To simulate dipoles without interaction strength renormalization,
construct a [`System`](@ref) using the mode `:dipole_uncorrected` instead of
`:dipole`. The fundamental difference between the two modes is the type of
operator expected by [`set_onsite_coupling!`](@ref). The mode
`:dipole_uncorrected` expects infinite-dimensional operators in the $s → ∞$
limit. These can be obtained by passing `Inf` to either [`spin_matrices`](@ref)
or [`stevens_matrices`](@ref).

## Definition of Stevens operators

Expand Down
4 changes: 3 additions & 1 deletion docs/src/versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#317](https://github.com/SunnySuite/Sunny.jl/pull/317)).
* Stabilize [`SpinWaveTheoryKPM`](@ref). It now automatically selects the
polynomial order according to an error tolerance.
* Rename mode `:dipole_large_S` to `:dipole_uncorrected` to emphasize that
corrections are missing.

## v0.7.2
(Sep 11, 2024)
Expand Down Expand Up @@ -49,7 +51,7 @@ This **major release** introduces breaking interface changes.
[`domain_average`](@ref), which wrap [`intensities`](@ref).
* [`System`](@ref) now expects supercell dimensions as a `dims` keyword
argument. [`Moment`](@ref) replaces `SpinInfo`. Lower-case `s` now labels
quantum spin. For example, use `:dipole_large_s` instead of `:dipole_large_S`.
quantum spin.
* In [`view_crystal`](@ref) and [`plot_spins`](@ref) use `ndims` instead of
`dims` for the number of spatial dimensions.
* Binning features have been removed. Some functionality may be added back in a
Expand Down
2 changes: 1 addition & 1 deletion examples/spinw_tutorials/SW12_Triangular_easy_plane.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ set_exchange!(sys, J1, Bond(1, 1, [1, 0, 0]))
# interaction strength as ``D → (1 - 1/2s) D``. We must "undo" Sunny's
# classical-to-quantum rescaling factor to reproduce the SpinW calculation.
# Alternatively, renormalization can be disabled by selecting the system mode
# `:dipole_large_s` instead of `:dipole`.
# `:dipole_uncorrected` instead of `:dipole`.

undo_classical_to_quantum_rescaling = 1 / (1 - 1/2s)
D = 0.2 * undo_classical_to_quantum_rescaling
Expand Down
4 changes: 2 additions & 2 deletions examples/spinw_tutorials/SW13_LiNiPO4.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ view_crystal(cryst)
# Create a system with exchange parameters taken from [T. Jensen, et al., PRB
# **79**, 092413 (2009)](https://doi.org/10.1103/PhysRevB.79.092413). The
# corrected anisotropy values are taken from the thesis of T. Jensen. The mode
# `:dipole_large_s` avoids a [classical-to-quantum rescaling factor](@ref
# `:dipole_uncorrected` avoids a [classical-to-quantum rescaling factor](@ref
# "Interaction Renormalization") of anisotropy strengths, as needed for
# consistency with the original fits.

sys = System(cryst, [1 => Moment(s=1, g=2)], :dipole_large_s)
sys = System(cryst, [1 => Moment(s=1, g=2)], :dipole_uncorrected)
Jbc = 1.036
Jb = 0.6701
Jc = -0.0469
Expand Down
5 changes: 3 additions & 2 deletions examples/spinw_tutorials/SW14_YVO3.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ cryst = Crystal(latvecs, positions, 1; types)

# Create a system following the model of [C. Ulrich, et al. PRL **91**, 257202
# (2003)](https://doi.org/10.1103/PhysRevLett.91.257202). The mode
# `:dipole_large_s` avoids a [classical-to-quantum rescaling factor](@ref
# `:dipole_uncorrected` avoids a [classical-to-quantum rescaling factor](@ref
# "Interaction Renormalization") of anisotropy strengths, as needed for
# consistency with the original fits.

sys = System(cryst, [1 => Moment(s=1/2, g=2), 2 => Moment(s=1/2, g=2)], :dipole_large_s; dims=(2,2,1))
moments = [1 => Moment(s=1/2, g=2), 2 => Moment(s=1/2, g=2)]
sys = System(cryst, moments, :dipole_uncorrected; dims=(2,2,1))
Jab = 2.6
Jc = 3.1
δ = 0.35
Expand Down
2 changes: 1 addition & 1 deletion src/EntangledUnits/EntangledSpinWaveTheory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ function dynamical_matrix!(H, swt::EntangledSpinWaveTheory, q_reshaped)
if swt.sys.mode == :SUN
swt_hamiltonian_SUN!(H, swt, q_reshaped)
else
@assert swt.sys.mode in (:dipole, :dipole_large_S)
@assert swt.sys.mode in (:dipole, :dipole_uncorrected)
swt_hamiltonian_dipole!(H, swt, q_reshaped)
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/KPM/SpinWaveTheoryKPM.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::
end
end
else
@assert sys.mode in (:dipole, :dipole_large_s)
@assert sys.mode in (:dipole, :dipole_uncorrected)
(; sqrtS, observables_localized) = swt.data::SWTDataDipole
for i in 1:Na
for μ in 1:Nobs
Expand Down
2 changes: 1 addition & 1 deletion src/SpinWaveTheory/DispersionAndIntensities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ function intensities_bands(swt::SpinWaveTheory, qpts; kT=0, with_negative=false)
end
end
else
@assert sys.mode in (:dipole, :dipole_large_s)
@assert sys.mode in (:dipole, :dipole_uncorrected)
data = swt.data::SWTDataDipole
t = reshape(view(T, :, band), Na, 2)
for i in 1:Na, μ in 1:Nobs
Expand Down
4 changes: 2 additions & 2 deletions src/SpinWaveTheory/LSWTCorrections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ end
magnetization_lswt_correction(swt::SpinWaveTheory; opts...)
Calculates the reduction in the classical dipole magnitude for all atoms in the
magnetic cell. In the case of `:dipole` and `:dipole_large_s` mode, the
magnetic cell. In the case of `:dipole` and `:dipole_uncorrected` mode, the
classical dipole magnitude is constrained to spin-`s`. While in `:SUN` mode, the
classical dipole magnitude can be smaller than `s` due to anisotropic
interactions.
Expand All @@ -113,7 +113,7 @@ function magnetization_lswt_correction(swt::SpinWaveTheory; opts...)
if sys.mode == :SUN
δS = magnetization_lswt_correction_sun(swt; opts...)
else
@assert sys.mode in (:dipole, :dipole_large_s)
@assert sys.mode in (:dipole, :dipole_uncorrected)
δS = magnetization_lswt_correction_dipole(swt; opts...)
end
return δS
Expand Down
2 changes: 1 addition & 1 deletion src/SpinWaveTheory/SpinWaveTheory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ function dynamical_matrix!(H, swt::SpinWaveTheory, q_reshaped)
if swt.sys.mode == :SUN
swt_hamiltonian_SUN!(H, swt, q_reshaped)
else
@assert swt.sys.mode in (:dipole, :dipole_large_s)
@assert swt.sys.mode in (:dipole, :dipole_uncorrected)
swt_hamiltonian_dipole!(H, swt, q_reshaped)
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/Spiral/LuttingerTisza.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# wavevector k between Bravais lattice cells. This analysis ignores local
# normalization constraints for the spins within the cell.
function luttinger_tisza_exchange(sys::System; k, ϵ=0)
@assert sys.mode in (:dipole, :dipole_large_s) "SU(N) mode not supported"
@assert sys.mode in (:dipole, :dipole_uncorrected) "SU(N) mode not supported"
@assert sys.dims == (1, 1, 1) "System must have only a single cell"

Na = natoms(sys.crystal)
Expand Down
2 changes: 1 addition & 1 deletion src/Spiral/SpinWaveTheorySpiral.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ function intensities_bands(sswt::SpinWaveTheorySpiral, qpts; kT=0) # TODO: branc
(; sys, data, measure) = swt
isempty(measure.observables) && error("No observables! Construct SpinWaveTheorySpiral with a `measure` argument.")
sys.mode == :SUN && error("SU(N) mode not supported for spiral calculation")
@assert sys.mode in (:dipole, :dipole_large_s)
@assert sys.mode in (:dipole, :dipole_uncorrected)

qpts = convert(AbstractQPoints, qpts)
cryst = orig_crystal(sys)
Expand Down
4 changes: 2 additions & 2 deletions src/Spiral/SpiralEnergy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ special case ``𝐤 = 0`` yields result is identical to [`energy`](@ref).
See also [`minimize_spiral_energy!`](@ref) and [`repeat_periodically_as_spiral`](@ref).
"""
function spiral_energy(sys::System{0}; k, axis)
sys.mode in (:dipole, :dipole_large_s) || error("SU(N) mode not supported")
sys.mode in (:dipole, :dipole_uncorrected) || error("SU(N) mode not supported")
sys.dims == (1, 1, 1) || error("System must have only a single cell")

check_rotational_symmetry(sys; axis, θ=0.01)
Expand Down Expand Up @@ -198,7 +198,7 @@ approximately commensurate with the returned propagation wavevector ``𝐤``.
function minimize_spiral_energy!(sys, axis; maxiters=10_000, k_guess=randn(sys.rng, 3))
axis = normalize(axis)

sys.mode in (:dipole, :dipole_large_s) || error("SU(N) mode not supported")
sys.mode in (:dipole, :dipole_uncorrected) || error("SU(N) mode not supported")
sys.dims == (1, 1, 1) || error("System must have only a single cell")
norm([S × axis for S in sys.dipoles]) > 1e-12 || error("Spins cannot be exactly aligned with polarization axis")

Expand Down
8 changes: 4 additions & 4 deletions src/System/Interactions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ function local_energy_change(sys::System{N}, site, state::SpinState) where N

# Biquadratic
if !iszero(pc.biquad)
if sys.mode in (:dipole, :dipole_large_s)
if sys.mode in (:dipole, :dipole_uncorrected)
ΔQ = quadrupole(S) - quadrupole(S₀)
Qⱼ = quadrupole(Sⱼ)
else
Expand Down Expand Up @@ -315,7 +315,7 @@ function energy_aux(ints::Interactions, sys::System{N}, i::Int, cells) where N

# Biquadratic
if !iszero(pc.biquad)
if sys.mode in (:dipole, :dipole_large_s)
if sys.mode in (:dipole, :dipole_uncorrected)
Qᵢ = quadrupole(Sᵢ)
Qⱼ = quadrupole(Sⱼ)
else
Expand Down Expand Up @@ -386,7 +386,7 @@ end
function set_energy_grad_dipoles_aux!(∇E, dipoles::Array{Vec3, 4}, ints::Interactions, sys::System{N}, i::Int, cells) where N
# Single-ion anisotropy only contributes in dipole mode. In SU(N) mode, the
# anisotropy matrix will be incorporated directly into local H matrix.
if sys.mode in (:dipole, :dipole_large_s)
if sys.mode in (:dipole, :dipole_uncorrected)
stvexp = ints.onsite :: StevensExpansion
for cell in cells
S = dipoles[cell, i]
Expand All @@ -409,7 +409,7 @@ function set_energy_grad_dipoles_aux!(∇E, dipoles::Array{Vec3, 4}, ints::Inter
∇E[cellⱼ, bond.j] += J' * Sᵢ

# Biquadratic for dipole mode only (SU(N) handled differently)
if sys.mode in (:dipole, :dipole_large_s)
if sys.mode in (:dipole, :dipole_uncorrected)
if !iszero(pc.biquad)
Qᵢ = quadrupole(Sᵢ)
Qⱼ = quadrupole(Sⱼ)
Expand Down
12 changes: 6 additions & 6 deletions src/System/OnsiteCoupling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ function onsite_coupling(sys, site, matrep::AbstractMatrix)
matrep matrep' || error("Operator is not Hermitian")

if N == 2 && isapprox(matrep, matrep[1, 1] * I; atol=1e-8)
suggest = sys.mode == :dipole ? " (use :dipole_large_s to reproduce legacy calculations)" : ""
suggest = sys.mode == :dipole ? " (use :dipole_uncorrected for legacy calculations)" : ""
@warn "Onsite coupling is always trivial for quantum spin s=1/2" * suggest
end

Expand All @@ -14,14 +14,14 @@ function onsite_coupling(sys, site, matrep::AbstractMatrix)
s = spin_label(sys, to_atom(site))
c = matrix_to_stevens_coefficients(hermitianpart(matrep))
return StevensExpansion(rcs_factors(s) .* c)
elseif sys.mode == :dipole_large_s
error("System with mode `:dipole_large_s` requires a symbolic operator.")
elseif sys.mode == :dipole_uncorrected
error("System with mode `:dipole_uncorrected` requires a symbolic operator.")
end
end

function onsite_coupling(sys, site, p::DP.AbstractPolynomialLike)
if sys.mode != :dipole_large_s
error("Symbolic operator only valid for system with mode `:dipole_large_s`.")
if sys.mode != :dipole_uncorrected
error("Symbolic operator only valid for system with mode `:dipole_uncorrected`.")
end

s = sys.κs[site]
Expand All @@ -47,7 +47,7 @@ function rcs_factors(s)
end

function empty_anisotropy(mode, N)
if mode == :dipole || mode == :dipole_large_s
if mode in (:dipole, :dipole_uncorrected)
c = map(k -> zeros(2k+1), OffsetArray(0:6, 0:6))
return StevensExpansion(c)
elseif mode == :SUN
Expand Down
22 changes: 11 additions & 11 deletions src/System/PairExchange.jl
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ end


function check_allowable_dipole_coupling(tensordec, mode)
if !isempty(tensordec.data) && mode in (:dipole, :dipole_large_s)
if !isempty(tensordec.data) && mode in (:dipole, :dipole_uncorrected)
error("""
Invalid pair coupling. In dipole mode, the most general allowed form is
(Si, Sj) -> Si'*J*Sj + [(Si'*K1*Si)*(Sj'*K2*Sj) + ...]
Expand Down Expand Up @@ -290,8 +290,8 @@ function set_pair_coupling!(sys::System{N}, op::AbstractMatrix, bond; extract_pa

op op' || error("Operator is not Hermitian")

if sys.mode == :dipole_large_s
error("Symbolic operators required for mode `:dipole_large_s`.")
if sys.mode == :dipole_uncorrected
error("Symbolic operators required for mode `:dipole_uncorrected`.")
end

Ni = Int(2spin_label(sys, bond.i)+1)
Expand All @@ -303,8 +303,8 @@ function set_pair_coupling!(sys::System{N}, op::AbstractMatrix, bond; extract_pa
end

function set_pair_coupling!(sys::System{N}, fn::Function, bond; extract_parts=true) where N
if sys.mode == :dipole_large_s
error("General couplings not yet supported for mode `:dipole_large_s`.")
if sys.mode == :dipole_uncorrected
error("General couplings not supported for mode `:dipole_uncorrected`.")
end

si = spin_label(sys, bond.i)
Expand All @@ -319,7 +319,7 @@ end
# are the five Stevens quadrupoles, and g is the `scalar_biquad_metric`. The
# parameter `biquad` is accepted as the coefficient to (Sᵢ⋅Sⱼ)², but is returned
# as the coefficient to Qᵢ⋅g Qⱼ. This is achieved via a shift of the bilinear
# and scalar parts. In the special case of :dipole_large_s, the limiting
# and scalar parts. In the special case of :dipole_uncorrected, the limiting
# behavior is Sᵢ²Sⱼ² → sᵢ²sⱼ² (just the spin labels squared), and 𝒪(s²) → 0
# (homogeneous in quartic order of spin).
function adapt_for_biquad(scalar, bilin, biquad, sys, site1, site2)
Expand All @@ -333,7 +333,7 @@ function adapt_for_biquad(scalar, bilin, biquad, sys, site1, site2)
bilin -= (bilin isa Number) ? biquad/2 : (biquad/2)*I
scalar += biquad * s1*(s1+1) * s2*(s2+1) / 3
else
@assert sys.mode == :dipole_large_s
@assert sys.mode == :dipole_uncorrected
s1 = sys.κs[to_cartesian(site1)]
s2 = sys.κs[to_cartesian(site2)]
scalar += biquad * s1^2 * s2^2 / 3
Expand Down Expand Up @@ -491,8 +491,8 @@ two sites. The documentation for [`set_pair_coupling!`](@ref) provides examples
constructing `op`.
"""
function set_pair_coupling_at!(sys::System{N}, op::AbstractMatrix, site1::Site, site2::Site; offset=nothing) where N
if sys.mode == :dipole_large_s
error("Symbolic operators required for mode `:dipole_large_s`.")
if sys.mode == :dipole_uncorrected
error("Symbolic operators required for mode `:dipole_uncorrected`.")
end

N1 = Int(2spin_label(sys, to_atom(site1))+1)
Expand All @@ -504,8 +504,8 @@ function set_pair_coupling_at!(sys::System{N}, op::AbstractMatrix, site1::Site,
end

function set_pair_coupling_at!(sys::System{N}, fn::Function, site1::Site, site2::Site; offset=nothing) where N
if sys.mode == :dipole_large_s
error("General couplings not yet supported for mode `:dipole_large_s`.")
if sys.mode == :dipole_uncorrected
error("General couplings not yet supported for mode `:dipole_uncorrected`.")
end

s1 = spin_label(sys, to_atom(site1))
Expand Down
Loading

0 comments on commit ff453e3

Please sign in to comment.