surfacetopography 0.91.2__py3.6-linux-x86_64.egg

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. EGG-INFO/PKG-INFO +11 -0
  2. EGG-INFO/SOURCES.txt +96 -0
  3. EGG-INFO/dependency_links.txt +1 -0
  4. EGG-INFO/native_libs.txt +1 -0
  5. EGG-INFO/not-zip-safe +1 -0
  6. EGG-INFO/requires.txt +6 -0
  7. EGG-INFO/top_level.txt +3 -0
  8. SurfaceTopography/ChangeLog.md +358 -0
  9. SurfaceTopography/Converters.py +197 -0
  10. SurfaceTopography/Generation.py +440 -0
  11. SurfaceTopography/HeightContainer.py +315 -0
  12. SurfaceTopography/IO/DI.py +261 -0
  13. SurfaceTopography/IO/FromFile.py +802 -0
  14. SurfaceTopography/IO/H5.py +79 -0
  15. SurfaceTopography/IO/IBW.py +176 -0
  16. SurfaceTopography/IO/MI.py +307 -0
  17. SurfaceTopography/IO/Matlab.py +106 -0
  18. SurfaceTopography/IO/NC.py +228 -0
  19. SurfaceTopography/IO/NPY.py +139 -0
  20. SurfaceTopography/IO/OPDx.py +885 -0
  21. SurfaceTopography/IO/Reader.py +389 -0
  22. SurfaceTopography/IO/__init__.py +241 -0
  23. SurfaceTopography/IO/__pycache__/DI.cpython-36.pyc +0 -0
  24. SurfaceTopography/IO/__pycache__/FromFile.cpython-36.pyc +0 -0
  25. SurfaceTopography/IO/__pycache__/H5.cpython-36.pyc +0 -0
  26. SurfaceTopography/IO/__pycache__/IBW.cpython-36.pyc +0 -0
  27. SurfaceTopography/IO/__pycache__/MI.cpython-36.pyc +0 -0
  28. SurfaceTopography/IO/__pycache__/Matlab.cpython-36.pyc +0 -0
  29. SurfaceTopography/IO/__pycache__/NC.cpython-36.pyc +0 -0
  30. SurfaceTopography/IO/__pycache__/NPY.cpython-36.pyc +0 -0
  31. SurfaceTopography/IO/__pycache__/OPDx.cpython-36.pyc +0 -0
  32. SurfaceTopography/IO/__pycache__/Reader.cpython-36.pyc +0 -0
  33. SurfaceTopography/IO/__pycache__/__init__.cpython-36.pyc +0 -0
  34. SurfaceTopography/Interpolation.py +30 -0
  35. SurfaceTopography/Nonuniform/Autocorrelation.py +161 -0
  36. SurfaceTopography/Nonuniform/Detrending.py +87 -0
  37. SurfaceTopography/Nonuniform/PowerSpectrum.py +178 -0
  38. SurfaceTopography/Nonuniform/ScalarParameters.py +140 -0
  39. SurfaceTopography/Nonuniform/VariableBandwidth.py +155 -0
  40. SurfaceTopography/Nonuniform/__init__.py +28 -0
  41. SurfaceTopography/Nonuniform/__pycache__/Autocorrelation.cpython-36.pyc +0 -0
  42. SurfaceTopography/Nonuniform/__pycache__/Detrending.cpython-36.pyc +0 -0
  43. SurfaceTopography/Nonuniform/__pycache__/PowerSpectrum.cpython-36.pyc +0 -0
  44. SurfaceTopography/Nonuniform/__pycache__/ScalarParameters.cpython-36.pyc +0 -0
  45. SurfaceTopography/Nonuniform/__pycache__/VariableBandwidth.cpython-36.pyc +0 -0
  46. SurfaceTopography/Nonuniform/__pycache__/__init__.cpython-36.pyc +0 -0
  47. SurfaceTopography/Nonuniform/__pycache__/common.cpython-36.pyc +0 -0
  48. SurfaceTopography/Nonuniform/common.py +95 -0
  49. SurfaceTopography/NonuniformLineScan.py +310 -0
  50. SurfaceTopography/Special.py +225 -0
  51. SurfaceTopography/Tools/__init__.py +28 -0
  52. SurfaceTopography/Tools/__pycache__/__init__.cpython-36.pyc +0 -0
  53. SurfaceTopography/Tools/__pycache__/common.cpython-36.pyc +0 -0
  54. SurfaceTopography/Tools/common.py +75 -0
  55. SurfaceTopography/Uniform/Autocorrelation.py +211 -0
  56. SurfaceTopography/Uniform/Detrending.py +155 -0
  57. SurfaceTopography/Uniform/Filtering.py +226 -0
  58. SurfaceTopography/Uniform/Interpolation.py +159 -0
  59. SurfaceTopography/Uniform/PowerSpectrum.py +197 -0
  60. SurfaceTopography/Uniform/ScalarParameters.py +173 -0
  61. SurfaceTopography/Uniform/VariableBandwidth.py +169 -0
  62. SurfaceTopography/Uniform/__init__.py +28 -0
  63. SurfaceTopography/Uniform/__pycache__/Autocorrelation.cpython-36.pyc +0 -0
  64. SurfaceTopography/Uniform/__pycache__/Detrending.cpython-36.pyc +0 -0
  65. SurfaceTopography/Uniform/__pycache__/Filtering.cpython-36.pyc +0 -0
  66. SurfaceTopography/Uniform/__pycache__/Interpolation.cpython-36.pyc +0 -0
  67. SurfaceTopography/Uniform/__pycache__/PowerSpectrum.cpython-36.pyc +0 -0
  68. SurfaceTopography/Uniform/__pycache__/ScalarParameters.cpython-36.pyc +0 -0
  69. SurfaceTopography/Uniform/__pycache__/VariableBandwidth.cpython-36.pyc +0 -0
  70. SurfaceTopography/Uniform/__pycache__/__init__.cpython-36.pyc +0 -0
  71. SurfaceTopography/Uniform/__pycache__/common.cpython-36.pyc +0 -0
  72. SurfaceTopography/Uniform/common.py +260 -0
  73. SurfaceTopography/UniformLineScanAndTopography.py +887 -0
  74. SurfaceTopography/__init__.py +62 -0
  75. SurfaceTopography/__pycache__/Converters.cpython-36.pyc +0 -0
  76. SurfaceTopography/__pycache__/Generation.cpython-36.pyc +0 -0
  77. SurfaceTopography/__pycache__/HeightContainer.cpython-36.pyc +0 -0
  78. SurfaceTopography/__pycache__/Interpolation.cpython-36.pyc +0 -0
  79. SurfaceTopography/__pycache__/NonuniformLineScan.cpython-36.pyc +0 -0
  80. SurfaceTopography/__pycache__/Special.cpython-36.pyc +0 -0
  81. SurfaceTopography/__pycache__/UniformLineScanAndTopography.cpython-36.pyc +0 -0
  82. SurfaceTopography/__pycache__/__init__.cpython-36.pyc +0 -0
  83. SurfaceTopography/__pycache__/common.cpython-36.pyc +0 -0
  84. SurfaceTopography/common.py +111 -0
  85. _SurfaceTopography.cpython-36m-x86_64-linux-gnu.so +0 -0
  86. _SurfaceTopography.py +8 -0
  87. __pycache__/_SurfaceTopography.cpython-36.pyc +0 -0
  88. test/IO/__init__.py +28 -0
  89. test/IO/__pycache__/__init__.cpython-36.pyc +0 -0
  90. test/IO/__pycache__/test_ibw.cpython-36.pyc +0 -0
  91. test/IO/__pycache__/test_io.cpython-36.pyc +0 -0
  92. test/IO/__pycache__/test_mi.cpython-36.pyc +0 -0
  93. test/IO/__pycache__/test_netcdf.cpython-36.pyc +0 -0
  94. test/IO/__pycache__/test_npy.cpython-36.pyc +0 -0
  95. test/IO/__pycache__/test_opdx.cpython-36.pyc +0 -0
  96. test/IO/test_ibw.py +178 -0
  97. test/IO/test_io.py +401 -0
  98. test/IO/test_mi.py +103 -0
  99. test/IO/test_netcdf.py +134 -0
  100. test/IO/test_npy.py +135 -0
  101. test/IO/test_opdx.py +441 -0
  102. test/__init__.py +26 -0
  103. test/__pycache__/__init__.cpython-36.pyc +0 -0
  104. test/__pycache__/compare-orientation-with-gwyddion.cpython-36.pyc +0 -0
  105. test/__pycache__/conftest.cpython-36.pyc +0 -0
  106. test/__pycache__/test_autocorrelation.cpython-36.pyc +0 -0
  107. test/__pycache__/test_bicubic.cpython-36.pyc +0 -0
  108. test/__pycache__/test_filtering.cpython-36.pyc +0 -0
  109. test/__pycache__/test_generation.cpython-36.pyc +0 -0
  110. test/__pycache__/test_import.cpython-36.pyc +0 -0
  111. test/__pycache__/test_make_sphere.cpython-36.pyc +0 -0
  112. test/__pycache__/test_power_spectrum.cpython-36.pyc +0 -0
  113. test/__pycache__/test_scalar_parameters.cpython-36.pyc +0 -0
  114. test/__pycache__/test_topography1.cpython-36.pyc +0 -0
  115. test/__pycache__/test_topography2.cpython-36.pyc +0 -0
  116. test/__pycache__/test_variable_bandwidth.cpython-36.pyc +0 -0
  117. test/compare-orientation-with-gwyddion.py +102 -0
  118. test/conftest.py +80 -0
  119. test/test_autocorrelation.py +311 -0
  120. test/test_bicubic.py +190 -0
  121. test/test_filtering.py +69 -0
  122. test/test_generation.py +259 -0
  123. test/test_import.py +54 -0
  124. test/test_make_sphere.py +108 -0
  125. test/test_power_spectrum.py +259 -0
  126. test/test_scalar_parameters.py +176 -0
  127. test/test_topography1.py +1287 -0
  128. test/test_topography2.py +328 -0
  129. test/test_variable_bandwidth.py +107 -0
