---
title: "ToBoundaryMesh"
language: "en"
type: "Symbol"
summary: "ToBoundaryMesh[r] generates a boundary ElementMesh object from the boundary of a region r. ToBoundaryMesh[r, {{xmin, xmax}, ...}] generates a boundary ElementMesh object from the boundary of a region r restricted to the bounding box [xmin, xmax]*\\[CenterEllipsis]. ToBoundaryMesh[rules] generates a boundary ElementMesh object from a set of rules specifying coordinates and boundary elements. ToBoundaryMesh[emesh] generates a new boundary ElementMesh object from an existing ElementMesh, MeshRegion, or BoundaryMeshRegion."
keywords: 
- Mesh
- Grid
- Region
- Elements
- Mesh elements
- Mesh plugin
- Mesh framework
- Mesher
- Mesh generator
- Grid elements
- Element mesh
- Wireframe
- Numerical region
- Boundary
- Boundary Mesh
- Boundary Element Mesh
- Boundary Mesh Generation
- Surface
- Surface Mesh
- Surface Mesh Generation
- Mesh Region
- Boundary Mesh Region
- Region Holes
- Material Region
- Discretized Region
- Discretized Boundary Region
- Boundary Discretized Region
- OpenCascade
canonical_url: "https://reference.wolfram.com/language/FEMDocumentation/ref/ToBoundaryMesh.html"
source: "Wolfram Language Documentation"
related_guides: 
  - 
    title: "Finite Element Method"
    link: "https://reference.wolfram.com/language/FEMDocumentation/guide/FiniteElementMethodGuide.en.md"
related_tutorials: 
  - 
    title: "Element Mesh Generation"
    link: "https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.en.md"
  - 
    title: "Element Mesh Visualization"
    link: "https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshVisualization.en.md"
  - 
    title: "Using OpenCascadeLink"
    link: "https://reference.wolfram.com/language/OpenCascadeLink/tutorial/UsingOpenCascadeLink.en.md"
  - 
    title: "NDSolve Finite Element Options"
    link: "https://reference.wolfram.com/language/FEMDocumentation/tutorial/FiniteElementOptions.en.md"
---
## NDSolve\`FEM\`

# ToBoundaryMesh

ToBoundaryMesh[r] generates a boundary ElementMesh object from the boundary of a region r.

ToBoundaryMesh[r, {{xmin, xmax}, …}] generates a boundary ElementMesh object from the boundary of a region r restricted to the bounding box $$\left[\text{\textit{$x$}}_{\text{\textit{min}}},\text{\textit{$x$}}_{\text{\textit{max}}}\right]\times \cdots$$.

ToBoundaryMesh[rules] generates a boundary ElementMesh object from a set of rules specifying coordinates and boundary elements.

ToBoundaryMesh[emesh] generates a new boundary ElementMesh object from an existing ElementMesh, MeshRegion, or BoundaryMeshRegion.

## Details and Options

* ``ToBoundaryMesh[r]`` generates a boundary ``ElementMesh`` object that approximates the boundary of the possibly symbolic region ``r``. In a boundary ``ElementMesh``, the ``"MeshElements"`` are set to ``Automatic`` and can be computed by using ``ToElementMesh``.

