Skip to content

Commit

Permalink
Merge pull request #1972 from modflowpy/v3.4.3
Browse files Browse the repository at this point in the history
Release 3.4.3
  • Loading branch information
wpbonelli authored Sep 30, 2023
2 parents 1f06875 + 0787432 commit 45cb0fa
Show file tree
Hide file tree
Showing 90 changed files with 788 additions and 247 deletions.
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ message: If you use this software, please cite both the article from preferred-c
and the software itself.
type: software
title: FloPy
version: 3.4.2
date-released: '2023-08-25'
version: 3.4.3
date-released: '2023-09-30'
doi: 10.5066/F7BK19FH
abstract: A Python package to create, run, and post-process MODFLOW-based models.
repository-artifact: https://pypi.org/project/flopy
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

<img src="https://raw.githubusercontent.com/modflowpy/flopy/master/examples/images/flopy3.png" alt="flopy3" style="width:50;height:20">

### Version 3.4.2
### Version 3.4.3
[![flopy continuous integration](https://github.com/modflowpy/flopy/actions/workflows/commit.yml/badge.svg?branch=develop)](https://github.com/modflowpy/flopy/actions/workflows/commit.yml)
[![Read the Docs](https://github.com/modflowpy/flopy/actions/workflows/rtd.yml/badge.svg?branch=develop)](https://github.com/modflowpy/flopy/actions/workflows/rtd.yml)

Expand Down Expand Up @@ -142,7 +142,7 @@ How to Cite

##### ***Software/Code citation for FloPy:***

[Bakker, Mark, Post, Vincent, Hughes, J. D., Langevin, C. D., White, J. T., Leaf, A. T., Paulinski, S. R., Bellino, J. C., Morway, E. D., Toews, M. W., Larsen, J. D., Fienen, M. N., Starn, J. J., Brakenhoff, D. A., and Bonelli, W. P., 2023, FloPy v3.4.2: U.S. Geological Survey Software Release, 25 August 2023, https://doi.org/10.5066/F7BK19FH](https://doi.org/10.5066/F7BK19FH)
[Bakker, Mark, Post, Vincent, Hughes, J. D., Langevin, C. D., White, J. T., Leaf, A. T., Paulinski, S. R., Bellino, J. C., Morway, E. D., Toews, M. W., Larsen, J. D., Fienen, M. N., Starn, J. J., Brakenhoff, D. A., and Bonelli, W. P., 2023, FloPy v3.4.3: U.S. Geological Survey Software Release, 30 September 2023, https://doi.org/10.5066/F7BK19FH](https://doi.org/10.5066/F7BK19FH)


Additional FloPy Related Publications
Expand Down
170 changes: 170 additions & 0 deletions autotest/test_binaryfile.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import os
from itertools import repeat

import numpy as np
import pytest
from autotest.conftest import get_example_data_path
from matplotlib import pyplot as plt
from matplotlib.axes import Axes
from modflow_devtools.markers import requires_exe

import flopy
from flopy.modflow import Modflow
from flopy.utils import (
BinaryHeader,
Expand Down Expand Up @@ -545,3 +548,170 @@ def test_cellbudgetfile_readrecord_waux(example_data_path):
record
)
v.close()


@pytest.fixture
@pytest.mark.mf6
@requires_exe("mf6")
def mf6_gwf_2sp_st_tr(function_tmpdir):
"""
A basic flow model with 2 stress periods,
first steady-state, the second transient.
"""

name = "mf6_gwf_2sp"
sim = flopy.mf6.MFSimulation(
sim_name=name,
version="mf6",
exe_name="mf6",
sim_ws=function_tmpdir,
)

tdis = flopy.mf6.ModflowTdis(
simulation=sim,
nper=2,
perioddata=[(0, 1, 1), (10, 10, 1)],
)

ims = flopy.mf6.ModflowIms(
simulation=sim,
complexity="SIMPLE",
)

gwf = flopy.mf6.ModflowGwf(
simulation=sim,
modelname=name,
save_flows=True,
)

dis = flopy.mf6.ModflowGwfdis(
model=gwf, nlay=1, nrow=1, ncol=10, delr=1, delc=10, top=10, botm=0
)

npf = flopy.mf6.ModflowGwfnpf(
model=gwf,
icelltype=[0],
k=10,
)

ic = flopy.mf6.ModflowGwfic(
model=gwf,
strt=0,
)

wel = flopy.mf6.ModflowGwfwel(
model=gwf,
stress_period_data={0: 0, 1: [[(0, 0, 0), -1]]},
)

sto = flopy.mf6.ModflowGwfsto(
model=gwf,
ss=1e-4,
steady_state={0: True},
transient={1: True},
)

chd = flopy.mf6.ModflowGwfchd(
model=gwf,
stress_period_data={0: [[(0, 0, 9), 0]]},
)

oc = flopy.mf6.ModflowGwfoc(
model=gwf,
budget_filerecord=f"{name}.cbc",
head_filerecord=f"{name}.hds",
saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")],
)

return sim


def test_read_mf6_2sp(mf6_gwf_2sp_st_tr):
sim = mf6_gwf_2sp_st_tr
gwf = sim.get_model()
sim.write_simulation(silent=False)
success, _ = sim.run_simulation(silent=False)
assert success

# load heads and flows
hds = gwf.output.head()
cbb = gwf.output.budget()

# check times
exp_times = [float(t) for t in range(11)]
assert hds.get_times() == exp_times
assert cbb.get_times() == exp_times

# check stress periods and time steps
exp_kstpkper = [(0, 0)] + [(i, 1) for i in range(10)]
assert hds.get_kstpkper() == exp_kstpkper
assert cbb.get_kstpkper() == exp_kstpkper

# check head data access by time
exp_hds_data = np.array([[list(repeat(0.0, 10))]])
hds_data = hds.get_data(totim=0)
assert np.array_equal(hds_data, exp_hds_data)

# check budget file data by time
cbb_data = cbb.get_data(totim=0)
assert len(cbb_data) > 0

# check head data access by kstp and kper
hds_data = hds.get_data(kstpkper=(0, 0))
assert np.array_equal(hds_data, exp_hds_data)

# check budget file data by kstp and kper
cbb_data_kstpkper = cbb.get_data(kstpkper=(0, 0))
assert len(cbb_data) == len(cbb_data_kstpkper)
for i in range(len(cbb_data)):
assert np.array_equal(cbb_data[i], cbb_data_kstpkper[i])


@pytest.mark.parametrize("compact", [True, False])
def test_read_mf2005_freyberg(example_data_path, function_tmpdir, compact):
m = flopy.modflow.Modflow.load(
example_data_path / "freyberg" / "freyberg.nam",
)
m.change_model_ws(function_tmpdir)
oc = m.get_package("OC")
oc.compact = compact

m.write_input()
success, buff = m.run_model(silent=False)
assert success

# load heads and flows
hds_file = function_tmpdir / "freyberg.hds"
cbb_file = function_tmpdir / "freyberg.cbc"
assert hds_file.is_file()
assert cbb_file.is_file()
hds = HeadFile(hds_file)
cbb = CellBudgetFile(cbb_file, model=m) # failing to specify a model...

# check times
exp_times = [10.0]
assert hds.get_times() == exp_times
assert cbb.get_times() == exp_times # ...causes get_times() to be empty

# check stress periods and time steps
exp_kstpkper = [(0, 0)]
assert hds.get_kstpkper() == exp_kstpkper
assert cbb.get_kstpkper() == exp_kstpkper

# check head data access by time
hds_data_totim = hds.get_data(totim=exp_times[0])
assert hds_data_totim.shape == (1, 40, 20)

# check budget file data by time
cbb_data = cbb.get_data(totim=exp_times[0])
assert len(cbb_data) > 0

# check head data access by kstp and kper
hds_data_kstpkper = hds.get_data(kstpkper=(0, 0))
assert np.array_equal(hds_data_kstpkper, hds_data_totim)

# check budget file data by kstp and kper
cbb_data_kstpkper = cbb.get_data(kstpkper=(0, 0))
assert len(cbb_data) == len(cbb_data_kstpkper)
for i in range(len(cbb_data)):
assert np.array_equal(cbb_data[i], cbb_data_kstpkper[i])
18 changes: 18 additions & 0 deletions autotest/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
from flopy.utils.crs import get_authority_crs
from flopy.utils.geometry import Polygon

HAS_PYPROJ = has_pkg("pyproj", strict=True)
if HAS_PYPROJ:
import pyproj


def namfiles() -> List[Path]:
mf2005_path = get_example_data_path() / "mf2005_test"
Expand Down Expand Up @@ -677,6 +681,13 @@ def test_export_contourf(function_tmpdir, example_data_path):
len(shapes) >= 65
), "multipolygons were skipped in contourf routine"

# debugging
# for s in shapes:
# x = [i[0] for i in s.points[:]]
# y = [i[1] for i in s.points[:]]
# plt.plot(x, y)
# plt.show()


@pytest.mark.mf6
@requires_pkg("shapefile", "shapely")
Expand Down Expand Up @@ -706,6 +717,13 @@ def test_export_contours(function_tmpdir, example_data_path):
# expect 65 with standard mpl contours (structured grids), 86 with tricontours
assert len(shapes) >= 65

# debugging
# for s in shapes:
# x = [i[0] for i in s.points[:]]
# y = [i[1] for i in s.points[:]]
# plt.plot(x, y)
# plt.show()


@pytest.mark.mf6
@requires_pkg("shapely")
Expand Down
21 changes: 20 additions & 1 deletion autotest/test_generate_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,29 @@
from pprint import pprint

import pytest
from modflow_devtools.misc import get_current_branch

branch = get_current_branch()


@pytest.mark.mf6
def test_generate_classes_from_dfn(virtualenv, project_root_path):
@pytest.mark.slow
@pytest.mark.regression
@pytest.mark.skipif(
branch == "master" or branch.startswith("v"),
reason="skip on master and release branches",
)
def test_generate_classes_from_github_refs(
request, virtualenv, project_root_path, ref, worker_id
):
argv = (
request.config.workerinput["mainargv"]
if hasattr(request.config, "workerinput")
else []
)
if worker_id != "master" and "loadfile" not in argv:
pytest.skip("can't run in parallel")

python = virtualenv.python
venv = Path(python).parent
print(f"Using temp venv at {venv} with python {python}")
Expand Down
32 changes: 15 additions & 17 deletions autotest/test_mbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from flopy.mbase import resolve_exe
from flopy.utils.flopy_io import relpath_safe

_system = system()


@pytest.fixture
def mf6_model_path(example_data_path):
Expand All @@ -18,10 +20,7 @@ def mf6_model_path(example_data_path):

@requires_exe("mf6")
@pytest.mark.parametrize("use_ext", [True, False])
def test_resolve_exe_by_name(function_tmpdir, use_ext):
if use_ext and system() != "Windows":
pytest.skip(".exe extensions are Windows-only")

def test_resolve_exe_by_name(use_ext):
ext = ".exe" if use_ext else ""
expected = which("mf6").lower()
actual = resolve_exe(f"mf6{ext}")
Expand All @@ -31,13 +30,14 @@ def test_resolve_exe_by_name(function_tmpdir, use_ext):

@requires_exe("mf6")
@pytest.mark.parametrize("use_ext", [True, False])
def test_resolve_exe_by_abs_path(function_tmpdir, use_ext):
if use_ext and system() != "Windows":
pytest.skip(".exe extensions are Windows-only")

ext = ".exe" if use_ext else ""
def test_resolve_exe_by_abs_path(use_ext):
abs_path = which("mf6")
if _system == "Windows" and not use_ext:
abs_path = abs_path[:-4]
elif _system != "Windows" and use_ext:
abs_path = f"{abs_path}.exe"
expected = which("mf6").lower()
actual = resolve_exe(which(f"mf6{ext}"))
actual = resolve_exe(abs_path)
assert actual.lower() == expected
assert which(actual)

Expand All @@ -46,9 +46,6 @@ def test_resolve_exe_by_abs_path(function_tmpdir, use_ext):
@pytest.mark.parametrize("use_ext", [True, False])
@pytest.mark.parametrize("forgive", [True, False])
def test_resolve_exe_by_rel_path(function_tmpdir, use_ext, forgive):
if use_ext and system() != "Windows":
pytest.skip(".exe extensions are Windows-only")

ext = ".exe" if use_ext else ""
expected = which("mf6").lower()

Expand All @@ -59,10 +56,11 @@ def test_resolve_exe_by_rel_path(function_tmpdir, use_ext, forgive):

with set_dir(inner_dir):
# copy exe to relative dir
copy(expected, bin_dir / "mf6")
assert (bin_dir / "mf6").is_file()
new_exe_path = bin_dir / Path(expected).name
copy(expected, new_exe_path)
assert new_exe_path.is_file()

expected = which(str(Path(bin_dir / "mf6").absolute())).lower()
expected = which(str(new_exe_path.absolute())).lower()
actual = resolve_exe(f"../bin/mf6{ext}")
assert actual.lower() == expected
assert which(actual)
Expand All @@ -77,7 +75,7 @@ def test_resolve_exe_by_rel_path(function_tmpdir, use_ext, forgive):


def test_run_model_when_namefile_not_in_model_ws(
mf6_model_path, example_data_path, function_tmpdir
mf6_model_path, function_tmpdir
):
# copy input files to temp workspace
ws = function_tmpdir / "ws"
Expand Down
12 changes: 6 additions & 6 deletions autotest/test_modflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,31 +207,31 @@ def test_exe_selection(example_data_path, function_tmpdir):

# no selection defaults to mf2005
exe_name = "mf2005"
assert Path(Modflow().exe_name).name == exe_name
assert Path(Modflow(exe_name=None).exe_name).name == exe_name
assert Path(Modflow().exe_name).stem == exe_name
assert Path(Modflow(exe_name=None).exe_name).stem == exe_name
assert (
Path(Modflow.load(namfile_path, model_ws=model_path).exe_name).name
Path(Modflow.load(namfile_path, model_ws=model_path).exe_name).stem
== exe_name
)
assert (
Path(
Modflow.load(
namfile_path, exe_name=None, model_ws=model_path
).exe_name
).name
).stem
== exe_name
)

# user-specified (just for testing - there is no legitimate reason
# to use mp7 with Modflow but Modpath7 derives from BaseModel too)
exe_name = "mp7"
assert Path(Modflow(exe_name=exe_name).exe_name).name == exe_name
assert Path(Modflow(exe_name=exe_name).exe_name).stem == exe_name
assert (
Path(
Modflow.load(
namfile_path, exe_name=exe_name, model_ws=model_path
).exe_name
).name
).stem
== exe_name
)

Expand Down
Loading

0 comments on commit 45cb0fa

Please sign in to comment.