#--------------------------- # Field generator module # PabloVD # Started: 11/5/20 #--------------------------- """ Collection of noise fields for generating maps. Noises included are: "gauss": Random gaussian field, with a given power spectrum, computed using the package powerbox "perlin": Perlin noise, computed using the package noise "warped_perlin": Perlin noise with domain warping, computed using the package noise "cos": Sinusoidal noise (to be improved) "fbm": Fractional Brownian Field """ import numpy as np import powerbox as pbox import noise # Define power spectrum as a power law with an spectral index indexlaw # With lower the spectral indexes (redder noise), small structures are removed def powerspec(k,indexlaw=-3.): return k**indexlaw # Generate a Gaussian field with a power law power spectrum def gaussian_field(boxsize,seed,indexlaw=-3.): field = pbox.PowerBox(boxsize, lambda k: powerspec(k,indexlaw), dim=2, boxlength=1.,seed=seed).delta_x() return field # Generate a Perlin field def perlin_field(boxsizex,seed,scale,octaves,persistence,lacunarity,boxsizey=None): if boxsizey==None: boxsizey=boxsizex shape = (boxsizex,boxsizey) field = np.zeros(shape) for i in range(shape[0]): for j in range(shape[1]): field[i,j] = noise.pnoise2(i/scale, j/scale, octaves=octaves, persistence=persistence, lacunarity=lacunarity, #repeatx=1024, #repeaty=1024, base=seed) return field # 2D cosinus (see Hill noise for something similar but better https://blog.bruce-hill.com/hill-noise) def cos_noise(X,Y,amp,frecx,frecy,phase): return amp*np.cos( frecx*X +frecy*Y + phase) #return Amp*(np.cos( frecx*X) +np.cos(frecy*Y + phase)) # Generate a noise using superposition of cosinus def cos_field(boxsizex,seed,scale,octaves,persistence,lacunarity,boxsizey=None): if boxsizey==None: boxsizey=boxsizex np.random.seed(seed=seed) frec0 = 5. x, y = np.linspace(0,boxsizex,num=boxsizex), np.linspace(0,boxsizey,num=boxsizey) X, Y = np.meshgrid(x,y) noise_tot = np.zeros((boxsizex,boxsizey)) for oct in range(octaves): Amp, frecx, frecy, phase = np.random.random(), 2.*np.pi*frec0*random.uniform(-1.,1.), 2.*np.pi*frec0*random.uniform(-1.,1.), 2.*np.pi*np.random.random() noise_tot += persistence**oct*cos_noise(X/scale,Y/scale,Amp,frecx*lacunarity**oct,frecy*lacunarity**oct,phase) return noise_tot # Generate a Perlin field with warping domain (see e.g. https://iquilezles.org/www/articles/warp/warp.htm) def warped_perlin_field(boxsizex,seed,scale,octaves,persistence,lacunarity,amplitude=None,boxsizey=None): if boxsizey==None: boxsizey=boxsizex shape = (boxsizex,boxsizey) if amplitude==None: amplitude = np.random.uniform(0.,30.) field = np.zeros(shape) for i in range(shape[0]): for j in range(shape[1]): vec = np.random.rand(2) ii = noise.pnoise2(i/scale,j/scale,octaves=octaves,persistence=persistence,lacunarity=lacunarity,base=seed) jj = noise.pnoise2(i/scale,j/scale,octaves=octaves,persistence=persistence,lacunarity=lacunarity,base=seed) field[i,j] = noise.pnoise2(i/scale + amplitude*ii,j/scale + amplitude*jj,octaves=octaves,persistence=persistence,lacunarity=lacunarity,base=seed) return field # Embedding of covariance function on a [0,R]^2 grid for fractional Brownian field # From https://gist.github.com/radarsat1/6f8b9b50d1ecd2546d8a765e8a144631 def rho(x,y,R,alpha): if alpha <= 1.5: # alpha=2*H, where H is the Hurst parameter beta = 0 c2 = alpha/2 c0 = 1-alpha/2 else: # parameters ensure piecewise function twice differentiable beta = alpha*(2-alpha)/(3*R*(R**2-1)) c2 = (alpha-beta*(R-1)**2*(R+2))/2 c0 = beta*(R-1)**3+1-c2 # create continuous isotropic function r = np.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2) if r<=1: out=c0-r**alpha+c2*r**2 elif r<=R: out=beta*(R-r)**3/r else: out=0 return out, c0, c2 # Fractional Brownian surface # The main control is the Hurst parameter: H should be between 0 and 1, where 0 is very noisy, and 1 is smoother. # From https://gist.github.com/radarsat1/6f8b9b50d1ecd2546d8a765e8a144631 def brownian_surface(boxsizex, H=0.8): N = 2*boxsizex R = 2 # [0,R]^2 grid, may have to extract only [0,R/2]^2 # size of grid is m*n; covariance matrix is m^2*n^2 M = N # create grid for field tx = np.linspace(0, R, M) ty = np.linspace(0, R, N) rows = np.zeros((M,N)) for i in range(N): for j in range(M): # rows of blocks of cov matrix rows[j,i] = rho([tx[i],ty[j]], [tx[0],ty[0]], R, 2*H)[0] BlkCirc_row = np.vstack( [np.hstack([rows, rows[:,-1:1:-1]]), np.hstack([rows[-1:1:-1,:], rows[-1:1:-1, -1:1:-1]])]) # compute eigen-values lam = np.real(np.fft.fft2(BlkCirc_row))/(4*(M-1)*(N-1)) lam = np.sqrt(lam) # generate field with covariance given by block circular matrix Z = np.vectorize(complex)(np.random.randn(2*(M-1), 2*(M-1)), np.random.randn(2*(M-1), 2*(M-1))) F = np.fft.fft2(lam*Z) F = F[:M, :N] # extract sub-block with desired covariance out,c0,c2 = rho([0,0],[0,0],R,2*H) field1 = np.real(F) # two independent fields #field2 = np.imag(F) #field1 = field1 - field1[0,0] # set field zero at origin #field2 = field2 - field2[0,0] # set field zero at origin # make correction for embedding with a term c2*r^2 field1 = field1 + np.kron(np.array([ty]).T * np.random.randn(), np.array([tx]) * np.random.randn())*np.sqrt(2*c2) #field2 = field2 + np.kron(np.array([ty]).T * np.random.randn(), np.array([tx]) * np.random.randn())*np.sqrt(2*c2) #X,Y = np.meshgrid(tx,ty) field1 = field1[:N//2, :M//2] #field2 = field2[:N//2, :M//2] return field1