22from typing import ClassVar , Optional
33
44import numpy as np
5- from attrs import Converter
5+ from attrs import Converter , setters
66from numpy .typing import NDArray
77from xattree import xattree
88
1212from flopy4 .mf6 .spec import array , field
1313
1414
15+ def _update_maxbound (instance , attribute , new_value ):
16+ """Update maxbound when period block arrays change."""
17+ if hasattr (instance , '_updating_maxbound' ):
18+ return new_value
19+
20+ # Calculate maxbound from all relevant arrays
21+ maxbound_values = []
22+
23+ # Check elev array
24+ elev_val = new_value if attribute and attribute .name == 'elev' else getattr (instance , 'elev' , None )
25+ if elev_val is not None :
26+ elev = elev_val if elev_val .data .shape == elev_val .shape else elev_val .todense ()
27+ maxbound_values .append (len (np .where (elev != FILL_DNODATA )[0 ]))
28+
29+ # Check cond array
30+ cond_val = new_value if attribute and attribute .name == 'cond' else getattr (instance , 'cond' , None )
31+ if cond_val is not None :
32+ cond = cond_val if cond_val .data .shape == cond_val .shape else cond_val .todense ()
33+ maxbound_values .append (len (np .where (cond != FILL_DNODATA )[0 ]))
34+
35+ # Check aux array
36+ aux_val = new_value if attribute and attribute .name == 'aux' else getattr (instance , 'aux' , None )
37+ if aux_val is not None :
38+ aux = aux_val if aux_val .data .shape == aux_val .shape else aux_val .todense ()
39+ maxbound_values .append (len (np .where (aux != FILL_DNODATA )[0 ]))
40+
41+ # Check boundname array
42+ boundname_val = new_value if attribute and attribute .name == 'boundname' else getattr (instance , 'boundname' , None )
43+ if boundname_val is not None :
44+ boundname = boundname_val if boundname_val .data .shape == boundname_val .shape else boundname_val .todense ()
45+ maxbound_values .append (len (np .where (boundname != "" )[0 ]))
46+
47+ # Update maxbound if we have values
48+ if maxbound_values :
49+ instance ._updating_maxbound = True
50+ try :
51+ instance .maxbound = max (maxbound_values )
52+ finally :
53+ delattr (instance , '_updating_maxbound' )
54+
55+ return new_value
56+
57+
1558@xattree
1659class Drn (Package ):
1760 multi_package : ClassVar [bool ] = True
@@ -33,13 +76,15 @@ class Drn(Package):
3376 default = None ,
3477 converter = Converter (dict_to_array , takes_self = True , takes_field = True ),
3578 reader = "urword" ,
79+ on_setattr = _update_maxbound ,
3680 )
3781 cond : Optional [NDArray [np .float64 ]] = array (
3882 block = "period" ,
3983 dims = ("nper" , "nnodes" ),
4084 default = None ,
4185 converter = Converter (dict_to_array , takes_self = True , takes_field = True ),
4286 reader = "urword" ,
87+ on_setattr = _update_maxbound ,
4388 )
4489 aux : Optional [NDArray [np .float64 ]] = array (
4590 block = "period" ,
@@ -50,6 +95,7 @@ class Drn(Package):
5095 default = None ,
5196 converter = Converter (dict_to_array , takes_self = True , takes_field = True ),
5297 reader = "urword" ,
98+ on_setattr = _update_maxbound ,
5399 )
54100 boundname : Optional [NDArray [np .str_ ]] = array (
55101 block = "period" ,
@@ -60,37 +106,10 @@ class Drn(Package):
60106 default = None ,
61107 converter = Converter (dict_to_array , takes_self = True , takes_field = True ),
62108 reader = "urword" ,
109+ on_setattr = _update_maxbound ,
63110 )
64111
65112 def __attrs_post_init__ (self ):
66- # TODO set up on_setattr hooks for period block
67- # arrays to update maxbound? for now do it here
68- # in post init. but this only works when values
69- # are set in the initializer, not when they are
70- # set later.
71- if self .elev is None :
72- maxelev = 0
73- else :
74- elev = self .elev if self .elev .data .shape == self .elev .shape else self .elev .todense ()
75- maxelev = len (np .where (elev != FILL_DNODATA ))
76- if self .cond is None :
77- maxcond = 0
78- else :
79- cond = self .cond if self .cond .data .shape == self .cond .shape else self .cond .todense ()
80- maxcond = len (np .where (cond != FILL_DNODATA ))
81- if self .aux is None :
82- maxaux = 0
83- else :
84- aux = self .aux if self .aux .data .shape == self .aux .shape else self .aux .todense ()
85- maxaux = len (np .where (aux != FILL_DNODATA ))
86- if self .boundname is None :
87- maxboundname = 0
88- else :
89- boundname = (
90- self .boundname
91- if self .boundname .data .shape == self .boundname .shape
92- else self .boundname .todense ()
93- )
94- maxboundname = len (np .where (boundname != "" ))
95-
96- self .maxbound = max (maxelev , maxcond , maxaux , maxboundname )
113+ # Trigger maxbound calculation on initialization
114+ if self .elev is not None or self .cond is not None or self .aux is not None or self .boundname is not None :
115+ _update_maxbound (self , None , None )
0 commit comments