From 5d51a64d521a85c4a11eaca535af2bdd70504d06 Mon Sep 17 00:00:00 2001 From: LSchueler Date: Fri, 10 Jan 2025 16:46:46 +0100 Subject: [PATCH] Add examples for cond. PGS & P-PGS --- examples/11_plurigaussian/05_conditioned.py | 142 ++++++++++++++++++ examples/11_plurigaussian/06_periodic.py | 76 ++++++++++ .../11_plurigaussian/conditional_values.npz | Bin 0 -> 10118 bytes 3 files changed, 218 insertions(+) create mode 100644 examples/11_plurigaussian/05_conditioned.py create mode 100644 examples/11_plurigaussian/06_periodic.py create mode 100644 examples/11_plurigaussian/conditional_values.npz diff --git a/examples/11_plurigaussian/05_conditioned.py b/examples/11_plurigaussian/05_conditioned.py new file mode 100644 index 00000000..7ef9e907 --- /dev/null +++ b/examples/11_plurigaussian/05_conditioned.py @@ -0,0 +1,142 @@ +""" +Creating conditioned PGS +------------------------ + +In case we have knowledge about values of the PGS in certain areas, we should +incorporate that knowledge. We can do this by using conditional fields, which +are created by combining kriged fields with SRFs. This can be done quite easily +with GSTools. For more details, have a look at the kriging and conditional +field examples. + +In this example, we assume that we know the categorical values of the PGS field +to be 2 in the lower left 20 by 20 grid cells. +""" + +import matplotlib.pyplot as plt +import numpy as np + +import gstools as gs + +dim = 2 +# no. of cells in both dimensions +N = [100, 80] + +# grid +x = np.arange(N[0]) +y = np.arange(N[1]) + +############################################################################### +# Now we want to read the known data in order to condition the PGS on them. +# Normally, we would probably get the data in a different format, but in order +# to keep the dependencies for this example to a minimum, we will simpy use a +# numpy format. After reading in the data, we will have a quick look at how the +# data looks like. +# We'll see the first few values, which are all 1. This value is not very +# important. However, it should be in the range of SRF values, to not mess +# with the kriging. The known value of the PGS, namely 2, will be set for the +# L-field, which will map it the the conditioned SRF values of 1 at given +# positions. + +cond_data = np.load("conditional_values.npz") +cond_pos = cond_data["cond_pos"] +cond_val = cond_data["cond_val"] +print(f"first 5 conditional positions:\n{cond_pos[:, :5]}") +print(f"first 5 conditional values:\n{cond_val[:5]}") + +############################################################################### +# With the conditional values ready, we can now set up the covariance model +# for the kriging. This knowledge has to normally be inferred, but here we just +# assume that we know the convariance structure of the underlying field. +# For better visualization, we use `Simple` kriging with a mean value of 0. + +model = gs.Gaussian(dim=dim, var=1, len_scale=[10, 5], angles=np.pi / 8) +krige = gs.krige.Simple(model, cond_pos=cond_pos, cond_val=cond_val, mean=0) +cond_srf = gs.CondSRF(krige) +cond_srf.set_pos([x, y], "structured") + +############################################################################### +# Now that the conditioned field class is set up, we can generate SRFs +# conditioned on our previous knowledge. We'll do that for the two SRFs needed +# for the PGS, and then we will also set up the PGS generator. Next, we'll +# use a little helper method, which can transform the coordinates from the SRFs +# to the L field. This helps us set up the area around the conditioned value +# `cond_val`. + +field1 = cond_srf(seed=484739) +field2 = cond_srf(seed=45755894) + +pgs = gs.PGS(dim, [field1, field2]) + +M = [100, 80] + +# size of the rectangle +R = [40, 32] + +L = np.zeros(M) +# calculate grid axes of the L-field +pos_l = pgs.calc_L_axes(L.shape) +# transform conditioned SRF value to L-field index +pos_l_i = pgs.transform_coords(L.shape, [cond_val[0], cond_val[0]]) + +# conditioned category of 2 around the conditioned values' positions +L[ + pos_l_i[0] - 5 : pos_l_i[0] + 5, + pos_l_i[1] - 5 : pos_l_i[1] + 5, +] = 2 + +############################################################################### +# With the two SRFs and `L` ready, we can create the actual PGS. + +P = pgs(L) + +############################################################################### +# Finally, we can plot the PGS, but we will also show the L-field and the two +# original Gaussian SRFs. We will set the colours of the SRF correlation +# scatter plot to be the sum of their respective position tuples (x+y), to get a +# feeling for which point corresponds to which position. The more blue the +# points, the smaller the sum is. We can nicely see that many blue points +# gather in the highlighted rectangle of the L-field where the categorical +# value of 2 is set. + +fig, axs = plt.subplots(2, 2) + +axs[0, 0].imshow(field1, cmap="copper", vmin=-3, vmax=2, origin="lower") +axs[0, 1].imshow(field2, cmap="copper", vmin=-3, vmax=2, origin="lower") +axs[1, 0].scatter( + field1.flatten(), + field2.flatten(), + s=0.1, + c=(x.reshape((len(x), 1)) + y.reshape((1, len(y))).flatten()), +) +axs[1, 0].pcolormesh(pos_l[0], pos_l[1], L.T, alpha=0.3, cmap="copper") +axs[1, 1].imshow(P, cmap="copper", origin="lower") + +plt.tight_layout() +plt.show() + +############################################################################### +# With all this set up, we can easily create an ensemble of PGS, which conform +# to the conditional values + +seed = gs.random.MasterRNG(20170519) + +ens_no = 9 +fields1 = [] +fields2 = [] +Ps = [] +for i in range(ens_no): + fields1.append(cond_srf(seed=seed())) + fields2.append(cond_srf(seed=seed())) + pgs = gs.PGS(dim, [fields1[-1], fields2[-1]]) + Ps.append(pgs(L)) + +fig, axs = plt.subplots(3, 3) +cnt = 0 +for i in range(int(np.sqrt(ens_no))): + for j in range(int(np.sqrt(ens_no))): + axs[i, j].imshow(Ps[cnt], cmap="copper", origin="lower") + + cnt += 1 + +plt.tight_layout() +plt.show() diff --git a/examples/11_plurigaussian/06_periodic.py b/examples/11_plurigaussian/06_periodic.py new file mode 100644 index 00000000..15aa24a1 --- /dev/null +++ b/examples/11_plurigaussian/06_periodic.py @@ -0,0 +1,76 @@ +""" +Creating PGS with periodic boundaries +------------------------------------- + +Plurigaussian fields with periodic boundaries (P-PGS) are used in various +applications, including the simulation of interactions between the landsurface +and the atmosphere, as well as the application of homogenisation theory to +porous media, e.g. [Ricketts 2024](https://doi.org/10.1007/s11242-024-02074-z). + +In this example we will use GSTools's Fourier generator to create periodic +random fields, which can in turn be used to generate P-PGS. +""" + +import matplotlib.pyplot as plt +import numpy as np + +import gstools as gs + +dim = 2 +# define the spatial grid, see the periodic random field [examples](https://geostat-framework.readthedocs.io/projects/gstools/en/latest/examples/01_random_field/08_fourier.html) +# for details. + +# domain size and periodicity +L = 200 +# no. of cells in both dimensions +N = [170, 153] + +x = np.linspace(0, L, N[0], endpoint=False) +y = np.linspace(0, L, N[1], endpoint=False) + +############################################################################### +# The parameters of the covariance model are very similar to previous examples. +# The interesting part is the SRF class. We set the `generator` to `"Fourier"`, +# which inherently generates periodic SRFs. The Fourier generator needs an +# extra parameter `period` which defines the periodicity. + +model = gs.Gaussian(dim=dim, var=0.8, len_scale=40) +srf = gs.SRF(model, generator="Fourier", period=L) +field1 = srf.structured([x, y], seed=19770319) +field2 = srf.structured([x, y], seed=19860912) + +############################################################################### +# Very similar to previous examples, we create a simple L-field. + +M = [200, 160] + +# size of the rectangle +R = [40, 32] + +L = np.zeros(M) +L[ + M[0] // 2 - R[0] // 2 : M[0] // 2 + R[0] // 2, + M[1] // 2 - R[1] // 2 : M[1] // 2 + R[1] // 2, +] = 1 + +############################################################################### +# With the two SRFs and the L-ield ready, we can create our first P-PGS. + +pgs = gs.PGS(dim, [field1, field2]) +P = pgs(L) + +############################################################################### +# Finally, we can plot the PGS, but we will also show the L-field and the two +# original periodic Gaussian fields. +# Especially with `field1` you can nicely see the periodic structures in the +# black structure in the upper right corner. This transfers to the P-PGS, where +# you can see that the structures seemlessly match the opposite boundaries. + +fig, axs = plt.subplots(2, 2) + +axs[0, 0].imshow(field1, cmap="copper", origin="lower") +axs[0, 1].imshow(field2, cmap="copper", origin="lower") +axs[1, 0].imshow(L, cmap="copper", origin="lower") +axs[1, 1].imshow(P, cmap="copper", origin="lower") + +plt.show() diff --git a/examples/11_plurigaussian/conditional_values.npz b/examples/11_plurigaussian/conditional_values.npz new file mode 100644 index 0000000000000000000000000000000000000000..dc1ca9b29c6e17067fe42e7cc66d1c5eece5dfe1 GIT binary patch literal 10118 zcmeI2%}T>S5P&CX>rbs(tru_W#VDjw#Dj=uFGb14gQpOhh(c*fQVJqM-@(@rz4#8k zga;o$&}?P~Y`xjbAhXbZP14OxI7Q892mpB7bkfy^G=L+dVB2o#=B0lQ5FrtG>a2^|19d~NpU~FiH1pW zy?5xFqfTevHxK5ItxgjMhd#K(3b9J85f_My#3kY~kq2C%>jrU^xJFzjHi;X=O=644 z(D|VxRDLK2)jwVtNL?SgKCA@PKBFG!{(*AP{TJn+{tL=M{VSA%`ll!dDgHgqM&3L7 zRZ_|EnpAQeDV41MQpx+4RI_E_U+;*jniyH9sn?2cPUz}004=3dx p{&^N|wx^A}%IK%H7h{z@DGm#|wKCc4f(xT!?9W~M3*Yw-kD34g literal 0 HcmV?d00001