Skip to content

Commit 58380ac

Browse files
authored
codegen baseline (#310)
1 parent 69f4d12 commit 58380ac

109 files changed

Lines changed: 16342 additions & 2820 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/dev/dfn-schema-plan.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ Type `integer`.
345345

346346
###### `time_series`
347347

348-
`boolean (default: false)`. Marks fields where the parser accepts either a numeric literal or a time-series name (referencing a `utl-ts` object). Not inferrable from structural type. Also appears on array fields (where it references a `utl-tas` object instead). Note that `utl-tas` currently only works with layered arrays, not full-grid arrays, though generalizing has been considered.
348+
`boolean (default: false)`. Marks fields where the parser accepts either a numeric literal or a time-series name (referencing a `utl-ts` object). Not inferable from structural type. Also appears on array fields (where it references a `utl-tas` object instead). Note that `utl-tas` currently only works with layered arrays, not full-grid arrays, though generalizing has been considered.
349349

350350
###### `pk`
351351

@@ -371,7 +371,7 @@ Type `double`.
371371

372372
###### `time_series`
373373

374-
`boolean (default: false)`. Marks fields where the parser accepts either a numeric literal or a time-series name (referencing a `utl-ts` object). Not inferrable from structural type. Also appears on array fields (where it references a `utl-tas` object instead). Note that `utl-tas` currently only works with layered arrays, not full-grid arrays, though generalizing has been considered.
374+
`boolean (default: false)`. Marks fields where the parser accepts either a numeric literal or a time-series name (referencing a `utl-ts` object). Not inferable from structural type. Also appears on array fields (where it references a `utl-tas` object instead). Note that `utl-tas` currently only works with layered arrays, not full-grid arrays, though generalizing has been considered.
375375

376376
#### Path
377377

docs/examples/circle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def plot_head_ugrid(head, cbc, workspace):
248248
outer_maximum=500,
249249
under_relaxation=None,
250250
inner_dvclose=1.0e-4,
251-
inner_rclose=0.001,
251+
rclose=flopy4.mf6.Ims.Rclose(inner_rclose=0.001),
252252
inner_maximum=100,
253253
linear_acceleration="cg",
254254
reordering_method=None,

docs/examples/frenchman-flat.py

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -656,38 +656,13 @@ def plot_head_ugrid(head, cbc, grid, workspace):
656656
dims=dims,
657657
)
658658

659-
# Save heads at every time step; save budget only at selected steps to keep
660-
# output file size manageable for this large model.
659+
# Save heads at every time step; save budget at the last step of each period.
661660
oc = flopy4.mf6.gwf.Oc(
662661
budget_file=Path("ff.cbc"),
663662
head_file=Path("ff.hds"),
664-
perioddata={
665-
0: flopy4.mf6.gwf.Oc.PrintSaveSetting(
666-
printrecord=[
667-
flopy4.mf6.gwf.Oc.PrintRecord(
668-
"budget",
669-
flopy4.mf6.gwf.Oc.Steps(
670-
steps=(
671-
0,
672-
99,
673-
)
674-
),
675-
),
676-
],
677-
saverecord=[
678-
flopy4.mf6.gwf.Oc.SaveRecord("head", flopy4.mf6.gwf.Oc.Steps(all=True)),
679-
flopy4.mf6.gwf.Oc.SaveRecord("budget", flopy4.mf6.gwf.Oc.Steps(steps=(0,))),
680-
],
681-
),
682-
1: flopy4.mf6.gwf.Oc.PrintSaveSetting(
683-
printrecord=[
684-
flopy4.mf6.gwf.Oc.PrintRecord("budget", flopy4.mf6.gwf.Oc.Steps(last=True)),
685-
],
686-
saverecord=[
687-
flopy4.mf6.gwf.Oc.SaveRecord("head", flopy4.mf6.gwf.Oc.Steps(all=True)),
688-
],
689-
),
690-
},
663+
save_head={"0": "all", 1: "all"},
664+
save_budget={"0": "STEPS 1"},
665+
print_budget={"0": "STEPS 1 15", 1: "last"},
691666
dims=dims,
692667
)
693668

@@ -719,7 +694,7 @@ def plot_head_ugrid(head, cbc, grid, workspace):
719694
under_relaxation_gamma=0.000000,
720695
under_relaxation_momentum=0.000000,
721696
inner_dvclose=0.00001,
722-
inner_rclose=0.1,
697+
rclose=flopy4.mf6.Ims.Rclose(inner_rclose=0.1),
723698
inner_maximum=100,
724699
linear_acceleration="bicgstab",
725700
number_orthogonalizations=0,

