geone 1.3.0__py313-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/multiGaussian.py ADDED
@@ -0,0 +1,388 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # -------------------------------------------------------------------------
5
+ # Python module: 'multiGaussian.py'
6
+ # author: Julien Straubhaar
7
+ # date: may-2022
8
+ # -------------------------------------------------------------------------
9
+
10
+ """
11
+ Module for multi-Gaussian simulation and estimation in 1D, 2D and 3D,
12
+ based on functions in other geone modules (wrapper).
13
+ """
14
+
15
+ import numpy as np
16
+ import inspect
17
+ from geone import covModel as gcm
18
+ from geone import img
19
+ from geone import geosclassicinterface as gci
20
+ from geone import grf
21
+
22
+ # ============================================================================
23
+ class MultiGaussianError(Exception):
24
+ """
25
+ Custom exception related to `multiGaussian` module.
26
+ """
27
+ pass
28
+ # ============================================================================
29
+
30
+ # ----------------------------------------------------------------------------
31
+ def multiGaussianRun(
32
+ cov_model,
33
+ dimension, spacing=None, origin=None,
34
+ mode='simulation',
35
+ algo='fft',
36
+ output_mode='img',
37
+ retrieve_warnings=False,
38
+ verbose=2,
39
+ use_multiprocessing=False,
40
+ logger=None,
41
+ **kwargs):
42
+ """
43
+ Runs multi-Gaussian simulation or estimation.
44
+
45
+ Wrapper of other functions, the space dimension (1, 2, or 3) is detected
46
+ (from the parameter `dimension`), and the method is selected according to
47
+ the parameters `mode` and `algo`. Moreover, the type of output can be
48
+ specified (parameter `output_mode`).
49
+
50
+ Parameters
51
+ ----------
52
+ cov_model : :class:`geone.covModel.CovModel<d>D`
53
+ covariance model in 1D or 2D or 3D
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
+ note : this parameter determines the space dimension (1, 2, or 3)
63
+
64
+ spacing : [sequence of] float(s), optional
65
+ cell size along each axis, for simulation in:
66
+
67
+ - 1D: `spacing=sx`
68
+ - 2D: `spacing=(sx, sy)`
69
+ - 3D: `spacing=(sx, sy, sz)`
70
+
71
+ by default (`None`): 1.0 along each axis
72
+
73
+ origin : [sequence of] float(s), optional
74
+ origin of the grid ("corner of the first cell"), for simulation in:
75
+
76
+ - 1D: `origin=ox`
77
+ - 2D: `origin=(ox, oy)`
78
+ - 3D: `origin=(ox, oy, oz)`
79
+
80
+ by default (`None`): 0.0 along each axis
81
+
82
+ mode : str {'simulation', 'estimation'}, default: 'simulation'
83
+ mode of computation:
84
+
85
+ - `mode='simulation'`: generates multi-Gaussian simulations
86
+ - `mode='estimation'`: computes multi-Gaussian estimation (and st. dev.)
87
+
88
+ algo : str {'fft', 'classic', 'classic_old'}, default: 'fft'
89
+ defines the algorithm used:
90
+
91
+ - `algo='fft'`: algorithm based on circulant embedding and FFT, function \
92
+ called for <d>D (d = 1, 2, or 3):
93
+ - 'geone.grf.grf<d>D', `if mode='simulation'`
94
+ - 'geone.grf.krige<d>D', `if mode='estimation'`
95
+ - `algo='classic'`: "classic" algorithm, based on the resolution of \
96
+ kriging system considering points in a search ellipsoid, function called:
97
+ - 'geone.geoscalassicinterface.simulate', `if mode='simulation'`
98
+ - 'geone.geoscalassicinterface.estimate', `if mode='estimation'`
99
+ - `algo='classic_old'`: "classic" algorithm (old version), \
100
+ based on the resolution of kriging system considering points in a search \
101
+ ellipsoid, function called for <d>D (d = 1, 2, or 3):
102
+ - 'geone.geoscalassicinterface.simulate<d>D', `if mode='simulation'`
103
+ - 'geone.geoscalassicinterface.estimate<d>D', `if mode='estimation'`
104
+
105
+ output_mode : str {'array', 'img'}, default: 'img'
106
+ defines the type of output returned (see below)
107
+
108
+ retrieve_warnings : bool, default: False
109
+ indicates if the warnings encountered during the run are retrieved in
110
+ output (`True`) or not (`False`) (see below)
111
+
112
+ verbose : int, default: 2
113
+ verbose mode, higher implies more printing (info)
114
+
115
+ use_multiprocessing : bool, default: False
116
+ indicates if multiprocessing is used in the case:
117
+ `algo='classic_old'`, `mode='simulation'`);
118
+ if `use_multiprocessing=True`, the function
119
+ `geone.geoscalassicinterface.simulate<d>D_mp` is used instead of
120
+ `geone.geoscalassicinterface.simulate<d>D`;
121
+ in other cases `use_multiprocessing` is ignored
122
+
123
+ logger : :class:`logging.Logger`, optional
124
+ logger (see package `logging`)
125
+ if specified, messages are written via `logger` (no print)
126
+
127
+ kwargs : dict
128
+ keyword arguments (additional parameters) to be passed to the function
129
+ that is called (according to `algo` and space dimension);
130
+ note: argument `mask` can also be used with `algo='fft'`; in this case
131
+ the mask is applied afterward
132
+
133
+ Returns
134
+ -------
135
+ output : ndarray or :class:`geone.img.Img`
136
+ - if `output_mode='array'`: output is a ndarray of shape
137
+ * for 1D:
138
+ * (1, nx) if `mode='estimation'` with kriging estimate only
139
+ * (2, nx) if `mode='estimation'` with kriging estimate and st. dev.
140
+ * (nreal, nx) if `mode='simulation'`
141
+ * for 2D:
142
+ * (1, ny, nx) if `mode='estimation'` with kriging estimate only
143
+ * (2, ny, nx) if `mode='estimation'` with kriging estimate and st. dev.
144
+ * (nreal, ny, nx) if `mode='simulation'`
145
+ * for 3D:
146
+ * (1, nz, ny, nx) if `mode='estimation'` with krig. est. only
147
+ * (2, nz, ny, nx) if `mode='estimation'` with krig. est. and st. dev.
148
+ * (nreal, nz, ny, nx) if `mode='simulation'`
149
+ - if `output_mode='img'`: output is an instance of :class:`geone.img.Img` \
150
+ an image with `output.nv` variables:
151
+ - `output.nv=1` if `mode='estimation'` with kriging estimate only
152
+ - `output.nv=2` if `mode='estimation'` with krig. est. and st. dev.
153
+ - `output.nv=nreal` if `mode='simulation'`
154
+
155
+ warnings : list of strs, optional
156
+ list of distinct warnings encountered (can be empty) during the run
157
+ (no warning (empty list) if `algo='fft'`);
158
+ returned if `retrieve_warnings=True`
159
+ """
160
+ fname = 'multiGaussianRun'
161
+
162
+ if mode.lower() not in ('simulation', 'estimation'):
163
+ err_msg = f"{fname}: `mode` invalid, should be 'simulation' or 'estimation' (default)"
164
+ if logger: logger.error(err_msg)
165
+ raise MultiGaussianError(err_msg)
166
+
167
+ if algo.lower() not in ('fft', 'classic', 'classic_old'):
168
+ err_msg = f"{fname}: `algo` invalid, should be 'fft' (default) or 'classic' or 'classic_old'"
169
+ if logger: logger.error(err_msg)
170
+ raise MultiGaussianError(err_msg)
171
+
172
+ if output_mode.lower() not in ('array', 'img'):
173
+ err_msg = f"{fname}: `output_mode` invalid, should be 'array' or 'img' (default)"
174
+ if logger: logger.error(err_msg)
175
+ raise MultiGaussianError(err_msg)
176
+
177
+ # Convert strings in lowercase
178
+ mode = mode.lower()
179
+ algo = algo.lower()
180
+ output_mode = output_mode.lower()
181
+
182
+ # Set space dimension: d
183
+ if hasattr(dimension, '__len__'):
184
+ d = len(dimension)
185
+ else:
186
+ # assume dimension is an int, nx
187
+ d = 1
188
+
189
+ if d not in (1, 2, 3):
190
+ err_msg = f'{fname}: space dimension not valid'
191
+ if logger: logger.error(err_msg)
192
+ raise MultiGaussianError(err_msg)
193
+
194
+ # Check (or set) argument 'spacing'
195
+ if spacing is None:
196
+ spacing = tuple(np.ones(d))
197
+ else:
198
+ if hasattr(spacing, '__len__') and len(spacing) != d:
199
+ err_msg = f'{fname}: `spacing` of incompatible length'
200
+ if logger: logger.error(err_msg)
201
+ raise MultiGaussianError(err_msg)
202
+
203
+ # Check (or set) argument 'origin'
204
+ if origin is None:
205
+ origin = tuple(np.zeros(d))
206
+ else:
207
+ if hasattr(origin, '__len__') and len(origin) != d:
208
+ err_msg = f'{fname}: `origin` of incompatible length'
209
+ if logger: logger.error(err_msg)
210
+ raise MultiGaussianError(err_msg)
211
+
212
+ # Check dimension of cov_model wrt. space dimension
213
+ if algo == 'classic_indicator' or algo == 'classic_indicator_ind':
214
+ cov_model_list = list(np.asarray(cov_model).reshape(-1))
215
+ for cm in cov_model_list:
216
+ if not isinstance(cm, gcm.CovModel1D):
217
+ if cm.__class__.__name__ != f'CovModel{d}D':
218
+ err_msg = f'{fname}: `cov_model` dimension is incompatible with `dimension`'
219
+ if logger: logger.error(err_msg)
220
+ raise MultiGaussianError(err_msg)
221
+ else:
222
+ if not isinstance(cov_model, gcm.CovModel1D):
223
+ if cov_model.__class__.__name__ != f'CovModel{d}D':
224
+ err_msg = f'{fname}: `cov_model` dimension is incompatible with `dimension`'
225
+ if logger: logger.error(err_msg)
226
+ raise MultiGaussianError(err_msg)
227
+
228
+ if algo == 'fft':
229
+ if mode == 'estimation':
230
+ if d == 1:
231
+ run_f = grf.krige1D
232
+ elif d == 2:
233
+ run_f = grf.krige2D
234
+ elif d == 3:
235
+ run_f = grf.krige3D
236
+ elif mode == 'simulation':
237
+ if d == 1:
238
+ run_f = grf.grf1D
239
+ elif d == 2:
240
+ run_f = grf.grf2D
241
+ elif d == 3:
242
+ run_f = grf.grf3D
243
+
244
+ # Filter unused keyword arguments
245
+ run_f_set_of_all_args = set([val.name for val in inspect.signature(run_f).parameters.values()])
246
+ kwargs_common_keys = run_f_set_of_all_args.intersection(kwargs.keys())
247
+ kwargs_new = {key: kwargs[key] for key in kwargs_common_keys}
248
+ kwargs_unexpected_keys = set(kwargs.keys()).difference(run_f_set_of_all_args)
249
+ apply_mask = False # default
250
+ if kwargs_unexpected_keys:
251
+ if 'mask' in kwargs_unexpected_keys:
252
+ kwargs_unexpected_keys.remove('mask')
253
+ if kwargs['mask'] is not None:
254
+ try:
255
+ mask = np.asarray(kwargs['mask']).reshape(np.atleast_1d(dimension)[::-1]).astype('bool')
256
+ except:
257
+ err_msg = f'{fname}: `mask` invalid'
258
+ if logger: logger.error(err_msg)
259
+ raise MultiGaussianError(err_msg)
260
+
261
+ apply_mask = True # to apply mask afterward
262
+
263
+ if kwargs_unexpected_keys:
264
+ # set kwargs_unexpected_keys is not empty
265
+ if verbose > 0:
266
+ s = "`, `".join(kwargs_unexpected_keys)
267
+ if logger:
268
+ logger.warning(f"{fname}: unexpected keyword arguments (`{s}`) passed to function `{run_f.__module__}.{run_f.__name__}` were ignored")
269
+ else:
270
+ print(f"{fname}: WARNING: unexpected keyword arguments (`{s}`) passed to function `{run_f.__module__}.{run_f.__name__}` were ignored")
271
+
272
+ try:
273
+ output = run_f(cov_model, dimension, spacing=spacing, origin=origin, verbose=verbose, logger=logger, **kwargs_new)
274
+ except Exception as exc:
275
+ err_msg = f'{fname}: computation failed'
276
+ if logger: logger.error(err_msg)
277
+ raise MultiGaussianError(err_msg) from exc
278
+
279
+ # -> if mode = 'simulation':
280
+ # output is an array with (d+1) dimension (axis 0 corresponds to realizations)
281
+ # -> if mode = 'estimation':
282
+ # output is an array (kriging estimate only) or a 2-tuple of array (kriging estimate and standard deviation);
283
+ # each array with d dimension
284
+ if mode == 'estimation':
285
+ if isinstance(output, tuple):
286
+ output = np.asarray(output)
287
+ else:
288
+ output = output[np.newaxis, :]
289
+ if apply_mask:
290
+ output[:, ~mask] = np.nan
291
+ if output_mode == 'img':
292
+ output = img.Img(
293
+ *np.hstack((np.atleast_1d(dimension), np.ones(3-d, dtype='int'))),
294
+ *np.hstack((np.atleast_1d(spacing), np.ones(3-d))),
295
+ *np.hstack((np.atleast_1d(origin), np.zeros(3-d))),
296
+ nv=output.shape[0], val=output,
297
+ logger=logger)
298
+ warnings = [] # no warning available if algo = 'fft'
299
+
300
+ elif algo in ('classic'):
301
+ if mode == 'estimation':
302
+ run_f = gci.estimate
303
+ elif mode == 'simulation':
304
+ run_f = gci.simulate
305
+
306
+ # Filter unused keyword arguments
307
+ run_f_set_of_all_args = set([val.name for val in inspect.signature(run_f).parameters.values()])
308
+ kwargs_common_keys = run_f_set_of_all_args.intersection(kwargs.keys())
309
+ kwargs_new = {key: kwargs[key] for key in kwargs_common_keys}
310
+ kwargs_unexpected_keys = set(kwargs.keys()).difference(run_f_set_of_all_args)
311
+ if kwargs_unexpected_keys:
312
+ # set kwargs_unexpected_keys is not empty
313
+ if verbose > 0:
314
+ s = "`, `".join(kwargs_unexpected_keys)
315
+ if logger:
316
+ logger.warning(f"{fname}: unexpected keyword arguments (`{s}`) passed to function `{run_f.__module__}.{run_f.__name__}` were ignored")
317
+ else:
318
+ print(f"{fname}: WARNING: unexpected keyword arguments (`{s}`) passed to function `{run_f.__module__}.{run_f.__name__}` were ignored")
319
+
320
+ try:
321
+ output = run_f(cov_model, dimension, spacing=spacing, origin=origin, verbose=verbose, logger=logger, **kwargs_new)
322
+ except Exception as exc:
323
+ err_msg = f'{fname}: computation failed'
324
+ if logger: logger.error(err_msg)
325
+ raise MultiGaussianError(err_msg) from exc
326
+
327
+ warnings = output['warnings']
328
+ output = output['image']
329
+ if output_mode == 'array':
330
+ # get the array of value and remove extra dimension for 1D and 2D
331
+ output = output.val.reshape(-1, *np.atleast_1d(dimension)[::-1])
332
+
333
+ elif algo in ('classic_old'):
334
+ if mode == 'estimation':
335
+ if d == 1:
336
+ run_f = gci.estimate1D
337
+ elif d == 2:
338
+ run_f = gci.estimate2D
339
+ elif d == 3:
340
+ run_f = gci.estimate3D
341
+ elif mode == 'simulation':
342
+ if use_multiprocessing:
343
+ if d == 1:
344
+ run_f = gci.simulate1D_mp
345
+ elif d == 2:
346
+ run_f = gci.simulate2D_mp
347
+ elif d == 3:
348
+ run_f = gci.simulate3D_mp
349
+ else:
350
+ if d == 1:
351
+ run_f = gci.simulate1D
352
+ elif d == 2:
353
+ run_f = gci.simulate2D
354
+ elif d == 3:
355
+ run_f = gci.simulate3D
356
+
357
+ # Filter unused keyword arguments
358
+ run_f_set_of_all_args = set([val.name for val in inspect.signature(run_f).parameters.values()])
359
+ kwargs_common_keys = run_f_set_of_all_args.intersection(kwargs.keys())
360
+ kwargs_new = {key: kwargs[key] for key in kwargs_common_keys}
361
+ kwargs_unexpected_keys = set(kwargs.keys()).difference(run_f_set_of_all_args)
362
+ if kwargs_unexpected_keys:
363
+ # set kwargs_unexpected_keys is not empty
364
+ if verbose > 0:
365
+ s = "', '".join(kwargs_unexpected_keys)
366
+ if logger:
367
+ logger.warning(f"{fname}: unexpected keyword arguments (`{s}`) passed to function '{run_f.__module__}.{run_f.__name__}' were ignored")
368
+ else:
369
+ print(f"{fname}: WARNING: unexpected keyword arguments (`{s}`) passed to function '{run_f.__module__}.{run_f.__name__}' were ignored")
370
+
371
+ try:
372
+ output = run_f(cov_model, dimension, spacing=spacing, origin=origin, verbose=verbose, logger=logger, **kwargs_new)
373
+ except Exception as exc:
374
+ err_msg = f'{fname}: computation failed'
375
+ if logger: logger.error(err_msg)
376
+ raise MultiGaussianError(err_msg) from exc
377
+
378
+ warnings = output['warnings']
379
+ output = output['image']
380
+ if output_mode == 'array':
381
+ # get the array of value and remove extra dimension for 1D and 2D
382
+ output = output.val.reshape(-1, *np.atleast_1d(dimension)[::-1])
383
+
384
+ if retrieve_warnings:
385
+ return output, warnings
386
+ else:
387
+ return output
388
+ # ----------------------------------------------------------------------------