feat(gnc): add ghost node correction data generation - #2787
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds first-class support in FloPy for generating Ghost Node Correction (GNC) package inputs for quadtree-like grids, either by reading GRIDGEN’s qtg.gnc.dat output or by computing equivalent GNC records directly from a model grid/connectivity. It also updates MODFLOW‑USG GNC file writing to avoid truncating contributing factors.
Changes:
- Added
flopy.utils.gncto compute/check GNC records and convert them into MODFLOW 6 / MODFLOW‑USG package constructor dictionaries. - Extended
Gridgenwithget_gnc,get_gridprops_gnc6, andget_gridprops_gnc5wrappers to consume GRIDGEN’s ghost node output. - Updated
MfUsgGnc.write_fileformatting behavior and added tests + notebooks demonstrating MF6 and MFUSG usage.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| flopy/utils/gridgen.py | Adds GRIDGEN-side GNC readers and helpers to build MF6/MFUSG GNC package inputs. |
| flopy/utils/gnc.py | New utility module to compute/check GNC records and build MF6/MFUSG constructor dictionaries. |
| flopy/utils/init.py | Re-exports new GNC utilities at flopy.utils level. |
| flopy/mfusg/mfusggnc.py | Adjusts USG GNC package writing (options serialization + higher precision formatting). |
| flopy/mfusg/init.py | Removes duplicate MfUsgGnc export entry. |
| autotest/test_gridgen.py | Adds integration tests validating GRIDGEN output reading and gridprops generation + round-trip. |
| autotest/test_gnc.py | Adds unit/integration tests for computed GNC matching GRIDGEN and solver invariants (padding). |
| .docs/Notebooks/mfusg_gnc_example.py | New MFUSG-focused notebook example for GRIDGEN-derived GNC. |
| .docs/Notebooks/gnc_example.py | New MF6 notebook example for GRIDGEN-derived and computed GNC workflows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #2787 +/- ##
===========================================
+ Coverage 55.5% 72.7% +17.1%
===========================================
Files 644 662 +18
Lines 124135 132609 +8474
===========================================
+ Hits 68947 96422 +27475
+ Misses 55188 36187 -19001
🚀 New features to boost your workflow:
|
Ghost node correction data could not be built with FloPy, so the GNC Package was difficult to use on a quadtree grid even though the correction is what makes the control volume formulation accurate where a coarse cell meets a finer one. The new flopy.utils.gnc module computes the data from a model grid, a grid conforming array of refinement levels, and the grid connectivity, and converts it to MODFLOW 6 GNC Package input. Connectivity is taken from the grid when it is not supplied, either from the iac and ja an unstructured grid carries or from the cells that share an edge. Records with fewer contributing cells than numalphaj repeat a cell and split its contributing factor rather than pad with a cellid of zero, so that the same records are also valid MODFLOW-USG input. The computed data reproduces the ghost nodes gridgen writes to qtg.gnc.dat exactly, which is checked over five grid configurations. Also removes a duplicate MfUsgGnc entry from the flopy.mfusg __all__ list, which ruff check reports as RUF068 and which blocks the lint job.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (1)
flopy/utils/gnc.py:105
- _check_gnc() can raise an IndexError instead of a ValueError when a record contains out-of-range node numbers (e.g., n >= nnodes). Since this function is used as a validation step (get_gridprops_gnc6(check=True)), it should detect invalid n/m indices explicitly and raise a clear ValueError message instead of crashing while slicing ia/ja.
for irec, rec in enumerate(gnc):
n = rec["n"]
neighbors = ja[ia[n] : ia[n + 1]]
# MODFLOW 6 rejects a ghost node whose n-m connection is absent
if rec["m"] not in neighbors:
flopy.utils.gncto compute ghost node correction data from a model grid, a grid conforming array of refinement levels, and the grid connectivityget_gridprops_gnc6to convert the data to MODFLOW 6 GNC Package inputqtg.gnc.datexactly, checked over five grid configurationsBase for #2789 and #2790, which build on this.