def doStep(self,dt): """ performs an iteration step of the penalty method. IterationDivergenceError is raised if pressure error cannot be reduced or max_iter is reached. """ penalty=self.viscosity/self.relaxation self.__pde.setValue(lame_mu=self.viscosity, \ lame_lambda=penalty, \ F=self.internal_force, \ sigma=self.pressure*kronecker(self.__pde.getDomain()), \ r=self.prescribed_velocity, \ q=self.location_prescribed_velocity) self.__pde.getSolverOptions().setTolerance(self.rel_tol/10.) self.velocity=self.__pde.getSolution() update=penalty*div(self.velocity) self.pressure=self.pressure-update self.__diff,diff_old=Lsup(update),self.__diff self.__iter+=1 self.trace("velocity range %e:%e"%(inf(self.velocity),sup(self.velocity))) self.trace("pressure range %e:%e"%(inf(self.pressure),sup(self.pressure))) self.trace("pressure correction: %e"%self.__diff) if self.__iter>2 and diff_old<self.__diff: self.trace("Pressure iteration failed!") raise IterationDivergenceError("no improvement in pressure iteration") if self.__iter>self.max_iter: raise IterationDivergenceError("Maximum number of iterations steps reached")
def __setBoundaryMask(self, X): """ DESCRIPTION: ----------- Define Dirichlet model boundaries conditions. ARGUMENTS: ---------- X :: escript object with all coordinates RETURNS: -------- boundary_mask :: escript object with mask values at boundaries """ # Boundaries are defined as masks (1 or 0) for all mesh coordinates; # values at the boundary are '1', whereas all other values are '0'. mask_l = escript.whereZero(X[0] - escript.inf(X[0])) mask_r = escript.whereZero(X[0] - escript.sup(X[0])) mask_t = escript.whereZero(X[1] - escript.inf(X[1])) mask_b = escript.whereZero(X[1] - escript.sup(X[1])) # Combine the mask for all boundaries: boundary_mask = mask_t + mask_b + mask_l + mask_r return boundary_mask
def doStep(self, dt): """ performs an iteration step of the penalty method. IterationDivergenceError is raised if pressure error cannot be reduced or max_iter is reached. """ penalty = self.viscosity / self.relaxation self.__pde.setValue(lame_mu=self.viscosity, \ lame_lambda=penalty, \ F=self.internal_force, \ sigma=self.pressure*es.kronecker(self.__pde.getDomain()), \ r=self.prescribed_velocity, \ q=self.location_prescribed_velocity) self.__pde.getSolverOptions().setTolerance(self.rel_tol / 10.) self.velocity = self.__pde.getSolution() update = penalty * es.div(self.velocity) self.pressure = self.pressure - update self.__diff, diff_old = es.Lsup(update), self.__diff self.__iter += 1 self.trace("velocity range %e:%e" % (es.inf(self.velocity), es.sup(self.velocity))) self.trace("pressure range %e:%e" % (es.inf(self.pressure), es.sup(self.pressure))) self.trace("pressure correction: %e" % self.__diff) if self.__iter > 2 and diff_old < self.__diff: self.trace("Pressure iteration failed!") raise esmf.IterationDivergenceError( "no improvement in pressure iteration") if self.__iter > self.max_iter: raise esmf.IterationDivergenceError( "Maximum number of iterations steps reached")
def getInverse(self, s): """ returns the value of the inverse of the mapping for s """ if not (inf(s) > self.s_min and sup(s) < self.s_max): raise ValueError("s is out of range [%f,%f]" % (inf(s), sup(s))) return 1. / 2. * log((s - self.s_min) / (self.s_max - s))
def getInverse(self, s): """ returns the value of the inverse of the mapping for s """ if not (inf(s) > self.s_min and sup(s) < self.s_max): raise ValueError("s is out of range [%f,%f]" % (inf(s), sup(s))) return 1.0 / 2.0 * log((s - self.s_min) / (self.s_max - s))
def getPotentialNumeric(self): """ Returns 3 list each made up of a number of list containing primary, secondary and total potentials diferences. Each of the lists contain a list for each value of n. """ primCon=self.primaryConductivity coords=self.domain.getX() tol=1e-8 pde=LinearPDE(self.domain, numEquations=1) pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() primaryPde=LinearPDE(self.domain, numEquations=1) primaryPde.getSolverOptions().setTolerance(tol) primaryPde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=whereZero(x[DIM-1]-inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi)) A = self.secondaryConductivity * kronecker(self.domain) APrimary = self.primaryConductivity * kronecker(self.domain) pde.setValue(A=A,q=q) primaryPde.setValue(A=APrimary,q=q) delPhiSecondaryList = [] delPhiPrimaryList = [] delPhiTotalList = [] for i in range(1,self.n+1): # 1 to n maxR = self.numElectrodes - 1 - (2*i) #max amount of readings that will fit in the survey delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] for j in range(maxR): y_dirac=Scalar(0,DiracDeltaFunctions(self.domain)) y_dirac.setTaggedValue(self.electrodeTags[j],self.current) y_dirac.setTaggedValue(self.electrodeTags[j + ((2*i) + 1)],-self.current) self.sources.append([self.electrodeTags[j], self.electrodeTags[j + ((2*i) + 1)]]) primaryPde.setValue(y_dirac=y_dirac) numericPrimaryPot = primaryPde.getSolution() X=(primCon-self.secondaryConductivity) * grad(numericPrimaryPot) pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[j+i],self.electrodes[j+i+1]]) self.samples.append([self.electrodeTags[j+i],self.electrodeTags[j+i+1]]) valPrimary=loc.getValue(numericPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j]) delPhiPrimaryList.append(delPhiPrimary) delPhiSecondaryList.append(delPhiSecondary) delPhiTotalList.append(delPhiTotal) self.delPhiPrimaryList=delPhiPrimaryList self.delPhiSecondaryList=delPhiSecondaryList self.delPhiTotalList = delPhiTotalList return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
def getPotentialNumeric(self): """ Returns 3 list each made up of a number of list containing primary, secondary and total potentials diferences. Each of the lists contain a list for each value of n. """ primCon=self.primaryConductivity coords=self.domain.getX() tol=1e-8 pde=LinearPDE(self.domain, numEquations=1) pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() primaryPde=LinearPDE(self.domain, numEquations=1) primaryPde.getSolverOptions().setTolerance(tol) primaryPde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi)) A = self.secondaryConductivity * es.kronecker(self.domain) APrimary = self.primaryConductivity * es.kronecker(self.domain) pde.setValue(A=A,q=q) primaryPde.setValue(A=APrimary,q=q) delPhiSecondaryList = [] delPhiPrimaryList = [] delPhiTotalList = [] for i in range(1,self.n+1): # 1 to n maxR = self.numElectrodes - 1 - (2*i) #max amount of readings that will fit in the survey delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] for j in range(maxR): y_dirac=es.Scalar(0,es.DiracDeltaFunctions(self.domain)) y_dirac.setTaggedValue(self.electrodeTags[j],self.current) y_dirac.setTaggedValue(self.electrodeTags[j + ((2*i) + 1)],-self.current) self.sources.append([self.electrodeTags[j], self.electrodeTags[j + ((2*i) + 1)]]) primaryPde.setValue(y_dirac=y_dirac) numericPrimaryPot = primaryPde.getSolution() X=(primCon-self.secondaryConductivity) * es.grad(numericPrimaryPot) pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[j+i],self.electrodes[j+i+1]]) self.samples.append([self.electrodeTags[j+i],self.electrodeTags[j+i+1]]) valPrimary=loc.getValue(numericPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j]) delPhiPrimaryList.append(delPhiPrimary) delPhiSecondaryList.append(delPhiSecondary) delPhiTotalList.append(delPhiTotal) self.delPhiPrimaryList=delPhiPrimaryList self.delPhiSecondaryList=delPhiSecondaryList self.delPhiTotalList = delPhiTotalList return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
def getPotential(self): """ returns a list containing 3 lists one for each the primary, secondary and total potential. """ primCon=self.primaryConductivity coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi)) A = self.secondaryConductivity * es.kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] if(len(self.electrodes[0])==3): for i in range(self.numElectrodes-1): analyticRs=es.Data(0,(3,),es.ContinuousFunction(self.domain)) analyticRs[0]=(coords[0]-self.electrodes[i][0]) analyticRs[1]=(coords[1]-self.electrodes[i][1]) analyticRs[2]=(coords[2]) rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5 analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(es.whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0 analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5 analyticRsPolePower = analyticRsPolePower+(es.whereZero(analyticRsPolePower)*0.0000001) gradUPrimary = es.Data(0,(3,),es.ContinuousFunction(self.domain)) gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower) gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower) gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower) gradUPrimary=-gradUPrimary X=(primCon-self.secondaryConductivity) * gradUPrimary pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,self.electrodes[i+1]) delPhiSecondary.append(loc.getValue(u)) delPhiPrimary.append(loc.getValue(analyticPrimaryPot)) else: raise NotImplementedError("2d forward model is not yet implemented") self.delPhiSecondary = delPhiSecondary self.delPhiPrimary = delPhiPrimary for i in range(len(delPhiPrimary)): delPhiTotal.append(delPhiPrimary[i] + delPhiSecondary[i]) self.delPhiTotal=delPhiTotal return [delPhiPrimary, delPhiSecondary, delPhiTotal]
def getPotential(self): """ returns a list containing 3 lists one for each the primary, secondary and total potential. """ primCon=self.primaryConductivity coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=whereZero(x[DIM-1]-inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi)) A = self.secondaryConductivity * kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] if(len(self.electrodes[0])==3): for i in range(self.numElectrodes-1): analyticRs=Data(0,(3,),ContinuousFunction(self.domain)) analyticRs[0]=(coords[0]-self.electrodes[i][0]) analyticRs[1]=(coords[1]-self.electrodes[i][1]) analyticRs[2]=(coords[2]) rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5 analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0 analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5 analyticRsPolePower = analyticRsPolePower+(whereZero(analyticRsPolePower)*0.0000001) gradUPrimary = Data(0,(3,),ContinuousFunction(self.domain)) gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower) gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower) gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower) gradUPrimary=-gradUPrimary X=(primCon-self.secondaryConductivity) * gradUPrimary pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,self.electrodes[i+1]) delPhiSecondary.append(loc.getValue(u)) delPhiPrimary.append(loc.getValue(analyticPrimaryPot)) else: raise NotImplementedError("2d forward model is not yet implemented") self.delPhiSecondary = delPhiSecondary self.delPhiPrimary = delPhiPrimary for i in range(len(delPhiPrimary)): delPhiTotal.append(delPhiPrimary[i] + delPhiSecondary[i]) self.delPhiTotal=delPhiTotal return [delPhiPrimary, delPhiSecondary, delPhiTotal]
def checkBounds(self): X = ContinuousFunction(self.domain).getX() xDim=[inf(X[0]),sup(X[0])] yDim=[inf(X[1]),sup(X[1])] zDim=[inf(X[2]),sup(X[2])] for i in range(self.numElectrodes): if (self.electrodes[i][0] < xDim[0] or self.electrodes[i][0] > xDim[1] or self.electrodes[i][1] < yDim[0] or self.electrodes[i][1] > yDim[1]): print (self.electrodes[i]) raise ValueError("Electrode setup extents past domain dimentions")
def checkBounds(self): X = es.ContinuousFunction(self.domain).getX() xDim=[es.inf(X[0]),es.sup(X[0])] yDim=[es.inf(X[1]),es.sup(X[1])] zDim=[es.inf(X[2]),es.sup(X[2])] for i in range(self.numElectrodes): if (self.electrodes[i][0] < xDim[0] or self.electrodes[i][0] > xDim[1] or self.electrodes[i][1] < yDim[0] or self.electrodes[i][1] > yDim[1]): print (self.electrodes[i]) raise ValueError("Electrode setup extents past domain dimentions")
def getValue(self, m): print("in get value inf(m)=", inf(m), " sup(m)=", sup(m)) # s=self.__sigma0 + (self.__sigma0 * self.__k*m) # s=self.__sigma0*exp(self.__k*m) #### use sigmoid mapping s = (self.__sigma0 * self.a / (1 + exp(-self.__k * m))) + self.minVal print("in get value inf(s)=", inf(s), " sup(s)=", sup(s)) if sup(s) != 0 and inf(s) != 0: print("in get value 1/inf(s)=", 1.0 / inf(s), " 1/sup(s)=", 1.0 / sup(s)) return s
def __setOutput(self): x = self.domain.getX() self.__location_of_constraint = es.Scalar(0, x.getFunctionSpace()) if self.domain.getDim() == 3: vertex = [es.inf(x[0]), es.inf(x[1]), es.inf(x[2])] else: vertex = [es.inf(x[0]), es.inf(x[1])] self.__location_of_constraint = es.whereZero(es.length(x - vertex), self.tol) if not self.value is None: self.__value_of_constraint = self.__location_of_constraint * self.value
def getValue(self, m): print("in get value inf(m)=", inf(m), " sup(m)=", sup(m)) # s=self.__sigma0 + (self.__sigma0 * self.__k*m) # s=self.__sigma0*exp(self.__k*m) #### use sigmoid mapping s = (self.__sigma0 * self.a / (1 + exp(-self.__k * m))) + self.minVal print("in get value inf(s)=", inf(s), " sup(s)=", sup(s)) if sup(s) != 0 and inf(s) != 0: print("in get value 1/inf(s)=", 1. / inf(s), " 1/sup(s)=", 1. / sup(s)) return s
def __setOutput(self): if self.__location_of_constraint is None: x = self.domain.getX() val = self.value if isinstance(val, int) or isinstance(val, float): shape = () elif isinstance(val, list) or isinstance(val, tuple): shape = (len(val), ) elif isinstance(val, numpy.ndarray): shape = val.shape elif val is None: shape = () else: shape = val.getShape() self.__location_of_constraint = Data(0, shape, x.getFunctionSpace()) if self.domain.getDim() == 3: x0, x1, x2 = x[0], x[1], x[2] if self.left: self.__location_of_constraint += es.whereZero( x0 - es.inf(x0), self.tol) if self.right: self.__location_of_constraint += es.whereZero( x0 - es.sup(x0), self.tol) if self.front: self.__location_of_constraint += es.whereZero( x1 - es.inf(x1), self.tol) if self.back: self.__location_of_constraint += es.whereZero( x1 - es.sup(x1), self.tol) if self.bottom: self.__location_of_constraint += es.whereZero( x2 - es.inf(x2), self.tol) if self.top: self.__location_of_constraint += es.whereZero( x2 - es.sup(x2), self.tol) else: x0, x1 = x[0], x[1] if self.left: self.__location_of_constraint += es.whereZero( x0 - es.inf(x0), self.tol) if self.right: self.__location_of_constraint += es.whereZero( x0 - es.sup(x0), self.tol) if self.bottom: self.__location_of_constraint += es.whereZero( x1 - es.inf(x1), self.tol) if self.top: self.__location_of_constraint += es.whereZero( x1 - es.sup(x1), self.tol) if not self.value is None: self.__value_of_constraint = self.__location_of_constraint * self.value
def toRegGrid(u, nx=50, ny=50): """ returns a nx x ny grid representation of the escript object u """ xx=u.getDomain().getX() x=u.getFunctionSpace().getX() coordX, coordY = toXYTuple(x) utemp = u.toListOfTuples() # create regular grid xi = np.linspace(inf(xx[0]),sup(xx[0]),nx) yi = np.linspace(inf(xx[1]),sup(xx[1]),ny) # interpolate u to grid zi = pl.matplotlib.mlab.griddata(coordX,coordY,utemp,xi,yi, interp='linear') return xi, yi, zi
def __init__(self, domain, v_p, wavelet, source_tag, dt=None, p0=None, p0_t=None, absorption_zone=300 * U.m, absorption_cut=1e-2, lumping=True): """ initialize the sonic wave solver :param domain: domain of the problem :type domain: `Domain` :param v_p: p-velocity field :type v_p: `escript.Scalar` :param wavelet: wavelet to describe the time evolution of source term :type wavelet: `Wavelet` :param source_tag: tag of the source location :type source_tag: 'str' or 'int' :param dt: time step size. If not present a suitable time step size is calculated. :param p0: initial solution. If not present zero is used. :param p0_t: initial solution change rate. If not present zero is used. :param absorption_zone: thickness of absorption zone :param absorption_cut: boundary value of absorption decay factor :param lumping: if True mass matrix lumping is being used. This is accelerates the computing but introduces some diffusion. """ f = createAbsorptionLayerFunction( escript.Function(domain).getX(), absorption_zone, absorption_cut) v_p = v_p * f if p0 == None: p0 = escript.Scalar(0., escript.Solution(domain)) else: p0 = escript.interpolate(p0, escript.Solution(domain)) if p0_t == None: p0_t = escript.Scalar(0., escript.Solution(domain)) else: p0_t = escript.interpolate(p0_t, escript.Solution(domain)) if dt == None: dt = min(escript.inf((1. / 5.) * domain.getSize() / v_p), wavelet.getTimeScale()) super(SonicWave, self).__init__(dt, u0=p0, v0=p0_t, t0=0.) self.__wavelet = wavelet self.__mypde = lpde.LinearSinglePDE(domain) if lumping: self.__mypde.getSolverOptions().setSolverMethod( lpde.SolverOptions.HRZ_LUMPING) self.__mypde.setSymmetryOn() self.__mypde.setValue(D=1. / v_p**2) self.__source_tag = source_tag self.__r = escript.Scalar( 0., escript.DiracDeltaFunctions(self.__mypde.getDomain()))
def getSafeTimeStepSize(self, dt): """ returns new step size """ h = self.domain.getSize() return self.safety_factor * inf( h**2 / (h * abs(self.heat_capacity * self.density) * length(self.velocity) + self.thermal_permabilty))
def doStepPostprocessing(self,dt): """ perform taylor galerkin step """ T=self.temperature self.__rhocp=self.heat_capacity*self.density self.__fixed_T=self.fixed_temperature self.temperature=dt*self.G(dt/2*self.G(T,1./dt)+T,1./dt)+T self.trace("Temperature range is %e %e"%(inf(self.temperature),sup(self.temperature)))
def toRegGrid(u, nx=50, ny=50): """ returns a nx x ny grid representation of the escript object u """ xx = u.getDomain().getX() x = u.getFunctionSpace().getX() coordX, coordY = toXYTuple(x) utemp = u.toListOfTuples() # create regular grid xi = np.linspace(inf(xx[0]), sup(xx[0]), nx) yi = np.linspace(inf(xx[1]), sup(xx[1]), ny) # interpolate u to grid #zi = pl.matplotlib.mlab.griddata(coordX,coordY,utemp,xi,yi, interp='linear') zi = interpolate.griddata((coordX, coordY), utemp, (xi[None, :], yi[:, None]), method='linear') return xi, yi, zi
def __setOutput(self): x = self.domain.getX() self.__location_of_constraint = es.Vector(0, x.getFunctionSpace()) if self.domain.getDim() == 3: vertex = [es.inf(x[0]), es.inf(x[1]), es.inf(x[2])] msk = numpy.zeros((3, )) if self.comp_mask[0]: msk[0] = 1 if self.comp_mask[1]: msk[1] = 1 if self.comp_mask[2]: msk[2] = 1 else: vertex = [es.inf(x[0]), es.inf(x[1])] msk = numpy.zeros((2, )) if self.comp_mask[0]: msk[0] = 1 if self.comp_mask[1]: msk[1] = 1 self.__location_of_constraint = es.whereZero( es.length(x - vertex), self.tol) * numpy.ones(shape) if not self.value is None: self.__value_of_constraint = self.__location_of_constraint * self.value
def __setOutput(self): x = self.domain.getX() self.__location_of_constraint = es.Scalar(0, x.getFunctionSpace()) if self.domain.getDim() == 3: x0, x1, x2 = x[0], x[1], x[2] d = max( es.sup(x0) - es.inf(x0), sup(x1) - es.inf(x1), sup(x2) - es.inf(x2)) if self.left: self.__location_of_constraint += es.whereZero( x0 - es.inf(x0), self.tol * d) if self.right: self.__location_of_constraint += es.whereZero( x0 - es.sup(x0), self.tol * d) if self.front: self.__location_of_constraint += es.whereZero( x1 - es.inf(x1), self.tol * d) if self.back: self.__location_of_constraint += es.whereZero( x1 - es.sup(x1), self.tol * d) if self.bottom: self.__location_of_constraint += es.whereZero( x2 - es.inf(x2), self.tol * d) if self.top: self.__location_of_constraint += es.whereZero( x2 - es.sup(x2), self.tol * d) else: x0, x1 = x[0], x[1] d = max(es.sup(x0) - es.inf(x0), es.sup(x1) - es.inf(x1)) if self.left: self.__location_of_constraint += es.whereZero( x0 - es.inf(x0), self.tol * d) if self.right: self.__location_of_constraint += es.whereZero( x0 - es.sup(x0), self.tol * d) if self.bottom: self.__location_of_constraint += es.whereZero( x1 - es.inf(x1), self.tol * d) if self.top: self.__location_of_constraint += es.whereZero( x1 - es.sup(x1), self.tol * d) if not self.value is None: self.__value_of_constraint = self.__location_of_constraint * self.value
def out(self): """ values at domain locations by bilinear interpolation of the given values. Link against this method to get the output of this model. """ x = self.domain.getX() if self.domain.getDim() == 2: x0, x1 = x[0], x[1] left_bottom_front0, right_top_back0 = inf(x0), sup(x0) left_bottom_front1, right_top_back1 = inf(x1), sup(x1) f_right = (x0 - left_bottom_front0) / (right_top_back0 - left_bottom_front0) f_left = 1. - f_right f_top = (x1 - left_bottom_front1) / (right_top_back1 - left_bottom_front1) f_bottom = 1. - f_top out = f_left * f_bottom * self.value_left_bottom_front \ + f_right * f_bottom * self.value_right_bottom_front \ + f_left * f_top * self.value_left_top_front \ + f_right * f_top * self.value_right_top_front else: x0, x1, x2 = x[0], x[1], x[2] left_bottom_front0, right_top_back0 = inf(x0), sup(x0) left_bottom_front1, right_top_back1 = inf(x1), sup(x1) left_bottom_front2, right_top_back2 = inf(x2), sup(x2) f_right = (x0 - left_bottom_front0) / (right_top_back0 - left_bottom_front0) f_left = 1. - f_right f_top = (x1 - left_bottom_front1) / (right_top_back1 - left_bottom_front1) f_bottom = 1. - f_top f_back = (x2 - left_bottom_front1) / (right_top_back2 - left_bottom_front2) f_front = 1. - f_back out = f_left * f_bottom * f_front * self.value_left_bottom_front\ + f_right * f_bottom * f_front * self.value_right_bottom_front\ + f_left * f_top * f_front * self.value_left_top_front\ + f_right * f_top * f_front * self.value_right_top_front\ + f_left * f_bottom * f_back * self.value_left_bottom_back\ + f_right * f_bottom * f_back * self.value_right_bottom_back\ + f_left * f_top * f_back * self.value_left_top_back\ + f_right * f_top * f_back * self.value_right_top_back return out
def subsample(u, nx=50, ny=50): """ subsamples ```u``` over an ```nx``` x ```ny``` grid and returns ``numpy`` arrays for the values and locations used for subsampling. """ xx = u.getDomain().getX() # points of the domain x0 = inf(xx[0]) y0 = inf(xx[1]) dx = (sup(xx[0]) - x0) / nx # x spacing dy = (sup(xx[1]) - y0) / ny # y spacing grid = [] for j in range(0, ny - 1): for i in range(0, nx - 1): grid.append([x0 + dx / 2 + dx * i, y0 + dy / 2 + dy * j]) uLoc = Locator(u.getFunctionSpace(), grid) subu = uLoc(u) # get data of u at sample points closests to grid points usublocs = uLoc.getX() #returns actual locations from data return np.array(usublocs), np.array(subu)
def subsample(u, nx=50, ny=50): """ subsamples ```u``` over an ```nx``` x ```ny``` grid and returns ``numpy`` arrays for the values and locations used for subsampling. """ xx=u.getDomain().getX() # points of the domain x0=inf(xx[0]) y0=inf(xx[1]) dx = (sup(xx[0])-x0)/nx # x spacing dy = (sup(xx[1])-y0)/ny # y spacing grid = [ ] for j in range(0,ny-1): for i in range(0,nx-1): grid.append([x0+dx/2+dx*i,y0+dy/2+dy*j]) uLoc = Locator(u.getFunctionSpace(),grid) subu= uLoc(u) # get data of u at sample points closests to grid points usublocs = uLoc.getX() #returns actual locations from data return np.array(usublocs), np.array(subu)
def getInverse(self, s): """ returns the value of the inverse of the mapping for s """ # return (s-self.__sigma0) / (self.__sigma0 * self.__k) if inf(((self.__sigma0 * self.a) / s)) <= 1.0: raise ValueError("sigma 0*a/s < 1 this is not valid as log cannot be 0 or negative") m = -1.0 / self.__k * log(((self.__sigma0 * self.a) / (s - self.minVal)) - 1) print("inv(s)=", m) return m
def doStepPostprocessing(self, dt): """ perform taylor galerkin step """ T = self.temperature self.__rhocp = self.heat_capacity * self.density self.__fixed_T = self.fixed_temperature self.temperature = dt * self.G(dt / 2 * self.G(T, 1. / dt) + T, 1. / dt) + T self.trace("Temperature range is %e %e" % (inf(self.temperature), sup(self.temperature)))
def getInverse(self, s): """ returns the value of the inverse of the mapping for s """ # return (s-self.__sigma0) / (self.__sigma0 * self.__k) if inf(((self.__sigma0 * self.a) / s)) <= 1.: raise ValueError( "sigma 0*a/s < 1 this is not valid as log cannot be 0 or negative" ) m = -1. / self.__k * log(((self.__sigma0 * self.a) / (s - self.minVal)) - 1) print("inv(s)=", m) return m
def __setOutput(self): if self.__location_of_constraint is None: x = self.domain.getX() val = self.value if isinstance(val, int) or isinstance(val, float): shape = () elif isinstance(val, list) or isinstance(val, tuple): shape = (len(val), ) elif isinstance(val, numpy.ndarray): shape = val.shape elif val is None: shape = () else: shape = val.getShape() if self.domain.getDim() == 3: vertex = [es.inf(x[0]), es.inf(x[1]), es.inf(x[2])] else: vertex = [es.inf(x[0]), es.inf(x[1])] self.__location_of_constraint = es.whereZero( es.length(x - vertex), self.tol) * numpy.ones(shape) if not self.value is None: self.__value_of_constraint = self.__location_of_constraint * self.value
def out(self): """ values at domain locations by bilinear interpolation of the given values. Link against this method to get the output of this model. """ x = self.domain.getX() if self.domain.getDim() == 2: x0,x1=x[0],x[1] left_bottom_front0,right_top_back0=inf(x0),sup(x0) left_bottom_front1,right_top_back1=inf(x1),sup(x1) f_right = (x0 - left_bottom_front0)/(right_top_back0 -left_bottom_front0) f_left = 1. - f_right f_top = (x1 - left_bottom_front1)/(right_top_back1 - left_bottom_front1) f_bottom = 1. - f_top out = f_left * f_bottom * self.value_left_bottom_front \ + f_right * f_bottom * self.value_right_bottom_front \ + f_left * f_top * self.value_left_top_front \ + f_right * f_top * self.value_right_top_front else: x0,x1,x2=x[0],x[1],x[2] left_bottom_front0,right_top_back0=inf(x0),sup(x0) left_bottom_front1,right_top_back1=inf(x1),sup(x1) left_bottom_front2,right_top_back2=inf(x2),sup(x2) f_right = (x0 - left_bottom_front0)/(right_top_back0 - left_bottom_front0) f_left = 1. - f_right f_top = (x1 - left_bottom_front1)/(right_top_back1 - left_bottom_front1) f_bottom = 1. - f_top f_back = (x2 - left_bottom_front1)/(right_top_back2 - left_bottom_front2) f_front = 1. - f_back out = f_left * f_bottom * f_front * self.value_left_bottom_front\ + f_right * f_bottom * f_front * self.value_right_bottom_front\ + f_left * f_top * f_front * self.value_left_top_front\ + f_right * f_top * f_front * self.value_right_top_front\ + f_left * f_bottom * f_back * self.value_left_bottom_back\ + f_right * f_bottom * f_back * self.value_right_bottom_back\ + f_left * f_top * f_back * self.value_left_top_back\ + f_right * f_top * f_back * self.value_right_top_back return out
def test_geodetic_domain(self): COORDINATES = WGS84ReferenceSystem() db = DomainBuilder(reference_system=COORDINATES) source1a = NetCdfData(DataSource.GRAVITY, NC_DATA1, scale_factor=1., reference_system=COORDINATES) db.addSource(source1a) db.setVerticalExtents(depth=20000., air_layer=30000., num_cells=10) dom = db.getDomain() x = dom.getX() self.assertAlmostEqual(inf(x[0]), 120.2, delta=0.001, msg="phi range wrong") self.assertAlmostEqual(inf(x[1]), -29.2, delta=0.0001, msg="lambda range wrong") self.assertAlmostEqual(inf(x[2]), -0.2, msg="h range wrong" + str(x[2])) # Cannot check upper bounds of coordinates with more than 1 rank # because ripley may adjust internally. if getMPISizeWorld() == 1: self.assertAlmostEqual(sup(x[0]), 120.3, delta=0.001, msg="phi range wrong") self.assertAlmostEqual(sup(x[1]), -29.1, delta=0.0001, msg="lambda range wrong") self.assertAlmostEqual(sup(x[2]), 0.3, msg="h range wrong: " + str(x[2]))
def __init__(self, domain, v_p, wavelet, source_tag, source_vector=[1., 0.], eps=0., delta=0., azimuth=0., dt=None, p0=None, v0=None, absorption_zone=300 * U.m, absorption_cut=1e-2, lumping=True): """ initialize the HTI wave solver :param domain: domain of the problem :type domain: `Doamin` :param v_p: vertical p-velocity field :type v_p: `escript.Scalar` :param v_s: vertical s-velocity field :type v_s: `escript.Scalar` :param wavelet: wavelet to describe the time evolution of source term :type wavelet: `Wavelet` :param source_tag: tag of the source location :type source_tag: 'str' or 'int' :param source_vector: source orientation vector :param eps: first Thompsen parameter :param azimuth: azimuth (rotation around verticle axis) :param gamma: third Thompsen parameter :param rho: density :param dt: time step size. If not present a suitable time step size is calculated. :param p0: initial solution (Q(t=0), P(t=0)). If not present zero is used. :param v0: initial solution change rate. If not present zero is used. :param absorption_zone: thickness of absorption zone :param absorption_cut: boundary value of absorption decay factor :param lumping: if True mass matrix lumping is being used. This is accelerates the computing but introduces some diffusion. """ DIM = domain.getDim() f = createAbsorptionLayerFunction(v_p.getFunctionSpace().getX(), absorption_zone, absorption_cut) self.v2_p = v_p**2 self.v2_t = self.v2_p * escript.sqrt(1 + 2 * delta) self.v2_n = self.v2_p * (1 + 2 * eps) if p0 == None: p0 = escript.Data(0., (2, ), escript.Solution(domain)) else: p0 = escript.interpolate(p0, escript.Solution(domain)) if v0 == None: v0 = escript.Data(0., (2, ), escript.Solution(domain)) else: v0 = escript.interpolate(v0, escript.Solution(domain)) if dt == None: dt = min( min(escript.inf(domain.getSize() / escript.sqrt(self.v2_p)), escript.inf(domain.getSize() / escript.sqrt(self.v2_t)), escript.inf(domain.getSize() / escript.sqrt(self.v2_n))), wavelet.getTimeScale()) * 0.2 super(SonicHTIWave, self).__init__(dt, u0=p0, v0=v0, t0=0.) self.__wavelet = wavelet self.__mypde = lpde.LinearPDESystem(domain) if lumping: self.__mypde.getSolverOptions().setSolverMethod( lpde.SolverOptions.HRZ_LUMPING) self.__mypde.setSymmetryOn() self.__mypde.setValue(D=escript.kronecker(2), X=self.__mypde.createCoefficient('X')) self.__source_tag = source_tag self.__r = escript.Vector( 0, escript.DiracDeltaFunctions(self.__mypde.getDomain())) self.__r.setTaggedValue(self.__source_tag, source_vector)
def __getBoundaryValues(self, mode, X, rho_1d, ifc_1d, xstep, zstep, frequency): """ DESCRIPTION: ----------- Returns a list with boundary values along each Dirichlet boundary. Values at the left and right side of the domain are evaluated at sample points and interpolated across the domain. The subroutine expects that layers at the right- and left-hand-side are defined. ARGUMENTS: ---------- mode :: string with TE or TM mode X :: escript object with all coordinates rho_1d :: dictionary with resistivities at the left/right boundary ifc_1d :: dictionary with layer interfaces at the left/right boundary xstep :: number with step size for horizontal sample list zstep :: number with step size for vertical sample list frequency :: number with actual sounding frequency RETURNS: -------- bondary_value :: dictionary with lists of boundary values at sample points """ # --- # Sample lists at vertical and horizontal boundaries. # --- # Horizontal extents: xmin = escript.inf(X[0]) xmax = escript.sup(X[0]) # Vertical extents: zmin = escript.inf(X[1]) zmax = escript.sup(X[1]) # Setup a list with sample points along the vertical mesh extent, bottom to top: zsamples = self.__getSamplePoints(-zmax, -zmin, zstep) # --- # Calculate the 1D response at the left- and right-hand-side boundaries # --- # Instantiate an 'mt1d' object for the left- and right-hand-sides: mt1d_left = mt1d.MT_1D(frequency, ifc_1d['left'], rho_1d['left'], zsamples) mt1d_rght = mt1d.MT_1D(frequency, ifc_1d['right'], rho_1d['right'], zsamples) # Compute the 1D field values at the sample nodes: te1d_left, tm1d_left = mt1d_left.mt1d() te1d_rght, tm1d_rght = mt1d_rght.mt1d() # Distinguish TE and TM mode and save 1D values in dictionary: if mode.upper() == "TE": mt_1d = {"left": te1d_left, "right": te1d_rght} else: mt_1d = {"left": tm1d_left, "right": tm1d_rght} # --- # Interpolation across mesh. # --- # Now setup a 2D-table from left to right at each sampled depth for mesh-interpolation. table2d_real = [] table2d_imag = [] # 1D-interpolation of values from left to right at different depths 'i': for i in range(len(zsamples)): table2d_real.append( self.__interpolLinear(xstep, xmin, xmax, mt_1d["left"].real[i], mt_1d["right"].real[i])) table2d_imag.append( self.__interpolLinear(xstep, xmin, xmax, mt_1d["left"].imag[i], mt_1d["right"].imag[i])) # 2D-interpolation to map the values on the mesh coordinates: bondary_value_real = escript.interpolateTable(table2d_real, X, (xmin, zmin), (xstep, zstep)) bondary_value_imag = escript.interpolateTable(table2d_imag, X, (xmin, zmin), (xstep, zstep)) # Return the real and imaginary values as a dictionary: boundary_value = { "real": bondary_value_real, "imag": bondary_value_imag } return boundary_value
def __init__(self, domain, mode, freq_def, tags, rho, rho_1d, ifc_1d, xstep=100, zstep=100, maps=None, plot=False, limits=None): """ DESCRIPTION: ------------ Constructor which initialises the 2D magnetotelluric class: (*) check for argument type (*) check for valid argument values (*) initialises required values ARGUMENTS: ---------- param domain :: the 2d mesh domain type domain :: ``escript data object`` param mode :: TE or TM mode type mode :: ``string`` param freq_def :: highest/lowest frequency & points per decade type freq_def :: ``dictionary`` param tags :: the tag names of the regions defined in the mesh type tags :: ``list`` param rho :: the resistivity values of the regions in the mesh type rho :: ``list`` param rho_1d :: the resistivity values at the left & right boundary type rho_1d :: ``dictionary`` param ifc_1d :: the layer interface depths of the left & right boundary type ifc_1d :: ``dictionary`` param xstep :: user-defined step size for horizontal sample list type xstep :: ``number`` (optional) param zstep :: user-defined step size for vertical sample list type zstep :: ``number`` (optional) param maps :: list with user-defined functions which map the resistivity for each region type maps :: ``list`` (optional) param plot :: user-defined flag to show a plot of apparent resistivity and phase at each frequency type plot :: ``boolean`` (optional) DATA ATTRIBUTES: ---------------- self.domain :: escript data object of mesh self.X :: escript data object with all mesh coordinates self.mode :: string with TE or TM mode self.xmin :: float with x-coordinate minimum self.xmax :: float with x-coordinate maximum self.zmin :: float with z-coordinate minimum self.zmax :: float with z-coordinate maximum self.zstep :: number with sample step in vertical direction self.xstep :: number with sample step in horizontal direction self.rho :: list with resistivity values of all regions self.rho_1d :: dictionary with resistivity values at boundaries left/right self.ifc_1d :: dictionary with interface depths at boundaries left/right self.plot :: boolean flag to show plots of apparent resistivity and phase self.sigma :: escript data object with the conductivity model (based on 'rho' and 'maps') self.frequencies :: list of sounding frequencies self.boundary_mask :: Dirichlet mask at boundaries """ if not HAVE_FINLEY: raise ImportError("Finley module not available") #make python3 compatible, since long disappeared in python 3 if sys.version_info[0] == 3: long_type = int else: long_type = long # --- # Checks # --- # Types: if not isinstance(domain, escript.Domain): raise ValueError("Input parameter DOMAIN must be an Escript mesh") if not isinstance(mode, str): raise ValueError("Input parameter MODE must be a string") if not isinstance(freq_def, dict) or len(freq_def) != 3: raise ValueError( "Input parameter FREQ_DEF must be a dictionary with length 3") if not isinstance(tags, list) or not all( isinstance(t, str) for t in tags): raise ValueError("Input parameter TAGS must be a list of strings") if not isinstance(rho, list) or not all( isinstance(d, (int, long_type, float)) for d in rho): raise ValueError("Input parameter RHO must be a list of numbers") if not isinstance(rho_1d, dict) or len(rho_1d) != 2: raise ValueError( "Input parameter RHO_1D must be a dictionary with length 2") if not isinstance(ifc_1d, dict) or len(ifc_1d) != 2: raise ValueError( "Input parameter IFC_1D must be a dictionary with length 2") if not isinstance(xstep, (int, long_type, float)): raise ValueError("Optional input parameter XSTEP must be a number") if not isinstance(zstep, (int, long_type, float)): raise ValueError("Optional input parameter ZSTEP must be a number") if maps is not None: if not isinstance(maps, list) or not all( isinstance(m, (types.FunctionType, types.NoneType)) for m in maps): raise ValueError( "Optional input parameter MAPS must be a list of Functions or Nones" ) if plot is not None: if not isinstance(plot, bool): raise ValueError( "Optional input parameter PLOT must be True or False") # Values: if mode.upper() != "TE" and mode.upper() != "TM": # Check mode: raise ValueError( "Input parameter mode must be either 'TE' or 'TM'") if not 'high' in freq_def and not 'low' in freq_def and not 'step' in freq_def: raise ValueError( "Input dictionary FREQ_DEF must have keys 'high', 'low' and 'step' defined" ) if freq_def['high'] < freq_def['low']: raise ValueError( "High frequency value is smaller than low frequency value in input parameter FREQ_DEF" ) if freq_def['step'] < 1: raise ValueError( "Step frequency value is smaller than 1 in input parameter FREQ_DEF" ) if not all(r > 0 for r in rho): # Check resistivity values: raise ValueError("Input parameter RHO must be all positive") if len(rho) != len(tags): # Check resistivity list-length: raise ValueError( "Input parameter RHO must have the same length as input parameter TAGS" ) if not 'left' in rho_1d and not 'right' in rho_1d: raise ValueError( "Input dictionary RHO_1D must have keys 'left' and 'right' defined" ) if not 'left' in ifc_1d and not 'right' in ifc_1d: raise ValueError( "Input dictionary IFC_1D must have keys 'left' and 'right' defined" ) if len(ifc_1d['left']) - 1 != len(rho_1d['left']) and len( ifc_1d['right']) - 1 != len(rho_1d['right']): raise ValueError( "Lists with values in input dictionary RHO_1D must have length equal to IFC_1D" ) if xstep < 0.5: # Step size should be non-zero but should not be smaller than 0.5m: raise ValueError("Input parameter XSTEP must be at least 0.5") if zstep < 0.5: # Step size should be non-zero but should not be smaller than 0.5m: raise ValueError("Input parameter ZSTEP must be at least 0.5") # --- # Domain coordinates & tags: # --- # Extract the model coordinates.. X = escript.Solution(domain).getX() # Get the Min/Max coordinates: xmin = escript.inf(X[0]) xmax = escript.sup(X[0]) zmin = escript.inf(X[1]) zmax = escript.sup(X[1]) # Get the tag names from the mesh file mesh_tags = escript.getTagNames(domain) if xmin >= xmax or zmin >= zmax: raise ValueError("The mesh limits are not valid (min >= max)") if zmin >= 0: raise ValueError( "The mesh must be defined with a negative vertical axis") if not set(mesh_tags) == set(tags): print("user-tags:", tags) print("mesh-tags:", mesh_tags) raise ValueError( "Input parameter TAGS does not match the tags defined in the mesh" ) # --- # Define the boundary mask: # --- boundary_mask = self.__setBoundaryMask(X) # --- # Calculate list of sounding frequencies: # --- frequencies = self.__getSoundingFrequencies(freq_def) # --- # Tag the domain with conductivity maps: # --- sigma_model = self.__tagDomain(domain, X, tags, rho, maps) # Check for valid values if escript.inf(sigma_model) < 0 or escript.sup(sigma_model) < 0: raise ValueError("Negative conductivity encountered") if cmath.isnan( escript.inf(sigma_model) ) or \ cmath.isnan( escript.sup(sigma_model) ) or \ cmath.isinf( escript.sup(sigma_model) ): raise ValueError("The conductivity model contains NaNs or INFs") # --- # Projector and Locator objects. # --- print("Setting up Escript Locator and Projector objects...") # Setup a list with sample points along the vertical mesh extent, bottom to top: xsample = self.__getSamplePoints(escript.inf(X[0]), escript.sup(X[0]), xstep, constant=0.0) # Get the locations of mesh points at the surface via the Locator object # operating on the continuous function-space (i.e. nodes) of the domain. loc = pdetools.Locator(escript.ContinuousFunction(domain), xsample) # Instantiate the Projector class with smoothing on (fast=False); # the Projector is used to calculate the gradient correctly. proj = pdetools.Projector(domain, reduce=False, fast=False) # --- # Print information: # --- print("") print("=" * 72) print("Escript MT2D, version", self.__version) print("=" * 72) print("Mesh XMin/XMax : ", xmin, xmax) print("Mesh ZMin/ZMax : ", zmin, zmax) print("Number of Tags : ", len(tags)) print("Mapping : ", { True: 'Yes', False: 'No' }[maps is not None]) print("Conductivity Model : ", sigma_model) print("Nr of Frequencies : ", len(frequencies)) print("Start/End/Step (Hz) : ", freq_def["high"], freq_def["low"], freq_def["step"]) print("Mode : ", mode.upper()) print("Solver : ", MT_2D._solver) print("Show plots : ", plot) print("=" * 72) print("") if self._debug: print("Mesh-Info : ") print(domain.print_mesh_info(full=False)) # --- # Set all required variables as data attributes # --- self.domain = domain self.X = X self.mode = mode self.xmin = xmin self.xmax = xmax self.zmin = zmin self.zmax = zmax self.zstep = zstep self.xstep = xstep self.rho = rho self.rho_1d = rho_1d self.ifc_1d = ifc_1d self.plot = plot self.limits = limits self.sigma = sigma_model self.frequencies = frequencies self.boundary_mask = boundary_mask self.proj = proj self.loc = loc
def getPotential(self): """ Returns 3 list each made up of a number of list containing primary, secondary and total potentials diferences. Each of the lists contain a list for each value of n. """ coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() primCon=self.primaryConductivity DIM=self.domain.getDim() x=self.domain.getX() q=whereZero(x[DIM-1]-inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi)) A = self.secondaryConductivity * kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondaryList = [] delPhiPrimaryList = [] delPhiTotalList = [] for i in range(1,self.n+1): # 1 to n maxR = self.numElectrodes - 2 - (i) #max amount of readings that will fit in the survey delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] for j in range(maxR): analyticRsOne=Data(0,(3,),ContinuousFunction(self.domain)) analyticRsOne[0]=(coords[0]-self.electrodes[j][0]) analyticRsOne[1]=(coords[1]-self.electrodes[j][1]) analyticRsOne[2]=(coords[2]) rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5 analyticRsTwo=Data(0,(3,),ContinuousFunction(self.domain)) analyticRsTwo[0]=(coords[0]-self.electrodes[j + 1][0]) analyticRsTwo[1]=(coords[1]-self.electrodes[j + 1][1]) analyticRsTwo[2]=(coords[2]) rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5 rsMagOne+=(whereZero(rsMagOne)*0.0000001) rsMagTwo+=(whereZero(rsMagTwo)*0.0000001) analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagTwo))-(self.current/(2*pi*primCon*rsMagOne)) analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5 analyticRsOnePower = analyticRsOnePower+(whereZero(analyticRsOnePower)*0.0001) analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5 analyticRsTwoPower = analyticRsTwoPower+(whereZero(analyticRsTwoPower)*0.0001) gradAnalyticPrimaryPot = Data(0,(3,),ContinuousFunction(self.domain)) gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) * ((analyticRsOne[0]/analyticRsOnePower) - (analyticRsTwo[0]/analyticRsTwoPower)) gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) * ((analyticRsOne[1]/analyticRsOnePower) - (analyticRsTwo[1]/analyticRsTwoPower)) gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) * ((analyticRsOne[2]/analyticRsOnePower) - (analyticRsTwo[2]/analyticRsTwoPower)) X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot) pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[1+j+i],self.electrodes[j+i+2]]) valPrimary=loc.getValue(analyticPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j]) delPhiPrimaryList.append(delPhiPrimary) delPhiSecondaryList.append(delPhiSecondary) delPhiTotalList.append(delPhiTotal) self.delPhiPrimaryList=delPhiPrimaryList self.delPhiSecondaryList=delPhiSecondaryList self.delPhiTotalList = delPhiTotalList return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
def getSafeTimeStepSize(self,dt): """ returns new step size """ h=self.domain.getSize() return self.safety_factor*inf(h**2/(h*abs(self.heat_capacity*self.density)*length(self.velocity)+self.thermal_permabilty))
def getPotential(self): """ Returns 3 list each made up of a number of list containing primary, secondary and total potentials diferences. Each of the lists contain a list for each value of n. """ primCon=self.primaryConductivity coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=whereZero(x[DIM-1]-inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi)) A = self.secondaryConductivity * kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondaryList = [] delPhiPrimaryList = [] delPhiTotalList = [] for i in range(1,self.n+1): # 1 to n maxR = self.numElectrodes - 1 - i #max amount of readings that will fit in the survey delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] for j in range(maxR): analyticRs=Data(0,(3,),ContinuousFunction(self.domain)) analyticRs[0]=(coords[0]-self.electrodes[j][0]) analyticRs[1]=(coords[1]-self.electrodes[j][1]) analyticRs[2]=(coords[2]) rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5 analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0 analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5 analyticRsPolePower = analyticRsPolePower+(whereZero(analyticRsPolePower)*0.0000001) gradUPrimary = Data(0,(3,),ContinuousFunction(self.domain)) gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower) gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower) gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower) gradUPrimary=-gradUPrimary X=(primCon-self.secondaryConductivity) * gradUPrimary pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[i+j],self.electrodes[i+j+1]]) valPrimary=loc.getValue(analyticPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j]) delPhiPrimaryList.append(delPhiPrimary) delPhiSecondaryList.append(delPhiSecondary) delPhiTotalList.append(delPhiTotal) self.delPhiPrimaryList=delPhiPrimaryList self.delPhiSecondaryList=delPhiSecondaryList self.delPhiTotalList = delPhiTotalList return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
def getPotential(self): """ returns a list containing 3 lists one for each the primary, secondary and total potential. """ primCon=self.primaryConductivity coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=whereZero(x[DIM-1]-inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi)) A = self.secondaryConductivity * kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] if(len(self.electrodes[0])==3): for i in range(self.numElectrodes-3): analyticRsOne=Data(0,(3,),ContinuousFunction(self.domain)) analyticRsOne[0]=(coords[0]-self.electrodes[i][0]) analyticRsOne[1]=(coords[1]-self.electrodes[i][1]) analyticRsOne[2]=(coords[2]) rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5 analyticRsTwo=Data(0,(3,),ContinuousFunction(self.domain)) analyticRsTwo[0]=(coords[0]-self.electrodes[i+3][0]) analyticRsTwo[1]=(coords[1]-self.electrodes[i+3][1]) analyticRsTwo[2]=(coords[2]) rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5 rsMagOne+=(whereZero(rsMagOne)*0.0000001) rsMagTwo+=(whereZero(rsMagTwo)*0.0000001) analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagOne))-(self.current/(2*pi*primCon*rsMagTwo)) analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5 analyticRsOnePower = analyticRsOnePower+(whereZero(analyticRsOnePower)*0.0001) analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5 analyticRsTwoPower = analyticRsTwoPower+(whereZero(analyticRsTwoPower)*0.0001) gradAnalyticPrimaryPot = Data(0,(3,),ContinuousFunction(self.domain)) gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) \ * ((-analyticRsOne[0]/analyticRsOnePower) \ + (analyticRsTwo[0]/analyticRsTwoPower)) gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) \ * ((-analyticRsOne[1]/analyticRsOnePower) \ + (analyticRsTwo[1]/analyticRsTwoPower)) gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) \ * ((-analyticRsOne[2]/analyticRsOnePower) + (analyticRsTwo[2]/analyticRsTwoPower)) X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot) pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[i+1],self.electrodes[i+2]]) valPrimary=loc.getValue(analyticPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[i]+delPhiSecondary[i]) else: raise NotImplementedError("2d forward model is not yet implemented") self.delPhiSecondary = delPhiSecondary self.delPhiPrimary = delPhiPrimary self.delPhiTotal=delPhiTotal return [delPhiPrimary, delPhiSecondary, delPhiTotal]
elif (NY+1)%(d1*f) == 0: d1 = d1 * f d2 = d2 / f else: if (NY+1)%(d1*f) == 0: d1 = d1 * f d2 = d2 / f elif (NX+1)%(d0*f) == 0: d0 = d0 * f d2 = d2 / f # create domain print("Domain subdivisions: %d x %d x %d"%(d0,d1,d2)) dom = Brick(NX, NY, n_cells_v, l0, l1, l2, d0, d1, d2) dom_len = [sup(dom.getX()[i])-inf(dom.getX()[i]) for i in range(dom.getDim())] # report domain setup print("Domain size: "+str([NX, NY, n_cells_v])) print(" length: "+str(dom_len)) print(" origin: "+str(dom_origin)) DIM = dom.getDim() # = 3 datacoords = ReducedFunction(dom).getX() # create the output directory if not existing already try: os.mkdir(OUTPUTDIR) except: pass
def __init__(self, domain, v_p, v_s, wavelet, source_tag, source_vector=[1., 0., 0.], eps=0., gamma=0., delta=0., rho=1., dt=None, u0=None, v0=None, absorption_zone=None, absorption_cut=1e-2, lumping=True, disable_fast_assemblers=False): """ initialize the VTI wave solver :param domain: domain of the problem :type domain: `Domain` :param v_p: vertical p-velocity field :type v_p: `escript.Scalar` :param v_s: vertical s-velocity field :type v_s: `escript.Scalar` :param wavelet: wavelet to describe the time evolution of source term :type wavelet: `Wavelet` :param source_tag: tag of the source location :type source_tag: 'str' or 'int' :param source_vector: source orientation vector :param eps: first Thompsen parameter :param delta: second Thompsen parameter :param gamma: third Thompsen parameter :param rho: density :param dt: time step size. If not present a suitable time step size is calculated. :param u0: initial solution. If not present zero is used. :param v0: initial solution change rate. If not present zero is used. :param absorption_zone: thickness of absorption zone :param absorption_cut: boundary value of absorption decay factor :param lumping: if True mass matrix lumping is being used. This is accelerates the computing but introduces some diffusion. :param disable_fast_assemblers: if True, forces use of slower and more general PDE assemblers """ DIM = domain.getDim() self.fastAssembler = hasattr( domain, "createAssembler") and not disable_fast_assemblers f = createAbsorptionLayerFunction(v_p.getFunctionSpace().getX(), absorption_zone, absorption_cut) v_p = v_p * f v_s = v_s * f if u0 == None: u0 = escript.Vector(0., escript.Solution(domain)) else: u0 = escript.interpolate(p0, escript.Solution(domain)) if v0 == None: v0 = escript.Vector(0., escript.Solution(domain)) else: v0 = escript.interpolate(v0, escript.Solution(domain)) if dt == None: dt = min((1. / 5.) * min(escript.inf(domain.getSize() / v_p), escript.inf(domain.getSize() / v_s)), wavelet.getTimeScale()) super(HTIWave, self).__init__(dt, u0=u0, v0=v0, t0=0.) self.__wavelet = wavelet self.c33 = v_p**2 * rho self.c44 = v_s**2 * rho self.c11 = (1 + 2 * eps) * self.c33 self.c66 = (1 + 2 * gamma) * self.c44 self.c13 = escript.sqrt(2 * self.c33 * (self.c33 - self.c44) * delta + (self.c33 - self.c44)**2) - self.c44 self.c23 = self.c33 - 2 * self.c66 if self.fastAssembler: C = [("c11", self.c11), ("c23", self.c23), ("c13", self.c13), ("c33", self.c33), ("c44", self.c44), ("c66", self.c66)] if "speckley" in domain.getDescription().lower(): C = [(n, escript.interpolate(d, escript.ReducedFunction(domain))) for n, d in C] self.__mypde = lpde.WavePDE(domain, C) else: self.__mypde = lpde.LinearPDESystem(domain) self.__mypde.setValue(X=self.__mypde.createCoefficient('X')) if lumping: self.__mypde.getSolverOptions().setSolverMethod( lpde.SolverOptions.HRZ_LUMPING) self.__mypde.setSymmetryOn() self.__mypde.setValue(D=rho * escript.kronecker(DIM)) self.__source_tag = source_tag if DIM == 2: source_vector = [source_vector[0], source_vector[2]] self.__r = escript.Vector( 0, escript.DiracDeltaFunctions(self.__mypde.getDomain())) self.__r.setTaggedValue(self.__source_tag, source_vector)
def getPotential(self): """ Returns 3 list each made up of a number of list containing primary, secondary and total potentials diferences. Each of the lists contain a list for each value of n. """ primCon=self.primaryConductivity coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi)) A = self.secondaryConductivity * es.kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondaryList = [] delPhiPrimaryList = [] delPhiTotalList = [] for i in range(1,self.n+1): # 1 to n maxR = self.numElectrodes - 1 - i #max amount of readings that will fit in the survey delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] for j in range(maxR): analyticRs=es.Data(0,(3,),es.ContinuousFunction(self.domain)) analyticRs[0]=(coords[0]-self.electrodes[j][0]) analyticRs[1]=(coords[1]-self.electrodes[j][1]) analyticRs[2]=(coords[2]) rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5 analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(es.whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0 analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5 analyticRsPolePower = analyticRsPolePower+(es.whereZero(analyticRsPolePower)*0.0000001) gradUPrimary = es.Data(0,(3,),es.ContinuousFunction(self.domain)) gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower) gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower) gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower) gradUPrimary=-gradUPrimary X=(primCon-self.secondaryConductivity) * gradUPrimary pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[i+j],self.electrodes[i+j+1]]) valPrimary=loc.getValue(analyticPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j]) delPhiPrimaryList.append(delPhiPrimary) delPhiSecondaryList.append(delPhiSecondary) delPhiTotalList.append(delPhiTotal) self.delPhiPrimaryList=delPhiPrimaryList self.delPhiSecondaryList=delPhiSecondaryList self.delPhiTotalList = delPhiTotalList return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
def getPotential(self): """ returns a list containing 3 lists one for each the primary, secondary and total potential. """ primCon=self.primaryConductivity coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() DIM=self.domain.getDim() x=self.domain.getX() q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi)) A = self.secondaryConductivity * es.kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] if(len(self.electrodes[0])==3): for i in range(self.numElectrodes-3): analyticRsOne=es.Data(0,(3,),es.ContinuousFunction(self.domain)) analyticRsOne[0]=(coords[0]-self.electrodes[i][0]) analyticRsOne[1]=(coords[1]-self.electrodes[i][1]) analyticRsOne[2]=(coords[2]) rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5 analyticRsTwo=es.Data(0,(3,),es.ContinuousFunction(self.domain)) analyticRsTwo[0]=(coords[0]-self.electrodes[i+3][0]) analyticRsTwo[1]=(coords[1]-self.electrodes[i+3][1]) analyticRsTwo[2]=(coords[2]) rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5 rsMagOne+=(es.whereZero(rsMagOne)*0.0000001) rsMagTwo+=(es.whereZero(rsMagTwo)*0.0000001) analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagOne))-(self.current/(2*pi*primCon*rsMagTwo)) analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5 analyticRsOnePower = analyticRsOnePower+(es.whereZero(analyticRsOnePower)*0.0001) analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5 analyticRsTwoPower = analyticRsTwoPower+(es.whereZero(analyticRsTwoPower)*0.0001) gradAnalyticPrimaryPot = es.Data(0,(3,),es.ContinuousFunction(self.domain)) gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) \ * ((-analyticRsOne[0]/analyticRsOnePower) \ + (analyticRsTwo[0]/analyticRsTwoPower)) gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) \ * ((-analyticRsOne[1]/analyticRsOnePower) \ + (analyticRsTwo[1]/analyticRsTwoPower)) gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) \ * ((-analyticRsOne[2]/analyticRsOnePower) + (analyticRsTwo[2]/analyticRsTwoPower)) X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot) pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[i+1],self.electrodes[i+2]]) valPrimary=loc.getValue(analyticPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[i]+delPhiSecondary[i]) else: raise NotImplementedError("2d forward model is not yet implemented") self.delPhiSecondary = delPhiSecondary self.delPhiPrimary = delPhiPrimary self.delPhiTotal=delPhiTotal return [delPhiPrimary, delPhiSecondary, delPhiTotal]
def getPotentialAnalytic(self): """ Returns 3 list each made up of a number of list containing primary, secondary and total potentials diferences. Each of the lists contain a list for each value of n. """ coords=self.domain.getX() pde=LinearPDE(self.domain, numEquations=1) tol=1e-8 pde.getSolverOptions().setTolerance(tol) pde.setSymmetryOn() primCon=self.primaryConductivity DIM=self.domain.getDim() x=self.domain.getX() q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1])) for i in xrange(DIM-1): xi=x[i] q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi)) A = self.secondaryConductivity * es.kronecker(self.domain) pde.setValue(A=A,q=q) delPhiSecondaryList = [] delPhiPrimaryList = [] delPhiTotalList = [] for i in range(1,self.n+1): # 1 to n maxR = self.numElectrodes - 1 - (2*i) #max amount of readings that will fit in the survey delPhiSecondary = [] delPhiPrimary = [] delPhiTotal = [] for j in range(maxR): analyticRsOne=es.Data(0,(3,),es.ContinuousFunction(self.domain)) analyticRsOne[0]=(coords[0]-self.electrodes[j][0]) analyticRsOne[1]=(coords[1]-self.electrodes[j][1]) analyticRsOne[2]=(coords[2]) rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5 analyticRsTwo=es.Data(0,(3,),es.ContinuousFunction(self.domain)) analyticRsTwo[0]=(coords[0]-self.electrodes[j + ((2*i) + 1)][0]) analyticRsTwo[1]=(coords[1]-self.electrodes[j + ((2*i) + 1)][1]) analyticRsTwo[2]=(coords[2]) rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5 self.sources.append([self.electrodeTags[j], self.electrodeTags[j + ((2*i) + 1)]]) rsMagOne+=(es.whereZero(rsMagOne)*0.0000001) rsMagTwo+=(es.whereZero(rsMagTwo)*0.0000001) analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagOne))-(self.current/(2*pi*primCon*rsMagTwo)) analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5 analyticRsOnePower = analyticRsOnePower+(es.whereZero(analyticRsOnePower)*0.0001) analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5 analyticRsTwoPower = analyticRsTwoPower+(es.whereZero(analyticRsTwoPower)*0.0001) gradAnalyticPrimaryPot = es.Data(0,(3,),es.ContinuousFunction(self.domain)) gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) * ((-analyticRsOne[0]/analyticRsOnePower) + (analyticRsTwo[0]/analyticRsTwoPower)) gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) * ((-analyticRsOne[1]/analyticRsOnePower) + (analyticRsTwo[1]/analyticRsTwoPower)) gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) * ((-analyticRsOne[2]/analyticRsOnePower) + (analyticRsTwo[2]/analyticRsTwoPower)) X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot) pde.setValue(X=X) u=pde.getSolution() loc=Locator(self.domain,[self.electrodes[j+i],self.electrodes[j+i+1]]) self.samples.append([self.electrodeTags[j+i],self.electrodeTags[j+i+1]]) valPrimary=loc.getValue(analyticPrimaryPot) valSecondary=loc.getValue(u) delPhiPrimary.append(valPrimary[1]-valPrimary[0]) delPhiSecondary.append(valSecondary[1]-valSecondary[0]) delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j]) delPhiPrimaryList.append(delPhiPrimary) delPhiSecondaryList.append(delPhiSecondary) delPhiTotalList.append(delPhiTotal) self.delPhiPrimaryList=delPhiPrimaryList self.delPhiSecondaryList=delPhiSecondaryList self.delPhiTotalList = delPhiTotalList return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
def __init__(self, domain, v_p, v_s, wavelet, source_tag, source_vector=[0., 1.], eps=0., delta=0., theta=0., rho=1., dt=None, u0=None, v0=None, absorption_zone=300 * U.m, absorption_cut=1e-2, lumping=True): """ initialize the TTI wave solver :param domain: domain of the problem :type domain: `Domain` :param v_p: vertical p-velocity field :type v_p: `escript.Scalar` :param v_s: vertical s-velocity field :type v_s: `escript.Scalar` :param wavelet: wavelet to describe the time evolution of source term :type wavelet: `Wavelet` :param source_tag: tag of the source location :type source_tag: 'str' or 'int' :param source_vector: source orientation vector :param eps: first Thompsen parameter :param delta: second Thompsen parameter :param theta: tilting (in Rad) :param rho: density :param dt: time step size. If not present a suitable time step size is calculated. :param u0: initial solution. If not present zero is used. :param v0: initial solution change rate. If not present zero is used. :param absorption_zone: thickness of absorption zone :param absorption_cut: boundary value of absorption decay factor :param lumping: if True mass matrix lumping is being used. This is accelerates the computing but introduces some diffusion. """ cos = escript.cos sin = escript.sin DIM = domain.getDim() if not DIM == 2: raise ValueError("Only 2D is supported.") f = createAbsorptionLayerFunction( escript.Function(domain).getX(), absorption_zone, absorption_cut) v_p = v_p * f v_s = v_s * f if u0 == None: u0 = escript.Vector(0., escript.Solution(domain)) else: u0 = escript.interpolate(p0, escript.Solution(domain)) if v0 == None: v0 = escript.Vector(0., escript.Solution(domain)) else: v0 = escript.interpolate(v0, escript.Solution(domain)) if dt == None: dt = min((1. / 5.) * min(escript.inf(domain.getSize() / v_p), escript.inf(domain.getSize() / v_s)), wavelet.getTimeScale()) super(TTIWave, self).__init__(dt, u0=u0, v0=v0, t0=0.) self.__wavelet = wavelet self.__mypde = lpde.LinearPDESystem(domain) if lumping: self.__mypde.getSolverOptions().setSolverMethod( lpde.SolverOptions.HRZ_LUMPING) self.__mypde.setSymmetryOn() self.__mypde.setValue(D=rho * escript.kronecker(DIM), X=self.__mypde.createCoefficient('X')) self.__source_tag = source_tag self.__r = escript.Vector( 0, escript.DiracDeltaFunctions(self.__mypde.getDomain())) self.__r.setTaggedValue(self.__source_tag, source_vector) c0_33 = v_p**2 * rho c0_66 = v_s**2 * rho c0_11 = (1 + 2 * eps) * c0_33 c0_13 = escript.sqrt(2 * c0_33 * (c0_33 - c0_66) * delta + (c0_33 - c0_66)**2) - c0_66 self.c11 = c0_11 * cos(theta)**4 - 2 * c0_13 * cos( theta)**4 + 2 * c0_13 * cos(theta)**2 + c0_33 * sin( theta)**4 - 4 * c0_66 * cos(theta)**4 + 4 * c0_66 * cos( theta)**2 self.c13 = -c0_11 * cos(theta)**4 + c0_11 * cos( theta)**2 + c0_13 * sin(theta)**4 + c0_13 * cos( theta)**4 - c0_33 * cos(theta)**4 + c0_33 * cos( theta)**2 + 4 * c0_66 * cos(theta)**4 - 4 * c0_66 * cos( theta)**2 self.c16 = (-2 * c0_11 * cos(theta)**2 - 4 * c0_13 * sin(theta)**2 + 2 * c0_13 + 2 * c0_33 * sin(theta)**2 - 8 * c0_66 * sin(theta)**2 + 4 * c0_66) * sin(theta) * cos(theta) / 2 self.c33 = c0_11 * sin(theta)**4 - 2 * c0_13 * cos( theta)**4 + 2 * c0_13 * cos(theta)**2 + c0_33 * cos( theta)**4 - 4 * c0_66 * cos(theta)**4 + 4 * c0_66 * cos( theta)**2 self.c36 = (2 * c0_11 * cos(theta)**2 - 2 * c0_11 + 4 * c0_13 * sin(theta)**2 - 2 * c0_13 + 2 * c0_33 * cos(theta)**2 + 8 * c0_66 * sin(theta)**2 - 4 * c0_66) * sin(theta) * cos(theta) / 2 self.c66 = -c0_11 * cos(theta)**4 + c0_11 * cos( theta)**2 + 2 * c0_13 * cos(theta)**4 - 2 * c0_13 * cos( theta)**2 - c0_33 * cos(theta)**4 + c0_33 * cos( theta)**2 + c0_66 * sin(theta)**4 + 3 * c0_66 * cos( theta)**4 - 2 * c0_66 * cos(theta)**2
def solve_steady_state_pressure_eq_new(mesh, topo_gradient, pressure_pde, rho_f, k_tensor, k_vector, viscosity, g_vector, fluid_source, rch_bnd_loc, recharge_mass_flux, specified_pressure_bnd, specified_pressure, drain_bnd_loc, fluid_density, proj, debug=True): """ Solve the steady-state fluid flow equation for fluid pressure using escript with optional seepage bnd condition. """ year = 365.25 * 24 * 60 * 60.0 # calculate boundary flux specified_flux = rch_bnd_loc * recharge_mass_flux a = rho_f * k_tensor / viscosity * es.kronecker(mesh) d = 0.0 x = rho_f**2 * k_vector / viscosity * g_vector y = fluid_source # pressure_pde.setValue(A=a, D=d, X=x, Y=y, y=specified_flux, q=specified_pressure_bnd, r=specified_pressure) # calculate pressure, without seepage bnd pressure = pressure_pde.getSolution() debug = True if debug is True: print('initial calculated steady-state pressure: ', pressure) if es.sup(drain_bnd_loc) == 0: print('no drain or seepage bnd') active_seepage_bnd = es.wherePositive(drain_bnd_loc) return pressure, active_seepage_bnd # check if h > surface elevation #pressure_threshold = 0.01 * 9.81 * 1000.0 pressure_threshold = 0.0 active_seepage_bnd = es.wherePositive(drain_bnd_loc * pressure - pressure_threshold) if debug is True: print('active seepage nodes: ', np.sum(np.array(active_seepage_bnd.toListOfTuples()))) print('potential seepage nodes: ', np.sum(np.array(drain_bnd_loc.toListOfTuples()))) n_seepage_change = 9999 n_seepage_nodes = 99999 n_iter = 0 max_seepage_iter = 2000 while n_seepage_change > 0 and n_iter < max_seepage_iter: # add seepage to specified pressure bnd and update bnd conditions specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd) active_rch_bnd = rch_bnd_loc specified_flux = active_rch_bnd * recharge_mass_flux pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod, y=specified_flux) # recalculate pressure pressure = pressure_pde.getSolution() if debug is True: print('new pressure: ', pressure) # calculate flux q = calculate_q(k_vector, viscosity, pressure, fluid_density, g_vector) proj.setValue(D=es.kronecker(mesh), Y=q) try: nodal_q = proj.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # calculate max vertical flux into the model domain at # drain bnd nodes # using outer norm to calculate correct bnd flux does not work because # flux cannot be interpolated to same functionspace as pressure variable #nodal_q_norm_bnd = nodal_q * nodal_q.getDomain().getNormal() #nodal_q_norm = es.interpolate(nodal_q_norm_bnd, active_seepage_bnd.getFunctionSpace()) nodal_q_norm = rotate_vector_escript(nodal_q, topo_gradient) flux_seepage_bnd = active_seepage_bnd * nodal_q_norm[1] # remove inlfow nodes that are <= recharge flux_seepage_bnd_corr = flux_seepage_bnd + recharge_mass_flux / fluid_density # changed back to old method to speed up seepage bnd calc #flux_seepage_bnd_corr = flux_seepage_bnd # and remove only the worst x % of seepage nodes to avoid oscillations: seepage_threshold = es.inf(flux_seepage_bnd_corr) * 0.5 #seepage_threshold = 0.0 seepage_inflow_nodes = \ es.whereNegative(flux_seepage_bnd_corr - seepage_threshold) removed_seepage_inflow_nodes = seepage_inflow_nodes if debug is True: print('number of seepage inflow nodes: ', \ np.sum(np.array(seepage_inflow_nodes.toListOfTuples()))) xmin_seep = es.inf(seepage_inflow_nodes * seepage_inflow_nodes.getDomain().getX()[0] + (1 - seepage_inflow_nodes) * 999999) xmax_seep = es.sup(seepage_inflow_nodes * seepage_inflow_nodes.getDomain().getX()[0]) print('from x= %0.2f m to x= %0.2f m' % (xmin_seep, xmax_seep)) # add boundary nodes with P>0 to seepage bnd new_seepage_nodes = \ es.wherePositive(drain_bnd_loc * (1 - active_seepage_bnd) * pressure) if debug is True: print('number of new seepage nodes: ', \ np.sum(np.array(new_seepage_nodes.toListOfTuples()))) # update the seepage bnd active_seepage_bnd = (active_seepage_bnd + new_seepage_nodes - removed_seepage_inflow_nodes) n_seepage_nodes_old = n_seepage_nodes n_seepage_nodes = np.sum(np.array(active_seepage_bnd.toListOfTuples())) n_seepage_change = np.abs(n_seepage_nodes_old - n_seepage_nodes) if debug is True: print('final active seepage nodes: ', np.sum(np.array(active_seepage_bnd.toListOfTuples()))) print('potential seepage nodes: ', np.sum(np.array(drain_bnd_loc.toListOfTuples()))) if n_seepage_nodes_old < n_seepage_nodes: print( 'lowest number of seepage nodes reached, stopping iterations') n_seepage_change = 0 print('seepage iteration %i' % n_iter) print('seepage threshold ', seepage_threshold * year) print('change in seepage nodes from %0.0f to %0.0f' % (n_seepage_nodes_old, n_seepage_nodes)) n_iter += 1 # update specified pressure bnd condition specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd) active_rch_bnd = rch_bnd_loc specified_flux = active_rch_bnd * recharge_mass_flux pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod, y=specified_flux) # recalculate pressure pressure = pressure_pde.getSolution() if debug is True: print('final pressure: ', pressure) return pressure, active_seepage_bnd
def iterate_coupled_flow_eqs(mesh, topo_gradient, pressure_pde, solute_pde, pressure_convergence_criterion, concentration_convergence_criterion, min_iterations, max_iterations, dt, g_vector, pressure, concentration, rho_f, phi, diffusivity, l_disp, t_disp, solute_source, specific_storage, k_tensor, k_vector, dispersion_tensor, viscosity, gamma, alpha, fluid_source, rho_f_0, specified_pressure_bnd, specified_pressure, specified_concentration_bnd, specified_concentration, specified_concentration_rho_f, rch_bnd_loc, recharge_mass_flux, coupled_iterations=True, solute_transport=True, heat_transport=False, steady_state=False, proj=None, drain_loc=None, seepage_bnd=False, recalculate_seepage_bnd=True, active_seepage_bnd=None, concentration_bnd_inflow_only=False, concentration_bnd_inflow_direction='up', max_allowed_CFL_number=None, force_CFL_timestep=False, dt_max=None, calculate_viscosity=False, verbose=False, iterate_seepage_in_one_timestep=False, max_seepage_iterations=50, ignore_convergence_failure=False): """ Iterative solve groundwater flow, solute transport and heat flow equations. solves either steady state or 1 timestep in implicit or explicit mode iterative coupling scheme of solute transport, pressure & flow eqs. and eqs of state follows Ackerer (2004), Geophysical Research Letters 31(12) Parameters --------- mesh : escript mesh object pressure_pde : groundwater flow PDE solute_pde solute transport PDE pressure_convergence_criterion : float convergence criterion groundwater flow eq. (Pa) concentration_convergence_criterion : float convergence criterion solute transport eq. (kg/kg) max_iterations : int max number of iterations dt : int timestep g_vector : gravity vector (0,g) pressure : pressure (Pa) concentration : solute concentration (kg/kg) rho_f : fluid density (kg / m3) phi : porosity D : solute diffusivity (...) l_disp : longitudinal dispersivity (...) t_disp : transverse dispersivity (...) solute_source : solute source (units...) specific_storage : specific storativity (...) k : permeability (m2) anisotropy : permeability anisotropy = horizontal/vertical permeability (dimensionless) viscosity : viscosity (...) gamma : ? alpha : ? fluid_source : fluid source term (...) rho_f_0 fluid density at solute concentration C=0 (kg/m3) specified_pressure_bnd location of specified pressure boundary specified_pressure specified pressure (Pa) specified_concentration_bnd location of specified concentration boundary specified_concentration specified concentration (kg/kg) rch_bnd_loc : recharge_mass_flux : float coupled_iterations : bool, optional couple groundwater and solute transport equations iteratively by adjusting density term solute_transport : bool, optional if True, simulate solute transport heat_transport : bool, optional if True, simulate heat transport steady_state : bool, optional True for steady state groundwater flow, False for transient verbose : bool, optional verbose text output drain_loc : location of drain boundary nodes debug : bool, optional debugging dt_max : float? =None ... proj : escript PDE for projecting element data to nodes seepage_optimization_automated : boolean Returns ------- pressure_t2_i2 : pressure at next timestep (t2) and last iteration (i2) concentration_t2_i2 : solute concentration (kg/kg) rho_f_t2_i2 : fluid density iteration : int number of iterations dt_max : max timestep size """ # calculate transverse dispersivity #t_disp = l_disp * disp_ratio year = 365.25 * 24 * 60 * 60. if verbose is True: print('running iterative solver for pressure and concentration PDEs') if coupled_iterations is False: print('pressure and concentration are not coupled') #pressure_new = pressure pressure_old_ts = pressure concentration_old_ts = concentration fluid_density_new = fluid_density_old = rho_f #pressure_t1 = pressure.copy() #concentration_t1 = concentration.copy() # added 22 jun 2016, not sure if this is ok: active_rch_bnd = rch_bnd_loc if coupled_iterations is True and calculate_viscosity is True: viscosity_new = calculate_viscosity_simple(concentration) else: viscosity_new = viscosity active_specified_concentration_bnd = specified_concentration_bnd iteration = 0 converged = False non_convergence = False ele_size = None q = None v = None while converged is False and non_convergence is False: if verbose is True: print('iteration ', iteration) if iteration > 0: print('pressure convergence ', es.Lsup(pressure_conv)) if solute_transport is True: # get flux q = calculate_q(k_vector, viscosity_new, pressure_old_ts, fluid_density_new, g_vector) v = q / phi # calculate new solute concentration concentration_old_iteration = concentration # finite element solute transport if concentration_bnd_inflow_only is True and iteration == 0: # only apply concentration bnd for inflow into model domain # assumes a horizontal model bnd # TODO: calculate flux normal to model boundary to account # for non-horizontal upper boundaries proj.setValue(D=es.kronecker(mesh), Y=q) try: nodal_q = proj.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True nodal_q_norm = rotate_vector_escript(nodal_q, topo_gradient) nodal_v = nodal_q / phi if concentration_bnd_inflow_direction == 'up': inflow_bnd = (es.whereNegative(nodal_q_norm[1]) * specified_concentration_bnd) elif concentration_bnd_inflow_direction == 'down': inflow_bnd = (es.wherePositive(nodal_q_norm[1]) * specified_concentration_bnd) elif concentration_bnd_inflow_direction == 'left': inflow_bnd = (es.wherePositive(nodal_q[0]) * specified_concentration_bnd) elif concentration_bnd_inflow_direction == 'right': inflow_bnd = (es.whereNegative(nodal_q[0]) * specified_concentration_bnd) if es.sup(inflow_bnd) > 0: active_specified_concentration_bnd = inflow_bnd else: min_x = es.inf( specified_concentration_bnd * specified_concentration_bnd.getDomain().getX()[0]) active_specified_concentration_bnd = \ (specified_concentration_bnd * es.whereZero( specified_concentration_bnd.getDomain().getX()[0] - min_x)) if verbose is True: print('warning, outflow for all specified ' \ 'concentration boundary nodes') #print 'using entire bnd instead' #active_specified_concentration_bnd = \ # specified_concentration_bnd print('using first node as fixed conc bnd instead') print('number of active conc bnd nodes:') print( np.sum( np.array(active_specified_concentration_bnd. toListOfTuples()))) if verbose is True: import grompy_lib xyi, ia = grompy_lib.convert_to_array( active_specified_concentration_bnd) xyc, ca = grompy_lib.convert_to_array( specified_concentration_bnd) print('inflow conc bnd nodes = %0.0f / %0.0f' \ % (ia.sum(), ca.sum())) print('x = %0.3f - %0.3f' % (xyi[ia == 1, 0].min(), xyi[ia == 1, 0].max())) print('qv conc bnd: ', (nodal_q[1] * specified_concentration_bnd)) #solute_pde.setValue(D=1, # r=specified_concentration_rho_f, # q=active_specified_concentration_bnd) solute_pde.setValue(D=1, r=specified_concentration, q=active_specified_concentration_bnd) solute_pde = update_solute_transport_pde(mesh, solute_pde, concentration_old_ts, v, dt, solute_source, dispersion_tensor, diffusivity, l_disp, t_disp, fluid_density_old) try: #solute_mass = solute_pde.getSolution() concentration = solute_pde.getSolution() except RuntimeError(error_msg): print('!! runtime error ', error_msg) print('solver options: ') print(solute_pde.getSolverOptions().getSummary()) non_convergence = True #raise RuntimeError(error_msg) # calculate concentration, using new solute mass and eq of state #concentration_new = calculate_concentration( # solute_mass, rho_f_0, gamma) #concentration_new = solve_solute_transport_v2( # solute_pde, mesh, # steady_state, # concentration_t1, v, dt, solute_source, # diffusivity, l_disp, t_disp, fluid_density_old, # rho_f_0, gamma) concentration_change_rate = \ (concentration - concentration_old_ts) / dt else: # no solute transport: concentration_change_rate = 0 if heat_transport is True: # no temperature in models yet: temperature_change_rate = 0 else: # no heat transport: temperature_change_rate = 0 if coupled_iterations is True: if verbose is True: print('recalculating fluid density and viscosity') # recalculate fluid density fluid_density_old = fluid_density_new fluid_density_new = \ calculate_fluid_density(concentration, gamma, rho_f_0) if calculate_viscosity is True: viscosity_new = \ calculate_viscosity_simple(concentration) else: # leave fluid density unchanged concentration_change_rate = 0 temperature_change_rate = 0 # store old pressure pressure_old_iteration = pressure if drain_loc is None or es.sup(drain_loc) == 0: # calculate pressure, no drain or seepage bnd pressure_pde = \ update_pressure_pde(pressure_pde, pressure_old_ts, phi, specific_storage, k_tensor, k_vector, fluid_density_new, viscosity_new, dt, rch_bnd_loc, recharge_mass_flux, fluid_source, g_vector, gamma, concentration_change_rate, alpha, temperature_change_rate) try: pressure = pressure_pde.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True #print 'no seepage bnd' else: # implement drain or seepage boundary if seepage_bnd is True: ## use seepage boundary: if active_seepage_bnd is None: # calculate pressure without any drain boundary pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd) active_rch_bnd = rch_bnd_loc else: # incorporate active drain bnd of previous timestep specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod) # do not change active rch bnd active_rch_bnd = rch_bnd_loc #active_rch_bnd = rch_bnd_loc * \ # es.whereZero(specified_pressure_bnd) #specified_flux = rch_bnd_loc * dt * recharge_mass_flux # calculate pressure with existing seepage bnd pressure_pde = \ update_pressure_pde(pressure_pde, pressure_old_ts, phi, specific_storage, k_tensor, k_vector, fluid_density_new, viscosity_new, dt, active_rch_bnd, recharge_mass_flux, fluid_source, g_vector, gamma, concentration_change_rate, alpha, temperature_change_rate) try: pressure = pressure_pde.getSolution() except RuntimeError: print("error, pressure PDE solver failed") converged = True non_convergence = True #if pressure_new not in locals(): # pressure_new = pressure_t1 # assign drain bnd nodes if active_seepage_bnd is None: active_seepage_bnd = \ es.wherePositive(drain_loc * pressure) if iteration < max_seepage_iterations and recalculate_seepage_bnd is True: # adjust seepage boundary, but only for first x iterations # to avoid instability if verbose is True: seepage_xy = active_seepage_bnd.getDomain().getX() seepage_nodes_xy = \ np.array(seepage_xy.toListOfTuples()) seepage_array = np.array( active_seepage_bnd.toListOfTuples()) ind = seepage_array > 0 print('\tbefore adjustment:') print('\tactive seepage bnd from x=%0.0f to %0.0f m' \ % (seepage_nodes_xy[ind, 0].min(), seepage_nodes_xy[ind, 0].max())) # remove seepage nodes that have become source of water q = calculate_q(k_vector, viscosity_new, pressure, fluid_density_new, g_vector) proj.setValue(D=es.kronecker(mesh), Y=q) try: nodal_q = proj.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # calculate max vertical flux into the model domain at # drain bnd nodes # -> not possible, cannot mix face elements and normal elements # later on to adjust seepage... #nodal_q_norm = nodal_q * nodal_q.getDomain().getNormal() # nodal_q_norm = rotate_vector_escript( nodal_q, topo_gradient) #flux_seepage_bnd = active_seepage_bnd * nodal_q[1] flux_seepage_bnd = active_seepage_bnd * nodal_q_norm[1] #flux_seepage_bnd_corr = flux_seepage_bnd + seepage_change_buffer = 1e-3 / year seepage_inflow_nodes = \ es.whereNegative(flux_seepage_bnd + recharge_mass_flux / fluid_density_new) if verbose is True: print('\tflux seepage bnd (m/yr): ', flux_seepage_bnd * year) print('recharge ') print('\tseepage inflow nodes: ', seepage_inflow_nodes) #seepage_inflow_nodes = \ # es.whereNegative(flux_seepage_bnd) removed_seepage_inflow_nodes = seepage_inflow_nodes # add boundary nodes with P>0 to seepage bnd new_seepage_nodes = \ es.wherePositive(drain_loc * (1 - active_seepage_bnd) * pressure) # update the seepage bnd active_seepage_bnd = (active_seepage_bnd + new_seepage_nodes - removed_seepage_inflow_nodes) if verbose is True: seepage_xy = active_seepage_bnd.getDomain().getX() seepage_nodes_xy = \ np.array(seepage_xy.toListOfTuples()) seepage_array = np.array( active_seepage_bnd.toListOfTuples()) ind = np.array(seepage_array > 0) print('\tafter adjustment:') print('\tN=%i active seepage bnd x=%0.0f to %0.0f m' \ % (np.sum(ind.astype(int)), seepage_nodes_xy[ind, 0].min(), seepage_nodes_xy[ind, 0].max())) if iterate_seepage_in_one_timestep is True: # update the specified pressure boundary to include # new seepage nodes specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd_mod) # changed to have steady recharge bnd regardless of seepage bnd, # 11 apr 2016, Elco active_rch_bnd = rch_bnd_loc # experiment, adjust recharge to have 0 rehcarge at seepage nodes # not sure if this makes sense... #specified_flux_adj = active_rch_bnd * dt * recharge_mass_flux # #pressure_pde.setValue(r=specified_pressure, # q=specified_pressure_bnd_mod, # y=specified_flux_adj) pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod) # recalculate pressure #pressure_pde = \ # update_pressure_pde(pressure_pde, # pressure_t1, # phi, specific_storage, # k_tensor, k_vector, # fluid_density_new, # viscosity_new, dt, # rch_bnd_loc, recharge_mass_flux, # fluid_source, g_vector, # gamma, concentration_change_rate, # alpha, temperature_change_rate) # recalculate pressure try: pressure = pressure_pde.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # remove inflow nodes again #q = (k_vector / viscosity_new * # -(es.grad(pressure_new) # - fluid_density_new * g_vector) # / phi) q = calculate_q(k_vector, viscosity_new, pressure, fluid_density_new, g_vector) proj.setValue(D=es.kronecker(mesh), Y=q) nodal_q = proj.getSolution() # calculate max vertical flux into the model domain at # drain bnd nodes #nodal_q_norm = nodal_q * nodal_q.getDomain().getNormal() nodal_q_norm = rotate_vector_escript( nodal_q, topo_gradient) flux_seepage_bnd = active_seepage_bnd * nodal_q_norm[1] #removed_seepage_inflow_nodes = \ # es.whereNegative(flux_seepage_bnd - # seepage_inflow_threshold) #removed_seepage_inflow_nodes = \ # es.whereNegative(flux_seepage_bnd # + recharge_mass_flux # / fluid_density_new) removed_seepage_inflow_nodes = \ es.whereNegative(flux_seepage_bnd) active_seepage_bnd = (active_seepage_bnd - removed_seepage_inflow_nodes) if verbose is True: seepage_xy = active_seepage_bnd.getDomain().getX() seepage_nodes_xy = \ np.array(seepage_xy.toListOfTuples()) seepage_array = np.array( active_seepage_bnd.toListOfTuples()) ind = seepage_array > 0 print( '\tafter 2nd adjustment (removing inflow nodes):' ) print('\tN=%i active seepage bnd from ' \ 'x=%0.0f to %0.0f m' \ % (np.sum(ind.astype(int)), seepage_nodes_xy[ind, 0].min(), seepage_nodes_xy[ind, 0].max())) if iterate_seepage_in_one_timestep is True: # assign updated seepage nodes: specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd_mod) active_rch_bnd = rch_bnd_loc # experiment, adjust recharge to have 0 rehcarge at seepage nodes # not sure if this makes sense... # ! probably the source of long timescale instability of seepage/rch bnd #specified_flux_adj = active_rch_bnd * dt * recharge_mass_flux #pressure_pde.setValue(r=specified_pressure, # q=specified_pressure_bnd_mod, # y=specified_flux_adj) pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod) # recalculate final pressure #pressure_pde = \ # update_pressure_pde(pressure_pde, # pressure_t1, # phi, specific_storage, # k_tensor, k_vector, # fluid_density_new, # viscosity_new, dt, # rch_bnd_loc, recharge_mass_flux, # fluid_source, g_vector, # gamma, concentration_change_rate, # alpha, temperature_change_rate) try: pressure = pressure_pde.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # calculate convergence criteria pressure_conv = pressure - pressure_old_iteration if solute_transport is True: conc_conv = concentration - concentration_old_iteration else: conc_conv = 0.0 # check whether iterations have converged or not: if (es.Lsup(pressure_conv) < pressure_convergence_criterion) and \ (es.Lsup(conc_conv) < concentration_convergence_criterion)\ and iteration + 1 >= min_iterations: if iteration > 0 and verbose is True: print('iterations converged after %i iterations' % iteration) converged = True else: if verbose is True: print('iteration %i, max. pressure change %0.3e ' \ % (iteration, es.Lsup(pressure_conv))) print(' max. C change %0.3e ' \ % (es.Lsup(conc_conv))) if iteration + 1 >= max_iterations: print('warning, reached maximum number of %i iterations' \ % (iteration + 1)) print('iteration %i, max. pressure change %0.3e Pa, ' \ 'convergence at %0.2e Pa' \ % (iteration, es.Lsup(pressure_conv), pressure_convergence_criterion)) print(' max. C change %0.3e kg/kg, ' \ 'convergence at %0.2e kg/kg' \ % (es.Lsup(conc_conv), concentration_convergence_criterion)) converged = True non_convergence = True # check CFL number #max_CFL_number = calculate_CFL_number(q, dt) if ele_size is None: ele_size = q.getDomain().getSize() #print ele_size - q.getDomain().getSize() CFL_number = q * dt / ele_size max_CFL_number = es.Lsup(CFL_number) if max_CFL_number > 0.5 and verbose is True: print('warning, max CFL number = %0.2f, exceeds 0.5' \ % max_CFL_number) # recaclulcate timestep if max timestep exceeds CFL number if (max_allowed_CFL_number is not None and max_CFL_number > max_allowed_CFL_number and iteration == 0) \ or (force_CFL_timestep is True and iteration <= 1): # make sure iteration is repeated converged = False #CFL_number / flux * flux.getDomain().getSize() = dtc / dtc = max_allowed_CFL_number / q * ele_size new_timestep = es.inf((dtc**2)**0.5) if dt_max is not None and new_timestep > dt_max: new_timestep = dt_max dt = new_timestep if verbose is True: print('max CFL number = ', max_CFL_number) print('changing timestep from %0.2e sec to %0.2e sec' \ % (dt, new_timestep)) if coupled_iterations is False: converged = True iteration += 1 return (pressure, concentration, fluid_density_new, viscosity_new, q, v, active_specified_concentration_bnd, active_seepage_bnd, active_rch_bnd, iteration, es.Lsup(pressure_conv), es.Lsup(conc_conv), max_CFL_number, non_convergence, dt)