diff --git a/flopy4/mf6/gwf/chd.py b/flopy4/mf6/gwf/chd.py index f9013aa2..8bcd11c9 100644 --- a/flopy4/mf6/gwf/chd.py +++ b/flopy4/mf6/gwf/chd.py @@ -2,7 +2,6 @@ from typing import ClassVar, Optional import numpy as np -from attrs import define from numpy.typing import NDArray from xattree import dict_to_array_converter, xattree @@ -15,14 +14,6 @@ class Chd(Package): multi_package: ClassVar[bool] = True - @define(slots=False) - class Steps: - all: bool = field() - first: bool = field() - last: bool = field() - steps: list[int] = field() - frequency: int = field() - auxiliary: Optional[list[str]] = array(block="options", default=None) auxmultname: Optional[str] = field(block="options", default=None) boundnames: bool = field(block="options", default=False) @@ -31,8 +22,8 @@ class Steps: 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) - dev_no_newton: bool = field(default=False, metadata={"block": "options"}) - maxbound: Optional[int] = field(block="dimensions", default=None) + 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( block="period", dims=( @@ -63,14 +54,6 @@ class Steps: converter=dict_to_array_converter, reader="urword", ) - steps: Optional[NDArray[np.object_]] = array( - Steps, - block="period", - dims=("nper", "nnodes"), - default=None, - converter=dict_to_array_converter, - reader="urword", - ) def __attrs_post_init__(self): # TODO set up on_setattr hooks for period block @@ -98,5 +81,4 @@ def __attrs_post_init__(self): ) maxboundname = len(np.where(boundname != "")) - # maxsteps = len(np.where(self.steps != None)) if self.steps is not None else 0 self.maxbound = max(maxhead, maxaux, maxboundname) diff --git a/flopy4/mf6/gwf/drn.py b/flopy4/mf6/gwf/drn.py new file mode 100644 index 00000000..5858d34d --- /dev/null +++ b/flopy4/mf6/gwf/drn.py @@ -0,0 +1,94 @@ +from pathlib import Path +from typing import ClassVar, Optional + +import numpy as np +from numpy.typing import NDArray +from xattree import dict_to_array_converter + +from flopy4.mf6.constants import FILL_DNODATA +from flopy4.mf6.package import Package +from flopy4.mf6.spec import array, field + + +class Drn(Package): + multi_package: ClassVar[bool] = True + + auxiliary: Optional[list[str]] = array(block="options", default=None) + auxmultname: Optional[str] = field(block="options", default=None) + auxdepthname: Optional[str] = field(block="options", default=None) + boundnames: bool = field(block="options", default=False) + 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) + 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) + elev: Optional[NDArray[np.float64]] = array( + block="period", + dims=("nper", "nnodes"), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + cond: Optional[NDArray[np.float64]] = array( + block="period", + dims=("nper", "nnodes"), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + aux: Optional[NDArray[np.float64]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + boundname: Optional[NDArray[np.str_]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + + def __attrs_post_init__(self): + # TODO set up on_setattr hooks for period block + # arrays to update maxbound? for now do it here + # in post init. but this only works when values + # are set in the initializer, not when they are + # set later. + if self.head is None: + maxhead = 0 + else: + head = self.head if self.head.data.shape == self.head.shape else self.head.todense() + maxhead = len(np.where(head != FILL_DNODATA)) + if self.cond is None: + maxcond = 0 + else: + cond = self.cond if self.cond.data.shape == self.cond.shape else self.cond.todense() + maxcond = len(np.where(cond != FILL_DNODATA)) + if self.aux is None: + maxaux = 0 + else: + aux = self.aux if self.aux.data.shape == self.aux.shape else self.aux.todense() + maxaux = len(np.where(aux != FILL_DNODATA)) + if self.boundname is None: + maxboundname = 0 + else: + boundname = ( + self.boundname + if self.boundname.data.shape == self.boundname.shape + else self.boundname.todense() + ) + maxboundname = len(np.where(boundname != "")) + + self.maxbound = max(maxhead, maxcond, maxaux, maxboundname) diff --git a/flopy4/mf6/gwf/rch.py b/flopy4/mf6/gwf/rch.py new file mode 100644 index 00000000..0b5dbbfa --- /dev/null +++ b/flopy4/mf6/gwf/rch.py @@ -0,0 +1,84 @@ +from pathlib import Path +from typing import ClassVar, Optional + +import numpy as np +from numpy.typing import NDArray +from xattree import dict_to_array_converter, xattree + +from flopy4.mf6.constants import FILL_DNODATA +from flopy4.mf6.package import Package +from flopy4.mf6.spec import array, field + + +@xattree +class Rch(Package): + multi_package: ClassVar[bool] = True + + fixed_cell: bool = field(block="options", default=False) + auxiliary: Optional[list[str]] = array(block="options", default=None) + auxmultname: Optional[str] = field(block="options", default=None) + boundnames: bool = field(block="options", default=False) + 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) + maxbound: Optional[int] = field(block="dimensions", default=None, init=False) + recharge: Optional[NDArray[np.float64]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + aux: Optional[NDArray[np.float64]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + boundname: Optional[NDArray[np.str_]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + + def __attrs_post_init__(self): + # TODO set up on_setattr hooks for period block + # arrays to update maxbound? for now do it here + # in post init. but this only works when values + # are set in the initializer, not when they are + # set later. + if self.recharge is None: + maxrecharge = 0 + else: + recharge = self.head if self.head.data.shape == self.head.shape else self.head.todense() + maxrecharge = len(np.where(recharge != FILL_DNODATA)) + if self.aux is None: + maxaux = 0 + else: + aux = self.aux if self.aux.data.shape == self.aux.shape else self.aux.todense() + maxaux = len(np.where(aux != FILL_DNODATA)) + if self.boundname is None: + maxboundname = 0 + else: + boundname = ( + self.boundname + if self.boundname.data.shape == self.boundname.shape + else self.boundname.todense() + ) + maxboundname = len(np.where(boundname != "")) + + self.maxbound = max(maxrecharge, maxaux, maxboundname) diff --git a/flopy4/mf6/gwf/sto.py b/flopy4/mf6/gwf/sto.py new file mode 100644 index 00000000..2f1693e7 --- /dev/null +++ b/flopy4/mf6/gwf/sto.py @@ -0,0 +1,52 @@ +from pathlib import Path +from typing import Optional + +import numpy as np +from numpy.typing import NDArray +from xattree import dict_to_array_converter + +from flopy4.mf6.package import Package +from flopy4.mf6.spec import array, field + + +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) + 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) + dev_oldstorageformulation: bool = field(block="options", default=False) + iconvert: NDArray[np.int32] = array( + block="griddata", + dims=("nnodes",), + default=0, + converter=dict_to_array_converter, + ) + ss: NDArray[np.float64] = array( + block="griddata", + dims=("nnodes",), + default=1e-5, + converter=dict_to_array_converter, + ) + sy: NDArray[np.float64] = array( + block="griddata", + dims=("nnodes",), + default=0.15, + converter=dict_to_array_converter, + ) + steady_state: Optional[NDArray[np.bool_]] = array( + block="period", + dims=("nper",), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + transient: Optional[NDArray[np.bool_]] = array( + block="period", + dims=("nper",), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) diff --git a/flopy4/mf6/gwf/wel.py b/flopy4/mf6/gwf/wel.py new file mode 100644 index 00000000..beb4bc9c --- /dev/null +++ b/flopy4/mf6/gwf/wel.py @@ -0,0 +1,85 @@ +from pathlib import Path +from typing import ClassVar, Optional + +import numpy as np +from numpy.typing import NDArray +from xattree import dict_to_array_converter + +from flopy4.mf6.constants import FILL_DNODATA +from flopy4.mf6.package import Package +from flopy4.mf6.spec import array, field + + +class Wel(Package): + multi_package: ClassVar[bool] = True + + auxiliary: Optional[list[str]] = array(block="options", default=None) + auxmultname: Optional[str] = field(block="options", default=None) + boundnames: bool = field(block="options", default=False) + print_input: bool = field(block="options", default=False) + 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) + mover: bool = field(block="options", default=False) + maxbound: Optional[int] = field(block="dimensions", default=None, init=False) + q: Optional[NDArray[np.float64]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + aux: Optional[NDArray[np.float64]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + boundname: Optional[NDArray[np.str_]] = array( + block="period", + dims=( + "nper", + "nnodes", + ), + default=None, + converter=dict_to_array_converter, + reader="urword", + ) + + def __attrs_post_init__(self): + # TODO set up on_setattr hooks for period block + # arrays to update maxbound? for now do it here + # in post init. but this only works when values + # are set in the initializer, not when they are + # set later. + if self.q is None: + maxq = 0 + else: + q = self.q if self.q.data.shape == self.q.shape else self.q.todense() + maxq = len(np.where(q != FILL_DNODATA)) + if self.aux is None: + maxaux = 0 + else: + aux = self.aux if self.aux.data.shape == self.aux.shape else self.aux.todense() + maxaux = len(np.where(aux != FILL_DNODATA)) + if self.boundname is None: + maxboundname = 0 + else: + boundname = ( + self.boundname + if self.boundname.data.shape == self.boundname.shape + else self.boundname.todense() + ) + maxboundname = len(np.where(boundname != "")) + + self.maxbound = max(maxq, maxaux, maxboundname)