pycsamt.ai.geology.fields#

Regular geological grids and anisotropic correlated Gaussian fields.

The canonical array order is (z, x) in 2-D and (z, y, x) in 3-D. Coordinates represent cell centres in metres, with depth z increasing downward. Field generation uses spectral synthesis and never invokes an EM forward solver.

Functions

directional_variogram(field, axis, *[, ...])

Calculate an unbinned empirical directional semivariogram.

generate_gaussian_field(grid, correlation, ...)

Generate a deterministic anisotropic Gaussian random field.

Classes

CorrelatedField(values, grid, correlation, seed)

Immutable generated scalar field with complete generation provenance.

DirectionalVariogram(axis, lag_m, ...)

Empirical semivariance along one canonical geological axis.

GaussianCorrelation(length_x_m, length_z_m)

Anisotropic Gaussian spatial-correlation model.

GeologyGrid(x_m, z_m[, y_m, crs])

Immutable regular cell-centre grid for 2-D or 3-D geological priors.

class pycsamt.ai.geology.fields.GeologyGrid(x_m, z_m, y_m=None, crs=None)[source]

Bases: object

Immutable regular cell-centre grid for 2-D or 3-D geological priors.

Parameters:
  • x_m (array-like) – Strictly increasing horizontal and depth cell centres in metres.

  • z_m (array-like) – Strictly increasing horizontal and depth cell centres in metres.

  • y_m (array-like or None, optional) – Strictly increasing second horizontal axis. Omit it for a 2-D profile.

  • crs (str or None, optional) – Coordinate reference system identifier for horizontal coordinates.

Examples

Construct a 2-D grid directly:

>>> grid = GeologyGrid(x_m=[50, 150, 250], z_m=[25, 75])
>>> grid.shape
(2, 3)
>>> grid.dimension
2

Use regular_3d() for a volume:

>>> volume = GeologyGrid.regular_3d(
...     nx=4, ny=3, nz=2, dx_m=100, dy_m=200, dz_m=50
... )
>>> volume.shape
(2, 3, 4)
x_m: ndarray
z_m: ndarray
y_m: ndarray | None = None
crs: str | None = None
classmethod regular_2d(*, nx, nz, dx_m, dz_m, x_origin_m=0.0, z_origin_m=0.0, crs=None)[source]

Construct a uniform 2-D cell-centre grid.

Parameters:
  • nx (int) – At least two cells in the horizontal and depth directions.

  • nz (int) – At least two cells in the horizontal and depth directions.

  • dx_m (float) – Positive cell widths in metres.

  • dz_m (float) – Positive cell widths in metres.

  • x_origin_m (float, default=0.0) – Coordinate of the outer edge before the first cell centre.

  • z_origin_m (float, default=0.0) – Coordinate of the outer edge before the first cell centre.

  • crs (str or None, optional) – Coordinate reference system identifier.

Returns:

Grid whose centres begin one half-cell from each origin.

Return type:

GeologyGrid

Examples

>>> grid = GeologyGrid.regular_2d(nx=3, nz=2, dx_m=100, dz_m=50)
>>> grid.x_m.tolist(), grid.z_m.tolist()
([50.0, 150.0, 250.0], [25.0, 75.0])
classmethod regular_3d(*, nx, ny, nz, dx_m, dy_m, dz_m, x_origin_m=0.0, y_origin_m=0.0, z_origin_m=0.0, crs=None)[source]

Construct a uniform 3-D cell-centre grid.

Parameters:
  • nx (int) – At least two cells along each axis.

  • ny (int) – At least two cells along each axis.

  • nz (int) – At least two cells along each axis.

  • dx_m (float) – Positive cell widths in metres.

  • dy_m (float) – Positive cell widths in metres.

  • dz_m (float) – Positive cell widths in metres.

  • x_origin_m (float, default=0.0) – Outer-edge coordinate before the first centre on each axis.

  • y_origin_m (float, default=0.0) – Outer-edge coordinate before the first centre on each axis.

  • z_origin_m (float, default=0.0) – Outer-edge coordinate before the first centre on each axis.

  • crs (str or None, optional) – Coordinate reference system identifier.