* The specification for regions includes objects described by [geometric regions](https://reference.wolfram.com/language/guide/GeometricSpecialRegions.en.md) and ``ImplicitRegion``.

* The region ``r`` should be a constant region for which ``ConstantRegionQ`` gives ``True``.

* In ``ToBoundaryMesh[rules]``, the following ``rules`` should be given in this order:

|                               |                                                                   |
| ----------------------------- | ----------------------------------------------------------------- |
| "Coordinates" -> {c1, c2, …}  | required to specify the coordinates to be c1, c2, …               |
| "BoundaryElements" -> {b1, …} | required to specify the boundary elements in the mesh to be b1, … |
| "PointElements" -> {p1, …}    | optionally specify the point elements in the mesh to be p1, …     |

* For a 1D boundary element mesh, ``"BoundaryElements"`` are ``PointElement``.

* For a 2D boundary element mesh, ``"BoundaryElements"`` are ``LineElement``.

* For a 3D boundary element mesh, ``"BoundaryElements"`` can be ``TriangleElement`` and ``QuadElement``.

* ``ToBoundaryMesh[emesh]`` may be used to modify an ``ElementMesh`` object ``emesh`` in various ways, including extracting the boundary and adding markers.

* ``ToBoundaryMesh`` has the following options:

|                               |           |                                                           |
| ----------------------------- | --------- | --------------------------------------------------------- |
| AccuracyGoal                  | Infinity  | digits of accuracy sought                                 |
| "BoundaryGroupingThreshold"   | Automatic | control the grouping of boundaries by normal direction    |
| "BoundaryMarkerFunction"      | None      | function to compute markers for boundary elements         |
| "BoundaryMeshGenerator"       | Automatic | function to generate boundary ElementMesh                 |
| "CheckIntersections"          | Automatic | control testing for intersecting boundaries               |
| "DeleteDuplicateCoordinates"  | True      | delete duplicate coordinates                              |
| "IncludePoints"               | {}        | points to be included and considered part of the boundary |
| "MaxBoundaryCellMeasure"      | Automatic | control the size of boundary elements                     |
| MaxCellMeasure                | Automatic | control the size of mesh elements                         |
| "MeshOrder"                   | Automatic | order of the element mesh                                 |
| "MessageHead"                 | Automatic | symbol for messages                                       |
| "PointMarkerFunction"         | None      | function to compute markers for point elements            |
| PrecisionGoal                 | Automatic | digits of precision sought                                |
| "RegionHoles"                 | Automatic | specify region holes                                      |

* With ``MaxCellMeasure -> m``, the measure of the cells created in the region dimension ``d`` will be at most ``m``. The measure for the ``d - 1``-dimensional boundary representation cells will by default be chosen to be compatible with good quality ``d``-dimensional cells with measure ``m``. Limits for specific dimensions may be specified by using ``MaxCellMeasure -> {"Volume" -> v, "Area" -> a, "Length" -> l}``.

* With ``AccuracyGoal -> a`` and ``PrecisionGoal -> p``, an attempt will be made to keep the maximum distance between the region ``r`` or the discretized region ``dreg`` and any point in ``RegionSymmetricDifference[r, dreg]`` to less than $10^{-a}+h 10^{-p}$, where $h$ is the length of the diagonal of the bounding box.

* ``ToBoundaryMesh[bmesh, opts]`` or ``ToBoundaryMesh[emesh, opts]`` may be used to modify a boundary ``ElementMesh`` object ``bmesh`` or extract the boundary from a full ``ElementMesh`` object ``emesh`` in various ways by specifying any one of the options to ``ToBoundaryMesh``.

* If ``ToBoundaryMesh`` cannot generate an ``ElementMesh``, then ``\$Failed`` is returned.

* Invoking ``ToBoundaryMesh`` on an ``EmptyRegion`` will return an ``EmptyRegion``.

* Options given to ``ToBoundaryMesh`` can be given to ``NDSolve`` by specifying ``"MeshOptions"``. »

* Setting options for ``ToBoundaryMesh`` from ``NDSolve`` and related functions is explained in [NDSolve Finite Element Options](https://reference.wolfram.com/language/FEMDocumentation/tutorial/FiniteElementOptions.en.md).

---

## Examples (54)

### Basic Examples (4)

Load the package:

```wl
In[1]:= <<NDSolve`FEM`
```

Create a boundary element mesh of a ``Disk`` :

```wl
In[2]:= bmesh = ToBoundaryMesh[Disk[]]

Out[2]= ElementMesh[{{-1., 1.}, {-1., 1.}}, Automatic]
```

Visualize the boundary mesh:

```wl
In[3]:= bmesh["Wireframe"]

Out[3]= [image]
```

Convert the boundary element mesh to a full mesh:

```wl
In[4]:= ToElementMesh[bmesh]["Wireframe"]

Out[4]= [image]
```

---

Create a boundary mesh of a union of a ``Disk`` and a ``Rectangle`` :

```wl
In[1]:= ToBoundaryMesh[RegionUnion[Disk[], Rectangle[{0, 0}, {2, 2}]]]

Out[1]= ElementMesh[{{-1., 2.}, {-1., 2.}}, Automatic]
```

Visualize the boundary mesh:

```wl
In[2]:= %["Wireframe"]

Out[2]= [image]
```

---

Create a boundary mesh by explicitly specifying the boundary elements:

```wl
In[1]:=
bmesh = ToBoundaryMesh[
	"Coordinates" -> {{0., 0.}, {1., 0.}, {1., 0.2}, {1., 1.}, {0., 1.}, {0., 0.22}, {0.3, 0.22}, {0.3, 0.2}, {0., 0.2}}, 
	"BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {9, 1}, {3, 8}}]}
	]

Out[1]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]
```

Visualize the boundary mesh:

```wl
In[2]:= bmesh["Wireframe"]

Out[2]= [image]
```

Create a full element mesh from the boundary mesh and visualize the resulting element mesh:

```wl
In[3]:= ToElementMesh[bmesh]["Wireframe"]

Out[3]= [image]
```

---

Get a discretized boundary representation for a cylinder:

```wl
In[1]:= ToBoundaryMesh[Cylinder[]]["Wireframe"]

Out[1]= [image]
```

### Scope (15)

Generate a boundary mesh of a ``Disk`` :

```wl
In[1]:= ToBoundaryMesh[Disk[]]["Wireframe"]

Out[1]= [image]
```

---

Convert an ``ImplicitRegion`` with an explicit bounding box:

```wl
In[1]:= ToBoundaryMesh[ImplicitRegion[x < y ^ 2, {x, y}], {{-1 / 2, 1}, {-1, 1}}]["Wireframe"]

Out[1]= [image]
```

---

Convert a ``GraphicsComplex`` to a boundary element mesh:

```wl
In[1]:= ToBoundaryMesh[DiscretizeGraphics[GraphicsComplex[{{0, 0}, {1, 0}, {2, 2}, {0, 1}}, Polygon[{1, 2, 3, 4}]]]]["Wireframe"]

Out[1]= [image]
```

---

Convert a ``TransformedRegion`` to a boundary element mesh:

```wl
In[1]:= ToBoundaryMesh[TransformedRegion[Disk[{1, 1}, 4], {Indexed[#, 1] Indexed[#, 2], Indexed[#, 1] + Indexed[#, 2]}&]]["Wireframe"]

Out[1]= [image]
```

---

Convert a 3D ``Ball`` to a boundary element mesh:

```wl
In[1]:= ToBoundaryMesh[Ball[{0, 0, 0}, 1]]

Out[1]= ElementMesh[{{-1., 1.}, {-1., 1.}, {-1., 1.}}, Automatic]

In[2]:= %["Wireframe"]

Out[2]= [image]
```

---

Convert a 3D ``GraphicsComplex`` to a boundary element mesh:

```wl
In[1]:= ToBoundaryMesh[DiscretizeGraphics[GraphicsComplex[{{0, 0, 0}, {20, 0, 0}, {20, 2, 0}, {0, 2, 0}, {0, 0, 1}, {20, 0, 1}, {20, 2, 1}, {0, 2, 1}}, Polygon[{{4, 1, 5, 8}, {2, 3, 7, 6}, {1, 2, 3, 4}, {5, 6, 7, 8}, {1, 2, 6, 5}, {3, 4, 8, 7}}]]]]["Wireframe"]

Out[1]= [image]
```

---

Convert a 3D ``TransformedRegion`` to a boundary element mesh:

```wl
In[1]:= ToBoundaryMesh[TransformedRegion[Cuboid[], ShearingTransform[30Degree, {1, 0, 0}, {0, 1, 1}]]]["Wireframe"]

Out[1]= [image]
```

---

Create a boundary element mesh from four coordinates and one boundary element:

```wl
In[1]:= ToBoundaryMesh["Coordinates" -> {{0., 0.}, {1., 0.}, {1., 1.}, {0., 1.}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]}]

Out[1]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]

In[2]:= %["Wireframe"]

Out[2]= [image]
```

---

For a set of points, ``ToBoundaryMesh`` will return the convex hull:

```wl
In[1]:= ToBoundaryMesh["Coordinates" -> RandomReal[{-1, 1}, {10, 2}]]["Wireframe"]

Out[1]= [image]
```

#### 1D Point Element Boundary Meshes (2)

Create a point element boundary mesh:

```wl
In[1]:= ToBoundaryMesh["Coordinates" -> {{0.}, {1.}}, "BoundaryElements" -> {PointElement[{{1}, {2}}]}, "MeshOrder" -> 1]

Out[1]= ElementMesh[{{0., 1.}}, Automatic]

In[2]:= %["Wireframe"]

Out[2]= [image]
```

---

Create a 1D boundary mesh from an implicit region:

```wl
In[1]:= ToBoundaryMesh[ImplicitRegion[(x < -5) || (-1 ≤ x ≤ 1) || (x > 5), {x}], {{-10, 10}}]

Out[1]= ElementMesh[{{-10., 10.}}, Automatic]

In[2]:= %["Wireframe"]

Out[2]= [image]
```

#### 2D Line Element Boundary Meshes (1)

Create a line element boundary mesh with an additional node inside the domain:

```wl
In[1]:= bmesh = ToBoundaryMesh["Coordinates" -> {{1.293, 0.228}, {1., 0.}, {0.94, 0.342}, {1.293, 0.}, {1.215, 0.442}, {2., 0.}, {1.879, 0.684}}, "BoundaryElements" -> {LineElement[{{3, 2}, {2, 4}, {4, 6}, {6, 7}, {7, 5}, {5, 3}}]}]

Out[1]= ElementMesh[{{0.94, 2.}, {0., 0.684}}, Automatic]

In[2]:= Show[bmesh["Wireframe"], bmesh["Wireframe"["MeshElement" -> "PointElements"]]]

Out[2]= [image]
```

#### 2D Higher-Order Boundary Element Meshes (1)

Create a second-order boundary element mesh:

```wl
In[1]:= bmesh = ToBoundaryMesh["Coordinates" -> {{0., 0.}, {1., 0.}, {2., 0.}, {2.5, 0.5}, {0., 1.}, {3., 1.}, {2.5, 1.5}, {0., 2.}, {1., 2.}, {2., 2.}, {0., 0.5}, {0., 1.5}, {0.5, 0.}, {0.5, 2.}, {1.5, 0.}, {1.5, 2.}, {2.25, 0.25}, {2.25, 1.75}, {2.75, 0.75}, {2.75, 1.25}}, "BoundaryElements" -> {LineElement[{{3, 4, 17}, {4, 6, 19}, {7, 10, 18}, {6, 7, 20}, {1, 2, 13}, {5, 1, 11}, {2, 3, 15}, {9, 8, 14}, {8, 5, 12}, {10, 9, 16}}]}]

Out[1]= ElementMesh[{{0., 3.}, {0., 2.}}, Automatic]

In[2]:=
Show[bmesh["Wireframe"], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementStyle" -> Directive[Red, PointSize[0.02]], "MeshElementIDStyle" -> Blue]]]

Out[2]= [image]
```

#### Boundary Markers (1)

Boundary markers are useful to specify ``NeumannValue`` on a region boundary and are explained further in the section [Markers in the Element Mesh Generation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.en.md#180054820).

Create a rectangle with boundary element markers and one additional node inside the domain:

```wl
In[1]:= boundaryMarker = {1, 1, 2, 2, 3, 3, 4, 4}

Out[1]= {1, 1, 2, 2, 3, 3, 4, 4}

In[2]:= bcEle = {LineElement[{{1, 2}, {2, 3}, {3, 6}, {6, 9}, {9, 8}, {8, 7}, {7, 4}, {4, 1}}, boundaryMarker]}

Out[2]= {LineElement[{{1, 2}, {2, 3}, {3, 6}, {6, 9}, {9, 8}, {8, 7}, {7, 4}, {4, 1}}, {1, 1, 2, 2, 3, 3, 4, 4}]}
```

Create the boundary mesh:

```wl
In[3]:= bmesh = ToBoundaryMesh["Coordinates" -> {{0., 0.}, {1., 0.}, {2., 0.}, {0., 1.}, {1., 1.}, {2., 1.}, {0., 2.}, {1., 2.}, {2., 2.}}, "BoundaryElements" -> bcEle]

Out[3]= ElementMesh[{{0., 2.}, {0., 2.}}, Automatic]
```

Visualize the boundary mesh and the markers:

```wl
In[4]:=
Show[
	bmesh["Wireframe"["MeshElement" -> "BoundaryElements", "MeshElementMarkerStyle" -> Red]], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementStyle" -> Directive[PointSize[0.02]], 
	"MeshElementIDStyle" -> Blue
	]]]

Out[4]= [image]
```

#### Point Markers (1)

Point markers are useful to specify ``DirichletCondition`` on a region boundary and are explained further in the section [Markers in the Element Mesh Generation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.en.md#180054820).

Create a rectangle with a boundary element and point elements with markers and one additional node inside the domain:

```wl
In[1]:=
pointMarkers = {1, 1, 1, 4, 0, 2, 3, 3, 3};
pEle = {PointElement[{{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}}, pointMarkers]}

Out[1]= {PointElement[{{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}}, {1, 1, 1, 4, 0, 2, 3, 3, 3}]}
```

Create the boundary mesh:

```wl
In[2]:= bmesh = ToBoundaryMesh["Coordinates" -> {{0., 0.}, {1., 0.}, {2., 0.}, {0., 1.}, {1., 1.}, {2., 1.}, {0., 2.}, {1., 2.}, {2., 2.}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 6}, {6, 9}, {9, 8}, {8, 7}, {7, 4}, {4, 1}}]}, "PointElements" -> pEle]

Out[2]= ElementMesh[{{0., 2.}, {0., 2.}}, Automatic]
```

Visualize the point elements in the boundary mesh with node numbers in blue and markers in red:

```wl
In[3]:=
Show[
	bmesh["Wireframe"], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementMarkerStyle" -> Red]], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementStyle" -> Directive[PointSize[0.02]], 
	"MeshElementIDStyle" -> Blue
	]]]

Out[3]= [image]
```

### Options (27)

#### "AccuracyGoal" (1)

Ensure that the discretized boundary representation is close to the boundary:

```wl
In[1]:=
bem = ToBoundaryMesh[Disk[], AccuracyGoal -> 2];
bem["Wireframe"]

Out[1]= [image]
```

A helper function to compute the centroid of the boundary segments:

```wl
In[2]:= centroid = Compile[{{coords, _Real, 2}, {inci, _Integer, 1}}, Total[coords[[inci]]] / 2, RuntimeAttributes -> Listable];
```

The largest deviation from the true disk is at the centers of the segments:

```wl
In[3]:= ListPlot[Map[{ArcTan@@#, Norm[#] - 1}&, centroid[bem["Coordinates"], Join@@ElementIncidents[bem["BoundaryElements"]]]]]

Out[3]= [image]
```

With a larger accuracy goal, the error is reduced by using more points:

```wl
In[4]:=
bem = ToBoundaryMesh[Disk[], AccuracyGoal -> 4];
ListPlot[Map[{ArcTan@@#, Norm[#] - 1}&, centroid[bem["Coordinates"], Join@@ElementIncidents[bem["BoundaryElements"]]]]]

Out[4]= [image]
```

#### "BoundaryGroupingThreashold" (7)

``ToBoundaryMesh`` automatically attributes markers to groups of contiguous boundary segments by comparing the boundary normal vectors.

Create a numerical region:

```wl
In[1]:= nr = ToNumericalRegion[ImplicitRegion[And@@(# ≤ 0& /@ {-y, 1 / 25 - (-3 / 2 + x) ^ 2 - y ^ 2, 1 - x ^ 2 - y ^ 2, -4 + x ^ 2 + y ^ 2, (-x + 2 * y) / Sqrt[5]}), {x, y}], {{0.8, 2.2}, {-0.2, 1.}}];
```

Create a boundary mesh from the numerical region:

```wl
In[2]:= bmesh = ToBoundaryMesh[nr];
```

Inspect the boundary element markers present:

```wl
In[3]:= groups = bmesh["BoundaryElementMarkerUnion"]

Out[3]= {1, 2, 3, 4, 5, 6}
```

Visualize the contiguous boundaries:

```wl
In[4]:= bmesh["Wireframe"["MeshElementStyle" -> "BoundaryGrouping"]]

Out[4]= [image]
```

Visualize the boundaries with element marker 2 or 4:

```wl
In[5]:= bmesh["Wireframe"[Or@@(ElementMarker == #& /@ {2, 4})]]

Out[5]= [image]
```

Inspect the point element markers present:

```wl
In[6]:= groups = bmesh["PointElementMarkerUnion"]

Out[6]= {1, 2, 3, 4, 5, 6}
```

Visualize the point markers:

```wl
In[7]:=
temp = Most[Range[0, 1, 1 / (Length[groups])]];
colors = ColorData["BrightBands"][#]& /@ temp

Out[7]= {RGBColor[0.90222, 0.101808, 0.198306], RGBColor[0.43315467307722716, 0.3384619423049553, 0.9999676857362557], RGBColor[0.14287214708202123, 0.7165497398815136, 1.], RGBColor[0.23499123809468608, 0.9676036067435916, 0.1156329799245431], RGBColor[0.9729188047180777, 0.9391259962046635, 0.201748879675973], RGBColor[1., 0.5034084923371307, 0.0060676902730088965]}

In[8]:= bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementStyle" -> (Directive /@ colors)]]

Out[8]= [image]
```

Use the element markers for specifying boundary conditions in ``NDSolveValue`` :

```wl
In[9]:=
mesh = ToElementMesh[nr];
ufun = NDSolveValue[{Laplacian[u[x, y], {x, y}] == 1, DirichletCondition[u[x, y] == 1, Or@@(ElementMarker == #& /@ {2, 4})]}, u, {x, y}∈mesh];
```

``DirichletCondition`` extracts the point element markers, while ``NeumannValue`` and ``PeriodicBoundaryCondition`` extract boundary element markers.

Visualize the solution:

```wl
In[10]:= ContourPlot[ufun[x, y], {x, y}∈mesh]

Out[10]= [image]
```

---

Point element markers (red) are deduced from boundary markers (blue). If the node's connected boundary element markers are the same, then the node will have the same marker. On corners, the connected boundary markers typically differ, and an arbitrary choice of the available markers is made:

```wl
In[1]:=
bmesh = ToBoundaryMesh[Rectangle[], "MaxCellMeasure" -> 0.25];
Show[bmesh["Wireframe"], bmesh["Wireframe"["MeshElement" -> "BoundaryElements", "MeshElementMarkerStyle" -> Blue]], bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementMarkerStyle" -> Red]]]

Out[1]= [image]
```

To control the behavior of which marker gets assigned to a corner node, a ``"PointMarkerFunction"`` can be specified. In the following case, all nodes on the left get a 2 as a marker, and on the right get a 4. The remaining markers are deduced from the boundary segment markers:

```wl
In[2]:=
pointMarkerFunction = Compile[{{coords, _Real, 2}, {pMarker, _Integer, 1}}, MapThread[Which[#[[1]] == 0., 2, #[[1]] == 1., 4, True, #2]&, {coords, pMarker}]];
bmesh = ToBoundaryMesh[Rectangle[], "PointMarkerFunction" -> pointMarkerFunction, "MaxCellMeasure" -> 0.25];
Show[bmesh["Wireframe"], 
	bmesh["Wireframe"["MeshElement" -> "BoundaryElements", "MeshElementMarkerStyle" -> Blue]], bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementMarkerStyle" -> Red]]]

Out[2]= [image]
```

---

Switch off the automatic boundary grouping:

```wl
In[1]:= bmesh = ToBoundaryMesh[Rectangle[], "BoundaryGroupingThreshold" -> None]

Out[1]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]
```

The only boundary element marker is the default of 0:

```wl
In[2]:= bmesh["BoundaryElementMarkerUnion"]

Out[2]= {0}
```

---

The automatic boundary grouping chooses boundary segments to be contiguous by computing the normals of the boundary segments and then computing the dot product of the neighboring segments. When the dot product is above a threshold $t$, the neighboring segments are considered contiguous.

Create a coarse boundary mesh where boundary elements are grouped if the threshold $t\geq 0.8$ :

```wl
In[1]:= bmesh = ToBoundaryMesh[Disk[], AccuracyGoal -> 1, "MaxBoundaryCellMeasure" -> 0.4, "BoundaryGroupingThreshold" -> 0.8]

Out[1]= ElementMesh[{{-1., 1.}, {-1., 1.}}, Automatic]
```

Inspect the union of the boundary element markers:

```wl
In[2]:= bmesh["BoundaryElementMarkerUnion"]

Out[2]= {1}
```

Create a coarse boundary mesh where boundary elements are grouped if the threshold $t\geq 0.95$ :

```wl
In[3]:= bmesh = ToBoundaryMesh[Disk[], AccuracyGoal -> 1, "MaxBoundaryCellMeasure" -> 0.4, "BoundaryGroupingThreshold" -> 0.95]

Out[3]= ElementMesh[{{-1., 1.}, {-1., 1.}}, Automatic]
```

Inspect the union of the boundary element markers:

```wl
In[4]:= bmesh["BoundaryElementMarkerUnion"]

Out[4]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
```

---

Construct a 1D boundary mesh with an internal boundary:

```wl
In[1]:= bmesh = ToBoundaryMesh["Coordinates" -> {{0.}, {1.}, {3.}}, "BoundaryElements" -> {PointElement[{{1}, {3}}], PointElement[{{2}}]}]

Out[1]= ElementMesh[{{0., 3.}}, Automatic]
```

The boundary elements include the internal boundary at 1.0 (index 2):

```wl
In[2]:= bmesh["BoundaryElements"]

Out[2]= {PointElement[{{1}, {3}}, {1, 2}], PointElement[{{2}}, {3}]}
```

In 1D, the boundary normal for an interior boundary is taken to be 0:

```wl
In[3]:= bmesh["BoundaryNormals"]

Out[3]= {{{-1.}, {1.}}, {{0.}}}
```

Inspect the union of boundary element markers:

```wl
In[4]:= bmesh["BoundaryElementMarkerUnion"]

Out[4]= {1, 2, 3}
```

---

Create a boundary element mesh in 3D from a discretized graphic:

```wl
In[1]:=
gr = ExampleData[{"Geometry3D", "SpaceShuttle"}]
bmesh = ToBoundaryMesh[DiscretizeGraphics[gr]]

Out[1]= [image]

Out[1]= ElementMesh[{{-7.64998, 7.04371}, {-4.6821, 4.6821}, {-1.35165, 4.1572}}, Automatic]
```

Generate a list of colors for the many boundary groups:

```wl
In[2]:=
groups = bmesh["BoundaryElementMarkerUnion"];
temp = Most[Range[0, 1, 1 / (Length[groups])]];
Short[colors = ColorData["BrightBands"][#]& /@ temp]

Out[2]//Short= {RGBColor[0.90222, 0.101808, 0.198306], RGBColor[0.9088797031970934, 0.13590877252012212, 0.2359330506273661], RGBColor[0.9155394063941868, 0.1700095450402443, 0.27356010125473224], RGBColor[0.9221991095912803, 0.2041103175603664, 0.311187151882098 ... 553533, 0.34645946571031516], RGBColor[1., 0.6881661230842827, 0.37740417256825237], RGBColor[1., 0.7035625923132119, 0.4083488794261892], RGBColor[1., 0.7189590615421413, 0.43929358628412607], RGBColor[1., 0.7343555307710707, 0.4702382931420632]}

In[3]:= bmesh["Wireframe"["MeshElementStyle" -> (Directive[FaceForm[#], EdgeForm[]]& /@ colors)]]

Out[3]= [image]
```

Visualize only the boundaries with element marker 1:

```wl
In[4]:= bmesh["Wireframe"[ElementMarker == 1]]

Out[4]= [image]
```

Inspect the boundaries with a different element marker:

```wl
In[5]:=
Needs["NDSolve`FEM`"]
Manipulate[Show[glider["Wireframe"], 
	glider["Wireframe"[ElementMarker == m, "MeshElementStyle" -> Directive[FaceForm[colors[[m]]], EdgeForm[]], PlotRange -> glider["Bounds"]]]], {{m, 15}, 1, groups, 1}, Initialization :> (Needs["NDSolve`FEM`"];glider := ToBoundaryMesh[DiscretizeGraphics[ExampleData[{"Geometry3D", "SpaceShuttle"}]]];
	groups := Length[glider["BoundaryElementMarkerUnion"]];
	colors := ColorData["BrightBands"][#]& /@ Most[Range[0, 1, 1 / groups]];), SaveDefinitions -> True]

Out[5]= DynamicModule[«9»]

 DynamicModule[«3»]
```

---

The automatic boundary grouping can be useful even for complicated geometries with a large number of groups. Import a gear as an element mesh:

```wl
In[1]:= bmesh = Import["http://exampledata.wolfram.com/gear.1", {"STL", "ElementMesh"}]

Out[1]= ElementMesh[{{-37., 47.394}, {-35.396, 49.}, {-0.002, 8.996}}, Automatic]
```

Visualize the boundary mesh and the contiguous boundary groups:

```wl
In[2]:= bmesh["Wireframe"["MeshElementStyle" -> "BoundaryGrouping"]]

Out[2]= [image]
```

#### "BoundaryMarkerFunction" (1)

Boundary markers are useful to specify ``NeumannValue`` on a region boundary and are explained further in the section [Markers in the Element Mesh Generation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.en.md#180054820).

Here is a function that computes integer markers for edges, depending on the coordinates of the edge elements:

```wl
In[1]:=
boundaryMarkerFunction = Compile[{{boundaryElementCoords, _Real, 3}, {pointMarkres, _Integer, 2}}, 
	Module[{pt1 = #[[1]], pt2 = #[[2]]}, 
	Which[
	pt1[[1]] > 0.9 && pt2[[1]] > 0.9, 2, 
	pt1[[1]] < 0.1 && pt2[[1]] < 0.1, 3, 
	True, 4 ]]& /@ boundaryElementCoords];

In[2]:=
bmesh = ToBoundaryMesh[FullRegion[2], {{0, 1}, {0, 1}}, 
	"BoundaryMarkerFunction" -> boundaryMarkerFunction, 
	"MaxBoundaryCellMeasure" -> 0.2
	]

Out[2]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]
```

Visualize the boundary mesh and the boundary markers:

```wl
In[3]:=
Show[bmesh["Wireframe"], 
	bmesh["Wireframe"["MeshElement" -> "BoundaryElements", "MeshElementMarkerStyle" -> Red]]]

Out[3]= [image]
```

Inspect the boundary elements:

```wl
In[4]:= bmesh["BoundaryElements"]

Out[4]= {LineElement[{{1, 7}, {2, 1}, {3, 2}, {4, 3}, {5, 4}, {8, 6}, {6, 5}, {7, 9}, {10, 8}, {9, 11}, {12, 10}, {11, 13}, {14, 12}, {13, 15}, {15, 16}, {16, 17}, {17, 18}, {18, 19}, {19, 20}, {20, 14}}, {4, 3, 3, 3, 3, 4, 3, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 4}]}
```

If ``"PointElementMarkers"`` are also computed, those are accessible as the second argument in the function given to ``"BoundaryMarkerFunction"``.

#### "BoundaryMeshGenerator" (8)

The ``"Continuation"`` method uses a curve continuation method that can in many cases resolve corners, cusps, and sharp changes quite well:

```wl
In[1]:= ℛ = ImplicitRegion[-1 + 2 x^2 ≤ y ≤ x^2, {{x, -1, 1}, {y, -1, 1}}];

In[2]:= ToBoundaryMesh[ℛ, "BoundaryMeshGenerator" -> "Continuation"]["Wireframe"]

Out[2]= [image]
```

The ``"RegionPlot"`` method is based on improving output from ``RegionPlot`` and can sometimes be faster:

```wl
In[3]:= ToBoundaryMesh[ℛ, "BoundaryMeshGenerator" -> "RegionPlot"]["Wireframe"]

Out[3]= [image]
```

---

The ``"RegionPlot"`` method has suboptions to improve the quality of the boundary mesh:

```wl
In[1]:= ℛ = ImplicitRegion[-1 + 2 x^2 ≤ y ≤ x^2, {{x, -1, 1}, {y, -1, 1}}];

In[2]:= ToBoundaryMesh[ℛ, "BoundaryMeshGenerator" -> {"RegionPlot", "SamplePoints" -> 41}]["Wireframe"]

Out[2]= [image]

In[3]:= ToBoundaryMesh[ℛ, "BoundaryMeshGenerator" -> {"RegionPlot", MaxRecursion -> 7}]["Wireframe"]

Out[3]= [image]
```

---

Pass a method option to the ``BoundaryDiscretizeRegion`` boundary mesh generator:

```wl
In[1]:= ToBoundaryMesh[ImplicitRegion[{2 Round[x] + y + z ≥ 1}, {x, y, z}], {{-1, 1}, {-1, 1}, {-1, 1}}, "BoundaryMeshGenerator" -> {"BoundaryDiscretizeRegion", Method -> "PostProcessing" -> None}]

Out[1]= ElementMesh[{{-0.5, 1.}, {-1., 1.}, {-1., 1.}}, Automatic]
```

---

Specify a region:

```wl
In[1]:= Ω = RegionDifference[RegionDifference[RegionUnion[Cylinder[{{0, 0, 0}, {0, 0, 10}}, 5], Cylinder[{{0, 0, 5}, {0, -6, 5}}, 3]], Cylinder[{{0, 0, 0}, {0, 0, 10}}, 2]], Cylinder[{{0, 0, 5}, {0, -6, 5}}, 2]];
```

Pass a method option to the ``BoundaryDiscretizeRegion`` boundary mesh generator to fine-tune the discretization algorithm:

```wl
In[2]:= bmesh = ToBoundaryMesh[Ω, "BoundaryMeshGenerator" -> {"BoundaryDiscretizeRegion", Method -> {"MarchingCubes", PlotPoints -> 33}}]

Out[2]= ElementMesh[{{-4.99829, 4.99829}, {-6., 5.}, {0., 10.}}, Automatic]
```

Visualize the boundary mesh as a mesh region:

```wl
In[3]:= MeshRegion[bmesh]

Out[3]= [image]
```

---

The "OpenCascade" boundary mesh generator is well suited for 2D or 3D ``BooleanRegion`` expressions consisting of ``Graphics`` or ``Graphics3D`` primitives.

Specify a region:

```wl
In[1]:= Ω = RegionDifference[RegionDifference[RegionUnion[Cylinder[{{0, 0, 0}, {0, 0, 10}}, 5], Cylinder[{{0, 0, 5}, {0, -6, 5}}, 3]], Cylinder[{{0, 0, 0}, {0, 0, 10}}, 2]], Cylinder[{{0, 0, 5}, {0, -6, 5}}, 2]];
```

Use "OpenCascade" as a boundary mesh generator:

```wl
In[2]:= bmesh = ToBoundaryMesh[Ω, "BoundaryMeshGenerator" -> {"OpenCascade"}]

Out[2]= ElementMesh[{{-5., 5.}, {-6., 4.97261}, {0., 10.}}, Automatic]
```

Generate colors for the boundary markers:

```wl
In[3]:=
groups = bmesh["BoundaryElementMarkerUnion"];
temp = Most[Range[0, 1, 1 / (Length[groups])]];
colors = ColorData["BrightBands"][#]& /@ temp

Out[3]= {RGBColor[0.90222, 0.101808, 0.198306], RGBColor[0.9935530724172814, 0.5694757374188179, 0.7143341228895923], RGBColor[0.715556369908912, 0.6539895279626498, 0.9996874125623944], RGBColor[0.45245314114414814, 0.8476217847751885, 1.], RGBColor[0.4487905481043478, 0.9818223421255908, 0.3098509370597005], RGBColor[0.9807483805553391, 0.9499935355728077, 0.316373124420957], RGBColor[1., 0.5386004220032548, 0.07679844880543597]}
```

Visualize the boundary element mesh with highlighted boundary markers:

```wl
In[4]:= bmesh["Wireframe"["MeshElementStyle" -> FaceForm /@ colors]]

Out[4]= [image]
```

Options for the "OpenCascade" boundary mesh generator are:

```wl
In[5]:= "BoundaryMeshGenerator" -> {"OpenCascade", "ShapeSurfaceMeshOptions" -> {"AngularDeflection" -> 0.25, "LinearDeflection" -> 0.005}}
```

More information about boundary mesh generation options can be found in the [section on surface meshes in the OpenCascadeLink tutorial](https://reference.wolfram.com/language/OpenCascadeLink/tutorial/UsingOpenCascadeLink.en.md#2068564608).

---

Use the "OpenCascade" boundary mesh generator for a union of a ``Disk`` and a ``Rectangle`` :

```wl
In[1]:= bmesh = ToBoundaryMesh[RegionUnion[Disk[], Rectangle[{0, 0}, {2, 2}]], "BoundaryMeshGenerator" -> {"OpenCascade"}]

Out[1]= ElementMesh[{{-0.996917, 2.}, {-0.996917, 2.}}, Automatic]
```

Visualize the boundary mesh:

```wl
In[2]:= bmesh["Wireframe"]

Out[2]= [image]
```

The "OpenCascade" boundary mesh generator cannot distinguish between internal boundaries and region holes:

```wl
In[3]:= bmesh = ToBoundaryMesh[RegionDifference[RegionUnion[Disk[], Rectangle[{0, 0}, {2, 2}]], Disk[{1.5, 1.5}, 1 / 5]], "BoundaryMeshGenerator" -> {"OpenCascade"}]

Out[3]= ElementMesh[{{-0.996917, 2.}, {-0.996917, 2.}}, Automatic]
```

Visualize the boundary mesh:

```wl
In[4]:= bmesh["Wireframe"]

Out[4]= [image]
```

When the full mesh is generated, the entire region will mesh meshed:

```wl
In[5]:= ToElementMesh[bmesh]["Wireframe"]

Out[5]= [image]
```

Manually specify a region hole by setting ``"RegionHoles"`` :

```wl
In[6]:= bmesh = ToBoundaryMesh[RegionDifference[RegionUnion[Disk[], Rectangle[{0, 0}, {2, 2}]], Disk[{1.5, 1.5}, 1 / 5]], "BoundaryMeshGenerator" -> {"OpenCascade"}, "RegionHoles" -> {{1.5, 1.5}}]

Out[6]= ElementMesh[{{-0.996917, 2.}, {-0.996917, 2.}}, Automatic]
```

The full mesh now respects the region hole and the material boundary:

```wl
In[7]:= ToElementMesh[bmesh]["Wireframe"]

Out[7]= [image]
```

For complicated geometries, one can use the default boundary mesh generator, since in that case no internal boundaries are generated:

```wl
In[8]:=
defaultBMesh = ToBoundaryMesh[RegionDifference[RegionUnion[Disk[], Rectangle[{0, 0}, {2, 2}]], Disk[{1.5, 1.5}, 1 / 5]]];
defaultBMesh["Wireframe"]

Out[8]= [image]
```

You can now extract the region holes of that boundary mesh:

```wl
In[9]:= regionHoles = defaultBMesh["RegionHoles"]

Out[9]= {{1.5, 1.5}}
```

The found region holes can be used in conjunction with the "OpenCascade" boundary mesh generator:

```wl
In[10]:=
bmesh = ToBoundaryMesh[RegionDifference[RegionUnion[Disk[], Rectangle[{0, 0}, {2, 2}]], Disk[{1.5, 1.5}, 1 / 5]], "BoundaryMeshGenerator" -> {"OpenCascade"}, "RegionHoles" -> regionHoles];
ToElementMesh[bmesh]["Wireframe"]

Out[10]= [image]
```

In case internal boundaries are present, specifying boundary conditions, like ``DirichletCondition`` or ``NeumannValue``, with the position predicate ``True`` will apply them at these internal boundaries; a more refined boundary predicate should be used to specify specific parts of the boundary.

---

Replace the internal boundary mesh generator with a custom mesh generator:

```wl
In[1]:=
boundaryMeshGenerator[region_, opts : OptionsPattern[]] := Module[{}, 
	Print[region];
	ToBoundaryMesh[region]
	]

In[2]:= ToBoundaryMesh[FullRegion[2], {{0, 1}, {0, 1}}, "BoundaryMeshGenerator" -> boundaryMeshGenerator]

During evaluation of In[2]:= NumericalRegion[FullRegion[2], {{0, 1}, {0, 1}}]

Out[2]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]
```

---

Specify options for a custom mesh generator:

```wl
In[1]:=
Options[boundaryMeshGenerator] = {"MyOption" -> Automatic, MaxCellMeasure -> Automatic};
boundaryMeshGenerator[region_, opts : OptionsPattern[]] := Module[{}, 
	Print[{opts}];
	ToBoundaryMesh[region]
	]

In[2]:= ToBoundaryMesh[FullRegion[2], {{0, 1}, {0, 1}}, "BoundaryMeshGenerator" -> {boundaryMeshGenerator, "MyOption" -> "Test", MaxCellMeasure -> 0.1}]

During evaluation of In[3]:= {{"MyOption" -> "Test", MaxCellMeasure -> {"Area" -> 0.0025, "Length" -> 0.0759836}}}

Out[2]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]
```

#### "DeleteDuplicateCoordinates" (2)

Generally, it is not advisable to have meshes with duplicate coordinates, since the mesh generation process becomes unpredictable. By default, duplicate coordinates are deleted:

```wl
In[1]:= bm = ToBoundaryMesh["Coordinates" -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]}];

In[2]:= bm["Coordinates"]

Out[2]= {{0., 0.}, {1., 0.}, {1., 1.}, {0., 1.}}

In[3]:= bm = ToBoundaryMesh["Coordinates" -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]}, "DeleteDuplicateCoordinates" -> False]

Out[3]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]

In[4]:= bm["Coordinates"]

Out[4]= {{0., 0.}, {1., 0.}, {1., 1.}, {0., 1.}, {0., 0.}}
```

---

When duplicate coordinates are deleted, the incidents of the elements my be re-indexed:

```wl
In[1]:= bm = ToBoundaryMesh["Coordinates" -> {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 5}}]}];

In[2]:= bm["BoundaryElements"]

Out[2]= {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}, {1, 2, 3, 4}]}
```

#### "IncludePoints" (1)

Create a numerical region:

```wl
In[1]:= nr = ToNumericalRegion[ImplicitRegion[And@@(# ≤ 0& /@ {-y, 1 / 25 - (-3 / 2 + x) ^ 2 - y ^ 2, 1 - x ^ 2 - y ^ 2, -4 + x ^ 2 + y ^ 2, (-x + 2 * y) / Sqrt[5]}), {x, y}], {{0.8, 2.2}, {-0.2, 1.}}];
```

Inspect the boundary mesh:

```wl
In[2]:= ToBoundaryMesh[nr]["Wireframe"]

Out[2]= [image]
```

Specify some additional points to be included in the boundary mesh:

```wl
In[3]:= additionalPoints = DeleteDuplicates[Table[{1.5 + 0.2Sin[ϕ], 0.4 + 0.2Cos[ϕ]}, {ϕ, 0, 2π, π / 25}]];
```

Include the points in the boundary mesh:

```wl
In[4]:= bmesh = ToBoundaryMesh[nr, "IncludePoints" -> additionalPoints];
```

Visualize the boundary mesh with the included points:

```wl
In[5]:=
Show[bmesh["Wireframe"], 
	bmesh["Wireframe"["MeshElement" -> "PointElements"]]
	]

Out[5]= [image]
```

These additional points are considered to be part of the boundary, but need not be on the actual boundary. Note that with ``"IncludePoints"`` you cannot add additional points on the boundary that has been specified by the implicit region. In other words, all ``"IncludePoints"`` instances must be strictly within the boundary region.

Create the full mesh from the numerical region:

```wl
In[6]:= ToElementMesh[nr]["Wireframe"]

Out[6]= [image]
```

Points added with ``"IncludePoints"`` will be addressable with ``DirichletCondition``. Additionally, if the predicate in ``DirichletCondition`` is ``True``, then the boundary condition will take effect at the include points.

---

#### "MaxBoundaryCellMeasure" (2)

With ``"MaxBoundaryCellMeasure" -> m``, a boundary cell size is chosen:

```wl
In[1]:=
bem = ToBoundaryMesh[Disk[], "MaxBoundaryCellMeasure" -> .5, AccuracyGoal -> 1];
bem["Wireframe"]

Out[1]= [image]
```

---

Specify a boundary cell size and a cell size:

```wl
In[1]:= ToElementMesh[Disk[], MaxCellMeasure -> 0.1, "MaxBoundaryCellMeasure" -> 0.05]["Wireframe"]

Out[1]= [image]
```

#### "MaxCellMeasure" (2)

With ``MaxCellMeasure -> m``, a cell size is chosen and a boundary cell size is set to be appropriate for good-quality cells in the embedding dimension:

```wl
In[1]:=
bem = ToBoundaryMesh[Disk[], MaxCellMeasure -> .2, AccuracyGoal -> 1];
bem["Wireframe"]

Out[1]= [image]

In[2]:=
em = ToElementMesh[Disk[], MaxCellMeasure -> .2, AccuracyGoal -> 1];
em["Wireframe"]

Out[2]= [image]
```

---

A specific length can be specified using ``MaxCellMeasure -> {"Length" -> len}`` :

```wl
In[1]:=
bem = ToBoundaryMesh[Disk[], MaxCellMeasure -> {"Length" -> .05}];
bem["Wireframe"]

Out[1]= [image]
```

A helper function to compute the length of the boundary segments:

```wl
In[2]:=
length = Compile[{{coords, _Real, 2}, {inci, _Integer, 1}}, 
	Block[{p1, p2}, {p1, p2} = coords[[inci]];
	Sqrt[Total[(p2 - p1) ^ 2]]], RuntimeAttributes -> Listable];
```

This gives the maximum lengths of the segments:

```wl
In[3]:= Max[length[bem["Coordinates"], Join@@ElementIncidents[bem["BoundaryElements"]]]]

Out[3]= 0.0449962
```

Create a full mesh from the boundary mesh:

```wl
In[4]:= ToElementMesh[bem]["Wireframe"]

Out[4]= [image]
```

#### "MeshOrder" (1)

``ToBoundaryMesh`` by default generates a first-order boundary mesh:

```wl
In[1]:= bmesh = ToBoundaryMesh[Disk[]]

Out[1]= ElementMesh[{{-1., 1.}, {-1., 1.}}, Automatic]
```

Inspect the mesh order:

```wl
In[2]:= %["MeshOrder"]

Out[2]= 1
```

Integrate over the boundary mesh and assess the accuracy of the integration:

```wl
In[3]:= 2π - NIntegrate[1, {x, y}∈bmesh]

Out[3]= 0.00454626
```

To obtain a second-order boundary mesh, the option ``"MeshOrder"`` may be used:

```wl
In[4]:= bmesh = ToBoundaryMesh[Disk[], "MeshOrder" -> 2]

Out[4]= ElementMesh[{{-1., 1.}, {-1., 1.}}, Automatic]
```

Inspect the mesh order:

```wl
In[5]:= %["MeshOrder"]

Out[5]= 2
```

Integrate over the boundary mesh and assess the accuracy of the integration:

```wl
In[6]:= 2π - NIntegrate[1, {x, y}∈bmesh]

Out[6]= 2.000194447404624`*^-6
```

More information about the ``"MeshOrder"`` option can be found in this section of the [Element Mesh Generation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.en.md#1987775913) tutorial and this section of the [Element Mesh Generation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshVisualization.en.md#56252879) tutorial.

#### "PointMarkerFunction" (1)

Point markers are useful to specify ``DirichletCondition`` on a region boundary and are explained further in the section [Markers in the Element Mesh Generation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.en.md#180054820).

Here is a function that computes integer markers for point elements, depending on the coordinates of the edge nodes:

```wl
In[1]:=
pointMarkerFunction = Compile[{{coords, _Real, 2}, {pMarker, _Integer, 1}}, 
	Which[
	#[[1]] == 1., 2, 
	#[[1]] == 0., 3, 
	True, 4 ]& /@ coords];

In[2]:=
bmesh = ToBoundaryMesh[ImplicitRegion[True, {{x, 0, 1}, {y, 0, 1}}], 
	"PointMarkerFunction" -> pointMarkerFunction, 
	"MaxCellMeasure" -> {"Length" -> 0.25}, 
	"MeshOrder" -> 1
	]

Out[2]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]

In[3]:=
Show[bmesh["Wireframe"], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementMarkerStyle" -> Blue]]]

Out[3]= [image]
```

Inspect the boundary elements:

```wl
In[4]:= bmesh["PointElements"]

Out[4]= {PointElement[{{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}}, {3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4}]}
```

#### "RegionHoles" (1)

Create a boundary mesh with an internal region:

```wl
In[1]:=
bmesh = ToBoundaryMesh["Coordinates" -> {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}, {-1 / 2, -1 / 2}, {1 / 2, -1 / 2}, {1 / 2, 1 / 2}, {-1 / 2, 1 / 2}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}], LineElement[{{5, 6}, {6, 7}, {7, 8}, {8, 5}}]}];
bmesh["Wireframe"]

Out[1]= [image]
```

Create a full mesh from the boundary mesh:

```wl
In[2]:= ToElementMesh[bmesh]["Wireframe"]

Out[2]= [image]
```

Create a boundary mesh with an internal region and a region hole specification:

```wl
In[3]:=
bmesh = ToBoundaryMesh["Coordinates" -> {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}, {-1 / 2, -1 / 2}, {1 / 2, -1 / 2}, {1 / 2, 1 / 2}, {-1 / 2, 1 / 2}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}], LineElement[{{5, 6}, {6, 7}, {7, 8}, {8, 5}}]}, "RegionHoles" -> {{0, 0}}];
bmesh["Wireframe"]

