I'm currently analysing some spatial point patterns that come from some fluid dynamics simulations and I'm having some difficulty computing the structure factor, $S(\pmb{k})$, from both the positions of the points and the radial distribution function, $g(\pmb{r})$. I have been following the structure factor Wikipedia page and also this paper on arxiv but I seem to be getting results that don't make sense.

Below is my class object to compute the radial distribution function for said spatial point patterns (I've omitted the help data and assertions from the code for clarity). The class takes in a numpy array or list of numpy arrays, finds the locations of the nonzero values, computes the distances between each nonzero value and then computes the radial distribution. Note that here, we consider an array cell a point if it has a nonzero value

import numpy as np
import bisect

class pair_correlation_function():

    def __init__(self,data,annulus_width,boundary=None):

        self.data = [data] if type(data) is np.ndarray else data
        self.dr = annulus_width
        self.boundary = boundary

    def get_positions(self):

        positions = list()

        for item in self.data:

            rows,columns = np.nonzero(item)

        return positions

    def RDF(self):

        positions = self.get_positions()

        Lx,Ly = np.shape(self.data[0])
        area = Lx*Ly
        radii = list(x for x in range(int(Lx/(2*self.dr))))

        all_radial_distributions = list()

        for item in positions:

            if self.boundary == 'periodic':

                item_new = np.vstack(np.array([np.abs(item[k]-item[k+1:]) for k in range(len(item)-1)]))
                item_new[item_new > Lx/2] = Lx-item_new[item_new > Lx/2]
                norms = [np.sqrt((position**2).sum(axis=0)) for position in item_new]
                norms = sorted(norms)


                norms = [np.sqrt(((item[k]-item[k+1:])**2).sum(axis=1)) for k in range(len(item)-1)]
                norms = sorted(np.hstack(norms))

            number_particles = len(item)
            item_rdf = list()

            for r in radii:

                i = bisect.bisect_left(norms,r*self.dr)
                j = bisect.bisect_left(norms,(r+1)*self.dr)
                if i != len(norms) and j != len(norms): particle_count = len(norms[i:j])

                normalisation = (2*r+self.dr)*np.pi*self.dr*number_particles**2
                bin_value     = 2*area*particle_count/normalisation



        radii = np.array(radii)*self.dr

    return radii,all_radial_distributions

Below is a sample array (a triangular lattice) with which to play around (we are assuming periodic boundary conditions here because my actual data comes from a simulation with periodic boundaries)

tri_period = np.array([[0,0,0,0],[0,1,0,1],[0,0,0,0],[1,0,1,0]])
triangular_lattice = np.tile(tri_period,(16,16))
dr = .025

radii, RDF = pair_correlation_function(triangular_lattice,dr,'periodic').RDF()

and plotting gives


Here, we see a series of Bragg peaks as expected (though I'm not sure why the peaks segment into smooth, monotone decreasing bands? My actual data doesn't do this though and looks correct.) Now, if I try to compute the structure factor given the positions (see arxiv paper above, equation $(2)$)

def sf_from_positions(positions,box_dimension):

    sf = list()
    modes = list(x for x in range(1,int(box_dimension)))

    dk = 2*np.pi/box_dimension

    for h in modes:

        k_vec = np.array([1,1])*dk*h
        summation = 0

        for position in positions:

            summation += np.exp(-1j*k_vec.dot(position))


    return sf, modes

rows,columns = np.nonzero(triangular_lattice)
positions = list(zip(rows,columns))
dimension = np.shape(triangular_lattice)[0]
factor, wave_modes = sf_from_positions(positions,dimension)

and plotting gives


which looks off, because the Fourier transform of Bragg peaks should also be Bragg peaks (see this arxiv paper, page 12).

So, my first question is, what is going wrong in my computation of the structure factor? Can anyone see what I have done wrong? And secondly, I was wondering how I would integrate the radial distribution function to get the structure factor, through the formula $$S(\pmb{k}) = 1 + 4 \pi \rho \int_{\mathbb{R^{n}}} [g(\pmb{r})-1] e^{-i \pmb{k} \cdot \pmb{r}} d \pmb{r} $$ I tried to use the cumtrapz module in scipy but it gave me a structure factor that was negative (impossible) and that oscillated with an increasing wave packet.

Sorry if the question is overly involved. Thanks for your help.

  • 1
    $\begingroup$ I suggest that you split your post into two different questions. $\endgroup$
    – nicoguaro
    Apr 30, 2019 at 18:29


Browse other questions tagged or ask your own question.