Skip to content

Commit

Permalink
feat(get/set data record): Updated get_data/set_data functionality an…
Browse files Browse the repository at this point in the history
…d new get_record/set_record methods (#1568)

* feat(get/set data record): New feature where MFArray and MFList objects have get_record and set_record method that allow the user to get a set data with all metadata.  Behavior of set_data has also been changed to set data without changing the metadata.  When set_data is passed metadata it gives a deprecation warning and sets the data and metadata.

* feat(get/set record): Updated tutorial to include get/set record and moved get/set record testing to its own test case
  • Loading branch information
spaulins-usgs authored Oct 6, 2022
1 parent a0ca344 commit 984227d
Show file tree
Hide file tree
Showing 7 changed files with 983 additions and 131 deletions.
280 changes: 280 additions & 0 deletions autotest/test_mf6.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,286 @@ def test_create_and_run_model(tmpdir):
assert success


@requires_exe("mf6")
def test_get_set_data_record(tmpdir):
# names
sim_name = "testrecordsim"
model_name = "testrecordmodel"
exe_name = "mf6"

# set up simulation
tdis_name = f"{sim_name}.tdis"
sim = MFSimulation(
sim_name=sim_name, version="mf6", exe_name=exe_name, sim_ws=str(tmpdir)
)
tdis_rc = [(10.0, 4, 1.0), (6.0, 3, 1.0)]
tdis = mftdis.ModflowTdis(
sim, time_units="DAYS", nper=2, perioddata=tdis_rc
)

# create model instance
model = mfgwf.ModflowGwf(
sim, modelname=model_name, model_nam_file=f"{model_name}.nam"
)

# create solution and add the model
ims_package = mfims.ModflowIms(
sim,
print_option="ALL",
complexity="SIMPLE",
outer_dvclose=0.00001,
outer_maximum=50,
under_relaxation="NONE",
inner_maximum=30,
inner_dvclose=0.00001,
linear_acceleration="CG",
preconditioner_levels=7,
preconditioner_drop_tolerance=0.01,
number_orthogonalizations=2,
)
sim.register_ims_package(ims_package, [model_name])

# add packages to model
dis_package = mfgwfdis.ModflowGwfdis(
model,
length_units="FEET",
nlay=3,
nrow=10,
ncol=10,
delr=500.0,
delc=500.0,
top=100.0,
botm=[50.0, 10.0, -50.0],
filename=f"{model_name}.dis",
)
ic_package = mfgwfic.ModflowGwfic(
model,
strt=[100.0, 90.0, 80.0],
filename=f"{model_name}.ic",
)
npf_package = mfgwfnpf.ModflowGwfnpf(
model, save_flows=True, icelltype=1, k=50.0, k33=1.0
)

sto_package = mfgwfsto.ModflowGwfsto(
model, save_flows=True, iconvert=1, ss=0.000001, sy=0.15
)
# wel packages
period_one = ModflowGwfwel.stress_period_data.empty(
model,
maxbound=3,
aux_vars=["var1", "var2", "var3"],
boundnames=True,
timeseries=True,
)
period_one[0][0] = ((0, 9, 2), -50.0, -1, -2, -3, None)
period_one[0][1] = ((1, 4, 7), -100.0, 1, 2, 3, "well_1")
period_one[0][2] = ((1, 3, 2), -20.0, 4, 5, 6, "well_2")
period_two = ModflowGwfwel.stress_period_data.empty(
model,
maxbound=2,
aux_vars=["var1", "var2", "var3"],
boundnames=True,
timeseries=True,
)
period_two[0][0] = ((2, 3, 2), -80.0, 1, 2, 3, "well_2")
period_two[0][1] = ((2, 4, 7), -10.0, 4, 5, 6, "well_1")
stress_period_data = {}
stress_period_data[0] = period_one[0]
stress_period_data[1] = period_two[0]
wel_package = ModflowGwfwel(
model,
print_input=True,
print_flows=True,
auxiliary=[("var1", "var2", "var3")],
maxbound=5,
stress_period_data=stress_period_data,
boundnames=True,
save_flows=True,
)
# rch package
rch_period_list = []
for row in range(0, 10):
for col in range(0, 10):
rch_amt = (1 + row / 10) * (1 + col / 10)
rch_period_list.append(((0, row, col), rch_amt, 0.5))
rch_period = {}
rch_period[0] = rch_period_list
rch_package = ModflowGwfrch(
model,
fixed_cell=True,
auxiliary="MULTIPLIER",
auxmultname="MULTIPLIER",
print_input=True,
print_flows=True,
save_flows=True,
maxbound=54,
stress_period_data=rch_period,
)

# write simulation to new location
sim.set_all_data_external()
sim.write_simulation()

# test get_record, set_record for list data
wel = model.get_package("wel")
spd_record = wel.stress_period_data.get_record()
well_sp_1 = spd_record[0]
assert (
well_sp_1["filename"] == "testrecordmodel.wel_stress_period_data_1.txt"
)
assert well_sp_1["binary"] is False
assert well_sp_1["data"][0][0] == (0, 9, 2)
assert well_sp_1["data"][0][1] == -50.0
# modify
del well_sp_1["filename"]
well_sp_1["data"][0][0] = (1, 9, 2)
well_sp_2 = spd_record[1]
del well_sp_2["filename"]
well_sp_2["data"][0][0] = (1, 1, 1)
# save
spd_record[0] = well_sp_1
spd_record[1] = well_sp_2
wel.stress_period_data.set_record(spd_record)
# verify changes
spd_record = wel.stress_period_data.get_record()
well_sp_1 = spd_record[0]
assert "filename" not in well_sp_1
assert well_sp_1["data"][0][0] == (1, 9, 2)
assert well_sp_1["data"][0][1] == -50.0
well_sp_2 = spd_record[1]
assert "filename" not in well_sp_2
assert well_sp_2["data"][0][0] == (1, 1, 1)
spd = wel.stress_period_data.get_data()
assert spd[0][0][0] == (1, 9, 2)
# change well_sp_2 back to external
well_sp_2["filename"] = "wel_spd_data_2.txt"
spd_record[1] = well_sp_2
wel.stress_period_data.set_record(spd_record)
# change well_sp_2 data
spd[1][0][0] = (1, 2, 2)
wel.stress_period_data.set_data(spd)
# verify changes
spd_record = wel.stress_period_data.get_record()
well_sp_2 = spd_record[1]
assert well_sp_2["filename"] == "wel_spd_data_2.txt"
assert well_sp_2["data"][0][0] == (1, 2, 2)

# test get_data/set_data vs get_record/set_record
dis = model.get_package("dis")
botm = dis.botm.get_record()
assert len(botm) == 3
layer_2 = botm[1]
layer_3 = botm[2]
# verify layer 2
assert layer_2["filename"] == "testrecordmodel.dis_botm_layer2.txt"
assert layer_2["binary"] is False
assert layer_2["factor"] == 1.0
assert layer_2["iprn"] is None
assert layer_2["data"][0][0] == 10.0
# change and set layer 2
layer_2["filename"] = "botm_layer2.txt"
layer_2["binary"] = True
layer_2["iprn"] = 3
layer_2["factor"] = 2.0
layer_2["data"] = layer_2["data"] * 0.5
botm[1] = layer_2
# change and set layer 3
del layer_3["filename"]
layer_3["factor"] = 0.5
layer_3["data"] = layer_3["data"] * 2.0
botm[2] = layer_3
dis.botm.set_record(botm)

# get botm in two different ways, verifying changes made
botm_record = dis.botm.get_record()
layer_1 = botm_record[0]
assert layer_1["filename"] == "testrecordmodel.dis_botm_layer1.txt"
assert layer_1["binary"] is False
assert layer_1["iprn"] is None
assert layer_1["data"][0][0] == 50.0
layer_2 = botm_record[1]
assert layer_2["filename"] == "botm_layer2.txt"
assert layer_2["binary"] is True
assert layer_2["factor"] == 2.0
assert layer_2["iprn"] == 3
assert layer_2["data"][0][0] == 5.0
layer_3 = botm_record[2]
assert "filename" not in layer_3
assert layer_3["factor"] == 0.5
assert layer_3["data"][0][0] == -100.0
botm_data = dis.botm.get_data(apply_mult=True)
assert botm_data[0][0][0] == 50.0
assert botm_data[1][0][0] == 10.0
assert botm_data[2][0][0] == -50.0
botm_data = dis.botm.get_data()
assert botm_data[0][0][0] == 50.0
assert botm_data[1][0][0] == 5.0
assert botm_data[2][0][0] == -100.0
# modify and set botm data with set_data
botm_data[0][0][0] = 6.0
botm_data[1][0][0] = -8.0
botm_data[2][0][0] = -205.0
dis.botm.set_data(botm_data)
# verify that data changed and metadata did not change
botm_record = dis.botm.get_record()
layer_1 = botm_record[0]
assert layer_1["filename"] == "testrecordmodel.dis_botm_layer1.txt"
assert layer_1["binary"] is False
assert layer_1["iprn"] is None
assert layer_1["data"][0][0] == 6.0
assert layer_1["data"][0][1] == 50.0
layer_2 = botm_record[1]
assert layer_2["filename"] == "botm_layer2.txt"
assert layer_2["binary"] is True
assert layer_2["factor"] == 2.0
assert layer_2["iprn"] == 3
assert layer_2["data"][0][0] == -8.0
assert layer_2["data"][0][1] == 5.0
layer_3 = botm_record[2]
assert "filename" not in layer_3
assert layer_3["factor"] == 0.5
assert layer_3["data"][0][0] == -205.0
botm_data = dis.botm.get_data()
assert botm_data[0][0][0] == 6.0
assert botm_data[1][0][0] == -8.0
assert botm_data[2][0][0] == -205.0

spd_record = rch_package.stress_period_data.get_record()
assert 0 in spd_record
assert isinstance(spd_record[0], dict)
assert "filename" in spd_record[0]
assert (
spd_record[0]["filename"]
== "testrecordmodel.rch_stress_period_data_1.txt"
)
assert "binary" in spd_record[0]
assert spd_record[0]["binary"] is False
assert "data" in spd_record[0]
assert spd_record[0]["data"][0][0] == (0, 0, 0)
spd_record[0]["data"][0][0] = (0, 0, 8)
rch_package.stress_period_data.set_record(spd_record)

spd_data = rch_package.stress_period_data.get_data()
assert spd_data[0][0][0] == (0, 0, 8)
spd_data[0][0][0] = (0, 0, 7)
rch_package.stress_period_data.set_data(spd_data)

spd_record = rch_package.stress_period_data.get_record()
assert isinstance(spd_record[0], dict)
assert "filename" in spd_record[0]
assert (
spd_record[0]["filename"]
== "testrecordmodel.rch_stress_period_data_1.txt"
)
assert "binary" in spd_record[0]
assert spd_record[0]["binary"] is False
assert "data" in spd_record[0]
assert spd_record[0]["data"][0][0] == (0, 0, 7)

sim.write_simulation()


@requires_exe("mf6")
def test_output(tmpdir, example_data_path):
ex_name = "test001e_UZF_3lay"
Expand Down
75 changes: 61 additions & 14 deletions examples/Tutorials/modflow6data/tutorial08_mf6_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,25 +145,72 @@
}

