Skip to content

Commit c80fc6c

Browse files
authored
fix(model_splitter): multiple bug fixes (#2728)
* set data internal for model splitting * fix(split_model): update external file handling for model splitter Maintain external file paths when user passes optional sim_ws parameter * updates(model_splitter.py): multiple bug fixes * filter (-1,) SFR cellids from splitting mask creation in optimize_splitting_mask * adjust (-1,) SFR cellid layer number to 0 in _remap_sfr to avoid index errors * handle external model files by allowing user to specify new simulation workspace `sim_ws` to `split_model` and `split_multi_model` * dynamically adjust `max_columns_of_data` when external model files are maintained in the split models * update external ascii test * Add "angrot" to the offsets dictionary
1 parent 11432e4 commit c80fc6c

2 files changed

Lines changed: 64 additions & 17 deletions

File tree

autotest/test_model_splitter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ def test_control_records(function_tmpdir):
437437
split_ws.mkdir()
438438
with set_dir(split_ws):
439439
mfsplit = flopy.mf6.utils.Mf6Splitter(sim)
440-
new_sim = mfsplit.split_model(arr)
440+
new_sim = mfsplit.split_model(arr, split_ws)
441441

442442
ml1 = new_sim.get_model("model_1")
443443

flopy/mf6/utils/model_splitter.py

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def __init__(self, sim, modelname=None):
181181
self._allow_splitting = True
182182

183183
self._fdigits = 1
184+
self._keep_external = True
184185

185186
# multi-model splitting attr
186187
self._multimodel_exchange_gwf_names = {}
@@ -678,12 +679,13 @@ def optimize_splitting_mask(self, nparts, active_only=False, options=None, verbo
678679
else:
679680
cellids = package.packagedata.array.cellid
680681
if self._modelgrid.grid_type == "structured":
681-
cellids = [(0, i[1], i[2]) for i in cellids]
682+
# skip disconnected cells in SFR package
683+
cellids = [(0, i[1], i[2]) for i in cellids if i != (-1, -1, -1)]
682684
nodes = self._modelgrid.get_node(cellids)
683685
elif self._modelgrid.grid_type == "vertex":
684-
nodes = [i[1] for i in cellids]
686+
nodes = [i[1] for i in cellids if i != (-1, -1)]
685687
else:
686-
nodes = [i[0] for i in cellids]
688+
nodes = [i[0] for i in cellids if i != (-1,)]
687689

688690
if isinstance(package, (modflow.ModflowGwflak, modflow.ModflowGwtlkt, modflow.ModflowGwelke)):
689691
lakenos = package.connectiondata.array.ifno + 1
@@ -758,15 +760,24 @@ def optimize_splitting_mask(self, nparts, active_only=False, options=None, verbo
758760
cellids2 = recarray.cellid2
759761
_, nodes1 = self._cellid_to_layer_node(cellids1)
760762
_, nodes2 = self._cellid_to_layer_node(cellids2)
761-
mnums1 = membership[nodes1]
762-
mnums2 = membership[nodes2]
763-
ev = np.equal(mnums1, mnums2)
764-
if np.all(ev):
765-
continue
766-
idx = np.asarray(~ev).nonzero()[0]
767-
mnum_to = mnums1[idx]
768-
adj_nodes = nodes2[idx]
769-
membership[adj_nodes] = mnum_to
763+
cnt = 0
764+
while cnt < len(nodes1):
765+
mnums1 = membership[nodes1]
766+
mnums2 = membership[nodes2]
767+
ev = np.equal(mnums1, mnums2)
768+
if np.all(ev):
769+
break
770+
idx = np.asarray(~ev).nonzero()[0]
771+
mnum_to = mnums1[idx]
772+
adj_nodes = np.array(nodes2)[idx]
773+
membership[adj_nodes] = mnum_to
774+
cnt += 1
775+
776+
if cnt == len(nodes1):
777+
raise AssertionError(
778+
"Cannot uniquely spilt around HFB boundaries, try another "
779+
"value for nparts"
780+
)
770781

771782
return membership.reshape(shape)
772783

@@ -1012,6 +1023,7 @@ def _remap_nodes(self, array):
10121023
self._offsets[m] = {
10131024
"xorigin": self._modelgrid.xvertices[rmax + 1, cmin],
10141025
"yorigin": self._modelgrid.yvertices[rmax + 1, cmin],
1026+
"angrot": self._modelgrid.angrot
10151027
}
10161028
# get new nrow and ncol information
10171029
nrow = (rmax - rmin) + 1
@@ -1035,6 +1047,7 @@ def _remap_nodes(self, array):
10351047
self._offsets[m] = {
10361048
"xorigin": self._modelgrid.xoffset,
10371049
"yorigin": self._modelgrid.yoffset,
1050+
"angrot": self._modelgrid.angrot
10381051
}
10391052

10401053
new_ncpl = {}
@@ -1536,6 +1549,9 @@ def _remap_array(self, item, mfarray, mapped_data, **kwargs):
15361549
# external array
15371550
tmp = fnames[lay].split(".")
15381551
filename = f"{'.'.join(tmp[:-1])}.{mkey :0{self._fdigits}d}.{tmp[-1]}"
1552+
folder_path = (self._new_sim.sim_path / filename).parent
1553+
if not folder_path.exists():
1554+
folder_path.mkdir(parents=True)
15391555

15401556
cr = {
15411557
"filename": filename,
@@ -1621,6 +1637,9 @@ def _remap_mflist(
16211637
if how == 3 and new_recarray is not None:
16221638
tmp = fname.split(".")
16231639
filename = f"{'.'.join(tmp[:-1])}.{mkey :0{self._fdigits}d}.{tmp[-1]}"
1640+
folder_path = (self._new_sim.sim_path / filename).parent
1641+
if not folder_path.exists():
1642+
folder_path.mkdir(parents=True)
16241643

16251644
new_recarray = {
16261645
"data": new_recarray,
@@ -2040,6 +2059,10 @@ def _remap_sfr(self, package, mapped_data):
20402059
cellids[messy_idx] = rcids
20412060

20422061
layers, nodes = self._cellid_to_layer_node(cellids)
2062+
# adjust the messy_idx layer number
2063+
if layers is not None and messy_idx:
2064+
layers[messy_idx] = 0
2065+
20432066
new_model, new_node = self._get_new_model_new_node(nodes)
20442067

20452068
for mkey, model in self._model_dict.items():
@@ -3519,6 +3542,15 @@ def _remap_package(self, package, ismvr=False):
35193542
if "stress_period_data" in data:
35203543
if not data["stress_period_data"]:
35213544
continue
3545+
3546+
if self._keep_external:
3547+
shape = self._grid_info[mdl][0]
3548+
if len(shape) == 2:
3549+
max_cols = shape[1]
3550+
else:
3551+
max_cols = shape[0]
3552+
self._new_sim.simulation_data.max_columns_of_data = max_cols
3553+
35223554
paks[mdl] = pak_cls(
35233555
self._model_dict[mdl], pname=package.name[0], **data
35243556
)
@@ -3842,7 +3874,7 @@ def create_multi_model_exchanges(self, mname0, mname1):
38423874
filename=filename,
38433875
)
38443876

3845-
def split_model(self, array):
3877+
def split_model(self, array, sim_ws=None):
38463878
"""
38473879
User method to split a model based on an array
38483880
@@ -3852,6 +3884,10 @@ def split_model(self, array):
38523884
integer array of new model numbers. Array must either be of
38533885
dimension (NROW, NCOL), (NCPL), or (NNODES for unstructured grid
38543886
models).
3887+
sim_ws : PathLike or str
3888+
optional directory path for writing the new simulation to. This parameter
3889+
is recommended when the model contains external files and the user would
3890+
like to preserve external linkages while splitting.
38553891
38563892
Returns
38573893
-------
@@ -3863,6 +3899,10 @@ def split_model(self, array):
38633899
"is part of a split simulation"
38643900
)
38653901

3902+
if sim_ws is None:
3903+
self._keep_external = False
3904+
sim_ws = self._sim.sim_path
3905+
38663906
# set number formatting string for file paths
38673907
array = np.array(array).astype(int)
38683908
s = str(np.max(array))
@@ -3872,7 +3912,7 @@ def split_model(self, array):
38723912

38733913
if self._new_sim is None:
38743914
self._new_sim = modflow.MFSimulation(
3875-
version=self._sim.version, exe_name=self._sim.exe_name, sim_ws=self._sim.sim_path
3915+
version=self._sim.version, exe_name=self._sim.exe_name, sim_ws=sim_ws
38763916
)
38773917
self._create_sln_tdis()
38783918

@@ -3898,6 +3938,9 @@ def split_model(self, array):
38983938
**nam_options[mkey],
38993939
)
39003940

3941+
if not self._keep_external:
3942+
self._model.set_all_data_internal(check_data=True)
3943+
39013944
for package in self._model.packagelist:
39023945
paks = self._remap_package(package)
39033946

@@ -3909,7 +3952,7 @@ def split_model(self, array):
39093952

39103953
return self._new_sim
39113954

3912-
def split_multi_model(self, array):
3955+
def split_multi_model(self, array, sim_ws=None):
39133956
"""
39143957
Method to split integrated models such as GWF-GWT or GWF-GWE models.
39153958
Note: this method will not work to split multiple connected GWF models
@@ -3920,6 +3963,10 @@ def split_multi_model(self, array):
39203963
integer array of new model numbers. Array must either be of
39213964
dimension (NROW, NCOL), (NCPL), or (NNODES for unstructured grid
39223965
models).
3966+
sim_ws : PathLike or str
3967+
optional directory path for writing the new simulation to. This parameter
3968+
is recommended when the model contains external files and the user would
3969+
like to preserve external linkages while splitting.
39233970
39243971
Returns
39253972
-------
@@ -3986,7 +4033,7 @@ def split_multi_model(self, array):
39864033
new_sim = self.split_model(array)
39874034
for mname in model_names[1:]:
39884035
self.switch_models(modelname=mname, remap_nodes=False)
3989-
new_sim = self.split_model(array)
4036+
new_sim = self.split_model(array, sim_ws=sim_ws)
39904037

39914038
for mbase in model_names[1:]:
39924039
for label in model_labels:

0 commit comments

Comments
 (0)