For a finite object size diffraction simulator, I need to generate arrays which are the sum of thousands of instances of a Gaussian (or other) 2D kernel at centroids that will not fall in any repeatable way with respect to the grid points.
Below is a simple example with a simple hexagonal arrangement of centroids for clarity, along with a much faster analytical expression that comes close to approximating this particularly simple example, but in general the arrangement will be more complicated or even random.
import numpy as np
import matplotlib.pyplot as plt
hw = 10
N = 250
twoNp1 = 2 * N + 1
ximg, yimg = np.mgrid[-hw:hw:twoNp1 * 1j, -hw:hw:twoNp1 * 1j]
a = np.sqrt(2)
sig1 = hw/3.
sig2 = a/5.
r3o2, twopi = np.sqrt(3) / 2, 2 * np.pi
vecs = a * np.array([[1, 0], [1/2, r3o2]])
nmax = int(2 * (ximg.max()/a)) # overfill overkill
i, j = [thing.flatten() for thing in np.mgrid[-nmax:nmax+1, -nmax:nmax+1]]
keep = np.abs(i + j) <= nmax
i, j = [thing[keep, None] for thing in (i, j)]
points = i * vecs[0] + j * vecs[1]
img = np.zeros_like(ximg)
for x, y in points:
img += np.exp(-((x-ximg)**2 + (y-yimg)**2) / (2 * sig2**2))
# actual gaussians
img *= np.exp(-((ximg)**2 + (yimg)**2) / (2 * sig1**2))
# sinusoidal pattern
k = (twopi / (a * r3o2)) * np.array([[1, 0], [0.5, r3o2], [0.5, -r3o2]])
# that's 0, 60, -60
# try -30, 30, 90
k = (twopi / (a * r3o2)) * np.array([[r3o2, -1/2], [r3o2, 1/2], [0, 1]])
phases = [kay[0] * ximg + kay[1] * yimg for kay in k]
amplitudes = [np.cos(phase) for phase in phases]
amplitude = (1.5 + sum(amplitudes))/4.5
amplitude *= np.exp(-((ximg)**2 + (yimg)**2) / (2 * sig1**2))
if False:
n = int((2 * hw / a)**2 + 0.5)
randoms = hw * (2 * np.random.random((n,2)) - 1)
rand = np.zeros_like(ximg)
for x, y in randoms:
rand += np.exp(-((x-ximg)**2 + (y-yimg)**2) / (2 * sig2**2))
rand *= np.exp(-((ximg)**2 + (yimg)**2) / (2 * sig1**2))
if True:
fig, (ax1, ax2) = plt.subplots(2, 1)
extent = [ximg.min(), ximg.max(), yimg.min(), yimg.max()]
one = ax1.imshow(img, origin='lower', extent=extent)
ax1.plot(ximg, hw * (img[N] - 1), '-r', linewidth=0.5)
ax1.set_title('"thousands" of Gaussians')
# fig.colorbar(one, ax=ax1)
two = ax2.imshow(amplitude, origin='lower', extent=extent)
ax2.plot(ximg, hw * (amplitude[N] - 1), '-r', linewidth=0.5)
ax2.set_title('sinusoidal approximation')
# fig.colorbar(two, ax=ax2)
plt.show()