Skip to content

Commit 51ad165

Browse files
authored
Add LeakyBuildingPit element (#70)
* Add LeakyBuildingPit element - add LeakyIntHeadDiffEquation mixin class - add LeakyIntHeadDiffLineSink element for leaky pit boundaries with leaky walls - add LeakyBuildingPit element, inherits from BuildingPit but adds resistance term - add notebook testing LeakyBuildingPit allow BuildingPit to be placed in models with 1 layer isort __init__ * use self.disveclayers for RHS * fix #71 - resfac fixed in LineDoublet(1d) - resfac fixed in LineSink(1d) * update leaky building pit nb * allow passing kwargs to contour func * add integration of normal flux along line - add disvecnorm: calculate disvec normal to angle theta - intdisvecnorm: quad/legendre integration of disvecnorm - add quad integrand function for integrating from 0->r * rename *disvecnorm to *normflux - improve code and docstring * fix overwrite of label kwarg * add intnormflux notebook * add intnormflux notebook * add quad_vec import * improve normal flux nb * optimize code a bit and reorder coordinates * update normalflux nb * docstrings normflux functions * add circular leaky building pit notebook * rename to IntLeakyWallEquation * rename to IntLeakyWallEquation * delete nb * remove py3.7 testing, add 3.10 and 3.11 * fix buildingpit nb * add shapely to ci reqs
1 parent 6f682eb commit 51ad165

15 files changed

+1442
-111
lines changed

Diff for: .github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
python-version: [3.7, 3.8, 3.9]
17+
python-version: [3.8, 3.9, '3.10', '3.11']
1818
steps:
1919
- uses: actions/checkout@v3
2020

Diff for: notebooks/BuildingPit.ipynb

+50-64
Large diffs are not rendered by default.

Diff for: notebooks/circular_buildingpit.ipynb

+617
Large diffs are not rendered by default.

Diff for: notebooks/normal_flux.ipynb

+427
Large diffs are not rendered by default.

Diff for: requirements.ci.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ scipy>=0.19
33
numba>=0.39
44
matplotlib>=2.0,<3.5.0
55
pytest>=4.6
6-
jupyter>=1.0.0
6+
jupyter>=1.0.0
7+
shapely

Diff for: timml/__init__.py

+23-18
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,41 @@
1212
# --version number
1313
__name__ = "timml"
1414
__author__ = "Mark Bakker"
15-
from .version import __version__
15+
from . import bessel
1616

1717
# Import all classes and functions
18-
from .model import ModelMaq, Model3D, Model
19-
from .well import WellBase, Well, HeadWell
18+
from .circareasink import CircAreaSink
2019
from .constant import Constant, ConstantStar
21-
from .linesink import (
22-
LineSinkBase,
23-
HeadLineSinkZero,
24-
HeadLineSink,
25-
LineSinkDitch,
26-
HeadLineSinkString,
27-
LineSinkDitchString,
28-
HeadLineSinkContainer,
20+
from .inhomogeneity import (
21+
BuildingPit,
22+
LeakyBuildingPit,
23+
PolygonInhom3D,
24+
PolygonInhomMaq,
2925
)
26+
from .inhomogeneity1d import StripInhom3D, StripInhomMaq
3027
from .linedoublet import (
3128
ImpLineDoublet,
3229
ImpLineDoubletString,
3330
LeakyLineDoublet,
3431
LeakyLineDoubletString,
3532
)
36-
from .circareasink import CircAreaSink
37-
from .inhomogeneity import PolygonInhomMaq, PolygonInhom3D, BuildingPit
38-
from .inhomogeneity1d import StripInhomMaq, StripInhom3D
39-
from .uflow import Uflow
40-
from .trace import timtraceline, timtracelines
41-
from .linesink1d import LineSink1D, HeadLineSink1D
4233
from .linedoublet1d import ImpLineDoublet1D, LeakyLineDoublet1D
34+
from .linesink import (
35+
HeadLineSink,
36+
HeadLineSinkContainer,
37+
HeadLineSinkString,
38+
HeadLineSinkZero,
39+
LineSinkBase,
40+
LineSinkDitch,
41+
LineSinkDitchString,
42+
)
43+
from .linesink1d import HeadLineSink1D, LineSink1D
44+
from .model import Model, Model3D, ModelMaq
4345
from .stripareasink import StripAreaSink
44-
from . import bessel
46+
from .trace import timtraceline, timtracelines
47+
from .uflow import Uflow
48+
from .version import __version__
49+
from .well import HeadWell, Well, WellBase
4550

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

Diff for: timml/equation.py

+88
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,91 @@ def equation(self):
321321
rhs[istart:istart + self.nlayers] -= flux
322322

323323
return mat, rhs
324+
325+
326+
class IntLeakyWallEquation:
327+
def equation(self):
328+
mat = np.empty((self.nunknowns, self.model.neq))
329+
rhs = np.zeros(self.nunknowns) # Needs to be initialized to zero
330+
for icp in range(self.ncp):
331+
istart = icp * self.nlayers
332+
ieq = 0
333+
for e in self.model.elementlist:
334+
if e.nunknowns > 0:
335+
flux = self.intflux(
336+
e.disvecinflayers,
337+
self.xc[icp],
338+
self.yc[icp],
339+
self.xc[icp + 1],
340+
self.yc[icp + 1],
341+
self.layers,
342+
aq=self.aq,
343+
)
344+
headin = (
345+
self.intpot(
346+
e.potinflayers,
347+
self.xcin[icp],
348+
self.ycin[icp],
349+
self.xcin[icp + 1],
350+
self.ycin[icp + 1],
351+
self.layers,
352+
aq=self.aqin,
353+
)
354+
/ self.aqin.Tcol[self.layers]
355+
)
356+
headout = (
357+
self.intpot(
358+
e.potinflayers,
359+
self.xcout[icp],
360+
self.ycout[icp],
361+
self.xcout[icp + 1],
362+
self.ycout[icp + 1],
363+
self.layers,
364+
aq=self.aqout,
365+
)
366+
/ self.aqout.Tcol[self.layers]
367+
)
368+
369+
mat[
370+
istart : istart + self.nlayers, ieq : ieq + e.nunknowns
371+
] = flux - self.resfac * (headin - headout)
372+
ieq += e.nunknowns
373+
else:
374+
flux = self.intflux(
375+
e.disveclayers,
376+
self.xc[icp],
377+
self.yc[icp],
378+
self.xc[icp + 1],
379+
self.yc[icp + 1],
380+
self.layers,
381+
aq=self.aq,
382+
)
383+
headin = (
384+
self.intpot(
385+
e.potentiallayers,
386+
self.xcin[icp],
387+
self.ycin[icp],
388+
self.xcin[icp + 1],
389+
self.ycin[icp + 1],
390+
self.layers,
391+
aq=self.aqin,
392+
)
393+
/ self.aqin.T[self.layers]
394+
)
395+
headout = (
396+
self.intpot(
397+
e.potentiallayers,
398+
self.xcout[icp],
399+
self.ycout[icp],
400+
self.xcout[icp + 1],
401+
self.ycout[icp + 1],
402+
self.layers,
403+
aq=self.aqout,
404+
)
405+
/ self.aqout.T[self.layers]
406+
)
407+
408+
rhs[istart : istart + self.nlayers] += (-flux +
409+
self.resfac * (headin - headout)
410+
)
411+
return mat, rhs

Diff for: timml/inhomogeneity.py

+92-18
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
from .aquifer_parameters import param_maq, param_3d
55
from .element import Element
66
from .constant import ConstantInside, ConstantStar
7-
from .intlinesink import IntHeadDiffLineSink, IntFluxDiffLineSink, IntFluxLineSink
8-
from .controlpoints import controlpoints
7+
from .intlinesink import (
8+
IntHeadDiffLineSink,
9+
IntFluxDiffLineSink,
10+
IntFluxLineSink,
11+
LeakyIntHeadDiffLineSink,
12+
)
913

1014
__all__ = ['PolygonInhomMaq']
1115

@@ -147,7 +151,8 @@ def __init__(self, model, xy, kaq=1, z=[1, 0], c=[], npor=0.3,
147151
kaq, c, npor, ltype, = param_maq(kaq, z, c, npor, topboundary)
148152
PolygonInhom.__init__(self, model, xy, kaq, c, z, npor, ltype,
149153
hstar, N, order, ndeg)
150-
154+
155+
151156
class PolygonInhom3D(PolygonInhom):
152157
"""
153158
Model3D Class to create a multi-layer model object consisting of
@@ -332,7 +337,8 @@ def isinside(self, x, y):
332337
return rv
333338
angles = np.log(bigZmin1 / bigZplus1).imag
334339
angle = np.sum(angles)
335-
if angle > np.pi: rv = 1
340+
if angle > np.pi:
341+
rv = 1
336342
return rv
337343

338344
def create_elements(self):
@@ -357,20 +363,87 @@ def create_elements(self):
357363
order=self.order, ndeg=self.ndeg,
358364
label=None, addtomodel=True,
359365
aq=aqout, aqin=aqin, aqout=aqout)
366+
if len(self.nonimplayers) > 0:
367+
# use these conditions for layers without impermeable or leaky walls
368+
IntHeadDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
369+
x2=self.x[i + 1], y2=self.y[i + 1],
370+
layers=self.nonimplayers,
371+
order=self.order, ndeg=self.ndeg,
372+
label=None, addtomodel=True,
373+
aq=aqin, aqin=aqin, aqout=aqout)
374+
IntFluxDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
375+
x2=self.x[i + 1], y2=self.y[i + 1],
376+
layers=self.nonimplayers,
377+
order=self.order, ndeg=self.ndeg,
378+
label=None, addtomodel=True,
379+
aq=aqout, aqin=aqin, aqout=aqout)
380+
381+
if aqin.ltype[0] == 'a': # add constant on inside
382+
c = ConstantInside(self.model, self.zcin.real, self.zcin.imag)
383+
c.inhomelement = True
384+
if aqin.ltype[0] == 'l':
385+
assert self.hstar is not None, 'Error: hstar needs to be set'
386+
c = ConstantStar(self.model, self.hstar, aq=aqin)
387+
c.inhomelement = True
360388

361-
# use these conditions for layers without impermeable walls
362-
IntHeadDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
363-
x2=self.x[i + 1], y2=self.y[i + 1],
364-
layers=self.nonimplayers,
365-
order=self.order, ndeg=self.ndeg,
366-
label=None, addtomodel=True,
367-
aq=aqin, aqin=aqin, aqout=aqout)
368-
IntFluxDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
369-
x2=self.x[i + 1], y2=self.y[i + 1],
370-
layers=self.nonimplayers,
371-
order=self.order, ndeg=self.ndeg,
372-
label=None, addtomodel=True,
373-
aq=aqout, aqin=aqin, aqout=aqout)
389+
390+
class LeakyBuildingPit(BuildingPit):
391+
def __init__(self, model, xy, kaq=1, z=[1, 0], c=[], npor=0.3,
392+
topboundary="conf", hstar=None, order=3,
393+
ndeg=3, layers=[0], res=np.inf):
394+
super().__init__(
395+
model=model,
396+
xy=xy,
397+
kaq=kaq,
398+
z=z,
399+
c=c,
400+
npor=npor,
401+
topboundary=topboundary,
402+
hstar=hstar,
403+
order=order,
404+
ndeg=ndeg,
405+
layers=layers,
406+
)
407+
self.res = res
408+
409+
def create_elements(self):
410+
aqin = self.model.aq.find_aquifer_data(self.zcin[0].real,
411+
self.zcin[0].imag)
412+
for i in range(self.Nsides):
413+
aqout = self.model.aq.find_aquifer_data(self.zcout[i].real,
414+
self.zcout[i].imag)
415+
if (aqout == self.model.aq) or (
416+
aqout.inhom_number > self.inhom_number):
417+
418+
# Conditions for layers with impermeable walls
419+
LeakyIntHeadDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
420+
x2=self.x[i + 1], y2=self.y[i + 1],
421+
res=self.res, layers=self.layers,
422+
order=self.order, ndeg=self.ndeg,
423+
label=None, addtomodel=True,
424+
aq=aqin, aqin=aqin, aqout=aqout)
425+
426+
LeakyIntHeadDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
427+
x2=self.x[i + 1], y2=self.y[i + 1],
428+
res=self.res, layers=self.layers,
429+
order=self.order, ndeg=self.ndeg,
430+
label=None, addtomodel=True,
431+
aq=aqout, aqin=aqin, aqout=aqout)
432+
433+
if len(self.nonimplayers) > 0:
434+
# use these conditions for layers without impermeable or leaky walls
435+
IntHeadDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
436+
x2=self.x[i + 1], y2=self.y[i + 1],
437+
layers=self.nonimplayers,
438+
order=self.order, ndeg=self.ndeg,
439+
label=None, addtomodel=True,
440+
aq=aqin, aqin=aqin, aqout=aqout)
441+
IntFluxDiffLineSink(self.model, x1=self.x[i], y1=self.y[i],
442+
x2=self.x[i + 1], y2=self.y[i + 1],
443+
layers=self.nonimplayers,
444+
order=self.order, ndeg=self.ndeg,
445+
label=None, addtomodel=True,
446+
aq=aqout, aqin=aqin, aqout=aqout)
374447

375448
if aqin.ltype[0] == 'a': # add constant on inside
376449
c = ConstantInside(self.model, self.zcin.real, self.zcin.imag)
@@ -379,7 +452,8 @@ def create_elements(self):
379452
assert self.hstar is not None, 'Error: hstar needs to be set'
380453
c = ConstantStar(self.model, self.hstar, aq=aqin)
381454
c.inhomelement = True
382-
455+
456+
383457
class AreaSinkInhom(Element):
384458
def __init__(self, model, N, xc, \
385459
name='AreaSinkInhom', label=None, aq=None):

Diff for: timml/intlinesink.py

+61-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import numpy as np
22
from .linesink import LineSinkHoBase
3-
from .equation import HeadDiffEquation2, DisvecDiffEquation2, IntDisVecEquation
3+
from .equation import (
4+
HeadDiffEquation2,
5+
DisvecDiffEquation2,
6+
IntDisVecEquation,
7+
IntLeakyWallEquation,
8+
)
49
from .controlpoints import controlpoints
510
# needed for testing
611
# from .equation import DisvecDiffEquation
@@ -178,3 +183,58 @@ def initialize(self):
178183

179184
def setparams(self, sol):
180185
self.parameters[:, 0] = sol
186+
187+
188+
class LeakyIntHeadDiffLineSink(LineSinkHoBase, IntLeakyWallEquation):
189+
"""Element to set numerically integrated head
190+
along linesink to equal to:
191+
Qnormal = H * (headin - headout) / res
192+
Used in LeakyBuildingPit element
193+
"""
194+
195+
def __init__(self, model, x1=-1, y1=0, x2=1, y2=0, res=np.inf, layers=None,
196+
order=0, ndeg=3, label=None, addtomodel=True, aq=None,
197+
aqin=None, aqout=None):
198+
if layers is None:
199+
layers = list(range(model.aq.naq))
200+
LineSinkHoBase.__init__(self, model, x1, y1, x2, y2, Qls=0,
201+
layers=layers, order=order,
202+
name='LeakyIntHeadDiffLineSink', label=label,
203+
addtomodel=addtomodel, aq=aq)
204+
self.res = res
205+
self.inhomelement = True
206+
self.ndeg = ndeg
207+
self.Xleg, self.wleg = np.polynomial.legendre.leggauss(self.ndeg)
208+
self.nunknowns = self.nparam
209+
self.aqin = aqin
210+
self.aqout = aqout
211+
212+
def initialize(self):
213+
LineSinkHoBase.initialize(self)
214+
215+
# recalculated with ncp - 1 points instead of ncp points
216+
self.xcin, self.ycin = controlpoints(self.ncp - 1, self.z1, self.z2,
217+
eps=1e-6, include_ends=True)
218+
self.xcout, self.ycout = controlpoints(self.ncp - 1, self.z1, self.z2,
219+
eps=-1e-6, include_ends=True)
220+
221+
if self.aqin is None:
222+
self.aqin = self.model.aq.find_aquifer_data(self.xcin[0],
223+
self.ycin[0])
224+
if self.aqout is None:
225+
self.aqout = self.model.aq.find_aquifer_data(self.xcout[0],
226+
self.ycout[0])
227+
# set control points x,y depending on which side of
228+
# the boundary the element is for
229+
if self.aq == self.aqin:
230+
self.xc = self.xcin
231+
self.yc = self.ycin
232+
if self.aq == self.aqout:
233+
self.xc = self.xcout
234+
self.yc = self.ycout
235+
236+
# set resistance factor
237+
self.resfac = self.aq.Haq[self.layers] / self.res
238+
239+
def setparams(self, sol):
240+
self.parameters[:, 0] = sol

Diff for: timml/linedoublet.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def initialize(self):
5656
eps=-1e-6)
5757
if self.aq is None:
5858
self.aq = self.model.aq.find_aquifer_data(self.xc[0], self.yc[0])
59-
self.resfac = self.aq.T[self.layers] / self.res
59+
self.resfac = self.aq.Haq[self.layers] / self.res
6060
if self.addtomodel:
6161
self.aq.add_element(self)
6262
self.parameters = np.empty((self.nparam, 1))

0 commit comments

Comments
 (0)