Skip to content

Commit 5098c56

Browse files
authored
bug(flopy_io): limit fixed point format string to fixed column widths (#1172)
flopy_io.write_fixed_var could write a fixed point string (f) that exceeded the specified fixed column width. Added test to revert to general format (g) with precision equal to column width - 6 to preserve as much precision as possible. This value allows for a string with a sign (+/-) and +/-e307. Added test for issue 1164 to t015_test.py autotest. Closes #1164
1 parent 64094ae commit 5098c56

File tree

2 files changed

+64
-9
lines changed

2 files changed

+64
-9
lines changed

autotest/t015_test.py

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,54 @@
3232
else:
3333
path = os.path.join("..", "examples", "data", "mf2005_test")
3434

35-
str_items = {0: {"mfnam": "str.nam", "sfrfile": "str.str"}}
35+
str_items = {
36+
0: {
37+
"mfnam": "str.nam",
38+
"sfrfile": "str.str",
39+
"lstfile": "str.lst",
40+
}
41+
}
42+
43+
44+
def test_str_issue1164():
45+
m = flopy.modflow.Modflow.load(
46+
str_items[0]["mfnam"],
47+
exe_name=mfexe,
48+
model_ws=path,
49+
verbose=False,
50+
check=False,
51+
)
52+
53+
ws = os.path.join(tpth, "issue-1164")
54+
m.change_model_ws(ws)
55+
56+
# adjust stress period data
57+
spd0 = m.str.stress_period_data[0]
58+
spd0["flow"][0] = 2.1149856e6 # 450000000000000000.0000e-17
59+
m.str.stress_period_data[0] = spd0
60+
61+
# write model datasets and run fixed
62+
m.write_input()
63+
success = m.run_model()
64+
assert success, "could not run base model"
65+
66+
# get the budget
67+
lst_pth = os.path.join(ws, str_items[0]["lstfile"])
68+
base_wb = flopy.utils.MfListBudget(lst_pth).get_dataframes()[0]
69+
70+
# set the model to free format
71+
m.set_ifrefm()
72+
73+
# write model datasets and run revised
74+
m.write_input()
75+
success = m.run_model()
76+
assert success, "could not run revised model"
77+
78+
# get the revised budget
79+
revised_wb = flopy.utils.MfListBudget(lst_pth).get_dataframes()[0]
80+
81+
# test if the budgets are the same
82+
assert revised_wb.equals(base_wb), "water budgets do not match"
3683

3784

3885
def test_str_free():
@@ -43,7 +90,7 @@ def test_str_free():
4390
verbose=False,
4491
check=False,
4592
)
46-
ws = tpth
93+
ws = os.path.join(tpth, "fixed")
4794
m.change_model_ws(ws)
4895

4996
# get pointer to str package
@@ -116,7 +163,7 @@ def test_str_free():
116163
msg = "could not load the fixed format model with aux variables"
117164
assert m2 is not None, msg
118165

119-
ws = os.path.join(tpth, "mf2005")
166+
ws = os.path.join(tpth, "free")
120167
m.change_model_ws(ws)
121168
m.set_ifrefm()
122169
m.write_input()
@@ -145,7 +192,7 @@ def test_str_free():
145192
# compare the fixed and free format head files
146193
if run:
147194
if pymake is not None:
148-
fn1 = os.path.join(tpth, "str.nam")
195+
fn1 = os.path.join(tpth, "fixed", "str.nam")
149196
fn2 = os.path.join(ws, "str.nam")
150197
success = pymake.compare_heads(fn1, fn2, verbose=True)
151198
msg = "fixed and free format input output head files are different"
@@ -162,5 +209,6 @@ def test_str_plot():
162209

163210

164211
if __name__ == "__main__":
212+
test_str_issue1164()
165213
test_str_free()
166214
test_str_plot()

flopy/utils/flopy_io.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,25 @@ def write_fixed_var(v, length=10, ipos=None, free=False, comment=None):
185185
if free:
186186
write_fmt = "{} "
187187
else:
188+
width = ipos[n]
188189
if isinstance(v[n], (float, np.float32, np.float64)):
189-
width = ipos[n] - 6
190-
vmin, vmax = 10 ** -width, 10 ** width
190+
decimal = width - 6
191+
vmin, vmax = 10 ** -decimal, 10 ** decimal
191192
if abs(v[n]) < vmin or abs(v[n]) > vmax:
192-
ctype = "g"
193+
ctype = "g" # default precision is 6 if not specified
193194
else:
194-
ctype = ".{}f".format(width)
195+
ctype = ".{}f".format(decimal)
196+
# evaluate if the fixed format value will exceed width
197+
if (
198+
len("{{:>{}{}}}".format(width, ctype).format(v[n]))
199+
> width
200+
):
201+
ctype = ".{}g".format(decimal) # preserve precision
195202
elif isinstance(v[n], (int, np.int32, np.int64)):
196203
ctype = "d"
197204
else:
198205
ctype = ""
199-
write_fmt = "{{:>{}{}}}".format(ipos[n], ctype)
206+
write_fmt = "{{:>{}{}}}".format(width, ctype)
200207
out += write_fmt.format(v[n])
201208
if comment is not None:
202209
out += " # {}".format(comment)

0 commit comments

Comments
 (0)