random-fields 1.0.1__py3-none-any.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.
@@ -0,0 +1 @@
1
+ from .__version__ import __version__
@@ -0,0 +1,3 @@
1
+ __title__ = "Random Fields Generator"
2
+ __version__ = "1.0.1"
3
+ __author__ = "STEM team"
@@ -0,0 +1,96 @@
1
+ from enum import Enum
2
+ from typing import List
3
+ import numpy as np
4
+ import gstools as gs
5
+
6
+
7
+ class ModelName(Enum):
8
+ """
9
+ Name of the model to be used. Options are: "Gaussian", "Exponential", "Matern", "Linear"
10
+ """
11
+ Gaussian = gs.Gaussian
12
+ Exponential = gs.Exponential
13
+ Matern = gs.Matern
14
+ Linear = gs.Linear
15
+
16
+
17
+ class RandomFields:
18
+ """
19
+ Generate random fields
20
+ """
21
+
22
+ def __init__(self, model_name: ModelName, n_dim: int,
23
+ mean: float, variance: float,
24
+ v_scale_fluctuation: float, anisotropy: List[float], angle: List[float],
25
+ seed: int = 14, v_dim: int = 1) -> None:
26
+ """
27
+ Initialise generation of random fields
28
+
29
+ Parameters:
30
+ -----------
31
+ model_name: str
32
+ Name of the model to be used. Options are: "Gaussian", "Exponential", "Matern", "Linear"
33
+ n_dim: int
34
+ The dimension of the random field
35
+ mean: float
36
+ The mean of the random field
37
+ variance: float
38
+ The variance of the random field
39
+ v_scale_fluctuation: float
40
+ The vertical scale of fluctuation of the random field
41
+ anisotropy: list
42
+ The anisotropy of the random field (per dimension)
43
+ angle: list
44
+ The angle of the random field (per dimension)
45
+ seed: int
46
+ The seed number for the random number generator
47
+ v_dim: int
48
+ The dimension of the vertical scale of fluctuation
49
+ """
50
+ # initialise model
51
+ if model_name.name not in ModelName.__members__:
52
+ raise ValueError(f'Model name: {model_name} is not supported')
53
+
54
+ if n_dim not in [1, 2, 3]:
55
+ raise ValueError('Only 1, 2, 3 dimensions are supported')
56
+
57
+ aux_keys = [anisotropy, angle]
58
+ for key in aux_keys:
59
+ if len(key) != n_dim - 1:
60
+ raise ValueError(f'Number of dimensions: {n_dim} does not match number of\
61
+ random field properties: {len(variance)}. It should be {n_dim - 1}.')
62
+
63
+ self.random_field_model = model_name.value
64
+ self.n_dim = n_dim
65
+ self.seed = seed
66
+ self.mean = mean
67
+ self.variance = variance
68
+ self.vertical_scale_fluctuation = v_scale_fluctuation
69
+ self.anisotropy = anisotropy
70
+ self.angle = angle
71
+ self.v_dim = v_dim
72
+ self.random_field = None
73
+
74
+ def generate(self, nodes: np.ndarray) -> None:
75
+ """
76
+ Generate random field
77
+
78
+ Parameters:
79
+ ------------
80
+ nodes: list
81
+ The nodes of the random field
82
+ """
83
+ # check dimensions of nodes agrees with dimensions of model
84
+ if nodes.shape[1] != self.n_dim:
85
+ raise ValueError(f'Dimensions of nodes: {nodes.shape[1]} do not match dimensions of model: {self.n_dim}')
86
+
87
+ # scale of fluctuation
88
+ scale_fluctuation = np.ones(self.n_dim) * self.vertical_scale_fluctuation
89
+
90
+ # apply the anisotropy to the other dimensions
91
+ mask = np.arange(len(scale_fluctuation)) != self.v_dim
92
+ scale_fluctuation[mask] = scale_fluctuation[mask] * self.anisotropy
93
+
94
+ model = self.random_field_model(dim=self.n_dim, var=self.variance, len_scale=scale_fluctuation, angles=self.angle)
95
+ self.random_field = gs.SRF(model, mean=self.mean, seed=self.seed)
96
+ self.random_field(nodes.T)
random_fields/utils.py ADDED
@@ -0,0 +1,97 @@
1
+ import os
2
+ import numpy as np
3
+ import matplotlib.pylab as plt
4
+ import matplotlib as mpl
5
+
6
+
7
+ def plot3D(coordinates: list, random_field: list, title: str = "Random Field",
8
+ output_folder = "./", output_name: str = "random_field.png"):
9
+ """
10
+ Plot 3D random field
11
+
12
+ Parameters
13
+ ----------
14
+ coordinates : list
15
+ List of coordinates of the random field
16
+ random_field : list
17
+ List of random field values
18
+ title : str
19
+ Title of the plot
20
+ output_folder : str
21
+ Output folder
22
+ output_name : str
23
+ Output fine name
24
+ """
25
+ # create output folder
26
+ if not os.path.exists(output_folder):
27
+ os.makedirs(output_folder)
28
+
29
+ # make plot
30
+ fig = plt.figure(1, figsize=(6, 5))
31
+ ax = fig.add_subplot(projection='3d')
32
+ ax.set_position([0.1, 0.1, 0.8, 0.8])
33
+ # rotate the axes so that y is vertical
34
+ ax.view_init(125, -90, 0)
35
+
36
+ vmin = np.min([np.min(aux) for aux in random_field])
37
+ vmax = np.max([np.max(aux) for aux in random_field])
38
+
39
+ for i, coord in enumerate(coordinates):
40
+ x, y, z = coord[:, 0], coord[:, 1], coord[:, 2]
41
+ ax.scatter(x, y, z, c=random_field[i], vmin=vmin, vmax=vmax, cmap="viridis", edgecolors=None, marker="s")
42
+
43
+ ax.set_xlabel('x coordinate')
44
+ ax.set_ylabel('y coordinate')
45
+ ax.set_zlabel('z coordinate')
46
+
47
+ cax = ax.inset_axes([1.1, 0., 0.05, 1])
48
+ norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
49
+ fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap="viridis"), ax=ax, cax=cax)
50
+ fig.suptitle(title)
51
+ plt.savefig(os.path.join(output_folder, output_name))
52
+ plt.close()
53
+
54
+
55
+ def plot2D(coordinates: list, random_field: list, title: str = "Random Field",
56
+ output_folder = "./", output_name: str = "random_field.png"):
57
+ """
58
+ Plot 2D random field
59
+
60
+ Parameters
61
+ ----------
62
+ coordinates : list
63
+ List of coordinates of the random field
64
+ random_field : list
65
+ List of random field values
66
+ title : str
67
+ Title of the plot
68
+ output_folder : str
69
+ Output folder
70
+ output_name : str
71
+ Output fine name
72
+ """
73
+
74
+ # create output folder
75
+ if not os.path.exists(output_folder):
76
+ os.makedirs(output_folder)
77
+
78
+ # make plot
79
+ fig, ax = plt.subplots(figsize=(6, 5))
80
+ ax.set_position([0.1, 0.1, 0.7, 0.8])
81
+
82
+ vmin = np.min([np.min(aux) for aux in random_field])
83
+ vmax = np.max([np.max(aux) for aux in random_field])
84
+
85
+ for i, coord in enumerate(coordinates):
86
+ x, y = coord[:, 0], coord[:, 1]
87
+ ax.scatter(x, y, c=random_field[i], vmin=vmin, vmax=vmax, cmap="viridis", edgecolors=None, marker="s")
88
+
89
+ ax.set_xlabel('x coordinate')
90
+ ax.set_ylabel('y coordinate')
91
+
92
+ cax = ax.inset_axes([1.1, 0., 0.05, 1])
93
+ norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
94
+ fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap="viridis"), ax=ax, cax=cax)
95
+ fig.suptitle(title)
96
+ plt.savefig(os.path.join(output_folder, output_name))
97
+ plt.close()
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023-2025, StemVibrations
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.2
2
+ Name: random_fields
3
+ Version: 1.0.1
4
+ Summary: Random fields on unstructured grids
5
+ Author: attr: random_fields.__author__
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: BSD License
8
+ Classifier: Operating System :: Microsoft :: Windows
9
+ Classifier: Operating System :: Unix
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: numpy>=2.0.2
14
+ Requires-Dist: matplotlib>=3.9.3
15
+ Requires-Dist: gstools>=1.6.0
16
+ Provides-Extra: testing
17
+ Requires-Dist: pytest>=7.4.2; extra == "testing"
18
+ Requires-Dist: pytest-cov>=4.1.0; extra == "testing"
19
+ Requires-Dist: tox>=4.4; extra == "testing"
20
+ Provides-Extra: docs
21
+ Requires-Dist: Sphinx==6.2.1; extra == "docs"
22
+ Requires-Dist: sphinx-rtd-theme==1.2.2; extra == "docs"
23
+
24
+ # RandomFields
25
+ ![Tests](https://github.com/StemVibrations/RandomFields/actions/workflows/tests.yml/badge.svg)
26
+ [![codecov](https://codecov.io/gh/StemVibrations/RandomFields/graph/badge.svg?token=KNV140ZHKQ)](https://codecov.io/gh/StemVibrations/RandomFields)
27
+
28
+ # Random Fields
29
+
30
+ Python package to generate random fields in 2D or 3D on unstructured grids.
31
+
32
+ ## Background information
33
+ The random fields are generated using [gstools](https://geostat-framework.readthedocs.io/en/latest/).
34
+
35
+ Currently, the following covariance models are available:
36
+ - Gaussian
37
+ - Exponential
38
+ - Matern
39
+ - Linear
40
+
41
+ ## Installation
42
+
43
+ To install the package, run the following command in the terminal:
44
+ ```bash
45
+ pip install git+https://github.com/StemVibrations/RandomFields
46
+ ```
47
+
48
+ # Tutorial
49
+ ## Example in 2D
50
+
51
+ In this example we generate a random field on a 2D regular grid.
52
+
53
+ First you need to import the packages:
54
+
55
+ ```python
56
+ import numpy as np
57
+ from random_fields.generate_field import RandomFields, ModelName
58
+ from random_fields.utils import plot2D
59
+ ```
60
+
61
+ Then you need to define the grid:
62
+
63
+ ```python
64
+ x = np.linspace(0, 100, 50)
65
+ y = np.linspace(0, 50, 50)
66
+
67
+ x, y = np.meshgrid(x, y)
68
+ ```
69
+
70
+ In this example we create a mesh of 50x50 points between 0 and 100 in x and 0 and 50 in y direction.
71
+
72
+ Then we define the random field properties:
73
+
74
+ ```python
75
+ nb_dimensions = 2
76
+ mean = 10
77
+ variance = 2
78
+ vertical_scale_fluctuation = 10
79
+ anisotropy = [1]
80
+ angle = [0]
81
+ model_rf = ModelName.Gaussian
82
+ ```
83
+
84
+ In this example we use a Gaussian covariance model.
85
+
86
+ Then we create the random field:
87
+
88
+ ```python
89
+ rf = RandomFields(model_rf, nb_dimensions, mean, variance, vertical_scale_fluctuation, anisotropy, angle, seed=14)
90
+ rf.generate(np.array([x.ravel(), y.ravel()]).T)
91
+ ```
92
+
93
+ To visualise the results you can run:
94
+
95
+ ```python
96
+ plot2D([np.array([x.ravel(), y.ravel()]).T], [rf.random_field], title="Random Field", output_folder="./", output_name="random_field.png")
97
+ ```
98
+
99
+ The result is the following random field:
100
+
101
+ ![Random field 2D](./docs/static/random_field.png)
102
+
103
+ ## Example in 3D
104
+
105
+ In this example we generate a random field on a 3D regular grid.
106
+
107
+ First you need to import the packages:
108
+
109
+ ```python
110
+ import numpy as np
111
+ from random_fields.generate_field import RandomFields, ModelName
112
+ from random_fields.utils import plot3D
113
+ ```
114
+
115
+ Then you need to define the grid:
116
+
117
+ ```python
118
+ x = np.linspace(0, 100, 50)
119
+ y = np.linspace(0, 50, 50)
120
+ z = np.linspace(0, 25, 25)
121
+
122
+ x, y, z = np.meshgrid(x, y, z)
123
+ ```
124
+ In this example we create a mesh of 50x50x25 points between 0 and 100 in x, 0 and 50 in y direction and 0 and 25 in z direction.
125
+
126
+ Then we define the random field properties:
127
+
128
+ ```python
129
+ nb_dimensions = 3
130
+ mean = 10
131
+ variance = 2
132
+ vertical_scale_fluctuation = 10
133
+ anisotropy = [5, 5]
134
+ angle = [0, 0]
135
+ model_rf = ModelName.Gaussian
136
+ ```
137
+
138
+ Then we create the random field:
139
+
140
+ ```python
141
+ rf = RandomFields(model_rf, nb_dimensions, mean, variance, vertical_scale_fluctuation, anisotropy, angle, seed=14)
142
+ rf.generate(np.array([x.ravel(), y.ravel(), z.ravel()]).T)
143
+ ```
144
+
145
+ To visualise the results you can run:
146
+
147
+ ```python
148
+ plot3D([np.array([x.ravel(), y.ravel(), z.ravel()]).T], [rf.random_field], title="Random Field", output_folder="./", output_name="random_field_3D.png")
149
+
150
+ ```
151
+ The result is the following random field:
152
+
153
+ ![Random field 3D](./docs/static/random_field_3D.png)
@@ -0,0 +1,9 @@
1
+ random_fields/__init__.py,sha256=e3S0EcDGJlWIp2hddSFjvt633jfeZ4W7VyAeLpfTWVM,37
2
+ random_fields/__version__.py,sha256=DLIwjEWAWM3KBFCJ_Q00fL6GY1GlsiYH0pveSBIDxww,85
3
+ random_fields/generate_field.py,sha256=0C4h9viPoj7o4szOBzI7WGzmPb2SC-YH5_C72JJ2w94,3397
4
+ random_fields/utils.py,sha256=dBMqWMe0knF-Leb0aCXCqztGeOYx5FnukOQyPWIbfPo,2967
5
+ random_fields-1.0.1.dist-info/LICENSE,sha256=WbcTzZtVX4H8BLF_mD2BtvCL8oh43TQtyugBiPKVDwo,1506
6
+ random_fields-1.0.1.dist-info/METADATA,sha256=PXgQ1zIBAgF9v57W4g6csZ9kyN0R30wdHM8i8rk3RL8,4073
7
+ random_fields-1.0.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
8
+ random_fields-1.0.1.dist-info/top_level.txt,sha256=yFqhrKXL6hMrmI_OAUg9EBy5DvJsOQYBpLqqGdqL2zE,14
9
+ random_fields-1.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.8.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ random_fields