Skip to content

fix(mf6): find external files in model subdirectories with any path separator - #2879

Merged
wpbonelli merged 1 commit into
modflowpy:developfrom
hydrocomputing:fix-external-file-path-separator
Oct 31, 2026
Merged

wpbonelli merged 1 commit into
modflowpy:developfrom
hydrocomputing:fix-external-file-path-separator

Conversation

@pya

@pya pya commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Close #2878

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).

  • autotest/test_mf6.py: new test_load_subdir_ext_files_with_any_sep,
    parametrized with posix and win separators. It writes a model with
    model_rel_path="level2" and external array (DIS) and list (CHD) data, rewrites
    the OPEN/CLOSE paths to level2<sep>dat<sep><file>, reloads the simulation and
    checks botm and the CHD stress period data. The win case is skipped on macOS
    and Linux, where \ is not a path separator.

Testing

On Windows 11, Python 3.14, MODFLOW 6.8.2:

  • Without the fix, test_load_subdir_ext_files_with_any_sep[posix] fails with the
    doubled model directory; [win] passes. With the fix, both pass.
  • Related existing tests pass: test_subdir, test_basic_gwf,
    test_load_sim_when_namefile_uses_rel_paths.
  • Smoke tests (pytest -n auto -S): the tests that fail with this change fail
    identically without it. They need executables missing in my environment
    (e.g. gridgen, mf2005).
  • ruff check . and ruff format --check . pass.

Not tested on macOS or Linux; CI will cover these.

…eparator

When loading a model with a model relative path, flopy checks whether an
external file path already starts with the model directory before
prepending it. The check split the path on `os.path.sep`, so on Windows a
path like `level2/dat/botm.txt` was not recognized and the model directory
was prepended again (`level2\level2/dat/botm.txt"``. Split with
`PureWindowsPath(...).parts` instead, which accepts `/`` and `\`` on all
platforms, and compare the leading parts as a whole.

Close modflowpy#2878
@pya pya changed the title fix(mf6): find external files in model subdirectories with any path separator fix(mf6): find external files in model subdirectories with any path separator Oct 31, 2026
@codecov

codecov Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.42857% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.7%. Comparing base (556c088) to head (a10000f).
⚠️ Report is 249 commits behind head on develop.

Files with missing lines Patch % Lines
flopy/mf6/data/mfdataplist.py 55.5% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2879      +/-   ##
===========================================
+ Coverage     55.5%    72.7%   +17.1%     
===========================================
  Files          644      659      +15     
  Lines       124135   132499    +8364     
===========================================
+ Hits         68947    96373   +27426     
+ Misses       55188    36126   -19062     
Files with missing lines Coverage Δ
flopy/mf6/data/mfdatastorage.py 70.7% <100.0%> (-3.3%) ⬇️
flopy/mf6/data/mfdataplist.py 72.2% <55.5%> (-6.7%) ⬇️

... and 584 files with indirect coverage changes

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

@wpbonelli wpbonelli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe worth a small function somewhere for the identical code? No strong feelings

I also wonder if we should really be comparing case sensitively, but that is a separate question from this bug

Thanks for this @pya

@wpbonelli

Copy link
Copy Markdown
Member

the CI failures are unrelated

@wpbonelli
wpbonelli merged commit ad4623e into modflowpy:develop Oct 31, 2026
20 of 25 checks passed
@pya

pya commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

@wpbonelli Good points.

I though about a separate function that takes care of such kind of path handling. Maybe there are other places that do similar things. I was not sure if the reuse potential is high enough yet. Maybe worth some refactoring that makes all path handling more consistent.

As for case sensitiv, as far as I know, only Linux is truly case sensitive whereas Windows and macOS are only case preserving. I think using pathlib as much as possible should help to take care of this. Another TODO for the refactoring. ;)

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: external files of models in subdirectories not found on Windows when OPEN/CLOSE paths use forward slashes

2 participants