def update_solute_transport_pde(mesh, solute_pde, concentration_old, v, dt, solute_source, dispersion_tensor, diffusivity, l_disp, t_disp, rho_f, verbose=False): """ Solve the solute transport equation. """ # calculate hydrodynamic dispersivity tensor from molecular diffusivity # and longitudinal and transverse dispersivity # calculate absolute velocity v_abs = (v[0]**2 + v[1]**2)**0.5 # get rid of 0 values of velocity v_abs_1 = es.whereZero(v_abs) * 1e-20 + es.whereNonZero(v_abs) * v_abs Dxx = l_disp * (v[0]**2) / v_abs_1 + diffusivity Dyy = t_disp * (v[1]**2) / v_abs_1 + diffusivity # and set dispersion tensor to 0 where 0 absolute velocity Dxx = (Dxx * es.whereNonZero(v_abs) + es.whereZero(v_abs) * diffusivity) Dyy = (Dyy * es.whereNonZero(v_abs) + es.whereZero(v_abs) * diffusivity) # horizontal and vertical values of dispersion (Dxx and Dyy) dispersion_tensor[0, 0] = Dxx dispersion_tensor[1, 1] = Dyy # off-diagonal terms in tensor (Dxy and Dyx) # set to 0 for now, model becomes numerically unstable # testing with fixed values did not result in significant cahnges in solute conc... # todo, figure out why this is unstable or check the potential error dispersion_tensor[0, 1] = (l_disp - t_disp) * (v[0] * v[1]) / v_abs_1 dispersion_tensor[1, 0] = (l_disp - t_disp) * (v[0] * v[1]) / v_abs_1 u_old = concentration_old a_coeff = dt * dispersion_tensor * es.kronecker(mesh) c_coeff = dt * v d_coeff = 1 y_coeff = u_old + solute_source * dt if verbose is True: print('solute transport coefficients') print('A: ', a_coeff.getShape(), a_coeff) print('C: ', c_coeff.getShape(), c_coeff) print('D: ', d_coeff) print('Y: ', y_coeff.getShape(), y_coeff) solute_pde.setValue(A=a_coeff, C=c_coeff, D=d_coeff, Y=y_coeff) return solute_pde
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 test_yDirection(self): dim=self.domain.getDim() if dim==3: return u = Symbol('u',(2,), dim=dim) q = Symbol('q', (2,2)) theta = Symbol('theta') theta=3.141/6 q[0,0]=cos(theta) q[0,1]=-sin(theta) q[1,0]=sin(theta) q[1,1]=cos(theta) sigma = Symbol('sigma',(2,2)) p = NonlinearPDE(self.domain, u, debug=0) epsilon = symmetric(grad(u)) # epsilon = matrixmult(matrixmult(q,epsilon0),q.transpose(1)) c00=10;c01=8;c05=0 c01=8;c11=10;c15=0 c05=0;c15=0;c55=1 sigma[0,0]=c00*epsilon[0,0]+c01*epsilon[1,1]+c05*2*epsilon[1,0] sigma[1,1]=c01*epsilon[0,0]+c11*epsilon[1,1]+c15*2*epsilon[1,0] sigma[0,1]=c05*epsilon[0,0]+c15*epsilon[1,1]+c55*2*epsilon[1,0] sigma[1,0]=sigma[0,1] # sigma0=matrixmult(matrixmult(q.transpose(1),epsilon),q) x = self.domain.getX() gammaD=whereZero(x[1])*[1,1]#+whereZero(x[0])*[1,0]+whereZero(x[0]-1)*[1,0] yconstraint = FunctionOnBoundary(self.domain).getX()[1] p.setValue(X=sigma,q=gammaD,y=[-50,0]*whereZero(yconstraint-1),r=[1,1]) v = p.getSolution(u=[0,0]) x=np.ndarray((2,)) x[0]=0.5 x[1]=0.5 loc=Locator(v.getFunctionSpace(),x) valAtX=loc(v) self.assertTrue(valAtX[0]>10*valAtX[1])
def getInverse(self, s): """ returns the value of the inverse of the mapping for s """ ms = whereZero(s) ms0 = whereZero(self.__sigma0) m = 1 / self.__a * log(((1 - ms) * s + ms * 1) / ((1 - ms0) * self.__sigma0 + ms0 * 1)) * (1 - ms) * (1 - ms0) return m
def test_run(self): #test just to confirm nlpde works u=Symbol('u', dim=self.domain.getDim()) nlpde = NonlinearPDE(self.domain, u) x=self.domain.getX() gammaD=whereZero(x[0])+whereZero(x[1]) nlpde.setValue(X=grad(u), Y=5*u, q=gammaD, r=1) v=nlpde.getSolution(u=1)
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 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 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 getInverse(self, s): """ returns the value of the inverse of the mapping for s """ ms = whereZero(s) ms0 = whereZero(self.__sigma0) m = 1 / self.__a * log( ((1 - ms) * s + ms * 1) / ((1 - ms0) * self.__sigma0 + ms0 * 1)) * (1 - ms) * (1 - ms0) 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() 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 setCoefficients(self, pde, system): """sets PDE coefficients""" FAC_DIAG = self.FAC_DIAG FAC_OFFDIAG =self.FAC_OFFDIAG x = Solution(self.domain).getX() mask = whereZero(x[0]) dim = self.domain.getDim() u_ex = self.getSolution(system) g_ex = self.getGrad(system) if system: A = Tensor4(0., Function(self.domain)) for i in range(dim): A[i,:,i,:] = kronecker(dim) Y = Vector(0., Function(self.domain)) if dim == 2: Y[0] = u_ex[0]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG-20 Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG-10 else: Y[0] = u_ex[0]*FAC_DIAG+u_ex[2]*FAC_OFFDIAG+u_ex[1]*FAC_OFFDIAG-60 Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG+u_ex[2]*FAC_OFFDIAG-20 Y[2] = u_ex[2]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG+u_ex[0]*FAC_OFFDIAG-22 pde.setValue(r=u_ex, q=mask*numpy.ones(dim,), A=A, D=kronecker(dim)*(FAC_DIAG-FAC_OFFDIAG)+numpy.ones((dim,dim))*FAC_OFFDIAG, Y=Y, y=matrixmult(g_ex,self.domain.getNormal())) else: pde.setValue(r=u_ex, q=mask, A=kronecker(dim), y=inner(g_ex, self.domain.getNormal())) if dim == 2: pde.setValue(Y=-20.) else: pde.setValue(Y=-60.)
def _setCoefficients(self, pde, system): """sets PDE coefficients""" FAC_DIAG = 1. FAC_OFFDIAG = -0.4 x = Solution(self.domain).getX() mask = whereZero(x[0]) dim = self.domain.getDim() u_ex = self._getSolution(system) g_ex = self._getGrad(system) if system: A = Tensor4(0., Function(self.domain)) for i in range(dim): A[i,:,i,:] = kronecker(dim) Y = Vector(0., Function(self.domain)) if dim == 2: Y[0] = u_ex[0]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG else: Y[0] = u_ex[0]*FAC_DIAG+u_ex[2]*FAC_OFFDIAG+u_ex[1]*FAC_OFFDIAG Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG+u_ex[2]*FAC_OFFDIAG Y[2] = u_ex[2]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG+u_ex[0]*FAC_OFFDIAG pde.setValue(r=u_ex, q=mask*numpy.ones(dim,), A=A, D=kronecker(dim)*(FAC_DIAG-FAC_OFFDIAG)+numpy.ones((dim,dim))*FAC_OFFDIAG, Y=Y, y=matrixmult(g_ex,self.domain.getNormal())) else: pde.setValue(r=u_ex, q=mask, A=kronecker(dim), y=inner(g_ex, self.domain.getNormal()))
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 __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 test_setVals1eq(self): #test setting Coefficients with 1 equation dim=self.domain.getDim() u=Symbol('u', dim=dim) nlpde = NonlinearPDE(self.domain, u) x=self.domain.getX() gammaD=whereZero(x[0])+whereZero(x[1]) nlpde.setValue(X=grad(u), Y=5*u, q=gammaD, r=1) A=nlpde.getCoefficient("A") B=nlpde.getCoefficient("B") C=nlpde.getCoefficient("C") D=nlpde.getCoefficient("D") if dim==2: ATest=numpy.empty((2,2), dtype=object) ATest[0]=1,0 ATest[1]=0,1 BTest=numpy.empty((2,), dtype=object) BTest[0]=0 BTest[1]=0 CTest=BTest self.assertTrue(A-ATest==Symbol(numpy.zeros((2,2)))) self.assertTrue(B-BTest==Symbol(numpy.zeros((2,)))) self.assertTrue(C-CTest==Symbol(numpy.zeros((2,)))) temp=Symbol('temp') self.assertTrue(D-temp.subs(temp,5)==temp.subs(temp,0)) else: ATest=numpy.empty((3,3), dtype=object) ATest[0]=1,0,0 ATest[1]=0,1,0 ATest[2]=0,0,1 BTest=numpy.empty((3,), dtype=object) BTest[0]=0 BTest[1]=0 BTest[2]=0 CTest=BTest self.assertTrue(A-ATest==Symbol(numpy.zeros((3,3)))) self.assertTrue(B-BTest==Symbol(numpy.zeros((3,)))) self.assertTrue(C-CTest==Symbol(numpy.zeros((3,)))) temp=Symbol('temp') self.assertTrue(D-temp.subs(temp,5)==temp.subs(temp,0))
def test_setVals2eq(self): #test setting Coefficients with 2 coeficients dim = self.domain.getDim() u = Symbol('u', (2, ), dim=dim) q = Symbol('q', (2, 2)) nlpde = NonlinearPDE(self.domain, u, debug=0) x = self.domain.getX() gammaD = whereZero( x[1]) * [1, 1] #+whereZero(x[0])*[1,0]+whereZero(x[0]-1)*[1,0] yconstraint = FunctionOnBoundary(self.domain).getX()[1] nlpde.setValue(X=grad(u), q=gammaD, Y=[-50, -50] * u) A = nlpde.getCoefficient("A") B = nlpde.getCoefficient("B") C = nlpde.getCoefficient("C") D = nlpde.getCoefficient("D") if dim == 2: ATest = numpy.empty((2, 2, 2, 2), dtype=object) ATest[0] = (((1, 0), (0, 0)), ((0, 1), (0, 0))) ATest[1] = (((0, 0), (1, 0)), ((0, 0), (0, 1))) BTest = numpy.empty((2, 2, 2), dtype=object) BTest[0] = 0 BTest[1] = 0 CTest = BTest DTest = numpy.empty((2, 2), dtype=object) DTest[0] = (-50, 0) DTest[1] = (0, -50) self.assertTrue(numpy.ndarray.__eq__(ATest, A).all()) self.assertTrue(numpy.ndarray.__eq__(BTest, B).all()) self.assertTrue(numpy.ndarray.__eq__(CTest, C).all()) self.assertTrue(numpy.ndarray.__eq__(DTest, D).all()) else: ATest = numpy.empty((2, 3, 2, 3), dtype=object) ATest[0] = (((1, 0, 0), (0, 0, 0)), ((0, 1, 0), (0, 0, 0)), ((0, 0, 1), (0, 0, 0))) ATest[1] = (((0, 0, 0), (1, 0, 0)), ((0, 0, 0), (0, 1, 0)), ((0, 0, 0), (0, 0, 1))) #ATest[1]=(((0,0,1),(1,0,0)),((0,0),(0,1))) BTest = numpy.empty((2, 3, 2), dtype=object) BTest[0] = 0 BTest[1] = 0 CTest = numpy.empty((2, 2, 3), dtype=object) CTest[0] = 0 CTest[1] = 0 DTest = numpy.empty((2, 2), dtype=object) DTest[0] = (-50, 0) DTest[1] = (0, -50) self.assertTrue(numpy.ndarray.__eq__(BTest, B).all()) self.assertTrue(numpy.ndarray.__eq__(CTest, C).all()) self.assertTrue(numpy.ndarray.__eq__(DTest, D).all())
def test_setVals2eq(self): #test setting Coefficients with 2 coeficients dim=self.domain.getDim() u = Symbol('u',(2,), dim=dim) q = Symbol('q', (2,2)) nlpde = NonlinearPDE(self.domain, u, debug=0) x = self.domain.getX() gammaD=whereZero(x[1])*[1,1]#+whereZero(x[0])*[1,0]+whereZero(x[0]-1)*[1,0] yconstraint = FunctionOnBoundary(self.domain).getX()[1] nlpde.setValue(X=grad(u),q=gammaD,Y=[-50,-50]*u) A=nlpde.getCoefficient("A") B=nlpde.getCoefficient("B") C=nlpde.getCoefficient("C") D=nlpde.getCoefficient("D") if dim==2: ATest=numpy.empty((2,2,2,2),dtype=object) ATest[0]=(((1,0),(0,0)),((0,1),(0,0))) ATest[1]=(((0,0),(1,0)),((0,0),(0,1))) BTest=numpy.empty((2,2,2),dtype=object) BTest[0]=0 BTest[1]=0 CTest=BTest DTest=numpy.empty((2,2),dtype=object) DTest[0]=(-50,0) DTest[1]=(0,-50) self.assertTrue(numpy.ndarray.__eq__(ATest, A).all()) self.assertTrue(numpy.ndarray.__eq__(BTest, B).all()) self.assertTrue(numpy.ndarray.__eq__(CTest, C).all()) self.assertTrue(numpy.ndarray.__eq__(DTest, D).all()) else: ATest=numpy.empty((2,3,2,3),dtype=object) ATest[0]=(((1,0,0),(0,0,0)),((0,1,0),(0,0,0)),((0,0,1),(0,0,0))) ATest[1]=(((0,0,0),(1,0,0)),((0,0,0),(0,1,0)),((0,0,0),(0,0,1))) #ATest[1]=(((0,0,1),(1,0,0)),((0,0),(0,1))) BTest=numpy.empty((2,3,2),dtype=object) BTest[0]=0 BTest[1]=0 CTest=numpy.empty((2,2,3),dtype=object) CTest[0]=0 CTest[1]=0 DTest=numpy.empty((2,2),dtype=object) DTest[0]=(-50,0) DTest[1]=(0,-50) self.assertTrue(numpy.ndarray.__eq__(BTest, B).all()) self.assertTrue(numpy.ndarray.__eq__(CTest, C).all()) self.assertTrue(numpy.ndarray.__eq__(DTest, D).all())
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 setup_rectangular_mesh(Parameters, mesh_filename): """ Create a rectangular mesh. """ nx = int(math.ceil(Parameters.L / Parameters.cellsize_x)) ny = int(math.ceil(Parameters.thickness / Parameters.cellsize_y)) mesh = fl.Rectangle(l0=Parameters.L, l1=Parameters.thickness, n0=nx, n1=ny) # calculate surface xy = mesh.getX() z_surface = (xy[0] - xy[0] + 1) * Parameters.thickness surface = es.whereZero(xy[1] - z_surface) sea_surface = None seawater = None return mesh, surface, sea_surface, seawater, z_surface
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 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 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]
# read in the FEILDS Voxet initial_model = readVoxet(dom, MODEL_DATASET, MODEL_PROPERTY, MODEL_ORIGIN, 0., COORDINATES) # set the extents of the desired "region" for clipping and computation, # mask is = 1 OUTSIDE area of interest mask_air = whereNonNegative(dom.getX()[2]+spacing[2]/2) #reg_mask for air layer mask_LONG = wherePositive(Longitude_W-datacoords[0]) + whereNegative(Longitude_E-datacoords[0]) #reg_mask for longitude mask_LAT = whereNegative(Latitude_N-datacoords[1]) + wherePositive(Latitude_S-datacoords[1]) #reg_mask for latitude mask_h = wherePositive(datacoords[2]+(h_top/100)) + whereNegative(datacoords[2]+(h_base/100)) #reg_mask for depth if ReverseSelection: reg_mask = whereNonZero(mask_LONG+mask_LAT+mask_h) else: reg_mask = whereZero(mask_LONG+mask_LAT+mask_h) # prior to any computation, write out the selected region model as CSV # and Silo if requested fn = os.path.join(OUTPUTDIR, "region_%s")%(MODEL_PROPERTY) saveDataCSV(fn+".csv", Long=datacoords[0], Lat=datacoords[1], h=datacoords[2], PROPERTY=initial_model, mask=reg_mask) print("CSV file written with the following fields: Longitude (degrees)" +" Latitude (degrees), h (100km), Property (kg/m^3 or Pa)") if SiloOutput: saveSilo(fn, PROPERTY=initial_model, mask=reg_mask) print('SILO file written with the following fields: Property (kg/m^3 or Pa), mask') def ResidualCalculation(reg_mask):
def __setOutput(self): x = self.domain.getX() self.__location_of_constraint = es.Vector(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), es.sup(x1) - es.inf(x1), es.sup(x2) - es.inf(x2)) left_mask = es.whereZero(x0 - es.inf(x0), self.tol * d) if self.left[0]: self.__location_of_constraint += left_mask * [1., 0., 0.] if self.left[1]: self.__location_of_constraint += left_mask * [0., 1., 0.] if self.left[2]: self.__location_of_constraint += left_mask * [0., 0., 1.] right_mask = es.whereZero(x0 - es.sup(x0), self.tol * d) if self.right[0]: self.__location_of_constraint += right_mask * [1., 0., 0.] if self.right[1]: self.__location_of_constraint += right_mask * [0., 1., 0.] if self.right[2]: self.__location_of_constraint += right_mask * [0., 0., 1.] front_mask = es.whereZero(x1 - es.inf(x1), self.tol * d) if self.front[0]: self.__location_of_constraint += front_mask * [1., 0., 0.] if self.front[1]: self.__location_of_constraint += front_mask * [0., 1., 0.] if self.front[2]: self.__location_of_constraint += front_mask * [0., 0., 1.] back_mask = es.whereZero(x1 - es.sup(x1), self.tol * d) if self.back[0]: self.__location_of_constraint += back_mask * [1., 0., 0.] if self.back[1]: self.__location_of_constraint += back_mask * [0., 1., 0.] if self.back[2]: self.__location_of_constraint += back_mask * [0., 0., 1.] bottom_mask = es.whereZero(x2 - es.inf(x2), self.tol * d) if self.bottom[0]: self.__location_of_constraint += bottom_mask * [1., 0., 0.] if self.bottom[1]: self.__location_of_constraint += bottom_mask * [0., 1., 0.] if self.bottom[2]: self.__location_of_constraint += bottom_mask * [0., 0., 1.] top_mask = es.whereZero(x2 - es.sup(x2), self.tol * d) if self.top[0]: self.__location_of_constraint += top_mask * [1., 0., 0.] if self.top[1]: self.__location_of_constraint += top_mask * [0., 1., 0.] if self.top[2]: self.__location_of_constraint += top_mask * [0., 0., 1.] if not self.value is None: self.__value_of_constraint = self.__location_of_constraint * self.value else: x0, x1 = x[0], x[1] d = max(es.sup(x0) - es.inf(x0), es.sup(x1) - es.inf(x1)) left_mask = es.whereZero(x0 - es.inf(x0), self.tol * d) if self.left[0]: self.__location_of_constraint += left_mask * [1., 0.] if self.left[1]: self.__location_of_constraint += left_mask * [0., 1.] right_mask = es.whereZero(x0 - es.sup(x0), self.tol * d) if self.right[0]: self.__location_of_constraint += right_mask * [1., 0.] if self.right[1]: self.__location_of_constraint += right_mask * [0., 1.] bottom_mask = es.whereZero(x1 - es.inf(x1), self.tol * d) if self.bottom[0]: self.__location_of_constraint += bottom_mask * [1., 0.] if self.bottom[1]: self.__location_of_constraint += bottom_mask * [0., 1.] top_mask = es.whereZero(x1 - es.sup(x1), self.tol * d) if self.top[0]: self.__location_of_constraint += top_mask * [1., 0.] if self.top[1]: self.__location_of_constraint += top_mask * [0., 1.] if not self.value is None: self.__value_of_constraint = self.__location_of_constraint * self.value[: 2]
def run_model_scenario_and_analyze_results(Parameters, ModelOptions, mesh_function, run, model_scenario_name, scenario_parameters, scenario_param_names, df, model_output_folder, scriptdir, scenario_name, nscenarios, dfo=None): year = 365.25 * 24 * 60 * 60 model_file_adj = '' run_id = 'S%i' % run #scenario_name = run_id print '-' * 30 print 'model scenario id %s, run %i of %i' % (run_id, run + 1, nscenarios) print '-' * 30 model_file_adj += 'run%s' % run_id # update default parameters in Parameter class for scenario_param_name, scenario_parameter in \ zip(scenario_param_names, scenario_parameters): if scenario_parameter is not None: # find model parameter name to adjust model_param_name = scenario_param_name[:-2] print 'updating parameter %s from %s to %s' \ % (model_param_name, str(getattr(Parameters, model_param_name)), str(scenario_parameter)) # update model parameter setattr(Parameters, model_param_name, scenario_parameter) # and add model param name to filename model_file_adj += '_%s_%s' % (model_param_name, str(scenario_parameter)) # set filename for mesh mesh_fn = os.path.join( scriptdir, 'model_output', '_%i_%s.msh' % (random.randint(0, 100), '%s.msh' % scenario_name)) # get names and values of input parameters attributes = inspect.getmembers( Parameters, lambda attribute: not (inspect.isroutine(attribute))) attribute_dict = [ attribute for attribute in attributes if not (attribute[0].startswith('__') and attribute[0].endswith('__')) ] if df is None: # get attributes attribute_names = [ attribute[0] for attribute in attributes if not ( attribute[0].startswith('__') and attribute[0].endswith('__')) ] # set up pandas dataframe to store model input params ind = [0] columns = attribute_names df = pd.DataFrame(index=ind, columns=columns) # store input parameters in dataframe for a in attribute_dict: if a[0] in df.columns: if type(a[1]) is list: df.ix[run, a[0]] = str(a[1]) else: df.ix[run, a[0]] = a[1] # start_time = datetime.datetime.now() start_date_str = '%i-%i-%i' % (start_time.day, start_time.month, start_time.year) start_time_str = '%i:%i:%i' % (start_time.hour, start_time.minute, start_time.second) df.ix[run, 'start_date'] = start_date_str df.ix[run, 'start_time'] = start_time_str # run a single model scenario model_results = grompy_salt_lib.run_model_scenario( scenario_name, model_output_folder, model_file_adj, ModelOptions, Parameters, mesh_function, mesh_fn) end_time = datetime.datetime.now() runtime = end_time - start_time df.ix[run, 'computing_runtime_sec'] = runtime.total_seconds() print 'processing model results' # get model results (mesh, surface, sea_surface, k_vector, P, Conc, rho_f, viscosity, h, q, q_abs, nodal_flux, Pdiff, Cdiff, pressure_differences_max, concentration_differences_max, pressure_differences_mean, concentration_differences_mean, dts, runtimes, nsteps, output_step, boundary_conditions, boundary_fluxes, boundary_flux_stats, reached_steady_state) = model_results dt = dts[-1] runtime = runtimes[-1] flux = q [ specified_pressure_bnd, specified_pressure, specified_concentration_bnd, active_concentration_bnd, specified_concentration, specified_concentration_rho_f, rch_bnd_loc, active_seepage_bnd ] = boundary_conditions [ flux_surface_norm, land_flux_in, land_flux_out, submarine_flux, submarine_flux_in, submarine_flux_out ] = boundary_fluxes [ total_flux_over_surface_norm, total_rch_flux, total_seepage_flux, total_land_flux_in, total_land_flux_out, total_submarine_flux, total_submarine_flux_in, total_submarine_flux_out, ext_rch, ext_seepage, ext_inflow, ext_outflow, ext_inflow_land, ext_outflow_land, ext_inflow_sea, ext_outflow_sea, ext_outflow_land_threshold, ext_outflow_sea_threshold, min_land_flux, max_land_flux, min_seepage_flux, max_seepage_flux, min_submarine_flux, max_submarine_flux ] = boundary_flux_stats newcols = [ 'model_scenario_id', 'P_min', 'P_max', 'C_min', 'C_max', 'h_min', 'h_max', 'vx_min', 'vx_max', 'vy_min', 'vy_max', 'max_pressure_change', 'max_concentration_change', 'runtime', 'nsteps', 'dt_final', 'total_flux_over_surface', 'total_rch_flux', 'total_seepage_flux', 'total_land_flux_in', 'total_land_flux_out', 'total_submarine_flux', 'total_submarine_flux_in', 'total_submarine_flux_out', 'ext_inflow_land', 'ext_outflow_land', 'ext_inflow_sea', 'ext_outflow_sea', 'ext_outflow_land_exc_threshold', 'ext_outflow_sea_exc_threshold', 'min_land_flux', 'max_land_flux', 'min_seepage_flux', 'max_seepage_flux', 'min_submarine_flux', 'max_submarine_flux' ] if run == 0: df = grompy_salt_lib.add_cols_to_df(df, newcols) df['model_scenario_id'] = '' # store model results in dataframe df.ix[run, 'model_scenario_id'] = run_id if model_scenario_name is not None: df.ix[run, 'model_scenario_name'] = model_scenario_name df.ix[run, 'P_min'] = es.inf(P) df.ix[run, 'P_max'] = es.sup(P) df.ix[run, 'C_min'] = es.inf(Conc) df.ix[run, 'C_max'] = es.sup(Conc) df.ix[run, 'h_min'] = es.inf(h) df.ix[run, 'h_max'] = es.sup(h) df.ix[run, 'vx_min'] = es.inf(flux[0]) df.ix[run, 'vx_max'] = es.sup(flux[0]) df.ix[run, 'vy_min'] = es.inf(flux[1]) df.ix[run, 'vy_max'] = es.sup(flux[1]) df.ix[run, 'max_pressure_change'] = es.Lsup(Pdiff) df.ix[run, 'max_concentration_change'] = es.Lsup(Cdiff) df.ix[run, 'runtime'] = runtime df.ix[run, 'nsteps'] = nsteps df.ix[run, 'dt_final'] = dt df.ix[run, 'total_flux_over_surface'] = total_flux_over_surface_norm[1] df.ix[run, 'total_rch_flux'] = total_rch_flux[1] df.ix[run, 'total_seepage_flux'] = total_seepage_flux[1] df.ix[run, 'total_land_flux_in'] = total_land_flux_in[1] df.ix[run, 'total_land_flux_out'] = total_land_flux_out[1] df.ix[run, 'total_submarine_flux'] = total_submarine_flux[1] df.ix[run, 'total_submarine_flux_in'] = total_submarine_flux_in[1] df.ix[run, 'total_submarine_flux_out'] = total_submarine_flux_out[1] df.ix[run, 'ext_inflow_land'] = ext_inflow_land[1] df.ix[run, 'ext_outflow_land'] = ext_outflow_land[1] df.ix[run, 'ext_inflow_sea'] = ext_inflow_sea[1] df.ix[run, 'ext_outflow_sea'] = ext_outflow_sea[1] df.ix[run, 'ext_outflow_land_exc_threshold'] = ext_outflow_land_threshold[1] df.ix[run, 'ext_outflow_sea_exc_threshold'] = ext_outflow_sea_threshold[1] df.ix[run, 'min_land_flux'] = min_land_flux df.ix[run, 'max_land_flux'] = max_land_flux df.ix[run, 'min_seepage_flux'] = min_seepage_flux df.ix[run, 'max_seepage_flux'] = max_seepage_flux df.ix[run, 'min_submarine_flux'] = min_submarine_flux df.ix[run, 'max_submarine_flux'] = max_submarine_flux # check if model output folder exists and create if not if not os.path.exists(model_output_folder): os.makedirs(model_output_folder) # save model output to vtk file if ModelOptions.save_vtk_files is True: vtk_folder = os.path.join(model_output_folder, 'vtk_files') if not os.path.exists(vtk_folder): os.makedirs(vtk_folder) fn_VTK = os.path.join(vtk_folder, '%s_final_output.vtu' % model_file_adj) print 'saving vtk file of model results: %s' % fn_VTK nodata = -99999 flux_surface_plot = nodal_flux * surface + \ nodata * es.whereZero(surface) if sea_surface is None: sea_surface_save = surface else: sea_surface_save = sea_surface esys.weipa.saveVTK(fn_VTK, pressure=P, concentration=Conc, h=h, flux=flux, qx=flux[0], qy=flux[1], kx=k_vector[0], ky=k_vector[1], nodal_flux=nodal_flux, surface=surface, sea_surface=sea_surface_save, nodal_flux_surface=flux_surface_plot, specified_pressure_bnd=specified_pressure_bnd, active_seepage_bnd=active_seepage_bnd, recharge_bnd=rch_bnd_loc, active_concentration_bnd=active_concentration_bnd, flux_surface_norm=flux_surface_norm, land_flux_in=land_flux_in, land_flux_out=land_flux_out, submarine_flux=submarine_flux, submarine_flux_in=submarine_flux_in, submarine_flux_out=submarine_flux_out) df.ix[run, 'vtk_filename'] = fn_VTK if ModelOptions.save_variables_to_csv is True: var_folder = os.path.join(model_output_folder, 'variables') if not os.path.exists(var_folder): os.makedirs(var_folder) q_abs = (flux[0]**2 + flux[1]**2)**0.5 model_vars = [P, Conc, h, q_abs * year, flux[0] * year, flux[1] * year] varlabels = ['P', 'conc', 'h', 'v', 'vx', 'vy'] for var, varlabel in zip(model_vars, varlabels): xya, va = grompy_salt_lib.convert_to_array(var) filename = os.path.join( var_folder, '%s_%s_final.csv' % (varlabel, model_file_adj)) csv_str = 'x,y,%s\n' % varlabel for x, y, vai in zip(xya[:, 0], xya[:, 1], va): csv_str += '%0.3f,%0.3f,%0.3e\n' % (x, y, vai) print 'writing final variable %s to file %s' % (varlabel, filename) fout = open(filename, 'w') fout.write(csv_str) fout.close() # write node and element locations and connectivity to separate file mesh_fn = os.path.join(var_folder, 'mesh_%s.fly' % model_file_adj) mesh.write(mesh_fn) # write pressure and concentration differences diff_folder = os.path.join(model_output_folder, 'P_and_C_change') if not os.path.exists(diff_folder): os.makedirs(diff_folder) df_diff = pd.DataFrame(columns=[ 'timestep', 'dt', 'runtime', 'pressure_change_mean', 'pressure_change_max', 'concentration_change_mean', 'concentration_change_max' ], index=np.arange(nsteps)) #df_diff['timestep'] = np.arange(nsteps) df_diff['pressure_change_mean'] = pressure_differences_mean df_diff['pressure_change_max'] = pressure_differences_max df_diff['concentration_change_mean'] = concentration_differences_mean df_diff['concentration_change_max'] = concentration_differences_max df_diff['dt'] = dts df_diff['runtime'] = runtimes filename = os.path.join(diff_folder, 'P_and_C_changes_%s_final.csv' % model_file_adj) print 'saving P and C changes to %s' % filename df_diff.to_csv(filename, index_label='timestep') # merge new model results dataframe with existing model output, if any dfm = df if dfo is not None: dfm = pd.concat([dfo, df]) # keep columnn order dfm = dfm[list(dfo.columns)] # save model runs input params and results to .csv file today = datetime.datetime.now() today_str = '%i-%i-%i' % (today.day, today.month, today.year) filename = os.path.join( model_output_folder, 'final_model_results_%s_%s_%i_runs.csv' % (scenario_name, today_str, nscenarios)) # check if file exists already if os.path.exists(filename): backup_filename = filename + '_backup' os.rename(filename, backup_filename) print 'moved previous input & output data to %s' % backup_filename print 'saving model run input & output data to %s' % filename dfm.to_csv(filename, index_label='model_run') return df
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)
def setup_coastal_mesh(Parameters, mesh_filename, extend_domain=True, max_length=1e5): """ Create a mesh consisting of 3 blocks, with a smaller cell size in the middle block The middle block is centered around the fresh-salt water interface, which is calculated using the Ghyben-Herzberg equation. """ #if Parameters.topo_gradient == 0: # extent_salt_water = 0 #else: # extent_salt_water = (Parameters.thickness / # (41.0 * Parameters.topo_gradient)) R = Parameters.recharge_flux B = Parameters.thickness K = Parameters.k * Parameters.rho_f_0 * 9.81 / Parameters.viscosity L = Parameters.L # calculate hydraulic head using analytical solution for confined aq # with uniform recharge + Dupuit assumptions xa = np.linspace(0, L, 101) h = R / (K * B) * (L * xa - 0.5 * xa**2) if h[-1] < (B / 40): print 'warning, calculated extent salt water toe exceeds model domain' print 'calculated h at model bnd = %0.2f m' % h[-1] print 'Ghyben-Herzberg depth of salt water interface = %0.2f m' % (h[-1] * 40) print 'thickness = %0.2f m' % Parameters.thickness if extend_domain is False: extent_salt_water = Parameters.L - Parameters.buffer_distance_land - 1.0 #print 'choosing maximum possible extent of %0.2f m for ' \ # 'designing model grid' % extent_salt_water print 'entire top right triangle at landward side of model domain has fine discretization' fine_mesh = True adjust_length = False else: adjust_length = True else: fine_mesh = False # salt water toe touches bottom of model domain a = 0.5 * R / (K * B) b = -(R * L) / (K * B) c = B / 40.0 D = np.sqrt(b**2 - 4 * a * c) extent_salt_water = (-b - D) / (2 * a) hs1 = R / (K * B) * (L * extent_salt_water - 0.5 * extent_salt_water**2) print 'calculated extent salt water toe = %0.2f m' % extent_salt_water try: assert np.abs(hs1 - B / 40.0) < 1e-3 except AssertionError: msg = 'error, something wrong with calculated extent ' \ 'salt water toe' raise ValueError(msg) if adjust_length is True: L_land = extent_salt_water + Parameters.buffer_distance_land * 2 if L_land > max_length: L_land = Parameters.L fine_mesh = True else: print 'extending model domain size to %0.3e' % L_land else: L_land = Parameters.L ############################### # use gmsh to construct domain ############################## xs = np.array([-Parameters.L_sea, extent_salt_water - Parameters.buffer_distance_sea, -Parameters.buffer_distance_sea, -Parameters.L_sea, extent_salt_water, 0, extent_salt_water + Parameters.buffer_distance_land, Parameters.buffer_distance_land, L_land, L_land, -Parameters.L_sea]) zs = xs * Parameters.topo_gradient zs[0:2] = zs[0:2] - Parameters.thickness zs[4] = zs[4] - Parameters.thickness zs[6] = zs[6] - Parameters.thickness zs[8] = zs[8] - Parameters.thickness zs[10] = 0.0 #points = create_points(xs,zs) points = [pc.Point(x, z) for x, z in zip(xs, zs)] line1 = pc.Line(points[0], points[1]) line2 = pc.Line(points[1], points[2]) line3 = pc.Line(points[2], points[3]) line4 = pc.Line(points[3], points[0]) line5 = pc.Line(points[1], points[4]) line6 = pc.Line(points[4], points[5]) line7 = pc.Line(points[5], points[2]) line8 = pc.Line(points[4], points[6]) line9 = pc.Line(points[6], points[7]) line10 = pc.Line(points[7], points[5]) line11 = pc.Line(points[6], points[8]) line12 = pc.Line(points[8], points[9]) line13 = pc.Line(points[9], points[7]) # new lines for sea surface # seabottom, x=-buffer to x=0 # -line3 (pt 3 to 2) # - line7 (pt 2 to pt 5) # coastline to x=0, z=0 #line14 = pc.Line(points[5], points[10]) # x=0, z=0 to x=0, z=sea bottom #line15 = pc.Line(points[10], points[3]) # coastline to x=0, sea bottom # finer grid cell size around fresh-salt water interface curve_a = pc.CurveLoop(line1, line2, line3, line4) curve_b = pc.CurveLoop(line5, line6, line7, -line2) curve_c = pc.CurveLoop(line8, line9, line10, -line6) curve_d = pc.CurveLoop(line11, line12, line13, -line9) #curve_seawater = pc.CurveLoop(-line3, -line7, line14, line15) surface_a = pc.PlaneSurface(curve_a) surface_b = pc.PlaneSurface(curve_b) surface_c = pc.PlaneSurface(curve_c) surface_d = pc.PlaneSurface(curve_d) #surface_seawater = pc.PlaneSurface(curve_seawater) surface_a.setLocalScale(factor=Parameters.grid_refinement_factor_sea) surface_b.setLocalScale(factor=Parameters.grid_refinement_factor) surface_c.setLocalScale(factor=Parameters.grid_refinement_factor) #surface_seawater.setLocalScale(factor=Parameters.grid_refinement_factor_seawater) if fine_mesh is True: print 'assigning refined grid from landward side of model domain' surface_d.setLocalScale(factor=Parameters.grid_refinement_factor) d = gmsh.Design(dim=2, element_size=Parameters.cellsize) ps1 = pc.PropertySet("sea_surface1", line3) ps2 = pc.PropertySet("sea_surface2", line7) ps3 = pc.PropertySet("land_surface1", line10) ps4 = pc.PropertySet("land_surface2", line13) #ps5 = pc.PropertySet("sea_surface", line14) #d.addItems(pc.PropertySet('sea', surface_a), # pc.PropertySet('salt_wedge_sea_side', surface_b), # pc.PropertySet('salt_wedge_land_side', surface_c), # pc.PropertySet('land', surface_d), # pc.PropertySet('seawater', surface_seawater), # ps1, ps2, ps3, ps4, ps5) d.addItems(pc.PropertySet('sea', surface_a), pc.PropertySet('salt_wedge_sea_side', surface_b), pc.PropertySet('salt_wedge_land_side', surface_c), pc.PropertySet('land', surface_d), ps1, ps2, ps3, ps4) d.setMeshFileName(mesh_filename) mesh = fl.MakeDomain(d, optimizeLabeling=True) # calculate surface xy = mesh.getX() z_surface = xy[0] * Parameters.topo_gradient surface = es.whereZero(xy[1] - z_surface) # sea surface sea_surface = es.whereZero(xy[1]) * es.whereNegative(xy[0]) seawater = es.whereNegative(xy[0]) * es.whereNegative(z_surface - xy[1]) print bla return mesh, surface, sea_surface, seawater, z_surface
def setup_coastal_mesh_glover1959(Parameters, mesh_filename): """ Create a mesh consisting of 3 blocks, with a smaller cell size in the middle block The middle block is centered around the fresh-salt water interface, which is calculated using an anlytical solution by Glover (1959) Journal of Geophys. Res. """ # if Parameters.topo_gradient == 0: # extent_salt_water = 0 # else: # extent_salt_water = (Parameters.thickness / # (41.0 * Parameters.topo_gradient)) if Parameters.ghyben_herzberg is True: R = Parameters.recharge_flux B = Parameters.thickness K = Parameters.k * Parameters.rho_f_0 * 9.81 / Parameters.viscosity L = Parameters.L # calculate hydraulic head using analytical solution for confined aq # with uniform recharge + Dupuit assumptions xa = np.linspace(0, L, 1001) h = R / (K * B) * (L * xa - 0.5 * xa ** 2) # calculate depth salt water interface from grompy_lib import depth_sw_interface_Glover1959 rho_f = Parameters.rho_f_0 * Parameters.freshwater_concentration * Parameters.gamma + Parameters.rho_f_0 rho_s = Parameters.rho_f_0 * Parameters.seawater_concentration * Parameters.gamma + Parameters.rho_f_0 Qmax = Parameters.recharge_flux * L y_sw, int_sw_top, int_sw_bottom = depth_sw_interface_Glover1959(xa, Parameters.k, Parameters.viscosity, Parameters.topo_gradient, Parameters.thickness, rho_f, rho_s, Parameters.gamma, Qmax=Qmax) if Parameters.recharge_flux == 0.0: # assume with no rehcarge that the hydraulic head follows the land surface h = Parameters.topo_gradient * xa #y_sw, int_sw_top, int_sw_bottom if int_sw_bottom > L: print 'warning, calculated extent salt water toe exceeds model domain' print 'calculated toe of fresh_salt water bnd = %0.2f m' % int_sw_bottom extent_salt_water = Parameters.L - Parameters.buffer_distance_land - 1.0 print 'choosing maximum possible extent of %0.2f m for ' \ 'designing model grid' % extent_salt_water fine_mesh = False else: fine_mesh = False extent_salt_water = int_sw_bottom print 'calculated extent salt water toe = %0.2f m' % int_sw_bottom else: print 'assuming a vertical fresh-salt water interface' extent_salt_water = 0.0 fine_mesh = False L_land = Parameters.L ############################### # use gmsh to construct domain ############################## xs = np.array([-Parameters.L_sea, extent_salt_water - Parameters.buffer_distance_sea, -Parameters.buffer_distance_sea, -Parameters.L_sea, extent_salt_water, 0, extent_salt_water + Parameters.buffer_distance_land, Parameters.buffer_distance_land, L_land, L_land]) zs = xs * Parameters.topo_gradient zs[0:2] = zs[0:2] - Parameters.thickness zs[4] = zs[4] - Parameters.thickness zs[6] = zs[6] - Parameters.thickness zs[8] = zs[8] - Parameters.thickness # points = create_points(xs,zs) points = [pc.Point(x, z) for x, z in zip(xs, zs)] line1 = pc.Line(points[0], points[1]) line2 = pc.Line(points[1], points[2]) line3 = pc.Line(points[2], points[3]) line4 = pc.Line(points[3], points[0]) line5 = pc.Line(points[1], points[4]) line6 = pc.Line(points[4], points[5]) line7 = pc.Line(points[5], points[2]) line8 = pc.Line(points[4], points[6]) line9 = pc.Line(points[6], points[7]) line10 = pc.Line(points[7], points[5]) line11 = pc.Line(points[6], points[8]) line12 = pc.Line(points[8], points[9]) line13 = pc.Line(points[9], points[7]) # finer grid cell size around fresh-salt water interface curve_a = pc.CurveLoop(line1, line2, line3, line4) curve_b = pc.CurveLoop(line5, line6, line7, -line2) curve_c = pc.CurveLoop(line8, line9, line10, -line6) curve_d = pc.CurveLoop(line11, line12, line13, -line9) surface_a = pc.PlaneSurface(curve_a) surface_b = pc.PlaneSurface(curve_b) surface_c = pc.PlaneSurface(curve_c) surface_d = pc.PlaneSurface(curve_d) surface_a.setLocalScale(factor=Parameters.grid_refinement_factor_sea) surface_b.setLocalScale(factor=Parameters.grid_refinement_factor) surface_c.setLocalScale(factor=Parameters.grid_refinement_factor) if fine_mesh is True: print 'assigning refined grid to entire landward side of model domain' surface_d.setLocalScale(factor=Parameters.grid_refinement_factor) d = gmsh.Design(dim=2, element_size=Parameters.cellsize) ps1 = pc.PropertySet("sea_surface1", line3) ps2 = pc.PropertySet("sea_surface2", line7) ps3 = pc.PropertySet("land_surface1", line10) ps4 = pc.PropertySet("land_surface2", line13) d.addItems(pc.PropertySet('sea', surface_a), pc.PropertySet('salt_wedge_sea_side', surface_b), pc.PropertySet('salt_wedge_land_side', surface_c), pc.PropertySet('land', surface_d), ps1, ps2, ps3, ps4) d.setMeshFileName(mesh_filename) print '=' * 30 mesh = fl.MakeDomain(d, optimizeLabeling=True) # calculate surface xy = mesh.getX() z_surface = xy[0] * Parameters.topo_gradient surface = es.whereZero(xy[1] - z_surface) # sea surface # sea_surface = surface * es.whereNegative(xy[0]) sea_surface = None seawater = None return mesh, surface, sea_surface, seawater, z_surface
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]
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, omega, x, Z, eta=None, w0=1., mu=4 * math.pi * 1e-7, sigma0=0.01, airLayerLevel=None, fixAirLayer=False, coordinates=None, tol=1e-8, saveMemory=False, directSolver=True): """ initializes a new forward model. :param domain: domain of the model :type domain: `Domain` :param omega: frequency :type omega: positive ``float`` :param x: coordinates of measurements :type x: ``list`` of ``tuple`` with ``float`` :param Z: measured impedance (possibly scaled) :type Z: ``list`` of ``complex`` :param eta: spatial confidence radius :type eta: positive ``float`` or ``list`` of positive ``float`` :param w0: confidence factors for meassurements. :type w0: ``None`` or a list of positive ``float`` :param mu: permeability :type mu: ``float`` :param sigma0: background conductivity :type sigma0: ``float`` :param airLayerLevel: position of the air layer from to bottom of the domain. If not set the air layer starts at the top of the domain :type airLayerLevel: ``float`` or ``None`` :param fixAirLayer: fix air layer (TM mode) :type fixAirLayer: ``bool`` :param coordinates: defines coordinate system to be used (not supported yet) :type coordinates: `ReferenceSystem` or `SpatialCoordinateTransformation` :param tol: tolerance of underlying PDE :type tol: positive ``float`` :param saveMemory: if true stiffness matrix is deleted after solution of the PDE to minimize memory use. This will require more compute time as the matrix needs to be reallocated at each iteration. :type saveMemory: ``bool`` :param directSolver: if true a direct solver (rather than an iterative solver) will be used to solve the PDE :type directSolver: ``bool`` """ super(MT2DBase, self).__init__() self.__trafo = coords.makeTransformation(domain, coordinates) if not self.getCoordinateTransformation().isCartesian(): raise ValueError( "Non-Cartesian coordinates are not supported yet.") if len(x) != len(Z): raise ValueError( "Number of data points and number of impedance values don't match." ) if eta is None: eta = escript.sup(domain.getSize()) * 0.45 if isinstance(eta, float) or isinstance(eta, int): eta = [float(eta)] * len(Z) elif not len(eta) == len(Z): raise ValueError( "Number of confidence radii and number of impedance values don't match." ) if isinstance(w0, float) or isinstance(w0, int): w0 = [float(w0)] * len(Z) elif not len(w0) == len(Z): raise ValueError( "Number of confidence factors and number of impedance values don't match." ) self.__domain = domain self._omega_mu = omega * mu self._ks = escript.sqrt(self._omega_mu * sigma0 / 2.) xx = escript.Function(domain).getX() totalS = 0 self._Z = [ escript.Scalar(0., escript.Function(domain)), escript.Scalar(0., escript.Function(domain)) ] self._weight = escript.Scalar(0., escript.Function(domain)) for s in range(len(Z)): chi = self.getWeightingFactor(xx, 1., x[s], eta[s]) f = escript.integrate(chi) if f < eta[s]**2 * 0.01: raise ValueError( "Zero weight (almost) for data point %s. Change eta or refine mesh." % (s, )) w02 = w0[s] / f totalS += w02 self._Z[0] += chi * Z[s].real self._Z[1] += chi * Z[s].imag self._weight += chi * w02 / (abs(Z[s])**2) if not totalS > 0: raise ValueError( "Scaling of weight factors failed as sum is zero.") DIM = domain.getDim() z = domain.getX()[DIM - 1] self._ztop = escript.sup(z) self._zbottom = escript.inf(z) if airLayerLevel is None: airLayerLevel = self._ztop self._airLayerLevel = airLayerLevel # botton: mask0 = escript.whereZero(z - self._zbottom) r = mask0 * [ escript.exp(self._ks * (self._zbottom - airLayerLevel)) * escript.cos(self._ks * (self._zbottom - airLayerLevel)), escript.exp(self._ks * (self._zbottom - airLayerLevel)) * escript.sin(self._ks * (self._zbottom - airLayerLevel)) ] #top: if fixAirLayer: mask1 = escript.whereNonNegative(z - airLayerLevel) r += mask1 * [1, 0] else: mask1 = escript.whereZero(z - self._ztop) r += mask1 * [ self._ks * (self._ztop - airLayerLevel) + 1, self._ks * (self._ztop - airLayerLevel) ] self._q = (mask0 + mask1) * [1, 1] self._r = r #==================================== self.__tol = tol self._directSolver = directSolver self._saveMemory = saveMemory self.__pde = None if not saveMemory: self.__pde = self.setUpPDE()