MeshRefinementFunction for region gives error

I would like to refine a mesh within a given path/volume, that is specified by the MeshRefinementFunction option given in ToElementMesh[]. For starting i took a simple 3D MeshRegion of a Tetrahedron

<< NDSolveFEM
reg=MeshRegion[{{0,0,0},{1,0,0},{0,1,0},{0,0,1}},Tetrahedron[{1,2,3,4}]]


Next i defined the MeshRefinementFunction

coord={{0,0,0},{1,0,0},{0,1,0},{0,0,1}};
cf = Function[{coordinates, vol},RegionMember[Ball[Mean[coord], 0.5], Mean[coordinates]]];


So that whenever the element lies within the balls region there should be a refinement. Then i proceed with

mesh = ToElementMesh[reg,MeshRefinementFunction -> cf]

which evaluates to the error code

ToElementMesh::mrff: The MeshRefinementFunction Function[{coordinates,vol},RegionMember[Ball[Mean[coord],0.5],Mean[coordinates]]] is not valid and will be ignored. Possibly the function does not return either True or False or the (auto) compiled function does not evaluate message free.

I dont understand whats the problem in here. cf evaluates to True or False as required.

I agree that the error message is a bit confusing. Maybe the problem is that this MeshRefinementFunction is not compilable? It seems that coordinates not being inlined correctly into the pure function leads to problems: The autocompilation might be run with options RuntimeAttributes -> {Listable}, Parallelization -> True. Since coordinates is not inlined, there may occur calls to MainEvaluate which will lead to a warning message that this cannot be run in parallel. And this warning message will cause that

the (auto) compiled function does not evaluate message free.

The following seems to work, although RegionMemberFunctions in general are not compilable to my knowledge (but for very simple regions like Ball and Cuboid, they are).

Notice that your original MeshRefinementFunction would lead to an infinite recursion of refinements which is why I added an upper bound for the elements' volume.

coord = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
cf = With[{f = RegionMember[Ball[Mean[coord], 0.5]]},
Function[{coordinates, vol}, vol > 0.00001 && f[Mean[coordinates]]]
];
mesh = ToElementMesh[reg, MeshRefinementFunction -> cf]


By the way: This uses the operator version of RegionMember which might be more efficient than calling RegionMember[Ball[Mean[coord], 0.5], Mean[coordinates]] multiple times.

You can even save about one third of runtime (with essentially the same result) by using this more elementary MeshRefinementFunction

cf = Function[{coordinates, vol}, vol > 0.00001 && Norm[Mean[N@coord] - Mean[coordinates]] <= 0.5]

• @HenrikSchumacher, how should the message be written such that it is not confusing? If you have a suggestion I could improve the text. Sep 27, 2018 at 4:51
• (iii) The doc page of MeshRefinementFunction does not state the requirement that the function should be compilable. Actually, DiscretizeRegion[reg, MeshRefinementFunction -> cf] just ignores cf = Function[{coordinates, vol}, vol > 0.00001 && RegionMember[Ball[Mean[coord], 0.5], Mean[coordinates]]]; without any message; that is also not a good user experience. If TetGenLink really cannot handle uncompiled refinement functions, in might be worth adding a section "Possible issues" that explains that. Sep 27, 2018 at 7:05
• @HenrikSchumacher, thanks for the detailed response. How about the message text: "The MeshRefinementFunction XXX is not valid and will be ignored. Possibly the function does not return either True or False or the (auto) compilation failed. Try to provide a compiled function that evaluates to True or False with the arguments of YYYY" where YYYY in this case would be {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {0., 0., 1.}}, 1/6.} . Sep 27, 2018 at 8:34
• @HenrikSchumacher, as a side note, the CF does not have to be free of MainEvaluate in fact that is the mechanism for a general call back from Triangle/TetGen for arbitrary functions. The problem in this case is that the compilation does not happen message free. One would have to go through some length to see if the CF is still OK to use, so I just gave a message (which I think is better then not saying anything at all). Sep 27, 2018 at 10:14
• @HenrikSchumacher, just a quick ping: I just merged the new code: So will get a different message when the function does not return True|False or a different one when the compilation does not work and it suggests with which arguments the compiled function needs to work. Sep 28, 2018 at 13:52