def __init__(self, name, mesh, verbose=False, **kwargs): """Create a Domain. Parameters ---------- name : str Object name. mesh : Mesh A mesh defining the domain. """ Domain.__init__(self, name, mesh=mesh, verbose=verbose, **kwargs) if len(mesh.descs) > 1: msg = 'meshes with several cell kinds are not supported!' raise NotImplementedError(msg) self.geom_els = geom_els = {} for ig, desc in enumerate(mesh.descs): gel = GeometryElement(desc) if gel.dim > 0: # Create geometry elements of dimension - 1. gel.create_surface_facet() geom_els[desc] = gel for gel in six.itervalues(geom_els): key = gel.get_interpolation_name() gel.poly_space = PolySpace.any_from_args(key, gel, 1) gel = gel.surface_facet if gel is not None: key = gel.get_interpolation_name() gel.poly_space = PolySpace.any_from_args(key, gel, 1) self.vertex_set_bcs = self.mesh.nodal_bcs self.cmesh = self.mesh.cmesh # Must be before creating derived connectivities. self.fix_element_orientation() from sfepy.discrete.fem.geometry_element import create_geometry_elements gels = create_geometry_elements() self.cmesh.set_local_entities(gels) self.cmesh.setup_entities() n_nod, dim = self.mesh.coors.shape self.shape = Struct(n_nod=n_nod, dim=dim, tdim=self.cmesh.tdim, n_el=self.cmesh.n_el, n_gr=len(self.geom_els)) self.reset_regions() self.clear_surface_groups()
def test_base_functions_delta(self): """ Test :math:`\delta` property of base functions evaluated in the reference element nodes. """ from sfepy.base.base import ordered_iteritems from sfepy.discrete import PolySpace ok = True for key, gel in ordered_iteritems(self.gels): for order in range(11): ps = PolySpace.any_from_args('aux', gel, order, base='lagrange', force_bubble=False) bf = ps.eval_base(ps.node_coors) _ok = nm.allclose(nm.eye(ps.n_nod), bf.squeeze(), rtol=0.0, atol=(order + 1) * 1e-14) self.report('%s order %d (n_nod: %d): %s' % (key, order, ps.n_nod, _ok)) if not _ok: import pdb pdb.set_trace() ok = ok and _ok return ok
def __init__(self, coors, conn, poly_space=None, gel=None, order=1): self.coors = coors self.conn = conn try: nm.take(self.coors, self.conn) except IndexError: output('coordinates shape: %s' % list(coors.shape)) output('connectivity: min: %d, max: %d' % (conn.min(), conn.max())) msg = 'incompatible connectivity and coordinates (see above)' raise IndexError(msg) self.n_el, self.n_ep = conn.shape self.dim = self.coors.shape[1] if poly_space is None: poly_space = PolySpace.any_from_args(None, gel, order, base='lagrange', force_bubble=False) self.poly_space = poly_space self.indices = slice(None)
def test_partition_of_unity(self): from sfepy.linalg import combine from sfepy.discrete import Integral, PolySpace ok = True orders = {'2_3' : 5, '2_4' : 5, '3_4' : 5, '3_8' : 5} bases = ( [ii for ii in combine([['2_4', '3_8'], ['lagrange', 'serendipity', 'bernstein']] )] + [ii for ii in combine([['2_3', '3_4'], ['lagrange', 'bernstein']])] ) for geom, poly_space_base in bases: max_order = orders[geom] for order in range(max_order + 1): if (poly_space_base == 'serendipity') and not (0 < order < 4): continue self.report('geometry: %s, base: %s, order: %d' % (geom, poly_space_base, order)) integral = Integral('i', order=2 * order) coors, _ = integral.get_qp(geom) ps = PolySpace.any_from_args('ps', self.gels[geom], order, base=poly_space_base) vals = ps.eval_base(coors) _ok = nm.allclose(vals.sum(axis=-1), 1, atol=1e-14, rtol=0.0) self.report('partition of unity:', _ok) ok = ok and _ok return ok
def test_normals(self): """ Check orientations of surface normals on the reference elements. """ import sfepy from sfepy.discrete import Integral, PolySpace from sfepy.discrete.fem import Mesh, FEDomain from sfepy.discrete.fem.mappings import SurfaceMapping from sfepy.linalg import normalize_vectors ok = True for geom in ['2_3', '2_4', '3_4', '3_8']: mesh = Mesh.from_file('meshes/elements/%s_1.mesh' % geom, prefix_dir=sfepy.data_dir) domain = FEDomain('domain', mesh) surface = domain.create_region('Surface', 'vertices of surface', 'facet') domain.create_surface_group(surface) sd = domain.surface_groups[surface.name] coors = domain.get_mesh_coors() gel = domain.geom_els[geom].surface_facet ps = PolySpace.any_from_args('aux', gel, 1) mapping = SurfaceMapping(coors, sd.get_connectivity(), ps) integral = Integral('i', order=1) vals, weights = integral.get_qp(gel.name) # Evaluate just in the first quadrature point... geo = mapping.get_mapping(vals[:1], weights[:1]) expected = expected_normals[geom].copy() normalize_vectors(expected) _ok = nm.allclose(expected, geo.normal[:, 0, :, 0], rtol=0.0, atol=1e-14) self.report('%s: %s' % (geom, _ok)) if not _ok: self.report('expected:') self.report(expected) self.report('actual:') self.report(geo.normal[:, 0, :, 0]) ok = ok and _ok return ok
def _create_interpolant(self): name = self.gel.name + '_DG_legendre' ps = PolySpace.any_from_args(name, self.gel, self.approx_order, base=self.poly_space_base, force_bubble=False) self.poly_space = ps # 'legendre_simplex' is created for '1_2'. if self.gel.name in ["2_4", "3_8"]: self.extended = True else: self.extended = False
def compute_nodal_normals(nodes, region, field, return_imap=False): """ Nodal normals are computed by simple averaging of element normals of elements every node is contained in. """ dim = region.dim field.domain.create_surface_group(region) field.setup_surface_data(region) # Custom integral with quadrature points in nodes. ps = PolySpace.any_from_args('', field.gel.surface_facet, field.approx_order) qp_coors = ps.node_coors # Unit normals -> weights = ones. qp_weights = nm.ones(qp_coors.shape[0], dtype=nm.float64) integral = Integral('aux', coors=qp_coors, weights=qp_weights) normals = nm.zeros((nodes.shape[0], dim), dtype=nm.float64) mask = nm.zeros((nodes.max() + 1, ), dtype=nm.int32) imap = nm.empty_like(mask) imap.fill(nodes.shape[0]) # out-of-range index for normals. imap[nodes] = nm.arange(nodes.shape[0], dtype=nm.int32) cmap, _ = field.get_mapping(region, integral, 'surface') e_normals = cmap.normal[..., 0] sd = field.surface_data[region.name] econn = sd.get_connectivity() mask[econn] += 1 # normals[imap[econn]] += e_normals im = imap[econn] for ii, en in enumerate(e_normals): normals[im[ii]] += en # All nodes must have a normal. if not nm.all(mask[nodes] > 0): raise ValueError('region %s has not complete faces!' % region.name) norm = la.norm_l2_along_axis(normals)[:, nm.newaxis] if (norm < 1e-15).any(): raise ValueError('zero nodal normal! (a node in volume?)') normals /= norm if return_imap: return normals, imap else: return normals
def test_hessians(self): """ Test the second partial derivatives of basis functions using finite differences. """ from sfepy.linalg import combine from sfepy.discrete import Integral, PolySpace ok = True orders = {'2_3': 3, '2_4': 3, '3_4': 4, '3_8': 3} bases = ([ ii for ii in combine([['2_3', '2_4', '3_4', '3_8'], ['lagrange']]) ]) for geom, poly_space_base in bases: self.report('geometry: %s, base: %s' % (geom, poly_space_base)) order = orders[geom] integral = Integral('i', order=order) coors, _ = integral.get_qp(geom) ps = PolySpace.any_from_args('ps', self.gels[geom], order, base=poly_space_base) dim = coors.shape[1] h1 = nm.zeros((coors.shape[0], dim, dim, ps.n_nod), nm.float64) eps = 1e-8 for ir in range(dim): cc = coors.copy() cc[:, ir] -= eps aux0 = ps.eval_base(cc, diff=1) cc[:, ir] += 2 * eps aux1 = ps.eval_base(cc, diff=1) h1[:, :, ir, :] = 0.5 * (aux1 - aux0) / eps h2 = ps.eval_base(coors, diff=2) _ok = nm.allclose(h1, h2, rtol=0, atol=50 * eps) self.report('hessians: error: %.2e ok: %s' % (nm.abs(h1 - h2).max(), _ok)) ok = ok and _ok return ok
def describe_geometry(field, region, integral): """ Describe membrane geometry in a given region. Parameters ---------- field : Field instance The field defining the FE approximation. region : Region instance The surface region to describe. integral : Integral instance The integral defining the quadrature points. Returns ------- mtx_t : array The transposed transformation matrix :math:`T`, see :func:`create_transformation_matrix`. membrane_geo : CMapping instance The mapping from transformed elements to a reference elements. """ # Coordinates of element vertices. sg, _ = field.get_mapping(region, integral, 'surface') sd = field.surface_data[region.name] coors = field.coors[sd.econn[:, :sg.n_ep]] # Coordinate transformation matrix (transposed!). mtx_t = create_transformation_matrix(coors) # Transform coordinates to the local coordinate system. coors_loc = dot_sequences((coors - coors[:, 0:1, :]), mtx_t) # Mapping from transformed elements to reference elements. gel = field.gel.surface_facet vm = create_mapping(coors_loc, gel, 1) qp = integral.get_qp(gel.name) ps = PolySpace.any_from_args(None, gel, field.approx_order) membrane_geo = vm.get_mapping(qp[0], qp[1], poly_space=ps) membrane_geo.bf[:] = ps.eval_base(qp[0]) return mtx_t, membrane_geo
def test_base_functions_values(self): """ Compare base function values and their gradients with correct data. Also test that sum of values over all element nodes gives one. """ from sfepy.base.base import ordered_iteritems from sfepy.discrete import PolySpace ok = True for key, val in ordered_iteritems(test_bases): gel = self.gels[key[:3]] diff = key[-4:] == 'grad' order = int(key[5]) force_bubble = key[6:7] == 'B' ps = PolySpace.any_from_args('aux', gel, order, base='lagrange', force_bubble=force_bubble) dim = ps.geometry.dim coors = nm.r_[ps.geometry.coors, [[0.2] * dim]] bf = ps.eval_base(coors, diff=diff) _ok = nm.allclose(val, bf, rtol=0.0, atol=1e-14) ## if not _ok: ## nm.set_printoptions(threshold=1000000, linewidth=65) ## print bf.__repr__() if not diff: _ok = _ok and nm.allclose( bf.sum(axis=2), 1.0, rtol=0.0, atol=1e-14) self.report('%s: %s' % (key, _ok)) ok = ok and _ok return ok
def _create_interpolant(self): name = '%s_%s_%s_%d' % (self.gel.name, self.space, self.poly_space_base, self.approx_order) ps = PolySpace.any_from_args(name, self.gel, self.approx_order, base='lagrange', force_bubble=False) self.poly_space = ps
def main(): parser = ArgumentParser(description=__doc__) parser.add_argument('--version', action='version', version='%(prog)s') parser.add_argument('-b', '--basis', metavar='name', action='store', dest='basis', default='lagrange', help=helps['basis']) parser.add_argument('-d', '--derivative', metavar='d', type=int, action='store', dest='derivative', default=0, help=helps['derivative']) parser.add_argument('-n', '--max-order', metavar='order', type=int, action='store', dest='max_order', default=2, help=helps['max_order']) parser.add_argument('-g', '--geometry', metavar='name', action='store', dest='geometry', default='2_4', help=helps['geometry']) parser.add_argument('-m', '--mesh', metavar='mesh', action='store', dest='mesh', default=None, help=helps['mesh']) parser.add_argument('--permutations', metavar='permutations', action='store', dest='permutations', default=None, help=helps['permutations']) parser.add_argument('--dofs', metavar='dofs', action='store', dest='dofs', default=None, help=helps['dofs']) parser.add_argument('-l', '--lin-options', metavar='options', action='store', dest='lin_options', default='min_level=2,max_level=5,eps=1e-3', help=helps['lin_options']) parser.add_argument('--plot-dofs', action='store_true', dest='plot_dofs', default=False, help=helps['plot_dofs']) parser.add_argument('output_dir') options = parser.parse_args() output_dir = options.output_dir output('polynomial space:', options.basis) output('max. order:', options.max_order) lin = Struct(kind='adaptive', min_level=2, max_level=5, eps=1e-3) for opt in options.lin_options.split(','): key, val = opt.split('=') setattr(lin, key, eval(val)) if options.mesh is None: dim, n_ep = int(options.geometry[0]), int(options.geometry[2]) output('reference element geometry:') output(' dimension: %d, vertices: %d' % (dim, n_ep)) gel = GeometryElement(options.geometry) gps = PolySpace.any_from_args(None, gel, 1, base=options.basis) ps = PolySpace.any_from_args(None, gel, options.max_order, base=options.basis) n_digit, _format = get_print_info(ps.n_nod, fill='0') name_template = os.path.join(output_dir, 'bf_%s.vtk' % _format) for ip in get_dofs(options.dofs, ps.n_nod): output('shape function %d...' % ip) def eval_dofs(iels, rx): if options.derivative == 0: bf = ps.eval_base(rx).squeeze() rvals = bf[None, :, ip:ip + 1] else: bfg = ps.eval_base(rx, diff=True) rvals = bfg[None, ..., ip] return rvals def eval_coors(iels, rx): bf = gps.eval_base(rx).squeeze() coors = nm.dot(bf, gel.coors)[None, ...] return coors (level, coors, conn, vdofs, mat_ids) = create_output(eval_dofs, eval_coors, 1, ps, min_level=lin.min_level, max_level=lin.max_level, eps=lin.eps) out = { 'bf': Struct(name='output_data', mode='vertex', data=vdofs, var_name='bf', dofs=None) } mesh = Mesh.from_data('bf_mesh', coors, None, [conn], [mat_ids], [options.geometry]) name = name_template % ip ensure_path(name) mesh.write(name, out=out) output('...done (%s)' % name) else: mesh = Mesh.from_file(options.mesh) output('mesh geometry:') output(' dimension: %d, vertices: %d, elements: %d' % (mesh.dim, mesh.n_nod, mesh.n_el)) if options.permutations: if options.permutations == 'all': from sfepy.linalg import cycle gel = GeometryElement(mesh.descs[0]) n_perms = gel.get_conn_permutations().shape[0] all_permutations = [ii for ii in cycle(mesh.n_el * [n_perms])] else: all_permutations = [ int(ii) for ii in options.permutations.split(',') ] all_permutations = nm.array(all_permutations) np = len(all_permutations) all_permutations.shape = (np // mesh.n_el, mesh.n_el) output('using connectivity permutations:\n', all_permutations) else: all_permutations = [None] for ip, permutations in enumerate(all_permutations): if permutations is None: suffix = '' else: suffix = '_' + '_'.join('%d' % ii for ii in permutations) save_basis_on_mesh(mesh, options, output_dir, lin, permutations, suffix)