def __init__(self, z=None, c=None, kD=None, Sat=None, Saq=None, rw=0.1, R=1e4, top_aquitard=True): r = np.logspace(np.log10(rw), np.log10(R), int(10 * np.ceil(np.log10(R / rw)) + 1)) self.gr = Grid(r, [-0.5, 0.5], np.array(z), axial=True) self.top_aquitard = top_aquitard self.bot_aquitard =\ ( self.top_aquitard and len(c) > len(kD) ) or\ ((not self.top_aquitard) and len(c) == len(kD) ) assert self.gr.nz == len(c) + len(kD), 'nz != len(c) + len(kD)' assert self.gr.nz == len(Sat) + len(Saq), 'nz = len(Sat) + len(Saq)' self.c = np.array(c) self.kD = np.array(kD) self.Sat = np.array(Sat) self.Saq = np.array(Saq) self.kh = np.zeros(self.gr.nz) self.kv = np.zeros(self.gr.nz) if self.top_aquitard: self.kv[::2] = self.gr.dz[::2] / c self.kh[::2] = 0. self.kh[1::2] = self.kD / self.gr.dz[1::2] self.kv[1::2] = np.inf else: self.kh[::2] = self.kD / self.gr.dz[::2] self.kv[::2] = np.inf self.kv[1::2] = self.gr.dz[1::2] / c self.kh[1::2] = 0. if self.top_aquitard: self.kv[0] *= 0.5 if self.bot_aquitard: self.kv[-1] *= 0.5 self.iaquif = np.ones(self.gr.nz, dtype=bool) if self.top_aquitard: self.iaquif[::2] = False else: self.iaquif[1::2] = False self.iatard = np.logical_not(self.iaquif) self.Ss = np.zeros(self.gr.nz) self.Ss[self.iaquif] = self.Saq / self.gr.dz[self.iaquif] self.Ss[self.iatard] = self.Sat / self.gr.dz[self.iatard]
#%% Grid z = [layer[0]['ztop']] kh = [] kv = [] ss = [] for i in range(len(layer)): z.append(layer[i]['zbot']) kh.append(layer[i]['kh']) kv.append(layer[i]['kv']) ss.append(layer[i]['ss']) y = [-0.5, 0.5] r = np.hstack((0, *rwell, *rwand, np.logspace(0, 4, 101))) gr = Grid(r, y, z, axial=True) wand = np.zeros(gr.shape, dtype=bool) wand[AND(AND(AND(gr.XM > rwand[0], gr.XM < rwand[1]), gr.ZM < zwand[0]), gr.ZM > zwand[1])] = True well = np.zeros(gr.shape, dtype=bool) well[AND(AND(AND(gr.XM > rwell[0], gr.XM < rwell[1]), gr.ZM < zwell[0]), gr.ZM > zwell[1])] = True vloer = np.zeros(gr.shape, dtype=bool) vloer[AND(gr.XM < rwand[0], gr.ZM > -21.9)] = True #%% parameters Kh = gr.const(np.array(kh)) Kh[wand] = kwand
def __init__(self, mluxmlfile, tshift=0, Rmax=1e4, **kwargs): '''Return MLUtest, pumping test results based on mlu file. parameters ---------- tshift : float Delay imposed on measurement time (pump upstart delay) Rmax: float Duter radius of model where head is fixed. kwargs ------ Rw : float Well radius (not in mlu file) if None, use 2 * screen of well ''' mlu = Mluobj(mluxmlfile) aqSys = mlu.aqSys self.obswells = mlu.obsWells nz = len(aqSys.aquifs) + len(aqSys.atards) self.iaquif = np.ones(nz, dtype=bool) self.top_aquitard = aqSys.top_aquitard != 'absent' if self.top_aquitard: self.iaquif[::2] = False else: self.iaquif[1::2] = False self.iatard = np.logical_not(self.iaquif) rmin = kwargs.pop('Rw', 2 * mlu.wells[0].screen) # Piezometer data from xml file tmax = 0 tmin = np.inf x0, y0 = mlu.wells[0].x, mlu.wells[0].y r = rmin rmax = rmin self.names = list() self.points = list() self.ow_aquif = list() for ow in mlu.obsWells: self.names.append(ow.name) r = max(rmin, np.sqrt((x0 - ow.x)**2 + (y0 - ow.y)**2)) rmax = max(rmax, r) tmin = min(tmin, ow.data[0, 0]) tmax = max(tmax, ow.data[-1, 0]) self.ow_aquif.append(ow.layer - 1) self.points.append((r, mlu.aqSys.aquifs[ow.layer - 1].zmid)) # attribute discharge over screened aquifers Qs = np.zeros(len(mlu.aqSys.aquifs)) kDtot = 0. for iaq, (screened, aq) in enumerate(zip(mlu.wells[0].screens, mlu.aqSys.aquifs)): if screened == '1': kDtot += aq.T Qs[iaq] = aq.T Qs *= mlu.wells[0].Q / kDtot # radial grid, general max r. r = np.hstack( (0, np.logspace(np.log10(rmin), np.log10(Rmax), int(10 * np.ceil(np.log10(Rmax / rmin)) + 1)))) zf = np.array([(aquif.ztop, aquif.zbot) for aquif in aqSys.aquifs]) za = np.array([(atard.ztop, atard.zbot) for atard in aqSys.atards]) z = np.unique(np.hstack((zf[:, 0], zf[:, 1], za[:, 0], za[:, 1]))) self.gr = Grid(r, [-0.5, 0.5], z, axial=True) # sufficiently detailed times for graphs self.t = np.logspace(np.log10(tmin), np.log10(tmax), 60) self.t = np.unique(np.hstack((0., self.t))) # start at 0 # conductivities kD = np.array([aq.T for aq in aqSys.aquifs]) c = np.array([at.c for at in aqSys.atards]) kh = np.zeros(self.gr.nz) kh[self.iaquif] = kD / self.gr.dz[self.iaquif] kv = np.ones(self.gr.nz) * 1e6 # just large kv[self.iatard] = self.gr.dz[self.iatard] / c if self.iatard[0]: kv[0] /= 2. if self.iatard[-1]: kv[-1] /= 2. self.Kh = self.gr.const(kh) self.Kv = self.gr.const(kv) self.Kh[self.iaquif, :, 0] = 100. # no resistance inside well # storativities (both aquitards and aquifers) Saq = np.array([aq.S for aq in aqSys.aquifs]) Sat = np.array([at.S for at in aqSys.atards]) ss = np.zeros(self.gr.nz) ss[self.iaquif] = Saq / self.gr.dz[self.iaquif] ss[self.iatard] = Sat / self.gr.dz[self.iatard] self.Ss = self.gr.const(ss) kd_tot = 0. kd = np.zeros_like(kD) for i, scr in enumerate(mlu.wells[0].screens): if scr == '1': kd[i] = kD[i] kd_tot += kD[i] self.Q = mlu.wells[0].Q * kd / kd_tot self.FQ = self.gr.const(0) self.FQ[self.iaquif, 0, 0] = self.Q self.HI = self.gr.const(0.) self.IBOUND = self.gr.const(1, dtype=int) self.IBOUND[:, :, -1] = -1 if self.iatard[0]: self.IBOUND[0] = -1 if self.iatard[-1]: self.IBOUND[-1] = -1 # TODO: verify that the inflow over the outer boundary is < 5% or so. self.out = fdm3t(gr=self.gr, t=self.t, kxyz=(self.Kh, self.Kh, self.Kv), Ss=self.Ss, FQ=self.FQ, HI=self.HI, IBOUND=self.IBOUND, epsilon=1.0) ax = mlu.plot_drawdown(mlu.obsNames, tshift=tshift, marker='.', linestyle='none', **kwargs) #hantush(...) r = np.array(self.points)[:, 0] self.dd = hantushn(Q=self.Q, r=r, t=self.t[1:], Saq=Saq, Sat=Sat, c=c, T=kD, N=8) self.show(xscale='log', ax=ax, labels=self.names)
class MLU_ptest: def __init__(self, mluxmlfile, tshift=0, Rmax=1e4, **kwargs): '''Return MLUtest, pumping test results based on mlu file. parameters ---------- tshift : float Delay imposed on measurement time (pump upstart delay) Rmax: float Duter radius of model where head is fixed. kwargs ------ Rw : float Well radius (not in mlu file) if None, use 2 * screen of well ''' mlu = Mluobj(mluxmlfile) aqSys = mlu.aqSys self.obswells = mlu.obsWells nz = len(aqSys.aquifs) + len(aqSys.atards) self.iaquif = np.ones(nz, dtype=bool) self.top_aquitard = aqSys.top_aquitard != 'absent' if self.top_aquitard: self.iaquif[::2] = False else: self.iaquif[1::2] = False self.iatard = np.logical_not(self.iaquif) rmin = kwargs.pop('Rw', 2 * mlu.wells[0].screen) # Piezometer data from xml file tmax = 0 tmin = np.inf x0, y0 = mlu.wells[0].x, mlu.wells[0].y r = rmin rmax = rmin self.names = list() self.points = list() self.ow_aquif = list() for ow in mlu.obsWells: self.names.append(ow.name) r = max(rmin, np.sqrt((x0 - ow.x)**2 + (y0 - ow.y)**2)) rmax = max(rmax, r) tmin = min(tmin, ow.data[0, 0]) tmax = max(tmax, ow.data[-1, 0]) self.ow_aquif.append(ow.layer - 1) self.points.append((r, mlu.aqSys.aquifs[ow.layer - 1].zmid)) # attribute discharge over screened aquifers Qs = np.zeros(len(mlu.aqSys.aquifs)) kDtot = 0. for iaq, (screened, aq) in enumerate(zip(mlu.wells[0].screens, mlu.aqSys.aquifs)): if screened == '1': kDtot += aq.T Qs[iaq] = aq.T Qs *= mlu.wells[0].Q / kDtot # radial grid, general max r. r = np.hstack( (0, np.logspace(np.log10(rmin), np.log10(Rmax), int(10 * np.ceil(np.log10(Rmax / rmin)) + 1)))) zf = np.array([(aquif.ztop, aquif.zbot) for aquif in aqSys.aquifs]) za = np.array([(atard.ztop, atard.zbot) for atard in aqSys.atards]) z = np.unique(np.hstack((zf[:, 0], zf[:, 1], za[:, 0], za[:, 1]))) self.gr = Grid(r, [-0.5, 0.5], z, axial=True) # sufficiently detailed times for graphs self.t = np.logspace(np.log10(tmin), np.log10(tmax), 60) self.t = np.unique(np.hstack((0., self.t))) # start at 0 # conductivities kD = np.array([aq.T for aq in aqSys.aquifs]) c = np.array([at.c for at in aqSys.atards]) kh = np.zeros(self.gr.nz) kh[self.iaquif] = kD / self.gr.dz[self.iaquif] kv = np.ones(self.gr.nz) * 1e6 # just large kv[self.iatard] = self.gr.dz[self.iatard] / c if self.iatard[0]: kv[0] /= 2. if self.iatard[-1]: kv[-1] /= 2. self.Kh = self.gr.const(kh) self.Kv = self.gr.const(kv) self.Kh[self.iaquif, :, 0] = 100. # no resistance inside well # storativities (both aquitards and aquifers) Saq = np.array([aq.S for aq in aqSys.aquifs]) Sat = np.array([at.S for at in aqSys.atards]) ss = np.zeros(self.gr.nz) ss[self.iaquif] = Saq / self.gr.dz[self.iaquif] ss[self.iatard] = Sat / self.gr.dz[self.iatard] self.Ss = self.gr.const(ss) kd_tot = 0. kd = np.zeros_like(kD) for i, scr in enumerate(mlu.wells[0].screens): if scr == '1': kd[i] = kD[i] kd_tot += kD[i] self.Q = mlu.wells[0].Q * kd / kd_tot self.FQ = self.gr.const(0) self.FQ[self.iaquif, 0, 0] = self.Q self.HI = self.gr.const(0.) self.IBOUND = self.gr.const(1, dtype=int) self.IBOUND[:, :, -1] = -1 if self.iatard[0]: self.IBOUND[0] = -1 if self.iatard[-1]: self.IBOUND[-1] = -1 # TODO: verify that the inflow over the outer boundary is < 5% or so. self.out = fdm3t(gr=self.gr, t=self.t, kxyz=(self.Kh, self.Kh, self.Kv), Ss=self.Ss, FQ=self.FQ, HI=self.HI, IBOUND=self.IBOUND, epsilon=1.0) ax = mlu.plot_drawdown(mlu.obsNames, tshift=tshift, marker='.', linestyle='none', **kwargs) #hantush(...) r = np.array(self.points)[:, 0] self.dd = hantushn(Q=self.Q, r=r, t=self.t[1:], Saq=Saq, Sat=Sat, c=c, T=kD, N=8) self.show(xscale='log', ax=ax, labels=self.names) def show(self, **kwargs): #points should be r, z tuples rz = np.array(self.points) if rz.shape[1] == 2: rz = np.hstack((rz[:, 0:1], np.zeros((rz.shape[0], 1)), rz[:, 1:2])) LRC = self.gr.lrc(rz[:, 0], rz[:, 1], rz[:, -1]) # squeeze out axis 2 (y) interpolator = interp1d(self.gr.xm, self.out['Phi'][:, :, 0, :], axis=2) # interpolate for all obswells at once along r-axis phi_t = interpolator(rz[:, 0]) # fancy indexing to select the correct col-row (r-z) combinations Ipnt = np.arange(phi_t.shape[-1], dtype=int) phi_t = phi_t[:, LRC[:, 0], Ipnt] # Add Hantushn points aqNr = np.array(self.ow_aquif) ddHant = self.dd[:, aqNr, np.arange(self.dd.shape[-1], dtype=int)] ax = kwargs.pop('ax', None) if ax is None: fig, ax = plt.subplots() ax.set_title('Ptest testing') ax.set_xlabel('t [d]') ax.set_ylabel('drawdown [m]') ax.grid(True) ax.invert_yaxis() xscale = kwargs.pop('xscale', None) yscale = kwargs.pop('yscale', None) grid = kwargs.pop('grid', None) if xscale: ax.set_xscale(xscale) if yscale: ax.set_yscale(yscale) if grid: ax.grid(grid) labels = kwargs.pop('labels', [''] * phi_t.shape[1]) # plot fdm3t lines for fi, label, color in zip(phi_t.T, labels, colors): ax.plot(self.t[1:], fi[1:], color=color, label=label + ' fdm', ls='--', marker='+', **kwargs) # plot hantush lines for fi, label, color in zip(ddHant.T, labels, colors): ax.plot(self.t[1:], fi, color=color, label=label + ' anal', lw=2, **kwargs) ax.legend(loc='best') return ax
mptest = MLU_ptest(xml_file, Rw=0.13, tshift=0.) #%% from ground up: =gat_boomse_klei using ptest ========================== # Firstly define the grid Rw = 0.13 # m, well borehole radius Rmax = 10000. # m, extent of model (should be large enough) r = np.hstack((0, np.logspace(np.log10(Rw), np.log10(Rmax), int(np.ceil(10 * np.log10(Rmax / Rw) + 1))))) dz = np.array( [9., 16., 0.01, 3.79, 3.7, 1., 3.7, 1., 3.8, 1., 1.3, 5., 3.4, 6.3]) z = np.hstack((0, -np.cumsum(dz))) gr = Grid(r, [-0.5, 0.5], z, axial=True) # Set soil parameters kD = np.array([48, 11.37, 0.13, 0.15, 0.16, 0.75, 31.5]) c = np.array([1500, 1.e-2, 2.85, 12.3, 76., 26., 56.7]) Sat = np.array([ 0., 0., 0., 0., 0., 0., 0., ]) Saq = np.array([1.e-04, 1.e-4, 1.e-5, 1.e-5, 1.e-5, 1.e-5, 1.e-5]) top_aquitard = True # whether the top layer is a top aquitard
top_aquitad = True kwand = 1e-3 # m/d rwand = (64.95, 65.05) zwand = (0., -45.) rwell = (rwand[0] - 0.5, rwand[0]) zwell = (-36., -42. ) hwell = -20. # c1 kD1 c2 kD2 c3 kD3 c4 kD4 z = [0, -2., -22., -36., -42, -42.5, -45, -50, -60] y = [ -0.5, 0.5] r = np.hstack((0, *rwell, rwand[0], rwand[1], *rwand, np.logspace(0, 4, 101))) gr = Grid(r, y, z, axial=True) wand = np.zeros(gr.shape, dtype=bool) wand[AND( AND( AND(gr.XM > rwand[0], gr.XM < rwand[1]), gr.ZM < zwand[0]), gr.ZM > zwand[1])] = True well = np.zeros(gr.shape, dtype=bool) well[AND( AND( AND(gr.XM > rwell[0], gr.XM < rwell[1]), gr.ZM < zwell[0]), gr.ZM > zwell[1])] = True vloer = np.zeros(gr.shape, dtype=bool) vloer[AND(gr.XM < rwand[0], gr.ZM > -22)] = True
east = np.hstack((np.zeros((gr.ny, 1), dtype=bool), west[:,:-1])) north= np.vstack((south[1:,:], np.zeros((1, gr.nx), dtype=bool))) pairs = np.array([np.hstack((gr.NOD[0][west], gr.NOD[0][north])), np.hstack((gr.NOD[0][east], gr.NOD[0][south]))]).T return pairs if __name__ == '__main__': x = np.linspace(-100., 100., 21) y = np.linspace(-100., 100., 21) z = [0, -10, -20] gr = Grid(x, y, z) polygon = np.array([(23, 15), (45, 50.), (10., 81.), (-5., 78), (-61., 51.), (-31., 11.), (-6., -4.), (-42., -20.), (-50., -63.), (-7., -95.), (31., -80.), (60., -71.), (81., -31.), (5., -63.), (25., -15.), (95., 40.), (23, 15)]) pairs = cell_pairs(gr, polygon) fig, ax = plt.subplots() ax.set_title('Node pairs for the hor. flow-barrier package of Modflow') ax.set_xlabel('x [m]') ax.set_ylabel('y [m]') gr.plot_grid(world=False, ax=ax) ax.plot(polygon[:,0], polygon[:,1])