diff --git a/.docs/Notebooks/groundwater_paper_example_1.py b/.docs/Notebooks/groundwater_paper_example_1.py index 34cb627ea..3fe0b12a1 100644 --- a/.docs/Notebooks/groundwater_paper_example_1.py +++ b/.docs/Notebooks/groundwater_paper_example_1.py @@ -17,7 +17,7 @@ # ## Basic Flopy example # # From: -# Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Starn, J. J. and Fienen, M. N., 2016, Scripting MODFLOW Model Development Using Python and FloPy: Groundwater, v. 54, p. 733–739, https://doi.org/10.1111/gwat.12413. +# Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Starn, J. J. and Fienen, M. N., 2016, Scripting MODFLOW Model Development Using Python and FloPy: Groundwater, v. 54, p. 733-739, https://doi.org/10.1111/gwat.12413. # Import the `modflow` and `utils` subpackages of FloPy and give them the aliases `fpm` and `fpu`, respectively diff --git a/.docs/Notebooks/groundwater_paper_uspb_example.py b/.docs/Notebooks/groundwater_paper_uspb_example.py index 690f6ef27..a4cd8d1f8 100644 --- a/.docs/Notebooks/groundwater_paper_uspb_example.py +++ b/.docs/Notebooks/groundwater_paper_uspb_example.py @@ -17,7 +17,7 @@ # # Capture fraction example # # From: -# Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Starn, J. J. and Fienen, M. N., 2016, Scripting MODFLOW Model Development Using Python and FloPy: Groundwater, v. 54, p. 733–739, https://doi.org/10.1111/gwat.12413. +# Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Starn, J. J. and Fienen, M. N., 2016, Scripting MODFLOW Model Development Using Python and FloPy: Groundwater, v. 54, p. 733-739, https://doi.org/10.1111/gwat.12413. # + import os diff --git a/.docs/Notebooks/mfusg_zaidel_example.py b/.docs/Notebooks/mfusg_zaidel_example.py index 477e18494..240fd6b0c 100644 --- a/.docs/Notebooks/mfusg_zaidel_example.py +++ b/.docs/Notebooks/mfusg_zaidel_example.py @@ -21,7 +21,7 @@ # # One of the most challenging numerical cases for MODFLOW arises from drying-rewetting problems often associated with abrupt changes in the elevations of impervious base of a thin unconfined aquifer. This problem simulates a discontinuous water table configuration over a stairway impervious base and flow between constant-head boundaries in column 1 and 200. This problem is based on # -# [Zaidel, J. (2013), Discontinuous Steady-State Analytical Solutions of the Boussinesq Equation and Their Numerical Representation by Modflow. Groundwater, 51: 952–959. doi: 10.1111/gwat.12019](https://doi.org/10.1111/gwat.12019) +# [Zaidel, J. (2013), Discontinuous Steady-State Analytical Solutions of the Boussinesq Equation and Their Numerical Representation by Modflow. Groundwater, 51: 952-959. doi: 10.1111/gwat.12019](https://doi.org/10.1111/gwat.12019) # # The model consistes of a grid of 200 columns, 1 row, and 1 layer; a bottom altitude of ranging from 20 to 0 m; constant heads of 23 and 5 m in column 1 and 200, respectively; and a horizontal hydraulic conductivity of $1x10^{-4}$ m/d. The discretization is 5 m in the row direction for all cells. # diff --git a/.docs/Notebooks/mt3dms_sft_lkt_uzt_tutorial.py b/.docs/Notebooks/mt3dms_sft_lkt_uzt_tutorial.py index 55ad5251f..88e29dd25 100644 --- a/.docs/Notebooks/mt3dms_sft_lkt_uzt_tutorial.py +++ b/.docs/Notebooks/mt3dms_sft_lkt_uzt_tutorial.py @@ -421,7 +421,7 @@ nlakes = int(np.max(LakArr)) ipakcb = ipakcb # From above theta = -1.0 # Implicit -nssitr = 10 # Maximum number of iterations for Newton’s method +nssitr = 10 # Maximum number of iterations for Newton's method sscncr = 1.000e-03 # Convergence criterion for equilibrium lake stage solution surfdep = 2.000e00 # Height of small topological variations in lake-bottom stages = 268.00 # Initial stage of each lake at the beginning of the run diff --git a/.docs/Notebooks/uzf_example.py b/.docs/Notebooks/uzf_example.py index 880d55e1d..ba4f9ce36 100644 --- a/.docs/Notebooks/uzf_example.py +++ b/.docs/Notebooks/uzf_example.py @@ -19,7 +19,7 @@ # # Unsaturated Zone Flow (UZF) Package demo # Demonstrates functionality of the flopy UZF module using the example from [Niswonger and others (2006)](https://pubs.usgs.gov/tm/2006/tm6a19/). This is the same as the SFR example problem from Prudic and others (2004; -# p. 13–19), except the UZF package replaces the ET and RCH packages. +# p. 13-19), except the UZF package replaces the ET and RCH packages. # # #### Problem description: # diff --git a/.docs/create_rstfiles.py b/.docs/create_rstfiles.py index 80436df6b..8eb6af103 100644 --- a/.docs/create_rstfiles.py +++ b/.docs/create_rstfiles.py @@ -98,7 +98,7 @@ def create_examples_rst(): "mt3d": {"title": "MT3D and SEAWAT examples", "files": []}, "2016gw-paper": { "title": "Examples from Bakker and others (2016)", - "description": "Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Starn, J. J. and Fienen, M. N., 2016, Scripting MODFLOW Model Development Using Python and FloPy: Groundwater, v. 54, p. 733–739, https://doi.org/10.1111/gwat.12413.", + "description": "Bakker, Mark, Post, Vincent, Langevin, C. D., Hughes, J. D., White, J. T., Starn, J. J. and Fienen, M. N., 2016, Scripting MODFLOW Model Development Using Python and FloPy: Groundwater, v. 54, p. 733–739, https://doi.org/10.1111/gwat.12413.", # noqa: RUF001 "files": [], }, "2023gw-paper": { diff --git a/autotest/test_cbc_full3D.py b/autotest/test_cbc_full3D.py index d714b777d..7183b380a 100644 --- a/autotest/test_cbc_full3D.py +++ b/autotest/test_cbc_full3D.py @@ -130,7 +130,7 @@ def test_cbc_full3D_mf6(function_tmpdir, path): sim.run_simulation() # get the groundwater model and determine the size of the model grid - gwf_name = list(sim.model_names)[0] + gwf_name = next(iter(sim.model_names)) gwf = sim.get_model(gwf_name) nnodes, shape3d = gwf.modelgrid.nnodes, gwf.modelgrid.shape diff --git a/autotest/test_grid.py b/autotest/test_grid.py index fa6c5cc7c..d38df29b7 100644 --- a/autotest/test_grid.py +++ b/autotest/test_grid.py @@ -349,7 +349,7 @@ def test_structured_xyz_intersect(example_data_path): def test_vertex_xyz_intersect(example_data_path): sim = MFSimulation.load(sim_ws=example_data_path / "mf6" / "test003_gwfs_disv") - ml = sim.get_model(list(sim.model_names)[0]) + ml = sim.get_model(next(iter(sim.model_names))) mg = ml.modelgrid assert mg.size == np.prod((mg.nlay, mg.ncpl)) diff --git a/autotest/test_zonbud_utility.py b/autotest/test_zonbud_utility.py index 1e9f71d01..2ca0573b6 100644 --- a/autotest/test_zonbud_utility.py +++ b/autotest/test_zonbud_utility.py @@ -320,7 +320,7 @@ def test_zonebudget_6(function_tmpdir, example_data_path): df = zb.get_dataframes() - assert list(df)[0] == "test_alias", "Alias testing failed" + assert next(iter(df)) == "test_alias", "Alias testing failed" @pytest.mark.mf6 diff --git a/flopy/export/metadata.py b/flopy/export/metadata.py index c1cdb462a..28864bee1 100644 --- a/flopy/export/metadata.py +++ b/flopy/export/metadata.py @@ -76,11 +76,11 @@ def __init__(self, sciencebase_id, model): self.creator_institution ) # also in CF convention for global attributes self.project = self.sb["title"] - self.publisher_name = [ + self.publisher_name = next( d.get("name") for d in self.sb["contacts"] if "publisher" in d.get("type").lower() - ][0] + ) self.publisher_email = self.sb["provenance"]["linkProcess"].get("processedBy") # TODO: should publisher_url be obtained from linkReference? # publisher_url = self.sb['provenance']['linkProcess'].get('linkReference') @@ -106,7 +106,7 @@ def __init__(self, sciencebase_id, model): def _get_xml_attribute(self, attr): try: - return list(self.xmlroot.iter(attr))[0].text + return next(iter(self.xmlroot.iter(attr))).text except: return None @@ -116,9 +116,9 @@ def bounds(self): @property def creator(self): - return [ + return next( d for d in self.sb["contacts"] if "point of contact" in d["type"].lower() - ][0] + ) @property def creator_url(self): @@ -181,7 +181,7 @@ def time_coverage(self): l = self.sb["dates"] tc = {} for t in ["start", "end"]: - tc[t] = [d.get("dateString") for d in l if t in d["type"].lower()][0] + tc[t] = next(d.get("dateString") for d in l if t in d["type"].lower()) if not np.all(self.model_time.steady_state) and pd is not None: # replace with times from model reference tc["start"] = self.model_time.start_datetime diff --git a/flopy/export/vtk.py b/flopy/export/vtk.py index d0d27d04c..7f15c2516 100644 --- a/flopy/export/vtk.py +++ b/flopy/export/vtk.py @@ -787,7 +787,7 @@ def add_transient_array(self, d, name=None, masked_values=None): if not self._vtk_geometry_set: self._set_vtk_grid_geometry() - k = list(d.keys())[0] + k = next(iter(d.keys())) transient = {} if isinstance(d[k], DataInterface): if d[k].data_type in (DataType.array2d, DataType.array3d): @@ -937,7 +937,7 @@ def add_transient_vector(self, d, name, masked_values=None): self._set_vtk_grid_geometry() if self.__transient_data: - k = list(self.__transient_data.keys())[0] + k = next(iter(self.__transient_data.keys())) if len(d) != len(self.__transient_data[k]): print( "Transient vector not same size as transient arrays time " diff --git a/flopy/mbase.py b/flopy/mbase.py index 528c89fc0..3a187a93e 100644 --- a/flopy/mbase.py +++ b/flopy/mbase.py @@ -1554,9 +1554,9 @@ def check( if p.unit_number[i] != 0: if p.unit_number[i] in package_units.values(): duplicate_units[p.name[i]] = p.unit_number[i] - otherpackage = [ + otherpackage = next( k for k, v in package_units.items() if v == p.unit_number[i] - ][0] + ) duplicate_units[otherpackage] = p.unit_number[i] if len(duplicate_units) > 0: for k, v in duplicate_units.items(): diff --git a/flopy/mfusg/mfusgcln.py b/flopy/mfusg/mfusgcln.py index e88b8f312..cd4cacd07 100644 --- a/flopy/mfusg/mfusgcln.py +++ b/flopy/mfusg/mfusgcln.py @@ -13,7 +13,7 @@ Process for MODFLOW-USG, GSI Environmental, March, 2021 Panday, Sorab, Langevin, C.D., Niswonger, R.G., Ibaraki, Motomu, and Hughes, -J.D., 2013, MODFLOW–USG version 1: An unstructured grid version of MODFLOW +J.D., 2013, MODFLOW-USG version 1: An unstructured grid version of MODFLOW for simulating groundwater flow and tightly coupled processes using a control volume finite-difference formulation: U.S. Geological Survey Techniques and Methods, book 6, chap. A45, 66 p. diff --git a/flopy/modflow/mfmnw1.py b/flopy/modflow/mfmnw1.py index dcdc4736e..2b543db50 100644 --- a/flopy/modflow/mfmnw1.py +++ b/flopy/modflow/mfmnw1.py @@ -447,11 +447,11 @@ def _parse_5(f, itmp, qfrcmn_default=None, qfrcmx_default=None, qcut_default="") qfrcmn = 0.0 qfrcmx = 0.0 if "qcut" in linetxt: - txt = [t for t in line if "qcut" in t][0] + txt = next(t for t in line if "qcut" in t) qcut = txt line.remove(txt) elif "%cut" in linetxt: - txt = [t for t in line if "%cut" in t][0] + txt = next(t for t in line if "%cut" in t) qcut = txt line.remove(txt) if "qcut" in linetxt or "%cut" in linetxt: diff --git a/flopy/modflow/mfmnw2.py b/flopy/modflow/mfmnw2.py index 6fb657367..091b9d4d6 100644 --- a/flopy/modflow/mfmnw2.py +++ b/flopy/modflow/mfmnw2.py @@ -1085,7 +1085,7 @@ def __init__( if ( "k" not in stress_period_data[ - list(stress_period_data.keys())[0] + next(iter(stress_period_data.keys())) ].dtype.names ): self._add_kij_to_stress_period_data() diff --git a/flopy/modflow/mfrch.py b/flopy/modflow/mfrch.py index f1279a4f9..080850756 100644 --- a/flopy/modflow/mfrch.py +++ b/flopy/modflow/mfrch.py @@ -193,7 +193,7 @@ def check( # check for unusually high or low values of mean R/T hk_package = {"UPW", "LPF"}.intersection(set(self.parent.get_package_list())) if len(hk_package) > 0 and self.parent.structured: - pkg = list(hk_package)[0] + pkg = next(iter(hk_package)) # handle quasi-3D layers # (ugly, would be nice to put this else where in a general function) diff --git a/flopy/modflow/mfuzf1.py b/flopy/modflow/mfuzf1.py index 69519b05a..bb03bd462 100644 --- a/flopy/modflow/mfuzf1.py +++ b/flopy/modflow/mfuzf1.py @@ -552,7 +552,7 @@ def __init__( # Dataset 9, 11, 13 and 15 will be written automatically in the # write_file function # Data Set 10 - # [FINF (NCOL, NROW)] – U2DREL + # [FINF (NCOL, NROW)] - U2DREL self.finf = Transient2d(model, (nrow, ncol), np.float32, finf, name="finf") if ietflg > 0: diff --git a/flopy/plot/crosssection.py b/flopy/plot/crosssection.py index ababa923b..14d8c3fb9 100644 --- a/flopy/plot/crosssection.py +++ b/flopy/plot/crosssection.py @@ -93,7 +93,7 @@ def __init__( else: self.ax = ax - onkey = list(line.keys())[0] + onkey = next(iter(line.keys())) self.__geographic_xpts = None # un-translate model grid into model coordinates @@ -199,7 +199,7 @@ def __init__( self.xypts[node] = points if len(self.xypts) < 2: - if len(list(self.xypts.values())[0]) < 2: + if len(next(iter(self.xypts.values()))) < 2: s = ( "cross-section cannot be created\n." " less than 2 points intersect the model grid\n" diff --git a/flopy/utils/compare.py b/flopy/utils/compare.py index 89c10619e..5c763178e 100644 --- a/flopy/utils/compare.py +++ b/flopy/utils/compare.py @@ -1083,8 +1083,8 @@ def compare_concentrations( def compare_stages( - namefile1: Union[str, os.PathLike] = None, - namefile2: Union[str, os.PathLike] = None, + namefile1: Optional[Union[str, os.PathLike]] = None, + namefile2: Optional[Union[str, os.PathLike]] = None, files1: Optional[Union[str, os.PathLike, list[Union[str, os.PathLike]]]] = None, files2: Optional[Union[str, os.PathLike, list[Union[str, os.PathLike]]]] = None, htol=0.001, @@ -1295,8 +1295,8 @@ def compare_stages( def compare( - namefile1: Union[str, os.PathLike] = None, - namefile2: Union[str, os.PathLike] = None, + namefile1: Optional[Union[str, os.PathLike]] = None, + namefile2: Optional[Union[str, os.PathLike]] = None, precision="auto", max_cumpd=0.01, max_incpd=0.01, diff --git a/flopy/utils/gridintersect.py b/flopy/utils/gridintersect.py index 58b614f28..812a8ce16 100644 --- a/flopy/utils/gridintersect.py +++ b/flopy/utils/gridintersect.py @@ -459,9 +459,10 @@ def _intersect_point_shapely( for ishp, cid in zip(ixresult, qcellids): points = [] for pnt in shapely.get_parts(ishp): - if tuple(pnt.coords)[0] not in parsed: + next_pnt = next(iter(pnt.coords)) + if next_pnt not in parsed: points.append(pnt) - parsed.append(tuple(pnt.coords)[0]) + parsed.append(next_pnt) if len(points) > 1: keep_pts.append(shapely.MultiPoint(points)) diff --git a/flopy/utils/mfreadnam.py b/flopy/utils/mfreadnam.py index 0ffc16e8c..0e3f96cdb 100644 --- a/flopy/utils/mfreadnam.py +++ b/flopy/utils/mfreadnam.py @@ -11,7 +11,7 @@ import os from os import PathLike from pathlib import Path, PurePosixPath, PureWindowsPath -from typing import Union +from typing import Optional, Union class NamData: @@ -295,9 +295,9 @@ def attribs_from_namfile_header(namefile): def get_entries_from_namefile( path: Union[str, PathLike], - ftype: str = None, - unit: int = None, - extension: str = None, + ftype: Optional[str] = None, + unit: Optional[int] = None, + extension: Optional[str] = None, ) -> list[tuple]: """Get entries from an MF6 namefile. Can select using FTYPE, UNIT, or file extension. @@ -481,7 +481,7 @@ def get_mf6_nper(tdisfile): """ with open(tdisfile) as f: lines = f.readlines() - line = [line for line in lines if "NPER" in line.upper()][0] + line = next(line for line in lines if "NPER" in line.upper()) nper = line.strip().split()[1] return nper diff --git a/flopy/utils/utils_def.py b/flopy/utils/utils_def.py index 38ef48d94..31798a0a3 100644 --- a/flopy/utils/utils_def.py +++ b/flopy/utils/utils_def.py @@ -123,10 +123,10 @@ def get_pak_vals_shape(model, vals): if nrow is None: # unstructured if isinstance(vals, dict): try: # check for iterable - _ = (v for v in list(vals.values())[0]) + _ = (v for v in next(iter(vals.values()))) except: return (1, ncol[0]) # default to layer 1 node count - return np.array(list(vals.values())[0], ndmin=2).shape + return np.array(next(iter(vals.values())), ndmin=2).shape else: # check for single iterable try: diff --git a/pyproject.toml b/pyproject.toml index 7d9cce9e9..884d2dca4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,6 +139,7 @@ select = [ "F", # Pyflakes "I001", # isort - unsorted-imports # "ISC001", # implicitly concatenated string literals + "RUF", # Ruff-specific rules ] ignore = [ "E402", # module level import not at top of file @@ -151,11 +152,14 @@ ignore = [ "F524", # `.format` missing argument(s) for placeholder(s) "F811", # Redefinition of unused variable "F841", # local variable assigned but never used + "RUF005", # collection literal concatenation + "RUF012", # mutable class default + "RUF017", # quadratic-list-summation ] [tool.ruff.lint.per-file-ignores] ".docs/**/*.py" = ["E501"] -"flopy/mf6/**/*.py" = ["C4", "E501", "F821", "ISC001"] +"flopy/mf6/**/*.py" = ["C4", "E", "F", "ISC", "RUF"] [tool.codespell] skip = "cliff.toml,./examples/data/*"