# A list containing data for the three specified layers is then passed in to
# the `botm` object's `set_data` method.
# the `botm` object's `set_record` method.

dis.botm.set_data([a0, a1, a2])
dis.botm.set_record([a0, a1, a2])
print(dis.botm.get_file_entry())

# Note that we could have also specified `botm` this way as part of the
# original `flopy.mf6.ModflowGwfdis` constructor:
# The botm data and its attributes (filename, binary, factor, iprn) can be
# retrieved as a dictionary of dictionaries using get_record.

a0 = {"factor": 0.5, "iprn": 1, "data": np.ones((10, 10))}
a1 = -100
a2 = {
"filename": "dis.botm.3.bin",
"factor": 2.0,
"iprn": 1,
"data": -100 * np.ones((4, 5)),
"binary": True,
botm_record = dis.botm.get_record()
print("botm layer 1 record:")
print(botm_record[0])
print("\nbotm layer 2 record:")
print(botm_record[1])
print("\nbotm layer 3 record:")
print(botm_record[2])

# The botm record retrieved can be modified and then saved with set_record.
# For example, the array data's "factor" can be modified and saved.

botm_record[0]["factor"] = 0.6
dis.botm.set_record(botm_record)

# The updated value can then be retrieved.

botm_record = dis.botm.get_record()
print(f"botm layer 1 factor: {botm_record[0]['factor']}")

# The get_record and set_record methods can also be used with list data to get
# and set the data and its "filename" and "binary" attributes. This is
# demonstrated with the wel package. First, a wel package is constructed.

welspdict = {
0: {"filename": "well_sp1.txt", "data": [[(0, 0, 0), 0.25]]},
1: [[(0, 0, 0), 0.1]],
}
botm = [a0, a1, a2]
flopy.mf6.ModflowGwfdis(gwf, nlay=3, nrow=10, ncol=10, botm=botm)
wel = flopy.mf6.ModflowGwfwel(
gwf,
print_input=True,
print_flows=True,
stress_period_data=welspdict,
save_flows=False,
)

# The wel stress period data and associated "filename" and "binary" attributes
# can be retrieved with get_record.

spd_record = wel.stress_period_data.get_record()
print("Stress period 1 record:")
print(spd_record[0])
print("\nStress period 2 record:")
print(spd_record[1])

# The wel data and associated attributes can be changed by modifying the
# record and then saving it with the set_record method.

spd_record[0]["filename"] = "well_package_sp1.txt"
spd_record[0]["binary"] = True
spd_record[1]["filename"] = "well_package_sp2.txt"
wel.stress_period_data.set_record(spd_record)

# The changes can be verified by calling get_record again.

spd_record = wel.stress_period_data.get_record()
print(f"New filename for stress period 1: {spd_record[0]['filename']}")
print(f"New binary flag for stress period 1: {spd_record[0]['binary']}")
print(f"New filename for stress period 2: {spd_record[1]['filename']}")

try:
temp_dir.cleanup()
Expand Down
Loading

0 comments on commit 984227d

Please sign in to comment.