Abstract
We present a numerical framework for solving partial differential equations within an isogeometric context using T-splines in two and three space dimensions. Within this paper, we explain the data structures used for the implementation of deal.t (deal.II with T-splines) and main differences when using deal.t in contrast to deal.II. The authors present numerical experiments with error-based refinement (2D) and a priori refinement (3D) for scalar-valued problems. A full tutorial is given in the appendix. Since the new framework is based on deal.II, T-splines may be applied to various different PDEs.
Similar content being viewed by others
Avoid common mistakes on your manuscript.
1 Introduction
This paper presents a new framework to solve partial differential equations (PDEs) using adaptive isogeometric analysis (IGA) based on T-splines, called deal.t. This package includes two and three-dimensional T-splines as explained in [1], and can be used to solve different PDEs of order two numerically with various (possibly mixed) boundary conditions on single patch domains.
IGA is the idea of directly using the shape functions from CAx modelling of physical domains as ansatz functions for the Finite Element Method (FEM). There are numerous software libraries available involving FEM-based solvers, e.g. FEniCS, see [2, 3], and deal.II, see [4], and libraries specifically designed for IGA-based FEM like e.g. G+Smo, see [5, 6], PetIGA, see [7]. However, the only publicly available implementations of T-splines for PDE discretizations currently known to the authors can be found in the Matlab package igafem, see [8], and the Python package tIGAr, see [9]. tIGAr uses a T-spline plugin for the commercial CAD software Rhinoceros 3D in order to run its calculations via T-splines. The underlying plugin has been discontinued from support and does not guarantee analysis-suitable T-splines.
IGA has been outlined in [10] where predominantly non-uniform rational B-splines (NURBS) were used as a tool to solve PDEs. Different concepts of refinement for NURBS have been introduced, i.e. h-refinement through knot insertion, p-refinement through order elevation, which are described in detail in [11], and k-refinement which simultaneously increases order and continuity.
However, local refinement techniques had already been introduced for B-splines, see e.g. hierarchical B-splines (HB-splines) in [12, 13], from which truncation methods of basis functions were developed in [14] (THB-splines) to further improve local refinement and properties of basis functions. T-splines have been introduced as an alternative to HB-splines in [15] for CAD as a strategy to directly refine the control mesh. They have been introduced as a mathematical tool in [16] and applied with promising results in [17], but it was eventually noticed that linear independence of basis functions is not always guaranteed, see [18]. Properties to guarantee linear independence have been introduced in [19] which introduced analysis suitability and analysis suitable (AS) T-splines. As an alternative to T-splines, locally refined (LR) B-splines were introduced in [20]. A first refinement algorithm for LR B-splines was introduced in [21] which showed promising results. However, linear independence is an issue for LR B-splines as well.
As T-splines were introduced, definitions were restricted to the two-dimensional case with degree three in both directions, and later AS T-splines of arbitrary degrees have been introduced in [22]. T-splines in arbitrary dimensions but odd polynomial degrees have been introduced in [23, 24] with proper refinement algorithms to retain analysis-suitability after refinement. Lastly, T-splines in arbitrary dimensions and arbitrary polynomial degrees were recently introduced in our previous work, see [1], allowing us to finally use T-splines in a framework for FEM, with arbitrary degree and local mesh refinement in three dimensions.
Recent advances in IGA can be found e.g. in [25], where a thorough comprehension of adaptive IGA is given for hierarchical splines with some applications. These results also include boundary element methods for an adaptive framework.
This paper is structured as follows. In Sect. 2, we discuss the prerequisites needed for the main parts. This includes notation and the definition of T-junctions in higher dimensions. We also briefly explain the concept of (geometric) analysis-suitability and AS T-splines with some examples. For a detailed guide through AS T-splines, we refer the reader to our previous work [1]. Section 2.2 is dedicated to the local refinement procedure explained in [23] and used in deal.t.
Further, we discuss in Sect. 3 possible meshes to serve as a base for implementing T-splines, with focus on the index mesh and the parametric mesh. We explain how both mesh classes are connected and how refinement on the parametric mesh affects the index mesh. Pseudocodes are given for relevant algorithms.
Section 4 explains main differences when using deal.II with deal.t compared to usual deal.II applications. It explains how to set up a proper triangulation, i.e. mesh, and the differences in assembly loops. deal.t is suited with a residual error estimator for Poisson-like problems, which is also explained in detail in Sect. 4.1. A full tutorial for readers unfamiliar with deal.II is given in Appendix C.
Numerical benchmark tests are discussed in Sect. 5. We consider two model problems based on a standard Poisson problem with Neumann and Dirichlet boundary conditions. The first problem is the result of the example given in Appendix C for various polynomial degrees, which is considered a pure benchmark test without singularities. The second problem is defined on the L-shape domain, given a solution with a corner singularity. A demonstration of mesh refinement in 3D is given in Fig. 2.
deal.t is based on the open source software deal.II and made available at [26], where the results of this work can be reproduced.
2 Prerequisites
In this section, we introduce the required mathematical notation. For details, see [1].
For any \(x\in \mathbb {R}\) we will denote by \(\lceil x \rceil \) the rounding of x to its next integer \(n\in \mathbb {N}\) with \(n\ge x\). Analogously, we denote by \(\lfloor x \rfloor \) the rounding of x to its next integer \(n\in \mathbb {N}\) with \(n\le x\).
We consider a box-shaped index domain , with \(N_k\in \mathbb {N}\), for \(k=1,\dots ,d\), and an associated parametric domain , with \(p_k\)-open knot vectors \(\Xi ^{(k)}=\{\xi _0^{(k)},\dots , \xi _{N_k}^{(k)}\}\), for polynomial degrees \(p_k\in \mathbb {N}\). Let \(\widehat{\mathscr {T}}= \mathscr {T}({\widehat{\Omega }})\) be a mesh of \({\widehat{\Omega }}\), consisting of open axis-parallel boxes. We suppose that \({\widehat{\Omega }}\) is constructed via symmetric bisection from an initial tensor-product mesh with integer vertices, which is described in detail in [1, Algorithm 2.1]. Consequently, we suppose that for each vertex \(\mathscr {V}= \{\texttt {V}_1\} \times \dots \times \{\texttt {V}_d\}\) in the index mesh, the components are of the form \(\texttt {V}_k = a_k\cdot 2^{-b_k}\), with \(a_k,b_k\in \mathbb {N}\), \(k=1,\dots ,d\). Therefore, all indices used below are from the set
In Sect. 3 we will see that the implementation is given on the parametric mesh \(\mathscr {T}= \mathscr {T}(\Omega )\), however, relevant definitions are done on the index mesh \(\widehat{\mathscr {T}}\). Throughout this paper, we will distinguish between the parametric mesh/elements \(\texttt {Q}\in \mathscr {T}\) and index mesh/elements \(\widehat{\texttt {Q}} \in \widehat{\mathscr {T}}\) with a hat in index domain notations which is dropped on the parametric domain. Where necessary, we introduce arguments to indicate which mesh we consider in this case.
For \(k=1,\dots ,d\), we denote the k-dimensional mesh entities of \(\widehat{\mathscr {T}}\) by \(\mathscr {H}^{(k)}(\widehat{\mathscr {T}})\), e.g. by \(\mathscr {H}^{(0)}(\widehat{\mathscr {T}})\) the set of vertices, resp. nodes, by \(\mathscr {H}^{(1)}(\widehat{\mathscr {T}})\) the set of one-dimensional edges without start and end point, and so on. The union of all d-dimensional element boundaries
is called the skeleton of \(\widehat{\mathscr {T}}\).
For an index set \(\kappa = \{ \kappa _1,\dots , \kappa _\ell \}\subset \{1,\dots ,d\}\) and a d-dimensional element \(\texttt {Q}= \texttt {Q}_1 \times \dots \times \texttt {Q}_d\in \mathscr {H}^{(d)}(\widehat{\mathscr {T}})\) we denote the \((d-\ell )\)-dimensional, \(\kappa \)-orthogonal interfaces by \(\texttt{H}^{(\kappa )}(\texttt {Q})\), e.g.
For polynomial degrees \({\textbf {p}}= (p_1,\dots ,p_d)\in \mathbb {N}^d\), we split the index domain \({\widehat{\Omega }}\) into an active region and a frame region , with
We continue to explain what a T-junction in higher dimensions is.
Definition 1
(T-junctions) We call an interface \(\texttt {T}\in \mathscr {H}^{(d-2)}(\widehat{\mathscr {T}})\) with \(\texttt {T}\nsubseteq \partial {\widehat{\Omega }}\) a T-junction if it is in the boundary of a cell \(\texttt {Q}=\texttt {Q}_1\times \dots \times \texttt {Q}_d\in \mathscr {T}\) without being connected to any of its vertices, \(\texttt {T}\subset \partial \texttt {Q}\), \(\overline{\texttt {T}}\cap \partial \texttt {Q}_1\times \dots \times \partial \texttt {Q}_d=\varnothing \). We then call \(\texttt {Q}\) the associated cell of \(\texttt {T}\) and write \(\texttt {Q}={{\,\textrm{ascell}\,}}(\texttt {T})\). Since \(\texttt {T}= \texttt {T}_1 \times \dots \times \texttt {T}_d\in \mathscr {H}^{(d-2)}(\widehat{\mathscr {T}})\), there are two unique and distinct directions \(i,j\in \{1,\dots ,d\}\) such that \(\texttt {T}_i,\texttt {T}_j\) are singletons, \(\texttt {T}\in \texttt{H}^{(\{i,j\})}(\widehat{\mathscr {T}})\), \(\texttt {T}_i\subsetneq \texttt {Q}_i\) and \(\texttt {T}_j \subsetneq \partial \texttt {Q}_j\). We call i the orthogonal direction and j the pointing direction of \(\texttt {T}\), and write \({{\,\textrm{odir}\,}}(\texttt {T})=i\), \({{\,\textrm{pdir}\,}}(\texttt {T})=j\).
From [1] we have uniqueness of the associated cell for each T-junction.
Before we jump into definitions of anchors and index vectors, we first define a criterion for meshes that may be taken into consideration for further computations.
Definition 2
(Admissible meshes) We define the slice
for \(k=1,\dots ,d\) and \(n=0,\dots ,N_k\), and the k-th frame region
A T-mesh \(\widehat{\mathscr {T}}\) is called admissible, if for \(k=1,\dots ,d\), there is no T-junction \(\texttt {T}\) with \({{\,\textrm{odir}\,}}(\texttt {T})=k\) or \({{\,\textrm{pdir}\,}}(\texttt {T})=k\) in the k-th frame region, and
For the rest of this paper, the index mesh \(\widehat{\mathscr {T}}\) is assumed to be admissible whenever used.
2.1 Multivariate T-splines and analysis-suitability
Anchors are a subset of mesh entities that have a one-to-one correspondence to the set of T-splines. For higher dimension and arbitrary degree \({\textbf {p}}= (p_1,\dots ,p_d)\), they have been introduced in [1] as
with \(\kappa =\{ \ell \in \{1,\dots ,d\}\mid p_\ell \text { odd } \}\). We associate to each anchor and each axis direction \(k=1,\dots ,d\) a non-decreasing knot vector of \(p_k+2\) indices from \(\mathbb {N}^{(2)}\). In [22, 27, 28], these index vectors (in 2D) are constructed via ray-tracing the anchor through the mesh along the k-th direction and choosing the \(p_k+2\) consecutive indices centered around the k-th component \({\textbf {A}}_k\) of \({\textbf {A}}\). This is generalized in [1] to arbitrary dimensions, i.e. for any mesh entity \({\texttt {E}}={\texttt {E}}_1\times \dots \times {\texttt {E}}_d\) and \(k\in \{1,\dots ,d\}\), we define the projection \(P_{k,n}({\texttt {E}})={\texttt {E}}|_{{\texttt {E}}_k=\{n\}}\) of \({\texttt {E}}\) on the slice \(\textrm{S}_k(n)\), and the global knot vector
with entries in ascending order. The local knot vector for an anchor \({\textbf {A}}= {\textbf {A}}_1\times \dots \times {\textbf {A}}_d\) is given by the \(p_k+2\) consecutive indices \(\ell _0,\dots ,\ell _{p_k+1}\in \mathscr {I}_{k}({\textbf {A}})\), such that \(\ell _k = \inf {\textbf {A}}_k\) for \(k = \lfloor \tfrac{p_k+1}{2}\rfloor \). If \(p_k\) is odd, the singleton \({\textbf {A}}_k\) contains the middle entry of , and if \(p_k\) is even, the two middle entries of are the boundary values of \({\textbf {A}}_k\).
An example is given in Fig. 1. Depicted is the construction principle using ray-tracing to generate the corresponding local index vectors. For on the top, we obtain . In contrast, for , the projection onto the slice at \(({\bar{m}} +3)\) is not a part of the skeleton of the mesh. The same applies to the index \({\bar{m}} + 1\), which yields . Note that \(p_1\) is odd and that is the middle element of the respective local index vectors.
On the lower part of Fig. 1, we get as before . However, the projection of onto the slice at \(m_2\) is not part of the skeleton, which yields . Note that \(p_1\) is even and that is the middle section of the local index vector .
Using anchors and their local index vectors, we can define a multivariate T-spline associated to an anchor.
Definition 3
(T-spline) For \(p_k\in \mathbb {N}\), we denote by the univariate B-spline function of degree \(p_k\) that is returned by the Cox-deBoor recursion with knot vector , see e.g. [11]. We assume that \(\xi _{\ell _0}^{(k)}<\xi _{\ell _{p_k+1}}^{(k)}\) is always fulfilled. The T-spline function associated with the anchor \({\textbf {A}}\) is defined as
and the corresponding T-spline space is given by . The index support of \(T_{\textbf {A}}\) will be denoted by , where is the closed interval from the first to the last entry of .
For the concept of analysis-suitability, we use a geometric concept based on the concepts from [22, 27, 28]. Note that there are different versions of analysis-suitability, see [1] for details.
Definition 4
(Geometric analysis-suitability) Let \(\texttt {T}\) be a T-junction with \(\texttt {Q}={{\,\textrm{ascell}\,}}(\texttt {T})\), \(i={{\,\textrm{odir}\,}}(\texttt {T})\) and \(j={{\,\textrm{pdir}\,}}(\texttt {T})\). We then define local knot vectors as follows.
-
1.
For \(k = j\), we define as the vector of \((p_j+1)\) consecutive indices from \(\mathscr {I}_{j}(\texttt {T})\), such that
$$\begin{aligned} \begin{aligned}&\{\ell _{p_j/2}\} = \texttt {T}_j,&\text { if }p_j\text { is even}, \\&\left. \begin{aligned} \ell _{\lfloor p_j/2\rfloor }&= \inf \texttt {Q}_j,\quad \\ \ell _{\lceil p_j/2\rceil }&= \sup \texttt {Q}_j, \end{aligned} \right\}&\text { if }p_j\text { is odd}. \end{aligned} \end{aligned}$$(12) -
2.
For \(k=i\), the local knot vector is the singleton .
-
3.
For \(k \not \in \{i, j\}\) we define , where \(c_k = p_k \text { mod }2\), as the vector of \((p_k + 2 + c_k)\) consecutive indices from \(\mathscr {I}_{k}(\texttt {T})\), such that
$$\begin{aligned} \texttt {T}_k = (\ell _{\lceil p_k/2\rceil }, \ell _{\lceil p_k/2\rceil +1}). \end{aligned}$$(13)This means that the local knot vector has \(p_k+3\) elements if \(p_k\) is odd and \(p_k + 2\) if \(p_k\) is even, and \(\texttt {T}_k\) is centred within these elements, cf.@ the definition of local knot vectors for anchors.
We then call
the geometric T-junction extension (GTJ) of \(\texttt {T}\), and we say that it is an i-orthogonal extension in j-direction. Note, that .
A mesh \(\mathscr {T}\) is strongly geometrically analysis-suitable (), if for any two T-junctions \(\texttt {T}_1,\texttt {T}_2\) with orthogonal directions \(i_1= {{\,\textrm{odir}\,}}(\texttt {T}_1)\ne {{\,\textrm{odir}\,}}(\texttt {T}_2)=i_2\) there is
We call \(\mathscr {T}\) weakly geometrically analysis-suitable (), if (15) holds for any two T-junctions \(\texttt {T}_1,\texttt {T}_2\) with orthogonal directions \({{\,\textrm{odir}\,}}(\texttt {T}_1)\ne {{\,\textrm{odir}\,}}(\texttt {T}_2)\) and pointing directions \({{\,\textrm{pdir}\,}}(\texttt {T}_1)\ne {{\,\textrm{pdir}\,}}(\texttt {T}_2)\).
We will omit the dependency of the orthogonal direction when it is clear from the context, e.g. write , for .
It was proven in [1] that is sufficient for linear independence of the generated spline space, which makes them suitable for application in a finite element setting.
2.2 Local refinement procedure
We now continue to explain the refinement strategy for a given T-mesh, intended to be applied in an adaptive Galerkin scheme with the steps solve, estimate, mark and refine as follows.
In Sect. 3 we will explain in detail how the parametric mesh \(\mathscr {T}= \mathscr {T}(\Omega )\) and the index mesh \(\widehat{\mathscr {T}}\) are connected and what we have to consider during the implementation. To this extent, we will thus only explain the refinement process on the parametric mesh \(\mathscr {T}\). The definitions below strictly follow [23, 24].
As for the index mesh, we consider a box-shaped parametric mesh \(\Omega \) consisting of axis-aligned open boxes that are generated via symmetric box bisections from an initial tensor-product structure. Note that a tensor-product structure is strongly geometric analysis-suitable due to the absence of T-junctions. We denote the initial tensor-product mesh by \(\mathscr {T}^{(0)}\) and every consecutive mesh by \(\mathscr {T}^{(n)}\), \(n = 1,\dots \)
Let \(\texttt {Q}\in \mathscr {T}^{(n)}\) be some cell. We define its level by the index \(m\le n\), s.t. \(\texttt {Q}\) is an element of the m-th uniform refinement of \(\mathscr {T}^{(0)}\), and write \(\ell (\texttt {Q}) = m\). Its vector-valued size is defined to be the component-wise length of its edges, i.e.
The level-dependent subdivision of a cell \(\texttt {Q}\in \mathscr {T}^{(n)}\) is defined by the \(k_{\ell (\texttt {Q})}\)-orthogonal bisection of \(\texttt {Q}\) where \(k_{\ell (\texttt {Q})} = 1 + (\ell (\texttt {Q}) \text { mod } d)\), denoted by \(\textsc {subdiv}(\texttt {Q}, k_{\ell (\texttt {Q})})\).
For a point \(z\in \Omega \), we define the vector-valued distance between z and \(\texttt {Q}\) as
and \({{\,\mathrm{\textsc {{mid}}}\,}}(\texttt {Q})\) is defined as the midpoint of \(\texttt {Q}\). For two cells \(\texttt {Q}^{(1)}\) and \(\texttt {Q}^{(2)}\), we define its distance as the vector-valued distance of its cells.
For two vectors \(x, y\in \Omega \), we denote by \(x \circ y\) the component-wise product, i.e.
With these definitions, we define the open environment \(U(\texttt {Q})\) of a cell \(\texttt {Q}\in \mathscr {T}^{(n)}\) by
and its coarse neighbourhood \(\mathscr {N}(\mathscr {T}^{(n)}, \texttt {Q})\) of a cell \(\texttt {Q}\in \mathscr {T}^{(n)}\) in the mesh \(\mathscr {T}^{(n)}\) by
Let \(\mathscr {M}\subset \mathscr {T}^{(n)}\) be a set of elements marked for refinement. Its closure is then defined recursively via
-
1.
Set \(\mathscr {N}^{(0)}(\mathscr {T}^{(n)}, \mathscr {M}) :=\bigcup _{\texttt {Q}\in \mathscr {M}} \mathscr {N}(\mathscr {T}^{(n)}, \texttt {Q})\) and \(\mathscr {M}^{(0)} = \mathscr {M}\),
-
2.
for \(j=1,2,\dots \), define
$$\begin{aligned} \mathscr {M}^{(j)} :=\mathscr {N}^{(j-1)}(\mathscr {T}^{(n)}, \mathscr {M}), \end{aligned}$$ -
3.
break recursion if \(\mathscr {M}^{(j)} = \mathscr {M}^{(j-1)}\),
-
4.
define \(\texttt{closure}(\mathscr {T}^{(n)}, \mathscr {M}):=\mathscr {M}^{(j)}\).
Note that this recursion will terminate in the worst case if \(\mathscr {N}^{(j)}(\mathscr {T}^{(n)}, \mathscr {M}) = \mathscr {T}^{(n)}\). We then define the refined mesh \(\mathscr {T}^{(n+1)}\) by
From [23] we know that if \(\mathscr {T}^{(n)}\) is analysis-suitable, \(\mathscr {T}^{(n+1)}\) is also analysis-suitable. Note that the analysis-suitability used in [23] is abstract and thus different from the introduced geometric analysis-suitability above. However, in [1] it was shown that the geometric one yields also the abstract one. Thus, we only use geometric analysis-suitability to find Bézier elements. A C++-style pseudocode for the construction of the coarse neighbourhood is given in Algorithm 1. Note that this algorithm has to be called recursively in order to get the complete coarse neighbourhood, see Algorithm 2.
An example is given in Fig. 2, where the 3D L-shape domain is considered. Cells that are adjacent to the red line \(\texttt {L} = \{0\}\times \{0\} \times [-1, 1]\) are marked for refinement in each iteration. Given a problem on the whole 3D L-Shape domain that is symmetric along the diagonal
this half domain is sufficient for computations. For simplicity, we have depicted only a 3D view of a cross-section along the diagonal \(\texttt {D}\). The blue dashed lines denote the next level of refinement. This example thus also demonstrates the definition of the coarse neighbourhood from Algorithm 1.
After refinement, the T-splines, resp. local knot vectors have to be updated. It was shown in [1] that under certain assumptions, local knot vectors may be inherited from parent anchors. The result reads as follows.
Lemma 1
Let be the associated index mesh to the parametric mesh \(\mathscr {T}\). Assume that each cell \(\widehat{\texttt {Q}}\in \widehat{\mathscr {T}}\) has active neighbours in at least three distinct directions for \(d\ge 3\) and two distinct directions if \(d=2\), i.e. the neighbouring cells are in the active region. Define for an arbitrary cell \(\texttt {Q}\in \mathscr {T}\) the subdivided parametric mesh
together with its subdivided index mesh . Then for every new index anchor there exists an old anchor with , .
The assumption from Lemma 1 is always fulfilled if the polynomial degree is greater than one. If the polynomial degree is one in some direction k, uniform refinement steps may be performed until direction k is refined to ensure this assumption.
3 Data structures
There are several possible meshes to take into consideration for an implementation, see Fig. 3. Firstly, there is the index mesh \(\mathscr {T}({\widehat{\Omega }})\) where theoretical definitions are done, see Sect. 2. However, if we base computations on the index mesh alone, this will lead to integrals over empty domains, as some distinct indices can lead to knots that coincide. This is no problem per se, but it will practically cause loop iterations within the program, that essentially do nothing. This should be avoided.
Another possible solution is the mesh in the physical domain \(\mathscr {T}(\overline{\Omega })\). This is indeed already done in another software project, called G+Smo, see [5], available at [29] with an overview of existing modules in [6], where other isogeometric concepts are already fully implemented, e.g. (hierarchical) NURBS, (hierarchical) LR-Splines, etc. However, an implementation of T-Splines in neither 2D nor 3D within the G+Smo library is unknown to the authors extent.
A standard finite element code is already given by the deal.II library, see [4], or FEniCS, see [30] and libraries derived thereof, e.g. [31]. We have opted to base our implementations on the deal.II library (version 9.3 [32]), as it features a larger group of researchers together with some important base implementations, the most important being an implementation of Bernstein polynomials which are used for Bézier extraction. We aim to keep integration as simple as possible during the implementation, which is the case if elements of a considered mesh are axis-aligned. Doing so ensures integral variables to be independent of each other, i.e. we integrate over d dimensional boxes. This is either achieved on the index domain or the parametric domain and as the index domain is already disregarded the implementation works fully on the parametric domain. However, as explained in [28, Proposition 7.6], we have to use the Bézier mesh \(\mathscr {B}(\Omega )\) of the parametric domain to be able to integrate a fixed number of splines on a specific cell.
For the implementation we thus consider the associated parametric domain \(\Omega \) from the underlying index domain \({\widehat{\Omega }}\). For the mesh \(\widehat{\mathscr {T}}\), we also consider the associated parametric mesh \(\mathscr {T}\), where knot repetitions are ignored; i.e. if \(\widehat{\texttt {E}}\in \mathscr {H}^{(k)}(\widehat{\mathscr {T}})\) is an arbitrary k-dimensional element of \(\widehat{\mathscr {T}}\), then its associated parametric element \({\texttt {E}}\) is an element of the parametric mesh \(\mathscr {T}\), if and only if for all directions j there is
In other words, no direction j may collapse to a single point, if the direction j was an interval in the index mesh.
Anchors are mapped together with its knot repetitions, i.e. if is an anchor of the index domain, then the anchor on the parametric domain is given by
Consider the following example. Let \(d = 2\) and \(\Xi _1 = \{0,0,1,1\}\), \(p_1 = 1\), and \(\Xi _2 = \{0, 0, 0, 1, 1, 1\}\), \(p_2=2\). The indices are given by \(I_1 = \{0,1,2,3\}\) and \(I_2 = \{0, 1, 2, 3, 4, 5\}\) and the set of index anchors are the horizontal lines of the index mesh constructed by \(I_1\times I_2\) within the active region. Choose \(\widehat{{\textbf {A}}} = \{1\} \times (2, 3)\), then its parametric anchor is given by \({\textbf {A}}= \{ 0 \} \times [0, 1]\). However, if we consider the parametric anchor from \(\widehat{{\textbf {A}}} = \{1\}\times (1, 2)\), we obtain \({\textbf {A}}=\{0\}\times \{0\}\). This demonstrates that anchors on the parametric mesh do not follow a certain rule, as is the case for the index mesh.
Further, local index vectors for an index anchor \(\widehat{{\textbf {A}}}\) are linked one-to-one to its parametric anchor \({\textbf {A}}\), i.e.
Local index vectors for T-junctions \(\widehat{\texttt {T}}\) are mapped in a similar way. However, if in some direction \(j\ne {{\,\textrm{pdir}\,}}(\widehat{\texttt {T}}) \), \(j\ne {{\,\textrm{odir}\,}}(\widehat{\texttt {T}})\) the interval \(\widehat{\texttt {T}}_j\) collapses to a point, the T-junction is not considered for the parametric mesh. This case will be addressed in Sect. 3.3.
3.1 Parametric mesh
The implementation is solely on the parametric mesh \(\mathscr {T}\), where knot repetitions are ignored for the construction of cells. However, they are essential for the construction of T-junction extensions and have to be available. For this, we store an array mof that stores the multiplicity of each face, i.e. mof[id] returns the multiplicity of face \(\texttt {F}_{\texttt {id}}\in \mathscr {H}^{(d-1)}(\widehat{\mathscr {T}})\). This can later be used to define terminating conditions for the construction of T-junction extensions. Note that per deal.II standards, faces of cells are indexed consecutively for the global mesh.
From [1] we have a dual-compatible set of T-Splines, hence [28, Proposition 7.6] gives us an upper bound for the amount of T-Splines on each element. The upper bound is met exactly, if cells along T-junction extensions are subdivided accordingly. On each such element we can then compute the Bézier representation of each T-Spline, i.e. let \(\mathscr {T}_{\texttt {T}}(\Omega )\) be the mesh described above, then there exists for each cell \(\texttt {Q}\in \mathscr {T}_{\texttt {T}}(\Omega )\) operators \({\textbf {C}}_\texttt {Q}\), s.t. the set of T-Splines \({\textbf {T}}\) with support on \(\texttt {Q}\) are given by a linear combination of Bernstein polynomials \({\textbf {B}}\) on the reference element \([0, 1]^d\),
see also [33, 34] for a detailed description of Bézier extraction of NURBS, resp. T-Splines.
The values of the Bézier splines on the reference elements can be computed preemptively. To use this in our programming, we need the arrays
-
1.
bezier_elements that stores every (active) cell that is intersecting some T-junction extension.
-
2.
extraction_operators that stores the extraction operators used in (29). Each extraction operator \({\textbf {C}}_\texttt {Q}\) is further represented as a matrix.
-
3.
IEN_array that returns a set of indices for a specific cell \(\texttt {Q}\). The returned indices correspond to indices of the globally indexed T-splines.
The extraction operators are computed based on the algorithm described in [34], however, some minor changes had to be done. The altered code can be found as MATLAB adaption in Appendix A, Algorithm 5.
In detail, for every T-Spline \({\textbf {T}}\), we use its 1D knot vectors to compute 1D extraction operator rows \({\textbf {c}}^j_\texttt {Q}\) from Algorithm 5 for a given cell \(\texttt {Q}\) with \({{\,\textrm{supp}\,}}{\textbf {T}}\cap \texttt {Q}\ne \varnothing \), and use the tensor structure to generate the full extraction operator row \({\textbf {c}}_\texttt {Q}\) for \({\textbf {T}}\), s.t.
To work with boundary indicators, an array boundary_dofs is stored that returns for a given boundary index a set of splines that have support on that boundary.
An example for the data structure used is given in Fig. 4. The example on the right displays the quantities that are stored, except for extraction operators and the IEN_array. In this example we consider polynomial degrees \(p_1 = 3\) and \(p_2 = 2\), the frame region of the index mesh on the left is highlighted by diagonal lines and consists of the outermost cells. Cells in the active region with knot repetitions are highlighted in green. Only the parametric mesh on the right, together with the multiplicities of each face, is stored. The numbers in the circles on the lines indicate the multiplicity of that line (face). These values are stored in the aforementioned mof-array. Further, note that the T-junction extension from \(\texttt {T}= \{ 6 \} \times \{ 5 \}\) yields only a single cell in the parametric mesh due to knot repetitions. Since the implementation is on the parametric mesh, this example also demonstrates that not every cell in the active region of the index mesh will be exclusively marked for refinement, albeit no theoretical restrictions forbid it. For example, it is theoretically allowed to refine \(\texttt {Q}= (2, 3) \times (3, 4)\) in any direction, but since it is practically non-existent in the parametric mesh, it will never be marked for refinement on its own.
3.2 T-Splines
A T-Spline \({\textbf {T}}_{\textbf {A}}\) is a function defined by its parametric anchor \({\textbf {A}}\), resp. the corresponding knot vectors. Hence, they are internally described by a d-dimensional array of arrays kv, where kv[k] corresponds to the k-th index vector. Note that we do not have to calculate the knot vectors for each T-Spline; from Lemma 1 we know that knot vectors are inherited by bisection. We stress again the fact that the initial mesh is a tensor-product mesh of the given knots. In addition to its knot vectors we also store its associated anchor \({\textbf {A}}\) as a pair of two points in the parametric mesh. They describe the bounding box for the anchor and can be used to obtain information about the global position of this T-Spline, e.g. we can easily access information whether or not the T-Spline is located at a specific face. The example from Fig. 4 is continued in Fig. 5 to demonstrate how index anchors and parametric anchors correspond to each other. In this example, index anchors are given as vertical lines of the mesh and are marked here by dots on these lines. When they are brought to the parametric domain on the right, some anchors collapse to a single point, and some anchors may coincide in their parametric representation. This is denoted by concentric circles in the parametric mesh on the right, where the number of circles denote the amount of parametric anchors on that entity. Note that we can not give a well-defined order of anchors, as some parametric anchors coincide.
Hence, we define the barycenter of the T-Spline associated to the parametric anchor \({\textbf {A}}\) by
The barycenters of each T-spline can be used to define a global order. Hence, a T-Spline also stores its barycenter. The example from Figs. 4 and 5 is continued in Fig. 6 to demonstrate the placement of barycenters throughout a given 2D mesh. This information can be used to order the T-splines in the mesh, e.g. in a lexicographic way, see (57).
We have chosen to store the needed control points used for the isogeometric mapping directly within the T-Splines data structure. This way, we do not need to worry about indexing errors between T-Splines and control points. It should be mentioned that in fact the weighted control points are stored, i.e. if P is a control point with weight \(\omega \in (0, 1)\), then the control point saved is \(P^\omega = (\omega P, \omega )\).
3.3 T-junctions
Since the implementation works on the parametric mesh, subdividing a (parametric) cell \(\texttt {Q}\) in direction k with a face of multiplicity greater than one, will also result in a subdivision of all related index cells corresponding to that face. In detail, consider \(\texttt {Q}\in \mathscr {T}\) from its associated index cell \(\widehat{\texttt {Q}} \in \widehat{\mathscr {T}}\) with a subdivision along direction \(k'\) with a face \(\texttt {F}\subset \partial \texttt {Q}\), \(\texttt {F}\in \mathscr {H}^{(d-1)}(\mathscr {T})\cap \texttt{H}^{(k)}(\mathscr {T})\), that is k-orthogonal, \(k\ne k'\), and has multiplicity \(m = \,\) mof[id] > 1, where id is the global index of the given face. Since the multiplicity is greater than one, there exists multiple cells \(\widehat{\texttt {Q}}^{(i)}\), \(i = 1,\dots ,m-1\) on the index mesh \(\widehat{\mathscr {T}}\) that are associated to \(\texttt {F}\), i.e. (26) reads
for all \(i=1,\dots ,m-1\). The subdivision of cell \(\texttt {Q}\) along direction k then also subdivides the above face \(\texttt {F}\) into two children \({\texttt {F}^{(1)}}\) and \({\texttt {F}^{(2)}}\) with multiplicity m. Each child then has new multiple cells from the index mesh \(\widehat{\texttt {Q}}^{(c, i)}\), \(c = 1,2\), \(i = 1,\dots ,m-1\). Now, since \(\texttt {F}^{(1)}\) and \(\texttt {F}^{(2)}\) originate from the same face \(\texttt {F}\) there is a common interface \(\texttt {T}= \partial \texttt {F}^{(1)}\cap \partial \texttt {F}^{(2)}\) and in particular \(\inf \texttt {F}^{(1)}_{k'} = \sup \texttt {F}^{(2)}_{k'}\) or vice versa. This yields common faces for the index cells \(\widehat{\texttt {Q}}^{(c, i)}\), i.e. \(\inf \widehat{\texttt {Q}}^{(1, i)}_{k'} = \sup \widehat{\texttt {Q}}^{(2,i)}_{k'}\) or vice versa for all \(i=1,\dots ,m\). However, this is equivalent to subdividing cells \(\texttt {Q}^{(i)}\) along direction \(k'\) and obtaining the children \(\texttt {Q}^{(c, i)}\), \(c=1, 2\), \(i=1,\dots ,m-1\). See also Fig. 7.
The example depicted shows the effect of subdividing a 2D parametric cell \(\texttt {Q}\) (left) on its corresponding index mesh counterparts (right). As in the previous examples, for the parametric mesh the numbers in circles denote the multiplicity of the corresponding line (face). From this setup, we can infer the that there is a cell \(\widehat{\texttt {Q}}\) in the index mesh that will be mapped to the parametric mesh. Consider now its third face, i.e. the face on the top of the cell with multiplicity two. From the multiplicity we infer a knot repetition of two in the parametric domain, hence there are two indices in the index mesh. This yields a cell \(\widehat{\texttt {Q}'}\) above the corresponding index cell \(\widehat{\texttt {Q}}\) whose corresponding parametric “cell” has empty volume. From the multiplicity of face 1, i.e. the right line, we infer two consecutive index cells that collapse to a single line by the same arguments. Repeating these arguments for the remaining two faces, we obtain the corresponding index region on the right of Fig. 7. The cells found above are highlighted in green. The subdivision of \(\texttt {Q}\) yields a subdivision of its faces, and from the corresponding index cells we also get a subdivision of these cells. To stress the fact that only corresponding cells are subdivided in the index mesh, we have displayed a slightly larger region in the index mesh highlighted by diagonal lines.
Thus, the refinement of the parametric mesh ensures that every generated T-junction on the index level is mapped according to condition (26).
T-junctions are not stored internally, instead we only store the cells that intersect the T-junction extensions as mentioned in Sect. 3.1. However, to find these cells, we first have to detect T-junctions. Since the mesh is generated from a tensor product structure on the coarsest level using symmetric box subdivisions, we can find associated cells of T-junctions \(\texttt {T}\) by iterating the faces \(\texttt {F}\subset \partial \texttt {Q}\) of a cell and check whether or not it has children. From the associated cell \(\texttt {Q}\) and a face \(\texttt {F}\) of the cell, we can then use the definition of local knot vectors for T-junctions to find all cells that intersect the T-junction extension.
How to exactly find the Bézier cells is given in Algorithm 3 and , for the dimensions 2 and 3 respectively. Each algorithm finds the Bézier cells \(\mathcal {Q}\), i.e. the cells cut by the T-junction extension, of a T-junction \(\texttt {T}\) with its associated cell \(\texttt {Q}= {{\,\textrm{ascell}\,}}(\texttt {T})\). The input face_no refers to the corresponding index, s.t. \(\texttt {T}\subset \texttt {F}_{\texttt {face\_no}}\subset \partial \texttt {Q}\). Further, both algorithms use neighbouring relations between cells. This is a functionality made available by the deal.II library. In general, neighbour(\(\texttt {Q}\), f) returns the cell on the opposite side of face f. Note that the neighbour may be an in-active cell, i.e. it may already be refined. For the readers unfamiliar with deal.II internal data structures and conventions, we have briefly explained enumeration of entities and neighbours in Appendix B.
The algorithm for \(d=2\) is relatively simple, as we just have to find cells along a single direction, that is the pointing direction. We distinguish between positive and negative directions \(\pm {{\,\textrm{pdir}\,}}(\texttt {T}) = \pm k\), where we traverse the neighbours in positive direction, if \(\texttt {T}_{k} \subset \inf {{\,\textrm{ascell}\,}}(\texttt {T})_{k}\), and in negative direction, if \(\texttt {T}_{k} \subset \sup {{\,\textrm{ascell}\,}}(\texttt {T})\). And since face_no yields the face of \(\texttt {Q}\) on which the T-junction lies, we simply have to search the neighbours in the opposite direction.
If the new neighbour \(\texttt {Q}' = \texttt {neighbor}(\texttt {Q}, \texttt {ofn})\) in that direction is already refined, the refinement algorithm provided by [23] guarantees that this cells refinement direction is not orthogonal to the pointing direction of \(\texttt {T}\), i.e. in 2D it is refined by a \(k'\)-orthogonal subdivision, \(k' = {{\,\textrm{odir}\,}}(\texttt {T})\). If the next neighbour \(\texttt {neighbor}(\texttt {Q}, \texttt {ofn})\) is again not refined, then the next neighbour is an associated cell of a new T-junction \(\texttt {T}'\) from which we run the algorithm again. Thus, the algorithm for \(\texttt {T}\) may stop at \(\texttt {Q}'\).
The 3D case is very similar, however, we have to extend the T-junction in two directions now. We denote the second direction to traverse neighbours with rdir in Algorithm 4. For every step in ofn, defined as in 2D, we traverse the neighbours in positive and negative direction. Since the T-junction extension is defined by the index vectors at the position of the T-junction, we have to ensure that these limits are given while traversing all neighbours. The quantities are denoted by rmin, resp. rmax, and are defined as
for the parametric knot vectors
where \(\widehat{\texttt {T}}\in \mathscr {T}({\widehat{\Omega }})\) is the associated T-junction in the index domain.
4 Differences between deal.II and deal.t
In this section we will discuss main differences when using a deal.t based finite element solver versus a standard deal.ii technique. A full Tutorial for readers unfamiliar with deal.II is given in Appendix C.
As the TS_Triangulation class inherits from deal.IIs Triangulation, we can use every function of the base class for the derived class. However, some functions have been overwritten to fit either the data structures explained in Sect. 3, or enforce special behaviour. For example the create_triangulation usually generates a grid from a list of vertices and a list of cell data. It has been overwritten to take the data used for the isoparametric mapping instead, i.e. a list of knot vectors, a list of polynomial degrees, and a list of weighted control points. The function header is then declared as
We also provide an object IPF_Data that essentially stores these data types and allows us to overload the definition of create_triangulation with
These two functions are then called respectively within the constructor of TS_Triangulation. We highly recommend using the constructor based on IPF_Data as it allows creating the correct grid within a single line, see also the example in Appendix C.
Further, remember that the implementation differs between the parametric mesh \(\mathscr {T}\) and the parametric Bézier mesh \(\mathscr {B}\). The former is used for refinement and the latter for assembly routines. To switch between these types of meshes we have provided the functions
that find all relevant Bézier elements using Algorithm 3 in 2D, resp. Algorithm 4 in 3D, and refines them via a symmetric, level-dependent box bisection, resp. coarsens the refined Bézier elements.
Since finite elements are defined globally and not on the reference cell, we have to provide information to applications about which T-spline, resp. DoF, is at the boundary. This information can be obtained with
where the former function computes the object returned by the latter. We advise to directly call set_boundary_dofs after each refinement.
The rest of this chapter explains main differences when assembling vectors, matrices, (estimated) errors, etc., manipulating the mesh, and other miscellaneous functions such as output routines to print e.g. the grid.
4.1 Assembly
Similar to deal.IIs FEValues object to define finite element values on a cell, deal.T provides an object of the type TSValues that mimics the behaviour of the deal.II original function. Note that it does not inherit from the original. T-spline values on a cell are computed using Bézier extraction, as explained in Sect. 3.2. During construction of a TSValues object, we store tables of Bernstein-values on quadrature points on the reference element \([0,1]^d\), \(d = 2, 3\). The constructor takes as input a reference to the used triangulation, to retrieve the necessary data, i.e. control points and extraction operators, a list of integers to determine the amount of points used for Gaussian quadrature and a deal.II object UpdateFlags, see Appendix C for a short description of this object, or [4] for a detailed explanation. In summary, we get
Note that we allow different polynomial degrees in every direction, i.e. an-isotropic polynomials can be used for applications.
Similarly, deal.t provides an object TSFaceValues, that represents finite element values on faces. Its syntax and construction is as above.
Similar to the deal.II equivalents, these functions are suited with appropriate reinit() methods, in order to reinitialize finite element values on given cells, resp. faces.
Thus, applications with deal.t are very similar to deal.II, i.e. we get approximately the following lines
Note, that deal.t does not provide a deal.II equivalent to the DoFHandler object. In standard deal.II applications this object does all the work of handling the information about DoFs at each vertex, line, quad, etc. This information is processed already within the TS_Triangulation implementation. An object TS_DoFHandler is already planned out for future implementations. This will enable further deal.II functionality that require an object DoFHandler.
For error estimations we have suited the TS_Triangulation with an internal error estimator for scalar Poisson-like problems, i.e. find \(u:\mathbb {R}^d\rightarrow \mathbb {R}\), s.t.
where \(\sigma :\mathbb {R}^d\rightarrow \mathbb {R}\), \(f:\mathbb {R}^d\rightarrow \mathbb {R}\), \(g_i^N:\mathbb {R}^d\rightarrow \mathbb {R}\), \(i=1,\dots ,n\), \(g_j^D:\mathbb {R}^d\rightarrow \mathbb {R}\), \(j=1,\dots ,m\), \(n,m\in \mathbb {N}\) are given. The error estimator used is given by a standard residual error estimator, i.e. \(\eta _\mathscr {T}:\mathscr {T}\rightarrow \mathbb {R}\) given the Galerkin solution \(u_h\) is defined by
where \(h_\texttt {Q}\) is the diameter of \(\texttt {Q}\) given by
\({\texttt {E}}\) is a face of \(\texttt {Q}\), \(h_{\texttt {E}}\) is the diameter of \({\texttt {E}}\), i.e.
where k is the index s.t. \({\texttt {E}}\) is a singleton. The edge residual \(R_E\) is given by
where n is a normal to the face \({\texttt {E}}= \texttt {Q}\cap \texttt {Q}'\) and \(\llbracket \bullet \rrbracket _{\texttt {E}}= \bullet |_\texttt {Q}- \bullet |_{\texttt {Q}'}\) denotes the jump along the face \({\texttt {E}}\).
The general syntax of the implemented error estimator is given by
Corresponding overloads are given for \(\sigma = 1\) and/or \(n=0\), i.e. no Neumann boundary conditions, by dropping the respective arguments.
Note that if there is at least \(C^1\)-continuity along some face \({\texttt {E}}\), the jump along this face is zero. The implemented error estimator uses the mof-array, described in Sect. 3, to detect \(C^0\)-faces and only computes jumps at those faces. A face \({\texttt {E}}\in \texttt{H}^{(k)}(\mathscr {T})\subset \mathscr {H}^{(d-1)}(\mathscr {T})\) has \(C^0\)-continuity if mof[F] = \(p_{f}\). This follows from the definition of B-splines.
4.2 Manipulation of the mesh
We have provided different functionality to modify the mesh. We have overwritten the base class function execute_coarsening_and_refinement() to perform the refinements of the mesh and execute knot insertion from the subdivided cells. For more information on knot insertion see e.g. [11, 35]. It also computes the coarse neighbourhood of marked cells, as explained in Sect. 2.2. Note that despite the name, we have not yet implemented a proper coarsening algorithm for T-splines, since there is no coarsening algorithm given for T-splines to the authors extent.
We have also overwritten the base class function refine_global(int t) that refines the mesh globally t-times using a simple for loop of declaring the appropriate refinement flags on a cell and then executing the refinement.
For local refinement, the user has to provide a list of cells to be marked for refinement. Internally, upon initialization of a TS_Triangulation, we store an offset off for refinement that ensures that the first refinement is along the longest edge. E.g. if the mesh is given by the cell \(\texttt {Q}= [0, 1] \times [0, 2] \times [0, \tfrac{1}{2}]\) then the cell is longest in the second dimension and the first refinement will be carried out by a 2-orthogonal subdivision to obtain \(\texttt {Q}_1 = [0, 1]\times [0, 1]\times [0, \tfrac{1}{2}]\) and \(\texttt {Q}_2 = [0, 1]\times [1, 2]\times [0, \tfrac{1}{2}]\). Using this offset, the level-dependent subdivision of a cell \(\texttt {Q}\in \mathscr {T}\) becomes the \(k_{\ell (\texttt {Q})}\)-orthogonal bisection of \(\texttt {Q}\) where \(k_{\ell (\texttt {Q})} = 1 + \bigl ((\ell (\texttt {Q}) + \texttt {off}) \text { mod }d\bigr )\).
The user may handle this offset on his own and set the refinement flags manually, however, we also provide a function that considers this. To let the class handle the refinement flags, we use
where we provide a list of cells to be flagged for refinement.
Another important part of isogeometric grid manipulation is degree elevation, see again [11, 35] for details. Degree elevation in this implementation is only allowed at the coarsest level and can be obtained with
where the former elevates the degree of all T-splines by t in direction d and the latter elevates the degree in every direction by t.
4.3 Miscellaneous functions
It may be of interest to print different results of the grid to some files. One possibility from deal.II is to use a GridOut object to print the grid, e.g. to a .svg file. However, this will only print the underlying parametric mesh and not the mapped parametric mesh, which is of more significance for results.
We have provided functions to print quadrature points in the physical domain, grid lines, vertices, and the T-spline functions to ".dat" files.
To print the isoparametric mapping and or the respective T-splines used, we simply use
where we provide a std::string as output file, and a precision that determines how many decimals are being saved. A suffix "_IPF_2d.dat" or "_IPF_3d.dat" is added to the name for the isoparametric mapping, depending on the space dimension. The suffix "_splines.dat" is added to the name of the T-spline file. Note that corresponding folders and sub-folders have to exist. Note further that this function assumes that we are on the parametric Bézier mesh and have already calculated the Bernstein tables with setup_tables.
To obtain the grid lines in the physical domain, we use
where a suffix "_parametric_grid.dat" is added to the declared output name. The new input n_intervals determines the amount of sub-intervals an existing edge is partitioned for the output. Choose a low number if the physical mesh consists of straight lines only, see e.g. Figure 2 and choose a higher number if the mesh has curved parts, see Fig. 8.
To obtain the grid lines in the parametric domain, we use print_grid(...) with the same arguments as before. A suffix "_grid.dat" is added to the declared output name.
To obtain the corresponding grid lines of the Bézier mesh, rerun the above functions on the Bézier mesh, preferably with a different name to ensure the previous outputs are not overwritten. Alternatively, for the parametric Bézier mesh, there is a function print_bezier_grid().
4.4 Limitations
As mentioned before, one big limitation is the restriction of scalar-valued problems.
Note that the algorithm provided by [23] allows q-graded refinement, i.e. the k-orthogonal subdivision of a cell \(\texttt {Q}\) into q equal parts. However, by deal.II limitations this is not possible since multi-level hanging nodes are not implemented.
Further, note that the refinement we carry out within our implementation is anisotropic. There is currently no support for a parallel triangulation when using anisotropic refinements. This, however, does not restrict us from using parallel computations as assembly loops may be divided along processors or threads. It just means that the subdivision of a TS_Triangulation may not be carried out on multiple processors or threads.
The way we designed the TSValues and TSFaceValues objects uses a lot of space to store the necessary T-spline value tables. This can be reduced by further adapting deal.II behaviour, i.e. by reinitializing the tables of a TSValues object on each cell instead of assigning each cell a new object of that type.
5 Experiments
In this section we consider a few examples to use deal.t. Each subsection gives the corresponding problem to be solved, explains the data used for the geometric mapping, and gives an example after some refinement steps. Where necessary for Neumann boundary conditions, the geometric mapping is explicitly stated. Lastly, results are given for various polynomial degrees.
Note that every considered problem uses isotropic polynomial degrees, i.e. \(p_x = p_y (= p_z)\). However, it is technically possible to use anisotropic polynomials. Further, the geometric mapping is given at a base level, and higher polynomial degrees are obtained by order elevation, see [11], and where necessary global refinements are employed to obtain a finer initial mesh.
Further, the resulting linear systems \(K_h u_h = F_h\) are solved using CG-iterations and a diagonal preconditioner. On level \(l>0\) a relative accuracy of \(e_{l-1}\cdot 10^{-4}\) for the iterative solver was chosen, and for level \(l=0\), the relative accuracy was defined as \(10^{-4}\).
5.1 Poisson’s equation on a squished domain
We consider
where the right-hand-side function f and the boundary function g are defined using the exact solution
The physical domain is defined using the data \(\Xi _x = \{0, 0, 0, 1, 1, 1\} = \Xi _y\), \(p = 2\), and controls
The resulting domain is depicted in Fig. 8 together with its boundaries \(\Gamma _N\) and \(\Gamma _D\). This data yields the isoparametric mapping
from which we can define the normal vectors to the boundary \(\overline{\Gamma }_N\) as orthogonal vectors to the tangents \(\partial _x \Phi (x, 0)\) and \(\partial _x \Phi (x, 1)\), since
For error estimation, we use the standard residual error estimator \(\eta (\texttt {Q})\) on a cell \(\texttt {Q}\) given in Sect. 4.1. We use a quantile marking strategy by refining the
cells with the highest errors, where \(\alpha = 0.0075\). The resulting meshes are depicted in Fig. 9. On the left we see the physical mesh for polynomial degree \(p = 3\) after 8 refinement levels. Red dashed lines indicate Bézier extensions and hence the Bézier mesh. On the right, we see the same domain after 15 refinement levels.
We have computed the numerical solution for T-spline degrees \(p = 3,\dots , 7\) and given the \(H^1\)-errors to the exact solution over the degrees of freedom in Fig. 10. Additionally, the \(H^1\)-error using a standard finite element method with \(p=1\) is given. We have performed refinements, until we reached an error smaller than \(10^{-8}\). Note, that in FEM, each new cell yields \((p+1)^d\) new basis functions. Using IGA-FEM with T-splines on the other hand, a bisection of a cell yields \((p+1)^{d-1}\) additional basis functions, assuming the same polynomial degree in every direction. Thus, for a full refinement of a cell, i.e. the cell is bisected once in each direction, we obtain (\(d>1\))
additional basis functions. Since for fixed \(d>1\), the term \(2^d-1\) is constant in p, the number of new basis functions added to the system grows by one order slower.
Note that we consider this example as a benchmark for our implementation. It does not involve any sort of singularities. It is just a short demonstration of deal.t to show its usage as explained in Appendix C and demonstrates how to set Neumann boundary conditions for a manufactured problem on curved domains.
Asymptotically, we obtain optimal convergence rates, as shown in Fig. 10. Note that the resulting linear systems are relatively small compared to systems derived from standard FEM. In fact, the considered example goes as high as around \(10^5\) DoFs for the \(p=3\) case. The other polynomial degrees are below \(10^4\) DoFs. As mentioned in the introduction, for a fair comparison we opted to use a CG-solver with a diagonal preconditioner to solve the linear systems. However, considering the size of the problems, it is more reasonable to use a direct method for T-splines.
This also becomes clear, when considering iteration numbers for the iterative method in Fig. 11. The reason lies in the nature of IGA-FEM, since the basis functions span multiple cells of a triangulation, especially for higher degrees. This results in mass- and stiffness-matrices with low sparsity compared to standard FEM.
As a last note on this subsection, let us explain the procedure for solving this problem using standard FEM. In this case, we have to define a mapping to get the new vertices of the triangulation after refinement, in order to reduce the discretization error at the smooth boundary \(\Gamma _N\). This mapping is know as \(\Phi \). A new vertex after (isotropic) subdivision is then given as
where \(P_0\), and \(P_1\) are two neighbored vertices of a common cell. This includes knowledge of the inverse, \(\Phi ^{-1}\), which in this case is given by
There are also other methods to get an approximation of the boundary vertices, e.g. by assigning certain types of manifolds to it. However, the above method guarantees exact boundary vertices.
5.2 L-Shape domain
We consider again problem (40) on the L-Shape domain \(\Omega _L = [-1, 1]^2 \setminus (-1, 0)^2\). For this setting, we have \(f(x, y) = 0\) and the boundary function g is defined from the exact solution
given in polar coordinates. The solution is symmetric along the diagonal \(y = x\), hence we only give a parametrization of half the L-Shape domain by knot vectors \(\Xi _x = \{0, 0, 1, 1\} = \Xi _y\), degrees \(p = 1\), and control points
The resulting mapping is depicted in Fig. 12. Note that we did not set boundary conditions along the line \(y=x\). An intermediate mesh at level 21 and polynomial degree \(p = 2\) is given in Fig. 13, where symmetry is already employed. Computations began after three initial global refinement steps, yielding eight elements as initial mesh. In general, the initial mesh for computations for any considered polynomial degree p was globally refined \(N_p = \lfloor \tfrac{3p}{2}\rfloor \) times to obtain \(2^{N_p}\) elements before local refinements and error estimations took part.
The marking strategy used for refinement is again the same as in Sect. 5.1. The results for polynomial degrees \(p = 3, \dots , 7\) are given in Fig. 14. Note that in contrast to Sect. 5.1, we refined until we reached 41 (half-)levels, yielding 20 full refinement steps.
Note that the reference lines correspond to \(\mathcal {O}(h^{p+1})\), thus the results exceed optimal convergence. Note also that we beat the optimal convergence rates by up to three magnitudes, e.g. the result for \(p=6\) behaves asymptotically as \(\mathcal {O}(h^9)\). This is most likely due to some symmetry effects of the considered PDE.
For comparison we have solved this problem with standard \(Q_p\) elements for the same polynomial degrees on the whole domain. The results are depicted in Fig. 15. There, we have not run a proper number of global refinements, which can be seen in the plots as well, by a sudden drop of the error. The super-convergent effects mentioned before can be seen here as well. This may be subject to Gaussian quadrature rules used in this example. For simplicity’s sake, we have omitted the reference lines in this plot.
To get an idea about the condition number of the matrix, we take a look at the quotient
where \(k^p_i\) and \(N^p_i\) are the iteration numbers, resp. number of DoFs of the CG-iteration for the basis functions of degree p at level \(i>0\). Take e.g. \(p = 4\), and \(i = 4\). From Fig. 16 we infer
In fact, we can see that
for all \(p= 3,\dots , 7\), and i. This yields
for the condition number \(\kappa (K_{p, h})\) of the stiffness matrix from polynomial degree p.
We finish this subsection with a comparison of DoFs between the two approaches. Note that the data from Fig. 15 was computed on a full parametrization of the L-shape, with three initial quads. In Table 1 we see a direct comparison of the DoFs needed to obtain an error smaller than \(5\cdot 10^{-4}\). Note that T-splines need less DoFs for each polynomial degree.
6 Outlook
We have successfully implemented and demonstrated a framework to use isogeometric analysis within deal.II. Standard error estimators are given in the implementation for different Poisson-like problems, see (35). During applications, the main difficulty for the user is the correct application of Neumann boundary data for the error estimator. In the presented examples, it was necessary to use the explicit definition of the geometric mapping to define the normals on the boundary. This will be automated in future extensions.
We only considered scalar elliptic PDEs in this work, however, in the next step the focus will also shift to vector valued problems, e.g. for linear elasticity. Once this is done, deal.II allows us to extend the results to almost arbitrary PDEs of order two.
Further, note that the considered problems are defined on single patch domains. We will also focus on an implementation of multiple patched domains to apply T-splines with deal.t in a real-world setting with vector valued problems. The source-code is available at [26] with instructions to reconstruct the given examples.
References
Hiniborch R, Morgenstern P (2023) Multivariate analysis-suitable t-splines of arbitrary degree. Comput Methods Appl Math. https://doi.org/10.1515/cmam-2022-0071
Logg A, Mardal K-A, Wells GN (2012) Automated Solution of Differential Equations by the Finite Element Method, vol 84, 1st edn. Lecture Notes in Computational Science and Engineering. Springer, Heidelberg
Kirby RC, Logg A (2006) A compiler for variational forms. ACM Trans Math Softw 32(3):417–444. https://doi.org/10.1145/1163641.1163644
Arndt D, Bangerth W, Davydov D, Heister T, Heltai L, Kronbichler M, Maier M, Pelteret J-P, Turcksin B, Wells D (2021) The deal.II finite element library: design, features, and insights. Comput Math Appl 81:407–422. https://doi.org/10.1016/j.camwa.2020.02.022
Jüttler B, Langer U, Mantzaflaris A, Moore S, Zulehner W (2014) Geometry + simulation modules: Implementing isogeometric analysis. Proc. Appl. Math. Mech. 14(1):961–962 Special Issue: 85th GAMM, Erlangen 2014
Mantzaflaris A (2020) An overview of geometry plus simulation modules. In: Mathematical Aspects of Computer and Information Sciences, pp. 453–456. Springer, Cham. https://doi.org/10.1007/978-3-030-43120-4_35
Dalcin L, Collier N, Vignal P, Côrtes AMA, Calo VM (2016) Petiga: A framework for high-performance isogeometric analysis. Comput Methods Appl Mech Eng 308:151–181. https://doi.org/10.1016/j.cma.2016.05.011
Nguyen VP, Anitescu C, Bordas SPA, Rabczuk T (2015) Isogeometric analysis: An overview and computer implementation aspects. Math Comput Simul 117:89–116. https://doi.org/10.1016/j.matcom.2015.05.008
Kamensky D, Bazilevs Y (2019) tigar: Automating isogeometric analysis with fenics. Comput Methods Appl Mech Eng 344:477–498. https://doi.org/10.1016/j.cma.2018.10.002
Hughes TJR, Cottrell JA, Bazilevs Y (2005) Isogeometric analysis: CAD, finite elements, NURBS, exact geometry and mesh refinement. Comput Methods Appl Mech Eng 194(39):4135–4195. https://doi.org/10.1016/j.cma.2004.10.008
Piegl L, Tiller W (1997) The NURBS Book, 2nd edn. Monographs in visual communication. Springer, Heidelberg. https://doi.org/10.1007/978-3-642-97385-7
Forsey DR, Bartels RH (1988) Hierarchical b-spline refinement. SIGGRAPH. Comput Graph 22(4):205–212. https://doi.org/10.1145/378456.378512
Forsey DR, Bartels RH (1995) Surface fitting with hierarchical splines. ACM Trans Graph 14(2):134–161. https://doi.org/10.1145/221659.221665
Giannelli C, Jüttler B, Speleers H (2012) THB-splines: The truncated basis for hierarchical splines. Computer Aided Geometric Design. 29(7):485–498. https://doi.org/10.1016/j.cagd.2012.03.025 Geometric Modeling and Processing 2012
Sederberg TW, Zheng J, Bakenov A, Nasri A (2003) T-splines and T-NURCCs. ACM Trans Graph 22(3):477–484. https://doi.org/10.1145/882262.882295
Bazilevs Y, Calo VM, Cottrell JA, Evans JA, Hughes TJR, Lipton S, Scott MA, Sederberg TW (2010) Isogeometric analysis using t-splines. Computer Methods in Applied Mechanics and Engineering 199(5):229–263. https://doi.org/10.1016/j.cma.2009.02.036 Computational Geometry and Analysis
Dörfel MR, Jüttler B, Simeon B (2010) Adaptive isogeometric analysis by local h-refinement with T-splines. Computer Methods in Applied Mechanics and Engineering. 199(5):264–275. https://doi.org/10.1016/j.cma.2008.07.012 Computational Geometry and Analysis
Buffa A, Cho D, Sangalli G (2010) Linear independence of the T-spline blending functions associated with some particular T-meshes. Comput Methods Appl Mech Eng 199(23):1437–1445. https://doi.org/10.1016/j.cma.2009.12.004
Analysis-suitable T-splines are dual-compatible (2012) Beirão da Veiga, L., Buffa, A., Cho, D., Sangalli, G. Computer Methods in Applied Mechanics and Engineering 249–252:42–51. https://doi.org/10.1016/j.cma.2012.02.025 Higher Order Finite Element and Isogeometric Methods
Dokken T, Lyche T, Pettersen KF (2013) Polynomial splines over locally refined box-partitions. Comput Aided Geometric Design 30(3):331–356. https://doi.org/10.1016/j.cagd.2012.12.005
Johannessen KA, Kvamsdal T, Dokken T (2014) Isogeometric analysis using lr b-splines. Comput Methods Appl Mech Eng 269:471–514. https://doi.org/10.1016/j.cma.2013.09.014
Beirão da Veiga L, Buffa A, Sangalli G, Vázquez R (2013) Analysis Suitable T-splines of arbitrary degree: Definition, linear independance and approximation properties. Mathematical Models and Methods in Applied Sciences 23(11), 1979–2003. https://doi.org/10.1142/S0218202513500231
Morgenstern P (2016) Globally structured three-dimensional analysis-suitable T-splines: Definition, linear independence and \(m\)-graded local refinement. SIAM J Numer Anal 54(4):2163–2186. https://doi.org/10.1137/15M102229X
Morgenstern P (June 2017) Mesh refinement strategies for the adaptive isogeometric method. PhD thesis, Friedrich-Wilhelm-University Bonn. https://hdl.handle.net/20.500.11811/7237
Buffa A, Gantner G, Giannelli C, Praetorius D, Vázquez R (2022) Mathematical foundations of adaptive isogeometric analysis. Arch Comput Methods Eng 29(7):4479–4555. https://doi.org/10.1007/s11831-022-09752-5
Hiniborch R (2024) deal.t: An implementation of TSplines within deal.II. Zenodo. https://doi.org/10.5281/zenodo.7994627
Li X, Zheng J, Sederberg TW, Hughes TJR, Scott MA (2012) On linear independence of T-spline blending functions. Computer Aided Geometric Design 29(1), 63–76. https://doi.org/10.1016/j.cagd.2011.08.005. Geometric Constraints and Reasoning
da Veiga LB, Buffa A, Sangalli G, Vázquez R (2014) Mathematical analysis of variational isogeometric methods. Acta Numerica 23:157–287. https://doi.org/10.1017/S096249291400004X
Mantzaflaris A (2021) ..., others (see website): G+Smo (Geometry plus Simulation modules) v21.12. http://github.com/gismo
Scroggs MW, Dokken JS, Richardson CN, Wells GN (2022) Construction of arbitrary order finite element degree-of-freedom maps on polygonal and polyhedral cell meshes. ACM Transactions on Mathematical Software. https://doi.org/10.1145/3524456. To appear
Scroggs MW, Baratta IA, Richardson CN, Wells GN (2022) Basix: a runtime finite element basis evaluation library. Journal of Open Source Software 7(73), 3982. https://doi.org/10.21105/joss.03982
Arndt D, Bangerth W, Blais B, Fehling M, Gassmöller R, Heister T, Heltai L, Köcher U, Kronbichler M, Maier M, Munch P, Pelteret J-P, Proell S, Simon K, Turcksin B, Wells D, Zhang J (2021) The deal.II library, version 9.3. Journal of Numerical Mathematics 29(3), 171–186. https://doi.org/10.1515/jnma-2021-0081
Borden MJ, Scott MA, Evans JA, Hughes TJR (2011) Isogeometric finite element data structures based on bézier extraction of nurbs. Int J Numer Methods Eng 87(1–5):15–47. https://doi.org/10.1002/nme.2968
Scott MA, Borden MJ, Verhoosel CV, Sederberg TW, Hughes TJR (2011) Isogeometric finite element data structures based on bézier extraction of T-splines. Int J Numer Methods Eng 88(2):126–156. https://doi.org/10.1002/nme.3167
Cottrell JA, Hughes TJR, Bazilevs Y (2009) Isogeometric Analysis. John Wiley & Sons, Ltd, West Sussex. https://doi.org/10.1002/9780470749081
Hiniborch R (2023) Correction on Bezier extraction of TSplines. Zenodo. https://doi.org/10.5281/zenodo.7994448
Funding
Open Access funding enabled and organized by Projekt DEAL.
Author information
Authors and Affiliations
Corresponding author
Ethics declarations
Financial or non-financial interests
The first author acknowledges the Deutsche Forschungsgemeinschaft (DFG) under Germany Excellence Strategy within the Cluster of Excellence PhoenixD (EXC 2122, Project ID 390833453). The authors have no relevant financial or non-financial interests to disclose.
Additional information
Publisher's Note
Springer Nature remains neutral with regard to jurisdictional claims in published maps and institutional affiliations.
Appendices
Bézier extraction algorithm of TSplines
The code stated in Algorithm 5 is a direct application of the algorithm provided by [34] with two corrections. The function below computes the extraction operator rows used for a single B-Spline over each knot-interval and inserts additionally some interior knots provided by knot_in. The function compute_extended_kv() returns the extended knot vector of a given local knot vector by adding the first and last knots until multiplicity p is reached, e.g. compute_extended_kv([0 0 1 2]) returns Ubar = [0 0 0 1 2 2 2], nf = 1 for one insertion at the front, and ne = 2 for two insertions at the end.
Originally, the special case for the multiplicity of a knot (mult == p) has not been handled, see Algorithm 65 and following, as well as properly initializing the new extraction operator row, see Algorithm 36. The code can also be found online at [36].
Important deal.II data structures
To fully understand parts of Algorithm 3 and it is important to learn deal.II internal indexing of quads, lines, and vertices of a cell. For a fixed cell \(\texttt {Q}\) the vertices \(\texttt {V}_i\) are numbered in lexicographic ordering, i.e.
Its faces \(\texttt {F}_i\) are ordered according to its outward-pointing normals, i.e.
where \(n_\texttt {Q}(\texttt {F})\) is the outward pointing normal of face \(\texttt {F}\) from cell \(\texttt {Q}\), e.g. for a 2D reference cell we get the normal vectors \(-e_1,+e_1,-e_2,+e_2\) (in that order).
This defines the ordering of vertices, lines in 2D, and quads in 3D. The order of lines in 3D is as follows. From a cell \(\texttt {Q}\), we first enumerate the lines of the face \(\texttt {F}= \texttt {Q}_1\times \texttt {Q}_2\times \{ \inf \texttt {Q}_3 \}\) using 2D line ordering, resp. (58). Then we enumerate the lines of face \(\texttt {F}= \texttt {Q}_1\times \texttt {Q}_2\times \{ \sup \texttt {Q}_3 \}\) using 2D line ordering, and finally the lines in z-direction are enumerated in lexicographic ordering.
A visual explanation is given in Fig. 17.
Further, Algorithm 3 and use neighbor relations between cells. A neighbor neighbor\((\texttt {Q}, \mathtt {face\_no})\) of a cell \(\texttt {Q}\) at face \(\texttt {F}_{\mathtt {face\_no}}\) is defined to have at maximum the level of refinement as the cell itself, i.e.
Let \(\texttt {Q}_1\) and \(\texttt {Q}_2\) be two adjacent cells, with distinct refinement levels. If \(\texttt {Q}_1\) is coarser than \(\texttt {Q}_2\) and we ask for its neighbor on the other side of the common face \(\texttt {F}= \partial \texttt {Q}_1 \cap \partial \texttt {Q}_2\), then \(\texttt{parent}(\texttt {Q}_2)\) with the same refinement level of \(\texttt {Q}_1\) is returned; if the parent of \(\texttt {Q}_2\) has a higher refinement, the parent of the parent is returned, and so on. On the other hand, if we ask for the neighbor of \(\texttt {Q}_2\) at face \(\texttt {F}\), simply \(\texttt {Q}_1\) is returned, in which case neighbor and cell have different refinement levels.
A detailed example is given in Fig. 18. Consider first the two (active) adjacent cells \(\texttt {Q}_3^{(0)}\) and \(\texttt {Q}_4^{(2)}\). Obviously, for the first neighbor of \(\texttt {Q}_4^{(2)}\) we get \(\texttt {Q}_3^{(0)}\), i.e.
However, on the other side of face 0 from \(\texttt {Q}_3^{(0)}\) are two distinct cells, and it is arbitrary to return either as zeroth neighbor. Instead of returning either of the children of \(\texttt {Q}_2^{(1)}\), the parent with refinement level 0 is returned, that is the parent \(\texttt {Q}_2^{(0)}\) of the parent \(\texttt {Q}_2^{(1)}\) of the cell \(\texttt {Q}_4^{(2)}\), and hence we get
Similar arguments for \(\texttt {Q}_3^{(1)}\) and \(\texttt {Q}_3^{(0)}\) lead to
A tutorial for deal.t
We demonstrate the usage with the example from Sect. 5.1.
Since all deal.II and deal.t functions and classes are in a namespace dealii, resp. dealt we firstly import the respective namespaces into our program. with the following lines
We define a new class for each new application. Further classes are introduced to resemble abstract mathematical functions. This incorporates deal.IIs Function<dim, spacedim> object that resembles an abstract base class from which every (mathematical) function should be derived from. Since this object is an abstract class, every derived class needs to define its own functions to calculate values, derivatives, etc.
The following subsections give the source code for the application to the above problem with sufficient explanations between relevant lines. Each source code is numbered within its own block, i.e. a function may be split in two or more parts, but the whole function is numbered consecutively line-by-line.
1.1 Writing a C++ class for each problem
The data for the isogeometric mapping is stored within an additional object from the deal.t namespace, called IPF_Data<int dim, int spacedim>. It essentially stores only the provided (global) knot vectors, the corresponding degrees, as well as the control points. For this purpose, the parametric dimension dim is two, as well as the physical dimension spacedim. Further, it is important to store the (parametric) mesh used for applications. This is the main part of deal.t and is called TS_Triangulation<int dim, int spacedim>
Depending on what the user wishes to accomplish, there is a variety of additional variables the user may choose to store. For this demonstration we intend to calculate
-
1.
the stiffness matrix \(\underline{K}_h\),
-
2.
the right-hand-side \(\underline{F}_h\)
-
3.
the solution vector \(\underline{u}_h\), s.t. \(\underline{K}_h \underline{u}_h = \underline{F}_h\),
-
4.
the exact \(L_2\)-error \(\Vert u - u_h\Vert _{L_2(\overline{\Omega })}\),
-
5.
the exact \(H^1\)-error \(\Vert u - u_h\Vert _{H^1(\overline{\Omega })}\), and
-
6.
the residual error \(\Vert \Delta u + f \Vert _{L_2(\texttt {Q})}\) for each cell \(\texttt {Q}\) of the triangulation.
Since the size of the stiffness matrix is usually large, it is defined as a dealii::SparseMatrix<double>, which has to be initialized using a sparsity pattern.
The implementation is encapsuled in a separate namespace to avoid ambiguity with similar names along other namespaces. This is done by
The right-hand-side function f is stored in a separate class and derived from deal.II Function<dim, spacedim> as mentioned before, i.e.
To compute the exact \(L_2\)- and \(H^1\)-errors, we define a class for the exact solution which is again derived from deal.II Function<dim, spacedim>, but also needs to compute the gradient. Hence, its header declaration is extended by an override of the base gradient function.
We further need another object to describe values along the Neumann boundary \(\Gamma _N\). This is done by yet another deal.II inherited function as above similar to Poisson_RHS, called Poisson_NC that will resemble the boundary values. The header declarations are the same as in Poisson_RHS with exchanged names; thus we will not display the code here. We will also skip the implementations of the values and gradients for these classes in this subsection.
For the main class, we get from the explanations above the following member variables.
Here, ref determines the amount of refinement steps and order determines for which (global) degree we want to compute the solution. The remaining variables mesh_size and dofs_per_level store the smallest cell diameter, resp. the DoFs at level l. For the diameter we define
where \(h_\texttt {Q}\) is the diameter of a cell \(\texttt {Q}\) defined in (37).
Next, we need to determine the methods, resp. functions, we want to employ. Firstly, we need a public constructor for the class that initializes every necessary variable. It should take the variables ref, and order and store them for use in the program. We declare the constructor as
To prevent mistakes on the user side, only a single function is allowed to be called outside the class. That function calls other functions from this class in the right order. It does not need any input arguments, as it is supposed to simply start the program. It is declared as
Every other function is declared private. For a smooth initialization of the triangulation, we figured it is the best technique to write a function that returns the correct IPF_Data<2, 2>, i.e.
The source for this function is omitted in this paper.
There are a few steps that have to be done after every refinement:
-
1.
The system to be solved has to be setup to the correct dimensions,
-
2.
The system has to be assembled,
-
3.
The system has to be solved after assembly,
-
4.
After the system is solved, we have to compute the \(L_2\)- and \(H^1\)-errors,
-
5.
important quantities shall be printed to files
-
6.
additionally, we may be interested in the point-wise error for each cell,
-
7.
The cells have to be marked and refined for further computations.
These tasks are split into their respective subroutines,
1.2 Creating the grid
Most of the variables introduced in subsection C.1 can be initialized before the actual source code is given. This follows the syntax A(int a): var(a)... and reads in the source code of the main class as
Note that Algorithm 5 initializes the TS_Triangulation.
Firstly, we increase the initial polynomial degree according to the user input across every direction using
There is also a function degree_elevate(int d, int times) that increases the polynomial degree only in a specified direction. Note that the implementation makes no restrictions on the polynomial degrees, i.e. anisotropic polynomials are a possibility.
Next, we will define the boundary indicators using deal.II internal iterators over faces. From the base class Triangulation<dim, spacedim> we can use inherited functions for our derived class TS_Triangulation<dim, spacedim>. Figure 8 shows which parametric boundary is mapped to which physical boundary. From this, and the definition of \(\Xi _x\) and \(\Xi _y\) we get that the parametric Neumann boundary is given at faces whose second coordinate is either zero or one. Thus, the source code for the boundary indicators is
Where an iterator can be thought of as a pointer to some object resembling an active face. It is dereferenced using the arrow notation-> . Note that initially every boundary id of each face is given by zero.
In standard finite element code, boundary DoFs are recognized during assembly, as we define the same finite element functions for each cell. Using T-Splines this is not possible, and in fact we have to preemptively determine every spline that has non-zero values on the boundary. This is done in the following line
where the boundary DoFs are sorted by boundary ids, i.e. internally within tria we save a container boundary_dofs that encodes a boundary id to a list of boundary DoF indices.
Since we wish to perform computations on this mesh, we have to tell the program that it should switch from the parametric mesh to the parametric Bézier mesh with a call to
At this point, this function essentially just sets a bool which enables functionality we will use for matrix assembly. However, later on this will perform one additional refinement step by subdividing active cells along T-junction extensions. Since the program is now on the parametric Bézier mesh, it can compute extraction operators used for Bézier extraction. This information is computed using
After this, we can initialize the other quantities of interest to the right size as follows
1.3 System setup
The routine
should initialize the right-hand-side vector \(F_h\), the solution vector \(u_h\), and the stiffness matrix \(K_h\) to the correct size. Remember that to this extent the finite element space is \(\{T_i \circ \Phi ^{-1}\}_{i}\) for each T-Spline \(T_i\) and that the support of each T-Spline spans multiple parametric cells. The number of active splines of the mesh thus sets the dimensions of the system. Since \(F_h\) and \(u_h\) are simply vectors, they are initialized with
The stiffness matrix has only non-zero entries (i, j), whenever \({{\,\textrm{supp}\,}}T_i \cap {{\,\textrm{supp}\,}}T_j \ne \varnothing \). This information is cell-wise stored within IEN_array and is returned by
For a given cell it returns which global DoF has support on it. There is also an overload of this function that takes a cell as input, to return the vector of local DoFs on the specified cell. This will be used in the assembly routine.
Next, the sparsity pattern of the sparse stiffness matrix has to be defined. From deal.II, a SparsityPattern object can be reinitialized using a call to reinit(int n, int m, int nnz), where n corresponds to the rows of the sparse matrix, m corresponds to the columns of the sparse matrix and nnz correspond to the maximum number of non-zero support on each row. Unfortunately, we are lacking a proper estimate on this quantity, hence we set this number to the maximum, i.e.
We then have to add information about which entries are actually non-zero. This is done using add(int i, int j) that allows to add entries at position (i, j). The complete information over all non-zero supports is given by a few nested for-loops
Before we use this pattern to initialize the system matrix \(K_h\), it has to free superfluous space by compressing the data as much as possible. Only then can it be used to define the system matrix
1.4 Matrix assembly and solving
The main part of the implementation is the matrix assembly in
The implementation of T-Splines is based on Bézier extraction. We can then define tables for the Bernstein polynomials on the reference cell \(\texttt {Q}^{(ref)} = [0, 1]^d\) a-priori and use these tables to define the values of a specific T-spline on a cell. Values on the reference cell are computed using Gaussian quadrature rules. Before we start the computations, we thus have to generate objects that represents the finite element values on cells, resp. faces. This is done using the TSValues and TSFaceValues objects. They mimic the behavior of their deal.II variants as explained in 4, and are initialized in the application as
Since it is costly, to write into the whole stiffness matrix \(K_h\), we define a smaller matrix that stores the integral values for a specific cell together with a smaller right-hand-side vector.
Afterwards we can assemble the stiffness matrix cell-by-cell using a loop over cell iterators of the triangulation
At the beginning of each iteration, we have to get the corresponding values of T-splines on this cell with
and reset the small cell matrix and rhs with
Then we start the quadrature sum with
We initially store
as it is used in two distinct places. To compute the cell-matrix at the given quadrature point q, we have to calculate
and add that to position i and j of \(K_h\). This will be done later, instead we store it in the cell matrix using local DoF indices.
Further, the values to the local right-hand-side have to be computed using
which is done in a similar loop
and ends the quadrature loop. To get the Neumann values, we have to check, whether or not the cell is on the boundary and then check if a face of that cell is at the Neumann boundary. For this purpose, we have set the boundary indicators during the creation of the mesh. The code-block for the Neumann values then becomes similar to the code-block for a cell, where we calculate
with
where n(q) is the normal vector at quadrature point q. This gives
This almost ends the cell-loop. We still have to add the computed values to the global system. For this, we need information about the global indices of the used local indices. The standard in deal.II is to use a function copy_local_to_global() that does the trick automatically together with applying Dirichlet boundary conditions, however, it assumes background information we cannot provide at this point. Hence, we have to do it manually. The local cell information is copied to the global matrix using the IEN_array as before, with
To end the matrix assembly, we have to apply Dirichlet conditions. In this case, we have homogeneous boundary conditions, which simplifies the process. For Dirichlet boundary DoFs we simply set the corresponding column and row to 0, add a 1 to the diagonal and set the right-hand-side to 1:
To solve the system, we use a standard deal.II solver. We have to define an object SolverControl that defines the maximum number of iterations and a tolerance and give it to the constructor of a solver. For this application, we chose a CG-solver. The solve() function of the solver takes the stiffness matrix \(\underline{K}_h\), the solution vector \(\underline{u}_h\), the right-hand-side \(\underline{F}_h\), and a precondition matrix which is chosen to be the diagonal of \(\underline{K}_h\), i.e. a Jacobi-type preconditioner. The complete routine is
1.5 Error estimation and marking
The residual error estimation from
defines a list of – possibly in-active – cells to be marked for refinement
Note that matrix assembly, error calculations and error estimations are performed on the parametric Bézier mesh \(\mathscr {B}(\Omega )\), while the mesh refinement itself has to be performed on the parametric mesh \(\mathscr {T}(\Omega )\). This stems from the definition of anchors and their knot vectors on the parametric mesh. Finite element values, however, are given on the parametric Bézier mesh. If the error estimator detects a parametric Bézier cell to be refined, instead we save its parent in the temporary list mark. A special case occurs if there is only a single active cell. In that case, we simply skip error estimation and save the single cell
Otherwise, we need to access the local residuals for each cell. The core implementation of deal.t offers some functions to calculate the residual errors of Poisson-like problems, see Sect. 4.1.
For this example, we have \(\sigma \equiv 1\) with in-homogeneous Neumann boundary conditions. The list of local residuals is simply defined by
that will later return the residual of a cell iterator cell by simply calling local_residuals[cell]. Further, the Neumann data is stored in a similar list, that encodes a boundary id to a given function. This enables the error computations of problems with multiple Neumann functions \(g^N_i\) at different Neumann boundaries \(\Gamma ^N_i\). For our purpose we thus get
Note that we use a pointer to the different boundary conditions. We then call the corresponding error estimator with
The marking strategy is explained in detail in Sect. 5.1. For this, we define a list of all cells for which we have a local residual error, i.e. every active cell, then sort the list in descending order, such that the local residual of cell i is greater than the local residual of cell j if i<j. We get
Next, we need a list that tells us which cell of the mesh was refined to obtain the parametric Bézier mesh. These elements are stored separately within a TS_Triangulation and can be obtained by get_bezier_elements(). It returns a std::vector that stores (in-active) parent cells of refined cells during refine_bezier_elements(). The total amount of active parametric cells (without refined Bézier cells) is given by the number of active cells subtracting the number of Bézier cells. Before we push a cell into the container of marked elements, we have to check if its parent has been refined during the transition from parametric mesh to the parametric Bézier mesh.
The next few calls are essential when working with our code. We can not simply refine the marked cells, since there may be parent elements in this list. Further, we already stressed the fact that refinement is only possible on the parametric mesh. Hence, we have to coarsen the previously refined Bézier cells with a call to
This additionally sets a boolean within TS_Triangulation that allows further refinements. The user may now decide whether to set the refinement flags manually, or let the TS_Triangulation set the refine flags of the cells. We encourage the user to use the line
As there are some internal processes at the setup of a TS_Triangulation which the user has to consider when setting refinement flags manually, see section 4.
Now, that cells are marked for refinement, we can perform refinement. Note that the program will usually refine not only the marked cells, but also computes its coarse neighbourhood from [24] to assure analysis suitability, and hence linear independence, of T-Splines, see Sect. 2.2. The refinement process is then essentially carried out using
where the algorithms described in Sect. 2.2 are used to determine the next level. Note that, despite the name, coarsening of cells is not yet implemented—the name for this function had to be inherited from the base class.
Next, we first use the parametric mesh, to define the list of boundary DoFs, as in the initialization of this mesh
then, we have to switch back to the parametric Bézier mesh using the opposite function of coarsen_bezier_elements()
that essentially performs another refinement step of the mesh. Finally, we have to recompute the extraction operators for the new T-Splines with
This ends the estimate_and_mark() routine.
The most important function
will then call these functions in the correct order, i.e.
where the function print_error() prints the point-wise error to some files, output_system() prints the stiffness matrix, the right-hand-side, the solution, the mapped (Bézier) mesh wireframe, and the mapped quadrature points on the physical domain, i.e. the mapping \(\Phi \), to distinct files, and compute_h1_error() computes the \(H^1\) and \(L_2\) error of the solution to another file. We do not use deal.II functions to output the mesh with a GridOut object, as it again imposes conditions on the system matrix we cannot provide at this point, e.g. the dimension of the matrix being \(n_B\cdot n_C\), where \(n_B\) is the number of Bernstein polynomials on the reference cell and \(n_C\) is the number of active cells.
The errors computed in print_error() and compute_h1_error() are computed using a similar technique as for the matrix assembly in assemble_system() and are hence not given here. Further, the output commands from output_system are rather tedious and not considered important, and hence also not given here.
Rights and permissions
Open Access This article is licensed under a Creative Commons Attribution 4.0 International License, which permits use, sharing, adaptation, distribution and reproduction in any medium or format, as long as you give appropriate credit to the original author(s) and the source, provide a link to the Creative Commons licence, and indicate if changes were made. The images or other third party material in this article are included in the article's Creative Commons licence, unless indicated otherwise in a credit line to the material. If material is not included in the article's Creative Commons licence and your intended use is not permitted by statutory regulation or exceeds the permitted use, you will need to obtain permission directly from the copyright holder. To view a copy of this licence, visit http://creativecommons.org/licenses/by/4.0/.
About this article
Cite this article
Beuchler, S., Hiniborch, R. & Morgenstern, P. Deal.t: an implementation of multivariate analysis suitable T-splines within the deal.ii framework. Engineering with Computers (2024). https://doi.org/10.1007/s00366-024-02002-1
Received:
Accepted:
Published:
DOI: https://doi.org/10.1007/s00366-024-02002-1