Skip to content

Commit

Permalink
misc cleanup and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
wpbonelli committed Jul 26, 2024
1 parent c062953 commit 81a258c
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 50 deletions.
2 changes: 1 addition & 1 deletion flopy4/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ def how(self):

return self._how

def write(self, f):
def write(self, f, **kwargs):
# todo
pass

Expand Down
15 changes: 5 additions & 10 deletions flopy4/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@
from flopy4.utils import find_upper, strip


def single_keystring(members):
params = list(members.values())
return len(params) == 1 and isinstance(params[0], MFKeystring)


def get_param(members, name, block):
ks = [m for m in members.values() if isinstance(m, MFKeystring)]
if len(ks) == 1:
Expand Down Expand Up @@ -84,19 +79,19 @@ def __init__(self, name=None, index=None, params=None):
super().__init__(params)

def __getattribute__(self, name: str) -> Any:
if name in ["data", "params"]:
if name == "data":
return super().__getattribute__(name)

if name == "params":
return MFParams({k: v.value for k, v in self.data.items()})

param = self.data.get(name)
return (
param.value
if param is not None
else super().__getattribute__(name)
)

def __repr__(self):
return pformat({k: v for k, v in self.data.items()})

def __str__(self):
buffer = StringIO()
self.write(buffer)
Expand Down Expand Up @@ -166,7 +161,7 @@ def __init__(self, blocks=None):
setattr(self, key, block)

def __repr__(self):
return pformat({k: repr(v) for k, v in self.data.items()})
return pformat(self.data)

def write(self, f):
"""Write the blocks to file."""
Expand Down
15 changes: 6 additions & 9 deletions flopy4/compound.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from collections.abc import Mapping
from dataclasses import asdict
from io import StringIO
from pprint import pformat
from typing import Any, Dict

