def injection_matrix(Vc, Vf, fine_mesh, data): '''Injection mapping from Vc to Vf''' mesh_c = Vc.mesh() assert fine_mesh.id() == Vf.mesh().id() mesh_f = Vf.mesh() if data['not_nested_method'] == 'interpolate': return df.PETScDMCollection.create_transfer_matrix(Vc, Vf) elif data['not_nested_method'] == 'project': raise ValueError('Missing projection') # Fallback to our interpolate with lookup, which, however is slower # to `create_transfer_matrix` tdim = mesh_f.topology().dim() # Refine was used to create it keys, fine_to_coarse = list( zip(*list(fine_mesh.parent_entity_map[mesh_c.id()][tdim].items()))) fine_to_coarse = np.array(fine_to_coarse, dtype='uintp') fine_to_coarse[np.argsort(keys)] = fine_to_coarse # The idea is to evaluate Vf's degrees of freedom at basis functions of Vc fdmap = Vf.dofmap() Vf_dof = DegreeOfFreedom(Vf) cdmap = Vc.dofmap() Vc_basis_f = FEBasisFunction(Vc) # Column values visited_rows = [False] * Vf.dim() row_values = np.zeros(Vc_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(Vf, Vc) as mat: for f_cell, c_cell in enumerate(fine_to_coarse): Vc_basis_f.cell = c_cell # These are the colums coarse_dofs = cdmap.cell_dofs(c_cell) Vf_dof.cell = f_cell fine_dofs = fdmap.cell_dofs(f_cell) for local, dof in enumerate(fine_dofs): if visited_rows[dof]: continue else: visited_rows[dof] = True Vf_dof.dof = local # Evaluete coarse basis foos here for local_c, dof_c in enumerate(coarse_dofs): Vc_basis_f.dof = local_c row_values[local_c] = Vf_dof.eval(Vc_basis_f) # Insert mat.setValues([dof], coarse_dofs, row_values, PETSc.InsertMode.INSERT_VALUES) return PETScMatrix(mat)
def block_mat_to_petsc(bmat): '''Block mat to PETScMatrix via assembly''' # This is beautiful but slow as hell :) def iter_rows(matrix): for i in range(matrix.size(0)): yield matrix.getrow(i) row_sizes, col_sizes = get_sizes(bmat) row_offsets = np.cumsum([0] + list(row_sizes)) col_offsets = np.cumsum([0] + list(col_sizes)) with petsc_serial_matrix(row_offsets[-1], col_offsets[-1]) as mat: row = 0 for row_blocks in bmat.blocks: # Zip the row iterators of the matrices together for indices_values in itertools.izip(*map(iter_rows, row_blocks)): indices, values = zip(*indices_values) indices = [list(index+offset) for index, offset in zip(indices, col_offsets)] indices = sum(indices, []) row_values = np.hstack(values) mat.setValues([row], indices, row_values, PETSc.InsertMode.INSERT_VALUES) row += 1 return PETScMatrix(mat)
def block_mat_to_petsc(bmat): '''Block mat to PETScMatrix via assembly''' # This is beautiful but slow as hell :) def iter_rows(matrix): for i in range(matrix.size(0)): yield matrix.getrow(i) row_sizes, col_sizes = get_sizes(bmat) row_offsets = np.cumsum([0] + list(row_sizes)) col_offsets = np.cumsum([0] + list(col_sizes)) with petsc_serial_matrix(row_offsets[-1], col_offsets[-1]) as mat: row = 0 for row_blocks in bmat.blocks: # Zip the row iterators of the matrices together for indices_values in itertools.izip(*map(iter_rows, row_blocks)): indices, values = zip(*indices_values) indices = [ list(index + offset) for index, offset in zip(indices, col_offsets) ] indices = sum(indices, []) row_values = np.hstack(values) mat.setValues([row], indices, row_values, PETSc.InsertMode.INSERT_VALUES) row += 1 return PETScMatrix(mat)
def DLT_trace_mat(V, TV, trace_mesh=None, tag_data=None): '''Inject dofs from facets to DLT''' mesh = V.mesh() if trace_mesh is None: trace_mesh = TV.mesh() fdim = trace_mesh.topology().dim() # None means all if tag_data is None: try: marking_function = trace_mesh.marking_function tag_data = (marking_function, set(marking_function.array())) except AttributeError: tag_data = (MeshFunction('size_t', trace_mesh, trace_mesh.topology().dim(), 0), set( (0, ))) trace_mesh_subdomains, tags = tag_data # Init/extract the mapping # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][ fdim] # Map cell of TV to cells of V if TV.num_sub_spaces() == 0: Tdmaps = [TV.dofmap()] else: Tdmaps = [TV.sub(i).dofmap() for i in range(TV.num_sub_spaces())] if V.num_sub_spaces() == 0: dmap = V.dofmap() facet2dofs = [dmap.entity_dofs(mesh, fdim)] else: facet2dofs = [ V.sub(i).dofmap().entity_dofs(mesh, fdim) for i in range(V.num_sub_spaces()) ] assert len(Tdmaps) == len(facet2dofs) # Only look at tagged cells trace_cells = list( itertools.chain(*[ map(operator.methodcaller('index'), SubsetIterator(trace_mesh_subdomains, tag)) for tag in tags ])) # Rows with petsc_serial_matrix(TV, V) as mat: for Tdmap, facet2dof in zip(Tdmaps, facet2dofs): for trace_cell in trace_cells: trace_dof, = Tdmap.cell_dofs(trace_cell) DLT_dof = facet2dof[mapping[trace_cell]] mat.setValues([trace_dof], [DLT_dof], [1.], PETSc.InsertMode.INSERT_VALUES) return mat
def restriction_matrix(V, TV, rmesh): '''The first cell connected to the facet gets to set the values of TV''' assert TV.mesh().id() == rmesh.id() mesh = V.mesh() tdim = mesh.topology().dim() # Let's get the mapping or cell of TV mesh to V mesh cells mapping = rmesh.parent_entity_map[mesh.id()][tdim] # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) # Rows visited_dofs = [False] * TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in range(TV.mesh().num_cells()): TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # The corresponding cell in V mesh cell = mapping[trace_cell] V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = TV_dof.eval(V_basis_f) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') # Insert mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) return mat
def restriction_matrix(V, TV, rmesh): '''The first cell connected to the facet gets to set the values of TV''' assert TV.mesh().id() == rmesh.id() mesh = V.mesh() tdim = mesh.topology().dim() # Let's get the mapping or cell of TV mesh to V mesh cells mapping = rmesh.parent_entity_map[mesh.id()][tdim] # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) # Rows visited_dofs = [False]*TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in range(TV.mesh().num_cells()): TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # The corresponding cell in V mesh cell = mapping[trace_cell] V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = TV_dof.eval(V_basis_f) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') # Insert mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) return mat
def injection_matrix(Vc, Vf): '''Injection mapping from Vc to Vf''' mesh_c = Vc.mesh() mesh_f = Vf.mesh() assert mesh_f.has_parent() and mesh_c.has_child() assert mesh_f.parent().id() == mesh_c.id() fine_to_coarse = mesh_f.data().array('parent_cell', mesh_f.topology().dim()) # The idea is to evaluate Vf's degrees of freedom at basis functions of Vc fdmap = Vf.dofmap() Vf_dof = DegreeOfFreedom(Vf) cdmap = Vc.dofmap() Vc_basis_f = FEBasisFunction(Vc) # Column values visited_rows = [False] * Vf.dim() row_values = np.zeros(Vc_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(Vf, Vc) as mat: for f_cell, c_cell in enumerate(fine_to_coarse): Vc_basis_f.cell = c_cell # These are the colums coarse_dofs = cdmap.cell_dofs(c_cell) Vf_dof.cell = f_cell fine_dofs = fdmap.cell_dofs(f_cell) for local, dof in enumerate(fine_dofs): if visited_rows[dof]: continue else: visited_rows[dof] = True Vf_dof.dof = local # Evaluete coarse basis foos here for local_c, dof_c in enumerate(coarse_dofs): Vc_basis_f.dof = local_c row_values[local_c] = Vf_dof.eval(Vc_basis_f) # Insert mat.setValues([dof], coarse_dofs, row_values, PETSc.InsertMode.INSERT_VALUES) # Transpose return mat
def interpolation_mat(V, Q): ''' Matrix representation of interpolation operator from V to Q ''' # Compatibility of spaces assert V.dolfin_element().value_rank() == Q.dolfin_element().value_rank() assert V.ufl_element().value_shape() == Q.ufl_element().value_shape() # We assume that the spaces are constructed on the same mesh assert V.mesh().id() == Q.mesh().id() # The idea is to evaluate Q's degrees of freedom at basis functions of V V_dm = V.dofmap() V_basis_f = FEBasisFunction(V) Q_dm = Q.dofmap() Q_dof = DegreeOfFreedom(Q) visited_rows = np.zeros(Q.dim(), dtype=bool) # Column values for row column_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(Q, V) as mat: for cell in xrange(V.mesh().num_cells()): Q_dof.cell = cell V_basis_f.cell = cell cell_rows = Q_dm.cell_dofs(cell) column_indices = np.array(V_dm.cell_dofs(cell), dtype='int32') for local_row, row in enumerate(cell_rows): if visited_rows[row]: continue visited_rows[row] = True # Define dof Q_dof.dof = local_row # Eval at V basis functions for local_col, col in enumerate(column_indices): # Set which basis foo V_basis_f.dof = local_col column_values[local_col] = Q_dof.eval(V_basis_f) # Can fill the matrix now mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) return mat
def point_trace_matrix(V, TV, x0): ''' Let u in V; u = ck phi_k then u(x0) \in TV = ck phi_k(x0). So this is a 1 by dim(V) matrix where the column values are phi_k(x0). ''' mesh = V.mesh() tree = mesh.bounding_box_tree() cell = tree.compute_first_entity_collision(Point(*x0)) assert cell < mesh.num_cells() # Cell for restriction Vcell = Cell(mesh, cell) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() x0 = np.fromiter(x0, dtype=float) # Columns - get all components at once all_dofs = V.dofmap().cell_dofs(cell).tolist() Vel = V.element() value_size = V.ufl_element().value_size() basis_values = np.zeros(V.element().space_dimension() * value_size) Vel.evaluate_basis_all(basis_values, x0, vertex_coordinates, cell_orientation) with petsc_serial_matrix(TV, V) as mat: # Scalar gets all if value_size == 1: component_dofs = lambda component: V.dofmap().cell_dofs(cell) # Slices else: component_dofs = lambda component: V.sub(component).dofmap( ).cell_dofs(cell) for row in map(int, TV.dofmap().cell_dofs(cell)): # R^n components sub_dofs = component_dofs(row) sub_dofs_local = [all_dofs.index(dof) for dof in sub_dofs] print row, sub_dofs, sub_dofs_local, basis_values[sub_dofs_local] mat.setValues([row], sub_dofs, basis_values[sub_dofs_local], PETSc.InsertMode.INSERT_VALUES) return mat
def point_trace_matrix(V, TV, x0): ''' Let u in V; u = ck phi_k then u(x0) \in TV = ck phi_k(x0). So this is a 1 by dim(V) matrix where the column values are phi_k(x0). ''' mesh = V.mesh() tree = mesh.bounding_box_tree() cell = tree.compute_first_entity_collision(Point(*x0)) assert cell < mesh.num_cells() # Cell for restriction Vcell = Cell(mesh, cell) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() x0 = np.fromiter(x0, dtype=float) # Columns - get all components at once all_dofs = V.dofmap().cell_dofs(cell).tolist() Vel = V.element() value_size = V.ufl_element().value_size() basis_values = np.zeros(V.element().space_dimension()*value_size) Vel.evaluate_basis_all(basis_values, x0, vertex_coordinates, cell_orientation) with petsc_serial_matrix(TV, V) as mat: # Scalar gets all if value_size == 1: component_dofs = lambda component: V.dofmap().cell_dofs(cell) # Slices else: component_dofs = lambda component: V.sub(component).dofmap().cell_dofs(cell) for row in map(int, TV.dofmap().cell_dofs(cell)): # R^n components sub_dofs = component_dofs(row) sub_dofs_local = [all_dofs.index(dof) for dof in sub_dofs] print row, sub_dofs, sub_dofs_local, basis_values[sub_dofs_local] mat.setValues([row], sub_dofs, basis_values[sub_dofs_local], PETSc.InsertMode.INSERT_VALUES) return mat
def trace_mat_one_restrict(V, TV, restriction, normal, trace_mesh=None, tag_data=None): ''' Compute the trace values using +/- restriction. A + plus is the one for which the vector cell.midpoint - facet.midpoint agrees in orientation with the normal on the facet. ''' mesh = V.mesh() fdim = mesh.topology().dim() - 1 if trace_mesh is None: trace_mesh = TV.mesh() # None means all if tag_data is None: tag_data = (MeshFunction('size_t', trace_mesh, trace_mesh.topology().dim(), 0), set((0, ))) trace_mesh_subdomains, tags = tag_data # Only look at tagged cells trace_cells = itertools.chain(*[ itertools.imap(operator.methodcaller('index'), SubsetIterator(trace_mesh_subdomains, tag)) for tag in tags ]) # Init/extract entity map assert get_entity_map(mesh, trace_mesh, trace_mesh_subdomains, tags) # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][ fdim] # Map cell of TV to cells of V mesh.init(fdim, fdim + 1) f2c = mesh.topology()(fdim, fdim + 1) # Facets of V to cell of V # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) gdim = mesh.geometry().dim() # Rows visited_dofs = [False] * TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in trace_cells: TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # Figure out the dofs of V to use here facet_cells = f2c(mapping[trace_cell]) assert 0 < len(facet_cells) < 3 # Ignore boundary facets if len(facet_cells) == 1: cell = facet_cells[0] # Search which cell has the right sign else: signs = [] for fcell in facet_cells: t_mp = Cell(trace_mesh, trace_cell).midpoint().array()[:gdim] mp = Cell(mesh, fcell).midpoint().array()[:gdim] sign = '+' if np.inner(mp - t_mp, normal(t_mp)) > 0 else '-' signs.append(sign) cell = facet_cells[signs.index(restriction)] V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = TV_dof.eval(V_basis_f) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') # Insert mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) return mat
def trace_mat_no_restrict(V, TV, trace_mesh=None): '''The first cell connected to the facet gets to set the values of TV''' mesh = V.mesh() if trace_mesh is None: trace_mesh = TV.mesh() fdim = trace_mesh.topology().dim() # Init/extract the mapping assert get_entity_map(mesh, trace_mesh) # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][ fdim] # Map cell of TV to cells of V mesh.init(fdim, fdim + 1) f2c = mesh.topology()(fdim, fdim + 1) # Facets of V to cell of V # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) # Rows visited_dofs = [False] * TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in range(TV.mesh().num_cells()): TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # Figure out the dofs of V to use here. Does not matter which # cell of the connected ones we pick cell = f2c(mapping[trace_cell])[0] V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = TV_dof.eval(V_basis_f) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') # Insert mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) return mat
def cylinder_average_matrix(V, TV, radius, quad_degree): '''Averaging matrix''' mesh = V.mesh() line_mesh = TV.mesh() # We are going to perform the integration with Gauss quadrature at # the end (PI u)(x): # A cell of mesh (an edge) defines a normal vector. Let P be the plane # that is defined by the normal vector n and some point x on Gamma. Let L # be the circle that is the intersect of P and S. The value of q (in Q) at x # is defined as # # q(x) = (1/|L|)*\int_{L}g(x)*dL # # which simplifies to g(x) = (1/(2*pi*R))*\int_{-pi}^{pi}u(L)*R*d(theta) and # or = (1/2) * \int_{-1}^{1} u (L(pi*s)) * ds # This can be integrated no problemo once we figure out L. To this end, let # t_1 and t_2 be two unit mutually orthogonal vectors that are orthogonal to # n. Then L(pi*s) = p + R*t_1*cos(pi*s) + R*t_2*sin(pi*s) can be seen to be # such that i) |x-p| = R and ii) x.n = 0 [i.e. this the suitable # parametrization] # Clearly we can scale the weights as well as precompute # cos and sin terms. xq, wq = leggauss(quad_degree) wq *= 0.5 cos_xq = np.cos(np.pi*xq).reshape((-1, 1)) sin_xq = np.sin(np.pi*xq).reshape((-1, 1)) if is_number(radius): radius = lambda x, radius=radius: radius mesh_x = TV.mesh().coordinates() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() # Eval at points will require serch tree = mesh.bounding_box_tree() limit = mesh.num_cells() TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension()*value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # Get the tangent => orthogonal tangent vectors v0, v1 = mesh_x[line_cell.entities(0)] n = v0 - v1 t1 = np.array([n[1]-n[2], n[2]-n[0], n[0]-n[1]]) t2 = np.cross(n, t1) t1 /= np.linalg.norm(t1) t2 = t2/np.linalg.norm(t2) # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # Get radius and integration points rad = radius(avg_point) integration_points = avg_point + rad*t1*sin_xq + rad*t2*cos_xq data = {} for index, ip in enumerate(integration_points): c = tree.compute_first_entity_collision(Point(*ip)) if c >= limit: continue Vcell = Cell(mesh, c) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() Vel.evaluate_basis_all(basis_values, ip, vertex_coordinates, cell_orientation) cols_ip = V_dm.cell_dofs(c) values_ip = basis_values*wq[index] # Add for col, value in zip(cols_ip, values_ip.reshape((-1, value_size))): if col in data: data[col] += value else: data[col] = value # The thing now that with data we can assign to several # rows of the matrix column_indices = np.array(data.keys(), dtype='int32') for shift in range(value_size): row = scalar_row + shift column_values = np.array([data[col][shift] for col in column_indices]) mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return PETScMatrix(mat)
def sphere_average_matrix(V, TV, radius, quad_degree): '''Averaging matrix over the sphere''' mesh = V.mesh() line_mesh = TV.mesh() # Lebedev below need off degrees if quad_degree % 2 == 0: quad_degree += 1 # NOTE: this is a dependency from quadpy.sphere import Lebedev integrator = Lebedev(quad_degree) xq = integrator.points wq = integrator.weights if is_number(radius): radius = lambda x, radius=radius: radius mesh_x = TV.mesh().coordinates() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() # Eval at points will require serch tree = mesh.bounding_box_tree() limit = mesh.num_cells() TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension()*value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # Get radius and integration points rad = radius(avg_point) # Scale and shift the unit sphere to the point integration_points = xq*rad + avg_point data = {} for index, ip in enumerate(integration_points): c = tree.compute_first_entity_collision(Point(*ip)) if c >= limit: continue Vcell = Cell(mesh, c) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() Vel.evaluate_basis_all(basis_values, ip, vertex_coordinates, cell_orientation) cols_ip = V_dm.cell_dofs(c) values_ip = basis_values*wq[index] # Add for col, value in zip(cols_ip, values_ip.reshape((-1, value_size))): if col in data: data[col] += value else: data[col] = value # The thing now that with data we can assign to several # rows of the matrix column_indices = np.array(data.keys(), dtype='int32') for shift in range(value_size): row = scalar_row + shift column_values = np.array([data[col][shift] for col in column_indices]) mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return PETScMatrix(mat)
def trace_3d1d_matrix(V, TV, reduced_mesh): '''Trace from 3d to 1d. Makes sense only for CG space''' assert reduced_mesh.id() == TV.mesh().id() assert V.ufl_element().family() == 'Lagrange' mesh = V.mesh() line_mesh = TV.mesh() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() # We use the map to get (1d cell -> [3d edge) -> 3d cell] if hasattr(reduced_mesh, 'parent_entity_map'): # ( ) mapping = reduced_mesh.parent_entity_map[mesh.id()][1] # [ ] mesh.init(1) mesh.init(1, 3) e2c = mesh.topology()(1, 3) # From 1d cell (by index) get_cell3d = lambda c, d1d3=mapping, d3d3=e2c: d3d3(d1d3[c.index()])[0] # Tree collision by midpoint else: tree = mesh.bounding_box_tree() limit = mesh.num_cells() get_cell3d = lambda c, tree=tree, bound=limit: ( lambda index: index if index<bound else None )(tree.compute_first_entity_collision(c.midpoint())) TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension()*value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # Get the tangent => orthogonal tangent vectors # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] # Let's get a 3d cell to use for getting the V values # CG assumption allows taking any tet_cell = get_cell3d(line_cell) if tet_cell is None: continue Vcell = Cell(mesh, tet_cell) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = 0 # Columns are determined by V cell! I guess the sparsity # could be improved if for x_dofs of TV only x_dofs of V # were considered column_indices = np.array(V_dm.cell_dofs(tet_cell), dtype='int32') for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # 3d at point Vel.evaluate_basis_all(basis_values, avg_point, vertex_coordinates, cell_orientation) # The thing now is that with data we can assign to several # rows of the matrix. Shift determines the (x, y, ... ) or # (xx, xy, yx, ...) component of Q data = basis_values.reshape((-1, value_size)).T for shift, column_values in enumerate(data): row = scalar_row + shift mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return PETScMatrix(mat)
def trace_mat_one_restrict(V, TV, restriction, normal, trace_mesh=None): ''' Compute the trace values using +/- restriction. A + plus is the one for which the vector cell.midpoint - facet.midpoint agrees in orientation with the normal on the facet. ''' mesh = V.mesh() fdim = mesh.topology().dim() - 1 if trace_mesh is None: trace_mesh = TV.mesh() # Init/extract entity map assert get_entity_map(mesh, trace_mesh) # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][fdim] # Map cell of TV to cells of V mesh.init(fdim, fdim+1) f2c = mesh.topology()(fdim, fdim+1) # Facets of V to cell of V # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) gdim = mesh.geometry().dim() # Rows visited_dofs = [False]*TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in range(trace_mesh.num_cells()): TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # Figure out the dofs of V to use here facet_cells = f2c(mapping[trace_cell]) assert 0 < len(facet_cells) < 3 # Ignore boundary facets if len(facet_cells) == 1: cell = facet_cells[0] # Search which cell has the right sign else: signs = [] for fcell in facet_cells: t_mp = Cell(trace_mesh, trace_cell).midpoint().array()[:gdim] mp = Cell(mesh, fcell).midpoint().array()[:gdim] sign = '+' if np.inner(mp - t_mp, normal(t_mp)) > 0 else '-' signs.append(sign) cell = facet_cells[signs.index(restriction)] V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = TV_dof.eval(V_basis_f) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') # Insert mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) return mat
def cylinder_average_matrix(V, TV, radius, quad_degree): '''Averaging matrix''' mesh = V.mesh() line_mesh = TV.mesh() # We are going to perform the integration with Gauss quadrature at # the end (PI u)(x): # A cell of mesh (an edge) defines a normal vector. Let P be the plane # that is defined by the normal vector n and some point x on Gamma. Let L # be the circle that is the intersect of P and S. The value of q (in Q) at x # is defined as # # q(x) = (1/|L|)*\int_{L}g(x)*dL # # which simplifies to g(x) = (1/(2*pi*R))*\int_{-pi}^{pi}u(L)*R*d(theta) and # or = (1/2) * \int_{-1}^{1} u (L(pi*s)) * ds # This can be integrated no problemo once we figure out L. To this end, let # t_1 and t_2 be two unit mutually orthogonal vectors that are orthogonal to # n. Then L(pi*s) = p + R*t_1*cos(pi*s) + R*t_2*sin(pi*s) can be seen to be # such that i) |x-p| = R and ii) x.n = 0 [i.e. this the suitable # parametrization] # Clearly we can scale the weights as well as precompute # cos and sin terms. xq, wq = leggauss(quad_degree) wq *= 0.5 cos_xq = np.cos(np.pi * xq).reshape((-1, 1)) sin_xq = np.sin(np.pi * xq).reshape((-1, 1)) if is_number(radius): radius = lambda x, radius=radius: radius mesh_x = TV.mesh().coordinates() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() # Eval at points will require serch tree = mesh.bounding_box_tree() limit = mesh.num_cells() TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension() * value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # Get the tangent => orthogonal tangent vectors v0, v1 = mesh_x[line_cell.entities(0)] n = v0 - v1 t1 = np.array([n[1] - n[2], n[2] - n[0], n[0] - n[1]]) t2 = np.cross(n, t1) t1 /= np.linalg.norm(t1) t2 = t2 / np.linalg.norm(t2) # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # Get radius and integration points rad = radius(avg_point) integration_points = avg_point + rad * t1 * sin_xq + rad * t2 * cos_xq data = {} for index, ip in enumerate(integration_points): c = tree.compute_first_entity_collision(Point(*ip)) if c >= limit: continue Vcell = Cell(mesh, c) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() Vel.evaluate_basis_all(basis_values, ip, vertex_coordinates, cell_orientation) cols_ip = V_dm.cell_dofs(c) values_ip = basis_values * wq[index] # Add for col, value in zip(cols_ip, values_ip.reshape((-1, value_size))): if col in data: data[col] += value else: data[col] = value # The thing now that with data we can assign to several # rows of the matrix column_indices = np.array(data.keys(), dtype='int32') for shift in range(value_size): row = scalar_row + shift column_values = np.array( [data[col][shift] for col in column_indices]) mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return PETScMatrix(mat)
def sphere_average_matrix(V, TV, radius, quad_degree): '''Averaging matrix over the sphere''' mesh = V.mesh() line_mesh = TV.mesh() # Lebedev below need off degrees if quad_degree % 2 == 0: quad_degree += 1 # NOTE: this is a dependency from quadpy.sphere import Lebedev integrator = Lebedev(quad_degree) xq = integrator.points wq = integrator.weights if is_number(radius): radius = lambda x, radius=radius: radius mesh_x = TV.mesh().coordinates() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() # Eval at points will require serch tree = mesh.bounding_box_tree() limit = mesh.num_cells() TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension() * value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # Get radius and integration points rad = radius(avg_point) # Scale and shift the unit sphere to the point integration_points = xq * rad + avg_point data = {} for index, ip in enumerate(integration_points): c = tree.compute_first_entity_collision(Point(*ip)) if c >= limit: continue Vcell = Cell(mesh, c) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() Vel.evaluate_basis_all(basis_values, ip, vertex_coordinates, cell_orientation) cols_ip = V_dm.cell_dofs(c) values_ip = basis_values * wq[index] # Add for col, value in zip(cols_ip, values_ip.reshape((-1, value_size))): if col in data: data[col] += value else: data[col] = value # The thing now that with data we can assign to several # rows of the matrix column_indices = np.array(data.keys(), dtype='int32') for shift in range(value_size): row = scalar_row + shift column_values = np.array( [data[col][shift] for col in column_indices]) mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return PETScMatrix(mat)
def trace_3d1d_matrix(V, TV, reduced_mesh): '''Trace from 3d to 1d. Makes sense only for CG space''' assert reduced_mesh.id() == TV.mesh().id() assert V.ufl_element().family() == 'Lagrange' mesh = V.mesh() line_mesh = TV.mesh() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() # We use the map to get (1d cell -> [3d edge) -> 3d cell] if hasattr(reduced_mesh, 'parent_entity_map'): # ( ) mapping = reduced_mesh.parent_entity_map[mesh.id()][1] # [ ] mesh.init(1) mesh.init(1, 3) e2c = mesh.topology()(1, 3) # From 1d cell (by index) get_cell3d = lambda c, d1d3=mapping, d3d3=e2c: d3d3(d1d3[c.index()])[0] # Tree collision by midpoint else: tree = mesh.bounding_box_tree() limit = mesh.num_cells() get_cell3d = lambda c, tree=tree, bound=limit: ( lambda index: index if index < bound else None)( tree.compute_first_entity_collision(c.midpoint())) TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension() * value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # Get the tangent => orthogonal tangent vectors # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] # Let's get a 3d cell to use for getting the V values # CG assumption allows taking any tet_cell = get_cell3d(line_cell) if tet_cell is None: continue Vcell = Cell(mesh, tet_cell) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = 0 # Columns are determined by V cell! I guess the sparsity # could be improved if for x_dofs of TV only x_dofs of V # were considered column_indices = np.array(V_dm.cell_dofs(tet_cell), dtype='int32') for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # 3d at point Vel.evaluate_basis_all(basis_values, avg_point, vertex_coordinates, cell_orientation) # The thing now is that with data we can assign to several # rows of the matrix. Shift determines the (x, y, ... ) or # (xx, xy, yx, ...) component of Q data = basis_values.reshape((-1, value_size)).T for shift, column_values in enumerate(data): row = scalar_row + shift mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return PETScMatrix(mat)
def average_matrix(V, TV, shape): ''' Averaging matrix for reduction of g in V to TV by integration over shape. ''' # We build a matrix representation of u in V -> Pi(u) in TV where # # Pi(u)(s) = |L(s)|^-1*\int_{L(s)}u(t) dx(s) # # Here L is the shape over which u is integrated for reduction. # Its measure is |L(s)|. mesh_x = TV.mesh().coordinates() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() mesh = V.mesh() # Eval at points will require serch tree = mesh.bounding_box_tree() limit = mesh.num_cells() TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) line_mesh = TV.mesh() TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension() * value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # Get the tangent (normal of the plane which cuts the virtual # surface to yield the bdry curve v0, v1 = mesh_x[line_cell.entities(0)] n = v0 - v1 # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # Avg point here has the role of 'height' coordinate quadrature = shape.quadrature(avg_point, n) integration_points = quadrature.points wq = quadrature.weights curve_measure = sum(wq) data = {} for index, ip in enumerate(integration_points): c = tree.compute_first_entity_collision(Point(*ip)) if c >= limit: continue cs = tree.compute_entity_collisions(Point(*ip)) # assert False for c in cs[:1]: Vcell = Cell(mesh, c) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() basis_values[:] = Vel.evaluate_basis_all( ip, vertex_coordinates, cell_orientation) cols_ip = V_dm.cell_dofs(c) values_ip = basis_values * wq[index] # Add for col, value in zip( cols_ip, values_ip.reshape((-1, value_size))): if col in data: data[col] += value / curve_measure else: data[col] = value / curve_measure # The thing now that with data we can assign to several # rows of the matrix column_indices = np.array(list(data.keys()), dtype='int32') for shift in range(value_size): row = scalar_row + shift column_values = np.array( [data[col][shift] for col in column_indices]) mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return mat
def nonconforming_trace_mat(V, T): ''' Matrix taking function f from d dim space V to g in (d-1) space T. T(f) ~ g should hold. ''' # For this to work I only make sure that function values are the same assert V.ufl_element().value_shape() == T.ufl_element().value_shape() # I want to evaluate T degrees of freedom at V basis functions, i.e. # L^T_k{phi^V_l}. The picture is # # ------ # \ /\ # ===\==/==\====T # \/----\ # # and thus is assume that each dof of T space (row) will involve # collisions with several cells/basis function of V space. However # here we only snap to the first one mesh = V.mesh() # The (d-1)trace mesh tree = mesh.bounding_box_tree() limit = mesh.num_entities_global(mesh.topology().dim()) Tdm = T.dofmap() elm_T = T.element() # Colliding cells with trace dofs collisions = [] for Tdof_x in T.tabulate_dof_coordinates().reshape((T.dim(), -1)): c = tree.compute_first_entity_collision(df.Point(*Tdof_x)) # Contained? c >= limit and df.warning('Some colliding cells not found') collisions.append(c) # So we fill rows by checking basis functions of on the isected cells Vdm = V.dofmap() elm_V = V.element() V_basis_function = FEBasisFunction(V) T_degree_of_freedom = DegreeOfFreedom(T) X_T = T.tabulate_dof_coordinates().reshape((T.dim(), -1)) X_V = V.tabulate_dof_coordinates().reshape((V.dim(), -1)) visited_dofs = np.zeros(T.dim(), dtype=bool) col_values = np.zeros(V_basis_function.elm.space_dimension(), dtype='double') with petsc_serial_matrix(T, V) as mat: for Tcell in range(T.mesh().num_cells()): # Set for this cell T_degree_of_freedom.cell = Tcell Tdofs = Tdm.cell_dofs(Tcell) for local_T, Tdof in enumerate(Tdofs): # Seen the row? if visited_dofs[Tdof]: continue visited_dofs[Tdof] = True # Set to current dof T_degree_of_freedom.dof = local_T # Now all the V cells and their basis functions c = collisions[Tdof] # If we have no reasonable cell leave the row empty if c >= limit: continue # Set the dof cell V_basis_function.cell = c Vdofs = np.array(Vdm.cell_dofs(c), dtype='int32') # These are columns # Fill column for local_V, Vdof in enumerate(Vdofs): # Set as basis_function V_basis_function.dof = local_V # Evaluate trace dof at basis function dof_value = T_degree_of_freedom.eval(V_basis_function) col_values[local_V] = dof_value mat.setValues([Tdof], Vdofs, col_values, PETSc.InsertMode.INSERT_VALUES) return mat
def trace_mat_two_restrict(V, TV, restriction, normal, trace_mesh=None): ''' Compute the trace values using avg/jump restriction. A + plus is the one for which the vector cell.midpoint - facet.midpoint agrees in orientation with the normal on the facet. ''' mesh = V.mesh() fdim = mesh.topology().dim() - 1 if trace_mesh is None: trace_mesh = TV.mesh() # Init/extract entity map assert get_entity_map(mesh, trace_mesh) # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][fdim] # Map cell of TV to cells of V mesh.init(fdim, fdim+1) f2c = mesh.topology()(fdim, fdim+1) # Facets of V to cell of V # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) # We define avg as sum(+, -)/2 and jump as sum(+, neg(-)) operator_pieces = {'avg': (lambda x: x/2, lambda x: x/2), 'jump': (lambda x: x, lambda x: -x)}[restriction] gdim = mesh.geometry().dim() # Rows visited_dofs = [False]*TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in range(trace_mesh.num_cells()): TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # Figure out the dofs of V to use here facet_cells = f2c(mapping[trace_cell]) assert 0 < len(facet_cells) < 3 # Ignore boundary facets if len(facet_cells) == 1: facet_cells = [facet_cells[0]] modifiers = (lambda x: x, ) # Do nothing # Search which cell has the right sign else: signs = [None, None] # Order such that '+' is first t_mp = Cell(trace_mesh, trace_cell).midpoint().array()[:gdim] mp = Cell(mesh, facet_cells[0]).midpoint().array()[:gdim] sign = '+' if np.inner(mp - t_mp, normal(t_mp)) > 0 else '-' # Need to flip if sign == '-': facet_cells = facet_cells[::-1] # As requested modifiers = operator_pieces for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Two sweeps to set the values in the row ADD_VALUES = False for modify, cell in zip(modifiers, facet_cells): V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = modify(TV_dof.eval(V_basis_f)) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') if not ADD_VALUES: mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) ADD_VALUES = True #print 'setting', dof_T, col_indices, dof_values else: mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.ADD_VALUES) #print 'adding', dof_T, col_indices, dof_values return mat
def trace_mat_two_restrict(V, TV, restriction, normal, trace_mesh=None): ''' Compute the trace values using avg/jump restriction. A + plus is the one for which the vector cell.midpoint - facet.midpoint agrees in orientation with the normal on the facet. ''' mesh = V.mesh() fdim = mesh.topology().dim() - 1 if trace_mesh is None: trace_mesh = TV.mesh() # Init/extract entity map assert get_entity_map(mesh, trace_mesh) # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][ fdim] # Map cell of TV to cells of V mesh.init(fdim, fdim + 1) f2c = mesh.topology()(fdim, fdim + 1) # Facets of V to cell of V # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) # We define avg as sum(+, -)/2 and jump as sum(+, neg(-)) operator_pieces = { 'avg': (lambda x: x / 2, lambda x: x / 2), 'jump': (lambda x: x, lambda x: -x) }[restriction] gdim = mesh.geometry().dim() # Rows visited_dofs = [False] * TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in range(trace_mesh.num_cells()): TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # Figure out the dofs of V to use here facet_cells = f2c(mapping[trace_cell]) assert 0 < len(facet_cells) < 3 # Ignore boundary facets if len(facet_cells) == 1: facet_cells = [facet_cells[0]] modifiers = (lambda x: x, ) # Do nothing # Search which cell has the right sign else: signs = [None, None] # Order such that '+' is first t_mp = Cell(trace_mesh, trace_cell).midpoint().array()[:gdim] mp = Cell(mesh, facet_cells[0]).midpoint().array()[:gdim] sign = '+' if np.inner(mp - t_mp, normal(t_mp)) > 0 else '-' # Need to flip if sign == '-': facet_cells = facet_cells[::-1] # As requested modifiers = operator_pieces for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Two sweeps to set the values in the row ADD_VALUES = False for modify, cell in zip(modifiers, facet_cells): V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = modify(TV_dof.eval(V_basis_f)) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') if not ADD_VALUES: mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) ADD_VALUES = True #print 'setting', dof_T, col_indices, dof_values else: mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.ADD_VALUES) #print 'adding', dof_T, col_indices, dof_values return mat
def nonconforming_trace_mat(V, T): ''' Matrix taking function f from d dim space V to g in (d-1) space T. T(f) ~ g should hold. ''' # For this to work I only make sure that function values are the same assert V.dolfin_element().value_rank() == T.dolfin_element().value_rank() assert V.ufl_element().value_shape() == T.ufl_element().value_shape() # I want to evaluate T degrees of freedom at V basis functions, i.e. # L^T_k{phi^V_l}. The picture is # # ------ # \ /\ # ===\==/==\====T # \/----\ # # and thus is assume that each dof of T space (row) will involve # collisions with several cells/basis function of V space mesh = V.mesh() # The (d-1)trace mesh tree = mesh.bounding_box_tree() limit = mesh.topology().size_global(mesh.topology().dim()) Tdm = T.dofmap() elm_T = T.element() # Colliding cells with trace dofs collisions = [] for Tdof_x in T.tabulate_dof_coordinates().reshape((T.dim(), -1)): cs = tree.compute_entity_collisions(Point(*Tdof_x)) if any(c >= limit for c in cs): warning('Some colliding cells not found') cs = filter(lambda c: c < limit, cs) collisions.append(cs) # So we fill rows by checking basis functions of on the isected cells Vdm = V.dofmap() elm_V = V.element() V_basis_function = FEBasisFunction(V) T_degree_of_freedom = DegreeOfFreedom(T) visited_dofs = [False]*T.dim() with petsc_serial_matrix(T, V) as mat: for Tcell in range(T.mesh().num_cells()): # Set for this cell T_degree_of_freedom.cell = Tcell Tdofs = Tdm.cell_dofs(Tcell) for local_T, Tdof in enumerate(Tdofs): # Seen the row? if visited_dofs[Tdof]: continue visited_dofs[Tdof] = True # Set to current dof T_degree_of_freedom.dof = local_T col_indices, col_values = [], [] # Now all the V cells and their basis functions for c in collisions[Tdof]: # Set the dof cell V_basis_function.cell = c Vdofs = Vdm.cell_dofs(c) # Columns for local_V, Vdof in enumerate(Vdofs): if Vdof in col_indices: continue # Set as basis_function V_basis_function.dof = local_V # Evaluate trace dof at basis function dof_value = T_degree_of_freedom.eval(V_basis_function) col_indices.append(Vdof) col_values.append(dof_value) # Can fill the matrix row col_indices = np.array(col_indices, dtype='int32') col_values = np.array(col_values) mat.setValues([Tdof], col_indices, col_values, PETSc.InsertMode.INSERT_VALUES) return mat
def trace_mat_no_restrict(V, TV, trace_mesh=None): '''The first cell connected to the facet gets to set the values of TV''' mesh = V.mesh() if trace_mesh is None: trace_mesh = TV.mesh() fdim = trace_mesh.topology().dim() # Init/extract the mapping try: assert get_entity_map(mesh, trace_mesh) except AssertionError: warning('Using non-conforming trace') return nonconforming_trace_mat(V, TV) # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][fdim] # Map cell of TV to cells of V mesh.init(fdim, fdim+1) f2c = mesh.topology()(fdim, fdim+1) # Facets of V to cell of V # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) # Rows visited_dofs = [False]*TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in range(TV.mesh().num_cells()): TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # Figure out the dofs of V to use here. Does not matter which # cell of the connected ones we pick cell = f2c(mapping[trace_cell])[0] V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = TV_dof.eval(V_basis_f) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') # Insert mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) return mat
def trace_mat_no_restrict(V, TV, trace_mesh=None, tag_data=None): '''The first cell connected to the facet gets to set the values of TV''' mesh = V.mesh() if trace_mesh is None: trace_mesh = TV.mesh() fdim = trace_mesh.topology().dim() # None means all if tag_data is None: tag_data = (MeshFunction('size_t', trace_mesh, trace_mesh.topology().dim(), 0), set((0, ))) trace_mesh_subdomains, tags = tag_data # Init/extract the mapping try: assert get_entity_map(mesh, trace_mesh, trace_mesh_subdomains, tags) except (AssertionError, IndexError): warning('Using non-conforming trace') # So non-conforming matrix returns PETSc.Mat return nonconforming_trace_mat(V, TV) # We can get it mapping = trace_mesh.parent_entity_map[mesh.id()][ fdim] # Map cell of TV to cells of V mesh.init(fdim, fdim + 1) f2c = mesh.topology()(fdim, fdim + 1) # Facets of V to cell of V # The idea is to evaluate TV's degrees of freedom at basis functions # of V Tdmap = TV.dofmap() TV_dof = DegreeOfFreedom(TV) dmap = V.dofmap() V_basis_f = FEBasisFunction(V) # Only look at tagged cells trace_cells = itertools.chain(*[ itertools.imap(operator.methodcaller('index'), SubsetIterator(trace_mesh_subdomains, tag)) for tag in tags ]) # Rows visited_dofs = [False] * TV.dim() # Column values dof_values = np.zeros(V_basis_f.elm.space_dimension(), dtype='double') with petsc_serial_matrix(TV, V) as mat: for trace_cell in trace_cells: # We might TV_dof.cell = trace_cell trace_dofs = Tdmap.cell_dofs(trace_cell) # Figure out the dofs of V to use here. Does not matter which # cell of the connected ones we pick cell = f2c(mapping[trace_cell])[0] V_basis_f.cell = cell dofs = dmap.cell_dofs(cell) for local_T, dof_T in enumerate(trace_dofs): if visited_dofs[dof_T]: continue else: visited_dofs[dof_T] = True # Define trace dof TV_dof.dof = local_T # Eval at V basis functions for local, dof in enumerate(dofs): # Set which basis foo V_basis_f.dof = local dof_values[local] = TV_dof.eval(V_basis_f) # Can fill the matrix now col_indices = np.array(dofs, dtype='int32') # Insert mat.setValues([dof_T], col_indices, dof_values, PETSc.InsertMode.INSERT_VALUES) return mat
def surface_average_matrix(V, TV, bdry_curve): '''Averaging matrix''' mesh = V.mesh() line_mesh = TV.mesh() # We build a matrix representation of u in V -> Pi(u) in TV where # # Pi(u)(s) = |L(s)|^-1*\int_{L(s)}u(t) dL(s) # # Here L represents a curve bounding the surface at 'height' s. # # We do this numerically as |L(s)|^-1*\sum_q u(x_q)*w_q # Weights remaing fixed wq = bdry_curve.weights mesh_x = TV.mesh().coordinates() # The idea for point evaluation/computing dofs of TV is to minimize # the number of evaluation. I mean a vector dof if done naively would # have to evaluate at same x number of component times. value_size = TV.ufl_element().value_size() # Eval at points will require serch tree = mesh.bounding_box_tree() limit = mesh.num_cells() TV_coordinates = TV.tabulate_dof_coordinates().reshape((TV.dim(), -1)) TV_dm = TV.dofmap() V_dm = V.dofmap() # For non scalar we plan to make compoenents by shift if value_size > 1: TV_dm = TV.sub(0).dofmap() Vel = V.element() basis_values = np.zeros(V.element().space_dimension() * value_size) with petsc_serial_matrix(TV, V) as mat: for line_cell in cells(line_mesh): # Get the tangent (normal of the plane which cuts the virtual # surface to yield the bdry curve v0, v1 = mesh_x[line_cell.entities(0)] n = v0 - v1 # We can specialize quadrature points; we can have several # height points with same normal pts_at_n = bdry_curve.points(n) len_at_n = bdry_curve.length(n) # The idea is now to minimize the point evaluation scalar_dofs = TV_dm.cell_dofs(line_cell.index()) scalar_dofs_x = TV_coordinates[scalar_dofs] for scalar_row, avg_point in zip(scalar_dofs, scalar_dofs_x): # Avg point here has the role of 'height' coordinate integration_points = pts_at_n(avg_point) len_bdry_curve = len_at_n(avg_point) data = {} for index, ip in enumerate(integration_points): c = tree.compute_first_entity_collision(Point(*ip)) if c >= limit: continue Vcell = Cell(mesh, c) vertex_coordinates = Vcell.get_vertex_coordinates() cell_orientation = Vcell.orientation() Vel.evaluate_basis_all(basis_values, ip, vertex_coordinates, cell_orientation) cols_ip = V_dm.cell_dofs(c) values_ip = basis_values * wq[index] # Add for col, value in zip(cols_ip, values_ip.reshape((-1, value_size))): if col in data: data[col] += value / len_bdry_curve else: data[col] = value / len_bdry_curve # The thing now that with data we can assign to several # rows of the matrix column_indices = np.array(data.keys(), dtype='int32') for shift in range(value_size): row = scalar_row + shift column_values = np.array( [data[col][shift] for col in column_indices]) mat.setValues([row], column_indices, column_values, PETSc.InsertMode.INSERT_VALUES) # On to next avg point # On to next cell return PETScMatrix(mat)