Skip to content

Commit bef063f

Browse files
authored
fix(mp7): fix input file writing (#2679)
Util2d assumes its first argument is a model, but a ParticleGroup was being passed. This caused an error when writing MP7 input files with certain release configurations. Don't pass ParticleGroup to Util2d as it's not needed, and only check self.model in Util2d if it's not None. Likewise in the ArrayFormat class.
1 parent aad0682 commit bef063f

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

autotest/test_particlegroup.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import tempfile
2+
from pathlib import Path
3+
14
import numpy as np
25

36
from flopy.modpath import ParticleData, ParticleGroup
@@ -51,3 +54,57 @@ def test_pgroup_release_data():
5154
f"mp7: pgroup with releaseoption 3 returned "
5255
f"len(releasetimes)={len(pgrd3.releasetimes)}. Should be {nripg3}"
5356
)
57+
58+
59+
def test_pgroup_write_with_release_timing():
60+
"""Test that particle groups can be written to files with all release options."""
61+
# create particles
62+
partlocs = []
63+
partids = []
64+
nrow = 21
65+
for i in range(nrow):
66+
partlocs.append((0, i, 2))
67+
partids.append(i)
68+
pdata = ParticleData(partlocs, structured=True, particleids=partids)
69+
70+
with tempfile.TemporaryDirectory() as tmpdir:
71+
tmpdir = Path(tmpdir)
72+
73+
# Test releaseoption 1 (single release time)
74+
pgrd1 = ParticleGroup(
75+
particlegroupname="PG1",
76+
particledata=pdata,
77+
filename=None, # Write internally to test that path
78+
releasedata=0.0,
79+
)
80+
81+
# Test releaseoption 2 (time range with interval)
82+
nripg2 = 10
83+
ripg2 = 1.0
84+
pgrd2 = ParticleGroup(
85+
particlegroupname="PG2",
86+
particledata=pdata,
87+
filename=None,
88+
releasedata=[nripg2, 0.0, ripg2],
89+
)
90+
91+
# Test releaseoption 3 (explicit list of times).
92+
# this triggers Util2d with None model
93+
nripg3 = 7
94+
pgrd3 = ParticleGroup(
95+
particlegroupname="PG3",
96+
particledata=pdata,
97+
filename=None,
98+
releasedata=[nripg3, np.array([0.0, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0])],
99+
)
100+
101+
# Write each particle group to test all release options
102+
for pg in [pgrd1, pgrd2, pgrd3]:
103+
sim_file = tmpdir / f"{pg.particlegroupname}_test.sim"
104+
with open(sim_file, "w") as f:
105+
# This should not raise an AttributeError
106+
pg.write(f, ws=str(tmpdir))
107+
108+
# Verify file was created and has content
109+
assert sim_file.exists()
110+
assert sim_file.stat().st_size > 0

flopy/modpath/mp7particlegroup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def write(self, fp=None, ws="."):
149149
fp.write(f"{self.releasetimecount}\n")
150150
# item 31
151151
tp = self.releasetimes
152-
v = Util2d(self, (tp.shape[0],), np.float32, tp, name="temp", locat=0)
152+
v = Util2d(None, (tp.shape[0],), np.float32, tp, name="temp", locat=0)
153153
fp.write(v.string)
154154

155155
# item 32

flopy/utils/util_array.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,11 @@ def __init__(self, u2d, python=None, fortran=None, array_free_format=None):
8989
self._decimal = None
9090
if array_free_format is not None:
9191
self._freeformat_model = bool(array_free_format)
92-
else:
92+
elif u2d.model is not None:
9393
self._freeformat_model = bool(u2d.model.array_free_format)
94+
else:
95+
# Default to free format when no model is available
96+
self._freeformat_model = True
9497

9598
self.default_float_width = 15
9699
self.default_int_width = 10
@@ -1868,7 +1871,9 @@ def _decide_how(self):
18681871
if self.vtype in [np.int32, np.float32]:
18691872
self._how = "constant"
18701873
# if a filename was passed in or external path was set
1871-
elif self._model.external_path is not None or self.vtype == str:
1874+
elif (
1875+
self._model is not None and self._model.external_path is not None
1876+
) or self.vtype == str:
18721877
if self.format.array_free_format:
18731878
self._how = "openclose"
18741879
else:
@@ -2710,7 +2715,7 @@ def parse_value(self, value):
27102715
if len(value.shape) == 3 and value.shape[0] == 1:
27112716
value = value[0]
27122717

2713-
if self.model.version == "mfusg":
2718+
if self.model is not None and self.model.version == "mfusg":
27142719
if self.shape != value.shape:
27152720
value = np.array([np.squeeze(value)])
27162721

0 commit comments

Comments
 (0)