Grid processing: geometry, mask, query, regridding

This notebook shows how to use some extra features for your gridmarthe treatments

# import modules
import numpy as np
import gridmarthe as gm
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
# load data for example
grid = gm.load_marthe_grid('./data/craie_npc.permh', drop_nan=True)  # NaN needs to be drop or set (change 0 to nan for permh for example)
grid
<xarray.Dataset> Size: 9MB
Dimensions:  (time: 1, zone: 216742)
Coordinates:
  * time     (time) int64 8B 0
  * zone     (zone) int32 867kB 915 916 917 1300 ... 1111740 1111741 1111742
Data variables:
    permeab  (time, zone) float64 2MB 0.0001 0.0001 0.0001 ... 1.5e-05 1.5e-05
    z        (zone) int64 2MB 1 1 1 1 1 1 1 1 1 1 ... 10 10 10 10 10 10 10 10 10
    x        (zone) float32 867kB 6.138e+05 6.142e+05 ... 6.818e+05 6.822e+05
    y        (zone) float32 867kB 2.677e+06 2.677e+06 ... 2.61e+06 2.61e+06
    dx       (zone) float32 867kB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    dy       (zone) float32 867kB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    izone    (zone) int32 867kB 1 2 3 4 5 ... 216738 216739 216740 216741 216742