Out[3]= [image]
```

Investigate the region hole in the boundary mesh:

```wl
In[4]:= bmesh["RegionHoles"]

Out[4]= {{0., 0.}}
```

Create a full mesh from the boundary mesh:

```wl
In[5]:= ToElementMesh[bmesh]["Wireframe"]

Out[5]= [image]
```

### Applications (1)

Generate a boundary mesh of two nested half-annuli with $x\geq 0$. Each annulus represents a different subdomain. The first subdomain has an inner radius of $r_1$ and an outer radius of $r_2$, and the second subdomain has an inner radius of $r_2$ and an outer radius of $r_3$.

First, create a series of three half-disks, then a union of the three subregions is generated and finally, to create the two annuli, the first half-disk is taken as a hole.

Define the inner and outer radius of the first annulus:

```wl
In[1]:=
Subscript[r, 1] = 3 / 1000;
Subscript[r, 2] = 10 / 1000;
```

Define the outer radius of the second annulus:

```wl
In[2]:= Subscript[r, 3] = 12 / 1000;
```

Create the half-disks using ``Circle`` :

```wl
In[3]:= c = Circle[{0, 0}, #, {-π / 2, π / 2}]& /@ {Subscript[r, 1], Subscript[r, 2], Subscript[r, 3]};
```

When a union of half-disks is created, the left boundary will automatically be deleted from the union. So to bound the region again, a line segment is created and joined to the union of half-disks. To cut a hole out of the first half-disk, you can just bound the union from $r_1$ to $r_3$ and from $-r_1$ to $-r_3$ in the $y$ direction, leaving aside the first half-disk.

Create two line segments:

```wl
In[4]:= l = {Line[{{0, Subscript[r, 1]}, {0, Subscript[r, 3]}}], Line[{{0, -Subscript[r, 1]}, {0, -Subscript[r, 3]}}]};
```

Generate a union of the half-disks and the lines:

```wl
In[5]:= ru = RegionUnion[Flatten[{l, c}]];
```

Generate a boundary mesh restricted to $\left\{\left\{-0.01,r_3\right\},\left\{-r_3,r_3\right\}\right\}$ :

```wl
In[6]:= bmesh = ToBoundaryMesh[ru, {{-0.01, Subscript[r, 3]}, {-Subscript[r, 3], Subscript[r, 3]}}]

Out[6]= ElementMesh[{{0., 0.012}, {-0.012, 0.012}}, Automatic]
```

Visualize the boundary mesh wireframe:

```wl
In[7]:= bmesh["Wireframe"]

Out[7]= [image]
```

### Properties & Relations (1)

Pass options for ``ToBoundaryMesh`` to ``NDSolve`` to solve a stationary PDE:

```wl
In[1]:= if = NDSolveValue[{-(D[u[x, y], x, x] + D[u[x, y], y, y]) == 1, DirichletCondition[u[x, y] == 0, True]}, u, {x, y}∈Disk[], Method -> {"FiniteElement", "MeshOptions" -> {"MaxBoundaryCellMeasure" -> 0.05, "MaxCellMeasure" -> 0.1}}]

Out[1]=
InterpolatingFunction[{{-1.0000000000000067, 1.000000000000007}, 
  {-1.0000000000000067, 1.0000000000000067}}, {5, 4225, 0, {1249, 0}, {3, 3}, 0, 0, 0, 0, 
  Indeterminate & , {}, {}, False}, 
 {NDSolve`FEM`ElementMesh[CompressedData["«22667»"], 
 ...  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
      1, 1, 1, 1, 1}]}]}, CompressedData["«9825»"], {Automatic}]
