Skip to content

Commit

Permalink
Consistent notation for spin-label and expectation
Browse files Browse the repository at this point in the history
  • Loading branch information
kbarros committed Sep 20, 2024
1 parent 0604431 commit dd8f530
Show file tree
Hide file tree
Showing 21 changed files with 235 additions and 238 deletions.
4 changes: 2 additions & 2 deletions examples/05_MC_Ising.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ crystal = Crystal(latvecs, [[0, 0, 0]])
# Create a [`System`](@ref) of spin dipoles. Following the Ising convention, we
# will restrict the dipoles to ``±1`` along the global ``\hat{z}``-axis. Select
# ``g=-1`` so that the Zeeman coupling between external field ``𝐁`` and spin
# dipole ``𝐬`` is ``-𝐁⋅𝐬``. The system size is 128×128.
# dipole ``𝐒`` is ``-𝐁⋅𝐒``. The system size is 128×128.

L = 128
sys = System(crystal, [1 => Moment(s=1, g=-1)], :dipole; dims=(L, L, 1), seed=0)
Expand All @@ -40,7 +40,7 @@ Tc = 2/log(1+√2)
# consists of, on average, one trial update per spin in the system. Each
# proposed update is accepted or rejected according to the Metropolis acceptance
# probability. As its name suggests, the [`propose_flip`](@ref) function will
# only propose pure spin flips, ``𝐬 \rightarrow -𝐬``.
# only propose pure spin flips, ``𝐒 \rightarrow -𝐒``.

nsweeps = 4000
sampler = LocalSampler(kT=Tc, propose=propose_flip)
Expand Down
2 changes: 1 addition & 1 deletion examples/06_CP2_Skyrmions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ positions = [[0, 0, 0]]
cryst = Crystal(lat_vecs, positions)

# Create a spin [`System`](@ref) containing ``L×L`` cells. Following previous
# worse, select ``g=-1`` so that the Zeeman coupling has the form ``-𝐁⋅𝐬``.
# worse, select ``g=-1`` so that the Zeeman coupling has the form ``-𝐁⋅𝐒``.

L = 40
sys = System(cryst, [1 => Moment(s=1, g=-1)], :SUN; dims=(L, L, 1))
Expand Down
1 change: 0 additions & 1 deletion examples/spinw_tutorials/SW13_LiNiPO4.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ view_crystal(cryst)
# "Interaction Renormalization") of anisotropy strengths, as needed for
# consistency with the original fits.

