def test_subdomains(compile_args): cell = ufl.triangle element = ufl.FiniteElement("Lagrange", cell, 1) u, v = ufl.TrialFunction(element), ufl.TestFunction(element) a0 = ufl.inner(u, v) * ufl.dx + ufl.inner(u, v) * ufl.dx(2) a1 = ufl.inner(u, v) * ufl.dx(2) + ufl.inner(u, v) * ufl.dx a2 = ufl.inner(u, v) * ufl.dx(2) + ufl.inner(u, v) * ufl.dx(1) a3 = ufl.inner(u, v) * ufl.ds(210) + ufl.inner(u, v) * ufl.ds(0) forms = [a0, a1, a2, a3] compiled_forms, module, code = ffcx.codegeneration.jit.compile_forms( forms, parameters={'scalar_type': 'double'}, cffi_extra_compile_args=compile_args) for f, compiled_f in zip(forms, compiled_forms): assert compiled_f.rank == len(f.arguments()) form0 = compiled_forms[0] ids = form0.integral_ids(module.lib.cell) assert ids[0] == -1 and ids[1] == 2 form1 = compiled_forms[1] ids = form1.integral_ids(module.lib.cell) assert ids[0] == -1 and ids[1] == 2 form2 = compiled_forms[2] ids = form2.integral_ids(module.lib.cell) assert ids[0] == 1 and ids[1] == 2 form3 = compiled_forms[3] assert form3.num_integrals(module.lib.cell) == 0 ids = form3.integral_ids(module.lib.exterior_facet) assert ids[0] == 0 and ids[1] == 210
def test_assemble_manifold(): """Test assembly of poisson problem on a mesh with topological dimension 1 but embedded in 2D (gdim=2). """ points = numpy.array([[0.0, 0.0], [0.2, 0.0], [0.4, 0.0], [0.6, 0.0], [0.8, 0.0], [1.0, 0.0]], dtype=numpy.float64) cells = numpy.array([[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]], dtype=numpy.int32) cell = ufl.Cell("interval", geometric_dimension=points.shape[1]) domain = ufl.Mesh(ufl.VectorElement("Lagrange", cell, 1)) mesh = create_mesh(MPI.COMM_WORLD, cells, points, domain) assert mesh.geometry.dim == 2 assert mesh.topology.dim == 1 U = dolfinx.FunctionSpace(mesh, ("P", 1)) u, v = ufl.TrialFunction(U), ufl.TestFunction(U) w = dolfinx.Function(U) a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx(mesh) L = ufl.inner(1.0, v) * ufl.dx(mesh) bcdofs = dolfinx.fem.locate_dofs_geometrical( U, lambda x: numpy.isclose(x[0], 0.0)) bcs = [dolfinx.DirichletBC(w, bcdofs)] A = dolfinx.fem.assemble_matrix(a, bcs) A.assemble() b = dolfinx.fem.assemble_vector(L) dolfinx.fem.apply_lifting(b, [a], [bcs]) dolfinx.fem.set_bc(b, bcs) assert numpy.isclose(b.norm(), 0.41231) assert numpy.isclose(A.norm(), 25.0199)
def run_scalar_test(mesh, V, degree): """ Manufactured Poisson problem, solving u = x[1]**p, where p is the degree of the Lagrange function space. """ u, v = TrialFunction(V), TestFunction(V) a = inner(grad(u), grad(v)) * dx # Get quadrature degree for bilinear form integrand (ignores effect of non-affine map) a = inner(grad(u), grad(v)) * dx(metadata={"quadrature_degree": -1}) a.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(a) a = form(a) # Source term x = SpatialCoordinate(mesh) u_exact = x[1]**degree f = -div(grad(u_exact)) # Set quadrature degree for linear form integrand (ignores effect of non-affine map) L = inner(f, v) * dx(metadata={"quadrature_degree": -1}) L.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(L) L = form(L) u_bc = Function(V) u_bc.interpolate(lambda x: x[1]**degree) # Create Dirichlet boundary condition facetdim = mesh.topology.dim - 1 mesh.topology.create_connectivity(facetdim, mesh.topology.dim) bndry_facets = np.where( np.array(compute_boundary_facets(mesh.topology)) == 1)[0] bdofs = locate_dofs_topological(V, facetdim, bndry_facets) bc = dirichletbc(u_bc, bdofs) b = assemble_vector(L) apply_lifting(b, [a], bcs=[[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) a = form(a) A = assemble_matrix(a, bcs=[bc]) A.assemble() # Create LU linear solver solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A) uh = Function(V) solver.solve(b, uh.vector) uh.x.scatter_forward() M = (u_exact - uh)**2 * dx M = form(M) error = mesh.comm.allreduce(assemble_scalar(M), op=MPI.SUM) assert np.absolute(error) < 1.0e-14
def forms(arguments, coefficients): v, u = arguments c, f = coefficients n = FacetNormal(triangle) a = u * v * dx L = f * v * dx b = u * v * dx(0) + inner(c * grad(u), grad(v)) * \ dx(1) + dot(n, grad(u)) * v * ds + f * v * dx return (a, L, b)
def test_assemble_functional(): mesh = dolfin.generation.UnitSquareMesh(dolfin.MPI.comm_world, 12, 12) M = 1.0 * dx(domain=mesh) value = dolfin.fem.assemble(M) assert value == pytest.approx(1.0, 1e-12) x = dolfin.SpatialCoordinate(mesh) M = x[0] * dx(domain=mesh) value = dolfin.fem.assemble(M) assert value == pytest.approx(0.5, 1e-12)
def test_assemble_functional(): mesh = dolfin.generation.UnitSquareMesh(dolfin.MPI.comm_world, 12, 12) M = dolfin.Constant(1.0) * dx(domain=mesh) value = dolfin.fem.assemble(M) assert value == pytest.approx(1.0, 1e-12) f = dolfin.function.expression.Expression("x[0]", degree=1) M = f * dx(domain=mesh) value = dolfin.fem.assemble(M) assert value == pytest.approx(0.5, 1e-12)
def test_assemble_functional_dx(mode): mesh = create_unit_square(MPI.COMM_WORLD, 12, 12, ghost_mode=mode) M = form(1.0 * dx(domain=mesh)) value = assemble_scalar(M) value = mesh.comm.allreduce(value, op=MPI.SUM) assert value == pytest.approx(1.0, 1e-12) x = ufl.SpatialCoordinate(mesh) M = form(x[0] * dx(domain=mesh)) value = assemble_scalar(M) value = mesh.comm.allreduce(value, op=MPI.SUM) assert value == pytest.approx(0.5, 1e-12)
def test_assemble_functional(): mesh = dolfinx.generation.UnitSquareMesh(dolfinx.MPI.comm_world, 12, 12) M = 1.0 * dx(domain=mesh) value = dolfinx.fem.assemble_scalar(M) value = dolfinx.MPI.sum(mesh.mpi_comm(), value) assert value == pytest.approx(1.0, 1e-12) x = SpatialCoordinate(mesh) M = x[0] * dx(domain=mesh) value = dolfinx.fem.assemble_scalar(M) value = dolfinx.MPI.sum(mesh.mpi_comm(), value) assert value == pytest.approx(0.5, 1e-12)
def test_assemble_functional_dx(mode): mesh = UnitSquareMesh(MPI.COMM_WORLD, 12, 12, ghost_mode=mode) M = 1.0 * dx(domain=mesh) value = dolfinx.fem.assemble_scalar(M) value = mesh.mpi_comm().allreduce(value, op=MPI.SUM) assert value == pytest.approx(1.0, 1e-12) x = ufl.SpatialCoordinate(mesh) M = x[0] * dx(domain=mesh) value = dolfinx.fem.assemble_scalar(M) value = mesh.mpi_comm().allreduce(value, op=MPI.SUM) assert value == pytest.approx(0.5, 1e-12)
def test_expression_assemble(V1, vV1, squaremesh_5): u1, u2 = dolfinx.Function(V1), dolfinx.Function(vV1) dx = ufl.dx(squaremesh_5) u1.vector.set(3.0) u2.vector.set(2.0) u1.vector.ghostUpdate() u2.vector.ghostUpdate() # check assembled shapes assert numpy.shape(dolfiny.expression.assemble(1.0, dx)) == () assert numpy.shape(dolfiny.expression.assemble(ufl.grad(u1), dx)) == (2,) assert numpy.shape(dolfiny.expression.assemble(ufl.grad(u2), dx)) == (2, 2) # check assembled values assert numpy.isclose(dolfiny.expression.assemble(1.0, dx), 1.0) assert numpy.isclose(dolfiny.expression.assemble(u1, dx), 3.0) assert numpy.isclose(dolfiny.expression.assemble(u2, dx), 2.0).all() assert numpy.isclose(dolfiny.expression.assemble(u1 * u2, dx), 6.0).all() assert numpy.isclose(dolfiny.expression.assemble(ufl.grad(u1), dx), 0.0).all() assert numpy.isclose(dolfiny.expression.assemble(ufl.grad(u2), dx), 0.0).all()
def test_additivity(mode): mesh = create_unit_square(MPI.COMM_WORLD, 12, 12, ghost_mode=mode) V = FunctionSpace(mesh, ("Lagrange", 1)) f1 = Function(V) f2 = Function(V) f3 = Function(V) f1.x.array[:] = 1.0 f2.x.array[:] = 2.0 f3.x.array[:] = 3.0 j1 = ufl.inner(f1, f1) * ufl.dx(mesh) j2 = ufl.inner(f2, f2) * ufl.ds(mesh) j3 = ufl.inner(ufl.avg(f3), ufl.avg(f3)) * ufl.dS(mesh) # Assemble each scalar form separately J1 = mesh.comm.allreduce(assemble_scalar(form(j1)), op=MPI.SUM) J2 = mesh.comm.allreduce(assemble_scalar(form(j2)), op=MPI.SUM) J3 = mesh.comm.allreduce(assemble_scalar(form(j3)), op=MPI.SUM) # Sum forms and assemble the result J12 = mesh.comm.allreduce(assemble_scalar(form(j1 + j2)), op=MPI.SUM) J13 = mesh.comm.allreduce(assemble_scalar(form(j1 + j3)), op=MPI.SUM) J23 = mesh.comm.allreduce(assemble_scalar(form(j2 + j3)), op=MPI.SUM) J123 = mesh.comm.allreduce(assemble_scalar(form(j1 + j2 + j3)), op=MPI.SUM) # Compare assembled values assert (J1 + J2) == pytest.approx(J12) assert (J1 + J3) == pytest.approx(J13) assert (J2 + J3) == pytest.approx(J23) assert (J1 + J2 + J3) == pytest.approx(J123)
def test_expression_linearise(V1, V2, squaremesh_5): u1, u10 = dolfinx.Function(V1), dolfinx.Function(V1) u2, u20 = dolfinx.Function(V2), dolfinx.Function(V2) dx = ufl.dx(squaremesh_5) # Test linearisation of expressions at u0 assert dolfiny.expression.linearise(1 * u1, u1, u0=u10) == \ u10 + (u1 + (-1) * u10) assert dolfiny.expression.linearise(2 * u1 + u2, u1, u0=u10) == \ (u2 + 2 * u10) + (2 * u1 + (-1) * (2 * u10)) assert dolfiny.expression.linearise(u1**2 + u2, u1, u0=u10) == \ (u10 * (2 * u1) + (-1) * (u10 * 2 * u10)) + (u10**2 + u2) assert dolfiny.expression.linearise(u1**2 + u2**2, [u1, u2], u0=[u10, u20]) == \ (u10 * (2 * u1) + u20 * (2 * u2) + (-1) * (u10 * (2 * u10) + u20 * (2 * u20))) + (u10**2 + u20**2) assert dolfiny.expression.linearise([u1**2, u2], u1, u0=u10) == \ [(u10 * (2 * u1) + (-1) * (u10 * 2 * u10)) + u10**2, u2] assert dolfiny.expression.linearise([u1**2 + u2, u2], [u1, u2], u0=[u10, u20]) == \ [(u10 * (2 * u1) + u2 + (-1) * (u10 * (2 * u10) + u20)) + (u10**2 + u20), (u2 + (-1) * u20) + u20] # Test linearisation of forms at u0 assert dolfiny.expression.linearise(1 * u1 * dx, u1, u0=u10) == \ u10 * dx + (u1 * dx + (-1) * u10 * dx) assert dolfiny.expression.linearise([u1**2 * dx, u2 * dx], u1, u0=u10) == \ [u10**2 * dx + u10 * (2 * u1) * dx + (-1) * (u10 * 2 * u10) * dx, u2 * dx]
def test_gmsh_input_quad(order): pygmsh = pytest.importorskip("pygmsh") # Parameterize test if gmsh gets wider support R = 1 res = 0.2 if order == 2 else 0.2 algorithm = 2 if order == 2 else 5 element = "quad{0:d}".format(int((order + 1)**2)) geo = pygmsh.opencascade.Geometry() geo.add_raw_code("Mesh.ElementOrder={0:d};".format(order)) geo.add_ball([0, 0, 0], R, char_length=res) geo.add_raw_code("Recombine Surface {1};") geo.add_raw_code("Mesh.Algorithm = {0:d};".format(algorithm)) msh = pygmsh.generate_mesh(geo, verbose=True, dim=2) if order > 2: # Quads order > 3 have a gmsh specific ordering, and has to be permuted. msh_to_dolfin = np.array([0, 3, 11, 10, 1, 2, 6, 7, 4, 9, 12, 15, 5, 8, 13, 14]) cells = np.zeros(msh.cells_dict[element].shape) for i in range(len(cells)): for j in range(len(msh_to_dolfin)): cells[i, j] = msh.cells_dict[element][i, msh_to_dolfin[j]] else: # XDMF does not support higher order quads cells = permute_cell_ordering(msh.cells_dict[element], permutation_vtk_to_dolfin( CellType.quadrilateral, msh.cells_dict[element].shape[1])) mesh = Mesh(MPI.comm_world, CellType.quadrilateral, msh.points, cells, [], GhostMode.none) surface = assemble_scalar(1 * dx(mesh)) assert MPI.sum(mesh.mpi_comm(), surface) == pytest.approx(4 * np.pi * R * R, rel=1e-5)
def test_additivity(mode): mesh = dolfinx.UnitSquareMesh(MPI.COMM_WORLD, 12, 12, ghost_mode=mode) V = dolfinx.FunctionSpace(mesh, ("CG", 1)) f1 = dolfinx.Function(V) f2 = dolfinx.Function(V) f3 = dolfinx.Function(V) with f1.vector.localForm() as f1_local: f1_local.set(1.0) with f2.vector.localForm() as f2_local: f2_local.set(2.0) with f3.vector.localForm() as f3_local: f3_local.set(3.0) j1 = ufl.inner(f1, f1) * ufl.dx(mesh) j2 = ufl.inner(f2, f2) * ufl.ds(mesh) j3 = ufl.inner(ufl.avg(f3), ufl.avg(f3)) * ufl.dS(mesh) # Assemble each scalar form separately J1 = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(j1), op=MPI.SUM) J2 = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(j2), op=MPI.SUM) J3 = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(j3), op=MPI.SUM) # Sum forms and assemble the result J12 = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(j1 + j2), op=MPI.SUM) J13 = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(j1 + j3), op=MPI.SUM) J23 = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(j2 + j3), op=MPI.SUM) J123 = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(j1 + j2 + j3), op=MPI.SUM) # Compare assembled values assert (J1 + J2) == pytest.approx(J12) assert (J1 + J3) == pytest.approx(J13) assert (J2 + J3) == pytest.approx(J23) assert (J1 + J2 + J3) == pytest.approx(J123)
def test_custom_quadrature(compile_args): ve = ufl.VectorElement("P", "triangle", 1) mesh = ufl.Mesh(ve) e = ufl.FiniteElement("P", mesh.ufl_cell(), 2) V = ufl.FunctionSpace(mesh, e) u, v = ufl.TrialFunction(V), ufl.TestFunction(V) points = [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [0.5, 0.5], [0.0, 0.5], [0.5, 0.0]] weights = [1 / 12] * 6 a = u * v * ufl.dx(metadata={"quadrature_rule": "custom", "quadrature_points": points, "quadrature_weights": weights}) forms = [a] compiled_forms, module = ffcx.codegeneration.jit.compile_forms(forms, cffi_extra_compile_args=compile_args) ffi = cffi.FFI() form = compiled_forms[0][0] default_integral = form.create_cell_integral(-1) A = np.zeros((6, 6), dtype=np.float64) w = np.array([], dtype=np.float64) c = np.array([], dtype=np.float64) coords = np.array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0], dtype=np.float64) default_integral.tabulate_tensor( ffi.cast("double *", A.ctypes.data), ffi.cast("double *", w.ctypes.data), ffi.cast("double *", c.ctypes.data), ffi.cast("double *", coords.ctypes.data), ffi.NULL, ffi.NULL, 0) # Check that A is diagonal assert np.count_nonzero(A - np.diag(np.diagonal(A))) == 0
def test_expression_evaluate(V1, V2, squaremesh_5): u1, v1 = ufl.TrialFunction(V1), ufl.TestFunction(V1) u2, v2 = ufl.TrialFunction(V2), ufl.TestFunction(V2) dx = ufl.dx(squaremesh_5) # Test evaluation of expressions assert dolfiny.expression.evaluate(1 * u1, u1, u2) == 1 * u2 assert dolfiny.expression.evaluate(2 * u1 + u2, u1, u2) == 2 * u2 + 1 * u2 assert dolfiny.expression.evaluate(u1**2 + u2, u1, u2) == u2**2 + 1 * u2 assert dolfiny.expression.evaluate(u1 + u2, [u1, u2], [v1, v2]) == v1 + v2 assert dolfiny.expression.evaluate([u1, u2], u1, v1) == [v1, u2] assert dolfiny.expression.evaluate([u1, u2], [u1, u2], [v1, v2]) == [v1, v2] # Test evaluation of forms assert dolfiny.expression.evaluate(1 * u1 * dx, u1, u2) == 1 * u2 * dx assert dolfiny.expression.evaluate(2 * u1 * dx + u2 * dx, u1, u2) == 2 * u2 * dx + 1 * u2 * dx assert dolfiny.expression.evaluate(u1**2 * dx + u2 * dx, u1, u2) == u2**2 * dx + 1 * u2 * dx assert dolfiny.expression.evaluate(u1 * dx + u2 * dx, [u1, u2], [v1, v2]) == v1 * dx + v2 * dx assert dolfiny.expression.evaluate([u1 * dx, u2 * dx], u1, v1) == [v1 * dx, u2 * dx] assert dolfiny.expression.evaluate([u1 * dx, u2 * dx], [u1, u2], [v1, v2]) == [v1 * dx, v2 * dx]
def __init__(self, mesh, k: int, omega, c, c0, lumped): P = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), k) self.V = FunctionSpace(mesh, P) self.u, self.v = Function(self.V), Function(self.V) self.g1 = Function(self.V) self.g2 = Function(self.V) self.omega = omega self.c = c self.c0 = c0 n = FacetNormal(mesh) # Pieces for plane wave incident field x = ufl.geometry.SpatialCoordinate(mesh) cos_wave = ufl.cos(self.omega / self.c0 * x[0]) sin_wave = ufl.sin(self.omega / self.c0 * x[0]) plane_wave = self.g1 * cos_wave + self.g2 * sin_wave dv, p = TrialFunction(self.V), TestFunction(self.V) self.L1 = - inner(grad(self.u), grad(p)) * dx(degree=k) \ - (1 / self.c) * inner(self.v, p) * ds \ - (1 / self.c**2) * (-self.omega**2) * inner(plane_wave, p) * dx \ - inner(grad(plane_wave), grad(p)) * dx \ + inner(dot(grad(plane_wave), n), p) * ds # Vector to be re-used for assembly self.b = None # TODO: precompile/pre-process Form L self.lumped = lumped if self.lumped: a = (1 / self.c**2) * p * dx(degree=k) self.M = dolfinx.fem.assemble_vector(a) self.M.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) else: a = (1 / self.c**2) * inner(dv, p) * dx(degree=k) M = dolfinx.fem.assemble_matrix(a) M.assemble() self.solver = PETSc.KSP().create(mesh.mpi_comm()) opts = PETSc.Options() opts["ksp_type"] = "cg" opts["ksp_rtol"] = 1.0e-8 self.solver.setFromOptions() self.solver.setOperators(M)
def test_fourth_order_quad(L, H, Z): """Test by comparing integration of z+x*y against sympy/scipy integration of a quad element. Z>0 implies curved element. *---------* 20-21-22-23-24-41--42--43--44 | | | | | | | 15 16 17 18 19 37 38 39 40 | | | | | | | 10 11 12 13 14 33 34 35 36 | | | | | | | 5 6 7 8 9 29 30 31 32 | | | | | *---------* 0--1--2--3--4--25--26--27--28 """ points = np.array([[0, 0, 0], [L / 4, 0, 0], [L / 2, 0, 0], # 0 1 2 [3 * L / 4, 0, 0], [L, 0, 0], # 3 4 [0, H / 4, -Z / 3], [L / 4, H / 4, -Z / 3], [L / 2, H / 4, -Z / 3], # 5 6 7 [3 * L / 4, H / 4, -Z / 3], [L, H / 4, -Z / 3], # 8 9 [0, H / 2, 0], [L / 4, H / 2, 0], [L / 2, H / 2, 0], # 10 11 12 [3 * L / 4, H / 2, 0], [L, H / 2, 0], # 13 14 [0, (3 / 4) * H, 0], [L / 4, (3 / 4) * H, 0], # 15 16 [L / 2, (3 / 4) * H, 0], [3 * L / 4, (3 / 4) * H, 0], # 17 18 [L, (3 / 4) * H, 0], [0, H, Z], [L / 4, H, Z], # 19 20 21 [L / 2, H, Z], [3 * L / 4, H, Z], [L, H, Z], # 22 23 24 [(5 / 4) * L, 0, 0], [(6 / 4) * L, 0, 0], # 25 26 [(7 / 4) * L, 0, 0], [2 * L, 0, 0], # 27 28 [(5 / 4) * L, H / 4, -Z / 3], [(6 / 4) * L, H / 4, -Z / 3], # 29 30 [(7 / 4) * L, H / 4, -Z / 3], [2 * L, H / 4, -Z / 3], # 31 32 [(5 / 4) * L, H / 2, 0], [(6 / 4) * L, H / 2, 0], # 33 34 [(7 / 4) * L, H / 2, 0], [2 * L, H / 2, 0], # 35 36 [(5 / 4) * L, 3 / 4 * H, 0], # 37 [(6 / 4) * L, 3 / 4 * H, 0], # 38 [(7 / 4) * L, 3 / 4 * H, 0], [2 * L, 3 / 4 * H, 0], # 39 40 [(5 / 4) * L, H, Z], [(6 / 4) * L, H, Z], # 41 42 [(7 / 4) * L, H, Z], [2 * L, H, Z]]) # 43 44 # VTK ordering cells = np.array([[0, 4, 24, 20, 1, 2, 3, 9, 14, 19, 21, 22, 23, 5, 10, 15, 6, 7, 8, 11, 12, 13, 16, 17, 18], [4, 28, 44, 24, 25, 26, 27, 32, 36, 40, 41, 42, 43, 9, 14, 19, 29, 30, 31, 33, 34, 35, 37, 38, 39]]) cells = cells[:, perm_vtk(CellType.quadrilateral, cells.shape[1])] cell = ufl.Cell("quadrilateral", geometric_dimension=points.shape[1]) domain = ufl.Mesh(ufl.VectorElement("Lagrange", cell, 4)) mesh = create_mesh(MPI.COMM_WORLD, cells, points, domain) def e2(x): return x[2] + x[0] * x[1] V = FunctionSpace(mesh, ("CG", 4)) u = Function(V) u.interpolate(e2) intu = assemble_scalar(u * dx(mesh)) intu = mesh.mpi_comm().allreduce(intu, op=MPI.SUM) nodes = [0, 5, 10, 15, 20] ref = sympy_scipy(points, nodes, 2 * L, H) assert ref == pytest.approx(intu, rel=1e-5)
def test_ufl_block_extraction(V1, V2, squaremesh_5): mesh = squaremesh_5 u1, v1 = ufl.TrialFunction(V1), ufl.TestFunction(V1) u2, v2 = ufl.TrialFunction(V2), ufl.TestFunction(V2) a = u1 * v1 * ufl.dx(mesh) + ufl.grad(u1 * v2)[0] * ufl.dx(mesh) ablocks = dolfiny.function.extract_blocks(a, [v1, v2], [u1, u2]) assert ablocks[1][1].empty() assert ablocks[0][1].empty() L = ufl.grad(v2)[0] * ufl.dx(mesh) Lblocks = dolfiny.function.extract_blocks(L, [v1, v2]) assert Lblocks[0].empty() assert Lblocks[1] == L
def translate_cellvolume(terminal, mt, ctx): integrand, degree = one_times(ufl.dx(domain=terminal.ufl_domain())) interface = CellVolumeKernelInterface(ctx, mt.restriction) config = {name: getattr(ctx, name) for name in ["ufl_cell", "precision", "index_cache"]} config.update(interface=interface, quadrature_degree=degree) expr, = compile_ufl(integrand, point_sum=True, **config) return expr
def test_block(V1, V2, squaremesh_5, nest): mesh = squaremesh_5 u0 = dolfinx.Function(V1, name="u0") u1 = dolfinx.Function(V2, name="u1") v0 = ufl.TestFunction(V1) v1 = ufl.TestFunction(V2) Phi = (ufl.sin(u0) - 0.5)**2 * ufl.dx(mesh) + (4.0 * u0 - u1)**2 * ufl.dx(mesh) F0 = ufl.derivative(Phi, u0, v0) F1 = ufl.derivative(Phi, u1, v1) F = [F0, F1] u = [u0, u1] opts = PETSc.Options("block") opts.setValue('snes_type', 'newtontr') opts.setValue('snes_rtol', 1.0e-08) opts.setValue('snes_max_it', 12) if nest: opts.setValue('ksp_type', 'cg') opts.setValue('pc_type', 'fieldsplit') opts.setValue('fieldsplit_pc_type', 'lu') opts.setValue('ksp_rtol', 1.0e-10) else: opts.setValue('ksp_type', 'preonly') opts.setValue('pc_type', 'lu') opts.setValue('pc_factor_mat_solver_type', 'mumps') problem = dolfiny.snesblockproblem.SNESBlockProblem(F, u, nest=nest, prefix="block") sol = problem.solve() assert problem.snes.getConvergedReason() > 0 assert np.isclose((sol[0].vector - np.arcsin(0.5)).norm(), 0.0) assert np.isclose((sol[1].vector - 4.0 * np.arcsin(0.5)).norm(), 0.0)
def test_xdmf_input_tri(datadir): with XDMFFile(MPI.COMM_WORLD, os.path.join(datadir, "mesh.xdmf"), "r", encoding=XDMFFile.Encoding.ASCII) as xdmf: mesh = xdmf.read_mesh(name="Grid") surface = assemble_scalar(1 * dx(mesh)) assert mesh.mpi_comm().allreduce(surface, op=MPI.SUM) == pytest.approx(4 * np.pi, rel=1e-4)
def test_expression_derivative(V1, V2, squaremesh_5): u1, du1, v1 = dolfinx.fem.Function(V1), ufl.TrialFunction( V1), ufl.TestFunction(V1) u2, du2, v2 = dolfinx.fem.Function(V2), ufl.TrialFunction( V2), ufl.TestFunction(V2) dx = ufl.dx(squaremesh_5) # Test derivative of expressions assert dolfiny.expression.derivative(1 * u1, u1, du1) == 1 * du1 assert dolfiny.expression.derivative(2 * u1 + u2, u1, du1) == 2 * du1 assert dolfiny.expression.derivative(u1**2 + u2, u1, du1) == du1 * 2 * u1 assert dolfiny.expression.derivative(u1 + u2, [u1, u2], [v1, v2]) == v1 + v2 assert dolfiny.expression.derivative([u1, u2], u1, v1) == [v1, 0] assert dolfiny.expression.derivative([u1, u2], [u1, u2], [v1, v2]) == [v1, v2] # Test derivative of forms assert dolfiny.expression.derivative(1 * u1 * dx, u1, du1) == 1 * du1 * dx assert dolfiny.expression.derivative(2 * u1 * dx + u2 * dx, u1, du1) == 2 * du1 * dx assert dolfiny.expression.derivative(u1**2 * dx + u2 * dx, u1, du2) == du2 * 2 * u1 * dx assert dolfiny.expression.derivative(u1 * dx + u2 * dx, [u1, u2], [v1, v2]) == v1 * dx + v2 * dx assert dolfiny.expression.derivative([u1 * dx, u2 * dx], u1, v1) == [v1 * dx, ufl.Form([])] assert dolfiny.expression.derivative([u1 * dx, u2 * dx], [u1, u2], [v1, v2]) == [v1 * dx, v2 * dx] # Test derivative of expressions at u0 u10, u20 = dolfinx.fem.Function(V1), dolfinx.fem.Function(V2) assert dolfiny.expression.derivative(1 * u1, u1, du1, u0=u10) == 1 * du1 assert dolfiny.expression.derivative(2 * u1 + u2, u1, du1, u0=u10) == 2 * du1 assert dolfiny.expression.derivative(u1**2 + u2, u1, du1, u0=u10) == du1 * 2 * u10 assert dolfiny.expression.derivative( u1**2 + u2**2, [u1, u2], [v1, v2], [u10, u20]) == v1 * 2 * u10 + v2 * 2 * u20 assert dolfiny.expression.derivative([u1**2, u2], u1, v1, u0=u10) == [v1 * 2 * u10, 0] assert dolfiny.expression.derivative( [u1**2 + u2, u2], [u1, u2], [v1, v2], [u10, u20]) == [v1 * 2 * u10 + v2, v2]
def xtest_third_order_tri(): # *---*---*---* 3--11--10--2 # | \ | | \ | # * * * * 8 7 15 13 # | \ | | \ | # * * * * 9 14 6 12 # | \ | | \ | # *---*---*---* 0--4---5---1 for H in (1.0, 2.0): for Z in (0.0, 0.5): L = 1 points = np.array([ [0, 0, 0], [L, 0, 0], [L, H, Z], [0, H, Z], # 0, 1, 2, 3 [L / 3, 0, 0], [2 * L / 3, 0, 0], # 4, 5 [2 * L / 3, H / 3, 0], [L / 3, 2 * H / 3, 0], # 6, 7 [0, 2 * H / 3, 0], [0, H / 3, 0], # 8, 9 [2 * L / 3, H, Z], [L / 3, H, Z], # 10, 11 [L, H / 3, 0], [L, 2 * H / 3, 0], # 12, 13 [L / 3, H / 3, 0], # 14 [2 * L / 3, 2 * H / 3, 0] ]) # 15 cells = np.array([[0, 1, 3, 4, 5, 6, 7, 8, 9, 14], [1, 2, 3, 12, 13, 10, 11, 7, 6, 15]]) cells = permute_cell_ordering( cells, permutation_vtk_to_dolfin(CellType.triangle, cells.shape[1])) mesh = Mesh(MPI.COMM_WORLD, CellType.triangle, points, cells, [], degree=3) def e2(x): return x[2] + x[0] * x[1] degree = mesh.geometry.dofmap_layout().degree() # Interpolate function V = FunctionSpace(mesh, ("CG", degree)) u = Function(V) u.interpolate(e2) intu = assemble_scalar(u * dx(metadata={"quadrature_degree": 40})) intu = mesh.mpi_comm().allreduce(intu, op=MPI.SUM) nodes = [0, 9, 8, 3] ref = sympy_scipy(points, nodes, L, H) assert ref == pytest.approx(intu, rel=1e-6)
def test_gmsh_input_3d(order, cell_type): try: import gmsh except ImportError: pytest.skip() if cell_type == CellType.hexahedron and order > 2: pytest.xfail("GMSH permutation for order > 2 hexahedra not implemented in DOLFINx.") res = 0.2 gmsh.initialize() if cell_type == CellType.hexahedron: gmsh.option.setNumber("Mesh.RecombinationAlgorithm", 2) gmsh.option.setNumber("Mesh.RecombineAll", 2) gmsh.option.setNumber("Mesh.CharacteristicLengthMin", res) gmsh.option.setNumber("Mesh.CharacteristicLengthMax", res) circle = gmsh.model.occ.addDisk(0, 0, 0, 1, 1) if cell_type == CellType.hexahedron: gmsh.model.occ.extrude([(2, circle)], 0, 0, 1, numElements=[5], recombine=True) else: gmsh.model.occ.extrude([(2, circle)], 0, 0, 1, numElements=[5]) gmsh.model.occ.synchronize() gmsh.model.mesh.generate(3) gmsh.model.mesh.setOrder(order) idx, points, _ = gmsh.model.mesh.getNodes() points = points.reshape(-1, 3) idx -= 1 srt = np.argsort(idx) assert np.all(idx[srt] == np.arange(len(idx))) x = points[srt] element_types, element_tags, node_tags = gmsh.model.mesh.getElements(dim=3) name, dim, order, num_nodes, local_coords, num_first_order_nodes = gmsh.model.mesh.getElementProperties( element_types[0]) cells = node_tags[0].reshape(-1, num_nodes) - 1 if cell_type == CellType.tetrahedron: gmsh_cell_id = MPI.COMM_WORLD.bcast(gmsh.model.mesh.getElementType("tetrahedron", order), root=0) elif cell_type == CellType.hexahedron: gmsh_cell_id = MPI.COMM_WORLD.bcast(gmsh.model.mesh.getElementType("hexahedron", order), root=0) gmsh.finalize() # Permute the mesh topology from GMSH ordering to DOLFINx ordering domain = ufl_mesh_from_gmsh(gmsh_cell_id, 3) cells = cells[:, perm_gmsh(cell_type, cells.shape[1])] mesh = create_mesh(MPI.COMM_WORLD, cells, x, domain) volume = assemble_scalar(form(1 * dx(mesh))) assert mesh.comm.allreduce(volume, op=MPI.SUM) == pytest.approx(np.pi, rel=10 ** (-1 - order))
def cellvolume(restriction): from ufl import dx integrand, degree = ufl_utils.one_times(dx(domain=domain)) integrand = ufl_utils.replace_coordinates(integrand, coordinate_coefficient) interface = CellVolumeKernelInterface(kernel_config["interface"], restriction) config = {k: v for k, v in kernel_config.items() if k in ["ufl_cell", "precision", "index_cache"]} config.update(interface=interface, quadrature_degree=degree) expr, = fem.compile_ufl(integrand, point_sum=True, **config) return expr
def test_subdomains(compile_args): cell = ufl.triangle element = ufl.FiniteElement("Lagrange", cell, 1) u, v = ufl.TrialFunction(element), ufl.TestFunction(element) a0 = ufl.inner(u, v) * ufl.dx + ufl.inner(u, v) * ufl.dx(2) a1 = ufl.inner(u, v) * ufl.dx(2) + ufl.inner(u, v) * ufl.dx a2 = ufl.inner(u, v) * ufl.dx(2) + ufl.inner(u, v) * ufl.dx(1) a3 = ufl.inner(u, v) * ufl.ds(210) + ufl.inner(u, v) * ufl.ds(0) forms = [a0, a1, a2, a3] compiled_forms, module = ffcx.codegeneration.jit.compile_forms( forms, parameters={'scalar_type': 'double'}, cffi_extra_compile_args=compile_args) for f, compiled_f in zip(forms, compiled_forms): assert compiled_f.rank == len(f.arguments()) ffi = cffi.FFI() form0 = compiled_forms[0][0] ids = np.zeros(form0.num_cell_integrals, dtype=np.int32) form0.get_cell_integral_ids(ffi.cast('int *', ids.ctypes.data)) assert ids[0] == -1 and ids[1] == 2 form1 = compiled_forms[1][0] ids = np.zeros(form1.num_cell_integrals, dtype=np.int32) form1.get_cell_integral_ids(ffi.cast('int *', ids.ctypes.data)) assert ids[0] == -1 and ids[1] == 2 form2 = compiled_forms[2][0] ids = np.zeros(form2.num_cell_integrals, dtype=np.int32) form2.get_cell_integral_ids(ffi.cast('int *', ids.ctypes.data)) assert ids[0] == 1 and ids[1] == 2 form3 = compiled_forms[3][0] ids = np.zeros(form3.num_cell_integrals, dtype=np.int32) form3.get_cell_integral_ids(ffi.cast('int *', ids.ctypes.data)) assert len(ids) == 0 ids = np.zeros(form3.num_exterior_facet_integrals, dtype=np.int32) form3.get_exterior_facet_integral_ids(ffi.cast('int *', ids.ctypes.data)) assert ids[0] == 0 and ids[1] == 210
def test_third_order_quad(L, H, Z): """Test by comparing integration of z+x*y against sympy/scipy integration of a quad element. Z>0 implies curved element. *---------* 3--8--9--2-22-23-17 | | | | | | | 11 14 15 7 26 27 21 | | | | | | | 10 12 13 6 24 25 20 | | | | | *---------* 0--4--5--1-18-19-16 """ points = np.array([[0, 0, 0], [L, 0, 0], [L, H, Z], [0, H, Z], # 0 1 2 3 [L / 3, 0, 0], [2 * L / 3, 0, 0], # 4 5 [L, H / 3, 0], [L, 2 * H / 3, 0], # 6 7 [L / 3, H, Z], [2 * L / 3, H, Z], # 8 9 [0, H / 3, 0], [0, 2 * H / 3, 0], # 10 11 [L / 3, H / 3, 0], [2 * L / 3, H / 3, 0], # 12 13 [L / 3, 2 * H / 3, 0], [2 * L / 3, 2 * H / 3, 0], # 14 15 [2 * L, 0, 0], [2 * L, H, Z], # 16 17 [4 * L / 3, 0, 0], [5 * L / 3, 0, 0], # 18 19 [2 * L, H / 3, 0], [2 * L, 2 * H / 3, 0], # 20 21 [4 * L / 3, H, Z], [5 * L / 3, H, Z], # 22 23 [4 * L / 3, H / 3, 0], [5 * L / 3, H / 3, 0], # 24 25 [4 * L / 3, 2 * H / 3, 0], [5 * L / 3, 2 * H / 3, 0]]) # 26 27 # Change to multiple cells when matthews dof-maps work for quads cells = np.array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [1, 16, 17, 2, 18, 19, 20, 21, 22, 23, 6, 7, 24, 25, 26, 27]]) cells = permute_cell_ordering(cells, permutation_vtk_to_dolfin(CellType.quadrilateral, cells.shape[1])) mesh = Mesh(MPI.comm_world, CellType.quadrilateral, points, cells, [], GhostMode.none) def e2(x): return x[2] + x[0] * x[1] # Interpolate function V = FunctionSpace(mesh, ("CG", 3)) u = Function(V) cmap = fem.create_coordinate_map(mesh.ufl_domain()) mesh.geometry.coord_mapping = cmap u.interpolate(e2) intu = assemble_scalar(u * dx(mesh)) intu = MPI.sum(mesh.mpi_comm(), intu) nodes = [0, 3, 10, 11] ref = sympy_scipy(points, nodes, 2 * L, H) assert ref == pytest.approx(intu, rel=1e-6)
def norm(v, norm_type="L2", degree=None): """Compute the norm of ``v``. :arg v: a ufl expression (:class:`~.ufl.classes.Expr`) to compute the norm of :arg norm_type: the type of norm to compute, see below for options. Available norm types are: * L2 .. math:: ||v||_{L^2}^2 = \int (v, v) \mathrm{d}x * H1 .. math:: ||v||_{H^1}^2 = \int (v, v) + (\\nabla v, \\nabla v) \mathrm{d}x * Hdiv .. math:: ||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\\nabla\cdot v, \\nabla \cdot v) \mathrm{d}x * Hcurl .. math:: ||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\\nabla \wedge v, \\nabla \wedge v) \mathrm{d}x """ if not degree == None: dxn = dx(2 * degree + 1) else: dxn = dx typ = norm_type.lower() if typ == 'l2': form = inner(v, v) * dxn elif typ == 'h1': form = inner(v, v) * dxn + inner(grad(v), grad(v)) * dxn elif typ == "hdiv": form = inner(v, v) * dxn + div(v) * div(v) * dxn elif typ == "hcurl": form = inner(v, v) * dxn + inner(curl(v), curl(v)) * dxn else: raise RuntimeError("Unknown norm type '%s'" % norm_type) normform = ZeroForm(form) return sqrt(normform.assembleform())
def test_monolithic(V1, V2, squaremesh_5): mesh = squaremesh_5 Wel = ufl.MixedElement([V1.ufl_element(), V2.ufl_element()]) W = dolfinx.FunctionSpace(mesh, Wel) u = dolfinx.Function(W) u0, u1 = ufl.split(u) v = ufl.TestFunction(W) v0, v1 = ufl.split(v) Phi = (ufl.sin(u0) - 0.5)**2 * ufl.dx(mesh) + (4.0 * u0 - u1)**2 * ufl.dx(mesh) F = ufl.derivative(Phi, u, v) opts = PETSc.Options("monolithic") opts.setValue('snes_type', 'newtonls') opts.setValue('snes_linesearch_type', 'basic') opts.setValue('snes_rtol', 1.0e-10) opts.setValue('snes_max_it', 20) opts.setValue('ksp_type', 'preonly') opts.setValue('pc_type', 'lu') opts.setValue('pc_factor_mat_solver_type', 'mumps') problem = dolfiny.snesblockproblem.SNESBlockProblem([F], [u], prefix="monolithic") sol, = problem.solve() u0, u1 = sol.split() u0 = u0.collapse() u1 = u1.collapse() assert np.isclose((u0.vector - np.arcsin(0.5)).norm(), 0.0) assert np.isclose((u1.vector - 4.0 * np.arcsin(0.5)).norm(), 0.0)
def cellvolume(restriction): from ufl import dx integrand, degree = ufl_utils.one_times(dx(domain=domain)) integrand = ufl_utils.replace_coordinates(integrand, coordinate_coefficient) interface = CellVolumeKernelInterface(kernel_config["interface"], restriction) quadrature_index = gem.Index(name='q') config = {k: v for k, v in kernel_config.items() if k in ["ufl_cell", "precision", "index_cache"]} config.update(interface=interface, quadrature_degree=degree, point_index=quadrature_index) expr, = fem.compile_ufl(integrand, **config) if quadrature_index in expr.free_indices: expr = gem.IndexSum(expr, quadrature_index) return expr
def error_indicators(self): """ Generate and return linear form defining error indicators """ # Extract these to increase readability R_T = self._R_T R_dT = self._R_dT z = self._Ez_h z_h = self._z_h # Define linear form for computing error indicators v = self.module.TestFunction(self._DG0) eta_T = (v * inner(R_T, z - z_h) * dx(self.domain) + avg(v)*(inner(R_dT('+'), (z - z_h)('+')) + inner(R_dT('-'), (z - z_h)('-'))) * dS(self.domain) + v * inner(R_dT, z - z_h) * ds(self.domain)) return eta_T
def cell_residual(self): """ Generate and return (bilinear, linear) forms defining linear variational problem for the strong cell residual """ # Define trial and test functions for the cell residuals on # discontinuous version of primal trial space R_T = self.module.TrialFunction(self._dV) v = self.module.TestFunction(self._dV) # Extract original test function in the weak residual v_h = self.weak_residual.arguments()[0] # Define forms defining linear variational problem for cell # residual v_T = self._b_T * v a_R_T = inner(v_T, R_T) * dx(self.domain) L_R_T = replace(self.weak_residual, {v_h: v_T}) return (a_R_T, L_R_T)
def facet_residual(self): """ Generate and return (bilinear, linear) forms defining linear variational problem for the strong facet residual(s) """ # Define trial and test functions for the facet residuals on # discontinuous version of primal trial space R_e = self.module.TrialFunction(self._dV) v = self.module.TestFunction(self._dV) # Extract original test function in the weak residual v_h = self.weak_residual.arguments()[0] # Define forms defining linear variational problem for facet # residual v_e = self._b_e*v a_R_dT = ((inner(v_e('+'), R_e('+')) + inner(v_e('-'), R_e('-')))*dS(self.domain) + inner(v_e, R_e)*ds(self.domain)) L_R_dT = (replace(self.weak_residual, {v_h: v_e}) - inner(v_e, self._R_T)*dx(self.domain)) return (a_R_dT, L_R_dT)
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(find_geometric_dimension(u)) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt}, {'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity p_ = Function(self.Q) # current pressure or pressure help function from rotation scheme p_mod = Function(self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5 * u0 - 0.5 * u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) if self.args.cbc_tau: # used in Simula cbcflow project tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h) else: # proposed in R. Codina: On stabilized finite element methods for linear systems of # convection-diffusion-reaction equations. tau = Constant(self.stabCoef) * k * h ** 2 / ( 2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) + h ** 2) # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0 if self.use_full_SUPG: v1 = v + tau * 0.5 * dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v def nonlinearity(function): if self.args.ema: return 2 * inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function) * u_ext, v1) * dx else: return inner(dot(grad(function), u_ext), v1) * dx def diffusion(fce): if self.useLaplace: return nu * inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': return form if self.bcv == 'LAP': return form - inner(nu * dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form() if self.bcv == 'DDN': return form # additional term must be added to non-constant part def pressure_rhs(): if self.args.bc == 'outflow': return inner(p0, div(v1)) * dx else: return inner(p0, div(v1)) * dx - inner(p0 * n, v1) * problem.get_outflow_measure_form() a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u) a1_change = nonlinearity(0.5 * u) if self.bcv == 'DDN': # does not penalize influx for current step, only for the next one # this can lead to oscilation: # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5 * min_value(Constant(0.), inner(u_ext, n)) * inner(u, v1) * problem.get_outflow_measure_form() # NT works only with uflacs compiler L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity(0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner(u0, v1) * problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot(grad(v), u_ext)) * dx(None, {'quadrature_degree': 6}) # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed: # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx else: # Projection, solve to p_ if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx for m in problem.get_outflow_measures(): F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m else: F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx else: F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0), v) * dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure F4 = (p - p0 - p_ + nu * div(u_)) * q * dx a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble(a1_const) # must be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwritten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwritten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('mumps') if self.useRotationScheme: self.solver_rot = LUSolver('mumps') else: # NT 2016-1 KrylovSolver >> PETScKrylovSolver # not needed, chosen not to use hypre_parasails: # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = PETScKrylovSolver('gmres', self.args.precV) # nonsymetric > gmres # cannot use 'ilu' in parallel self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC) self.solver_p = PETScKrylovSolver(self.args.solP, self.args.precP) # almost (up to BC) symmetric > CG if self.useRotationScheme: self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg') # setup Krylov solvers if self.solvers == 'krylov': # Get the nullspace if there are no pressure boundary conditions foo = Function(self.Q) # auxiliary vector for setting pressure nullspace if self.args.bc == 'nullspace': null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0 / null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers solver_options = {'monitor_convergence': True, 'maximum_iterations': 10000, 'nonzero_initial_guess': True} # 'nonzero_initial_guess': True with solver.solve(A, u, b) means that # Solver will use anything stored in u as an initial guess for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 if self.args.solP == 'richardson': self.solver_p.parameters['monitor_convergence'] = False self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.args.prv1) self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.args.pav1) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10 ** (-self.args.prp) self.solver_p.parameters['absolute_tolerance'] = 10 ** (-self.args.pap) if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10E-10 self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.args.Vrestart > 0: self.solver_vel_tent.parameters['gmres']['restart'] = self.args.Vrestart if self.args.solP == 'gmres' and self.args.Prestart > 0: self.solver_p.parameters['gmres']['restart'] = self.args.Prestart # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 # debug function if problem.args.debug_rot: plot_cor_v = Function(self.V) while t < (ttime + dt / 2.0): self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble(a1_change, tensor=A1_change) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble(a1_stab, tensor=A1_stab) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow - out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.args.bc == 'nullspace': self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) foo.assign(p_ + p0) if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(True, foo) else: foo = Function(self.Q) foo.assign(p_) # we do not want to change p_ by averaging if save_this_step and not onlyVel: problem.averaging_pressure(foo) problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.args.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step and not onlyVel: problem.averaging_pressure(p_mod) problem.save_pressure(False, p_mod) end() if problem.args.debug_rot: # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step) # see comment next to argument definition plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V)) problem.fileDict['grad_cor']['file'].write(plot_cor_v, t) # compute functionals (e. g. forces) problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else p_, t, step) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign(u_cor) # use corrected velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0
def mass_cg(cell, degree): m = Mesh(VectorElement('Q', cell, 1)) V = FunctionSpace(m, FiniteElement('Q', cell, degree, variant='spectral')) u = TrialFunction(V) v = TestFunction(V) return u*v*dx(rule=gll_quadrature_rule(cell, degree))
def norm(v, norm_type="L2", mesh=None): """ Return the norm of a given vector or function. *Arguments* v a :py:class:`Vector <dolfin.cpp.Vector>` or a :py:class:`Function <dolfin.functions.function.Function>`. norm_type see below for alternatives. mesh optional :py:class:`Mesh <dolfin.cpp.Mesh>` on which to compute the norm. If the norm type is not specified, the standard :math:`L^2` -norm is computed. Possible norm types include: *Vectors* ================ ================= ================ Norm Usage ================ ================= ================ :math:`l^2` norm(x, 'l2') Default :math:`l^1` norm(x, 'l1') :math:`l^\infty` norm(x, 'linf') ================ ================= ================ *Functions* ================ ================= ================================= Norm Usage Includes the :math:`L^2` -term ================ ================= ================================= :math:`L^2` norm(v, 'L2') Yes :math:`H^1` norm(v, 'H1') Yes :math:`H^1_0` norm(v, 'H10') No :math:`H` (div) norm(v, 'Hdiv') Yes :math:`H` (div) norm(v, 'Hdiv0') No :math:`H` (curl) norm(v, 'Hcurl') Yes :math:`H` (curl) norm(v, 'Hcurl0') No ================ ================= ================================= *Examples of usage* .. code-block:: python v = Function(V) x = v.vector() print norm(x, 'linf') # print the infinity norm of vector x n = norm(v) # compute L^2 norm of v print norm(v, 'Hdiv') # print H(div) norm of v n = norm(v, 'H1', mesh) # compute H^1 norm of v on given mesh """ if not isinstance(v, (GenericVector, GenericFunction)): raise TypeError, "expected a GenericVector or GenericFunction" # Check arguments if not isinstance(norm_type, str): cpp.dolfin_error("norms.py", "compute norm", "Norm type must be a string, not " + str(type(norm_type))) if mesh is not None and not isinstance(mesh, Mesh): cpp.dolfin_error("norms.py", "compute norm", "Expecting a Mesh, not " + str(type(mesh))) # Select norm type if isinstance(v, GenericVector): return v.norm(norm_type.lower()) elif (isinstance(v, Coefficient) and isinstance(v, GenericFunction)): if norm_type.lower() == "l2": M = inner(v, v)*dx() elif norm_type.lower() == "h1": M = inner(v, v)*dx() + inner(grad(v), grad(v))*dx() elif norm_type.lower() == "h10": M = inner(grad(v), grad(v))*dx() elif norm_type.lower() == "hdiv": M = inner(v, v)*dx() + div(v)*div(v)*dx() elif norm_type.lower() == "hdiv0": M = div(v)*div(v)*dx() elif norm_type.lower() == "hcurl": M = inner(v, v)*dx() + inner(curl(v), curl(v))*dx() elif norm_type.lower() == "hcurl0": M = inner(curl(v), curl(v))*dx() else: cpp.dolfin_error("norms.py", "compute norm", "Unknown norm type (\"%s\") for functions" % str(norm_type)) else: cpp.dolfin_error("norms.py", "compute norm", "Unknown object type. Must be a vector or a function") # Get mesh if isinstance(v, Function) and mesh is None: mesh = v.function_space().mesh() # Assemble value r = assemble(M, mesh=mesh, form_compiler_parameters={"representation": "quadrature"}) # Check value if r < 0.0: cpp.dolfin_error("norms.py", "compute norm", "Square of norm is negative, might be a round-off error") elif r == 0.0: return 0.0 else: return sqrt(r)
def project(v, V, bcs=None, mesh=None, solver_parameters=None, form_compiler_parameters=None, name=None): """Project an :class:`.Expression` or :class:`.Function` into a :class:`.FunctionSpace` :arg v: the :class:`.Expression`, :class:`ufl.Expr` or :class:`.Function` to project :arg V: the :class:`.FunctionSpace` or :class:`.Function` to project into :arg bcs: boundary conditions to apply in the projection :arg mesh: the mesh to project into :arg solver_parameters: parameters to pass to the solver used when projecting. :arg form_compiler_parameters: parameters to the form compiler :arg name: name of the resulting :class:`.Function` If ``V`` is a :class:`.Function` then ``v`` is projected into ``V`` and ``V`` is returned. If `V` is a :class:`.FunctionSpace` then ``v`` is projected into a new :class:`.Function` and that :class:`.Function` is returned. The ``bcs``, ``mesh`` and ``form_compiler_parameters`` are currently ignored.""" from firedrake import function if isinstance(V, functionspace.FunctionSpaceBase): ret = function.Function(V, name=name) elif isinstance(V, function.Function): ret = V V = V.function_space() else: raise RuntimeError( 'Can only project into functions and function spaces, not %r' % type(V)) if isinstance(v, expression.Expression): shape = v.value_shape() # Build a function space that supports PointEvaluation so that # we can interpolate into it. if isinstance(V.ufl_element().degree(), tuple): deg = max(V.ufl_element().degree()) else: deg = V.ufl_element().degree() if v.rank() == 0: fs = functionspace.FunctionSpace(V.mesh(), 'DG', deg+1) elif v.rank() == 1: fs = functionspace.VectorFunctionSpace(V.mesh(), 'DG', deg+1, dim=shape[0]) else: fs = functionspace.TensorFunctionSpace(V.mesh(), 'DG', deg+1, shape=shape) f = function.Function(fs) f.interpolate(v) v = f elif isinstance(v, function.Function): if v.function_space().mesh() != ret.function_space().mesh(): raise RuntimeError("Can't project between mismatching meshes") elif not isinstance(v, ufl.core.expr.Expr): raise RuntimeError("Can't only project from expressions and functions, not %r" % type(v)) if v.ufl_shape != ret.ufl_shape: raise RuntimeError('Shape mismatch between source %s and target function spaces %s in project' % (v.ufl_shape, ret.ufl_shape)) p = ufl_expr.TestFunction(V) q = ufl_expr.TrialFunction(V) a = ufl.inner(p, q) * ufl.dx(domain=V.mesh()) L = ufl.inner(p, v) * ufl.dx(domain=V.mesh()) # Default to 1e-8 relative tolerance if solver_parameters is None: solver_parameters = {'ksp_type': 'cg', 'ksp_rtol': 1e-8} else: solver_parameters.setdefault('ksp_type', 'cg') solver_parameters.setdefault('ksp_rtol', 1e-8) _solve(a == L, ret, bcs=bcs, solver_parameters=solver_parameters, form_compiler_parameters=form_compiler_parameters) return ret
def laplace(cell, degree): m = Mesh(VectorElement('Q', cell, 1)) V = FunctionSpace(m, FiniteElement('Q', cell, degree, variant='spectral')) u = TrialFunction(V) v = TestFunction(V) return dot(grad(u), grad(v))*dx(rule=gll_quadrature_rule(cell, degree))
def norm(v, norm_type="L2", mesh=None): r""" Return the norm of a given vector or function. *Arguments* v a :py:class:`Vector <dolfin.cpp.Vector>` or a :py:class:`Function <dolfin.functions.function.Function>`. norm_type see below for alternatives. mesh optional :py:class:`Mesh <dolfin.cpp.Mesh>` on which to compute the norm. If the norm type is not specified, the standard :math:`L^2` -norm is computed. Possible norm types include: *Vectors* ================ ================= ================ Norm Usage ================ ================= ================ :math:`l^2` norm(x, 'l2') Default :math:`l^1` norm(x, 'l1') :math:`l^\infty` norm(x, 'linf') ================ ================= ================ *Functions* ================ ================= ================================= Norm Usage Includes the :math:`L^2` -term ================ ================= ================================= :math:`L^2` norm(v, 'L2') Yes :math:`H^1` norm(v, 'H1') Yes :math:`H^1_0` norm(v, 'H10') No :math:`H` (div) norm(v, 'Hdiv') Yes :math:`H` (div) norm(v, 'Hdiv0') No :math:`H` (curl) norm(v, 'Hcurl') Yes :math:`H` (curl) norm(v, 'Hcurl0') No ================ ================= ================================= *Examples of usage* .. code-block:: python v = Function(V) x = v.vector() print norm(x, 'linf') # print the infinity norm of vector x n = norm(v) # compute L^2 norm of v print norm(v, 'Hdiv') # print H(div) norm of v n = norm(v, 'H1', mesh) # compute H^1 norm of v on given mesh """ # if not isinstance(v, (GenericVector, GenericFunction)): # cpp.dolfin_error("norms.py", # "compute norm", # "expected a GenericVector or GenericFunction") # Check arguments # if not isinstance(norm_type, string_types): # cpp.dolfin_error("norms.py", # "compute norm", # "Norm type must be a string, not " + # str(type(norm_type))) # if mesh is not None and not isinstance(mesh, cpp.Mesh): # cpp.dolfin_error("norms.py", # "compute norm", # "Expecting a Mesh, not " + str(type(mesh))) # Get mesh from function if isinstance(v, cpp.function.Function) and mesh is None: mesh = v.function_space().mesh() elif isinstance(v, MultiMeshFunction) and mesh is None: mesh = v.function_space().multimesh() # Define integration measure and domain if isinstance(v, MultiMeshFunction): dc = ufl.dx(mesh) + ufl.dC(mesh) assemble_func = functools.partial(assemble_multimesh, form_compiler_parameters={"representation": "quadrature"}) else: dc = ufl.dx(mesh) assemble_func = assemble # Select norm type if isinstance(v, cpp.la.GenericVector): return v.norm(norm_type.lower()) elif isinstance(v, ufl.Coefficient): if norm_type.lower() == "l2": M = v**2 * dc elif norm_type.lower() == "h1": M = (v**2 + grad(v)**2) * dc elif norm_type.lower() == "h10": M = grad(v)**2 * dc elif norm_type.lower() == "hdiv": M = (v**2 + div(v)**2) * dc elif norm_type.lower() == "hdiv0": M = div(v)**2 * dc elif norm_type.lower() == "hcurl": M = (v**2 + curl(v)**2) * dc elif norm_type.lower() == "hcurl0": M = curl(v)**2 * dc else: raise ValueError("Unknown norm type {}".format(str(norm_type))) else: raise TypeError("Do not know how to compute norm of {}".format(str(v))) # Assemble value and return return sqrt(assemble_func(M))
def project(v, V=None, bcs=None, mesh=None, solver_type="cg", preconditioner_type="default", form_compiler_parameters=None): """ Return projection of given expression *v* onto the finite element space *V*. *Arguments* v a :py:class:`Function <dolfin.functions.function.Function>` or an :py:class:`Expression <dolfin.functions.expression.Expression>` bcs Optional argument :py:class:`list of BoundaryCondition <dolfin.fem.bcs.BoundaryCondition>` V Optional argument :py:class:`FunctionSpace <dolfin.functions.functionspace.FunctionSpace>` mesh Optional argument :py:class:`mesh <dolfin.cpp.Mesh>`. solver_type see :py:func:`solve <dolfin.fem.solving.solve>` for options. preconditioner_type see :py:func:`solve <dolfin.fem.solving.solve>` for options. form_compiler_parameters see :py:class:`Parameters <dolfin.cpp.Parameters>` for more information. *Example of usage* .. code-block:: python v = Expression("sin(pi*x[0])") V = FunctionSpace(mesh, "Lagrange", 1) Pv = project(v, V) This is useful for post-processing functions or expressions which are not readily handled by visualization tools (such as for example discontinuous functions). """ # If trying to project an Expression if V is None and isinstance(v, Expression): if mesh is not None and isinstance(mesh, cpp.Mesh): V = FunctionSpaceBase(mesh, v.ufl_element()) else: raise TypeError, "expected a mesh when projecting an Expression" # Try extracting function space if not specified if V is None: V = _extract_function_space(v, mesh) # Check arguments if not isinstance(V, FunctionSpaceBase): cpp.dolfin_error("projection.py", "compute projection", "Illegal function space for projection, not a FunctionSpace: " + str(v)) # Define variational problem for projection w = TestFunction(V) Pv = TrialFunction(V) a = ufl.inner(w, Pv)*ufl.dx() L = ufl.inner(w, v)*ufl.dx() # Assemble linear system A, b = assemble_system(a, L, bcs=bcs, form_compiler_parameters=form_compiler_parameters) # Solve linear system for projection Pv = Function(V) cpp.la_solve(A, Pv.vector(), b, solver_type, preconditioner_type) return Pv
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) # TODO check proper use of watches self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space if self.bc == 'lagrange': L = FunctionSpace(mesh, "R", 0) QL = self.Q*L problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) if self.bc == 'lagrange': (pQL, rQL) = TrialFunction(QL) (qQL, lQL) = TestFunction(QL) else: p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt}, {'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) if doSave: problem.save_vel(False, u0, 0.0) problem.save_vel(True, u0, 0.0) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity if self.bc == 'lagrange': p_QL = Function(QL) # current pressure or pressure help function from rotation scheme pQ = Function(self.Q) # auxiliary function for conversion between QL.sub(0) and Q else: p_ = Function(self.Q) # current pressure or pressure help function from rotation scheme p_mod = Function(self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5*u0 - 0.5*u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) # CBC delta: if self.cbcDelta: delta = Constant(self.stabCoef)*h/(sqrt(inner(u_ext, u_ext))+h) else: delta = Constant(self.stabCoef)*h**2/(2*nu*k + k*h*inner(u_ext, u_ext)+h**2) if self.use_full_SUPG: v1 = v + delta*0.5*k*dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v def nonlinearity(function): if self.use_ema: return 2*inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function)*u_ext, v1) * dx # return 2*inner(dot(sym(grad(function)), u_ext), v) * dx + inner(div(u_ext)*function, v) * dx # QQ implement this way? else: return inner(dot(grad(function), u_ext), v1) * dx def diffusion(fce): if self.useLaplace: return nu*inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form if self.bcv == 'LAP': return form - inner(nu*dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form() if self.bcv == 'DDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form # additional term must be added to non-constant part def pressure_rhs(): if self.useLaplace or self.bcv == 'LAP': return inner(p0, div(v1)) * dx - inner(p0*n, v1) * problem.get_outflow_measure_form() # NT term inner(inner(p, n), v) is 0 when p=0 on outflow else: return inner(p0, div(v1)) * dx a1_const = (1./k)*inner(u, v1)*dx + diffusion(0.5*u) a1_change = nonlinearity(0.5*u) if self.bcv == 'DDN': # IMP Problem: Does not penalize influx for current step, only for the next one # IMP this can lead to oscilation: DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5*min_value(Constant(0.), inner(u_ext, n))*inner(u, v1)*problem.get_outflow_measure_form() # IMP works only with uflacs compiler L1 = (1./k)*inner(u0, v1)*dx - nonlinearity(0.5*u0) - diffusion(0.5*u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5*min_value(0., inner(u_ext, n))*inner(u0, v1)*problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5*delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) # NT optional: use Crank Nicolson in stabilisation term: change RHS # L1 += -0.5*delta*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F2 = inner(grad(pQL), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: F2 = inner(grad(p), grad(q))*dx + (1./k)*q*div(u_)*dx else: # Projection, solve to p_ if self.bc == 'lagrange': F2 = inner(grad(pQL - p0), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx for m in problem.get_outflow_measures(): F2 += (1./k)*(1./outflow_area)*need_outflow*q*m else: F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0)), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_), v)*dx else: if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0) - p0), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_ - p0), v)*dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure if self.bc == 'lagrange': pr = TrialFunction(self.Q) qr = TestFunction(self.Q) F4 = (pr - p0 - p_QL.sub(0) + nu*div(u_))*qr*dx else: F4 = (p - p0 - p_ + nu*div(u_))*q*dx # TODO zkusit, jestli to nebude rychlejsi? nepocitat soustavu, ale p.assign(...), nutno project(div(u),Q) coz je pocitani podobne soustavy # TODO zkusit v project zadat solver_type='lu' >> primy resic by mel byt efektivnejsi a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble(a1_const) # need to be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('umfpack') if self.useRotationScheme: self.solver_rot = LUSolver('umfpack') else: # NT not needed, chosen not to use hypre_parasails # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = KrylovSolver('gmres', self.prec_v) # nonsymetric > gmres # IMP cannot use 'ilu' in parallel (choose different default option) self.solver_vel_cor = KrylovSolver('cg', 'hypre_amg') # nonsymetric > gmres self.solver_p = KrylovSolver('cg', self.prec_p) # symmetric > CG if self.useRotationScheme: self.solver_rot = KrylovSolver('cg', self.prec_p) solver_options = {'monitor_convergence': True, 'maximum_iterations': 1000, 'nonzero_initial_guess': True} # 'nonzero_initial_guess': True with solver.solbe(A, u, b) means that # Solver will use anything stored in u as an initial guess # Get the nullspace if there are no pressure boundary conditions foo = Function(self.Q) # auxiliary vector for setting pressure nullspace if self.bc in ['nullspace', 'nullspace_s']: null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0/null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) if self.bc == 'nullspace': as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.precision_rel_v_tent) self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.precision_abs_v_tent) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_p.parameters['absolute_tolerance'] = 10E-10 if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.solvers == 'krylov': for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_p, self.solver_rot] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 solver.parameters['preconditioner']['structure'] = 'same' # matrices A2-A4 do not change, so we can reuse preconditioners self.solver_vel_tent.parameters['preconditioner']['structure'] = 'same_nonzero_pattern' # matrix A1 changes every time step, so change of preconditioner must be allowed if self.bc == 'lagrange': fa = FunctionAssigner(self.Q, QL.sub(0)) # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt/2.0): info("t = %f" % t) self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # DDN debug # u_ext_in = assemble(inner(u_ext, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_ext, n))*problem.get_outflow_measure_form()) # print('DDN: u_ext*n dSout = ', u_ext_in) # print('DDN: negative part of u_ext*n dSout = ', DDN_triggered) # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble(a1_change, tensor=A1_change) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble(a1_stab, tensor=A1_stab) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() # DDN debug # u_ext_in = assemble(inner(u_, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_, n))*problem.get_outflow_measure_form()) # print('DDN: u_tent*n dSout = ', u_ext_in) # print('DDN: negative part of u_tent*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow-out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.bc in ['nullspace', 'nullspace_s']: self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') if self.bc == 'lagrange': self.solver_p.solve(A2, p_QL.vector(), b) else: self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) foo.assign(pQ + p0) else: foo.assign(p_+p0) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(True, foo) else: if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) problem.averaging_pressure(pQ) if save_this_step and not onlyVel: problem.save_pressure(False, pQ) else: # we do not want to change p=0 on outflow, it conflicts with do-nothing conditions foo = Function(self.Q) foo.assign(p_) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() # DDN debug # u_ext_in = assemble(inner(u_cor, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_cor, n))*problem.get_outflow_measure_form()) # print('DDN: u_cor*n dSout = ', u_ext_in) # print('DDN: negative part of u_cor*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 problem.averaging_pressure(p_mod) if save_this_step and not onlyVel: problem.save_pressure(False, p_mod) end() # compute functionals (e. g. forces) problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else (pQ if self.bc == 'lagrange' else p_), t) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign(u_cor) # use corretced velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: if self.bc == 'lagrange': p0.assign(pQ) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0