Every AB test is wrong
The industry, through concepts like agility and Lean Startups, has taken on rapidly the practice of performing AB tests. In a nutshell, the AB tests are cont...
A surprising result! We insisted several times that the structure factor should be always positive, yet we get, using the very same definition, a negative structure factor for wavenumbers near 10. Where did these negative values come from? From the construction that used to help us a lot, the minimum image convention. The pair distance now isn’t always \(r_{ij} = r_j - r_i\), but depends on whether we use the original particles or their images. Therefore, this “new” structure factor isn’t the product of two conjugate complex numbers3. What if we avoid the periodic boundary conditions? We have a comparision of the structure factor with and without boundary conditions (i. e., with the 64 atoms in a void):
This shows that the structure factor, when we use its definition without minimum image convention, is (as expected) always positive.
We can also use the pair distribution function and calculate the structure factor as the Fourier Transform. But keep in mind that if you calculate the pair distribution function with PBC, when you get the structure factor related to it you might get negative numbers.
The question then, remains: how can we simulate an infinite medium when calculating structure factor? The first answer is that it is not that obvious that we would actually need this infinite medium, since the periodic images of the cell would be aligned in a crystal that might interfere with the structure within the cell — the one we actually do want to study. However, a couple of replicas should be enough to smear out some of the finite size effects. One of the possibilities is to replicate explicitly the box, creating the particles in the neighboring cells by duplication of the original ones. This, though, implies a calculation much harder, since the sum is over \(N^2\) particles, and replicating only one cell right and left in each direction would imply a computational time of \((3^3\cdot N)^2 \approx 700\cdot N^2\). In general, the complexity \(\mathcal{O}(N^2)\) makes structure factor calculation very expensive for large systems.
There is an alternative to add the boundary conditions. We begin with the definition of the sample scattering amplitude, but writing explicitly the periodic boundary images we want to consider:
\[\begin{equation} \Psi(\mathbf{Q}) = \sum_i \sum_j \text{e}^{i\mathbf{Q}\cdot(\mathbf{R}_i+\mathbf{\Delta L}_j}) \end{equation}\]where \(\mathbf{\Delta L}_j\) is the distance between a particle and its \(j\)-th periodic replica. Since the sums are independent, we can write:
\[\begin{equation} \Psi(\mathbf{Q}) = \left(\sum_i \text{e}^{i\mathbf{Q}\cdot\mathbf{R}_i}\right) \left(\sum_j\text{e}^{i\mathbf{Q}\cdot\mathbf{\Delta L}_j}\right) \end{equation}\]Multiplying by the conjugate gives us the structure factor
\[\begin{align} S(\mathbf{Q}) &= \left|\sum_i \text{e}^{i\mathbf{Q}\cdot\mathbf{R}_i}\right|^2 \left|\sum_j \text{e}^{i\mathbf{Q}\cdot\mathbf{\Delta L}_j}\right|^2\\ &= S_{\text{cell}}(\mathbf{Q})\,S_{\text{PBC}}(\mathbf{Q}) \end{align}\]The advantage of this calculation is that it is linear in the sum of the number of particles \(N\) and the number of replicas \(M\) consider, \(\mathcal{O}(N+M)\), much lower than the previous \(\mathcal{O}(N^2M^2)\). Consequently, if we want to focus in a region of \(\mathbf{Q}\), this new approach will be useful4. We are left with only one detail, respecting to the powder average. It is not trivial how to calculate this integral, since we need to give proper weights to each angle. One of the alternatives are to use the Lebedev quadrature5, although other methods like Importance Sampling Montecarlo can be useful in this situation.
Here is the code with which we generated the figures above:
import numpy as np
import itertools as it
def ssf(x, size, q, pbc=False):
"""
From a series of positions x in a cubic box of length size we get
the structure factor for momentum q
"""
natoms = np.shape(x)[0]
sf = 0.0
for i in range(natoms):
x1 = x[i]
for j in range(i+1, natoms):
x2 = x[j]
dx = x2 - x1
if pbc:
for i in range(3):
if dx[i] > size/2: dx[i] -= size
if dx[i] < -size/2: dx[i] += size
r = np.linalg.norm(dx)
sf += 2*np.sin(q*r)/(q*r)
sf /= natoms
sf += 1
return sf
def generate_sc(size, n):
"""
Generate the positions of a simple cubic crystal in a box of
length size with n atoms in each direction (order parameter =
size/n)
"""
natoms = n**3
pos = range(n)
x = np.zeros((natoms, 3))
i = 0
for px, py, pz in it.product(pos, pos, pos):
x[i] = (px, py, pz)
x[i] = x[i] * (size/n)
i += 1
return x
if __name__ == '__main__':
import pylab as pl
size = 1.0
x = generate_sc(size, 4)
q = np.linspace(0, 20.0, 101)[1:]
sf_pbc = [ssf(x, size, _, True) for _ in q]
sf_sing = [ssf(x, size, _, False) for _ in q]
fig, ax = pl.subplots()
ax.plot(q, sf_pbc)
ax.axhline(y=0, c='k', ls='--')
ax.set_xlabel('Wavenumber')
ax.set_ylabel('Structure factor')
fig.tight_layout()
fig.savefig('ssf_pbc.png')
pl.close()
fig, ax = pl.subplots()
ax.plot(q, sf_pbc, label='with PBC')
ax.plot(q, sf_sing, label='without PBC')
ax.axhline(y=0, c='k', ls='--')
ax.set_xlabel('Wavenumber')
ax.set_ylabel('Structure factor')
ax.legend()
fig.tight_layout()
fig.savefig('ssf_comp.png')
pl.close()
Egami T, Billinge, S. Underneath the Bragg Peaks: Structural Analysis of Complex Materials. Amsterdam: Pergamon, 2003 ↩
This definition already uses that all particles are equal. For the general definition, see Egami and Billinge. ↩
Even further, now the imaginary part of \(S(q)\) is no longer zero! ↩
We should consider though that in this approach, we will need \(\mathcal{O}(N+M)\) calculations for each \(\mathbf{Q}\), so we can’t use it to sweep the whole \(\mathbf{Q}\) spectrum. ↩
Lebedev, V. I. (1975) Zh. Vȳchisl. Mat. Mat. Fiz. 15 (1): 48–54. doi:10.1016/0041-5553(75)90133-0. ↩
Leave a Comment