def interpolate(self, w: ndarray) -> Union[DiscreteField, Tuple[DiscreteField, ...]]: """Interpolate a solution vector to quadrature points. Useful when a solution vector is needed in the forms, e.g., when evaluating functionals or when solving nonlinear problems. Parameters ---------- w A solution vector. """ if w.shape[0] != self.N: raise ValueError("Input array has wrong size.") if isinstance(self.elem, ElementVector): # ElementVector shouldn't get split here: workaround refs: List[Tuple[ndarray, 'AbstractBasis']] = [(np.array([]), self)] else: refs = self.split(w) dfs: List[DiscreteField] = [] # loop over solution components for c in range(len(refs)): ref = refs[c][1].basis[0][0] if ref.is_zero(): dfs.append(ref) continue ref = ref.astuple fs = [] def linear_combination(n, refn): """Global discrete function at quadrature points.""" out = 0. * refn.copy() for i in range(self.Nbfun): values = w[self.element_dofs[i]] if self.basis[i][c].is_zero(): continue out += np.einsum('...,...j->...j', values, self.basis[i][c].get(n)) return out # interpolate DiscreteField for n in range(len(ref)): if ref[n] is not None: fs.append(linear_combination(n, ref[n])) else: fs.append(None) dfs.append(DiscreteField(*fs)) if len(dfs) > 1: return tuple(dfs) return dfs[0]
def curl(u: DiscreteField): """Curl.""" if u.is_zero(): return u if u.curl is not None: return u.curl elif u.grad is not None: if u.grad.shape[0] == 2: return np.array([u.grad[1], -u.grad[0]]) raise NotImplementedError
def d(u: DiscreteField): """Gradient, divergence or curl.""" if u.is_zero(): return u if u.grad is not None: return u.grad elif u.div is not None: return u.div elif u.curl is not None: return u.curl raise NotImplementedError
def div(u: DiscreteField): """Divergence.""" if u.is_zero(): return u if u.div is not None: return u.div elif u.grad is not None: try: return np.einsum('ii...', u.grad) except ValueError: # one-dimensional u? return u.grad[0] raise NotImplementedError
def interpolate( self, w: ndarray) -> Union[DiscreteField, Tuple[DiscreteField, ...]]: """Interpolate a solution vector to quadrature points. Useful when a solution vector is needed in the forms, e.g., when evaluating functionals or when solving nonlinear problems. Parameters ---------- w A solution vector. """ if w.shape[0] != self.N: raise ValueError("Input array has wrong size.") refs = self.basis[0] dfs: List[DiscreteField] = [] # loop over solution components for c in range(len(refs)): ref = refs[c] fs = [] def linear_combination(n, refn): """Global discrete function at quadrature points.""" out = 0. * refn.copy() for i in range(self.Nbfun): values = w[self.element_dofs[i]] out += np.einsum('...,...j->...j', values, self.basis[i][c][n]) return out # interpolate DiscreteField for n in range(len(ref)): if ref[n] is not None: fs.append(linear_combination(n, ref[n])) else: fs.append(None) dfs.append(DiscreteField(*fs)) if len(dfs) > 1: return tuple(dfs) return dfs[0]
def mesh_parameters(self) -> DiscreteField: return DiscreteField( np.abs(self.mapping.detDF(self.X, self.tind))**(1. / self.mesh.dim()))
def global_coordinates(self) -> DiscreteField: return DiscreteField(self.mapping.F(self.X, tind=self.tind))
def __init__(self, mesh: Mesh, elem: Element, mapping: Optional[Mapping] = None, intorder: Optional[int] = None, quadrature: Optional[Tuple[ndarray, ndarray]] = None, facets: Optional[ndarray] = None, _side: int = 0, dofs: Optional[Dofs] = None): """Precomputed global basis on boundary facets. Parameters ---------- mesh An object of type :class:`~skfem.mesh.Mesh`. elem An object of type :class:`~skfem.element.Element`. mapping An object of type :class:`skfem.mapping.Mapping`. If `None`, uses `mesh.mapping`. intorder Optional integration order, i.e. the degree of polynomials that are integrated exactly by the used quadrature. Not used if `quadrature` is specified. quadrature Optional tuple of quadrature points and weights. facets Optional subset of facet indices. dofs Optional :class:`~skfem.assembly.Dofs` object. """ logger.info("Initializing {}({}, {})".format( type(self).__name__, type(mesh).__name__, type(elem).__name__)) super(BoundaryFacetBasis, self).__init__(mesh, elem, mapping, intorder, quadrature, mesh.brefdom, dofs) # facets where the basis is evaluated if facets is None: self.find = np.nonzero(self.mesh.f2t[1] == -1)[0] else: self.find = facets self.tind = self.mesh.f2t[_side, self.find] self._side = _side # for debugging # boundary refdom to global facet x = self.mapping.G(self.X, find=self.find) # global facet to refdom facet Y = self.mapping.invF(x, tind=self.tind) # construct normal vectors from side=0 always Y0 = self.mapping.invF(x, tind=self.mesh.f2t[0, self.find]) self.normals = DiscreteField(value=self.mapping.normals( Y0, self.mesh.f2t[0, self.find], self.find, self.mesh.t2f)) self.nelems = len(self.find) self.basis = [ self.elem.gbasis(self.mapping, Y, j, tind=self.tind) for j in range(self.Nbfun) ] self.dx = (np.abs(self.mapping.detDG(self.X, find=self.find)) * np.tile(self.W, (self.nelems, 1))) logger.info("Initializing finished.")
def mesh_parameters(self) -> DiscreteField: return DiscreteField((np.abs(self.mapping.detDG(self.X, self.find))**( 1. / (self.mesh.dim() - 1.)) ) if self.mesh.dim() != 1 else np.array([0.]))
def __init__(self, mesh: Mesh, elem: Element, mapping: Optional[Mapping] = None, intorder: Optional[int] = None, quadrature: Optional[Tuple[ndarray, ndarray]] = None, facets: Optional[Any] = None, dofs: Optional[Dofs] = None, side: int = 0): """Precomputed global basis on boundary facets. Parameters ---------- mesh An object of type :class:`~skfem.mesh.Mesh`. elem An object of type :class:`~skfem.element.Element`. mapping An object of type :class:`skfem.mapping.Mapping`. If `None`, uses `mesh.mapping`. intorder Optional integration order, i.e. the degree of polynomials that are integrated exactly by the used quadrature. Not used if `quadrature` is specified. quadrature Optional tuple of quadrature points and weights. facets Optional subset of facet indices. dofs Optional :class:`~skfem.assembly.Dofs` object. """ typestr = ("{}({}, {})".format(type(self).__name__, type(mesh).__name__, type(elem).__name__)) logger.info("Initializing {}".format(typestr)) super(FacetBasis, self).__init__( mesh, elem, mapping, intorder, quadrature, mesh.brefdom, dofs, ) # by default use boundary facets if facets is None: self.find = np.nonzero(self.mesh.f2t[1] == -1)[0] else: self.find = mesh.normalize_facets(facets) # fix the orientation if isinstance(self.find, OrientedBoundary): self.tind = self.mesh.f2t[(-1) ** side * self.find.ori - side, self.find] self.tind_normals = self.mesh.f2t[self.find.ori, self.find] else: self.tind = self.mesh.f2t[side, self.find] self.tind_normals = self.mesh.f2t[0, self.find] if len(self.find) == 0: logger.warning("Initializing {} with no facets.".format(typestr)) # boundary refdom to global facet x = self.mapping.G(self.X, find=self.find) # global facet to refdom facet Y = self.mapping.invF(x, tind=self.tind) # calculate normals Y0 = self.mapping.invF(x, tind=self.tind_normals) assert self.tind_normals is not None # satisfy mypy self.normals = DiscreteField( value=self.mapping.normals(Y0, self.tind_normals, self.find, self.mesh.t2f) ) self.nelems = len(self.find) self.basis = [self.elem.gbasis(self.mapping, Y, j, tind=self.tind) for j in range(self.Nbfun)] self.dx = (np.abs(self.mapping.detDG(self.X, find=self.find)) * np.tile(self.W, (self.nelems, 1))) logger.info("Initializing finished.")
def __init__(self, mesh, elem, mapping=None, intorder: int = None, side: int = None, facets: ndarray = None, quadrature: Tuple[ndarray, ndarray] = None): """Combine :class:`~skfem.mesh.Mesh` and :class:`~skfem.element.Element` into a set of precomputed global basis functions at element facets. Parameters ---------- mesh An object of type :class:`~skfem.mesh.Mesh`. elem An object of type :class:`~skfem.element.Element`. mapping An object of type :class:`skfem.mapping.Mapping`. If `None`, uses `mesh.mapping`. intorder Optional integration order, i.e. the degree of polynomials that are integrated exactly by the used quadrature. Not used if `quadrature` is specified. side If 0 or 1, basis functions are evaluated on the interior facets. The numbers 0 and 1 refer to the different sides of the facets. Side 0 corresponds to the indices `mesh.f2t[0]`. If `None`, basis is evaluated only on the exterior facets. facets Optional subset of facet indices. quadrature Optional tuple of quadrature points and weights. """ super(FacetBasis, self).__init__(mesh, elem, mapping) if quadrature is not None: self.X, self.W = quadrature else: self.X, self.W = get_quadrature( self.brefdom, intorder if intorder is not None else 2 * self.elem.maxdeg) # facets where the basis is evaluated if facets is None: if side is None: self.find = np.nonzero(self.mesh.f2t[1] == -1)[0] self.tind = self.mesh.f2t[0, self.find] elif hasattr(self.mapping, 'helper_to_orig') and side in [0, 1]: self.mapping.side = side self.find = self.mapping.helper_to_orig[side] self.tind = self.mesh.f2t[0, self.find] elif side in [0, 1]: self.find = np.nonzero(self.mesh.f2t[1] != -1)[0] self.tind = self.mesh.f2t[side, self.find] else: raise Exception("Parameter 'side' must be either 0 or 1. " "A facet shares only two elements.") else: self.find = facets self.tind = self.mesh.f2t[0, self.find] # boundary refdom to global facet x = self.mapping.G(self.X, find=self.find) # global facet to refdom facet Y = self.mapping.invF(x, tind=self.tind) # construct normal vectors from side=0 always Y0 = self.mapping.invF(x, tind=self.mesh.f2t[0, self.find]) self.normals = DiscreteField(value=self.mapping.normals( Y0, self.mesh.f2t[0, self.find], self.find, self.mesh.t2f)) self.nelems = len(self.find) self.basis = [ self.elem.gbasis(self.mapping, Y, j, tind=self.tind) for j in range(self.Nbfun) ] self.dx = (np.abs(self.mapping.detDG(self.X, find=self.find)) * np.tile(self.W, (self.nelems, 1)))
def interpolate( self, w: ndarray) -> Union[DiscreteField, Tuple[DiscreteField, ...]]: """Interpolate a solution vector to quadrature points. Useful when a solution vector is needed in the forms, e.g., when evaluating functionals or when solving nonlinear problems. Parameters ---------- w A solution vector. """ if w.shape[0] != self.N: raise ValueError("Input array has wrong size.") refs = self.basis[0] dfs: List[DiscreteField] = [] # loop over solution components for c in range(len(refs)): ref = refs[c] fs = [] def linear_combination(n, refn): """Global discrete function at quadrature points.""" out = 0. * refn.copy() for i in range(self.Nbfun): values = w[self.element_dofs[i]][:, None] if len(refn.shape) == 2: # values out += values * self.basis[i][c][n] elif len(refn.shape) == 3: # derivatives for j in range(out.shape[0]): out[j, :, :] += values * self.basis[i][c][n][j] elif len(refn.shape) == 4: # second derivatives for j in range(out.shape[0]): for k in range(out.shape[1]): out[j, k, :, :] += \ values * self.basis[i][c][n][j, k] elif len(refn.shape) == 5: # third derivatives for j in range(out.shape[0]): for k in range(out.shape[1]): for l in range(out.shape[2]): out[j, k, l, :, :] += \ values * \ self.basis[i][c][-1][n][j, k, l] elif len(refn.shape) == 6: # fourth derivatives for j in range(out.shape[0]): for k in range(out.shape[1]): for l in range(out.shape[2]): for m in range(out.shape[3]): out[j, k, l, m, :, :] += \ values *\ self.basis[i][c][-1][n][j, k, l, m] else: raise ValueError("The requested order of " "derivatives not supported.") return out # interpolate first and second derivatives for n in range(len(ref) - 1): if ref[n] is not None: fs.append(linear_combination(n, ref[n])) else: fs.append(None) # interpolate high-order derivatives fs.append([]) if ref[-1] is not None: for n in range(len(ref[-1])): fs[-1].append(linear_combination(n, ref[-1][n])) dfs.append(DiscreteField(*fs)) if len(dfs) > 1: return tuple(dfs) return dfs[0]
def dddd(u: DiscreteField): """Fourth derivative (for :class:`~skfem.element.ElementGlobal`).""" if u.is_zero(): return u return u.grad4
def dd(u: DiscreteField): """Hessian (for :class:`~skfem.element.ElementGlobal`).""" if u.is_zero(): return u return u.hess
def sym_grad(u: DiscreteField): """Symmetric gradient.""" if u.is_zero(): return u return .5 * (u.grad + transpose(u.grad))
def global_coordinates(self) -> ndarray: return DiscreteField(self.mapping.G(self.X, find=self.find))
def grad(u: DiscreteField): """Gradient.""" if u.is_zero(): return u return u.grad