# How do you refine the elements of a 3D mesh?

Original Question:

I am trying to refine a 3D mesh in a certain region. This seems relevant but does not work for me. I start by making the region which consists of a box with an internal plate attached to one side.

Needs["NDSolveFEM"];
a = 1;
b = 0.5;
L = 0.2 + 0.0302;
d = 1;
tk = 0.01;
pts = {{L, -tk, 0}, {L, tk, 0}, {-L, tk, 0}, {-L, -tk, 0}, {L, -tk,
d}, {L, tk, d}, {-L, tk, d}, {-L, -tk, d}};
pts1 = {{a, -a, -b}, {a, a, -b}, {-a, a, -b}, {-a, -a, -b}, {a, -a,
d}, {a, a, d}, {-a, a, d}, {-a, -a, d}};
allpts = Join[pts, pts1];

hex = {{1, 2, 3, 4}, {1, 5, 6, 2}, {2, 6, 7, 3}, {7, 8, 4, 3}, {5, 1,
4, 8}};
hex1 = {{12, 9, 10, 11}, {9, 13, 14, 10}, {14, 15, 11, 10}, {15, 16,
12, 11}, {16, 13, 9, 12}};
hex2 = {{16, 8, 5, 13}, {13, 5, 6, 14}, {6, 7, 15, 14}, {16, 15, 7,
8}};
\[ScriptCapitalR] =
BoundaryMeshRegion[
allpts, {Polygon[hex], Polygon[hex1], Polygon[hex2]},
MeshCellStyle -> Opacity[0.3]];
HighlightMesh[\[ScriptCapitalR], Labeled[0, "Index"]]


This looks satisfactory so I continue to make a boundary mesh and a full mesh

bmesh = ToBoundaryMesh[\[ScriptCapitalR],
"BoundaryMeshGenerator" -> "Continuation"];
mesh = ToElementMesh[bmesh]


(* ElementMesh[{{-1., 1.}, {-1., 1.}, {-0.5, 1.}}, {TetrahedronElement[ "<" 16873 ">"]}] *)

I notice I get 16873 elements. The wire frame looks reasonable and I also plot within the outside box to inspect my plate.

m1 = mesh["Wireframe"]
Graphics3D[m1[[1]], Axes -> True,
PlotRange -> {All, {-0.5, 0.5}, All}]


I can improve my mesh by setting MaxCellMeasure

mesh = ToElementMesh[bmesh, MaxCellMeasure -> 0.0001]
m2 = mesh["Wireframe"]
Graphics3D[m2[[1]], Axes -> True,
PlotRange -> {All, {-0.5, 0.5}, All}]


(* ElementMesh[{{-1., 1.}, {-1., 1.}, {-0.5, 1.}}, {TetrahedronElement[ "<" 114246 ">"]}]*)

I now have 114246 elements. I only need the small elements around the plate so I attempt to make a mesh refinement function

ClearAll[mrf];
mrf = Function[{vert, vol},
Block[{y},
y = Min[Abs[vert[[All, 2]]]];
y < 0.2 && vol > 0.005
]
];


When I apply this option it seems to get ignored and I get back my first attempt with 16873 elements.

mesh = ToElementMesh[bmesh, MeshRefinementFunction -> mrf]


(* ElementMesh[{{-1., 1.}, {-1., 1.}, {-0.5, 1.}}, {TetrahedronElement[ "<" 16873 ">"]}]*)

How do I refine in a local region? Thanks for any enlightenment.

User21 (always very helpful; thank you) has given a very useful answer but has not quite got me to where I wish to be.

Using MaxCellMeasure -> Infinity does give a refined mesh in some locations but not as much as I was expecting. Thus using this code

ClearAll[mrf];
mrf[{vert_, vol_}] := Module[{y},
y = Min[Abs[vert[[All, 2]]]];
y < 0.4 && vol > 0.00001
];
mesh = ToElementMesh[bmesh, MaxCellMeasure -> Infinity,
MeshRefinementFunction -> mrf]
m3 = mesh["Wireframe"]
Graphics3D[m3[[1]], Axes -> True, PlotRange -> {All, {-0.5, 0.5}, All}]
Graphics3D[{Point[mesh[[1]]]},
AspectRatio -> Automatic, Axes -> True
]


