Skip to content

Commit 1b6587c

Browse files
committed
Merge remote-tracking branch 'origin/master' into solve_mp
2 parents 3ad7aec + b62a03d commit 1b6587c

9 files changed

+785
-120
lines changed

Diff for: notebooks/test_polygon_areasink.ipynb

+169
Large diffs are not rendered by default.

Diff for: notebooks/timml_notebook0_sol.ipynb

+67-78
Large diffs are not rendered by default.

Diff for: notebooks/timml_notebook3_3D_sol.ipynb

+384
Large diffs are not rendered by default.

Diff for: timml/__init__.py

+21-21
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,36 @@
1212
# --version number
1313
__name__ = "timml"
1414
__author__ = "Mark Bakker"
15-
from .version import __version__
16-
17-
# Import all classes and functions
18-
from .model import ModelMaq, Model3D, Model
19-
from .well import WellBase, Well, HeadWell
15+
from . import bessel
16+
from .areasink import CircAreaSink
2017
from .constant import Constant, ConstantStar
21-
from .linesink import (
22-
LineSinkBase,
23-
HeadLineSinkZero,
24-
HeadLineSink,
25-
LineSinkDitch,
26-
HeadLineSinkString,
27-
LineSinkDitchString,
28-
HeadLineSinkContainer,
29-
)
18+
from .inhomogeneity import BuildingPit, PolygonInhom3D, PolygonInhomMaq
19+
from .inhomogeneity1d import StripInhom3D, StripInhomMaq
3020
from .linedoublet import (
3121
ImpLineDoublet,
3222
ImpLineDoubletString,
3323
LeakyLineDoublet,
3424
LeakyLineDoubletString,
3525
)
36-
from .areasink import CircAreaSink
37-
from .inhomogeneity import PolygonInhomMaq, BuildingPit
38-
from .inhomogeneity1d import StripInhomMaq, StripInhom3D
39-
from .uflow import Uflow
40-
from .trace import timtraceline, timtracelines
41-
from .linesink1d import LineSink1D, HeadLineSink1D
4226
from .linedoublet1d import ImpLineDoublet1D, LeakyLineDoublet1D
27+
from .linesink import (
28+
HeadLineSink,
29+
HeadLineSinkContainer,
30+
HeadLineSinkString,
31+
HeadLineSinkZero,
32+
LineSinkBase,
33+
LineSinkDitch,
34+
LineSinkDitchString,
35+
)
36+
from .linesink1d import HeadLineSink1D, LineSink1D
37+
38+
# Import all classes and functions
39+
from .model import Model, Model3D, ModelMaq
4340
from .stripareasink import StripAreaSink
44-
from . import bessel
41+
from .trace import timtraceline, timtracelines
42+
from .uflow import Uflow
43+
from .version import __version__
44+
from .well import HeadWell, Well, WellBase
4545

4646
__all__ = [s for s in dir() if not s.startswith("_")]
4747

Diff for: timml/areasink.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ def disvecinf(self, x, y, aq=None):
114114
else:
115115
r = np.sqrt((x - self.xc) ** 2 + (y - self.yc) ** 2)
116116
if r <= self.R:
117-
raise 'CircAreaSink should add constant inside inhomogeneity'
117+
raise ValueError(
118+
"CircAreaSink should be either fully inside or fully "
119+
"outside of an inhomogeneity"
120+
)
118121
return rv
119122

120123
def K1RI0r(self, rin):
@@ -185,7 +188,7 @@ def I1RK1r(self, rin):
185188
rv[~self.islarge * index] = self.i1Roverlab[index] * k1(r)
186189
return rv
187190

188-
def qztop(self, x, y):
191+
def qztop(self, x, y, aq):
189192
rv = 0.0
190193
if np.sqrt((x - self.xc) ** 2 + (y - self.yc) ** 2) <= self.R:
191194
rv = -self.parameters[0, 0] # minus cause the parameter is the infiltration rate

