Source code for sfepy.fem.facets

import numpy as nm
import scipy.sparse as sp

from sfepy.base.base import Struct, dict_to_array, assert_
from sfepy.base.compat import in1d, unique
from sfepy.linalg import permutations, map_permutations, insert_strided_axis

def _build_orientation_map(n_fp):
    """
    The keys are binary masks of the lexicographical ordering of facet
    vertices. A bit i set to one means `v[i] < v[i+1]`.

    The values are `[original_order, permutation, orientation]`, where
    `permutation` can be used to sort facet vertices lexicographically,
    and `orientation` is the order of the first vertex + 1 times the
    sign. Hence `permuted_facet = facet[permutation]`.
    """
    indices = range(n_fp)

    cmps = [(i1, i2) for i2 in indices for i1 in indices[:i2]]
    powers = [2**ii for ii in range(len(cmps))]

    ori_map = {}
    for indx in permutations(indices):
        key = 0
        sign = 1
        for ip, power in enumerate(powers):
            i1, i2 = cmps[ip]
            less = (indx[i1] < indx[i2])
            key += power * less
            if not less:
                sign *= -1

        isort = nm.argsort(indx)
        ori_map[key] = [indx, isort, sign * (indx[0] + 1)]

    return ori_map, cmps, powers

def _get_signed_orientation(ori, ori_map):
    """
    Transform orientation according to `ori_map`, i.e. from bit mask
    encoding to signed values.
    """
    i_from = nm.array(ori_map.keys())
    i_to = [ii[-1] for ii in ori_map.values()]

    i_map = nm.zeros((i_from.max() + 1,), dtype=nm.int8)
    i_map[i_from] = i_to

    signed_ori = i_map[ori]
    assert_((signed_ori != 0).all())

    return signed_ori

def _permute_facets(facets, ori, ori_map):
    """
    Return a copy of `facets` array with vertices sorted lexicographically.
    """
    assert_((in1d(nm.unique(ori), ori_map.keys())).all())

    permuted_facets = facets.copy()

    for key, ori_map in ori_map.iteritems():
        perm = ori_map[1]
        ip = nm.where(ori == key)[0]
        for ic0, ic1 in enumerate(perm):
            permuted_facets[ip,ic0] = facets[ip,ic1]

    return permuted_facets

def _orient_facets(ori, facets, cmps, powers):
    for ip, power in enumerate(powers):
        i1, i2 = cmps[ip]
        iw = nm.where(facets[:,i1] < facets[:,i2])[0]
        ori[iw] += power

_quad_ori_groups = {
    0 : 0,
    1 : 0,
    3 : 7,
    4 : 0,
    6 : 7,
    7 : 7,
    11 : 11,
    15 : 11,
    20 : 52,
    22 : 30,
    30 : 30,
    31 : 63,
    32 : 33,
    33 : 33,
    41 : 33,
    43 : 11,
    48 : 56,
    52 : 52,
    56 : 56,
    57 : 56,
    59 : 63,
    60 : 52,
    62 : 30,
    63 : 63,
}

_quad_orientations = {
    0  : [0, 1, 3, 2],
    7  : [3, 2, 0, 1],
    11 : [2, 3, 0, 1],
    30 : [1, 0, 2, 3],
    33 : [1, 0, 3, 2],
    52 : [2, 3, 1, 0],
    56 : [3, 2, 1, 0],
    63 : [0, 1, 2, 3],
}

