Skip to content

Commit 44a5df1

Browse files
committed
wip
1 parent d493924 commit 44a5df1

File tree

10 files changed

+66
-176
lines changed

10 files changed

+66
-176
lines changed

docs/examples/quickstart.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,22 @@
2626
parent=gwf,
2727
budget_file=f"{gwf.name}.bud",
2828
head_file=f"{gwf.name}.hds",
29-
save_head={"*": "all"},
30-
save_budget={"*": "all"},
29+
saverecord={"*": {"head": "all", "budget": "all"}},
3130
)
3231

3332
# sim.write()
3433
sim.run(verbose=True)
3534

3635
# check CHD
37-
assert chd.data["head"][0, 0] == 1.0
38-
assert chd.data.head.sel(per=0)[99] == 0.0
39-
assert np.allclose(chd.data.head[:, 1:99], np.full(98, 1e30))
36+
assert chd.data.perioddata[0, 0].head == 1.0
37+
assert chd.data.perioddata.sel(per=0, node=99).head == 0.0
38+
assert chd.data.perioddata.sel(per=0, node=98).head == 1e30
4039

4140
# check DIS
4241
assert gwf.dis.data.botm.sel(lay=0, col=0, row=0) == 0.0
4342

4443
# check OC
45-
assert oc.data["save_head"][0] == "all"
44+
assert oc.data["saverecord"][0] == "all"
4645
assert oc.data.save_head.sel(per=0) == "all"
4746

4847
# get head and budget results

flopy4/mf6/context.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
@xattree
1111
class Context(Component, ABC):
12+
"""Base class for MF6 components associated with a workspace directory on disk."""
13+
1214
workspace: Path = field(default=None)
1315

1416
def __attrs_post_init__(self):

flopy4/mf6/exchange.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010
@xattree
1111
class Exchange(Package, ABC):
12-
# mypy doesn't understand that kw_only=True on the
12+
"""Base class for MF6 exchange packages."""
13+
14+
# mypy doesn't understand that kw_only=True on base
1315
# Component means we can have required fields here
1416
exgtype: type = field() # type: ignore
1517
exgfile: Path = field() # type: ignore

flopy4/mf6/gwf/chd.py

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from xattree import xattree
88

99
from flopy4.mf6.codec import structure_array
10-
from flopy4.mf6.constants import FILL_DNODATA
1110
from flopy4.mf6.package import Package
1211
from flopy4.mf6.spec import array, field
1312

@@ -17,12 +16,10 @@ class Chd(Package):
1716
multi_package: ClassVar[bool] = True
1817

1918
@define(slots=False)
20-
class Steps:
21-
all: bool = field()
22-
first: bool = field()
23-
last: bool = field()
24-
steps: list[int] = field()
25-
frequency: int = field()
19+
class PeriodData:
20+
head: float = field()
21+
aux: Optional[tuple[np.floating]] = field(default=None)
22+
boundname: Optional[str] = field(default=None)
2623

