def test_form_splitter_matrices(shape): mesh = UnitSquareMesh(dolfin.MPI.comm_self, 3, 3) Vu = FunctionSpace(mesh, 'DG', 2) Vp = FunctionSpace(mesh, 'DG', 1) def define_eq(u, v, p, q): "A simple Stokes-like coupled weak form" eq = Constant(2) * dot(u, v) * dx eq += dot(grad(p), v) * dx eq -= dot(Constant([1, 1]), v) * dx eq += dot(grad(q), u) * dx eq -= dot(Constant(0.3), q) * dx return eq if shape == (2, 2): eu = MixedElement([Vu.ufl_element(), Vu.ufl_element()]) ew = MixedElement([eu, Vp.ufl_element()]) W = FunctionSpace(mesh, ew) u, p = TrialFunctions(W) v, q = TestFunctions(W) u = as_vector([u[0], u[1]]) v = as_vector([v[0], v[1]]) elif shape == (3, 3): ew = MixedElement( [Vu.ufl_element(), Vu.ufl_element(), Vp.ufl_element()]) W = FunctionSpace(mesh, ew) u0, u1, p = TrialFunctions(W) v0, v1, q = TestFunctions(W) u = as_vector([u0, u1]) v = as_vector([v0, v1]) eq = define_eq(u, v, p, q) # Define coupled problem eq = define_eq(u, v, p, q) # Split the weak form into a saddle point block matrix system mat, vec = split_form_into_matrix(eq, W, W) # Check shape and that appropriate elements are none assert mat.shape == shape assert mat[-1, -1] is None for i in range(shape[0] - 1): for j in range(shape[1] - 1): if i != j: assert mat[i, j] is None # Check that the split matrices are identical with the # coupled matrix when reassembled into a single matrix compare_split_matrices(eq, mat, vec, W, W)
def compute_babuska_infsup(c, m, W, bc=None, inverse=False): """ For a given form c : V x V \rightarrow \R and an inner product m defining V and a element space function space W, compute the Babuska inf-sup constant. The boolean 'inverse' is for ... It defaults to False. """ u = TrialFunctions(W) v = TestFunctions(W) if len(u) == 1: u, v = u[0], v[0] lhs = c(v, u) rhs = m(v, u) if inverse: lhs, rhs = rhs, lhs # Compute eigenvalues params = ascot_parameters["babuska"] num = ascot_parameters["number_of_eigenvalues"] eigenvalues = EigenProblem(lhs, rhs, params, bc).solve(num) return InfSupConstant(W.mesh().hmax(), eigenvalues)
def _init_form(self, **kwargs): M_i = self.parameters['M_i'] M_e = self.parameters['M_e'] I_a = self.parameters['I_a'] # externally applied current dt = self.parameters['dt'] theta = self.parameters['theta'] use_constraint = self.parameters['use_average_u_constraint'] if use_constraint: v, u, l = TrialFunctions(self.solution_space) w, q, lbd = TestFunctions(self.solution_space) else: v, u = TrialFunctions(self.solution_space) w, q = TestFunctions(self.solution_space) dx = self.geometry.dx v_ = self.prev_current k = Constant(1 / dt) t = float(self.time) # bidomain equation Vmid = theta * v + (1 - theta) * v_ theta_parabolic = (inner(M_i*grad(Vmid), grad(w))*dx\ + inner(M_i*grad(u), grad(w))*dx) theta_elliptic = (inner(M_i*grad(Vmid), grad(q))*dx\ + inner((M_i + M_e)*grad(u), grad(q))*dx) self._form = k * (v - v_) * w * dx + theta_parabolic + theta_elliptic if use_constraint: self._form += (lbd * u + l * q) * dx # external current self._form += I_a * q * dx # stimulus if 'STIMULUS' in self.geometry.markers.keys(): stim_marker = self.geometry.markers['STIMULUS'] I_s = Stimulus(self.geometry.markers, stim_marker, amplitude=self.parameters["I_s"]['amplitude'], period=self.parameters["I_s"]['amplitude'], duration=self.parameters["I_s"]['duration'], t=self.time, degree=1) self._form += I_s * w * dx
def solve(self): """ Solve Stokes problem with current mesh """ (u, p) = TrialFunctions(self.VQ) (v, q) = TestFunctions(self.VQ) a = inner(grad(u), grad(v))*dx - div(u)*q*dx - div(v)*p*dx l = inner(self.f, v)*dx solve(a==l, self.w, bcs=self.bcs) self.u, self.p = self.w.split()
def compute_brezzi_coercivity(xxx_todo_changeme1, m, W, bc=None): """ For given forms a: V x V \rightarrow \R and b: V x Q \rightarrow \R and an inner product m defining V and a function space W = V_h x Q_h, compute the Brezzi coercivity constant. """ (a, b) = xxx_todo_changeme1 (u, p) = TrialFunctions(W) (v, q) = TestFunctions(W) lhs = a(v, u) + b(v, p) + b(u, q) rhs = m(v, u) # Compute eigenvalues params = ascot_parameters["brezzi_coercivity"] num = ascot_parameters["number_of_eigenvalues"] eigenvalues = EigenProblem(lhs, rhs, params, bc).solve(num) return InfSupConstant(W.mesh().hmax(), eigenvalues)
def compute_brezzi_infsup(b, xxx_todo_changeme, W, bc=None): """ For a given form b: V x Q \rightarrow \R and inner products m and n defining V and Q respectively and a function space W = V_h x Q_h, compute the Brezzi inf-sup constant. """ (m, n) = xxx_todo_changeme (u, p) = TrialFunctions(W) (v, q) = TestFunctions(W) lhs = m(v, u) + b(v, p) + b(u, q) rhs = -n(q, p) # Get parameters params = ascot_parameters["brezzi_infsup"] num = ascot_parameters["number_of_eigenvalues"] # Compute eigenvalues eigenvalues = EigenProblem(lhs, rhs, params, bc).solve(num) return InfSupConstant(W.mesh().hmax(), eigenvalues, operator=sqrt)
def assemble_matrices(self): nedelec = FiniteElement( "Nedelec 1st kind H(curl)", self.mesh.ufl_cell(), self.nedelec_order) lagrange = FiniteElement( "Lagrange", self.mesh.ufl_cell(), self.lagrange_order) self._finite_element = nedelec*lagrange self._combined_space = FunctionSpace(self.mesh, nedelec*lagrange) # define the test and trial functions from the combined space (N_i, L_i) = TestFunctions(self._combined_space) (N_j, L_j) = TrialFunctions(self._combined_space) # construct matrices for each domain if not isinstance(self.materials, collections.Iterable): material = self.materials u_r = material.em.u_r e_r = material.em.e_r DX = material.domain (A_ij, B_ij) = em_weak_form(N_i, N_j, L_i, L_j, u_r, e_r, self._k_o_squared,DX) else: counter = 0 # a work around for summing forms for material in self.materials: u_r = material.em.u_r e_r = material.em.e_r DX = material.domain if counter == 0: (A_ij, B_ij) = em_weak_form(N_i, N_j, L_i, L_j, u_r, e_r, self._k_o_squared, DX) counter += 1 else: (a_ij, b_ij) = em_weak_form(N_i, N_j, L_i, L_j, u_r, e_r, self._k_o_squared, DX) A_ij += a_ij B_ij += b_ij # assemble the matrices assemble(A_ij, tensor=self._A) assemble(B_ij, tensor=self._B)
def solve(self): """ Solves the stokes equation with the current multimesh """ (u, p) = TrialFunctions(self.VQ) (v, q) = TestFunctions(self.VQ) n = FacetNormal(self.multimesh) h = 2.0 * Circumradius(self.multimesh) alpha = Constant(6.0) tensor_jump = lambda u: outer(u("+"), n("+")) + outer(u("-"), n("-")) a_s = inner(grad(u), grad(v)) * dX a_IP = - inner(avg(grad(u)), tensor_jump(v))*dI\ - inner(avg(grad(v)), tensor_jump(u))*dI\ + alpha/avg(h) * inner(jump(u), jump(v))*dI a_O = inner(jump(grad(u)), jump(grad(v))) * dO b_s = -div(u) * q * dX - div(v) * p * dX b_IP = jump(u, n) * avg(q) * dI + jump(v, n) * avg(p) * dI l_s = inner(self.f, v) * dX s_C = h*h*inner(-div(grad(u)) + grad(p), -div(grad(v)) - grad(q))*dC\ + h("+")*h("+")*inner(-div(grad(u("+"))) + grad(p("+")), -div(grad(v("+"))) + grad(q("+")))*dO l_C = h*h*inner(self.f, -div(grad(v)) - grad(q))*dC\ + h("+")*h("+")*inner(self.f("+"), -div(grad(v("+"))) - grad(q("+")))*dO a = a_s + a_IP + a_O + b_s + b_IP + s_C l = l_s + l_C A = assemble_multimesh(a) L = assemble_multimesh(l) [bc.apply(A, L) for bc in self.bcs] self.VQ.lock_inactive_dofs(A, L) solve(A, self.w.vector(), L, "mumps") self.splitMMF()
def stokes_solve(up_out, mu, u_bcs, p_bcs, f, my_dx=dx): # Some initial sanity checks. assert mu > 0.0 WP = up_out.function_space() # Translate the boundary conditions into the product space. new_bcs = helpers.dbcs_to_productspace(WP, [u_bcs, p_bcs]) # TODO define p*=-1 and reverse sign in the end to get symmetric system? # Define variational problem (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) mesh = WP.mesh() r = SpatialCoordinate(mesh)[0] # build system f = F(u, p, v, q, f, r, mu, my_dx) a = lhs(f) L = rhs(f) A, b = assemble_system(a, L, new_bcs) mode = "lu" assert mode == "lu" solve(A, up_out.vector(), b, "lu") # TODO Krylov solver for Stokes # assert mode == 'gmres' # # For preconditioners for the Stokes system, see # # # # Fast iterative solvers for discrete Stokes equations; # # J. Peters, V. Reichelt, A. Reusken. # # # prec = mu * inner(r * grad(u), grad(v)) * 2 * pi * my_dx \ # - p * q * 2 * pi * r * my_dx # P, _ = assemble_system(prec, L, new_bcs) # solver = KrylovSolver('tfqmr', 'hypre_amg') # # solver = KrylovSolver('gmres', 'hypre_amg') # solver.set_operators(A, P) # solver.parameters['monitor_convergence'] = verbose # solver.parameters['report'] = verbose # solver.parameters['absolute_tolerance'] = 0.0 # solver.parameters['relative_tolerance'] = tol # solver.parameters['maximum_iterations'] = maxiter # # Solve # solver.solve(up_out.vector(), b) # elif mode == 'fieldsplit': # # For an assortment of preconditioners, see # # # # Performance and analysis of saddle point preconditioners # # for the discrete steady-state Navier-Stokes equations; # # H.C. Elman, D.J. Silvester, A.J. Wathen; # # Numer. Math. (2002) 90: 665-688; # # <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>. # # # # Set up field split. # W = SubSpace(WP, 0) # P = SubSpace(WP, 1) # u_dofs = W.dofmap().dofs() # p_dofs = P.dofmap().dofs() # prec = PETScPreconditioner() # prec.set_fieldsplit([u_dofs, p_dofs], ['u', 'p']) # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'additive') # PETScOptions.set('fieldsplit_u_pc_type', 'lu') # PETScOptions.set('fieldsplit_p_pc_type', 'jacobi') # # Create Krylov solver with custom preconditioner. # solver = PETScKrylovSolver('gmres', prec) # solver.set_operator(A) return
def xtest_mg_solver_stokes(): mesh0 = UnitCubeMesh(2, 2, 2) mesh1 = UnitCubeMesh(4, 4, 4) mesh2 = UnitCubeMesh(8, 8, 8) Ve = VectorElement("CG", mesh0.ufl_cell(), 2) Qe = FiniteElement("CG", mesh0.ufl_cell(), 1) Ze = MixedElement([Ve, Qe]) Z0 = FunctionSpace(mesh0, Ze) Z1 = FunctionSpace(mesh1, Ze) Z2 = FunctionSpace(mesh2, Ze) W = Z2 # Boundaries def right(x, on_boundary): return x[0] > (1.0 - DOLFIN_EPS) def left(x, on_boundary): return x[0] < DOLFIN_EPS def top_bottom(x, on_boundary): return x[1] > 1.0 - DOLFIN_EPS or x[1] < DOLFIN_EPS # No-slip boundary condition for velocity noslip = Constant((0.0, 0.0, 0.0)) bc0 = DirichletBC(W.sub(0), noslip, top_bottom) # Inflow boundary condition for velocity inflow = Expression(("-sin(x[1]*pi)", "0.0", "0.0"), degree=2) bc1 = DirichletBC(W.sub(0), inflow, right) # Collect boundary conditions bcs = [bc0, bc1] # Define variational problem (u, p) = TrialFunctions(W) (v, q) = TestFunctions(W) f = Constant((0.0, 0.0, 0.0)) a = inner(grad(u), grad(v)) * dx + div(v) * p * dx + q * div(u) * dx L = inner(f, v) * dx # Form for use in constructing preconditioner matrix b = inner(grad(u), grad(v)) * dx + p * q * dx # Assemble system A, bb = assemble_system(a, L, bcs) # Assemble preconditioner system P, btmp = assemble_system(b, L, bcs) spaces = [Z0, Z1, Z2] dm_collection = PETScDMCollection(spaces) solver = PETScKrylovSolver() solver.set_operators(A, P) PETScOptions.set("ksp_type", "gcr") PETScOptions.set("pc_type", "mg") PETScOptions.set("pc_mg_levels", 3) PETScOptions.set("pc_mg_galerkin") PETScOptions.set("ksp_monitor_true_residual") PETScOptions.set("ksp_atol", 1.0e-10) PETScOptions.set("ksp_rtol", 1.0e-10) solver.set_from_options() from petsc4py import PETSc ksp = solver.ksp() ksp.setDM(dm_collection.dm()) ksp.setDMActive(False) x = PETScVector() solver.solve(x, bb) # Check multigrid solution against LU solver solver = LUSolver(A) # noqa x_lu = PETScVector() solver.solve(x_lu, bb) assert round((x - x_lu).norm("l2"), 10) == 0 # Clear all PETSc options opts = PETSc.Options() for key in opts.getAll(): opts.delValue(key)
# The first argument to # :py:class:`DirichletBC <dolfin.cpp.fem.DirichletBC>` # specifies the :py:class:`FunctionSpace # <dolfin.cpp.function.FunctionSpace>`. Since we have a # mixed function space, we write # ``W.sub(0)`` for the velocity component of the space, and # ``W.sub(1)`` for the pressure component of the space. # The second argument specifies the value on the Dirichlet # boundary. The last two arguments specify the marking of the subdomains: # ``sub_domains`` contains the subdomain markers, and the final argument is the subdomain index. # # The bilinear and linear forms corresponding to the weak mixed # formulation of the Stokes equations are defined as follows:: # Define variational problem (u, p) = TrialFunctions(W) (v, q) = TestFunctions(W) f = Function(W.sub(0).collapse()) a = (inner(grad(u), grad(v)) - inner(p, div(v)) + inner(div(u), q)) * dx L = inner(f, v) * dx # We also need to create a :py:class:`Function # <dolfin.cpp.function.Function>` to store the solution(s). The (full) # solution will be stored in ``w``, which we initialize using the mixed # function space ``W``. The actual # computation is performed by calling solve with the arguments ``a``, # ``L``, ``w`` and ``bcs``. The separate components ``u`` and ``p`` of # the solution can be extracted by calling the :py:meth:`split # <dolfin.functions.function.Function.split>` function. Here we use an # optional argument True in the split function to specify that we want a # deep copy. If no argument is given we will get a shallow copy. We want
def solverNeilan(P, opt): ''' This function implements the method presented in Neilan:Convergence Analysis of a Finite Element Method for Second Order Non-Variational Problems Main characteristics: - no stabilization - method relies on a discontinuous Hessian approximation - no action on test function of solution u_h - no normalization of system ''' (trial_u, trial_H) = TrialFunctions(P.mixedSpace) (test_u, test_H) = TestFunctions(P.mixedSpace) # Define bilinear form jump_outer = inner(grad(trial_u), test_H * P.nE) * ds jump_inner = inner(avg(grad(trial_u)), test_H('+') * P.nE('+') + test_H('-') * P.nE('-')) * dS a_h = -inner(P.a, trial_H) * test_u * dx \ + inner(trial_H, test_H) * dx \ + inner(grad(trial_u), div(test_H)) * dx \ - jump_inner - jump_outer if P.hasDrift: a_h += -inner(P.b, grad(trial_u)) * test_u * dx if P.hasPotential: a_h += -P.c * trial_u * test_u * dx # Define linear form f_h = -P.f * test_u * dx # Adjust system matrix and load vector in case of time-dependent problem if P.isTimeDependant: a_h += 1. / P.dt * trial_u * test_u * dx f_h += 1. / P.dt * P.u_np1 * test_u * dx # Set boundary conditions print('Setting boundary conditions') if isinstance(P.g, list): bc_V = [] for fun, dom in P.g: bc_V.append(DirichletBC(P.mixedSpace.sub(0), fun, dom)) else: if isinstance(P.g, Function): bc_V = DirichletBC(P.mixedSpace.sub(0), P.g, 'on_boundary') else: g = project(P.g, P.V) bc_V = DirichletBC(P.mixedSpace.sub(0), g, 'on_boundary') if opt['time_check']: t1 = time() S, rhs = assemble_system(a_h, f_h, bc_V) try: solve(S, P.x.vector(), rhs) except RuntimeError: try: solve(S, P.x.vector(), rhs, 'gmres', 'hypre_amg') except RuntimeError: import ipdb ipdb.set_trace() N_iter = 1 if opt['time_check']: print("Solve linear equation system ... %.2fs" % (time() - t1)) sys.stdout.flush() return N_iter
def cmscr1dnewton(img: np.array, alpha0: float, alpha1: float, alpha2: float, alpha3: float, beta: float) \ -> (np.array, np.array, float, float, bool): """Same as cmscr1d but doesn't use FEniCS Newton method. Args: img (np.array): A 1D image sequence of shape (m, n), where m is the number of time steps and n is the number of pixels. alpha0 (float): The spatial regularisation parameter for v. alpha1 (float): The temporal regularisation parameter for v. alpha2 (float): The spatial regularisation parameter for k. alpha3 (float): The temporal regularisation parameter for k. beta (float): The parameter for convective regularisation. Returns: v: A velocity array of shape (m, n). k: A source array of shape (m, n). res (float): The residual. fun (float): The function value. converged (bool): True if Newton's method converged. """ # Create mesh. [m, n] = img.shape mesh = UnitSquareMesh(m - 1, n - 1) # Define function space and functions. W = VectorFunctionSpace(mesh, 'CG', 1, dim=2) w = Function(W) v, k = split(w) w1, w2 = TestFunctions(W) # Convert image to function. V = FunctionSpace(mesh, 'CG', 1) f = Function(V) f.vector()[:] = dh.img2funvec(img) # Define derivatives of data. ft = Function(V) ftv = np.diff(img, axis=0) * (m - 1) ftv = np.concatenate((ftv, ftv[-1, :].reshape(1, n)), axis=0) ft.vector()[:] = dh.img2funvec(ftv) fx = Function(V) fxv = np.gradient(img, 1 / (n - 1), axis=1) fx.vector()[:] = dh.img2funvec(fxv) # Define weak formulation. F = - (ft + fx*v + f*v.dx(1) - k) * (fx*w1 + f*w1.dx(1))*dx \ - alpha0*v.dx(1)*w1.dx(1)*dx - alpha1*v.dx(0)*w1.dx(0)*dx \ - beta*k.dx(1)*(k.dx(0) + k.dx(1)*v)*w1*dx \ + (ft + fx*v + f*v.dx(1) - k)*w2*dx \ - alpha2*k.dx(1)*w2.dx(1)*dx - alpha3*k.dx(0)*w2.dx(0)*dx \ - beta*(k.dx(0)*w2.dx(0) + k.dx(1)*v*w2.dx(0) + k.dx(0)*v*w2.dx(1) + k.dx(1)*v*v*w2.dx(1))*dx # Define derivative. dv, dk = TrialFunctions(W) J = - (fx*dv + f*dv.dx(1) - dk)*(fx*w1 + f*w1.dx(1))*dx \ - alpha0*dv.dx(1)*w1.dx(1)*dx - alpha1*dv.dx(0)*w1.dx(0)*dx \ - beta*(k.dx(1)*(dk.dx(0) + dk.dx(1)*v) + k.dx(1)**2*dv + dk.dx(1)*(k.dx(0) + k.dx(1)*v))*w1*dx \ + (fx*dv + f*dv.dx(1) - dk)*w2*dx \ - alpha2*dk.dx(1)*w2.dx(1)*dx - alpha3*dk.dx(0)*w2.dx(0)*dx \ - beta*(dv*k.dx(1) + dk.dx(0) + v*dk.dx(1))*w2.dx(0)*dx \ - beta*(dv*k.dx(0) + 2*v*dv*k.dx(1) + v*dk.dx(0) + v*v*dk.dx(1))*w2.dx(1)*dx # Alternatively, use automatic differentiation. # J = derivative(F, w) # Define algorithm parameters. tol = 1e-10 maxiter = 100 # Define increment. dw = Function(W) # Run Newton iteration. niter = 0 eps = 1 res = 1 while res > tol and niter < maxiter: niter += 1 # Solve for increment. solve(J == -F, dw) # Update solution. w.assign(w + dw) # Compute norm of increment. eps = dw.vector().norm('l2') # Compute norm of residual. a = assemble(F) res = a.norm('l2') # Print statistics. print("Iteration {0} eps={1} res={2}".format(niter, eps, res)) # Split solution. v, k = w.split(deepcopy=True) # Evaluate and print residual and functional value. res = abs(ft + fx * v + f * v.dx(1) - k) fun = 0.5 * (res**2 + alpha0 * v.dx(1)**2 + alpha1 * v.dx(0)**2 + alpha2 * k.dx(1)**2 + alpha3 * k.dx(0)**2 + beta * (k.dx(0) + k.dx(1) * v)**2) print('Res={0}, Func={1}'.format(assemble(res * dx), assemble(fun * dx))) # Convert back to array. vel = dh.funvec2img(v.vector().get_local(), m, n) k = dh.funvec2img(k.vector().get_local(), m, n) return vel, k, assemble(res * dx), assemble(fun * dx), res > tol
def build_system(V, dx, Mu, Sigma, omega, f_list, f_degree, convections, bcs): """Build FEM system for .. math:: \\div\\left(\\frac{1}{\\mu r} \\nabla(r\\phi)\\right) + \\left\\langle u, \\frac{1}{r} \\nabla(r\\phi)\\right\\rangle + \\text{i} \\sigma \\omega \\phi = f by multiplying with :math:`2\\pi r v` and integrating over the domain and the preconditioner given by :cite:`KL2012`. """ r = SpatialCoordinate(V.mesh())[0] subdomain_indices = Mu.keys() ee = V.ufl_element() * V.ufl_element() W = FunctionSpace(V.mesh(), ee) # Bilinear form. ur, ui = TrialFunctions(W) vr, vi = TestFunctions(W) # build right-hand sides b_list = [] for f in f_list: L = +Constant(0.0) * vr * dx(0) + Constant(0.0) * vi * dx(0) for i, fval in f.items(): L += +fval[0] * vr * 2 * pi * r * dx( i, degree=f_degree) + fval[1] * vi * 2 * pi * r * dx( i, degree=f_degree) b_list.append(assemble(L)) # div(1/(mu r) grad(r phi)) + i sigma omega phi # # Split up diffusive and reactive terms to be able to assemble them with # different FFC parameters. # # Make omega a constant function to avoid rebuilding the equation system # when omega changes. om = Constant(omega) a1 = Constant(0.0) * ur * vr * dx(0) a2 = Constant(0.0) * ur * vr * dx(0) for i in subdomain_indices: # The term 1/r looks like it might cause problems. The dubious term is # # 1/r d/dr (r u_r) = u_r + 1/r du_r/dr, # # so we have to make sure that 1/r du_r/dr is bounded for all trial # functions u. This is guaranteed when taking Dirichlet boundary # conditions at r=0. sigma = Constant(Sigma[i]) a1 += +1.0 / (Mu[i] * r) * dot(grad(r * ur), grad( r * vr)) * 2 * pi * dx(i) + 1.0 / (Mu[i] * r) * dot( grad(r * ui), grad(r * vi)) * 2 * pi * dx(i) a2 += -om * sigma * ui * vr * 2 * pi * r * dx( i) + om * sigma * ur * vi * 2 * pi * r * dx(i) # Don't do anything at the interior boundary. Taking the Poisson # problem as an example, the weak formulation is # # \int \Delta(u) v = -\int grad(u).grad(v) + \int_ n.grad(u) v. # # If we have 'artificial' boundaries through the domain, we would # like to make sure that along those boundaries, the equation is # exactly what it would be without the them. The important case to # look at are the trial and test functions which are nonzero on # the boundary. It is clear that the integral along the interface # boundary has to be omitted. # Add the convective component for the workpiece, # a += <u, 1/r grad(r phi)> *2*pi*r*dx for i, conv in convections.items(): a1 += +dot(conv, grad(r * ur)) * vr * 2 * pi * dx(i) + dot( conv, grad(r * ui)) * vi * 2 * pi * dx(i) force_m_matrix = False if force_m_matrix: A1 = assemble(a1) A2 = assemble( a2, form_compiler_parameters={ "quadrature_rule": "vertex", "quadrature_degree": 1, }, ) A = A1 + A2 else: # Assembling the thing into one single object makes it possible to # extract .data() for conversion to SciPy's sparse types later. A = assemble(a1 + a2) # Compute the preconditioner as described in # # A robust preconditioned MINRES-solver for time-periodic eddy-current # problems; # M. Kolmbauer, U. Langer; # <http://www.numa.uni-linz.ac.at/Publications/List/2012/2012-02.pdf>. # # For the real-imag system # # ( K M ) # (-M K ), # # Kolmbauer and Langer suggest the preconditioner # # ( K+M ) # ( -(K+M) ). # # The diagonal blocks can, for example, be solved with standard AMG # methods. p1 = Constant(0.0) * ur * vr * dx(0) p2 = Constant(0.0) * ur * vr * dx(0) # Diffusive terms. for i in subdomain_indices: p1 += +1.0 / (Mu[i] * r) * dot(grad(r * ur), grad( r * vr)) * 2 * pi * dx(i) - 1.0 / (Mu[i] * r) * dot( grad(r * ui), grad(r * vi)) * 2 * pi * dx(i) p2 += +om * Constant(Sigma[i]) * ur * vr * 2 * pi * r * dx( i) - om * Constant(Sigma[i]) * ui * vi * 2 * pi * r * dx(i) P = assemble(p1 + p2) # build mass matrix # mm = sum([(ur * vr + ui * vi) * 2*pi*r * dx(i) # for i in subdomain_indices # ]) mm = Constant(0.0) * ur * vr * dx(0) for i in subdomain_indices: mm += +ur * vr * 2 * pi * r * dx(i) + ui * vi * 2 * pi * r * dx(i) M = assemble(mm) # Apply boundary conditions. if bcs: bcs.apply(A) bcs.apply(P) bcs.apply(M) for b in b_list: bcs.apply(b) # helpers.show_matrix(A) # print(helpers.get_eigenvalues(A)) # helpers.show_matrix(M) # helpers.show_matrix(P) return A, P, b_list, M, W
def forward(mu_expression, lmbda_expression, rho, Lx=10, Ly=10, t_end=1, omega_p=5, amplitude=5000, center=0, target=False): Lpml = Lx / 10 #c_p = cp(mu.vector(), lmbda.vector(), rho) max_velocity = 200 #c_p.max() stable_hx = stable_dx(max_velocity, omega_p) nx = int(Lx / stable_hx) + 1 #nx = max(nx, 60) ny = int(Ly * nx / Lx) + 1 mesh = mesh_generator(Lx, Ly, Lpml, nx, ny) used_hx = Lx / nx dt = stable_dt(used_hx, max_velocity) cfl_ct = cfl_constant(max_velocity, dt, used_hx) print(used_hx, stable_hx) print(cfl_ct) #time.sleep(10) PE = FunctionSpace(mesh, "DG", 0) mu = interpolate(mu_expression, PE) lmbda = interpolate(lmbda_expression, PE) m = 2 R = 10e-8 t = 0.0 gamma = 0.50 beta = 0.25 ff = MeshFunction("size_t", mesh, mesh.geometry().dim() - 1) Dirichlet(Lx, Ly, Lpml).mark(ff, 1) # Create function spaces VE = VectorElement("CG", mesh.ufl_cell(), 1, dim=2) TE = TensorElement("DG", mesh.ufl_cell(), 0, shape=(2, 2), symmetry=True) W = FunctionSpace(mesh, MixedElement([VE, TE])) F = FunctionSpace(mesh, "CG", 2) V = W.sub(0).collapse() M = W.sub(1).collapse() alpha_0 = Alpha_0(m, stable_hx, R, Lpml) alpha_1 = Alpha_1(alpha_0, Lx, Lpml, degree=2) alpha_2 = Alpha_2(alpha_0, Ly, Lpml, degree=2) beta_0 = Beta_0(m, max_velocity, R, Lpml) beta_1 = Beta_1(beta_0, Lx, Lpml, degree=2) beta_2 = Beta_2(beta_0, Ly, Lpml, degree=2) alpha_1 = interpolate(alpha_1, F) alpha_2 = interpolate(alpha_2, F) beta_1 = interpolate(beta_1, F) beta_2 = interpolate(beta_2, F) a_ = alpha_1 * alpha_2 b_ = alpha_1 * beta_2 + alpha_2 * beta_1 c_ = beta_1 * beta_2 Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]]) Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]]) a_ = alpha_1 * alpha_2 b_ = alpha_1 * beta_2 + alpha_2 * beta_1 c_ = beta_1 * beta_2 Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]]) Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]]) # Set up boundary condition bc = DirichletBC(W.sub(0), Constant(("0.0", "0.0")), ff, 1) # Create measure for the source term dx = Measure("dx", domain=mesh) ds = Measure("ds", domain=mesh, subdomain_data=ff) # Set up initial values u0 = Function(V) u0.set_allow_extrapolation(True) v0 = Function(V) a0 = Function(V) U0 = Function(M) V0 = Function(M) A0 = Function(M) # Test and trial functions (u, S) = TrialFunctions(W) (w, T) = TestFunctions(W) g = ModifiedRickerPulse(0, omega_p, amplitude, center) F = rho * inner(a_ * N_ddot(u, u0, a0, v0, dt, beta) \ + b_ * N_dot(u, u0, v0, a0, dt, beta, gamma) + c_ * u, w) * dx \ + inner(N_dot(S, U0, V0, A0, dt, beta, gamma).T * Lambda_e + S.T * Lambda_p, grad(w)) * dx \ - inner(g, w) * ds \ + inner(compliance(a_ * N_ddot(S, U0, A0, V0, dt, beta) + b_ * N_dot(S, U0, V0, A0, dt, beta, gamma) + c_ * S, u, mu, lmbda), T) * dx \ - 0.5 * inner(grad(u) * Lambda_p + Lambda_p * grad(u).T + grad(N_dot(u, u0, v0, a0, dt, beta, gamma)) * Lambda_e \ + Lambda_e * grad(N_dot(u, u0, v0, a0, dt, beta, gamma)).T, T) * dx \ a, L = lhs(F), rhs(F) # Assemble rhs (once) A = assemble(a) # Create GMRES Krylov solver solver = KrylovSolver(A, "gmres") # Create solution function S = Function(W) if target: xdmffile_u = XDMFFile("inversion_temporal_file/target/u.xdmf") pvd = File("inversion_temporal_file/target/u.pvd") xdmffile_u.write(u0, t) timeseries_u = TimeSeries( "inversion_temporal_file/target/u_timeseries") else: xdmffile_u = XDMFFile("inversion_temporal_file/obs/u.xdmf") xdmffile_u.write(u0, t) timeseries_u = TimeSeries("inversion_temporal_file/obs/u_timeseries") rec_counter = 0 while t < t_end - 0.5 * dt: t += float(dt) if rec_counter % 10 == 0: print( '\n\rtime: {:.3f} (Progress: {:.2f}%)'.format( t, 100 * t / t_end), ) g.t = t # Assemble rhs and apply boundary condition b = assemble(L) bc.apply(A, b) # Compute solution solver.solve(S.vector(), b) (u, U) = S.split(True) # Update previous time step update(u, u0, v0, a0, beta, gamma, dt) update(U, U0, V0, A0, beta, gamma, dt) xdmffile_u.write(u, t) pvd << (u, t) timeseries_u.store(u.vector(), t) energy = inner(u, u) * dx E = assemble(energy) print("E = ", E) print(u.vector().max())
def __trial_functions(self): u, p = TrialFunctions(self.mixedL) ubar, pbar = TrialFunctions(self.mixedG) return (u, p, ubar, pbar)
def of2dmcs(img1: np.array, img2: np.array, alpha0: float, alpha1: float, beta0: float, beta1: float) -> (np.array, np.array): """Computes the L2-H1 optical flow for a 2D two-channel image sequence and source for the second channel (optical flow 2d multi-channel with source). Takes a two-dimensional image sequence and returns a minimiser of the Horn-Schunck functional with source and with spatio-temporal regularisation for both velocity and source. Args: img1 (np.array): A 2D image sequence of shape (t, m, n), where t is the number of time steps and (n, n) is the number of pixels. img2 (np.array): A 2D image sequence of shape (t, m, n), where t is the number of time steps and (n, n) is the number of pixels. alpha0 (float): The spatial regularisation parameter. alpha1 (float): The temporal regularisation parameter. Returns: v (np.array): A velocity array of shape (t, m, n, 2). k (np.array): A source array of shape (t, m, n). """ # Create mesh. [t, m, n] = img1.shape mesh = UnitCubeMesh(t-2, m-1, n-1) # Define function space and functions. V = FunctionSpace(mesh, 'CG', 1) W = VectorFunctionSpace(mesh, 'CG', 1, dim=3) v1, v2, k = TrialFunctions(W) w1, w2, w3 = TestFunctions(W) # Convert image to function. f1, f2 = Function(V), Function(V) f1.vector()[:] = dh.imgseq2funvec(img1[0:-1]) f2.vector()[:] = dh.imgseq2funvec(img2[0:-1]) # Define function to compute temporal derivative. def time_deriv(img: np.array) -> Function: # Evaluate function at vertices. mc = mesh.coordinates().reshape((-1, 3)) hx, hy, hz = 1./(t-2), 1./(m-1), 1./(n-1) x = np.array(mc[:, 0]/hx, dtype=int) y = np.array(mc[:, 1]/hy, dtype=int) z = np.array(mc[:, 2]/hz, dtype=int) # Map pixel values to vertices. d2v = dof_to_vertex_map(V) # Compute derivative wrt. time. imgt = img[1:] - img[0:-1] ftv = imgt[x, y, z] # Create function. ft = Function(V) ft.vector()[:] = ftv[d2v] return ft # Compute temporal derivatives. f1t = time_deriv(img1) f2t = time_deriv(img2) # Define derivatives of data. f1x, f1y = f1.dx(1), f1.dx(2) f2x, f2y = f2.dx(1), f2.dx(2) # Define weak formulation. A = f1x*(f1x*v1 + f1y*v2)*w1*dx + f2x*(f2x*v1 + f2y*v2 - k)*w1*dx \ + f1y*(f1x*v1 + f1y*v2)*w2*dx + f2y*(f2x*v1 + f2y*v2 - k)*w2*dx \ - (f2x*v1 + f2y*v2 - k)*w3*dx \ + alpha0*v1.dx(1)*w1.dx(1)*dx + alpha0*v1.dx(2)*w1.dx(2)*dx \ + alpha1*v1.dx(0)*w1.dx(0)*dx \ + alpha0*v2.dx(1)*w2.dx(1)*dx + alpha0*v2.dx(2)*w2.dx(2)*dx \ + alpha1*v2.dx(0)*w2.dx(0)*dx \ + beta0*k.dx(1)*w3.dx(1)*dx + beta0*k.dx(2)*w3.dx(2)*dx \ + beta1*k.dx(0)*w3.dx(0)*dx b = - f1x*f1t*w1*dx - f2x*f2t*w1*dx \ - f1y*f1t*w2*dx - f2y*f2t*w2*dx \ + f2t*w3*dx # Compute solution. v = Function(W) solve(A == b, v, [], solver_parameters={"linear_solver": "cg"}) # Split solution into functions. v1, v2, k = v.split(deepcopy=True) # Convert back to arrays. v1 = dh.funvec2imgseq(v1.vector().get_local(), t-1, m, n) v2 = dh.funvec2imgseq(v2.vector().get_local(), t-1, m, n) k = dh.funvec2imgseq(k.vector().get_local(), t-1, m, n) return (np.stack((v1, v2), axis=3), k)
class InitialConditions(UserExpression): def eval(self, values, x): values[0] = 0.0 values[1] = 0.0 def value_shape(self): return (2, ) # Define mesh, function space and test functions mesh = RectangleMesh(Point(0.0, 0.0), Point(1.0, 1.0), 49, 49) P1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1) ME = FunctionSpace(mesh, P1 * P1) q1, q2 = TestFunctions(ME) U1, U2 = TrialFunctions(ME) # Define and interpolate initial condition W = Function(ME) W.interpolate(InitialConditions()) u, v = split(W) domainSource = Expression( "0.1*t*exp(-((x[0]-0.7)*(x[0]-0.7) + (x[1]-0.5)*(x[1]-0.5))/0.01)", t=0, degree=1) # Define the right hand side for each of the PDEs F1 = (-inner(grad(U1), grad(q1)) + U2 * q1 + domainSource * q1) * dx F2 = (-inner(grad(U2), grad(q2)) - U1 * q2) * dx
bc = DirichletBC(W.sub(0), Constant(("0.0", "0.0")), ff, 1) # Create measure for the source term ds = Measure("ds", domain=mesh, subdomain_data=ff) # Set up initial values u0 = Function(V) u0.set_allow_extrapolation(True) v0 = Function(V) a0 = Function(V) U0 = Function(M) V0 = Function(M) A0 = Function(M) # Test and trial functions (u, S) = TrialFunctions(W) (w, T) = TestFunctions(W) pulses = [ ModifiedRickerPulse(t, omega_p_list[i], amplitude_list[i], center=sources_positions[i]) for i in range(len(omega_p_list)) ] g = sum(pulses) F = rho * inner(a_ * N_ddot(u, u0, a0, v0, dt, beta) \ + b_ * N_dot(u, u0, v0, a0, dt, beta, gamma) + c_ * u, w) * dx \ + inner(N_dot(S, U0, V0, A0, dt, beta, gamma).T * Lambda_e + S.T * Lambda_p, grad(w)) * dx \
def solve(WP, bcs, mu, f, verbose=True, tol=1.0e-13, max_iter=500): # Some initial sanity checks. assert mu > 0.0 # Define variational problem (u, p) = TrialFunctions(WP) (v, q) = TestFunctions(WP) # Build system. # The sign of the div(u)-term is somewhat arbitrary since the right-hand # side is 0 here. We can either make the system symmetric or positive- # definite. # On a second note, we have # # \int grad(p).v = - \int p * div(v) + \int_\Gamma p n.v. # # Since, we have either p=0 or n.v=0 on the boundary, we could as well # replace the term dot(grad(p), v) by -p*div(v). # a = mu * inner(grad(u), grad(v))*dx \ - p * div(v) * dx \ - q * div(u) * dx # a = mu * inner(grad(u), grad(v))*dx + dot(grad(p), v) * dx \ # - div(u) * q * dx L = inner(f, v) * dx A, b = assemble_system(a, L, bcs) # Use the preconditioner as recommended in # <http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html>, # # prec = inner(grad(u), grad(v))*dx - p*q*dx # # although it doesn't seem to be too efficient. # The sign on the last term doesn't matter. prec = mu * inner(grad(u), grad(v))*dx \ - p*q*dx M, _ = assemble_system(prec, L, bcs) # solver = KrylovSolver('tfqmr', 'hypre_amg') solver = KrylovSolver('gmres', 'hypre_amg') solver.set_operators(A, M) # For an assortment of preconditioners, see # # Performance and analysis of saddle point preconditioners # for the discrete steady-state Navier-Stokes equations; # H.C. Elman, D.J. Silvester, A.J. Wathen; # Numer. Math. (2002) 90: 665-688; # <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>. # # Set up field split. # <https://fenicsproject.org/qa/12856/fieldsplit-petscpreconditioner_set_fieldsplit-arguments> # PETScOptions.set('ksp_view') # PETScOptions.set('ksp_monitor_true_residual') # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'additive') # PETScOptions.set('pc_fieldsplit_detect_saddle_point') # PETScOptions.set('fieldsplit_0_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_0_pc_type', 'lu') # PETScOptions.set('fieldsplit_1_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_1_pc_type', 'jacobi') # solver = PETScKrylovSolver('gmres') # solver.set_operator(A) # solver.set_from_options() # http://scicomp.stackexchange.com/questions/7288/which-preconditioners-and-solver-in-petsc-for-indefinite-symmetric-systems-sho # PETScOptions.set('pc_type', 'fieldsplit') # #PETScOptions.set('pc_fieldsplit_type', 'schur') # #PETScOptions.set('pc_fieldsplit_schur_fact_type', 'upper') # PETScOptions.set('pc_fieldsplit_detect_saddle_point') # #PETScOptions.set('fieldsplit_u_pc_type', 'lsc') # #PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('fieldsplit_u_pc_type', 'hypre') # PETScOptions.set('fieldsplit_u_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_p_pc_type', 'jacobi') # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') # # From PETSc/src/ksp/ksp/examples/tutorials/ex42-fsschur.opts: # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'SCHUR') # PETScOptions.set('pc_fieldsplit_schur_fact_type', 'UPPER') # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly') # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') # From # # Composable Linear Solvers for Multiphysics; # J. Brown, M. Knepley, D.A. May, L.C. McInnes, B. Smith; # <http://www.computer.org/csdl/proceedings/ispdc/2012/4805/00/4805a055-abs.html>; # <http://www.mcs.anl.gov/uploads/cels/papers/P2017-0112.pdf>. # # PETScOptions.set('pc_type', 'fieldsplit') # PETScOptions.set('pc_fieldsplit_type', 'schur') # PETScOptions.set('pc_fieldsplit_schur_factorization_type', 'upper') # # # PETScOptions.set('fieldsplit_u_ksp_type', 'cg') # PETScOptions.set('fieldsplit_u_ksp_rtol', 1.0e-6) # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi') # PETScOptions.set('fieldsplit_u_sub_pc_type', 'cholesky') # # # PETScOptions.set('fieldsplit_p_ksp_type', 'fgmres') # PETScOptions.set('fieldsplit_p_ksp_constant_null_space') # PETScOptions.set('fieldsplit_p_pc_type', 'lsc') # # # PETScOptions.set('fieldsplit_p_lsc_ksp_type', 'cg') # PETScOptions.set('fieldsplit_p_lsc_ksp_rtol', 1.0e-2) # PETScOptions.set('fieldsplit_p_lsc_ksp_constant_null_space') # #PETScOptions.set('fieldsplit_p_lsc_ksp_converged_reason') # PETScOptions.set('fieldsplit_p_lsc_pc_type', 'bjacobi') # PETScOptions.set('fieldsplit_p_lsc_sub_pc_type', 'icc') solver.parameters['monitor_convergence'] = verbose solver.parameters['report'] = verbose solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['relative_tolerance'] = tol solver.parameters['maximum_iterations'] = max_iter solver.parameters['error_on_nonconvergence'] = True # Solve up = Function(WP) solver.solve(up.vector(), b) # Get sub-functions u, p = up.split(True) return u, p