[docs]def get_facet_dof_permutations(int_coors, ori_maps): """ Prepare DOF permutation vector for each possible facet orientation. """ dof_perms = {} ori = nm.empty((int_coors.shape[0],), dtype=nm.int32) for ig, ori_map in ori_maps.iteritems(): dof_perms[ig] = {} for key in ori_map.iterkeys(): ori.fill(key) permuted_int_coors = _permute_facets(int_coors, ori, ori_map) perm = map_permutations(permuted_int_coors, int_coors, check_same_items=True) dof_perms[ig][key] = perm return dof_perms
[docs]class Facets(Struct): @staticmethod
[docs] def from_domain(domain, kind): groups = domain.groups if kind == 'edges': n_obj = [group.shape.n_edge_total for group in groups.itervalues()] nn = 2 else: n_obj = [group.shape.n_face_total for group in groups.itervalues()] nn = 4 n_all_obj = sum(n_obj) indices = nm.zeros((n_all_obj, 3), nm.int32) all_facets = nm.empty((n_all_obj, nn), nm.int32) all_facets.fill(-1) single_facets = {} ii = 0 for ig, group in groups.iteritems(): if n_obj[ig] == 0: single_facets[ig] = nm.array([[]], dtype=nm.int32) continue conn, gel = group.conn, group.gel n_el = group.shape.n_el n_all_item = n_obj[ig] if kind == 'edges': n_item, items = gel.n_edge, gel.edges else: n_item, items = gel.n_face, gel.faces n_fp = items.shape[1] single_facets[ig] = items io = slice(ii, ii + n_all_item) indices[io,0] = ig ie = nm.arange(n_el, dtype=nm.int32) ie = nm.repeat(ie, n_item) indices[io,1] = ie iobj = nm.arange(n_item, dtype=nm.int32) indices[io,2] = nm.tile(iobj, n_el) facets = conn[:, items] facets = facets.reshape((n_all_item, n_fp)) all_facets[io,:n_fp] = facets ii += n_all_item if (ii != sum( n_obj )): msg = 'neighbour_list size mismatch! (%d == %d = sum( %s ))'\ % (ii, sum( n_obj ), n_obj) raise ValueError( msg ) obj = Facets('facets', kind, domain, single_facets, n_obj, indices, all_facets) return obj
def __init__(self, name, kind, domain, single_facets, n_obj, indices, facets): Struct.__init__(self, name=name, kind=kind, domain=domain, single_facets=single_facets, n_obj=n_obj, indices=indices, facets=facets) self.n_all_obj, self.n_col = facets.shape self.n_gr = len(self.n_obj) self.indx = {} ii = 0 for ig, nn in enumerate(self.n_obj): self.indx[ig] = slice(ii, ii+nn) ii += nn self.n_fps_vec = nm.empty(self.n_all_obj, dtype=nm.int32) self.n_fps = {} for ig, facet in self.single_facets.iteritems(): self.n_fps_vec[self.indx[ig]] = facet.shape[1] self.n_fps[ig] = facet.shape[1]
[docs] def sort_and_orient(self): all_permuted_facets = nm.empty((self.n_all_obj + 2, self.n_col), dtype=nm.int32) all_permuted_facets.fill(-1) sentinel = self.domain.shape.n_nod aux = nm.repeat(nm.array([sentinel], nm.int32), self.n_col) all_permuted_facets[-2] = aux all_permuted_facets[-1] = aux + 1 oris = {} signed_oris = {} ori_maps = {} for ig in range(self.n_gr): if self.n_obj[ig] == 0: continue io = self.indx[ig] facets = self.facets[io] n_fp = self.n_fps[ig] ori_map, cmps, powers = _build_orientation_map(n_fp) # Determine orientation. ori = nm.zeros((facets.shape[0],), dtype=nm.int8) _orient_facets(ori, facets, cmps, powers) # Permute each facet to have indices in ascending order, so # that lexicographic sorting works. permuted_facets = _permute_facets(facets, ori, ori_map) all_permuted_facets[io] = permuted_facets signed_ori = _get_signed_orientation(ori, ori_map) oris[ig] = ori signed_oris[ig] = signed_ori ori_maps[ig] = ori_map self.permuted_facets = all_permuted_facets self.oris = oris self.signed_oris = signed_oris self.ori_maps = ori_maps
[docs] def get_orientation(self, ig, tp_edge_ori=None): """ Get the orientation flag in group `ig`. Parameters ---------- ig : int The group index. tp_edge_ori : array, optional If given, use the tensor edge orientation to fix the flag - the flag is flipped for edges, where `tp_edge_ori` is False. Returns ------- ori : array The orientation flag. """ ori = self.oris[ig] if tp_edge_ori is not None: n_facet = self.single_facets[ig].shape[0] n_el = ori.shape[0] / n_facet aux = nm.tile(tp_edge_ori, n_el) ori = nm.where(aux, ori, 1 - ori) return ori
[docs] def setup_unique(self): """ `sorted_facets` == `permuted_facets[perm]` `permuted_facets` == `sorted_facets[perm_i]` `uid` : unique id in order of `sorted_facets` `uid_i` : unique id in order of `permuted_facets` or `facets` """ self.perm = nm.lexsort(self.permuted_facets.T[::-1]) self.sorted_facets = self.permuted_facets[self.perm] self.perm_i = nm.zeros_like(self.perm) self.perm_i[self.perm] = nm.arange(self.perm.shape[0], dtype=nm.int32) ic = nm.where(nm.abs(nm.diff(self.sorted_facets, axis=0)).sum(1), 0, 1) ic = ic.astype(nm.int32) self.n_unique = len(ic) - nm.sum(ic) - 1 self.unique_list = nm.where(ic[:-1] == 0)[0].astype(nm.int32) assert_(len(self.unique_list) == self.n_unique) ii = nm.cumsum( ic[:-1] == 0, dtype = nm.int32 ) self.uid = ii.copy() self.uid[0], self.uid[1:] = 0, ii[:-1] self.uid_i = self.uid[self.perm_i[:-2]]
[docs] def setup_neighbours(self): """ For each unique facet: - indices of facets - sparse matrix (n_unique x n_all_obj) mtx[i, j] == 1 if facet[j] has uid[i] - number of elements it is in """ ones = nm.ones((self.n_all_obj,), dtype=nm.bool) self.mtx = sp.coo_matrix((ones, (self.uid, self.perm[:-2]))) self.n_in_el = self.mtx * ones.astype(nm.int32)
[docs] def find_group_interfaces(self, return_surface=True): """ Find facets that create boundary between different element groups, i.e. facets that each belongs to two elements in different groups. Parameters ---------- return_surface : bool If True, the surface facets are also returned. Returns ------- inter_facets : array The array with indices to `self.facets` of shape `(n_i, 2)`, where `n_i` is the number of the interface facets. Each row corresponds to a single unique facet, each column to the corresponding two facets from each side of the interface. surface_facets : array, optional The array with indices to `self.facets` of shape `(n_s,)`, where `n_s` is the number of the surface facets. """ mtx = self.mtx.tocsr() # ... inner facets are in two elements i2 = nm.where(self.n_in_el == 2)[0] face_map = mtx[i2] uid, ifacets = face_map.nonzero() # ... sort by uid ii = nm.argsort(uid) ifacets = ifacets[ii] ifacets.shape = (i2.shape[0], 2) igs = self.indices[ifacets, 0] # ... interface facets are in two groups ii = nm.where(igs[:, 0] != igs[:, 1])[0] inter_facets = ifacets[ii] out = [inter_facets] if return_surface: i1 = nm.where(self.n_in_el == 1)[0] face_map = mtx[i1] _, surface_facets = face_map.nonzero() out = out + [surface_facets] return out
[docs] def setup_group_interfaces(self): """ Setup facets that create boundary between different element groups. """ if not hasattr(self, 'group_interfaces'): self.group_interfaces = \ self.find_group_interfaces(return_surface=False)[0]
[docs] def mark_surface_facets(self): """ flag: 0 .. inner, 2 .. edge, 3 .. triangle, 4 .. quadrangle """ nn = self.n_in_el[self.uid_i] flag = self.n_fps_vec * (nn == 1) return flag
[docs] def get_coors(self, ig=None): """ Get the coordinates of vertices of unique facets in group `ig`. Parameters ---------- ig : int, optional The element group. If None, the coordinates for all groups are returned, filled with zeros at places of missing vertices, i.e. where facets having less then the full number of vertices (`n_v`) are. Returns ------- coors : array The coordinates in an array of shape `(n_f, n_v, dim)`. uid : array The unique ids of facets in the order of `coors`. """ cc = self.domain.get_mesh_coors() if ig is None: uid, ii = unique(self.uid_i, return_index=True) facets = self.facets[ii] aux = insert_strided_axis(facets, 2, cc.shape[1]) coors = nm.where(aux >= 0, cc[facets], 0.0) else: uid_i = self.uid_i[self.indx[ig]] uid, ii = unique(uid_i, return_index=True) coors = cc[self.facets[ii,:self.n_fps[ig]]] return coors, uid
[docs] def get_uid_per_elements(self, ig=0): """ Get unique ids of facets for all elements in group `ig`. """ n_facet = self.single_facets[ig].shape[0] uid_i = self.uid_i[self.indx[ig]] uid_i.shape = (uid_i.shape[0] / n_facet, n_facet) return uid_i
[docs] def get_dof_orientation_maps(self, nodes): """ Given description of facet DOF nodes, return the corresponding integer coordinates and orientation maps. Notes ----- Assumes single facet type in all groups. """ inod = nm.arange(self.n_fps[0], dtype=nm.int32) int_coors = nodes[0][:, inod] if int_coors.shape[1] <= 3: # Simplex facet. ori_maps = self.ori_maps else: # Tensor product facet. ori_maps = {} for ig, ori_map in self.ori_maps.iteritems(): ori_maps[ig] = {} for key in ori_map.iterkeys(): new_key = _quad_ori_groups[key] ori_maps[ig][key] = [None, _quad_orientations[new_key]] return int_coors, ori_maps
[docs] def get_facet_dof_permutations(self, nodes): """ Given description of facet DOF nodes, return the DOF permutations for all possible facet orientations. """ int_coors, ori_maps = self.get_dof_orientation_maps(nodes) aux = get_facet_dof_permutations(int_coors, ori_maps) dof_perms = {} for ig, val in aux.iteritems(): dof_perms[ig] = dict_to_array(val) return dof_perms
[docs] def get_complete_facets(self, vertices, ig=0, mask=None): """ Get complete facets in group `ig` that are defined by the given vertices, or mask, if given. Parameters ---------- vertices : array The list of vertices. ig : int The group index. mask : array, optional Alternatively to `vertices`, a mask can be given with 1 at indices equal to the vertices and 0 elsewhere. Returns ------- ifacets : array The indices into `self.facets`. """ n_fp = self.n_fps[ig] indx = self.indx[ig] ii = self.facets[indx,:n_fp] if mask is None: mask = nm.zeros(ii.max()+1, dtype=nm.bool) mask[vertices] = True aux = nm.sum(mask[ii], 1) # Points to self.facets. ifacets = indx.start + nm.where(aux == n_fp)[0] return ifacets