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
57 changes: 0 additions & 57 deletions flopy4/mf6/attr_hooks.py

This file was deleted.

85 changes: 85 additions & 0 deletions flopy4/mf6/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,67 @@
from pathlib import Path
from typing import ClassVar

import numpy as np
from attrs import fields
from modflow_devtools.dfn import Dfn, Field
from xattree import xattree

from flopy4.mf6.constants import FILL_DNODATA
from flopy4.mf6.spec import field, fields_dict, to_dfn_field
from flopy4.uio import IO, Loader, Writer


def update_maxbound(instance, attribute, new_value):
"""
Generalized function to update maxbound when period block arrays change.

This function automatically finds all period block arrays in the instance
and calculates maxbound based on the maximum number of non-default values
across all arrays.

Args:
instance: The package instance
attribute: The attribute being set (from attrs on_setattr)
new_value: The new value being set

Returns:
The new_value (unchanged)
"""

period_arrays = []
instance_fields = fields(instance.__class__)
for f in instance_fields:
if (
f.metadata
and f.metadata.get("block") == "period"
and f.metadata.get("xattree", {}).get("dims")
):
period_arrays.append(f.name)

maxbound_values = []
for array_name in period_arrays:
if attribute and attribute.name == array_name:
array_val = new_value
else:
array_val = getattr(instance, array_name, None)

if array_val is not None:
array_data = (
array_val if array_val.data.shape == array_val.shape else array_val.todense()
)

if array_data.dtype.kind in ["U", "S"]: # String arrays
non_default_count = len(np.where(array_data != "")[0])
else: # Numeric arrays
non_default_count = len(np.where(array_data != FILL_DNODATA)[0])

maxbound_values.append(non_default_count)
if maxbound_values:
instance.maxbound = max(maxbound_values)

return new_value


COMPONENTS = {}
"""MF6 component registry."""

Expand Down Expand Up @@ -50,6 +105,36 @@ def default_filename(self) -> str:
cls_name = self.__class__.__name__.lower()
return f"{name}.{cls_name}"

def __attrs_post_init__(self):
"""
Post-initialization hook for all components.

Automatically handles common post-init tasks like computing maxbound
for components with period block arrays.
"""
self._update_maxbound_if_needed()

def _update_maxbound_if_needed(self):
"""
Update maxbound if this component has period block arrays.

This method checks if the component has any period block arrays defined
and calls update_maxbound if needed. This generalizes the pattern that
was previously repeated in multiple component classes.
"""
# Check if component has a maxbound field and period block arrays
component_fields = fields(self.__class__)
has_maxbound = any(f.name == "maxbound" for f in component_fields)
has_period_arrays = any(
f.metadata
and f.metadata.get("block") == "period"
and f.metadata.get("xattree", {}).get("dims")
for f in component_fields
)

if has_maxbound and has_period_arrays:
update_maxbound(self, None, None)

@classmethod
def __attrs_init_subclass__(cls):
COMPONENTS[cls.__name__.lower()] = cls
Expand Down
6 changes: 1 addition & 5 deletions flopy4/mf6/gwf/chd.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from numpy.typing import NDArray
from xattree import xattree

from flopy4.mf6.attr_hooks import update_maxbound
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converters import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
Expand Down Expand Up @@ -58,7 +58,3 @@ class Chd(Package):
reader="urword",
on_setattr=update_maxbound,
)

def __attrs_post_init__(self):
if self.head is not None or self.aux is not None or self.boundname is not None:
update_maxbound(self, None, None)
11 changes: 1 addition & 10 deletions flopy4/mf6/gwf/drn.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from numpy.typing import NDArray
from xattree import xattree

from flopy4.mf6.attr_hooks import update_maxbound
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converters import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
Expand Down Expand Up @@ -65,12 +65,3 @@ class Drn(Package):
reader="urword",
on_setattr=update_maxbound,
)

def __attrs_post_init__(self):
if (
self.elev is not None
or self.cond is not None
or self.aux is not None
or self.boundname is not None
):
update_maxbound(self, None, None)
6 changes: 1 addition & 5 deletions flopy4/mf6/gwf/rch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from numpy.typing import NDArray
from xattree import xattree

from flopy4.mf6.attr_hooks import update_maxbound
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converters import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
Expand Down Expand Up @@ -58,7 +58,3 @@ class Rch(Package):
reader="urword",
on_setattr=update_maxbound,
)

def __attrs_post_init__(self):
if self.recharge is not None or self.aux is not None or self.boundname is not None:
update_maxbound(self, None, None)
6 changes: 1 addition & 5 deletions flopy4/mf6/gwf/wel.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from numpy.typing import NDArray
from xattree import xattree

from flopy4.mf6.attr_hooks import update_maxbound
from flopy4.mf6.component import update_maxbound
from flopy4.mf6.converters import dict_to_array
from flopy4.mf6.package import Package
from flopy4.mf6.spec import array, field
Expand Down Expand Up @@ -60,7 +60,3 @@ class Wel(Package):
reader="urword",
on_setattr=update_maxbound,
)

def __attrs_post_init__(self):
if self.q is not None or self.aux is not None or self.boundname is not None:
update_maxbound(self, None, None)
Loading