Source code for projection_trace

"""
An implementation of the projection for the trace spaces on a 3D surface
:math:`\\Gamma`. The projection can be divided into
two processes, *reduction* and *reconstruction*.

The *reduction* process will use the given scalar or vector to obtain
the coefficients of its projection in the discrete space.

The *reconstruction* process first assembles the coefficients with the
correct basis functions and evaluates the discrete variable at given
points.

⭕ To access the source code, click on the [source] button at the right
side or click on
:download:`[projection_trace.py]</contents/LIBRARY/ptc/mathischeap_ptc/projection_trace.py>`.
Dependence may exist. In case of error, check import and install required
packages or download required scripts. © mathischeap.com

"""

import numpy as np
from quadrature import Gauss
from mimetic_basis_polynomials_2d import grid2d



[docs] class ReductionTrace(object): """A wrapper of reduction functions. :param bf: The basis functions in :math:`\\Pi_{\\mathrm{ref}}`. :type bf: MimeticBasisPolynomials2D :param ct: The coordinate transformation representing the mapping :math:`\\Psi`, .. math:: \\Psi: \\Pi_{\\mathrm{ref}}\\to\\Gamma :type ct: CoordinateTransformationSurface :param quad_degree: (default: ``None``) The degree used for the numerical integral. It should be a list or tuple of two positive integers. If it is ``None``, a suitable degree will be obtained from ``bf``. :type quad_degree: list, tuple :example: >>> def p(x,y,z): return np.sin(np.pi*x) * np.sin(np.pi*y) * np.sin(np.pi*z) >>> from mimetic_basis_polynomials_2d import MimeticBasisPolynomials2D >>> from coordinate_transformation_surface import CoordinateTransformationSurface >>> from coordinate_transformation_surface import Psi, d_Psi >>> bf = MimeticBasisPolynomials2D('Lobatto-3', 'Lobatto-3') >>> ct = CoordinateTransformationSurface(Psi, d_Psi) >>> Rd = ReductionTrace(bf, ct) >>> Rd.TF(p) array([0.03873144, 0.02717954, 0.02752846, 0.02717954, 0.04952738, 0.04752636, 0.02752846, 0.04752636, 0.01838736]) """ def __init__(self, bf, ct, quad_degree=None): assert bf.__class__.__name__ == 'MimeticBasisPolynomials2D' assert ct.__class__.__name__ == 'CoordinateTransformationSurface' self.bf = bf self.ct = ct if quad_degree is None: bf_N = bf.degree quad_degree = [bf_N[i]+2 for i in range(2)] else: pass self.quad_degree = quad_degree
[docs] def TN(self, scalar): """Reduce a scalar on :math:`\\Gamma` to :math:`\\text{TN}_{N}(\\Gamma)`. :param scalar: :return: A 1d np.array representing the local coefficients/dofs of the discrete scalar. """ raise NotImplementedError("Could you code it?")
[docs] def TE(self, vector): """Reduce a vector on :math:`\\Gamma` to :math:`\\text{TE}_{N-1}(\\Gamma)`. :param vector: :return: A 1d np.array representing the local coefficients/dofs of the discrete vector. """ raise NotImplementedError("Could you code it?")
[docs] def TF(self, scalar): """Reduce a scalar on :math:`\\Gamma` to :math:`\\text{TF}_{N-1}(\\Gamma)`. :param scalar: :return: A 1d np.array representing the local coefficients/dofs of the discrete scalar. """ N = self.bf.degree NUM_basis = N[0]*N[1] nodes = self.bf.nodes p = self.quad_degree qn0, qw0 = Gauss(p[0]) qn1, qw1 = Gauss(p[1]) quad_weights = [qw0, qw1] quad_nodes = [qn0, qn1] magic_factor = 0.25 rho = np.zeros((NUM_basis, p[0] + 1, p[1] + 1)) tau = np.zeros((NUM_basis, p[0] + 1, p[1] + 1)) volume = np.zeros(NUM_basis) for j in range(N[1]): for i in range(N[0]): m = i + j*N[0] rho[m,...] = (quad_nodes[0][:,np.newaxis].repeat(p[1]+1, axis=1) + 1)\ * (nodes[0][i+1]-nodes[0][i] )/2 + nodes[0][i] tau[m,...] = (quad_nodes[1][np.newaxis,:].repeat(p[0]+1, axis=0) + 1)\ * (nodes[1][j+1]-nodes[1][j] )/2 + nodes[1][j] volume[m] = (nodes[0][i+1]-nodes[0][i]) \ * (nodes[1][j+1]-nodes[1][j]) * magic_factor g = self.ct.metric(rho, tau) xyz = self.ct.mapping(rho, tau) fxyz = scalar(*xyz) return np.einsum('jkl, k, l, j -> j', fxyz * np.sqrt(g), quad_weights[0], quad_weights[1], volume, optimize='greedy' )
[docs] class ReconstructionTrace(object): """A wrapper of reconstruction functions. :param bf: The basis functions in :math:`\\Pi_{\\mathrm{ref}}`. :type bf: MimeticBasisPolynomials2D :param ct: The coordinate transformation representing the mapping :math:`\\Psi`, .. math:: \\Psi: \\Pi_{\\mathrm{ref}}\\to\\Gamma :type ct: CoordinateTransformationSurface :example: >>> def p(x,y,z): return np.sin(np.pi*x) * np.sin(np.pi*y) * np.sin(np.pi*z) >>> from mimetic_basis_polynomials_2d import MimeticBasisPolynomials2D >>> from coordinate_transformation_surface import CoordinateTransformationSurface >>> from coordinate_transformation_surface import Psi, d_Psi >>> bf = MimeticBasisPolynomials2D('Lobatto-20', 'Lobatto-20') >>> ct = CoordinateTransformationSurface(Psi, d_Psi) >>> Rd = ReductionTrace(bf, ct) >>> dofs = Rd.TF(p) >>> Rc = ReconstructionTrace(bf, ct) >>> rho = np.linspace(-1,1,5) >>> tau = np.linspace(-1,1,5) >>> xyz, v = Rc.TF(dofs, rho, tau) >>> np.max(np.abs(p(*xyz) - v)) # the max error from the exact function # doctest: +ELLIPSIS 0.0002989... """ def __init__(self, bf, ct): assert bf.__class__.__name__ == 'MimeticBasisPolynomials2D' assert ct.__class__.__name__ == 'CoordinateTransformationSurface' self.bf = bf self.ct = ct
[docs] def TN(self, loc_dofs, rho, tau, ravel=False): """Reconstruct a discrete trace polynomial in :math:`\\text{TN}_{N}(\\Gamma)` evaluated at :math:`\\Psi\\circ \\text{grid2d}(\\varrho, \\tau)``. :param loc_dofs: A 1d np.array containing the coefficients of the discrete variable in :math:`\\text{TN}_{N}(\\Gamma)`. :type loc_dofs: np.array :param rho: :math:`\\varrho`. :param tau: :math:`\\tau`. The reconstruction will be evaluated at :math:`\\Psi\\circ \\text{grid2d}( \\varrho, \\tau)``. :type rho: 1d np.array :type tau: 1d np.array :param ravel: (default: ``False``) If ``ravel`` is ``True``, we will flat the outputs (as a 1d array) according to local numbering. Otherwise, you get 2d outputs corresponding to the indexing. :returns: A tuple of two outputs: * :math:`(x,y,z)`: The reconstruction is evaluated at :math:`(x,y,z):=\\Psi\\circ \\text{grid2d}(\\varrho, \\tau)`. * values: The values of the reconstructed variable at :math:`(x,y,z)`. """ raise NotImplementedError("Could you code it?")
[docs] def TE(self, loc_dofs, rho, tau, ravel=False): """Reconstruct a discrete trace polynomial in :math:`\\text{TE}_{N-1}(\\Gamma)` evaluated at :math:`\\Psi\\circ \\text{grid2d}(\\varrho, \\tau)``. :param loc_dofs: A 1d np.array containing the coefficients of the discrete variable in :math:`\\text{TE}_{N-1}(\\Gamma)`. :type loc_dofs: np.array :param rho: :math:`\\varrho`. :param tau: :math:`\\tau`. The reconstruction will be evaluated at :math:`\\Psi\\circ \\text{grid2d}( \\varrho, \\tau)``. :type rho: 1d np.array :type tau: 1d np.array :param ravel: (default: ``False``) If ``ravel`` is ``True``, we will flat the outputs (as a 1d array) according to local numbering. Otherwise, you get 2d outputs corresponding to the indexing. :returns: A tuple of two outputs: * :math:`(x,y,z)`: The reconstruction is evaluated at :math:`(x,y,z):=\\Psi\\circ \\text{grid2d}(\\varrho, \\tau)`. * values: The values of the reconstructed variable at :math:`(x,y,z)`. """ raise NotImplementedError("Could you code it?")
[docs] def TF(self, loc_dofs, rho, tau, ravel=False): """Reconstruct a discrete trace polynomial in :math:`\\text{TF}_{N-1}(\\Gamma)` evaluated at :math:`\\Psi\\circ \\text{grid2d}(\\varrho, \\tau)``. :param loc_dofs: A 1d np.array containing the coefficients of the discrete variable in :math:`\\text{TF}_{N-1}(\\Gamma)`. :type loc_dofs: np.array :param rho: :math:`\\varrho`. :param tau: :math:`\\tau`. The reconstruction will be evaluated at :math:`\\Psi\\circ \\text{grid2d}( \\varrho, \\tau)``. :type rho: 1d np.array :type tau: 1d np.array :param ravel: (default: ``False``) If ``ravel`` is ``True``, we will flat the outputs (as a 1d array) according to local numbering. Otherwise, you get 2d outputs corresponding to the indexing. :returns: A tuple of two outputs: * :math:`(x,y,z)`: The reconstruction is evaluated at :math:`(x,y,z):=\\Psi\\circ \\text{grid2d}(\\varrho, \\tau)`. * values: The values of the reconstructed variable at :math:`(x,y,z)`. """ basis = self.bf.face_polynomials(rho, tau) rho_tau = grid2d(rho, tau) xyz = self.ct.mapping(*rho_tau) g = self.ct.metric(*rho_tau) v = np.einsum('ij, i -> j', basis, loc_dofs, optimize='greedy') * np.reciprocal(np.sqrt(g)) if ravel: pass else: shape = [len(rho), len(tau)] xyz = [xyz[j].reshape(shape, order='F') for j in range(3)] v = v.reshape(shape, order='F') return xyz, v
if __name__ == '__main__': def p(x,y,z): return np.sin(np.pi*x) + 0 * y * z # * np.sin(np.pi*y) * np.sin(np.pi*z) from mimetic_basis_polynomials_2d import MimeticBasisPolynomials2D from coordinate_transformation_surface import CoordinateTransformationSurface from coordinate_transformation_surface import Psi, d_Psi bf = MimeticBasisPolynomials2D('Lobatto-10', 'Lobatto-10') ct = CoordinateTransformationSurface(Psi, d_Psi) Rd = ReductionTrace(bf, ct) dofs = Rd.TF(p) Rc = ReconstructionTrace(bf, ct) rho = np.linspace(-1,1,100) tau = np.linspace(-1,1,100) xyz, v = Rc.TF(dofs, rho, tau) import matplotlib.pyplot as plt from matplotlib import cm fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') V = (v + 1) / 2 cmap = cm.bwr ax.plot_surface(*xyz, facecolors=cmap(V)) mappable = cm.ScalarMappable(cmap=cmap) mappable.set_array(V) cbar = plt.colorbar(mappable, ax=ax, ticks=[0, 0.5, 1], shrink=1, aspect=20, extend='both', orientation='vertical', label='Some Units') cbar.ax.set_yticklabels( [r"-1", "0", "1"]) # vertically oriented colorbar ax.set_xlabel(r'$x$') ax.set_ylabel(r'$y$') ax.set_zlabel(r'$z$') plt.show() # print(v[:,0]- v[:,1])