Diff for: timml/constant.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,4 @@ def potentiallayers(self, x, y, layers, aq=None):
181181
def disvecinf(self, x, y, aq=None):
182182
if aq is None: aq = self.model.aq.find_aquifer_data(x, y)
183183
rv = np.zeros((2, 1, aq.naq))
184-
return rv
184+
return rv

Diff for: timml/element.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,15 @@ def storeinput(self,frame):
111111
#def stoptrace(self, xyz, layer, ltype, step, direction):
112112
# return False, 0
113113

114-
def changetrace(self, xyzt1, xyzt2, aq, layer, ltype, modellayer, direction, hstepmax):
114+
def changetrace(self, xyzt1, xyzt2, aq, layer, ltype, modellayer,
115+
direction, hstepmax):
115116
changed = False
116117
terminate = False
117118
xyztnew = 0
118119
message = None
119120
return changed, terminate, xyztnew, message
120121

121-
def qztop(self, x, y):
122+
def qztop(self, x, y, aq):
122123
# given flux at top of aquifer system (as for area-sinks)
123124
return 0
124125

Diff for: timml/inhomogeneity.py

+134-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import numpy as np
22
import inspect # Used for storing the input
33
from .aquifer import AquiferData
4-
from .aquifer_parameters import param_maq
4+
from .aquifer_parameters import param_maq, param_3d
5+
from .element import Element
56
from .constant import ConstantInside, ConstantStar
67
from .intlinesink import IntHeadDiffLineSink, IntFluxDiffLineSink, IntFluxLineSink
78
from .controlpoints import controlpoints
@@ -12,15 +13,15 @@
1213
class PolygonInhom(AquiferData):
1314
tiny = 1e-8
1415

15-
def __init__(self, model, xy, kaq, c, z, npor, ltype, hstar,
16-
order, ndeg, recharge):
16+
def __init__(self, model, xy, kaq, c, z, npor, ltype, hstar, N,
17+
order, ndeg):
1718
# All input variables except model should be numpy arrays
1819
# That should be checked outside this function):
1920
AquiferData.__init__(self, model, kaq, c, z, npor, ltype)
2021
self.order = order
2122
self.ndeg = ndeg
2223
self.hstar = hstar
23-
self.recharge = recharge
24+
self.N = N
2425
self.inhom_number = self.model.aq.add_inhom(self)
2526
self.z1, self.z2 = compute_z1z2(xy)
2627
zcenter = np.sum(self.z1) / len(self.z1) # center of inhom
@@ -30,9 +31,11 @@ def __init__(self, model, xy, kaq, c, z, npor, ltype, hstar,
3031
Zin = 1e-6j
3132
Zout = -1e-6j
3233
self.zcin = 0.5 * (self.z2 - self.z1) * Zin + 0.5 * (
33-
self.z1 + self.z2) # points at center on inside
34+
self.z1 + self.z2) # point at center on inside
3435
self.zcout = 0.5 * (self.z2 - self.z1) * Zout + 0.5 * (
35-
self.z1 + self.z2) # points at center on outside
36+
self.z1 + self.z2) # point at center on outside
37+
self.zcenter = np.mean(self.z1)
38+
self.xcenter, self.ycenter = self.zcenter.real, self.zcenter.imag
3639
self.x = np.hstack((self.z1.real, self.z2[-1].real))
3740
self.y = np.hstack((self.z1.imag, self.z2[-1].imag))
3841
self.xmin = min(self.x)
@@ -83,9 +86,9 @@ def create_elements(self):
8386
if aqin.ltype[0] == 'a': # add constant on inside
8487
c = ConstantInside(self.model, self.zcin.real, self.zcin.imag)
8588
c.inhomelement = True
86-
if self.recharge is not None:
87-
AreaSinkInhom(self.model, self.xcenter, self.ycenter, recharge=self.recharge, \
88-
layer=0, aqin=self)
89+
if self.N is not None:
90+
a = AreaSinkInhom(self.model, self.N, self.xcenter, aq=aqin)
91+
a.inhomelement = True
8992
if aqin.ltype[0] == 'l':
9093
assert self.hstar is not None, 'Error: hstar needs to be set'
9194
c = ConstantStar(self.model, self.hstar, aq=aqin)
@@ -101,9 +104,8 @@ class PolygonInhomMaq(PolygonInhom):
101104
model to which the element is added
102105
xy : array or list
103106
list or array of (x,y) pairs of coordinates of corners of the
104-
inhomogeneity
105-
polygonal boundary is automatically closed (so first point
106-
is not repeated)
107+
inhomogeneity. polygonal boundary is automatically closed (so first
108+
point is not repeated)
107109
kaq : float, array or list
108110
hydraulic conductivity of each aquifer from the top down
109111
if float, hydraulic conductivity is the same in all aquifers
@@ -128,6 +130,9 @@ class PolygonInhomMaq(PolygonInhom):
128130
semi-confined ('semi')
129131
hstar : float or None (default is None)
130132
head value above semi-confining top, only read if topboundary='semi'
133+
N : float or None (default is None)
134+
infiltration rate (L/T) inside inhomogeneity. Only possible if
135+
topboundary='conf'
131136
order : int
132137
polynomial order of flux along each segment
133138
ndeg : int
@@ -138,12 +143,81 @@ class PolygonInhomMaq(PolygonInhom):
138143

