File size: 6,274 Bytes
bf74b80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#---------------------------
# 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