Skip to content

feat(gridgen): build GNC package input from gridgen output - #2789

Open
jdhughes-dev wants to merge 4 commits into
modflowpy:developfrom
jdhughes-dev:feat-gridgen-gnc
Open

feat(gridgen): build GNC package input from gridgen output#2789
jdhughes-dev wants to merge 4 commits into
modflowpy:developfrom
jdhughes-dev:feat-gridgen-gnc

Conversation

@jdhughes-dev

@jdhughes-dev jdhughes-dev commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
  • Added Gridgen.get_gnc to read the ghost node data gridgen writes to qtg.gnc.dat

  • Added Gridgen.get_gridprops_gnc6, Gridgen.get_gridprops_gnc5, and get_gridprops_gnc5 to convert it to MODFLOW 6 and MODFLOW-USG GNC Package input

  • Fixed MfUsgGnc.write_file, which truncated the contributing factors to three significant digits and wrote the options list with its Python repr

  • Fixed fmt_string, which joined the field formats with no separator regardless of the format of the list, so a value that filled its width ran into the next value

  • Closes bug: CLN package list output has no separator between fields #2788

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.0%. Comparing base (556c088) to head (f8df65a).
⚠️ Report is 183 commits behind head on develop.

Additional details and impacted files
@@            Coverage Diff             @@
##           develop   #2789      +/-   ##
==========================================
+ Coverage     55.5%   70.0%   +14.5%     
==========================================
  Files          644     315     -329     
  Lines       124135   65915   -58220     
==========================================
- Hits         68947   46205   -22742     
+ Misses       55188   19710   -35478     
Files with missing lines Coverage Δ
flopy/mfusg/mfusg.py 78.3% <100.0%> (+0.8%) ⬆️
flopy/mfusg/mfusggnc.py 83.0% <100.0%> (+1.9%) ⬆️
flopy/utils/__init__.py 100.0% <100.0%> (ø)
flopy/utils/gnc.py 100.0% <100.0%> (ø)
flopy/utils/gridgen.py 76.6% <100.0%> (-10.5%) ⬇️

... and 588 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Gridgen computes the ghost node data whenever it exports a grid and writes it
to qtg.gnc.dat, but the file was not read, so the data it already had was not
reaching the GNC Package.

Gridgen now reads the file with get_gnc and converts it to MODFLOW 6 and
MODFLOW-USG GNC Package input with get_gridprops_gnc6 and get_gridprops_gnc5.
Two notebooks show the workflow, and the MODFLOW 6 notebook compares the
corrected and uncorrected solutions against the exact solution for a confined
homogeneous problem with a linear head field.

Also corrects MfUsgGnc.write_file, which formatted the contributing factors
with %10.2e and truncated them to three significant digits, wrote the options
list with its Python repr, and wrote the record fields with no separator so
that a value filling its width ran into the next value.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for building Ghost Node Correction (GNC) package inputs directly from GRIDGEN output (qtg.gnc.dat) and improves MODFLOW-USG GNC/CLN-style list writing so free-format records remain tokenizable (URWORD-safe).

Changes:

  • Added Gridgen.get_gnc and helpers to read qtg.gnc.dat and convert it to MODFLOW 6 / MODFLOW-USG GNC package constructor inputs.
  • Added get_gridprops_gnc5 conversion utilities and exposed them via flopy.utils.
  • Fixed MODFLOW-USG GNC writer output (options formatting + avoid truncating contributing factors) and fixed fmt_string to separate free-format fields with spaces.

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
flopy/utils/gridgen.py Adds GRIDGEN qtg.gnc.dat reader and Gridgen helpers to build MODFLOW 6/USG GNC gridprops.
flopy/utils/gnc.py Extends utilities with MODFLOW-USG (get_gridprops_gnc5) conversion support.
flopy/utils/init.py Re-exports get_gridprops_gnc5 from flopy.utils.
flopy/mfusg/mfusggnc.py Fixes GNC package file writing (options keywords + higher-precision/free-format list output).
flopy/mfusg/mfusg.py Fixes fmt_string so free-format lists are space-separated to keep fields readable by URWORD.
autotest/test_gridgen.py Adds end-to-end and unit tests for reading GRIDGEN GNC output and building gridprops.
autotest/test_gnc.py Adds tests to validate agreement between computed vs GRIDGEN GNC and validates output formatting behavior.
.docs/Notebooks/mfusg_gnc_example.py New notebook example demonstrating MODFLOW-USG GNC workflow with GRIDGEN output.
.docs/Notebooks/gnc_example.py New/updated notebook example demonstrating MODFLOW 6 GNC workflows including GRIDGEN route.
examples/data/mfusg_transport/Ex3_CLN_Conduit/Dispersion/Conduit_Dispersion_CLN.CBB Adds/updates example dataset artifact used by documentation/examples.
Suppressed comments (1)

autotest/test_gnc.py:485

  • This assertion is duplicated (the exact same np.allclose(run(2), run(4), ...) check is performed twice). Remove the duplicate to avoid redundant solver runs and keep the test intent clear.
    assert np.allclose(run(2), run(4), atol=1e-8)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread autotest/test_gnc.py Outdated
Comment on lines +376 to +379
@requires_exe("gridgen")
@requires_pkg("shapely", "geopandas")
@requires_exe("gridgen")
@requires_pkg("shapely", "geopandas")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both removed. They are leftovers from splitting these tests across branches.

The second one is not a decorator. Line 485 repeated the assertion at the end of test_mfusg_gnc_padding:

    assert np.allclose(run(2), run(4), atol=1e-8)

    assert np.allclose(run(2), run(4), atol=1e-8)

Each run() builds and runs a MODFLOW-USG model, so the test was doing four model runs where two are needed.

Swept the rest of the split files for the same kind of damage and found nothing else.

fmt_string joined the field formats with no separator regardless of the format
of the list, so the fields of a free format list were held apart only by the
padding in each format. A value that filled its width ran into the next value
and the record could no longer be read with URWORD. Ten digit node numbers
were enough to merge four fields of a gnc record into one token, and a free
format CLN model was already writing output that only held together because of
the field widths.

The separator now follows the format of the list, the way MfList.fmt_string
already does it: a space for free format, which URWORD reads, and nothing for
fixed format, which is read by position. This replaces the sep argument added
earlier in this branch, which put the choice on the caller instead of taking
it from the format.

Closes modflowpy#2788
Running the MODFLOW-USG transport tests writes model output into the example
data directory, which left an untracked cbb file that was easy to commit by
accident. Both cases of the extension are ignored because the tests write it
in upper case.

The cbb files already tracked as expected output are unaffected, since an
ignore rule does not apply to a tracked file.
Splitting the tests across branches left a repeated requires_exe and
requires_pkg pair on one test, and a repeated assertion on another. The
repeated assertion ran the two MODFLOW-USG models a second time, so the test
did four model runs where two were needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: CLN package list output has no separator between fields

2 participants