139144
tiny = 1e-8
140145

141-
def __init__(self, model, xy, kaq=1, z=[1, 0], c=[], npor=0.3, topboundary='conf',
142-
hstar=None, order=3, ndeg=3, recharge=None):
146+
def __init__(self, model, xy, kaq=1, z=[1, 0], c=[], npor=0.3,
147+
topboundary='conf', hstar=None, N=None, order=3, ndeg=3):
148+
if N is not None:
149+
assert topboundary[:4] == 'conf', \
150+
"Error: infiltration can only be added if topboundary='conf'"
143151
self.storeinput(inspect.currentframe())
144152
kaq, c, npor, ltype, = param_maq(kaq, z, c, npor, topboundary)
145153
PolygonInhom.__init__(self, model, xy, kaq, c, z, npor, ltype,
146-
hstar, order, ndeg, recharge)
154+
hstar, N, order, ndeg)
155+
156+
class PolygonInhom3D(PolygonInhom):
157+
"""
158+
Model3D Class to create a multi-layer model object consisting of
159+
many aquifer layers. The resistance between the layers is computed
160+
from the vertical hydraulic conductivity of the layers.
161+
162+
Parameters
163+
----------
164+
model : Model object
165+
model to which the element is added
166+
xy : array or list
167+
list or array of (x,y) pairs of coordinates of corners of the
168+
inhomogeneity. polygonal boundary is automatically closed (so first
169+
point is not repeated)
170+
kaq : float, array or list
171+
hydraulic conductivity of each layer from the top down
172+
if float, hydraulic conductivity is the same in all aquifers
173+
z : array or list
174+
elevation of top of system followed by bottoms of all layers
175+
from the top down
176+
bottom of layer is automatically equal to top of layer below it
177+
length is number of aquifer layers + 1
178+
kzoverkh : float
179+
vertical anisotropy ratio vertical k divided by horizontal k
180+
if float, value is the same for all layers
181+
length is number of layers
182+
npor : float, array or list
183+
porosity of all aquifer layers
184+
from the top down
185+
if float, porosity is the same for all layers
186+
if topboundary='conf': length is number of layers
187+
if topboundary='semi': length is number of layers + 1
188+
topboundary : string, 'conf' or 'semi' (default is 'conf')
189+
indicating whether the top is confined ('conf') or
190+
semi-confined ('semi')
191+
topres : float
192+
resistance of top semi-confining layer (read if topboundary='semi')
193+
topthick: float
194+
thickness of top semi-confining layer (read if topboundary='semi')
195+
hstar : float or None (default is None)
196+
head value above semi-confining top (read if topboundary='semi')
197+
N : float or None (default is None)
198+
infiltration rate (L/T) inside inhomogeneity. Only possible if
199+
topboundary='conf'
200+
order : int
201+
polynomial order of flux along each segment
202+
ndeg : int
203+
number of points used between two segments to numerically
204+
integrate normal discharge
205+
206+
"""
207+
208+
def __init__(self, model, xy, kaq=1, z=[1, 0], kzoverkh=1, npor=0.3,
209+
topboundary='conf', topres=0, topthick=0, hstar=0,
210+
N=None, order=3, ndeg=3):
211+
if N is not None:
212+
assert topboundary[:4] == 'conf', \
213+
"Error: infiltration can only be added if topboundary='conf'"
214+
self.storeinput(inspect.currentframe())
215+
kaq, c, npor, ltype = param_3d(kaq, z, kzoverkh, npor, topboundary,
216+
topres)
217+
if topboundary == 'semi':
218+
z = np.hstack((z[0] + topthick, z))
219+
PolygonInhom.__init__(self, model, xy, kaq, c, z, npor, ltype,
220+
hstar, N, order, ndeg)
147221

