Execution plan for a consistent PWN boundary-condition structure. Self-contained: independent of nlmod — no upstream issues or PRs are part of this work. Every new function carries a docstring section Path forward to nlmod describing where it would eventually live upstream; that note is the future upstreaming spec and the deletion criterion once nlmod ships it.
modelscript: load geojson/shapefile via nhflodata paths, map columns to the canonical schema
boundname | stage | rbot/elev | c OR cond_per_m OR cond_per_m2 | concentration
① nlmod.grid.gdf_to_grid(gdf, ds) # gridding (exists)
② aggregate_per_feature_per_cell(gdf_grid, methods) # pieces of one feature merge per cell
③ aggregate_per_cell(per_feature, methods) # OPTIONAL cross-feature merge
④ nlmod.gwf.build_spd(celldata, kind, ds, layer_method) # exists
⑤ bc_package(...) # flopy ctor + boundnames + aux + ssm_sources
side product of ①: open_water_fraction(gdf_grid, ds) # feeds RCH/EVT/DRN land-share scaling
Context: consistency review on NHFLO/models#135; fractional-coverage groundwork in #71.
Execution plan for a consistent PWN boundary-condition structure. Self-contained: independent of nlmod — no upstream issues or PRs are part of this work. Every new function carries a docstring section
Path forward to nlmoddescribing where it would eventually live upstream; that note is the future upstreaming spec and the deletion criterion once nlmod ships it.Target pipeline (per stage-boundary BC)
New functions in
nhflotools/boundaries.pyaggregate_per_feature_per_cell(gdf_grid, fields_methods, boundname='boundname')— groupby[cellid, boundname]; replaces the per-feature groupby loop inlakes._aggregate_lake_cells. Path forward to nlmod: aby=parameter onnlmod.grid.aggregate_vector_per_cell(filed as aggregate_vector_per_cell: support grouping per feature (by=['cellid', boundname]) gwmod/nlmod#589).cond_from_measure(gdf_grid)— conductance from clipped geometry:area/c,length*cond_per_m,area*cond_per_m2, plaincondfor points; recomputes clipped measures from.geometry(carriedareacan be stale; thegdf_to_gridline-lengthbranch is dead code); handles mixed geometry types in one call. Path forward to nlmod: helper innlmod.grid+ the line-length fix.open_water_fraction(gdf_grid, ds)— clipped piece areas summed per cell / cell area, clipped at 1. Deduplicateslake_coverage(carve_lake_cells) andpanden_coverage(riv_from_oppervlakte_pwn). Path forward to nlmod:nlmod.gridhelper or part ofgdf_to_gridoutput.bc_package(celldata, kind, ds, gwf, *, pname, layer_method='lay_of_rbot')—build_spd+ flopy constructor with boundnames, aux CONCENTRATION, save_flows, ssm_sources registration. Path forward to nlmod: uniform package wrapper; WEL self-registration is wel_from_df / maw_from_df do not register aux concentration in ssm_sources gwmod/nlmod#578.get_oppervlakte_pwn_shapes(exists), a waterboard level-areas loader out ofdrn_from_waterboard_data, an RWS loader out ofmajor_surface_waters. Hardcoded knowledge (ICAS 2.8 / IKIEF 5.8 m NAP, 'None' sanitizing, HHNK quirks) lives here.Migration order (one tools PR each + lockstep models commit — models installs nhflotools@main)
09pwnmodel2/01_pwnmodel2.py): purest canonical case (lines + polygons, per-measure metadata). Proves the pipeline.riv_from_oppervlakte_pwn(ds, gwf, data_path)->(ds, gwf, gdf); per-feature step fixes theNaam: 'first'boundname loss on shared cells._aggregate_lake_cellson the per-feature aggregator; carve/threshold/LAK prep stay lake-specific. Step ③ deliberately skipped (per-lake budgets, boundnames, future MVR/outlets).(ds, gwf, gdf, ...); the vlakdekkend full-cell conductance ([polder] Waterboard DRN conductance uses full cell area instead of intersected peilgebied area (contradicts its docstring) #51) becomes an explicit override ofcond_from_measure; maaiveld fallback stays a documented domain-wide step.discretize_surface_wateris a different paradigm; join the pipeline or document as the exception. Wells/HFB stay outside (point/face family).Guardrails
Context: consistency review on NHFLO/models#135; fractional-coverage groundwork in #71.