Source code for sfepy.fem.mappings

"""
Finite element reference mappings.
"""
import numpy as nm

from sfepy.base.base import output, get_default, Struct
from sfepy.fem.poly_spaces import PolySpace
from sfepy.fem.extmods.mappings import CMapping

[docs]class PhysicalQPs(Struct): """ Physical quadrature points in a region. """
[docs] def get_merged_values(self): qps = nm.concatenate([self.values[ig] for ig in self.igs], axis=0) return qps
[docs] def get_shape(self, rshape, ig=None): """ Get shape from raveled shape. """ if ig is None: if self.is_uniform: n_qp = self.shape[self.igs[0]][1] else: msg = 'ig argument must be given for non-uniform QPs!' raise ValueError(msg) else: n_qp = self.shape[ig][1] if (rshape[0] / n_qp) * n_qp != rshape[0]: raise ValueError('incompatible shapes! (n_qp: %d, %s)' % (n_qp, rshape)) shape = (rshape[0] / n_qp, n_qp) + rshape[1:] return shape
[docs]def get_physical_qps(region, integral): """ Get physical quadrature points corresponding to the given region and integral. """ phys_qps = PhysicalQPs(igs=region.igs, n_total=0, indx={}, rindx={}, n_per_group={}, shape={}, values={}, is_uniform=True) ii = 0 for ig in region.igs: gmap = region.create_mapping(integral.kind[0], ig) gel = gmap.get_geometry() qp_coors, _ = integral.get_qp(gel.name) qps = gmap.get_physical_qps(qp_coors) n_el, n_qp = qps.shape[0], qps.shape[1] phys_qps.n_per_group[ig] = n_per_group = n_el * n_qp phys_qps.shape[ig] = qps.shape phys_qps.indx[ig] = slice(ii, ii + n_el) phys_qps.rindx[ig] = slice(ii * n_qp, (ii + n_el) * n_qp) ii += qps.shape[0] qps.shape = (n_per_group, qps.shape[2]) phys_qps.values[ig] = qps phys_qps.n_total += n_el * n_qp return phys_qps
[docs]def get_mapping_data(name, field, integral, region=None, integration='volume'): """ General helper function for accessing reference mapping data. Get data attribute `name` from reference mapping corresponding to `field` in `region` in quadrature points of the given `integral` and `integration` type. Parameters ---------- name : str The reference mapping attribute name. field : Field instance The field defining the reference mapping. integral : Integral instance The integral defining quadrature points. region : Region instance, optional If given, use the given region instead of `field` region. integration : one of ('volume', 'surface', 'surface_extra') The integration type. Returns ------- data : array The required data merged for all element groups. Notes ----- Assumes the same element geometry in all element groups of the field! """ data = None if region is None: region = field.region for ig in region.igs: geo, _ = field.get_mapping(ig, region, integral, integration) _data = getattr(geo, name) if data is None: data = _data else: data = nm.concatenate((data, _data), axis=0) return data
[docs]def get_jacobian(field, integral, region=None, integration='volume'): """ Get the jacobian of reference mapping corresponding to `field`. Parameters ---------- field : Field instance The field defining the reference mapping. integral : Integral instance The integral defining quadrature points. region : Region instance, optional If given, use the given region instead of `field` region. integration : one of ('volume', 'surface', 'surface_extra') The integration type. Returns ------- jac : array The jacobian merged for all element groups. See Also -------- get_mapping_data() Notes ----- Assumes the same element geometry in all element groups of the field! """ jac = get_mapping_data('det', field, integral, region=region, integration=integration) return jac
[docs]def get_normals(field, integral, region): """ Get the normals of element faces in `region`. Parameters ---------- field : Field instance The field defining the reference mapping. integral : Integral instance The integral defining quadrature points. region : Region instance The given of the element faces. Returns ------- normals : array The normals merged for all element groups. See Also -------- get_mapping_data() Notes ----- Assumes the same element geometry in all element groups of the field! """ normals = get_mapping_data('normal', field, integral, region=region, integration='surface') return normals
[docs]class Mapping(Struct): """ Base class for mappings. """ 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
[docs] def get_geometry(self): """ Return reference element geometry as a GeometryElement instance. """ return self.poly_space.geometry
[docs] def get_base(self, coors, diff=False): """ Get base functions or their gradient evaluated in given coordinates. """ bf = self.poly_space.eval_base(coors, diff=diff) return bf
[docs] def get_physical_qps(self, qp_coors): """ Get physical quadrature points corresponding to given reference element quadrature points. Returns ------- qps : array The physical quadrature points ordered element by element, i.e. with shape (n_el, n_qp, dim). """ bf = self.get_base(qp_coors) qps = nm.dot(nm.atleast_2d(bf.squeeze()), self.coors[self.conn]) # Reorder so that qps are really element by element. qps = nm.ascontiguousarray(nm.swapaxes(qps, 0, 1)) return qps
[docs]class VolumeMapping(Mapping): """ Mapping from reference domain to physical domain of the same space dimension. """
[docs] def get_mapping(self, qp_coors, weights, poly_space=None, ori=None): """ Get the mapping for given quadrature points, weights, and polynomial space. Returns ------- cmap : CMapping instance The volume mapping. """ poly_space = get_default(poly_space, self.poly_space) bf_g = self.get_base(qp_coors, diff=True) ebf_g = poly_space.eval_base(qp_coors, diff=True, ori=ori, force_axis=True) flag = ori is not None cmap = CMapping(self.n_el, qp_coors.shape[0], self.dim, poly_space.n_nod, mode='volume', flag=flag) cmap.describe(self.coors, self.conn, bf_g, ebf_g, weights) return cmap
[docs]class SurfaceMapping(Mapping): """ Mapping from reference domain to physical domain of the space dimension higher by one. """
[docs] def get_mapping(self, qp_coors, weights, poly_space=None, mode='surface'): """ Get the mapping for given quadrature points, weights, and polynomial space. Returns ------- cmap : CMapping instance The surface mapping. """ poly_space = get_default(poly_space, self.poly_space) bf_g = self.get_base(qp_coors, diff=True) if nm.allclose(bf_g, 0.0): raise ValueError('zero base function gradient!') cmap = CMapping(self.n_el, qp_coors.shape[0], self.dim, poly_space.n_nod, mode=mode) cmap.describe(self.coors, self.conn, bf_g, None, weights) return cmap