S = 3/2
sys = System(cryst, [1 => Moment(s=1, g=2)], :dipole_large_s)
Jbc = 1.036
Jb = 0.6701
Expand Down
2 changes: 1 addition & 1 deletion ext/PlottingExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ function Sunny.view_crystal(cryst::Crystal; refbonds=10, orthographic=false, gho
view_crystal_aux(cryst, nothing; refbonds, orthographic, ghost_radius, ndims, compass, size)
end

function Sunny.view_crystal(sys::System; refbonds=8, orthographic=false, ghost_radius=nothing, ndims=3, compass=true, size=(768, 512), dims=nothing)
function Sunny.view_crystal(sys::System; refbonds=10, orthographic=false, ghost_radius=nothing, ndims=3, compass=true, size=(768, 512), dims=nothing)
isnothing(dims) || error("Use notation `ndims=$dims` instead of `dims=$dims`")
Sunny.is_homogeneous(sys) || error("Cannot plot interactions for inhomogeneous system.")
view_crystal_aux(orig_crystal(sys), sys;
Expand Down
70 changes: 35 additions & 35 deletions src/Integrators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ prevents energy drift through exact conservation of the symplectic 2-form.
If the [`System`](@ref) has `mode = :dipole`, then the dynamics is the
stochastic Landau-Lifshitz equation,
```math
d𝐬/dt = -𝐬 × (ξ - 𝐁 + λ 𝐬 × 𝐁),
d𝐒/dt = -𝐒 × (ξ - 𝐁 + λ 𝐒 × 𝐁),
```
where ``𝐁 = -dE/d𝐬`` is the effective field felt by the expected spin dipole
``𝐬``. The components of ``ξ`` are Gaussian white noise, with magnitude ``√(2
where ``𝐁 = -dE/d𝐒`` is the effective field felt by the expected spin dipole
``𝐒``. The components of ``ξ`` are Gaussian white noise, with magnitude ``√(2
k_B T λ)`` set by a fluctuation-dissipation theorem. The parameter `damping`
sets the phenomenological coupling ``λ`` to the thermal bath.
Expand All @@ -38,11 +38,11 @@ parameter `damping` here sets ``λ̃``, which is analogous to ``λ`` above.
When applied to SU(2) coherent states, the generalized spin dynamics reduces
exactly to the stochastic Landau-Lifshitz equation. The mapping is as follows.
Normalized coherent states ``𝐙`` map to dipole expectation values ``𝐬 = 𝐙^{†}
Ŝ 𝐙``, where spin operators ``Ŝ`` are a spin-``|𝐬|`` representation of
Normalized coherent states ``𝐙`` map to dipole expectation values ``𝐒 = 𝐙^{†}
Ŝ 𝐙``, where spin operators ``Ŝ`` are a spin-``|𝐒|`` representation of
SU(2). The local effective Hamiltonian ``ℋ = -𝐁 ⋅ Ŝ`` generates rotation of
the dipole in analogy to the vector cross product ``S × 𝐁``. The coupling to
the thermal bath maps as ``λ̃ = |𝐬| λ``. Note, therefore, that the scaling of
the thermal bath maps as ``λ̃ = |𝐒| λ``. Note, therefore, that the scaling of
the `damping` parameter varies subtly between `:dipole` and `:SUN` modes.
References:
Expand Down Expand Up @@ -137,7 +137,7 @@ end
Suggests a timestep for the numerical integration of spin dynamics according to
a given error tolerance `tol`. The `integrator` should be [`Langevin`](@ref) or
[`ImplicitMidpoint`](@ref). The suggested ``dt`` will be inversely proportional
to the magnitude of the effective field ``|dE/d𝐬|`` arising from the current
to the magnitude of the effective field ``|dE/d𝐒|`` arising from the current
spin configuration in `sys`. The recommended timestep ``dt`` scales like `√tol`,
which assumes second-order accuracy of the integrator.
Expand Down Expand Up @@ -258,14 +258,14 @@ end
################################################################################


@inline function rhs_dipole!(Δs, s, ξ, ∇E, integrator)
@inline function rhs_dipole!(ΔS, S, ξ, ∇E, integrator)
(; dt, damping) = integrator
λ = damping

if iszero(λ)
@. Δs = - s × (dt*∇E)
@. ΔS = - S × (dt*∇E)
else
@. Δs = - s ×+ dt*∇E - dt*λ*(s × ∇E))
@. ΔS = - S ×+ dt*∇E - dt*λ*(S × ∇E))
end
end

Expand Down Expand Up @@ -308,20 +308,20 @@ function step! end
function step!(sys::System{0}, integrator::Langevin)
check_timestep_available(integrator)

(s′, Δs₁, Δs₂, ξ, ∇E) = get_dipole_buffers(sys, 5)
s = sys.dipoles
(S′, ΔS₁, ΔS₂, ξ, ∇E) = get_dipole_buffers(sys, 5)
S = sys.dipoles

fill_noise!(sys.rng, ξ, integrator)

# Euler prediction step
set_energy_grad_dipoles!(∇E, s, sys)
rhs_dipole!(Δs₁, s, ξ, ∇E, integrator)
@. s= normalize_dipole(s + Δs₁, sys.κs)
set_energy_grad_dipoles!(∇E, S, sys)
rhs_dipole!(ΔS₁, S, ξ, ∇E, integrator)
@. S= normalize_dipole(S + ΔS₁, sys.κs)

# Correction step
set_energy_grad_dipoles!(∇E, s′, sys)
rhs_dipole!(Δs₂, s′, ξ, ∇E, integrator)
@. s = normalize_dipole(s + (Δs₁+Δs₂)/2, sys.κs)
set_energy_grad_dipoles!(∇E, S′, sys)
rhs_dipole!(ΔS₂, S′, ξ, ∇E, integrator)
@. S = normalize_dipole(S + (ΔS₁+ΔS₂)/2, sys.κs)

return
end
Expand Down Expand Up @@ -366,42 +366,42 @@ function fast_isapprox(x, y; atol)
end

# The spherical midpoint method, Phys. Rev. E 89, 061301(R) (2014)
# Integrates ds/dt = s × ∂E/∂s one timestep ss′ via implicit equations
# = (s′ + s) / 2
# = / ||
# (s′ - s)/dt = 2( - s)/dt = - × B,
# where B = -∂E/∂.
# Integrates dS/dt = S × ∂E/∂S one timestep SS′ via implicit equations
# = (S′ + S) / 2
# = / ||
# (S′ - S)/dt = 2( - S)/dt = - × B,
# where B = -∂E/∂.
function step!(sys::System{0}, integrator::ImplicitMidpoint; max_iters=100)
check_timestep_available(integrator)

s = sys.dipoles
atol = integrator.atol * length(s)
S = sys.dipoles
atol = integrator.atol * length(S)

(Δs, ŝ, s′, s″, ξ, ∇E) = get_dipole_buffers(sys, 6)
(ΔS, Ŝ, S′, S″, ξ, ∇E) = get_dipole_buffers(sys, 6)

fill_noise!(sys.rng, ξ, integrator)

@. s= s
@. s= s
@. S= S
@. S= S

for _ in 1:max_iters
# Current guess for midpoint ŝ
@. = normalize_dipole((s + s′)/2, sys.κs)
@. = normalize_dipole((S + S′)/2, sys.κs)

set_energy_grad_dipoles!(∇E, , sys)
rhs_dipole!(Δs, ŝ, ξ, ∇E, integrator)
set_energy_grad_dipoles!(∇E, , sys)
rhs_dipole!(ΔS, Ŝ, ξ, ∇E, integrator)

@. s= s + Δs
@. S= S + ΔS

# If converged, then we can return
if fast_isapprox(s′, s″; atol)
if fast_isapprox(S′, S″; atol)
# Normalization here should not be necessary in principle, but it
# could be useful in practice for finite `atol`.
@. s = normalize_dipole(s″, sys.κs)
@. S = normalize_dipole(S″, sys.κs)
return
end

s′, s= s″, s
S′, S= S″, S
end

error("Spherical midpoint method failed to converge to tolerance $atol after $max_iters iterations.")
Expand Down
13 changes: 6 additions & 7 deletions src/KPM/SpinWaveTheoryKPM.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,24 +102,23 @@ function intensities!(data, swt_kpm::SpinWaveTheoryKPM, qpts; energies, kernel::
end

if sys.mode == :SUN
data_sun = swt.data::SWTDataSUN
(; observables_localized) = swt.data::SWTDataSUN
N = sys.Ns[1]
for i in 1:Na, μ in 1:Nobs
@views O = data_sun.observables_localized[μ, i]
O = observables_localized[μ, i]
for f in 1:Nf
u[μ, f + (i-1)*Nf] = Avec_pref[i] * O[f, N]
u[μ, f + (i-1)*Nf + L] = Avec_pref[i] * O[N, f]
end
end
else
@assert sys.mode in (:dipole, :dipole_large_s)
data_dip = swt.data::SWTDataDipole
(; sqrtS, observables_localized) = swt.data::SWTDataDipole
for i in 1:Na
sqrt_halfS = data_dip.sqrtS[i]/sqrt(2)
for μ in 1:Nobs
O = data_dip.observables_localized[μ, i]
u[μ, i] = Avec_pref[i] * sqrt_halfS * (O[1] + im*O[2])
u[μ, i+L] = Avec_pref[i] * sqrt_halfS * (O[1] - im*O[2])
O = observables_localized[μ, i]
u[μ, i] = Avec_pref[i] * (sqrtS[i] / 2) * (O[1] + im*O[2])
u[μ, i+L] = Avec_pref[i] * (sqrtS[i] / 2) * (O[1] - im*O[2])
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/MonteCarlo/ParallelTempering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function ParallelTempering(system::System{N}, sampler::SMP, kT_sched::Vector{Flo
if samplers[1] isa LocalSampler
for rank in 1:n_replicas
samplers[rank].ΔE = energy(systems[rank])
samplers[rank].Δs = sum(systems[rank].dipoles)
samplers[rank].ΔS = sum(systems[rank].dipoles)
samplers[rank].nsweeps = 1.0
end
end
Expand Down Expand Up @@ -63,7 +63,7 @@ function replica_exchange!(PT::ParallelTempering, exch_start::Int64)

if PT.samplers[1] isa LocalSampler
PT.samplers[id₁].ΔE, PT.samplers[id₂].ΔE = PT.samplers[id₂].ΔE, PT.samplers[id₁].ΔE
PT.samplers[id₁].Δs, PT.samplers[id₂].Δs = PT.samplers[id₂].Δs, PT.samplers[id₁].Δs
PT.samplers[id₁].ΔS, PT.samplers[id₂].ΔS = PT.samplers[id₂].ΔS, PT.samplers[id₁].ΔS
end

PT.n_accept[id₁] += 1
Expand Down
22 changes: 11 additions & 11 deletions src/MonteCarlo/Samplers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const propose_uniform = randspin
propose_flip
Function to propose pure spin flip updates in the context of a
[`LocalSampler`](@ref). Dipoles are flipped as ``𝐬 → -𝐬``. SU(_N_) coherent
[`LocalSampler`](@ref). Dipoles are flipped as ``𝐒 → -𝐒``. SU(_N_) coherent
states are flipped using the time-reversal operator.
"""
propose_flip(sys::System{N}, site) where N = flip(getspin(sys, site))
Expand All @@ -27,8 +27,8 @@ propose_flip(sys::System{N}, site) where N = flip(getspin(sys, site))
Generate a proposal function that adds a Gaussian perturbation to the existing
spin state. In `:dipole` mode, the procedure is to first introduce a random
three-vector perturbation ``𝐬′ = 𝐬 + |𝐬| ξ`` and then return the properly
normalized spin ``|𝐬| (𝐬′/|𝐬′|)``. Each component of the random vector ``ξ``
three-vector perturbation ``𝐒′ = 𝐒 + |𝐒| ξ`` and then return the properly
normalized spin ``|𝐒| (𝐒′/|𝐒′|)``. Each component of the random vector ``ξ``
is Gaussian distributed with a standard deviation of `magnitude`; the latter is
dimensionless and typically smaller than one.
Expand All @@ -45,14 +45,14 @@ function propose_delta(magnitude)
function ret(sys::System{N}, site) where N
κ = sys.κs[site]
if N == 0
s = sys.dipoles[site] + magnitude * κ * randn(sys.rng, Vec3)
s = normalize_dipole(s, κ)
return SpinState(s, CVec{0}())
S = sys.dipoles[site] + magnitude * κ * randn(sys.rng, Vec3)
S = normalize_dipole(S, κ)
return SpinState(S, CVec{0}())
else
Z = sys.coherents[site] + magnitude * sqrt(κ) * randn(sys.rng, CVec{N})
Z = normalize_ket(Z, κ)
s = expected_spin(Z)
return SpinState(s, Z)
S = expected_spin(Z)
return SpinState(S, Z)
end
end
return ret
Expand Down Expand Up @@ -115,7 +115,7 @@ The trial spin updates are sampled using the `propose` function. Options include
[`propose_uniform`](@ref), [`propose_flip`](@ref), and [`propose_delta`](@ref).
Multiple proposals can be mixed with the macro [`@mix_proposals`](@ref).
The returned object stores fields `ΔE` and `Δs`, which represent the cumulative
The returned object stores fields `ΔE` and `ΔS`, which represent the cumulative
change to the net energy and dipole, respectively.
!!! warning "Efficiency considerations
Expand All @@ -131,7 +131,7 @@ mutable struct LocalSampler{F}
nsweeps :: Float64 # Number of MCMC sweeps per `step!`
propose :: F # Function: (System, Site) -> SpinState
ΔE :: Float64 # Cumulative energy change
Δs :: Vec3 # Cumulative net dipole change
ΔS :: Vec3 # Cumulative net dipole change

function LocalSampler(; kT, nsweeps=1.0, propose=propose_uniform)
new{typeof(propose)}(kT, nsweeps, propose, 0.0, zero(Vec3))
Expand All @@ -158,7 +158,7 @@ function step!(sys::System{N}, sampler::LocalSampler) where N

if accept
sampler.ΔE += ΔE
sampler.Δs += state.s - sys.dipoles[site]
sampler.ΔS += state.S - sys.dipoles[site]
setspin!(sys, state, site)
end
end
Expand Down
4 changes: 2 additions & 2 deletions src/Optimization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ end

function optim_set_gradient!(G, sys::System{0}, αs, ns)
(αs, G) = reinterpret.(reshape, Vec3, (αs, G))
set_energy_grad_dipoles!(G, sys.dipoles, sys) # G = dE/ds
@. G *= norm(sys.dipoles) # G = dE/ds * ds/du = dE/du
set_energy_grad_dipoles!(G, sys.dipoles, sys) # G = dE/dS
@. G *= norm(sys.dipoles) # G = dE/dS * ds/du = dE/du
@. G = adjoint(vjp_stereographic_projection(G, αs, ns)) # G = dE/du du/dα = dE/dα
end
function optim_set_gradient!(G, sys::System{N}, αs, ns) where N
Expand Down
2 changes: 1 addition & 1 deletion src/SpinWaveTheory/DispersionAndIntensities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ function intensities_bands(swt::SpinWaveTheory, qpts; kT=0, with_negative=false)
# local frame, z is longitudinal, and we are computing
# the transverse part only, so the last entry is zero)
displacement_local_frame = SA[t[i, 2] + t[i, 1], im * (t[i, 2] - t[i, 1]), 0.0]
Avec[μ] += Avec_pref[μ, i] * (data.sqrtS[i]/sqrt(2)) * (O' * displacement_local_frame)[1]
Avec[μ] += Avec_pref[μ, i] * (data.sqrtS[i]/2) * (O' * displacement_local_frame)[1]
end
end

Expand Down
Loading

0 comments on commit dd8f530

Please sign in to comment.