148222

149223
def compute_z1z2(xy):
@@ -310,3 +384,48 @@ def create_elements(self):
310384
assert self.hstar is not None, 'Error: hstar needs to be set'
311385
c = ConstantStar(self.model, self.hstar, aq=aqin)
312386
c.inhomelement = True
387+
388+
class AreaSinkInhom(Element):
389+
def __init__(self, model, N, xc, \
390+
name='AreaSinkInhom', label=None, aq=None):
391+
Element.__init__(self, model, nparam=1, nunknowns=0, layers=0, \
392+
name=name, label=label)
393+
self.N = N
394+
self.xc = xc # x-center of area-sink
395+
#self.nparam = 1 # Defined here and not in Element as other elements can have multiple parameters per layers
396+
#self.nunknowns = 0
397+
self.aq = aq
398+
self.model.add_element(self)
399+
400+
def __repr__(self):
401+
return self.name
402+
403+
def initialize(self):
404+
assert self.aq is not None, 'Error: no aquifer passed'
405+
self.aq.add_element(self)
406+
self.plabsq = self.aq.coef[self.layers, 1:] * self.aq.lab[1:] ** 2
407+
self.parameters = np.atleast_2d(self.N)
408+
409+
def potinf(self, x, y, aq=None):
410+
if aq is None: aq = self.model.aq.find_aquifer_data(x, y)
411+
rv = np.zeros((1, aq.naq))
412+
if aq == self.aq:
413+
rv[0, 0] = -0.5 * (x - self.xc) ** 2
414+
rv[0, 1:] = self.plabsq
415+
return rv
416+
417+
def disvecinf(self, x, y, aq=None):
418+
if aq is None: aq = self.model.aq.find_aquifer_data(x, y)
419+
rv = np.zeros((2, self.nparam, aq.naq))
420+
if aq == self.aq:
421+
rv[0, 0, 0] = x - self.xc
422+
return rv
423+
424+
def qztop(self, x, y, aq=None):
425+
if aq is None: aq = self.model.aq.find_aquifer_data(x, y)
426+
rv = 0.0
427+
if aq == self.aq:
428+
rv = -self.parameters[0, 0]
429+
return rv
430+
431+

Diff for: timml/model.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def qztop(self, x, y, aq=None):
103103
rv = 0.0
104104
if aq.ltype[0] == 'a': # otherwise recharge cannot be added
105105
for e in aq.elementlist:
106-
rv += e.qztop(x, y)
106+
rv += e.qztop(x, y, aq)
107107
return rv
108108

109109
def head(self, x, y, layers=None, aq=None):

0 commit comments

Comments
 (0)