def compute_force(self, velocity, pressure, t): self.tc.start('errorForce') I = Identity(3) # Identity tensor def T(p, v): return -p * I + 2.0 * self.nu * sym(grad(v)) error_force = sqrt( assemble(inner((T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal, (T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal) * self.dsWall)) an_force = sqrt(assemble(inner(T(self.sol_p, self.solution) * self.normal, T(self.sol_p, self.solution) * self.normal) * self.dsWall)) an_f_normal = sqrt(assemble(inner(inner(T(self.sol_p, self.solution) * self.normal, self.normal), inner(T(self.sol_p, self.solution) * self.normal, self.normal)) * self.dsWall)) error_f_normal = sqrt( assemble(inner(inner((T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal), inner((T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal)) * self.dsWall)) an_f_shear = sqrt( assemble(inner((I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal, (I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal) * self.dsWall)) error_f_shear = sqrt( assemble(inner((I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, (I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal) * self.dsWall)) self.listDict['a_force_wall']['list'].append(an_force) self.listDict['a_force_wall_normal']['list'].append(an_f_normal) self.listDict['a_force_wall_shear']['list'].append(an_f_shear) self.listDict['force_wall']['list'].append(error_force) self.listDict['force_wall_normal']['list'].append(error_f_normal) self.listDict['force_wall_shear']['list'].append(error_f_shear) if self.isWholeSecond: self.listDict['force_wall']['slist'].append( sqrt(sum([i*i for i in self.listDict['force_wall']['list'][self.N0:self.N1]])/self.stepsInSecond)) print(' Relative force error:', error_force/an_force) self.tc.end('errorForce')
def compute_force(self, velocity, pressure, t): self.tc.start('errorForce') I = Identity(3) # Identity tensor def T(p, v): return -p * I + 2.0 * self.nu * sym(grad(v)) error_force = sqrt( assemble( inner((T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal, (T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal) * self.dsWall)) an_force = sqrt( assemble( inner( T(self.sol_p, self.solution) * self.normal, T(self.sol_p, self.solution) * self.normal) * self.dsWall)) an_f_normal = sqrt( assemble( inner( inner( T(self.sol_p, self.solution) * self.normal, self.normal), inner( T(self.sol_p, self.solution) * self.normal, self.normal)) * self.dsWall)) error_f_normal = sqrt( assemble( inner( inner( (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal), inner( (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal)) * self.dsWall)) an_f_shear = sqrt( assemble( inner( (I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal, (I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal) * self.dsWall)) error_f_shear = sqrt( assemble( inner((I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, (I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal) * self.dsWall)) self.listDict['a_force_wall']['list'].append(an_force) self.listDict['a_force_wall_normal']['list'].append(an_f_normal) self.listDict['a_force_wall_shear']['list'].append(an_f_shear) self.listDict['force_wall']['list'].append(error_force) self.listDict['force_wall_normal']['list'].append(error_f_normal) self.listDict['force_wall_shear']['list'].append(error_f_shear) print(' Relative force error:', error_force / an_force) self.tc.end('errorForce')
def test_physically_mapped_facet(): mesh = Mesh(VectorElement("P", triangle, 1)) # set up variational problem U = FiniteElement("Morley", mesh.ufl_cell(), 2) V = FiniteElement("P", mesh.ufl_cell(), 1) R = FiniteElement("P", mesh.ufl_cell(), 1) Vv = VectorElement(BrokenElement(V)) Qhat = VectorElement(BrokenElement(V[facet])) Vhat = VectorElement(V[facet]) Z = FunctionSpace(mesh, MixedElement(U, Vv, Qhat, Vhat, R)) z = Coefficient(Z) u, d, qhat, dhat, lam = split(z) s = FacetNormal(mesh) trans = as_matrix([[1, 0], [0, 1]]) mat = trans*grad(grad(u))*trans + outer(d, d) * u J = (u**2*dx + u**3*dx + u**4*dx + inner(mat, mat)*dx + inner(grad(d), grad(d))*dx + dot(s, d)**2*ds) L_match = inner(qhat, dhat - d) L = J + inner(lam, inner(d, d)-1)*dx + (L_match('+') + L_match('-'))*dS + L_match*ds compile_form(L)
def blocks(self): aa, ff = super(FormulationMinimallyConstrained, self).blocks() n = ufl.FacetNormal(self.mesh) ds = ufl.Measure("ds", self.mesh) u, P = self.uu_[0], self.uu_[2] v, Q = self.vv_[0], self.vv_[2] aa[0].append(-ufl.inner(P, ufl.outer(v, n)) * ds) aa[1].append(0) aa.append([-ufl.inner(Q, ufl.outer(u, n)) * ds, 0, 0]) ff.append(0) return [aa, ff]
def dtheta_dC(self, u_, p_, ivar, theta_old_, thres, dt): theta_ = ivar["theta"] dFg_dtheta = self.F_g(theta_,tang=True) ktheta = self.res_dtheta_growth(u_, p_, ivar, theta_old_, thres, dt, 'ktheta') K_growth = self.res_dtheta_growth(u_, p_, ivar, theta_old_, thres, dt, 'tang') i, j, k, l = indices(4) if self.growth_trig == 'volstress': Cmat = self.S(u_,p_,ivar,tang=True) # TeX: \frac{\partial \vartheta}{\partial \boldsymbol{C}} = \frac{k(\vartheta) \Delta t}{\frac{\partial r}{\partial \vartheta}}\left(\boldsymbol{S} + \boldsymbol{C} : \frac{1}{2} \check{\mathbb{C}}\right) tangdC = (ktheta*dt/K_growth) * (self.S(u_,p_,ivar=ivar) + 0.5*as_tensor(self.kin.C(u_)[i,j]*Cmat[i,j,k,l], (k,l))) elif self.growth_trig == 'fibstretch': # TeX: \frac{\partial \vartheta}{\partial \boldsymbol{C}} = \frac{k(\vartheta) \Delta t}{\frac{\partial r}{\partial \vartheta}} \frac{1}{2\lambda_{f}} \boldsymbol{f}_{0}\otimes \boldsymbol{f}_{0} tangdC = (ktheta*dt/K_growth) * outer(self.kin.fib_funcs[0],self.kin.fib_funcs[0])/(2.*self.kin.fibstretch(u_,self.kin.fib_funcs[0])) else: raise NameError("Unkown growth_trig!") return tangdC
def test_automatic_simplification(self): cell = triangle element = FiniteElement("Lagrange", cell, 1) v = TestFunction(element) u = TrialFunction(element) assert inner(u, v) == u * conj(v) assert dot(u, v) == u * v assert outer(u, v) == conj(u) * v
def test_apply_algebra_lowering_complex(self): cell = triangle element = FiniteElement("Lagrange", cell, 1) v = TestFunction(element) u = TrialFunction(element) gv = grad(v) gu = grad(u) a = dot(gu, gv) b = inner(gv, gu) c = outer(gu, gv) lowered_a = apply_algebra_lowering(a) lowered_b = apply_algebra_lowering(b) lowered_c = apply_algebra_lowering(c) lowered_a_index = lowered_a.index() lowered_b_index = lowered_b.index() lowered_c_indices = lowered_c.indices() assert lowered_a == gu[lowered_a_index] * gv[lowered_a_index] assert lowered_b == gv[lowered_b_index] * conj(gu[lowered_b_index]) assert lowered_c == as_tensor(conj(gu[lowered_c_indices[0]]) * gv[lowered_c_indices[1]], (lowered_c_indices[0],) + (lowered_c_indices[1],))
def test_diff_then_integrate(): # Define 1D geometry n = 21 mesh = UnitIntervalMesh(MPI.comm_world, n) # Shift and scale mesh x0, x1 = 1.5, 3.14 mesh.coordinates()[:] *= (x1 - x0) mesh.coordinates()[:] += x0 x = SpatialCoordinate(mesh)[0] xs = 0.1 + 0.8 * x / x1 # scaled to be within [0.1,0.9] # Define list of expressions to test, and configure # accuracies these expressions are known to pass with. # The reason some functions are less accurately integrated is # likely that the default choice of quadrature rule is not perfect F_list = [] def reg(exprs, acc=10): for expr in exprs: F_list.append((expr, acc)) # FIXME: 0*dx and 1*dx fails in the ufl-ffc-jit framework somewhere # reg([Constant(0.0, cell=cell)]) # reg([Constant(1.0, cell=cell)]) monomial_list = [x**q for q in range(2, 6)] reg(monomial_list) reg([2.3 * p + 4.5 * q for p in monomial_list for q in monomial_list]) reg([x**x]) reg([x**(x**2)], 8) reg([x**(x**3)], 6) reg([x**(x**4)], 2) # Special functions: reg([atan(xs)], 8) reg([sin(x), cos(x), exp(x)], 5) reg([ln(xs), pow(x, 2.7), pow(2.7, x)], 3) reg([asin(xs), acos(xs)], 1) reg([tan(xs)], 7) try: import scipy except ImportError: scipy = None if hasattr(math, 'erf') or scipy is not None: reg([erf(xs)]) else: print( "Warning: skipping test of erf, old python version and no scipy.") # if 0: # print("Warning: skipping tests of bessel functions, doesn't build on all platforms.") # elif scipy is None: # print("Warning: skipping tests of bessel functions, missing scipy.") # else: # for nu in (0, 1, 2): # # Many of these are possibly more accurately integrated, # # but 4 covers all and is sufficient for this test # reg([bessel_J(nu, xs), bessel_Y(nu, xs), bessel_I(nu, xs), bessel_K(nu, xs)], 4) # To handle tensor algebra, make an x dependent input tensor # xx and square all expressions def reg2(exprs, acc=10): for expr in exprs: F_list.append((inner(expr, expr), acc)) xx = as_matrix([[2 * x**2, 3 * x**3], [11 * x**5, 7 * x**4]]) x3v = as_vector([3 * x**2, 5 * x**3, 7 * x**4]) cc = as_matrix([[2, 3], [4, 5]]) reg2([xx]) reg2([x3v]) reg2([cross(3 * x3v, as_vector([-x3v[1], x3v[0], x3v[2]]))]) reg2([xx.T]) reg2([tr(xx)]) reg2([det(xx)]) reg2([dot(xx, 0.1 * xx)]) reg2([outer(xx, xx.T)]) reg2([dev(xx)]) reg2([sym(xx)]) reg2([skew(xx)]) reg2([elem_mult(7 * xx, cc)]) reg2([elem_div(7 * xx, xx + cc)]) reg2([elem_pow(1e-3 * xx, 1e-3 * cc)]) reg2([elem_pow(1e-3 * cc, 1e-3 * xx)]) reg2([elem_op(lambda z: sin(z) + 2, 0.03 * xx)], 2) # pretty inaccurate... # FIXME: Add tests for all UFL operators: # These cause discontinuities and may be harder to test in the # above fashion: # 'inv', 'cofac', # 'eq', 'ne', 'le', 'ge', 'lt', 'gt', 'And', 'Or', 'Not', # 'conditional', 'sign', # 'jump', 'avg', # 'LiftingFunction', 'LiftingOperator', # FIXME: Test other derivatives: (but algorithms for operator # derivatives are the same!): # 'variable', 'diff', # 'Dx', 'grad', 'div', 'curl', 'rot', 'Dn', 'exterior_derivative', # Run through all operators defined above and compare integrals debug = 0 for F, acc in F_list: # Apply UFL differentiation f = diff(F, SpatialCoordinate(mesh))[..., 0] if debug: print(F) print(x) print(f) # Apply integration with DOLFIN # (also passes through form compilation and jit) M = f * dx f_integral = assemble_scalar(M) # noqa f_integral = MPI.sum(mesh.mpi_comm(), f_integral) # Compute integral of f manually from anti-derivative F # (passes through PyDOLFIN interface and uses UFL evaluation) F_diff = F((x1, )) - F((x0, )) # Compare results. Using custom relative delta instead # of decimal digits here because some numbers are >> 1. delta = min(abs(f_integral), abs(F_diff)) * 10**-acc assert f_integral - F_diff <= delta
def test_div_grad_then_integrate_over_cells_and_boundary(): # Define 2D geometry n = 10 mesh = RectangleMesh(Point(0.0, 0.0), Point(2.0, 3.0), 2 * n, 3 * n) x, y = SpatialCoordinate(mesh) xs = 0.1 + 0.8 * x / 2 # scaled to be within [0.1,0.9] # ys = 0.1 + 0.8 * y / 3 # scaled to be within [0.1,0.9] n = FacetNormal(mesh) # Define list of expressions to test, and configure accuracies # these expressions are known to pass with. The reason some # functions are less accurately integrated is likely that the # default choice of quadrature rule is not perfect F_list = [] def reg(exprs, acc=10): for expr in exprs: F_list.append((expr, acc)) # FIXME: 0*dx and 1*dx fails in the ufl-ffc-jit framework somewhere # reg([Constant(0.0, cell=cell)]) # reg([Constant(1.0, cell=cell)]) monomial_list = [x**q for q in range(2, 6)] reg(monomial_list) reg([2.3 * p + 4.5 * q for p in monomial_list for q in monomial_list]) reg([xs**xs]) reg( [xs**(xs**2)], 8 ) # Note: Accuracies here are from 1D case, not checked against 2D results. reg([xs**(xs**3)], 6) reg([xs**(xs**4)], 2) # Special functions: reg([atan(xs)], 8) reg([sin(x), cos(x), exp(x)], 5) reg([ln(xs), pow(x, 2.7), pow(2.7, x)], 3) reg([asin(xs), acos(xs)], 1) reg([tan(xs)], 7) # To handle tensor algebra, make an x dependent input tensor # xx and square all expressions def reg2(exprs, acc=10): for expr in exprs: F_list.append((inner(expr, expr), acc)) xx = as_matrix([[2 * x**2, 3 * x**3], [11 * x**5, 7 * x**4]]) xxs = as_matrix([[2 * xs**2, 3 * xs**3], [11 * xs**5, 7 * xs**4]]) x3v = as_vector([3 * x**2, 5 * x**3, 7 * x**4]) cc = as_matrix([[2, 3], [4, 5]]) reg2( [xx] ) # TODO: Make unit test for UFL from this, results in listtensor with free indices reg2([x3v]) reg2([cross(3 * x3v, as_vector([-x3v[1], x3v[0], x3v[2]]))]) reg2([xx.T]) reg2([tr(xx)]) reg2([det(xx)]) reg2([dot(xx, 0.1 * xx)]) reg2([outer(xx, xx.T)]) reg2([dev(xx)]) reg2([sym(xx)]) reg2([skew(xx)]) reg2([elem_mult(7 * xx, cc)]) reg2([elem_div(7 * xx, xx + cc)]) reg2([elem_pow(1e-3 * xxs, 1e-3 * cc)]) reg2([elem_pow(1e-3 * cc, 1e-3 * xx)]) reg2([elem_op(lambda z: sin(z) + 2, 0.03 * xx)], 2) # pretty inaccurate... # FIXME: Add tests for all UFL operators: # These cause discontinuities and may be harder to test in the # above fashion: # 'inv', 'cofac', # 'eq', 'ne', 'le', 'ge', 'lt', 'gt', 'And', 'Or', 'Not', # 'conditional', 'sign', # 'jump', 'avg', # 'LiftingFunction', 'LiftingOperator', # FIXME: Test other derivatives: (but algorithms for operator # derivatives are the same!): # 'variable', 'diff', # 'Dx', 'grad', 'div', 'curl', 'rot', 'Dn', 'exterior_derivative', # Run through all operators defined above and compare integrals debug = 0 if debug: F_list = F_list[1:] for F, acc in F_list: if debug: print('\n', "F:", str(F)) # Integrate over domain and its boundary int_dx = assemble(div(grad(F)) * dx(mesh)) # noqa int_ds = assemble(dot(grad(F), n) * ds(mesh)) # noqa if debug: print(int_dx, int_ds) # Compare results. Using custom relative delta instead of # decimal digits here because some numbers are >> 1. delta = min(abs(int_dx), abs(int_ds)) * 10**-acc assert int_dx - int_ds <= delta
def crossfiber(self, f0): F_g = self.theta * self.I + (1. - self.theta) * outer(f0, f0) return F_g
) source = lambda u1, u2, u3, v: -1 / spiral_eps * u1 * (1 - u2) * (u3 - ustar(v) ) xForm = inner(diffusiveFlux(u, grad(u)), grad(phi)) * dx xForm += ufl.conditional(uh_n < ustar(vh_n), source(u, uh_n, uh_n, vh_n), source(uh_n, u, uh_n, vh_n)) * phi * dx # <markdowncell> # To handle the possible non conforming intersection we add DG type # skeleton terms: # <codecell> penalty = 5 * (order * (order + 1)) * spiral_D if nonConforming: xForm -= ( inner( outer(jump(u), n('+')), avg(diffusiveFlux(u,grad(phi)))) +\ inner( avg(diffusiveFlux(u,grad(u))), outer(jump(phi), n('+'))) ) * dS xForm += penalty / hS * inner(jump(u), jump(phi)) * dS # <markdowncell> # After adding the time discretization (a simple backward Euler scheme), # the model is now completely implemented and the scheme can be created: # <codecell> form = (inner(u, phi) - inner(uh_n, phi)) * dx + dt * xForm solverParameters =\ {"newton.tolerance": 1e-8, "newton.linear.tolerance": 1e-12, "newton.linear.preconditioning.method": "amg-ilu", "newton.linear.maxiterations":1000, "newton.verbose": False,
# Jacobi matrix of map reference -> undeformed J0 = ufl.geometry.Jacobian(mesh) # Tangent basis gs = J0[:, 0] gη = ufl.as_vector([0, 1, 0]) # unit vector e_y (assume curve in x-z plane) gξ = ufl.cross(gs, gη) # Unit tangent basis gs /= ufl.sqrt(ufl.dot(gs, gs)) gη /= ufl.sqrt(ufl.dot(gη, gη)) gξ /= ufl.sqrt(ufl.dot(gξ, gξ)) # Interpolate normal vector dolfiny.interpolation.interpolate(gξ, n0i) # ---------------------------------------------------------------------------- # Orthogonal projection operator (assumes sufficient geometry approximation) P = ufl.Identity(mesh.geometry.dim) - ufl.outer(n0i, n0i) # Thickness variable X = dolfinx.FunctionSpace(mesh, ("DG", q)) ξ = dolfinx.Function(X, name='ξ') # Undeformed configuration: director d0 and placement b0 d0 = n0i # normal of manifold mesh, interpolated b0 = x0 + ξ * d0 # Deformed configuration: director d and placement b, assumed kinematics, director uses rotation matrix d = ufl.as_matrix([[ufl.cos(r), 0, ufl.sin(r)], [0, 1, 0], [-ufl.sin(r), 0, ufl.cos(r)]]) * d0 b = x0 + ufl.as_vector([u, 0, w]) + ξ * d # Configuration gradient, undeformed configuration
a01 = - ufl.inner(p, ufl.div(v)) * ufl.dx a10 = - ufl.inner(ufl.div(u), q) * ufl.dx a11 = None L0 = ufl.inner(f, v) * ufl.dx L1 = ufl.inner(dolfinx.fem.Constant(mesh, PETSc.ScalarType(0.0)), q) * ufl.dx # No prescribed shear stress n = ufl.FacetNormal(mesh) g_tau = tangential_proj(dolfinx.fem.Constant( mesh, PETSc.ScalarType(((0, 0), (0, 0)))) * n, n) ds = ufl.Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=1) # Terms due to slip condition # Explained in for instance: https://arxiv.org/pdf/2001.10639.pdf a00 -= ufl.inner(ufl.outer(n, n) * ufl.dot(2 * mu * sym_grad(u), n), v) * ds a01 -= ufl.inner(ufl.outer(n, n) * ufl.dot( - p * ufl.Identity(u.ufl_shape[0]), n), v) * ds L0 += ufl.inner(g_tau, v) * ds a = [[dolfinx.fem.form(a00), dolfinx.fem.form(a01)], [dolfinx.fem.form(a10), dolfinx.fem.form(a11)]] L = [dolfinx.fem.form(L0), dolfinx.fem.form(L1)] # Assemble LHS matrix and RHS vector with dolfinx.common.Timer("~Stokes: Assemble LHS and RHS"): A = dolfinx_mpc.create_matrix_nest(a, [mpc, mpc_q]) dolfinx_mpc.assemble_matrix_nest(A, a, [mpc, mpc_q], bcs) A.assemble() b = dolfinx_mpc.create_vector_nest(L, [mpc, mpc_q])
f = fem.Constant(mesh, PETSc.ScalarType((0, 0))) (u, p) = TrialFunctions(W) (v, q) = TestFunctions(W) a = (2 * mu * inner(sym_grad(u), sym_grad(v)) - inner(p, div(v)) - inner(div(u), q)) * dx L = inner(f, v) * dx # No prescribed shear stress n = FacetNormal(mesh) g_tau = tangential_proj( fem.Constant(mesh, PETSc.ScalarType(((0, 0), (0, 0)))) * n, n) ds = Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=1) # Terms due to slip condition # Explained in for instance: https://arxiv.org/pdf/2001.10639.pdf a -= inner(outer(n, n) * dot(T(u, p, mu), n), v) * ds L += inner(g_tau, v) * ds # Solve linear problem petsc_options = { "ksp_type": "preonly", "pc_type": "lu", "pc_factor_solver_type": "mumps" } problem = LinearProblem(a, L, mpc, bcs=bcs, petsc_options=petsc_options) U = problem.solve() # ------------------------------ Output ---------------------------------- u = U.sub(0).collapse() p = U.sub(1).collapse() u.name = "u"
def active_fiber(self, tau, f0): S = tau * outer(f0, f0) return S
def fiber(self, f0): F_g = self.I + (self.theta - 1.) * outer(f0, f0) return F_g
def tangential_proj(u, n): """ See for instance: https://link.springer.com/content/pdf/10.1023/A:1022235512626.pdf """ return (ufl.Identity(u.ufl_shape[0]) - ufl.outer(n, n)) * u
def radial(self, f0, s0): r0 = cross(f0, s0) F_g = self.I + (self.theta - 1.) * outer(r0, r0) return F_g
def test_mixed_element(cell_type, ghost_mode): N = 4 mesh = dolfinx.mesh.create_unit_square( MPI.COMM_WORLD, N, N, cell_type=cell_type, ghost_mode=ghost_mode) # Inlet velocity Dirichlet BC bc_facets = dolfinx.mesh.locate_entities_boundary( mesh, mesh.topology.dim - 1, lambda x: np.isclose(x[0], 0.0)) other_facets = dolfinx.mesh.locate_entities_boundary( mesh, mesh.topology.dim - 1, lambda x: np.isclose(x[0], 1.0)) arg_sort = np.argsort(other_facets) mt = dolfinx.mesh.meshtags(mesh, mesh.topology.dim - 1, other_facets[arg_sort], np.full_like(other_facets, 1)) # Rotate the mesh to induce more interesting slip BCs th = np.pi / 4.0 rot = np.array([[np.cos(th), -np.sin(th)], [np.sin(th), np.cos(th)]]) gdim = mesh.geometry.dim mesh.geometry.x[:, :gdim] = (rot @ mesh.geometry.x[:, :gdim].T).T # Create the function space Ve = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2) Qe = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) V = dolfinx.fem.FunctionSpace(mesh, Ve) Q = dolfinx.fem.FunctionSpace(mesh, Qe) W = dolfinx.fem.FunctionSpace(mesh, Ve * Qe) inlet_velocity = dolfinx.fem.Function(V) inlet_velocity.interpolate( lambda x: np.zeros((mesh.geometry.dim, x[0].shape[0]), dtype=np.double)) inlet_velocity.x.scatter_forward() # -- Nested assembly dofs = dolfinx.fem.locate_dofs_topological(V, 1, bc_facets) bc1 = dolfinx.fem.dirichletbc(inlet_velocity, dofs) # Collect Dirichlet boundary conditions bcs = [bc1] mpc_v = dolfinx_mpc.MultiPointConstraint(V) n_approx = dolfinx_mpc.utils.create_normal_approximation(V, mt, 1) mpc_v.create_slip_constraint(V, (mt, 1), n_approx, bcs=bcs) mpc_v.finalize() mpc_q = dolfinx_mpc.MultiPointConstraint(Q) mpc_q.finalize() f = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0))) (u, p) = ufl.TrialFunction(V), ufl.TrialFunction(Q) (v, q) = ufl.TestFunction(V), ufl.TestFunction(Q) a00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx a01 = - ufl.inner(p, ufl.div(v)) * ufl.dx a10 = - ufl.inner(ufl.div(u), q) * ufl.dx a11 = None L0 = ufl.inner(f, v) * ufl.dx L1 = ufl.inner( dolfinx.fem.Constant(mesh, PETSc.ScalarType(0.0)), q) * ufl.dx n = ufl.FacetNormal(mesh) g_tau = ufl.as_vector((0.0, 0.0)) ds = ufl.Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=1) a00 -= ufl.inner(ufl.outer(n, n) * ufl.dot(ufl.grad(u), n), v) * ds a01 -= ufl.inner(ufl.outer(n, n) * ufl.dot( - p * ufl.Identity(u.ufl_shape[0]), n), v) * ds L0 += ufl.inner(g_tau, v) * ds a_nest = dolfinx.fem.form(((a00, a01), (a10, a11))) L_nest = dolfinx.fem.form((L0, L1)) # Assemble MPC nest matrix A_nest = dolfinx_mpc.create_matrix_nest(a_nest, [mpc_v, mpc_q]) dolfinx_mpc.assemble_matrix_nest(A_nest, a_nest, [mpc_v, mpc_q], bcs) A_nest.assemble() # Assemble original nest matrix A_org_nest = dolfinx.fem.petsc.assemble_matrix_nest(a_nest, bcs) A_org_nest.assemble() # MPC nested rhs b_nest = dolfinx_mpc.create_vector_nest(L_nest, [mpc_v, mpc_q]) dolfinx_mpc.assemble_vector_nest(b_nest, L_nest, [mpc_v, mpc_q]) dolfinx.fem.petsc.apply_lifting_nest(b_nest, a_nest, bcs) for b_sub in b_nest.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = dolfinx.fem.bcs_by_block( dolfinx.fem.extract_function_spaces(L_nest), bcs) dolfinx.fem.petsc.set_bc_nest(b_nest, bcs0) # Original dolfinx rhs b_org_nest = dolfinx.fem.petsc.assemble_vector_nest(L_nest) dolfinx.fem.petsc.apply_lifting_nest(b_org_nest, a_nest, bcs) for b_sub in b_org_nest.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.petsc.set_bc_nest(b_org_nest, bcs0) # -- Monolithic assembly dofs = dolfinx.fem.locate_dofs_topological((W.sub(0), V), 1, bc_facets) bc1 = dolfinx.fem.dirichletbc(inlet_velocity, dofs, W.sub(0)) bcs = [bc1] V, V_to_W = W.sub(0).collapse() mpc_vq = dolfinx_mpc.MultiPointConstraint(W) n_approx = dolfinx_mpc.utils.create_normal_approximation(V, mt, 1) mpc_vq.create_slip_constraint(W.sub(0), (mt, 1), n_approx, bcs=bcs) mpc_vq.finalize() f = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0))) (u, p) = ufl.TrialFunctions(W) (v, q) = ufl.TestFunctions(W) a = ( ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx - ufl.inner(p, ufl.div(v)) * ufl.dx - ufl.inner(ufl.div(u), q) * ufl.dx ) L = ufl.inner(f, v) * ufl.dx + ufl.inner( dolfinx.fem.Constant(mesh, PETSc.ScalarType(0.0)), q) * ufl.dx # No prescribed shear stress n = ufl.FacetNormal(mesh) g_tau = ufl.as_vector((0.0, 0.0)) ds = ufl.Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=1) # Terms due to slip condition # Explained in for instance: https://arxiv.org/pdf/2001.10639.pdf a -= ufl.inner(ufl.outer(n, n) * ufl.dot(ufl.grad(u), n), v) * ds a -= ufl.inner(ufl.outer(n, n) * ufl.dot( - p * ufl.Identity(u.ufl_shape[0]), n), v) * ds L += ufl.inner(g_tau, v) * ds a, L = dolfinx.fem.form(a), dolfinx.fem.form(L) # Assemble LHS matrix and RHS vector A = dolfinx_mpc.assemble_matrix(a, mpc_vq, bcs) A.assemble() A_org = dolfinx.fem.petsc.assemble_matrix(a, bcs) A_org.assemble() b = dolfinx_mpc.assemble_vector(L, mpc_vq) b_org = dolfinx.fem.petsc.assemble_vector(L) # Set Dirichlet boundary condition values in the RHS dolfinx_mpc.apply_lifting(b, [a], [bcs], mpc_vq) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.petsc.set_bc(b, bcs) dolfinx.fem.petsc.apply_lifting(b_org, [a], [bcs]) b_org.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.petsc.set_bc(b_org, bcs) # -- Verification def nest_matrix_norm(A): assert A.getType() == "nest" nrows, ncols = A.getNestSize() sub_A = [A.getNestSubMatrix(row, col) for row in range(nrows) for col in range(ncols)] return sum(map(lambda A_: A_.norm()**2 if A_ else 0.0, sub_A))**0.5 # -- Ensure monolithic and nest matrices are the same assert np.isclose(nest_matrix_norm(A_nest), A.norm())