6
6
import imod
7
7
from imod .wq .pkgbase import Package
8
8
9
+
9
10
class HorizontalAnisotropyFile (Package ):
10
11
"""
11
12
Horizontal Anisotropy package.
12
13
13
14
Parameters
14
15
----------
15
16
anifile: str
16
- is the file location of the imod-wq ani-file. This file contains the
17
+ is the file location of the imod-wq ani-file. This file contains the
17
18
anisotropy factor and angle of each layer, either as a constant or a
18
- reference to the file location of an '.arr' file. No checks are
19
- implemented for this file, user is responsible for consistency with
19
+ reference to the file location of an '.arr' file. No checks are
20
+ implemented for this file, user is responsible for consistency with
20
21
model.
21
22
"""
22
23
23
24
_pkg_id = "ani"
24
25
25
- _template = (
26
- "[ani]\n "
27
- " anifile = {anifile}\n "
28
- )
26
+ _template = "[ani]\n " " anifile = {anifile}\n \n "
29
27
30
28
def __init__ (
31
29
self ,
32
30
anifile ,
33
31
):
34
32
super ().__init__ ()
35
33
self ["anifile" ] = anifile
36
-
37
- def _render (self , directory , * args , ** kwargs ):
38
- path_ani = pathlib .Path (str (self ['anifile' ].values ))
39
- d = {"anifile" : f"ani/{ path_ani .name } " }
34
+
35
+ def _render (self , modelname , directory , nlayer ):
36
+ # write ani file
37
+ # in render function, as here we know where the model is run from
38
+ # and how many layers: store this info for later use in save
39
+ self .anifile = f"{ modelname } .ani"
40
+ self .rendir = directory
41
+ self .nlayer = nlayer
42
+
43
+ d = {"anifile" : f"{ directory .as_posix ()} /{ modelname } .ani" }
40
44
41
45
return self ._template .format (** d )
42
-
46
+
43
47
def save (self , directory ):
44
- """Overload save function.
45
- Saves anifile to location, along with referenced .arr files"""
46
- directory .mkdir (exist_ok = True )
47
-
48
- path_ani = pathlib .Path (str (self ['anifile' ].values ))
49
-
48
+ """Overload save function.
49
+ Saves anifile to location, along with referenced .arr files
50
+ assumes _render() to have run previously"""
51
+ directory .mkdir (exist_ok = True ) # otherwise handled by idf.save
52
+
53
+ path_ani = pathlib .Path (str (self ["anifile" ].values ))
54
+
50
55
# regex adapted from stackoverflow: https://stackoverflow.com/questions/54990405/a-general-regex-to-extract-file-paths-not-urls
51
- rgx = r' ((?:[a-zA-Z]:|(?<![:/\\])[\\\/](?!CLOSE)(?!close )|\~[\\\/]|(?:\.{1,2}[\\\/])+)[\w+\\\s_\-\(\)\/]*(?:\.\w+)*)'
52
- with open (path_ani , "r" ) as f , open (directory / path_ani . name , "w" ) as f2 :
56
+ rgx = r" ((?:[a-zA-Z]:|(?<![:/\\])[\\\/](?!CLOSE)(?!close )|\~[\\\/]|(?:\.{1,2}[\\\/])+)[\w+\\\s_\-\(\)\/]*(?:\.\w+)*)"
57
+ with open (path_ani , "r" ) as f , open (directory / self . anifile , "w" ) as f2 :
53
58
for line in f :
54
59
p = re .search (rgx , line )
55
60
if p :
56
- # path to file detected,
61
+ # path to file detected,
57
62
# replace to relative and
58
63
# copy to directory
59
64
path = pathlib .Path (p [0 ])
60
- f2 .write (line .replace (p [0 ], f"{ directory .stem } /{ path .name } " ))
65
+ f2 .write (
66
+ line .replace (p [0 ], f"{ self .rendir .as_posix ()} /{ path .name } " )
67
+ )
61
68
if not path .is_absolute ():
62
69
path = path_ani .parent / path
63
70
shutil .copyfile (path , directory / path .name )
@@ -72,47 +79,50 @@ class HorizontalAnisotropy(Package):
72
79
"""
73
80
Horizontal Anisotropy package.
74
81
Anisotropy is a phenomenon for which the permeability k is not equal along the x- and y Cartesian axis.
75
-
82
+
76
83
Parameters
77
84
----------
78
85
factor : float or xr.DataArray of floats
79
- The anisotropic factor perpendicular to the main principal axis (axis of highest permeability).
86
+ The anisotropic factor perpendicular to the main principal axis (axis of highest permeability).
80
87
Factor between 0.0 (full anisotropic) and 1.0 (full isotropic).
81
88
angle : float or xr.DataArray of floats
82
89
The angle along the main principal axis (highest permeability) measured in degrees from north (0),
83
90
east (90), south (180) and west (270).
84
- fn_ani : filename for created anifile, optional
85
- Default: ani.ani
86
91
"""
87
92
88
93
_pkg_id = "ani"
89
94
90
- _template = (
91
- "[ani]\n "
92
- " anifile = {anifile}\n "
93
- )
95
+ _template = "[ani]\n " " anifile = {anifile}\n \n "
94
96
95
97
def __init__ (
96
98
self ,
97
99
factor ,
98
100
angle ,
99
- fn_ani = "ani.ani"
100
101
):
101
102
super ().__init__ ()
102
103
self ["factor" ] = factor
103
- self ["factor" ] = self ["factor" ].fillna (1. )
104
+ self ["factor" ] = self ["factor" ].fillna (1.0 )
104
105
self ["angle" ] = angle
105
- self ["angle" ] = self ["angle" ].fillna (0. )
106
- self ["fn_ani" ] = fn_ani
107
-
108
- def _render (self , directory , * args , ** kwargs ):
109
- d = {"anifile" : f"ani/{ str (self ['fn_ani' ].values )} " }
106
+ self ["angle" ] = self ["angle" ].fillna (0.0 )
107
+
108
+ def _render (self , modelname , directory , nlayer ):
109
+ # write ani file
110
+ # in render function, as here we know where the model is run from
111
+ # and how many layers: store this info for later use in save
112
+ self .anifile = f"{ modelname } .ani"
113
+ self .rendir = directory
114
+ self .nlayer = nlayer
115
+
116
+ d = {"anifile" : f"{ directory .as_posix ()} /{ modelname } .ani" }
110
117
111
118
return self ._template .format (** d )
112
-
119
+
113
120
def save (self , directory ):
114
- """Overload save function.
115
- Saves anifile to location, along with referenced .arr files"""
121
+ """Overload save function.
122
+ Saves anifile to location, along with created .arr files
123
+ assumes _render() to have run previously"""
124
+ directory .mkdir (exist_ok = True ) # otherwise handled by idf.save
125
+
116
126
def _write (path , a , nodata = 1.0e20 , dtype = np .float32 ):
117
127
if not np .all (a == a [0 ][0 ]):
118
128
return np .savetxt (path , a , fmt = "%.5f" , delimiter = " " )
@@ -124,22 +134,39 @@ def _write(path, a, nodata=1.0e20, dtype=np.float32):
124
134
if "y" in da .coords and "x" in da .coords :
125
135
path = pathlib .Path (directory ).joinpath (f"{ name } .arr" )
126
136
if name == "factor" :
127
- nodata = 1.
137
+ nodata = 1.0
128
138
elif name == "angle" :
129
- nodata = 0.
139
+ nodata = 0.0
130
140
else :
131
141
nodata = 1.0e20
132
- imod .array_io .writing ._save (path , da , nodata = nodata , pattern = None , dtype = np .float32 , write = _write )
133
-
134
- # write ani file
135
- with open (directory / f"{ str (self ['fn_ani' ].values )} " , "w" ) as f :
136
- for l in self .dataset .layer .values :
137
- for prm in ["factor" ,"angle" ]:
138
- a = self .dataset [prm ].sel (layer = l ).values
139
- if not np .all (a == a [0 ][0 ]):
140
- f .write (f"open/close { directory .as_posix ()} /{ prm } _l{ float (l ):.0f} .arr 1.0D0 (FREE) -1 { prm } _l{ float (l ):.0f} \n " )
142
+ imod .array_io .writing ._save (
143
+ path ,
144
+ da ,
145
+ nodata = nodata ,
146
+ pattern = None ,
147
+ dtype = np .float32 ,
148
+ write = _write ,
149
+ )
150
+
151
+ # save anifile with data stored during _render
152
+ with open (directory / self .anifile , "w" ) as f :
153
+ for l in range (1 , self .nlayer + 1 ):
154
+ for prm in ["factor" , "angle" ]:
155
+ da = self .dataset [prm ]
156
+ if "layer" in da .coords and "y" in da .coords and "x" in da .coords :
157
+ a = da .sel (layer = l ).values
158
+ if not np .all (a == a [0 ][0 ]):
159
+ f .write (
160
+ f"open/close { self .rendir .as_posix ()} /{ prm } _l{ float (l ):.0f} .arr 1.0D0 (FREE) -1 { prm } _l{ float (l ):.0f} \n "
161
+ )
162
+ else :
163
+ f .write (
164
+ f"constant { float (a [0 ][0 ]):.5f} { prm } _l{ float (l ):.0f} \n "
165
+ )
141
166
else :
142
- f .write (f"constant { float (a [0 ][0 ]):.5f} { prm } _l{ float (l ):.0f} \n " )
167
+ f .write (
168
+ f"constant { float (self .dataset [prm ].values ):.5f} { prm } _l{ float (l ):.0f} \n "
169
+ )
143
170
144
171
def _pkgcheck (self , ibound = None ):
145
172
pass
0 commit comments