Attributes: (12/15)
    conventions:          CF-1.10
    title:                
    marthe_grid_version:  9.0
    original_dimensions:  x,y,z [grids]: 387 304 10
    crs:                  {'crs_wkt': 'PROJCRS["NTF (Paris) / Lambert zone II...
    lon_resolution:       500.0
    ...                   ...
    nested_grid:          False
    extend:               xymin : 543750.0 2526350.0; xymax: 736750.0 2677850.0
    creation_date:        Created on 2026-07-16T16:29:14Z UTC
    comment:              Hydrogeological model created with MARTHE code (Thi...
    domain:               FR-France
    institution:          BRGM, French Geological Survey, Orléans, France

Query grid

gridmarthe load data as a spatially reduced grid (spatial dimension is reduced to 1D). Hence, x and y coordinates are stored as variables, attached to grid indices, and not as dimensions. It is then not possible to query the grid with x and y coordinates and xarray.Dataset.sel() method.

To query the grid with x and y coordinates, one can use the :py:func:gridmarthe.assign_coords function to set coordinates as dimension, or use the custom function :py:func:gridmarthe.sel_by_coords to query the grid with x and y coordinates directly on 1D spatial array.

subset = gm.sel_by_coords(grid, x=(586000, 646000), y=(2593100, 2633600))
subset
<xarray.Dataset> Size: 2MB
Dimensions:  (time: 1, zone: 46342)
Coordinates:
  * time     (time) int64 8B 0
  * zone     (zone) int32 185kB 34630 34631 34632 ... 1104316 1104702 1104703
Data variables:
    permeab  (time, zone) float64 371kB 1e-05 1e-05 1e-05 ... 5e-07 5e-07 5e-07
    z        (zone) int64 371kB 1 1 1 1 1 1 1 1 1 ... 10 10 10 10 10 10 10 10 10
    x        (zone) float32 185kB 6.368e+05 6.372e+05 ... 6.452e+05 6.458e+05
    y        (zone) float32 185kB 2.633e+06 2.633e+06 ... 2.619e+06 2.619e+06
    dx       (zone) float32 185kB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    dy       (zone) float32 185kB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    izone    (zone) int32 185kB 2823 2824 2825 2826 ... 216026 216069 216070
Attributes: (12/15)
    conventions:          CF-1.10
    title:                
    marthe_grid_version:  9.0
    original_dimensions:  x,y,z [grids]: 387 304 10
    crs:                  {'crs_wkt': 'PROJCRS["NTF (Paris) / Lambert zone II...
    lon_resolution:       500.0
    ...                   ...
    nested_grid:          False
    extend:               xymin : 543750.0 2526350.0; xymax: 736750.0 2677850.0
    creation_date:        Created on 2026-07-16T16:29:14Z UTC
    comment:              Hydrogeological model created with MARTHE code (Thi...
    domain:               FR-France
    institution:          BRGM, French Geological Survey, Orléans, France
fig, ax = plt.subplots(ncols=2, figsize=(10, 3.5))
gm.plot_nested_grid(gm.assign_coords(grid).sel(z=6).squeeze('time'), ax=ax[0], norm=LogNorm())
gm.plot_nested_grid(gm.assign_coords(subset).sel(z=6).squeeze('time'), ax=ax[1], norm=LogNorm())
ax[0].set_title('Full data set')
ax[1].set_title('Subset by coords')
fig.tight_layout()
../../_images/d963c055a1493d9e4c4d3d48aac7c3cf9c9dbf306a05f48f7aa219dc182af378.png

For convenience and interoperability with other tools, user can also search for the zone index of a particular cell point, using either x, y coordinates or the i (column) and j (row) indices (:py:func:gridmarthe.search_zone).

ds = gm.load_marthe_grid("data/chasim_hallue.out", add_col_row=True).isel(time=0)
# search with column/row indices:
idx = gm.search_zone(ds, i=23, j=32)
print(idx)
<xarray.Dataset> Size: 52B
Dimensions:  (zone: 1)
Coordinates:
  * zone     (zone) int32 4B 1666
    time     int64 8B 0
Data variables:
    charge   (zone) float64 8B 44.08
    col      (zone) float64 8B 23.0
    row      (zone) float64 8B 32.0
    x        (zone) float32 4B 607.8
    y        (zone) float32 4B 2.553e+03
    dx       (zone) float32 4B 0.5
    dy       (zone) float32 4B 0.5
Attributes: (12/15)
    conventions:          CF-1.10
    title:                Modélisation du bassin de la SOMME Nappe_Libre
    marthe_grid_version:  9.0
    original_dimensions:  x,y,z [grids]: 53 54 1
    crs:                  {'crs_wkt': 'PROJCRS["NTF (Paris) / Lambert zone II...
    lon_resolution:       0.5
    ...                   ...
    nested_grid:          False
    extend:               xymin : 596.75 2542.25; xymax: 622.75 2568.75
    creation_date:        Created on 2026-07-16T16:29:15Z UTC
    comment:              Hydrogeological model created with MARTHE code (Thi...
    domain:               FR-France
    institution:          BRGM, French Geological Survey, Orléans, France

Get surface mask

See :py:func:gridmarthe.get_surface_layer for more details.

surf = gm.get_surface_layer(grid)
surf
<xarray.Dataset> Size: 3MB
Dimensions:  (zone: 53501)
Coordinates:
  * zone     (zone) int32 214kB 59988 59601 59214 58827 ... 643191 643966 643579
Data variables:
    time     (zone) int64 428kB 0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0 0
    permeab  (zone) float64 428kB 1e-05 1e-05 1e-05 1e-05 ... 3e-05 3e-05 3e-05
    z        (zone) int64 428kB 1 1 1 1 1 1 1 1 1 1 1 ... 6 6 6 6 6 6 6 6 6 6 6
    x        (zone) float32 214kB 5.448e+05 5.448e+05 ... 7.358e+05 7.358e+05
    y        (zone) float32 214kB 2.6e+06 2.601e+06 ... 2.606e+06 2.607e+06
    dx       (zone) float32 214kB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    dy       (zone) float32 214kB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    izone    (zone) int32 214kB 4584 4523 4464 4409 ... 86862 86507 87201 86863
# xarray version
toto = gm.assign_coords(surf, add_lay=False) # add lay false because it needs to be a variable to plot, not a dimension
gm.plot_outcrop(toto)
plt.show()
../../_images/964101244326e9dc67627adbd9634fd73b19f34c025e3ed15b0ce3318560c326.png

This function also allow user to get the value of a variable in the surface layer, or a subset of aquifer layers.

For example, to get the value of your variable (groundwater head for example) in the first layer encountered between 3 layers:

head_surf = gm.get_surface_layer(grid, aquif_layers=[6,8,9])  # get the values in the first layer between layers 6, 8 and 9

Get mask of active domain

See :py:func:gridmarthe.get_active_mask

mask = gm.get_active_mask(grid) # return a geopandas geodataframe
mask.boundary.plot(color='k')
plt.show()
../../_images/8316b276e6d1e1b88575e6a9a8ef64d9c36e220d3b97f9a4f550ff15c4f42aa9.png

Compute depths and thickness

Users can get geometry attributes (depth, thickness, upper/lower altitudes) in a dataset:

See compute_geometry() for more details.

topo = gm.load_marthe_grid('./data/example.topog')   # be careful with nan here, topography can be set outside of active area
hsubs = gm.load_marthe_grid('./data/example.hsubs')  # same here, hsubs can be set outside of active area
geom = gm.compute_geometry(topo, hsubs)
# print(geom.where(~geom['depth'].isnull(), drop=True))
geom
<xarray.Dataset> Size: 80MB
Dimensions:    (zone: 1176480)
Coordinates:
  * zone       (zone) int32 5MB 1 2 3 4 5 ... 1176477 1176478 1176479 1176480
Data variables:
    h_topogr   (zone) float64 9MB 9.999e+03 9.999e+03 ... 9.999e+03 9.999e+03
    z          (zone) int64 9MB 1 1 1 1 1 1 1 1 1 ... 10 10 10 10 10 10 10 10 10
    x          (zone) float32 5MB 5.438e+05 5.442e+05 ... 7.362e+05 7.368e+05
    y          (zone) float32 5MB 2.678e+06 2.678e+06 ... 2.526e+06 2.526e+06
    dx         (zone) float32 5MB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    dy         (zone) float32 5MB 500.0 500.0 500.0 500.0 ... 500.0 500.0 500.0
    z_lower    (zone) float64 9MB nan nan nan nan nan ... nan nan nan nan nan
    z_upper    (zone) float64 9MB nan nan nan nan nan ... nan nan nan nan nan
    thickness  (zone) float64 9MB nan nan nan nan nan ... nan nan nan nan nan
    depth      (zone) float64 9MB nan nan nan nan nan ... nan nan nan nan nan
Attributes: (12/15)
    conventions:          CF-1.10
    title:                
    marthe_grid_version:  9.0
    original_dimensions:  x,y,z [grids]: 387 304 10
    crs:                  {'crs_wkt': 'PROJCRS["NTF (Paris) / Lambert zone II...
    lon_resolution:       500.0
    ...                   ...
    nested_grid:          False
    extend:               xymin : 543750.0 2526350.0; xymax: 736750.0 2677850.0
    creation_date:        Created on 2026-07-16T16:29:24Z UTC
    comment:              Hydrogeological model created with MARTHE code (Thi...
    domain:               FR-France
    institution:          BRGM, French Geological Survey, Orléans, France

Interpolation, regridding, transformations

Regrid (coarse/refine resolution)

Utils function to coarse/refine grid resolution are provided, wrapping xarray.DataArray.interp method. By default, a linear interpolation is used.

See docs.xarray.dev/interpolation for more details

ds_coarse = gm.rescale_grid(grid, res=1e3)  # kwargs can be passed to interp method
ds_8km = gm.rescale_grid(grid, res=8e3)
ds_100m = gm.rescale_grid(grid, res=1e2)
fig, ax = plt.subplots(ncols=2, nrows=2, figsize=(10,8))
gm.plot_nested_grid(grid, ax=ax[0,0], norm=LogNorm(), itime=0, layer=6)
gm.plot_nested_grid(ds_coarse, ax=ax[0,1], norm=LogNorm(), itime=0, layer=6)
gm.plot_nested_grid(ds_8km, ax=ax[1,0], norm=LogNorm(), itime=0, layer=6)
gm.plot_nested_grid(ds_100m, ax=ax[1,1], norm=LogNorm(), itime=0, layer=6)
ax[0,0].set_title('Original grid, 500m')
ax[0,1].set_title('Regrid to 1km')
ax[1,0].set_title('Regrid to 8km')
ax[1,1].set_title('Regrid to 100m')
fig.tight_layout()
../../_images/664317c6bfaccadfa6cefdf05aedfab6a75aa35cfb5b25db30981b43e9a5ba80.png

Projection transformation

Warning: this function is still experimental and only works for regular grids, read without dropping NaNs and invalid data (a full x, y, dx, dy grid is required).

ds_l2e = gm.load_marthe_grid('data/chasim_hallue.out', xyfactor=1e3).isel(time=0)
ds_l93 = gm.reproj_grid(ds_l2e, from_epsg='EPSG:27572', to_epsg='EPSG:2154', decimals=0)
fig, ax = plt.subplots(ncols=2, figsize=(7, 3))
gm.assign_coords(ds_l2e)['charge'].where(lambda x: x < 9999.).plot.pcolormesh(ax=ax[0])
gm.assign_coords(ds_l93)['charge'].where(lambda x: x < 9999.).plot.pcolormesh(ax=ax[1])
fig.tight_layout()
ax[0].set_title('Lambert II étendu')
ax[1].set_title('Lambert 93')
plt.show()
../../_images/2c3e3886e0ecf80f87bf1333f4eec81efae66bc33908eaa1d3011341a5d32856.png