Returns:

Uniform grid with canonical shape (nz, ny, nx).

Return type:

GeologyGrid

Examples

>>> grid = GeologyGrid.regular_3d(
...     nx=2, ny=3, nz=4, dx_m=10, dy_m=20, dz_m=5
... )
>>> grid.shape
(4, 3, 2)
property dimension: int[source]

Return whether the grid is two- or three-dimensional.

Returns:

Spatial dimension.

Return type:

{2, 3}

Examples

>>> GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1).dimension
2
property shape: tuple[int, ...][source]

Return the canonical geological array shape.

Returns:

(nz, nx) in 2-D or (nz, ny, nx) in 3-D.

Return type:

tuple of int

Examples

>>> GeologyGrid.regular_2d(nx=5, nz=3, dx_m=1, dz_m=1).shape
(3, 5)
property spacing_m: tuple[float, ...][source]

Return regular spacing in canonical array-axis order.

Returns:

(dz, dx) in 2-D or (dz, dy, dx) in 3-D.

Return type:

tuple of float

Raises:

ValueError – If any coordinate axis is not regularly spaced.

Examples

>>> GeologyGrid.regular_2d(nx=2, nz=2, dx_m=100, dz_m=25).spacing_m
(25.0, 100.0)
property extent_m: dict[str, tuple[float, float]][source]

Return outer cell-edge extents along all available axes.

Returns:

Axis names mapped to (minimum_edge, maximum_edge) in metres.

Return type:

dict

Examples

>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=100, dz_m=50)
>>> grid.extent_m["x"]
(0.0, 200.0)
to_dict()[source]

Return a JSON-serializable grid definition.

Returns:

Schema version, coordinates, and CRS.

Return type:

dict

Examples

>>> GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1).to_dict()[
...     "dimension"
... ]
2
classmethod from_dict(data)[source]

Restore a validated serialized grid.

Parameters:

data (mapping) – State returned by to_dict().

Returns:

Immutable grid.

Return type:

GeologyGrid

Examples

>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1)
>>> GeologyGrid.from_dict(grid.to_dict()).shape == grid.shape
True
class pycsamt.ai.geology.fields.GaussianCorrelation(length_x_m, length_z_m, length_y_m=None, azimuth_deg=0.0)[source]

Bases: object

Anisotropic Gaussian spatial-correlation model.

Parameters:
  • length_x_m (float) – Positive horizontal and vertical correlation lengths in metres for C(h) = exp(-0.5 * (h / length)**2).

  • length_z_m (float) – Positive horizontal and vertical correlation lengths in metres for C(h) = exp(-0.5 * (h / length)**2).

  • length_y_m (float or None, optional) – Second horizontal length required for 3-D generation.

  • azimuth_deg (float, default=0.0) – Clockwise rotation of horizontal principal axes. It affects 3-D fields and is normalized to [0, 180) because Gaussian axes are bidirectional.

Examples

>>> model = GaussianCorrelation(1000, 100, length_y_m=500, azimuth_deg=210)
>>> model.azimuth_deg
30.0
>>> model.anisotropy_xz
10.0
length_x_m: float
length_z_m: float
length_y_m: float | None = None
azimuth_deg: float = 0.0
property anisotropy_xz: float[source]

Return horizontal-to-vertical correlation-length ratio.

Returns:

length_x_m / length_z_m.

Return type:

float

Examples

>>> GaussianCorrelation(500, 100).anisotropy_xz
5.0
validate_grid(grid)[source]

Validate dimensional compatibility with a geological grid.

Parameters:

grid (GeologyGrid) – Grid on which the model will be sampled.

Returns:

Successful return means all required correlation lengths exist.

Return type:

None

Raises:

Examples

>>> grid = GeologyGrid.regular_2d(nx=2, nz=2, dx_m=1, dz_m=1)
>>> GaussianCorrelation(2, 1).validate_grid(grid) is None
True
to_dict()[source]

Return a JSON-serializable correlation model.

Returns:

Versioned Gaussian lengths and azimuth.

Return type:

dict

Examples

