5
$\begingroup$

Response to Answer below.

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["NDSolve`FEM`"];
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"]]

Mathematica graphics

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}]

Mathematica graphics

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 ">"]}]*)

Mathematica graphics

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.

Edit in response to answer

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
 ]

Mathematica graphics

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
 ]

Mathematica graphics

This gives me what I need. Thanks

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

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"]

enter image description here

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}]]

enter image description here

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]]
    ]
   ];

enter image description here

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.

$\endgroup$
  • $\begingroup$ 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. $\endgroup$ – Hugh May 17 '16 at 9:30
  • $\begingroup$ 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. $\endgroup$ – Hugh May 17 '16 at 11:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.