```

Visualize the mesh used for the computation:

```wl
In[2]:= if["ElementMesh"]["Wireframe"]

Out[2]= [image]
```

### Possible Issues (6)

If the input symbolic region is unbounded, a default bounding box from $\{-1,1\}$ is assumed in each space direction:

```wl
In[1]:= ToBoundaryMesh[ImplicitRegion[x ≤ 1 / 3 || 2 / 3 ≤ x, {x, y}]]["Wireframe"]
```

ToBoundaryMesh::femtemnbb: The bounds for ImplicitRegion[x<=1/3\|\|2/3<=x,{x,y}] are {{-\[Infinity],\[Infinity]},{-\[Infinity],\[Infinity]}}. Unless finite numeric bounds are specified, the mesh generation will constrain the region to have finite bounds.

```wl
Out[1]= [image]
```

By specifying an explicit bounding box, the default can be overridden:

```wl
In[2]:= ToBoundaryMesh[ImplicitRegion[x ≤ 1 / 3 || 2 / 3 ≤ x, {x, y}], {{0, 1}, {0, 1}}]["Wireframe"]

Out[2]= [image]
```

---

Sometime boundary features may not be resolved well:

```wl
In[1]:=
bem = ToBoundaryMesh[ImplicitRegion[!((x - 0.2) ^ 2 + (y - 0.2) ^ 2 ≤ 0.05 ^ 2), {{x, 0, 2.2}, {y, 0, 0.41}}], "BoundaryMeshGenerator" -> {"RegionPlot"}];
bem["Wireframe"[PlotRange -> {{0, 0.5}, {-0.01, 0.42}}]]