from flopy4.param import MFParam, MFParams, MFReader
Expand Down Expand Up @@ -58,13 +57,10 @@ def __init__(
def __get__(self, obj, type=None):
return self

def __repr__(self):
return pformat(self.data)

@property
def params(self) -> MFParams:
"""Component parameters."""
return self.data
return MFParams(self.data)

@property
def value(self) -> Mapping[str, Any]:
Expand Down Expand Up @@ -150,11 +146,12 @@ def parse(line, params, **kwargs) -> Dict[str, MFScalar]:

return loaded

def write(self, f):
def write(self, f, **kwargs):
"""Write the record to file."""
f.write(f"{PAD}{self.name.upper()}")
last = len(self) - 1
for i, param in enumerate(self.data.values()):
param.write(f, newline=i == last)
param.write(f, newline=i == last, **kwargs)


class MFKeystring(MFCompound):
Expand Down Expand Up @@ -225,6 +222,6 @@ def load(cls, f, params, **kwargs) -> "MFKeystring":

return cls(loaded, **kwargs)

def write(self, f):
def write(self, f, **kwargs):
"""Write the keystring to file."""
super().write(f)
super().write(f, **kwargs)
34 changes: 23 additions & 11 deletions flopy4/package.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from abc import ABCMeta
from io import StringIO
from itertools import groupby
from pprint import pformat
from typing import Any

from flopy4.block import MFBlock, MFBlockMeta, MFBlocks
Expand All @@ -10,9 +9,12 @@


def get_block(pkg_name, block_name, params):
return MFBlockMeta(
f"{pkg_name.title()}{block_name.title()}Block", (MFBlock,), params
)(params=params, name=block_name)
cls = MFBlockMeta(
f"{pkg_name.title()}{block_name.title()}Block",
(MFBlock,),
params.copy(),
)
return cls(params=params, name=block_name)


class MFPackageMeta(type):
Expand Down Expand Up @@ -70,21 +72,31 @@ class MFPackage(MFBlocks, metaclass=MFPackageMappingMeta):
def __init__(self, blocks=None):
super().__init__(blocks)

def __repr__(self):
return pformat(self.data)

def __str__(self):
buffer = StringIO()
self.write(buffer)
return buffer.getvalue()

def __getattribute__(self, name: str) -> Any:
if name in ["data", "params", "blocks"]:
if name == "data":
return super().__getattribute__(name)

block = self.data.get(name)
if block is not None:
return block
if name == "blocks":
return MFBlocks(self.data)

if name == "params":
# todo cache this
return MFParams(
{
param_name: param
for block in self.values()
for param_name, param in block.items()
}
)

# shortcut to block value
if name in self:
return self[name]

# shortcut to parameter value for instance attribute.
# the class attribute is the parameter specification.
Expand Down
27 changes: 14 additions & 13 deletions flopy4/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,20 @@ def with_block(self, block) -> "MFParamSpec":
class MFParam(MFParamSpec):
"""
MODFLOW 6 input parameter. Can be a scalar or compound of
scalars, an array, or a list (i.e. a table). `MFParameter`
classes play a dual role: first, to define the blocks that
scalars, an array, or a list (i.e. a table).
Notes
-----
This class plays a dual role: first, to define blocks that
specify the input required for MF6 components; and second,
as a data access layer by which higher components (blocks,
packages, etc) can read/write parameters. The former is a
developer task (though it may be automated as classes are
generated from DFNs) while the latter happens at runtime,
but both APIs are user-facing; the user can first inspect
a package's specification via class attributes, then load
an input file and inspect the package data.
an input file and inspect package data via instance attrs.
Notes
-----
Specification attributes are set at import time. A parent
block or package defines parameters as class attributes,
including a description, whether the parameter is optional,
Expand Down Expand Up @@ -149,11 +150,6 @@ def __init__(
default_value=default_value,
)

def __repr__(self):
return (
super().__repr__() if self.value is None else pformat(self.value)
)

def __str__(self):
buffer = StringIO()
self.write(buffer)
Expand All @@ -165,6 +161,11 @@ def value(self) -> Optional[Any]:
"""Get the parameter's value, if loaded."""
pass

@abstractmethod
def write(self, f, **kwargs):
"""Write the parameter to file."""
pass


class MFParams(UserDict):
"""
Expand All @@ -178,9 +179,9 @@ def __init__(self, params=None):
setattr(self, key, param)

def __repr__(self):
return pformat({k: repr(v) for k, v in self.data.items()})
return pformat(self.data)

def write(self, f):
def write(self, f, **kwargs):
"""Write the parameters to file."""
for param in self.data.values():
param.write(f)
param.write(f, **kwargs)
15 changes: 10 additions & 5 deletions flopy4/scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def load(cls, f, **kwargs) -> "MFKeyword":
kwargs["name"] = line
return cls(value=True, **kwargs)

def write(self, f, newline=True):
def write(self, f, **kwargs):
newline = kwargs.pop("newline", True)
if self.value:
f.write(
f"{PAD}" f"{self.name.upper()}" + ("\n" if newline else "")
Expand Down Expand Up @@ -180,7 +181,8 @@ def load(cls, f, **kwargs) -> "MFInteger":
kwargs["name"] = words[0]
return cls(value=int(words[1]), **kwargs)

def write(self, f, newline=True):
def write(self, f, **kwargs):
newline = kwargs.pop("newline", True)
f.write(
f"{PAD}"
f"{self.name.upper()} "
Expand Down Expand Up @@ -243,7 +245,8 @@ def load(cls, f, **kwargs) -> "MFDouble":
kwargs["name"] = words[0]
return cls(value=float(words[1]), **kwargs)

def write(self, f, newline=True):
def write(self, f, **kwargs):
newline = kwargs.pop("newline", True)
f.write(
f"{PAD}"
f"{self.name.upper()} "
Expand Down Expand Up @@ -306,7 +309,8 @@ def load(cls, f, **kwargs) -> "MFString":
kwargs["name"] = words[0]
return cls(value=words[1], **kwargs)

def write(self, f, newline=True):
def write(self, f, **kwargs):
newline = kwargs.pop("newline", True)
f.write(
f"{PAD}"
f"{self.name.upper()} "
Expand Down Expand Up @@ -381,7 +385,8 @@ def load(cls, f, **kwargs) -> "MFFilename":
**kwargs,
)

def write(self, f, newline=True):
def write(self, f, **kwargs):
newline = kwargs.pop("newline", True)
f.write(
f"{PAD}{self.name.upper()} "
f"{self.inout.value.upper()} "
Expand Down
5 changes: 4 additions & 1 deletion test/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,11 @@ def test_load_gwfic(tmp_path):
with open(fpth, "r") as f:
gwfic = TestGwfIc.load(f)

assert len(TestGwfIc.blocks) == 2
assert len(TestGwfIc.params) == 3

assert len(gwfic.blocks) == 2
assert len(gwfic.params) == 3
assert len(gwfic.params) == 2 # only two params loaded

# instance attributes: shortcut access to param values
assert isinstance(gwfic.export_array_ascii, bool)
Expand Down

0 comments on commit 81a258c

Please sign in to comment.