@@ -0,0 +1,440 @@
1
+ #
2
+ # Copyright 2015-2016, 2018, 2020 Lars Pastewka
3
+ # 2018, 2020 Antoine Sanner
4
+ # 2015-2016 Till Junge
5
+ #
6
+ # ### MIT license
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+ #
26
+
27
+ """
28
+ Helper functions for the generation of random fractal surfaces
29
+ """
30
+
31
+ import numpy as np
32
+ from . import Topography, UniformLineScan
33
+ from .Tools.common import compute_wavevectors, ifftn
34
+
35
+
36
+ class CapillaryWavesExact(object):
37
+ """Frozen capillary waves"""
38
+ Error = Exception
39
+
40
+ def __init__(self, nb_grid_pts, physical_sizes, mass_density,
41
+ surface_tension,
42
+ bending_stiffness, seed=None):
43
+ """
44
+ Generates a surface with an exact power spectrum (deterministic
45
+ amplitude)
46
+ Keyword Arguments:
47
+ nb_grid_pts -- Tuple containing number of points in spatial
48
+ directions. The length of the tuple determines the
49
+ spatial dimension of the problem (for the time
50
+ being, only 1D or square 2D)
51
+ physical_sizes -- domain physical_sizes. For multidimensional
52
+ problems, a tuple can be provided to specify the
53
+ length per dimension. If the tuple has less
54
+ entries than dimensions, the last value in
55
+ repeated.
56
+ mass_density -- Mass density
57
+ surface_tension -- SurfaceTopography tension
58
+ bending_stiffness -- Bending stiffness
59
+ rms_height -- root mean square height of surface
60
+ rms_slope -- root mean square slope of surface
61
+ seed -- (default hash(None)) for repeatability, the random
62
+ number generator is seeded previous to outputting
63
+ the generated surface
64
+ """
65
+ if seed is not None:
66
+ np.random.seed(hash(seed))
67
+ if not hasattr(nb_grid_pts, "__iter__"):
68
+ nb_grid_pts = (nb_grid_pts,)
69
+ if not hasattr(physical_sizes, "__iter__"):
70
+ physical_sizes = (physical_sizes,)
71
+
72
+ self.dim = len(nb_grid_pts)
73
+ if self.dim not in (1, 2):
74
+ raise self.Error(
75
+ "Dimension of this problem is {}. Only 1 and 2-dimensional "
76
+ "problems are supported".format(self.dim))
77
+ self.nb_grid_pts = nb_grid_pts
78
+ tmpsize = list()
79
+ for i in range(self.dim):
80
+ tmpsize.append(physical_sizes[min(i, len(physical_sizes) - 1)])
81
+ self.size = tuple(tmpsize)
82
+
83
+ self.mass_density = mass_density
84
+ self.surface_tension = surface_tension
85
+ self.bending_stiffness = bending_stiffness
86
+
87
+ self.q = compute_wavevectors( # pylint: disable=invalid-name
88
+ self.nb_grid_pts, self.size, self.dim)
89
+ self.coeffs = self.generate_phases()
90
+ self.generate_amplitudes()
91
+ self.distribution = self.amplitude_distribution()
92
+ self.active_coeffs = None
93
+
94
+ def get_negative_frequency_iterator(self):
95
+ " frequency complement"
96
+
97
+ def iterator(): # pylint: disable=missing-docstring
98
+ for i in range(self.nb_grid_pts[0]):
99
+ for j in range(self.nb_grid_pts[1] // 2 + 1):
100
+ yield (i, j), (-i, -j)
101
+
102
+ return iterator()
103
+
104
+ def amplitude_distribution(self): # pylint: disable=no-self-use
105
+ """
106
+ returns a multiplicative factor to apply to the fourier coeffs before
107
+ computing the inverse transform (trivial in this case, since there's no
108
+ stochastic distro in this case)
109
+ """
110
+ return 1.
111
+
112
+ @property
113
+ def abs_q(self):
114
+ " radial distances in q-space"
115
+ q_norm = np.sqrt((self.q[0] ** 2).reshape((-1, 1)) + self.q[1] ** 2)
116
+ order = np.argsort(q_norm, axis=None)
117
+ # The first entry (for |q| = 0) is rejected, since it's 0 by construct
118
+ return q_norm.flatten()[order][1:]
119
+
120
+ def generate_phases(self):
121
+ """
122
+ generates appropriate random phases (φ(-q) = -φ(q))
123
+ """
124
+ rand_phase = np.random.rand(*self.nb_grid_pts) * 2 * np.pi
125
+ coeffs = np.exp(1j * rand_phase)
126
+ for pos_it, neg_it in self.get_negative_frequency_iterator():
127
+ if pos_it != (0, 0):
128
+ coeffs[neg_it] = coeffs[pos_it].conj()
129
+ if self.nb_grid_pts[0] % 2 == 0:
130
+ r_2 = self.nb_grid_pts[0] // 2
131
+ coeffs[r_2, 0] = coeffs[r_2, r_2] = coeffs[0, r_2] = 1
132
+ return coeffs
133
+
134
+ def generate_amplitudes(self):
135
+ "compute an amplitude distribution"
136
+ q_2 = self.q[0].reshape(-1, 1) ** 2 + self.q[1] ** 2
137
+ q_2[0, 0] = 1 # to avoid div by zeros, needs to be fixed after
138
+ self.coeffs *= 1 / (self.mass_density + self.surface_tension * q_2 +
139
+ self.bending_stiffness * q_2 * q_2)
140
+ self.coeffs[0, 0] = 0 # et voilà
141
+ # Fix Shannon limit:
142
+ # self.coeffs[q_2 > self.q_max**2] = 0
143
+
144
+ def get_topography(self):
145
+ """
146
+ Computes and returs a NumpySurface object with the specified
147
+ properties. This follows appendices A and B of Persson et al. (2005)
148
+
149
+ Persson et al., On the nature of surface roughness with application to
150
+ contact mechanics, sealing, rubber friction and adhesion, J. Phys.:
151
+ Condens. Matter 17 (2005) R1-R62, http://arxiv.org/abs/cond-mat/0502419
152
+
153
+ Keyword Arguments:
154
+ lambda_max -- (default None) specifies a cutoff value for the longest
155
+ wavelength. By default, this is the domain physical_sizes
156
+ in the smallest dimension
157
+ lambda_min -- (default None) specifies a cutoff value for the shortest
158
+ wavelength. by default this is determined by Shannon's
159
+ Theorem.
160
+ """
161
+ active_coeffs = self.coeffs.copy()
162
+ active_coeffs *= self.distribution
163
+ area = np.prod(self.size)
164
+ profile = ifftn(active_coeffs, area).real
165
+ self.active_coeffs = active_coeffs
166
+ return Topography(profile, self.size)
167
+
168
+
169
+ ###
170
+
171
+ def _irfft2(karr, rarr, progress_callback=None):
172
+ """
173
+ Inverse 2d real-to-complex FFT with callback that allows to report status
174
+ of the computation to user.
175
+
176
+ Parameters
177
+ ----------
178
+ karr : array_like
179
+ Fourier-space representation
180
+ rarr : array_like
181
+ Real-space representation
182
+ progress_callback : function(i, n)
183
+ Function that is called to report progress.
184
+ """
185
+ nrows, ncolumns = karr.shape
186
+ for i in range(ncolumns):
187
+ if progress_callback is not None:
188
+ progress_callback(i, ncolumns + nrows - 1)
189
+ karr[:, i] = np.fft.ifft(karr[:, i])
190
+ for i in range(nrows):
191
+ if progress_callback is not None:
192
+ progress_callback(i + ncolumns, ncolumns + nrows - 1)
193
+ if rarr.shape[1] % 2 == 0:
194
+ rarr[i, :] = np.fft.irfft(karr[i, :])
195
+ else:
196
+ rarr[i, :] = np.fft.irfft(karr[i, :], n=rarr.shape[1])
197
+
198
+
199
+ def self_affine_prefactor(nb_grid_pts, physical_sizes, Hurst, rms_height=None,
200
+ rms_slope=None, short_cutoff=None, long_cutoff=None):
201
+ r"""
202
+ Compute prefactor :math:`C_0` for the power-spectrum density of an ideal
203
+ self-affine topography given by
204
+
205
+ .. math ::
206
+
207
+ C(q) = C_0 q^{-2-2H}
208
+
209
+ for two-dimensional topography maps and
210
+
211
+ .. math ::
212
+
213
+ C(q) = C_0 q^{-1-2H}
214
+
215
+ for one-dimensional line scans. Here :math:`H` is the Hurst exponent.
216
+
217
+ Note:
218
+ In the 2D case:
219
+
220
+ .. math ::
221
+
222
+ h^2_{rms} = \frac{1}{2 \pi} \int_{0}^{\infty} q C^{iso}(q) dq
223
+
224
+ whereas in the 1D case:
225
+
226
+ .. math ::
227
+
228
+ h^2_{rms} = \frac{1}{\pi} \int_{0}^{\infty} C^{1D}(q) dq
229
+
230
+ See Equations (1) and (4) in [1].
231
+
232
+
233
+ Parameters
234
+ ----------
235
+ nb_grid_pts : array_like
236
+ Resolution of the topography map or the line scan.
237
+ physical_sizes : array_like
238
+ Physical physical_sizes of the topography map or the line scan.
239
+ Hurst : float
240
+ Hurst exponent.
241
+ rms_height : float
242
+ Root mean-squared height.
243
+ rms_slope : float
244
+ Root mean-squared slope of the topography map or the line scan.
245
+ short_cutoff : float
246
+ Short-wavelength cutoff.
247
+ long_cutoff : float
248
+ Long-wavelength cutoff.
249
+
250
+ Returns
251
+ -------
252
+ prefactor : float
253
+ Prefactor :math:`\sqrt{C_0}`
254
+
255
+ References
256
+ -----------
257
+ [1]: Jacobs, Junge, Pastewka,
258
+ Surf. Topgogr.: Metrol. Prop. 5, 013001 (2017)
259
+
260
+ """
261
+ nb_grid_pts = np.asarray(nb_grid_pts)
262
+ physical_sizes = np.asarray(physical_sizes)
263
+
264
+ if short_cutoff is not None:
265
+ q_max = 2 * np.pi / short_cutoff
266
+ else:
267
+ q_max = np.pi * np.min(nb_grid_pts / physical_sizes)
268
+
269
+ if long_cutoff is not None:
270
+ q_min = 2 * np.pi / long_cutoff
271
+ else:
272
+ q_min = 2 * np.pi * np.max(1 / physical_sizes)
273
+
274
+ area = np.prod(physical_sizes)
275
+
276
+ if rms_height is not None:
277
+ # Assuming no rolloff region
278
+ fac = 2 * rms_height / np.sqrt(q_min ** (-2 * Hurst) -
279
+ q_max ** (-2 * Hurst)) * np.sqrt(
280
+ Hurst * np.pi)
281
+ elif rms_slope is not None:
282
+ fac = 2 * rms_slope / np.sqrt(q_max ** (2 - 2 * Hurst) -
283
+ q_min ** (2 - 2 * Hurst)) * np.sqrt(
284
+ (1 - Hurst) * np.pi)
285
+ else:
286
+ raise ValueError('Neither rms height nor rms slope is defined!')
287
+
288
+ if len(nb_grid_pts) == 1:
289
+ fac /= np.sqrt(2)
290
+
291
+ return fac * np.prod(nb_grid_pts) / np.sqrt(area)
292
+
293
+
294
+ def fourier_synthesis(nb_grid_pts, physical_sizes, hurst,
295
+ rms_height=None, rms_slope=None, c0=None,
296
+ short_cutoff=None, long_cutoff=None, rolloff=1.0,
297
+ amplitude_distribution=lambda n: np.random.normal(
298
+ size=n),
299
+ periodic=True, rfn=None, kfn=None,
300
+ progress_callback=None):
301
+ r"""
302
+ Create a self-affine, randomly rough surface using a Fourier filtering
303
+ algorithm. The algorithm is described in:
304
+ Ramisetti et al., J. Phys.: Condens. Matter 23, 215004 (2011);
305
+ Jacobs, Junge, Pastewka, Surf. Topgogr.: Metrol. Prop. 5, 013001 (2017)
306
+
307
+ Parameters
308
+ ----------
309
+ nb_grid_pts : array_like
310
+ Resolution of the topography map.
311
+ physical_sizes : array_like
312
+ Physical physical_sizes of the topography map.
313
+ hurst : float
314
+ Hurst exponent.
315
+ rms_height : float
316
+ Root mean-squared height.
317
+ rms_slope : float
318
+ Root mean-squared slope.
319
+ c0: float
320
+ self affine prefactor :math:`C_0`:
321
+ :math:`C(q) = C_0 q^{-2-2H}`
322
+ short_cutoff : float
323
+ Short-wavelength cutoff.
324
+ long_cutoff : float
325
+ Long-wavelength cutoff.
326
+ rolloff : float
327
+ Value for the power-spectral density (PSD) below the long-wavelength
328
+ cutoff. This multiplies the value at the cutoff, i.e. unit will give a
329
+ PSD that is flat below the cutoff, zero will give a PSD that is
330
+ vanishes below cutoff. (Default: 1.0)
331
+ amplitude_distribution : function
332
+ Function that generates the distribution of amplitudes.
333
+ (Default: np.random.normal)
334
+ periodic : bool
335
+ Return a periodic surface. (Default: True)
336
+ rfn : str
337
+ Name of file that stores the real-space array. If specified, real-space
338
+ array will be created as a memory mapped file. This is useful for
339
+ creating very large topography maps. (Default: None)
340
+ kfn : str
341
+ Name of file that stores the Fourie-space array. If specified,
342
+ real-space array will be created as a memory mapped file. This is
343
+ useful for creating very large topography maps. (Default: None)
344
+ progress_callback : function(i, n)
345
+ Function that is called to report progress.
346
+
347
+ Returns
348
+ -------
349
+ topography : UniformTopography or UniformLineScan
350
+ The topography.
351
+ """
352
+ if short_cutoff is not None:
353
+ q_max = 2 * np.pi / short_cutoff
354
+ else:
355
+ q_max = np.pi * np.min(
356
+ np.asarray(nb_grid_pts) / np.asarray(physical_sizes))
357
+
358
+ if long_cutoff is not None:
359
+ q_min = 2 * np.pi / long_cutoff
360
+ else:
361
+ q_min = None
362
+
363
+ if c0 is None:
364
+ fac = self_affine_prefactor(nb_grid_pts, physical_sizes, hurst,
365
+ rms_height=rms_height,
366
+ rms_slope=rms_slope,
367
+ short_cutoff=short_cutoff,
368
+ long_cutoff=long_cutoff)
369
+ else:
370
+ # prefactor for the fourier heights
371
+ fac = np.sqrt(c0) * np.prod(nb_grid_pts) / np.sqrt(
372
+ np.prod(physical_sizes))
373
+ # C(q) = c0 q^(-2-2H) = 1 / A |fh(q)|^2
374
+ # and h(x,y) = sum(1/A fh(q) e^(iqx)))
375
+ # compensate for the np.fft normalisation
376
+
377
+ if len(nb_grid_pts) == 2:
378
+ nx, ny = nb_grid_pts
379
+ sx, sy = physical_sizes
380
+ kny = ny // 2 + 1
381
+ kshape = (nx, kny)
382
+ else:
383
+ nx = 1
384
+ ny, = nb_grid_pts
385
+ sx = 1
386
+ sy, = physical_sizes
387
+ kny = ny // 2 + 1
388
+ kshape = (kny,)
389
+
390
+ # Create in-memory or memory-mapped arrays as storage buffers
391
+ if rfn is None:
392
+ rarr = np.empty(nb_grid_pts, dtype=np.float64)
393
+ else:
394
+ rarr = np.memmap(rfn, np.float64, 'w+', shape=nb_grid_pts)
395
+ if kfn is None:
396
+ karr = np.empty(kshape, dtype=np.complex128)
397
+ else:
398
+ karr = np.memmap(kfn, np.complex128, 'w+', shape=kshape)
399
+
400
+ qy = 2 * np.pi * np.arange(kny) / sy
401
+ for x in range(nx):
402
+ if progress_callback is not None:
403
+ progress_callback(x, nx - 1)
404
+ if x > nx // 2:
405
+ qx = 2 * np.pi * (nx - x) / sx
406
+ else:
407
+ qx = 2 * np.pi * x / sx
408
+ q_sq = qx ** 2 + qy ** 2
409
+ if x == 0:
410
+ q_sq[0] = 1.
411
+ phase = np.exp(2 * np.pi * np.random.rand(kny) * 1j)
412
+ ran = fac * phase * amplitude_distribution(kny)
413
+ if len(nb_grid_pts) == 2:
414
+ karr[x, :] = ran * q_sq ** (-(1 + hurst) / 2)
415
+ karr[x, q_sq > q_max ** 2] = 0.
416
+ else:
417
+ karr[:] = ran * q_sq ** (-(0.5 + hurst) / 2)
418
+ karr[q_sq > q_max ** 2] = 0.
419
+ if q_min is not None:
420
+ mask = q_sq < q_min ** 2
421
+ if len(nb_grid_pts) == 2:
422
+ karr[x, mask] = rolloff * ran[mask] * q_min ** (-(1 + hurst))
423
+ else:
424
+ karr[mask] = rolloff * ran[mask] * q_min ** (-(0.5 + hurst))
425
+ if len(nb_grid_pts) == 2:
426
+ for iy in [0, -1] if ny % 2 == 0 else [0]:
427
+ # Enforce symmetry
428
+ if nx % 2 == 0:
429
+ karr[0, iy] = np.real(karr[0, iy])
430
+ karr[nx // 2, iy] = np.real(karr[nx // 2, iy])
431
+ karr[1:nx // 2, iy] = karr[-1:nx // 2:-1, iy].conj()
432
+ else:
433
+ karr[0, iy] = np.real(karr[0, iy])
434
+ karr[1:nx // 2 + 1, iy] = karr[-1:nx // 2:-1, iy].conj()
435
+ _irfft2(karr, rarr, progress_callback)
436
+ return Topography(rarr, physical_sizes, periodic=periodic)
437
+ else:
438
+ karr[0] = np.real(karr[0])
439
+ rarr[:] = np.fft.irfft(karr)
440
+ return UniformLineScan(rarr, sy, periodic=periodic)