geone 1.2.10__py311-none-manylinux_2_35_x86_64.whl

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.
geone/pgs.py ADDED
@@ -0,0 +1,1171 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # -------------------------------------------------------------------------
5
+ # Python module: 'pgs.py'
6
+ # author: Julien Straubhaar
7
+ # date: may-2022
8
+ # -------------------------------------------------------------------------
9
+
10
+ """
11
+ Module for plurig-Gaussian simulations in 1D, 2D and 3D.
12
+ """
13
+
14
+ import numpy as np
15
+ from geone import covModel as gcm
16
+ from geone import multiGaussian
17
+
18
+ # ----------------------------------------------------------------------------
19
+ def pluriGaussianSim_unconditional(cov_model_T1, cov_model_T2, flag_value,
20
+ dimension, spacing=None, origin=None,
21
+ algo_T1='fft', params_T1={},
22
+ algo_T2='fft', params_T2={},
23
+ nreal=1,
24
+ full_output=True,
25
+ verbose=4):
26
+ """
27
+ Generates unconditional pluri-Gaussian simulations.
28
+
29
+ The simulated variable Z at a point x is defined as
30
+
31
+ * Z(x) = flag_value(T1(x), T2(x))
32
+
33
+ where
34
+
35
+ * T1, T2 are two multi-Gaussian random fields (latent fields)
36
+ * `flag_value` is a function of two variables defining the final value \
37
+ (given as a "flag")
38
+
39
+ Z and T1, T2 are fields in 1D, 2D or 3D.
40
+
41
+ Parameters
42
+ ----------
43
+ cov_model_T1 : :class:`geone.CovModel.CovModel<d>D`
44
+ covariance model for T1, in 1D or 2D or 3D (same space dimension for T1 and T2);
45
+ note: if `algo_T1='deterministic'`, `cov_model_T1` can be `None` (unused)
46
+
47
+ cov_model_T2 : :class:`geone.CovModel.CovModel<d>D`
48
+ covariance model for T2, in 1D or 2D or 3D (same space dimension for T1 and T2);
49
+ note: if `algo_T2='deterministic'`, `cov_model_T2` can be `None` (unused)
50
+
51
+ flag_value : function (`callable`)
52
+ function of tow arguments (xi, yi) that returns the "flag_value" at
53
+ location (xi, yi)
54
+
55
+ dimension : [sequence of] int(s)
56
+ number of cells along each axis, for simulation in:
57
+
58
+ - 1D: `dimension=nx`
59
+ - 2D: `dimension=(nx, ny)`
60
+ - 3D: `dimension=(nx, ny, nz)`
61
+
62
+ spacing : [sequence of] float(s), optional
63
+ cell size along each axis, for simulation in:
64
+
65
+ - 1D: `spacing=sx`
66
+ - 2D: `spacing=(sx, sy)`
67
+ - 3D: `spacing=(sx, sy, sz)`
68
+
69
+ by default (`None`): 1.0 along each axis
70
+
71
+ origin : [sequence of] float(s), optional
72
+ origin of the grid ("corner of the first cell"), for simulation in:
73
+
74
+ - 1D: `origin=ox`
75
+ - 2D: `origin=(ox, oy)`
76
+ - 3D: `origin=(ox, oy, oz)`
77
+
78
+ by default (`None`): 0.0 along each axis
79
+
80
+ algo_T1 : str {'fft', 'classic', 'deterministic'}, default: 'fft'
81
+ defines the algorithm used for T1:
82
+
83
+ - 'fft': algorithm based on circulant embedding and FFT, function \
84
+ called for <d>D (d = 1, 2, or 3): 'geone.grf.grf<d>D'
85
+ - 'classic': "classic" algorithm, based on the resolution of \
86
+ kriging system considered points in a search ellipsoid, function called \
87
+ for <d>D (d = 1, 2, or 3): \
88
+ 'geone.geoscalassicinterface.simulate<d>D'
89
+ - 'deterministic': use a deterministic field, given by `param_T1['mean']`
90
+
91
+ algo_T2 : str {'fft', 'classic', 'deterministic'}, default: 'fft'
92
+ defines the algorithm used for T2 (see `algo_T1` for detail)
93
+
94
+ params_T1 : dict
95
+ keyword arguments (additional parameters) to be passed to the function
96
+ that is called (according to `algo_T1` and space dimension) for simulation
97
+ of T1
98
+
99
+ params_T2 : dict
100
+ keyword arguments (additional parameters) to be passed to the function
101
+ that is called (according to `algo_T2` and space dimension) for simulation
102
+ of T2
103
+
104
+ nreal : int, default: 1
105
+ number of realization(s)
106
+
107
+ full_output : bool, default: True
108
+ - if `True`: simulation(s) of Z, T1, and T2 are retrieved in output
109
+ - if `False`: simulation(s) of Z only is retrieved in output
110
+
111
+ verbose : int, default: 2
112
+ verbose mode, higher implies more printing (info)
113
+
114
+ Returns
115
+ -------
116
+ Z : ndarray
117
+ array of shape
118
+
119
+ - for 1D: (nreal, nx)
120
+ - for 2D: (nreal, ny, nx)
121
+ - for 3D: (nreal, nz, ny, nx)
122
+
123
+ Z[k] is the k-th realization of Z
124
+
125
+ T1 : ndarray, optional
126
+ array of shape
127
+
128
+ - for 1D: (nreal, nx)
129
+ - for 2D: (nreal, ny, nx)
130
+ - for 3D: (nreal, nz, ny, nx)
131
+
132
+ T1[k] is the k-th realization of T1;
133
+ returned if `full_output=True`
134
+
135
+ T2 : ndarray, optional
136
+ array of shape
137
+
138
+ - for 1D: (nreal, nx)
139
+ - for 2D: (nreal, ny, nx)
140
+ - for 3D: (nreal, nz, ny, nx)
141
+
142
+ T2[k] is the k-th realization of T2;
143
+ returned if `full_output=True`
144
+ """
145
+ fname = 'pluriGaussianSim_unconditional'
146
+
147
+ if full_output:
148
+ out = None, None, None
149
+ else:
150
+ out = None
151
+
152
+ if not callable(flag_value):
153
+ if verbose > 0:
154
+ print(f"ERROR ({fname}): `flag_value` invalid, should be a function (callable) of two arguments")
155
+ return out
156
+
157
+ if algo_T1 not in ('fft', 'FFT', 'classic', 'CLASSIC', 'deterministic', 'DETERMINISTIC'):
158
+ if verbose > 0:
159
+ print(f"ERROR ({fname}): `algo_T1` invalid, should be 'fft' (default) or 'classic' or 'deterministic'")
160
+ return out
161
+
162
+ if algo_T2 not in ('fft', 'FFT', 'classic', 'CLASSIC', 'deterministic', 'DETERMINISTIC'):
163
+ if verbose > 0:
164
+ print(f"ERROR ({fname}): `algo_T2` invalid, should be 'fft' (default) or 'classic' or 'deterministic'")
165
+ return out
166
+
167
+ # Ignore covariance model if 'algo' is deterministic for T1, T2
168
+ if algo_T1 in ('deterministic', 'DETERMINISTIC'):
169
+ cov_model_T1 = None
170
+
171
+ if algo_T2 in ('deterministic', 'DETERMINISTIC'):
172
+ cov_model_T2 = None
173
+
174
+ # Set space dimension (of grid) according to covariance model for T1
175
+ d = 0
176
+ if cov_model_T1 is None:
177
+ if algo_T1 not in ('deterministic', 'DETERMINISTIC'):
178
+ if verbose > 0:
179
+ print(f"ERROR ({fname}): `cov_model_T1` is None, then `algo_T1` must be 'deterministic'")
180
+ return out
181
+ elif isinstance(cov_model_T1, gcm.CovModel1D):
182
+ d = 1
183
+ elif isinstance(cov_model_T1, gcm.CovModel2D):
184
+ d = 2
185
+ elif isinstance(cov_model_T1, gcm.CovModel3D):
186
+ d = 3
187
+ else:
188
+ if verbose > 0:
189
+ print(f"ERROR ({fname}): `cov_model_T1` invalid, should be a class: <geone.covModel.CovModel1D>, <geone.covModel.CovModel2D>, or <geone.covModel.CovModel3D>")
190
+ return out
191
+
192
+ if cov_model_T2 is None:
193
+ if algo_T2 not in ('deterministic', 'DETERMINISTIC'):
194
+ if verbose > 0:
195
+ print(f"ERROR ({fname}): `cov_model_T2` is None, then `algo_T2` must be 'deterministic'")
196
+ return out
197
+ # if d == 0:
198
+ # if verbose > 0:
199
+ # print(f"ERROR ({fname}): `cov_model_T1` and `cov_model_T2` are None, at least one covariance model is required")
200
+ # return out
201
+ elif (d == 1 and not isinstance(cov_model_T2, gcm.CovModel1D)) or (d == 2 and not isinstance(cov_model_T2, gcm.CovModel2D)) or (d == 3 and not isinstance(cov_model_T2, gcm.CovModel3D)):
202
+ if verbose > 0:
203
+ print(f"ERROR ({fname}): `cov_model_T1` and `cov_model_T2` not compatible (dimension differs)")
204
+ return out
205
+
206
+ if d == 0:
207
+ # Set space dimension (of grid) according to 'dimension'
208
+ if hasattr(dimension, '__len__'):
209
+ d = len(dimension)
210
+ else:
211
+ d = 1
212
+
213
+ # Check argument 'dimension'
214
+ if hasattr(dimension, '__len__') and len(dimension) != d:
215
+ if verbose > 0:
216
+ print(f"ERROR ({fname}): `dimension` of incompatible length")
217
+ return out
218
+
219
+ if d == 1:
220
+ grid_size = dimension
221
+ else:
222
+ grid_size = np.prod(dimension)
223
+
224
+ # Check (or set) argument 'spacing'
225
+ if spacing is None:
226
+ if d == 1:
227
+ spacing = 1.0
228
+ else:
229
+ spacing = tuple(np.ones(d))
230
+ else:
231
+ if hasattr(spacing, '__len__') and len(spacing) != d:
232
+ if verbose > 0:
233
+ print(f"ERROR ({fname}): `spacing` of incompatible length")
234
+ return out
235
+
236
+ # Check (or set) argument 'origin'
237
+ if origin is None:
238
+ if d == 1:
239
+ origin = 0.0
240
+ else:
241
+ origin = tuple(np.zeros(d))
242
+ else:
243
+ if hasattr(origin, '__len__') and len(origin) != d:
244
+ if verbose > 0:
245
+ print(f"ERROR ({fname}): `origin` of incompatible length")
246
+ return out
247
+
248
+ # if not cov_model_T1.is_stationary(): # prevent calculation if covariance model is not stationary
249
+ # if verbose > 0:
250
+ # print(f"ERROR ({fname}): `cov_model_T1` is not stationary")
251
+
252
+ # if not cov_model_T2.is_stationary(): # prevent calculation if covariance model is not stationary
253
+ # if verbose > 0:
254
+ # print(f"ERROR ({fname}): `cov_model_T2` is not stationary")
255
+
256
+ # Set default parameter 'verbose' for params_T1, params_T2
257
+ if 'verbose' not in params_T1.keys():
258
+ params_T1['verbose'] = 0
259
+ if 'verbose' not in params_T2.keys():
260
+ params_T2['verbose'] = 0
261
+
262
+ # Generate T1
263
+ if cov_model_T1 is not None:
264
+ sim_T1 = multiGaussian.multiGaussianRun(cov_model_T1, dimension, spacing, origin,
265
+ mode='simulation', algo=algo_T1, output_mode='array',
266
+ **params_T1, nreal=nreal)
267
+ else:
268
+ sim_T1 = np.array([params_T1['mean'].reshape(1,*dimension[::-1]) for _ in range(nreal)])
269
+ # -> sim_T1: nd-array of shape
270
+ # (nreal_T, dimension) (for T1 in 1D)
271
+ # (nreal_T, dimension[1], dimension[0]) (for T1 in 2D)
272
+ # (nreal_T, dimension[2], dimension[1], dimension[0]) (for T1 in 3D)
273
+ if sim_T1 is None:
274
+ if verbose > 0:
275
+ print(f'ERROR ({fname}): simulation of T1 failed')
276
+ return out
277
+ #
278
+ # Generate T2
279
+ if cov_model_T2 is not None:
280
+ sim_T2 = multiGaussian.multiGaussianRun(cov_model_T2, dimension, spacing, origin,
281
+ mode='simulation', algo=algo_T2, output_mode='array',
282
+ **params_T2, nreal=nreal)
283
+ else:
284
+ sim_T2 = np.array([params_T2['mean'].reshape(1,*dimension[::-1]) for _ in range(nreal)])
285
+ # -> sim_T2: nd-array of shape
286
+ # (nreal_T, dimension) (for T2 in 1D)
287
+ # (nreal_T, dimension[1], dimension[0]) (for T2 in 2D)
288
+ # (nreal_T, dimension[2], dimension[1], dimension[0]) (for T2 in 3D)
289
+ if sim_T2 is None:
290
+ if verbose > 0:
291
+ print(f'ERROR ({fname}): simulation of T2 failed')
292
+ return out
293
+
294
+ # Generate Z
295
+ if verbose > 2:
296
+ print('PLURIGAUSSIANSIM_UNCONDITIONAL: retrieving Z...')
297
+ Z = flag_value(sim_T1, sim_T2)
298
+ # Z = np.asarray(Z).reshape(len(Z), *np.atleast_1d(dimension)[::-1])
299
+
300
+ if full_output:
301
+ return Z, sim_T1, sim_T2
302
+ else:
303
+ return Z
304
+ # ----------------------------------------------------------------------------
305
+
306
+ # ----------------------------------------------------------------------------
307
+ def pluriGaussianSim(cov_model_T1, cov_model_T2, flag_value,
308
+ dimension, spacing=None, origin=None,
309
+ x=None, v=None,
310
+ algo_T1='fft', params_T1={},
311
+ algo_T2='fft', params_T2={},
312
+ accept_init=0.25, accept_pow=2.0,
313
+ mh_iter_min=100, mh_iter_max=200,
314
+ ntry_max=1,
315
+ retrieve_real_anyway=False,
316
+ nreal=1,
317
+ full_output=True,
318
+ verbose=4):
319
+ """
320
+ Generates (conditional) pluri-Gaussian simulations.
321
+
322
+ The simulated variable Z at a point x is defined as
323
+
324
+ * Z(x) = flag_value(T1(x), T2(x))
325
+
326
+ where
327
+
328
+ * T1, T2 are two multi-Gaussian random fields (latent fields)
329
+ * `flag_value` is a function of two variables defining the final value \
330
+ (given as a "flag")
331
+
332
+ Z and T1, T2 are fields in 1D, 2D or 3D.
333
+
334
+ Parameters
335
+ ----------
336
+ cov_model_T1 : :class:`geone.CovModel.CovModel<d>D`
337
+ covariance model for T1, in 1D or 2D or 3D (same space dimension for T1 and T2);
338
+ note: if `algo_T1='deterministic'`, `cov_model_T1` can be `None` (unused)
339
+
340
+ cov_model_T2 : :class:`geone.CovModel.CovModel<d>D`
341
+ covariance model for T2, in 1D or 2D or 3D (same space dimension for T1 and T2);
342
+ note: if `algo_T2='deterministic'`, `cov_model_T2` can be `None` (unused)
343
+
344
+ flag_value : function (`callable`)
345
+ function of tow arguments (xi, yi) that returns the "flag_value" at
346
+ location (xi, yi)
347
+
348
+ dimension : [sequence of] int(s)
349
+ number of cells along each axis, for simulation in:
350
+
351
+ - 1D: `dimension=nx`
352
+ - 2D: `dimension=(nx, ny)`
353
+ - 3D: `dimension=(nx, ny, nz)`
354
+
355
+ spacing : [sequence of] float(s), optional
356
+ cell size along each axis, for simulation in:
357
+
358
+ - 1D: `spacing=sx`
359
+ - 2D: `spacing=(sx, sy)`
360
+ - 3D: `spacing=(sx, sy, sz)`
361
+
362
+ by default (`None`): 1.0 along each axis
363
+
364
+ origin : [sequence of] float(s), optional
365
+ origin of the grid ("corner of the first cell"), for simulation in:
366
+
367
+ - 1D: `origin=ox`
368
+ - 2D: `origin=(ox, oy)`
369
+ - 3D: `origin=(ox, oy, oz)`
370
+
371
+ by default (`None`): 0.0 along each axis
372
+
373
+ x : array-like of floats, optional
374
+ data points locations (float coordinates), for simulation in:
375
+
376
+ - 1D: 1D array-like of floats
377
+ - 2D: 2D array-like of floats of shape (n, 2)
378
+ - 3D: 2D array-like of floats of shape (n, 3)
379
+
380
+ note: if one point (n=1), a float in 1D, a 1D array of shape (2,) in 2D,
381
+ a 1D array of shape (3,) in 3D, is accepted
382
+
383
+ v : 1D array-like of floats, optional
384
+ data values at `x` (`v[i]` is the data value at `x[i]`)
385
+
386
+ algo_T1 : str {'fft', 'classic', 'deterministic'}, default: 'fft'
387
+ defines the algorithm used for T1:
388
+
389
+ - 'fft': algorithm based on circulant embedding and FFT, function \
390
+ called for <d>D (d = 1, 2, or 3): 'geone.grf.grf<d>D'
391
+ - 'classic': "classic" algorithm, based on the resolution of \
392
+ kriging system considered points in a search ellipsoid, function called \
393
+ for <d>D (d = 1, 2, or 3): \
394
+ 'geone.geoscalassicinterface.simulate<d>D'
395
+ - 'deterministic': use a deterministic field, given by `param_T1['mean']`
396
+
397
+ algo_T2 : str {'fft', 'classic', 'deterministic'}, default: 'fft'
398
+ defines the algorithm used for T2 (see `algo_T1` for detail)
399
+
400
+ params_T1 : dict
401
+ keyword arguments (additional parameters) to be passed to the function
402
+ that is called (according to `algo_T1` and space dimension) for simulation
403
+ of T1
404
+
405
+ params_T2 : dict
406
+ keyword arguments (additional parameters) to be passed to the function
407
+ that is called (according to `algo_T2` and space dimension) for simulation
408
+ of T2
409
+
410
+ accept_init : float, default: 0.25
411
+ initial acceptation probability
412
+ (see parameters `mh_iter_min`, `mh_iter_max`)
413
+
414
+ accept_pow : float, default: 2.0
415
+ power for computing acceptation probability
416
+ (see parameters `mh_iter_min`, `mh_iter_max`)
417
+
418
+ mh_iter_min : int, default: 100
419
+ see parameter `mh_iter_max`
420
+
421
+ mh_iter_max : int, default: 200
422
+ `mh_iter_min` and `mh_iter_max` are the number of iterations
423
+ (min and max) for Metropolis-Hasting algorithm
424
+ (for conditional simulation) when updating T1 and T2 at conditioning
425
+ locations at iteration `nit` (in 0, ..., `mh_iter_max-1`):
426
+
427
+ * if `nit < mh_iter_min`: for any k:
428
+ - simulate new candidate at `x[k]`: `(T1(x[k]), T2(x[k]))`
429
+ - if `flag_value(T1(x[k]), T2(x[k])=v[k]` (conditioning ok): \
430
+ accept the new candidate
431
+ - else (conditioning not ok): \
432
+ accept the new candidate with probability
433
+ * p = `accept_init * (1 - 1/mh_iter_min)**accept_pow`
434
+
435
+ * if nit >= mh_iter_min:
436
+ - if conditioning ok at every `x[k]`: stop and exit the loop,
437
+ - else: for any k:
438
+ - if conditioning ok at `x[k]`: skip
439
+ - else:
440
+ * simulate new candidate at `x[k]`: `(T1(x[k]), T2(x[k]))`
441
+ * if `flag_value(T1(x[k]), T2(x[k])=v[k]` (conditioning ok): \
442
+ accept the new candidate
443
+ * else (conditioning not ok): \
444
+ reject the new candidate
445
+
446
+ ntry_max : int, default: 1
447
+ number of trial(s) per realization before giving up if something goes
448
+ wrong
449
+
450
+ retrieve_real_anyway : bool, default: False
451
+ if after `ntry_max` trial(s) a conditioning data is not honoured, then
452
+ the realization is:
453
+
454
+ - retrieved, if `retrieve_real_anyway=True`
455
+ - not retrieved (missing realization), if `retrieve_real_anyway=False`
456
+
457
+ nreal : int, default: 1
458
+ number of realization(s)
459
+
460
+ full_output : bool, default: True
461
+ - if `True`: simulation(s) of Z, T1, T2, and `n_cond_ok` are \
462
+ retrieved in output
463
+ - if `False`: simulation(s) of Z only is retrieved in output
464
+
465
+ verbose : int, default: 2
466
+ verbose mode, higher implies more printing (info)
467
+
468
+ Returns
469
+ -------
470
+ Z : ndarray
471
+ array of shape
472
+
473
+ - for 1D: (nreal, nx)
474
+ - for 2D: (nreal, ny, nx)
475
+ - for 3D: (nreal, nz, ny, nx)
476
+
477
+ Z[k] is the k-th realization of Z
478
+
479
+ T1 : ndarray, optional
480
+ array of shape
481
+
482
+ - for 1D: (nreal, nx)
483
+ - for 2D: (nreal, ny, nx)
484
+ - for 3D: (nreal, nz, ny, nx)
485
+
486
+ T1[k] is the k-th realization of T1;
487
+ returned if `full_output=True`
488
+
489
+ T2 : ndarray, optional
490
+ array of shape
491
+
492
+ - for 1D: (nreal, nx)
493
+ - for 2D: (nreal, ny, nx)
494
+ - for 3D: (nreal, nz, ny, nx)
495
+
496
+ T2[k] is the k-th realization of T2;
497
+ returned if `full_output=True`
498
+
499
+ n_cond_ok : list of 1D array
500
+ list of length `nreal`
501
+
502
+ - n_cond_ok[k]: 1D array of ints
503
+ number of conditioning locations honoured at each iteration of the
504
+ Metropolis-Hasting algorithm for the k-th realization, in particular
505
+ `len(n_cond_ok[k])` is the number of iteration done,
506
+ `n_cond_ok[k][-1]` is the number of conditioning locations honoured
507
+ at the end;
508
+
509
+ returned if `full_output=True`
510
+ """
511
+ fname = 'pluriGaussianSim'
512
+
513
+ if full_output:
514
+ out = None, None, None, None
515
+ else:
516
+ out = None
517
+
518
+ if not callable(flag_value):
519
+ if verbose > 0:
520
+ print(f"ERROR ({fname}): `flag_value` invalid, should be a function (callable) of two arguments")
521
+ return out
522
+
523
+ if algo_T1 not in ('fft', 'FFT', 'classic', 'CLASSIC', 'deterministic', 'DETERMINISTIC'):
524
+ if verbose > 0:
525
+ print(f"ERROR ({fname}): `algo_T1` invalid, should be 'fft' (default) or 'classic' or 'deterministic'")
526
+ return out
527
+
528
+ if algo_T2 not in ('fft', 'FFT', 'classic', 'CLASSIC', 'deterministic', 'DETERMINISTIC'):
529
+ if verbose > 0:
530
+ print(f"ERROR ({fname}): `algo_T2` invalid, should be 'fft' (default) or 'classic' or 'deterministic'")
531
+ return out
532
+
533
+ # Ignore covariance model if 'algo' is deterministic for T1, T2
534
+ if algo_T1 in ('deterministic', 'DETERMINISTIC'):
535
+ cov_model_T1 = None
536
+
537
+ if algo_T2 in ('deterministic', 'DETERMINISTIC'):
538
+ cov_model_T2 = None
539
+
540
+ # Set space dimension (of grid) according to covariance model for T1
541
+ d = 0
542
+ if cov_model_T1 is None:
543
+ if algo_T1 not in ('deterministic', 'DETERMINISTIC'):
544
+ if verbose > 0:
545
+ print(f"ERROR ({fname}): `cov_model_T1` is None, then `algo_T1` must be 'deterministic'")
546
+ return out
547
+ elif isinstance(cov_model_T1, gcm.CovModel1D):
548
+ d = 1
549
+ elif isinstance(cov_model_T1, gcm.CovModel2D):
550
+ d = 2
551
+ elif isinstance(cov_model_T1, gcm.CovModel3D):
552
+ d = 3
553
+ else:
554
+ if verbose > 0:
555
+ print(f"ERROR ({fname}): `cov_model_T1` invalid, should be a class: <geone.covModel.CovModel1D>, <geone.covModel.CovModel2D>, or <geone.covModel.CovModel3D>")
556
+ return out
557
+
558
+ if cov_model_T2 is None:
559
+ if algo_T2 not in ('deterministic', 'DETERMINISTIC'):
560
+ if verbose > 0:
561
+ print(f"ERROR ({fname}): `cov_model_T2` is None, then `algo_T2` must be 'deterministic'")
562
+ return out
563
+ # if d == 0:
564
+ # if verbose > 0:
565
+ # print(f"ERROR ({fname}): `cov_model_T1` and `cov_model_T2` are None, at least one covariance model is required")
566
+ # return out
567
+ elif (d == 1 and not isinstance(cov_model_T2, gcm.CovModel1D)) or (d == 2 and not isinstance(cov_model_T2, gcm.CovModel2D)) or (d == 3 and not isinstance(cov_model_T2, gcm.CovModel3D)):
568
+ if verbose > 0:
569
+ print(f"ERROR ({fname}): `cov_model_T1` and `cov_model_T2` not compatible (dimension differs)")
570
+ return out
571
+
572
+ if d == 0:
573
+ # Set space dimension (of grid) according to 'dimension'
574
+ if hasattr(dimension, '__len__'):
575
+ d = len(dimension)
576
+ else:
577
+ d = 1
578
+
579
+ # Check argument 'dimension'
580
+ if hasattr(dimension, '__len__') and len(dimension) != d:
581
+ if verbose > 0:
582
+ print(f"ERROR ({fname}): `dimension` of incompatible length")
583
+ return out
584
+
585
+ if d == 1:
586
+ grid_size = dimension
587
+ else:
588
+ grid_size = np.prod(dimension)
589
+
590
+ # Check (or set) argument 'spacing'
591
+ if spacing is None:
592
+ if d == 1:
593
+ spacing = 1.0
594
+ else:
595
+ spacing = tuple(np.ones(d))
596
+ else:
597
+ if hasattr(spacing, '__len__') and len(spacing) != d:
598
+ if verbose > 0:
599
+ print(f"ERROR ({fname}): `spacing` of incompatible length")
600
+ return out
601
+
602
+ # Check (or set) argument 'origin'
603
+ if origin is None:
604
+ if d == 1:
605
+ origin = 0.0
606
+ else:
607
+ origin = tuple(np.zeros(d))
608
+ else:
609
+ if hasattr(origin, '__len__') and len(origin) != d:
610
+ if verbose > 0:
611
+ print(f"ERROR ({fname}): `origin` of incompatible length")
612
+ return out
613
+
614
+ # if not cov_model_T1.is_stationary(): # prevent calculation if covariance model is not stationary
615
+ # if verbose > 0:
616
+ # print(f"ERROR ({fname}): `cov_model_T1` is not stationary")
617
+
618
+ # if not cov_model_T2.is_stationary(): # prevent calculation if covariance model is not stationary
619
+ # if verbose > 0:
620
+ # print(f"ERROR ({fname}): `cov_model_T2` is not stationary")
621
+
622
+ # Compute meshgrid over simulation domain if needed (see below)
623
+ if ('mean' in params_T1.keys() and callable(params_T1['mean'])) or ('var' in params_T1.keys() and callable(params_T1['var'])) \
624
+ or ('mean' in params_T2.keys() and callable(params_T2['mean'])) or ('var' in params_T2.keys() and callable(params_T2['var'])):
625
+ if d == 1:
626
+ xi = origin + spacing*(0.5+np.arange(dimension)) # x-coordinate of cell center
627
+ elif d == 2:
628
+ xi = origin[0] + spacing[0]*(0.5+np.arange(dimension[0])) # x-coordinate of cell center
629
+ yi = origin[1] + spacing[1]*(0.5+np.arange(dimension[1])) # y-coordinate of cell center
630
+ yyi, xxi = np.meshgrid(yi, xi, indexing='ij')
631
+ elif d == 3:
632
+ xi = origin[0] + spacing[0]*(0.5+np.arange(dimension[0])) # x-coordinate of cell center
633
+ yi = origin[1] + spacing[1]*(0.5+np.arange(dimension[1])) # y-coordinate of cell center
634
+ zi = origin[2] + spacing[2]*(0.5+np.arange(dimension[2])) # z-coordinate of cell center
635
+ zzi, yyi, xxi = np.meshgrid(zi, yi, xi, indexing='ij')
636
+
637
+ # Set mean_T1 (as array) from params_T1
638
+ if 'mean' not in params_T1.keys():
639
+ mean_T1 = np.array([0.0])
640
+ else:
641
+ mean_T1 = params_T1['mean']
642
+ if mean_T1 is None:
643
+ mean_T1 = np.array([0.0])
644
+ elif callable(mean_T1):
645
+ if d == 1:
646
+ mean_T1 = mean_T1(xi).reshape(-1) # replace function 'mean_T1' by its evaluation on the grid
647
+ elif d == 2:
648
+ mean_T1 = mean_T1(xxi, yyi).reshape(-1) # replace function 'mean_T1' by its evaluation on the grid
649
+ elif d == 3:
650
+ mean_T1 = mean_T1(xxi, yyi, zzi).reshape(-1) # replace function 'mean_T1' by its evaluation on the grid
651
+ else:
652
+ mean_T1 = np.asarray(mean_T1).reshape(-1)
653
+ if mean_T1.size not in (1, grid_size):
654
+ if verbose > 0:
655
+ print(f"ERROR ({fname}): 'mean' parameter for T1 (in `params_T1`) has incompatible size")
656
+ return out
657
+
658
+ # Set var_T1 (as array) from params_T1, if given
659
+ var_T1 = None
660
+ if 'var' in params_T1.keys():
661
+ var_T1 = params_T1['var']
662
+ if var_T1 is not None:
663
+ if callable(var_T1):
664
+ if d == 1:
665
+ var_T1 = var_T1(xi).reshape(-1) # replace function 'var_T1' by its evaluation on the grid
666
+ elif d == 2:
667
+ var_T1 = var_T1(xxi, yyi).reshape(-1) # replace function 'var_T1' by its evaluation on the grid
668
+ elif d == 3:
669
+ var_T1 = var_T1(xxi, yyi, zzi).reshape(-1) # replace function 'var_T1' by its evaluation on the grid
670
+ else:
671
+ var_T1 = np.asarray(var_T1).reshape(-1)
672
+ if var_T1.size not in (1, grid_size):
673
+ if verbose > 0:
674
+ print(f"ERROR ({fname}): 'var' parameter for T1 (in `params_T1`) has incompatible size")
675
+ return out
676
+
677
+ # Set mean_T2 (as array) from params_T2
678
+ if 'mean' not in params_T2.keys():
679
+ mean_T2 = np.array([0.0])
680
+ else:
681
+ mean_T2 = params_T2['mean']
682
+ if mean_T2 is None:
683
+ mean_T2 = np.array([0.0])
684
+ elif callable(mean_T2):
685
+ if d == 1:
686
+ mean_T2 = mean_T2(xi).reshape(-1) # replace function 'mean_T2' by its evaluation on the grid
687
+ elif d == 2:
688
+ mean_T2 = mean_T2(xxi, yyi).reshape(-1) # replace function 'mean_T2' by its evaluation on the grid
689
+ elif d == 3:
690
+ mean_T2 = mean_T2(xxi, yyi, zzi).reshape(-1) # replace function 'mean_T2' by its evaluation on the grid
691
+ else:
692
+ mean_T2 = np.asarray(mean_T2).reshape(-1)
693
+ if mean_T2.size not in (1, grid_size):
694
+ if verbose > 0:
695
+ print(f"ERROR ({fname}): 'mean' parameter for T2 (in `params_T2`) has incompatible size")
696
+ return out
697
+
698
+ # Set var_T2 (as array) from params_T2, if given
699
+ var_T2 = None
700
+ if 'var' in params_T2.keys():
701
+ var_T2 = params_T2['var']
702
+ if var_T2 is not None:
703
+ if callable(var_T2):
704
+ if d == 1:
705
+ var_T2 = var_T2(xi).reshape(-1) # replace function 'var_T2' by its evaluation on the grid
706
+ elif d == 2:
707
+ var_T2 = var_T2(xxi, yyi).reshape(-1) # replace function 'var_T2' by its evaluation on the grid
708
+ elif d == 3:
709
+ var_T2 = var_T2(xxi, yyi, zzi).reshape(-1) # replace function 'var_T2' by its evaluation on the grid
710
+ else:
711
+ var_T2 = np.asarray(var_T2).reshape(-1)
712
+ if var_T2.size not in (1, grid_size):
713
+ if verbose > 0:
714
+ print(f"ERROR ({fname}): 'var' parameter for T2 (in `params_T2`) has incompatible size")
715
+ return out
716
+
717
+ # Note: format of data (x, v) not checked !
718
+
719
+ if x is None:
720
+ if v is not None:
721
+ if verbose > 0:
722
+ print(f"ERROR ({fname}): `x` is not given (None) but `v` is given (not None)")
723
+ return out
724
+ #
725
+ else:
726
+ # Preparation for conditional case
727
+ if v is None:
728
+ if verbose > 0:
729
+ print(f"ERROR ({fname}): `x` is given (not None) but `v` is not given (None)")
730
+ return out
731
+ #
732
+ x = np.asarray(x, dtype='float').reshape(-1, d) # cast in d-dimensional array if needed
733
+ v = np.asarray(v, dtype='float').reshape(-1) # cast in 1-dimensional array if needed
734
+ if len(v) != x.shape[0]:
735
+ if verbose > 0:
736
+ print(f"ERROR ({fname}): length of `v` is not valid")
737
+ return out
738
+ #
739
+ # Compute
740
+ # indc: node index of conditioning node (nearest node),
741
+ # rounded to lower index if between two grid node and index is positive
742
+ indc_f = (x-origin)/spacing
743
+ indc = indc_f.astype(int)
744
+ indc = indc - 1 * np.all((indc == indc_f, indc > 0), axis=0)
745
+ if d == 1:
746
+ indc = 1 * indc[:, 0] # multiply by 1.0 makes a copy of the array !
747
+ elif d == 2:
748
+ indc = indc[:, 0] + dimension[0] * indc[:, 1]
749
+ elif d == 3:
750
+ indc = indc[:, 0] + dimension[0] * (indc[:, 1] + dimension[1] * indc[:, 2])
751
+ indc_unique, indc_inv = np.unique(indc, return_inverse=True)
752
+ if len(indc_unique) != len(x):
753
+ if np.any([len(np.unique(v[indc_inv==j])) > 1 for j in range(len(indc_unique))]):
754
+ if verbose > 0:
755
+ print(f'ERROR ({fname}): more than one conditioning point fall in a same grid cell and have different conditioning values')
756
+ return out
757
+ else:
758
+ if verbose > 1:
759
+ print(f'WARNING ({fname}): more than one conditioning point fall in a same grid cell with same conditioning value (consistent)')
760
+ x = np.array([x[indc_inv==j][0] for j in range(len(indc_unique))])
761
+ v = np.array([v[indc_inv==j][0] for j in range(len(indc_unique))])
762
+ #
763
+ # Number of conditioning points
764
+ npt = x.shape[0]
765
+ #
766
+ # Get index in mean_T1 for each conditioning point
767
+ x_mean_T1_grid_ind = None
768
+ if mean_T1.size == 1:
769
+ x_mean_T1_grid_ind = np.zeros(npt, dtype='int')
770
+ else:
771
+ indc_f = (x-origin)/spacing
772
+ indc = indc_f.astype(int)
773
+ indc = indc - 1 * np.all((indc == indc_f, indc > 0), axis=0)
774
+ if d == 1:
775
+ x_mean_T1_grid_ind = 1 * indc[:, 0] # multiply by 1.0 makes a copy of the array !
776
+ elif d == 2:
777
+ x_mean_T1_grid_ind = indc[:, 0] + dimension[0] * indc[:, 1]
778
+ elif d == 3:
779
+ x_mean_T1_grid_ind = indc[:, 0] + dimension[0] * (indc[:, 1] + dimension[1] * indc[:, 2])
780
+ #
781
+ # Get index in var_T1 (if not None) for each conditioning point
782
+ if var_T1 is not None:
783
+ if var_T1.size == 1:
784
+ x_var_T1_grid_ind = np.zeros(npt, dtype='int')
785
+ else:
786
+ if x_mean_T1_grid_ind is not None:
787
+ x_var_T1_grid_ind = x_mean_T1_grid_ind
788
+ else:
789
+ indc_f = (x-origin)/spacing
790
+ indc = indc_f.astype(int)
791
+ indc = indc - 1 * np.all((indc == indc_f, indc > 0), axis=0)
792
+ if d == 1:
793
+ x_var_T1_grid_ind = 1 * indc[:, 0] # multiply by 1.0 makes a copy of the array !
794
+ elif d == 2:
795
+ x_var_T1_grid_ind = indc[:, 0] + dimension[0] * indc[:, 1]
796
+ elif d == 3:
797
+ x_var_T1_grid_ind = indc[:, 0] + dimension[0] * (indc[:, 1] + dimension[1] * indc[:, 2])
798
+ #
799
+ # Get index in mean_T2 for each conditioning point
800
+ x_mean_T2_grid_ind = None
801
+ if mean_T2.size == 1:
802
+ x_mean_T2_grid_ind = np.zeros(npt, dtype='int')
803
+ else:
804
+ indc_f = (x-origin)/spacing
805
+ indc = indc_f.astype(int)
806
+ indc = indc - 1 * np.all((indc == indc_f, indc > 0), axis=0)
807
+ if d == 1:
808
+ x_mean_T2_grid_ind = 1 * indc[:, 0] # multiply by 1.0 makes a copy of the array !
809
+ elif d == 2:
810
+ x_mean_T2_grid_ind = indc[:, 0] + dimension[0] * indc[:, 1]
811
+ elif d == 3:
812
+ x_mean_T2_grid_ind = indc[:, 0] + dimension[0] * (indc[:, 1] + dimension[1] * indc[:, 2])
813
+ #
814
+ # Get index in var_T2 (if not None) for each conditioning point
815
+ if var_T2 is not None:
816
+ if var_T2.size == 1:
817
+ x_var_T2_grid_ind = np.zeros(npt, dtype='int')
818
+ else:
819
+ if x_mean_T2_grid_ind is not None:
820
+ x_var_T2_grid_ind = x_mean_T2_grid_ind
821
+ else:
822
+ indc_f = (x-origin)/spacing
823
+ indc = indc_f.astype(int)
824
+ indc = indc - 1 * np.all((indc == indc_f, indc > 0), axis=0)
825
+ if d == 1:
826
+ x_var_T2_grid_ind = 1 * indc[:, 0] # multiply by 1.0 makes a copy of the array !
827
+ elif d == 2:
828
+ x_var_T2_grid_ind = indc[:, 0] + dimension[0] * indc[:, 1]
829
+ elif d == 3:
830
+ x_var_T2_grid_ind = indc[:, 0] + dimension[0] * (indc[:, 1] + dimension[1] * indc[:, 2])
831
+ #
832
+ # Get covariance function for T1, T2 and Y, and their evaluation at 0
833
+ if cov_model_T1 is not None:
834
+ cov_func_T1 = cov_model_T1.func() # covariance function
835
+ cov0_T1 = cov_func_T1(np.zeros(d))
836
+ if cov_model_T2 is not None:
837
+ cov_func_T2 = cov_model_T2.func() # covariance function
838
+ cov0_T2 = cov_func_T2(np.zeros(d))
839
+ #
840
+ if cov_model_T1 is not None:
841
+ # Set kriging matrix for T1 (mat_T1) of order npt, "over every conditioining point"
842
+ mat_T1 = np.ones((npt, npt))
843
+ for i in range(npt-1):
844
+ # lag between x[i] and x[j], j=i+1, ..., npt-1
845
+ h = x[(i+1):] - x[i]
846
+ cov_h_T1 = cov_func_T1(h)
847
+ mat_T1[i, (i+1):npt] = cov_h_T1
848
+ mat_T1[(i+1):npt, i] = cov_h_T1
849
+ mat_T1[i, i] = cov0_T1
850
+ #
851
+ mat_T1[-1,-1] = cov0_T1
852
+ #
853
+ if var_T1 is not None:
854
+ varUpdate = np.sqrt(var_T1[x_var_T1_grid_ind]/cov0_T1)
855
+ mat_T1 = varUpdate*(mat_T1.T*varUpdate).T
856
+ #
857
+ if cov_model_T2 is not None:
858
+ # Set kriging matrix for T2 (mat_T2) of order npt, "over every conditioining point"
859
+ mat_T2 = np.ones((npt, npt))
860
+ for i in range(npt-1):
861
+ # lag between x[i] and x[j], j=i+1, ..., npt-1
862
+ h = x[(i+1):] - x[i]
863
+ cov_h_T2 = cov_func_T2(h)
864
+ mat_T2[i, (i+1):npt] = cov_h_T2
865
+ mat_T2[(i+1):npt, i] = cov_h_T2
866
+ mat_T2[i, i] = cov0_T2
867
+ #
868
+ mat_T2[-1,-1] = cov0_T2
869
+ #
870
+ if var_T2 is not None:
871
+ varUpdate = np.sqrt(var_T2[x_var_T2_grid_ind]/cov0_T2)
872
+ mat_T2 = varUpdate*(mat_T2.T*varUpdate).T
873
+
874
+ # Set (again if given) default parameter 'mean' and 'var' for T1, T2
875
+ if cov_model_T1 is not None:
876
+ params_T1['mean'] = mean_T1
877
+ params_T1['var'] = var_T1
878
+ else:
879
+ if mean_T1.size == grid_size:
880
+ params_T1['mean'] = mean_T1.reshape(*dimension[::-1])
881
+ else:
882
+ params_T1['mean'] = mean_T1 * np.ones(dimension[::-1])
883
+ if cov_model_T2 is not None:
884
+ params_T2['mean'] = mean_T2
885
+ params_T2['var'] = var_T2
886
+ else:
887
+ if mean_T2.size == grid_size:
888
+ params_T2['mean'] = mean_T2.reshape(*dimension[::-1])
889
+ else:
890
+ params_T2['mean'] = mean_T2 * np.ones(dimension[::-1])
891
+
892
+ # Set default parameter 'verbose' for params_T1, params_T2
893
+ if 'verbose' not in params_T1.keys():
894
+ params_T1['verbose'] = 0
895
+ if 'verbose' not in params_T2.keys():
896
+ params_T2['verbose'] = 0
897
+
898
+ # Initialization for output
899
+ Z = []
900
+ if full_output:
901
+ T1 = []
902
+ T2 = []
903
+ n_cond_ok = []
904
+
905
+ for ireal in range(nreal):
906
+ # Generate ireal-th realization
907
+ if verbose > 2:
908
+ print('PLURIGAUSSIANSIM: simulation {} of {}...'.format(ireal+1, nreal))
909
+ for ntry in range(ntry_max):
910
+ sim_ok = True
911
+ nhd_ok = [] # to be appended for full output...
912
+ if verbose > 3 and ntry > 0:
913
+ print(' ... new trial ({} of {}) for simulation {} of {}...'.format(ntry+1, ntry_max, ireal+1, nreal))
914
+ if x is None:
915
+ # Unconditional case
916
+ # ------------------
917
+ # Generate T1 (one real)
918
+ if cov_model_T1 is not None:
919
+ sim_T1 = multiGaussian.multiGaussianRun(cov_model_T1, dimension, spacing, origin,
920
+ mode='simulation', algo=algo_T1, output_mode='array',
921
+ **params_T1, nreal=1)
922
+ else:
923
+ sim_T1 = params_T1['mean'].reshape(1,*dimension[::-1])
924
+ # -> sim_T1: nd-array of shape
925
+ # (1, dimension) (for T1 in 1D)
926
+ # (1, dimension[1], dimension[0]) (for T1 in 2D)
927
+ # (1, dimension[2], dimension[1], dimension[0]) (for T1 in 3D)
928
+ if sim_T1 is None:
929
+ sim_ok = False
930
+ if verbose > 3:
931
+ print(' ... simulation of T1 failed')
932
+ continue
933
+ #
934
+ # Generate T2 (one real)
935
+ if cov_model_T2 is not None:
936
+ sim_T2 = multiGaussian.multiGaussianRun(cov_model_T2, dimension, spacing, origin,
937
+ mode='simulation', algo=algo_T2, output_mode='array',
938
+ **params_T2, nreal=1)
939
+ else:
940
+ sim_T2 = params_T2['mean'].reshape(1,*dimension[::-1])
941
+ # -> sim_T2: nd-array of shape
942
+ # (1, dimension) (for T2 in 1D)
943
+ # (1, dimension[1], dimension[0]) (for T2 in 2D)
944
+ # (1, dimension[2], dimension[1], dimension[0]) (for T2 in 3D)
945
+ if sim_T2 is None:
946
+ sim_ok = False
947
+ if verbose > 3:
948
+ print(' ... simulation of T2 failed')
949
+ continue
950
+ #
951
+ else:
952
+ # Conditional case
953
+ # ----------------
954
+ v_T = np.zeros((npt, 2))
955
+ # Initialize: unconditional simulation of T1 at x (values in v_T[:,0])
956
+ ind = np.random.permutation(npt)
957
+ for j, k in enumerate(ind):
958
+ if cov_model_T1 is not None:
959
+ # Simulate value at x[k] (= x[ind[j]]), conditionally to the previous ones
960
+ # Solve the kriging system (for T1)
961
+ try:
962
+ w = np.linalg.solve(
963
+ mat_T1[ind[:j], :][:, ind[:j]], # kriging matrix
964
+ mat_T1[ind[:j], ind[j]], # second member
965
+ )
966
+ except:
967
+ sim_ok = False
968
+ break
969
+
970
+ # Mean (kriged) value at x[k]
971
+ mu_T1_k = mean_T1[x_mean_T1_grid_ind[k]] + (v_T[ind[:j], 0] - mean_T1[x_mean_T1_grid_ind[ind[:j]]]).dot(w)
972
+ # Standard deviation (of kriging) at x[k]
973
+ std_T1_k = np.sqrt(np.maximum(0, cov0_T1 - np.dot(w, mat_T1[ind[:j], ind[j]])))
974
+ # Draw value in N(mu_T1_k, std_T1_k^2)
975
+ v_T[k, 0] = np.random.normal(loc=mu_T1_k, scale=std_T1_k)
976
+ else:
977
+ v_T[k, 0] = mean_T1[x_mean_T1_grid_ind[k]]
978
+
979
+ if not sim_ok:
980
+ sim_ok = False
981
+ if verbose > 3:
982
+ print(' ... unable to solve kriging system (for T1, initialization)')
983
+ continue
984
+
985
+ # Initialize: unconditional simulation of T2 at x (values in v_T[:,1])
986
+ ind = np.random.permutation(npt)
987
+ for j, k in enumerate(ind):
988
+ if cov_model_T2 is not None:
989
+ # Simulate value at x[k] (= x[ind[j]]), conditionally to the previous ones
990
+ # Solve the kriging system (for T2)
991
+ try:
992
+ w = np.linalg.solve(
993
+ mat_T2[ind[:j], :][:, ind[:j]], # kriging matrix
994
+ mat_T2[ind[:j], ind[j]], # second member
995
+ )
996
+ except:
997
+ sim_ok = False
998
+ break
999
+
1000
+ # Mean (kriged) value at x[k]
1001
+ mu_T2_k = mean_T2[x_mean_T2_grid_ind[k]] + (v_T[ind[:j], 1] - mean_T2[x_mean_T2_grid_ind[ind[:j]]]).dot(w)
1002
+ # Standard deviation (of kriging) at x[k]
1003
+ std_T2_k = np.sqrt(np.maximum(0, cov0_T2 - np.dot(w, mat_T2[ind[:j], ind[j]])))
1004
+ # Draw value in N(mu_T2_k, std_T2_k^2)
1005
+ v_T[k, 1] = np.random.normal(loc=mu_T2_k, scale=std_T2_k)
1006
+ else:
1007
+ v_T[k, 1] = mean_T2[x_mean_T2_grid_ind[k]]
1008
+
1009
+ if not sim_ok:
1010
+ sim_ok = False
1011
+ if verbose > 3:
1012
+ print(' ... unable to solve kriging system (for T2, initialization)')
1013
+ continue
1014
+
1015
+ # Update simulated values v_T at x using Metropolis-Hasting (MH) algorithm
1016
+ v_T_k_new = np.zeros(2)
1017
+ stop_mh = False
1018
+ for nit in range(mh_iter_max):
1019
+ #hd_ok = np.array([flag_value(v_T[k, 0], v_T[k, 1]) == v[k] for k in range(npt)])
1020
+ hd_ok = flag_value(v_T[:, 0], v_T[:, 1]) == v
1021
+ nhd_ok.append(np.sum(hd_ok))
1022
+ if nit >= mh_iter_min:
1023
+ if nhd_ok[-1] == npt:
1024
+ stop_mh = True
1025
+ break
1026
+ else:
1027
+ # Set acceptation probability for bad case
1028
+ p_accept = accept_init * np.power(1.0 - nit/mh_iter_min, accept_pow)
1029
+ if verbose > 4:
1030
+ print(' ... sim {} of {}: MH iter {} of {}, {}...'.format(ireal+1, nreal, nit+1, mh_iter_min, mh_iter_max))
1031
+ ind = np.random.permutation(npt)
1032
+ for k in ind:
1033
+ if nit >= mh_iter_min and hd_ok[k]:
1034
+ #print('skip')
1035
+ continue
1036
+ #
1037
+ # Sequence of indexes without k
1038
+ indmat = np.hstack((np.arange(k), np.arange(k+1, npt)))
1039
+ # Simulate possible new value v_T_new at x[k], conditionally to all the ohter ones
1040
+ #
1041
+ if cov_model_T1 is not None:
1042
+ # Solve the kriging system for T1
1043
+ try:
1044
+ w = np.linalg.solve(
1045
+ mat_T1[indmat, :][:, indmat], # kriging matrix
1046
+ mat_T1[indmat, k], # second member
1047
+ )
1048
+ except:
1049
+ sim_ok = False
1050
+ if verbose > 3:
1051
+ print(' ... unable to solve kriging system (for T1)')
1052
+ break
1053
+ #
1054
+ # Mean (kriged) value at x[k]
1055
+ mu_T1_k = mean_T1[x_mean_T1_grid_ind[k]] + (v_T[indmat, 0] - mean_T1[x_mean_T1_grid_ind[indmat]]).dot(w)
1056
+ # Standard deviation (of kriging) at x[k]
1057
+ std_T1_k = np.sqrt(np.maximum(0, cov0_T1 - np.dot(w, mat_T1[indmat, k])))
1058
+ # Draw value in N(mu, std^2)
1059
+ v_T_k_new[0] = np.random.normal(loc=mu_T1_k, scale=std_T1_k)
1060
+ else:
1061
+ v_T_k_new[0] = mean_T1[x_mean_T1_grid_ind[k]]
1062
+ #
1063
+ # Solve the kriging system for T2
1064
+ if cov_model_T2 is not None:
1065
+ try:
1066
+ w = np.linalg.solve(
1067
+ mat_T2[indmat, :][:, indmat], # kriging matrix
1068
+ mat_T2[indmat, k], # second member
1069
+ )
1070
+ except:
1071
+ sim_ok = False
1072
+ if verbose > 3:
1073
+ print(' ... unable to solve kriging system (for T2)')
1074
+ break
1075
+ #
1076
+ # Mean (kriged) value at x[k]
1077
+ mu_T2_k = mean_T2[x_mean_T2_grid_ind[k]] + (v_T[indmat, 1] - mean_T2[x_mean_T2_grid_ind[indmat]]).dot(w)
1078
+ # Standard deviation (of kriging) at x[k]
1079
+ std_T2_k = np.sqrt(np.maximum(0, cov0_T2 - np.dot(w, mat_T2[indmat, k])))
1080
+ # Draw value in N(mu, std^2)
1081
+ v_T_k_new[1] = np.random.normal(loc=mu_T2_k, scale=std_T2_k)
1082
+ else:
1083
+ v_T_k_new[1] = mean_T2[x_mean_T2_grid_ind[k]]
1084
+ #
1085
+ # Accept or not the new candidate
1086
+ if flag_value(v_T_k_new[0], v_T_k_new[1]) == v[k]:
1087
+ # Accept the new candidate
1088
+ v_T[k] = v_T_k_new
1089
+ elif nit < mh_iter_min and np.random.random() < p_accept:
1090
+ # Accept the new candidate
1091
+ v_T[k] = v_T_k_new
1092
+
1093
+ if not sim_ok:
1094
+ break
1095
+
1096
+ if not sim_ok:
1097
+ continue
1098
+
1099
+ if not stop_mh:
1100
+ hd_ok = flag_value(v_T[:, 0], v_T[:, 1]) == v
1101
+ nhd_ok.append(np.sum(hd_ok))
1102
+ if nhd_ok[-1] != npt:
1103
+ # sim_ok kept to True
1104
+ if verbose > 3:
1105
+ print(' ... conditioning failed')
1106
+ if ntry < ntry_max - 1 or not retrieve_real_anyway:
1107
+ continue
1108
+
1109
+ # Generate T1 conditional to (x, v_T[:, 0]) (one real)
1110
+ if cov_model_T1 is not None:
1111
+ sim_T1 = multiGaussian.multiGaussianRun(cov_model_T1, dimension, spacing, origin, x=x, v=v_T[:, 0],
1112
+ mode='simulation', algo=algo_T1, output_mode='array',
1113
+ **params_T1, nreal=1)
1114
+ else:
1115
+ sim_T1 = params_T1['mean'].reshape(1,*dimension[::-1])
1116
+ # -> sim_T1: nd-array of shape
1117
+ # (1, dimension) (for T1 in 1D)
1118
+ # (1, dimension[1], dimension[0]) (for T1 in 2D)
1119
+ # (1, dimension[2], dimension[1], dimension[0]) (for T1 in 3D)
1120
+ if sim_T1 is None:
1121
+ sim_ok = False
1122
+ if verbose > 3:
1123
+ print(' ... conditional simulation of T1 failed')
1124
+ continue
1125
+ #
1126
+ # Generate T2 conditional to (x, v_T[:, 1]) (one real)
1127
+ if cov_model_T2 is not None:
1128
+ sim_T2 = multiGaussian.multiGaussianRun(cov_model_T2, dimension, spacing, origin, x=x, v=v_T[:, 1],
1129
+ mode='simulation', algo=algo_T2, output_mode='array',
1130
+ **params_T2, nreal=1)
1131
+ else:
1132
+ sim_T2 = params_T2['mean'].reshape(1,*dimension[::-1])
1133
+ # -> sim_T2: nd-array of shape
1134
+ # (1, dimension) (for T2 in 1D)
1135
+ # (1, dimension[1], dimension[0]) (for T2 in 2D)
1136
+ # (1, dimension[2], dimension[1], dimension[0]) (for T2 in 3D)
1137
+ if sim_T2 is None:
1138
+ sim_ok = False
1139
+ if verbose > 3:
1140
+ print(' ... conditional simulation of T2 failed')
1141
+ continue
1142
+
1143
+ # Generate Z (one real)
1144
+ if sim_ok:
1145
+ if x is not None:
1146
+ if nhd_ok[-1] != npt:
1147
+ if not retrieve_real_anyway:
1148
+ break
1149
+ else:
1150
+ if verbose > 1:
1151
+ print(f'WARNING ({fname}): realization does not honoured all data, but retrieved anyway')
1152
+ Z_real = flag_value(sim_T1[0], sim_T2[0])
1153
+ Z.append(Z_real)
1154
+ if full_output:
1155
+ T1.append(sim_T1[0])
1156
+ T2.append(sim_T2[0])
1157
+ n_cond_ok.append(np.asarray(nhd_ok))
1158
+ break
1159
+
1160
+ # Get Z
1161
+ if verbose > 1 and len(Z) < nreal:
1162
+ print(f'WARNING ({fname}): some realization failed (missing)')
1163
+ Z = np.asarray(Z).reshape(len(Z), *np.atleast_1d(dimension)[::-1])
1164
+
1165
+ if full_output:
1166
+ T1 = np.asarray(T1).reshape(len(T1), *np.atleast_1d(dimension)[::-1])
1167
+ T2 = np.asarray(T2).reshape(len(T2), *np.atleast_1d(dimension)[::-1])
1168
+ return Z, T1, T2, n_cond_ok
1169
+ else:
1170
+ return Z
1171
+ # ----------------------------------------------------------------------------