>>> GaussianCorrelation(10, 2).to_dict()["kind"]
'gaussian'
classmethod from_dict(data)[source]

Restore a validated Gaussian correlation model.

Parameters:

data (mapping) – State returned by to_dict().

Returns:

Immutable model.

Return type:

GaussianCorrelation

Examples

>>> model = GaussianCorrelation(10, 2)
>>> GaussianCorrelation.from_dict(model.to_dict()) == model
True
class pycsamt.ai.geology.fields.CorrelatedField(values, grid, correlation, seed, boundary='reflect', standardized=True)[source]

Bases: object

Immutable generated scalar field with complete generation provenance.

Parameters:
  • values (ndarray) – Finite values shaped exactly like grid.shape.

  • grid (GeologyGrid) – Spatial cell-centre grid.

  • correlation (GaussianCorrelation) – Requested correlation model.

  • seed (int) – Recorded random seed.

  • boundary ({"periodic", "reflect"}, default="reflect") – Spectral boundary policy used during generation.

  • standardized (bool, default=True) – Whether the stored sample was standardized to zero sample mean and unit sample standard deviation.

Examples

>>> grid = GeologyGrid.regular_2d(nx=8, nz=6, dx_m=100, dz_m=50)
>>> field = generate_gaussian_field(
...     grid, GaussianCorrelation(300, 100), seed=4
... )
>>> field.values.shape
(6, 8)
>>> field.seed
4
values: ndarray
grid: GeologyGrid
correlation: GaussianCorrelation
seed: int
boundary: str = 'reflect'
standardized: bool = True
property field_hash: str[source]

Return a digest covering values and generation configuration.

Returns:

Lowercase SHA-256 digest. Values are hashed as contiguous little- endian float64 bytes for platform-stable identity.

Return type:

str

Examples

>>> grid = GeologyGrid.regular_2d(nx=4, nz=4, dx_m=1, dz_m=1)
>>> field = generate_gaussian_field(
...     grid, GaussianCorrelation(2, 1), seed=0
... )
>>> len(field.field_hash)
64
provenance()[source]

Return JSON-compatible generation provenance without field values.

Returns:

Grid, correlation, seed, boundary, and standardization state.

Return type:

dict

Examples

>>> grid = GeologyGrid.regular_2d(nx=4, nz=4, dx_m=1, dz_m=1)
>>> field = generate_gaussian_field(
...     grid, GaussianCorrelation(2, 1), seed=0
... )
>>> field.provenance()["seed"]
0
rescale(*, mean=0.0, standard_deviation=1.0)[source]

Return a copy with a requested sample mean and standard deviation.

Parameters:
  • mean (float, default=0.0) – Target arithmetic mean.

  • standard_deviation (float, default=1.0) – Positive target population standard deviation.

Returns:

New field with unchanged spatial pattern and standardized=False unless the requested values are exactly zero and one.

Return type:

CorrelatedField

Examples

>>> grid = GeologyGrid.regular_2d(nx=8, nz=8, dx_m=1, dz_m=1)
>>> field = generate_gaussian_field(
...     grid, GaussianCorrelation(2, 1), seed=1
... ).rescale(mean=2, standard_deviation=3)
>>> (
...     round(float(np.mean(field.values)), 12),
...     round(float(np.std(field.values)), 12),
... )
(2.0, 3.0)
to_npz(path)[source]

Persist values and provenance in a pickle-free NPZ archive.

Parameters:

path (str or pathlib.Path) – Destination archive.

Returns:

Requested destination path.

Return type:

pathlib.Path

Examples

>>> from tempfile import TemporaryDirectory
>>> grid = GeologyGrid.regular_2d(nx=4, nz=4, dx_m=1, dz_m=1)
>>> field = generate_gaussian_field(
...     grid, GaussianCorrelation(2, 1), seed=0
... )
>>> with TemporaryDirectory() as directory:
...     path = field.to_npz(Path(directory) / "field.npz")
...     restored = CorrelatedField.from_npz(path)
>>> restored.field_hash == field.field_hash
True
classmethod from_npz(path)[source]

Load and validate a field archive without enabling pickle.

Parameters:

path (str or pathlib.Path) – Archive written by to_npz().

Returns:

Immutable restored field.

Return type:

CorrelatedField

Examples

>>> from tempfile import TemporaryDirectory
>>> field = generate_gaussian_field(
...     GeologyGrid.regular_2d(nx=3, nz=3, dx_m=1, dz_m=1),
...     GaussianCorrelation(2, 1),
...     seed=3,
... )
>>> with TemporaryDirectory() as directory:
...     path = field.to_npz(Path(directory) / "f.npz")
...     restored = CorrelatedField.from_npz(path)
>>> np.array_equal(restored.values, field.values)
True
class pycsamt.ai.geology.fields.DirectionalVariogram(axis, lag_m, semivariance, pair_count)[source]

Bases: object

Empirical semivariance along one canonical geological axis.

Parameters:
  • axis ({"x", "y", "z"}) – Direction along which cell pairs were compared.

  • lag_m (ndarray) – Positive lag distances and corresponding finite semivariances.

  • semivariance (ndarray) – Positive lag distances and corresponding finite semivariances.

  • pair_count (ndarray of int) – Number of finite cell pairs supporting each lag.

Examples

>>> result = DirectionalVariogram("x", [1, 2], [0.2, 0.5], [10, 8])
>>> result.n_lags
2
axis: str
lag_m: ndarray
semivariance: ndarray
pair_count: ndarray
property n_lags: int[source]

Return the number of empirical lag bins.

Returns:

Length of the variogram arrays.

Return type:

int

Examples

>>> DirectionalVariogram("z", [1], [0.1], [4]).n_lags
1
pycsamt.ai.geology.fields.generate_gaussian_field(grid, correlation, *, seed, boundary='reflect', standardize=True)[source]

Generate a deterministic anisotropic Gaussian random field.

Parameters:
  • grid (GeologyGrid) – Regular 2-D or 3-D cell-centre grid.

  • correlation (GaussianCorrelation) – Requested Gaussian covariance lengths and horizontal azimuth.

  • seed (int) – Explicit seed recorded in the returned field.

  • boundary ({"reflect", "periodic"}, default="reflect") – periodic synthesizes directly on the requested grid. reflect synthesizes on a grid doubled along each axis and crops the centre, reducing wrap-around correlation at opposite model edges.

  • standardize (bool, default=True) – Shift and scale the realized sample to zero mean and unit population standard deviation.

Returns:

Immutable field with complete generation provenance.

Return type:

CorrelatedField

Raises:

ValueError – If the grid is irregular, correlation model is incompatible, or the filtered realization is numerically constant.

Examples

>>> grid = GeologyGrid.regular_2d(nx=32, nz=16, dx_m=100, dz_m=50)
>>> model = GaussianCorrelation(length_x_m=500, length_z_m=100)
>>> first = generate_gaussian_field(grid, model, seed=12)
>>> second = generate_gaussian_field(grid, model, seed=12)
>>> np.array_equal(first.values, second.values)
True
>>> (
...     abs(round(float(np.mean(first.values)), 12)),
...     round(float(np.std(first.values)), 12),
... )
(0.0, 1.0)
pycsamt.ai.geology.fields.directional_variogram(field, axis, *, max_lag_cells=None)[source]

Calculate an unbinned empirical directional semivariogram.

Parameters:
  • field (CorrelatedField) – Finite scalar field.

  • axis ({"x", "y", "z"}) – Geological direction. "y" is unavailable for 2-D fields.

  • max_lag_cells (int or None, optional) – Maximum positive integer cell offset. The default is half the selected axis length, with at least one lag.

Returns:

Lag distance, mean half-squared difference, and pair count.

Return type:

DirectionalVariogram

Examples

>>> grid = GeologyGrid.regular_2d(nx=16, nz=8, dx_m=100, dz_m=50)
>>> field = generate_gaussian_field(
...     grid, GaussianCorrelation(300, 100), seed=1
... )
>>> variogram = directional_variogram(field, "x", max_lag_cells=3)
>>> variogram.n_lags
3
>>> variogram.pair_count[0] > variogram.pair_count[-1]
True