I have added a visualization of the elements by plotting their end points. Looking at the boundary mesh on the plate it is approximately the same as the first attempt using default values. It is not as good at the second case where I refined everywhere. Where I refined everywhere the boundary mesh was improved as needed. For my MeshRefinementFunction I don't seem to have changed the boundary mesh much. Do I have to do a boundary refinement function as well?

Addition using the update from user21.

User21 used the centre of each tet rather than the node nearest the plate. This seems to make a better job. In an extension to this update I have generated a mesh refinement function that just refines the volume around the plate. I have also defined a value for the mesh outside this volume so that the elements are not too small elsewhere. Thus

ClearAll[cf];
cf = With[{tk = tk, d = d, L = L},
Compile[{{c, _Real, 2}, {a, _Real, 0}},
Block[{com},
com = Total[c]/4;
If[
- 5 tk <= com[[2]] <= 5 tk &&
-L - 5 tk < com[[1]] <= L + 5 tk &&
-5 tk < com[[3]] &&
a > 0.00001, True, False]
]
]
];
mesh = ToElementMesh[bmesh, MaxCellMeasure -> 0.0005,


MeshRefinementFunction -> cf]

mesh = ToElementMesh[bmesh, MaxCellMeasure -> 0.0005,
MeshRefinementFunction -> cf]
mesh["Wireframe"[Axes -> True, PlotRange -> {All, {-0.5, 0.5}, All}]]
Graphics3D[{Point[mesh[[1]]]},
AspectRatio -> Automatic, Axes -> True
]


This gives me what I need. Thanks

• As a side note, the boundary mesh generator "Continuation" is for 2D only. So it will not have any effect in this case. Commented May 17, 2016 at 3:18
• @user21 A point to note; I missed that.
– Hugh
Commented May 17, 2016 at 9:28

You are almost there. You need to specify a cell measure for the 'background' mesh like so - assuming you want as few elements in not refined regions:

ClearAll[mrf];
mrf = Function[{vert, vol}, Block[{y}, y = Min[Abs[vert[[All, 2]]]];
y < 0.2 && vol > 0.005]];
bmesh = ToBoundaryMesh[\[ScriptCapitalR]];
mesh = ToElementMesh[bmesh, MaxCellMeasure -> Infinity,
MeshRefinementFunction -> mrf]

mesh["Wireframe"]


There is an example of this in the ToElementMesh ref page. It's under Options, "MeshRefinementFunction", the last example, the 3D one. Hope that helps.

Update

To refine the inner plate, I used a different refinement function.

bmesh = ToBoundaryMesh[\[ScriptCapitalR]];
cf = With[{tk = tk, d = d, L = L},
Compile[{{c, _Real, 2}, {a, _Real, 0}},
Block[{com},
com = Total[c]/4;
If[-2 L <= com[[1]] <= 2 L && -2 tk <= com[[2]] <= 2 tk &&
0 <= com[[3]] && a > 0.000001, True, False]]
]
];
mesh = ToElementMesh[bmesh, MaxCellMeasure -> Infinity,
MeshRefinementFunction -> cf]
m3 = mesh[
"Wireframe"[Axes -> True, PlotRange -> {All, {-0.5, 0.5}, All}]]


How did I get there: Basically, I started with selecting the plate in one of the three dimensions:

cf = With[{tk = tk, d = d, L = L},
Compile[{{c, _Real, 2}, {a, _Real, 0}},
Block[{com},
com = Total[c]/4;
If[(*-2L\[LessEqual]com[[1]]\[LessEqual]2L&&*)-2 tk <=
com[[2]] <= 2 tk(*&&0\[LessEqual]com[[3]]*)&& a > 0.00001,
True, False]]
]
];


Then added the other two and then cranked up the minimal area for the tets in that volume. Note that the plate is thin compared to the rest of the structure. So you may need to change the 2*tk to less, maybe even down to one.

• Very helpful; thanks. However, I am not getting all the refinement I would like. I have added an extension to my question addressing the issue.
– Hugh
Commented May 17, 2016 at 9:30
• Yes this has got me to a good solution. I note that you are using the center of each tet rather than looking for the node nearest the plate. Your approach works best. Many thanks.
– Hugh
Commented May 17, 2016 at 11:37