Add drain_from_df helper - #564
Conversation
Build DRN packages from vector and tabular drain data with deterministic MVR provider mapping.
Clarify overlap with surface water seasonal DRN conversion and reject explicit 3D DRNs in inactive or pass-through cells.
Move explicit 3D DRNs in inactive or pass-through cells to the nearest active layer in the same column.
Share structured and vertex remapping logic and cover layered top datasets.
Reuse IDOMAIN during explicit 3D remapping and cover layered-top datasets.
Expose nearest active layer selection for boundary packages and use it from drain_from_df.
Support scalar and vector cellids with nearest-active layer selection and use it for batched drain remapping.
|
Review notes from exercising this branch against the NHFLO 1. An inactive explicit 3D cellid with a NaN elevation aborts the whole call
Reproduction on the current branch (fails): def test_inactive_3d_cellid_with_nan_elevation_is_omitted_not_aborted():
ds = test_010_wells.get_model_ds()
ds["active_domain"] = ds["botm"].notnull()
ds["active_domain"].data[0, 0, 0] = False # make (0,0,0) inactive
assert nlmod.dims.layers.get_idomain(ds).data[:, 0, 0].tolist() == [0, 1, 1]
_, gwf = test_010_wells.get_sim_and_gwf(ds)
drains = pd.DataFrame({"cellid": [(0, 0, 0)], "elevation": [np.nan], "cond": [5.0]})
drn, mapping = nlmod.gwf.drain.drain_from_df(
drains, gwf, ds, silent=True, return_provider_mapping=True)
assert drn is None and mapping.emptySuggestion: drop NaN-elevation rows before the remap batch (or skip them in it), so they are omitted consistently regardless of idomain. 2.
|
|
Reviewed this PR while assessing how the NHFLO Defects
Generalization gaps (to fully replace the manual NHFLO blocks)
Adoption notes (for the NHFLO side — no change needed here)Verified non-blockers for adopting |
Summary
nlmod.gwf.drain.drain_from_dffor DRN packages from vector/tabular drain inputnlmod.gwf.surface_water.gdf_to_seasonal_pkgnlmod.layers.get_layer_of_zwith sparse/vector cellid support and nearest-active IDOMAIN-aware lookupget_layer_of_zindrain_from_dffor batched explicit 3D DRN remappingRelationship to
surface_waterThere is deliberate overlap with
nlmod.gwf.surface_water.gdf_to_seasonal_pkgfor polygon-to-DRN conversion. Usegdf_to_seasonal_pkgfor surface-water polygons with winter/summer stages and seasonal conductance timeseries. Usedrain_from_dffor fixed drain features (line, polygon, point, or direct cellid), and when deterministic MVR provider IDs are needed for routing drain outflow.drain_from_dfreusessurface_water.build_spdfor vector and 2D-cellid layer placement, so those inputs are checked againstget_idomain(ds): fully inactive columns are omitted and drains are placed only in active cells (idomain > 0). FloPy/MODFLOW 6 receive explicit 3D cellids and do not relocate DRN boundaries. Therefore explicit 3D cellids targeting inactive (idomain == 0) or vertical pass-through (idomain < 0) cells are remapped by callingnlmod.layers.get_layer_of_z(..., nearest_active=True)for all affected direct rows in one vectorized batch.get_layer_of_znow remains backward compatible for dense full-grid calls, and additionally supports scalar/vector sparsecellid+zcalls. This makes the same IDOMAIN-aware layer lookup reusable for other boundary packages such as wells.Validation
uv run -q pytest tests\test_009_layers.py::test_get_layer_of_z tests\test_009_layers.py::test_get_layer_of_z_above_model tests\test_009_layers.py::test_get_layer_of_z_below_model tests\test_009_layers.py::test_get_layer_of_z_nearest_active_cellids tests\test_009_layers.py::test_get_layer_of_z_nearest_active_vector_cellids tests\test_027_drain.py tests\test_013_surface_water.py::test_gdf_to_seasonal_pkg -vuv run -q ruff format nlmod\dims\layers.py nlmod\gwf\drain.py tests\test_009_layers.py tests\test_027_drain.pyuv run -q ruff check --fix nlmod\gwf\drain.py tests\test_027_drain.pyuv run -q ruff check --fix --extend-ignore D103,D205,D400,B028,B905,E501,PT018,PT028 nlmod\dims\layers.py tests\test_009_layers.pyuv run -q python -m py_compile nlmod\dims\layers.py nlmod\gwf\drain.py tests\test_009_layers.py tests\test_027_drain.py