docs/examples/quickstart.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,16 @@
6464

6565
sim = Simulation(name=name, workspace=workspace, tdis=time)
6666
gwf_name = "mymodel"
67-
ims = Ims(parent=sim, models=[gwf_name]) # registered with sim; references the model by name
67+
ims = Ims(
68+
parent=sim,
69+
models=[gwf_name],
70+
outer_dvclose=1e-3,
71+
outer_maximum=25,
72+
inner_maximum=50,
73+
inner_dvclose=1e-3,
74+
rclose=Ims.Rclose(inner_rclose=0.1),
75+
linear_acceleration="cg",
76+
) # registered with sim; references the model by name
6877
gwf = Gwf(parent=sim, name=gwf_name, save_flows=True, dis=grid)
6978

7079
# Node-property flow: isotropic conductivity; saves specific-discharge for quiver plots.

docs/examples/quickstart_expanded.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@
9191
head_file = name + ".hds"
9292
oc = Oc(
9393
parent=gwf,
94-
budget_filerecord=budget_file,
95-
head_filerecord=head_file,
94+
budget_file=budget_file,
95+
head_file=head_file,
9696
# 1) tuples (like flopy3)
9797
perioddata=[("HEAD", "ALL"), ("BUDGET", "ALL")],
9898
# 2) typed records

docs/examples/twri.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def plot_head(head, workspace):
118118
icelltype=icelltype,
119119
k=k,
120120
k33=k33,
121-
cvoptions=flopy4.mf6.gwf.Npf.CvOptions(dewatered=True),
121+
cvoptions=flopy4.mf6.gwf.Npf.Cvoptions(dewatered=True),
122122
perched=True,
123123
save_flows=True,
124124
dims=dims,
@@ -199,7 +199,7 @@ def plot_head(head, workspace):
199199
outer_maximum=500,
200200
under_relaxation=None,
201201
inner_dvclose=1.0e-4,
202-
inner_rclose=0.001,
202+
rclose=flopy4.mf6.Ims.Rclose(inner_rclose=0.001),
203203
inner_maximum=100,
204204
linear_acceleration="cg",
205205
scaling_method=None,

flopy4/mf6/__init__.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,35 @@
66
from tomli_w import dump as dump_toml
77

88
# Import submodules to make them accessible via flopy4.mf6.*
9-
from flopy4.mf6 import gwf, simulation, solution, utils
9+
from flopy4.mf6 import gwe, gwf, gwt, prt, simulation, solution, utils
1010
from flopy4.mf6.codec import dump as dump_mf6
1111
from flopy4.mf6.codec import load as load_mf6
1212
from flopy4.mf6.component import Component
1313
from flopy4.mf6.converter import structure, unstructure
14+
from flopy4.mf6.ems import Ems
15+
from flopy4.mf6.exchange import GwfGwe, GwfGwt
1416
from flopy4.mf6.ims import Ims
1517
from flopy4.mf6.netcdf import NetCDFModel
1618
from flopy4.mf6.simulation import Simulation
1719
from flopy4.mf6.tdis import Tdis
1820
from flopy4.uio import DEFAULT_REGISTRY
1921

20-
__all__ = ["gwf", "simulation", "solution", "utils", "Ims", "NetCDFModel", "Tdis", "Simulation"]
22+
__all__ = [
23+
"gwf",
24+
"gwt",
25+
"gwe",
26+
"prt",
27+
"simulation",
28+
"solution",
29+
"utils",
30+
"Ems",
31+
"GwfGwe",
32+
"GwfGwt",
33+
"Ims",
34+
"NetCDFModel",
35+
"Tdis",
36+
"Simulation",
37+
]
2138

2239

2340
class WriteError(Exception):

flopy4/mf6/binding.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def from_component(cls, component: Component) -> "Binding":
2929
def _get_binding_type(component: Component) -> str:
3030
cls_name = component.__class__.__name__
3131
if isinstance(component, Exchange):
32-
return f"{'-'.join([cls_name[:2], cls_name[3:]]).upper()}6"
32+
return f"{cls_name[:3].upper()}6-{cls_name[3:].upper()}6"
3333
elif isinstance(component, Solution):
3434
return f"{component.slntype}6"
3535
else:

flopy4/mf6/codec/writer/filters.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -243,38 +243,64 @@ def dataset2list(value: xr.Dataset):
243243
yield (*name.split("_"), val) # type: ignore
244244

245245
else:
246-
vals = []
247-
for name in value.data_vars.keys():
248-
val = value[name]
249-
val = val.item() if val.shape == () else val
250-
vals.append(val)
251-
yield tuple(vals)
246+
row: list[Any] = []
247+
for name, da in value.data_vars.items():
248+
val = da.item() if da.shape == () else da
249+
if kw := da.attrs.get("row_keyword", False):
250+
if val:
251+
row.append(kw if isinstance(kw, str) else str(name).upper())
252+
else:
253+
row.extend(da.attrs.get("prefix", ()))
254+
row.append(val)
255+
yield tuple(row)
252256
return
253257

254258
combined_mask: Any = None
259+
spatial_da = None # a non-aux DataArray for deriving spatial dims/mask
255260
for name, first in value.data_vars.items():
256-
mask = nonempty(first)
257-
combined_mask = mask if combined_mask is None else combined_mask | mask
261+
if "naux" in first.dims:
262+
# nonempty gives (spatial..., naux) bool array; collapse naux with any()
263+
mask = nonempty(first).any(axis=-1)
264+
else:
265+
mask = nonempty(first)
266+
spatial_da = first
267+
combined_mask = mask if combined_mask is None else (combined_mask | mask)
258268
if combined_mask is None or not np.any(combined_mask):
259269
return
260270

261-
spatial_dims = [d for d in first.dims if d in ("nlay", "nrow", "ncol", "nodes")]
271+
if spatial_da is None:
272+
spatial_da = first
273+
spatial_dims = [d for d in spatial_da.dims if d in ("nlay", "nrow", "ncol", "nodes")]
262274
has_spatial_dims = len(spatial_dims) > 0
263275
indices = np.where(combined_mask)
264276
for i in range(len(indices[0])):
265277
if is_oc:
266278
for name in value.data_vars.keys():
267279
val = value[name][tuple(idx[i] for idx in indices)]
268280
val = val.item() if val.shape == () else val
269-
yield (*name.split("_"), val) # type: ignore
281+
yield (*str(name).split("_"), val) # type: ignore
270282
else:
271-
vals = []
272-
for name in value.data_vars.keys():
273-
val = value[name][tuple(idx[i] for idx in indices)]
283+
row2: list[Any] = []
284+
for name, da in value.data_vars.items():
285+
val = da[tuple(idx[i] for idx in indices)]
274286
val = val.item() if val.shape == () else val
275-
vals.append(val)
287+
if kw := da.attrs.get("row_keyword", False):
288+
if val:
289+
row2.append(kw if isinstance(kw, str) else str(name).upper())
290+
else:
291+
row2.extend(da.attrs.get("prefix", ()))
292+
if da.attrs.get("cellid"):
293+
if isinstance(val, tuple):
294+
row2.extend(c + 1 for c in val)
295+
else:
296+
row2.append(val + 1)
297+
else:
298+
if hasattr(val, "ndim") and val.ndim > 0:
299+
row2.extend(float(v) for v in np.asarray(val).flat)
300+
else:
301+
row2.append(val)
276302
if has_spatial_dims:
277303
cellid = tuple(idx[i] + 1 for idx in indices)
278-
yield cellid + tuple(vals)
304+
yield tuple(cellid) + tuple(row2)
279305
else:
280-
yield tuple(vals)
306+
yield tuple(row2)

flopy4/mf6/component.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from flopy4.mf6.write_context import WriteContext
1616
from flopy4.uio import IO, Loader, Writer
1717

18-
COMPONENTS = {}
18+
COMPONENTS: dict[str, type] = {}
1919
"""MF6 component registry."""
2020

2121

@@ -95,7 +95,14 @@ def _update_maxbound_if_needed(self):
9595

9696
@classmethod
9797
def __attrs_init_subclass__(cls):
98-
COMPONENTS[cls.__name__.lower()] = cls
98+
key = cls.__name__.lower()
99+
COMPONENTS[key] = cls
100+
# Also register a model-qualified key (e.g. "gwf-ic") for classes in a
101+
# model subpackage (flopy4.mf6.<model>.<pkg>), giving deterministic
102+
# per-model lookup when multiple models share a class name like "ic".
103+
parts = cls.__module__.split(".")
104+
if len(parts) >= 4 and parts[0] == "flopy4" and parts[1] == "mf6":
105+
COMPONENTS[f"{parts[2]}-{key}"] = cls
99106

100107
def __getitem__(self, key):
101108
# We use `children` from `xattree` to implement MutableMapping.

0 commit comments

Comments
 (0)