4.1 Outline of the method
The right part of Fig. 1 presents the workflow of our boundary layer mesh generation method. The inputs include the surface triangulation of the domain boundary and some user parameters (such as h_{0}, μ and n_{l}). A list of front faces L_{f} and a list of front nodes L_{n} are maintained during the entire workflow. Accordingly, flags are attached to the active front nodes and faces to distinguish them from others.
Initially, L_{f} and L_{n} are filled in with those input surface elements and surface nodes located on the viscous walls, respectively. After that, four steps are consecutively followed to create the boundary layer mesh: (1) computing marching directions, (2) computing marching distances, (3) creating a layer of elements and (4) updating L_{f} and L_{n}. To ensure the reliability of the algorithm and the validity and usability of the output mesh, the intermediate outputs of the former three steps are checked carefully.
In the following subsections, we will present the algorithmic details of the four main steps.
4.2 Computing marching directions
The computation of marching directions is based on the classification of front nodes. A front node is labelled as flat if the maximal angle is smaller than 5 degrees between any two normals of the faces connected to the node. For those unlabelled nodes, a further classification is conducted by computing the average of angles between neighbour face normals. Here, the average angle is denoted by β, and approximately, front nodes are classified as concave or convex ones by their β values.
For flat nodes, its marching direction is computed by a simple average of all neighbouring normals. For other nodes, three strategies are combined to set up a costeffective scheme for marching direction computation:
Strategy I. Compute the normals of faces connected to a given front node and classify them into groups such that the number of groups is as small as possible under the condition that the maximal angle is smaller than 25 degrees between any two normals belonging to the same group. For each group, a representative normal is computed by averaging all normals belonging to the group. The normal at the front node is exactly the average of all representative normals.
Strategy II. Compute the marching vector lying on the bisection plane of the two faces on the manifold forming the wedge with the smallest angle. The location of the marching vector on that plane is evaluated by bisecting the visibility region on that plane [6]. As is shown in Fig. 2, the visibility region is represented by a polyhedral cone extending outward from the point, and it can be simplified into a visibility cone with the circular cross section and halfcone angle, which can also be called the visibility angle β_{i}.
Strategy III. This is an iterative algorithm aimed at finding the ‘most normal’ normal, i.e., the normal that minimizes the maximal angle with the given set of normals [16]. Weights are given to each face normal depending on the angle created with the current normal. If the angle is high, more weight is given to the normal. See [16] for a pseudo code of this implementation.
To balance the tradeoff between efficiency and robustness, the above strategies are conducted in the order listed above. The quality of the normal at the front node is evaluated by the maximal angle between the normal and normals of manifold faces. A hillclimbing scheme is used to ensure the optimal normal is kept always. Meanwhile, the next level of strategy gets no opportunity in order to save computing time when the quality of the present ‘optimal’ normal is less than 30 degrees.
After computing the initial marching directions, a further smooth is executed to ensure a desirable variation across the front and facilitate the following marching process. Here, the smooth is performed by a weighted Laplacian approach, i.e.,
$$ {\boldsymbol{N}}_i^n=\left(1\omega \right){\boldsymbol{N}}_i^{n1}+\frac{\omega }{\sum_j{w}_{ij}}{\sum}_j\left({w}_{ij}\right){\boldsymbol{N}}_j^{n1}, $$
(1)
where \( {\boldsymbol{N}}_i^{n1} \) and \( {\boldsymbol{N}}_i^n \) are normals at front node p_{i} after n and n1 iterations, \( {\boldsymbol{N}}_j^{n1} \) is the normal at neighbouring front node p_{j} after n1 iterations, and w_{ij} is the weight defined at p_{j}. Note that it is beneficial to let normals at convex corners be closer to their neighbours, and vice versa for concave corners. To achieve this, n_{ij} is defined as below,
$$ {w}_{ij}=\left\{\begin{array}{cc}{k}_{ij}^2& {p}_i\ \mathrm{is}\ \mathrm{a}\ \mathrm{convex}\ \mathrm{point}\\ {}1/{k}_{ij}^2& {p}_i\ \mathrm{is}\ \mathrm{a}\ \mathrm{concave}\ \mathrm{point}\\ {}1& \mathrm{otherwise}\end{array}\right., $$
(2)
where
$$ {k}_{ij}=n\frac{d_{ij}}{\sum_{j=1,n}{d}_{ij}} $$
and d_{ij} is the distance between p_{i} and p_{j}.
There are cases where one single normal could not ensure the validity of visibility cone. A multinormal strategy is adopted in these cases. Interested readers are referred to [33] for more details.
To ensure the validity of the computed marching directions, for each marching direction, we check whether it is visible to all the front faces adjacent to the front nodes. If no valid marching direction can be defined at a front node, we stop propagating the front node and clear the ‘front’ flag attached to that node.
Figure 3 presents the computed marching directions in the neighbourhood of two typical corners. It can be seen that the computed directions are reasonable and no abrupt changes occur between neighbouring marching directions.
4.3 Computing marching distances
Suppose p_{i} is a front node, u_{i} is the marching direction at p_{i}, and m is the present layer number. The marching distance at p_{i} can be computed by
$$ {h}_i={\mu}^{m1}{h}_0. $$
(3)
If simply applying the above Equation in all front nodes, the computed marching sizes would be the same. As a result, the front will conduct a socalled advective motion at expansion. As illustrated in Fig. 4, wavefrontal motion may be more desirable than advective motion because it would smooth out convex or concave corners, whereas the advective motion would preserve them. To implement the effect of a wavefrontal motion, a coefficient α can be added such that the distances at the convex corners need to be shortened and the marching distances at the concave corners need to be lengthened.
$$ {h}_i=\alpha {\mu}^{m1}{h}_0, $$
(4)
where
$$ \alpha =\left\{\begin{array}{cc}1+\left\mathit{\cos}{\beta}_i\right& \beta <180{}^{\circ}\\ {}1\left\mathit{\cos}{\beta}_i\right& \beta \ge 180{}^{\circ}\end{array}\right.. $$
Another geometric factor that impacts the computation of marching distances is the gap between opposite viscous boundaries. Given a front node p_{i} and a direction starting from p_{i} to domain interiors, a minimal distance between p_{i} with viscous walls is computed first, denoted by \( {d}_i^G \). Denote the total height of viscous boundary at p_{i} by
$$ {l}_i={\sum}_{m=1,{n}_l}\alpha {\mu}^{m1}{h}_0. $$
(5)
If \( {d}_i^G>2{l}_i \), no further modification is required on the marching distance at p_{i}; otherwise, the height of the first viscous layer at p_{i} is computed by
$$ {h}_{i,0}=\frac{\varepsilon_1\times {d}_i^G}{\sum_{m=1,{n}_l}{\mu}^{m1}}, $$
(6)
where ε_{1} ∈ [0,0.5] is a user parameter that determines how large the space is left for unstructured elements (less is more).
Apparently, the computation of \( {d}_i^G \) should be speeded up with the aid of a spatial data structure. Here, we reuse the octree grid for speeding up front intersection checks to compute \( {d}_i^G \). Algorithm 1 presents the setup procedure of this octree grid, and Algorithm 2 presents the computation of \( {d}_i^G \) based on the grid.
In Algorithm 2, variable H_{i} is a prediction of the local height of boundary layer elements enumerated from front node p_{i}. Assuming h_{0} is the height of the first layer, μ is the ratio of heights of neighbouring layers, m_{i} is the possible layer number at p_{i}, then
$$ {H}_i={\sum}_1^{m_i}{\mu}^{m1}{h}_0. $$
(7)
Here, both h_{0} and μ are specified by the user, and m_{i} meets the equation as below under the requirement of stopping front propagation when the shapes of prisms are nearly isotropic:
$$ {S}_i\approx {\mu}^{m_i1}{h}_0. $$
(8)
Here S_{i} is the average length of surface edges connecting to p_{i}.
By adding Eq. 8 to Eq. 7, we can finally get
$$ {H}_i\approx \frac{S_i\mu {h}_0}{\mu 1}. $$
(9)
After executing Algorithm 2, a smoothing procedure is executed to avoid the abrupt changes of neighbouring marching directions. A simple Laplacian smoothing is presently employed.
4.4 Creating a layer of elements
For each front node qualified for propagation, we can compute its dual by marching it along the marching direction. After that, we can create a layer of prismatic elements by connecting all the front nodes qualified for propagation and their duals.
Lowquality elements may be created in this step, in particular in the vicinity of concave corners. In this study, we selected scaled aspect ratio [22] to evaluate the quality of a prism. This quality measure in effect combines the measures of triangle shapes and edge orthogonality. For a given prism τ, denote the scaled aspect ratio of this element by ρ(τ) (ρ(τ)∈[− 1,1]). ρ = 1 indicates an ideal prism, and ρ < 0 indicates an inverted element.
After creating a layer of elements, we pick those elements whose quality values are below 0.1 for removal. Meanwhile, we stop propagating the front faces that carry those elements.
4.5 Updating the front
The mesh nodes on viscous walls are regarded as the initial front nodes. Correspondingly, those fronts composed of the initial front nodes are regarded as the initial propagating fronts. As the propagation of nodes continues layer by layer, the propagating fronts are updated according to the propagating behaviour of their forming nodes. In this study, three stopping criteria are applied to each front node:

(1).
The current layer number of the node is equal to the prescribed maximum number of boundary layers, i.e., i_{l} = n_{l};

(2).
At each node of a newly generated element e, the scaled aspect ratio (denoted by ρ(p) hereafter) [22] is computed, and the propagation will stop when ρ(p) < 0;

(3).
The new front faces starting from the front node are involved in global intersections;

(4).
If one of the neighbouring nodes of the current node in the previous layer is set to stop propagating, then the propagation of that node is stopped.
Criterion 1 is straightforward and ensures the termination of the boundary layer mesh generation procedure. Criterion 2 avoids the generation of elements with negative signed volumes. Figure 5a presents a node p_{1} with ρ(p_{1}) < 0, which leads to an inverted element. Criterion 3 is used to avoid global intersection. Criterion 4 is only executed for 3D cases and ensures the difference in the layer numbers of the neighbouring nodes does not exceed one. In 2D problems, the exposed segments after boundary layer mesh generation are sent to a triangular mesh generator, and the difference in the layer numbers of the neighbouring nodes will not affect the resulting mesh quality. However, in 3D problems, the exposed faces include triangles and quadrilaterals, and some transition elements should be added to hide the quadrilateral faces before the exposed faces are sent to a tetrahedral mesh generator. If the difference in the layer numbers of the neighbouring nodes is allowed to be larger than one, stretched pyramids will be added as the transition elements. Figure 5b presents the added transition elements for two different cases.
Let F be one of the current propagating fronts and p_{i} (i = 0, 1, 2) be the forming nodes of that front. If p_{i} (i = 0, 1, 2) is propagated to a new position, i.e., p^{’}_{i} (i = 0, 1, 2), then F is propagated to F^{′}, with p^{’}_{i} as its forming nodes. In addition, F^{′} will replace F as a new front in the next layer. However, if at least one node of p_{i} (i = 0, 1, 2) is not allowed to propagate to the next layer, F will also be allowed to propagate to the next layer.