diff --git a/NEWS.md b/NEWS.md
index a2c41f59b..83b1c74d8 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -11,11 +11,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added AMR-related methods `mark` and `estimate` to `Adaptivity` module. Implemented Dorfler marking strategy. Since PR[#1063](https://github.com/gridap/Gridap.jl/pull/1063).
+- Documentation and refactoring of Gridap.Polynomials. Since PR[#TODO](https://github.com/gridap/Gridap.jl/pull/#TODO).
+- Two new families of polynomial bases in addition to `Monomial`, `Legendre` (former `Jacobi`) and `ModalC0`: `Chebyshev` and `Bernstein`
+- `MonomialBasis` and `Q[Curl]GradMonomialBasis` have been generalized to `Legendre`, `Chebyshev` and `Bernstein` using the new `UniformPolyBasis` and `CompWiseTensorPolyBasis` respectively.
+- `PCurlGradMonomialBasis` has been generalized to `Legendre` and `Chebyshev` using the new `RaviartThomasPolyBasis`.
+- New aliases and high level constructor for `UniformPolyBasis` (former MonomialBasis): `MonomialBasis`, `LegendreBasis`, `ChebyshevBasis` and `BernsteinBasis`.
+- New high level constructors for Nedelec and Raviart-Thomas polynomial bases:
+ - Nedelec on simplex `PGradBasis(PT<:Polynomial, Val(D), order)`
+ - Nedelec on n-cubes `QGradBasis(PT<:Polynomial, Val(D), order)`
+ - Raviart on simplex `PCurlGradBasis(PT<:Polynomial, Val(D), order)`
+ - Raviart on n-cubes `QCurlGradBasis(PT<:Polynomial, Val(D), order)`
+
### Changed
- Low level optimisations to reduce allocations. `AffineMap` renamed to `AffineField`. New `AffineMap <: Map`, doing the same as `AffineField` without struct allocation. New `ConstantMap <: Map`, doing the same as `ConstantField` without struct allocation. Since PR[#1043](https://github.com/gridap/Gridap.jl/pull/1043).
- `ConstantFESpaces` can now be built on triangulations. Since PR[#1069](https://github.com/gridap/Gridap.jl/pull/1069)
+- Existing Jacobi polynomial bases/spaces were renamed to Legendre (which they were).
+- `Monomial` is now subtype of the new abstract type`Polynomial <: Field`
+- `MonomialBasis` is now an alias for `UniformPolyBasis{...,Monomial}`
+- All polynomial bases are now subtypes of the new abstract type `PolynomialBasis <: AbstractVector{<:Polynomial}`
+- `get_order(b::(Q/P)[Curl]Grad...)`, now returns the order of the basis, +1 than the order parameter passed to the constructor.
+- `NedelecPreBasisOnSimplex` is renamed `NedelecPolyBasisOnSimplex`
+- `JacobiPolynomial` is renamed `Legendre` and subtypes `Polynomial`
+- `JacobiPolynomialBasis` is renamed `LegendreBasis`
+- `ModalC0BasisFunction` is renamed `ModalC0` and subtypes `Polynomial`
+
+### Deprecated
+
+- `num_terms(f::AbstractVector{<:Field})` in favor of `length(f::PolynomialBasis)`
+- `MonomialBasis{D}(args...)` in favor of `MonomialBasis(Val(D), args...)`
+- `[P/Q][Curl]GradMonomialBasis{D}(args...)` in favor of `[...]GradBasis(Monomial, Val(D), args...)`
+- `NedelecPreBasisOnSimplex{D}(args...)` in favor of `NedelecPolyBasisOnSimplex(Val(D), args...)`
+- `JacobiPolynomialBasis{D}(args...)` in favor of `LegendreBasis(Val(D), args...)`
+
## [0.18.8] - 2024-12-2
### Added
diff --git a/docs/Project.toml b/docs/Project.toml
index 44ea2eafb..96ff808c5 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -1,6 +1,7 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Gridap = "56d4f2e9-7ea1-5844-9cf6-b9c51ca7ce8e"
+Kroki = "b3565e16-c1f2-4fe9-b4ab-221c88942068"
[compat]
Documenter = "1.0"
diff --git a/docs/generate_diagrams.jl b/docs/generate_diagrams.jl
new file mode 100644
index 000000000..ba0e7fc3f
--- /dev/null
+++ b/docs/generate_diagrams.jl
@@ -0,0 +1,85 @@
+using Kroki
+
+macro export_kroki(name, str)
+ return quote
+ write("src/assets/"*string($name)*".svg", render($str, "svg"))
+ end
+end
+
+# Plantuml uml diagrams syntax: https://plantuml.com/class-diagram#49b7759afaffc066
+
+@export_kroki :poly_1 plantuml"""
+ @startuml
+ skinparam groupInheritance 2
+ hide empty members
+
+ together {
+ abstract Field
+ abstract Polynomial {
+ +isHierarchical
+ }
+ }
+ Field <|-left- Polynomial
+
+ together {
+ struct Monomial
+ struct Legendre
+ struct Chebyshev
+ struct ModalC0
+ struct Bernstein
+ }
+
+ Polynomial <|-- Monomial
+ Polynomial <|-- Legendre
+ Polynomial <|-- Chebyshev
+ Polynomial <|-- ModalC0
+ Polynomial <|-- Bernstein
+
+ @enduml
+ """
+
+@export_kroki :poly_2 plantuml"""
+ @startuml
+ skinparam groupInheritance 2
+ hide empty members
+
+ together {
+ abstract "AbstractArray{<:Polynomial}" as a1
+ abstract PolynomialBasis {
+ +get_order
+ +return_type
+ }
+ }
+ a1 <|-left- PolynomialBasis
+
+ together {
+ struct UniformPolyBasis {
+ +get_exponents
+ +get_orders
+ }
+ struct CompWiseTensorPolyBasis
+ struct RaviartThomasPolyBasis
+ struct NedelecPolyBasisOnSimplex
+ struct ModalC0Basis {
+ +get_orders
+ }
+ }
+
+ PolynomialBasis <|-- UniformPolyBasis
+ PolynomialBasis <|-- CompWiseTensorPolyBasis
+ PolynomialBasis <|-- RaviartThomasPolyBasis
+ PolynomialBasis <|-- NedelecPolyBasisOnSimplex
+ PolynomialBasis <|-- ModalC0Basis
+
+ object "(<:Polynomial)Basis" as m1
+ object "QGrad[<:Polynomial]Basis\nQCurlGrad[<:Polynomial]Basis" as m2
+ object "PCurlGrad[<:Polynomial]Basis" as m4
+ object "PGradMonomialBasis" as m5
+ UniformPolyBasis <-down- m1
+ CompWiseTensorPolyBasis <-down- m2
+ RaviartThomasPolyBasis <-down- m4
+ NedelecPolyBasisOnSimplex <-down- m5
+
+ @enduml
+ """
+
diff --git a/docs/make.jl b/docs/make.jl
index b05b679a5..cf0083e80 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -30,7 +30,8 @@ pages = [
makedocs(
sitename = "Gridap.jl",
format = Documenter.HTML(
- size_threshold=nothing
+ size_threshold=nothing,
+ size_threshold_warn=300 * 2^10 # 300KiB
),
modules = [Gridap],
pages = pages,
diff --git a/docs/src/Polynomials.md b/docs/src/Polynomials.md
index 33a3e658c..c1f116dea 100644
--- a/docs/src/Polynomials.md
+++ b/docs/src/Polynomials.md
@@ -1,10 +1,303 @@
+# Gridap.Polynomials
```@meta
CurrentModule = Gridap.Polynomials
```
-# Gridap.Polynomials
+The module documentation is organised as follows:
+- Summary of the main features
+- Mathematical definitions of the families of [polynomial bases](@ref "Families
+ of polynomial bases") and spanned polynomial [spaces](@ref "Polynomial spaces
+ in FEM")
+- Docstrings of implemented [families](@ref "Types for polynomial families")
+ and [bases](@ref "Polynomial bases") of polynomials
+- [Low level](@ref "Low level APIs and internals") APIs, internals and
+ [deprecated](@ref "Deprecated APIs") methods
+
+### Summary
+
+```@docs
+Polynomials
+```
+
+## Mathematical definitions
+
+### Families of polynomial bases
+
+#### Monomials
+
+The [`Monomial`](@ref)s are the standard basis polynomials of general
+polynomial spaces. The order ``K`` 1D monomial is
+```math
+x \rightarrow x^K,
+```
+and the order ``\boldsymbol{K}=(K_1, K_2, \dots, K_D)`` D-dimensional monomial is defined by
+```math
+\boldsymbol{x} = (x_1, x_2, \dots, x_D) \longrightarrow
+\boldsymbol{x}^{\boldsymbol{K}} = x_1^{K_1}x_2^{K_2}...x_D^{K_D} = \Pi_{i=1}^D
+x_i^{K_i}.
+```
+
+#### Legendre polynomials
+
+The Legendre polynomials are the orthogonal 1D basis recursively defined by
+```math
+P_0(x) = 1,\qquad
+P_1(x) = x,\qquad
+P_{n+1}(x) = \frac{1}{(n+1)}( (2n+1)x P_{n}(x)-n P_{n-1}(x) ),
+```
+the orthogonality is for the ``L^2`` scalar product on ``[-1,1]``.
+
+This module implements the normalized shifted [`Legendre`](@ref) polynomials,
+shifted to be orthogonal on ``[0,1]`` using the change of variable ``x
+\rightarrow 2x-1``, leading to
+```math
+P^*_{n}(x)=\frac{1}{\sqrt{2n+1}}P_n(2x-1)=\frac{1}{\sqrt{2n+1}}(-1)^{n}\sum _{i=0}^{n}{\binom{n}{i}}{\binom{n+i}{i}}(-x)^{i}.
+```
+
+#### Chebyshev polynomials
+
+The first kind Chebyshev polynomials ``T_n`` and second kind Chebyshev
+polynomials ``U_n`` can be recursively defined by
+```math
+ T_{0}(x)=1,\qquad T_{1}(x)=\ \,x,\qquad T_{n+1}(x)=2x\,T_{n}(x)-T_{n-1}(x).\\
+ U_{0}(x)=1,\qquad U_{1}(x)=2x,\qquad U_{n+1}(x)=2x\,U_{n}(x)-U_{n-1}(x).
+```
+or explicitly defined by
+```math
+T_{n}(x)=\sum _{i=0}^{\left\lfloor {n}/{2}\right\rfloor }{\binom
+ {n}{2i}}\left(x^{2}-1\right)^{i}x^{n-2i},\qquad
+U_{n}(x)=\sum _{i=0}^{\left\lfloor {n}/{2}\right\rfloor }{\binom
+ {n+1}{2i+1}}\left(x^{2}-1\right)^{i}x^{n-2i},
+```
+where ``\left\lfloor {n}/2\right\rfloor`` is `floor(n/2)`.
+
+Similarly to Legendre above, this module implements the shifted first kind
+Chebyshev by [`Chebyshev{:T}`](@ref), defined by
+```math
+T^*_n(x) = T_n(2x-1).
+```
+The analog second kind shifted Chebyshev polynomials can be implemented by
+[`Chebyshev{:U}`](@ref) in the future.
+
+#### Bernstein polynomials
+
+The [`Bernstein`](@ref) polynomials forming a basis of ``\mathbb{P}_K`` are
+defined by
+```math
+B^K_{n}(x) = \binom{K}{n} x^n (1-x)^{K-n}\qquad\text{ for } 0\leq n\leq K.
+```
+They are positive on ``[0,1]`` and sum to ``1``.
+
+#### ModalC0 polynomials
+
+The [`ModalC0`](@ref) polynomials are 1D hierarchical and orthogonal
+polynomials ``\phi_K`` for which ``\phi_K(0) = \delta_{0K}`` and ``\phi_K(1) =
+\delta_{1K}``. This module implements the generalised version introduced in Eq.
+17 of [Badia-Neiva-Verdugo 2022](https://doi.org/10.1016/j.camwa.2022.09.027).
+
+When `ModalC0` is used as a `<:Polynomial` parameter in
+[`UniformPolyBasis`](@ref) or other bases (except `ModalC0Basis`), the trivial
+bounding box `(a=Point{D}(0...), b=Point{D}(1...))` is assumed, which
+coincides with the usual definition of the ModalC0 bases.
+
+
+### Polynomial spaces in FEM
+
+#### P and Q spaces
+
+Let us denote ``\mathbb{P}_K(x)`` the space of univariate polynomials of order up to ``K`` in the varible ``x``
+```math
+\mathbb{P}_K(x) = \text{Span}\big\{\quad x\rightarrow x^i \quad\big|\quad 0\leq i\leq K \quad\big\}.
+```
+
+Then, ``\mathbb{Q}^D`` and ``\mathbb{P}^D`` are the spaces for Lagrange elements
+on D-cubes and D-simplices respectively, defined by
+```math
+\mathbb{Q}^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq
+ \alpha_1, \alpha_2, \dots, \alpha_D \leq K \quad\big\},
+```
+and
+```math
+\mathbb{P}^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq
+ \alpha_1, \alpha_2, \dots, \alpha_D \leq K;\quad \sum_{d=1}^D \alpha_d \leq
+ K \quad\big\}.
+```
+
+To note, there is ``\mathbb{P}_K = \mathbb{P}^1_K = \mathbb{Q}^1_K``.
+
+#### Serendipity space Sr
+
+The serendipity space, commonly used for serendipity finite elements on n-cubes,
+are defined by
+```math
+\mathbb{S}r^D_K = \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq
+ \alpha_1, \alpha_2, \dots, \alpha_D \leq K;\quad
+ \sum_{d=1}^D \alpha_d\;\mathbb{1}_{[2,K]}(\alpha_d) \leq K \quad\big\}
+```
+where ``\mathbb{1}_{[2,K]}(\alpha_d)`` is ``1`` if ``\alpha_d\geq 2`` or else
+``0``.
+
+#### Homogeneous P and Q spaces
+
+It will later be useful to define the homogeneous Q spaces
+```math
+\tilde{\mathbb{Q}}^D_K = \mathbb{Q}^D_K\backslash\mathbb{Q}^D_{K-1} =
+ \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq \alpha_1,
+ \alpha_2, \dots, \alpha_D \leq K; \quad \text{max}(\alpha) = K \quad\big\},
+```
+and homogeneous P spaces
+```math
+\tilde{\mathbb{P}}^D_K = \mathbb{P}^D_K\backslash \mathbb{P}^D_{K-1} =
+ \text{Span}\big\{\quad \bm{x}\rightarrow\bm{x}^\alpha \quad\big|\quad 0\leq \alpha_1,
+ \alpha_2, \dots, \alpha_D \leq K;\quad \sum_{d=1}^D \alpha_d = K \quad\big\}.
+```
+
+
+#### Nédélec spaces
+
+The Kᵗʰ Nédélec polynomial spaces on respectively rectangles and
+triangles are defined by
+```math
+\mathbb{ND}^2_K(\square) = \left(\mathbb{Q}^2_K\right)^2 \oplus
+ \left(\begin{array}{c} y^{K+1}\,\mathbb{P}_K(x)\\ x^{K+1}\,\mathbb{P}_K(y) \end{array}\right)
+,\qquad
+\mathbb{ND}^2_K(\bigtriangleup) =\left(\mathbb{P}^2_K\right)^2 \oplus\bm{x}\times(\tilde{\mathbb{P}}^2_K)^2,
+```
+where ``\times`` here means ``\left(\begin{array}{c} x\\ y
+\end{array}\right)\times\left(\begin{array}{c} p(\bm{x})\\ q(\bm{x})
+\end{array}\right) = \left(\begin{array}{c} y p(\bm{x})\\ -x q(\bm{x})
+\end{array}\right)`` and ``\oplus`` is the direct sum of vector spaces.
+
+Then, the Kᵗʰ Nédélec polynomial spaces on respectively hexahedra and
+tetrahedra are defined by
+```math
+\mathbb{ND}^3_K(\square) = \left(\mathbb{Q}^3_K\right)^3 \oplus \bm{x}\times(\tilde{\mathbb{Q}}^3_K)^3,\qquad
+\mathbb{ND}^3_K(\bigtriangleup) =\left(\mathbb{P}^3_K\right)^3 \oplus \bm{x}\times(\tilde{\mathbb{P}}^3_K)^3.
+```
+
+``\mathbb{ND}^D_K(\square)`` and ``\mathbb{ND}^D_K(\bigtriangleup)`` are of
+order K+1 and the curl of their elements are in ``(\mathbb{Q}^D_K)^D``
+and ``(\mathbb{P}^D_K)^D`` respectively.
+
+#### Raviart-Thomas spaces
+
+The Kᵗʰ Raviart-Thomas polynomial spaces on respectively D-cubes and
+D-simplices are defined by
+```math
+\mathbb{ND}^D_K(\square) = \left(\mathbb{Q}^D_K\right)^D \oplus \bm{x}\;\tilde{\mathbb{Q}}^D_K, \qquad
+\mathbb{ND}^D_K(\bigtriangleup) = \left(\mathbb{P}^D_K\right)^D \oplus \bm{x}\;\tilde{\mathbb{P}}^D_K,
+```
+these bases are of dimension K+1 and the divergence of their elements are in
+``\mathbb{Q}^D_K`` and ``\mathbb{P}^D_K`` respectively.
+
+
+#### Filter functions
+
+Some `filter` functions are used to select which terms of a `D`-dimensional
+tensor product space of 1D polynomial bases are to be used to create a
+`D`-multivariate basis. When a filter can be chosen, the default filter is
+always the trivial filter for space of type ℚ, yielding the full tensor-product
+space.
+
+The signature of the filter functions should be
+
+ (term,order) -> Bool
+
+where `term` is a tuple of `D` integers containing the exponents of a
+multivariate monomial, that correspond to the multi-index ``\alpha`` previously
+used in the P/Q spaces definitions.
+
+When using [`hierarchical`](@ref isHierarchical) 1D bases, the following
+filters can be used to define associated polynomial spaces:
+
+| space | filter |
+| :-----------| :--------------------------------------------------------------- |
+| ℚᴰ | `_q_filter(e,order) = maximum(e) <= order` |
+| ℚᴰₙ\\ℚᴰₙ₋₁ | `_qs_filter(e,order) = maximum(e) == order` |
+| ℙᴰ | `_p_filter(e,order) = sum(e) <= order` |
+| ℙᴰₙ\\ℙᴰₙ₋₁ | `_ps_filter(e,order) = sum(e) == order` |
+| 𝕊rᴰₙ | `_ser_filter(e,order) = sum( [ i for i in e if i>1 ] ) <= order` |
+
+
+## Types for polynomial families
+
+The following types represent particular polynomial bases 'families' or 'types', later
+shortened as `PT` in type parameters.
+
+```@docs
+Polynomial
+```
+![](./assets/poly_1.svg)
+!!! warning
+ [`Polynomial`](@ref)s do not implement the `Field` interface, only the
+ [`PolynomialBasis`](@ref) can be evaluated.
+
+
+```@docs
+isHierarchical
+Monomial
+Legendre
+Chebyshev
+```
+!!! todo
+ Second kind `Chebyshev{:U}` are not implemented yet.
+
+```@docs
+Bernstein
+ModalC0
+```
+
+## Polynomial bases
+
+```@docs
+PolynomialBasis
+get_order
+MonomialBasis(args...)
+MonomialBasis
+LegendreBasis(args...)
+LegendreBasis
+ChebyshevBasis(args...)
+ChebyshevBasis
+BernsteinBasis(args...)
+```
+!!! warning
+ Calling `BernsteinBasis` with the filters (e.g. a `_p_filter`) rarely
+ yields a basis for the associated space (e.g. ``\mathbb{P}``). Indeed, the
+ term numbers do not correspond to the degree of the polynomial, because the
+ basis is not [`hierarchical`](@ref isHierarchical).
+
+```@docs
+BernsteinBasis
+PGradBasis
+QGradBasis
+PCurlGradBasis
+QCurlGradBasis
+```
+## Low level APIs and internals
+
+![](./assets/poly_2.svg)
+
+```@docs
+UniformPolyBasis
+UniformPolyBasis(::Type, ::Val{D}, ::Type, ::Int, ::Function) where D
+UniformPolyBasis(::Type, ::Val{D}, ::Type{V}, ::NTuple{D,Int}, ::Function) where {D,V}
+get_orders
+get_exponents
+CompWiseTensorPolyBasis
+NedelecPolyBasisOnSimplex
+RaviartThomasPolyBasis
+ModalC0Basis
+ModalC0Basis()
+```
+
+### Deprecated APIs
-```@autodocs
-Modules = [Polynomials,]
+```@docs
+num_terms
+PGradMonomialBasis
+PCurlGradMonomialBasis
+QGradMonomialBasis
+QCurlGradMonomialBasis
+JacobiPolynomialBasis
```
diff --git a/docs/src/TensorValues.md b/docs/src/TensorValues.md
index 6b04631cf..5b32b22ba 100644
--- a/docs/src/TensorValues.md
+++ b/docs/src/TensorValues.md
@@ -34,7 +34,7 @@ C = inner.(g,B) # inner product of g against all TensorValues in the array B
# C = [2494 2494 2494 2494 2494]
```
-To create a [`::MultiValue`](@ref) tensor from components, these should be given
+To create a [`MultiValue`](@ref) tensor from components, these should be given
as separate arguments or all gathered in a `tuple`. The order of the arguments
is the order of the linearized Cartesian indices of the corresponding array
(order of the `Base.LinearIndices` indices):
diff --git a/docs/src/assets/poly_1.svg b/docs/src/assets/poly_1.svg
new file mode 100644
index 000000000..2e3ffcf55
--- /dev/null
+++ b/docs/src/assets/poly_1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/src/assets/poly_2.svg b/docs/src/assets/poly_2.svg
new file mode 100644
index 000000000..0299b85dc
--- /dev/null
+++ b/docs/src/assets/poly_2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/Adaptivity/Adaptivity.jl b/src/Adaptivity/Adaptivity.jl
index 8811c68dc..140f4ab7b 100644
--- a/src/Adaptivity/Adaptivity.jl
+++ b/src/Adaptivity/Adaptivity.jl
@@ -1,5 +1,7 @@
"""
Mesh Adaptivity for Gridap
+
+$(public_names_in_md(@__MODULE__))
"""
module Adaptivity
diff --git a/src/Algebra/Algebra.jl b/src/Algebra/Algebra.jl
index 46a520f35..87961270d 100644
--- a/src/Algebra/Algebra.jl
+++ b/src/Algebra/Algebra.jl
@@ -1,7 +1,6 @@
"""
-The exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module Algebra
diff --git a/src/Arrays/Arrays.jl b/src/Arrays/Arrays.jl
index 54f39222a..9a790df6a 100644
--- a/src/Arrays/Arrays.jl
+++ b/src/Arrays/Arrays.jl
@@ -6,7 +6,7 @@ This module provides:
The exported names in this module are:
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module Arrays
diff --git a/src/CellData/CellData.jl b/src/CellData/CellData.jl
index d96ecca48..a3317d1c1 100644
--- a/src/CellData/CellData.jl
+++ b/src/CellData/CellData.jl
@@ -1,7 +1,6 @@
"""
-The exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module CellData
@@ -86,7 +85,7 @@ export update_state!
export DiracDelta
-export SkeletonCellFieldPair
+export SkeletonCellFieldPair
include("CellDataInterface.jl")
diff --git a/src/FESpaces/FESpaces.jl b/src/FESpaces/FESpaces.jl
index 5537ed784..6302932ce 100644
--- a/src/FESpaces/FESpaces.jl
+++ b/src/FESpaces/FESpaces.jl
@@ -1,7 +1,6 @@
"""
-The exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module FESpaces
diff --git a/src/Fields/Fields.jl b/src/Fields/Fields.jl
index 0dd060b4c..bb01fc091 100644
--- a/src/Fields/Fields.jl
+++ b/src/Fields/Fields.jl
@@ -1,3 +1,7 @@
+"""
+
+$(public_names_in_md(@__MODULE__))
+"""
module Fields
using Gridap.Arrays
@@ -6,6 +10,7 @@ import Gridap.Arrays: inverse_map
import Gridap.Arrays: get_children
import Gridap.Arrays: testitem
+using Gridap.Helpers
using Gridap.Helpers: @abstractmethod, @notimplemented
using Gridap.Helpers: @notimplementedif, @unreachable, @check
using Gridap.Helpers: tfill
diff --git a/src/Geometry/Geometry.jl b/src/Geometry/Geometry.jl
index 471b420ff..6adc931c5 100644
--- a/src/Geometry/Geometry.jl
+++ b/src/Geometry/Geometry.jl
@@ -1,7 +1,6 @@
"""
-Exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module Geometry
diff --git a/src/Helpers/HelperFunctions.jl b/src/Helpers/HelperFunctions.jl
index 5fb5cda3d..b6b0cf20c 100644
--- a/src/Helpers/HelperFunctions.jl
+++ b/src/Helpers/HelperFunctions.jl
@@ -38,3 +38,29 @@ function first_and_tail(a::Tuple)
first(a), Base.tail(a)
end
+"""
+ public_names_in_md(m::Module)
+
+Return a string displaying exported and other public names of the module for
+printing in markdown.
+"""
+function public_names_in_md(m::Module)
+ publics = filter(!=(nameof(m)), names(m))
+ exported = filter(n->Base.isexported(m,n), publics)
+ non_exported_publics = filter(∉(exported), publics)
+
+ isempty(exported) && return ""
+
+ s = """
+ ### Exported names
+ [`$(join(exported,"`](@ref), [`"))`](@ref)
+ """
+
+ isempty(non_exported_publics) && return s
+
+ s * """
+
+ ### Other public names
+ [`$(join(non_exported_publics,"`](@ref), [`"))`](@ref)
+ """
+end
diff --git a/src/Helpers/Helpers.jl b/src/Helpers/Helpers.jl
index e2e7f3d3d..15e26a1de 100644
--- a/src/Helpers/Helpers.jl
+++ b/src/Helpers/Helpers.jl
@@ -1,10 +1,7 @@
"""
This module provides a set of helper macros and helper functions
-The exported macros are:
-
-$(EXPORTS)
-
+$(public_names_in_md(@__MODULE__))
"""
module Helpers
using DocStringExtensions
@@ -26,6 +23,7 @@ export get_val_parameter
export first_and_tail
export GridapType
export set_debug_mode, set_performance_mode
+export public_names_in_md
#export operate
include("Preferences.jl")
diff --git a/src/Io/Io.jl b/src/Io/Io.jl
index f8ed74aa1..5f6febd2d 100644
--- a/src/Io/Io.jl
+++ b/src/Io/Io.jl
@@ -1,7 +1,6 @@
"""
-The exported names in this module are:
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module Io
diff --git a/src/MultiField/MultiField.jl b/src/MultiField/MultiField.jl
index bd2697232..23f76f5af 100644
--- a/src/MultiField/MultiField.jl
+++ b/src/MultiField/MultiField.jl
@@ -1,7 +1,6 @@
"""
-The exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module MultiField
diff --git a/src/ODEs/ODEs.jl b/src/ODEs/ODEs.jl
index 30f294d5d..2be3e347d 100644
--- a/src/ODEs/ODEs.jl
+++ b/src/ODEs/ODEs.jl
@@ -1,7 +1,6 @@
"""
-The exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module ODEs
diff --git a/src/Polynomials/BernsteinBases.jl b/src/Polynomials/BernsteinBases.jl
new file mode 100644
index 000000000..655f2b988
--- /dev/null
+++ b/src/Polynomials/BernsteinBases.jl
@@ -0,0 +1,228 @@
+"""
+ Bernstein <: Polynomial
+
+Type representing Bernstein polynomials, c.f. [Bernstein polynomials](@ref) section.
+"""
+struct Bernstein <: Polynomial end
+
+isHierarchical(::Type{Bernstein}) = false
+
+"""
+ BernsteinBasis{D,V,K} = UniformPolyBasis{D,V,K,Bernstein}
+
+Alias for Bernstein multivariate scalar' or `Multivalue`'d basis.
+"""
+const BernsteinBasis{D,V,K} = UniformPolyBasis{D,V,K,Bernstein}
+
+"""
+ BernsteinBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector)
+ BernsteinBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function])
+ BernsteinBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function])
+
+High level constructors of [`BernsteinBasis`](@ref).
+"""
+BernsteinBasis(args...) = UniformPolyBasis(Bernstein, args...)
+
+
+# 1D evaluation implementation
+
+"""
+ binoms(::Val{K})
+
+Returns the tuple of binomials ( C₍ₖ₀₎, C₍ₖ₁₎, ..., C₍ₖₖ₎ ).
+"""
+binoms(::Val{K}) where K = ntuple( i -> binomial(K,i-1), Val(K+1))
+
+
+function _evaluate_1d!(::Type{Bernstein},::Val{0},v::AbstractMatrix{T},x,d) where {T<:Number}
+ @inbounds v[d,1] = one(T)
+end
+
+@inline function _De_Casteljau_step_1D!(v,d,i,λ1,λ2)
+ # i = k+1
+
+ # vₖ <- xvₖ₋₁ # Bᵏₖ(x) = x*Bᵏ⁻¹ₖ₋₁(x)
+ v[d,i] = λ2*v[d,i-1]
+ # vⱼ <- xvⱼ₋₁ + (1-x)vⱼ # Bᵏⱼ(x) = x*Bᵏ⁻¹ⱼ₋₁(x) + (1-x)*Bᵏ⁻¹ⱼ(x) for j = k-1, k-2, ..., 1
+ for l in i-1:-1:2
+ v[d,l] = λ2*v[d,l-1] + λ1*v[d,l]
+ end
+ # v₀ <- (1-x)v₀ # Bᵏ₀(x) = (1-x)*Bᵏ⁻¹₀(x)
+ v[d,1] = λ1*v[d,1]
+end
+
+# jth Bernstein poly of order K at x:
+# Bᵏⱼ(x) = binom(K,j) * x^j * (1-x)^(K-j) = x*Bᵏ⁻¹ⱼ₋₁(x) + (1-x)*Bᵏ⁻¹ⱼ(x)
+function _evaluate_1d!(::Type{Bernstein},::Val{K},v::AbstractMatrix{T},x,d) where {K,T<:Number}
+ @inbounds begin
+ n = K + 1 # n > 1
+ λ2 = x[d]
+ λ1 = one(T) - λ2
+
+ # In place De Casteljau: init with B¹₀(x)=x and B¹₁(x)=1-x
+ v[d,1] = λ1
+ v[d,2] = λ2
+
+ for i in 3:n
+ _De_Casteljau_step_1D!(v,d,i,λ1,λ2)
+ ## vₖ <- xvₖ₋₁ # Bᵏₖ(x) = x*Bᵏ⁻¹ₖ₋₁(x)
+ #v[d,i] = λ2*v[d,i-1]
+ ## vⱼ <- xvⱼ₋₁ + (1-x)vⱼ # Bᵏⱼ(x) = x*Bᵏ⁻¹ⱼ₋₁(x) + (1-x)*Bᵏ⁻¹ⱼ(x) for j = k-1, k-2, ..., 1
+ #for l in i-1:-1:2
+ # v[d,l] = λ2*v[d,l-1] + λ1*v[d,l]
+ #end
+ ## v₀ <- (1-x)v₀ # Bᵏ₀(x) = (1-x)*Bᵏ⁻¹₀(x)
+ #v[d,1] = λ1*v[d,1]
+ end
+ end
+ # still optimisable for K > 2/3:
+ # - compute bj = binoms(k,j) at compile time (binoms(Val(K)) function)
+ # - compute vj = xʲ*(1-x)ᴷ⁻ʲ recursively in place like De Casteljau (saving half the redundant multiplications)
+ # - do it in a stack allocated cache (MVector, Bumber.jl)
+ # - @simd affect bj * vj in v[d,i] for all j
+end
+
+function _gradient_1d!(::Type{Bernstein},::Val{0},g::AbstractMatrix{T},x,d) where {T<:Number}
+ @inbounds g[d,1] = zero(T)
+end
+function _gradient_1d!(::Type{Bernstein},::Val{1},g::AbstractMatrix{T},x,d) where {T<:Number}
+ o = one(T)
+ @inbounds g[d,1] = -o
+ @inbounds g[d,2] = o
+end
+
+# First derivative of the jth Bernstein poly of order K at x:
+# (Bᵏⱼ)'(x) = K * ( Bᵏ⁻¹ⱼ₋₁(x) - Bᵏ⁻¹ⱼ(x) )
+# = K * x^(j-1) * (1-x)^(K-j-1) * ((1-x)*binom(K-1,j-1) - x*binom(K-1,j))
+function _gradient_1d!(::Type{Bernstein},::Val{K}, g::AbstractMatrix{T},x,d) where {K,T<:Number}
+ @inbounds begin
+ n = K + 1 # n > 2
+
+ # De Casteljau for Bᵏ⁻¹ⱼ for j = k-1, k-2, ..., 1
+ _evaluate_1d!(Bernstein,Val(K-1),g,x,d)
+
+ # gₖ <- K*gₖ₋₁ # ∂ₓBᵏₖ(x) = K*Bᵏ⁻¹ₖ₋₁(x)
+ g[d,n] = K*g[d,n-1]
+ # gⱼ <- K(gⱼ₋₁ + gⱼ) # ∂ₓBᵏⱼ(x) = K(Bᵏ⁻¹ⱼ₋₁(x) - Bᵏ⁻¹ⱼ(x)) for j = k-1, k-2, ..., 1
+ for l in n-1:-1:2
+ g[d,l] = K*(g[d,l-1] - g[d,l])
+ end
+ # g₀ <- K*g₀ # ∂ₓBᵏ₀(x) = -K*Bᵏ⁻¹₀(x)
+ g[d,1] = -K*g[d,1]
+ end
+end
+
+
+function _hessian_1d!(::Type{Bernstein},::Val{0},h::AbstractMatrix{T},x,d) where {T<:Number}
+ @inbounds h[d,1] = zero(T)
+end
+function _hessian_1d!(::Type{Bernstein},::Val{1},h::AbstractMatrix{T},x,d) where {T<:Number}
+ @inbounds h[d,1] = zero(T)
+ @inbounds h[d,2] = zero(T)
+end
+function _hessian_1d!(::Type{Bernstein},::Val{2},h::AbstractMatrix{T},x,d) where {T<:Number}
+ o = one(T)
+ @inbounds h[d,1] = 2o
+ @inbounds h[d,2] = -4o
+ @inbounds h[d,3] = 2o
+end
+
+# Second derivative of the jth Bernstein poly of order K at x:
+# (Bᵏⱼ)''(x) = K(K-1) * ( Bᵏ⁻²ⱼ₋₂(x) -2*Bᵏ⁻²ⱼ₋₁(x) + Bᵏ⁻²ⱼ(x) )
+# = K(K-1) * x^(j-2) * (1-x)^(K-j-2) * ( (1-x)^2*binom(K-2,j-2)
+# - 2x*(1-x)*binom(K-2,j-1) + (x)^2*binom(K-2,j)
+# )
+function _hessian_1d!(::Type{Bernstein},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number}
+ @inbounds begin
+ n = K + 1 # n > 3
+ KK = K*(K-1)
+
+ # De Casteljau for Bᵏ⁻²ⱼ for j = k-2, k-3, ..., 1
+ _evaluate_1d!(Bernstein,Val(K-2),h,x,d)
+
+ # hₖ <- K(K-1)*hₖ₋₂
+ h[d,n] = KK*h[d,n-2]
+ # hₖ₋₁ <- K(K-1)*(-2*hₖ₋₁ + hₖ₋₂)
+ h[d,n-1] = KK*( h[d,n-3] -2*h[d,n-2] )
+
+ # hⱼ <- K(K-1)(hⱼ₋₂ -2hⱼ₋₁ + hⱼ)
+ for l in n-2:-1:3
+ h[d,l] = KK*( h[d,l-2] -2*h[d,l-1] + h[d,l] )
+ end
+
+ # h₁ <- K(K-1)*(-2h₀ + h₁)
+ h[d,2] = KK*( -2*h[d,1] + h[d,2] )
+ # h₀ <- K(K-1)*h₀
+ h[d,1] = KK*h[d,1]
+ end
+end
+
+function _derivatives_1d!(::Type{Bernstein},v::Val_01,t::NTuple{2},x,d)
+ @inline _evaluate_1d!(Bernstein, v, t[1], x, d)
+ @inline _gradient_1d!(Bernstein, v, t[2], x, d)
+end
+
+function _derivatives_1d!(::Type{Bernstein},::Val{K},t::NTuple{2},x,d) where K
+ @inbounds begin
+ n = K + 1 # n > 2
+ v, g = t
+
+ λ2 = x[d]
+ λ1 = one(eltype(v)) - λ2
+
+ # De Casteljau for Bᵏ⁻¹ⱼ for j = k-1, k-2, ..., 1
+ _evaluate_1d!(Bernstein,Val(K-1),v,x,d)
+
+ # Compute gradients as _gradient_1d!
+ g[d,n] = K*v[d,n-1]
+ @simd for l in n-1:-1:2
+ g[d,l] = K*(v[d,l-1] - v[d,l])
+ end
+ g[d,1] = -K*v[d,1]
+
+ # Last step of De Casteljau for _evaluate_1d!
+ _De_Casteljau_step_1D!(v,d,n,λ1,λ2)
+ end
+end
+
+function _derivatives_1d!(::Type{Bernstein},v::Val_012,t::NTuple{3},x,d)
+ @inline _evaluate_1d!(Bernstein, v, t[1], x, d)
+ @inline _gradient_1d!(Bernstein, v, t[2], x, d)
+ @inline _hessian_1d!( Bernstein, v, t[3], x, d)
+end
+
+function _derivatives_1d!(::Type{Bernstein},::Val{K},t::NTuple{3},x,d) where K
+ @inbounds begin
+ n = K + 1 # n > 3
+ v, g, h = t
+
+ KK = K*(K-1)
+ λ2 = x[d]
+ λ1 = one(eltype(v)) - λ2
+
+ # De Casteljau until Bᵏ⁻²ⱼ ∀j
+ _evaluate_1d!(Bernstein,Val(K-2),v,x,d)
+
+ # Compute hessians as _hessian_1d!
+ h[d,n] = KK*v[d,n-2]
+ h[d,n-1] = KK*( v[d,n-3] -2*v[d,n-2] )
+ @simd for l in n-2:-1:3
+ h[d,l] = KK*( v[d,l-2] -2*v[d,l-1] + v[d,l] )
+ end
+ h[d,2] = KK*( -2*v[d,1] + v[d,2] )
+ h[d,1] = KK*v[d,1]
+
+ # One step of De Casteljau to get Bᵏ⁻¹ⱼ ∀j
+ _De_Casteljau_step_1D!(v,d,n-1,λ1,λ2)
+
+ # Compute gradients as _gradient_1d!
+ g[d,n] = K*v[d,n-1]
+ @simd for l in n-1:-1:2
+ g[d,l] = K*(v[d,l-1] - v[d,l])
+ end
+ g[d,1] = -K*v[d,1]
+
+ # Last step of De Casteljau for _evaluate_1d!
+ _De_Casteljau_step_1D!(v,d,n,λ1,λ2)
+ end
+end
diff --git a/src/Polynomials/ChangeBasis.jl b/src/Polynomials/ChangeBasis.jl
index d10427338..8380b365c 100644
--- a/src/Polynomials/ChangeBasis.jl
+++ b/src/Polynomials/ChangeBasis.jl
@@ -13,7 +13,7 @@ using Gridap.Polynomials
D = 2
order = 1
-f = MonomialBasis{D}(Float64,order)
+f = MonomialBasis(Val(D),Float64,order)
nodes = Point{2,Int}[(0,0),(1,0),(0,1),(1,1)]
change = inv(evaluate(f,nodes))
diff --git a/src/Polynomials/ChebyshevBases.jl b/src/Polynomials/ChebyshevBases.jl
new file mode 100644
index 000000000..913b58e21
--- /dev/null
+++ b/src/Polynomials/ChebyshevBases.jl
@@ -0,0 +1,85 @@
+"""
+ Chebyshev{kind} <: Polynomial
+
+Type representing Chebyshev polynomials of the
+- first kind: `Chebyshev{:T}`
+- second kind: `Chebyshev{:U}`
+C.f. [Chebyshev polynomials](@ref) section.
+"""
+struct Chebyshev{kind} <: Polynomial end
+
+isHierarchical(::Type{<:Chebyshev}) = true
+
+"""
+ ChebyshevBasis{D,V,kind,K} = UniformPolyBasis{D,V,K,Chebyshev{kind}}
+
+Alias for Chebyshev multivariate scalar' or `Multivalue`'d basis.
+"""
+const ChebyshevBasis{D,V,kind,K} = UniformPolyBasis{D,V,K,Chebyshev{kind}}
+
+"""
+ ChebyshevBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector; kind=:T)
+ ChebyshevBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function; kind=:T])
+ ChebyshevBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function; kind=:T])
+
+High level constructors of [`ChebyshevBasis`](@ref).
+"""
+ChebyshevBasis(args...; kind=:T) = UniformPolyBasis(Chebyshev{kind}, args...)
+
+function UniformPolyBasis(
+ ::Type{Chebyshev{:U}}, ::Val{D}, ::Type{V}, ::Int) where {D, V}
+
+ @notimplemented "1D evaluation for second kind need to be implemented here"
+end
+
+
+# 1D evaluation implementation
+
+function _evaluate_1d!(
+ ::Type{Chebyshev{kind}},::Val{0},c::AbstractMatrix{T},x,d) where {kind,T<:Number}
+
+ @inbounds c[d,1] = one(T)
+end
+
+function _evaluate_1d!(
+ ::Type{Chebyshev{kind}},::Val{K},c::AbstractMatrix{T},x,d) where {kind,K,T<:Number}
+
+ n = K + 1 # n > 1
+ ξ = (2*x[d] - 1) # ξ ∈ [-1,1]
+ ξ2 = 2*ξ
+
+ @inbounds c[d,1] = one(T)
+ @inbounds c[d,2] = (kind == :T) ? ξ : ξ2
+ for i in 3:n
+ @inbounds c[d,i] = c[d,i-1]*ξ2 - c[d,i-2]
+ end
+end
+
+function _gradient_1d!(
+ ::Type{Chebyshev{:T}},::Val{0},g::AbstractMatrix{T},x,d) where T<:Number
+
+ @inbounds g[d,1] = zero(T)
+end
+
+function _gradient_1d!(
+ ::Type{Chebyshev{:T}},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number}
+
+ n = K + 1 # n>1
+ z = zero(T)
+ o = one(T)
+ ξ = T(2*x[d] - 1)
+ dξdx = T(2.0)
+
+ unm1 = o
+ un = 2*ξ
+ @inbounds g[d,1] = z # dT_0 = 0
+ @inbounds g[d,2] = dξdx*o # dT_1 = 1*U_0 = 1
+ for i in 3:n
+ @inbounds g[d,i] = dξdx*(i-1)*un # dT_i = i*U_{i-1}
+ un, unm1 = 2*ξ*un - unm1, un
+ end
+end
+
+_gradient_1d!(::Type{Chebyshev{:U}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented
+_hessian_1d!( ::Type{Chebyshev{:U}},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number} = @notimplemented
+
diff --git a/src/Polynomials/ChebyshevPolynomialBases.jl b/src/Polynomials/ChebyshevPolynomialBases.jl
deleted file mode 100644
index 7e6ff5b40..000000000
--- a/src/Polynomials/ChebyshevPolynomialBases.jl
+++ /dev/null
@@ -1,581 +0,0 @@
-struct ChebyshevPolynomial <: Field end
-
-struct ChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial}
- orders::NTuple{D,Int}
- terms::Vector{CartesianIndex{D}}
- function ChebyshevPolynomialBasis{D}(
- ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T}
- new{D,T}(orders,terms)
- end
-end
-
-@inline Base.size(a::ChebyshevPolynomialBasis{D,T}) where {D,T} = (length(a.terms)*num_components(T),)
-@inline Base.getindex(a::ChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial()
-@inline Base.IndexStyle(::ChebyshevPolynomialBasis) = IndexLinear()
-
-function ChebyshevPolynomialBasis{D}(
- ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T}
-
- terms = _define_terms(filter, orders)
- ChebyshevPolynomialBasis{D}(T,orders,terms)
-end
-
-function ChebyshevPolynomialBasis{D}(
- ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T}
-
- orders = tfill(order,Val{D}())
- ChebyshevPolynomialBasis{D}(T,orders,filter)
-end
-
-# API
-
-function get_exponents(b::ChebyshevPolynomialBasis)
- indexbase = 1
- [Tuple(t) .- indexbase for t in b.terms]
-end
-
-function get_order(b::ChebyshevPolynomialBasis)
- maximum(b.orders)
-end
-
-function get_orders(b::ChebyshevPolynomialBasis)
- b.orders
-end
-
-return_type(::ChebyshevPolynomialBasis{D,T}) where {D,T} = T
-
-# Field implementation
-
-function return_cache(f::ChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f.terms)*num_components(T)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c)
-end
-
-function evaluate!(cache,f::ChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- r, v, c = cache
- np = length(x)
- ndof = length(f.terms)*num_components(T)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _evaluate_nd_ch!(v,xi,f.orders,f.terms,c)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{1,ChebyshevPolynomialBasis{D,V}},
- x::AbstractVector{<:Point}) where {D,V}
-
- f = fg.fa
- @assert D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f.terms)*num_components(V)
- xi = testitem(x)
- T = gradient_type(V,xi)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- g = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c, g)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{1,ChebyshevPolynomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- r, v, c, g = cache
- np = length(x)
- ndof = length(f.terms) * num_components(T)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd_ch!(v,xi,f.orders,f.terms,c,g,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-# Optimizing evaluation at a single point
-
-function return_cache(f::ChebyshevPolynomialBasis{D,T},x::Point) where {D,T}
- ndof = length(f.terms)*num_components(T)
- r = CachedArray(zeros(T,(ndof,)))
- xs = [x]
- cf = return_cache(f,xs)
- r, cf, xs
-end
-
-function evaluate!(cache,f::ChebyshevPolynomialBasis{D,T},x::Point) where {D,T}
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
-end
-
-function return_cache(
- f::FieldGradientArray{N,ChebyshevPolynomialBasis{D,V}}, x::Point) where {N,D,V}
- xs = [x]
- cf = return_cache(f,xs)
- v = evaluate!(cf,f,xs)
- r = CachedArray(zeros(eltype(v),(size(v,2),)))
- r, cf, xs
-end
-
-function evaluate!(
- cache, f::FieldGradientArray{N,ChebyshevPolynomialBasis{D,V}}, x::Point) where {N,D,V}
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
-end
-
-# Helpers
-
-function _evaluate_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- o = one(T)
- @inbounds v[d,1] = o
- if n > 1
- ξ = (2*x[d] - 1) # ξ ∈ [-1,1]
- ξ2 = 2*ξ
- v[d,2] = ξ
- for i in 3:n
- @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2]
- end
- end
-end
-
-function _evaluate_1d_ch_U!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- o = one(T)
- @inbounds v[d,1] = o
- if n > 1
- ξ = (2*x[d] - 1) # ξ ∈ [-1,1]
- ξ2 = 2*ξ
- v[d,2] = ξ2
- for i in 3:n
- @inbounds v[d,i] = v[d,i-1]*ξ2 - v[d,i-2]
- end
- end
-end
-
-function _gradient_1d_ch_T!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- z = zero(T)
- o = one(T)
- dξdx = T(2.0)
- @inbounds v[d,1] = z # dT_0 = 0
- if n > 1
- ξ = T(2*x[d] - 1)
- @inbounds v[d,2] = dξdx*o # dT_1 = 1*U_0 = 1
- unm1 = o
- un = 2*ξ
- for i in 3:n
- @inbounds v[d,i] = dξdx*(i-1)*un # dT_i = i*U_{i-1}
- un, unm1 = 2*ξ*un - unm1, un
- end
- end
-end
-
-function _evaluate_nd_ch!(
- v::AbstractVector{V},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T}) where {V,T,D}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_ch_T!(c,x,orders[d],d)
- end
-
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = o
- for d in 1:dim
- @inbounds s *= c[d,ci[d]]
- end
-
- k = _set_value!(v,s,k)
-
- end
-
-end
-
-function _gradient_nd_ch!(
- v::AbstractVector{G},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_ch_T!(c,x,orders[d],d)
- _gradient_1d_ch_T!(g,x,orders[d],d)
- end
-
- z = zero(Mutable(VectorValue{D,T}))
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = z
- for i in eachindex(s)
- @inbounds s[i] = o
- end
- for q in 1:dim
- for d in 1:dim
- if d != q
- @inbounds s[q] *= c[d,ci[d]]
- else
- @inbounds s[q] *= g[d,ci[d]]
- end
- end
- end
-
- k = _set_gradient!(v,s,k,V)
-
- end
-
-end
-
-############################################################################################
-
-"""
- struct QGradChebyshevPolynomialBasis{...} <: AbstractVector{Monomial}
-
-This type implements a multivariate vector-valued polynomial basis
-spanning the space needed for Nedelec reference elements on n-cubes.
-The type parameters and fields of this `struct` are not public.
-This type fully implements the [`Field`](@ref) interface, with up to first order
-derivatives.
-"""
-struct QGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial}
- order::Int
- terms::CartesianIndices{D}
- perms::Matrix{Int}
- function QGradChebyshevPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T}
- new{D,T}(order,terms,perms)
- end
-end
-
-Base.size(a::QGradChebyshevPolynomialBasis) = (_ndofs_qgrad_ch(a),)
-Base.getindex(a::QGradChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial()
-Base.IndexStyle(::QGradChebyshevPolynomialBasis) = IndexLinear()
-
-"""
- QGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
-
-Returns a `QGradChebyshevPolynomialBasis` object. `D` is the dimension
-of the coordinate space and `T` is the type of the components in the vector-value.
-The `order` argument has the following meaning: the curl of the functions in this basis
-is in the Q space of degree `order`.
-"""
-function QGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
- @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
- _order = order + 1
- _t = tfill(_order+1,Val{D-1}())
- t = (_order,_t...)
- terms = CartesianIndices(t)
- perms = _prepare_perms(D)
- QGradChebyshevPolynomialBasis(T,order,terms,perms)
-end
-
-"""
- num_terms(f::QGradChebyshevPolynomialBasis{D,T}) where {D,T}
-"""
-num_terms(f::QGradChebyshevPolynomialBasis{D,T}) where {D,T} = length(f.terms)*D
-
-get_order(f::QGradChebyshevPolynomialBasis) = f.order
-
-function return_cache(f::QGradChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = _ndofs_qgrad_ch(f)
- n = 1 + f.order+1
- V = VectorValue{D,T}
- r = CachedArray(zeros(V,(np,ndof)))
- v = CachedArray(zeros(V,(ndof,)))
- c = CachedArray(zeros(T,(D,n)))
- (r, v, c)
-end
-
-function evaluate!(cache,f::QGradChebyshevPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- r, v, c = cache
- np = length(x)
- ndof = _ndofs_qgrad_ch(f)
- n = 1 + f.order+1
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _evaluate_nd_qgrad_ch!(v,xi,f.order+1,f.terms,f.perms,c)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{1,QGradChebyshevPolynomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = _ndofs_qgrad_ch(f)
- n = 1 + f.order+1
- xi = testitem(x)
- V = VectorValue{D,T}
- G = gradient_type(V,xi)
- r = CachedArray(zeros(G,(np,ndof)))
- v = CachedArray(zeros(G,(ndof,)))
- c = CachedArray(zeros(T,(D,n)))
- g = CachedArray(zeros(T,(D,n)))
- (r, v, c, g)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{1,QGradChebyshevPolynomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- r, v, c, g = cache
- np = length(x)
- ndof = _ndofs_qgrad_ch(f)
- n = 1 + f.order+1
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- V = VectorValue{D,T}
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd_qgrad_ch!(v,xi,f.order+1,f.terms,f.perms,c,g,V)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-# Helpers
-
-_ndofs_qgrad_ch(f::QGradChebyshevPolynomialBasis{D}) where D = D*(length(f.terms))
-
-function _evaluate_nd_qgrad_ch!(
- v::AbstractVector{V},
- x,
- order,
- terms::CartesianIndices{D},
- perms::Matrix{Int},
- c::AbstractMatrix{T}) where {V,T,D}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_ch_T!(c,x,order,d)
- end
-
- o = one(T)
- k = 1
- m = zero(Mutable(V))
- js = eachindex(m)
- z = zero(T)
-
- for ci in terms
-
- for j in js
-
- @inbounds for i in js
- m[i] = z
- end
-
- s = o
- @inbounds for d in 1:dim
- s *= c[d,ci[perms[d,j]]]
- end
-
- m[j] = s
- v[k] = m
- k += 1
-
- end
-
- end
-
-end
-
-function _gradient_nd_qgrad_ch!(
- v::AbstractVector{G},
- x,
- order,
- terms::CartesianIndices{D},
- perms::Matrix{Int},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_ch_T!(c,x,order,d)
- _gradient_1d_ch_T!(g,x,order,d)
- end
-
- z = zero(Mutable(V))
- m = zero(Mutable(G))
- js = eachindex(z)
- mjs = eachindex(m)
- o = one(T)
- zi = zero(T)
- k = 1
-
- for ci in terms
-
- for j in js
-
- s = z
- for i in js
- s[i] = o
- end
-
- for q in 1:dim
- for d in 1:dim
- if d != q
- @inbounds s[q] *= c[d,ci[perms[d,j]]]
- else
- @inbounds s[q] *= g[d,ci[perms[d,j]]]
- end
- end
- end
-
- @inbounds for i in mjs
- m[i] = zi
- end
-
- for i in js
- @inbounds m[i,j] = s[i]
- end
- @inbounds v[k] = m
- k += 1
-
- end
-
- end
-
-end
-
-############################################################################################
-
-"""
- struct QCurlGradChebyshevPolynomialBasis{...} <: AbstractArray{Monomial}
-
-This type implements a multivariate vector-valued polynomial basis
-spanning the space needed for Raviart-Thomas reference elements on n-cubes.
-The type parameters and fields of this `struct` are not public.
-This type fully implements the [`Field`](@ref) interface, with up to first order
-derivatives.
-"""
-struct QCurlGradChebyshevPolynomialBasis{D,T} <: AbstractVector{ChebyshevPolynomial}
- qgrad::QGradChebyshevPolynomialBasis{D,T}
- function QCurlGradChebyshevPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T}
- qgrad = QGradChebyshevPolynomialBasis(T,order,terms,perms)
- new{D,T}(qgrad)
- end
-end
-
-Base.size(a::QCurlGradChebyshevPolynomialBasis) = (length(a.qgrad),)
-Base.getindex(a::QCurlGradChebyshevPolynomialBasis,i::Integer) = ChebyshevPolynomial()
-Base.IndexStyle(::QCurlGradChebyshevPolynomialBasis) = IndexLinear()
-
-"""
- QCurlGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
-
-Returns a `QCurlGradChebyshevPolynomialBasis` object. `D` is the dimension
-of the coordinate space and `T` is the type of the components in the vector-value.
-The `order` argument has the following meaning: the divergence of the functions in this basis
-is in the Q space of degree `order`.
-"""
-function QCurlGradChebyshevPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
- @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
- _order = order+1
- _t = tfill(_order,Val{D-1}())
- t = (_order+1,_t...)
- terms = CartesianIndices(t)
- perms = _prepare_perms(D)
- QCurlGradChebyshevPolynomialBasis(T,order,terms,perms)
-end
-
-return_type(::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = T
-
-function return_cache(f::QCurlGradChebyshevPolynomialBasis,x::AbstractVector{<:Point})
- return_cache(f.qgrad,x)
-end
-
-function evaluate!(cache,f::QCurlGradChebyshevPolynomialBasis,x::AbstractVector{<:Point})
- evaluate!(cache,f.qgrad,x)
-end
-
-function return_cache(
- fg::FieldGradientArray{N,<:QCurlGradChebyshevPolynomialBasis},
- x::AbstractVector{<:Point}) where N
-
- f = fg.fa
- return_cache(FieldGradientArray{N}(f.qgrad),x)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{N,<:QCurlGradChebyshevPolynomialBasis},
- x::AbstractVector{<:Point}) where N
-
- f = fg.fa
- evaluate!(cache,FieldGradientArray{N}(f.qgrad),x)
-end
-
-"""
- num_terms(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T}
-"""
-num_terms(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D
-
-get_order(f::QCurlGradChebyshevPolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad)
-
diff --git a/src/Polynomials/CompWiseTensorPolyBases.jl b/src/Polynomials/CompWiseTensorPolyBases.jl
new file mode 100644
index 000000000..f0206e125
--- /dev/null
+++ b/src/Polynomials/CompWiseTensorPolyBases.jl
@@ -0,0 +1,330 @@
+"""
+ CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT}
+
+"Polynomial basis of component wise tensor product polynomial spaces"
+
+Polynomial basis for a `D`-multivariate `V`-valued polynomial space:
+
+`V`(𝕊¹, 𝕊², ..., 𝕊ᴸ)
+
+with `L`>1, where the scalar `D`-multivariate spaces 𝕊ˡ (for 1 ≤ l ≤ `L`) of each
+(independent) component of `V` is the tensor product of 1D ℙ spaces of order
+α(l,n) for 1 ≤ n ≤ `D`, that is:
+
+𝕊¹ = ℙα(1,1) ⊗ … ⊗ ℙα(1,`D`)\\
+⋮\\
+𝕊ˡ = ⊗ₙ ℙα(l,n)\\
+⋮\\
+𝕊ᴸ = ℙα(`L`,1) ⊗ … ⊗ ℙα(`L`,`D`)
+
+The `L`×`D` matrix of orders α is given in the constructor, and `K` is the
+maximum of α. Any 1D polynomial family `PT<:Polynomial` is usable.
+"""
+struct CompWiseTensorPolyBasis{D,V,K,PT,L} <: PolynomialBasis{D,V,K,PT}
+ orders::SMatrix{L,D,Int}
+
+ function CompWiseTensorPolyBasis{D}(
+ ::Type{PT}, ::Type{V}, orders::SMatrix{L,D,Int}) where {D,PT<:Polynomial,V,L}
+
+ msg1 = "The orders matrix rows number must match the number of independent components of V"
+ @check L == num_indep_components(V) msg1
+ msg2 = "The Component Wise construction is useless for one component, use UniformPolyBasis instead"
+ @check L > 1 msg2
+ @check D > 0
+ @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type"
+ K = maximum(orders)
+
+ new{D,V,K,PT,L}(orders)
+ end
+end
+
+Base.size(a::CompWiseTensorPolyBasis) = ( sum(prod.(eachrow(a.orders .+ 1))), )
+
+"""
+ get_comp_terms(f::CompWiseTensorPolyBasis{D,V})
+
+Return a tuple (terms\\_1, ..., terms\\_l, ..., terms\\_L) containing, for each
+component of V, the Cartesian indices iterator over the terms that define 𝕊ˡ,
+that is all elements of ⟦1,`o`(l,1)+1⟧ × ⟦1,`o`(l,2)+1⟧ × … × ⟦1,`o`(l,D)+1⟧.
+
+E.g., if `orders=[ 0 1; 1 0]`, then the `comp_terms` are
+`( CartesianIndices{2}((1,2)), CartesianIndices{2}((2,1)) )`.
+"""
+function get_comp_terms(f::CompWiseTensorPolyBasis{D,V,K,PT,L}) where {D,V,K,PT,L}
+ _terms(l) = CartesianIndices( Tuple(f.orders[l,:] .+ 1) )
+ comp_terms = ntuple(l -> _terms(l), Val(L))
+ comp_terms::NTuple{L,CartesianIndices{D}}
+end
+
+
+#################################
+# nD evaluations implementation #
+#################################
+
+function _evaluate_nd!(
+ b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x,
+ r::AbstractMatrix{V}, i,
+ c::AbstractMatrix{T}) where {D,V,K,PT,L,T}
+
+ orders = b.orders
+ comp_terms = get_comp_terms(b)
+
+ for d in 1:D
+ # for each coordinate d, the order at which the basis should be evaluated is
+ # the maximum d-order for any component l
+ Kd = Val(maximum(orders[:,d]))
+ _evaluate_1d!(PT,Kd,c,x,d)
+ end
+
+ m = zero(Mutable(V))
+ k = 1
+
+ for (l,terms) in enumerate(comp_terms)
+ for ci in terms
+
+ s = one(T)
+ @inbounds for d in 1:D
+ s *= c[d,ci[d]]
+ end
+
+ k = _comp_wize_set_value!(r,i,s,k,l)
+ end
+ end
+end
+
+"""
+ _comp_wize_set_value!(r::AbstractMatrix{V},i,s::T,k,l)
+
+```
+r[i,k] = V(0, ..., 0, s, 0, ..., 0); return k+1
+```
+
+where `s` is at position `l` in `V<:MultiValue`.
+"""
+function _comp_wize_set_value!(r::AbstractMatrix{V},i,s::T,k,l) where {V,T}
+ z = zero(T)
+ ncomp = num_indep_components(V)
+ r[i,k] = ntuple(i -> ifelse(i == l, s, z),Val(ncomp))
+ return k + 1
+end
+
+function _gradient_nd!(
+ b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x,
+ r::AbstractMatrix{G}, i,
+ c::AbstractMatrix{T},
+ g::AbstractMatrix{T},
+ s::MVector{D,T}) where {D,V,K,PT,L,G,T}
+
+ orders = b.orders
+ comp_terms = get_comp_terms(b)
+
+ for d in 1:D
+ # for each spatial coordinate d, the order at which the basis should be
+ # evaluated is the maximum d-order for any component l
+ Kd = Val(maximum(orders[:,d]))
+ _derivatives_1d!(PT,Kd,(c,g),x,d)
+ end
+
+ k = 1
+
+ for (l,terms) in enumerate(comp_terms)
+ for ci in terms
+
+ for i in eachindex(s)
+ s[i] = one(T)
+ end
+
+ for q in 1:D
+ for d in 1:D
+ if d != q
+ @inbounds s[q] *= c[d,ci[d]]
+ else
+ @inbounds s[q] *= g[d,ci[d]]
+ end
+ end
+ end
+
+ k = _comp_wize_set_derivative!(r,i,s,k,Val(l),V)
+ end
+ end
+end
+
+"""
+ _comp_wize_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V})
+
+```
+z = zero(s)
+r[i,k] = G(z…, ..., z…, s…, z…, ..., z…) = (Dbᵏ)(xi)
+return k+1
+```
+
+where `s…` is the `l`ᵗʰ set of components. This is the gradient or hessian of
+the `k`ᵗʰ basis polynomial, whose nonzero component in `V` is the `l`ᵗʰ.
+"""
+@generated function _comp_wize_set_derivative!(
+ r::AbstractMatrix{G},i,s,k,::Val{l},::Type{V}) where {G,l,V}
+
+ N_val_dims = length(size(V))
+ s_size = size(G)[1:end-N_val_dims]
+
+ body = "T = eltype(s); z = zero(T);"
+ m = Array{String}(undef, size(G))
+ m .= "z"
+
+ for ci in CartesianIndices(s_size)
+ m[ci,l] = "(@inbounds s[$ci])"
+ end
+ body *= "@inbounds r[i,k] = ($(join(tuple(m...), ", ")));"
+
+ body = Meta.parse(string("begin ",body," end"))
+ return Expr(:block, body ,:(return k+1))
+end
+
+# See _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D
+@generated function _comp_wize_set_derivative!(
+ r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D
+
+ @notimplemented
+end
+
+function _hessian_nd!(
+ b::CompWiseTensorPolyBasis{D,V,K,PT,L}, x,
+ r::AbstractMatrix{H}, i,
+ c::AbstractMatrix{T},
+ g::AbstractMatrix{T},
+ h::AbstractMatrix{T},
+ s::MMatrix{D,D,T}) where {D,V,K,PT,L,H,T}
+
+ orders = b.orders
+ comp_terms = get_comp_terms(b)
+
+ for d in 1:D
+ # for each spatial coordinate d, the order at which the basis should be
+ # evaluated is the maximum d-order for any component l
+ Kd = Val(maximum(orders[:,d]))
+ _derivatives_1d!(PT,Kd,(c,g,h),x,d)
+ end
+
+ k = 1
+
+ for (l,terms) in enumerate(comp_terms)
+ for ci in terms
+
+ for i in eachindex(s)
+ s[i] = one(T)
+ end
+
+ for r in 1:D
+ for q in 1:D
+ for d in 1:D
+ if d != q && d != r
+ @inbounds s[r,q] *= c[d,ci[d]]
+ elseif d == q && d ==r
+ @inbounds s[r,q] *= h[d,ci[d]]
+ else
+ @inbounds s[r,q] *= g[d,ci[d]]
+ end
+ end
+ end
+ end
+
+ k = _comp_wize_set_derivative!(r,i,s,k,Val(l),V)
+ end
+ end
+end
+
+
+################################
+# Basis for Nedelec on D-cubes #
+################################
+
+"""
+ QGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis
+
+Return a basis of
+
+ℕ𝔻ᴰₙ(□) = (ℚᴰₙ)ᴰ ⊕ x × (ℚᴰₙ \\ ℚᴰₙ₋₁)ᴰ
+
+with n=`order`, the polynomial space for Nedelec elements on `D`-dimensional
+cubes with scalar type `T`.
+
+The `order`=n argument has the following meaning: the curl of the functions in
+this basis is in (ℚᴰₙ)ᴰ.
+
+`PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials.
+
+# Example:
+
+```jldoctest
+# a basis for Nedelec on hexahedra with divergence in ℚ₂
+b = QGradBasis(Monomial, Val(3), Float64, 2)
+```
+
+For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QGradBasis` returns
+an instance of\\
+`CompWiseTensorPolyBasis{D, VectorValue{D,T}, order+1, PT}` for `D`>1, or\\
+`UniformPolyBasis{1, VectorValue{1,T}, order+1, PT}` for `D`=1.
+"""
+function QGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T}
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+
+ V = VectorValue{D,T}
+ m = [ order + (i==j ? 0 : 1) for i in 1:D, j in 1:D ]
+ orders = SMatrix{D,D,Int}(m)
+ CompWiseTensorPolyBasis{D}(PT, V, orders)
+end
+
+function QGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T}
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+
+ V = VectorValue{1,T}
+ UniformPolyBasis(PT, Val(1), V, order+1)
+end
+
+
+#######################################
+# Basis for Raviart-Thomas on D-cubes #
+#######################################
+
+"""
+ QCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis
+
+Return a basis of
+
+ℝ𝕋ᴰₙ(□) = (ℚᴰₙ)ᴰ ⊕ x (ℚᴰₙ \\ ℚᴰₙ₋₁)
+
+with n=`order`, the polynomial space for Raviart-Thomas elements on
+`D`-dimensional cubes with scalar type `T`.
+
+The `order`=n argument has the following meaning: the divergence of the functions
+in this basis is in ℚᴰₙ.
+
+`PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials.
+
+# Example:
+
+```jldoctest
+# a basis for Raviart-Thomas on rectangles with divergence in ℚ₃
+b = QCurlGradBasis(Bernstein, Val(2), Float64, 3)
+```
+
+For more details, see [`CompWiseTensorPolyBasis`](@ref), as `QCurlGradBasis`
+returns an instance of\\
+`CompWiseTensorPolyBasis{D, VectorValue{D,T}, order+1, PT}` for `D`>1, or\\
+`UniformPolyBasis{1, VectorValue{1,T}, order+1, PT}` for `D`=1.
+"""
+function QCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T}
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+
+ V = VectorValue{D,T}
+ m = [ order + (i==j ? 1 : 0) for i in 1:D, j in 1:D ]
+ orders = SMatrix{D,D,Int}(m)
+ CompWiseTensorPolyBasis{D}(PT, V, orders)
+end
+
+function QCurlGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T}
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+
+ V = VectorValue{1,T}
+ UniformPolyBasis(PT, Val(1), V, order+1)
+end
diff --git a/src/Polynomials/Deprecated.jl b/src/Polynomials/Deprecated.jl
new file mode 100644
index 000000000..6d5b1ed2d
--- /dev/null
+++ b/src/Polynomials/Deprecated.jl
@@ -0,0 +1,89 @@
+"""
+ num_terms(a::PolynomialBasis)
+
+!!! warning
+ Deprecated in favor of length(a).
+"""
+function num_terms end
+
+@deprecate num_terms(a::PolynomialBasis) length(a)
+
+@deprecate MonomialBasis{D}(args...) where D MonomialBasis(Val(D), args...)
+
+"""
+ PGradMonomialBasis{D}(args...) where D
+
+!!! warning
+ Deprecated in favor of PGradBasis(Monomial, Val(D), args...).
+"""
+struct PGradMonomialBasis{D}
+ function PGradMonomialBasis()
+ @unreachable
+ new{0}()
+ end
+end
+@deprecate PGradMonomialBasis{D}(args...) where D PGradBasis(Monomial, Val(D), args...) false
+
+"""
+ PCurlGradMonomialBasis{D}(args...) where D
+
+!!! warning
+ Deprecated in favor of PCurlGradBasis(Monomial, Val(D), args...).
+"""
+struct PCurlGradMonomialBasis{D}
+ function PCurlGradMonomialBasis()
+ @unreachable
+ new{0}()
+ end
+end
+@deprecate PCurlGradMonomialBasis{D}(args...) where D PCurlGradBasis(Monomial, Val(D), args...)
+
+"""
+ QGradMonomialBasis{D}(args...) where D
+
+!!! warning
+ Deprecated in favor of QGradBasis(Monomial, Val(D), args...).
+"""
+struct QGradMonomialBasis{D}
+ function QGradMonomialBasis()
+ @unreachable
+ new{0}()
+ end
+end
+@deprecate QGradMonomialBasis{D}(args...) where D QGradBasis(Monomial, Val(D), args...)
+
+"""
+ QCurlGradMonomialBasis{D}(args...) where D
+
+!!! warning
+ Deprecated in favor of QCurlGradBasis(Monomial, Val(D), args...).
+"""
+struct QCurlGradMonomialBasis{D}
+ function QCurlGradMonomialBasis()
+ @unreachable
+ new{0}()
+ end
+end
+@deprecate QCurlGradMonomialBasis{D}(args...) where D QCurlGradBasis(Monomial, Val(D), args...)
+
+struct NedelecPreBasisOnSimplex{D}
+ function NedelecPreBasisOnSimplex()
+ @unreachable
+ new{0}()
+ end
+end
+@deprecate NedelecPreBasisOnSimplex{D}(args...) where D NedelecPolyBasisOnSimplex{D}(args...) false
+
+"""
+ JacobiPolynomialBasis{D}(args...) where D
+
+!!! warning
+ Deprecated in favor of LegendreBasis(Val(D), args...).
+"""
+struct JacobiPolynomialBasis{D}
+ function JacobiPolynomialBasis()
+ @unreachable
+ new{0}()
+ end
+end
+@deprecate JacobiPolynomialBasis{D}(args...) where D LegendreBasis(Val(D), args...)
diff --git a/src/Polynomials/JacobiPolynomialBases.jl b/src/Polynomials/JacobiPolynomialBases.jl
deleted file mode 100644
index a9ef86417..000000000
--- a/src/Polynomials/JacobiPolynomialBases.jl
+++ /dev/null
@@ -1,359 +0,0 @@
-struct JacobiPolynomial <: Field end
-
-struct JacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial}
- orders::NTuple{D,Int}
- terms::Vector{CartesianIndex{D}}
- function JacobiPolynomialBasis{D}(
- ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T}
- new{D,T}(orders,terms)
- end
-end
-
-@inline Base.size(a::JacobiPolynomialBasis{D,T}) where {D,T} = (length(a.terms)*num_indep_components(T),)
-@inline Base.getindex(a::JacobiPolynomialBasis,i::Integer) = JacobiPolynomial()
-@inline Base.IndexStyle(::JacobiPolynomialBasis) = IndexLinear()
-
-function JacobiPolynomialBasis{D}(
- ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T}
-
- terms = _define_terms(filter, orders)
- JacobiPolynomialBasis{D}(T,orders,terms)
-end
-
-function JacobiPolynomialBasis{D}(
- ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T}
-
- orders = tfill(order,Val{D}())
- JacobiPolynomialBasis{D}(T,orders,filter)
-end
-
-# API
-
-function get_exponents(b::JacobiPolynomialBasis)
- indexbase = 1
- [Tuple(t) .- indexbase for t in b.terms]
-end
-
-function get_order(b::JacobiPolynomialBasis)
- maximum(b.orders)
-end
-
-function get_orders(b::JacobiPolynomialBasis)
- b.orders
-end
-
-return_type(::JacobiPolynomialBasis{D,T}) where {D,T} = T
-
-# Field implementation
-
-function return_cache(f::JacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c)
-end
-
-function evaluate!(cache,f::JacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- r, v, c = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _evaluate_nd_jp!(v,xi,f.orders,f.terms,c)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{1,JacobiPolynomialBasis{D,V}},
- x::AbstractVector{<:Point}) where {D,V}
-
- f = fg.fa
- @assert D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- xi = testitem(x)
- T = gradient_type(V,xi)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- g = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c, g)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{1,JacobiPolynomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- r, v, c, g = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd_jp!(v,xi,f.orders,f.terms,c,g,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{2,JacobiPolynomialBasis{D,V}},
- x::AbstractVector{<:Point}) where {D,V}
-
- f = fg.fa
- @assert D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- xi = testitem(x)
- T = gradient_type(gradient_type(V,xi),xi)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- g = CachedArray(zeros(eltype(T),(D,n)))
- h = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c, g, h)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{2,JacobiPolynomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- r, v, c, g, h = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- setsize!(h,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _hessian_nd_jp!(v,xi,f.orders,f.terms,c,g,h,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-# Optimizing evaluation at a single point
-
-function return_cache(f::AbstractVector{JacobiPolynomial},x::Point)
- xs = [x]
- cf = return_cache(f,xs)
- v = evaluate!(cf,f,xs)
- r = CachedArray(zeros(eltype(v),(size(v,2),)))
- r, cf, xs
-end
-
-function evaluate!(cache,f::AbstractVector{JacobiPolynomial},x::Point)
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
-end
-
-function return_cache(
- f::FieldGradientArray{N,<:AbstractVector{JacobiPolynomial}}, x::Point) where {N}
- xs = [x]
- cf = return_cache(f,xs)
- v = evaluate!(cf,f,xs)
- r = CachedArray(zeros(eltype(v),(size(v,2),)))
- r, cf, xs
-end
-
-function evaluate!(
- cache, f::FieldGradientArray{N,<:AbstractVector{JacobiPolynomial}}, x::Point) where {N}
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
-end
-
-# Helpers
-
-function _evaluate_1d_jp!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- z = one(T)
- @inbounds v[d,1] = z
- if n > 1
- ξ = ( 2*x[d] - 1 )
- for i in 2:n
- @inbounds v[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0)
- end
- end
-end
-
-function _gradient_1d_jp!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- z = zero(T)
- @inbounds v[d,1] = z
- if n > 1
- ξ = ( 2*x[d] - 1 )
- for i in 2:n
- @inbounds v[d,i] = sqrt(2*i-1)*i*jacobi(ξ,i-2,1,1)
- end
- end
-end
-
-function _hessian_1d_jp!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- z = zero(T)
- @inbounds v[d,1] = z
- if n > 1
- @inbounds v[d,2] = z
- ξ = ( 2*x[d] - 1 )
- for i in 3:n
- @inbounds v[d,i] = sqrt(2*i-1)*(i*(i+1)/2)*jacobi(ξ,i-3,2,2)
- end
- end
-end
-
-function _evaluate_nd_jp!(
- v::AbstractVector{V},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T}) where {V,T,D}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_jp!(c,x,orders[d],d)
- end
-
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = o
- for d in 1:dim
- @inbounds s *= c[d,ci[d]]
- end
-
- k = _set_value!(v,s,k)
-
- end
-
-end
-
-function _gradient_nd_jp!(
- v::AbstractVector{G},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_jp!(c,x,orders[d],d)
- _gradient_1d_jp!(g,x,orders[d],d)
- end
-
- z = zero(Mutable(VectorValue{D,T}))
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = z
- for i in eachindex(s)
- @inbounds s[i] = o
- end
- for q in 1:dim
- for d in 1:dim
- if d != q
- @inbounds s[q] *= c[d,ci[d]]
- else
- @inbounds s[q] *= g[d,ci[d]]
- end
- end
- end
-
- k = _set_gradient!(v,s,k,V)
-
- end
-
-end
-
-function _hessian_nd_jp!(
- v::AbstractVector{G},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- h::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_jp!(c,x,orders[d],d)
- _gradient_1d_jp!(g,x,orders[d],d)
- _hessian_1d_jp!(h,x,orders[d],d)
- end
-
- z = zero(Mutable(TensorValue{D,D,T}))
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = z
- for i in eachindex(s)
- @inbounds s[i] = o
- end
- for r in 1:dim
- for q in 1:dim
- for d in 1:dim
- if d != q && d != r
- @inbounds s[r,q] *= c[d,ci[d]]
- elseif d == q && d ==r
- @inbounds s[r,q] *= h[d,ci[d]]
- else
- @inbounds s[r,q] *= g[d,ci[d]]
- end
- end
- end
- end
-
- k = _set_gradient!(v,s,k,V)
-
- end
-
-end
diff --git a/src/Polynomials/LegendreBases.jl b/src/Polynomials/LegendreBases.jl
new file mode 100644
index 000000000..9b5109d47
--- /dev/null
+++ b/src/Polynomials/LegendreBases.jl
@@ -0,0 +1,69 @@
+"""
+ Legendre <: Polynomial
+
+Type representing the normalised shifted Legendre polynomials, c.f. [Legendre polynomials](@ref) section.
+"""
+struct Legendre <: Polynomial end
+
+isHierarchical(::Type{Legendre}) = true
+
+"""
+ LegendreBasis{D,V,K} = UniformPolyBasis{D,V,K,Legendre}
+
+Alias for Legendre multivariate scalar' or `Multivalue`'d basis.
+"""
+const LegendreBasis{D,V,K} = UniformPolyBasis{D,V,K,Legendre}
+
+"""
+ LegendreBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector)
+ LegendreBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function])
+ LegendreBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function])
+
+High level constructors of [`LegendreBasis`](@ref).
+"""
+LegendreBasis(args...) = UniformPolyBasis(Legendre, args...)
+
+
+# 1D evaluation implementation
+
+# TODO optimize evaluation by using the iterative formula explicitely
+
+function _evaluate_1d!(::Type{Legendre},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number}
+ n = K + 1
+ @inbounds c[d,1] = one(T)
+ if n > 1
+ ξ = ( 2*x[d] - 1 )
+ for i in 2:n
+ # The sqrt(2i-1) factor normalizes the basis polynomial for L2 scalar
+ # product on ξ∈[0,1], indeed:
+ # ∫[0,1] Pn(2ξ-1)^2 dξ = 1/2 ∫[-1,1] Pn(t)^2 dt = 1/(2n+1)
+ # C.f. Eq. (1.25) in Section 1.1.5 in Ern & Guermond book (2013).
+ @inbounds c[d,i] = sqrt(2*i-1)*jacobi(ξ,i-1,0,0)
+ end
+ end
+end
+
+function _gradient_1d!(::Type{Legendre},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number}
+ n = K + 1
+ z = zero(T)
+ @inbounds g[d,1] = z
+ if n > 1
+ ξ = ( 2*x[d] - 1 )
+ for i in 2:n
+ @inbounds g[d,i] = sqrt(2*i-1)*i*jacobi(ξ,i-2,1,1)
+ end
+ end
+end
+
+function _hessian_1d!(::Type{Legendre},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number}
+ n = K + 1
+ z = zero(T)
+ @inbounds h[d,1] = z
+ if n > 1
+ @inbounds h[d,2] = z
+ ξ = ( 2*x[d] - 1 )
+ for i in 3:n
+ @inbounds h[d,i] = sqrt(2*i-1)*(i*(i+1)/2)*jacobi(ξ,i-3,2,2)
+ end
+ end
+end
diff --git a/src/Polynomials/ModalC0Bases.jl b/src/Polynomials/ModalC0Bases.jl
index 3fbe918d2..42cc40a0c 100644
--- a/src/Polynomials/ModalC0Bases.jl
+++ b/src/Polynomials/ModalC0Bases.jl
@@ -1,267 +1,131 @@
-struct ModalC0BasisFunction <: Field end
+"""
+ ModalC0 <: Polynomial
+
+Type representing ModalC0 polynomials, c.f. [ModalC0 polynomials](@ref) section.
-struct ModalC0Basis{D,T,V} <: AbstractVector{ModalC0BasisFunction}
+Reference: Eq. (17) in https://doi.org/10.1016/j.camwa.2022.09.027
+"""
+struct ModalC0 <: Polynomial end
+
+"""
+ ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0}
+
+Tensor product basis of generalised modal C0 1D basis from section 5.2 in
+https://doi.org/10.1016/j.camwa.2022.09.027.
+See also [ModalC0 polynomials](@ref) section of the documentation.
+"""
+struct ModalC0Basis{D,V,T,K} <: PolynomialBasis{D,V,K,ModalC0}
orders::NTuple{D,Int}
terms::Vector{CartesianIndex{D}}
- a::Vector{Point{D,V}}
- b::Vector{Point{D,V}}
+ a::Vector{Point{D,T}}
+ b::Vector{Point{D,T}}
+
function ModalC0Basis{D}(
- ::Type{T},
+ ::Type{V},
orders::NTuple{D,Int},
terms::Vector{CartesianIndex{D}},
- a::Vector{Point{D,V}},
- b::Vector{Point{D,V}}) where {D,T,V}
+ a::Vector{Point{D,T}},
+ b::Vector{Point{D,T}}) where {D,V,T}
+
+ _msg = "The number of bounding box points in a and b should match the number of terms"
+ @check length(terms) == length(a) == length(b) _msg
+ @check T == eltype(V) "Point and polynomial values should have the same scalar body"
+ K = maximum(orders, init=0)
- new{D,T,V}(orders,terms,a,b)
+ new{D,V,T,K}(orders,terms,a,b)
end
end
-@inline Base.size(a::ModalC0Basis{D,T,V}) where {D,T,V} = (length(a.terms)*num_indep_components(T),)
-@inline Base.getindex(a::ModalC0Basis,i::Integer) = ModalC0BasisFunction()
-@inline Base.IndexStyle(::ModalC0Basis) = IndexLinear()
+"""
+ ModalC0Basis{D}(::Type{V},order::Int; [, filter][, sort!:])
+ ModalC0Basis{D}(::Type{V},order::Int, a::Point ,b::Point ; [, filter][, sort!:])
+ ModalC0Basis{D}(::Type{V},orders::Tuple; [, filter][, sort!:])
+ ModalC0Basis{D}(::Type{V},orders::Tuple,a::Point ,b::Point ; [, filter][, sort!:])
+ ModalC0Basis{D}(::Type{V},orders::Tuple,a::Vector,b::Vector; [, filter][, sort!:])
+
+where `filter` is a `Function` defaulting to `_q_filter`, and `sort!` is a
+`Function` defaulting to `_sort_by_nfaces!`.
+
+At last, all scalar basis polynomial will have its bounding box `(a[i],b[i])`,
+but they are assumed iddentical if only two points `a` and `b` are provided,
+and default to `a=Point{D}(0...)`, `b=Point{D}(1...)` if not provided.
+
+The basis is uniform, isotropic if one `order` is provided, or anisotropic if a
+`D` tuple `orders` is provided.
+"""
+function ModalC0Basis() end
function ModalC0Basis{D}(
- ::Type{T},
+ ::Type{V},
orders::NTuple{D,Int},
- a::Vector{Point{D,V}},
- b::Vector{Point{D,V}};
+ a::Vector{Point{D,T}},
+ b::Vector{Point{D,T}};
filter::Function=_q_filter,
- sort!::Function=_sort_by_nfaces!) where {D,T,V}
+ sort!::Function=_sort_by_nfaces!) where {D,V,T}
terms = _define_terms_mc0(filter, sort!, orders)
- ModalC0Basis{D}(T,orders,terms,a,b)
+ ModalC0Basis{D}(V,orders,terms,a,b)
end
function ModalC0Basis{D}(
- ::Type{T},
+ ::Type{V},
orders::NTuple{D,Int},
- sa::Point{D,V},
- sb::Point{D,V};
+ sa::Point{D,T},
+ sb::Point{D,T};
filter::Function=_q_filter,
- sort!::Function=_sort_by_nfaces!) where {D,T,V}
+ sort!::Function=_sort_by_nfaces!) where {D,V,T}
terms = _define_terms_mc0(filter, sort!, orders)
a = fill(sa,length(terms))
b = fill(sb,length(terms))
- ModalC0Basis{D}(T,orders,terms,a,b)
+ ModalC0Basis{D}(V,orders,terms,a,b)
end
function ModalC0Basis{D}(
- ::Type{T},
+ ::Type{V},
orders::NTuple{D,Int};
filter::Function=_q_filter,
- sort!::Function=_sort_by_nfaces!) where {D,T}
+ sort!::Function=_sort_by_nfaces!) where {D,V}
- sa = Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}()))
- sb = Point{D,eltype(T)}(tfill(one(eltype(T)),Val{D}()))
- ModalC0Basis{D}(T,orders,sa,sb,filter=filter,sort! = sort!)
+ T = eltype(V)
+ sa = Point{D,T}(tfill(zero(T),Val{D}()))
+ sb = Point{D,T}(tfill( one(T),Val{D}()))
+ ModalC0Basis{D}(V,orders,sa,sb; filter=filter, sort! =sort!)
end
function ModalC0Basis{D}(
- ::Type{T},
+ ::Type{V},
order::Int,
- a::Vector{Point{D,V}},
- b::Vector{Point{D,V}};
+ a::Vector{Point{D,T}},
+ b::Vector{Point{D,T}};
filter::Function=_q_filter,
- sort!::Function=_sort_by_nfaces!) where {D,T,V}
+ sort!::Function=_sort_by_nfaces!) where {D,V,T}
orders = tfill(order,Val{D}())
- ModalC0Basis{D}(T,orders,a,b,filter=filter,sort! = sort!)
+ ModalC0Basis{D}(V,orders,a,b; filter=filter, sort! =sort!)
end
function ModalC0Basis{D}(
- ::Type{T},
+ ::Type{V},
order::Int;
filter::Function=_q_filter,
- sort!::Function=_sort_by_nfaces!) where {D,T}
+ sort!::Function=_sort_by_nfaces!) where {D,V}
orders = tfill(order,Val{D}())
- ModalC0Basis{D}(T,orders,filter=filter,sort! = sort!)
+ ModalC0Basis{D}(V,orders; filter=filter, sort! =sort!)
end
+
# API
-"""
- get_order(b::ModalC0Basis)
-"""
-function get_order(b::ModalC0Basis)
- maximum(b.orders)
-end
+@inline Base.size(a::ModalC0Basis{D,V}) where {D,V} = (length(a.terms)*num_indep_components(V),)
-"""
- get_orders(b::ModalC0Basis)
-"""
function get_orders(b::ModalC0Basis)
b.orders
end
-return_type(::ModalC0Basis{D,T,V}) where {D,T,V} = T
-
-# Field implementation
-
-function return_cache(f::ModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V}
- @assert D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c)
-end
-
-function evaluate!(cache,f::ModalC0Basis{D,T,V},x::AbstractVector{<:Point}) where {D,T,V}
- r, v, c = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _evaluate_nd_mc0!(v,xi,f.a,f.b,f.orders,f.terms,c)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{1,ModalC0Basis{D,V,W}},
- x::AbstractVector{<:Point}) where {D,V,W}
-
- f = fg.fa
- @assert D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- xi = testitem(x)
- T = gradient_type(V,xi)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- g = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c, g)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{1,ModalC0Basis{D,T,V}},
- x::AbstractVector{<:Point}) where {D,T,V}
-
- f = fg.fa
- r, v, c, g = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd_mc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{2,ModalC0Basis{D,V,W}},
- x::AbstractVector{<:Point}) where {D,V,W}
-
- f = fg.fa
- @assert D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- xi = testitem(x)
- T = gradient_type(gradient_type(V,xi),xi)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- g = CachedArray(zeros(eltype(T),(D,n)))
- h = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c, g, h)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{2,ModalC0Basis{D,T,V}},
- x::AbstractVector{<:Point}) where {D,T,V}
-
- f = fg.fa
- r, v, c, g, h = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- setsize!(h,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _hessian_nd_mc0!(v,xi,f.a,f.b,f.orders,f.terms,c,g,h,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-# Optimizing evaluation at a single point
-
-function return_cache(f::AbstractVector{ModalC0BasisFunction},x::Point)
- xs = [x]
- cf = return_cache(f,xs)
- v = evaluate!(cf,f,xs)
- r = CachedArray(zeros(eltype(v),(size(v,2),)))
- r, cf, xs
-end
-
-function evaluate!(cache,f::AbstractVector{ModalC0BasisFunction},x::Point)
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
-end
-
-function return_cache(
- f::FieldGradientArray{N,<:AbstractVector{ModalC0BasisFunction}}, x::Point) where {N}
- xs = [x]
- cf = return_cache(f,xs)
- v = evaluate!(cf,f,xs)
- r = CachedArray(zeros(eltype(v),(size(v,2),)))
- r, cf, xs
-end
-
-function evaluate!(
- cache, f::FieldGradientArray{N,<:AbstractVector{ModalC0BasisFunction}}, x::Point) where {N}
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
-end
-
# Helpers
-_s_filter_mc0(e,o) = ( sum( [ i for i in e if i>1 ] ) <= o )
-
-_sort_by_tensor_prod!(terms,orders) = terms
-
function _sort_by_nfaces!(terms::Vector{CartesianIndex{D}},orders) where D
# Generate indices of n-faces and order s.t.
@@ -296,7 +160,7 @@ end
function _compute_filter_mask(terms,filter,orders)
g = (0 .* orders) .+ 1
to = CartesianIndex(g)
- maxorder = _maximum(orders)
+ maxorder = maximum(orders)
term_to_is_fterm = lazy_map(t->filter(Int[Tuple(t-to)...],maxorder),terms)
findall(term_to_is_fterm)
end
@@ -308,134 +172,81 @@ function _define_terms_mc0(filter,sort!,orders)
collect(lazy_map(Reindex(terms),mask))
end
-function _evaluate_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T
- @assert order > 0
- n = order + 1
- z = one(T)
- @inbounds v[d,1] = z - x[d]
- @inbounds v[d,2] = x[d]
- if n > 2
- ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] )
- for i in 3:n
- @inbounds v[d,i] = -sqrt(2*i-3)*v[d,1]*v[d,2]*jacobi(ξ,i-3,1,1)/(i-2)
- end
- end
-end
-function _gradient_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T
- @assert order > 0
- n = order + 1
- z = one(T)
- @inbounds v[d,1] = -z
- @inbounds v[d,2] = z
- if n > 2
- ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] )
- v1 = z - x[d]
- v2 = x[d]
- for i in 3:n
- j, dj = jacobi_and_derivative(ξ,i-3,1,1)
- @inbounds v[d,i] = -sqrt(2*i-3)*(v[d,1]*v2*j+v1*v[d,2]*j+v1*v2*(2/(b[d]-a[d]))*dj)/(i-2)
- end
- end
-end
+#################################
+# nD evaluations implementation #
+#################################
-function _hessian_1d_mc0!(v::AbstractMatrix{T},x,a,b,order,d) where T
- @assert order > 0
- n = order + 1
- y = zero(T)
- z = one(T)
- @inbounds v[d,1] = y
- @inbounds v[d,2] = y
- if n > 2
- ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] )
- v1 = z - x[d]
- v2 = x[d]
- dv1 = -z
- dv2 = z
- for i in 3:n
- j, dj = jacobi_and_derivative(ξ,i-3,1,1)
- _, d2j = jacobi_and_derivative(ξ,i-4,2,2)
- @inbounds v[d,i] = -sqrt(2*i-3)*(2*dv1*dv2*j+2*(dv1*v2+v1*dv2)*(2/(b[d]-a[d]))*dj+v1*v2*d2j*2*i*((b[d]-a[d])^2))/(i-2)
- end
- end
-end
+function _evaluate_nd!(
+ basis::ModalC0Basis{D,V,T,K}, x,
+ r::AbstractMatrix{V}, i,
+ c::AbstractMatrix{T}) where {D,V,T,K}
-function _evaluate_nd_mc0!(
- v::AbstractVector{V},
- x,
- a::Vector{Point{D,T}},
- b::Vector{Point{D,T}},
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T}) where {V,T,D}
+ terms = basis.terms
+ orders = basis.orders
+ a = basis.a
+ b = basis.b
- dim = D
- o = one(T)
k = 1
l = length(terms)
- for (i,ci) in enumerate(terms)
+ for (n,ci) in enumerate(terms)
- for d in 1:dim
- _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d)
+ for d in 1:D
+ _evaluate_1d_mc0!(c,x,a[n],b[n],orders[d],d)
end
- s = o
- for d in 1:dim
+ s = one(T)
+ for d in 1:D
@inbounds s *= c[d,ci[d]]
end
- k = _set_value_mc0!(v,s,k,l)
-
+ k = _set_value_mc0!(r,i,s,k,l)
end
-
end
-@inline function _set_value_mc0!(v::AbstractVector{V},s::T,k,l) where {V,T}
+@inline function _set_value_mc0!(r::AbstractMatrix{V},i,s::T,k,l) where {V,T}
ncomp = num_indep_components(V)
z = zero(T)
for j in 1:ncomp
m = k+l*(j-1)
- @inbounds v[m] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp))
+ @inbounds r[i,m] = ntuple(p -> ifelse(p == j, s, z),Val(ncomp))
end
k+1
end
-@inline function _set_value_mc0!(v::AbstractVector{<:Real},s,k,l)
- @inbounds v[k] = s
+@inline function _set_value_mc0!(r::AbstractMatrix{<:Real},i,s,k,l)
+ @inbounds r[i,k] = s
k+1
end
-function _gradient_nd_mc0!(
- v::AbstractVector{G},
- x,
- a::Vector{Point{D,T}},
- b::Vector{Point{D,T}},
- orders,
- terms::AbstractVector{CartesianIndex{D}},
+function _gradient_nd!(
+ basis::ModalC0Basis{D,V,T,K}, x,
+ r::AbstractMatrix{G}, i,
c::AbstractMatrix{T},
g::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
+ s::MVector{D,T}) where {D,V,T,K,G}
+
+ terms = basis.terms
+ orders = basis.orders
+ a = basis.a
+ b = basis.b
- dim = D
- z = zero(Mutable(VectorValue{D,T}))
- o = one(T)
k = 1
l = length(terms)
- for (i,ci) in enumerate(terms)
+ for (n,ci) in enumerate(terms)
- for d in 1:dim
- _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d)
- _gradient_1d_mc0!(g,x,a[i],b[i],orders[d],d)
+ for d in 1:D
+ _evaluate_1d_mc0!(c,x,a[n],b[n],orders[d],d)
+ _gradient_1d_mc0!(g,x,a[n],b[n],orders[d],d)
end
- s = z
- for i in eachindex(s)
- @inbounds s[i] = o
+ for j in eachindex(s)
+ @inbounds s[j] = one(T)
end
- for q in 1:dim
- for d in 1:dim
+ for q in 1:D
+ for d in 1:D
if d != q
@inbounds s[q] *= c[d,ci[d]]
else
@@ -444,27 +255,25 @@ function _gradient_nd_mc0!(
end
end
- k = _set_gradient_mc0!(v,s,k,l,V)
-
+ k = _set_derivative_mc0!(r,i,s,k,l,V)
end
-
end
-@inline function _set_gradient_mc0!(
- v::AbstractVector{G},s,k,l,::Type{<:Real}) where G
+@inline function _set_derivative_mc0!(
+ r::AbstractMatrix{G},i,s,k,l,::Type{<:Real}) where G
- @inbounds v[k] = s
+ @inbounds r[i,k] = s
k+1
end
# Indexing and m definition should be fixed if G contains symmetries, that is
# if the code is optimized for symmetric tensor V valued FESpaces
# (if gradient_type(V) returned a symmetric higher order tensor type G)
-@inline @generated function _set_gradient_mc0!(
- v::AbstractVector{G},s,k,l,::Type{V}) where {V,G}
+@inline @generated function _set_derivative_mc0!(
+ r::AbstractMatrix{G},i1,s,k,l,::Type{V}) where {V,G}
# Git blame me for readable non-generated version
- @notimplementedif num_indep_components(G) != num_components(G) "Not implemented for symmetric Jacobian or Hessian"
-
+ @notimplementedif num_indep_components(V) != num_components(V) "Not implemented for symmetric Jacobian or Hessian"
+
m = Array{String}(undef, size(G))
N_val_dims = length(size(V))
s_size = size(G)[1:end-N_val_dims]
@@ -474,7 +283,7 @@ end
id = join(Tuple(ci))
body *= "@inbounds s$id = s[$ci];"
end
-
+
V_size = size(V)
for (ij,j) in enumerate(CartesianIndices(V_size))
for i in CartesianIndices(m)
@@ -485,46 +294,43 @@ end
m[ci,j] = "s$id"
end
body *= "i = k + l*($ij-1);"
- body *= "@inbounds v[i] = ($(join(tuple(m...), ", ")));"
+ body *= "@inbounds r[i1,i] = ($(join(tuple(m...), ", ")));"
end
body = Meta.parse(string("begin ",body," end"))
return Expr(:block, body ,:(return k+1))
end
-function _hessian_nd_mc0!(
- v::AbstractVector{G},
- x,
- a::Vector{Point{D,T}},
- b::Vector{Point{D,T}},
- orders,
- terms::AbstractVector{CartesianIndex{D}},
+function _hessian_nd!(
+ basis::ModalC0Basis{D,V,T,K}, x,
+ r::AbstractMatrix{G}, i,
c::AbstractMatrix{T},
g::AbstractMatrix{T},
h::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
+ s::MMatrix{D,D,T}) where {D,V,T,K,G}
+
+ terms = basis.terms
+ orders = basis.orders
+ a = basis.a
+ b = basis.b
- dim = D
- z = zero(Mutable(TensorValue{D,D,T}))
- o = one(T)
k = 1
l = length(terms)
- for (i,ci) in enumerate(terms)
+ for (n,ci) in enumerate(terms)
- for d in 1:dim
- _evaluate_1d_mc0!(c,x,a[i],b[i],orders[d],d)
- _gradient_1d_mc0!(g,x,a[i],b[i],orders[d],d)
- _hessian_1d_mc0!(h,x,a[i],b[i],orders[d],d)
+ for d in 1:D
+ _evaluate_1d_mc0!(c,x,a[n],b[n],orders[d],d)
+ _gradient_1d_mc0!(g,x,a[n],b[n],orders[d],d)
+ _hessian_1d_mc0!(h,x,a[n],b[n],orders[d],d)
end
- s = z
- for i in eachindex(s)
- @inbounds s[i] = o
+ for j in eachindex(s)
+ @inbounds s[j] = one(T)
end
- for r in 1:dim
- for q in 1:dim
- for d in 1:dim
+ for r in 1:D
+ for q in 1:D
+ for d in 1:D
if d != q && d != r
@inbounds s[r,q] *= c[d,ci[d]]
elseif d == q && d ==r
@@ -536,8 +342,95 @@ function _hessian_nd_mc0!(
end
end
- k = _set_gradient_mc0!(v,s,k,l,V)
+ k = _set_derivative_mc0!(r,i,s,k,l,V)
+ end
+end
+
+
+#################################
+# 1D evaluations implementation #
+#################################
+
+# Reference: equation (17) in
+#
+# Badia, S.; Neiva, E. & Verdugo, F.; (2022);
+# Robust high-order unfitted finite elements by interpolation-based discrete extension,
+# Computers & Mathematics with Applications,
+# https://doi.org/10.1016/j.camwa.2022.09.027
+function _evaluate_1d_mc0!(c::AbstractMatrix{T},x,a,b,order,d) where T
+ @assert order > 0
+ n = order + 1
+ o = one(T)
+ @inbounds c[d,1] = o - x[d]
+ @inbounds c[d,2] = x[d]
+ if n > 2
+ ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] )
+ for i in 3:n
+ @inbounds c[d,i] = -sqrt(2*i-3)*c[d,1]*c[d,2]*jacobi(ξ,i-3,1,1)/(i-2)
+ end
+ end
+end
+
+function _gradient_1d_mc0!(g::AbstractMatrix{T},x,a,b,order,d) where T
+ @assert order > 0
+ n = order + 1
+ o = one(T)
+ @inbounds g[d,1] = -o
+ @inbounds g[d,2] = o
+ if n > 2
+ ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] )
+ v1 = o - x[d]
+ v2 = x[d]
+ for i in 3:n
+ j, dj = jacobi_and_derivative(ξ,i-3,1,1)
+ @inbounds g[d,i] = -sqrt(2*i-3)*(g[d,1]*v2*j+v1*g[d,2]*j+v1*v2*(2/(b[d]-a[d]))*dj)/(i-2)
+ end
+ end
+end
+function _hessian_1d_mc0!(h::AbstractMatrix{T},x,a,b,order,d) where T
+ @assert order > 0
+ n = order + 1
+ z = zero(T)
+ o = one(T)
+ @inbounds h[d,1] = z
+ @inbounds h[d,2] = z
+ if n > 2
+ ξ = ( 2*x[d] - ( a[d] + b[d] ) ) / ( b[d] - a[d] )
+ v1 = o - x[d]
+ v2 = x[d]
+ dv1 = -o
+ dv2 = o
+ for i in 3:n
+ j, dj = jacobi_and_derivative(ξ,i-3,1,1)
+ _, d2j = jacobi_and_derivative(ξ,i-4,2,2)
+ @inbounds h[d,i] = -sqrt(2*i-3)*(2*dv1*dv2*j+2*(dv1*v2+v1*dv2)*(2/(b[d]-a[d]))*dj+v1*v2*d2j*2*i*((b[d]-a[d])^2))/(i-2)
+ end
end
+end
+
+
+#######################################
+# Generic 1D internal polynomial APIs #
+#######################################
+
+# For possible use with UniformPolyBasis etc.
+# Make it for x∈[0,1] like the other 1D bases.
+
+function _evaluate_1d!(::Type{ModalC0},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number}
+ a = zero(x)
+ b = zero(x) .+ one(T)
+ @inline _evaluate_1d_mc0!(c,x,a,b,K,d)
+end
+
+function _gradient_1d!(::Type{ModalC0},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number}
+ a = zero(x)
+ b = zero(x) .+ one(T)
+ @inline _gradient_1d_mc0!(g,x,a,b,K,d)
+end
+function _hessian_1d!(::Type{ModalC0},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number}
+ a = zero(x)
+ b = zero(x) .+ one(T)
+ @inline _hessian_1d_mc0!(h,x,a,b,K,d)
end
diff --git a/src/Polynomials/MonomialBases.jl b/src/Polynomials/MonomialBases.jl
index f1452f34b..f37a7ab55 100644
--- a/src/Polynomials/MonomialBases.jl
+++ b/src/Polynomials/MonomialBases.jl
@@ -1,601 +1,80 @@
-struct Monomial <: Field end
-
"""
- struct MonomialBasis{D,T} <: AbstractVector{Monomial}
+ Monomial <: Polynomial
-Type representing a basis of multivariate scalar-valued, vector-valued, or
-tensor-valued, iso- or aniso-tropic monomials. The fields
-of this `struct` are not public.
-This type fully implements the [`Field`](@ref) interface, with up to second order
-derivatives.
+Type representing the monomial polynomials, c.f. [Monomials](@ref) section.
"""
-struct MonomialBasis{D,T} <: AbstractVector{Monomial}
- orders::NTuple{D,Int}
- terms::Vector{CartesianIndex{D}}
- function MonomialBasis{D}(
- ::Type{T}, orders::NTuple{D,Int}, terms::Vector{CartesianIndex{D}}) where {D,T}
- new{D,T}(orders,terms)
- end
-end
+struct Monomial <: Polynomial end
-Base.size(a::MonomialBasis{D,T}) where {D,T} = (length(a.terms)*num_indep_components(T),)
-# @santiagobadia : Not sure we want to create the monomial machinery
-Base.getindex(a::MonomialBasis,i::Integer) = Monomial()
-Base.IndexStyle(::MonomialBasis) = IndexLinear()
+isHierarchical(::Type{Monomial}) = true
"""
- MonomialBasis{D}(::Type{T}, orders::Tuple [, filter::Function]) where {D,T}
+ MonomialBasis{D,V,K} = UniformPolyBasis{D,V,K,Monomial}
-This version of the constructor allows to pass a tuple `orders` containing the
-polynomial order to be used in each of the `D` dimensions in order to construct
-an anisotropic tensor-product space.
+Alias for monomial Multivariate scalar' or `Multivalue`'d basis.
"""
-function MonomialBasis{D}(
- ::Type{T}, orders::NTuple{D,Int}, filter::Function=_q_filter) where {D,T}
-
- terms = _define_terms(filter, orders)
- MonomialBasis{D}(T,orders,terms)
-end
+const MonomialBasis{D,V,K} = UniformPolyBasis{D,V,K,Monomial}
"""
- MonomialBasis{D}(::Type{T}, order::Int [, filter::Function]) where {D,T}
-
-Returns an instance of `MonomialBasis` representing a multivariate polynomial basis
-in `D` dimensions, of polynomial degree `order`, whose value is represented by the type `T`.
-The type `T` is typically `<:Number`, e.g., `Float64` for scalar-valued functions and `VectorValue{D,Float64}`
-for vector-valued ones.
-
-# Filter function
-
-The `filter` function is used to select which terms of the tensor product space
-of order `order` in `D` dimensions are to be used. If the filter is not provided, the full tensor-product
-space is used by default leading to a multivariate polynomial space of type Q.
-The signature of the filter function is
-
- (e,order) -> Bool
-
-where `e` is a tuple of `D` integers containing the exponents of a multivariate monomial. The following filters
-are used to select well known polynomial spaces
-
-- Q space: `(e,order) -> true`
-- P space: `(e,order) -> sum(e) <= order`
-- "Serendipity" space: `(e,order) -> sum( [ i for i in e if i>1 ] ) <= order`
+ MonomialBasis(::Val{D}, ::Type{V}, order::Int, terms::Vector)
+ MonomialBasis(::Val{D}, ::Type{V}, order::Int [, filter::Function])
+ MonomialBasis(::Val{D}, ::Type{V}, orders::Tuple [, filter::Function])
+High level constructors of [`MonomialBasis`](@ref).
"""
-function MonomialBasis{D}(
- ::Type{T}, order::Int, filter::Function=_q_filter) where {D,T}
-
- orders = tfill(order,Val{D}())
- MonomialBasis{D}(T,orders,filter)
-end
-
-# API
-
-"""
- get_exponents(b::MonomialBasis)
-
-Get a vector of tuples with the exponents of all the terms in the
-monomial basis.
-
-# Examples
-
-```jldoctest
-using Gridap.Polynomials
-
-b = MonomialBasis{2}(Float64,2)
-
-exponents = get_exponents(b)
-
-println(exponents)
-
-# output
-Tuple{Int,Int}[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]
-```
-"""
-function get_exponents(b::MonomialBasis)
- indexbase = 1
- [Tuple(t) .- indexbase for t in b.terms]
-end
-
-"""
- get_order(b::MonomialBasis)
-"""
-function get_order(b::MonomialBasis)
- maximum(b.orders)
-end
-
-"""
- get_orders(b::MonomialBasis)
-"""
-function get_orders(b::MonomialBasis)
- b.orders
-end
-
-"""
-"""
-return_type(::MonomialBasis{D,T}) where {D,T} = T
-
-# Field implementation
-function return_cache(f::MonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- @check D == length(eltype(x)) "Incorrect number of point components"
- zT = zero(T)
- zxi = zero(eltype(eltype(x)))
- Tp = typeof( zT*zxi*zxi + zT*zxi*zxi )
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(Tp,(np,ndof)))
- v = CachedArray(zeros(Tp,(ndof,)))
- c = CachedArray(zeros(eltype(Tp),(D,n)))
- (r, v, c)
-end
-
-function evaluate!(cache,f::MonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- r, v, c = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _evaluate_nd!(v,xi,f.orders,f.terms,c)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function _return_cache(
- fg::FieldGradientArray{1,MonomialBasis{D,V}},
- x::AbstractVector{<:Point},
- ::Type{T},
- TisbitsType::Val{true}) where {D,V,T}
-
- f = fg.fa
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- g = CachedArray(zeros(eltype(T),(D,n)))
- (r,v,c,g)
-end
-
-function _return_cache(
- fg::FieldGradientArray{1,MonomialBasis{D,V}},
- x::AbstractVector{<:Point},
- ::Type{T},
- TisbitsType::Val{false}) where {D,V,T}
-
- cache = _return_cache(fg,x,T,Val{true}())
- z = CachedArray(zeros(eltype(T),D))
- (cache...,z)
-end
-
-function return_cache(
- fg::FieldGradientArray{1,MonomialBasis{D,V}},
- x::AbstractVector{<:Point}) where {D,V}
-
- xi = testitem(x)
- T = gradient_type(V,xi)
- TisbitsType = Val(isbitstype(T))
- _return_cache(fg,x,T,TisbitsType)
-end
-
-function _evaluate!(
- cache,
- fg::FieldGradientArray{1,MonomialBasis{D,T}},
- x::AbstractVector{<:Point},
- TisbitsType::Val{true}) where {D,T}
-
- f = fg.fa
- r, v, c, g = cache
- z = zero(Mutable(VectorValue{D,eltype(T)}))
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd!(v,xi,f.orders,f.terms,c,g,z,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function _evaluate!(
- cache,
- fg::FieldGradientArray{1,MonomialBasis{D,T}},
- x::AbstractVector{<:Point},
- TisbitsType::Val{false}) where {D,T}
-
- f = fg.fa
- r, v, c, g, z = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd!(v,xi,f.orders,f.terms,c,g,z,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{1,MonomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- r, v, c, g = cache
- TisbitsType = Val(isbitstype(eltype(c)))
- _evaluate!(cache,fg,x,TisbitsType)
-end
-
-function return_cache(
- fg::FieldGradientArray{2,MonomialBasis{D,V}},
- x::AbstractVector{<:Point}) where {D,V}
-
- f = fg.fa
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = length(f)
- xi = testitem(x)
- T = gradient_type(gradient_type(V,xi),xi)
- n = 1 + _maximum(f.orders)
- r = CachedArray(zeros(T,(np,ndof)))
- v = CachedArray(zeros(T,(ndof,)))
- c = CachedArray(zeros(eltype(T),(D,n)))
- g = CachedArray(zeros(eltype(T),(D,n)))
- h = CachedArray(zeros(eltype(T),(D,n)))
- (r, v, c, g, h)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{2,MonomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- r, v, c, g, h = cache
- np = length(x)
- ndof = length(f)
- n = 1 + _maximum(f.orders)
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- setsize!(h,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _hessian_nd!(v,xi,f.orders,f.terms,c,g,h,T)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-# Optimizing evaluation at a single point
-
-function return_cache(f::AbstractVector{Monomial},x::Point)
- xs = [x]
- cf = return_cache(f,xs)
- v = evaluate!(cf,f,xs)
- r = CachedArray(zeros(eltype(v),(size(v,2),)))
- r, cf, xs
-end
-
-function evaluate!(cache,f::AbstractVector{Monomial},x::Point)
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
-end
+MonomialBasis(args...) = UniformPolyBasis(Monomial, args...)
-function return_cache(
- f::FieldGradientArray{N,<:AbstractVector{Monomial}}, x::Point) where {N}
- xs = [x]
- cf = return_cache(f,xs)
- v = evaluate!(cf,f,xs)
- r = CachedArray(zeros(eltype(v),(size(v,2),)))
- r, cf, xs
+function PGradBasis(::Type{Monomial},::Val{D},::Type{T},order::Int) where {D,T}
+ NedelecPolyBasisOnSimplex{D}(Monomial,T,order)
end
-
-function evaluate!(
- cache, f::FieldGradientArray{N,<:AbstractVector{Monomial}}, x::Point) where {N}
- r, cf, xs = cache
- xs[1] = x
- v = evaluate!(cf,f,xs)
- ndof = size(v,2)
- setsize!(r,(ndof,))
- a = r.array
- copyto!(a,v)
- a
+function PGradBasis(::Type{Monomial},::Val{1},::Type{T},order::Int) where T
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+ V = VectorValue{1,T}
+ UniformPolyBasis(Monomial, Val(1), V, order+1)
end
-# Helpers
-
-_q_filter(e,o) = true
+# 1D evaluation implementation
-function _define_terms(filter,orders)
- t = orders .+ 1
- g = (0 .* orders) .+ 1
- cis = CartesianIndices(t)
- co = CartesianIndex(g)
- maxorder = _maximum(orders)
- [ ci for ci in cis if filter(Int[Tuple(ci-co)...],maxorder) ]
-end
-
-function _evaluate_1d!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- z = one(T)
- @inbounds v[d,1] = z
+function _evaluate_1d!(::Type{Monomial},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number}
+ n = K + 1
+ xn = one(T)
@inbounds xd = x[d]
- xn = xd
- for i in 2:n
- @inbounds v[d,i] = xn
- xn *= xd
- end
-end
-function _gradient_1d!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
- z = zero(T)
- @inbounds v[d,1] = z
- @inbounds xd = x[d]
- xn = one(T)
- for i in 2:n
- @inbounds v[d,i] = (i-1)*xn
+ for i in 1:n
+ @inbounds c[d,i] = xn
xn *= xd
end
end
-function _hessian_1d!(v::AbstractMatrix{T},x,order,d) where T
- n = order + 1
+
+function _gradient_1d!(::Type{Monomial},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number}
+ n = K + 1
z = zero(T)
- @inbounds v[d,1] = z
- if n>1
- @inbounds v[d,2] = z
- end
- @inbounds xd = x[d]
xn = one(T)
- for i in 3:n
- @inbounds v[d,i] = (i-1)*(i-2)*xn
+ @inbounds xd = x[d]
+
+ @inbounds g[d,1] = z
+ for i in 2:n
+ @inbounds g[d,i] = (i-1)*xn
xn *= xd
end
end
-function _evaluate_nd!(
- v::AbstractVector{V},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T}) where {V,T,D}
-
- dim = D
- for d in 1:dim
- _evaluate_1d!(c,x,orders[d],d)
- end
-
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = o
- for d in 1:dim
- @inbounds s *= c[d,ci[d]]
- end
-
- k = _set_value!(v,s,k)
-
- end
+function _hessian_1d!(::Type{Monomial},::Val{0},h::AbstractMatrix{T},x,d) where {T<:Number}
+ @inbounds h[d,1] = zero(T)
end
-function _set_value!(v::AbstractVector{V},s::T,k) where {V,T}
- ncomp = num_indep_components(V)
+function _hessian_1d!(::Type{Monomial},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number}
+ n = K + 1 # n>1
z = zero(T)
- @inbounds for j in 1:ncomp
- v[k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp))
- k += 1
- end
- k
-end
-
-function _set_value!(v::AbstractVector{<:Real},s,k)
- @inbounds v[k] = s
- k+1
-end
-
-function _gradient_nd!(
- v::AbstractVector{G},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- z::AbstractVector{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d!(c,x,orders[d],d)
- _gradient_1d!(g,x,orders[d],d)
- end
-
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = z
- for i in eachindex(s)
- @inbounds s[i] = o
- end
- for q in 1:dim
- for d in 1:dim
- if d != q
- @inbounds s[q] *= c[d,ci[d]]
- else
- @inbounds s[q] *= g[d,ci[d]]
- end
- end
- end
-
- k = _set_gradient!(v,s,k,V)
-
- end
-
-end
-
-function _set_gradient!(
- v::AbstractVector{G},s,k,::Type{<:Real}) where G
-
- @inbounds v[k] = s
- k+1
-end
-
-@generated function _set_gradient!(
- v::AbstractVector{G},s,k,::Type{V}) where {V,G}
- # Git blame me for readable non-generated version
-
- w = zero(V)
- m = Array{String}(undef, size(G))
- N_val_dims = length(size(V))
- s_size = size(G)[1:end-N_val_dims]
-
- body = "T = eltype(s); z = zero(T);"
- for ci in CartesianIndices(s_size)
- id = join(Tuple(ci))
- body *= "@inbounds s$id = s[$ci];"
- end
-
- for j in CartesianIndices(w)
- for i in CartesianIndices(m)
- m[i] = "z"
- end
- for ci in CartesianIndices(s_size)
- id = join(Tuple(ci))
- m[ci,j] = "s$id"
- end
- body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));"
- body *= "k = k + 1;"
- end
-
- body = Meta.parse(string("begin ",body," end"))
- return Expr(:block, body ,:(return k))
-end
-
-# Specialization for SymTensorValue and SymTracelessTensorValue,
-# necessary as long as outer(Point, V<:AbstractSymTensorValue)::G does not
-# return a tensor type that implements the appropriate symmetries of the
-# gradient (and hessian)
-@generated function _set_gradient!(
- v::AbstractVector{G},s,k,::Type{V}) where {V<:AbstractSymTensorValue{D},G} where D
- # Git blame me for readable non-generated version
-
- T = eltype(s)
- m = Array{String}(undef, size(G))
- s_length = size(G)[1]
-
- is_traceless = V <: SymTracelessTensorValue
- skip_last_diagval = is_traceless ? 1 : 0 # Skid V_DD if traceless
-
- body = "z = $(zero(T));"
- for i in 1:s_length
- body *= "@inbounds s$i = s[$i];"
- end
-
- for c in 1:(D-skip_last_diagval) # Go over cols
- for r in c:D # Go over lower triangle, current col
- for i in eachindex(m)
- m[i] = "z"
- end
- for i in 1:s_length # indices of the Vector s
- m[i,r,c] = "s$i"
- if (r!=c)
- m[i,c,r] = "s$i"
- elseif is_traceless # V_rr contributes negatively to V_DD (tracelessness)
- m[i,D,D] = "-s$i"
- end
- end
- body *= "@inbounds v[k] = ($(join(tuple(m...), ", ")));"
- body *= "k = k + 1;"
- end
- end
-
- body = Meta.parse(string("begin ",body," end"))
- return Expr(:block, body ,:(return k))
-end
-
-function _hessian_nd!(
- v::AbstractVector{G},
- x,
- orders,
- terms::AbstractVector{CartesianIndex{D}},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- h::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d!(c,x,orders[d],d)
- _gradient_1d!(g,x,orders[d],d)
- _hessian_1d!(h,x,orders[d],d)
- end
-
- z = zero(Mutable(TensorValue{D,D,T}))
- o = one(T)
- k = 1
-
- for ci in terms
-
- s = z
- for i in eachindex(s)
- @inbounds s[i] = o
- end
- for r in 1:dim
- for q in 1:dim
- for d in 1:dim
- if d != q && d != r
- @inbounds s[r,q] *= c[d,ci[d]]
- elseif d == q && d ==r
- @inbounds s[r,q] *= h[d,ci[d]]
- else
- @inbounds s[r,q] *= g[d,ci[d]]
- end
- end
- end
- end
-
- k = _set_gradient!(v,s,k,V)
+ xn = one(T)
+ @inbounds xd = x[d]
+ @inbounds h[d,1] = z
+ @inbounds h[d,2] = z
+ for i in 3:n
+ @inbounds h[d,i] = (i-1)*(i-2)*xn
+ xn *= xd
end
-
end
-_maximum(orders::Tuple{}) = 0
-_maximum(orders) = maximum(orders)
diff --git a/src/Polynomials/NedelecPolyBases.jl b/src/Polynomials/NedelecPolyBases.jl
new file mode 100644
index 000000000..a5d49af9d
--- /dev/null
+++ b/src/Polynomials/NedelecPolyBases.jl
@@ -0,0 +1,276 @@
+"""
+ NedelecPolyBasisOnSimplex{D,V,K,PT} <: PolynomialBasis{D,V,K,PT}
+
+Basis of the vector valued (`V<:VectorValue{D}`) space ℕ𝔻ᴰₙ(△) for `D`=2,3.
+This space is the polynomial space for Nedelec elements on simplices with
+curl in (ℙᴰₙ)ᴰ. Its maximum degree is n+1 = `K`. `get_order` on it returns `K`.
+
+Currently, the basis is implemented as the union of a UniformPolyBasis{...,PT}
+for ℙᴰₙ and a monomial basis for x × (ℙᴰₙ \\ ℙᴰₙ₋₁)ᴰ.
+"""
+struct NedelecPolyBasisOnSimplex{D,V,K,PT} <: PolynomialBasis{D,V,K,PT}
+ order::Int
+ function NedelecPolyBasisOnSimplex{D}(::Type{PT},::Type{T},order::Integer) where {D,PT<:Polynomial,T}
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+ @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type"
+ @notimplementedif !(D in (2,3))
+ K = Int(order)+1
+ V = VectorValue{D,T}
+ new{D,V,K,PT}(Int(order))
+ end
+end
+
+function Base.size(a::NedelecPolyBasisOnSimplex{D}) where D
+ k = a.order+1
+ n = div(k*prod(i->(k+i),2:D),factorial(D-1))
+ (n,)
+end
+
+function return_cache(
+ f::NedelecPolyBasisOnSimplex{D,V,K,PT},x::AbstractVector{<:Point}) where {D,V,K,PT}
+
+ np = length(x)
+ ndofs = length(f)
+ a = zeros(V,(np,ndofs))
+ P = UniformPolyBasis(PT,Val(D),V,K-1,_p_filter)
+ cP = return_cache(P,x)
+ CachedArray(a), cP, P
+end
+
+function evaluate!(
+ cache,f::NedelecPolyBasisOnSimplex{3,V},x::AbstractVector{<:Point}) where V
+ ca,cP,P = cache
+ K = get_order(f)
+ np = length(x)
+ ndofs = length(f)
+ ndofsP = length(P)
+ setsize!(ca,(np,ndofs))
+ Px = evaluate!(cP,P,x)
+ a = ca.array
+ T = eltype(V)
+ z = zero(T)
+ for (i,xi) in enumerate(x)
+ # terms for (ℙₖ)³
+ for j in 1:ndofsP
+ a[i,j] = Px[i,j]
+ end
+ # terms for x × (ℙₖ\ℙₖ₋₁)³
+ j = ndofsP
+ x1,x2,x3 = x[i]
+ for β in 1:K
+ for α in 1:(K+1-β)
+ j += 1
+ a[i,j] = VectorValue(
+ -x1^(α-1)*x2^(K-α-β+2)*x3^(β-1),
+ x1^α*x2^(K-α-β+1)*x3^(β-1),
+ z)
+ j += 1
+ a[i,j] = VectorValue(
+ -x1^(K-α-β+1)*x2^(β-1)*x3^α,
+ z,
+ x1^(K-α-β+2)*x2^(β-1)*x3^(α-1))
+ end
+ end
+ for γ in 1:K
+ j += 1
+ a[i,j] = VectorValue(
+ z,
+ -x2^(γ-1)*x3^(K-γ+1),
+ x2^γ*x3^(K-γ))
+ end
+ end
+ a
+end
+
+function evaluate!(
+ cache,f::NedelecPolyBasisOnSimplex{2,V},x::AbstractVector{<:Point}) where V
+ ca,cP,P = cache
+ K = get_order(f)
+ np = length(x)
+ ndofs = length(f)
+ ndofsP = length(P)
+ setsize!(ca,(np,ndofs))
+ a = ca.array
+ T = eltype(V)
+ z = zero(T)
+ Px = evaluate!(cP,P,x)
+ for (i,xi) in enumerate(x)
+ # terms for (ℙₖ)²
+ for j in 1:ndofsP
+ a[i,j] = Px[i,j]
+ end
+ # terms for x × (ℙₖ\ℙₖ₋₁)²
+ j = ndofsP
+ x1,x2 = xi
+ for α in 1:K
+ j += 1
+ a[i,j] = VectorValue(-x1^(α-1)*x2^(K-α+1),x1^α*x2^(K-α))
+ end
+ #u = one(T)
+ #a[i,1] = VectorValue((u,z))
+ #a[i,2] = VectorValue((z,u))
+ #a[i,3] = VectorValue((-xi[2],xi[1]))
+ end
+ a
+end
+
+function return_cache(
+ g::FieldGradientArray{1,<:NedelecPolyBasisOnSimplex{D,V,K,PT}},
+ x::AbstractVector{<:Point}) where {D,V,K,PT}
+ f = g.fa
+ np = length(x)
+ ndofs = length(f)
+ xi = testitem(x)
+ G = gradient_type(V,xi)
+ a = zeros(G,(np,ndofs))
+ mb = UniformPolyBasis(PT,Val(D),V,K-1,_p_filter)
+ P = Broadcasting(∇)(mb)
+ cP = return_cache(P,x)
+ CachedArray(a), cP, P
+end
+
+function evaluate!(
+ cache,
+ g::FieldGradientArray{1,<:NedelecPolyBasisOnSimplex{3,V}},
+ x::AbstractVector{<:Point}) where V
+ ca,cP,P = cache
+ f = g.fa
+ K = get_order(f)
+ np = length(x)
+ ndofs = length(f)
+ setsize!(ca,(np,ndofs))
+ a = ca.array
+ fill!(a,zero(eltype(a)))
+ ndofsP = length(P)
+ Px = evaluate!(cP,P,x)
+ T = eltype(V)
+ z = zero(T)
+ for (i,xi) in enumerate(x)
+ # terms for ∇((ℙₖ)³)
+ for j in 1:ndofsP
+ a[i,j] = Px[i,j]
+ end
+ # terms for ∇(x × (ℙₖ\ℙₖ₋₁)³)
+ j = ndofsP
+ x1,x2,x3 = x[i]
+ for β in 1:K
+ for α in 1:(K+1-β)
+ j += 1
+ a[i,j] = TensorValue(
+ #-x1^(α-1)*x2^(K-α-β+2)*x3^(β-1),
+ -(α-1)*_exp(x1,α-2)*x2^(K-α-β+2)*x3^(β-1),
+ -x1^(α-1)*(K-α-β+2)*_exp(x2,K-α-β+1)*x3^(β-1),
+ -x1^(α-1)*x2^(K-α-β+2)*(β-1)*_exp(x3,β-2),
+ #x1^α*x2^(K-α-β+1)*x3^(β-1),
+ α*_exp(x1,α-1)*x2^(K-α-β+1)*x3^(β-1),
+ x1^α*(K-α-β+1)*_exp(x2,K-α-β)*x3^(β-1),
+ x1^α*x2^(K-α-β+1)*(β-1)*_exp(x3,β-2),
+ z,z,z)
+ j += 1
+ a[i,j] = TensorValue(
+ #-x1^(K-α-β+1)*x2^(β-1)*x3^α,
+ -(K-α-β+1)*_exp(x1,K-α-β)*x2^(β-1)*x3^α,
+ -x1^(K-α-β+1)*(β-1)*_exp(x2,β-2)*x3^α,
+ -x1^(K-α-β+1)*x2^(β-1)*α*_exp(x3,α-1),
+ z,z,z,
+ #x1^(K-α-β+2)*x2^(β-1)*x3^(α-1),
+ (K-α-β+2)*_exp(x1,K-α-β+1)*x2^(β-1)*x3^(α-1),
+ x1^(K-α-β+2)*(β-1)*_exp(x2,β-2)*x3^(α-1),
+ x1^(K-α-β+2)*x2^(β-1)*(α-1)*_exp(x3,α-2))
+ end
+ end
+ for γ in 1:K
+ j += 1
+ a[i,j] = TensorValue(
+ z,z,z,
+ #-x2^(γ-1)*x3^(K-γ+1),
+ -0*x2^(γ-1)*x3^(K-γ+1),
+ -(γ-1)*_exp(x2,γ-2)*x3^(K-γ+1),
+ -x2^(γ-1)*(K-γ+1)*_exp(x3,K-γ),
+ #x2^γ*x3^(K-γ),
+ 0*x2^γ*x3^(K-γ),
+ γ*_exp(x2,γ-1)*x3^(K-γ),
+ x2^γ*(K-γ)*_exp(x3,K-γ-1))
+ end
+ #u = one(T)
+ #a[i,4] = TensorValue((z,-u,z, u,z,z, z,z,z))
+ #a[i,5] = TensorValue((z,z,-u, z,z,z, u,z,z))
+ #a[i,6] = TensorValue((z,z,z, z,z,-u, z,u,z))
+ end
+ a
+end
+
+_exp(a,y) = y>0 ? a^y : one(a)
+
+function evaluate!(
+ cache,
+ g::FieldGradientArray{1,<:NedelecPolyBasisOnSimplex{2,V}},
+ x::AbstractVector{<:Point}) where V
+ f = g.fa
+ ca,cP,P = cache
+ K = get_order(f)
+ np = length(x)
+ ndofs = length(f)
+ setsize!(ca,(np,ndofs))
+ a = ca.array
+ fill!(a,zero(eltype(a)))
+ T = eltype(V)
+ z = zero(T)
+ ndofsP = length(P)
+ Px = evaluate!(cP,P,x)
+ for (i,xi) in enumerate(x)
+ # terms for ∇((ℙₖ)²)
+ for j in 1:ndofsP
+ a[i,j] = Px[i,j]
+ end
+ # terms for ∇(x × (ℙₖ\ℙₖ₋₁)²)
+ j = ndofsP
+ x1,x2 = x[i]
+ for α in 1:K
+ j += 1
+ a[i,j] = TensorValue(
+ #-x1^(α-1)*x2^(K-α+1),
+ -(α-1)*_exp(x1,α-2)*x2^(K-α+1),
+ -x1^(α-1)*(K-α+1)*_exp(x2,K-α),
+ #x1^α*x2^(K-α),
+ α*_exp(x1,α-1)*x2^(K-α),
+ x1^α*(K-α)*_exp(x2,K-α-1))
+ end
+ #u = one(T)
+ #a[i,3] = TensorValue((z,-u, u,z))
+ end
+ a
+end
+
+####################################
+# Basis for Nedelec on D-simplices #
+####################################
+
+"""
+ PGradBasis(::Type{Monomial}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis
+
+Return a basis of
+
+ℕ𝔻ᴰₙ(△) = (ℙᴰₙ)ᴰ ⊕ x × (ℙᴰₙ \\ ℙᴰₙ₋₁)ᴰ
+
+with n=`order`, the polynomial space for Nedelec elements on `D`-dimensional
+simplices with scalar type `T`. `D` must be 1, 2 or 3.
+
+The `order`=n argument has the following meaning: the curl of the functions in
+this basis is in (ℙᴰₙ)ᴰ.
+
+# Example:
+
+```jldoctest
+# a basis for Nedelec on tetrahedra with curl in ℙ₂
+b = PGradBasis(Monomial, Val(3), Float64, 2)
+```
+"""
+function PGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T}
+ # Although NedelecPolyBasisOnSimplex can be constructed with any PT1 ] ) <= order) # Serendipity
+
+function _define_terms(filter,orders)
+ t = orders .+ 1
+ g = (0 .* orders) .+ 1
+ cis = CartesianIndices(t)
+ co = CartesianIndex(g)
+ maxorder = maximum(orders, init=0)
+ [ ci for ci in cis if filter(Int[Tuple(ci-co)...],maxorder) ]
+end
+
+
+#########################################
+# Generic array of field implementation #
+#########################################
+
+function _return_cache(
+ f::PolynomialBasis{D}, x,::Type{G},::Val{N_deriv}) where {D,G,N_deriv}
+
+ @assert D == length(eltype(x)) "Incorrect number of point components"
+ T = eltype(G)
+ np = length(x)
+ ndof = length(f)
+ ndof_1d = get_order(f) + 1
+ # Cache for the returned array
+ r = CachedArray(zeros(G,(np,ndof)))
+ # Cache for the 1D basis function values in each dimension (to be
+ # tensor-producted), and of their N_deriv'th 1D derivatives
+ t = ntuple( _ -> CachedArray(zeros(T,(D,ndof_1d ))), Val(N_deriv+1))
+ (r, t...)
+end
+
+function return_cache(f::PolynomialBasis{D,V}, x::AbstractVector{<:Point}) where {D,V}
+ _return_cache(f,x,V,Val(0))
+end
+
+function return_cache(
+ fg::FieldGradientArray{N,<:PolynomialBasis{D,V}},
+ x::AbstractVector{<:Point}) where {N,D,V}
+
+ f = fg.fa
+ xi = testitem(x)
+ G = V
+ for _ in 1:N
+ G = gradient_type(G,xi)
+ end
+ _return_cache(f,x,G,Val(N))
+end
+
+
+function _setsize!(f::PolynomialBasis{D}, np, r, t...) where D
+ ndof = length(f)
+ ndof_1d = get_order(f) + 1
+ setsize!(r,(np,ndof))
+ for c in t
+ setsize!(c,(D,ndof_1d))
+ end
+end
+
+function evaluate!(cache,
+ f::PolynomialBasis,
+ x::AbstractVector{<:Point})
+
+ r, c = cache
+ np = length(x)
+ _setsize!(f,np,r,c)
+ for i in 1:np
+ @inbounds xi = x[i]
+ _evaluate_nd!(f,xi,r,i,c)
+ end
+ r.array
+end
+
+function evaluate!(cache,
+ fg::FieldGradientArray{1,<:PolynomialBasis{D,V}},
+ x::AbstractVector{<:Point}) where {D,V}
+
+ f = fg.fa
+ r, c, g = cache
+ np = length(x)
+ _setsize!(f,np,r,c,g)
+ s = zero(Mutable(VectorValue{D,eltype(V)}))
+ for i in 1:np
+ @inbounds xi = x[i]
+ _gradient_nd!(f,xi,r,i,c,g,s)
+ end
+ r.array
+end
+
+function evaluate!(cache,
+ fg::FieldGradientArray{2,<:PolynomialBasis{D,V}},
+ x::AbstractVector{<:Point}) where {D,V}
+
+ f = fg.fa
+ r, c, g, h = cache
+ np = length(x)
+ _setsize!(f,np,r,c,g,h)
+ s = zero(Mutable(TensorValue{D,D,eltype(V)}))
+ for i in 1:np
+ @inbounds xi = x[i]
+ _hessian_nd!(f,xi,r,i,c,g,h,s)
+ end
+ r.array
+end
+
+
+##############################################
+# Optimizing of evaluation at a single point #
+##############################################
+
+function return_cache(f::PolynomialBasis,x::Point)
+ xs = [x]
+ cf = return_cache(f,xs)
+ v = evaluate!(cf,f,xs)
+ r = CachedArray(zeros(eltype(v),(size(v,2),)))
+ r, cf, xs
+end
+
+function evaluate!(cache,f::PolynomialBasis,x::Point)
+ r, cf, xs = cache
+ xs[1] = x
+ v = evaluate!(cf,f,xs)
+ ndof = size(v,2)
+ setsize!(r,(ndof,))
+ a = r.array
+ copyto!(a,v)
+ a
+end
+
+function return_cache(
+ f::FieldGradientArray{N,<:PolynomialBasis}, x::Point) where N
+
+ xs = [x]
+ cf = return_cache(f,xs)
+ v = evaluate!(cf,f,xs)
+ r = CachedArray(zeros(eltype(v),(size(v,2),)))
+ r, cf, xs
+end
+
+function evaluate!(
+ cache, f::FieldGradientArray{N,<:PolynomialBasis}, x::Point) where N
+
+ r, cf, xs = cache
+ xs[1] = x
+ v = evaluate!(cf,f,xs)
+ ndof = size(v,2)
+ setsize!(r,(ndof,))
+ a = r.array
+ copyto!(a,v)
+ a
+end
+
+
+###############################
+# nD internal polynomial APIs #
+###############################
+
+"""
+ _evaluate_nd!(b,xi,r,i,c)
+
+Compute and assign: `r`[`i`] = `b`(`xi`) = (`b`₁(`xi`), ..., `b`ₙ(`xi`))
+
+where n = length(`b`) (cardinal of the basis), that is the function computes
+the basis polynomials at a single point `xi` and setting the result in the `i`th
+row of `r`.
+"""
+function _evaluate_nd!(
+ b::PolynomialBasis, xi,
+ r::AbstractMatrix, i,
+ c::AbstractMatrix)
+
+ @abstractmethod
+end
+
+"""
+ _gradient_nd!(b,xi,r,i,c,g,s)
+
+Compute and assign: `r`[`i`] = ∇`b`(`xi`) = (∇`b`₁(`xi`), ..., ∇`b`ₙ(`xi`))
+
+where n = length(`b`) (cardinal of the basis), like [`_evaluate_nd!`](@ref) but
+for gradients of `b`ₖ(`xi`), and
+
+- `g` is a mutable `D`×`K` cache (for the 1D polynomials first derivatives).
+- `s` is a mutable length `D` cache for ∇`b`ₖ(`xi`).
+"""
+function _gradient_nd!(
+ b::PolynomialBasis, xi,
+ r::AbstractMatrix, i,
+ c::AbstractMatrix,
+ g::AbstractMatrix,
+ s::MVector)
+
+ @abstractmethod
+end
+
+"""
+ _hessian_nd!(b,xi,r,i,c,g,h,s)
+
+Compute and assign: `r`[`i`] = H`b`(`xi`) = (H`b`₁(`xi`), ..., H`b`ₙ(`xi`))
+
+where n = length(`b`) (cardinal of the basis), like [`_evaluate_nd!`](@ref) but
+for hessian matrices/tensor of `b`ₖ(`xi`), and
+
+- `h` is a mutable `D`×`K` cache (for the 1D polynomials second derivatives).
+- `s` is a mutable `D`×`D` cache for H`b`ₖ(`xi`).
+"""
+function _hessian_nd!(
+ b::PolynomialBasis, xi,
+ r::AbstractMatrix, i,
+ c::AbstractMatrix,
+ g::AbstractMatrix,
+ h::AbstractMatrix,
+ s::MMatrix)
+
+ @abstractmethod
+end
+
+
+###############################
+# 1D internal polynomial APIs #
+###############################
+
+"""
+ _evaluate_1d!(PT::Type{<:Polynomial},::Val{K},c,x,d)
+
+Evaluates in place the 1D basis polynomials of the family `PT` at one D-dim.
+point `x` along the given coordinate 1 ≤ `d` ≤ D.
+
+`c` is an AbstractMatrix of size (at least) `d`×(`K`+1), such that the
+1 ≤ i ≤ `k`+1 values are stored in `c[d,i]`.
+"""
+function _evaluate_1d!(::Type{<:Polynomial},::Val{K},c::AbstractMatrix{T},x,d) where {K,T<:Number}
+ @abstractmethod
+end
+
+"""
+ _gradient_1d!(PT::Type{<:Polynomial},::Val{K},g,x,d)
+
+Like [`_evaluate_1d!`](@ref), but computes the first derivative of the basis
+polynomials.
+"""
+function _gradient_1d!(::Type{<:Polynomial},::Val{K},g::AbstractMatrix{T},x,d) where {K,T<:Number}
+ @abstractmethod
+end
+
+"""
+ _hessian_1d!(PT::Type{<:Polynomial},::Val{K},g,x,d)
+
+Like [`_evaluate_1d!`](@ref), but computes the second derivative of the basis
+polynomials.
+"""
+function _hessian_1d!(::Type{<:Polynomial},::Val{K},h::AbstractMatrix{T},x,d) where {K,T<:Number}
+ @abstractmethod
+end
+
+# Dispatch helpers for base cases
+const Val_01 = Union{Val{0},Val{1}}
+const Val_012 = Union{Val{0},Val{1},Val{2}}
+
+"""
+ _derivatives_1d!(PT::Type{<:Polynomial}, ::Val{K}, (c,g,...), x, d)
+
+Same as calling
+```
+_evaluate_1d!(PT, Val(K), c, x d)
+_gradient_1d!(PT, Val(K), g, x d)
+ ⋮
+```
+but with possible performance optimization.
+"""
+function _derivatives_1d!( ::Type{<:Polynomial},v::Val,t::NTuple{N},x,d) where N
+ @abstractmethod
+end
+
+function _derivatives_1d!(PT::Type{<:Polynomial},v::Val,t::NTuple{1},x,d)
+ @inline _evaluate_1d!(PT, v, t[1], x, d)
+end
+
+function _derivatives_1d!(PT::Type{<:Polynomial},v::Val,t::NTuple{2},x,d)
+ @inline _evaluate_1d!(PT, v, t[1], x, d)
+ @inline _gradient_1d!(PT, v, t[2], x, d)
+end
+
+function _derivatives_1d!(PT::Type{<:Polynomial},v::Val,t::NTuple{3},x,d)
+ @inline _evaluate_1d!(PT, v, t[1], x, d)
+ @inline _gradient_1d!(PT, v, t[2], x, d)
+ @inline _hessian_1d!( PT, v, t[3], x, d)
+end
diff --git a/src/Polynomials/Polynomials.jl b/src/Polynomials/Polynomials.jl
index 81a4b04c2..b4fb645f2 100644
--- a/src/Polynomials/Polynomials.jl
+++ b/src/Polynomials/Polynomials.jl
@@ -1,9 +1,80 @@
"""
-This module provides a collection of multivariate polynomial bases.
-
-The exported names are:
-
-$(EXPORTS)
+This module provides a collection of uni- and multi-variate scalar- and multi-value'd polynomial bases.
+
+Most of the basis polynomials are composed using products of 1D polynomials,
+represented by the type [`Polynomial`](@ref).
+Five `Polynomial` families are currently implemented: [`Monomial`](@ref),
+[`Legendre`](@ref), [`Chebyshev`](@ref), [`Bernstein`](@ref) and [`ModalC0`](@ref).
+
+The polynomial bases all subtype [`PolynomialBasis`](@ref), which subtypes
+`AbstractVector{<:Field}`, so they implement the `Field` interface up to first
+or second derivatives.
+
+Constructors for commonly used bases (see the documentation for the spaces definitions):
+- ℚ spaces: `[Polynomial]Basis(Val(D), V, order)`
+- ℙ spaces: `[Polynomial]Basis(..., Polynomials._p_filter)`
+- ℚₙ\\ℚₙ₋₁: `[Polynomial]Basis(..., Polynomials._qs_filter)`
+- ℙₙ\\ℙₙ₋₁: `[Polynomial]Basis(..., Polynomials._ps_filter)`
+- ℕ𝔻(△): [`PGradBasis`](@ref)`(Val(D), T, order)`
+- ℕ𝔻(□): [`QGradBasis`](@ref)`(...)`
+- ℝ𝕋(△): [`PCurlGradBasis`](@ref)`(...)`
+- ℝ𝕋(□): [`QCurlGradBasis`](@ref)`(...)`
+
+### Examples
+
+```julia
+using Gridap
+using Gridap.Polynomials
+using Gridap.Fields: return_type
+
+# Basis of ℚ¹₂ of Float64 value type based on Bernstein polynomials:
+# {(1-x)², 2x(1-x), x²}
+D = 1; n = 2 # spatial dimension and order
+b = BernsteinBasis(Val(D), Float64, n)
+
+# APIs
+length(b) # 3
+return_type(b) # Float64
+get_order(b) # 2
+
+xi =Point(0.1)
+evaluate(b, xi)
+evaluate(Broadcasting(∇)(b), xi) # gradients
+evaluate(Broadcasting(∇∇)(b), xi) # hessians, not all basis support hessians
+evaluate(b, [xi, xi]) # evaluation on arrays of points
+
+# Basis of ℚ²₂ of Float64 value type based on Legendre polynomials, our 1D
+# Legendre polynomials are normalized for L2 scalar product and moved from
+# [-1,1] to [0,1] using the change of variable x -> 2x-1
+# { 1, √3(2x-1), √5(6x²-6x+2),
+# √3(2y-1), √3(2x-1)√3(2y-1), √5(6x²-6x+2)√3(2y-1),
+# √5(6y²-6y+2), √3(2x-1)√5(6x²-6x+2), √5(6x²-6x+2)√5(6y²-6y+2) }
+D = 2; n = 2
+b = LegendreBasis(Val(D), Float64, n)
+
+# Basis of (ℙ³₁)³ of VectorValue{3,Float64} value type, based on monomials:
+# {(1,0,0), (0,1,0), (0,0,1)
+# (x,0,0), (0,x,0), (0,0,x)
+# (y,0,0), (0,y,0), (0,0,y)
+# (z,0,0), (0,z,0), (0,0,z)}
+D = 3; n = 1
+b = MonomialBasis(Val(D), VectorValue{D,Float64}, n, Polynomials._p_filter)
+evaluate(b, Point(.1, .2, .3)
+
+# a basis for Nedelec on tetrahedra with curl in ℙ₂
+b = PGradBasis(Monomial, Val(3), Float64, 2) # basis of order 3
+
+# a basis for Nedelec on hexahedra with divergence in ℚ₂
+b = QGradBasis(Bernstein, Val(3), Float64, 2) # basis of order 3
+
+# a basis for Raviart-Thomas on tetrahedra with divergence in ℙ₂
+b = PCurlGradBasis(Chebyshev{:T}, Val(3), Float64, 2) # basis of order 3
+
+# a basis for Raviart-Thomas on rectangles with divergence in ℚ₃
+b = QCurlGradBasis(Bernstein, Val(2), Float64, 3) # basis of order 4
+```
+
+$(public_names_in_md(@__MODULE__))
"""
module Polynomials
@@ -21,40 +92,64 @@ import Gridap.Fields: evaluate!
import Gridap.Fields: return_cache
import Gridap.Arrays: return_type
+export Polynomial
+export isHierarchical
+export Monomial
+export Legendre
+export Chebyshev
+export ModalC0
+export Bernstein
+
+export PolynomialBasis
+export get_order
+
+export UniformPolyBasis
+export get_exponents
+export get_orders
export MonomialBasis
+export LegendreBasis
+export ChebyshevBasis
+export BernsteinBasis
+
+export CompWiseTensorPolyBasis
+export QGradBasis
+export QCurlGradBasis
+
+export NedelecPolyBasisOnSimplex
+export PGradBasis
+
+export RaviartThomasPolyBasis
+export PCurlGradBasis
+
+export ModalC0Basis
+
+# deprecated
+export num_terms
export QGradMonomialBasis
export QCurlGradMonomialBasis
export PCurlGradMonomialBasis
-export ModalC0Basis
-export JacobiPolynomialBasis
-export QGradJacobiPolynomialBasis
-export QCurlGradJacobiPolynomialBasis
-export PCurlGradJacobiPolynomialBasis
-export ChebyshevPolynomialBasis
-export QGradChebyshevPolynomialBasis
-export QCurlGradChebyshevPolynomialBasis
-export get_exponents
-export get_order
-export get_orders
-export num_terms
-include("MonomialBases.jl")
+include("PolynomialInterfaces.jl")
-include("QGradMonomialBases.jl")
+include("UniformPolyBases.jl")
-include("QCurlGradMonomialBases.jl")
+include("CompWiseTensorPolyBases.jl")
-include("PCurlGradMonomialBases.jl")
+include("NedelecPolyBases.jl")
-include("ModalC0Bases.jl")
+include("RaviartThomasPolyBases.jl")
+
+include("MonomialBases.jl")
+
+include("LegendreBases.jl")
-include("JacobiPolynomialBases.jl")
+include("ChebyshevBases.jl")
-include("QGradJacobiPolynomialBases.jl")
+include("BernsteinBases.jl")
-include("PCurlGradJacobiPolynomialBases.jl")
+include("ModalC0Bases.jl")
-include("ChebyshevPolynomialBases.jl")
+include("Deprecated.jl")
end # module
diff --git a/src/Polynomials/QCurlGradMonomialBases.jl b/src/Polynomials/QCurlGradMonomialBases.jl
deleted file mode 100644
index e4b519dda..000000000
--- a/src/Polynomials/QCurlGradMonomialBases.jl
+++ /dev/null
@@ -1,74 +0,0 @@
-
-"""
- struct QCurlGradMonomialBasis{...} <: AbstractArray{Monomial}
-
-This type implements a multivariate vector-valued polynomial basis
-spanning the space needed for Raviart-Thomas reference elements on n-cubes.
-The type parameters and fields of this `struct` are not public.
-This type fully implements the [`Field`](@ref) interface, with up to first order
-derivatives.
-"""
-struct QCurlGradMonomialBasis{D,T} <: AbstractVector{Monomial}
- qgrad::QGradMonomialBasis{D,T}
- function QCurlGradMonomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T}
- qgrad = QGradMonomialBasis(T,order,terms,perms)
- new{D,T}(qgrad)
- end
-end
-
-Base.size(a::QCurlGradMonomialBasis) = (length(a.qgrad),)
-# @santiagobadia : Not sure we want to create the monomial machinery
-Base.getindex(a::QCurlGradMonomialBasis,i::Integer) = Monomial()
-Base.IndexStyle(::QCurlGradMonomialBasis) = IndexLinear()
-
-"""
- QCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T}
-
-Returns a `QCurlGradMonomialBasis` object. `D` is the dimension
-of the coordinate space and `T` is the type of the components in the vector-value.
-The `order` argument has the following meaning: the divergence of the functions in this basis
-is in the Q space of degree `order`.
-"""
-function QCurlGradMonomialBasis{D}(::Type{T},order::Int) where {D,T}
- @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
- _order = order+1
- _t = tfill(_order,Val{D-1}())
- t = (_order+1,_t...)
- terms = CartesianIndices(t)
- perms = _prepare_perms(D)
- QCurlGradMonomialBasis(T,order,terms,perms)
-end
-
-return_type(::QCurlGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T}
-
-function return_cache(f::QCurlGradMonomialBasis,x::AbstractVector{<:Point})
- return_cache(f.qgrad,x)
-end
-
-function evaluate!(cache,f::QCurlGradMonomialBasis,x::AbstractVector{<:Point})
- evaluate!(cache,f.qgrad,x)
-end
-
-function return_cache(
- fg::FieldGradientArray{N,<:QCurlGradMonomialBasis},
- x::AbstractVector{<:Point}) where N
-
- f = fg.fa
- return_cache(FieldGradientArray{N}(f.qgrad),x)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{N,<:QCurlGradMonomialBasis},
- x::AbstractVector{<:Point}) where N
-
- f = fg.fa
- evaluate!(cache,FieldGradientArray{N}(f.qgrad),x)
-end
-
-"""
- num_terms(f::QCurlGradMonomialBasis{D,T}) where {D,T}
-"""
-num_terms(f::QCurlGradMonomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D
-
-get_order(f::QCurlGradMonomialBasis{D,T}) where {D,T} = get_order(f.qgrad)
diff --git a/src/Polynomials/QGradJacobiPolynomialBases.jl b/src/Polynomials/QGradJacobiPolynomialBases.jl
deleted file mode 100644
index d160363ef..000000000
--- a/src/Polynomials/QGradJacobiPolynomialBases.jl
+++ /dev/null
@@ -1,305 +0,0 @@
-
-"""
- struct QGradJacobiPolynomialBasis{...} <: AbstractVector{Monomial}
-
-This type implements a multivariate vector-valued polynomial basis
-spanning the space needed for Nedelec reference elements on n-cubes.
-The type parameters and fields of this `struct` are not public.
-This type fully implements the [`Field`](@ref) interface, with up to first order
-derivatives.
-"""
-struct QGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial}
- order::Int
- terms::CartesianIndices{D}
- perms::Matrix{Int}
- function QGradJacobiPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T}
- new{D,T}(order,terms,perms)
- end
-end
-
-Base.size(a::QGradJacobiPolynomialBasis) = (_ndofs_qgrad_jp(a),)
-Base.getindex(a::QGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial()
-Base.IndexStyle(::QGradJacobiPolynomialBasis) = IndexLinear()
-
-"""
- QGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
-
-Returns a `QGradJacobiPolynomialBasis` object. `D` is the dimension
-of the coordinate space and `T` is the type of the components in the vector-value.
-The `order` argument has the following meaning: the curl of the functions in this basis
-is in the Q space of degree `order`.
-"""
-function QGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
- @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
- _order = order + 1
- _t = tfill(_order+1,Val{D-1}())
- t = (_order,_t...)
- terms = CartesianIndices(t)
- perms = _prepare_perms(D)
- QGradJacobiPolynomialBasis(T,order,terms,perms)
-end
-
-"""
- num_terms(f::QGradJacobiPolynomialBasis{D,T}) where {D,T}
-"""
-num_terms(f::QGradJacobiPolynomialBasis{D,T}) where {D,T} = length(f.terms)*D
-
-get_order(f::QGradJacobiPolynomialBasis) = f.order
-
-return_type(::QGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T}
-
-function return_cache(f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = _ndofs_qgrad_jp(f)
- n = 1 + f.order+1
- V = VectorValue{D,T}
- r = CachedArray(zeros(V,(np,ndof)))
- v = CachedArray(zeros(V,(ndof,)))
- c = CachedArray(zeros(T,(D,n)))
- (r, v, c)
-end
-
-function evaluate!(cache,f::QGradJacobiPolynomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- r, v, c = cache
- np = length(x)
- ndof = _ndofs_qgrad_jp(f)
- n = 1 + f.order+1
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _evaluate_nd_qgrad_jp!(v,xi,f.order+1,f.terms,f.perms,c)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{1,QGradJacobiPolynomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = _ndofs_qgrad_jp(f)
- n = 1 + f.order+1
- xi = testitem(x)
- V = VectorValue{D,T}
- G = gradient_type(V,xi)
- r = CachedArray(zeros(G,(np,ndof)))
- v = CachedArray(zeros(G,(ndof,)))
- c = CachedArray(zeros(T,(D,n)))
- g = CachedArray(zeros(T,(D,n)))
- (r, v, c, g)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{1,QGradJacobiPolynomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- r, v, c, g = cache
- np = length(x)
- ndof = _ndofs_qgrad_jp(f)
- n = 1 + f.order+1
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- V = VectorValue{D,T}
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd_qgrad_jp!(v,xi,f.order+1,f.terms,f.perms,c,g,V)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-# Helpers
-
-_ndofs_qgrad_jp(f::QGradJacobiPolynomialBasis{D}) where D = D*(length(f.terms))
-
-function _evaluate_nd_qgrad_jp!(
- v::AbstractVector{V},
- x,
- order,
- terms::CartesianIndices{D},
- perms::Matrix{Int},
- c::AbstractMatrix{T}) where {V,T,D}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_jp!(c,x,order,d)
- end
-
- o = one(T)
- k = 1
- m = zero(Mutable(V))
- js = eachindex(m)
- z = zero(T)
-
- for ci in terms
-
- for j in js
-
- @inbounds for i in js
- m[i] = z
- end
-
- s = o
- @inbounds for d in 1:dim
- s *= c[d,ci[perms[d,j]]]
- end
-
- m[j] = s
- v[k] = m
- k += 1
-
- end
-
- end
-
-end
-
-function _gradient_nd_qgrad_jp!(
- v::AbstractVector{G},
- x,
- order,
- terms::CartesianIndices{D},
- perms::Matrix{Int},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d_jp!(c,x,order,d)
- _gradient_1d_jp!(g,x,order,d)
- end
-
- z = zero(Mutable(V))
- m = zero(Mutable(G))
- js = eachindex(z)
- mjs = eachindex(m)
- o = one(T)
- zi = zero(T)
- k = 1
-
- for ci in terms
-
- for j in js
-
- s = z
- for i in js
- s[i] = o
- end
-
- for q in 1:dim
- for d in 1:dim
- if d != q
- @inbounds s[q] *= c[d,ci[perms[d,j]]]
- else
- @inbounds s[q] *= g[d,ci[perms[d,j]]]
- end
- end
- end
-
- @inbounds for i in mjs
- m[i] = zi
- end
-
- for i in js
- @inbounds m[i,j] = s[i]
- end
- @inbounds v[k] = m
- k += 1
-
- end
-
- end
-
-end
-
-############################################################################################
-
-"""
- struct QCurlGradJacobiPolynomialBasis{...} <: AbstractArray{Monomial}
-
-This type implements a multivariate vector-valued polynomial basis
-spanning the space needed for Raviart-Thomas reference elements on n-cubes.
-The type parameters and fields of this `struct` are not public.
-This type fully implements the [`Field`](@ref) interface, with up to first order
-derivatives.
-"""
-struct QCurlGradJacobiPolynomialBasis{D,T} <: AbstractVector{JacobiPolynomial}
- qgrad::QGradJacobiPolynomialBasis{D,T}
- function QCurlGradJacobiPolynomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T}
- qgrad = QGradJacobiPolynomialBasis(T,order,terms,perms)
- new{D,T}(qgrad)
- end
-end
-
-Base.size(a::QCurlGradJacobiPolynomialBasis) = (length(a.qgrad),)
-Base.getindex(a::QCurlGradJacobiPolynomialBasis,i::Integer) = JacobiPolynomial()
-Base.IndexStyle(::QCurlGradJacobiPolynomialBasis) = IndexLinear()
-
-"""
- QCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
-
-Returns a `QCurlGradJacobiPolynomialBasis` object. `D` is the dimension
-of the coordinate space and `T` is the type of the components in the vector-value.
-The `order` argument has the following meaning: the divergence of the functions in this basis
-is in the Q space of degree `order`.
-"""
-function QCurlGradJacobiPolynomialBasis{D}(::Type{T},order::Int) where {D,T}
- @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
- _order = order+1
- _t = tfill(_order,Val{D-1}())
- t = (_order+1,_t...)
- terms = CartesianIndices(t)
- perms = _prepare_perms(D)
- QCurlGradJacobiPolynomialBasis(T,order,terms,perms)
-end
-
-return_type(::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = VectorValue{D,T}
-
-function return_cache(f::QCurlGradJacobiPolynomialBasis,x::AbstractVector{<:Point})
- return_cache(f.qgrad,x)
-end
-
-function evaluate!(cache,f::QCurlGradJacobiPolynomialBasis,x::AbstractVector{<:Point})
- evaluate!(cache,f.qgrad,x)
-end
-
-function return_cache(
- fg::FieldGradientArray{N,<:QCurlGradJacobiPolynomialBasis},
- x::AbstractVector{<:Point}) where N
-
- f = fg.fa
- return_cache(FieldGradientArray{N}(f.qgrad),x)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{N,<:QCurlGradJacobiPolynomialBasis},
- x::AbstractVector{<:Point}) where N
-
- f = fg.fa
- evaluate!(cache,FieldGradientArray{N}(f.qgrad),x)
-end
-
-"""
- num_terms(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T}
-"""
-num_terms(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = length(f.qgrad.terms)*D
-
-get_order(f::QCurlGradJacobiPolynomialBasis{D,T}) where {D,T} = get_order(f.qgrad)
-
-############################################################################################
diff --git a/src/Polynomials/QGradMonomialBases.jl b/src/Polynomials/QGradMonomialBases.jl
deleted file mode 100644
index f31319429..000000000
--- a/src/Polynomials/QGradMonomialBases.jl
+++ /dev/null
@@ -1,487 +0,0 @@
-
-"""
- struct QGradMonomialBasis{...} <: AbstractVector{Monomial}
-
-This type implements a multivariate vector-valued polynomial basis
-spanning the space needed for Nedelec reference elements on n-cubes.
-The type parameters and fields of this `struct` are not public.
-This type fully implements the [`Field`](@ref) interface, with up to first order
-derivatives.
-"""
-struct QGradMonomialBasis{D,T} <: AbstractVector{Monomial}
- order::Int
- terms::CartesianIndices{D}
- perms::Matrix{Int}
- function QGradMonomialBasis(::Type{T},order::Int,terms::CartesianIndices{D},perms::Matrix{Int}) where {D,T}
- new{D,T}(order,terms,perms)
- end
-end
-
-Base.size(a::QGradMonomialBasis) = (_ndofs_qgrad(a),)
-# @santiagobadia : Not sure we want to create the monomial machinery
-Base.getindex(a::QGradMonomialBasis,i::Integer) = Monomial()
-Base.IndexStyle(::QGradMonomialBasis) = IndexLinear()
-
-"""
- QGradMonomialBasis{D}(::Type{T},order::Int) where {D,T}
-
-Returns a `QGradMonomialBasis` object. `D` is the dimension
-of the coordinate space and `T` is the type of the components in the vector-value.
-The `order` argument has the following meaning: the curl of the functions in this basis
-is in the Q space of degree `order`.
-"""
-function QGradMonomialBasis{D}(::Type{T},order::Int) where {D,T}
- @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
- _order = order + 1
- _t = tfill(_order+1,Val{D-1}())
- t = (_order,_t...)
- terms = CartesianIndices(t)
- perms = _prepare_perms(D)
- QGradMonomialBasis(T,order,terms,perms)
-end
-
-"""
- num_terms(f::QGradMonomialBasis{D,T}) where {D,T}
-"""
-num_terms(f::QGradMonomialBasis{D,T}) where {D,T} = length(f.terms)*D
-
-get_order(f::QGradMonomialBasis) = f.order
-
-return_type(::QGradMonomialBasis{D,T}) where {D,T} = VectorValue{D,T}
-
-function return_cache(f::QGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = _ndofs_qgrad(f)
- n = 1 + f.order+1
- V = VectorValue{D,T}
- r = CachedArray(zeros(V,(np,ndof)))
- v = CachedArray(zeros(V,(ndof,)))
- c = CachedArray(zeros(T,(D,n)))
- (r, v, c)
-end
-
-function evaluate!(cache,f::QGradMonomialBasis{D,T},x::AbstractVector{<:Point}) where {D,T}
- r, v, c = cache
- np = length(x)
- ndof = _ndofs_qgrad(f)
- n = 1 + f.order+1
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- for i in 1:np
- @inbounds xi = x[i]
- _evaluate_nd_qgrad!(v,xi,f.order+1,f.terms,f.perms,c)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-function return_cache(
- fg::FieldGradientArray{1,QGradMonomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- @check D == length(eltype(x)) "Incorrect number of point components"
- np = length(x)
- ndof = _ndofs_qgrad(f)
- n = 1 + f.order+1
- xi = testitem(x)
- V = VectorValue{D,T}
- G = gradient_type(V,xi)
- r = CachedArray(zeros(G,(np,ndof)))
- v = CachedArray(zeros(G,(ndof,)))
- c = CachedArray(zeros(T,(D,n)))
- g = CachedArray(zeros(T,(D,n)))
- (r, v, c, g)
-end
-
-function evaluate!(
- cache,
- fg::FieldGradientArray{1,QGradMonomialBasis{D,T}},
- x::AbstractVector{<:Point}) where {D,T}
-
- f = fg.fa
- r, v, c, g = cache
- np = length(x)
- ndof = _ndofs_qgrad(f)
- n = 1 + f.order+1
- setsize!(r,(np,ndof))
- setsize!(v,(ndof,))
- setsize!(c,(D,n))
- setsize!(g,(D,n))
- V = VectorValue{D,T}
- for i in 1:np
- @inbounds xi = x[i]
- _gradient_nd_qgrad!(v,xi,f.order+1,f.terms,f.perms,c,g,V)
- for j in 1:ndof
- @inbounds r[i,j] = v[j]
- end
- end
- r.array
-end
-
-# Helpers
-
-_ndofs_qgrad(f::QGradMonomialBasis{D}) where D = D*(length(f.terms))
-
-function _prepare_perms(D)
- perms = zeros(Int,D,D)
- for j in 1:D
- for d in j:D
- perms[d,j] = d-j+1
- end
- for d in 1:(j-1)
- perms[d,j] = d+(D-j)+1
- end
- end
- perms
-end
-
-function _evaluate_nd_qgrad!(
- v::AbstractVector{V},
- x,
- order,
- terms::CartesianIndices{D},
- perms::Matrix{Int},
- c::AbstractMatrix{T}) where {V,T,D}
-
- dim = D
- for d in 1:dim
- _evaluate_1d!(c,x,order,d)
- end
-
- o = one(T)
- k = 1
- m = zero(Mutable(V))
- js = eachindex(m)
- z = zero(T)
-
- for ci in terms
-
- for j in js
-
- @inbounds for i in js
- m[i] = z
- end
-
- s = o
- @inbounds for d in 1:dim
- s *= c[d,ci[perms[d,j]]]
- end
-
- m[j] = s
- v[k] = m
- k += 1
-
- end
-
- end
-
-end
-
-function _gradient_nd_qgrad!(
- v::AbstractVector{G},
- x,
- order,
- terms::CartesianIndices{D},
- perms::Matrix{Int},
- c::AbstractMatrix{T},
- g::AbstractMatrix{T},
- ::Type{V}) where {G,T,D,V}
-
- dim = D
- for d in 1:dim
- _evaluate_1d!(c,x,order,d)
- _gradient_1d!(g,x,order,d)
- end
-
- z = zero(Mutable(V))
- m = zero(Mutable(G))
- js = eachindex(z)
- mjs = eachindex(m)
- o = one(T)
- zi = zero(T)
- k = 1
-
- for ci in terms
-
- for j in js
-
- s = z
- for i in js
- s[i] = o
- end
-
- for q in 1:dim
- for d in 1:dim
- if d != q
- @inbounds s[q] *= c[d,ci[perms[d,j]]]
- else
- @inbounds s[q] *= g[d,ci[perms[d,j]]]
- end
- end
- end
-
- @inbounds for i in mjs
- m[i] = zi
- end
-
- for i in js
- @inbounds m[i,j] = s[i]
- end
- @inbounds v[k] = m
- k += 1
-
- end
-
- end
-
-end
-
-struct NedelecPrebasisOnSimplex{D} <: AbstractVector{Monomial}
- order::Int
- function NedelecPrebasisOnSimplex{D}(order::Integer) where D
- new{D}(Int(order))
- end
-end
-
-function Base.size(a::NedelecPrebasisOnSimplex{d}) where d
- k = a.order+1
- n = div(k*prod(i->(k+i),2:d),factorial(d-1))
- (n,)
-end
-
-Base.getindex(a::NedelecPrebasisOnSimplex,i::Integer) = Monomial()
-Base.IndexStyle(::Type{<:NedelecPrebasisOnSimplex}) = IndexLinear()
-
-num_terms(a::NedelecPrebasisOnSimplex) = length(a)
-get_order(f::NedelecPrebasisOnSimplex) = f.order
-
-return_type(::NedelecPrebasisOnSimplex{D}) where {D} = VectorValue{D,Float64}
-
-function return_cache(
- f::NedelecPrebasisOnSimplex{d},x::AbstractVector{<:Point}) where d
- np = length(x)
- ndofs = num_terms(f)
- V = eltype(x)
- a = zeros(V,(np,ndofs))
- k = f.order+1
- P = MonomialBasis{d}(VectorValue{d,Float64},k-1,(e,order)->sum(e)<=order)
- cP = return_cache(P,x)
- CachedArray(a), cP, P
-end
-
-function evaluate!(
- cache,f::NedelecPrebasisOnSimplex{3},x::AbstractVector{<:Point})
- ca,cP,P = cache
- k = f.order+1
- np = length(x)
- ndofs = num_terms(f)
- ndofsP = length(P)
- setsize!(ca,(np,ndofs))
- Px = evaluate!(cP,P,x)
- a = ca.array
- V = eltype(x)
- T = eltype(V)
- z = zero(T)
- u = one(T)
- for (ip,p) in enumerate(x)
- for j in 1:ndofsP
- a[ip,j] = Px[ip,j]
- end
- i = ndofsP
- x1,x2,x3 = x[ip]
- zp = zero(x1)
- for β in 1:k
- for α in 1:(k+1-β)
- i += 1
- a[ip,i] = VectorValue(
- -x1^(α-1)*x2^(k-α-β+2)*x3^(β-1),
- x1^α*x2^(k-α-β+1)*x3^(β-1),
- zp)
- i += 1
- a[ip,i] = VectorValue(
- -x1^(k-α-β+1)*x2^(β-1)*x3^α,
- zp,
- x1^(k-α-β+2)*x2^(β-1)*x3^(α-1))
- end
- end
- for γ in 1:k
- i += 1
- a[ip,i] = VectorValue(
- zp,
- -x2^(γ-1)*x3^(k-γ+1),
- x2^γ*x3^(k-γ))
- end
- end
- a
-end
-
-function evaluate!(
- cache,f::NedelecPrebasisOnSimplex{2},x::AbstractVector{<:Point})
- ca,cP,P = cache
- k = f.order+1
- np = length(x)
- ndofs = num_terms(f)
- ndofsP = length(P)
- setsize!(ca,(np,ndofs))
- a = ca.array
- V = eltype(x)
- T = eltype(V)
- z = zero(T)
- u = one(T)
- Px = evaluate!(cP,P,x)
- for (ip,p) in enumerate(x)
- for j in 1:ndofsP
- a[ip,j] = Px[ip,j]
- end
- i = ndofsP
- x1,x2 = x[ip]
- zp = zero(x1)
- for α in 1:k
- i += 1
- a[ip,i] = VectorValue(-x1^(α-1)*x2^(k-α+1),x1^α*x2^(k-α))
- end
- #a[ip,1] = VectorValue((u,z))
- #a[ip,2] = VectorValue((z,u))
- #a[ip,3] = VectorValue((-p[2],p[1]))
- end
- a
-end
-
-function return_cache(
- g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{D}},
- x::AbstractVector{<:Point}) where D
- f = g.fa
- np = length(x)
- ndofs = num_terms(f)
- xi = testitem(x)
- V = eltype(x)
- G = gradient_type(V,xi)
- a = zeros(G,(np,ndofs))
- k = f.order+1
- mb = MonomialBasis{D}(VectorValue{D,Float64},k-1,(e,order)->sum(e)<=order)
- P = Broadcasting(∇)(mb)
- cP = return_cache(P,x)
- CachedArray(a), cP, P
-end
-
-function evaluate!(
- cache,
- g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{3}},
- x::AbstractVector{<:Point})
- ca,cP,P = cache
- f = g.fa
- k = f.order+1
- np = length(x)
- ndofs = num_terms(f)
- setsize!(ca,(np,ndofs))
- a = ca.array
- fill!(a,zero(eltype(a)))
- ndofsP = length(P)
- Px = evaluate!(cP,P,x)
- V = eltype(x)
- T = eltype(V)
- z = zero(T)
- u = one(T)
- for (ip,p) in enumerate(x)
- for j in 1:ndofsP
- a[ip,j] = Px[ip,j]
- end
- i = ndofsP
- x1,x2,x3 = x[ip]
- zp = zero(x1)
- for β in 1:k
- for α in 1:(k+1-β)
- i += 1
- a[ip,i] = TensorValue(
- #-x1^(α-1)*x2^(k-α-β+2)*x3^(β-1),
- -(α-1)*_exp(x1,α-2)*x2^(k-α-β+2)*x3^(β-1),
- -x1^(α-1)*(k-α-β+2)*_exp(x2,k-α-β+1)*x3^(β-1),
- -x1^(α-1)*x2^(k-α-β+2)*(β-1)*_exp(x3,β-2),
- #x1^α*x2^(k-α-β+1)*x3^(β-1),
- α*_exp(x1,α-1)*x2^(k-α-β+1)*x3^(β-1),
- x1^α*(k-α-β+1)*_exp(x2,k-α-β)*x3^(β-1),
- x1^α*x2^(k-α-β+1)*(β-1)*_exp(x3,β-2),
- #zp,
- zp,zp,zp)
- i += 1
- a[ip,i] = TensorValue(
- #-x1^(k-α-β+1)*x2^(β-1)*x3^α,
- -(k-α-β+1)*_exp(x1,k-α-β)*x2^(β-1)*x3^α,
- -x1^(k-α-β+1)*(β-1)*_exp(x2,β-2)*x3^α,
- -x1^(k-α-β+1)*x2^(β-1)*α*_exp(x3,α-1),
- # zp
- zp,zp,zp,
- #x1^(k-α-β+2)*x2^(β-1)*x3^(α-1),
- (k-α-β+2)*_exp(x1,k-α-β+1)*x2^(β-1)*x3^(α-1),
- x1^(k-α-β+2)*(β-1)*_exp(x2,β-2)*x3^(α-1),
- x1^(k-α-β+2)*x2^(β-1)*(α-1)*_exp(x3,α-2))
- end
- end
- for γ in 1:k
- i += 1
- a[ip,i] = TensorValue(
- #zp
- zp,zp,zp,
- #-x2^(γ-1)*x3^(k-γ+1),
- -0*x2^(γ-1)*x3^(k-γ+1),
- -(γ-1)*_exp(x2,γ-2)*x3^(k-γ+1),
- -x2^(γ-1)*(k-γ+1)*_exp(x3,k-γ),
- #x2^γ*x3^(k-γ),
- 0*x2^γ*x3^(k-γ),
- γ*_exp(x2,γ-1)*x3^(k-γ),
- x2^γ*(k-γ)*_exp(x3,k-γ-1))
- end
- #a[ip,4] = TensorValue((z,-u,z, u,z,z, z,z,z))
- #a[ip,5] = TensorValue((z,z,-u, z,z,z, u,z,z))
- #a[ip,6] = TensorValue((z,z,z, z,z,-u, z,u,z))
- end
- a
-end
-
-_exp(a,y) = y>0 ? a^y : one(a)
-
-function evaluate!(
- cache,
- g::FieldGradientArray{1,<:NedelecPrebasisOnSimplex{2}},
- x::AbstractVector{<:Point})
- f = g.fa
- ca,cP,P = cache
- k = f.order+1
- np = length(x)
- ndofs = num_terms(f)
- setsize!(ca,(np,ndofs))
- a = ca.array
- fill!(a,zero(eltype(a)))
- V = eltype(x)
- T = eltype(V)
- z = zero(T)
- u = one(T)
- ndofsP = length(P)
- Px = evaluate!(cP,P,x)
- for (ip,p) in enumerate(x)
- for j in 1:ndofsP
- a[ip,j] = Px[ip,j]
- end
- i = ndofsP
- x1,x2 = x[ip]
- zp = zero(x1)
- for α in 1:k
- i += 1
- a[ip,i] = TensorValue(
- #-x1^(α-1)*x2^(k-α+1),
- -(α-1)*_exp(x1,α-2)*x2^(k-α+1),
- -x1^(α-1)*(k-α+1)*_exp(x2,k-α),
- #x1^α*x2^(k-α),
- α*_exp(x1,α-1)*x2^(k-α),
- x1^α*(k-α)*_exp(x2,k-α-1))
- end
- #a[ip,3] = TensorValue((z,-u, u,z))
- end
- a
-end
-
diff --git a/src/Polynomials/RaviartThomasPolyBases.jl b/src/Polynomials/RaviartThomasPolyBases.jl
new file mode 100644
index 000000000..bc810f6ca
--- /dev/null
+++ b/src/Polynomials/RaviartThomasPolyBases.jl
@@ -0,0 +1,229 @@
+"""
+ RaviartThomasPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT}
+
+Basis of the vector valued (`V<:VectorValue{D}`) space
+
+ℝ𝕋ᴰₙ = (𝕊ₙ)ᴰ ⊕ x (𝕊ₙ\\𝕊₍ₙ₋₁₎)
+
+where 𝕊ₙ is a `D`-multivariate scalar polynomial space of maximum degree n = `K`-1.
+
+This ℝ𝕋ᴰₙ is the polynomial space for Raviart-Thomas elements with divergence in 𝕊ₙ.
+Its maximum degree is n+1 = `K`. `get_order` on it returns `K`.
+
+The space 𝕊ₙ, typically ℙᴰₙ or ℚᴰₙ, does not need to have a tensor product
+structure of 1D scalar spaces. Thus, the ℝ𝕋ᴰₙ component's scalar spaces are not
+tensor products either.
+
+𝕊ₙ is defined like a scalar valued [`UniformPolyBasis`](@ref) via the `_filter`
+argument of the constructor, by default `_p_filter` for ℙᴰₙ.
+As a consequence, `PT` must be hierarchical, see [`isHierarchical`](@ref).
+"""
+struct RaviartThomasPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT}
+ pterms::Vector{CartesianIndex{D}}
+ sterms::Vector{CartesianIndex{D}}
+
+ """
+ RaviartThomasPolyBasis{D}(::Type{PT}, ::Type{T}, order::Int, _filter::Function=_p_filter)
+
+ Where `_filter` defines 𝕊ₙ and `order` = n = K-1 (cf. struct docstring).
+ """
+ function RaviartThomasPolyBasis{D}(
+ ::Type{PT}, ::Type{T}, order::Int,
+ _filter::Function=_p_filter
+ ) where {PT<:Polynomial,D,T}
+
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+ @check D > 1
+ @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type"
+ @check isHierarchical(PT) "The polynomial basis must be hierarchical for this space."
+
+ V = VectorValue{D,T}
+ indexbase = 1
+
+ # terms defining 𝕊ₙ
+ P_k = MonomialBasis(Val(D), T, order, _filter)
+ pterms = P_k.terms
+ msg = "Some term defining `𝕊ₙ` contain a higher index than the maximum,
+ `order`+1, please fix the `_filter` argument"
+ @check all( pterm -> (maximum(Tuple(pterm) .- indexbase, init=0) <= order), pterms) msg
+
+ # terms defining 𝕊ₙ\𝕊ₙ₋₁
+ _minus_one_order_filter = term -> _filter(Tuple(term) .- indexbase, order-1)
+ sterms = filter(!_minus_one_order_filter, pterms)
+
+ new{D,V,order+1,PT}(pterms,sterms)
+ end
+end
+
+Base.size(a::RaviartThomasPolyBasis{D}) where {D} = (D*length(a.pterms) + length(a.sterms), )
+
+
+#################################
+# nD evaluations implementation #
+#################################
+
+function _evaluate_nd!(
+ b::RaviartThomasPolyBasis{D,V,K,PT}, x,
+ r::AbstractMatrix{V}, i,
+ c::AbstractMatrix{T}) where {D,V,K,PT,T}
+
+ pterms = b.pterms
+ sterms = b.sterms
+
+ for d in 1:D
+ Kv = Val(K)
+ _evaluate_1d!(PT,Kv,c,x,d)
+ end
+
+ m = zero(Mutable(V))
+ k = 1
+
+ @inbounds begin
+ for l in 1:D
+ for ci in pterms
+
+ s = one(T)
+ for d in 1:D
+ s *= c[d,ci[d]]
+ end
+
+ k = _comp_wize_set_value!(r,i,s,k,l)
+ end
+ end
+
+ for ci in sterms
+ for i in 1:D
+ m[i] = zero(T)
+ end
+
+ for l in 1:D
+
+ s = x[l]
+ for d in 1:D
+ s *= c[d,ci[d]]
+ end
+
+ m[l] = s
+ end
+
+ r[i,k] = m
+ k += 1
+ end
+
+ end
+end
+
+function _gradient_nd!(
+ b::RaviartThomasPolyBasis{D,V,K,PT}, x,
+ r::AbstractMatrix{G}, i,
+ c::AbstractMatrix{T},
+ g::AbstractMatrix{T},
+ s::MVector{D,T}) where {D,V,K,PT,G,T}
+
+ pterms = b.pterms
+ sterms = b.sterms
+
+ for d in 1:D
+ Kv = Val(K)
+ _derivatives_1d!(PT,Kv,(c,g),x,d)
+ end
+
+ m = zero(Mutable(G))
+ k = 1
+
+ @inbounds begin
+ for l in 1:D
+ for ci in pterms
+
+ for i in eachindex(s)
+ s[i] = one(T)
+ end
+
+ for q in 1:D
+ for d in 1:D
+ if d != q
+ s[q] *= c[d,ci[d]]
+ else
+ s[q] *= g[d,ci[d]]
+ end
+ end
+ end
+
+ k = _comp_wize_set_derivative!(r,i,s,k,Val(l),V)
+ end
+ end
+
+ for ci in sterms
+
+ for i in eachindex(m)
+ m[i] = zero(T)
+ end
+
+ for l in 1:D
+
+ for i in eachindex(s)
+ s[i] = x[l]
+ end
+
+ aux = one(T)
+ for q in 1:D
+ aux *= c[q,ci[q]]
+ for d in 1:D
+ if d != q
+ s[q] *= c[d,ci[d]]
+ else
+ s[q] *= g[d,ci[d]]
+ end
+ end
+ end
+ s[l] += aux
+
+ for i in 1:D
+ m[i,l] = s[i]
+ end
+ end
+ r[i,k] = m
+ k += 1
+ end
+
+ end
+end
+
+"""
+ PCurlGradBasis(::Type{PT}, ::Val{D}, ::Type{T}, order::Int) :: PolynomialBasis
+
+Return a basis of
+
+ℝ𝕋ᴰₙ(△) = (ℙᴰₙ)ᴰ ⊕ x (ℙᴰₙ \\ ℙᴰₙ₋₁)
+
+with n=`order`, the polynomial space for Raviart-Thomas elements on
+`D`-dimensional simplices with scalar type `T`.
+
+The `order`=n argument of this function has the following meaning: the divergence
+of the functions in this basis is in ℙᴰₙ.
+
+`PT<:Polynomial` is the choice of the family of the scalar 1D basis polynomials,
+it must be hierarchical, see [`isHierarchical`](@ref).
+
+# Example:
+
+```jldoctest
+# a basis for Raviart-Thomas on tetrahedra with divergence in ℙ₂
+b = PCurlGradBasis(Monomial, Val(3), Float64, 2)
+```
+
+For more details, see [`RaviartThomasPolyBasis`](@ref), as `PCurlGradBasis` returns
+an instance of\\
+`RaviartThomasPolyBasis{D, VectorValue{D,T}, order+1, PT}` for `D`>1, or\\
+`UniformPolyBasis{1, VectorValue{1,T}, order+1, PT}` for `D`=1.
+"""
+function PCurlGradBasis(::Type{PT},::Val{D},::Type{T},order::Int) where {PT,D,T}
+ RaviartThomasPolyBasis{D}(PT, T, order)
+end
+
+function PCurlGradBasis(::Type{PT},::Val{1},::Type{T},order::Int) where {PT,T}
+ @check T<:Real "T needs to be <:Real since represents the type of the components of the vector value"
+
+ V = VectorValue{1,T}
+ UniformPolyBasis(PT, Val(1), V, order+1)
+end
diff --git a/src/Polynomials/UniformPolyBases.jl b/src/Polynomials/UniformPolyBases.jl
new file mode 100644
index 000000000..8068a9c48
--- /dev/null
+++ b/src/Polynomials/UniformPolyBases.jl
@@ -0,0 +1,376 @@
+#################################
+# Tensorial nD polynomial bases #
+#################################
+
+"""
+ struct UniformPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT}
+
+Type representing a uniform basis of (an)isotropic `D`-multivariate `V`-valued
+polynomial space
+
+`V`(𝕊, 𝕊, ..., 𝕊)
+
+where 𝕊 is a scalar multivariate polynomial space. So each (independant)
+component of `V` holds the same space (hence the name 'uniform').
+
+The scalar polynomial basis spanning 𝕊 is defined as
+
+ { x ⟶ bα`ᴷ`(x) = bα₁`ᴷ`(x₁) × bα₂`ᴷ`(x₂) × ... × bα`Dᴷ`(x`D`) | α ∈ `terms` }
+
+where bαᵢ`ᴷ`(xᵢ) is the αᵢth 1D basis polynomial of the basis `PT` of order `K`
+evaluated at xᵢ (iᵗʰ comp. of x), and where α = (α₁, α₂, ..., α`D`) is a
+multi-index in `terms`, a subset of ⟦0,`K`⟧`ᴰ`. `terms` is a field that can be
+passed in a constructor.
+
+The fields of this `struct` are not public.
+This type fully implements the [`Field`](@ref) interface, with up to second
+order derivatives.
+"""
+struct UniformPolyBasis{D,V,K,PT} <: PolynomialBasis{D,V,K,PT}
+ orders::NTuple{D,Int}
+ terms::Vector{CartesianIndex{D}}
+
+ function UniformPolyBasis{D}(
+ ::Type{PT},
+ ::Type{V},
+ orders::NTuple{D,Int},
+ terms::Vector{CartesianIndex{D}}) where {D,V,PT<:Polynomial}
+
+ @check isconcretetype(PT) "PT needs to be a concrete <:Polynomial type"
+
+ K = maximum(orders; init=0)
+ msg = "Some term contain a higher index than the maximum degree + 1."
+ @check all( term -> (maximum(Tuple(term), init=0) <= K+1), terms) msg
+ new{D,V,K,PT}(orders,terms)
+ end
+end
+
+@inline Base.size(a::UniformPolyBasis{D,V}) where {D,V} = (length(a.terms)*num_indep_components(V),)
+
+function UniformPolyBasis(
+ ::Type{PT},
+ ::Val{D},
+ ::Type{V},
+ orders::NTuple{D,Int},
+ terms::Vector{CartesianIndex{D}}) where {PT<:Polynomial,D,V}
+
+ UniformPolyBasis{D}(PT,V,orders,terms)
+end
+
+"""
+ UniformPolyBasis(::Type{PT}, ::Val{D}, ::Type{V}, orders::Tuple [, filter=_q_filter])
+
+This constructor allows to pass a tuple `orders` containing the maximal
+polynomial order to be used in each of the `D` spatial dimensions in order to
+construct a tensorial anisotropic `D`-multivariate space 𝕊.
+
+If a filter is provided, it is applied on the cartesian product terms
+CartesianIndices(`orders`), with maximum(`orders`) as order argument.
+"""
+function UniformPolyBasis(
+ ::Type{PT}, ::Val{D}, ::Type{V}, orders::NTuple{D,Int}, filter::Function=_q_filter
+ ) where {PT,D,V}
+
+ terms = _define_terms(filter, orders)
+ UniformPolyBasis{D}(PT,V,orders,terms)
+end
+
+"""
+ UniformPolyBasis(::Type{PT}, ::Type{V}, ::Val{D}, order::Int [, filter=_q_filter])
+
+Return a `UniformPolyBasis{D,V,order,PT}` where 𝕊 is defined by the terms
+filtered by
+
+ term -> filter(term, order).
+
+See the [Filter functions](@ref) section of the documentation for more details.
+"""
+function UniformPolyBasis(
+ ::Type{PT}, VD::Val{D}, ::Type{V}, order::Int, filter::Function=_q_filter) where {PT,D,V}
+
+ orders = tfill(order,VD)
+ UniformPolyBasis(PT,Val(D),V,orders,filter)
+end
+
+# API
+
+"""
+ get_exponents(b::UniformPolyBasis)
+
+Get a vector of tuples with the exponents of all the terms in the basis of 𝕊,
+the components scalar space of `b`.
+
+# Example
+
+```jldoctest
+using Gridap.Polynomials
+
+b = MonomialBasis(Val(2),Float64,2)
+
+exponents = get_exponents(b)
+
+println(exponents)
+
+# output
+Tuple{Int,Int}[(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)]
+```
+"""
+function get_exponents(b::UniformPolyBasis)
+ indexbase = 1
+ [Tuple(t) .- indexbase for t in b.terms]
+end
+
+"""
+ get_orders(b::UniformPolyBasis)
+
+Return the D-tuple of polynomial orders in each spatial dimension
+"""
+function get_orders(b::UniformPolyBasis)
+ b.orders
+end
+
+#################################
+# nD evaluations implementation #
+#################################
+
+function _evaluate_nd!(
+ b::UniformPolyBasis{D,V,K,PT}, x,
+ r::AbstractMatrix{V}, i,
+ c::AbstractMatrix{T}) where {D,V,K,PT,T}
+
+ terms = b.terms
+ orders = b.orders
+
+ for d in 1:D
+ Kd = Val(orders[d])
+ _evaluate_1d!(PT,Kd,c,x,d)
+ end
+
+ k = 1
+ for ci in terms
+
+ s = one(T)
+ for d in 1:D
+ @inbounds s *= c[d,ci[d]]
+ end
+
+ k = _uniform_set_value!(r,i,s,k)
+ end
+end
+
+"""
+ _uniform_set_value!(r::AbstractMatrix{<:Real},i,s,k)
+
+r[i,k] = s; return k+1
+"""
+function _uniform_set_value!(r::AbstractMatrix{<:Real},i,s,k)
+ @inbounds r[i,k] = s
+ k+1
+end
+
+"""
+ _uniform_set_value!(r::AbstractMatrix{V},i,s::T,k,l)
+
+```
+r[i,k] = V(s, 0, ..., 0)
+r[i,k+1] = V(0, s, 0, ..., 0)
+⋮
+r[i,k+N-1] = V(0, ..., 0, s)
+return k+N
+```
+
+where `N = num_indep_components(V)`.
+"""
+function _uniform_set_value!(r::AbstractMatrix{V},i,s::T,k) where {V,T}
+ ncomp = num_indep_components(V)
+ z = zero(T)
+ @inbounds for j in 1:ncomp
+ r[i,k] = ntuple(i -> ifelse(i == j, s, z),Val(ncomp))
+ k += 1
+ end
+ k
+end
+
+function _gradient_nd!(
+ b::UniformPolyBasis{D,V,K,PT}, x,
+ r::AbstractMatrix{G}, i,
+ c::AbstractMatrix{T},
+ g::AbstractMatrix{T},
+ s::MVector{D,T}) where {D,V,K,PT,G,T}
+
+ terms = b.terms
+ orders = b.orders
+
+ for d in 1:D
+ Kd = Val(orders[d])
+ _derivatives_1d!(PT,Kd,(c,g),x,d)
+ end
+
+ k = 1
+ for ci in terms
+
+ for i in eachindex(s)
+ @inbounds s[i] = one(T)
+ end
+
+ for q in 1:D
+ for d in 1:D
+ if d != q
+ @inbounds s[q] *= c[d,ci[d]]
+ else
+ @inbounds s[q] *= g[d,ci[d]]
+ end
+ end
+ end
+
+ k = _uniform_set_derivative!(r,i,s,k,V)
+ end
+end
+
+"""
+ _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{<:Real})
+
+```
+r[i,k] = s = (∇bᵏ)(xi); return k+1
+```
+
+where bᵏ is the kᵗʰ basis polynomial. Note that `r[i,k]` is a `VectorValue` or
+`TensorValue` and `s` a `MVector` or `MMatrix` respectively, of same size.
+"""
+function _uniform_set_derivative!(
+ r::AbstractMatrix{G},i,s,k,::Type{<:Real}) where G
+
+ @inbounds r[i,k] = s
+ k+1
+end
+
+"""
+ _uniform_set_derivative!(r::AbstractMatrix{G},i,s,k,::Type{V})
+
+```
+z = zero(s)
+r[i,k] = G(s…, z…, ..., z…) = (Dbᵏ )(xi)
+r[i,k+1] = G(z…, s…, z…, ..., z…) = (Dbᵏ⁺¹ )(xi)
+⋮
+r[i,k+n-1] = G(z…, ..., z…, s…) = (Dbᵏ⁺ⁿ⁻¹)(xi)
+return k+n
+```
+
+Note that `r[i,k]` is a `TensorValue` or `ThirdOrderTensorValue` and `s` a
+`MVector` or `MMatrix`.
+"""
+@generated function _uniform_set_derivative!(
+ r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V}
+ # Git blame me for readable non-generated version
+
+ w = zero(V)
+ m = Array{String}(undef, size(G))
+ N_val_dims = length(size(V))
+ s_size = size(G)[1:end-N_val_dims]
+
+ body = "T = eltype(s); z = zero(T);"
+ for ci in CartesianIndices(s_size)
+ id = join(Tuple(ci))
+ body *= "@inbounds s$id = s[$ci];"
+ end
+
+ for j in CartesianIndices(w)
+ for i in CartesianIndices(m)
+ m[i] = "z"
+ end
+ for ci in CartesianIndices(s_size)
+ id = join(Tuple(ci))
+ m[ci,j] = "s$id"
+ end
+ body *= "@inbounds r[i,k] = ($(join(tuple(m...), ", ")));"
+ body *= "k = k + 1;"
+ end
+
+ body = Meta.parse(string("begin ",body," end"))
+ return Expr(:block, body ,:(return k))
+end
+
+# Specialization for SymTensorValue and SymTracelessTensorValue,
+# necessary as long as outer(Point, V<:AbstractSymTensorValue)::G does not
+# return a tensor type G that implements the appropriate symmetries of the
+# gradient (and hessian)
+@generated function _uniform_set_derivative!(
+ r::AbstractMatrix{G},i,s,k,::Type{V}) where {G,V<:AbstractSymTensorValue{D}} where D
+ # Git blame me for readable non-generated version
+
+ T = eltype(s)
+ m = Array{String}(undef, size(G))
+ s_length = size(G)[1]
+
+ is_traceless = V <: SymTracelessTensorValue
+ skip_last_diagval = is_traceless ? 1 : 0 # Skid V_DD if traceless
+
+ body = "z = $(zero(T));"
+ for i in 1:s_length
+ body *= "@inbounds s$i = s[$i];"
+ end
+
+ for c in 1:(D-skip_last_diagval) # Go over cols
+ for r in c:D # Go over lower triangle, current col
+ for i in eachindex(m)
+ m[i] = "z"
+ end
+ for i in 1:s_length # indices of the Vector s
+ m[i,r,c] = "s$i"
+ if (r!=c)
+ m[i,c,r] = "s$i"
+ elseif is_traceless # V_rr contributes negatively to V_DD (tracelessness)
+ m[i,D,D] = "-s$i"
+ end
+ end
+ body *= "@inbounds r[i,k] = ($(join(tuple(m...), ", ")));"
+ body *= "k = k + 1;"
+ end
+ end
+
+ body = Meta.parse(string("begin ",body," end"))
+ return Expr(:block, body ,:(return k))
+end
+
+function _hessian_nd!(
+ b::UniformPolyBasis{D,V,K,PT}, x,
+ r::AbstractMatrix{G}, i,
+ c::AbstractMatrix{T},
+ g::AbstractMatrix{T},
+ h::AbstractMatrix{T},
+ s::MMatrix{D,D,T}) where {D,V,K,PT,G,T}
+
+ terms = b.terms
+ orders = b.orders
+
+ for d in 1:D
+ Kd = Val(orders[d])
+ _derivatives_1d!(PT,Kd,(c,g,h),x,d)
+ end
+
+ k = 1
+
+ for ci in terms
+
+ for i in eachindex(s)
+ @inbounds s[i] = one(T)
+ end
+
+ for t in 1:D
+ for q in 1:D
+ for d in 1:D
+ if d != q && d != t
+ @inbounds s[t,q] *= c[d,ci[d]]
+ elseif d == q && d ==t
+ @inbounds s[t,q] *= h[d,ci[d]]
+ else
+ @inbounds s[t,q] *= g[d,ci[d]]
+ end
+ end
+ end
+ end
+
+ k = _uniform_set_derivative!(r,i,s,k,V)
+ end
+end
+
diff --git a/src/ReferenceFEs/AWRefFEs.jl b/src/ReferenceFEs/AWRefFEs.jl
index 72a9a527f..e73ddb54f 100644
--- a/src/ReferenceFEs/AWRefFEs.jl
+++ b/src/ReferenceFEs/AWRefFEs.jl
@@ -19,10 +19,10 @@ References:
function ArnoldWintherRefFE(::Type{T},p::Polytope,order::Integer) where T
@assert p == TRI "ArnoldWinther Reference FE only defined for TRIangles"
conforming = true # TODO: Make this an argument
-
+
VT = SymTensorValue{2,T}
- prebasis = MonomialBasis{2}(VT,3,Polynomials._p_filter)
- fb = MonomialBasis{D-1}(T,0,Polynomials._p_filter)
+ prebasis = MonomialBasis(Val(2),VT,3,Polynomials._p_filter)
+ fb = MonomialBasis(Val(D-1),T,0,Polynomials._p_filter)
cb = map(constant_field,component_basis(VT))
function cmom(φ,μ,ds) # Cell and Node moment function: σ_K(φ,μ) = ∫(φ:μ)dK
diff --git a/src/ReferenceFEs/BDMRefFEs.jl b/src/ReferenceFEs/BDMRefFEs.jl
index 8fee9e4eb..156d5b35b 100644
--- a/src/ReferenceFEs/BDMRefFEs.jl
+++ b/src/ReferenceFEs/BDMRefFEs.jl
@@ -15,9 +15,9 @@ function BDMRefFE(::Type{T},p::Polytope,order::Integer) where T
D = num_dims(p)
if is_simplex(p)
- prebasis = MonomialBasis{D}(VectorValue{D,T},order,Polynomials._p_filter)
- fb = MonomialBasis{D-1}(T,order,Polynomials._p_filter)
- cb = Polynomials.NedelecPrebasisOnSimplex{D}(order-2)
+ prebasis = MonomialBasis(Val(D),VectorValue{D,T},order,Polynomials._p_filter)
+ fb = MonomialBasis(Val(D-1),T,order,Polynomials._p_filter)
+ cb = PGradBasis(Monomial,Val(D),T,order-2)
else
@notimplemented "BDM Reference FE only available for simplices"
end
diff --git a/src/ReferenceFEs/BezierRefFEs.jl b/src/ReferenceFEs/BezierRefFEs.jl
index 4620d5620..0273b5429 100644
--- a/src/ReferenceFEs/BezierRefFEs.jl
+++ b/src/ReferenceFEs/BezierRefFEs.jl
@@ -94,7 +94,7 @@ end
function compute_node_to_bezier_node(prebasis::MonomialBasis{D,T},nodes) where {D,T}
orders = get_orders(prebasis)
terms = _coords_to_terms(nodes,orders)
- _prebasis = MonomialBasis{D}(T,orders,terms)
+ _prebasis = MonomialBasis(Val(D),T,orders,terms)
_exps = get_exponents(_prebasis)
exps = get_exponents(prebasis)
[ findfirst( isequal(i), exps) for i in _exps ]
diff --git a/src/ReferenceFEs/CDLagrangianRefFEs.jl b/src/ReferenceFEs/CDLagrangianRefFEs.jl
index f4b071e08..f456ec9d9 100644
--- a/src/ReferenceFEs/CDLagrangianRefFEs.jl
+++ b/src/ReferenceFEs/CDLagrangianRefFEs.jl
@@ -136,7 +136,7 @@ end
if active_faces[offset+iface]
face = Polytope{d}(p,iface)
face_ref_x = get_vertex_coordinates(face)
- face_prebasis = MonomialBasis(Float64,face,1)
+ face_prebasis = monomial_basis(Float64,face,1)
change = inv(evaluate(face_prebasis,face_ref_x))
face_shapefuns = linear_combination(change,face_prebasis)
face_vertex_ids = get_faces(p,d,0)[iface]
diff --git a/src/ReferenceFEs/CLagrangianRefFEs.jl b/src/ReferenceFEs/CLagrangianRefFEs.jl
index a4eb860ad..0cd4e5fc0 100644
--- a/src/ReferenceFEs/CLagrangianRefFEs.jl
+++ b/src/ReferenceFEs/CLagrangianRefFEs.jl
@@ -286,7 +286,7 @@ function _lagrangian_ref_fe(::Type{T},p::Polytope{D},orders) where {T,D}
end
-function MonomialBasis(::Type{T},p::Polytope,orders) where T
+function monomial_basis(::Type{T},p::Polytope,orders) where T
compute_monomial_basis(T,p,orders)
end
@@ -369,9 +369,9 @@ function LagrangianRefFE(::Type{T},p::Polytope{D},order::Int;space::Symbol=_defa
LagrangianRefFE(T,p,orders;space=space)
end
-function MonomialBasis(::Type{T},p::Polytope{D},order::Int) where {D,T}
+function monomial_basis(::Type{T},p::Polytope{D},order::Int) where {D,T}
orders = tfill(order,Val{D}())
- MonomialBasis(T,p,orders)
+ monomial_basis(T,p,orders)
end
function LagrangianDofBasis(::Type{T},p::Polytope{D},order::Int) where {T,D}
@@ -508,7 +508,7 @@ end
for iface in 1:num_faces(p,d)
face = Polytope{d}(p,iface)
face_ref_x = get_vertex_coordinates(face)
- face_prebasis = MonomialBasis(Float64,face,1)
+ face_prebasis = monomial_basis(Float64,face,1)
change = inv(evaluate(face_prebasis,face_ref_x))
face_shapefuns = linear_combination(change,face_prebasis)
face_vertex_ids = get_faces(p,d,0)[iface]
@@ -538,7 +538,7 @@ _compute_node_permutations(::Polytope{0}, interior_nodes) = [[1]]
function _compute_node_permutations(p, interior_nodes)
vertex_to_coord = get_vertex_coordinates(p)
- lbasis = MonomialBasis(Float64,p,1)
+ lbasis = monomial_basis(Float64,p,1)
change = inv(evaluate(lbasis,vertex_to_coord))
lshapefuns = linear_combination(change,lbasis)
perms = get_vertex_permutations(p)
@@ -594,7 +594,7 @@ end
function compute_monomial_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T}
extrusion = Tuple(p.extrusion)
terms = _monomial_terms(extrusion,orders)
- MonomialBasis{D}(T,orders,terms)
+ MonomialBasis(Val(D),T,orders,terms)
end
function compute_own_nodes(p::ExtrusionPolytope{D},orders) where D
diff --git a/src/ReferenceFEs/CRRefFEs.jl b/src/ReferenceFEs/CRRefFEs.jl
index 261fcd64b..c48874ac1 100644
--- a/src/ReferenceFEs/CRRefFEs.jl
+++ b/src/ReferenceFEs/CRRefFEs.jl
@@ -1,4 +1,4 @@
-struct CR <: ReferenceFEName end
+struct CR <: ReferenceFEName end
const cr = CR()
"""
@@ -8,19 +8,19 @@ The `order` argument has the following meaning: the divergence of the functions
is in the P space of degree `order-1`.
"""
-function CRRefFE(::Type{T},p::Polytope,order::Integer) where T
+function CRRefFE(::Type{T},p::Polytope,order::Integer) where T
D = num_dims(p)
if is_simplex(p) && order == 1
- prebasis = MonomialBasis{D}(T,order,Polynomials._p_filter)
- fb = MonomialBasis{D-1}(T,0,Polynomials._p_filter)
+ prebasis = MonomialBasis(Val(D),T,order,Polynomials._p_filter)
+ fb = MonomialBasis(Val(D-1),T,0,Polynomials._p_filter)
else
@notimplemented "CR Reference FE only available for simplices and lowest order"
end
function fmom(φ,μ,ds) # Face moment function : σ_F(φ,μ) = 1/|F| ( ∫((φ)*μ)dF )
D = num_dims(ds.cpoly)
- facet_measure = get_facet_measure(ds.cpoly, D-1)
+ facet_measure = get_facet_measure(ds.cpoly, D-1)
facet_measure_1 = Gridap.Fields.ConstantField(1 / facet_measure[ds.face])
φμ = Broadcasting(Operation(⋅))(φ,μ)
Broadcasting(Operation(*))(φμ,facet_measure_1)
diff --git a/src/ReferenceFEs/MTWRefFEs.jl b/src/ReferenceFEs/MTWRefFEs.jl
index d2f3ede6e..2611d482f 100644
--- a/src/ReferenceFEs/MTWRefFEs.jl
+++ b/src/ReferenceFEs/MTWRefFEs.jl
@@ -9,7 +9,7 @@ const mtw = MardalTaiWinther()
Mardal-Tai-Winther reference finite element.
-References:
+References:
- `A Robust Finite Element Method for Darcy-Stokes Flow`, Mardal, Tai and Winther (2002)
- `Transformations for Piola-mapped elements`, Aznaran, Farrell and Kirby (2022)
@@ -21,9 +21,9 @@ function MardalTaiWintherRefFE(::Type{T},p::Polytope,order::Integer) where T
@asset order == 3 "MardalTaiWinther Reference FE is by definition of order 3"
# TODO: We should just not allow this to be an argument
- prebasis = MonomialBasis{D}(VectorValue{D,T},3,Polynomials._p_filter)
- eb = MonomialBasis{1}(T,0,Polynomials._p_filter)
- fb = MonomialBasis{D-1}(T,1,Polynomials._p_filter)
+ prebasis = MonomialBasis(Val(D),VectorValue{D,T},3,Polynomials._p_filter)
+ eb = MonomialBasis(Val(1),T,0,Polynomials._p_filter)
+ fb = MonomialBasis(Val(D-1),T,1,Polynomials._p_filter)
function emom(φ,μ,ds) # Edge moment function: σ_K(φ,μ) = ∫((φ⋅t)*μ)dK
t = get_edge_tangent(ds)
diff --git a/src/ReferenceFEs/ModalC0RefFEs.jl b/src/ReferenceFEs/ModalC0RefFEs.jl
index c6529906c..631d6681d 100644
--- a/src/ReferenceFEs/ModalC0RefFEs.jl
+++ b/src/ReferenceFEs/ModalC0RefFEs.jl
@@ -150,7 +150,7 @@ function compute_cell_to_modalC0_reffe(
ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,space=space)
face_own_dofs = get_face_own_dofs(lag_reffe,GradConformity())
- filter = space == :Q ? _q_filter : _s_filter_mc0
+ filter = space == :Q ? _q_filter : _ser_filter
sh(bbs) = begin
a = fill(Point{D,eltype(T)}(tfill(zero(eltype(T)),Val{D}())),ndofs)
@@ -181,7 +181,7 @@ function compute_cell_to_modalC0_reffe(
@notimplementedif ! is_n_cube(p)
@notimplementedif minimum(orders) < one(eltype(orders))
- filter = space == :Q ? _q_filter : _s_filter_mc0
+ filter = space == :Q ? _q_filter : _ser_filter
ndofs, predofs, lag_reffe, face_dofs = compute_reffe_data(T,p,orders,space=space)
reffe = GenericRefFE{ModalC0}(ndofs,
@@ -193,4 +193,4 @@ function compute_cell_to_modalC0_reffe(
ModalC0Basis{D}(T,orders,filter=filter))
Fill(reffe,ncells)
-end
\ No newline at end of file
+end
diff --git a/src/ReferenceFEs/NedelecRefFEs.jl b/src/ReferenceFEs/NedelecRefFEs.jl
index f8bb0aef4..5c46c07a3 100644
--- a/src/ReferenceFEs/NedelecRefFEs.jl
+++ b/src/ReferenceFEs/NedelecRefFEs.jl
@@ -16,15 +16,15 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et
D = num_dims(p)
if is_n_cube(p)
- prebasis = QGradMonomialBasis{D}(et,order) # Prebasis
- eb = MonomialBasis{1}(et,order) # Edge basis
- fb = QGradMonomialBasis{D-1}(et,order-1) # Face basis
- cb = QCurlGradMonomialBasis{D}(et,order-1) # Cell basis
+ prebasis = QGradBasis(Monomial,Val(D),et,order) # Prebasis
+ eb = MonomialBasis(Val(1),et,order) # Edge basis
+ fb = QGradBasis(Monomial,Val(D-1),et,order-1) # Face basis
+ cb = QCurlGradBasis(Monomial,Val(D),et,order-1) # Cell basis
elseif is_simplex(p)
- prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order) # Prebasis
- eb = MonomialBasis{1}(et,order) # Edge basis
- fb = MonomialBasis{D-1}(VectorValue{D-1,et},order-1,Polynomials._p_filter) # Face basis
- cb = MonomialBasis{D}(VectorValue{D,et},order-D+1,Polynomials._p_filter) # Cell basis
+ prebasis = PGradBasis(Monomial,Val(D),et,order) # Prebasis
+ eb = MonomialBasis(Val(1),et,order) # Edge basis
+ fb = MonomialBasis(Val(D-1),VectorValue{D-1,et},order-1,Polynomials._p_filter) # Face basis
+ cb = MonomialBasis(Val(D),VectorValue{D,et},order-D+1,Polynomials._p_filter) # Cell basis
else
@unreachable "Nedelec Reference FE only implemented for n-cubes and simplices"
end
@@ -108,7 +108,7 @@ function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc
for dof in cface_own_dofs
push!(face_dofs[face],dof)
end
- end
+ end
end
for dof in face_own_dofs[face]
push!(face_dofs[face],dof)
diff --git a/src/ReferenceFEs/RaviartThomasRefFEs.jl b/src/ReferenceFEs/RaviartThomasRefFEs.jl
index 4920de0c4..2af553b9a 100644
--- a/src/ReferenceFEs/RaviartThomasRefFEs.jl
+++ b/src/ReferenceFEs/RaviartThomasRefFEs.jl
@@ -19,13 +19,13 @@ function RaviartThomasRefFE(
) where {T,D}
if is_n_cube(p)
- prebasis = QCurlGradJacobiPolynomialBasis{D}(T,order) # Prebasis
- cb = QGradJacobiPolynomialBasis{D}(T,order-1) # Cell basis
- fb = JacobiPolynomialBasis{D-1}(T,order,Polynomials._q_filter) # Face basis
+ prebasis = QCurlGradBasis(Legendre,Val(D),T,order) # Prebasis
+ cb = QGradBasis(Legendre,Val(D),T,order-1) # Cell basis
+ fb = LegendreBasis(Val(D-1),T,order,Polynomials._q_filter) # Face basis
elseif is_simplex(p)
- prebasis = PCurlGradMonomialBasis{D}(T,order) # Prebasis
- cb = JacobiPolynomialBasis{D}(VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis
- fb = JacobiPolynomialBasis{D-1}(T,order,Polynomials._p_filter) # Face basis
+ prebasis = PCurlGradBasis(Monomial,Val(D),T,order) # Prebasis
+ cb = LegendreBasis(Val(D),VectorValue{D,T},order-1,Polynomials._p_filter) # Cell basis
+ fb = LegendreBasis(Val(D-1),T,order,Polynomials._p_filter) # Face basis
else
@notimplemented "Raviart-Thomas Reference FE only available for cubes and simplices"
end
@@ -77,15 +77,15 @@ function get_face_own_dofs(reffe::GenericRefFE{RaviartThomas}, conf::DivConformi
end
# TODO: Please remove me
-function JacobiBasis(::Type{T},p::Polytope,orders) where T
- compute_jacobi_basis(T,p,orders)
+function legendreBasis(::Type{T},p::Polytope,orders) where T
+ compute_legendre_basis(T,p,orders)
end
-function JacobiBasis(::Type{T},p::Polytope{D},order::Int) where {D,T}
+function legendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T}
orders = tfill(order,Val{D}())
- JacobiBasis(T,p,orders)
+ legendreBasis(T,p,orders)
end
-function compute_jacobi_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T}
+function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T}
extrusion = Tuple(p.extrusion)
terms = _monomial_terms(extrusion,orders)
- JacobiPolynomialBasis{D}(T,orders,terms)
+ LegendreBasis(Val(D),T,orders,terms)
end
diff --git a/src/ReferenceFEs/ReferenceFEs.jl b/src/ReferenceFEs/ReferenceFEs.jl
index a433e49ba..289cdc256 100644
--- a/src/ReferenceFEs/ReferenceFEs.jl
+++ b/src/ReferenceFEs/ReferenceFEs.jl
@@ -1,7 +1,6 @@
"""
-The exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module ReferenceFEs
@@ -19,7 +18,7 @@ using Gridap.TensorValues
using Gridap.Fields
using Gridap.Polynomials
-using Gridap.Polynomials: _q_filter, _s_filter_mc0
+using Gridap.Polynomials: _q_filter, _ser_filter
using Gridap.Polynomials: _compute_filter_mask
using Gridap.Polynomials: _define_terms, _sort_by_nfaces!
@@ -33,7 +32,6 @@ import Gridap.Arrays: return_type
import Gridap.Fields: evaluate
import Gridap.Fields: lazy_map
import Gridap.Fields: linear_combination
-import Gridap.Polynomials: MonomialBasis
import Gridap.Polynomials: get_order
import Gridap.Polynomials: get_orders
diff --git a/src/ReferenceFEs/SerendipityRefFEs.jl b/src/ReferenceFEs/SerendipityRefFEs.jl
index 935a72c0e..064e75553 100644
--- a/src/ReferenceFEs/SerendipityRefFEs.jl
+++ b/src/ReferenceFEs/SerendipityRefFEs.jl
@@ -26,7 +26,7 @@ println( num_dofs(reffe) )
function SerendipityRefFE(::Type{T},p::Polytope,order::Int) where T
@assert is_n_cube(p) "Polytope not compatible with serendipity elements"
if order > 0
- sp = SerendipityPolytope(p)
+ sp = SerendipityPolytope(p)
else
sp = p
end
@@ -87,12 +87,8 @@ get_extrusion(p::SerendipityPolytope{D}) where D = Point(tfill(HEX_AXIS,Val{D}()
# Implemented polytope interface for LagrangianRefFEs
-function _s_filter(e,order)
- sum( [ i for i in e if i>1 ] ) <= order
-end
-
function compute_monomial_basis(::Type{T},p::SerendipityPolytope{D},orders) where {T,D}
- MonomialBasis{D}(T,orders,_s_filter)
+ MonomialBasis(Val(D),T,orders,_ser_filter)
end
function compute_own_nodes(p::SerendipityPolytope{0},orders)
diff --git a/src/ReferenceFEs/deprecated/BDMRefFEs.jl b/src/ReferenceFEs/deprecated/BDMRefFEs.jl
index 81d3bd9af..0a425b247 100644
--- a/src/ReferenceFEs/deprecated/BDMRefFEs.jl
+++ b/src/ReferenceFEs/deprecated/BDMRefFEs.jl
@@ -169,9 +169,7 @@ function Conformity(reffe::GenericRefFE{BDM},sym::Symbol)
# Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ ()
if is_simplex(p)
- T = VectorValue{num_dims(p),et}
- # cbasis = GradMonomialBasis{num_dims(p)}(T,order-1)
- cbasis = Polynomials.NedelecPrebasisOnSimplex{num_dims(p)}(order-2)
+ cbasis = PGradBasis(Monomial,Val(num_dims(p)),et,order-2)
else
@notimplemented
end
diff --git a/src/ReferenceFEs/deprecated/NedelecRefFEs.jl b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl
index 81924f856..753354db7 100644
--- a/src/ReferenceFEs/deprecated/NedelecRefFEs.jl
+++ b/src/ReferenceFEs/deprecated/NedelecRefFEs.jl
@@ -16,7 +16,7 @@ function NedelecRefFE(::Type{et},p::Polytope,order::Integer) where et
D = num_dims(p)
if is_n_cube(p)
- prebasis = QGradMonomialBasis{D}(et,order)
+ prebasis = QGradMonomialBasis(Val(D),et,order)
elseif is_simplex(p)
prebasis = Polynomials.NedelecPrebasisOnSimplex{D}(order)
else
@@ -96,7 +96,7 @@ function get_face_dofs(reffe::GenericRefFE{Nedelec,Dc}) where Dc
for dof in cface_own_dofs
push!(face_dofs[face],dof)
end
- end
+ end
end
for dof in face_own_dofs[face]
push!(face_dofs[face],dof)
@@ -168,7 +168,7 @@ function _Nedelec_edge_values(p,et,order)
c_eips, ecips, ewips = _nfaces_evaluation_points_weights(p, egeomap, cips, wips)
# Edge moments, i.e., M(Ei)_{ab} = q_RE^a(xgp_REi^b) w_Fi^b t_Ei ⋅ ()
- eshfs = MonomialBasis(et,ep,order)
+ eshfs = monomial_basis(et,ep,order)
emoments = _Nedelec_edge_moments(p, eshfs, c_eips, ecips, ewips)
return ecips, emoments
@@ -204,7 +204,7 @@ function _Nedelec_face_values(p,et,order)
c_fips, fcips, fwips = _nfaces_evaluation_points_weights(p, fgeomap, fips, wips)
# Face moments, i.e., M(Fi)_{ab} = w_Fi^b q_RF^a(xgp_RFi^b) (n_Fi × ())
- fshfs = QGradMonomialBasis{num_dims(fp)}(et,order-1)
+ fshfs = QGradMonomialBasis(Val(num_dims(fp)),et,order-1)
fmoments = _Nedelec_face_moments(p, fshfs, c_fips, fcips, fwips)
@@ -250,7 +250,7 @@ function _Nedelec_face_values_simplex(p,et,order)
c_fips, fcips, fwips, fJtips = _nfaces_evaluation_points_weights_with_jac(p, fgeomap, fips, wips)
Df = num_dims(fp)
- fshfs = MonomialBasis{Df}(VectorValue{Df,et},order-1,(e,k)->sum(e)<=k)
+ fshfs = MonomialBasis(Val(Df),VectorValue{Df,et},order-1,(e,k)->sum(e)<=k)
fmoments = _Nedelec_face_moments_simplex(p, fshfs, c_fips, fcips, fwips, fJtips)
@@ -296,10 +296,10 @@ function _Nedelec_cell_values(p,et,order)
# Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ ()
if is_n_cube(p)
- cbasis = QCurlGradMonomialBasis{num_dims(p)}(et,order-1)
+ cbasis = QCurlGradMonomialBasis(Val(num_dims(p)),et,order-1)
else
D = num_dims(p)
- cbasis = MonomialBasis{D}(VectorValue{D,et},order-D+1,(e,k)->sum(e)<=k)
+ cbasis = MonomialBasis(Val(D),VectorValue{D,et},order-D+1,(e,k)->sum(e)<=k)
end
cmoments = _Nedelec_cell_moments(p, cbasis, ccips, cwips )
diff --git a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl
index 11d7f9b8a..006d92e65 100644
--- a/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl
+++ b/src/ReferenceFEs/deprecated/RaviartThomasRefFEs.jl
@@ -16,20 +16,20 @@ is in the Q space of degree `order`.
function RaviartThomasRefFE(
::Type{et},p::Polytope,order::Integer;basis_type=:monomial,phi=GenericField(identity)
) where et
- @assert basis_type ∈ (:monomial, :jacobi, :chebyshev)
+ @assert basis_type ∈ (:monomial, :legendre, :chebyshev)
D = num_dims(p)
if is_n_cube(p) && basis_type == :monomial
- prebasis = QCurlGradMonomialBasis{D}(et,order)
+ prebasis = QCurlGradMonomialBasis(Val(D),et,order)
elseif is_simplex(p) && basis_type == :monomial
- prebasis = PCurlGradMonomialBasis{D}(et,order)
- elseif is_n_cube(p) && basis_type == :jacobi
- prebasis = QCurlGradJacobiPolynomialBasis{D}(et,order)
- elseif is_simplex(p) && basis_type == :jacobi
- prebasis = PCurlGradJacobiPolynomialBasis{D}(et,order)
+ prebasis = PCurlGradMonomialBasis(Val(D),et,order)
+ elseif is_n_cube(p) && basis_type == :legendre
+ prebasis = QCurlGradLegendreBasis(Val(D),et,order)
+ elseif is_simplex(p) && basis_type == :legendre
+ prebasis = PCurlGradLegendreBasis(Val(D),et,order)
elseif is_n_cube(p) && basis_type == :chebyshev
- prebasis = QCurlGradChebyshevPolynomialBasis{D}(et,order)
+ prebasis = QCurlGradChebyshevBasis(Val(D),et,order)
else
@notimplemented "H(div) Reference FE only available for cubes and simplices"
end
@@ -196,8 +196,8 @@ function _RT_face_values(p,et,order,phi)
# Moments (fmoments)
# The RT prebasis is expressed in terms of shape function
#fshfs = MonomialBasis(et,fp,order)
- fshfs = JacobiBasis(et,fp,order)
- #fshfs = ChebyshevBasis(et,fp,order)
+ fshfs = legendreBasis(et,fp,order)
+ #fshfs = chebyshevBasis(et,fp,order)
#fshfs = get_shapefuns(LagrangianRefFE(et,fp,order))
# Face moments, i.e., M(Fi)_{ab} = q_RF^a(xgp_RFi^b) w_Fi^b n_Fi ⋅ ()
@@ -206,30 +206,30 @@ function _RT_face_values(p,et,order,phi)
return fcips, fmoments
end
-function JacobiBasis(::Type{T},p::Polytope,orders) where T
- compute_jacobi_basis(T,p,orders)
+function legendreBasis(::Type{T},p::Polytope,orders) where T
+ compute_legendre_basis(T,p,orders)
end
-function JacobiBasis(::Type{T},p::Polytope{D},order::Int) where {D,T}
+function legendreBasis(::Type{T},p::Polytope{D},order::Int) where {D,T}
orders = tfill(order,Val{D}())
- JacobiBasis(T,p,orders)
+ legendreBasis(T,p,orders)
end
-function compute_jacobi_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T}
+function compute_legendre_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T}
extrusion = Tuple(p.extrusion)
terms = _monomial_terms(extrusion,orders)
- JacobiPolynomialBasis{D}(T,orders,terms)
+ LegendreBasis(Val(D),T,orders,terms)
end
-function ChebyshevBasis(::Type{T},p::Polytope,orders) where T
+function chebyshevBasis(::Type{T},p::Polytope,orders) where T
compute_chebyshev_basis(T,p,orders)
end
-function ChebyshevBasis(::Type{T},p::Polytope{D},order::Int) where {D,T}
+function chebyshevBasis(::Type{T},p::Polytope{D},order::Int) where {D,T}
orders = tfill(order,Val{D}())
- ChebyshevBasis(T,p,orders)
+ chebyshevBasis(T,p,orders)
end
function compute_chebyshev_basis(::Type{T},p::ExtrusionPolytope{D},orders) where {D,T}
extrusion = Tuple(p.extrusion)
terms = _monomial_terms(extrusion,orders)
- ChebyshevPolynomialBasis{D}(T,orders,terms)
+ ChebyshevBasis(Val(D),T,orders,terms)
end
function _RT_cell_moments(p, cbasis, ccips, cwips)
@@ -250,13 +250,13 @@ function _RT_cell_values(p,et,order,phi)
# Cell moments, i.e., M(C)_{ab} = q_C^a(xgp_C^b) w_C^b ⋅ ()
if is_n_cube(p)
- #cbasis = QGradMonomialBasis{num_dims(p)}(et,order-1)
- cbasis = QGradJacobiPolynomialBasis{num_dims(p)}(et,order-1)
- #cbasis = QGradChebyshevPolynomialBasis{num_dims(p)}(et,order-1)
+ #cbasis = QGradMonomialBasis(Val(num_dims(p)),et,order-1)
+ cbasis = QGradLegendreBasis(Val(num_dims(p)),et,order-1)
+ #cbasis = QGradChebyshevBasis(Val(num_dims(p)),et,order-1)
#cbasis = get_shapefuns(RaviartThomasRefFE(et,p,order-1))
elseif is_simplex(p)
T = VectorValue{num_dims(p),et}
- cbasis = MonomialBasis{num_dims(p)}(T,order-1, _p_filter)
+ cbasis = MonomialBasis(Val(num_dims(p)),T,order-1, _p_filter)
else
@notimplemented
end
diff --git a/src/TensorValues/TensorValues.jl b/src/TensorValues/TensorValues.jl
index 5d491f997..b46285e71 100644
--- a/src/TensorValues/TensorValues.jl
+++ b/src/TensorValues/TensorValues.jl
@@ -33,6 +33,8 @@ SymTensorValue( SMatrix{2}(1,2,3,4) ) # SymTensorValue{2, Int64, 3}(1, 3, 4)
```
See the official documentation for more details.
+
+$(public_names_in_md(@__MODULE__))
"""
module TensorValues
diff --git a/src/Visualization/Visualization.jl b/src/Visualization/Visualization.jl
index c20fb41ea..614ce8ee0 100644
--- a/src/Visualization/Visualization.jl
+++ b/src/Visualization/Visualization.jl
@@ -1,7 +1,6 @@
"""
-The exported names are
-$(EXPORTS)
+$(public_names_in_md(@__MODULE__))
"""
module Visualization
diff --git a/test/PolynomialsTests/BernsteinBasesTests.jl b/test/PolynomialsTests/BernsteinBasesTests.jl
new file mode 100644
index 000000000..60abe1ccd
--- /dev/null
+++ b/test/PolynomialsTests/BernsteinBasesTests.jl
@@ -0,0 +1,147 @@
+module BernsteinBasisTests
+
+using Test
+using Gridap.TensorValues
+using Gridap.Fields
+using Gridap.Polynomials
+using Gridap.Polynomials: binoms
+using ForwardDiff
+
+@test isHierarchical(Bernstein) == false
+
+np = 3
+x = [Point(0.),Point(1.),Point(.4)]
+x1 = x[1]
+
+# Only test 1D evaluations as tensor product structure is tested in monomial tests
+#
+V = Float64
+G = gradient_type(V,x1)
+H = gradient_type(G,x1)
+
+function test_internals(order,x,bx,∇bg,Hbx)
+ sz = (1,order+1)
+ for (i,xi) in enumerate(x)
+ v2 = zeros(sz)
+ Polynomials._evaluate_1d!(Bernstein,Val(order),v2,xi,1)
+ @test all( [ bxi[1]≈vxi[1] for (bxi,vxi) in zip(bx[i,:],v2[:,1]) ] )
+
+ g2 = zeros(sz)
+ Polynomials._gradient_1d!(Bernstein,Val(order),g2,xi,1)
+ @test all( [ bxi[1]≈vxi[1] for (bxi,vxi) in zip(∇bx[i,:],g2[:,1]) ] )
+
+ h2 = zeros(sz)
+ Polynomials._hessian_1d!(Bernstein,Val(order),h2,xi,1)
+ @test all( [ bxi[1]≈vxi[1] for (bxi,vxi) in zip(Hbx[i,:],h2[:,1]) ] )
+ end
+end
+
+
+# order 0 degenerated case
+
+order = 0
+b = BernsteinBasis(Val(1),V,order)
+@test get_order(b) == 0
+@test get_orders(b) == (0,)
+
+bx = [ 1.; 1.; 1.;; ]
+∇bx = G[ 0.; 0.; 0.;; ]
+Hbx = H[ 0.; 0.; 0.;; ]
+
+test_internals(order,x,bx,∇bx,Hbx)
+
+test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
+
+
+# Order 1
+
+order = 1
+b = BernsteinBasis(Val(1),V,order)
+
+bx = [ 1.0 0.0
+ 0.0 1.0
+ 0.6 0.4]
+
+∇bx = G[ -1. 1.
+ -1. 1.
+ -1. 1. ]
+
+Hbx = H[ 0. 0.
+ 0. 0.
+ 0. 0. ]
+
+test_internals(order,x,bx,∇bx,Hbx)
+
+test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
+
+
+# Order 2
+
+order = 2
+b = BernsteinBasis(Val(1),V,order)
+
+bx = [ 1.0 0.0 0.0
+ 0.0 0.0 1.0
+ 0.36 0.48 0.16]
+
+
+∇bx = G[ -2. 2. 0.
+ 0. -2. 2.
+ -1.2 0.4 .8 ]
+
+Hbx = H[ 2. -4. 2.
+ 2. -4. 2.
+ 2. -4. 2. ]
+
+test_internals(order,x,bx,∇bx,Hbx)
+
+test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
+
+
+# Order 3
+
+function bernstein(K,N)
+ b = binoms(Val(K))
+ t -> b[N+1]*(t^N)*((1-t)^(K-N))
+end
+_∇(b) = t -> ForwardDiff.derivative(b, t)
+_H(b) = t -> ForwardDiff.derivative(y -> ForwardDiff.derivative(b, y), t)
+
+order = 3
+b = BernsteinBasis(Val(1),V,order)
+
+# x=x^1; x2 = x^2; x3 = x^3
+# -x3+3x2-3x+1 3x3-6x2+3x -3x3+3x2 x3
+bx = [ bernstein(order,n)( xi[1]) for xi in x, n in 0:order]
+
+# -3x2+6x-3 9x2-12x+3 -9x2+6x 3x2
+∇bx = [ G(_∇(bernstein(order,n))(xi[1])) for xi in x, n in 0:order]
+
+# -6x+6 18x-12 -18x+6 6x
+Hbx = [ H(_H(bernstein(order,n))(xi[1])) for xi in x, n in 0:order]
+
+test_internals(order,x,bx,∇bx,Hbx)
+
+test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
+
+
+# Order 4
+
+order = 4
+b = BernsteinBasis(Val(1),V,order)
+
+bx = [ bernstein(order,n)( xi[1]) for xi in x, n in 0:order]
+∇bx = [ G(_∇(bernstein(order,n))(xi[1])) for xi in x, n in 0:order]
+Hbx = [ H(_H(bernstein(order,n))(xi[1])) for xi in x, n in 0:order]
+
+test_internals(order,x,bx,∇bx,Hbx)
+
+test_field_array(b,x,bx,≈, grad=∇bx,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
+
+
+end # module
diff --git a/test/PolynomialsTests/ChangeBasisTests.jl b/test/PolynomialsTests/ChangeBasisTests.jl
index 9119d3609..09dee2b58 100644
--- a/test/PolynomialsTests/ChangeBasisTests.jl
+++ b/test/PolynomialsTests/ChangeBasisTests.jl
@@ -13,7 +13,7 @@ order = 1
V = Float64
G = gradient_type(V,xi)
H = gradient_type(G,xi)
-f = MonomialBasis{2}(V,order)
+f = MonomialBasis(Val(2),V,order)
change = inv(evaluate(f,nodes))
diff --git a/test/PolynomialsTests/ChebyshevBasesTests.jl b/test/PolynomialsTests/ChebyshevBasesTests.jl
new file mode 100644
index 000000000..b32ddfd90
--- /dev/null
+++ b/test/PolynomialsTests/ChebyshevBasesTests.jl
@@ -0,0 +1,119 @@
+module ChebyshevBasisTests
+
+using Test
+using Gridap.TensorValues
+using Gridap.Fields
+using Gridap.Polynomials
+using Gridap.Polynomials: binoms
+using ForwardDiff
+
+@test isHierarchical(Chebyshev) == true
+
+np = 3
+x = [Point(2.),Point(3.),Point(4.)]
+x1 = x[1]
+
+
+# Only test 1D evaluations as tensor product structure is tested in monomial tests
+
+V = Float64
+G = gradient_type(V,x1)
+H = gradient_type(G,x1)
+
+chebyshev_T(N) = t -> begin
+ ξ = 2t-1
+ sq = sqrt(ξ*ξ-1)
+ .5*( (ξ - sq)^N + (ξ + sq)^N )
+end
+chebyshev_U(N) = t -> begin
+ ξ = 2t-1
+ sq = sqrt(ξ*ξ-1)
+ .5*( (ξ + sq)^(N+1) - (ξ - sq)^(N+1) )/sq
+end
+_∇(b) = t -> ForwardDiff.derivative(b, t)
+_H(b) = t -> ForwardDiff.derivative(y -> ForwardDiff.derivative(b, y), t)
+
+######################################
+# First Kind Chebyshev ( Hessian TODO)
+######################################
+
+# order 0 degenerated case
+order = 0
+b = ChebyshevBasis(Val(1),V,order)
+@test get_order(b) == 0
+@test get_orders(b) == (0,)
+
+bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order]
+∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+
+test_field_array(b,x,bx,≈, grad=∇bx)#,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:])
+
+
+# Order 1
+order = 1
+b = ChebyshevBasis(Val(1),V,order)
+
+bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order]
+∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+
+test_field_array(b,x,bx,≈,grad=∇bx)#,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:])
+
+
+# Order 2
+order = 2
+b = ChebyshevBasis(Val(1),V,order)
+
+
+bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order]
+∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+
+test_field_array(b,x,bx,≈,grad=∇bx)#,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:])
+
+
+# Order 3
+order = 3
+b = ChebyshevBasis(Val(1),V,order)
+
+bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order]
+∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+
+test_field_array(b,x,bx,≈, grad=∇bx)#,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:])
+
+
+# Order 4
+order = 4
+b = ChebyshevBasis(Val(1),V,order)
+
+bx = [ chebyshev_T(n)( xi[1]) for xi in x, n in 0:order]
+∇bx = [ G(_∇(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+Hbx = [ H(_H(chebyshev_T(n))(xi[1])) for xi in x, n in 0:order]
+
+test_field_array(b,x,bx,≈,grad=∇bx)#,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],≈,grad=∇bx[1,:])#,gradgrad=Hbx[1,:])
+
+
+############################
+# Second Kind Chebyshev TODO
+############################
+
+@test_throws ErrorException ChebyshevBasis(Val(1),Float64,0;kind=:U)
+
+order = 1
+d = 1
+b = ChebyshevBasis(Val(1),V,order)
+Hb = FieldGradientArray{2}(b)
+r, c, g, h = return_cache(Hb,x)
+
+@test_throws ErrorException Polynomials._gradient_1d!(Chebyshev{:U},Val(order),g,x1,d)
+@test_throws ErrorException Polynomials._hessian_1d!( Chebyshev{:U},Val(order),h,x1,d)
+
+
+end # module
diff --git a/test/PolynomialsTests/JacobiPolynomialBasesTests.jl b/test/PolynomialsTests/LegendreBasesTests.jl
similarity index 85%
rename from test/PolynomialsTests/JacobiPolynomialBasesTests.jl
rename to test/PolynomialsTests/LegendreBasesTests.jl
index fdbfe87fc..404492b77 100644
--- a/test/PolynomialsTests/JacobiPolynomialBasesTests.jl
+++ b/test/PolynomialsTests/LegendreBasesTests.jl
@@ -1,4 +1,4 @@
-module JacobiPolynomialBasisTests
+module LegendreBasisTests
using Test
using Gridap.TensorValues
@@ -6,6 +6,8 @@ using Gridap.Fields
using Gridap.Fields: Broadcasting
using Gridap.Polynomials
+@test isHierarchical(Legendre) == true
+
# Real-valued Q space with isotropic order
x1 = Point(0.0)
@@ -17,24 +19,24 @@ G = gradient_type(V,x1)
H = gradient_type(G,x1)
order = 3
-b1 = JacobiPolynomialBasis{1}(V,order)
+b1 = LegendreBasis(Val(1),V,order)
∇b1 = Broadcasting(∇)(b1)
∇∇b1 = Broadcasting(∇)(∇b1)
@test evaluate(b1,[x1,x2,x3,]) ≈ [ 1.0 -1.7320508075688772 2.23606797749979 -2.6457513110645907;
1.0 0.0 -1.118033988749895 -0.0;
1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 ]
-@test evaluate(∇b1,[x1,x2,x3,]) ≈ G[ (0.0,) (3.4641016151377544,) (-13.416407864998739,) (31.74901573277509,);
- (0.0,) (3.4641016151377544,) (0.0,) (-7.937253933193772,);
+@test evaluate(∇b1,[x1,x2,x3,]) ≈ G[ (0.0,) (3.4641016151377544,) (-13.416407864998739,) (31.74901573277509,);
+ (0.0,) (3.4641016151377544,) (0.0,) (-7.937253933193772,);
(0.0,) (3.4641016151377544,) (13.416407864998739,) (31.74901573277509,) ]
-@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ H[ (0.0,) (0.0,) (13.416407864998739,) (-79.37253933193772,);
+@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ H[ (0.0,) (0.0,) (13.416407864998739,) (-79.37253933193772,);
(0.0,) (0.0,) (13.416407864998739,) (0.0,);
(0.0,) (0.0,) (13.416407864998739,) (79.37253933193772,) ]
x1 = Point(0.0,0.0)
x2 = Point(0.5,0.5)
x3 = Point(1.0,1.0)
-b2 = JacobiPolynomialBasis{2}(V,order)
+b2 = LegendreBasis(Val(2),V,order)
∇b2 = Broadcasting(∇)(b2)
∇∇b2 = Broadcasting(∇)(∇b2)
@@ -45,18 +47,18 @@ H = gradient_type(G,x1)
=# -1.7320508075688772 2.9999999999999996 -3.872983346207417 #=
=# 4.58257569495584 2.23606797749979 -3.872983346207417 #=
=# 5.000000000000001 -5.916079783099617 -2.6457513110645907 #=
- =# 4.58257569495584 -5.916079783099617 7.000000000000001;
+ =# 4.58257569495584 -5.916079783099617 7.000000000000001;
1.0 0.0 -1.118033988749895 -0.0 0.0 0.0 -0.0 -0.0 #=
- =# -1.118033988749895 -0.0 1.2500000000000002 0.0 -0.0 -0.0 0.0 0.0;
- 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 #=
+ =# -1.118033988749895 -0.0 1.2500000000000002 0.0 -0.0 -0.0 0.0 0.0;
+ 1.0 1.7320508075688772 2.23606797749979 2.6457513110645907 #=
=# 1.7320508075688772 2.9999999999999996 3.872983346207417 #=
=# 4.58257569495584 2.23606797749979 3.872983346207417 #=
- =# 5.000000000000001 5.916079783099617 2.6457513110645907 #=
+ =# 5.000000000000001 5.916079783099617 2.6457513110645907 #=
=# 4.58257569495584 5.916079783099617 7.000000000000001 ]
-@test evaluate(∇b2,[x1,x2,x3,])[:,10] ≈ G[ (7.745966692414834, 23.2379000772445);
- (-3.872983346207417, 0.0);
+@test evaluate(∇b2,[x1,x2,x3,])[:,10] ≈ G[ (7.745966692414834, 23.2379000772445);
+ (-3.872983346207417, 0.0);
(7.745966692414834, 23.2379000772445) ]
-@test evaluate(∇∇b2,[x1,x2,x3,])[:,10] ≈ H[ (0.0, -46.475800154489, -46.475800154489, -23.2379000772445);
+@test evaluate(∇∇b2,[x1,x2,x3,])[:,10] ≈ H[ (0.0, -46.475800154489, -46.475800154489, -23.2379000772445);
(-0.0, 0.0, 0.0, 0.0);
(0.0, 46.475800154489, 46.475800154489, 23.2379000772445) ]
diff --git a/test/PolynomialsTests/ModalC0BasesTests.jl b/test/PolynomialsTests/ModalC0BasesTests.jl
index f5e084633..3ba41a78e 100644
--- a/test/PolynomialsTests/ModalC0BasesTests.jl
+++ b/test/PolynomialsTests/ModalC0BasesTests.jl
@@ -4,6 +4,7 @@ using Test
using Gridap.TensorValues
using Gridap.Fields
using Gridap.Polynomials
+using StaticArrays
# using BenchmarkTools
import Gridap.Fields: Broadcasting
@@ -22,6 +23,9 @@ order = 3
a = fill(Point(-0.5),order+1)
b = fill(Point(2.5),order+1)
b1 = ModalC0Basis{1}(V,order,a,b)
+
+@test IndexStyle(b1) == IndexLinear()
+
∇b1 = Broadcasting(∇)(b1)
∇∇b1 = Broadcasting(∇)(∇b1)
@@ -35,6 +39,24 @@ b1 = ModalC0Basis{1}(V,order,a,b)
(0.0,) (0.0,) (3.4641016151377544,) (-1.4907119849998598,);
(0.0,) (0.0,) (3.4641016151377544,) (2.9814239699997196,)]
+# Validate generic 1D implem using UniformPolyBasis
+
+order = 3
+a = fill(Point(0.),order+1)
+b = fill(Point(1.),order+1)
+b1 = ModalC0Basis{1}(V,order,a,b)
+b1u= UniformPolyBasis(ModalC0,Val(1),V,order)
+
+∇b1 = Broadcasting(∇)(b1)
+∇b1u = Broadcasting(∇)(b1u)
+∇∇b1 = Broadcasting(∇)(∇b1)
+∇∇b1u= Broadcasting(∇)(∇b1u)
+
+@test evaluate(b1, [x1,x2,x3,]) ≈ evaluate(b1u, [x1,x2,x3,])
+@test evaluate(∇b1, [x1,x2,x3,]) ≈ evaluate(∇b1u, [x1,x2,x3,])
+@test evaluate(∇∇b1,[x1,x2,x3,]) ≈ evaluate(∇∇b1u,[x1,x2,x3,])
+
+
x1 = Point(0.0,0.0)
x2 = Point(0.5,0.5)
x3 = Point(1.0,1.0)
@@ -61,4 +83,47 @@ H = gradient_type(G,x1)
(0.0, 0.5590169943749475, 0.5590169943749475, 1.118033988749895);
(0.0, -2.23606797749979, -2.23606797749979, 0.0) ]
-end # module
\ No newline at end of file
+# Validate generic 2D implem using UniformPolyBasis
+
+order = 3
+len_b2 = (order+1)^2
+a = fill(Point(0.,0.), len_b2)
+b = fill(Point(1.,1.), len_b2)
+
+b2 = ModalC0Basis{2}(V,order,a,b)
+b2u= UniformPolyBasis(ModalC0,Val(2),V,order)
+∇b2 = Broadcasting(∇)(b2)
+∇b2u = Broadcasting(∇)(b2u)
+
+b2x = collect(eachcol(evaluate(b2, [x1,x2,x3,])))
+b2xu = collect(eachcol(evaluate(b2u, [x1,x2,x3,])))
+∇b2x = collect(eachcol(evaluate(∇b2, [x1,x2,x3,])))
+∇b2xu = collect(eachcol(evaluate(∇b2u, [x1,x2,x3,])))
+
+# re order basis polynomials as each basis has different ordering ...
+b2x_perm = b2x[ sortperm(b2x)[ invperm(sortperm(b2xu))]]
+∇b2x_perm = ∇b2x[ sortperm(∇b2x)[ invperm(sortperm(∇b2xu))]]
+
+@test b2xu == b2x_perm
+@test ∇b2xu == ∇b2x_perm
+
+
+# Misc
+
+# Derivatives not implemented for symetric tensor types
+
+D = 2
+T = Float64
+V = SymTensorValue{D,T}
+G = gradient_type(V,x1)
+s = MVector(0.,0.)
+r = zeros(G, (1,1))
+@test_throws ErrorException Polynomials._set_derivative_mc0!(r,1,s,0,0,V)
+
+V = SymTracelessTensorValue{D,T}
+G = gradient_type(V,x1)
+r = zeros(G, (1,1))
+@test_throws ErrorException Polynomials._set_derivative_mc0!(r,1,s,0,0,V)
+
+
+end # module
diff --git a/test/PolynomialsTests/MonomialBasesTests.jl b/test/PolynomialsTests/MonomialBasesTests.jl
index e087d71b8..d3a734982 100644
--- a/test/PolynomialsTests/MonomialBasesTests.jl
+++ b/test/PolynomialsTests/MonomialBasesTests.jl
@@ -5,6 +5,10 @@ using Gridap.TensorValues
using Gridap.Fields
using Gridap.Polynomials
+using Gridap.Polynomials: _q_filter, _qs_filter, _p_filter, _ps_filter
+
+@test isHierarchical(Monomial) == true
+
xi = Point(2,3)
np = 5
x = fill(xi,np)
@@ -15,7 +19,7 @@ order = 0
V = Float64
G = gradient_type(V,xi)
H = gradient_type(G,xi)
-b = MonomialBasis{2}(V,order)
+b = MonomialBasis(Val(2),V,order)
@test get_order(b) == 0
@test get_orders(b) == (0,0)
@@ -35,7 +39,7 @@ order = 1
V = Float64
G = gradient_type(V,xi)
H = gradient_type(G,xi)
-b = MonomialBasis{2}(V,order)
+b = MonomialBasis(Val(2),V,order)
v = V[1.0, 2.0, 3.0, 6.0]
g = G[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (3.0, 2.0)]
@@ -52,7 +56,7 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
orders = (1,2)
V = Float64
G = gradient_type(V,xi)
-b = MonomialBasis{2}(V,orders)
+b = MonomialBasis(Val(2),V,orders)
v = V[1.0, 2.0, 3.0, 6.0, 9.0, 18.0]
g = G[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (3.0, 2.0), (0.0, 6.0), (9.0, 12.0)]
@@ -68,7 +72,7 @@ order = 1
V = VectorValue{3,Float64}
G = gradient_type(V,xi)
H = gradient_type(G,xi)
-b = MonomialBasis{2}(V,order)
+b = MonomialBasis(Val(2),V,order)
v = V[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
[2.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 2.0],
@@ -107,7 +111,7 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
orders = (1,2)
V = VectorValue{2,Float64}
G = gradient_type(V,xi)
-b = MonomialBasis{2}(V,orders)
+b = MonomialBasis(Val(2),V,orders)
v = V[
(1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 2.0),
@@ -133,7 +137,7 @@ order = 1
V = Float64
G = gradient_type(V,xi)
filter = (e,o) -> sum(e) <= o
-b = MonomialBasis{2}(V,order,filter)
+b = MonomialBasis(Val(2),V,order,filter)
v = V[1.0, 2.0, 3.0]
g = G[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]
@@ -149,7 +153,7 @@ order = 1
V = VectorValue{3,Float64}
G = gradient_type(V,xi)
filter = (e,o) -> sum(e) <= o
-b = MonomialBasis{2}(V,order,filter)
+b = MonomialBasis(Val(2),V,order,filter)
v = V[[1.0; 0.0; 0.0], [0.0; 1.0; 0.0], [0.0; 0.0; 1.0],
[2.0; 0.0; 0.0], [0.0; 2.0; 0.0], [0.0; 0.0; 2.0],
@@ -172,7 +176,7 @@ order = 1
V = SymTensorValue{2,Float64}
G = gradient_type(V,xi)
filter = (e,o) -> sum(e) <= o
-b = MonomialBasis{2}(V,order,filter)
+b = MonomialBasis(Val(2),V,order,filter)
v = V[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0),
(2.0, 0.0, 0.0), (0.0, 2.0, 0.0), (0.0, 0.0, 2.0),
@@ -199,7 +203,7 @@ order = 1
V = SymTracelessTensorValue{2,Float64}
G = gradient_type(V,xi)
filter = (e,o) -> sum(e) <= o
-b = MonomialBasis{2}(V,order,filter)
+b = MonomialBasis(Val(2),V,order,filter)
v = V[(1.0, 0.0), (0.0, 1.0),
(2.0, 0.0), (0.0, 2.0),
@@ -219,27 +223,54 @@ test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
order = 1
-b = MonomialBasis{1}(Float64,order)
+b = MonomialBasis(Val(1),Float64,order)
@test evaluate(b,Point{1,Float64}[(0,),(1,)]) == [1.0 0.0; 1.0 1.0]
-b = MonomialBasis{0}(VectorValue{2,Float64},order)
+b = MonomialBasis(Val(0),VectorValue{2,Float64},order)
@test evaluate(b,Point{0,Float64}[(),()]) == VectorValue{2,Float64}[(1.0, 0.0) (0.0, 1.0); (1.0, 0.0) (0.0, 1.0)]
-b = MonomialBasis{0}(TensorValue{2,2,Float64},order)
+b = MonomialBasis(Val(0),TensorValue{2,2,Float64},order)
@test evaluate(b,Point{0,Float64}[(),()]) == TensorValue{2,2,Float64}[
(1.0, 0.0, 0.0, 0.0) (0.0, 1.0, 0.0, 0.0) (0.0, 0.0, 1.0, 0.0) (0.0, 0.0, 0.0, 1.0);
(1.0, 0.0, 0.0, 0.0) (0.0, 1.0, 0.0, 0.0) (0.0, 0.0, 1.0, 0.0) (0.0, 0.0, 0.0, 1.0)
]
-b = MonomialBasis{0}(SymTensorValue{2,Float64},order)
+b = MonomialBasis(Val(0),SymTensorValue{2,Float64},order)
@test evaluate(b,Point{0,Float64}[(),()]) == SymTensorValue{2,Float64}[
(1.0, 0.0, 0.0) (0.0, 1.0, 0.0) (0.0, 0.0, 1.0);
(1.0, 0.0, 0.0) (0.0, 1.0, 0.0) (0.0, 0.0, 1.0)
]
-b = MonomialBasis{0}(SymTracelessTensorValue{2,Float64},order)
+b = MonomialBasis(Val(0),SymTracelessTensorValue{2,Float64},order)
@test evaluate(b,Point{0,Float64}[(),()]) == SymTracelessTensorValue{2,Float64}[
(1.0, 0.0) (0.0, 1.0); (1.0, 0.0) (0.0, 1.0)
]
+order = 2
+
+@test _q_filter( (1,2) ,order) == true
+@test _q_filter( (2,0) ,order) == true
+@test _q_filter( (2,2) ,order) == true
+@test _q_filter( (1,1) ,order) == true
+@test _q_filter( (3,1) ,order) == false
+
+@test _qs_filter( (1,2) ,order) == true
+@test _qs_filter( (2,0) ,order) == true
+@test _qs_filter( (2,2) ,order) == true
+@test _qs_filter( (1,1) ,order) == false
+@test _qs_filter( (3,1) ,order) == false
+
+@test _p_filter( (1,2) ,order) == false
+@test _p_filter( (2,0) ,order) == true
+@test _p_filter( (2,2) ,order) == false
+@test _p_filter( (1,1) ,order) == true
+@test _p_filter( (3,1) ,order) == false
+@test _p_filter( (0,1) ,order) == true
+
+@test _ps_filter( (1,2) ,order) == false
+@test _ps_filter( (2,0) ,order) == true
+@test _ps_filter( (2,2) ,order) == false
+@test _ps_filter( (1,1) ,order) == true
+@test _ps_filter( (3,1) ,order) == false
+
end # module
diff --git a/test/PolynomialsTests/PCurlGradBasesTests.jl b/test/PolynomialsTests/PCurlGradBasesTests.jl
new file mode 100644
index 000000000..07d171796
--- /dev/null
+++ b/test/PolynomialsTests/PCurlGradBasesTests.jl
@@ -0,0 +1,81 @@
+module PCurlGradBasesTests
+
+using Test
+using Gridap.TensorValues
+using Gridap.Fields
+using Gridap.Polynomials
+using Gridap.Arrays
+
+xi = Point(4,2)
+np = 1
+x = fill(xi,np)
+
+PT = Monomial
+
+order = 2
+D = 2
+T = Float64
+V = VectorValue{D,T}
+G = gradient_type(V,xi)
+b = PCurlGradBasis(PT, Val(D),T,order)
+
+v = V[
+ (1.0, 0.0), (4.0, 0.0), (16.0, 0.0), (2.0, 0.0), (8.0, 0.0), (4.0, 0.0), # pterm ex
+ (0.0, 1.0), (0.0, 4.0), (0.0, 16.0), (0.0, 2.0), (0.0, 8.0), (0.0, 4.0), # pterm ey
+ (64.0,32.0), (32.0,16.0), (16.0, 8.0)] # sterms
+
+g = G[
+ (0.0, 0.0, 0.0, 0.0), (1.0, 0.0, 0.0, 0.0), (8.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0), (2.0, 4.0, 0.0, 0.0), (0.0, 4.0, 0.0, 0.0), # pterm ex
+ (0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, 0.0, 8.0, 0.0), (0.0, 0.0, 0.0, 1.0), (0.0, 0.0, 2.0, 4.0), (0.0, 0.0, 0.0, 4.0), # pterm ey
+ (48.0, 0.0, 16.0, 16.0), (16.0, 16.0, 4.0, 16.0), (4.0, 16.0, 0.0, 12.0)] # sterms
+
+vb = evaluate(b,x)
+
+for (vi,vbi) in zip(v,vb)
+ @test vi == vbi
+end
+
+vb = evaluate(b,xi)
+@test vb == v
+
+∇b = Broadcasting(gradient)(b)
+gvb = evaluate(∇b,x)
+for (vi,vbi) in zip(g,gvb)
+ @test vi == vbi
+end
+
+gvb = evaluate(∇b,xi)
+@test gvb == g
+
+@test length(b) == 15
+@test get_order(b) == 3
+
+xi = Point(2,3,5)
+np = 5
+x = fill(xi,np)
+
+order = 1
+D = 3
+T = Float64
+V = VectorValue{D,T}
+G = gradient_type(V,xi)
+b = PCurlGradBasis(PT, Val(D),T,order)
+
+@test length(b) == 15
+@test get_order(b) == 2
+
+
+# 1D
+
+order = 0
+D = 1
+T = Float64
+V = VectorValue{D,T}
+b = PCurlGradBasis(PT,Val(D),T,order)
+
+@test b isa UniformPolyBasis{D,V,order+1,PT}
+
+@test_throws AssertionError PCurlGradBasis(PT,Val(D),V,order)
+
+
+end # module
diff --git a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl b/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl
deleted file mode 100644
index f11e9f6c7..000000000
--- a/test/PolynomialsTests/PCurlGradMonomialBasesTests.jl
+++ /dev/null
@@ -1,67 +0,0 @@
-module PCurlGradMonomialBasesTests
-
-using Test
-using Gridap.TensorValues
-using Gridap.Fields
-using Gridap.Polynomials
-using Gridap.Arrays
-
-xi = Point(4,2)
-np = 1
-x = fill(xi,np)
-
-order = 2
-D = 2
-T = Float64
-V = VectorValue{D,T}
-G = gradient_type(V,xi)
-b = PCurlGradMonomialBasis{D}(T,order)
-
-v = V[
- (1.0, 0.0), (0.0, 1.0), (4.0, 0.0), (0.0, 2.0), (16.0, 0.0), (0.0, 4.0),
- (2.0, 0.0), (0.0, 4.0), (8.0, 0.0), (0.0, 8.0), (4.0, 0.0), (0.0, 16.0),
- (64.0, 32.0), (32.0, 16.0), (16.0, 8.0)]
-
-g = G[
- (0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0), (1.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 1.0), (8.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 4.0),
- (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (2.0, 4.0, 0.0, 0.0),
- (0.0, 0.0, 2.0, 4.0), (0.0, 4.0, 0.0, 0.0), (0.0, 0.0, 8.0, 0.0),
- (48.0, 0.0, 16.0, 16.0), (16.0, 16.0, 4.0, 16.0), (4.0, 16.0, 0.0, 12.0)]
-
- vb = evaluate(b,x)
-
- for (vi,vbi) in zip(v,vb)
- @test vi == vbi
- end
-
- vb = evaluate(b,xi)
- @test vb == v
-
- ∇b = Broadcasting(gradient)(b)
- gvb = evaluate(∇b,x)
- for (vi,vbi) in zip(g,gvb)
- @test vi == vbi
- end
-
- gvb = evaluate(∇b,xi)
- @test gvb == g
-
- @test num_terms(b) == 15
- @test get_order(b) == 2
-
- xi = Point(2,3,5)
- np = 5
- x = fill(xi,np)
-
- order = 1
- D = 3
- T = Float64
- V = VectorValue{D,T}
- G = gradient_type(V,xi)
- b = PCurlGradMonomialBasis{D}(T,order)
-
- @test num_terms(b) == 15
- @test get_order(b) == 1
-
-end # module
diff --git a/test/PolynomialsTests/PGradBasesTests.jl b/test/PolynomialsTests/PGradBasesTests.jl
new file mode 100644
index 000000000..01138775d
--- /dev/null
+++ b/test/PolynomialsTests/PGradBasesTests.jl
@@ -0,0 +1,72 @@
+module PGradBasesTests
+
+using Test
+using Gridap.TensorValues
+using Gridap.Fields
+using Gridap.Arrays
+using Gridap.Polynomials
+
+xi = Point(2.,3.,5.)
+np = 3
+x = fill(xi,np)
+T = Float64
+
+D = 2
+for PT in (Legendre, Chebyshev, ModalC0, Bernstein)
+ @test_throws ErrorException PGradBasis(PT,Val(D),T,0)
+end
+
+PT = Monomial
+
+order = 0
+D = 3
+b = NedelecPolyBasisOnSimplex{D}(PT, T, order)
+
+V = VectorValue{D, Float64}
+v = V[(1,0,0),(0,1,0),(0,0,1),(-3,2,0),(-5,0,2),(0,-5,3)]
+
+G = gradient_type(V,xi)
+g = G[
+ (0,0,0, 0,0,0, 0,0,0), (0,0,0, 0,0,0, 0,0,0), (0,0,0, 0,0,0, 0,0,0),
+ (0,-1,0, 1,0,0, 0,0,0),(0,0,-1, 0,0,0, 1,0,0),(0,0,0, 0,0,-1, 0,1,0)]
+
+bx = repeat(permutedims(v),np)
+∇bx = repeat(permutedims(g),np)
+test_field_array(b,x,bx,grad=∇bx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
+
+xi = Point(2.,3.)
+np = 4
+x = fill(xi,np)
+
+order = 0
+D = 2
+b = NedelecPolyBasisOnSimplex{D}(PT, T, order)
+V = VectorValue{D, Float64}
+v = V[(1,0),(0,1),(-3,2)]
+G = gradient_type(V,xi)
+g = G[(0,0, 0,0), (0,0, 0,0), (0,-1, 1,0)]
+bx = repeat(permutedims(v),np)
+∇bx = repeat(permutedims(g),np)
+test_field_array(b,x,bx,grad=∇bx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
+
+
+# 1D
+
+order = 0
+D = 1
+T = Float64
+V = VectorValue{D,T}
+b = PGradBasis(PT,Val(D),T,order)
+
+@test b isa UniformPolyBasis{D,V,order+1,PT}
+
+@test_throws AssertionError PGradBasis(PT,Val(D),V,order)
+
+# D > 3 not implemented
+
+@test_throws ErrorException NedelecPolyBasisOnSimplex{4}(PT, T, order)
+
+
+end # module
diff --git a/test/PolynomialsTests/PolynomialInterfacesTests.jl b/test/PolynomialsTests/PolynomialInterfacesTests.jl
new file mode 100644
index 000000000..7d4660761
--- /dev/null
+++ b/test/PolynomialsTests/PolynomialInterfacesTests.jl
@@ -0,0 +1,81 @@
+module ChangeBasisTests
+
+using Test
+using Gridap.Fields
+using Gridap.Polynomials
+using StaticArrays
+
+xi = Point(0.)
+np = 5
+x = fill(xi,np)
+
+######################
+# Polynomial interface
+######################
+
+struct MockPolynomial <: Polynomial end
+@test_throws ErrorException isHierarchical(Polynomial)
+
+# Interfaces to implement
+@test_throws ErrorException isHierarchical(MockPolynomial)
+
+D = 1
+K = 0
+c = zero(MMatrix{D,K+1})
+
+@test_throws ErrorException Polynomials._evaluate_1d!(MockPolynomial, Val(1), c, xi, 1)
+@test_throws ErrorException Polynomials._gradient_1d!(MockPolynomial, Val(1), c, xi, 1)
+@test_throws ErrorException Polynomials._hessian_1d!( MockPolynomial, Val(1), c, xi, 1)
+@test_throws ErrorException Polynomials._derivatives_1d!(MockPolynomial, Val(1), (nothing,nothing,nothing,nothing), xi, 1)
+
+function Polynomials._evaluate_1d!(
+ ::Type{MockPolynomial},::Val{K}, cc::AbstractMatrix{T}, xi, d) where {K,T<:Number}
+
+ cc[1,1] = 1.
+end
+Polynomials._derivatives_1d!(MockPolynomial, Val(1), (c,), xi, 1)
+@test c[1][1] == 1.
+
+###########################
+# PolynomialBasis interface
+###########################
+
+T = Float64
+D = 1
+struct MockPolyBasis <: PolynomialBasis{D,T,0,MockPolynomial} end
+
+mb = MockPolyBasis()
+
+
+# Implemented interfaces
+@test IndexStyle(mb) == IndexLinear()
+@test return_type(mb) == T
+@test get_order(mb) == 0
+@test mb[1] == MockPolynomial()
+
+
+# Interfaces to implement
+@test_throws ErrorException size(mb)
+import Base.size
+Base.size(::MockPolyBasis) = (1,)
+@test length(mb) == 1
+
+
+r, c = return_cache(mb,x)
+
+@test_throws ErrorException Polynomials._evaluate_nd!(mb, xi, r, 1, c)
+
+∇mb = FieldGradientArray{1}(mb)
+r, c, g = return_cache(∇mb,x)
+s = MVector{D,T}(0.)
+
+@test_throws ErrorException Polynomials._gradient_nd!(mb, xi, r, 1, c, g, s)
+
+Hmb = FieldGradientArray{2}(mb)
+r, c, g, h = return_cache(Hmb,x)
+s = MMatrix{D,D,T}(0.)
+
+@test_throws ErrorException Polynomials._hessian_nd!(mb, xi, r, 1, c, g, h, s)
+
+
+end
diff --git a/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl b/test/PolynomialsTests/QCurlGradBasesTests.jl
similarity index 52%
rename from test/PolynomialsTests/QCurlGradMonomialBasesTests.jl
rename to test/PolynomialsTests/QCurlGradBasesTests.jl
index a2c31a787..30a7d3207 100644
--- a/test/PolynomialsTests/QCurlGradMonomialBasesTests.jl
+++ b/test/PolynomialsTests/QCurlGradBasesTests.jl
@@ -1,4 +1,4 @@
-module QCurlGradMonomialBasesTests
+module QCurlGradBasesTests
using Test
using Gridap.TensorValues
@@ -9,15 +9,19 @@ xi = Point(2,3)
np = 5
x = fill(xi,np)
+PT = Monomial
+
order = 0
D = 2
T = Float64
V = VectorValue{D,T}
G = gradient_type(V,xi)
-b = QCurlGradMonomialBasis{D}(T,order)
+b = QCurlGradBasis(PT,Val(D),T,order)
+
+@test length(b) == 4
+@test get_order(b) == 1
-@test num_terms(b) == 4
-@test get_order(b) == 0
+@test_throws AssertionError QCurlGradBasis(PT,Val(D),V,order)
xi = Point(2,3,5)
np = 5
@@ -28,18 +32,19 @@ D = 3
T = Float64
V = VectorValue{D,T}
G = gradient_type(V,xi)
-b = QCurlGradMonomialBasis{D}(T,order)
+b = QCurlGradBasis(PT, Val(D),T,order)
v = V[
- (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0),
- (2.0, 0.0, 0.0), (0.0, 3.0, 0.0), (0.0, 0.0, 5.0)]
+ (1.0, 0.0, 0.0), (2.0, 0.0, 0.0),
+ (0.0, 1.0, 0.0), (0.0, 3.0, 0.0),
+ (0.0, 0.0, 1.0), (0.0, 0.0, 5.0)]
g = G[
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0),
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)]
bx = repeat(permutedims(v),np)
@@ -56,24 +61,42 @@ D = 2
T = Float64
V = VectorValue{D,T}
G = gradient_type(V,xi)
-b = QCurlGradMonomialBasis{D}(T,order)
+b = QCurlGradBasis(PT, Val(D),T,order)
v = V[
- (1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 3.0),
- (4.0, 0.0), (0.0, 9.0), (3.0, 0.0), (0.0, 2.0),
- (6.0, 0.0), (0.0, 6.0), (12.0, 0.0), (0.0, 18.0)]
+ ( 1., 0. ), ( 2., 0. ), ( 4., 0. ), ( 3., 0. ), ( 6., 0. ), (12., 0. ),
+ ( 0., 1. ), ( 0., 2. ), ( 0., 3. ), ( 0., 6. ), ( 0., 9. ), ( 0., 18.)]
g = G[
- (0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0),
- (1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0),
- (4.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 6.0),
- (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0),
- (3.0, 2.0, 0.0, 0.0), (0.0, 0.0, 3.0, 2.0),
- (12.0, 4.0, 0.0, 0.0),(0.0, 0.0, 9.0, 12.0)]
+ ( 0., 0., 0., 0.),
+ ( 1., 0., 0., 0.),
+ ( 4., 0., 0., 0.),
+ ( 0., 1., 0., 0.),
+ ( 3., 2., 0., 0.),
+ (12., 4., 0., 0.),
+ ( 0., 0., 0., 0.),
+ ( 0., 0., 1., 0.),
+ ( 0., 0., 0., 1.),
+ ( 0., 0., 3., 2.),
+ ( 0., 0., 0., 6.),
+ ( 0., 0., 9.,12.)]
bx = repeat(permutedims(v),np)
∇bx = repeat(permutedims(g),np)
test_field_array(b,x,bx,grad=∇bx)
test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
+# 1D
+
+order = 0
+D = 1
+T = Float64
+V = VectorValue{D,T}
+b = QCurlGradBasis(PT,Val(D),T,order)
+
+@test b isa UniformPolyBasis{D,V,order+1,PT}
+
+@test_throws AssertionError QCurlGradBasis(PT,Val(D),V,order)
+
+
end # module
diff --git a/test/PolynomialsTests/QGradBasesTests.jl b/test/PolynomialsTests/QGradBasesTests.jl
new file mode 100644
index 000000000..1a7d0efd1
--- /dev/null
+++ b/test/PolynomialsTests/QGradBasesTests.jl
@@ -0,0 +1,171 @@
+module QGradMonomialBasesTests
+
+using Test
+using Gridap.TensorValues
+using Gridap.Fields
+using Gridap.Arrays
+using Gridap.Polynomials
+
+xi = Point(2,3)
+np = 5
+x = fill(xi,np)
+PT = Monomial
+
+# 3D
+order = 0
+D = 2
+T = Float64
+V = VectorValue{D,T}
+G = gradient_type(V,xi)
+b = QGradBasis(PT, Val(D),T,order)
+
+@test length(b) == 4
+@test get_order(b) == 1
+
+@test_throws AssertionError QGradBasis(PT,Val(D),V,order)
+
+xi = Point(2,3,5)
+np = 5
+x = fill(xi,np)
+
+order = 0
+D = 3
+T = Float64
+V = VectorValue{D,T}
+G = gradient_type(V,xi)
+H = gradient_type(G,xi)
+b = QGradBasis(PT, Val(D),T,order)
+
+v = V[
+# (1.0, 0.0, 0.0), ( y , 0.0, 0.0), ( z , 0.0, 0.0), ( yz , 0.0, 0.0),
+# (0.0, 1.0, 0.0), (0.0, x , 0.0), (0.0, z , 0.0), (0.0, xz , 0.0),
+# (0.0, 0.0, 1.0), (0.0, 0.0, x ), (0.0, 0.0, y ), (0.0, 0.0, xy)]
+ (1.0, 0.0, 0.0), (3.0, 0.0, 0.0), (5.0, 0.0, 0.0), (15.0, 0.0, 0.0),
+ (0.0, 1.0, 0.0), (0.0, 2.0, 0.0), (0.0, 5.0, 0.0), (0.0, 10.0, 0.0),
+ (0.0, 0.0, 1.0), (0.0, 0.0, 2.0), (0.0, 0.0, 3.0), (0.0, 0.0, 6.0)]
+
+g = G[
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 1 , 0 , 0 )
+ (0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( y , 0 , 0 )
+ (0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( z , 0 , 0 )
+ (0.0, 5.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( yz , 0 , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 1 , 0 )
+ (0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , x , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0), # ( 0 , z , 0 )
+ (0.0, 0.0, 0.0, 5.0, 0.0, 2.0, 0.0, 0.0, 0.0), # ( 0 , xz, 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 0 , 1 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0), # ( 0 , 0 , x )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0), # ( 0 , 0 , y )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 2.0, 0.0)] # ( 0 , 0 , xy )
+
+h = H[
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 1 , 0 , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( y , 0 , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( z , 0 , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 1. , 0.0, 1. , 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( yz , 0 , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 1 , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , x , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , z , 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1. , 0.0, 0.0, 0.0, 1. , 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , xz, 0 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 0 , 1 )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 0 , x )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), # ( 0 , 0 , y )
+ (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1. , 0.0, 1. , 0.0, 0.0, 0.0, 0.0, 0.0)] # ( 0 , 0 , xy )
+
+bx = repeat(permutedims(v),np)
+∇bx = repeat(permutedims(g),np)
+Hbx = repeat(permutedims(h),np)
+
+evaluate(b,x)
+evaluate(Broadcasting(∇)(b),x)
+evaluate(Broadcasting(∇∇)(b),x)
+
+test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
+
+# 2D
+
+xi = Point(2,3)
+np = 5
+x = fill(xi,np)
+
+order = 1
+D = 2
+T = Float64
+V = VectorValue{D,T}
+G = gradient_type(V,xi)
+H = gradient_type(G,xi)
+b = QGradBasis(PT, Val(D),T,order)
+
+v = V[
+# (1., 0.), (x , 0.), (y , 0.), (xy, 0.), (y², 0.), (xy², 0.),
+# (0., 1.), (0., x ), (0., x²), (0., y ), (0., xy), (0., x²y)]
+ (1., 0.), (2., 0.), (3., 0.), (6., 0.), (9., 0.), (18., 0.),
+ (0., 1.), (0., 2.), (0., 4.), (0., 3.), (0., 6.), (0., 12.)]
+
+g = G[
+ # # ∂xV₁ ∂yV₁ ∂ₓV₂ ∂xV₂ # (V₁, V₂ )
+ (0., 0., 0., 0.), # (0 , 0 , 0 , 0 ) # (1 , 0 )
+ (1., 0., 0., 0.), # (1 , 0 , 0 , 0 ) # (x , 0 )
+ (0., 1., 0., 0.), # (0 , 1 , 0 , 0 ) # (y , 0 )
+ (3., 2., 0., 0.), # (y , x, 0 , 0 ) # (xy, 0 )
+ (0., 6., 0., 0.), # (0., 2y, 0 , 0 ) # (y², 0 )
+ (9., 12., 0., 0.), # (y², 2xy, 0 , 0 ) # (xy²,0 )
+ (0., 0., 0., 0.), # (0 , 0 , 0 , 0 ) # (0 , 1 )
+ (0., 0., 1., 0.), # (0 , 0 , 1 , 0 ) # (0 , x )
+ (0., 0., 4., 0.), # (0 , 0 , 2x, 0 ) # (0 , x² )
+ (0., 0., 0., 1.), # (0 , 0 , 0 , 1 ) # (0 , y )
+ (0., 0., 3., 2.), # (0 , 0 , y, x ) # (0 , xy )
+ (0., 0., 12., 4.)] # (0 , 0 , 2xy, x²) # (0 , x²y)
+
+h = H[
+ # ∂xxV₁ ∂yxV₁ ∂xyV₁ ∂yyV₁ ∂xxV₂ ∂yxV₂ ∂xxV₂ ∂yxV₂ # (V₁, V₂ )
+ (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (1., 0. ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )
+ (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (x , 0. ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )
+ (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (y , 0. ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )
+ (0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ), # (xy, 0. ) # (0 , 1 , 1, 0, 0 , 0 , 0 , 0 )
+ (0 , 0., 0 , 2 , 0 , 0 , 0 , 0 ), # (y², 0. ) # (0 , 0., 0, 2, 0 , 0 , 0 , 0 )
+ (0 , 6., 6., 4., 0 , 0 , 0 , 0 ), # (xy², 0.) # (0 , 2y, 2y, 2x, 0 , 0 , 0 , 0 )
+ (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (0., 1. ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )
+ (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (0., x ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )
+ (0 , 0 , 0 , 0 , 2 , 0 , 0 , 0 ), # (0., x² ) # (0 , 0 , 0 , 0 , 2 , 0 , 0 , 0 )
+ (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), # (0., y ) # (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )
+ (0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 ), # (0., xy ) # (0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 )
+ (0 , 0 , 0 , 0 , 6., 4., 4., 0 )] # (0., x²y) # (0 , 0 , 0 , 0 , 2y, 2x, 2x, 0 )
+
+bx = repeat(permutedims(v),np)
+∇bx = repeat(permutedims(g),np)
+Hbx = repeat(permutedims(h),np)
+test_field_array(b,x,bx,grad=∇bx,gradgrad=Hbx)
+test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:],gradgrad=Hbx[1,:])
+
+
+# 1D
+
+order = 0
+D = 1
+T = Float64
+V = VectorValue{D,T}
+b = QGradBasis(PT,Val(D),T,order)
+
+@test b isa UniformPolyBasis{D,V,order+1,PT}
+
+@test_throws AssertionError QGradBasis(PT,Val(D),V,order)
+
+# Misc
+
+# Derivatives not implemented for symetric tensor types
+
+T = Float64
+V = SymTensorValue{D,T}
+G = gradient_type(V,xi)
+r = zeros(G, (1,1))
+@test_throws ErrorException Polynomials._comp_wize_set_derivative!(r,0,0,0,V)
+
+T = Float64
+V = SymTracelessTensorValue{D,T}
+G = gradient_type(V,xi)
+r = zeros(G, (1,1))
+@test_throws ErrorException Polynomials._comp_wize_set_derivative!(r,0,0,0,V)
+
+end # module
diff --git a/test/PolynomialsTests/QGradMonomialBasesTests.jl b/test/PolynomialsTests/QGradMonomialBasesTests.jl
deleted file mode 100644
index 90f4cbdd1..000000000
--- a/test/PolynomialsTests/QGradMonomialBasesTests.jl
+++ /dev/null
@@ -1,125 +0,0 @@
-module QGradMonomialBasesTests
-
-using Test
-using Gridap.TensorValues
-using Gridap.Fields
-using Gridap.Arrays
-using Gridap.Polynomials
-
-xi = Point(2,3)
-np = 5
-x = fill(xi,np)
-
-order = 0
-D = 2
-T = Float64
-V = VectorValue{D,T}
-G = gradient_type(V,xi)
-b = QGradMonomialBasis{D}(T,order)
-
-@test num_terms(b) == 4
-@test b.order == 0
-@test get_order(b) == 0
-
-xi = Point(2,3,5)
-np = 5
-x = fill(xi,np)
-
-order = 0
-D = 3
-T = Float64
-V = VectorValue{D,T}
-G = gradient_type(V,xi)
-b = QGradMonomialBasis{D}(T,order)
-
-v = V[
- (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0),
- (3.0, 0.0, 0.0), (0.0, 5.0, 0.0), (0.0, 0.0, 2.0),
- (5.0, 0.0, 0.0), (0.0, 2.0, 0.0), (0.0, 0.0, 3.0),
- (15.0, 0.0, 0.0), (0.0, 10.0, 0.0), (0.0, 0.0, 6.0)]
-
-g = G[
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0),
- (0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0),
- (0.0, 5.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 5.0, 0.0, 2.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 2.0, 0.0)]
-
-bx = repeat(permutedims(v),np)
-∇bx = repeat(permutedims(g),np)
-test_field_array(b,x,bx,grad=∇bx)
-test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
-
-xi = Point(2,3)
-np = 5
-x = fill(xi,np)
-
-order = 1
-D = 2
-T = Float64
-V = VectorValue{D,T}
-G = gradient_type(V,xi)
-b = QGradMonomialBasis{D}(T,order)
-
-v = V[
- (1.0, 0.0), (0.0, 1.0), (2.0, 0.0), (0.0, 3.0),
- (3.0, 0.0), (0.0, 2.0), (6.0, 0.0), (0.0, 6.0),
- (9.0, 0.0), (0.0, 4.0), (18.0, 0.0), (0.0, 12.0)]
-
-g = G[
- (0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0), (1.0, 0.0, 0.0, 0.0),
- (0.0, 0.0, 0.0, 1.0), (0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0),
- (3.0, 2.0, 0.0, 0.0), (0.0, 0.0, 3.0, 2.0), (0.0, 6.0, 0.0, 0.0),
- (0.0, 0.0, 4.0, 0.0), (9.0, 12.0, 0.0, 0.0), (0.0, 0.0, 12.0, 4.0)]
-
-bx = repeat(permutedims(v),np)
-∇bx = repeat(permutedims(g),np)
-test_field_array(b,x,bx,grad=∇bx)
-test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
-
-xi = Point(2.,3.,5.)
-np = 3
-x = fill(xi,np)
-
-order = 0
-D = 3
-b = Polynomials.NedelecPrebasisOnSimplex{D}(order)
-
-V = VectorValue{D, Float64}
-v = V[(1,0,0),(0,1,0),(0,0,1),(-3,2,0),(-5,0,2),(0,-5,3)]
-
-G = gradient_type(V,xi)
-g = G[
- (0,0,0, 0,0,0, 0,0,0), (0,0,0, 0,0,0, 0,0,0), (0,0,0, 0,0,0, 0,0,0),
- (0,-1,0, 1,0,0, 0,0,0),(0,0,-1, 0,0,0, 1,0,0),(0,0,0, 0,0,-1, 0,1,0)]
-
-bx = repeat(permutedims(v),np)
-∇bx = repeat(permutedims(g),np)
-test_field_array(b,x,bx,grad=∇bx)
-test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
-
-xi = Point(2.,3.)
-np = 4
-x = fill(xi,np)
-
-order = 0
-D = 2
-b = Polynomials.NedelecPrebasisOnSimplex{D}(order)
-V = VectorValue{D, Float64}
-v = V[(1,0),(0,1),(-3,2)]
-G = gradient_type(V,xi)
-g = G[(0,0, 0,0), (0,0, 0,0), (0,-1, 1,0)]
-bx = repeat(permutedims(v),np)
-∇bx = repeat(permutedims(g),np)
-test_field_array(b,x,bx,grad=∇bx)
-test_field_array(b,x[1],bx[1,:],grad=∇bx[1,:])
-
-
-end # module
diff --git a/test/PolynomialsTests/runtests.jl b/test/PolynomialsTests/runtests.jl
index 813a48417..2c25b3ae4 100644
--- a/test/PolynomialsTests/runtests.jl
+++ b/test/PolynomialsTests/runtests.jl
@@ -2,17 +2,25 @@ module PolynomialsTests
using Test
+@testset "PolynomialInterfaces" begin include("PolynomialInterfacesTests.jl") end
+
@testset "MonomialBases" begin include("MonomialBasesTests.jl") end
-@testset "QGradMonomialBases" begin include("QGradMonomialBasesTests.jl") end
+@testset "QGradBases" begin include("QGradBasesTests.jl") end
+
+@testset "QCurlGradBases" begin include("QCurlGradBasesTests.jl") end
-@testset "QCurlGradMonomialBases" begin include("QCurlGradMonomialBasesTests.jl") end
+@testset "PGradBases" begin include("PGradBasesTests.jl") end
-@testset "PCurlGradMonomialBases" begin include("PCurlGradMonomialBasesTests.jl") end
+@testset "PCurlGradBases" begin include("PCurlGradBasesTests.jl") end
@testset "ModalC0Bases" begin include("ModalC0BasesTests.jl") end
-@testset "JacobiPolynomialBases" begin include("JacobiPolynomialBasesTests.jl") end
+@testset "LegendreBases" begin include("LegendreBasesTests.jl") end
+
+@testset "ChebyshevBases" begin include("ChebyshevBasesTests.jl") end
+
+@testset "BernsteinBases" begin include("BernsteinBasesTests.jl") end
#@testset "ChangeBasis" begin include("ChangeBasisTests.jl") end
diff --git a/test/ReferenceFEsTests/BezierRefFEsTests.jl b/test/ReferenceFEsTests/BezierRefFEsTests.jl
index 483cdfad8..c92052858 100644
--- a/test/ReferenceFEsTests/BezierRefFEsTests.jl
+++ b/test/ReferenceFEsTests/BezierRefFEsTests.jl
@@ -20,7 +20,7 @@ p_filter(e,o) = sum(e) ≤ o
# 1D
p = 2
-prebasis_seg = MonomialBasis{1}(Float64,p,p_filter)
+prebasis_seg = MonomialBasis(Val(1),Float64,p,p_filter)
C = _berstein_matrix(prebasis_seg,SEGMENT)
C12 =
[
@@ -41,7 +41,7 @@ Xi = lazy_map( evaluate, Ψ, ξ )
@test Xi == [ Point(0.0,0.0), Point(0.5,0.25), Point(1.0,0.0) ]
p = 3
-prebasis_seg = MonomialBasis{1}(Float64,p,p_filter)
+prebasis_seg = MonomialBasis(Val(1),Float64,p,p_filter)
C = _berstein_matrix(prebasis_seg,SEGMENT)
C13 =
[
@@ -65,7 +65,7 @@ Xi = lazy_map( evaluate, Ψ, ξ )
# 2D
p = 2
-prebasis_tri = MonomialBasis{2}(Float64,p,p_filter)
+prebasis_tri = MonomialBasis(Val(2),Float64,p,p_filter)
C = _berstein_matrix(prebasis_tri,TRI)
C22 =
[
@@ -89,7 +89,7 @@ Xi = lazy_map( evaluate, Ψ, ξ )
@test Xi == ξ
p = 3
-prebasis_tri = MonomialBasis{2}(Float64,p,p_filter)
+prebasis_tri = MonomialBasis(Val(2),Float64,p,p_filter)
C = _berstein_matrix(prebasis_tri,TRI)
C23 =
[
diff --git a/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl b/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl
index f2c6204f0..4dc8dee38 100644
--- a/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl
+++ b/test/ReferenceFEsTests/CLagrangianRefFEsTests.jl
@@ -8,23 +8,25 @@ using Gridap.Polynomials
using Gridap.ReferenceFEs
using JSON
+using Gridap.ReferenceFEs: monomial_basis
+
orders = (2,3)
-b = MonomialBasis(Float64,QUAD,orders)
+b = monomial_basis(Float64,QUAD,orders)
r = [(0,0), (1,0), (2,0), (0,1), (1,1), (2,1), (0,2), (1,2), (2,2), (0,3), (1,3), (2,3)]
@test get_exponents(b) == r
orders = (1,1,2)
-b = MonomialBasis(Float64,WEDGE,orders)
+b = monomial_basis(Float64,WEDGE,orders)
r = [(0,0,0), (1,0,0), (0,1,0), (0,0,1), (1,0,1), (0,1,1), (0,0,2), (1,0,2), (0,1,2)]
@test get_exponents(b) == r
orders = (1,1,1)
-b = MonomialBasis(Float64,PYRAMID,orders)
+b = monomial_basis(Float64,PYRAMID,orders)
r = [(0,0,0), (1,0,0), (0,1,0), (1,1, 0), (0,0,1)]
@test get_exponents(b) == r
orders = (1,1,1)
-b = MonomialBasis(Float64,TET,orders)
+b = monomial_basis(Float64,TET,orders)
r = [(0,0,0), (1,0,0), (0,1,0), (0,0,1)]
@test get_exponents(b) == r
@@ -71,7 +73,7 @@ dofs = LagrangianDofBasis(SymTensorValue{2,Int},VERTEX,())
dofs = LagrangianDofBasis(SymTracelessTensorValue{2,Int},VERTEX,())
@test dofs.node_and_comp_to_dof == SymTracelessTensorValue{2,Int}[(1,2)]
-b = MonomialBasis(VectorValue{2,Int},VERTEX,())
+b = monomial_basis(VectorValue{2,Int},VERTEX,())
@test length(b) == 2
@test evaluate(b,Point{0,Int}[(),()]) == VectorValue{2,Int}[(1, 0) (0, 1); (1, 0) (0, 1)]
diff --git a/test/ReferenceFEsTests/LagrangianRefFEsTests.jl b/test/ReferenceFEsTests/LagrangianRefFEsTests.jl
index 98b4d42f3..75ba8a86a 100644
--- a/test/ReferenceFEsTests/LagrangianRefFEsTests.jl
+++ b/test/ReferenceFEsTests/LagrangianRefFEsTests.jl
@@ -8,7 +8,7 @@ using Gridap.Fields
D = 2
T = Float64
order = 1
-prebasis = MonomialBasis{D}(T,order)
+prebasis = MonomialBasis(Val(D),T,order)
polytope = QUAD
x = get_vertex_coordinates(polytope)
diff --git a/test/ReferenceFEsTests/NedelecRefFEsTests.jl b/test/ReferenceFEsTests/NedelecRefFEsTests.jl
index 1e0db175c..99ccb68e9 100644
--- a/test/ReferenceFEsTests/NedelecRefFEsTests.jl
+++ b/test/ReferenceFEsTests/NedelecRefFEsTests.jl
@@ -15,8 +15,8 @@ order = 0
reffe = NedelecRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 4
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 4
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 4
@test Conformity(reffe) == CurlConformity()
@@ -28,9 +28,9 @@ order = 1
reffe = NedelecRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 12
+@test length(get_prebasis(reffe)) == 12
@test num_dofs(reffe) == 12
-@test get_order(get_prebasis(reffe)) == 1
+@test get_order(get_prebasis(reffe)) == 2
prebasis = get_prebasis(reffe)
dof_basis = get_dof_basis(reffe)
@@ -53,8 +53,8 @@ order = 0
reffe = NedelecRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 6
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 6
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 6
@test Conformity(reffe) == CurlConformity()
@@ -94,8 +94,8 @@ order = 0
reffe = NedelecRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 3
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 3
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 3
@test Conformity(reffe) == CurlConformity()
dof_basis = get_dof_basis(reffe)
@@ -114,14 +114,14 @@ dof_basis = get_dof_basis(reffe)
# Factory function
reffe = ReferenceFE(QUAD,nedelec,0)
-@test num_terms(get_prebasis(reffe)) == 4
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 4
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 4
@test Conformity(reffe) == CurlConformity()
reffe = ReferenceFE(QUAD,nedelec,Float64,0)
-@test num_terms(get_prebasis(reffe)) == 4
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 4
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 4
@test Conformity(reffe) == CurlConformity()
@@ -137,31 +137,31 @@ et = Float64
order = 1
reffe = NedelecRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 20
-@test get_order(get_prebasis(reffe)) == 1
+@test length(get_prebasis(reffe)) == 20
+@test get_order(get_prebasis(reffe)) == 2
@test num_dofs(reffe) == 20
@test Conformity(reffe) == CurlConformity()
dof_basis = get_dof_basis(reffe)
face_odofs_L2 = get_face_own_dofs(reffe,L2Conformity())
-@test face_odofs_L2 == [Int64[], Int64[], Int64[], Int64[],
- Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[],
+@test face_odofs_L2 == [Int64[], Int64[], Int64[], Int64[],
+ Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[], Int64[],
collect(1:20)]
face_odofs = get_face_own_dofs(reffe)
face_cdofs = get_face_dofs(reffe)
-@test face_odofs == [Int64[], Int64[], Int64[], Int64[],
- [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14], [15, 16], [17, 18], [19, 20],
+@test face_odofs == [Int64[], Int64[], Int64[], Int64[],
+ [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14], [15, 16], [17, 18], [19, 20],
Int64[]]
-@test face_cdofs == [Int64[], Int64[], Int64[], Int64[],
- [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12],
- [1, 2, 3, 4, 5, 6, 13, 14], [1, 2, 7, 8, 9, 10, 15, 16], [3, 4, 7, 8, 11, 12, 17, 18], [5, 6, 9, 10, 11, 12, 19, 20],
+@test face_cdofs == [Int64[], Int64[], Int64[], Int64[],
+ [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12],
+ [1, 2, 3, 4, 5, 6, 13, 14], [1, 2, 7, 8, 9, 10, 15, 16], [3, 4, 7, 8, 11, 12, 17, 18], [5, 6, 9, 10, 11, 12, 19, 20],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]]
-
+
#display(face_odofs)
using Gridap.Geometry
diff --git a/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl b/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl
index 1bd40ca7b..92ea581c9 100644
--- a/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl
+++ b/test/ReferenceFEsTests/RaviartThomasRefFEsTests.jl
@@ -21,10 +21,12 @@ order = 0
reffe = RaviartThomasRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 4
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 4
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 4
@test Conformity(reffe) == DivConformity()
+
+
p = QUAD
D = num_dims(QUAD)
et = Float64
@@ -32,9 +34,9 @@ order = 1
reffe = RaviartThomasRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 12
+@test length(get_prebasis(reffe)) == 12
@test num_dofs(reffe) == 12
-@test get_order(get_prebasis(reffe)) == 1
+@test get_order(get_prebasis(reffe)) == 2
prebasis = get_prebasis(reffe)
dof_basis = get_dof_basis(reffe)
@@ -78,11 +80,12 @@ order = 0
reffe = RaviartThomasRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 4
+@test length(get_prebasis(reffe)) == 4
@test num_dofs(reffe) == 4
-@test get_order(get_prebasis(reffe)) == 0
+@test get_order(get_prebasis(reffe)) == 1
@test Conformity(reffe) == DivConformity()
+
p = TET
D = num_dims(p)
et = Float64
@@ -90,9 +93,9 @@ order = 2
reffe = RaviartThomasRefFE(et,p,order)
test_reference_fe(reffe)
-@test num_terms(get_prebasis(reffe)) == 36
+@test length(get_prebasis(reffe)) == 36
@test num_dofs(reffe) == 36
-@test get_order(get_prebasis(reffe)) == 2
+@test get_order(get_prebasis(reffe)) == 3
@test Conformity(reffe) == DivConformity()
prebasis = get_prebasis(reffe)
@@ -111,14 +114,14 @@ test_dof_array(dof_basis,prebasis,r)
# Factory function
reffe = ReferenceFE(QUAD,raviart_thomas,0)
-@test num_terms(get_prebasis(reffe)) == 4
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 4
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 4
@test Conformity(reffe) == DivConformity()
reffe = ReferenceFE(QUAD,raviart_thomas,Float64,0)
-@test num_terms(get_prebasis(reffe)) == 4
-@test get_order(get_prebasis(reffe)) == 0
+@test length(get_prebasis(reffe)) == 4
+@test get_order(get_prebasis(reffe)) == 1
@test num_dofs(reffe) == 4
@test Conformity(reffe) == DivConformity()
diff --git a/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl b/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl
index 6830d6835..c1aae9fc3 100644
--- a/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl
+++ b/test/ReferenceFEsTests/ReferenceFEInterfacesTests.jl
@@ -8,7 +8,7 @@ using Gridap.ReferenceFEs
D = 2
T = Float64
order = 1
-prebasis = MonomialBasis{D}(T,order)
+prebasis = MonomialBasis(Val(D),T,order)
polytope = QUAD
x = get_vertex_coordinates(polytope)
diff --git a/test/TensorValuesTests/TypesTests.jl b/test/TensorValuesTests/TypesTests.jl
index bb1dfd073..44845c697 100644
--- a/test/TensorValuesTests/TypesTests.jl
+++ b/test/TensorValuesTests/TypesTests.jl
@@ -117,7 +117,7 @@ a = [11.0 21.0; NaN 22.0]
@test convert(SymTensorValue{2,Float64},a) == SymTensorValue{2,Float64,3}(11.0, 21.0, 22.0)
# Constructors (SymTracelessTensorValue)
-q_none = SymTracelessTensorValue{0, Int64, 0}()
+q_none = SymTracelessTensorValue{0, Int, 0}()
q = SymTracelessTensorValue()
@test q == q_none
q = SymTracelessTensorValue{0}()
@@ -681,13 +681,13 @@ a = SymTensorValue(11,21,22)
@test change_eltype(SymTensorValue{2,Float64},Int) == SymTensorValue{2,Int}
@test isa(Tuple(a),Tuple)
@test Tuple(a) == a.data
-b = Matrix{Int64}(undef,2,2)
+b = Matrix{Int}(undef,2,2)
b[1,1] = a[1,1]
b[1,2] = a[1,2]
b[2,1] = a[2,1]
b[2,2] = a[2,2]
a = SymTensorValue(11,21,22)
-bt = SymTensorValue{2,Int64}(b)
+bt = SymTensorValue{2,Int}(b)
@test all(bt .== a)
a = SymTracelessTensorValue(11,21)
@@ -695,12 +695,12 @@ a = SymTracelessTensorValue(11,21)
@test change_eltype(SymTracelessTensorValue{2,Float64},Int) == SymTracelessTensorValue{2,Int}
@test isa(Tuple(a),Tuple)
@test Tuple(a) == a.data
-b = Matrix{Int64}(undef,2,2)
+b = Matrix{Int}(undef,2,2)
b[1,1] = a[1,1]
b[1,2] = a[1,2]
b[2,1] = a[2,1]
b[2,2] = a[2,2]
-bt = SymTracelessTensorValue{2,Int64}(b)
+bt = SymTracelessTensorValue{2,Int}(b)
@test all(bt .== a)
a = SymFourthOrderTensorValue(1111,1121,1122, 2111,2121,2122, 2211,2221,2222)