def create_mapping(self, kind, ig): """ Create mapping from reference elements to physical elements, given the integration kind ('v' or 's'). This mapping can be used to compute the physical quadrature points. Returns ------- mapping : VolumeMapping or SurfaceMapping instance The requested mapping. """ from sfepy.fem.mappings import VolumeMapping, SurfaceMapping from sfepy.fem.fe_surface import FESurface coors = self.domain.get_mesh_coors() if kind == 's': coors = coors[self.all_vertices] gel = self.domain.groups[ig].gel conn = self.domain.groups[ig].conn if kind == 'v': cells = self.cells[ig] mapping = VolumeMapping(coors, conn[cells], gel=gel) elif kind == 's': aux = FESurface('aux', self, gel.get_surface_entities(), conn, ig) mapping = SurfaceMapping(coors, aux.leconn, gel=gel.surface_facet) return mapping
def create_mapping(coors, gel, order): """ Create mapping from transformed (in `x-y` plane) element faces to reference element faces. Parameters ---------- coors : array The transformed coordinates of element nodes, shape `(n_el, n_ep, dim)`. The function verifies that the all `z` components are zero. gel : GeometryElement instance The geometry element corresponding to the faces. order : int The polynomial order of the mapping. Returns ------- mapping : VolumeMapping instance The reference element face mapping. """ # Strip 'z' component (should be 0 now...). assert_(nm.allclose(coors[:, :, -1], 0.0, rtol=1e-12, atol=1e-12)) coors = coors[:, :, :-1].copy() # Mapping from transformed element to reference element. sh = coors.shape seq_coors = coors.reshape((sh[0] * sh[1], sh[2])) seq_conn = nm.arange(seq_coors.shape[0], dtype=nm.int32) seq_conn.shape = sh[:2] mapping = VolumeMapping(seq_coors, seq_conn, gel=gel, order=1) return mapping
def test_gradients(self): from sfepy.fem.mappings import VolumeMapping ok = True order = 3 bads = [] bad_families = set() for (geom, poly_space_base, qp_weights, mesh, ir, ic, ap, ps, rrc, crc, vec, edofs, fdofs) in _gen_common_data(order, self.gels, self.report): conn = mesh.conns[0] gel = self.gels[geom] geo_ps = ap.interp.get_geom_poly_space('v') rmapping = VolumeMapping(mesh.coors, conn[:1], poly_space=geo_ps) rori = ap.ori[:1] if ap.ori is not None else None rvg = rmapping.get_mapping(rrc, qp_weights, poly_space=ps, ori=rori) rbfg = rvg.bfg cmapping = VolumeMapping(mesh.coors, conn[1:], poly_space=geo_ps) cori = ap.ori[1:] if ap.ori is not None else None cvg = cmapping.get_mapping(crc, qp_weights, poly_space=ps, ori=cori) cbfg = cvg.bfg dofs = nm.r_[edofs, fdofs] res = nm.zeros((2, dofs.shape[0]), dtype=nm.int32) res[0, :] = dofs for ii, ip in enumerate(dofs): vec.fill(0.0) vec[ip] = 1.0 evec = vec[ap.econn] rvals = nm.dot(rbfg, evec[0])[0] cvals = nm.dot(cbfg, evec[1])[0] okx = nm.allclose(rvals[:, 0], cvals[:, 0], atol=1e-14, rtol=0.0) if gel.dim == 2: oky = nm.allclose(rvals[:, 1], -cvals[:, 1], atol=1e-14, rtol=0.0) _ok = okx and oky else: oky = nm.allclose(rvals[:, 1], cvals[:, 1], atol=1e-14, rtol=0.0) okz = nm.allclose(rvals[:, 2], -cvals[:, 2], atol=1e-14, rtol=0.0) _ok = okx and oky and okz res[1, ii] = _ok if not _ok: bads.append([geom, poly_space_base, ir, ic, ip]) bad_families.add((geom, poly_space_base)) ok = ok and _ok self.report('results (dofs, status: 1 ok, 0 failure):\n%s' % res) if not ok: self.report('gradient continuity errors:\n', bads) self.report('%d in total!' % len(bads)) self.report('gradient continuity errors occurred in these' ' spaces:\n', bad_families) return ok
def describe_geometry(self, field, gtype, region, integral=None, return_mapping=False): """ Compute jacobians, element volumes and base function derivatives for Volume-type geometries (volume mappings), and jacobians, normals and base function derivatives for Surface-type geometries (surface mappings). Notes ----- - volume mappings can be defined on a part of an element group, although the field has to be defined always on the whole group. - surface mappings are defined on the surface region - surface mappings require field order to be > 0 """ domain = field.domain group = domain.groups[self.ig] coors = domain.get_mesh_coors(actual=True) if gtype == 'volume': if integral is None: from sfepy.fem import Integral integral = Integral('i_tmp', 'v', 1) qp = self.get_qp('v', integral) iels = region.cells[self.ig] geo_ps = self.interp.get_geom_poly_space('v') ps = self.interp.poly_spaces['v'] bf = self.get_base('v', 0, integral, iels=iels) conn = nm.take(group.conn, iels, axis=0) mapping = VolumeMapping(coors, conn, poly_space=geo_ps) vg = mapping.get_mapping(qp.vals, qp.weights, poly_space=ps, ori=self.ori) out = vg elif (gtype == 'surface') or (gtype == 'surface_extra'): assert_(field.approx_order > 0) if self.ori is not None: msg = 'surface integrals do not work yet with the' \ ' hierarchical basis!' raise ValueError(msg) sd = domain.surface_groups[self.ig][region.name] esd = self.surface_data[region.name] qp = self.get_qp(sd.face_type, integral) geo_ps = self.interp.get_geom_poly_space(sd.face_type) ps = self.interp.poly_spaces[esd.face_type] bf = self.get_base(esd.face_type, 0, integral) conn = sd.get_connectivity() mapping = SurfaceMapping(coors, conn, poly_space=geo_ps) sg = mapping.get_mapping(qp.vals, qp.weights, poly_space=ps) if gtype == 'surface_extra': sg.alloc_extra_data(self.get_v_data_shape()[2]) self.create_bqp(region.name, integral) qp = self.qp_coors[(integral.name, esd.bkey)] v_geo_ps = self.interp.get_geom_poly_space('v') bf_bg = v_geo_ps.eval_base(qp.vals, diff=True) ebf_bg = self.get_base(esd.bkey, 1, integral) sg.evaluate_bfbgm(bf_bg, ebf_bg, coors, sd.fis, group.conn) out = sg elif gtype == 'point': out = mapping = None else: raise ValueError('unknown geometry type: %s' % gtype) if out is not None: # Store the integral used. out.integral = integral out.qp = qp out.ps = ps # Update base. out.bf[:] = bf if return_mapping: out = (out, mapping) return out
def test_gradients(self): from sfepy.fem.mappings import VolumeMapping ok = True orders = {'2_3' : 3, '2_4' : 3, '3_4' : 4, '3_8' : 3} bads = [] bad_families = set() for (geom, poly_space_base, qp_weights, mesh, ir, ic, ap, ps, rrc, rcell, crc, ccell, vec, edofs, fdofs) in _gen_common_data(orders, self.gels, self.report): conn = mesh.conns[0] gel = self.gels[geom] geo_ps = ap.interp.get_geom_poly_space('v') rmapping = VolumeMapping(mesh.coors, conn[rcell:rcell+1], poly_space=geo_ps) rori = ap.ori[:1] if ap.ori is not None else None rvg = rmapping.get_mapping(rrc, qp_weights, poly_space=ps, ori=rori) rbfg = rvg.bfg cmapping = VolumeMapping(mesh.coors, conn[ccell:ccell+1], poly_space=geo_ps) cori = ap.ori[1:] if ap.ori is not None else None cvg = cmapping.get_mapping(crc, qp_weights, poly_space=ps, ori=cori) cbfg = cvg.bfg dofs = nm.r_[edofs, fdofs] res = nm.zeros((2, dofs.shape[0]), dtype=nm.int32) res[0, :] = dofs for ii, ip in enumerate(dofs): vec.fill(0.0) vec[ip] = 1.0 evec = vec[ap.econn] rvals = nm.dot(rbfg, evec[rcell])[0] cvals = nm.dot(cbfg, evec[ccell])[0] okx = nm.allclose(rvals[:, 0], cvals[:, 0], atol=1e-12, rtol=0.0) if gel.dim == 2: oky = nm.allclose(rvals[:, 1], -cvals[:, 1], atol=1e-12, rtol=0.0) _ok = okx and oky else: oky = nm.allclose(rvals[:, 1], cvals[:, 1], atol=1e-12, rtol=0.0) okz = nm.allclose(rvals[:, 2], -cvals[:, 2], atol=1e-12, rtol=0.0) _ok = okx and oky and okz res[1, ii] = _ok if not _ok: bads.append([geom, poly_space_base, ir, ic, ip]) bad_families.add((geom, poly_space_base)) ok = ok and _ok self.report('results (dofs, status: 1 ok, 0 failure):\n%s' % res) if not ok: self.report('gradient continuity errors:\n', bads) self.report('%d in total!' % len(bads)) self.report('gradient continuity errors occurred in these' ' spaces:\n', bad_families) return ok
def describe_dual_surface(self, surface): n_fa, n_edge = surface.n_fa, self.sgel.n_edge mesh_coors = self.mesh_coors # Face centres. fcoors = mesh_coors[surface.econn] centre_coors = nm.dot(self.bf.squeeze(), fcoors) surface_coors = mesh_coors[surface.nodes] dual_coors = nm.r_[surface_coors, centre_coors] coor_offset = surface.nodes.shape[0] # Normals in primary mesh nodes. nodal_normals = compute_nodal_normals(surface.nodes, self.region, self.field) ee = surface.leconn[:, self.sgel.edges].copy() edges_per_face = ee.copy() sh = edges_per_face.shape ee.shape = edges_per_face.shape = (sh[0] * sh[1], sh[2]) edges_per_face.sort(axis=1) eo = nm.empty((sh[0] * sh[1], ), dtype=nm.object) eo[:] = [tuple(ii) for ii in edges_per_face] ueo, e_sort, e_id = unique(eo, return_index=True, return_inverse=True) ueo = edges_per_face[e_sort] # edge centre, edge point 1, face centre, edge point 2 conn = nm.empty((n_edge * n_fa, 4), dtype=nm.int32) conn[:, 0] = e_id conn[:, 1] = ee[:, 0] conn[:,2] = nm.repeat(nm.arange(n_fa, dtype=nm.int32), n_edge) \ + coor_offset conn[:, 3] = ee[:, 1] # face centre, edge point 2, edge point 1 tri_conn = nm.ascontiguousarray(conn[:, [2, 1, 3]]) # Ensure orientation - outward normal. cc = dual_coors[tri_conn] v1 = cc[:, 1] - cc[:, 0] v2 = cc[:, 2] - cc[:, 0] normals = nm.cross(v1, v2) nn = nodal_normals[surface.leconn].sum(axis=1).repeat(n_edge, 0) centre_normals = (1.0 / surface.n_fp) * nn centre_normals /= la.norm_l2_along_axis(centre_normals)[:, None] dot = nm.sum(normals * centre_normals, axis=1) assert_((dot > 0.0).all()) # Prepare mapping from reference triangle e_R to a # triangle within reference face e_D. gel = self.gel.surface_facet ref_coors = gel.coors ref_centre = nm.dot(self.bf.squeeze(), ref_coors) cc = nm.r_[ref_coors, ref_centre[None, :]] rconn = nm.empty((n_edge, 3), dtype=nm.int32) rconn[:, 0] = gel.n_vertex rconn[:, 1] = gel.edges[:, 0] rconn[:, 2] = gel.edges[:, 1] map_er_ed = VolumeMapping(cc, rconn, gel=gel) # Prepare mapping from reference triangle e_R to a # physical triangle e. map_er_e = SurfaceMapping(dual_coors, tri_conn, gel=gel) # Compute triangle basis (edge) vectors. nn = surface.nodes[ueo] edge_coors = mesh_coors[nn] edge_centre_coors = 0.5 * edge_coors.sum(axis=1) edge_normals = 0.5 * nodal_normals[ueo].sum(axis=1) edge_normals /= la.norm_l2_along_axis(edge_normals)[:, None] nn = surface.nodes[ueo] edge_dirs = edge_coors[:, 1] - edge_coors[:, 0] edge_dirs /= la.norm_l2_along_axis(edge_dirs)[:, None] edge_ortho = nm.cross(edge_normals, edge_dirs) edge_ortho /= la.norm_l2_along_axis(edge_ortho)[:, None] # Primary face - dual sub-faces map. # i-th row: indices to conn corresponding to sub-faces of i-th face. face_map = nm.arange(n_fa * n_edge, dtype=nm.int32) face_map.shape = (n_fa, n_edge) # The actual connectivity for assembling (unique nodes per master # faces). asm_conn = e_id[face_map] n_nod = ueo.shape[0] # One node per unique edge. n_components = self.dim - 1 dual_surface = Struct(name='dual_surface_description', dim=self.dim, n_dual_fa=conn.shape[0], n_dual_fp=self.dim, n_fa=n_fa, n_edge=n_edge, n_nod=n_nod, n_components=n_components, n_dof=n_nod * n_components, dual_coors=dual_coors, coor_offset=coor_offset, e_sort=e_sort, conn=conn, tri_conn=tri_conn, map_er_e=map_er_e, map_er_ed=map_er_ed, face_map=face_map, asm_conn=asm_conn, nodal_normals=nodal_normals, edge_centre_coors=edge_centre_coors, edge_normals=edge_normals, edge_dirs=edge_dirs, edge_ortho=edge_ortho) return dual_surface
def describe_geometry(self, field, gtype, region, integral=None, coors=None): """Compute jacobians, element volumes and base function derivatives for Volume-type geometries, and jacobians, normals and base function derivatives for Surface-type geometries. """ if coors is None: coors = field.aps.coors if gtype == 'volume': if integral is None: from sfepy.fem import Integral dim = field.domain.shape.dim quad_name = 'gauss_o1_d%d' % dim integral = Integral('i_tmp', 'v', quad_name) qp = self.get_qp('v', integral) mapping = VolumeMapping(coors, self.econn, poly_space=self.interp.poly_spaces['v']) try: vg = mapping.get_mapping(qp.vals, qp.weights) except ValueError: # Constant bubble. domain = self.region.domain group = domain.groups[self.ig] coors = domain.get_mesh_coors() ps = self.interp.gel.interp.poly_spaces['v'] mapping = VolumeMapping(coors, group.conn, poly_space=ps) vg = mapping.get_mapping(qp.vals, qp.weights) out = vg elif (gtype == 'surface') or (gtype == 'surface_extra'): sd = self.surface_data[region.name] qp = self.get_qp(sd.face_type, integral) if not self.is_surface: ps = self.interp.poly_spaces[sd.face_type] else: ps = self.interp.poly_spaces['v'] mapping = SurfaceMapping(coors, sd.econn, poly_space=ps) sg = mapping.get_mapping(qp.vals, qp.weights) if gtype == 'surface_extra': sg.alloc_extra_data( self.get_v_data_shape()[2] ) self.create_bqp( region.name, integral ) bf_bg = self.get_base(sd.bkey, 1, integral) sg.evaluate_bfbgm( bf_bg, coors, sd.fis, self.econn ) out = sg elif gtype == 'point': out = None else: raise ValueError('unknown geometry type: %s' % gtype) if out is not None: # Store the integral used. out.integral = integral return out