-
Notifications
You must be signed in to change notification settings - Fork 19
/
CorrelationSampling.jl
152 lines (123 loc) · 5.21 KB
/
CorrelationSampling.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
function observable_values!(buf, sys::System{N}, ops; apply_g = true) where N
if N == 0
for site in eachsite(sys), (i, op) in enumerate(ops)
dipole = sys.dipoles[site]
if apply_g
dipole = sys.gs[site] * dipole
end
buf[i,site] = op * dipole
end
else
Zs = sys.coherents
#num_ops = size(ops′, 3)
#ops = reinterpret(SMatrix{N, N, ComplexF64, N*N}, reshape(ops′, N*N, num_ops))
# SQTODO: This allocates :(
for (i, op) in enumerate(ops)
matrix_operator = convert(Matrix{ComplexF64},op)
for site in eachsite(sys)
buf[i,site] = dot(Zs[site], matrix_operator, Zs[site])
end
end
end
return nothing
end
function trajectory(sys::System{N}, Δt, nsnaps, ops; kwargs...) where N
num_ops = length(ops)
traj_buf = zeros(N == 0 ? Float64 : ComplexF64, num_ops, sys.latsize..., natoms(sys.crystal), nsnaps)
trajectory!(traj_buf, sys, Δt, nsnaps, ops; kwargs...)
return traj_buf
end
function trajectory!(buf, sys, Δt, nsnaps, ops; measperiod = 1, apply_g = true)
@assert length(ops) == size(buf, 1)
integrator = ImplicitMidpoint(Δt)
observable_values!(@view(buf[:,:,:,:,:,1]), sys, ops; apply_g = apply_g)
for n in 2:nsnaps
for _ in 1:measperiod
step!(sys, integrator)
end
observable_values!(@view(buf[:,:,:,:,:,n]), sys, ops; apply_g = apply_g)
end
return nothing
end
function new_sample!(sc::SampledCorrelations, sys::System; processtraj! = no_processing)
(; Δt, samplebuf, measperiod, apply_g, observables) = sc
nsnaps = size(samplebuf, 6)
@assert size(sys.dipoles) == size(samplebuf)[2:5] "`System` size not compatible with given `SampledCorrelations`"
trajectory!(samplebuf, sys, Δt, nsnaps, observables.observables; measperiod = measperiod, apply_g = apply_g)
processtraj!(sc)
return nothing
end
function symmetrize!(sc::SampledCorrelations)
(; samplebuf) = sc
nsteps = size(samplebuf, 6)
for t in 1:nsteps
selectdim(samplebuf, 6, t) .= 0.5*(selectdim(samplebuf, 6, t) + selectdim(samplebuf, 6, nsteps-t+1))
end
end
function subtract_mean!(sc::SampledCorrelations)
(; samplebuf) = sc
nsteps = size(samplebuf, 6)
meanvals = sum(samplebuf, dims=6) ./ nsteps
samplebuf .-= meanvals
end
function no_processing(::SampledCorrelations)
nothing
end
function accum_sample!(sc::SampledCorrelations)
(; data, M, observables, samplebuf, nsamples, fft!) = sc
natoms = size(samplebuf)[5]
fft! * samplebuf # Apply pre-planned and pre-normalized FFT
count = nsamples[1] += 1
# Note that iterating over the `correlations` (a SortedDict) causes
# allocations here. The contents of the loop contains no allocations. There
# does not seem to be a big performance penalty associated with these
# allocations.
for j in 1:natoms, i in 1:natoms, (ci, c) in observables.correlations
α, β = ci.I
sample_α = @view samplebuf[α,:,:,:,i,:]
sample_β = @view samplebuf[β,:,:,:,j,:]
databuf = @view data[c,i,j,:,:,:,:]
if isnothing(M)
for k in eachindex(databuf)
# Store the diff for one complex number on the stack.
diff = sample_α[k] * conj(sample_β[k]) - databuf[k]
# Accumulate into running average
databuf[k] += diff * (1/count)
end
else
Mbuf = @view M[c,i,j,:,:,:,:]
for k in eachindex(databuf)
# Store old (complex) mean on stack.
μ_old = databuf[k]
# Update running mean.
matrixelem = sample_α[k] * conj(sample_β[k])
databuf[k] += (matrixelem - databuf[k]) * (1/count)
μ = databuf[k]
# Update variance estimate.
# Note that the first term of `diff` is real by construction
# (despite appearances), but `real` is explicitly called to
# avoid automatic typecasting errors caused by roundoff.
Mbuf[k] += real((matrixelem - μ_old)*conj(matrixelem - μ))
end
end
end
return nothing
end
"""
add_sample!(sc::SampledCorrelations, sys::System)
`add_trajectory` uses the spin configuration contained in the `System` to
generate a correlation data and accumulate it into `sc`. For static structure
factors, this involves analyzing the spin-spin correlations of the spin
configuration provided. For a dynamic structure factor, a trajectory is
calculated using the given spin configuration as an initial condition. The
spin-spin correlations are then calculating in time and accumulated into `sc`.
This function will change the state of `sys` when calculating dynamical
structure factor data. To preserve the initial state of `sys`, it must be saved
separately prior to calling `add_sample!`. Alternatively, the initial spin
configuration may be copied into a new `System` and this new `System` can be
passed to `add_sample!`.
"""
function add_sample!(sc::SampledCorrelations, sys::System; processtraj! = no_processing)
new_sample!(sc, sys; processtraj!)
accum_sample!(sc)
end