Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 13 additions & 22 deletions flopy4/mf6/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,17 @@
from flopy4.mf6.config import SPARSE_THRESHOLD
from flopy4.mf6.constants import FILL_DNODATA
from flopy4.mf6.context import Context
from flopy4.mf6.spec import fields_dict
from flopy4.mf6.spec import FileInOut


def _attach_field_metadata(
dataset: xr.Dataset, component_type: type, field_names: list[str]
) -> None:
# TODO: attach metadata to array attrs instead of dataset attrs
field_metadata = {}
component_fields = fields_dict(component_type)
for field_name in field_names:
if field_name in component_fields:
field_metadata[field_name] = component_fields[field_name].metadata
dataset.attrs["field_metadata"] = field_metadata


def _path_to_tuple(field_name: str, path_value: Path) -> tuple:
if field_name.endswith("_file"):
base_name = field_name.replace("_file", "").upper()
return (base_name, "FILEOUT", str(path_value))
return (field_name.upper(), "FILEOUT", str(path_value))
def path_to_tuple(name: str, value: Path, inout: FileInOut) -> tuple[str, ...]:
t = [name.upper()]
if name.endswith("_file"):
t[0] = name.replace("_file", "").upper()
if inout:
t.append(inout.upper())
t.append(str(value))
return tuple(t)


def get_binding_blocks(value: Component) -> dict[str, dict[str, list[tuple[str, ...]]]]:
Expand Down Expand Up @@ -100,9 +91,11 @@ def unstructure_component(value: Component) -> dict[str, Any]:
# (and split the period data into separate kper-indexed blocks)
# - other values to their original form
if isinstance(field_value, Path):
rec = _path_to_tuple(field_name, field_value)
field_spec = xatspec.attrs[field_name]
field_meta = getattr(field_spec, "metadata", {})
t = path_to_tuple(field_name, field_value, inout=field_meta.get("inout", "fileout"))
# name may have changed e.g dropping '_file' suffix
blocks[block_name][rec[0]] = rec
blocks[block_name][t[0]] = t
elif isinstance(field_value, datetime):
blocks[block_name][field_name] = field_value.isoformat()
elif (
Expand Down Expand Up @@ -165,7 +158,6 @@ def unstructure_component(value: Component) -> dict[str, Any]:

if block_name in period_data and isinstance(period_data[block_name], dict):
dataset = xr.Dataset(period_data[block_name])
_attach_field_metadata(dataset, type(value), list(period_data[block_name].keys())) # type: ignore
blocks[block_name] = {block_name: dataset}
del period_data[block_name]

Expand All @@ -177,7 +169,6 @@ def unstructure_component(value: Component) -> dict[str, Any]:

for kper, block in period_blocks.items():
dataset = xr.Dataset(block)
_attach_field_metadata(dataset, type(value), list(block.keys()))
blocks[f"{block_name} {kper + 1}"] = {block_name: dataset}

# make sure options block always comes first
Expand Down
15 changes: 11 additions & 4 deletions flopy4/mf6/gwf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
from flopy4.mf6.gwf.oc import Oc
from flopy4.mf6.gwf.wel import Wel
from flopy4.mf6.model import Model
from flopy4.mf6.spec import field
from flopy4.mf6.spec import field, path
from flopy4.mf6.utils import open_cbc, open_hds
from flopy4.utils import to_path

__all__ = ["Gwf", "Chd", "Dis", "Drn", "Ic", "Npf", "Oc", "Wel"]

Expand Down Expand Up @@ -61,9 +62,15 @@ def budget(self):
print_flows: bool = field(block="options", default=False)
save_flows: bool = field(block="options", default=False)
newtonoptions: Optional[NewtonOptions] = field(block="options", default=None)
nc_mesh2d_filerecord: Optional[Path] = field(block="options", default=None)
nc_structured_filerecord: Optional[Path] = field(block="options", default=None)
nc_filerecord: Optional[Path] = field(block="options", default=None)
nc_mesh2d_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
nc_structured_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
nc_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
dis: Dis = field(converter=convert_grid, block="packages")
ic: Ic = field(block="packages")
oc: Oc = field(block="packages")
Expand Down
11 changes: 8 additions & 3 deletions flopy4/mf6/gwf/chd.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converter import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
from flopy4.mf6.spec import array, field, path
from flopy4.utils import to_path


@xattree
Expand All @@ -21,8 +22,12 @@ class Chd(Package):
print_input: bool = field(block="options", default=False)
print_flows: bool = field(block="options", default=False)
save_flows: bool = field(block="options", default=False)
ts_filerecord: Optional[Path] = field(block="options", default=None)
obs_filerecord: Optional[Path] = field(block="options", default=None)
ts_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="filein"
)
obs_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
dev_no_newton: bool = field(default=False, block="options")
maxbound: Optional[int] = field(block="dimensions", default=None, init=False)
head: Optional[NDArray[np.float64]] = array(
Expand Down
11 changes: 8 additions & 3 deletions flopy4/mf6/gwf/drn.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converter import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
from flopy4.mf6.spec import array, field, path
from flopy4.utils import to_path


@xattree
Expand All @@ -22,8 +23,12 @@ class Drn(Package):
print_input: bool = field(block="options", default=False)
print_flows: bool = field(block="options", default=False)
save_flows: bool = field(block="options", default=False)
ts_filerecord: Optional[Path] = field(block="options", default=None)
obs_filerecord: Optional[Path] = field(block="options", default=None)
ts_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="filein"
)
obs_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
mover: bool = field(block="options", default=False)
dev_cubic_scaling: bool = field(default=False, block="options")
maxbound: Optional[int] = field(block="dimensions", default=None, init=False)
Expand Down
7 changes: 5 additions & 2 deletions flopy4/mf6/gwf/npf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from flopy4.mf6.converter import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
from flopy4.mf6.spec import array, field, path
from flopy4.utils import to_path


