From d1ae78ec2f62e61e94821a2302653b0ec0704fbe Mon Sep 17 00:00:00 2001 From: mjreno Date: Wed, 31 Jul 2024 08:25:40 -0400 Subject: [PATCH 1/6] implement array write --- flopy4/array.py | 79 ++++++++++++++++++++++++++++++++++++++++---- spec/make_ipkg.py | 2 +- spec/make_toml.py | 1 - test/test_array.py | 31 ++++++++++++++--- test/test_block.py | 4 ++- test/test_package.py | 4 ++- 6 files changed, 106 insertions(+), 15 deletions(-) diff --git a/flopy4/array.py b/flopy4/array.py index 173756f..369a41f 100644 --- a/flopy4/array.py +++ b/flopy4/array.py @@ -5,8 +5,8 @@ from typing import Optional import numpy as np -from flopy.utils.flopy_io import line_strip, multi_line_strip +from flopy.utils.flopy_io import line_strip, multi_line_strip from flopy4.constants import CommonNames from flopy4.param import MFParam, MFReader @@ -203,6 +203,7 @@ def __init__( tagged=False, reader=MFReader.urword, default_value=None, + extpath=None, ): MFParam.__init__( self, @@ -227,6 +228,7 @@ def __init__( self._shape = shape self._how = how self._factor = factor + self._extpath = extpath def __getitem__(self, item): return self.raw[item] @@ -343,8 +345,65 @@ def how(self): return self._how def write(self, f, **kwargs): - # todo - pass + PAD = " " + if self.layered: + f.write(f"{PAD}" + f"{self.name.upper()} LAYERED\n") + for mfa in self._value: + values = mfa.raw + if mfa._how == MFArrayType.internal: + if isinstance(self._shape, int) or len(self._shape) == 1: + v = ( + f"{PAD*3}" + + f"{' '.join([str(x) for x in values])}\n" + ) + else: + v = f"\n{PAD*3}" + v = f"{v.join(' '.join(str(x) for x in y) \ + for y in values)}" + lines = f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)}" + if mfa._factor: + lines += f" FACTOR {mfa._factor}" + lines += f"\n{PAD*3}" + f"{v}\n" + elif mfa._how == MFArrayType.external: + lines = ( + f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)} " + f"{mfa._extpath}\n" + ) + elif mfa._how == MFArrayType.constant: + lines = ( + f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)} " + f"{str(mfa._value)}\n" + ) + f.write(lines) + else: + values = self.raw.ravel() + if self._how == MFArrayType.internal: + if isinstance(self._shape, int) or len(self._shape) == 1: + v = f"{PAD*3}" + f"{' '.join([str(x) for x in values])}" + else: + v = f"\n{PAD*3}" + v = f"{v.join(' '.join(str(x) for x in y) \ + for y in values)}" + lines = ( + f"{PAD}" + f"{self.name.upper()}\n" + f"{PAD*2}" + f"{MFArrayType.to_string(self._how)}" + ) + if self._factor: + lines += f" FACTOR {self._factor}" + lines += f"\n{v}\n" + elif self._how == MFArrayType.external: + lines = ( + f"{PAD}" + f"{self.name.upper()}\n" + f"{PAD*2}" + + f"{MFArrayType.to_string(self._how)} {self._extpath}\n" + ) + elif self._how == MFArrayType.constant: + lines = ( + f"{PAD}" + f"{self.name.upper()}\n" + f"{PAD*2}" + f"{MFArrayType.to_string(self._how)} " + f"{str(self._value)}\n" + ) + f.write(lines) @classmethod def load(cls, f, cwd, shape, header=True, **kwargs): @@ -391,6 +450,7 @@ def _load(cls, f, cwd, shape, layered=False, **kwargs): control_line.pop(idx) how = MFArrayType.from_string(control_line[0]) + extpath = None clpos = 1 if how == MFArrayType.internal: @@ -401,8 +461,8 @@ def _load(cls, f, cwd, shape, layered=False, **kwargs): clpos += 1 elif how == MFArrayType.external: - ext_path = Path(control_line[clpos]) - fpath = cwd / ext_path + extpath = Path(control_line[clpos]) + fpath = cwd / extpath with open(fpath) as foo: array = cls.read_array(foo) clpos += 1 @@ -414,7 +474,14 @@ def _load(cls, f, cwd, shape, layered=False, **kwargs): if len(control_line) > 2: factor = float(control_line[clpos + 1]) - return cls(shape, array=array, how=how, factor=factor, **kwargs) + return cls( + shape, + array=array, + how=how, + factor=factor, + extpath=extpath, + **kwargs, + ) @staticmethod def read_array(f): diff --git a/spec/make_ipkg.py b/spec/make_ipkg.py index 896b1cd..048c737 100644 --- a/spec/make_ipkg.py +++ b/spec/make_ipkg.py @@ -35,7 +35,7 @@ def __init__( with open(f"{PROJ_ROOT}/spec/mf6pkg.template") as f: tout = Template(f.read()) - if not (tin or tout): + if not (tin and tout): raise ValueError("FileSystem NO-OPT") jinja_blocks = [] diff --git a/spec/make_toml.py b/spec/make_toml.py index ffafe31..14f369c 100644 --- a/spec/make_toml.py +++ b/spec/make_toml.py @@ -176,7 +176,6 @@ def _set_var_d(self): self._var_d = vardict def _substitute(self, blockname, component, subcomponent): - block_d = dict() block_d = {} for k in self._var_d: varname, block = k diff --git a/test/test_array.py b/test/test_array.py index 61d4220..cb4ac14 100644 --- a/test/test_array.py +++ b/test/test_array.py @@ -9,9 +9,9 @@ def test_array_load_1d(tmp_path): how = "INTERNAL" v = [1.0, 2.0, 3.0] value = " ".join(str(x) for x in v) + with open(fpth, "w") as f: f.write(f"{name.upper()}\n{how}\n{value}\n") - with open(fpth, "r") as f: array = MFArray.load(f, cwd=tmp_path, shape=(3)) assert array.name == name @@ -28,9 +28,31 @@ def test_array_load_3d(tmp_path): for b in a: value += " ".join(str(x) for x in b) value += " " + with open(fpth, "w") as f: f.write(f"{name.upper()}\n{how}\n{value}\n") + with open(fpth, "r") as f: + array = MFArray.load(f, cwd=tmp_path, shape=(3, 1, 3)) + assert array.name == name + assert np.allclose(array.value, np.array(v)) + +def test_array_load_3d_external(tmp_path): + name = "array" + fpth = tmp_path / f"{name}.txt" + extfpth = f"external_{name}.txt" + how = "OPEN/CLOSE" + v = [[[1.0, 2.0, 3.0]], [[4.0, 5.0, 6.0]], [[7.0, 8.0, 9.0]]] + value = "" + for a in v: + for b in a: + value += " ".join(str(x) for x in b) + value += " " + + with open(tmp_path / extfpth, "w") as f: + f.write(f"{value}") + with open(fpth, "w") as f: + f.write(f"{name.upper()}\n{how} {extfpth}\n") with open(fpth, "r") as f: array = MFArray.load(f, cwd=tmp_path, shape=(3, 1, 3)) assert array.name == name @@ -45,13 +67,12 @@ def test_array_load_layered(tmp_path): value = "" for a in v: for b in a: - # TODO: MFArray expects this on separate line - value += f"{how}\n" - value += " ".join(str(x) for x in b) + value += " " + f"{how}\n" + value += " " + " ".join(str(x) for x in b) value += "\n" + with open(fpth, "w") as f: f.write(f"{name.upper()} LAYERED\n{value}") - with open(fpth, "r") as f: array = MFArray.load(f, cwd=tmp_path, shape=(3, 1, 3)) assert array.name == name diff --git a/test/test_block.py b/test/test_block.py index 680c7d1..a8d0f4b 100644 --- a/test/test_block.py +++ b/test/test_block.py @@ -122,7 +122,9 @@ def test_load_write(tmp_path): assert " D 1.0\n" in lines assert " S value\n" in lines assert f" F FILEIN {fpth}\n" in lines - # assert " A\n INTERNAL\n 1.0 2.0 3.0\n" in lines + assert " A\n" in lines + assert " INTERNAL\n" in lines + assert " 1.0 2.0 3.0\n" in lines assert " R RK RI 2 RD 2.0\n" in lines assert "END OPTIONS\n" in lines diff --git a/test/test_package.py b/test/test_package.py index cf87ec2..3832a0a 100644 --- a/test/test_package.py +++ b/test/test_package.py @@ -277,7 +277,9 @@ def test_load_write(tmp_path): assert " D 1.0\n" in lines assert " S value\n" in lines assert f" F FILEIN {fpth}\n" in lines - # todo check array + assert " A\n" in lines + assert " INTERNAL\n" in lines + assert f" {array}\n" in lines assert "END OPTIONS\n" in lines From b37b6626f6a8a6f19d6cef7c52061fc4bde9eca1 Mon Sep 17 00:00:00 2001 From: mjreno Date: Wed, 31 Jul 2024 08:29:37 -0400 Subject: [PATCH 2/6] is ruff out there --- flopy4/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flopy4/array.py b/flopy4/array.py index 369a41f..a91e38a 100644 --- a/flopy4/array.py +++ b/flopy4/array.py @@ -5,8 +5,8 @@ from typing import Optional import numpy as np - from flopy.utils.flopy_io import line_strip, multi_line_strip + from flopy4.constants import CommonNames from flopy4.param import MFParam, MFReader From 58f5cc8bba18d5e3e23acf525e33b20805fd5eeb Mon Sep 17 00:00:00 2001 From: mjreno Date: Wed, 31 Jul 2024 09:11:36 -0400 Subject: [PATCH 3/6] fix formatted string --- flopy4/array.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/flopy4/array.py b/flopy4/array.py index a91e38a..9aa88e6 100644 --- a/flopy4/array.py +++ b/flopy4/array.py @@ -358,8 +358,8 @@ def write(self, f, **kwargs): ) else: v = f"\n{PAD*3}" - v = f"{v.join(' '.join(str(x) for x in y) \ - for y in values)}" + v = v.join(" ".join(str(x) for x in y) for y in values) + lines = f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)}" if mfa._factor: lines += f" FACTOR {mfa._factor}" @@ -382,8 +382,7 @@ def write(self, f, **kwargs): v = f"{PAD*3}" + f"{' '.join([str(x) for x in values])}" else: v = f"\n{PAD*3}" - v = f"{v.join(' '.join(str(x) for x in y) \ - for y in values)}" + v = v.join(" ".join(str(x) for x in y) for y in values) lines = ( f"{PAD}" + f"{self.name.upper()}\n" f"{PAD*2}" + f"{MFArrayType.to_string(self._how)}" From 953e43dce54bc6a96d946b1b1388196c73770884 Mon Sep 17 00:00:00 2001 From: mjreno Date: Wed, 31 Jul 2024 09:39:01 -0400 Subject: [PATCH 4/6] cleanup --- flopy4/array.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/flopy4/array.py b/flopy4/array.py index 9aa88e6..68dfc8f 100644 --- a/flopy4/array.py +++ b/flopy4/array.py @@ -352,14 +352,12 @@ def write(self, f, **kwargs): values = mfa.raw if mfa._how == MFArrayType.internal: if isinstance(self._shape, int) or len(self._shape) == 1: - v = ( - f"{PAD*3}" - + f"{' '.join([str(x) for x in values])}\n" - ) + v = f"{PAD*3}" + v += " ".join([str(x) for x in values]) + v += "\n" else: v = f"\n{PAD*3}" v = v.join(" ".join(str(x) for x in y) for y in values) - lines = f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)}" if mfa._factor: lines += f" FACTOR {mfa._factor}" @@ -376,10 +374,11 @@ def write(self, f, **kwargs): ) f.write(lines) else: - values = self.raw.ravel() + values = self.raw if self._how == MFArrayType.internal: if isinstance(self._shape, int) or len(self._shape) == 1: - v = f"{PAD*3}" + f"{' '.join([str(x) for x in values])}" + v = f"{PAD*3}" + v += " ".join([str(x) for x in values]) else: v = f"\n{PAD*3}" v = v.join(" ".join(str(x) for x in y) for y in values) From 25bdb72c257884cc91deae1d385d55b2e3d5bfff Mon Sep 17 00:00:00 2001 From: mjreno Date: Wed, 31 Jul 2024 13:18:20 -0400 Subject: [PATCH 5/6] adjustments to internal write --- flopy4/array.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/flopy4/array.py b/flopy4/array.py index 68dfc8f..71caee4 100644 --- a/flopy4/array.py +++ b/flopy4/array.py @@ -351,13 +351,20 @@ def write(self, f, **kwargs): for mfa in self._value: values = mfa.raw if mfa._how == MFArrayType.internal: - if isinstance(self._shape, int) or len(self._shape) == 1: + if len(values.shape) == 1: v = f"{PAD*3}" v += " ".join([str(x) for x in values]) v += "\n" - else: + elif len(values.shape) == 2: v = f"\n{PAD*3}" v = v.join(" ".join(str(x) for x in y) for y in values) + elif len(values.shape) == 3: + v = f"{PAD*3}" + for i in range(len(values)): + v += v.join( + " ".join(str(x) for x in y) for y in values[i] + ) + v += f"\n{PAD*3}" lines = f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)}" if mfa._factor: lines += f" FACTOR {mfa._factor}" @@ -376,12 +383,19 @@ def write(self, f, **kwargs): else: values = self.raw if self._how == MFArrayType.internal: - if isinstance(self._shape, int) or len(self._shape) == 1: + if len(values.shape) == 1: v = f"{PAD*3}" v += " ".join([str(x) for x in values]) - else: + elif len(values.shape) == 2: v = f"\n{PAD*3}" v = v.join(" ".join(str(x) for x in y) for y in values) + elif len(values.shape) == 3: + v = f"{PAD*3}" + for i in range(len(values)): + v += v.join( + " ".join(str(x) for x in y) for y in values[i] + ) + v += f"\n{PAD*3}" lines = ( f"{PAD}" + f"{self.name.upper()}\n" f"{PAD*2}" + f"{MFArrayType.to_string(self._how)}" From 8136ad1ef92280e4543c7757f60c65c8c0dc73fc Mon Sep 17 00:00:00 2001 From: mjreno Date: Wed, 31 Jul 2024 14:24:52 -0400 Subject: [PATCH 6/6] review updates --- flopy4/array.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/flopy4/array.py b/flopy4/array.py index 71caee4..bf70ac5 100644 --- a/flopy4/array.py +++ b/flopy4/array.py @@ -203,7 +203,7 @@ def __init__( tagged=False, reader=MFReader.urword, default_value=None, - extpath=None, + path: Optional[Path] = None, ): MFParam.__init__( self, @@ -228,7 +228,7 @@ def __init__( self._shape = shape self._how = how self._factor = factor - self._extpath = extpath + self._path = path def __getitem__(self, item): return self.raw[item] @@ -361,10 +361,10 @@ def write(self, f, **kwargs): elif len(values.shape) == 3: v = f"{PAD*3}" for i in range(len(values)): - v += v.join( - " ".join(str(x) for x in y) for y in values[i] + v += " ".join( + f"\n{PAD*3}" + " ".join(str(x) for x in y) + for y in values[i] ) - v += f"\n{PAD*3}" lines = f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)}" if mfa._factor: lines += f" FACTOR {mfa._factor}" @@ -372,7 +372,7 @@ def write(self, f, **kwargs): elif mfa._how == MFArrayType.external: lines = ( f"{PAD*2}" + f"{MFArrayType.to_string(mfa._how)} " - f"{mfa._extpath}\n" + f"{mfa._path}\n" ) elif mfa._how == MFArrayType.constant: lines = ( @@ -384,7 +384,7 @@ def write(self, f, **kwargs): values = self.raw if self._how == MFArrayType.internal: if len(values.shape) == 1: - v = f"{PAD*3}" + v = f"\n{PAD*3}" v += " ".join([str(x) for x in values]) elif len(values.shape) == 2: v = f"\n{PAD*3}" @@ -392,22 +392,22 @@ def write(self, f, **kwargs): elif len(values.shape) == 3: v = f"{PAD*3}" for i in range(len(values)): - v += v.join( - " ".join(str(x) for x in y) for y in values[i] + v += " ".join( + f"\n{PAD*3}" + " ".join(str(x) for x in y) + for y in values[i] ) - v += f"\n{PAD*3}" lines = ( f"{PAD}" + f"{self.name.upper()}\n" f"{PAD*2}" + f"{MFArrayType.to_string(self._how)}" ) if self._factor: lines += f" FACTOR {self._factor}" - lines += f"\n{v}\n" + lines += f"{v}\n" elif self._how == MFArrayType.external: lines = ( f"{PAD}" + f"{self.name.upper()}\n" f"{PAD*2}" - + f"{MFArrayType.to_string(self._how)} {self._extpath}\n" + + f"{MFArrayType.to_string(self._how)} {self._path}\n" ) elif self._how == MFArrayType.constant: lines = ( @@ -491,7 +491,7 @@ def _load(cls, f, cwd, shape, layered=False, **kwargs): array=array, how=how, factor=factor, - extpath=extpath, + path=extpath, **kwargs, )