Out[1]= [image]
```

Setting the size of the boundary elements and increasing the sample points may help:

```wl
In[2]:=
bem = ToBoundaryMesh[ImplicitRegion[!((x - 0.2) ^ 2 + (y - 0.2) ^ 2 ≤ 0.05 ^ 2), {{x, 0, 2.2}, {y, 0, 0.41}}], 
	"MaxBoundaryCellMeasure" -> 0.01, 
	"BoundaryMeshGenerator" -> {"RegionPlot", "SamplePoints" -> 43}];
bem["Wireframe"[PlotRange -> {{0, 0.5}, {-0.01, 0.42}}]]

Out[2]= [image]
```

Alternatively, the ``"Continuation"`` boundary mesh generator may work better:

```wl
In[3]:=
bem = ToBoundaryMesh[ImplicitRegion[!((x - 0.2) ^ 2 + (y - 0.2) ^ 2 ≤ 0.05 ^ 2), {{x, 0, 2.2}, {y, 0, 0.41}}], 
	"BoundaryMeshGenerator" -> "Continuation", AccuracyGoal -> 4];
bem["Wireframe"[PlotRange -> {{0, 0.5}, {-0.01, 0.42}}]]

Out[3]= [image]
```

---

Specifying ``"IncludePoints"`` that lie on the boundary of the specified region can create boundary meshes that cannot be converted to full meshes:

```wl
In[1]:= includePoint = N[4 * {Cos[π / 7], Sin[π / 7]}];

