def stokes(self): P2 = VectorElement("CG", self.mesh.ufl_cell(), 2) P1 = FiniteElement("CG", self.mesh.ufl_cell(), 1) TH = P2 * P1 VQ = FunctionSpace(self.mesh, TH) mf = self.mf self.no_slip = Constant((0., 0)) self.topflow = Expression(("-x[0] * (x[0] - 1.0) * 6.0 * m", "0.0"), m=self.U_m, degree=2) bc0 = DirichletBC(VQ.sub(0), self.topflow, mf, self.bc_dict["top"]) bc1 = DirichletBC(VQ.sub(0), self.no_slip, mf, self.bc_dict["left"]) bc2 = DirichletBC(VQ.sub(0), self.no_slip, mf, self.bc_dict["bottom"]) bc3 = DirichletBC(VQ.sub(0), self.no_slip, mf, self.bc_dict["right"]) # bc4 = DirichletBC(VQ.sub(1), Constant(0), mf, self.bc_dict["top"]) bcs = [bc0, bc1, bc2, bc3] vup = TestFunction(VQ) up = TrialFunction(VQ) # the solution will be in here: up_ = Function(VQ) u, p = split(up) # Trial vu, vp = split(vup) # Test u_, p_ = split(up_) # Function holding the solution F = self.mu*inner(grad(vu), grad(u))*dx - inner(div(vu), p)*dx \ - inner(vp, div(u))*dx + dot(self.g*self.rho, vu)*dx solve(lhs(F) == rhs(F), up_, bcs=bcs) self.u_.assign(project(u_, self.V)) self.p_.assign(project(p_, self.Q)) return
def test_clear_sub_map_data_vector(mesh): mesh = UnitSquareMesh(8, 8) P1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1) W = FunctionSpace(mesh, P1 * P1) # Check block size assert W.dofmap().index_map.block_size == 2 W.dofmap().clear_sub_map_data() with pytest.raises(RuntimeError): W0 = W.sub(0) assert (W0) with pytest.raises(RuntimeError): W1 = W.sub(1) assert (W1)
def test_tabulate_dofs(mesh_factory): func, args = mesh_factory mesh = func(*args) W0 = FiniteElement("Lagrange", mesh.ufl_cell(), 1) W1 = VectorElement("Lagrange", mesh.ufl_cell(), 1) W = FunctionSpace(mesh, W0 * W1) L0 = W.sub(0) L1 = W.sub(1) L01 = L1.sub(0) L11 = L1.sub(1) for i, cell in enumerate(Cells(mesh)): dofs0 = L0.dofmap().cell_dofs(cell.index()) dofs1 = L01.dofmap().cell_dofs(cell.index()) dofs2 = L11.dofmap().cell_dofs(cell.index()) dofs3 = L1.dofmap().cell_dofs(cell.index()) assert np.array_equal(dofs0, L0.dofmap().cell_dofs(i)) assert np.array_equal(dofs1, L01.dofmap().cell_dofs(i)) assert np.array_equal(dofs2, L11.dofmap().cell_dofs(i)) assert np.array_equal(dofs3, L1.dofmap().cell_dofs(i)) assert len(np.intersect1d(dofs0, dofs1)) == 0 assert len(np.intersect1d(dofs0, dofs2)) == 0 assert len(np.intersect1d(dofs1, dofs2)) == 0 assert np.array_equal(np.append(dofs1, dofs2), dofs3)
def compute_error(problem, mesh_size): mesh = problem.mesh_generator(mesh_size) u = problem.solution['u'] u_sol = Expression((ccode(u['value'][0]), ccode(u['value'][1])), degree=u['degree']) p = problem.solution['p'] p_sol = Expression(ccode(p['value']), degree=p['degree']) f = Expression( (ccode(problem.f['value'][0]), ccode(problem.f['value'][1])), degree=problem.f['degree']) W = VectorElement('Lagrange', mesh.ufl_cell(), 2) P = FiniteElement('Lagrange', mesh.ufl_cell(), 1) WP = FunctionSpace(mesh, W * P) # Get Dirichlet boundary conditions u_bcs = DirichletBC(WP.sub(0), u_sol, 'on_boundary') p_bcs = DirichletBC(WP.sub(1), p_sol, 'on_boundary') u_approx, p_approx = flow.stokes.solve(WP, bcs=[u_bcs, p_bcs], mu=problem.mu, f=f, verbose=True, tol=1.0e-12) # compute errors u_error = errornorm(u_sol, u_approx) p_error = errornorm(p_sol, p_approx) return mesh.hmax(), u_error, p_error
def generate_collapsed_bilinear_form_space(mesh): element_0 = VectorElement("Lagrange", mesh.ufl_cell(), 2) element_1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1) element = MixedElement(element_0, element_1) U = FunctionSpace(mesh, element) V = U.sub(0).collapse() return (V, U)
def CollapsedFunctionSpaces(mesh): element_0 = VectorElement("Lagrange", mesh.ufl_cell(), 2, dim=2) element_1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1) element = MixedElement(element_0, element_1) U = FunctionSpace(mesh, element) V = U.sub(0).collapse() return (V, U)
def _test_eigen_solver_sparse(callback_type): from rbnics.backends.dolfin import EigenSolver # Define mesh mesh = UnitSquareMesh(10, 10) # Define function space V_element = VectorElement("Lagrange", mesh.ufl_cell(), 2) Q_element = FiniteElement("Lagrange", mesh.ufl_cell(), 1) W_element = MixedElement(V_element, Q_element) W = FunctionSpace(mesh, W_element) # Create boundaries class Wall(SubDomain): def inside(self, x, on_boundary): return on_boundary and (x[1] < 0 + DOLFIN_EPS or x[1] > 1 - DOLFIN_EPS) boundaries = MeshFunction("size_t", mesh, mesh.topology().dim() - 1) boundaries.set_all(0) wall = Wall() wall.mark(boundaries, 1) # Define variational problem vq = TestFunction(W) (v, q) = split(vq) up = TrialFunction(W) (u, p) = split(up) lhs = inner(grad(u), grad(v)) * dx - div(v) * p * dx - div(u) * q * dx rhs = -inner(p, q) * dx # Define boundary condition bc = [DirichletBC(W.sub(0), Constant((0., 0.)), boundaries, 1)] # Define eigensolver depending on callback type assert callback_type in ("form callbacks", "tensor callbacks") if callback_type == "form callbacks": solver = EigenSolver(W, lhs, rhs, bc) elif callback_type == "tensor callbacks": LHS = assemble(lhs) RHS = assemble(rhs) solver = EigenSolver(W, LHS, RHS, bc) # Solve the eigenproblem solver.set_parameters({ "linear_solver": "mumps", "problem_type": "gen_non_hermitian", "spectrum": "target real", "spectral_transform": "shift-and-invert", "spectral_shift": 1.e-5 }) solver.solve(1) r, c = solver.get_eigenvalue(0) assert abs(c) < 1.e-10 assert r > 0., "r = " + str(r) + " is not positive" print("Sparse inf-sup constant: ", sqrt(r)) return (sqrt(r), solver.condensed_A, solver.condensed_B)
def get_function_subspace(function_space: FunctionSpace, component: list_of(str)): assert len(set([function_space.component_to_index(c) for c in component])) == 1 output = function_space.sub(component[0]).collapse() output._component_to_index.clear() for c in component: output._component_to_index[c] = None output._index_to_components.clear() output._index_to_components[None] = component return output
def test_tabulate_dofs_periodic(mesh_factory): class PeriodicBoundary2(SubDomain): def inside(self, x, on_boundary): return x[0] < np.finfo(float).eps def map(self, x, y): y[0] = x[0] - 1.0 y[1] = x[1] func, args = mesh_factory mesh = func(*args) # Create periodic boundary periodic_boundary = PeriodicBoundary2() V = FiniteElement("Lagrange", mesh.ufl_cell(), 2) Q = VectorElement("Lagrange", mesh.ufl_cell(), 2) W = V * Q V = FunctionSpace(mesh, V, constrained_domain=periodic_boundary) Q = FunctionSpace(mesh, Q, constrained_domain=periodic_boundary) W = FunctionSpace(mesh, W, constrained_domain=periodic_boundary) L0 = W.sub(0) L1 = W.sub(1) L01 = L1.sub(0) L11 = L1.sub(1) # Check dimensions assert V.dim == 110 assert Q.dim == 220 assert L0.dim == V.dim assert L1.dim == Q.dim assert L01.dim == V.dim assert L11.dim == V.dim for i, cell in enumerate(Cells(mesh)): dofs0 = L0.dofmap().cell_dofs(cell.index()) dofs1 = L01.dofmap().cell_dofs(cell.index()) dofs2 = L11.dofmap().cell_dofs(cell.index()) dofs3 = L1.dofmap().cell_dofs(cell.index()) assert np.array_equal(dofs0, L0.dofmap().cell_dofs(i)) assert np.array_equal(dofs1, L01.dofmap().cell_dofs(i)) assert np.array_equal(dofs2, L11.dofmap().cell_dofs(i)) assert np.array_equal(dofs3, L1.dofmap().cell_dofs(i)) assert len(np.intersect1d(dofs0, dofs1)) == 0 assert len(np.intersect1d(dofs0, dofs2)) == 0 assert len(np.intersect1d(dofs1, dofs2)) == 0 assert np.array_equal(np.append(dofs1, dofs2), dofs3)
def test_tabulate_coord_periodic(mesh_factory): class PeriodicBoundary2(SubDomain): def inside(self, x, on_boundary): return x[0] < np.finfo(float).eps def map(self, x, y): y[0] = x[0] - 1.0 y[1] = x[1] # Create periodic boundary condition periodic_boundary = PeriodicBoundary2() func, args = mesh_factory mesh = func(*args) V = FiniteElement("Lagrange", mesh.ufl_cell(), 1) Q = VectorElement("Lagrange", mesh.ufl_cell(), 1) W = V * Q V = FunctionSpace(mesh, V, constrained_domain=periodic_boundary) W = FunctionSpace(mesh, W, constrained_domain=periodic_boundary) L0 = W.sub(0) L1 = W.sub(1) L01 = L1.sub(0) L11 = L1.sub(1) sdim = V.element().space_dimension() coord0 = np.zeros((sdim, 2), dtype="d") coord1 = np.zeros((sdim, 2), dtype="d") coord2 = np.zeros((sdim, 2), dtype="d") coord3 = np.zeros((sdim, 2), dtype="d") for cell in Cells(mesh): coord0 = V.element().tabulate_dof_coordinates(cell) coord1 = L0.element().tabulate_dof_coordinates(cell) coord2 = L01.element().tabulate_dof_coordinates(cell) coord3 = L11.element().tabulate_dof_coordinates(cell) coord4 = L1.element().tabulate_dof_coordinates(cell) assert (coord0 == coord1).all() assert (coord0 == coord2).all() assert (coord0 == coord3).all() assert (coord4[:sdim] == coord0).all() assert (coord4[sdim:] == coord0).all()
def test_tabulate_coord_periodic(mesh_factory): def periodic_boundary(x): return x[0] < np.finfo(float).eps func, args = mesh_factory mesh = func(*args) V = FiniteElement("Lagrange", mesh.ufl_cell(), 1) Q = VectorElement("Lagrange", mesh.ufl_cell(), 1) W = V * Q V = FunctionSpace(mesh, V, constrained_domain=periodic_boundary) W = FunctionSpace(mesh, W, constrained_domain=periodic_boundary) L0 = W.sub(0) L1 = W.sub(1) L01 = L1.sub(0) L11 = L1.sub(1) sdim = V.element.space_dimension() coord0 = np.zeros((sdim, 2), dtype="d") coord1 = np.zeros((sdim, 2), dtype="d") coord2 = np.zeros((sdim, 2), dtype="d") coord3 = np.zeros((sdim, 2), dtype="d") for i in range(mesh.num_cells()): cell = MeshEntity(mesh, mesh.topology.dim, i) coord0 = V.element.tabulate_dof_coordinates(cell) coord1 = L0.element.tabulate_dof_coordinates(cell) coord2 = L01.element.tabulate_dof_coordinates(cell) coord3 = L11.element.tabulate_dof_coordinates(cell) coord4 = L1.element.tabulate_dof_coordinates(cell) assert (coord0 == coord1).all() assert (coord0 == coord2).all() assert (coord0 == coord3).all() assert (coord4[:sdim] == coord0).all() assert (coord4[sdim:] == coord0).all()
def test_clear_sub_map_data_scalar(mesh): V = FunctionSpace(mesh, ("CG", 2)) with pytest.raises(ValueError): V.sub(1) V = VectorFunctionSpace(mesh, ("CG", 2)) V1 = V.sub(1) assert (V1) # Clean sub-map data V.dofmap().clear_sub_map_data() # Can still get previously computed map V1 = V.sub(1) # New sub-map should throw an error with pytest.raises(RuntimeError): V.sub(0)
TH = P2 * P1 W = FunctionSpace(mesh, TH) # The mixed finite element space is known as Taylor–Hood. # It is a stable, standard element pair for the Stokes # equations. Now we can define boundary conditions:: # Extract subdomain facet arrays mf = sub_domains.values mf0 = np.where(mf == 0) mf1 = np.where(mf == 1) # No-slip boundary condition for velocity # x1 = 0, x1 = 1 and around the dolphin noslip = Function(W.sub(0).collapse()) noslip.interpolate(lambda x: np.zeros((x.shape[0], 2))) bc0 = DirichletBC(W.sub(0), noslip, mf0[0]) # Inflow boundary condition for velocity # x0 = 1 def inflow_eval(x): values = np.zeros((x.shape[0], 2)) values[:, 0] = - np.sin(x[:, 1] * np.pi) return values inflow = Function(W.sub(0).collapse())
# x1 = 0, x1 = 1 and around the dolphin @function.expression.numba_eval def noslip_eval(values, x, cell): values[:, 0] = 0.0 values[:, 1] = 0.0 # Extract subdomain facet arrays mf = sub_domains.array() mf0 = np.where(mf == 0) mf1 = np.where(mf == 1) noslip_expr = Expression(noslip_eval, shape=(2, )) noslip = interpolate(noslip_expr, W.sub(0).collapse()) bc0 = DirichletBC(W.sub(0), noslip, mf0[0]) # Inflow boundary condition for velocity # x0 = 1 @function.expression.numba_eval def inflow_eval(values, x, cell): values[:, 0] = -np.sin(x[:, 1] * np.pi) values[:, 1] = 0.0 inflow_expr = Expression(inflow_eval, shape=(2, )) inflow = interpolate(inflow_expr, W.sub(0).collapse()) bc1 = DirichletBC(W.sub(0), inflow, mf1[0])
def get_function_subspace(function_space: FunctionSpace, component: (int, str)): return function_space.sub(component).collapse()
class Ball_in_tube(object): def __init__(self): # https://fenicsproject.org/qa/12891/initialize-mesh-from-vertices-connectivities-at-once points, cells, _, cell_data, _ = meshes.ball_in_tube_cyl.generate() # 2018.1 # self.mesh = Mesh( # dolfin.mpi_comm_world(), dolfin.cpp.mesh.CellType.Type_triangle, # points[:, :2], cells['triangle'] # ) with TemporaryDirectory() as temp_dir: tmp_filename = os.path.join(temp_dir, "test.xml") meshio.write_points_cells( tmp_filename, points, cells, cell_data=cell_data, file_format="dolfin-xml", ) self.mesh = Mesh(tmp_filename) V0_element = FiniteElement("CG", self.mesh.ufl_cell(), 2) V1_element = FiniteElement("B", self.mesh.ufl_cell(), 3) self.W = FunctionSpace(self.mesh, V0_element * V1_element) self.P = FunctionSpace(self.mesh, "CG", 1) # Define mesh and boundaries. class LeftBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): return on_boundary and x[0] < GMSH_EPS left_boundary = LeftBoundary() class RightBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): return on_boundary and x[0] > 1.0 - GMSH_EPS right_boundary = RightBoundary() class LowerBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): return on_boundary and x[1] < GMSH_EPS lower_boundary = LowerBoundary() # class UpperBoundary(SubDomain): # # pylint: disable=no-self-use # def inside(self, x, on_boundary): # return on_boundary and x[1] > 5.0-GMSH_EPS class CoilBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): # One has to pay a little bit of attention when defining the # coil boundary; it's easy to miss the edges closest to x[0]=0. return (on_boundary and x[1] > 1.0 - GMSH_EPS and x[1] < 2.0 + GMSH_EPS and x[0] < 1.0 - GMSH_EPS) coil_boundary = CoilBoundary() self.u_bcs = [ DirichletBC(self.W, (0.0, 0.0), right_boundary), DirichletBC(self.W.sub(0), 0.0, left_boundary), DirichletBC(self.W, (0.0, 0.0), lower_boundary), DirichletBC(self.W, (0.0, 0.0), coil_boundary), ] self.p_bcs = [] # self.p_bcs = [DirichletBC(Q, 0.0, upper_boundary)] return
class Ball_in_tube(object): def __init__(self): # https://fenicsproject.org/qa/12891/initialize-mesh-from-vertices-connectivities-at-once points, cells, _, cell_data, _ = meshes.ball_in_tube_cyl.generate() # 2018.1 # self.mesh = Mesh( # dolfin.mpi_comm_world(), dolfin.cpp.mesh.CellType.Type_triangle, # points[:, :2], cells['triangle'] # ) with TemporaryDirectory() as temp_dir: tmp_filename = os.path.join(temp_dir, "test.xml") meshio.write_points_cells( tmp_filename, points, cells, cell_data=cell_data, file_format="dolfin-xml", ) self.mesh = Mesh(tmp_filename) V0_element = FiniteElement("CG", self.mesh.ufl_cell(), 2) V1_element = FiniteElement("B", self.mesh.ufl_cell(), 3) self.W = FunctionSpace(self.mesh, V0_element * V1_element) self.P = FunctionSpace(self.mesh, "CG", 1) # Define mesh and boundaries. class LeftBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): return on_boundary and x[0] < GMSH_EPS left_boundary = LeftBoundary() class RightBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): return on_boundary and x[0] > 1.0 - GMSH_EPS right_boundary = RightBoundary() class LowerBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): return on_boundary and x[1] < GMSH_EPS lower_boundary = LowerBoundary() # class UpperBoundary(SubDomain): # # pylint: disable=no-self-use # def inside(self, x, on_boundary): # return on_boundary and x[1] > 5.0-GMSH_EPS class CoilBoundary(SubDomain): # pylint: disable=no-self-use def inside(self, x, on_boundary): # One has to pay a little bit of attention when defining the # coil boundary; it's easy to miss the edges closest to x[0]=0. return ( on_boundary and x[1] > 1.0 - GMSH_EPS and x[1] < 2.0 + GMSH_EPS and x[0] < 1.0 - GMSH_EPS ) coil_boundary = CoilBoundary() self.u_bcs = [ DirichletBC(self.W, (0.0, 0.0), right_boundary), DirichletBC(self.W.sub(0), 0.0, left_boundary), DirichletBC(self.W, (0.0, 0.0), lower_boundary), DirichletBC(self.W, (0.0, 0.0), coil_boundary), ] self.p_bcs = [] # self.p_bcs = [DirichletBC(Q, 0.0, upper_boundary)] return
def test_steady_stokes(k): # Polynomial order and mesh resolution nx_list = [4, 8, 16] nu = Constant(1) if comm.Get_rank() == 0: print('{:=^72}'.format('Computing for polynomial order ' + str(k))) # Error listst error_u, error_p, error_div = [], [], [] for nx in nx_list: if comm.Get_rank() == 0: print('# Resolution ' + str(nx)) mesh = UnitSquareMesh(nx, nx) # Get forcing from exact solutions u_exact, p_exact = exact_solution(mesh) f = div(p_exact * Identity(2) - 2 * nu * sym(grad(u_exact))) # Define FunctionSpaces and functions V = VectorElement("DG", mesh.ufl_cell(), k) Q = FiniteElement("DG", mesh.ufl_cell(), k - 1) Vbar = VectorElement("DGT", mesh.ufl_cell(), k) Qbar = FiniteElement("DGT", mesh.ufl_cell(), k) mixedL = FunctionSpace(mesh, MixedElement([V, Q])) mixedG = FunctionSpace(mesh, MixedElement([Vbar, Qbar])) Uh = Function(mixedL) Uhbar = Function(mixedG) # Set forms alpha = Constant(6 * k * k) forms_stokes = FormsStokes(mesh, mixedL, mixedG, alpha).forms_steady(nu, f) # No-slip boundary conditions, set pressure in one of the corners bc0 = DirichletBC(mixedG.sub(0), Constant((0, 0)), Gamma) bc1 = DirichletBC(mixedG.sub(1), Constant(0), Corner, "pointwise") bcs = [bc0, bc1] # Initialize static condensation class ssc = StokesStaticCondensation(mesh, forms_stokes['A_S'], forms_stokes['G_S'], forms_stokes['B_S'], forms_stokes['Q_S'], forms_stokes['S_S'], bcs) # Assemble global system and incorporates bcs ssc.assemble_global_system(True) # Solve using mumps ssc.solve_problem(Uhbar, Uh, "mumps", "default") # Compute velocity/pressure/local div error uh, ph = Uh.split() e_u = np.sqrt(np.abs(assemble(dot(uh - u_exact, uh - u_exact) * dx))) e_p = np.sqrt(np.abs(assemble((ph - p_exact) * (ph - p_exact) * dx))) e_d = np.sqrt(np.abs(assemble(div(uh) * div(uh) * dx))) if comm.rank == 0: error_u.append(e_u) error_p.append(e_p) error_div.append(e_d) print('Error in velocity ' + str(error_u[-1])) print('Error in pressure ' + str(error_p[-1])) print('Local mass error ' + str(error_div[-1])) if comm.rank == 0: iterator_list = [1. / float(nx) for nx in nx_list] conv_u = compute_convergence(iterator_list, error_u) conv_p = compute_convergence(iterator_list, error_p) assert any(conv > k + 0.75 for conv in conv_u) assert any(conv > (k - 1) + 0.75 for conv in conv_p)
a = (nu*inner(grad(u), grad(v)) - div(v)*p + q*div(u))*dx # Body force - Linear form f = Constant((0, 0)) Lf = inner(f, v)*dx # Neumann boundary condition - Linear form L_neumann = dot(-Constant(P_inlet)*n,v) * ds(mark["inlet"]) + \ dot(-Constant(P_outlet)*n,v) * ds(mark["outlet"]) L= Lf + L_neumann # Dirichlet boundary condition noslip = Constant(zero_vec) bc_wall = DirichletBC(W.sub(0), noslip, boundaries, mark["wall"]) bcs = [bc_wall] # Compute solution w = Function(W) solve(a == L, w, bcs) # Split the mixed solution using deepcopy # (needed for further computation on coefficient vector) (u, p) = w.split(True) # # Split the mixed solution using a shallow copy (u, p) = w.split()
class Lid_driven_cavity(object): def __init__(self): n = 40 self.mesh = UnitSquareMesh(n, n, "crossed") # Define mesh and boundaries. class LeftBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] < DOLFIN_EPS class RightBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] > 1.0 - DOLFIN_EPS class LowerBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] < DOLFIN_EPS class UpperBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] > 1.0 - DOLFIN_EPS class RestrictedUpperBoundary(SubDomain): def inside(self, x, on_boundary): return (on_boundary and x[1] > 1.0 - DOLFIN_EPS and DOLFIN_EPS < x[0] and x[0] < 0.5 - DOLFIN_EPS) left = LeftBoundary() right = RightBoundary() lower = LowerBoundary() upper = UpperBoundary() # restricted_upper = RestrictedUpperBoundary() # Be particularly careful with the boundary conditions. # The main problem here is that the PPE system is consistent if and # only if # # \int_\Omega div(u) = \int_\Gamma n.u = 0. # # This is exactly and even pointwise fulfilled for the continuous # problem. In the discrete case, we can have to make sure that n.u is # 0 all along the boundary. # In the lid-driven cavity problem, of particular interest are the # corner points at the lid. One has to assert that the z-component of u # is 0 all across the lid, and the x-component of u is 0 everywhere but # the lid. Since u is L2-"continuous", the lid condition on u_x must # not be enforced in the corner points. The u_y component must be # enforced all over the lid, including the end points. V_element = FiniteElement("CG", self.mesh.ufl_cell(), 2) self.W = FunctionSpace(self.mesh, V_element * V_element) self.u_bcs = [ DirichletBC(self.W, (0.0, 0.0), left), DirichletBC(self.W, (0.0, 0.0), right), # DirichletBC(self.W.sub(0), Expression('x[0]'), restricted_upper), DirichletBC(self.W, (0.0, 0.0), lower), DirichletBC(self.W.sub(0), Constant("1.0"), upper), DirichletBC(self.W.sub(1), 0.0, upper), # DirichletBC(self.W.sub(0), Constant('-1.0'), lower), # DirichletBC(self.W.sub(1), 0.0, lower), # DirichletBC(self.W.sub(1), Constant('1.0'), left), # DirichletBC(self.W.sub(0), 0.0, left), # DirichletBC(self.W.sub(1), Constant('-1.0'), right), # DirichletBC(self.W.sub(0), 0.0, right), ] self.P = FunctionSpace(self.mesh, "CG", 1) self.p_bcs = [] return
def run_with_params(Tb, mu_value, k_s, path): run_time_init = clock() mesh = BoxMesh(Point(0.0, 0.0, 0.0), Point(mesh_width, mesh_width, mesh_height), nx, ny, nz) pbc = PeriodicBoundary() WE = VectorElement('CG', mesh.ufl_cell(), 2) SE = FiniteElement('CG', mesh.ufl_cell(), 1) WSSS = FunctionSpace(mesh, MixedElement(WE, SE, SE, SE), constrained_domain=pbc) # W = FunctionSpace(mesh, WE, constrained_domain=pbc) # S = FunctionSpace(mesh, SE, constrained_domain=pbc) W = WSSS.sub(0).collapse() S = WSSS.sub(1).collapse() temperature_vals = [27.0 + 273, Tb + 273, 1300.0 + 273, 1305.0 + 273] temp_prof = TemperatureProfile(temperature_vals, element=S.ufl_element()) mu_a = mu_value # this was taken from the Blankenbach paper, can change Ep = b / temp_prof.delta mu_bot = exp(-Ep * (temp_prof.bottom * temp_prof.delta - 1573.0) + cc) * mu_a # TODO: verify exponentiation Ra = rho_0 * alpha * g * temp_prof.delta * h**3 / (kappa_0 * mu_a) w0 = rho_0 * alpha * g * temp_prof.delta * h**2 / mu_a tau = h / w0 p0 = mu_a * w0 / h log(mu_a, mu_bot, Ra, w0, p0) slip_vx = 1.6E-09 / w0 # Non-dimensional slip_velocity = Constant((slip_vx, 0.0, 0.0)) zero_slip = Constant((0.0, 0.0, 0.0)) time_step = 3.0E11 / tau * 2 dt = Constant(time_step) t_end = 3.0E15 / tau / 5.0 # Non-dimensional times u = Function(WSSS) # Instead of TrialFunctions, we use split(u) for our non-linear problem v, p, T, Tf = split(u) v_t, p_t, T_t, Tf_t = TestFunctions(WSSS) T0 = interpolate(temp_prof, S) mu_exp = Expression( 'exp(-Ep * (T_val * dTemp - 1573.0) + cc * x[2] / mesh_height)', Ep=Ep, dTemp=temp_prof.delta, cc=cc, mesh_height=mesh_height, T_val=T0, element=S.ufl_element()) Tf0 = interpolate(temp_prof, S) mu = Function(S) v0 = Function(W) v_theta = (1.0 - theta) * v0 + theta * v T_theta = (1.0 - theta) * T0 + theta * T Tf_theta = (1.0 - theta) * Tf0 + theta * Tf # TODO: Verify forms r_v = (inner(sym(grad(v_t)), 2.0 * mu * sym(grad(v))) - div(v_t) * p - T * v_t[2]) * dx r_p = p_t * div(v) * dx heat_transfer = Constant(k_s) * (Tf_theta - T_theta) * dt r_T = ( T_t * ((T - T0) + dt * inner(v_theta, grad(T_theta))) # TODO: Inner vs dot + (dt / Ra) * inner(grad(T_t), grad(T_theta)) - T_t * heat_transfer) * dx v_melt = Function(W) z_hat = Constant((0.0, 0.0, 1.0)) # TODO: inner -> dot, take out Tf_t r_Tf = (Tf_t * ((Tf - Tf0) + dt * inner(v_melt, grad(Tf_theta))) + Tf_t * heat_transfer) * dx r = r_v + r_p + r_T + r_Tf bcv0 = DirichletBC(WSSS.sub(0), zero_slip, top) bcv1 = DirichletBC(WSSS.sub(0), slip_velocity, bottom) bcv2 = DirichletBC(WSSS.sub(0).sub(1), Constant(0.0), back) bcv3 = DirichletBC(WSSS.sub(0).sub(1), Constant(0.0), front) bcp0 = DirichletBC(WSSS.sub(1), Constant(0.0), bottom) bct0 = DirichletBC(WSSS.sub(2), Constant(temp_prof.surface), top) bct1 = DirichletBC(WSSS.sub(2), Constant(temp_prof.bottom), bottom) bctf1 = DirichletBC(WSSS.sub(3), Constant(temp_prof.bottom), bottom) bcs = [bcv0, bcv1, bcv2, bcv3, bcp0, bct0, bct1, bctf1] t = 0 count = 0 files = DefaultDictByKey(partial(create_xdmf, path)) while t < t_end: mu.interpolate(mu_exp) rhosolid = rho_0 * (1.0 - alpha * (T0 * temp_prof.delta - 1573.0)) deltarho = rhosolid - rho_melt # TODO: project (accuracy) vs interpolate assign( v_melt, project( v0 - darcy * (grad(p) * p0 / h - deltarho * z_hat * g) / w0, W)) # TODO: Written out one step later? # v_melt.assign(v0 - darcy * (grad(p) * p0 / h - deltarho * yvec * g) / w0) # TODO: use nP after to avoid projection? solve(r == 0, u, bcs) nV, nP, nT, nTf = u.split() # TODO: write with Tf, ... etc if count % output_every == 0: time_left(count, t_end / time_step, run_time_init) # TODO: timestep vs dt # TODO: Make sure all writes are to the same function for each time step files['T_fluid'].write(nTf, t) files['p'].write(nP, t) files['v_solid'].write(nV, t) files['T_solid'].write(nT, t) files['mu'].write(mu, t) files['v_melt'].write(v_melt, t) files['gradp'].write(project(grad(nP), W), t) files['rho'].write(project(rhosolid, S), t) files['Tf_grad'].write(project(grad(Tf), W), t) files['advect'].write(project(dt * dot(v_melt, grad(nTf))), t) files['ht'].write(project(heat_transfer, S), t) assign(T0, nT) assign(v0, nV) assign(Tf0, nTf) t += time_step count += 1 log('Case mu={}, Tb={}, k={} complete. Run time = {:.2f} minutes'.format( mu_a, Tb, k_s, (clock() - run_time_init) / 60.0))
"0.1*t*exp(-((x[0]-0.7)*(x[0]-0.7) + (x[1]-0.5)*(x[1]-0.5))/0.01)", t=0, degree=1) # Define the right hand side for each of the PDEs F1 = (-inner(grad(U1), grad(q1)) + U2 * q1 + domainSource * q1) * dx F2 = (-inner(grad(U2), grad(q2)) - U1 * q2) * dx # Define Dirichlet boundary, zero on all edges. def boundary(x): return x[0] < DOLFIN_EPS or x[0] > 1.0 - DOLFIN_EPS or x[ 1] < DOLFIN_EPS or x[1] > 1.0 - DOLFIN_EPS bc_u = DirichletBC(ME.sub(0), 0.0, boundary) bc_v = DirichletBC(ME.sub(1), 0.0, boundary) # Define the time domain T = [0, 10] # Create the solver object and adjust tolerance obj = backwardEuler(T, W, [F1, F2], tdf=[domainSource], bcs=[bc_u, bc_v]) # Turn on some output and save run time statistics obj.parameters["verbose"] = True obj.parameters["drawplot"] = False obj.parameters["output"]["path"] = "CoupledHeatEquation_folder" obj.parameters["output"]["statistics"] = True obj.parameters["timestepping"]["dt"] = T[1] / 40.0
def run_with_params(Tb, mu_value, k_s, path): run_time_init = clock() mesh = BoxMesh(Point(0.0, 0.0, 0.0), Point(mesh_width, mesh_width, mesh_height), nx, ny, nz) pbc = PeriodicBoundary() WE = VectorElement('CG', mesh.ufl_cell(), 2) SE = FiniteElement('CG', mesh.ufl_cell(), 1) WSSS = FunctionSpace(mesh, MixedElement(WE, SE, SE, SE), constrained_domain=pbc) # W = FunctionSpace(mesh, WE, constrained_domain=pbc) # S = FunctionSpace(mesh, SE, constrained_domain=pbc) W = WSSS.sub(0).collapse() S = WSSS.sub(1).collapse() temperature_vals = [27.0 + 273, Tb + 273, 1300.0 + 273, 1305.0 + 273] temp_prof = TemperatureProfile(temperature_vals, element=S.ufl_element()) mu_a = mu_value # this was taken from the Blankenbach paper, can change Ep = b / temp_prof.delta mu_bot = exp(-Ep * (temp_prof.bottom * temp_prof.delta - 1573.0) + cc) * mu_a # TODO: verify exponentiation Ra = rho_0 * alpha * g * temp_prof.delta * h ** 3 / (kappa_0 * mu_a) w0 = rho_0 * alpha * g * temp_prof.delta * h ** 2 / mu_a tau = h / w0 p0 = mu_a * w0 / h log(mu_a, mu_bot, Ra, w0, p0) slip_vx = 1.6E-09 / w0 # Non-dimensional slip_velocity = Constant((slip_vx, 0.0, 0.0)) zero_slip = Constant((0.0, 0.0, 0.0)) time_step = 3.0E11 / tau * 2 dt = Constant(time_step) t_end = 3.0E15 / tau / 5.0 # Non-dimensional times u = Function(WSSS) # Instead of TrialFunctions, we use split(u) for our non-linear problem v, p, T, Tf = split(u) v_t, p_t, T_t, Tf_t = TestFunctions(WSSS) T0 = interpolate(temp_prof, S) mu_exp = Expression('exp(-Ep * (T_val * dTemp - 1573.0) + cc * x[2] / mesh_height)', Ep=Ep, dTemp=temp_prof.delta, cc=cc, mesh_height=mesh_height, T_val=T0, element=S.ufl_element()) Tf0 = interpolate(temp_prof, S) mu = Function(S) v0 = Function(W) v_theta = (1.0 - theta) * v0 + theta * v T_theta = (1.0 - theta) * T0 + theta * T Tf_theta = (1.0 - theta) * Tf0 + theta * Tf # TODO: Verify forms r_v = (inner(sym(grad(v_t)), 2.0 * mu * sym(grad(v))) - div(v_t) * p - T * v_t[2]) * dx r_p = p_t * div(v) * dx heat_transfer = Constant(k_s) * (Tf_theta - T_theta) * dt r_T = (T_t * ((T - T0) + dt * inner(v_theta, grad(T_theta))) # TODO: Inner vs dot + (dt / Ra) * inner(grad(T_t), grad(T_theta)) - T_t * heat_transfer) * dx v_melt = Function(W) z_hat = Constant((0.0, 0.0, 1.0)) # TODO: inner -> dot, take out Tf_t r_Tf = (Tf_t * ((Tf - Tf0) + dt * inner(v_melt, grad(Tf_theta))) + Tf_t * heat_transfer) * dx r = r_v + r_p + r_T + r_Tf bcv0 = DirichletBC(WSSS.sub(0), zero_slip, top) bcv1 = DirichletBC(WSSS.sub(0), slip_velocity, bottom) bcv2 = DirichletBC(WSSS.sub(0).sub(1), Constant(0.0), back) bcv3 = DirichletBC(WSSS.sub(0).sub(1), Constant(0.0), front) bcp0 = DirichletBC(WSSS.sub(1), Constant(0.0), bottom) bct0 = DirichletBC(WSSS.sub(2), Constant(temp_prof.surface), top) bct1 = DirichletBC(WSSS.sub(2), Constant(temp_prof.bottom), bottom) bctf1 = DirichletBC(WSSS.sub(3), Constant(temp_prof.bottom), bottom) bcs = [bcv0, bcv1, bcv2, bcv3, bcp0, bct0, bct1, bctf1] t = 0 count = 0 files = DefaultDictByKey(partial(create_xdmf, path)) while t < t_end: mu.interpolate(mu_exp) rhosolid = rho_0 * (1.0 - alpha * (T0 * temp_prof.delta - 1573.0)) deltarho = rhosolid - rho_melt # TODO: project (accuracy) vs interpolate assign(v_melt, project(v0 - darcy * (grad(p) * p0 / h - deltarho * z_hat * g) / w0, W)) # TODO: Written out one step later? # v_melt.assign(v0 - darcy * (grad(p) * p0 / h - deltarho * yvec * g) / w0) # TODO: use nP after to avoid projection? solve(r == 0, u, bcs) nV, nP, nT, nTf = u.split() # TODO: write with Tf, ... etc if count % output_every == 0: time_left(count, t_end / time_step, run_time_init) # TODO: timestep vs dt # TODO: Make sure all writes are to the same function for each time step files['T_fluid'].write(nTf, t) files['p'].write(nP, t) files['v_solid'].write(nV, t) files['T_solid'].write(nT, t) files['mu'].write(mu, t) files['v_melt'].write(v_melt, t) files['gradp'].write(project(grad(nP), W), t) files['rho'].write(project(rhosolid, S), t) files['Tf_grad'].write(project(grad(Tf), W), t) files['advect'].write(project(dt * dot(v_melt, grad(nTf))), t) files['ht'].write(project(heat_transfer, S), t) assign(T0, nT) assign(v0, nV) assign(Tf0, nTf) t += time_step count += 1 log('Case mu={}, Tb={}, k={} complete. Run time = {:.2f} minutes'.format(mu_a, Tb, k_s, (clock() - run_time_init) / 60.0))
class Lid_driven_cavity(object): def __init__(self): n = 40 self.mesh = UnitSquareMesh(n, n, "crossed") # Define mesh and boundaries. class LeftBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] < DOLFIN_EPS class RightBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] > 1.0 - DOLFIN_EPS class LowerBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] < DOLFIN_EPS class UpperBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] > 1.0 - DOLFIN_EPS class RestrictedUpperBoundary(SubDomain): def inside(self, x, on_boundary): return ( on_boundary and x[1] > 1.0 - DOLFIN_EPS and DOLFIN_EPS < x[0] and x[0] < 0.5 - DOLFIN_EPS ) left = LeftBoundary() right = RightBoundary() lower = LowerBoundary() upper = UpperBoundary() # restricted_upper = RestrictedUpperBoundary() # Be particularly careful with the boundary conditions. # The main problem here is that the PPE system is consistent if and # only if # # \int_\Omega div(u) = \int_\Gamma n.u = 0. # # This is exactly and even pointwise fulfilled for the continuous # problem. In the discrete case, we can have to make sure that n.u is # 0 all along the boundary. # In the lid-driven cavity problem, of particular interest are the # corner points at the lid. One has to assert that the z-component of u # is 0 all across the lid, and the x-component of u is 0 everywhere but # the lid. Since u is L2-"continuous", the lid condition on u_x must # not be enforced in the corner points. The u_y component must be # enforced all over the lid, including the end points. V_element = FiniteElement("CG", self.mesh.ufl_cell(), 2) self.W = FunctionSpace(self.mesh, V_element * V_element) self.u_bcs = [ DirichletBC(self.W, (0.0, 0.0), left), DirichletBC(self.W, (0.0, 0.0), right), # DirichletBC(self.W.sub(0), Expression('x[0]'), restricted_upper), DirichletBC(self.W, (0.0, 0.0), lower), DirichletBC(self.W.sub(0), Constant("1.0"), upper), DirichletBC(self.W.sub(1), 0.0, upper), # DirichletBC(self.W.sub(0), Constant('-1.0'), lower), # DirichletBC(self.W.sub(1), 0.0, lower), # DirichletBC(self.W.sub(1), Constant('1.0'), left), # DirichletBC(self.W.sub(0), 0.0, left), # DirichletBC(self.W.sub(1), Constant('-1.0'), right), # DirichletBC(self.W.sub(0), 0.0, right), ] self.P = FunctionSpace(self.mesh, "CG", 1) self.p_bcs = [] return
forms_pde["R_a"], forms_pde["S_a"], phi_bcs, property_idx, ) # Function spaces for Stokes mixedL = FunctionSpace(mesh, MixedElement([W_e_2, Q_E])) mixedG = FunctionSpace(mesh, MixedElement([Wbar_e_2_H12, Qbar_E])) U0, Uh = Function(mixedL), Function(mixedL) Uhbar = Function(mixedG) # BCs bcs = [ DirichletBC(mixedG.sub(0), Constant((0, 0)), "near(x[1], 0.0) or near(x[1], 1.0)"), DirichletBC( mixedG.sub(0).sub(0), Constant(0), CompiledSubDomain("near(x[0], 0.0) or near(x[0], lmbda)", lmbda=lmbda), ), ] # Forms Stokes alpha = Constant(6 * k * k) Rb = Constant(1.0) eta_top = Constant(1.0) eta_bottom = Constant(0.01) eta = eta_bottom + phi * (eta_top - eta_bottom) forms_stokes = FormsStokes(mesh, mixedL, mixedG,
forms_pde['N_a'], forms_pde['G_a'], forms_pde['L_a'], forms_pde['H_a'], forms_pde['B_a'], forms_pde['Q_a'], forms_pde['R_a'], forms_pde['S_a'], phi_bcs, property_idx) # Function spaces for Stokes mixedL = FunctionSpace(mesh, MixedElement([W_e_2, Q_E])) mixedG = FunctionSpace(mesh, MixedElement([Wbar_e_2_H12, Qbar_E])) U0, Uh = Function(mixedL), Function(mixedL) Uhbar = Function(mixedG) # BCs bcs = [DirichletBC(mixedG.sub(0), Constant((0, 0, 0.0)), "near(x[1], 0.0) or near(x[1], 1.0)"), DirichletBC(mixedG.sub(0).sub(0), Constant(0), CompiledSubDomain("near(x[0], 0.0) or near(x[0], lmbda)", lmbda=lmbdax)), DirichletBC(mixedG.sub(0).sub(2), Constant(0), CompiledSubDomain("near(x[2], 0.0) or near(x[2], lmbda)", lmbda=lmbdaz))] # Forms Stokes alpha = Constant(6*k*k) Rb = Constant(1.0) eta_top = Constant(1.0) eta_bottom = Constant(0.01) eta = eta_bottom + phi * (eta_top - eta_bottom) forms_stokes = FormsStokes(mesh, mixedL, mixedG, alpha) \ .forms_steady(eta, Rb * phi * Constant((0, -1, 0))) ssc = StokesStaticCondensation(mesh,
def test_karman(num_steps=2, lcar=0.1, show=False): mesh = create_mesh(lcar) W_element = VectorElement('Lagrange', mesh.ufl_cell(), 2) P_element = FiniteElement('Lagrange', mesh.ufl_cell(), 1) WP = FunctionSpace(mesh, W_element * P_element) W = WP.sub(0) # P = WP.sub(1) mesh_eps = 1.0e-12 # Define mesh and boundaries. # pylint: disable=no-self-use class LeftBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] < x0 + mesh_eps left_boundary = LeftBoundary() class RightBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[0] > x1 - mesh_eps right_boundary = RightBoundary() class LowerBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] < y0 + mesh_eps lower_boundary = LowerBoundary() class UpperBoundary(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] > y1 - mesh_eps upper_boundary = UpperBoundary() class ObstacleBoundary(SubDomain): def inside(self, x, on_boundary): return (on_boundary and x0 + mesh_eps < x[0] < x1 - mesh_eps and y0 + mesh_eps < x[1] < y1 - mesh_eps) obstacle_boundary = ObstacleBoundary() # Boundary conditions for the velocity. # Proper inflow and outflow conditions are a matter of voodoo. See for # example Gresho/Sani, or # # Boundary conditions for open boundaries for the incompressible # Navier-Stokes equation; # B.C.V. Johansson; # J. Comp. Phys. 105, 233-251 (1993). # # The latter in particularly suggest for the inflow: # # u = u0, # d^r v / dx^r = v_r, # div(u) = 0, # # where u and v are the velocities in normal and tangential directions, # respectively, and r\in{0,1,2}. The setting r=0 essentially means to set # (u,v) statically at the left boundary, r=1 means to set u and control # dv/dn, which is what we do here (namely implicitly by dv/dn=0). # At the outflow, # # d^j u / dx^j = 0, # d^q v / dx^q = 0, # p = p0, # # is suggested with j=q+1. Choosing q=0, j=1 means setting the tangential # component of the outflow to 0, and letting the normal component du/dn=0 # (again, this is achieved implicitly by the weak formulation). # inflow = Expression('%e * (%e - x[1]) * (x[1] - %e) / %e' % (entrance_velocity, y1, y0, (0.5 * (y1 - y0))**2), degree=2) outflow = Expression('%e * (%e - x[1]) * (x[1] - %e) / %e' % (entrance_velocity, y1, y0, (0.5 * (y1 - y0))**2), degree=2) u_bcs = [ DirichletBC(W, (0.0, 0.0), upper_boundary), DirichletBC(W, (0.0, 0.0), lower_boundary), DirichletBC(W, (0.0, 0.0), obstacle_boundary), DirichletBC(W.sub(0), inflow, left_boundary), # DirichletBC(W.sub(0), outflow, right_boundary), ] # dudt_bcs = [ # DirichletBC(W, (0.0, 0.0), upper_boundary), # DirichletBC(W, (0.0, 0.0), lower_boundary), # DirichletBC(W, (0.0, 0.0), obstacle_boundary), # DirichletBC(W.sub(0), 0.0, left_boundary), # # DirichletBC(W.sub(1), 0.0, right_boundary), # ] # If there is a penetration boundary (i.e., n.u!=0), then the pressure must # be set somewhere to make sure that the Navier-Stokes problem remains # consistent. # When solving Stokes with no Dirichlet conditions whatsoever, the pressure # tends to 0 at the outlet. This is natural since there, the liquid can # flow out at the rate it needs to be under no pressure at all. # Hence, at outlets, set the pressure to 0. p_bcs = [ # DirichletBC(P, 0.0, right_boundary) ] # Getting vortices is not easy. If we take the actual viscosity of water, # they don't appear. mu = 0.002 # mu = materials.water.dynamic_viscosity(T=293.0) # For starting off, solve the Stokes equation. u0, p0 = flow.stokes.solve(WP, u_bcs + p_bcs, mu, f=Constant((0.0, 0.0)), verbose=False, tol=1.0e-13, max_iter=10000) u0.rename('velocity', 'velocity') p0.rename('pressure', 'pressure') rho = materials.water.density(T=293.0) # stepper = flow.navier_stokes.Chorin() # stepper = flow.navier_stokes.IPCS() stepper = flow.navier_stokes.Rotational() W2 = u0.function_space() P2 = p0.function_space() u_bcs = [ DirichletBC(W2, (0.0, 0.0), upper_boundary), DirichletBC(W2, (0.0, 0.0), lower_boundary), DirichletBC(W2, (0.0, 0.0), obstacle_boundary), DirichletBC(W2.sub(0), inflow, left_boundary), # DirichletBC(W2.sub(0), outflow, right_boundary), ] # TODO settting the outflow _and_ the pressure at the outlet is actually # not necessary. Even without the pressure Dirichlet conditions, the # pressure correction system should be consistent. p_bcs = [DirichletBC(P2, 0.0, right_boundary)] # Report Reynolds number. # https://en.wikipedia.org/wiki/Reynolds_number#Sphere_in_a_fluid reynolds = entrance_velocity * obstacle_diameter * rho / mu print('Reynolds number: %e' % reynolds) dt = 1.0e-5 dt_max = 1.0 t = 0.0 with XDMFFile(mpi_comm_world(), 'karman.xdmf') as xdmf_file: xdmf_file.parameters['flush_output'] = True xdmf_file.parameters['rewrite_function_mesh'] = False k = 0 while k < num_steps: k += 1 print() print('t = %f' % t) if show: plot(u0) plot(p0) xdmf_file.write(u0, t) xdmf_file.write(p0, t) u1, p1 = stepper.step(Constant(dt), {0: u0}, p0, u_bcs, p_bcs, Constant(rho), Constant(mu), f={ 0: Constant((0.0, 0.0)), 1: Constant((0.0, 0.0)) }, verbose=False, tol=1.0e-10) u0.assign(u1) p0.assign(p1) # Adaptive stepsize control based solely on the velocity field. # CFL-like condition for time step. This should be some sort of # average of the temperature in the current step and the target # step. # # More on step-size control for Navier--Stokes: # # Adaptive time step control for the incompressible # Navier-Stokes equations; # Volker John, Joachim Rang; # Comput. Methods Appl. Mech. Engrg. 199 (2010) 514-524; # <http://www.wias-berlin.de/people/john/ELECTRONIC_PAPERS/JR10.CMAME.pdf>. # # Section 3.3 in that paper notes that time-adaptivity for theta- # schemes is too costly. They rather reside to DIRK- and # Rosenbrock-methods. # begin('Step size adaptation...') ux, uy = u0.split() unorm = project(sqrt(ux**2 + uy**2), FunctionSpace(mesh, 'Lagrange', 2), form_compiler_parameters={'quadrature_degree': 4}) unorm = norm(unorm.vector(), 'linf') # print('||u||_inf = %e' % unorm) # Some smooth step-size adaption. target_dt = 1.0 * mesh.hmax() / unorm print('current dt: %e' % dt) print('target dt: %e' % target_dt) # alpha is the aggressiveness factor. The distance between the # current step size and the target step size is reduced by # |1-alpha|. Hence, if alpha==1 then dt_next==target_dt. Otherwise # target_dt is approached more slowly. alpha = 0.5 dt = min( dt_max, # At most double the step size from step to step. dt * min(2.0, 1.0 + alpha * (target_dt - dt) / dt)) print('next dt: %e' % dt) t += dt end() return
class Crucible: def __init__(self): GMSH_EPS = 1.0e-15 # https://fenicsproject.org/qa/12891/initialize-mesh-from-vertices-connectivities-at-once points, cells, point_data, cell_data, _ = meshes.crucible_with_coils.generate( ) # Convert the cell data to 'uint' so we can pick a size_t MeshFunction # below as usual. for k0 in cell_data: for k1 in cell_data[k0]: cell_data[k0][k1] = numpy.array(cell_data[k0][k1], dtype=numpy.dtype("uint")) with TemporaryDirectory() as temp_dir: tmp_filename = os.path.join(temp_dir, "test.xml") meshio.write_points_cells( tmp_filename, points, cells, cell_data=cell_data, file_format="dolfin-xml", ) self.mesh = Mesh(tmp_filename) self.subdomains = MeshFunction( "size_t", self.mesh, os.path.join(temp_dir, "test_gmsh:physical.xml")) self.subdomain_materials = { 1: my_materials.porcelain, 2: materials.argon, 3: materials.gallium_arsenide_solid, 4: materials.gallium_arsenide_liquid, 27: materials.air, } # coils for k in range(5, 27): self.subdomain_materials[k] = my_materials.ek90 # Define the subdomains which together form a single coil. self.coil_domains = [ [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23], [24, 25, 26], ] self.wpi = 4 self.submesh_workpiece = SubMesh(self.mesh, self.subdomains, self.wpi) # http://fenicsproject.org/qa/2026/submesh-workaround-for-parallel-computation # submesh_parallel_bug_fixed = False # if submesh_parallel_bug_fixed: # submesh_workpiece = SubMesh(self.mesh, self.subdomains, self.wpi) # else: # # To get the mesh in parallel, we need to read it in from a file. # # Writing out can only happen in serial mode, though. :/ # base = os.path.join(current_path, # '../../meshes/2d/crucible-with-coils-submesh' # ) # filename = base + '.xml' # if not os.path.isfile(filename): # warnings.warn( # 'Submesh file \'{}\' does not exist. Creating... '.format( # filename # )) # if MPI.size(mpi_comm_world()) > 1: # raise RuntimeError( # 'Can only write submesh in serial mode.' # ) # submesh_workpiece = \ # SubMesh(self.mesh, self.subdomains, self.wpi) # output_stream = File(filename) # output_stream << submesh_workpiece # # Read the mesh # submesh_workpiece = Mesh(filename) coords = self.submesh_workpiece.coordinates() ymin = min(coords[:, 1]) ymax = max(coords[:, 1]) # Find the top right point. k = numpy.argmax(numpy.sum(coords, 1)) topright = coords[k, :] # Initialize mesh function for boundary domains class Left(SubDomain): def inside(self, x, on_boundary): # Explicitly exclude the lowest and the highest point of the # symmetry axis. # It is necessary for the consistency of the pressure-Poisson # system in the Navier-Stokes solver that the velocity is # exactly 0 at the boundary r>0. Hence, at the corner points # (r=0, melt-crucible, melt-crystal) we must enforce u=0 # already and cannot have a component in z-direction. return (on_boundary and x[0] < GMSH_EPS and x[1] < ymax - GMSH_EPS and x[1] > ymin + GMSH_EPS) class Crucible(SubDomain): def inside(self, x, on_boundary): return on_boundary and ( (x[0] > GMSH_EPS and x[1] < ymax - GMSH_EPS) or (x[0] > topright[0] - GMSH_EPS and x[1] > topright[1] - GMSH_EPS) or (x[0] < GMSH_EPS and x[1] < ymin + GMSH_EPS)) # At the top right part (boundary melt--gas), slip is allowed, so only # n.u=0 is enforced. Very weirdly, the PPE is consistent if and only if # the end points of UpperRight are in UpperRight. This contrasts # Left(), where the end points must NOT belong to Left(). Judging from # the experiments, these settings do the right thing. # TODO try to better understand the PPE system/dolfin's boundary # settings class Upper(SubDomain): def inside(self, x, on_boundary): return on_boundary and x[1] > ymax - GMSH_EPS class UpperRight(SubDomain): def inside(self, x, on_boundary): return (on_boundary and x[1] > ymax - GMSH_EPS and x[0] > 0.038 - GMSH_EPS) # The crystal boundary is taken to reach up to 0.038 where the # Dirichlet boundary data is about the melting point of the crystal, # 1511K. This setting gives pretty acceptable results when there is no # convection except the one induced by buoyancy. Is there is any more # stirring going on, though, the end point of the crystal with its # fixed temperature of 1511K might be the hottest point globally. This # looks rather unphysical. # TODO check out alternatives class UpperLeft(SubDomain): def inside(self, x, on_boundary): return (on_boundary and x[1] > ymax - GMSH_EPS and x[0] < 0.038 + GMSH_EPS) left = Left() crucible = Crucible() upper_left = UpperLeft() upper_right = UpperRight() self.wp_boundaries = MeshFunction( "size_t", self.submesh_workpiece, self.submesh_workpiece.topology().dim() - 1, ) self.wp_boundaries.set_all(0) left.mark(self.wp_boundaries, 1) crucible.mark(self.wp_boundaries, 2) upper_right.mark(self.wp_boundaries, 3) upper_left.mark(self.wp_boundaries, 4) if DEBUG: from dolfin import plot, interactive plot(self.wp_boundaries, title="Boundaries") interactive() submesh_boundary_indices = { "left": 1, "crucible": 2, "upper right": 3, "upper left": 4, } # Boundary conditions for the velocity. # # [1] Incompressible flow and the finite element method; volume two; # Isothermal Laminar Flow; # P.M. Gresho, R.L. Sani; # # For the choice of function space, [1] says: # "In 2D, the triangular elements P_2^+P_1 and P_2^+P_{-1} are very # good [...]. [...] If you wish to avoid bubble functions on # triangular elements, P_2P_1 is not bad, and P_2(P_1+P_0) is even # better [...]." # # It turns out that adding the bubble space significantly hampers the # convergence of the Stokes solver and also considerably increases the # time it takes to construct the Jacobian matrix of the Navier--Stokes # problem if no optimization is applied. V_element = FiniteElement("CG", self.submesh_workpiece.ufl_cell(), 2) with_bubbles = False if with_bubbles: V_element += FiniteElement("B", self.submesh_workpiece.ufl_cell(), 2) self.W_element = MixedElement(3 * [V_element]) self.W = FunctionSpace(self.submesh_workpiece, self.W_element) rot0 = Expression(("0.0", "0.0", "-2*pi*x[0] * 5.0/60.0"), degree=1) # rot0 = (0.0, 0.0, 0.0) rot1 = Expression(("0.0", "0.0", "2*pi*x[0] * 5.0/60.0"), degree=1) self.u_bcs = [ DirichletBC(self.W, rot0, crucible), DirichletBC(self.W.sub(0), 0.0, left), DirichletBC(self.W.sub(2), 0.0, left), # Make sure that u[2] is 0 at r=0. DirichletBC(self.W, rot1, upper_left), DirichletBC(self.W.sub(1), 0.0, upper_right), ] self.p_bcs = [] self.P_element = FiniteElement("CG", self.submesh_workpiece.ufl_cell(), 1) self.P = FunctionSpace(self.submesh_workpiece, self.P_element) self.Q_element = FiniteElement("CG", self.submesh_workpiece.ufl_cell(), 2) self.Q = FunctionSpace(self.submesh_workpiece, self.Q_element) # Dirichlet. # This is a bit of a tough call since the boundary conditions need to # be read from a Tecplot file here. filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data/crucible-boundary.dat") data = tecplot_reader.read(filename) RZ = numpy.c_[data["ZONE T"]["node data"]["r"], data["ZONE T"]["node data"]["z"]] T_vals = data["ZONE T"]["node data"]["temp. [K]"] class TecplotDirichletBC(Expression): def eval(self, value, x): # Find on which edge x sits, and raise exception if it doesn't. edge_found = False for edge in data["ZONE T"]["element data"]: # Given a point X and an edge X0--X1, # # (1 - theta) X0 + theta X1, # # the minimum distance is assumed for # # argmin_theta ||(1-theta) X0 + theta X1 - X||^2 # = <X1 - X0, X - X0> / ||X1 - X0||^2. # # If the distance is 0 and 0<=theta<=1, we found the edge. # # Note that edges are 1-based in Tecplot. X0 = RZ[edge[0] - 1] X1 = RZ[edge[1] - 1] theta = numpy.dot(X1 - X0, x - X0) / numpy.dot( X1 - X0, X1 - X0) diff = (1.0 - theta) * X0 + theta * X1 - x if (numpy.dot(diff, diff) < 1.0e-10 and 0.0 <= theta and theta <= 1.0): # Linear interpolation of the temperature value. value[0] = (1.0 - theta) * T_vals[ edge[0] - 1] + theta * T_vals[edge[1] - 1] edge_found = True break # This class is supposed to be used for Dirichlet boundary # conditions. For some reason, FEniCS also evaluates # DirichletBC objects at coordinates which do not sit on the # boundary, see # <http://fenicsproject.org/qa/1033/dirichletbc-expressions-evaluated-away-from-the-boundary>. # The assigned values have no meaning though, so not assigning # values[0] here is okay. # # from matplotlib import pyplot as pp # pp.plot(x[0], x[1], 'xg') if not edge_found: value[0] = 0.0 if False: warnings.warn( "Coordinate ({:e}, {:e}) doesn't sit on edge.". format(x[0], x[1])) # pp.plot(RZ[:, 0], RZ[:, 1], '.k') # pp.plot(x[0], x[1], 'xr') # pp.show() # raise RuntimeError('Input coordinate ' # '{} is not on boundary.'.format(x)) return tecplot_dbc = TecplotDirichletBC(degree=5) self.theta_bcs_d = [DirichletBC(self.Q, tecplot_dbc, upper_left)] self.theta_bcs_d_strict = [ DirichletBC(self.Q, tecplot_dbc, upper_right), DirichletBC(self.Q, tecplot_dbc, crucible), DirichletBC(self.Q, tecplot_dbc, upper_left), ] # Neumann dTdr_vals = data["ZONE T"]["node data"]["dTempdx [K/m]"] dTdz_vals = data["ZONE T"]["node data"]["dTempdz [K/m]"] class TecplotNeumannBC(Expression): def eval(self, value, x): # Same problem as above: This expression is not only evaluated # at boundaries. for edge in data["ZONE T"]["element data"]: X0 = RZ[edge[0] - 1] X1 = RZ[edge[1] - 1] theta = numpy.dot(X1 - X0, x - X0) / numpy.dot( X1 - X0, X1 - X0) dist = numpy.linalg.norm((1 - theta) * X0 + theta * X1 - x) if dist < 1.0e-5 and 0.0 <= theta and theta <= 1.0: value[0] = (1 - theta) * dTdr_vals[ edge[0] - 1] + theta * dTdr_vals[edge[1] - 1] value[1] = (1 - theta) * dTdz_vals[ edge[0] - 1] + theta * dTdz_vals[edge[1] - 1] break return def value_shape(self): return (2, ) tecplot_nbc = TecplotNeumannBC(degree=5) n = FacetNormal(self.Q.mesh()) self.theta_bcs_n = { submesh_boundary_indices["upper right"]: dot(n, tecplot_nbc), submesh_boundary_indices["crucible"]: dot(n, tecplot_nbc), } self.theta_bcs_r = {} # It seems that the boundary conditions from above are inconsistent in # that solving with Dirichlet overall and mixed Dirichlet-Neumann give # different results; the value *cannot* correspond to one solution. # From looking at the solutions, the pure Dirichlet setting appears # correct, so extract the Neumann values directly from that solution. # Pick fixed coefficients roughly at the temperature that we expect. # This could be made less magic by having the coefficients depend on # theta and solving the quasilinear equation. temp_estimate = 1550.0 # Get material parameters wp_material = self.subdomain_materials[self.wpi] if isinstance(wp_material.specific_heat_capacity, float): cp = wp_material.specific_heat_capacity else: cp = wp_material.specific_heat_capacity(temp_estimate) if isinstance(wp_material.density, float): rho = wp_material.density else: rho = wp_material.density(temp_estimate) if isinstance(wp_material.thermal_conductivity, float): k = wp_material.thermal_conductivity else: k = wp_material.thermal_conductivity(temp_estimate) reference_problem = cyl_heat.Heat( self.Q, convection=None, kappa=k, rho=rho, cp=cp, source=Constant(0.0), dirichlet_bcs=self.theta_bcs_d_strict, ) theta_reference = reference_problem.solve_stationary() theta_reference.rename("theta", "temperature (Dirichlet)") # Create equivalent boundary conditions from theta_ref. This # makes sure that the potentially expensive Expression evaluation in # theta_bcs_* is replaced by something reasonably cheap. self.theta_bcs_d = [ DirichletBC(bc.function_space(), theta_reference, bc.domain_args[0]) for bc in self.theta_bcs_d ] # Adapt Neumann conditions. n = FacetNormal(self.Q.mesh()) self.theta_bcs_n = { k: dot(n, grad(theta_reference)) # k: Constant(1000.0) for k in self.theta_bcs_n } if DEBUG: # Solve the heat equation with the mixed Dirichlet-Neumann # boundary conditions and compare it to the Dirichlet-only # solution. theta_new = Function(self.Q, name="temperature (Neumann + Dirichlet)") from dolfin import Measure ds_workpiece = Measure("ds", subdomain_data=self.wp_boundaries) heat = cyl_heat.Heat( self.Q, convection=None, kappa=k, rho=rho, cp=cp, source=Constant(0.0), dirichlet_bcs=self.theta_bcs_d, neumann_bcs=self.theta_bcs_n, robin_bcs=self.theta_bcs_r, my_ds=ds_workpiece, ) theta_new = heat.solve_stationary() theta_new.rename("theta", "temperature (Neumann + Dirichlet)") from dolfin import plot, interactive, errornorm print("||theta_new - theta_ref|| = {:e}".format( errornorm(theta_new, theta_reference))) plot(theta_reference) plot(theta_new) plot(theta_reference - theta_new, title="theta_ref - theta_new") interactive() self.background_temp = 1400.0 # self.omega = 2 * pi * 10.0e3 self.omega = 2 * pi * 300.0 return
fc_val = [0.0, fc] help = np.asarray(subdomains.array(), dtype=np.int32) fc_function.vector()[:] = np.choose(help, fc_val) zeroth = plot(fc_function, title="Fixed charge density, $c^f$") plt.colorbar(zeroth) plot(fc_function) plt.xlim(0.0, 0.015) plt.xticks([0.0, 0.005, 0.01, 0.015]) plt.ylim(0.0, 0.015) plt.yticks([0.0, 0.005, 0.01, 0.015]) plt.show() Sol_c = Constant(1.0) Poten = 50.0E-3 l_bc_an = DirichletBC(ME.sub(0), Sol_c, left_boundary) r_bc_an = DirichletBC(ME.sub(0), Sol_c, right_boundary) l_bc_ca = DirichletBC(ME.sub(1), Sol_c, left_boundary) r_bc_ca = DirichletBC(ME.sub(1), Sol_c, right_boundary) l_bc_psi = DirichletBC(ME.sub(2), Constant(-Poten), left_boundary) r_bc_psi = DirichletBC(ME.sub(2), Constant(Poten), right_boundary) bcs = [l_bc_an, r_bc_an, l_bc_ca, r_bc_ca, l_bc_psi, r_bc_psi] u = Function(ME) V = FunctionSpace(mesh, P1) an_int = interpolate( Expression( '((pow((x[0] - 7.5E-3), 2) + pow((x[1] - 7.5E-3), 2)) <= pow(2.5E-3, 2)) ? 0.4142 : 1.0', degree=1), V) ca_int = interpolate(
def test_unsteady_stokes(): nx, ny = 15, 15 k = 1 nu = Constant(1.0e-0) dt = Constant(2.5e-2) num_steps = 20 theta0 = 1.0 # Initial theta value theta1 = 0.5 # Theta after 1 step theta = Constant(theta0) mesh = UnitSquareMesh(nx, ny) # The 'unsteady version' of the benchmark in the 2012 paper by Labeur&Wells u_exact = Expression( ( "sin(t) * x[0]*x[0]*(1.0 - x[0])*(1.0 - x[0])*(2.0*x[1] \ -6.0*x[1]*x[1] + 4.0*x[1]*x[1]*x[1])", "-sin(t)* x[1]*x[1]*(1.0 - x[1])*(1.0 - x[1])*(2.0*x[0] \ - 6.0*x[0]*x[0] + 4.0*x[0]*x[0]*x[0])", ), t=0, degree=7, domain=mesh, ) p_exact = Expression("sin(t) * x[0]*(1.0 - x[0])", t=0, degree=7, domain=mesh) du_exact = Expression( ( "cos(t) * x[0]*x[0]*(1.0 - x[0])*(1.0 - x[0])*(2.0*x[1] \ - 6.0*x[1]*x[1] + 4.0*x[1]*x[1]*x[1])", "-cos(t)* x[1]*x[1]*(1.0 - x[1])*(1.0 - x[1])*(2.0*x[0] \ -6.0*x[0]*x[0] + 4.0*x[0]*x[0]*x[0])", ), t=0, degree=7, domain=mesh, ) ux_exact = Expression( ( "x[0]*x[0]*(1.0 - x[0])*(1.0 - x[0])*(2.0*x[1] \ - 6.0*x[1]*x[1] + 4.0*x[1]*x[1]*x[1])", "-x[1]*x[1]*(1.0 - x[1])*(1.0 - x[1])*(2.0*x[0] \ - 6.0*x[0]*x[0] + 4.0*x[0]*x[0]*x[0])", ), degree=7, domain=mesh, ) px_exact = Expression("x[0]*(1.0 - x[0])", degree=7, domain=mesh) sin_ext = Expression("sin(t)", t=0, degree=7, domain=mesh) f = du_exact + sin_ext * div(px_exact * Identity(2) - 2 * sym(grad(ux_exact))) Vhigh = VectorFunctionSpace(mesh, "DG", 7) Phigh = FunctionSpace(mesh, "DG", 7) # New syntax: V = VectorElement("DG", mesh.ufl_cell(), k) Q = FiniteElement("DG", mesh.ufl_cell(), k - 1) Vbar = VectorElement("DGT", mesh.ufl_cell(), k) Qbar = FiniteElement("DGT", mesh.ufl_cell(), k) mixedL = FunctionSpace(mesh, MixedElement([V, Q])) mixedG = FunctionSpace(mesh, MixedElement([Vbar, Qbar])) V2 = FunctionSpace(mesh, V) Uh = Function(mixedL) Uhbar = Function(mixedG) U0 = Function(mixedL) Uhbar0 = Function(mixedG) u0, p0 = split(U0) ubar0, pbar0 = split(Uhbar0) ustar = Function(V2) # Then the boundary conditions bc0 = DirichletBC(mixedG.sub(0), Constant((0, 0)), Gamma) bc1 = DirichletBC(mixedG.sub(1), Constant(0), Corner, "pointwise") bcs = [bc0, bc1] alpha = Constant(6 * k * k) forms_stokes = FormsStokes(mesh, mixedL, mixedG, alpha).forms_unsteady(ustar, dt, nu, f) ssc = StokesStaticCondensation( mesh, forms_stokes["A_S"], forms_stokes["G_S"], forms_stokes["G_ST"], forms_stokes["B_S"], forms_stokes["Q_S"], forms_stokes["S_S"], ) t = 0.0 step = 0 for step in range(num_steps): step += 1 t += float(dt) if comm.Get_rank() == 0: print("Step " + str(step) + " Time " + str(t)) # Set time level in exact solution u_exact.t = t p_exact.t = t du_exact.t = t - (1 - float(theta)) * float(dt) sin_ext.t = t - (1 - float(theta)) * float(dt) ssc.assemble_global_lhs() ssc.assemble_global_rhs() for bc in bcs: ssc.apply_boundary(bc) ssc.solve_problem(Uhbar, Uh, "none", "default") assign(U0, Uh) assign(ustar, U0.sub(0)) assign(Uhbar0, Uhbar) if step == 1: theta.assign(theta1) udiv_e = sqrt(assemble(div(Uh.sub(0)) * div(Uh.sub(0)) * dx)) u_ex_h = interpolate(u_exact, Vhigh) p_ex_h = interpolate(p_exact, Phigh) u_error = sqrt(assemble(dot(Uh.sub(0) - u_ex_h, Uh.sub(0) - u_ex_h) * dx)) p_error = sqrt(assemble(dot(Uh.sub(1) - p_ex_h, Uh.sub(1) - p_ex_h) * dx)) assert udiv_e < 1e-12 assert u_error < 1.5e-4 assert p_error < 1e-2
def _prepare_solution_fcns(self): # Extract parameters needed to create finite elements N = self.parameters["N"] gdim = self._mesh.geometry().dim() # Group elements for phi, chi, v elements_ch = (N - 1) * [ self._FE["phi"], ] + (N - 1) * [ self._FE["chi"], ] # NOTE: Elements for phi and chi are grouped together to avoid special # treatment of the case with N = 2. For example, if N == 2 and # phi would be represented as a vector with a single component # then a special treatment is needed to plot this single # component (UFL/DOLFIN ban to plot 1D function on a 2D mesh). elements = [] elements.append(MixedElement(elements_ch)) elements.append(VectorElement(self._FE["v"], dim=gdim)) # Append elements for p and th elements.append(self._FE["p"]) if self._FE["th"] is not None: elements.append(self._FE["th"]) # Build function spaces W = FunctionSpace(self._mesh, MixedElement(elements), constrained_domain=self._constrained_domain) self._ndofs["total"] = W.dim() self._subspace["phi"] = [W.sub(0).sub(i) for i in range(N - 1)] self._subspace["chi"] = [ W.sub(0).sub(i) for i in range(N - 1, 2 * (N - 1)) ] self._ndofs["phi"] = W.sub(0).sub(0).dim() self._ndofs["chi"] = W.sub(0).sub(N - 1).dim() if gdim == 1: self._subspace["v"] = [ W.sub(1), ] self._ndofs["v"] = W.sub(1).dim() else: self._subspace["v"] = [W.sub(1).sub(i) for i in range(gdim)] self._ndofs["v"] = W.sub(1).sub(0).dim() self._subspace["p"] = W.sub(2) self._ndofs["p"] = W.sub(2).dim() self._ndofs["th"] = 0 if W.num_sub_spaces() == 4: self._subspace["th"] = W.sub(3) self._ndofs["th"] = W.sub(3).dim() self._ndofs["CH"] = W.sub(0).dim() self._ndofs["NS"] = (gdim * self._ndofs["v"] + self._ndofs["p"] + self._ndofs["th"]) assert self._ndofs["total"] == self._ndofs["CH"] + self._ndofs["NS"] # Create solution variable at ctl w_ctl = (Function(W), ) w_ctl[0].rename("mono_ctl", "solution_mono_ctl") # Create solution variables at ptl w_ptl = [(Function(W), ) for i in range(self.parameters["PTL"])] for (i, f) in enumerate(w_ptl): f[0].rename("mono_ptl%i" % i, "solution_mono_ptl%i" % i) return (w_ctl, w_ptl)
# Set log level for parallel set_log_level(LogLevel.ERROR) if rank == 0: set_log_level(LogLevel.PROGRESS) ff = MeshFunction("size_t", mesh, mesh.geometry().dim() - 1) Dirichlet(Lx, Ly, Lpml).mark(ff, 1) # Create function spaces VE = VectorElement("CG", mesh.ufl_cell(), 1, dim=2) TE = TensorElement("DG", mesh.ufl_cell(), 0, shape=(2, 2), symmetry=True) W = FunctionSpace(mesh, MixedElement([VE, TE])) F = FunctionSpace(mesh, "CG", 2) V = W.sub(0).collapse() M = W.sub(1).collapse() alpha_0 = alpha_0(m, car_dim, R, Lpml) alpha_1 = alpha_1(alpha_0, Lx, Lpml, degree=2) alpha_2 = alpha_2(alpha_0, Ly, Lpml, degree=2) beta_0 = beta_0(m, V_r, R, Lpml) beta_1 = beta_1(beta_0, Lx, Lpml, degree=2) beta_2 = beta_2(beta_0, Ly, Lpml, degree=2) alpha_1 = interpolate(alpha_1, F) alpha_2 = interpolate(alpha_2, F) beta_1 = interpolate(beta_1, F) beta_2 = interpolate(beta_2, F)
def _prepare_solution_fcns(self): # Extract parameters needed to create finite elements N = self.parameters["N"] gdim = self._mesh.geometry().dim() # Group elements for phi, chi, v elements_ch = (N - 1) * [ self._FE["phi"], ] + (N - 1) * [ self._FE["chi"], ] elements_ns = [ VectorElement(self._FE["v"], dim=gdim), ] # Append elements for p and th elements_ns.append(self._FE["p"]) if self._FE["th"] is not None: elements_ns.append(self._FE["th"]) # Build function spaces W_ch = FunctionSpace(self._mesh, MixedElement(elements_ch), constrained_domain=self._constrained_domain) W_ns = FunctionSpace(self._mesh, MixedElement(elements_ns), constrained_domain=self._constrained_domain) self._ndofs["CH"] = W_ch.dim() self._ndofs["NS"] = W_ns.dim() self._ndofs["total"] = W_ch.dim() + W_ns.dim() self._subspace["phi"] = [W_ch.sub(i) for i in range(N - 1)] self._subspace["chi"] = [ W_ch.sub(i) for i in range(N - 1, 2 * (N - 1)) ] self._ndofs["phi"] = W_ch.sub(0).dim() self._ndofs["chi"] = W_ch.sub(N - 1).dim() if gdim == 1: self._subspace["v"] = [ W_ns.sub(0), ] self._ndofs["v"] = W_ns.sub(0).dim() else: self._subspace["v"] = [W_ns.sub(0).sub(i) for i in range(gdim)] self._ndofs["v"] = W_ns.sub(0).sub(0).dim() self._subspace["p"] = W_ns.sub(1) self._ndofs["p"] = W_ns.sub(1).dim() self._ndofs["th"] = 0 if W_ns.num_sub_spaces() == 3: self._subspace["th"] = W_ns.sub(2) self._ndofs["th"] = W_ns.sub(2).dim() # ndofs = ( # (N-1)*(self._ndofs["phi"] + self._ndofs["chi"]) # + gdim*self._ndofs["v"] # + self._ndofs["p"] # + self._ndofs["th"] # ) # assert self._ndofs["total"] == ndofs # Create solution variables at ctl w_ctl = (Function(W_ch), Function(W_ns)) w_ctl[0].rename("semi_ctl_ch", "solution_semi_ch_ctl") w_ctl[1].rename("semi_ctl_ns", "solution_semi_ns_ctl") # Create solution variables at ptl w_ptl = [(Function(W_ch), Function(W_ns)) \ for i in range(self.parameters["PTL"])] for i, f in enumerate(w_ptl): f[0].rename("semi_ptl%i_ch" % i, "solution_semi_ch_ptl%i" % i) f[1].rename("semi_ptl%i_ns" % i, "solution_semi_ns_ptl%i" % i) return (w_ctl, w_ptl)