def solve_alpha_M_beta_F(self, alpha, beta, b, t): """Solve :code:`alpha * M * u + beta * F(u, t) = b` with Dirichlet conditions. """ matrix = alpha * self.M + beta * self.A # See above for float conversion right_hand_side = -float(beta) * self.b.copy() if b: right_hand_side += b for bc in self.dirichlet_bcs: bc.apply(matrix, right_hand_side) # TODO proper preconditioner for convection if self.convection: # Use HYPRE-Euclid instead of ILU for parallel computation. # However, this PC sometimes fails. # solver = KrylovSolver('gmres', 'hypre_euclid') # Fallback: solver = LUSolver() else: solver = KrylovSolver("gmres", "hypre_amg") solver.parameters["relative_tolerance"] = 1.0e-13 solver.parameters["absolute_tolerance"] = 0.0 solver.parameters["maximum_iterations"] = 100 solver.parameters["monitor_convergence"] = True solver.set_operator(matrix) u = Function(self.Q) solver.solve(u.vector(), right_hand_side) return u
def assert_solves( mesh: Mesh, diffusion: Coefficient, convection: Optional[Coefficient], reaction: Optional[Coefficient], source: Coefficient, exact: Coefficient, l2_tol: Optional[float] = 1.0e-8, h1_tol: Optional[float] = 1.0e-6, ): eafe_matrix = eafe_assemble(mesh, diffusion, convection, reaction) pw_linears = FunctionSpace(mesh, "Lagrange", 1) test_function = TestFunction(pw_linears) rhs_vector = assemble(source * test_function * dx) bc = DirichletBC(pw_linears, exact, lambda _, on_bndry: on_bndry) bc.apply(eafe_matrix, rhs_vector) solution = Function(pw_linears) solver = LUSolver(eafe_matrix, "default") solver.parameters["symmetric"] = False solver.solve(solution.vector(), rhs_vector) l2_err: float = errornorm(exact, solution, "l2", 3) assert l2_err <= l2_tol, f"L2 error too large: {l2_err} > {l2_tol}" h1_err: float = errornorm(exact, solution, "H1", 3) assert h1_err <= h1_tol, f"H1 error too large: {h1_err} > {h1_tol}"
def _get_constant_pressure(self, W): w = TrialFunction(W) w_ = TestFunction(W) p_ = self.test_functions()["p"] A, b = assemble_system(inner(w, w_) * dx, p_ * dx) null_fcn = Function(W) solver = LUSolver("mumps") solver.solve(A, null_fcn.vector(), b) return null_fcn
def solve_alpha_M_beta_F(self, alpha, beta, b, t): '''Solve alpha * M * u + beta * F(u, t) = b for u. ''' A = alpha * self.M + beta * self.A # See above for float conversion right_hand_side = - float(beta) * self.b.copy() if b: right_hand_side += b for bc in self.bcs: bc.apply(A, b) # The Krylov solver doesn't converge solver = LUSolver() solver.set_operator(A) u = Function(self.V) solver.solve(u.vector(), b) return u
class BilaplacianPrior(GaussianPrior): """Gaussian prior Parameters must be a dictionary containing: gamma = multiplicative factor applied to <Grad u, Grad v> term beta = multiplicative factor applied to <u,v> term (default=0.0) m0 = mean (or reference parameter when used as regularization) Vm = function space for parameter cost = 1/2 * (m-m0)^T.R.(m-m0)""" def _assemble(self): # Get input: self.gamma = self.Parameters['gamma'] if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta'] else: self.beta = 0.0 self.Vm = self.Parameters['Vm'] if self.Parameters.has_key('m0'): self.m0 = self.Parameters['m0'].copy(deepcopy=True) isFunction(self.m0) else: self.m0 = Function(self.Vm) self.mtrial = TrialFunction(self.Vm) self.mtest = TestFunction(self.Vm) self.mysample = Function(self.Vm) self.draw = Function(self.Vm) # Assemble: self.R = assemble(inner(nabla_grad(self.mtrial), \ nabla_grad(self.mtest))*dx) self.M = PETScMatrix() assemble(inner(self.mtrial, self.mtest) * dx, tensor=self.M) # preconditioner is Gamma^{-1}: if self.beta > 1e-16: self.precond = self.gamma * self.R + self.beta * self.M else: self.precond = self.gamma * self.R + (1e-14) * self.M # Discrete operator K: self.K = self.gamma * self.R + self.beta * self.M # Get eigenvalues for M: self.eigsolM = SLEPcEigenSolver(self.M) self.eigsolM.solve() # Solver for M^{-1}: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(self.M) # Solver for K^{-1}: self.solverK = LUSolver() self.solverK.parameters['reuse_factorization'] = True self.solverK.parameters['symmetric'] = True self.solverK.set_operator(self.K) def Minvpriordot(self, vect): """Here M.Gamma^{-1} = K M^{-1} K""" mhat = Function(self.Vm) self.solverM.solve(mhat.vector(), self.K * vect) return self.K * mhat.vector() def apply_sqrtM(self, vect): """Compute M^{1/2}.vect from Vector() vect""" sqrtMv = Function(self.Vm) for ii in range(self.Vm.dim()): r, c, rx, cx = self.eigsolM.get_eigenpair(ii) RX = Vector(rx) sqrtMv.vector().axpy( np.sqrt(r) * np.dot(rx.array(), vect.array()), RX) return sqrtMv.vector() def Ldot(self, vect): """Here L = K^{-1} M^{1/2}""" Lb = Function(self.Vm) self.solverK.solve(Lb.vector(), self.apply_sqrtM(vect)) return Lb.vector()
class ObjectiveAcoustic(LinearOperator): """ Computes data misfit, gradient and Hessian evaluation for the seismic inverse problem using acoustic wave data """ #TODO: add support for multiple sources # CONSTRUCTORS: def __init__(self, acousticwavePDE, regularization=None): """ Input: acousticwavePDE should be an instantiation from class AcousticWave """ self.PDE = acousticwavePDE self.PDE.exact = None self.fwdsource = self.PDE.ftime self.MG = Function(self.PDE.Vl) self.MGv = self.MG.vector() self.Grad = Function(self.PDE.Vl) self.Gradv = self.Grad.vector() self.srchdir = Function(self.PDE.Vl) self.delta_m = Function(self.PDE.Vl) LinearOperator.__init__(self, self.MG.vector(), self.MG.vector()) self.obsop = None # Observation operator self.dd = None # observations if regularization == None: self.regularization = ZeroRegularization() else: self.regularization = regularization self.alpha_reg = 1.0 # gradient self.lamtest, self.lamtrial = TestFunction(self.PDE.Vl), TrialFunction(self.PDE.Vl) self.p, self.v = Function(self.PDE.V), Function(self.PDE.V) self.wkformgrad = inner(self.lamtest*nabla_grad(self.p), nabla_grad(self.v))*dx # incremental rhs self.lamhat = Function(self.PDE.Vl) self.ptrial, self.ptest = TrialFunction(self.PDE.V), TestFunction(self.PDE.V) self.wkformrhsincr = inner(self.lamhat*nabla_grad(self.ptrial), nabla_grad(self.ptest))*dx # Hessian self.phat, self.vhat = Function(self.PDE.V), Function(self.PDE.V) self.wkformhess = inner(self.lamtest*nabla_grad(self.phat), nabla_grad(self.v))*dx \ + inner(self.lamtest*nabla_grad(self.p), nabla_grad(self.vhat))*dx # Mass matrix: weak_m = inner(self.lamtrial,self.lamtest)*dx Mass = assemble(weak_m) self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(Mass) # Time-integration factors self.factors = np.ones(self.PDE.times.size) self.factors[0], self.factors[-1] = 0.5, 0.5 self.factors *= self.PDE.Dt self.invDt = 1./self.PDE.Dt # Absorbing BCs if self.PDE.abc: #TODO: should probably be tested in other situations if self.PDE.lumpD: print '*** Warning: Damping matrix D is lumped. ',\ 'Make sure gradient is consistent.' self.vD, self.pD, self.p1D, self.p2D = Function(self.PDE.V), \ Function(self.PDE.V), Function(self.PDE.V), Function(self.PDE.V) self.wkformgradD = inner(0.5*sqrt(self.PDE.rho/self.PDE.lam)\ *self.pD, self.vD*self.lamtest)*self.PDE.ds(1) self.wkformDprime = inner(0.5*sqrt(self.PDE.rho/self.PDE.lam)\ *self.lamhat*self.ptrial, self.ptest)*self.PDE.ds(1) self.dp, self.dph, self.vhatD = Function(self.PDE.V), \ Function(self.PDE.V), Function(self.PDE.V) self.p1hatD, self.p2hatD = Function(self.PDE.V), Function(self.PDE.V) self.wkformhessD = inner(-0.25*sqrt(self.PDE.rho)/(self.PDE.lam*sqrt(self.PDE.lam))\ *self.lamhat*self.dp, self.vD*self.lamtest)*self.PDE.ds(1) \ + inner(0.5*sqrt(self.PDE.rho/self.PDE.lam)\ *self.dph, self.vD*self.lamtest)*self.PDE.ds(1)\ + inner(0.5*sqrt(self.PDE.rho/self.PDE.lam)\ *self.dp, self.vhatD*self.lamtest)*self.PDE.ds(1) def copy(self): """(hard) copy constructor""" newobj = self.__class__(self.PDE.copy()) setfct(newobj.MG, self.MG) setfct(newobj.srchdir, self.srchdir) newobj.obsop = self.obsop return newobj # FORWARD PROBLEM + COST: def solvefwd(self, cost=False): self.PDE.set_fwd() self.PDE.ftime = self.fwdsource self.solfwd,_ = self.PDE.solve() # observations: self.Bp = np.zeros((len(self.obsop.PtwiseObs.Points),len(self.solfwd))) for index, sol in enumerate(self.solfwd): setfct(self.p, sol[0]) self.Bp[:,index] = self.obsop.obs(self.p) if cost: assert not self.dd == None, "Provide observations" self.misfit = self.obsop.costfct(self.Bp, self.dd, self.PDE.times) self.cost_reg = self.regularization.cost(self.PDE.lam) self.cost = self.misfit + self.alpha_reg*self.cost_reg def solvefwd_cost(self): self.solvefwd(True) # ADJOINT PROBLEM + GRAD: #@profile def solveadj(self, grad=False): self.PDE.set_adj() self.obsop.assemble_rhsadj(self.Bp, self.dd, self.PDE.times, self.PDE.bc) self.PDE.ftime = self.obsop.ftimeadj self.soladj,_ = self.PDE.solve() if grad: self.MGv.zero() if self.PDE.abc: self.vD.vector().zero(); self.pD.vector().zero(); self.p1D.vector().zero(); self.p2D.vector().zero(); index = 0 for fwd, adj, fact in \ zip(self.solfwd, reversed(self.soladj), self.factors): ttf, tta = fwd[1], adj[1] assert isequal(ttf, tta, 1e-16), \ 'tfwd={}, tadj={}, reldiff={}'.format(ttf, tta, abs(ttf-tta)/ttf) setfct(self.p, fwd[0]) setfct(self.v, adj[0]) self.MGv.axpy(fact, assemble(self.wkformgrad)) # self.MGv.axpy(fact, assemble(self.wkformgrad, \ # form_compiler_parameters={'optimize':True,\ # 'representation':'quadrature'})) if self.PDE.abc: if index%2 == 0: self.p2D.vector().axpy(1.0, self.p.vector()) setfct(self.pD, self.p2D) #self.MGv.axpy(1.0*0.5*self.invDt, assemble(self.wkformgradD)) self.MGv.axpy(fact*0.5*self.invDt, assemble(self.wkformgradD)) setfct(self.p2D, -1.0*self.p.vector()) setfct(self.vD, self.v) else: self.p1D.vector().axpy(1.0, self.p.vector()) setfct(self.pD, self.p1D) #self.MGv.axpy(1.0*0.5*self.invDt, assemble(self.wkformgradD)) self.MGv.axpy(fact*0.5*self.invDt, assemble(self.wkformgradD)) setfct(self.p1D, -1.0*self.p.vector()) setfct(self.vD, self.v) index += 1 self.MGv.axpy(self.alpha_reg, self.regularization.grad(self.PDE.lam)) self.solverM.solve(self.Gradv, self.MGv) def solveadj_constructgrad(self): self.solveadj(True) # HESSIAN: def ftimeincrfwd(self, tt): """ Compute rhs for incremental forward at time tt """ try: index = int(np.where(isequal(self.PDE.times, tt, 1e-14))[0]) except: print 'Error in ftimeincrfwd at time {}'.format(tt) print np.min(np.abs(self.PDE.times-tt)) sys.exit(0) # lamhat * grad(p).grad(vtilde) assert isequal(tt, self.solfwd[index][1], 1e-16) setfct(self.p, self.solfwd[index][0]) setfct(self.v, self.C*self.p.vector()) # D'.dot(p) if self.PDE.abc and index > 0: setfct(self.p, \ self.solfwd[index+1][0] - self.solfwd[index-1][0]) self.v.vector().axpy(.5*self.invDt, self.Dp*self.p.vector()) return -1.0*self.v.vector().array() def ftimeincradj(self, tt): """ Compute rhs for incremental adjoint at time tt """ try: indexf = int(np.where(isequal(self.PDE.times, tt, 1e-14))[0]) indexa = int(np.where(isequal(self.PDE.times[::-1], tt, 1e-14))[0]) except: print 'Error in ftimeincradj at time {}'.format(tt) print np.min(np.abs(self.PDE.times-tt)) sys.exit(0) # lamhat * grad(ptilde).grad(v) assert isequal(tt, self.soladj[indexa][1], 1e-16) setfct(self.v, self.soladj[indexa][0]) setfct(self.vhat, self.C*self.v.vector()) # B* B phat assert isequal(tt, self.solincrfwd[indexf][1], 1e-16) setfct(self.phat, self.solincrfwd[indexf][0]) self.vhat.vector().axpy(1.0, self.obsop.incradj(self.phat, tt)) # D'.dot(v) if self.PDE.abc and indexa > 0: setfct(self.v, \ self.soladj[indexa-1][0] - self.soladj[indexa+1][0]) self.vhat.vector().axpy(-.5*self.invDt, self.Dp*self.v.vector()) return -1.0*self.vhat.vector().array() def mult(self, lamhat, y): """ mult(self, lamhat, y): return y = Hessian * lamhat inputs: y, lamhat = Function(V).vector() """ self.regularization.assemble_hessian(lamhat) setfct(self.lamhat, lamhat) self.C = assemble(self.wkformrhsincr) if self.PDE.abc: self.Dp = assemble(self.wkformDprime) # solve for phat self.PDE.set_fwd() self.PDE.ftime = self.ftimeincrfwd self.solincrfwd,_ = self.PDE.solve() # solve for vhat self.PDE.set_adj() self.PDE.ftime = self.ftimeincradj self.solincradj,_ = self.PDE.solve() # Compute Hessian*lamhat y.zero() index = 0 if self.PDE.abc: self.vD.vector().zero(); self.vhatD.vector().zero(); self.p1D.vector().zero(); self.p2D.vector().zero(); self.p1hatD.vector().zero(); self.p2hatD.vector().zero(); for fwd, adj, incrfwd, incradj, fact in \ zip(self.solfwd, reversed(self.soladj), \ self.solincrfwd, reversed(self.solincradj), self.factors): ttf, tta, ttf2 = incrfwd[1], incradj[1], fwd[1] assert isequal(ttf, tta, 1e-16), 'tfwd={}, tadj={}, reldiff={}'.\ format(ttf, tta, abs(ttf-tta)/ttf) assert isequal(ttf, ttf2, 1e-16), 'tfwd={}, tadj={}, reldiff={}'.\ format(ttf, ttf2, abs(ttf-ttf2)/ttf) setfct(self.p, fwd[0]) setfct(self.v, adj[0]) setfct(self.phat, incrfwd[0]) setfct(self.vhat, incradj[0]) y.axpy(fact, assemble(self.wkformhess)) if self.PDE.abc: if index%2 == 0: self.p2D.vector().axpy(1.0, self.p.vector()) self.p2hatD.vector().axpy(1.0, self.phat.vector()) setfct(self.dp, self.p2D) setfct(self.dph, self.p2hatD) y.axpy(1.0*0.5*self.invDt, assemble(self.wkformhessD)) setfct(self.p2D, -1.0*self.p.vector()) setfct(self.p2hatD, -1.0*self.phat.vector()) else: self.p1D.vector().axpy(1.0, self.p.vector()) self.p1hatD.vector().axpy(1.0, self.phat.vector()) setfct(self.dp, self.p1D) setfct(self.dph, self.p1hatD) y.axpy(1.0*0.5*self.invDt, assemble(self.wkformhessD)) setfct(self.p1D, -1.0*self.p.vector()) setfct(self.p1hatD, -1.0*self.phat.vector()) setfct(self.vD, self.v) setfct(self.vhatD, self.vhat) index += 1 # add regularization term y.axpy(self.alpha_reg, self.regularization.hessian(lamhat)) # SETTERS + UPDATE: def update_PDE(self, parameters): self.PDE.update(parameters) def update_m(self, lam): self.update_PDE({'lambda':lam}) def set_abc(self, mesh, class_bc_abc, lumpD): self.PDE.set_abc(mesh, class_bc_abc, lumpD) def backup_m(self): self.lam_bkup = self.getmarray() def restore_m(self): self.update_m(self.lam_bkup) def setsrcterm(self, ftime): self.PDE.ftime = ftime # GETTERS: def getmcopyarray(self): return self.lam_bkup def getmarray(self): return self.PDE.lam.vector().array() def getMGarray(self): return self.MGv.array()
class BilaplacianPrior(GaussianPrior): """Gaussian prior Parameters must be a dictionary containing: gamma = multiplicative factor applied to <Grad u, Grad v> term beta = multiplicative factor applied to <u,v> term (default=0.0) m0 = mean (or reference parameter when used as regularization) Vm = function space for parameter cost = 1/2 * (m-m0)^T.R.(m-m0)""" def _assemble(self): # Get input: self.gamma = self.Parameters['gamma'] if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta'] else: self.beta = 0.0 self.Vm = self.Parameters['Vm'] if self.Parameters.has_key('m0'): self.m0 = self.Parameters['m0'].copy(deepcopy=True) isFunction(self.m0) else: self.m0 = Function(self.Vm) self.mtrial = TrialFunction(self.Vm) self.mtest = TestFunction(self.Vm) self.mysample = Function(self.Vm) self.draw = Function(self.Vm) # Assemble: self.R = assemble(inner(nabla_grad(self.mtrial), \ nabla_grad(self.mtest))*dx) self.M = PETScMatrix() assemble(inner(self.mtrial, self.mtest)*dx, tensor=self.M) # preconditioner is Gamma^{-1}: if self.beta > 1e-16: self.precond = self.gamma*self.R + self.beta*self.M else: self.precond = self.gamma*self.R + (1e-14)*self.M # Discrete operator K: self.K = self.gamma*self.R + self.beta*self.M # Get eigenvalues for M: self.eigsolM = SLEPcEigenSolver(self.M) self.eigsolM.solve() # Solver for M^{-1}: self.solverM = LUSolver() self.solverM.parameters['reuse_factorization'] = True self.solverM.parameters['symmetric'] = True self.solverM.set_operator(self.M) # Solver for K^{-1}: self.solverK = LUSolver() self.solverK.parameters['reuse_factorization'] = True self.solverK.parameters['symmetric'] = True self.solverK.set_operator(self.K) def Minvpriordot(self, vect): """Here M.Gamma^{-1} = K M^{-1} K""" mhat = Function(self.Vm) self.solverM.solve(mhat.vector(), self.K*vect) return self.K * mhat.vector() def apply_sqrtM(self, vect): """Compute M^{1/2}.vect from Vector() vect""" sqrtMv = Function(self.Vm) for ii in range(self.Vm.dim()): r, c, rx, cx = self.eigsolM.get_eigenpair(ii) RX = Vector(rx) sqrtMv.vector().axpy(np.sqrt(r)*np.dot(rx.array(), vect.array()), RX) return sqrtMv.vector() def Ldot(self, vect): """Here L = K^{-1} M^{1/2}""" Lb = Function(self.Vm) self.solverK.solve(Lb.vector(), self.apply_sqrtM(vect)) return Lb.vector()