def initialize(self, b=escript.Data(), f=escript.Data(), specified_u_mask=escript.Data(), specified_u_val=escript.Data(), dt=0): """ initialize the model for each time step, e.g. assign parameters :param b: type vector, body force on FunctionSpace, e.g. gravity :param f: type vector, boundary traction on FunctionSpace (FunctionOnBoundary) :param specified_u_mask: type vector, mask of location for Dirichlet boundary :param specified_u_val: type vector, specified displacement for Dirichlet boundary """ self.__pde.setValue(Y=b, y=f, q=specified_u_mask, r=specified_u_val) # if FEDENodeMap is given if self.__FEDENodeMap: # assign dt_FE/dt_DE_ext to self.__nsOfDE_ext dt_ext = self.__pool.apply(getScenetDt, (self.__sceneExt, )) self.__nsOfDE_ext = int(round(dt / dt_ext)) print "Ratio between time step in FE and exterior DE domain: %1.1e" % self.__nsOfDE_ext dt_int = self.__pool.map(getScenetDt, self.__scenes) # assign a list of dt_FE/dt_DE_int to self.__nsOfDE_int self.__nsOfDE_int = numpy.round(numpy.array(dt) / dt_int).astype(int) print "Maximum ratio between time step in FE and interior DE domains: %1.1e" % max( self.__nsOfDE_int) if dt == 0: raise RuntimeError, "Time step in FE domain is not given" self.__dt = dt print "Time step in FE domain:%1.1e" % self.__dt
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 initialize(self, b=escript.Data(), f=escript.Data(), specified_u_mask=escript.Data(), specified_u_val=escript.Data()): """ initialize the model for each time step, e.g. assign parameters :param b: type vector, body force on FunctionSpace, e.g. gravity :param f: type vector, boundary traction on FunctionSpace (FunctionOnBoundary) :param specified_u_mask: type vector, mask of location for Dirichlet boundary :param specified_u_val: type vector, specified displacement for Dirichlet boundary """ self.__pde.setValue(Y=b,y=f,q=specified_u_mask,r=specified_u_val)
def initialize(self, b=escript.Data(), f=escript.Data(), umsk=escript.Data(), uvalue=escript.Data(), flux=escript.Data(), pmsk=escript.Data(), pvalue=escript.Data()): """ initialize the model for each time step, e.g. assign parameters :param b: type vector, body force on FunctionSpace, e.g. gravity :param f: type vector, boundary traction on FunctionSpace (FunctionOnBoundary) :param umsk: type vector, mask of location for Dirichlet boundary :param uvalue: type vector, specified displacement for Dirichlet boundary """ self.__upde.setValue(Y=b,y=f,q=umsk,r=uvalue) self.__ppde.setValue(y=flux,q=pmsk,r=pvalue) self.__r=uvalue
def getGradientAtPoint(self): """ returns the gradient of the cost function J with respect to m. :note: This implementation returns Y_k=dPsi/dm_k and X_kj=dPsi/dm_kj """ # Using cached values m = self.__pre_input grad_m = self.__pre_args mu = self.__mu mu_c = self.__mu_c DIM = self.getDomain().getDim() numLS = self.getNumLevelSets() grad_m = escript.grad(m, escript.Function(m.getDomain())) if self.__w0 is not None: Y = m * self.__w0 * mu else: if numLS == 1: Y = escript.Scalar(0, grad_m.getFunctionSpace()) else: Y = escript.Data(0, (numLS, ), grad_m.getFunctionSpace()) if self.__w1 is not None: if numLS == 1: X = grad_m * self.__w1 * mu else: X = grad_m * self.__w1 for k in range(numLS): X[k, :] *= mu[k] else: X = escript.Data(0, grad_m.getShape(), grad_m.getFunctionSpace()) # cross gradient terms: if numLS > 1: for k in range(numLS): grad_m_k = grad_m[k, :] l2_grad_m_k = escript.length(grad_m_k)**2 for l in range(k): grad_m_l = grad_m[l, :] l2_grad_m_l = escript.length(grad_m_l)**2 grad_m_lk = inner(grad_m_l, grad_m_k) f = mu_c[l, k] * self.__wc[l, k] X[l, :] += f * (l2_grad_m_k * grad_m_l - grad_m_lk * grad_m_k) X[k, :] += f * (l2_grad_m_l * grad_m_k - grad_m_lk * grad_m_l) return pdetools.ArithmeticTuple(Y, X)
def test_replaceNaNConstant(self): dom=self.domain dat = es.Data(10,es.ContinuousFunction(dom)) dat=(dat*0)/0 self.assertTrue(dat.hasNaN(),"dat should contain NaN but its doesn't") dat.replaceNaN(10) self.assertEqual(es.Lsup(dat), 10) dat = es.Data(10,es.ContinuousFunction(dom)) dat.promote() dat=(dat*0)/0 self.assertTrue(dat.hasNaN(),"dat should contain NaN but its doesn't") dat.replaceNaN(4+3j) self.assertEqual(es.Lsup(dat), 5)
def doInitialization(self): """ initialize model """ self.__p_old = None self.__p_very_old = None self.__dt_old = None self.__pde = lpde.LameEquation(self.domain) self.__pde.getSolverOptions().setSolverMethod( lpde.SolverOptions.DIRECT) if self.location_prescribed_velocity is None: self.location_prescribed_velocit = es.Data() if self.prescribed_velocity is None: self.prescribed_velocity = es.Data()
def applyStrain(self, st=escript.Data()): st = st.toListOfTuples() st = numpy.array(st).reshape(-1, 4) # load DEM packing with strain arScenes = self.__pool.map_async( shear2D, zip(self.__scenes, st, self.__nsOfDE_int)) return arScenes
def solveSolid(self, p_iter_gauss=escript.Data(), iter_max=50): """ solve the pde for displacement using Newton-Ralphson scheme """ k = util.kronecker(self.__domain) p = p_iter_gauss * k iterate = 0 rtol = self.__rtol stress_safe = self.__stress s_safe = self.__S x_safe = self.__domain.getX() self.__upde.setValue(A=s_safe, X=-stress_safe + p, r=self.__r) #residual0=util.L2(self.__pde.getRightHandSide()) # using force error u = self.__upde.getSolution() # trial solution, displacement D = util.grad(u) # trial strain tensor # !!!!!! obtain stress and tangent operator from DEM part update_stress, update_s, update_scenes = self.applyStrain_getStressTangentDEM( st=D) err = util.Lsup(u) # initial error before iteration converged = (err < 1.e-12) while (not converged) and (iterate < iter_max): #if iterate>iter_max: # raise RuntimeError,"Convergence for Newton-Raphson failed after %s steps."%(iter_max) iterate += 1 self.__domain.setX(x_safe + u) self.__upde.setValue(A=update_s, X=-update_stress + p, r=escript.Data()) #residual=util.L2(self.__pde.getRightHandSide()) du = self.__upde.getSolution() u += du l, d = util.L2(u), util.L2(du) err = d / l # displacement error, alternatively using force error 'residual' converged = (err < rtol) if err > rtol**3: # only update DEM parts when error is large enough self.__domain.setX(x_safe) D = util.grad(u) update_stress, update_s, update_scenes = self.applyStrain_getStressTangentDEM( st=D) """reset domain geometry to original until global convergence""" self.__domain.setX(x_safe) return u, D, update_stress, update_s, update_scenes
def applyDisplIncrement_getForceDEM(self, DEdu=escript.Data(), dynRelax=False): """ apply displacement increment to the external DE domain, and get boundary force from DE interface nodes """ arFEfAndSceneExt = self.__pool.apply_async( \ moveInterface_getForce2D,(self.__sceneExt,self.__conf,DEdu,dynRelax, \ self.__mIds,self.__FEDENodeMap,self.__nsOfDE_ext)) return arFEfAndSceneExt
def solve(self, iter_max=100): """ solve the equation using Newton-Ralphson scheme """ iterate = 0 rtol = self.getRelTolerance() stress = self.getCurrentStress() s = self.getCurrentTangent() x_safe = self.__domain.getX() self.__pde.setValue(A=s, X=-stress) #residual0=util.L2(self.__pde.getRightHandSide()) # using force error u = self.__pde.getSolution() # trial solution, displacement D = util.grad(u) # trial strain tensor # !!!!!! obtain stress and tangent operator from DEM part update_stress, update_s, update_scenes = self.applyStrain_getStressTangentDEM( st=D) err = 1.0 # initial error before iteration converged = (err < rtol) while (not converged) and (iterate < iter_max): if self.__verbose: print( "Not converged after %d iteration(s)! Relative error: %e" % (iterate, err)) iterate += 1 self.__domain.setX(x_safe + u) self.__pde.setValue(A=update_s, X=-update_stress, r=escript.Data()) #residual=util.L2(self.__pde.getRightHandSide()) du = self.__pde.getSolution() u += du l, d = util.L2(u), util.L2(du) err = d / l # displacement error, alternatively using force error 'residual' converged = (err < rtol) if err > rtol * 0.001: # only update DEM parts when error is large enough self.__domain.setX(x_safe) D = util.grad(u) update_stress, update_s, update_scenes = self.applyStrain_getStressTangentDEM( st=D) #if err>err_safe: # to ensure consistent convergence, however this may not be achieved due to fluctuation! # raise RuntimeError,"No improvement of convergence with iterations! Relative error: %e"%err """ update 'domain geometry', 'stress', 'tangent operator', 'accumulated strain' and 'simulation scenes'. """ self.__domain.setX(x_safe + u) self.__stress = update_stress self.__S = update_s self.__strain += D self.__scenes = update_scenes if self.__verbose: print( "Convergence reached after %d iteration(s)! Relative error: %e" % (iterate, err)) return u
def applyStrain_getStressTangentDEM(self, st=escript.Data()): st = st.toListOfTuples() st = numpy.array(st).reshape(-1, 9) stress = escript.Tensor(0, escript.Function(self.__domain)) S = escript.Tensor4(0, escript.Function(self.__domain)) scenes = self.__pool.map(shear, zip(self.__scenes, st)) st = self.__pool.map(getStressAndTangent, scenes) for i in xrange(self.__numGaussPoints): stress.setValueOfDataPoint(i, st[i][0]) S.setValueOfDataPoint(i, st[i][1]) return stress, S, scenes
def applyStrain_getStressDEM(self, st=escript.Data(), dynRelax=False): st = st.toListOfTuples() st = numpy.array(st).reshape(-1, 4) stress = escript.Tensor(0, escript.Function(self.__domain)) # load DEM packing with strain scenes = self.__pool.map( shear2D, zip(self.__scenes, st, self.__nsOfDE_int, repeat(dynRelax))) # return homogenized stress s = self.__pool.map(getStress2D, scenes) for i in xrange(self.__numGaussPoints): stress.setValueOfDataPoint(i, s[i]) return stress, scenes
def applyStrain_getStressTangentDEM(self,st=escript.Data()): st = st.toListOfTuples() st = numpy.array(st).reshape(-1,4) #-1 means the num of rows if determined by column num 4(strain tensor of each GP has 4 values) stress = escript.Tensor(0,escript.Function(self.__domain)) S = escript.Tensor4(0,escript.Function(self.__domain)) scenes = self.__pool.map(shear2D,zip(self.__scenes,st)) #zip is from python; shear2D is the key part of DEM if self.__usepert: s = self.__pool.map(getStressTensor,scenes) t = self.__pool.map(getTangentOperator,zip(scenes,repeat(self.__pert))) for i in xrange(self.__numGaussPoints): stress.setValueOfDataPoint(i,s[i]) S.setValueOfDataPoint(i,t[i]) else: ST = self.__pool.map(getStressAndTangent2D,scenes) for i in xrange(self.__numGaussPoints): stress.setValueOfDataPoint(i,ST[i][0]) S.setValueOfDataPoint(i,ST[i][1]) return stress,S,scenes #stress is sigma. S means tangent operator. scenes??
def applyStrain_getStressTangentDEM(self, st=escript.Data()): st = st.toListOfTuples() st = numpy.array(st).reshape(-1, 4) stress = escript.Tensor(0, escript.Function(self.__domain)) S = escript.Tensor4(0, escript.Function(self.__domain)) scenes = self.__pool.map(shear2D, list(zip(self.__scenes, st))) if self.__usepert: s = self.__pool.map(getStressTensor, scenes) t = self.__pool.map(getTangentOperator, list(zip(scenes, repeat(self.__pert)))) for i in range(self.__numGaussPoints): stress.setValueOfDataPoint(i, s[i]) S.setValueOfDataPoint(i, t[i]) else: ST = self.__pool.map(getStressAndTangent2D, scenes) for i in range(self.__numGaussPoints): stress.setValueOfDataPoint(i, ST[i][0]) S.setValueOfDataPoint(i, ST[i][1]) return stress, S, scenes
def setRHS(self, X, Y=escript.Data()): """ set right hande side of PDE, including X_{ij} and Y_i X: stress tensor at (n) time step Y: vector, (equivalent) body force at (n) time step Note that boundary force, if any, is set inhere """ # apply internal stress and equivalent body force self.__pde.setValue(X=X, Y=Y) # if exterior DE domain is given if self.__FEDENodeMap: FEf = self.getFEf() rhs = self.__pde.getRightHandSide() # !!!!!! apply boundary force to the right hande side of PDE for FEid in FEf.keys(): rhs_i = rhs.getTupleForDataPoint(FEid) rhs_i_new = [sum(f) for f in zip(rhs_i, FEf[FEid])] rhs.setValueOfDataPoint(FEid, rhs_i_new) # consider Neumann boundary conditions rhs -= rhs * self.__pde.getCoefficient('q') """
def solve(self, iter_max=100): """ solve the equation using Newton-Ralphson scheme ??where is the equation """ iterate=0 rtol=self.getRelTolerance() stress=self.getCurrentStress() s=self.getCurrentTangent() x_safe=self.__domain.getX() self.__pde.setValue(A=s, X=-stress)#stress is negative,here uses - to change to positive #residual0=util.L2(self.__pde.getRightHandSide()) # using force error u=self.__pde.getSolution() # trial solution, displacement D=util.grad(u) # trial strain tensor (obtained from FEM part) #fout1=open('./result/gradU.dat','w') #fout1.write(str(D)+'\n') # !!!!!! following steps: obtain stress and tangent operator from DEM part update_stress,update_s,update_scenes=self.applyStrain_getStressTangentDEM(st=D)#input grad(u) from FEM to DEM to get D&sigma #fout2=open('./result/stress&tangOper.dat','w') #fout2.write('tangent'+'\n'+str(update_s)+'\n'+'stress'+'\n'+str(update_stress)) #saveGauss2D(name='./result/gradU+stress+tangent.dat',gradU=D, stress=update_stress, tangent=update_s) #print(type(D)) gradU is a <class 'esys.escriptcore.escriptcpp.Data'> how to transfer to a list to output err=1.0 # initial error before iteration converged=(err<rtol) while not converged: if self.__verbose: print "Not converged after %d iteration(s)! Relative error: %e"%(iterate,err) if iterate>50: rtol = 0.05 #enlarge rtol from 0.01(default) to 0.05 when iterate > 50 if iterate>iter_max: raise RuntimeError,"Convergence not reached after %s steps."%(iter_max) iterate+=1 self.__domain.setX(x_safe+u)#update nodal displacement to do the following calculation self.__pde.setValue(A=update_s,X=-update_stress,r=escript.Data()) #residual=util.L2(self.__pde.getRightHandSide()) du=self.__pde.getSolution() #we do NR iteration to get du u+=du l,d=util.L2(u),util.L2(du) #to get the l2 norm of u and du err=d/l # displacement error, alternatively using force error 'residual' converged=(err<rtol) if err>rtol*0.001: # only update DEM parts when error is large enough???why times 0.001? #it seems whether or not it is converged, the lines below 'if' will always be excuted. So why do we need if? can we just remove it self.__domain.setX(x_safe) #x_safe is constant in this part 'solve' and is not updated. D=util.grad(u) update_stress,update_s,update_scenes=self.applyStrain_getStressTangentDEM(st=D)#DEM calculation!! ''' fout.write('iterate='+str(iterate)+'\n'+'scenes='+'\n'+str(update_scenes)+'\n') ''' #if err>err_safe: # to ensure consistent convergence, however this may not be achieved due to fluctuation! # raise RuntimeError,"No improvement of convergence with iterations! Relative error: %e"%err """ update 'domain geometry', 'stress', 'tangent operator', 'accumulated strain' and 'simulation scenes'. """ self.__domain.setX(x_safe+u) self.__stress=update_stress self.__S=update_s self.__strain+=D self.__scenes=update_scenes if self.__verbose: print "Convergence reached after %d iteration(s)! Relative error: %e"%(iterate,err) return u
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, numLevelSets=1, w0=None, w1=None, wc=None, location_of_set_m=escript.Data(), useDiagonalHessianApproximation=False, tol=1e-8, coordinates=None, scale=None, scale_c=None): """ initialization. :param domain: domain :type domain: `Domain` :param numLevelSets: number of level sets :type numLevelSets: ``int`` :param w0: weighting factor for the m**2 term. If not set zero is assumed. :type w0: ``Scalar`` if ``numLevelSets`` == 1 or `Data` object of shape (``numLevelSets`` ,) if ``numLevelSets`` > 1 :param w1: weighting factor for the grad(m_i) terms. If not set zero is assumed :type w1: ``Vector`` if ``numLevelSets`` == 1 or `Data` object of shape (``numLevelSets`` , DIM) if ``numLevelSets`` > 1 :param wc: weighting factor for the cross gradient terms. If not set zero is assumed. Used for the case if ``numLevelSets`` > 1 only. Only values ``wc[l,k]`` in the lower triangle (l<k) are used. :type wc: `Data` object of shape (``numLevelSets`` , ``numLevelSets``) :param location_of_set_m: marks location of zero values of the level set function ``m`` by a positive entry. :type location_of_set_m: ``Scalar`` if ``numLevelSets`` == 1 or `Data` object of shape (``numLevelSets`` ,) if ``numLevelSets`` > 1 :param useDiagonalHessianApproximation: if True cross gradient terms between level set components are ignored when calculating approximations of the inverse of the Hessian Operator. This can speed-up the calculation of the inverse but may lead to an increase of the number of iteration steps in the inversion. :type useDiagonalHessianApproximation: ``bool`` :param tol: tolerance when solving the PDE for the inverse of the Hessian Operator :type tol: positive ``float`` :param coordinates: defines coordinate system to be used :type coordinates: ReferenceSystem` or `SpatialCoordinateTransformation` :param scale: weighting factor for level set function variation terms. If not set one is used. :type scale: ``Scalar`` if ``numLevelSets`` == 1 or `Data` object of shape (``numLevelSets`` ,) if ``numLevelSets`` > 1 :param scale_c: scale for the cross gradient terms. If not set one is assumed. Used for the case if ``numLevelSets`` > 1 only. Only values ``scale_c[l,k]`` in the lower triangle (l<k) are used. :type scale_c: `Data` object of shape (``numLevelSets``,``numLevelSets``) """ if w0 is None and w1 is None: raise ValueError("Values for w0 or for w1 must be given.") if wc is None and numLevelSets > 1: raise ValueError("Values for wc must be given.") self.__pre_input = None self.__pre_args = None self.logger = logging.getLogger('inv.%s' % self.__class__.__name__) self.__domain = domain DIM = self.__domain.getDim() self.__numLevelSets = numLevelSets self.__trafo = makeTransformation(domain, coordinates) self.__pde = linearPDEs.LinearPDE(self.__domain, numEquations=self.__numLevelSets, numSolutions=self.__numLevelSets) self.__pde.getSolverOptions().setTolerance(tol) self.__pde.setSymmetryOn() self.__pde.setValue( A=self.__pde.createCoefficient('A'), D=self.__pde.createCoefficient('D'), ) try: self.__pde.setValue(q=location_of_set_m) except linearPDEs.IllegalCoefficientValue: raise ValueError( "Unable to set location of fixed level set function.") # =========== check the shape of the scales: ======================== if scale is None: if numLevelSets == 1: scale = 1. else: scale = np.ones((numLevelSets, )) else: scale = np.asarray(scale) if numLevelSets == 1: if scale.shape == (): if not scale > 0: raise ValueError("Value for scale must be positive.") else: raise ValueError("Unexpected shape %s for scale." % scale.shape) else: if scale.shape is (numLevelSets, ): if not min(scale) > 0: raise ValueError( "All values for scale must be positive.") else: raise ValueError("Unexpected shape %s for scale." % scale.shape) if scale_c is None or numLevelSets < 2: scale_c = np.ones((numLevelSets, numLevelSets)) else: scale_c = np.asarray(scale_c) if scale_c.shape == (numLevelSets, numLevelSets): if not all([[scale_c[l, k] > 0. for l in range(k)] for k in range(1, numLevelSets)]): raise ValueError( "All values in the lower triangle of scale_c must be positive." ) else: raise ValueError("Unexpected shape %s for scale." % scale_c.shape) # ===== check the shape of the weights: ============================= if w0 is not None: w0 = escript.interpolate( w0, self.__pde.getFunctionSpaceForCoefficient('D')) s0 = w0.getShape() if numLevelSets == 1: if not s0 == (): raise ValueError("Unexpected shape %s for weight w0." % (s0, )) else: if not s0 == (numLevelSets, ): raise ValueError("Unexpected shape %s for weight w0." % (s0, )) if not self.__trafo.isCartesian(): w0 *= self.__trafo.getVolumeFactor() if not w1 is None: w1 = escript.interpolate( w1, self.__pde.getFunctionSpaceForCoefficient('A')) s1 = w1.getShape() if numLevelSets == 1: if not s1 == (DIM, ): raise ValueError("Unexpected shape %s for weight w1." % (s1, )) else: if not s1 == (numLevelSets, DIM): raise ValueError("Unexpected shape %s for weight w1." % (s1, )) if not self.__trafo.isCartesian(): f = self.__trafo.getScalingFactors( )**2 * self.__trafo.getVolumeFactor() if numLevelSets == 1: w1 *= f else: for i in range(numLevelSets): w1[i, :] *= f if numLevelSets == 1: wc = None else: wc = escript.interpolate( wc, self.__pde.getFunctionSpaceForCoefficient('A')) sc = wc.getShape() if not sc == (numLevelSets, numLevelSets): raise ValueError("Unexpected shape %s for weight wc." % (sc, )) if not self.__trafo.isCartesian(): raise ValueError( "Non-cartesian coordinates for cross-gradient term is not supported yet." ) # ============= now we rescale weights: ============================= L2s = np.asarray(escript.boundingBoxEdgeLengths(domain))**2 L4 = 1 / np.sum(1 / L2s)**2 if numLevelSets == 1: A = 0 if w0 is not None: A = escript.integrate(w0) if w1 is not None: A += escript.integrate(inner(w1, 1 / L2s)) if A > 0: f = scale / A if w0 is not None: w0 *= f if w1 is not None: w1 *= f else: raise ValueError("Non-positive weighting factor detected.") else: # numLevelSets > 1 for k in range(numLevelSets): A = 0 if w0 is not None: A = escript.integrate(w0[k]) if w1 is not None: A += escript.integrate(inner(w1[k, :], 1 / L2s)) if A > 0: f = scale[k] / A if w0 is not None: w0[k] *= f if w1 is not None: w1[k, :] *= f else: raise ValueError( "Non-positive weighting factor for level set component %d detected." % k) # and now the cross-gradient: if wc is not None: for l in range(k): A = escript.integrate(wc[l, k]) / L4 if A > 0: f = scale_c[l, k] / A wc[l, k] *= f # else: # raise ValueError("Non-positive weighting factor for cross-gradient level set components %d and %d detected."%(l,k)) self.__w0 = w0 self.__w1 = w1 self.__wc = wc self.__pde_is_set = False if self.__numLevelSets > 1: self.__useDiagonalHessianApproximation = useDiagonalHessianApproximation else: self.__useDiagonalHessianApproximation = True self._update_Hessian = True self.__num_tradeoff_factors = numLevelSets + ( (numLevelSets - 1) * numLevelSets) // 2 self.setTradeOffFactors() self.__vol_d = escript.vol(self.__domain)
def __init__(self, domain, omega, w, data, F, coordinates=None, fixAtBottom=False, tol=1e-10, saveMemory=True, scaleF=True): """ initializes a new forward model with acoustic wave form inversion. :param domain: domain of the model :type domain: `Domain` :param w: weighting factors :type w: ``Scalar`` :param data: real and imaginary part of data :type data: ``escript.Data`` of shape (2,) :param F: real and imaginary part of source given at Dirac points, on surface or at volume. :type F: ``escript.Data`` of shape (2,) :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 PDE to minimize memory requests. This will require more compute time as the matrix needs to be reallocated. :type saveMemory: ``bool`` :param scaleF: if true source F is scaled to minimize defect. :type scaleF: ``bool`` :param fixAtBottom: if true pressure is fixed to zero at the bottom of the domain :type fixAtBottom: ``bool`` """ super(AcousticWaveForm, self).__init__() self.__trafo = edc.makeTransformation(domain, coordinates) if not self.getCoordinateTransformation().isCartesian(): raise ValueError( "Non-Cartesian Coordinates are not supported yet.") if not isinstance(data, escript.Data): raise ValueError("data must be an escript.Data object.") if not data.getFunctionSpace() == escript.FunctionOnBoundary(domain): raise ValueError("data must be defined on boundary") if not data.getShape() == (2, ): raise ValueError( "data must have shape (2,) (real and imaginary part).") if w is None: w = 1. if not isinstance(w, escript.Data): w = escript.Data(w, escript.FunctionOnBoundary(domain)) else: if not w.getFunctionSpace() == escript.FunctionOnBoundary(domain): raise ValueError("Weights must be defined on boundary.") if not w.getShape() == (): raise ValueError("Weights must be scalar.") self.__domain = domain self.__omega = omega self.__weight = w self.__data = data self.scaleF = scaleF if scaleF: A = escript.integrate(self.__weight * escript.length(self.__data)**2) if A > 0: self.__data *= 1. / escript.sqrt(A) self.__BX = escript.boundingBox(domain) self.edge_lengths = np.asarray(escript.boundingBoxEdgeLengths(domain)) if not isinstance(F, escript.Data): F = escript.interpolate(F, escript.DiracDeltaFunctions(domain)) if not F.getShape() == (2, ): raise ValueError( "Source must have shape (2,) (real and imaginary part).") self.__F = escript.Data() self.__f = escript.Data() self.__f_dirac = escript.Data() if F.getFunctionSpace() == escript.DiracDeltaFunctions(domain): self.__f_dirac = F elif F.getFunctionSpace() == escript.FunctionOnBoundary(domain): self.__f = F else: self.__F = F self.__tol = tol self.__fixAtBottom = fixAtBottom self.__pde = None if not saveMemory: self.__pde = self.setUpPDE()
def setup(self, domainbuilder, rho0=None, drho=None, rho_z0=None, rho_beta=None, k0=None, dk=None, k_z0=None, k_beta=None, w0=None, w1=None, w_gc=None, rho_at_depth=None, k_at_depth=None): """ Sets up the inversion from an instance ``domainbuilder`` of a `DomainBuilder`. Gravity and magnetic data attached to the ``domainbuilder`` are considered in the inversion. If magnetic data are given as scalar it is assumed that values are collected in direction of the background magnetic field. :param domainbuilder: Domain builder object with gravity source(s) :type domainbuilder: `DomainBuilder` :param rho0: reference density, see `DensityMapping`. If not specified, zero is used. :type rho0: ``float`` or `Scalar` :param drho: density scale, see `DensityMapping`. If not specified, 2750kg/m^3 is used. :type drho: ``float`` or `Scalar` :param rho_z0: reference depth for depth weighting for density, see `DensityMapping`. If not specified, zero is used. :type rho_z0: ``float`` or `Scalar` :param rho_beta: exponent for depth weighting for density, see `DensityMapping`. If not specified, zero is used. :type rho_beta: ``float`` or `Scalar` :param k0: reference susceptibility, see `SusceptibilityMapping`. If not specified, zero is used. :type k0: ``float`` or `Scalar` :param dk: susceptibility scale, see `SusceptibilityMapping`. If not specified, 2750kg/m^3 is used. :type dk: ``float`` or `Scalar` :param k_z0: reference depth for depth weighting for susceptibility, see `SusceptibilityMapping`. If not specified, zero is used. :type k_z0: ``float`` or `Scalar` :param k_beta: exponent for depth weighting for susceptibility, see `SusceptibilityMapping`. If not specified, zero is used. :type k_beta: ``float`` or `Scalar` :param w0: weighting factors for level set term regularization, see `Regularization`. If not set zero is assumed. :type w0: `es.Data` or ``ndarray`` of shape (2,) :param w1: weighting factor for the gradient term in the regularization see `Regularization`. If not set zero is assumed :type w1: `es.Data` or ``ndarray`` of shape (2,DIM) :param w_gc: weighting factor for the cross gradient term in the regularization, see `Regularization`. If not set one is assumed :type w_gc: `Scalar` or `float` :param k_at_depth: value for susceptibility at depth, see `DomainBuilder`. :type k_at_depth: ``float`` or ``None`` :param rho_at_depth: value for density at depth, see `DomainBuilder`. :type rho_at_depth: ``float`` or ``None`` """ self.logger.info('Retrieving domain...') dom = domainbuilder.getDomain() DIM = dom.getDim() trafo = makeTransformation(dom, domainbuilder.getReferenceSystem()) #======================== self.logger.info('Creating mappings ...') rho_mask = domainbuilder.getSetDensityMask() if rho_at_depth: rho2 = rho_mask * rho_at_depth + (1 - rho_mask) * rho0 elif rho0: rho2 = (1 - rho_mask) * rho0 else: rho2 = 0 k_mask = domainbuilder.getSetSusceptibilityMask() if k_at_depth: k2 = k_mask * k_at_depth + (1 - k_mask) * k0 elif k0: k2 = (1 - k_mask) * k0 else: k2 = 0 rho_mapping = DensityMapping(dom, rho0=rho2, drho=drho, z0=rho_z0, beta=rho_beta) rho_scale_mapping = rho_mapping.getTypicalDerivative() self.logger.debug("rho_scale_mapping = %s" % rho_scale_mapping) k_mapping = SusceptibilityMapping(dom, k0=k2, dk=dk, z0=k_z0, beta=k_beta) k_scale_mapping = k_mapping.getTypicalDerivative() self.logger.debug("k_scale_mapping = %s" % k_scale_mapping) #======================== self.logger.info("Setting up regularization...") if w1 is None: w1 = np.ones((2, DIM)) wc = es.Data(0., (2, 2), es.Function(dom)) if w_gc is None: wc[0, 1] = 1 else: wc[0, 1] = w_gc reg_mask = es.Data(0., (2, ), es.Solution(dom)) reg_mask[self.DENSITY] = rho_mask reg_mask[self.SUSCEPTIBILITY] = k_mask regularization=Regularization(dom, numLevelSets=2,\ w0=w0, w1=w1,wc=wc, location_of_set_m=reg_mask, coordinates=trafo) #==================================================================== self.logger.info("Retrieving gravity surveys...") surveys = domainbuilder.getGravitySurveys() g = [] w = [] for g_i, sigma_i in surveys: w_i = es.safeDiv(1., sigma_i) if g_i.getRank() == 0: g_i = g_i * es.kronecker(DIM)[DIM - 1] if w_i.getRank() == 0: w_i = w_i * es.kronecker(DIM)[DIM - 1] g.append(g_i) w.append(w_i) self.logger.debug("Added gravity survey:") self.logger.debug("g = %s" % g_i) self.logger.debug("sigma = %s" % sigma_i) self.logger.debug("w = %s" % w_i) self.logger.info("Setting up gravity model...") gravity_model = GravityModel( dom, w, g, fixPotentialAtBottom=self._fixGravityPotentialAtBottom, coordinates=trafo) gravity_model.rescaleWeights(rho_scale=rho_scale_mapping) #==================================================================== self.logger.info("Retrieving magnetic field surveys...") d_b = es.normalize(domainbuilder.getBackgroundMagneticFluxDensity()) surveys = domainbuilder.getMagneticSurveys() B = [] w = [] for B_i, sigma_i in surveys: w_i = es.safeDiv(1., sigma_i) if self.magnetic_intensity_data: if not B_i.getRank() == 0: B_i = es.length(B_i) if not w_i.getRank() == 0: w_i = length(w_i) else: if B_i.getRank() == 0: B_i = B_i * d_b if w_i.getRank() == 0: w_i = w_i * d_b B.append(B_i) w.append(w_i) self.logger.debug("Added magnetic survey:") self.logger.debug("B = %s" % B_i) self.logger.debug("sigma = %s" % sigma_i) self.logger.debug("w = %s" % w_i) self.logger.info("Setting up magnetic model...") if self.self_demagnetization: magnetic_model = SelfDemagnetizationModel( dom, w, B, domainbuilder.getBackgroundMagneticFluxDensity(), fixPotentialAtBottom=self._fixMagneticPotentialAtBottom, coordinates=trafo) else: if self.magnetic_intensity_data: magnetic_model = MagneticIntensityModel( dom, w, B, domainbuilder.getBackgroundMagneticFluxDensity(), fixPotentialAtBottom=self._fixMagneticPotentialAtBottom, coordinates=trafo) else: magnetic_model = MagneticModel( dom, w, B, domainbuilder.getBackgroundMagneticFluxDensity(), fixPotentialAtBottom=self._fixMagneticPotentialAtBottom, coordinates=trafo) magnetic_model.rescaleWeights(k_scale=k_scale_mapping) #==================================================================== self.logger.info("Setting cost function...") self.setCostFunction( InversionCostFunction(regularization, ((rho_mapping, self.DENSITY), (k_mapping, self.SUSCEPTIBILITY)), ((gravity_model, 0), (magnetic_model, 1))))
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)