In[2]:= bmesh = ToBoundaryMesh[Circle[{0, 0}, 4], "IncludePoints" -> {includePoint}]

Out[2]= ElementMesh[{{-4., 4.}, {-4., 4.}}, Automatic]

In[3]:= ToElementMesh[bmesh]
```

ToElementMesh::fememins: The mesh elements are not valid. A set of valid mesh element incidents needs to be positive integers and be able to form a complete sequence starting from 1 to the largest incident present. There are missing incidents; a complete sequence cannot be formed.

ToElementMesh::femtemnm: A mesh could not be generated.

```wl
Out[3]= $Failed
```

The specified ``"IncludePoints"`` need to be inside the region:

```wl
In[4]:= bmesh = ToBoundaryMesh[Circle[{0, 0}, 4], "IncludePoints" -> {includePoint - 0.01}]

Out[4]= ElementMesh[{{-4., 4.}, {-4., 4.}}, Automatic]

In[5]:= ToElementMesh[bmesh]

Out[5]= ElementMesh[{{-4., 4.}, {-4., 4.}}, {TriangleElement[<581>]}]
```

The amount by which the ``"IncludePoints"`` need to be inside the region depends on accuracy of the curvature of the mesh.

---

The arguments to ``ToBoundaryMesh`` are specified as rules and need to be given in the correct order: ``"Coordinates"``, ``"BoundaryElements"`` and optionally ``"PointElements"`` :

```wl
In[1]:= ToBoundaryMesh["BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]}, "Coordinates" -> {{0., 0.}, {1., 0.}, {1., 1.}, {0., 1.}}]

Out[1]= ToBoundaryMesh["BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]}, "Coordinates" -> {{0., 0.}, {1., 0.}, {1., 1.}, {0., 1.}}]
```

Specify the arguments in the correct order:

```wl
In[2]:= ToBoundaryMesh["Coordinates" -> {{0., 0.}, {1., 0.}, {1., 1.}, {0., 1.}}, "BoundaryElements" -> {LineElement[{{1, 2}, {2, 3}, {3, 4}, {4, 1}}]}]

Out[2]= ElementMesh[{{0., 1.}, {0., 1.}}, Automatic]
```

---

The automatic boundary marker assignment depends on a threshold that determines if two connected boundary elements are assigned the same boundary marker or not. Consider the following boundary mesh:

```wl
In[1]:=
coordinates = {{1 / 3, 0}, {1, 0}, {1, 1 / 2}, {0, 1 / 2}, {0, 1 / 20}};
lineSegments = {{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 1}};
bmesh = ToBoundaryMesh["Coordinates" -> coordinates, "BoundaryElements" -> {LineElement[lineSegments]}];
```

Visualize the mesh with boundary markers in blue and point node numbers in red:

```wl
In[2]:=
Show[bmesh["Wireframe"["MeshElement" -> "BoundaryElements", "MeshElementMarkerStyle" -> Blue]], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementIDStyle" -> Red]]]

Out[2]= [image]
```

Note that the edges between nodes 5 and 1 and 1 and 2 have both been attributed the boundary marker 1. The option ``"BoundaryGroupingThreshold"`` can be used to change the threshold:

```wl
In[3]:=
bmesh = ToBoundaryMesh["Coordinates" -> coordinates, "BoundaryElements" -> {LineElement[lineSegments]}, "BoundaryGroupingThreshold" -> 0.99];
Show[bmesh["Wireframe"["MeshElement" -> "BoundaryElements", "MeshElementMarkerStyle" -> Blue]], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementIDStyle" -> Red]]]

Out[3]= [image]
```

A second alternative is to specify the boundary markers during input:

```wl
In[4]:=
bmesh = ToBoundaryMesh["Coordinates" -> coordinates, "BoundaryElements" -> {LineElement[lineSegments, {1, 2, 3, 4, 5}]}];
Show[bmesh["Wireframe"["MeshElement" -> "BoundaryElements", "MeshElementMarkerStyle" -> Blue]], 
	bmesh["Wireframe"["MeshElement" -> "PointElements", "MeshElementIDStyle" -> Red]]]

Out[4]= [image]
```

---

## See Also

* [ElementMesh](https://reference.wolfram.com/language/FEMDocumentation/ref/ElementMesh.en.md)
* [ToElementMesh](https://reference.wolfram.com/language/FEMDocumentation/ref/ToElementMesh.en.md)
* [`DiscretizeGraphics`](https://reference.wolfram.com/language/ref/DiscretizeGraphics.en.md)
* [`DiscretizeRegion`](https://reference.wolfram.com/language/ref/DiscretizeRegion.en.md)
* [`BoundaryMeshRegion`](https://reference.wolfram.com/language/ref/BoundaryMeshRegion.en.md)
* [`MeshRegion`](https://reference.wolfram.com/language/ref/MeshRegion.en.md)
* [`RegionUnion`](https://reference.wolfram.com/language/ref/RegionUnion.en.md)
* [`RegionIntersection`](https://reference.wolfram.com/language/ref/RegionIntersection.en.md)
* [`RegionDifference`](https://reference.wolfram.com/language/ref/RegionDifference.en.md)
* [`BooleanRegion`](https://reference.wolfram.com/language/ref/BooleanRegion.en.md)
* [`ImplicitRegion`](https://reference.wolfram.com/language/ref/ImplicitRegion.en.md)
* [ToNumericalRegion](https://reference.wolfram.com/language/FEMDocumentation/ref/ToNumericalRegion.en.md)
* [ElementMeshProjection](https://reference.wolfram.com/language/FEMDocumentation/ref/ElementMeshProjection.en.md)

## Tech Notes

* [Element Mesh Generation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.en.md)
* [Element Mesh Visualization](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshVisualization.en.md)
* [Using OpenCascadeLink](https://reference.wolfram.com/language/OpenCascadeLink/tutorial/UsingOpenCascadeLink.en.md)
* [NDSolve Finite Element Options](https://reference.wolfram.com/language/FEMDocumentation/tutorial/FiniteElementOptions.en.md)

## Related Guides

* [Finite Element Method](https://reference.wolfram.com/language/FEMDocumentation/guide/FiniteElementMethodGuide.en.md)