@@ -181,6 +181,73 @@ pub fn summator_incompr(
181
181
}
182
182
}
183
183
184
+ /// The Fourier method for scalar fields.
185
+ ///
186
+ /// Computes the periodic, isotropic spatial random field $u(x)$ by the Fourier
187
+ /// method according to
188
+ /// $$
189
+ /// u(x) = \sum_{i=1}^N \sqrt{2S(k_i\Delta k}\left(
190
+ /// z_{1,i} \cdot \cos(\langle k_i, x \rangle) +
191
+ /// z_{2,i} \cdot \sin(\langle k_i, x \rangle)\right)
192
+ /// $$
193
+ /// with
194
+ /// * $S$ being the spectrum of the covariance model
195
+ /// * $N$ being the number of Fourier modes
196
+ /// * $z_1, z_2$ being independent samples from a standard normal distribution
197
+ /// * $k$ being the equidistant Fourier grid
198
+ /// and are given by the argument `modes`.
199
+ /// * $\Delta k$ being the cell size of the Fourier grid
200
+ ///
201
+ /// # Arguments
202
+ ///
203
+ /// * `spectrum_factor` - the pre-calculated factor $\sqrt{2S(k_i\Delta k)}
204
+ /// <br> dim = Fourier modes $N$
205
+ /// * `modes` - equidistant Fourier grid, $k$ in Eq. above
206
+ /// <br> dim = (spatial dim. of field $d$, Fourier modes $N$)
207
+ /// * `z1` - independent samples from a standard normal distribution
208
+ /// <br> dim = Fourier modes $N$
209
+ /// * `z2` - independent samples from a standard normal distribution
210
+ /// <br> dim = Fourier modes $N$
211
+ /// * `pos` - the position $x$ where the spatial random field is calculated
212
+ /// <br> dim = (spatial dim. of field $d$, no. of spatial points where field is calculated $j$)
213
+ /// * `num_threads` - the number of parallel threads used, if None, use rayon's default
214
+ ///
215
+ /// # Returns
216
+ ///
217
+ /// * `summed_modes` - the isotropic spatial field
218
+ /// <br> dim = no. of spatial points where field is calculated $j$
219
+ pub fn summator_fourier (
220
+ spectrum_factor : ArrayView1 < ' _ , f64 > ,
221
+ modes : ArrayView2 < ' _ , f64 > ,
222
+ z1 : ArrayView1 < ' _ , f64 > ,
223
+ z2 : ArrayView1 < ' _ , f64 > ,
224
+ pos : ArrayView2 < ' _ , f64 > ,
225
+ num_threads : Option < usize > ,
226
+ ) -> Array1 < f64 > {
227
+ assert_eq ! ( modes. dim( ) . 0 , pos. dim( ) . 0 ) ;
228
+ assert_eq ! ( modes. dim( ) . 1 , z1. dim( ) ) ;
229
+ assert_eq ! ( modes. dim( ) . 1 , z2. dim( ) ) ;
230
+
231
+ rayon:: ThreadPoolBuilder :: new ( )
232
+ . num_threads ( num_threads. unwrap_or ( rayon:: current_num_threads ( ) ) )
233
+ . build ( )
234
+ . unwrap ( )
235
+ . install ( || {
236
+ Zip :: from ( pos. columns ( ) ) . par_map_collect ( |pos| {
237
+ Zip :: from ( modes. columns ( ) )
238
+ . and ( spectrum_factor)
239
+ . and ( z1)
240
+ . and ( z2)
241
+ . fold ( 0.0 , |sum, k, & spectrum_factor, & z1, & z2| {
242
+ let phase = k. dot ( & pos) ;
243
+ let z12 = spectrum_factor * ( z1 * phase. cos ( ) + z2 * phase. sin ( ) ) ;
244
+
245
+ sum + z12
246
+ } )
247
+ } )
248
+ } )
249
+ }
250
+
184
251
#[ cfg( test) ]
185
252
mod tests {
186
253
use super :: * ;
@@ -224,8 +291,73 @@ mod tests {
224
291
}
225
292
}
226
293
294
+ struct SetupFourier {
295
+ spectrum_factor : Array1 < f64 > ,
296
+ modes : Array2 < f64 > ,
297
+ z_1 : Array1 < f64 > ,
298
+ z_2 : Array1 < f64 > ,
299
+ pos : Array2 < f64 > ,
300
+ }
301
+
302
+ impl SetupFourier {
303
+ fn new ( ) -> Self {
304
+ Self {
305
+ spectrum_factor : arr1 ( & [
306
+ -2.15 , 1.04 , 0.69 , -1.09 , -1.54 , -2.32 , -1.81 , -2.78 , 1.57 , -3.44 ,
307
+ ] ) ,
308
+ modes : arr2 ( & [
309
+ [
310
+ -2.15 , 1.04 , 0.69 , -1.09 , -1.54 , -2.32 , -1.81 , -2.78 , 1.57 , -3.44 ,
311
+ ] ,
312
+ [
313
+ 0.19 , -1.24 , -2.10 , -2.86 , -0.63 , -0.51 , -1.68 , -0.07 , 0.29 , -0.007 ,
314
+ ] ,
315
+ [
316
+ 0.98 , -2.83 , -0.10 , 3.23 , 0.51 , 0.13 , -1.03 , 1.53 , -0.51 , 2.82 ,
317
+ ] ,
318
+ ] ) ,
319
+ z_1 : arr1 ( & [
320
+ -1.93 , 0.46 , 0.66 , 0.02 , -0.10 , 1.29 , 0.93 , -1.14 , 1.81 , 1.47 ,
321
+ ] ) ,
322
+ z_2 : arr1 ( & [
323
+ -0.26 , 0.98 , -1.30 , 0.66 , 0.57 , -0.25 , -0.31 , -0.29 , 0.69 , 1.14 ,
324
+ ] ) ,
325
+ pos : arr2 ( & [
326
+ [ 0.00 , 1.43 , 2.86 , 4.29 , 5.71 , 7.14 , 9.57 , 10.00 ] ,
327
+ [ -5.00 , -3.57 , -2.14 , -0.71 , 0.71 , 2.14 , 3.57 , 5.00 ] ,
328
+ [ -6.00 , -4.00 , -2.00 , 0.00 , 2.00 , 4.00 , 6.00 , 8.00 ] ,
329
+ ] ) ,
330
+ }
331
+ }
332
+ }
333
+
334
+ #[ test]
335
+ fn test_summate_fourier_2d ( ) {
336
+ let setup = SetupFourier :: new ( ) ;
337
+ assert_eq ! (
338
+ summator_fourier(
339
+ setup. spectrum_factor. view( ) ,
340
+ setup. modes. view( ) ,
341
+ setup. z_1. view( ) ,
342
+ setup. z_2. view( ) ,
343
+ setup. pos. view( ) ,
344
+ None ,
345
+ ) ,
346
+ arr1( & [
347
+ 1.0666558330143816 ,
348
+ -3.5855143411414883 ,
349
+ -2.70208228699285 ,
350
+ 9.808554698975039 ,
351
+ 0.01634921830347258 ,
352
+ -2.2356422006860663 ,
353
+ 14.730786907708966 ,
354
+ -2.851408419726332 ,
355
+ ] )
356
+ ) ;
357
+ }
358
+
227
359
#[ test]
228
- fn test_summate_3d ( ) {
360
+ fn test_summate_2d ( ) {
229
361
let setup = Setup :: new ( ) ;
230
362
231
363
assert_eq ! (
@@ -250,7 +382,7 @@ mod tests {
250
382
}
251
383
252
384
#[ test]
253
- fn test_summate_incompr_3d ( ) {
385
+ fn test_summate_incompr_2d ( ) {
254
386
let setup = Setup :: new ( ) ;
255
387
256
388
assert_ulps_eq ! (
0 commit comments