diff --git a/.gitignore b/.gitignore index 9f0700e..d82364a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ scratch +.DS_Store # Byte-compiled / optimized / DLL files __pycache__/ @@ -132,6 +133,6 @@ dmypy.json # Pyre type checker .pyre/ - bin/ *.code-workspace +**/model_ws diff --git a/src/nhflotools/pwnlayers/io.py b/src/nhflotools/pwnlayers/io.py index 0afa8dd..0446bc0 100644 --- a/src/nhflotools/pwnlayers/io.py +++ b/src/nhflotools/pwnlayers/io.py @@ -54,7 +54,7 @@ def read_pwn_data2( ds_mask_transition : xarray Dataset mask dataset. True in transition zone. """ - modelgrid = nlmod.dims.grid.modelgrid_from_ds(ds) + modelgrid = nlmod.dims.grid.modelgrid_from_ds(ds, rotated=False) ix = GridIntersect(modelgrid, method="vertex") ds_out = xr.Dataset( @@ -163,7 +163,7 @@ def read_pwn_data2( return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_bergen_basis_aquitards( ds, pathname=None, @@ -271,7 +271,7 @@ def _read_bergen_basis_aquitards( return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_bergen_c_aquitards(ds, pathname, length_transition=100.0, ix=None): """Read vertical resistance of layers. @@ -313,7 +313,7 @@ def _read_bergen_c_aquitards(ds, pathname, length_transition=100.0, ix=None): return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_bergen_thickness_aquitards( ds, pathname=None, @@ -419,7 +419,7 @@ def _read_bergen_thickness_aquitards( return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_top_of_aquitards(ds, pathname, length_transition=100.0, ix=None): """Read top of aquitards. @@ -461,7 +461,7 @@ def _read_top_of_aquitards(ds, pathname, length_transition=100.0, ix=None): return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_thickness_of_aquitards(ds, pathname, length_transition=100.0, ix=None): """Read thickness of aquitards. @@ -505,7 +505,7 @@ def _read_thickness_of_aquitards(ds, pathname, length_transition=100.0, ix=None) return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_kd_of_aquitards(ds, pathname, length_transition=100.0, ix=None): """Read kd of aquitards. @@ -546,7 +546,7 @@ def _read_kd_of_aquitards(ds, pathname, length_transition=100.0, ix=None): return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_mask_of_aquifers(ds, pathname, length_transition=100.0, ix=None): """Read mask of aquifers. @@ -593,7 +593,7 @@ def _read_mask_of_aquifers(ds, pathname, length_transition=100.0, ix=None): return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_layer_kh(ds, pathname, length_transition=100.0, ix=None): """Read hydraulic conductivity of layers. @@ -636,7 +636,7 @@ def _read_layer_kh(ds, pathname, length_transition=100.0, ix=None): return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_kv_area(ds, pathname, length_transition=100.0, ix=None): # noqa: ARG001 """Read vertical resistance of layers. @@ -711,7 +711,7 @@ def _read_kv_area(ds, pathname, length_transition=100.0, ix=None): # noqa: ARG0 return ds_out -@cache.cache_netcdf(coords_2d=True) +@cache.cache_netcdf(datavars=["top"], coords_2d=True) def _read_topsysteem(ds, pathname): """Read topsysteem. diff --git a/src/nhflotools/pwnlayers2/__init__.py b/src/nhflotools/pwnlayers2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/nhflotools/pwnlayers2/layers.py b/src/nhflotools/pwnlayers2/layers.py new file mode 100644 index 0000000..32ae454 --- /dev/null +++ b/src/nhflotools/pwnlayers2/layers.py @@ -0,0 +1,114 @@ +"""Module containing functions to retrieve PWN bodemlagen.""" + +import logging +from pathlib import Path + +import geopandas as gpd +import numpy as np +import pykrige.ok +import xarray as xr +from flopy.discretization.vertexgrid import VertexGrid +from flopy.utils.gridintersect import GridIntersect +from nlmod.dims.grid import modelgrid_from_ds +from shapely.ops import unary_union + +logger = logging.getLogger(__name__) + + +def get_pwn_aquitard_data(ds: xr.Dataset, ix: GridIntersect, modelgrid: VertexGrid, data_dir: Path, transition_length: float) -> dict: + """ + Interpolate the thickness of the aquitard layers and the top of the aquitard layers using Kriging. + + The thickness of the aquitard layers is interpolated using the points in the file + `dikte_aquitard/D{layer_name}/D{layer_name}_interpolation_points.geojson`. + The top of the aquitard layers is interpolated using the points in the file + `top_aquitard/T{layer_name}/T{layer_name}_interpolation_points.geojson`. + The mask of the aquitard layers is defined in the file + `dikte_aquitard/D{layer_name}/D{layer_name}_mask_combined.geojson`. + + Parameters + ---------- + ds : xr.Dataset + The model dataset that contains the vertex grid information. + ix : flopy.utils.GridIntersect + The index of the model grid. + modelgrid : flopy.discretization.VertexGrid + The model grid. + data_dir : Path + The directory containing the data. Contains folders `dikte_aquitard` and `top_aquitard`. + transition_length : float + The length of the transition zone in meters. + + Returns + ------- + dict + A dictionary containing the interpolated values of the aquitard layers. + """ + verbose = logger.level <= logging.DEBUG + + if ix is None and modelgrid is None and ds is not None: + modelgrid = modelgrid_from_ds(ds) + + if ix is None and modelgrid is not None: + ix = GridIntersect(modelgrid, method="vertex") + + ncell = len(ix.mfgrid.cell2d) + layer_names = ["S11", "S12", "S13", "S21", "S22", "S31", "S32"] + data = {} + + for name in layer_names: + # Compute where the layer is _not_ present + logger.info("Interpolating aquitard layer %s data and its transition zone", name) + fp_mask = data_dir / "dikte_aquitard" / f"D{name}" / f"D{name}_mask_combined.geojson" + gdf_mask = gpd.read_file(fp_mask) + + multipolygon = unary_union(gdf_mask.geometry) + ids = ix.intersect(multipolygon, contains_centroid=False, min_area_fraction=0.5).cellids.astype(int) + data[f"{name}_mask"] = np.zeros(ncell, dtype=bool) + data[f"{name}_mask"][ids] = True + + # Compute where the layer transitions to REGIS + multipolygon_transition = multipolygon.buffer(transition_length).difference(multipolygon) + ids_trans = ix.intersect( + multipolygon_transition, contains_centroid=False, min_area_fraction=0.5 + ).cellids.astype(int) + data[f"{name}_transition"] = np.zeros(ncell, dtype=bool) + data[f"{name}_transition"][ids_trans] = True + + # Interpolate thickness points using Kriging + fp_pts = data_dir / "dikte_aquitard" / f"D{name}" / f"D{name}_interpolation_points.geojson" + gdf_pts = gpd.read_file(fp_pts) + ok = pykrige.ok.OrdinaryKriging( + gdf_pts.geometry.x.values, + gdf_pts.geometry.y.values, + gdf_pts.value.values, + variogram_model="linear", + verbose=verbose, + enable_plotting=False, + ) + xq = ix.mfgrid.xcellcenters[~data[f"{name}_mask"]] + yq = ix.mfgrid.ycellcenters[~data[f"{name}_mask"]] + kriging_result = ok.execute("points", xq, yq) + data[f"D{name}_value"] = np.zeros(ncell) + data[f"D{name}_value"][~data[f"{name}_mask"]] = kriging_result[0] + data[f"D{name}_value_unc"] = np.zeros(ncell) + data[f"D{name}_value_unc"][~data[f"{name}_mask"]] = kriging_result[1] + + # Interpolate top aquitard points using Kriging + fp_pts = data_dir / "top_aquitard" / f"T{name}" / f"T{name}_interpolation_points.geojson" + gdf_pts = gpd.read_file(fp_pts) + ok = pykrige.ok.OrdinaryKriging( + gdf_pts.geometry.x.values, + gdf_pts.geometry.y.values, + gdf_pts.value.values, + variogram_model="linear", + verbose=verbose, + enable_plotting=False, + ) + kriging_result = ok.execute("points", xq, yq) + data[f"T{name}_value"] = np.zeros(ncell) + data[f"T{name}_value"][~data[f"{name}_mask"]] = kriging_result[0] + data[f"T{name}_value_unc"] = np.zeros(ncell) + data[f"T{name}_value_unc"][~data[f"{name}_mask"]] = kriging_result[1] + + return data