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 describe_geometry(self, field, gtype, region, integral, 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': qp = self.get_qp('v', integral) iels = region.get_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.astype(nm.int32), 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 == 'plate': import sfepy.mechanics.membranes as mm from sfepy.linalg import dot_sequences qp = self.get_qp('v', integral) iels = region.get_cells(self.ig) ps = self.interp.poly_spaces['v'] bf = self.get_base('v', 0, integral, iels=iels) conn = nm.take(group.conn, nm.int32(iels), axis=0) ccoors = coors[conn] # Coordinate transformation matrix (transposed!). mtx_t = mm.create_transformation_matrix(ccoors) # Transform coordinates to the local coordinate system. coors_loc = dot_sequences((ccoors - ccoors[:, 0:1, :]), mtx_t) # Mapping from transformed elements to reference elements. mapping = mm.create_mapping(coors_loc, field.gel, 1) vg = mapping.get_mapping(qp.vals, qp.weights, poly_space=ps, ori=self.ori) vg.mtx_t = mtx_t 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, mode=gtype) if gtype == 'surface_extra': sg.alloc_extra_data(self.n_ep['v']) 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 create_mapping(self, region, integral, integration, return_mapping=True): """ Create a new reference mapping. 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 ----- - surface mappings are defined on the surface region - surface mappings require field order to be > 0 """ domain = self.domain coors = domain.get_mesh_coors(actual=True) dconn = domain.get_conn() if integration == 'volume': qp = self.get_qp('v', integral) iels = region.get_cells() geo_ps = self.gel.poly_space ps = self.poly_space bf = self.get_base('v', 0, integral, iels=iels) conn = nm.take(dconn, iels.astype(nm.int32), 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 integration == 'plate': import sfepy.mechanics.membranes as mm from sfepy.linalg import dot_sequences qp = self.get_qp('v', integral) iels = region.get_cells() ps = self.interp.poly_spaces['v'] bf = self.get_base('v', 0, integral, iels=iels) conn = nm.take(dconn, nm.int32(iels), axis=0) ccoors = coors[conn] # Coordinate transformation matrix (transposed!). mtx_t = mm.create_transformation_matrix(ccoors) # Transform coordinates to the local coordinate system. coors_loc = dot_sequences((ccoors - ccoors[:, 0:1, :]), mtx_t) # Mapping from transformed elements to reference elements. mapping = mm.create_mapping(coors_loc, self.gel, 1) vg = mapping.get_mapping(qp.vals, qp.weights, poly_space=ps, ori=self.ori) vg.mtx_t = mtx_t out = vg elif (integration == 'surface') or (integration == 'surface_extra'): assert_(self.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[region.name] esd = self.surface_data[region.name] geo_ps = self.gel.poly_space ps = self.poly_space conn = sd.get_connectivity() mapping = SurfaceMapping(coors, conn, poly_space=geo_ps) if not self.is_surface: self.create_bqp(region.name, integral) qp = self.qp_coors[(integral.order, esd.bkey)] abf = ps.eval_base(qp.vals[0]) bf = abf[..., self.efaces[0]] indx = self.gel.get_surface_entities()[0] # Fix geometry element's 1st facet orientation for gradients. indx = nm.roll(indx, -1)[::-1] mapping.set_basis_indices(indx) sg = mapping.get_mapping(qp.vals[0], qp.weights, poly_space=Struct(n_nod=bf.shape[-1]), mode=integration) if integration == 'surface_extra': sg.alloc_extra_data(self.econn.shape[1]) bf_bg = 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, dconn) else: # Do not use BQP for surface fields. qp = self.get_qp(sd.face_type, integral) bf = ps.eval_base(qp.vals) sg = mapping.get_mapping(qp.vals, qp.weights, poly_space=Struct(n_nod=bf.shape[-1]), mode=integration) out = sg elif integration == 'point': out = mapping = None else: raise ValueError('unknown inegration geometry type: %s' % integration) 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.discrete.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): gel = self.gels[geom] conn = mesh.get_conn(gel.name) 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 create_mapping(self, region, integral, integration, return_mapping=True): """ Create a new reference mapping. 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 ----- - surface mappings are defined on the surface region - surface mappings require field order to be > 0 """ domain = self.domain coors = domain.get_mesh_coors(actual=True) dconn = domain.get_conn() if integration == 'volume': qp = self.get_qp('v', integral) iels = region.get_cells() geo_ps = self.gel.poly_space ps = self.poly_space bf = self.get_base('v', 0, integral, iels=iels) conn = nm.take(dconn, iels.astype(nm.int32), 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 (integration == 'surface') or (integration == 'surface_extra'): assert_(self.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[region.name] esd = self.surface_data[region.name] geo_ps = self.gel.poly_space ps = self.poly_space conn = sd.get_connectivity() mapping = SurfaceMapping(coors, conn, poly_space=geo_ps) if not self.is_surface: self.create_bqp(region.name, integral) qp = self.qp_coors[(integral.order, esd.bkey)] abf = ps.eval_base(qp.vals[0]) bf = abf[..., self.efaces[0]] indx = self.gel.get_surface_entities()[0] # Fix geometry element's 1st facet orientation for gradients. indx = nm.roll(indx, -1)[::-1] mapping.set_basis_indices(indx) sg = mapping.get_mapping(qp.vals[0], qp.weights, poly_space=Struct(n_nod=bf.shape[-1]), mode=integration) if integration == 'surface_extra': sg.alloc_extra_data(self.econn.shape[1]) bf_bg = 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, dconn) else: # Do not use BQP for surface fields. qp = self.get_qp(sd.face_type, integral) bf = ps.eval_base(qp.vals) sg = mapping.get_mapping(qp.vals, qp.weights, poly_space=Struct(n_nod=bf.shape[-1]), mode=integration) out = sg elif integration == 'point': out = mapping = None elif integration == 'custom': raise ValueError('cannot create custom mapping!') else: raise ValueError('unknown integration geometry type: %s' % integration) 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 describe_geometry(self, field, gtype, region, integral, 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': qp = self.get_qp('v', integral) iels = region.get_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.astype(nm.int32), 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 == 'plate': import sfepy.mechanics.membranes as mm from sfepy.linalg import dot_sequences qp = self.get_qp('v', integral) iels = region.get_cells(self.ig) ps = self.interp.poly_spaces['v'] bf = self.get_base('v', 0, integral, iels=iels) conn = nm.take(group.conn, nm.int32(iels), axis=0) ccoors = coors[conn] # Coordinate transformation matrix (transposed!). mtx_t = mm.create_transformation_matrix(ccoors) # Transform coordinates to the local coordinate system. coors_loc = dot_sequences((ccoors - ccoors[:, 0:1, :]), mtx_t) # Mapping from transformed elements to reference elements. mapping = mm.create_mapping(coors_loc, field.gel, 1) vg = mapping.get_mapping(qp.vals, qp.weights, poly_space=ps, ori=self.ori) vg.mtx_t = mtx_t 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, mode=gtype) if gtype == 'surface_extra': sg.alloc_extra_data(self.n_ep['v']) self.create_bqp(region.name, integral) qp = self.qp_coors[(integral.order, 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 create_mapping(self, region, integral, integration, return_mapping=True): """Creates and returns mapping Parameters ---------- region : sfepy.discrete.common.region.Region integral : Integral integration : str 'volume' is only accepted option return_mapping : default True (Default value = True) Returns ------- mapping : VolumeMapping """ domain = self.domain coors = domain.get_mesh_coors(actual=True) dconn = domain.get_conn() # from FEField if integration == 'volume': qp = self.get_qp('v', integral) # qp = self.integral.get_qp(self.gel.name) iels = region.get_cells() geo_ps = self.gel.poly_space ps = self.poly_space bf = self.get_base('v', 0, integral, iels=iels) conn = nm.take(dconn, iels.astype(nm.int32), axis=0) mapping = VolumeMapping(coors, conn, poly_space=geo_ps) vg = mapping.get_mapping(qp.vals, qp.weights, poly_space=ps, ori=self.ori, transform=self.basis_transform) out = vg else: raise ValueError('unsupported integration geometry type: %s' % integration) 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.discrete.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, field, ps, rrc, rcell, crc, ccell, vec, edofs, fdofs) in _gen_common_data(orders, self.gels, self.report): gel = self.gels[geom] conn = mesh.get_conn(gel.name) geo_ps = field.gel.poly_space rmapping = VolumeMapping(mesh.coors, conn[rcell:rcell + 1], poly_space=geo_ps) rori = field.ori[:1] if field.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 = field.ori[1:] if field.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[field.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