Skip to content

Release 3.12.0 - #2880

Merged
wpbonelli merged 34 commits into
masterfrom
v3.12.0
Nov 1, 2026
Merged

wpbonelli merged 34 commits into
masterfrom
v3.12.0

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

FloPy 3.12.0

The release can be approved by merging this pull request into master. This will trigger a final job to publish the release to PyPI.

jlarsen-usgs and others added 30 commits September 3, 2026 10:01
* update(Mf6Splitter): update node_map representation to numpy arrays
* add a `no_remap_key` that can be used to ignore remapping inactive cells
   - no remap is automatically applied when `active_only` is True in `optimize_splitting_mask`

* Minor updates for boundname observations

* update self._connection representation

* change from neighbor dict to iac, ja representation to reduce memory

* Add test for reconstruct_recarray
* Fix(test_save_load_node_mapping_structured): update node_map to node_map_arr to follow new convention

* remove sklearn requirement

* linting
)

optimize_splitting_mask() built the adjacency graph without telling metis where
the barriers are, so a partition was free to cut one and the barriers were then
put back together by moving cells between models after the partition was built.
Moving cells undoes the balance metis produced. Pass edge weights that make a
face a barrier crosses expensive to cut, so metis routes the partition around it,
and keep the check afterward for any barrier that is still split. On a 40 by 40
model with three barriers the worst imbalance over three seeds and 4, 6, and 8
parts drops from 1.030 to 1.005.

Also report the cells the check moves when verbose is set, and correct the
spelling of split in the error message.
* test(model_splitter): cover the horizontal flow barrier package

The HFB package had no splitting test. Split a model with barriers on either side
of the split column and check that each model keeps its own barriers and that the
heads reconstruct to the original model, then check that splitting a model along a
barrier is an error.

* test(model_splitter): add a barrier junction and vertical barriers

Cover the corner four cells share, where two barriers between east west
neighbors and two between north south neighbors meet, and check that splitting
on either the row or the column the junction straddles is an error. Add barriers
on vertical connections, which the splitter never divides because both cells of
one are in the same stack.
* single reach models crashed due to np.diff(reach_data).max() > 0 comparison,
fix for this issue breaks up this comparison by taking the diff first and
evaluating if the diff returns an empty result prior to taking the max()

* Add single reach test case to test_sfr.py
* add a CI job to make sure the PR title follows commit guidelines
* expand release guide, reference it from DEVELOPER.md
Restore a CI job to automatically open a pull request from master into develop after a release. We had this a while ago but it was removed in #1856. Drop the old manual instructions from the release guide. Tweak update_version.py
use pyvista/setup-headless-display-action everywhere and drop the old osmesa workaround
Use a consistent, configurable retry mechanism for all three network calls

* list releases
* get info for a release
* download a release archive

Previously the download didn't retry, just the first two queries
…2840)

* Fix(test_save_load_node_mapping_structured): update node_map to node_map_arr to follow new convention

* update(_remap_sfr): add more robust handling for (0, 0, 0) SFR cells

* add logic to traverse the connectiondata graph to find the nearest connected GWF/SFR cell in which (0, 0, 0) cells are routed to

* Add testing for SFR None cells which are disconnected from the GWF system
MF6 list files can contain values with three-digit exponents written without the "E", a Fortran quirk for E/ES formats without an explicit exponent width. This caused ListBudget parsing to fail. Tolerate this when parsing budget lines. MF6 will be fixed too, but list files from existing versions will still contain these values.

Close #2855
#2809 moved the docs requirements from the doc extra to a docs dependency group, but .readthedocs.yml still installed .[doc]. This broke the RTD build.
* Add pathlib support to `Raster.write()`

* fix(to_geodataframe): fix data parsing for stress periods where data is not explicitly specified

* Add test for incomplete (implicit) stress period data

* Trap for case where Transient List data is None
* update(optimize_splitting_mask): add edge weighting to graph for LAK packages
* refactor weighting code for HFB edge weights
* add testing
…sed (#2859)

If ListBudget failed to parse a value in the first budget table, rather than raise an error, it would just print a message and continue with bad internal state, eventually raising a misleading unpacking error wrapped in a generic Exception. The only mention of the actual problem was the printed line, which easily gets buried under the unhelpful exceptions.

If any values in the first budget table fail to parse, raise ValueError naming the time step, stress period, and invalid line. I left behavior for parse failures in later budget tables unchanged: print a message and fill that time step with NaN. This means the same bad value will raise in the first table but become NaN in any other. This inconsistency isn't good (neither is giving NaN for bad values, IMO, since it makes it impossible to distinguish legitimate NaNs from parse failures), but people may have come to expect and rely on this behavior, so it seems safest to leave a comprehensive fix for 4.x.

Also reword list file parsing messages: "time step x, stress period y" instead of "ts,sp x y", readable names (e.g. "time step length" instead of "tslen"), and report the bad line where available.

This situation will now raise an error like

ValueError: unable to read first budget in list file model.lst: could not parse value for FLOW-JA-FACE (OUT) at time step 1, stress period 1: 'FLOW-JA-FACE =       8.5159-100 ...'
…#2872)

get-modflow only accepted the executables, modflow6, and modflow6-nightly-build repositories. Any repository can now be named with --repo or repo, provided its release assets are named with the operating system tag like those of the official repositories (e.g. linux.zip, macarm.zip, win64.zip).
The ubuntu-latest runner image moved to Ubuntu 26.04, whose system Python is 3.14. Jobs that didn't pass python-version to setup-uv failed building fiona from source (no cp314 wheels). The PYTHON_VERSION env var in the smoke and optional jobs was never read by uv.
SPEC 0 recommends dropping numpy <=2.1.0 (as of 2026 Q3) and pandas <=2.2.0 (as of 2026 Q1), leaving numpy 2.2.0 and pandas 2.3.0 as the minimum supported versions.
wpbonelli and others added 4 commits October 31, 2026 15:07
SPEC 0 recommends dropping Python 3.11 as of 2025 Q4. Bump the min to 3.12.
…eparator (#2879)

On Windows, MFSimulation.load() fails for a model in a subdirectory when the
OPEN/CLOSE paths of its external files use forward slashes, e.g. models written
by iMOD Python. Flopy prepends the model directory a second time and cannot find
the file (level2\level2/dat/gwf.dis_botm.txt). MODFLOW 6 accepts these files, and
flopy loads them on macOS and Linux.

Cause
------

The check added in #1298 (fix for #1289), which decides whether the model relative
path still has to be prepended, split paths on os.path.sep. On Windows that is \,
so a path with / comes back as a single item and never matches the model directory.

Changes
---------

flopy/mf6/data/mfdatastorage.py (array data) and flopy/mf6/data/mfdataplist.py (list data, two places): split both paths with PureWindowsPath(...).parts, which accepts / and \ on every OS, and prepend the model relative path only if the file path does not start with all of its parts:

    fp_parts = PureWindowsPath(fp_relative).parts
    rp_parts = PureWindowsPath(rel_path).parts
    if fp_parts[: len(rp_parts)] != rp_parts:
        fp_relative = os.path.join(rel_path, fp_relative)

Comparing the leading parts as a whole also fixes two edge cases of the old loop with multi-level model relative paths: an IndexError when the file path has fewer parts than the model relative path (e.g. k.dat with models/gwf), and prepending only some components (e.g. gwf/k.dat with models/gwf became models/gwf/k.dat).
@wpbonelli
wpbonelli marked this pull request as ready for review November 1, 2026 01:32
@wpbonelli
wpbonelli merged commit 1c5f475 into master Nov 1, 2026
18 of 19 checks passed
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.

6 participants