From 7a8a9852c9f7dcdeeae87c13b1ec97e745270cfc Mon Sep 17 00:00:00 2001 From: wpbonelli Date: Thu, 8 Aug 2024 14:30:37 -0400 Subject: [PATCH 01/10] ci(release): update version to 3.9.0.dev0 --- CITATION.cff | 2 +- README.md | 4 ++-- docs/PyPI_release.md | 2 +- flopy/version.py | 4 ++-- version.txt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index b9cf5acb6a..c68ec7d2fa 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,7 +3,7 @@ message: If you use this software, please cite both the article from preferred-c references, and the software itself. type: software title: FloPy -version: 3.8.0 +version: 3.9.0.dev0 date-released: '2024-08-08' doi: 10.5066/F7BK19FH abstract: A Python package to create, run, and post-process MODFLOW-based models. diff --git a/README.md b/README.md index 3c8bf20087..da73f1020a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ flopy3 -### Version 3.8.0 +### Version 3.9.0.dev0 [![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) @@ -150,7 +150,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., 2024, FloPy v3.8.0: U.S. Geological Survey Software Release, 08 August 2024, 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., 2024, FloPy v3.9.0.dev0: U.S. Geological Survey Software Release, 08 August 2024, https://doi.org/10.5066/F7BK19FH](https://doi.org/10.5066/F7BK19FH) Additional FloPy Related Publications diff --git a/docs/PyPI_release.md b/docs/PyPI_release.md index 016a42854a..7c224dcf80 100644 --- a/docs/PyPI_release.md +++ b/docs/PyPI_release.md @@ -30,4 +30,4 @@ 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., 2024, FloPy v3.8.0: U.S. Geological Survey Software Release, 08 August 2024, 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., 2024, FloPy v3.9.0.dev0: U.S. Geological Survey Software Release, 08 August 2024, https://doi.org/10.5066/F7BK19FH](https://doi.org/10.5066/F7BK19FH) diff --git a/flopy/version.py b/flopy/version.py index f0447fffaf..bc74242968 100644 --- a/flopy/version.py +++ b/flopy/version.py @@ -1,4 +1,4 @@ # flopy version file automatically created using -# update_version.py on August 08, 2024 13:58:49 +# update_version.py on August 08, 2024 14:29:26 -__version__ = "3.8.0" +__version__ = "3.9.0.dev0" diff --git a/version.txt b/version.txt index 0be1fc7d24..70abe80d9d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -3.8.0 \ No newline at end of file +3.9.0.dev0 \ No newline at end of file From d02967db167b98e32cfaa26ef6636475ea2441a8 Mon Sep 17 00:00:00 2001 From: Joshua Larsen Date: Fri, 16 Aug 2024 10:57:57 -0700 Subject: [PATCH 02/10] updates(model_splitter.py): update UnstructuredGrid support (#2292) * force remapping to be calculated from iac, ja array * add 0 digit padding to file names * update UnstructuredGrid, add cell2d input parameter and property method * add clean_iverts() to UnstructuredGrid to remove duplicated vertex and invert information * update testing for new features --- autotest/test_grid.py | 61 ++++++++ autotest/test_model_splitter.py | 176 ++++++++++++++++++++++- flopy/discretization/unstructuredgrid.py | 130 +++++++++++++++++ flopy/mf6/mfmodel.py | 16 +-- flopy/mf6/utils/model_splitter.py | 120 ++++++++++------ 5 files changed, 448 insertions(+), 55 deletions(-) diff --git a/autotest/test_grid.py b/autotest/test_grid.py index 142fed140d..f5eba2f233 100644 --- a/autotest/test_grid.py +++ b/autotest/test_grid.py @@ -1450,3 +1450,64 @@ def test_geo_dataframe(structured_grid, vertex_grid, unstructured_grid): raise AssertionError( f"Cell vertices incorrect for node={node}" ) + + +def test_unstructured_iverts_cleanup(): + grid = GridCases.structured_small() + + # begin building unstructured grid information + top = grid.top.ravel() + botm = grid.botm[0].ravel() + idomain = np.ones(botm.shape, dtype=int) + + # build iac and ja + neighbors = grid.neighbors(method="rook", reset=True) + iac, ja = [], [] + for cell, neigh in neighbors.items(): + iac.append(len(neigh) + 1) + ja.extend( + [ + cell, + ] + + neigh + ) + + # build iverts and verts without using shared vertices + verts, iverts = [], [] + xverts, yverts = grid.cross_section_vertices + ivt = 0 + for cid, xvs in enumerate(xverts): + yvs = yverts[cid] + ivts = [] + for ix, vert in enumerate(xvs[:-1]): + ivts.append(ivt) + verts.append([ivt, vert, yvs[ix]]) + ivt += 1 + + ivts.append(ivts[0]) + iverts.append(ivts) + + ugrid = UnstructuredGrid( + vertices=verts, + iverts=iverts, + xcenters=grid.xcellcenters.ravel(), + ycenters=grid.ycellcenters.ravel(), + iac=iac, + ja=ja, + top=top, + botm=botm, + idomain=idomain, + ) + + if ugrid.nvert != (grid.ncpl * 4): + raise AssertionError( + "UnstructuredGrid is being built incorrectly for test case" + ) + + cleaned_vert_num = (grid.nrow + 1) * (grid.ncol + 1) + clean_ugrid = ugrid.clean_iverts() + + if clean_ugrid.nvert != cleaned_vert_num: + raise AssertionError( + "Improper number of vertices for cleaned 'shared' iverts" + ) diff --git a/autotest/test_model_splitter.py b/autotest/test_model_splitter.py index c7756589d1..c7b3c4b53e 100644 --- a/autotest/test_model_splitter.py +++ b/autotest/test_model_splitter.py @@ -26,7 +26,7 @@ def test_structured_model_splitter(function_tmpdir): for row in range(modelgrid.nrow): if row != 0 and row % 2 == 0: ncol += 1 - array[row, ncol:] = 2 + array[row, ncol:] = 100 mfsplit = Mf6Splitter(sim) new_sim = mfsplit.split_model(array) @@ -37,13 +37,13 @@ def test_structured_model_splitter(function_tmpdir): original_heads = gwf.output.head().get_alldata()[-1] - ml0 = new_sim.get_model("freyberg_1") - ml1 = new_sim.get_model("freyberg_2") + ml0 = new_sim.get_model("freyberg_001") + ml1 = new_sim.get_model("freyberg_100") heads0 = ml0.output.head().get_alldata()[-1] heads1 = ml1.output.head().get_alldata()[-1] - new_heads = mfsplit.reconstruct_array({1: heads0, 2: heads1}) + new_heads = mfsplit.reconstruct_array({1: heads0, 100: heads1}) err_msg = "Heads from original and split models do not match" np.testing.assert_allclose(new_heads, original_heads, err_msg=err_msg) @@ -691,3 +691,171 @@ def test_idomain_none(function_tmpdir): err_msg = "Heads from original and split models do not match" np.testing.assert_allclose(new_head, head, atol=1e-07, err_msg=err_msg) + + +@requires_exe("mf6") +def test_unstructured_complex_disu(function_tmpdir): + sim_path = function_tmpdir + split_sim_path = sim_path / "model_split" + + # build the simulation structure + sim = flopy.mf6.MFSimulation(sim_ws=sim_path) + ims = flopy.mf6.ModflowIms(sim, complexity="SIMPLE") + tdis = flopy.mf6.ModflowTdis(sim) + + mname = "disu_model" + gwf = flopy.mf6.ModflowGwf(sim, modelname=mname) + + # start structured and then create a USG from it + nlay = 1 + nrow = 10 + ncol = 10 + delc = np.ones((nrow,)) + delr = np.ones((ncol,)) + top = np.ones((nrow, ncol)) + botm = np.zeros((nlay, nrow, ncol)) + idomain = np.ones(botm.shape, dtype=int) + idomain[0, 1, 4] = 0 + idomain[0, 8, 5] = 0 + + grid = flopy.discretization.StructuredGrid( + delc=delc, delr=delr, top=top, botm=botm, idomain=idomain + ) + + # build the USG connection information + neighbors = grid.neighbors(method="rook", reset=True) + iac, ja, ihc, cl12, hwva, angldegx = [], [], [], [], [], [] + for cell, neigh in neighbors.items(): + iac.append(len(neigh) + 1) + ihc.extend( + [ + 1, + ] + * (len(neigh) + 1) + ) + ja.extend( + [ + cell, + ] + + neigh + ) + cl12.extend( + [ + 0, + ] + + [ + 1, + ] + * len(neigh) + ) + hwva.extend( + [ + 0, + ] + + [ + 1, + ] + * len(neigh) + ) + adx = [ + 0, + ] + for n in neigh: + ev = cell - n + if ev == -1 * ncol: + adx.append(270) + elif ev == ncol: + adx.append(90) + elif ev == -1: + adx.append(0) + else: + adx.append(180) + angldegx.extend(adx) + + # build iverts and verts. Do not use shared iverts and mess with verts a + # tiny bit + verts, cell2d = [], [] + xverts, yverts = grid.cross_section_vertices + xcenters = grid.xcellcenters.ravel() + ycenters = grid.ycellcenters.ravel() + ivert = 0 + for cell_num, xvs in enumerate(xverts): + if (cell_num - 3) % 10 == 0: + xvs[2] -= 0.001 + xvs[3] -= 0.001 + yvs = yverts[cell_num] + + c2drec = [cell_num, xcenters[cell_num], ycenters[cell_num], len(xvs)] + for ix, vert in enumerate(xvs[:-1]): + c2drec.append(ivert) + verts.append([ivert, vert, yvs[ix]]) + ivert += 1 + + c2drec.append(c2drec[4]) + cell2d.append(c2drec) + + nodes = len(cell2d) + nja = len(ja) + nvert = len(verts) + + dis = flopy.mf6.ModflowGwfdisu( + gwf, + nodes=nodes, + nja=nja, + nvert=nvert, + top=np.ravel(grid.top), + bot=np.ravel(grid.botm), + area=np.ones((nodes,)), + idomain=grid.idomain.ravel(), + iac=iac, + ja=ja, + ihc=ihc, + cl12=cl12, + hwva=hwva, + angldegx=angldegx, + vertices=verts, + cell2d=cell2d, + ) + + # build npf, ic, CHD, OC package + npf = flopy.mf6.ModflowGwfnpf(gwf) + ic = flopy.mf6.ModflowGwfic(gwf) + + spd = [] + for i in range(nrow): + spd.append((0 + (i * 10), 0.9)) + spd.append((9 + (i * 10), 0.5)) + + chd = flopy.mf6.ModflowGwfchd(gwf, stress_period_data=spd) + + spd = {0: [("HEAD", "LAST")]} + oc = flopy.mf6.ModflowGwfoc( + gwf, head_filerecord=f"{mname}.hds", saverecord=spd + ) + + sim.write_simulation() + sim.run_simulation() + + heads = gwf.output.head().get_alldata()[-1] + + array = np.zeros((nrow, ncol), dtype=int) + array[:, 5:] = 1 + + mfsplit = Mf6Splitter(sim) + new_sim = mfsplit.split_model(array) + + new_sim.set_sim_path(split_sim_path) + new_sim.write_simulation() + new_sim.run_simulation() + + gwf0 = new_sim.get_model(f"{mname}_0") + gwf1 = new_sim.get_model(f"{mname}_1") + + heads0 = gwf0.output.head().get_alldata()[-1] + heads1 = gwf1.output.head().get_alldata()[-1] + + new_heads = mfsplit.reconstruct_array({0: heads0, 1: heads1}) + + diff = np.abs(heads - new_heads) + if np.max(diff) > 1e-07: + raise AssertionError("Reconstructed head results outside of tolerance") diff --git a/flopy/discretization/unstructuredgrid.py b/flopy/discretization/unstructuredgrid.py index 24d6b6cf73..ed0a201930 100644 --- a/flopy/discretization/unstructuredgrid.py +++ b/flopy/discretization/unstructuredgrid.py @@ -131,6 +131,7 @@ def __init__( angrot=0.0, iac=None, ja=None, + cell2d=None, **kwargs, ): super().__init__( @@ -146,6 +147,11 @@ def __init__( angrot=angrot, **kwargs, ) + if cell2d is not None: + # modflow 6 DISU + xcenters = np.array([i[1] for i in cell2d]) + ycenters = np.array([i[2] for i in cell2d]) + iverts = [list(t)[4:] for t in cell2d] # if any of these are None, then the grid is not valid self._vertices = vertices @@ -239,6 +245,31 @@ def nnodes(self): def nvert(self): return len(self._vertices) + @property + def cell2d(self): + if self.is_valid: + ncenters = len(self._xc) + is_layered = False + if ncenters != self.nnodes and ncenters / self.nnodes % 0: + is_layered = True + + ix_adj = 0 + cell2d = [] + for ix in range(self.nnodes): + if is_layered: + if ix % self.ncpl[0] == 0 and ix != 0: + ix_adj += self.ncpl[0] + iverts = self._iverts[ix - ix_adj] + c2drec = [ + ix, + self._xc[ix - ix_adj], + self._yc[ix - ix_adj], + len(iverts), + ] + c2drec.extend(iverts) + cell2d.append(c2drec) + return cell2d + @property def iverts(self): if self._iverts is not None: @@ -566,6 +597,45 @@ def geo_dataframe(self): gdf = super().geo_dataframe(polys) return gdf + def neighbors(self, node=None, **kwargs): + """ + Method to get nearest neighbors of a cell + + Parameters + ---------- + node : int + model grid node number + + ** kwargs: + method : str + "iac" for specified connections from the DISU package + "rook" for shared edge neighbors + "queen" for shared vertex neighbors + reset : bool + flag to reset the neighbor calculation + + Returns + ------- + list or dict : list of cell node numbers or dict of all cells and + neighbors + """ + method = kwargs.pop("method", None) + reset = kwargs.pop("reset", False) + if method == "iac": + if self._neighbors is None or reset: + neighors = {} + idx0 = 0 + for node, ia in enumerate(self._iac): + idx1 = idx0 + ia + neighors[node] = list(self._ja[idx0 + 1 : idx1]) + self._neighbors = neighors + if node is not None: + return self._neighbors[node] + else: + return self._neighbors + else: + return super().neighbors(node=node, method=method, reset=reset) + def convert_grid(self, factor): """ Method to scale the model grid based on user supplied scale factors @@ -599,6 +669,66 @@ def convert_grid(self, factor): "Grid is not complete and cannot be converted" ) + def clean_iverts(self, inplace=False): + """ + Method to clean up duplicated iverts/verts when vertex information + is supplied in the unstructured grid. + + Parameters: + ---------- + inplace : bool + flag to clean and reset iverts in the current modelgrid object. + Default is False and returns a new modelgrid object + + Returns + ------- + UnstructuredGrid or None + """ + if self.is_valid: + vset = {} + for rec in self._vertices: + vert = (rec[1], rec[2]) + if vert in vset: + vset[vert].add(rec[0]) + else: + vset[vert] = { + rec[0], + } + + cnt = 0 + ivert_remap = {} + vertices = [] + for (xv, yv), iverts in vset.items(): + for iv in iverts: + ivert_remap[iv] = cnt + vertices.append((cnt, xv, yv)) + cnt += 1 + + iverts = [[ivert_remap[v] for v in ivs] for ivs in self.iverts] + if inplace: + self._vertices = vertices + self._iverts = iverts + self._require_cache_updates() + else: + return UnstructuredGrid( + vertices, + iverts=iverts, + xcenters=self._xc, + ycenters=self._yc, + top=self._top, + botm=self._botm, + idomain=self._idomain, + lenuni=self.lenuni, + ncpl=self._ncpl, + crs=self._crs, + prjfile=self._prjfile, + xoff=self.xoffset, + yoff=self.yoffset, + angrot=self.angrot, + iac=self._iac, + ja=self._ja, + ) + def intersect(self, x, y, z=None, local=False, forgive=False): """ Get the CELL2D number of a point with coordinates x and y diff --git a/flopy/mf6/mfmodel.py b/flopy/mf6/mfmodel.py index 281173583d..d5b07f9bde 100644 --- a/flopy/mf6/mfmodel.py +++ b/flopy/mf6/mfmodel.py @@ -445,7 +445,9 @@ def modelgrid(self): if ncpl is None: ncpl = np.array([dis.nodes.get_data()], dtype=int) cell2d = dis.cell2d.array - idomain = np.ones(dis.nodes.array, np.int32) + idomain = dis.idomain.array + if idomain is None: + idomain = np.ones(dis.nodes.array, dtype=int) if cell2d is None: if ( self.simulation.simulation_data.verbosity_level.value @@ -455,13 +457,7 @@ def modelgrid(self): "WARNING: cell2d information missing. Functionality of " "the UnstructuredGrid will be limited." ) - iverts = None - xcenters = None - ycenters = None - else: - iverts = [list(i)[4:] for i in cell2d] - xcenters = dis.cell2d.array["xc"] - ycenters = dis.cell2d.array["yc"] + vertices = dis.vertices.array if vertices is None: if ( @@ -478,9 +474,7 @@ def modelgrid(self): self._modelgrid = UnstructuredGrid( vertices=vertices, - iverts=iverts, - xcenters=xcenters, - ycenters=ycenters, + cell2d=cell2d, top=dis.top.array, botm=dis.bot.array, idomain=idomain, diff --git a/flopy/mf6/utils/model_splitter.py b/flopy/mf6/utils/model_splitter.py index b16ba93b6b..a73daf27af 100644 --- a/flopy/mf6/utils/model_splitter.py +++ b/flopy/mf6/utils/model_splitter.py @@ -152,6 +152,7 @@ def __init__(self, sim, modelname=None): self._connection = None self._uconnection = None self._usg_metadata = None + self._has_angldegx = False self._connection_ivert = None self._model_dict = None self._ivert_vert_remap = None @@ -170,6 +171,8 @@ def __init__(self, sim, modelname=None): self._maw_remaps = {} self._allow_splitting = True + self._fdigits = 1 + @property def new_simulation(self): """ @@ -216,6 +219,7 @@ def switch_models(self, modelname, remap_nodes=False): self._connection = None self._uconnection = None self._usg_metadata = None + self._has_angldegx = False self._connection_ivert = None self._ivert_vert_remap = None self._sfr_mover_connections = [] @@ -657,8 +661,7 @@ def _remap_nodes(self, array): f"{bad_keys} are not in the active model extent; " f"please adjust the model splitting array" ) - - if self._modelgrid.iverts is None: + if self._modelgrid.grid_type == "unstructured": self._map_iac_ja_connections() else: self._connection = self._modelgrid.neighbors( @@ -703,14 +706,7 @@ def _remap_nodes(self, array): for m in np.unique(array): cells = np.asarray(array == m).nonzero()[0] - mapping = np.zeros( - ( - len( - cells, - ) - ), - dtype=int, - ) + mapping = np.zeros((len(cells),), dtype=int) mapping[:] = cells grid_info[m] = [(len(cells),), None, None, mapping] @@ -760,7 +756,7 @@ def _remap_nodes(self, array): if cmdl == mdl: if nnode in new_connections[mdl]["internal"]: new_connections[mdl]["internal"][nnode].append(cnnode) - if self._connection_ivert is None: + if self._uconnection is not None: usg_meta[mdl][nnode]["ihc"].append( int(self._uconnection[node]["ihc"][ix + 1]) ) @@ -770,10 +766,14 @@ def _remap_nodes(self, array): usg_meta[mdl][nnode]["hwva"].append( self._uconnection[node]["hwva"][ix + 1] ) + if self._has_angldegx: + usg_meta[mdl][nnode]["angldegx"].append( + self._uconnection[node]["angldegx"][ix + 1] + ) else: new_connections[mdl]["internal"][nnode] = [cnnode] - if self._connection_ivert is None: + if self._uconnection is not None: usg_meta[mdl][nnode] = { "ihc": [ self._uconnection[node]["ihc"][0], @@ -788,14 +788,20 @@ def _remap_nodes(self, array): self._uconnection[node]["hwva"][ix + 1], ], } + if self._has_angldegx: + usg_meta[mdl][nnode]["angldegx"] = [ + self._uconnection[node]["angldegx"][0], + self._uconnection[node]["angldegx"][ + ix + 1 + ], + ] else: if nnode in new_connections[mdl]["external"]: new_connections[mdl]["external"][nnode].append( (cmdl, cnnode) ) - if self._connection_ivert is not None: - tmp = self._connection_ivert[node] + if self._uconnection is None: exchange_meta[mdl][nnode][cnnode] = [ node, cnode, @@ -813,7 +819,7 @@ def _remap_nodes(self, array): new_connections[mdl]["external"][nnode] = [ (cmdl, cnnode) ] - if self._connection_ivert is not None: + if self._uconnection is None: exchange_meta[mdl][nnode] = { cnnode: [ node, @@ -832,7 +838,7 @@ def _remap_nodes(self, array): ] } - if self._modelgrid.grid_type == "vertex": + if self._modelgrid.grid_type in ("vertex", "unstructured"): self._map_verts_iverts(array) self._new_connections = new_connections @@ -854,16 +860,23 @@ def _map_iac_ja_connections(self): cl12 = self._model.disu.cl12.array ihc = self._model.disu.ihc.array hwva = self._model.disu.hwva.array + angldegx = self._model.disu.angldegx.array + if angldegx is not None: + self._has_angldegx = True idx0 = 0 for ia in iac: idx1 = idx0 + ia cn = ja[idx0 + 1 : idx1] - conn[ja[idx0]] = cn + conn[ja[idx0]] = list(cn) uconn[ja[idx0]] = { - "cl12": cl12[idx0:idx1], - "ihc": ihc[idx0:idx1], - "hwva": hwva[idx0:idx1], + "cl12": list(cl12[idx0:idx1]), + "ihc": list(ihc[idx0:idx1]), + "hwva": list(hwva[idx0:idx1]), } + + if self._has_angldegx: + uconn[ja[idx0]]["angldegx"] = list(angldegx[idx0:idx1]) + idx0 = idx1 self._connection = conn @@ -881,6 +894,8 @@ def _map_verts_iverts(self, array): """ iverts = self._modelgrid.iverts verts = self._modelgrid.verts + if iverts is None: + return ivlut = {mkey: {} for mkey in np.unique(array)} for mkey in np.unique(array): @@ -1013,9 +1028,7 @@ def _remap_filerecords(self, item, value, mapped_data): for mdl in mapped_data.keys(): if mapped_data[mdl]: new_val = value.split(".") - new_val = ( - f"{'.'.join(new_val[0:-1])}_{mdl}.{new_val[-1]}" - ) + new_val = f"{'.'.join(new_val[0:-1])}_{mdl :0{self._fdigits}d}.{new_val[-1]}" mapped_data[mdl][item] = new_val return mapped_data @@ -1034,7 +1047,7 @@ def _remap_disu(self, mapped_data): """ for mkey, metadata in self._usg_metadata.items(): - iac, ja, ihc, cl12, hwva = [], [], [], [], [] + iac, ja, ihc, cl12, hwva, angldegx = [], [], [], [], [], [] for node, params in metadata.items(): conns = [node] + self._new_connections[mkey]["internal"][node] iac.append(len(conns)) @@ -1042,6 +1055,8 @@ def _remap_disu(self, mapped_data): ihc.extend(params["ihc"]) cl12.extend(params["cl12"]) hwva.extend(params["hwva"]) + if self._has_angldegx: + angldegx.extend(params["angldegx"]) assert np.sum(iac) == len(ja) @@ -1051,6 +1066,8 @@ def _remap_disu(self, mapped_data): mapped_data[mkey]["ihc"] = ihc mapped_data[mkey]["cl12"] = cl12 mapped_data[mkey]["hwva"] = hwva + if self._has_angldegx: + mapped_data[mkey]["angldegx"] = angldegx return mapped_data @@ -1195,7 +1212,7 @@ def _remap_array(self, item, mfarray, mapped_data, **kwargs): else: # external array tmp = fnames[lay].split(".") - filename = f"{'.'.join(tmp[:-1])}.{mkey}.{tmp[-1]}" + filename = f"{'.'.join(tmp[:-1])}.{mkey :0{self._fdigits}d}.{tmp[-1]}" cr = { "filename": filename, @@ -1280,7 +1297,7 @@ def _remap_mflist( if how == 3 and new_recarray is not None: tmp = fname.split(".") - filename = f"{'.'.join(tmp[:-1])}.{mkey}.{tmp[-1]}" + filename = f"{'.'.join(tmp[:-1])}.{mkey :0{self._fdigits}d}.{tmp[-1]}" new_recarray = { "data": new_recarray, @@ -1329,7 +1346,7 @@ def _remap_adv_transport(self, package, item, pkg_remap, mapped_data): ) spd[per] = new_recarray - flow_package_const[-2] += f"_{mkey}" + flow_package_const[-2] += f"_{mkey :0{self._fdigits}d}" new_flow_package_name = ".".join(flow_package_const) mapped_data[mkey]["packagedata"] = new_packagedata mapped_data[mkey]["perioddata"] = spd @@ -2218,7 +2235,7 @@ def _remap_fmi(self, package, mapped_data): new_fnames = [] for fname in fnames: new_val = fname.split(".") - new_val = f"{'.'.join(new_val[0:-1])}_{mkey}.{new_val[-1]}" + new_val = f"{'.'.join(new_val[0:-1])}_{mkey :0{self._fdigits}d}.{new_val[-1]}" new_fnames.append(new_val) new_packagedata = packagedata.copy() @@ -2562,11 +2579,11 @@ def _remap_obs(self, package, mapped_data, remapper, pkg_type=None): if isinstance(ofile, (list, tuple)): fname = ofile[0] tmp = fname.split(".") - tmp[-2] += f"_{mkey}" + tmp[-2] += f"_{mkey :0{self._fdigits}d}" ofile[0] = ".".join(tmp) else: tmp = ofile.split(".") - tmp[-2] += f"_{mkey}" + tmp[-2] += f"_{mkey :0{self._fdigits}d}" ofile = ".".join(tmp) if pkg_type is None: @@ -2804,6 +2821,9 @@ def _remap_package(self, package, ismvr=False): ), ): for item, value in package.__dict__.items(): + if item in ("nja", "ja", "cl12", "ihc", "hwva", "angldegx"): + continue + if item in ("delr", "delc"): for mkey, d in self._grid_info.items(): if item == "delr": @@ -2832,13 +2852,27 @@ def _remap_package(self, package, ismvr=False): elif item == "iac": mapped_data = self._remap_disu(mapped_data) - break elif item == "xorigin": for mkey in self._model_dict.keys(): for k, v in self._offsets[mkey].items(): mapped_data[mkey][k] = v + elif item in ("vertices", "cell2d"): + if value.array is not None: + if item == "cell2d": + mapped_data = self._remap_cell2d( + item, value, mapped_data + ) + else: + for mkey in self._model_dict.keys(): + mapped_data[mkey][item] = ( + self._ivert_vert_remap[mkey][item] + ) + mapped_data[mkey]["nvert"] = len( + self._ivert_vert_remap[mkey][item] + ) + elif isinstance(value, mfdataarray.MFArray): mapped_data = self._remap_array(item, value, mapped_data) @@ -3066,19 +3100,26 @@ def _create_exchanges(self): if self._modelgrid.grid_type == "unstructured": # use existing connection information aux = False + grid_dict = {i: m.modelgrid for i, m in self._model_dict.items()} for m0, model in self._model_dict.items(): + grid0 = grid_dict[m0] exg_nodes = self._new_connections[m0]["external"] for m1 in nmodels: + grid1 = grid_dict[m1] if m1 in built: continue if m1 == m0: continue exchange_data = [] for node0, exg_list in exg_nodes.items(): + if grid0.idomain[node0] < 1: + continue for exg in exg_list: if exg[0] != m1: continue node1 = exg[-1] + if grid1.idomain[node1] < 1: + continue exg_meta0 = self._exchange_metadata[m0][node0][ node1 ] @@ -3104,7 +3145,7 @@ def _create_exchanges(self): exgmnameb=mname1, nexg=len(exchange_data), exchangedata=exchange_data, - filename=f"sim_{m0}_{m1}.{extension}", + filename=f"sim_{m0 :0{self._fdigits}d}_{m1 :0{self._fdigits}d}.{extension}", newton=newton, xt3d=xt3d, ) @@ -3242,7 +3283,7 @@ def _create_exchanges(self): auxiliary=["ANGLDEGX", "CDIST"], nexg=len(exchange_data), exchangedata=exchange_data, - filename=f"sim_{m0}_{m1}.{extension}", + filename=f"sim_{m0 :0{self._fdigits}d}_{m1 :0{self._fdigits}d}.{extension}", newton=newton, xt3d=xt3d, ) @@ -3286,6 +3327,11 @@ def split_model(self, array): "is part of a split simulation" ) + # set number formatting string for file paths + array = np.array(array).astype(int) + s = str(np.max(array)) + self._fdigits = len(s) + self._remap_nodes(array) if self._new_sim is None: @@ -3306,7 +3352,7 @@ def split_model(self, array): mdl_cls = PackageContainer.model_factory(self._model_type) self._model_dict[mkey] = mdl_cls( self._new_sim, - modelname=f"{self._modelname}_{mkey}", + modelname=f"{self._modelname}_{mkey :0{self._fdigits}d}", **nam_options, ) @@ -3320,9 +3366,3 @@ def split_model(self, array): epaks = self._create_exchanges() return self._new_sim - - -# todo: development notes: -# Then set up checks for model splitting -# (ex. doesn't parallel a fault, doesn't cut through a lake, -# active cells in modelgrid...) From 8656e4e018a26c3736ee65626bb30edbc3f1b407 Mon Sep 17 00:00:00 2001 From: wpbonelli Date: Fri, 16 Aug 2024 16:07:48 -0400 Subject: [PATCH 03/10] chore(git-cliff): relax patterns for refactor group (#2293) Add an unquantified wildcard to the pattern in cliff.toml so commit messages like 'updates(...): ...' are included in the refactor group in the auto-generated changelog at release time --- cliff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cliff.toml b/cliff.toml index 9d5ca0d7bd..624da85e54 100644 --- a/cliff.toml +++ b/cliff.toml @@ -26,7 +26,7 @@ commit_parsers = [ { message = "^[bB]ug", group = "fix"}, { message = "^[pP]erf", group = "perf"}, { message = "^[rR]efactor", group = "refactor"}, - { message = "^[uU]pdate", group = "refactor"}, + { message = "^[uU]pdate.*", group = "refactor"}, { message = "^[dD]oc.*", group = "docs", skip = true}, { message = "^[bB]inder", group = "docs", skip = true}, { message = "^[nN]otebook.*", group = "docs", skip = true}, From f86881d6354071bf7c675384c2684ea184e281eb Mon Sep 17 00:00:00 2001 From: Joshua Larsen Date: Fri, 16 Aug 2024 23:50:49 -0700 Subject: [PATCH 04/10] fix(ParticleTrackFile.write_shapefile): check for "k" even if "i", "j are not present (#2294) * fix for "k", "node" output present with mp7 pathline and timeseries --- flopy/utils/particletrackfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flopy/utils/particletrackfile.py b/flopy/utils/particletrackfile.py index 50c004db8e..378ff830ea 100644 --- a/flopy/utils/particletrackfile.py +++ b/flopy/utils/particletrackfile.py @@ -291,11 +291,11 @@ def write_shapefile( if "particlegroup" in names: t.append(ra.particlegroup[0]) t.append(ra.time.max()) + if "k" in names: + t.append(ra.k[loc_inds]) if "node" in names: t.append(ra.node[loc_inds]) else: - if "k" in names: - t.append(ra.k[loc_inds]) if "i" in names: t.append(ra.i[loc_inds]) if "j" in names: From c42d8787bbcd4131b2f493ebc5a5a384b2e8b861 Mon Sep 17 00:00:00 2001 From: langevin-usgs Date: Thu, 22 Aug 2024 13:20:13 -0500 Subject: [PATCH 05/10] fix(modelgrid): add more support for mf6 surface water models (#2295) * fix(modelgrid): add more support for mf6 surface water models * ruff --- flopy/discretization/structuredgrid.py | 18 +++++---- flopy/discretization/vertexgrid.py | 5 ++- flopy/mf6/mfmodel.py | 53 ++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/flopy/discretization/structuredgrid.py b/flopy/discretization/structuredgrid.py index 13f15346ce..59834b3826 100644 --- a/flopy/discretization/structuredgrid.py +++ b/flopy/discretization/structuredgrid.py @@ -192,14 +192,18 @@ def __init__( if top is not None: assert self.__nrow * self.__ncol == len(np.ravel(top)) if botm is not None: - assert self.__nrow * self.__ncol == len(np.ravel(botm[0])) - if nlay is not None: - self.__nlay = nlay - else: - if laycbd is not None: - self.__nlay = len(botm) - np.count_nonzero(laycbd) + if botm.ndim == 3: + assert self.__nrow * self.__ncol == len(np.ravel(botm[0])) + if nlay is not None: + self.__nlay = nlay else: - self.__nlay = len(botm) + if laycbd is not None: + self.__nlay = len(botm) - np.count_nonzero(laycbd) + else: + self.__nlay = len(botm) + elif botm.ndim == 2: + assert botm.shape == (self.__nrow, self.__ncol) + self.__nlay = 1 else: self.__nlay = nlay if laycbd is not None: diff --git a/flopy/discretization/vertexgrid.py b/flopy/discretization/vertexgrid.py index ea49d1291a..e4a2837631 100644 --- a/flopy/discretization/vertexgrid.py +++ b/flopy/discretization/vertexgrid.py @@ -140,7 +140,10 @@ def ncpl(self): if self._cell1d is not None: return len(self._cell1d) if self._botm is not None: - return len(self._botm[0]) + if self._botm.ndim == 2: # (nlay, ncpl) + return self._botm.shape[1] + elif self._botm.ndim == 1: # (ncpl,) + return self._botm.shape[0] if self._cell2d is not None and self._nlay is None: return len(self._cell2d) else: diff --git a/flopy/mf6/mfmodel.py b/flopy/mf6/mfmodel.py index d5b07f9bde..3a54e4f525 100644 --- a/flopy/mf6/mfmodel.py +++ b/flopy/mf6/mfmodel.py @@ -508,7 +508,7 @@ def modelgrid(self): angrot=self._modelgrid.angrot, ) else: - botm = dis.botm.array + botm = dis.bottom.array idomain = dis.idomain.array if idomain is None: force_resync = True @@ -516,7 +516,45 @@ def modelgrid(self): self._modelgrid = VertexGrid( vertices=dis.vertices.array, cell1d=dis.cell1d.array, - top=dis.top.array, + top=None, + botm=botm, + idomain=idomain, + lenuni=dis.length_units.array, + crs=self._modelgrid.crs, + xoff=self._modelgrid.xoffset, + yoff=self._modelgrid.yoffset, + angrot=self._modelgrid.angrot, + ) + elif self.get_grid_type() == DiscretizationType.DIS2D: + dis = self.get_package("dis2d") + if not hasattr(dis, "_init_complete"): + if not hasattr(dis, "delr"): + # dis package has not yet been initialized + return self._modelgrid + else: + # dis package has been partially initialized + self._modelgrid = StructuredGrid( + delc=dis.delc.array, + delr=dis.delr.array, + top=None, + botm=None, + idomain=None, + lenuni=None, + crs=self._modelgrid.crs, + xoff=self._modelgrid.xoffset, + yoff=self._modelgrid.yoffset, + angrot=self._modelgrid.angrot, + ) + else: + botm = dis.bottom.array + idomain = dis.idomain.array + if idomain is None: + force_resync = True + idomain = self._resolve_idomain(idomain, botm) + self._modelgrid = StructuredGrid( + delc=dis.delc.array, + delr=dis.delr.array, + top=None, botm=botm, idomain=idomain, lenuni=dis.length_units.array, @@ -546,7 +584,7 @@ def modelgrid(self): angrot=self._modelgrid.angrot, ) else: - botm = dis.botm.array + botm = dis.bottom.array idomain = dis.idomain.array if idomain is None: force_resync = True @@ -554,7 +592,7 @@ def modelgrid(self): self._modelgrid = VertexGrid( vertices=dis.vertices.array, cell2d=dis.cell2d.array, - top=dis.top.array, + top=None, botm=botm, idomain=idomain, lenuni=dis.length_units.array, @@ -1247,6 +1285,13 @@ def get_grid_type(self): is not None ): return DiscretizationType.DISV1D + elif ( + package_recarray.search_data( + f"dis2d{structure.get_version_string()}", 0 + ) + is not None + ): + return DiscretizationType.DIS2D elif ( package_recarray.search_data( f"disv2d{structure.get_version_string()}", 0 From 4ea71927f251c3675acf1b578bca623d023ccd2c Mon Sep 17 00:00:00 2001 From: langevin-usgs Date: Fri, 23 Aug 2024 13:52:40 -0500 Subject: [PATCH 06/10] feat(cell1d): add support for 1D vertex grids (#2296) --- flopy/discretization/grid.py | 4 ++-- flopy/discretization/vertexgrid.py | 38 ++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/flopy/discretization/grid.py b/flopy/discretization/grid.py index 2681a671cd..e2346e574f 100644 --- a/flopy/discretization/grid.py +++ b/flopy/discretization/grid.py @@ -595,7 +595,7 @@ def xyzvertices(self): def cross_section_vertices(self): return self.xyzvertices[0], self.xyzvertices[1] - def geo_dataframe(self, polys): + def geo_dataframe(self, features, featuretype="Polygon"): """ Method returns a geopandas GeoDataFrame of the Grid @@ -606,7 +606,7 @@ def geo_dataframe(self, polys): from ..utils.geospatial_utils import GeoSpatialCollection gc = GeoSpatialCollection( - polys, shapetype=["Polygon" for _ in range(len(polys))] + features, shapetype=[featuretype for _ in range(len(features))] ) gdf = gc.geo_dataframe if self.crs is not None: diff --git a/flopy/discretization/vertexgrid.py b/flopy/discretization/vertexgrid.py index e4a2837631..c45c4e245e 100644 --- a/flopy/discretization/vertexgrid.py +++ b/flopy/discretization/vertexgrid.py @@ -168,7 +168,7 @@ def iverts(self): def cell1d(self): if self._cell1d is not None: return [ - [ivt for ivt in t if ivt is not None] for t in self._cell2d + [ivt for ivt in t if ivt is not None] for t in self._cell1d ] @property @@ -221,15 +221,30 @@ def grid_lines(self): xgrid = self.xvertices ygrid = self.yvertices + # close the cell by connecting the last vertex with the first + close_cell = True + if self.cell1d is not None: + close_cell = False + + # go through each cell and create a line segement for each face lines = [] - for ncell, verts in enumerate(xgrid): - for ix, vert in enumerate(verts): + ncpl = len(xgrid) + for icpl in range(ncpl): + xcoords = xgrid[icpl] + ycoords = ygrid[icpl] + npoints = len(xcoords) + for ipoint in range(npoints - 1): lines.append( [ - (xgrid[ncell][ix - 1], ygrid[ncell][ix - 1]), - (xgrid[ncell][ix], ygrid[ncell][ix]), + (xcoords[ipoint], ycoords[ipoint]), + (xcoords[ipoint + 1], ycoords[ipoint + 1]), ] ) + if close_cell: + lines.append( + [(xcoords[-1], ycoords[-1]), (xcoords[0], ycoords[0])] + ) + self._copy_cache = True return lines @@ -300,8 +315,11 @@ def geo_dataframe(self): ------- GeoDataFrame """ - polys = [[self.get_cell_vertices(nn)] for nn in range(self.ncpl)] - gdf = super().geo_dataframe(polys) + cells = [[self.get_cell_vertices(nn)] for nn in range(self.ncpl)] + featuretype = "Polygon" + if self._cell1d is not None: + featuretype = "multilinestring" + gdf = super().geo_dataframe(cells, featuretype) return gdf def convert_grid(self, factor): @@ -458,12 +476,12 @@ def _build_grid_geometry_info(self): if self._cell1d is not None: zcenters = [] zvertices = [] - vertexdict = {v[0]: [v[1], v[2], v[3]] for v in self._vertices} + vertexdict = {v[0]: [v[1], v[2]] for v in self._vertices} for cell1d in self.cell1d: cell1d = tuple(cell1d) xcenters.append(cell1d[1]) ycenters.append(cell1d[2]) - zcenters.append(cell1d[3]) + zcenters.append(0.0) vert_number = [] for i in cell1d[3:]: @@ -475,7 +493,7 @@ def _build_grid_geometry_info(self): for ix in vert_number: xcellvert.append(vertexdict[ix][0]) ycellvert.append(vertexdict[ix][1]) - zcellvert.append(vertexdict[ix][2]) + zcellvert.append(0.0) xvertices.append(xcellvert) yvertices.append(ycellvert) zvertices.append(zcellvert) From 0b8be82dc3dd2d340e24a31f25ad1d046666eff9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:51:29 -0400 Subject: [PATCH 07/10] chore(deps): bump orhun/git-cliff-action from 3 to 4 (#2297) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f3a5717b0b..f564cec9e5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: - name: Generate changelog id: cliff - uses: orhun/git-cliff-action@v3 + uses: orhun/git-cliff-action@v4 with: config: cliff.toml args: --verbose --unreleased --tag ${{ steps.version.outputs.version }} From bfa344aa3f828b87ccac79c4985dedfe267f0db7 Mon Sep 17 00:00:00 2001 From: wpbonelli Date: Thu, 5 Sep 2024 09:28:37 -0400 Subject: [PATCH 08/10] chore(git-cliff): trim leading whitespace in commit messages (#2302) Commit messages were omitted from the generated changelog if they had any leading whitespace. Fix it. --- cliff.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cliff.toml b/cliff.toml index 624da85e54..7136fa3d43 100644 --- a/cliff.toml +++ b/cliff.toml @@ -20,6 +20,9 @@ trim = true conventional_commits = true filter_unconventional = true split_commits = false +commit_preprocessors = [ + { pattern = "^ *", replace = ""} +] commit_parsers = [ { message = "^[fF]eat", group = "feat"}, { message = "^[fF]ix", group = "fix"}, From 8229af8d7a0aa677e2a6df69f7ad3acf8c88a08c Mon Sep 17 00:00:00 2001 From: wpbonelli Date: Thu, 5 Sep 2024 09:56:24 -0400 Subject: [PATCH 09/10] ci(release): explicitly specify files to commit (#2304) Explicitly add files to commit after updating the version number and generating the changelog. Previously we used 'git add -A'. As of git-cliff-action@v4 this includes the git-cliff binary and related content which is apparently installed to the working directory. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f564cec9e5..0dd186bedc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -120,7 +120,7 @@ jobs: git config core.sharedRepository true git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A + git add flopy docs .docs CITATION.cff README.md version.txt git commit -m "ci(release): set version to ${{ steps.version.outputs.version }}, update plugins from DFN files, update changelog" git push origin "${{ github.ref_name }}" From 167ad393468b69d5bc4233643bd7f460a6b41ceb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:06:36 +0000 Subject: [PATCH 10/10] ci(release): set version to 3.8.1, update plugins from DFN files, update changelog --- .docs/md/version_changes.md | 15 +++++++++++++++ CITATION.cff | 4 ++-- README.md | 4 ++-- docs/PyPI_release.md | 2 +- flopy/version.py | 4 ++-- version.txt | 2 +- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.docs/md/version_changes.md b/.docs/md/version_changes.md index 68d22ebe19..4620819950 100644 --- a/.docs/md/version_changes.md +++ b/.docs/md/version_changes.md @@ -1,4 +1,19 @@ # Changelog +### Version 3.8.1 + +#### New features + +* [feat(cell1d)](https://github.com/modflowpy/flopy/commit/4ea71927f251c3675acf1b578bca623d023ccd2c): Add support for 1D vertex grids (#2296). Committed by langevin-usgs on 2024-08-23. + +#### Bug fixes + +* [fix(ParticleTrackFile.write_shapefile)](https://github.com/modflowpy/flopy/commit/f86881d6354071bf7c675384c2684ea184e281eb): Check for "k" even if "i", "j are not present (#2294). Committed by Joshua Larsen on 2024-08-17. +* [fix(modelgrid)](https://github.com/modflowpy/flopy/commit/c42d8787bbcd4131b2f493ebc5a5a384b2e8b861): Add more support for mf6 surface water models (#2295). Committed by langevin-usgs on 2024-08-22. + +#### Refactoring + +* [refactor(model_splitter.py)](https://github.com/modflowpy/flopy/commit/d02967db167b98e32cfaa26ef6636475ea2441a8): Update UnstructuredGrid support (#2292). Committed by Joshua Larsen on 2024-08-16. + ### Version 3.8.0 #### New features diff --git a/CITATION.cff b/CITATION.cff index c68ec7d2fa..9703b37c87 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,8 +3,8 @@ message: If you use this software, please cite both the article from preferred-c references, and the software itself. type: software title: FloPy -version: 3.9.0.dev0 -date-released: '2024-08-08' +version: 3.8.1 +date-released: '2024-09-05' doi: 10.5066/F7BK19FH abstract: A Python package to create, run, and post-process MODFLOW-based models. repository-artifact: https://pypi.org/project/flopy diff --git a/README.md b/README.md index da73f1020a..b2ae65df49 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ flopy3 -### Version 3.9.0.dev0 +### Version 3.8.1 [![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) @@ -150,7 +150,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., 2024, FloPy v3.9.0.dev0: U.S. Geological Survey Software Release, 08 August 2024, 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., 2024, FloPy v3.8.1: U.S. Geological Survey Software Release, 05 September 2024, https://doi.org/10.5066/F7BK19FH](https://doi.org/10.5066/F7BK19FH) Additional FloPy Related Publications diff --git a/docs/PyPI_release.md b/docs/PyPI_release.md index 7c224dcf80..c9a85823d7 100644 --- a/docs/PyPI_release.md +++ b/docs/PyPI_release.md @@ -30,4 +30,4 @@ 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., 2024, FloPy v3.9.0.dev0: U.S. Geological Survey Software Release, 08 August 2024, 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., 2024, FloPy v3.8.1: U.S. Geological Survey Software Release, 05 September 2024, https://doi.org/10.5066/F7BK19FH](https://doi.org/10.5066/F7BK19FH) diff --git a/flopy/version.py b/flopy/version.py index bc74242968..f697f5dcc5 100644 --- a/flopy/version.py +++ b/flopy/version.py @@ -1,4 +1,4 @@ # flopy version file automatically created using -# update_version.py on August 08, 2024 14:29:26 +# update_version.py on September 05, 2024 13:59:10 -__version__ = "3.9.0.dev0" +__version__ = "3.8.1" diff --git a/version.txt b/version.txt index 70abe80d9d..aaaff91926 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -3.9.0.dev0 \ No newline at end of file +3.8.1 \ No newline at end of file