2724
auxiliary: Optional[list[str]] = array(block="options", default=None)
2825
auxmultname: Optional[str] = field(block="options", default=None)
@@ -34,7 +31,8 @@ class Steps:
3431
obs_filerecord: Optional[Path] = field(block="options", default=None)
3532
dev_no_newton: bool = field(default=False, metadata={"block": "options"})
3633
maxbound: Optional[int] = field(block="dimensions", default=None)
37-
head: Optional[NDArray[np.floating]] = array(
34+
perioddata: Optional[NDArray[np.object_]] = array(
35+
PeriodData,
3836
block="period",
3937
dims=(
4038
"nper",
@@ -44,43 +42,11 @@ class Steps:
4442
converter=Converter(structure_array, takes_self=True, takes_field=True),
4543
reader="urword",
4644
)
47-
aux: Optional[NDArray[np.floating]] = array(
48-
block="period",
49-
dims=(
50-
"nper",
51-
"nnodes",
52-
),
53-
default=None,
54-
converter=Converter(structure_array, takes_self=True, takes_field=True),
55-
reader="urword",
56-
)
57-
boundname: Optional[NDArray[np.str_]] = array(
58-
block="period",
59-
dims=(
60-
"nper",
61-
"nnodes",
62-
),
63-
default=None,
64-
converter=Converter(structure_array, takes_self=True, takes_field=True),
65-
reader="urword",
66-
)
67-
steps: Optional[NDArray[np.object_]] = array(
68-
Steps,
69-
block="period",
70-
dims=("nper", "nnodes"),
71-
default=None,
72-
converter=Converter(structure_array, takes_self=True, takes_field=True),
73-
reader="urword",
74-
)
7545

7646
def __attrs_post_init__(self):
7747
# TODO set up on_setattr hooks for period block
7848
# arrays to update maxbound? for now do it here
7949
# in post init. but this only works when values
8050
# are set in the initializer, not when they are
8151
# set later.
82-
maxhead = len(np.where(self.head != FILL_DNODATA)) if self.head is not None else 0
83-
maxaux = len(np.where(self.aux != FILL_DNODATA)) if self.aux is not None else 0
84-
maxboundname = len(np.where(self.boundname != "")) if self.boundname is not None else 0
85-
# maxsteps = len(np.where(self.steps != None)) if self.steps is not None else 0
86-
self.maxbound = max(maxhead, maxaux, maxboundname)
52+
self.maxbound = len(np.where(self.perioddata != None)) if self.perioddata is not None else 0 # noqa: E711

flopy4/mf6/gwf/oc.py

Lines changed: 25 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import numpy as np
55
from attrs import Converter, define
6-
from modflow_devtools.dfn import Field
76
from numpy.typing import NDArray
87
from xattree import xattree
98

@@ -12,86 +11,37 @@
1211
from flopy4.mf6.spec import array, field
1312
from flopy4.utils import to_path
1413

15-
_OCSETTING = Field(
16-
name="ocsetting",
17-
type="keystring",
18-
reader="urword",
19-
children={
20-
"all": Field(
21-
name="all",
22-
type="keyword",
23-
reader="urword",
24-
),
25-
"first": Field(
26-
name="first",
27-
type="keyword",
28-
reader="urword",
29-
),
30-
"last": Field(
31-
name="last",
32-
type="keyword",
33-
reader="urword",
34-
),
35-
"steps": Field(
36-
name="steps",
37-
type="integer",
38-
reader="urword",
39-
),
40-
"frequency": Field(
41-
name="frequency",
42-
type="integer",
43-
reader="urword",
44-
),
45-
},
46-
)
47-
48-
_RTYPE = Field(
49-
name="rtype",
50-
type="string",
51-
reader="urword",
52-
)
53-
54-
55-
def _oc_action_field(action: str) -> Field:
56-
return Field(
57-
name=f"{action}record",
58-
type="recarray",
59-
dims=("nper",),
60-
block="perioddata",
61-
reader="urword",
62-
children={
63-
action: Field(
64-
name=action,
65-
type="keyword",
66-
reader="urword",
67-
),
68-
"rtype": _RTYPE,
69-
"ocsetting": _OCSETTING,
70-
},
71-
)
72-
7314

7415
@xattree
7516
class Oc(Package):
7617
@define(slots=False)
77-
class Format:
18+
class FormatRecord:
7819
columns: int = field(default=10)
7920
width: int = field(default=11)
8021
digits: int = field(default=4)
8122
format: Literal["exponential", "fixed", "general", "scientific"] = field(default="general")
8223

8324
@define(slots=False)
84-
class Steps:
85-
all: bool = field()
86-
first: bool = field()
87-
last: bool = field()
88-
steps: list[int] = field()
89-
frequency: int = field()
25+
class OCSettingOption:
26+
first: bool = field(default=True)
27+
last: bool = field(default=False)
28+
all: bool = field(default=False)
29+
steps: Optional[list[int]] = field(default=None)
30+
frequency: Optional[int] = field(default=None)
9031

9132
@define(slots=False)
92-
class Period:
33+
class OCSetting:
34+
ocsetting: Optional[list["Oc.OCSettingOption"]] = field(default=None)
35+
36+
@define(slots=False)
37+
class SaveRecord:
38+
rtype: str = field()
39+
ocsetting: "Oc.OCSetting" = field()
40+
41+
@define(slots=False)
42+
class PrintRecord:
9343
rtype: str = field()
94-
steps: "Oc.Steps" = field()
44+
ocsetting: "Oc.OCSetting" = field()
9545

9646
budget_file: Optional[Path] = field(
9747
block="options",
@@ -108,47 +58,20 @@ class Period:
10858
converter=to_path,
10959
default=None,
11060
)
111-
format: Optional[Format] = field(block="options", default=None, init=False)
112-
save_head: Optional[NDArray[np.object_]] = array(
113-
Steps,
61+
headprintrecord: Optional[FormatRecord] = field(block="options", default=None, init=False)
62+
saverecord: Optional[NDArray[np.object_]] = array(
63+
SaveRecord,
11464
block="period",
115-
default="all",
116-
dims=("nper",),
117-
converter=Converter(structure_array, takes_self=True, takes_field=True),
118-
reader="urword",
119-
)
120-
save_budget: Optional[NDArray[np.object_]] = array(
121-
Steps,
122-
block="period",
123-
default="all",
124-
dims=("nper",),
125-
converter=Converter(structure_array, takes_self=True, takes_field=True),
126-
reader="urword",
127-
)
128-
print_head: Optional[NDArray[np.object_]] = array(
129-
Steps,
130-
block="period",
131-
default="all",
65+
default=None,
13266
dims=("nper",),
13367
converter=Converter(structure_array, takes_self=True, takes_field=True),
13468
reader="urword",
13569
)
136-
print_budget: Optional[NDArray[np.object_]] = array(
137-
Steps,
70+
printrecord: Optional[NDArray[np.object_]] = array(
71+
PrintRecord,
13872
block="period",
139-
default="all",
73+
default=None,
14074
dims=("nper",),
14175
converter=Converter(structure_array, takes_self=True, takes_field=True),
14276
reader="urword",
14377
)
144-
145-
# original DFN
146-
# @classmethod
147-
# def get_dfn(cls) -> Dfn:
148-
# """Generate the component's MODFLOW 6 definition."""
149-
# dfn = super().get_dfn()
150-
# for field_name in list(dfn["perioddata"].keys()):
151-
# dfn["perioddata"].pop(field_name)
152-
# dfn["perioddata"]["saverecord"] = _oc_action_field("save")
153-
# dfn["perioddata"]["printrecord"] = _oc_action_field("print")
154-
# return dfn

flopy4/mf6/model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@
77

88
@xattree
99
class Model(Component, ABC):
10+
"""Base class for MF6 models."""
11+
1012
def default_filename(self) -> str:
1113
return f"{self.name}.nam" # type: ignore

flopy4/mf6/package.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
@xattree
99
class Package(Component, ABC):
10+
"""Base class for MF6 packages."""
11+
1012
def default_filename(self) -> str:
1113
name = self.parent.name if self.parent else self.name # type: ignore
1214
cls_name = self.__class__.__name__.lower()

flopy4/mf6/simulation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ def convert_time(value):
2323

2424
@xattree
2525
class Simulation(Context):
26+
"""MF6 simulation."""
27+
2628
models: dict[str, Model] = field()
2729
exchanges: dict[str, Exchange] = field()
2830
solutions: dict[str, Solution] = field()

flopy4/mf6/solution.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77

88
@xattree
99
class Solution(Package, ABC):
10+
"""Base class for MF6 solution packages."""
11+
1012
pass

0 commit comments

Comments
 (0)