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 @@ +FieldPolynomialisHierarchicalMonomialLegendreChebyshevModalC0Bernstein \ 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 @@ +AbstractArray{<:Polynomial}PolynomialBasisget_orderreturn_typeUniformPolyBasisget_exponentsget_ordersCompWiseTensorPolyBasisRaviartThomasPolyBasisNedelecPolyBasisOnSimplexModalC0Basisget_orders(<:Polynomial)BasisQGrad[<:Polynomial]BasisQCurlGrad[<:Polynomial]BasisPCurlGrad[<:Polynomial]BasisPGradMonomialBasis \ 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)