@xattree
Expand Down Expand Up @@ -42,7 +43,9 @@ class Xt3dOptions:
save_saturation: bool = field(block="options", default=None)
k22overk: bool = field(block="options", default=None)
k33overk: bool = field(block="options", default=None)
tvk_filerecord: Optional[Path] = field(block="options", default=None)
tvk_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="filein"
)
export_array_ascii: bool = field(block="options", default=False)
export_array_netcdf: bool = field(block="options", default=False)
dev_no_newton: bool = field(block="options", default=False)
Expand Down
20 changes: 7 additions & 13 deletions flopy4/mf6/gwf/oc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from flopy4.mf6.converter import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
from flopy4.mf6.spec import array, field, path
from flopy4.utils import to_path


Expand All @@ -35,20 +35,14 @@ class Period:
rtype: str = field()
steps: "Oc.Steps" = field()

budget_file: Optional[Path] = field(
block="options",
converter=to_path,
default=None,
budget_file: Optional[Path] = path(
block="options", converter=to_path, default=None, inout="fileout"
)
budget_csv_file: Optional[Path] = field(
block="options",
converter=to_path,
default=None,
budget_csv_file: Optional[Path] = path(
block="options", converter=to_path, default=None, inout="fileout"
)
head_file: Optional[Path] = field(
block="options",
converter=to_path,
default=None,
head_file: Optional[Path] = path(
block="options", converter=to_path, default=None, inout="fileout"
)
# TODO: needs coverter and then rename?
head: Optional[Format] = field(block="options", default=None)
Expand Down
11 changes: 8 additions & 3 deletions flopy4/mf6/gwf/rch.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converter import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
from flopy4.mf6.spec import array, field, path
from flopy4.utils import to_path


@xattree
Expand All @@ -22,8 +23,12 @@ class Rch(Package):
print_input: bool = field(block="options", default=False)
print_flows: bool = field(block="options", default=False)
save_flows: bool = field(block="options", default=False)
ts_filerecord: Optional[Path] = field(block="options", default=None)
obs_filerecord: Optional[Path] = field(block="options", default=None)
ts_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="filein"
)
obs_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
maxbound: Optional[int] = field(block="dimensions", default=None, init=False)
recharge: Optional[NDArray[np.float64]] = array(
block="period",
Expand Down
7 changes: 5 additions & 2 deletions flopy4/mf6/gwf/sto.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@

from flopy4.mf6.converter import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
from flopy4.mf6.spec import array, field, path
from flopy4.utils import to_path


@xattree
class Sto(Package):
save_flows: bool = field(block="options", default=False)
storagecoefficient: bool = field(block="options", default=False)
ss_confined_only: bool = field(block="options", default=False)
tvs_filerecord: Optional[Path] = field(block="options", default=None)
tvs_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="filein"
)
export_array_ascii: bool = field(block="options", default=False)
export_array_netcdf: bool = field(block="options", default=False)
dev_original_specific_storage: bool = field(block="options", default=False)
Expand Down
15 changes: 11 additions & 4 deletions flopy4/mf6/gwf/wel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converter import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
from flopy4.mf6.spec import array, field, path
from flopy4.utils import to_path


@xattree
Expand All @@ -22,9 +23,15 @@ class Wel(Package):
print_flows: bool = field(block="options", default=False)
save_flows: bool = field(block="options", default=False)
auto_flow_reduce: float = field(block="options", default=None)
afrcsv_filerecord: Optional[Path] = field(block="options", default=None)
ts_filerecord: Optional[Path] = field(block="options", default=None)
obs_filerecord: Optional[Path] = field(block="options", default=None)
afrcsv_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
ts_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="filein"
)
obs_filerecord: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
mover: bool = field(block="options", default=False)
maxbound: Optional[int] = field(block="dimensions", default=None, init=False)
q: Optional[NDArray[np.float64]] = array(
Expand Down
21 changes: 13 additions & 8 deletions flopy4/mf6/ims.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from xattree import xattree

from flopy4.mf6.solution import Solution
from flopy4.mf6.spec import field
from flopy4.mf6.spec import field, path
from flopy4.utils import to_path


@xattree
Expand All @@ -13,14 +14,18 @@ class Ims(Solution):

print_option: Optional[str] = field(block="options", default=None)
complexity: str = field(block="options", default="simple")
csv_outer_output_file: Optional[Path] = field(default=None, block="options")
csv_inner_output_file: Optional[Path] = field(block="options", default=None)
no_ptc: bool = field(default=False, block="options")
no_ptc_option: Optional[str] = field(default=None, block="options")
csv_outer_output_file: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
csv_inner_output_file: Optional[Path] = path(
block="options", default=None, converter=to_path, inout="fileout"
)
no_ptc: bool = field(block="options", default=False)
no_ptc_option: Optional[str] = field(block="options", default=None)
ats_outer_maximum_fraction: Optional[float] = field(block="options", default=None)
outer_dvclose: Optional[float] = field(default=None, block="nonlinear")
outer_maximum: Optional[int] = field(default=None, block="nonlinear")
under_relaxation: Optional[str] = field(default=None, block="nonlinear")
outer_dvclose: Optional[float] = field(block="nonlinear", default=None)
outer_maximum: Optional[int] = field(block="nonlinear", default=None)
under_relaxation: Optional[str] = field(block="nonlinear", default=None)
under_relaxation_gamma: Optional[float] = field(block="nonlinear", default=None)
under_relaxation_theta: Optional[float] = field(block="nonlinear", default=None)
under_relaxation_kappa: Optional[float] = field(block="nonlinear", default=None)
Expand Down
36 changes: 35 additions & 1 deletion flopy4/mf6/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import types
from datetime import datetime
from pathlib import Path
from typing import Union, get_args, get_origin
from typing import Literal, Union, get_args, get_origin

import numpy as np
from attrs import NOTHING, Attribute
Expand Down Expand Up @@ -49,6 +49,40 @@ def field(
)


FileInOut = Literal[None, "filein", "fileout"]


def path(
default=NOTHING,
validator=None,
converter=None,
repr=True,
eq=True,
init=True,
metadata=None,
on_setattr=None,
block: str | None = None,
inout: FileInOut | None = None,
):
"""Define a path field."""
if block:
metadata = metadata or {}
metadata["block"] = block
if inout:
metadata = metadata or {}
metadata["inout"] = inout
return flopy_field(
default=default,
validator=validator,
converter=converter,
repr=repr,
eq=eq,
init=init,
on_setattr=on_setattr,
metadata=metadata,
)


def dim(
scope=None,
coord: bool | str = True,
Expand Down
Loading