11from collections .abc import Hashable , Mapping
22from io import StringIO
3- from typing import Any
3+ from typing import Any , Literal
44
55import numpy as np
66import xarray as xr
77from modflow_devtools .dfn .schema .v2 import FieldType
88from numpy .typing import NDArray
9+ from xattree import Scalar
910
1011from flopy4 .mf6 .constants import FILL_DNODATA
1112
@@ -32,14 +33,34 @@ def field_type(value: Any) -> FieldType:
3233 raise ValueError (f"Unsupported field type: { type (value )} " )
3334
3435
35- def array_how (value : xr .DataArray ) -> str :
36- # TODO
37- # - detect constant arrays?
38- # - above certain size, use external?
36+ ArrayHow = Literal ["constant" , "internal" , "external" ]
37+
38+
39+ def array_how (value : xr .DataArray ) -> ArrayHow :
40+ """
41+ Determine how an array should be represented in MF6 input.
42+ Options are "constant", "internal", or "external". If the
43+ array dask-backed, assumed it's big and return "external".
44+ Otherwise there is no materialization cost to check if all
45+ values are the same, so return "constant" or "internal" as
46+ appropriate.
47+ """
48+ if hasattr (value .data , "blocks" ):
49+ return "external"
50+ if value .max () == value .min ():
51+ return "constant"
3952 return "internal"
4053
4154
42- def array_chunks (value : xr .DataArray , chunks : Mapping [Hashable , int ] | None = None ):
55+ def array2const (value : xr .DataArray ) -> Scalar :
56+ if np .issubdtype (value .dtype , np .integer ):
57+ return value .max ().item ()
58+ if np .issubdtype (value .dtype , np .floating ):
59+ return f"{ value .max ().item ():.8f} "
60+ return value .ravel ()[0 ]
61+
62+
63+ def array2chunks (value : xr .DataArray , chunks : Mapping [Hashable , int ] | None = None ):
4364 """
4465 Yield chunks from a dask-backed array of up to 3 dimensions.
4566 If it's not already chunked, split it into chunks of the
0 commit comments