Skip to content

feat(mfusg): add Transient IBOUND (TIB) package#2724

Open
reneangermeyer wants to merge 1 commit intomodflowpy:developfrom
reneangermeyer:feature/mfusg-tib-package
Open

feat(mfusg): add Transient IBOUND (TIB) package#2724
reneangermeyer wants to merge 1 commit intomodflowpy:developfrom
reneangermeyer:feature/mfusg-tib-package

Conversation

@reneangermeyer
Copy link
Contributor

The TIB package allows changing IBOUND and ICBUND values at any stress period in MODFLOW-USG. Use cases include excavation/reclamation, well drilling/plugging, and transient prescribed head/concentration cells. FloPy currently has no TIB support.

Supports six data categories per stress period: ib0, ib1, ibm1 (IBOUND) and icb0, icb1, icbm1 (ICBUND). Node arrays (ib0/icb0) use U2DINT format via Util2d, while list records (ib1/ibm1/icb1/icbm1) support HEAD/AVHEAD and CONC/AVCONC keywords. As required by the TIB specification, all node numbers are global — for structured grids, model.dis.get_node() can be used to convert (layer, row, col) tuples.

Changes:

  • flopy/mfusg/mfusgtib.py: new MfUsgTib(Package) class with write_file, load, and get_empty
  • flopy/mfusg/init.py: add import and all entry, remove pre-existing duplicate MfUsgGnc entry
  • flopy/mfusg/mfusg.py: register TIB in mfnam_packages
  • autotest/test_mfusg_tib.py: 10 tests covering round-trip, transport data, empty stress periods, input validation, and both DIS and DISU grids

Add MfUsgTib class for changing IBOUND and ICBUND values at any stress
period in MODFLOW-USG. Supports six data categories (ib0, ib1, ibm1,
icb0, icb1, icbm1) with U2DINT arrays and line-by-line records.

Also fix pre-existing duplicate MfUsgGnc entry in mfusg __all__.
@codecov
Copy link

codecov bot commented Mar 3, 2026

Codecov Report

❌ Patch coverage is 90.55556% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.4%. Comparing base (556c088) to head (1c57750).
⚠️ Report is 136 commits behind head on develop.

Files with missing lines Patch % Lines
flopy/mfusg/mfusgtib.py 90.5% 17 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2724      +/-   ##
===========================================
+ Coverage     55.5%    72.4%   +16.9%     
===========================================
  Files          644      676      +32     
  Lines       124135   130920    +6785     
===========================================
+ Hits         68947    94915   +25968     
+ Misses       55188    36005   -19183     
Files with missing lines Coverage Δ
flopy/mfusg/__init__.py 100.0% <100.0%> (ø)
flopy/mfusg/mfusg.py 78.3% <ø> (+0.8%) ⬆️
flopy/mfusg/mfusgtib.py 90.5% <90.5%> (ø)

... and 569 files with indirect coverage changes

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

@reneangermeyer reneangermeyer marked this pull request as ready for review March 3, 2026 15:02
@christianlangevin
Copy link

@aymanalz, how does this look to you? I took a look and think it seems okay. Maybe there is a USG-T example we could use to verify?

Copy link

@christianlangevin christianlangevin left a comment

Choose a reason for hiding this comment

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

Looks good to me, but wondering if there is a better test that could be added, perhaps like what is done in test_usg_transport.py. Also, requested copilot review. Will see what that shows. Thanks for the contribution!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds FloPy support for the MODFLOW-USG Transient IBOUND (TIB) package, enabling transient updates to IBOUND/ICBUND data by stress period.

Changes:

  • Introduces new MfUsgTib package with write_file(), load(), and get_empty() helpers.
  • Registers TIB in MfUsg.mfnam_packages and exports it from flopy.mfusg.
  • Adds a new test suite covering structured (DIS) and unstructured (DISU) workflows and round-trip I/O.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
flopy/mfusg/mfusgtib.py Implements the new TIB package, including serialization/deserialization and stress-period data handling.
flopy/mfusg/mfusg.py Registers "tib" in mfnam_packages for model loading support.
flopy/mfusg/init.py Exposes MfUsgTib at flopy.mfusg and removes a duplicate MfUsgGnc export.
autotest/test_mfusg_tib.py Adds unit tests for round-trip behavior, transport keywords, empty stress periods, validation, and DISU usage.

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

Comment on lines +313 to +317
return cls(
model,
stress_period_data=stress_period_data,
add_package=True,
)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

load() doesn’t use ext_unit_dict to set the package unitnumber and filenames. Most other MFUSG package loaders call get_unitnumber_from_ext_unit_dict() so a model loaded from a NAM file preserves the original unit number and file name; without it, TIB will default to unit 160 and modelname.tib, which can break round-tripping of existing models. Consider retrieving unitnumber, filenames from ext_unit_dict (when provided) and passing them into the constructor in the return statement.

Copilot uses AI. Check for mistakes.
Comment on lines +237 to +238
elif keyword in ("AVHEAD", "AVCONC"):
f_obj.write(f" {node_1based:9d} {keyword}\n")
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

AVHEAD and AVCONC are currently accepted/written for any list-record category (ib1/ibm1/icb1/icbm1). Per the package description, ib* records should only allow HEAD/AVHEAD and icb* records should only allow CONC/AVCONC; allowing AVCONC under IBOUND (or AVHEAD under ICBUND) can generate invalid input files. Consider restricting the allowed keywords based on is_icb and raising a clearer error when a keyword is used in the wrong category.

Suggested change
elif keyword in ("AVHEAD", "AVCONC"):
f_obj.write(f" {node_1based:9d} {keyword}\n")
elif (not is_icb) and keyword == "AVHEAD":
f_obj.write(f" {node_1based:9d} AVHEAD\n")
elif is_icb and keyword == "AVCONC":
f_obj.write(f" {node_1based:9d} AVCONC\n")

Copilot uses AI. Check for mistakes.
Comment on lines +126 to +133
for kper, data in spd.items():
if not isinstance(kper, (int, np.integer)):
raise ValueError(f"Stress period key must be int, got {type(kper)}.")
if not isinstance(data, dict):
raise ValueError(
f"Data for stress period {kper} must be a dict, got {type(data)}."
)
for key in data:
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

_validate_stress_period_data() validates key types but doesn’t validate the stress period numbers themselves. If a user provides stress_period_data for kper < 0 or kper >= model.nper, those entries will be silently ignored by write_file() (because it iterates range(nper)), which can lead to unexpected data loss. Consider validating that each kper is within [0, model.nper-1] during initialization (either by passing model.nper into the validator or adding a check in __init__).

Copilot uses AI. Check for mistakes.
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.

3 participants