feat(mfusg): add Transient IBOUND (TIB) package#2724
feat(mfusg): add Transient IBOUND (TIB) package#2724reneangermeyer wants to merge 1 commit intomodflowpy:developfrom
Conversation
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 Report❌ Patch coverage is
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
🚀 New features to boost your workflow:
|
|
@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? |
christianlangevin
left a comment
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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
MfUsgTibpackage withwrite_file(),load(), andget_empty()helpers. - Registers TIB in
MfUsg.mfnam_packagesand exports it fromflopy.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.
| return cls( | ||
| model, | ||
| stress_period_data=stress_period_data, | ||
| add_package=True, | ||
| ) |
There was a problem hiding this comment.
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.
| elif keyword in ("AVHEAD", "AVCONC"): | ||
| f_obj.write(f" {node_1based:9d} {keyword}\n") |
There was a problem hiding this comment.
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.
| 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") |
| 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: |
There was a problem hiding this comment.
_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__).
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: