xref: /libCEED/python/ceed_basis.py (revision fc2a3161298f961499d598ff4749d2d4d3a5027b)
15a21df39Svaleriabarra# Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
25a21df39Svaleriabarra# the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
35a21df39Svaleriabarra# reserved. See files LICENSE and NOTICE for details.
45a21df39Svaleriabarra#
55a21df39Svaleriabarra# This file is part of CEED, a collection of benchmarks, miniapps, software
65a21df39Svaleriabarra# libraries and APIs for efficient high-order finite element and spectral
75a21df39Svaleriabarra# element discretizations for exascale applications. For more information and
85a21df39Svaleriabarra# source code availability see http://github.com/ceed.
95a21df39Svaleriabarra#
105a21df39Svaleriabarra# The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
115a21df39Svaleriabarra# a collaborative effort of two U.S. Department of Energy organizations (Office
125a21df39Svaleriabarra# of Science and the National Nuclear Security Administration) responsible for
135a21df39Svaleriabarra# the planning and preparation of a capable exascale ecosystem, including
145a21df39Svaleriabarra# software, applications, hardware, advanced system engineering and early
155a21df39Svaleriabarra# testbed platforms, in support of the nation's exascale computing imperative.
165a21df39Svaleriabarra
175a21df39Svaleriabarrafrom _ceed_cffi import ffi, lib
185a21df39Svaleriabarraimport tempfile
195a21df39Svaleriabarraimport numpy as np
205a21df39Svaleriabarrafrom abc import ABC
215a21df39Svaleriabarrafrom .ceed_constants import TRANSPOSE, NOTRANSPOSE
225a21df39Svaleriabarra
235a21df39Svaleriabarra# ------------------------------------------------------------------------------
245a21df39Svaleriabarraclass Basis(ABC):
255a21df39Svaleriabarra  """Ceed Basis: finite element basis objects."""
265a21df39Svaleriabarra
275a21df39Svaleriabarra  # Attributes
285a21df39Svaleriabarra  _ceed = ffi.NULL
295a21df39Svaleriabarra  _pointer = ffi.NULL
305a21df39Svaleriabarra
315a21df39Svaleriabarra  # Representation
325a21df39Svaleriabarra  def __repr__(self):
335a21df39Svaleriabarra    return "<CeedBasis instance at " + hex(id(self)) + ">"
345a21df39Svaleriabarra
355a21df39Svaleriabarra  # String conversion for print() to stdout
365a21df39Svaleriabarra  def __str__(self):
375a21df39Svaleriabarra    """View a Basis via print()."""
385a21df39Svaleriabarra
395a21df39Svaleriabarra    # libCEED call
405a21df39Svaleriabarra    with tempfile.NamedTemporaryFile() as key_file:
415a21df39Svaleriabarra      with open(key_file.name, 'r+') as stream_file:
425a21df39Svaleriabarra        stream = ffi.cast("FILE *", stream_file)
435a21df39Svaleriabarra
445a21df39Svaleriabarra        lib.CeedBasisView(self._pointer[0], stream)
455a21df39Svaleriabarra
465a21df39Svaleriabarra        stream_file.seek(0)
475a21df39Svaleriabarra        out_string = stream_file.read()
485a21df39Svaleriabarra
495a21df39Svaleriabarra    return out_string
505a21df39Svaleriabarra
515a21df39Svaleriabarra  # Apply Basis
525a21df39Svaleriabarra  def apply(self, nelem, emode, u, v, tmode=NOTRANSPOSE):
535a21df39Svaleriabarra    """Apply basis evaluation from nodes to quadrature points or vice versa.
545a21df39Svaleriabarra
555a21df39Svaleriabarra       Args:
565a21df39Svaleriabarra         nelem: the number of elements to apply the basis evaluation to;
575a21df39Svaleriabarra                  the backend will specify the ordering in a
585a21df39Svaleriabarra                  BlockedElemRestriction
595a21df39Svaleriabarra         emode: basis evaluation mode
605a21df39Svaleriabarra         u: input vector
615a21df39Svaleriabarra         v: output vector
625a21df39Svaleriabarra         **tmode: CEED_NOTRANSPOSE to evaluate from nodes to quadrature
635a21df39Svaleriabarra                    points, CEED_TRANSPOSE to apply the transpose, mapping
645a21df39Svaleriabarra                    from quadrature points to nodes; default CEED_NOTRANSPOSE"""
655a21df39Svaleriabarra
665a21df39Svaleriabarra    # libCEED call
675a21df39Svaleriabarra    lib.CeedBasisApply(self._pointer[0], nelem, tmode, emode,
685a21df39Svaleriabarra                       u._pointer[0], v._pointer[0])
695a21df39Svaleriabarra
705a21df39Svaleriabarra  # Transpose a Basis
715a21df39Svaleriabarra  @property
725a21df39Svaleriabarra  def T(self):
735a21df39Svaleriabarra    """Transpose a Basis."""
745a21df39Svaleriabarra
755a21df39Svaleriabarra    return TransposeBasis(self)
765a21df39Svaleriabarra
775a21df39Svaleriabarra  # Transpose a Basis
785a21df39Svaleriabarra  @property
795a21df39Svaleriabarra  def transpose(self):
805a21df39Svaleriabarra    """Transpose a Basis."""
815a21df39Svaleriabarra
825a21df39Svaleriabarra    return TransposeBasis(self)
835a21df39Svaleriabarra
845a21df39Svaleriabarra  # Get number of nodes
855a21df39Svaleriabarra  def get_num_nodes(self):
865a21df39Svaleriabarra    """Get total number of nodes (in dim dimensions) of a Basis.
875a21df39Svaleriabarra
885a21df39Svaleriabarra       Returns:
895a21df39Svaleriabarra         num_nodes: total number of nodes"""
905a21df39Svaleriabarra
915a21df39Svaleriabarra    # Setup argument
925a21df39Svaleriabarra    p_pointer = ffi.new("CeedInt *")
935a21df39Svaleriabarra
945a21df39Svaleriabarra    # libCEED call
955a21df39Svaleriabarra    lib.CeedBasisGetNumNodes(self._pointer[0], p_pointer)
965a21df39Svaleriabarra
975a21df39Svaleriabarra    return p_pointer[0]
985a21df39Svaleriabarra
995a21df39Svaleriabarra  # Get number of quadrature points
1005a21df39Svaleriabarra  def get_num_quadrature_points(self):
1015a21df39Svaleriabarra    """Get total number of quadrature points (in dim dimensions) of a Basis.
1025a21df39Svaleriabarra
1035a21df39Svaleriabarra       Returns:
1045a21df39Svaleriabarra         num_qpts: total number of quadrature points"""
1055a21df39Svaleriabarra
1065a21df39Svaleriabarra    # Setup argument
1075a21df39Svaleriabarra    q_pointer = ffi.new("CeedInt *")
1085a21df39Svaleriabarra
1095a21df39Svaleriabarra    # libCEED call
1105a21df39Svaleriabarra    lib.CeedBasisGetNumQuadraturePoints(self._pointer[0], q_pointer)
1115a21df39Svaleriabarra
1125a21df39Svaleriabarra    return q_pointer[0]
1135a21df39Svaleriabarra
1145a21df39Svaleriabarra  # Gauss quadrature
1155a21df39Svaleriabarra  @staticmethod
1165a21df39Svaleriabarra  def gauss_quadrature(q):
1175a21df39Svaleriabarra    """Construct a Gauss-Legendre quadrature.
1185a21df39Svaleriabarra
1195a21df39Svaleriabarra       Args:
1205a21df39Svaleriabarra         Q: number of quadrature points (integrates polynomials of
1215a21df39Svaleriabarra              degree 2*Q-1 exactly)
1225a21df39Svaleriabarra
1235a21df39Svaleriabarra       Returns:
1245a21df39Svaleriabarra         (qref1d, qweight1d): array of length Q to hold the abscissa on [-1, 1]
1255a21df39Svaleriabarra                                and array of length Q to hold the weights"""
1265a21df39Svaleriabarra
1275a21df39Svaleriabarra    # Setup arguments
1285a21df39Svaleriabarra    qref1d = np.empty(q, dtype="float64")
129*fc2a3161SJed Brown    qweight1d = np.empty(q, dtype="float64")
1305a21df39Svaleriabarra
1315a21df39Svaleriabarra    qref1d_pointer = ffi.new("CeedScalar *")
1325a21df39Svaleriabarra    qref1d_pointer = ffi.cast("CeedScalar *", qref1d.__array_interface__['data'][0])
1335a21df39Svaleriabarra
1345a21df39Svaleriabarra    qweight1d_pointer = ffi.new("CeedScalar *")
1355a21df39Svaleriabarra    qweight1d_pointer = ffi.cast("CeedScalar *", qweight1d.__array_interface__['data'][0])
1365a21df39Svaleriabarra
1375a21df39Svaleriabarra    # libCEED call
1385a21df39Svaleriabarra    lib.CeedGaussQuadrature(q, qref1d_pointer, qweight1d_pointer)
1395a21df39Svaleriabarra
1405a21df39Svaleriabarra    return qref1d, qweight1d
1415a21df39Svaleriabarra
1425a21df39Svaleriabarra  # Lobatto quadrature
1435a21df39Svaleriabarra  @staticmethod
1445a21df39Svaleriabarra  def lobatto_quadrature(q):
1455a21df39Svaleriabarra    """Construct a Gauss-Legendre-Lobatto quadrature.
1465a21df39Svaleriabarra
1475a21df39Svaleriabarra       Args:
1485a21df39Svaleriabarra         q: number of quadrature points (integrates polynomials of
1495a21df39Svaleriabarra              degree 2*Q-3 exactly)
1505a21df39Svaleriabarra
1515a21df39Svaleriabarra       Returns:
1525a21df39Svaleriabarra         (qref1d, qweight1d): array of length Q to hold the abscissa on [-1, 1]
1535a21df39Svaleriabarra                                and array of length Q to hold the weights"""
1545a21df39Svaleriabarra
1555a21df39Svaleriabarra    # Setup arguments
1565a21df39Svaleriabarra    qref1d = np.empty(q, dtype="float64")
1575a21df39Svaleriabarra    qref1d_pointer = ffi.new("CeedScalar *")
1585a21df39Svaleriabarra    qref1d_pointer = ffi.cast("CeedScalar *", qref1d.__array_interface__['data'][0])
1595a21df39Svaleriabarra
160*fc2a3161SJed Brown    qweight1d = np.empty(q, dtype="float64")
1615a21df39Svaleriabarra    qweight1d_pointer = ffi.new("CeedScalar *")
1625a21df39Svaleriabarra    qweight1d_pointer = ffi.cast("CeedScalar *", qweight1d.__array_interface__['data'][0])
1635a21df39Svaleriabarra
1645a21df39Svaleriabarra    # libCEED call
1655a21df39Svaleriabarra    lib.CeedLobattoQuadrature(q, qref1d_pointer, qweight1d_pointer)
1665a21df39Svaleriabarra
1675a21df39Svaleriabarra    return qref1d, qweight1d
1685a21df39Svaleriabarra
1695a21df39Svaleriabarra  # QR factorization
1705a21df39Svaleriabarra  @staticmethod
1715a21df39Svaleriabarra  def qr_factorization(ceed, mat, tau, m, n):
1725a21df39Svaleriabarra    """Return QR Factorization of a matrix.
1735a21df39Svaleriabarra
1745a21df39Svaleriabarra       Args:
1755a21df39Svaleriabarra         ceed: Ceed context currently in use
1765a21df39Svaleriabarra         *mat: Numpy array holding the row-major matrix to be factorized in place
1775a21df39Svaleriabarra         *tau: Numpy array to hold the vector of lengt m of scaling factors
1785a21df39Svaleriabarra         m: number of rows
1795a21df39Svaleriabarra         n: numbef of columns"""
1805a21df39Svaleriabarra
1815a21df39Svaleriabarra    # Setup arguments
1825a21df39Svaleriabarra    mat_pointer = ffi.new("CeedScalar *")
1835a21df39Svaleriabarra    mat_pointer = ffi.cast("CeedScalar *", mat.__array_interface__['data'][0])
1845a21df39Svaleriabarra
1855a21df39Svaleriabarra    tau_pointer = ffi.new("CeedScalar *")
1865a21df39Svaleriabarra    tau_pointer = ffi.cast("CeedScalar *", tau.__array_interface__['data'][0])
1875a21df39Svaleriabarra
1885a21df39Svaleriabarra    # libCEED call
1895a21df39Svaleriabarra    lib.CeedQRFactorization(ceed._pointer[0], mat_pointer, tau_pointer, m, n)
1905a21df39Svaleriabarra
1915a21df39Svaleriabarra    return mat, tau
1925a21df39Svaleriabarra
1935a21df39Svaleriabarra  # Symmetric Schur decomposition
1945a21df39Svaleriabarra  @staticmethod
1955a21df39Svaleriabarra  def symmetric_schur_decomposition(ceed, mat, n):
1965a21df39Svaleriabarra    """Return symmetric Schur decomposition of a symmetric matrix
1975a21df39Svaleriabarra         via symmetric QR factorization.
1985a21df39Svaleriabarra
1995a21df39Svaleriabarra       Args:
2005a21df39Svaleriabarra         ceed: Ceed context currently in use
2015a21df39Svaleriabarra         *mat: Numpy array holding the row-major matrix to be factorized in place
2025a21df39Svaleriabarra         n: number of rows/columns
2035a21df39Svaleriabarra
2045a21df39Svaleriabarra       Returns:
2055a21df39Svaleriabarra         lbda: Numpy array of length n holding eigenvalues"""
2065a21df39Svaleriabarra
2075a21df39Svaleriabarra    # Setup arguments
2085a21df39Svaleriabarra    mat_pointer = ffi.new("CeedScalar *")
2095a21df39Svaleriabarra    mat_pointer = ffi.cast("CeedScalar *", mat.__array_interface__['data'][0])
2105a21df39Svaleriabarra
2115a21df39Svaleriabarra    lbda = np.empty(n, dtype="float64")
2125a21df39Svaleriabarra    l_pointer = ffi.new("CeedScalar *")
2135a21df39Svaleriabarra    l_pointer = ffi.cast("CeedScalar *", lbda.__array_interface__['data'][0])
2145a21df39Svaleriabarra
2155a21df39Svaleriabarra    # libCEED call
2165a21df39Svaleriabarra    lib.CeedSymmetricSchurDecomposition(ceed._pointer[0], mat_pointer, l_pointer, n)
2175a21df39Svaleriabarra
2185a21df39Svaleriabarra    return lbda
2195a21df39Svaleriabarra
2205a21df39Svaleriabarra  # Simultaneous Diagonalization
2215a21df39Svaleriabarra  @staticmethod
2225a21df39Svaleriabarra  def simultaneous_diagonalization(ceed, matA, matB, n):
2235a21df39Svaleriabarra    """Return Simultaneous Diagonalization of two matrices.
2245a21df39Svaleriabarra
2255a21df39Svaleriabarra       Args:
2265a21df39Svaleriabarra         ceed: Ceed context currently in use
2275a21df39Svaleriabarra         *matA: Numpy array holding the row-major matrix to be factorized with
2285a21df39Svaleriabarra                  eigenvalues
2295a21df39Svaleriabarra         *matB: Numpy array holding the row-major matrix to be factorized to identity
2305a21df39Svaleriabarra         n: number of rows/columns
2315a21df39Svaleriabarra
2325a21df39Svaleriabarra       Returns:
2335a21df39Svaleriabarra         (x, lbda): Numpy array holding the row-major orthogonal matrix and
2345a21df39Svaleriabarra                      Numpy array holding the vector of length n of generalized
2355a21df39Svaleriabarra                      eigenvalues"""
2365a21df39Svaleriabarra
2375a21df39Svaleriabarra    # Setup arguments
2385a21df39Svaleriabarra    matA_pointer = ffi.new("CeedScalar *")
2395a21df39Svaleriabarra    matA_pointer = ffi.cast("CeedScalar *", matA.__array_interface__['data'][0])
2405a21df39Svaleriabarra
2415a21df39Svaleriabarra    matB_pointer = ffi.new("CeedScalar *")
2425a21df39Svaleriabarra    matB_pointer = ffi.cast("CeedScalar *", matB.__array_interface__['data'][0])
2435a21df39Svaleriabarra
2445a21df39Svaleriabarra    lbda = np.empty(n, dtype="float64")
2455a21df39Svaleriabarra    l_pointer = ffi.new("CeedScalar *")
2465a21df39Svaleriabarra    l_pointer = ffi.cast("CeedScalar *", lbda.__array_interface__['data'][0])
2475a21df39Svaleriabarra
2485a21df39Svaleriabarra    x = np.empty(n*n, dtype="float64")
2495a21df39Svaleriabarra    x_pointer = ffi.new("CeedScalar *")
2505a21df39Svaleriabarra    x_pointer = ffi.cast("CeedScalar *", x.__array_interface__['data'][0])
2515a21df39Svaleriabarra
2525a21df39Svaleriabarra    # libCEED call
2535a21df39Svaleriabarra    lib.CeedSimultaneousDiagonalization(ceed._pointer[0], matA_pointer, matB_pointer,
2545a21df39Svaleriabarra                                        x_pointer, l_pointer, n)
2555a21df39Svaleriabarra
2565a21df39Svaleriabarra    return x, lbda
2575a21df39Svaleriabarra
2585a21df39Svaleriabarra  # Destructor
2595a21df39Svaleriabarra  def __del__(self):
2605a21df39Svaleriabarra    # libCEED call
2615a21df39Svaleriabarra    lib.CeedBasisDestroy(self._pointer)
2625a21df39Svaleriabarra
2635a21df39Svaleriabarra# ------------------------------------------------------------------------------
2645a21df39Svaleriabarraclass BasisTensorH1(Basis):
2655a21df39Svaleriabarra  """Ceed Tensor H1 Basis: finite element tensor-product basis objects for
2665a21df39Svaleriabarra       H^1 discretizations."""
2675a21df39Svaleriabarra
2685a21df39Svaleriabarra  # Constructor
2695a21df39Svaleriabarra  def __init__(self, ceed, dim, ncomp, P1d, Q1d, interp1d, grad1d,
2705a21df39Svaleriabarra               qref1d, qweight1d):
2715a21df39Svaleriabarra
2725a21df39Svaleriabarra    # Setup arguments
2735a21df39Svaleriabarra    self._pointer = ffi.new("CeedBasis *")
2745a21df39Svaleriabarra
2755a21df39Svaleriabarra    self._ceed = ceed
2765a21df39Svaleriabarra
2775a21df39Svaleriabarra    interp1d_pointer = ffi.new("CeedScalar *")
2785a21df39Svaleriabarra    interp1d_pointer = ffi.cast("CeedScalar *", interp1d.__array_interface__['data'][0])
2795a21df39Svaleriabarra
2805a21df39Svaleriabarra    grad1d_pointer = ffi.new("CeedScalar *")
2815a21df39Svaleriabarra    grad1d_pointer = ffi.cast("CeedScalar *", grad1d.__array_interface__['data'][0])
2825a21df39Svaleriabarra
2835a21df39Svaleriabarra    qref1d_pointer = ffi.new("CeedScalar *")
2845a21df39Svaleriabarra    qref1d_pointer = ffi.cast("CeedScalar *", qref1d.__array_interface__['data'][0])
2855a21df39Svaleriabarra
2865a21df39Svaleriabarra    qweight1d_pointer = ffi.new("CeedScalar *")
2875a21df39Svaleriabarra    qweight1d_pointer = ffi.cast("CeedScalar *", qweight1d.__array_interface__['data'][0])
2885a21df39Svaleriabarra
2895a21df39Svaleriabarra    # libCEED call
2905a21df39Svaleriabarra    lib.CeedBasisCreateTensorH1(self._ceed._pointer[0], dim, ncomp, P1d, Q1d,
2915a21df39Svaleriabarra                                interp1d_pointer, grad1d_pointer, qref1d_pointer,
2925a21df39Svaleriabarra                                qweight1d_pointer, self._pointer)
2935a21df39Svaleriabarra
2945a21df39Svaleriabarra# ------------------------------------------------------------------------------
2955a21df39Svaleriabarraclass BasisTensorH1Lagrange(Basis):
2965a21df39Svaleriabarra  """Ceed Tensor H1 Lagrange Basis: finite element tensor-product Lagrange basis
2975a21df39Svaleriabarra       objects for H^1 discretizations."""
2985a21df39Svaleriabarra
2995a21df39Svaleriabarra  # Constructor
3005a21df39Svaleriabarra  def __init__(self, ceed, dim, ncomp, P, Q, qmode):
3015a21df39Svaleriabarra
3025a21df39Svaleriabarra    # Setup arguments
3035a21df39Svaleriabarra    self._pointer = ffi.new("CeedBasis *")
3045a21df39Svaleriabarra
3055a21df39Svaleriabarra    self._ceed = ceed
3065a21df39Svaleriabarra
3075a21df39Svaleriabarra    # libCEED call
3085a21df39Svaleriabarra    lib.CeedBasisCreateTensorH1Lagrange(self._ceed._pointer[0], dim, ncomp, P,
3095a21df39Svaleriabarra                                        Q, qmode, self._pointer)
3105a21df39Svaleriabarra
3115a21df39Svaleriabarra# ------------------------------------------------------------------------------
3125a21df39Svaleriabarraclass BasisH1(Basis):
3135a21df39Svaleriabarra  """Ceed H1 Basis: finite element non tensor-product basis for H^1 discretizations."""
3145a21df39Svaleriabarra
3155a21df39Svaleriabarra  # Constructor
3165a21df39Svaleriabarra  def __init__(self, ceed, topo, ncomp, nnodes, nqpts, interp, grad, qref, qweight):
3175a21df39Svaleriabarra
3185a21df39Svaleriabarra    # Setup arguments
3195a21df39Svaleriabarra    self._pointer = ffi.new("CeedBasis *")
3205a21df39Svaleriabarra
3215a21df39Svaleriabarra    self._ceed = ceed
3225a21df39Svaleriabarra
3235a21df39Svaleriabarra    interp_pointer = ffi.new("CeedScalar *")
3245a21df39Svaleriabarra    interp_pointer = ffi.cast("CeedScalar *", interp.__array_interface__['data'][0])
3255a21df39Svaleriabarra
3265a21df39Svaleriabarra    grad_pointer = ffi.new("CeedScalar *")
3275a21df39Svaleriabarra    grad_pointer = ffi.cast("CeedScalar *", grad.__array_interface__['data'][0])
3285a21df39Svaleriabarra
3295a21df39Svaleriabarra    qref_pointer = ffi.new("CeedScalar *")
3305a21df39Svaleriabarra    qref_pointer = ffi.cast("CeedScalar *", qref.__array_interface__['data'][0])
3315a21df39Svaleriabarra
3325a21df39Svaleriabarra    qweight_pointer = ffi.new("CeedScalar *")
3335a21df39Svaleriabarra    qweight_pointer = ffi.cast("CeedScalar *", qweight.__array_interface__['data'][0])
3345a21df39Svaleriabarra
3355a21df39Svaleriabarra    # libCEED call
3365a21df39Svaleriabarra    lib.CeedBasisCreateH1(self._ceed._pointer[0], topo, ncomp, nnodes, nqpts,
3375a21df39Svaleriabarra                          interp_pointer, grad_pointer, qref_pointer,
3385a21df39Svaleriabarra                          qweight_pointer, self._pointer)
3395a21df39Svaleriabarra
3405a21df39Svaleriabarra# ------------------------------------------------------------------------------
3415a21df39Svaleriabarraclass TransposeBasis():
3425a21df39Svaleriabarra  """Transpose Ceed Basis: transpose of finite element tensor-product basis objects."""
3435a21df39Svaleriabarra
3445a21df39Svaleriabarra  # Attributes
3455a21df39Svaleriabarra  _basis = None
3465a21df39Svaleriabarra
3475a21df39Svaleriabarra  # Constructor
3485a21df39Svaleriabarra  def __init__(self, basis):
3495a21df39Svaleriabarra
3505a21df39Svaleriabarra    # Reference basis
3515a21df39Svaleriabarra    self._basis = basis
3525a21df39Svaleriabarra
3535a21df39Svaleriabarra  # Representation
3545a21df39Svaleriabarra  def __repr__(self):
3555a21df39Svaleriabarra    return "<Transpose CeedBasis instance at " + hex(id(self)) + ">"
3565a21df39Svaleriabarra
3575a21df39Svaleriabarra  # Apply Transpose Basis
3585a21df39Svaleriabarra  def apply(self, nelem, emode, u, v):
3595a21df39Svaleriabarra    """Apply basis evaluation from quadrature points to nodes.
3605a21df39Svaleriabarra
3615a21df39Svaleriabarra       Args:
3625a21df39Svaleriabarra         nelem: the number of elements to apply the basis evaluation to;
3635a21df39Svaleriabarra                  the backend will specify the ordering in a
3645a21df39Svaleriabarra                  Blocked ElemRestriction
3655a21df39Svaleriabarra         **emode: basis evaluation mode
3665a21df39Svaleriabarra         u: input vector
3675a21df39Svaleriabarra         v: output vector"""
3685a21df39Svaleriabarra
3695a21df39Svaleriabarra    # libCEED call
3705a21df39Svaleriabarra    self._basis.apply(nelem, emode, u, v, tmode=TRANSPOSE)
3715a21df39Svaleriabarra
3725a21df39Svaleriabarra# ------------------------------------------------------------------------------
373