-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #728 metaswap grid agnostic well #1256
Changes from 9 commits
ea3d2c0
f3859e5
602aaae
37f2b00
39cffe7
1de50d7
76f0775
738a19f
eaf4ed1
9e60cef
fd81100
97e5f0b
99ddd93
cecb7c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -218,6 +218,78 @@ def get_all_imod5_prj_well_times(imod5_data: dict) -> list[datetime]: | |
return sorted(set(wel_times_flat)) | ||
|
||
|
||
def derive_cellid_from_points( | ||
dst_grid: GridDataArray, | ||
x: list, | ||
y: list, | ||
layer: list, | ||
) -> GridDataArray: | ||
""" | ||
Create DataArray with Modflow6 cell identifiers based on x, y coordinates | ||
in a dataframe. For structured grid this DataArray contains 3 columns: | ||
``layer, row, column``. For unstructured grids, this contains 2 columns: | ||
``layer, cell2d``. | ||
See also: https://water.usgs.gov/water-resources/software/MODFLOW-6/mf6io_6.4.0.pdf#page=35 | ||
|
||
Note | ||
---- | ||
The "layer" coordinate should already be provided in the dataframe. | ||
To determine the layer coordinate based on screen depts, look at | ||
:func:`imod.prepare.wells.assign_wells`. | ||
|
||
Parameters | ||
---------- | ||
dst_grid: {xr.DataArray, xu.UgridDataArray} | ||
Destination grid to map the points to based on their x and y coordinates. | ||
x: {list, np.array} | ||
array-like with x-coordinates | ||
y: {list, np.array} | ||
array-like with y-coordinates | ||
layer: {list, np.array} | ||
array-like with layer-coordinates | ||
|
||
Returns | ||
------- | ||
cellid : xr.DataArray | ||
2D DataArray with a ``ncellid`` rows and 3 to 2 columns, depending | ||
on whether on a structured or unstructured grid.""" | ||
|
||
# Find indices belonging to x, y coordinates | ||
indices_cell2d = points_indices(dst_grid, out_of_bounds="ignore", x=x, y=y) | ||
# Convert cell2d indices from 0-based to 1-based. | ||
indices_cell2d = {dim: index + 1 for dim, index in indices_cell2d.items()} | ||
# Prepare layer indices, for later concatenation | ||
|
||
if isinstance(dst_grid, xu.UgridDataArray): | ||
indices_layer = xr.DataArray( | ||
layer, coords=indices_cell2d["mesh2d_nFaces"].coords | ||
) | ||
face_dim = dst_grid.ugrid.grid.face_dimension | ||
indices_cell2d_dims = [face_dim] | ||
cell2d_coords = ["cell2d"] | ||
else: | ||
indices_layer = xr.DataArray(layer, coords=indices_cell2d["x"].coords) | ||
indices_cell2d_dims = ["y", "x"] | ||
cell2d_coords = ["row", "column"] | ||
|
||
# Prepare cellid array of the right shape. | ||
cellid_ls = [indices_layer] + [indices_cell2d[dim] for dim in indices_cell2d_dims] | ||
cellid = xr.concat(cellid_ls, dim="nmax_cellid") | ||
# Rename generic dimension name "index" to ncellid. | ||
cellid = cellid.rename(index="ncellid") | ||
# Put dimensions in right order after concatenation. | ||
cellid = cellid.transpose("ncellid", "nmax_cellid") | ||
# Assign extra coordinate names. | ||
coords = { | ||
"nmax_cellid": ["layer"] + cell2d_coords, | ||
"x": ("ncellid", x), | ||
"y": ("ncellid", y), | ||
} | ||
cellid = cellid.assign_coords(coords=coords) | ||
|
||
return cellid | ||
|
||
|
||
class GridAgnosticWell(BoundaryCondition, IPointDataPackage, abc.ABC): | ||
""" | ||
Abstract base class for grid agnostic wells | ||
|
@@ -248,7 +320,7 @@ def _create_cellid( | |
df_for_cellid = assigned_wells.groupby("index").first() | ||
d_for_cellid = df_for_cellid[["x", "y", "layer"]].to_dict("list") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find the name cellid confusing. In MODFLOW terms this is the row, column, layer or the 2dcellid, layer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But row, column, layer is what I refer to with the |
||
|
||
return self._derive_cellid_from_points(like, **d_for_cellid) | ||
return derive_cellid_from_points(like, **d_for_cellid) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the well is ever moved the cellid needs to be updated. I don't know how likely that is but i cant imagine a use cage where you start with a base model with wells and then move some of them to see the effect of it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a likely use case, as the |
||
|
||
def _create_dataset_vars( | ||
self, assigned_wells: pd.DataFrame, cellid: xr.DataArray | ||
|
@@ -275,80 +347,6 @@ def _create_dataset_vars( | |
|
||
return ds_vars | ||
|
||
@staticmethod | ||
def _derive_cellid_from_points( | ||
dst_grid: GridDataArray, | ||
x: list, | ||
y: list, | ||
layer: list, | ||
) -> GridDataArray: | ||
""" | ||
Create DataArray with Modflow6 cell identifiers based on x, y coordinates | ||
in a dataframe. For structured grid this DataArray contains 3 columns: | ||
``layer, row, column``. For unstructured grids, this contains 2 columns: | ||
``layer, cell2d``. | ||
See also: https://water.usgs.gov/water-resources/software/MODFLOW-6/mf6io_6.4.0.pdf#page=35 | ||
|
||
Note | ||
---- | ||
The "layer" coordinate should already be provided in the dataframe. | ||
To determine the layer coordinate based on screen depts, look at | ||
:func:`imod.prepare.wells.assign_wells`. | ||
|
||
Parameters | ||
---------- | ||
dst_grid: {xr.DataArray, xu.UgridDataArray} | ||
Destination grid to map the points to based on their x and y coordinates. | ||
x: {list, np.array} | ||
array-like with x-coordinates | ||
y: {list, np.array} | ||
array-like with y-coordinates | ||
layer: {list, np.array} | ||
array-like with layer-coordinates | ||
|
||
Returns | ||
------- | ||
cellid : xr.DataArray | ||
2D DataArray with a ``ncellid`` rows and 3 to 2 columns, depending | ||
on whether on a structured or unstructured grid.""" | ||
|
||
# Find indices belonging to x, y coordinates | ||
indices_cell2d = points_indices(dst_grid, out_of_bounds="ignore", x=x, y=y) | ||
# Convert cell2d indices from 0-based to 1-based. | ||
indices_cell2d = {dim: index + 1 for dim, index in indices_cell2d.items()} | ||
# Prepare layer indices, for later concatenation | ||
|
||
if isinstance(dst_grid, xu.UgridDataArray): | ||
indices_layer = xr.DataArray( | ||
layer, coords=indices_cell2d["mesh2d_nFaces"].coords | ||
) | ||
face_dim = dst_grid.ugrid.grid.face_dimension | ||
indices_cell2d_dims = [face_dim] | ||
cell2d_coords = ["cell2d"] | ||
else: | ||
indices_layer = xr.DataArray(layer, coords=indices_cell2d["x"].coords) | ||
indices_cell2d_dims = ["y", "x"] | ||
cell2d_coords = ["row", "column"] | ||
|
||
# Prepare cellid array of the right shape. | ||
cellid_ls = [indices_layer] + [ | ||
indices_cell2d[dim] for dim in indices_cell2d_dims | ||
] | ||
cellid = xr.concat(cellid_ls, dim="nmax_cellid") | ||
# Rename generic dimension name "index" to ncellid. | ||
cellid = cellid.rename(index="ncellid") | ||
# Put dimensions in right order after concatenation. | ||
cellid = cellid.transpose("ncellid", "nmax_cellid") | ||
# Assign extra coordinate names. | ||
coords = { | ||
"nmax_cellid": ["layer"] + cell2d_coords, | ||
"x": ("ncellid", x), | ||
"y": ("ncellid", y), | ||
} | ||
cellid = cellid.assign_coords(coords=coords) | ||
|
||
return cellid | ||
|
||
def render(self, directory, pkgname, globaltimes, binary): | ||
raise NotImplementedError( | ||
textwrap.dedent( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
import pandas as pd | ||
import xarray as xr | ||
|
||
from imod.mf6.wel import WellDisStructured | ||
from imod.mf6.mf6_wel_adapter import Mf6Wel | ||
from imod.msw.fixed_format import VariableMetaData | ||
from imod.msw.pkgbase import MetaSwapPackage | ||
|
||
|
@@ -22,7 +22,7 @@ class Sprinkling(MetaSwapPackage): | |
max_abstraction_surfacewater: array of floats (xr.DataArray) | ||
Describes the maximum abstraction of surfacewater to SVAT units in m3 | ||
per day. This array must not have a subunit coordinate. | ||
well: WellDisStructured | ||
well: Mf6Wel | ||
Describes the sprinkling of SVAT units coming groundwater. | ||
""" | ||
|
||
|
@@ -55,23 +55,29 @@ def __init__( | |
self, | ||
max_abstraction_groundwater: xr.DataArray, | ||
max_abstraction_surfacewater: xr.DataArray, | ||
well: WellDisStructured, | ||
well: Mf6Wel, | ||
): | ||
super().__init__() | ||
self.dataset["max_abstraction_groundwater_m3_d"] = max_abstraction_groundwater | ||
self.dataset["max_abstraction_surfacewater_m3_d"] = max_abstraction_surfacewater | ||
if not isinstance(well, Mf6Wel): | ||
raise TypeError(rf"well not of type 'Mf6Wel', got '{type(well)}'") | ||
self.well = well | ||
|
||
self._pkgcheck() | ||
|
||
def _render(self, file, index, svat): | ||
well_row = self.well["row"] - 1 | ||
well_column = self.well["column"] - 1 | ||
well_layer = self.well["layer"] | ||
cellid = self.well["cellid"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could rename this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done for this local variable, also in the CouplerMapping equivalent function |
||
if len(cellid.coords["nmax_cellid"]) != 3: | ||
raise TypeError("Coupling to unstructured grids is not supported.") | ||
|
||
well_layer = cellid.sel(nmax_cellid="layer").data | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I based this name on an example I found in the UGRID conventions. There it is named In our case this is not happening, so the "max" is wrong here. I'll rename to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking better at this: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After a brief discussion with @Manangka, I renamed it to |
||
well_row = cellid.sel(nmax_cellid="row").data - 1 | ||
well_column = cellid.sel(nmax_cellid="column").data - 1 | ||
|
||
n_subunit = svat["subunit"].size | ||
|
||
well_svat = svat.values[:, well_row, well_column] | ||
well_svat = svat.data[:, well_row, well_column] | ||
well_active = well_svat != 0 | ||
|
||
# Tile well_layers for each subunit | ||
|
@@ -80,7 +86,7 @@ def _render(self, file, index, svat): | |
data_dict = {"svat": well_svat[well_active], "layer": layer[well_active]} | ||
|
||
for var in self._without_subunit: | ||
well_arr = self.dataset[var].values[well_row, well_column] | ||
well_arr = self.dataset[var].data[well_row, well_column] | ||
well_arr = np.tile(well_arr, (n_subunit, 1)) | ||
data_dict[var] = well_arr[well_active] | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would unique_assigned_wells be a better name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, renamed