# Prelude

Mathematica 11.3, Windows. 

Let us say I have a rectangular region with 10 little holes inside. Their coordinates are given by `positionList`. The resulting region `mr` is made through `RegionDifference` and `RegionUnion` of disks and rectangles:

    Needs["NDSolve`FEM`"]
    
    radius = 0.1;
    Louter = 2;
    OuterRegion = Rectangle[{-L/2, -L/2}, {L/2, L/2}] /. L -> Louter;
    
    positionList = {{0.706`, -0.14`}, {0.389`, 0.593`}, {-0.278`, 
        0.429`}, {0.254`, 
        0.844`}, {-0.46`, -0.367`}, {0.737`, -0.759`}, {-0.07`, -0.664`}, \
    {-0.469`, 0.626`}, {-0.755`, -0.509`}, {-0.455`, -0.015`}};
    diskRegions = Disk[#, radius] & /@ positionList;
    mr = RegionDifference[OuterRegion, RegionUnion@diskRegions];
    
    LP = ListPlot[positionList -> Table[k, {k, 1, Length@positionList}]];
    RP = Show[mr // RegionPlot, LP, ImageSize -> 400]

[![enter image description here][1]][1]

The labels of the disks show the order in which they appear in `positionList`. Ultimately I want a FEM mesh where I can apply specific boundary conditions on disk 1, disk 2, disk 3 etc. First, I make a boundary mesh:

    bmesh = ToBoundaryMesh[RegionBoundary@mr, 
       "RegionHoles" -> positionList, "BoundaryGroupingThreshold" -> 0, 
       AccuracyGoal -> 1];
    Show[bmesh["Wireframe"["MeshElementMarkerStyle" -> Blue]], 
     ImageSize -> 400]

[![enter image description here][2]][2]

# Problem

As you can see, the **Region Markers are scrambled**! In the boundary mesh, the hole corresponding to disk 2 (in the blue plot above) actually has marker 14. Hole 3 has marker 13, and so on. 

**I want the markers of the boundary mesh to be consistent with the disk numbers, here going from 1 to 10. The markers on the walls should have numbers 11-14.** 

# A manual solution

I have solved this problem with a `BoundaryMarkerFunction`, writing a condition manually for each disk and each of the walls:

    boundaryMarkerFunction = 
      Compile[{{boundaryElementCoords, _Real, 
         3}, {pointMarkres, _Integer, 2}},
       Module[{pt1 = #[[1]], pt2 = #[[2]]},
          Which[
           
           (pt1[[1]] - positionList[[1]][[1]])^2 + (pt1[[2]] - 
                positionList[[1]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[1]][[1]])^2 + (pt2[[2]] - 
                positionList[[1]][[2]])^2 < 1.1 radius^2 , 1,
           (pt1[[1]] - positionList[[2]][[1]])^2 + (pt1[[2]] - 
                positionList[[2]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[2]][[1]])^2 + (pt2[[2]] - 
                positionList[[2]][[2]])^2 < 1.1 radius^2 , 2,
           (pt1[[1]] - positionList[[3]][[1]])^2 + (pt1[[2]] - 
                positionList[[3]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[3]][[1]])^2 + (pt2[[2]] - 
                positionList[[3]][[2]])^2 < 1.1 radius^2 , 3,
           (pt1[[1]] - positionList[[4]][[1]])^2 + (pt1[[2]] - 
                positionList[[4]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[4]][[1]])^2 + (pt2[[2]] - 
                positionList[[4]][[2]])^2 < 1.1 radius^2 , 4,
           (pt1[[1]] - positionList[[5]][[1]])^2 + (pt1[[2]] - 
                positionList[[5]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[5]][[1]])^2 + (pt2[[2]] - 
                positionList[[5]][[2]])^2 < 1.1 radius^2 , 5,
           (pt1[[1]] - positionList[[6]][[1]])^2 + (pt1[[2]] - 
                positionList[[6]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[6]][[1]])^2 + (pt2[[2]] - 
                positionList[[6]][[2]])^2 < 1.1 radius^2 , 6,
           (pt1[[1]] - positionList[[7]][[1]])^2 + (pt1[[2]] - 
                positionList[[7]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[7]][[1]])^2 + (pt2[[2]] - 
                positionList[[7]][[2]])^2 < 1.1 radius^2 , 7,
           (pt1[[1]] - positionList[[8]][[1]])^2 + (pt1[[2]] - 
                positionList[[8]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[8]][[1]])^2 + (pt2[[2]] - 
                positionList[[8]][[2]])^2 < 1.1 radius^2 , 8,
           (pt1[[1]] - positionList[[9]][[1]])^2 + (pt1[[2]] - 
                positionList[[9]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[9]][[1]])^2 + (pt2[[2]] - 
                positionList[[9]][[2]])^2 < 1.1 radius^2 , 9,
           (pt1[[1]] - positionList[[10]][[1]])^2 + (pt1[[2]] - 
                positionList[[10]][[2]])^2 < 
             1.1 radius^2 && (pt2[[1]] - 
                positionList[[10]][[1]])^2 + (pt2[[2]] - 
                positionList[[10]][[2]])^2 < 1.1 radius^2 , 10,
           pt1[[1]] < -0.99 && pt2[[1]] < -0.99, 11, (* left wall *)
           pt1[[2]] < -0.99 && pt2[[2]] < -0.99, 12, (* bottom wall *)
           pt1[[1]] > 0.99 && pt2[[1]] > 0.99, 13, (* right wall *)
           pt1[[2]] > 0.99 && pt2[[2]] > 0.99, 14, (* top wall *)
           
           True, 4 ]] & /@ boundaryElementCoords];
    
    bmesh = ToBoundaryMesh[RegionBoundary@mr, 
       "RegionHoles" -> positionList, "BoundaryGroupingThreshold" -> 0, 
       AccuracyGoal -> 2, 
       "BoundaryMarkerFunction" -> boundaryMarkerFunction];
    {Show[bmesh["Wireframe"["MeshElementMarkerStyle" -> Blue]], 
      ImageSize -> 400], RP}


[![enter image description here][3]][3]
[![enter image description here][4]][4]
# An automatic solution?

Could this be automated, so I can vary the number of disks? I would have happily replaced the `Which` construct by some `Table`, but I am not sure what kind of arguments this mysterious `boundaryMarkerFunction` takes. For instance, I tried 

    boundaryMarkerFunction[bmesh["BoundaryElements"], 
     bmesh["Coordinates"]]

which fails because `boundaryElementCoords` is supposed to be 3D.

**Please help me find an automatic solution to label the boundary elements in my desired order.** 

# Just for fun

This allows us to use `ElementMarker` with `NDSolve`, specifying precise individual boundary conditions on the disks. For instance, here is the heat equation with a different `DirichletCondition` on each disk:  

    bmesh = ToBoundaryMesh[RegionBoundary@mr, 
       "RegionHoles" -> positionList, "BoundaryGroupingThreshold" -> 0, 
       AccuracyGoal -> 4, 
       "BoundaryMarkerFunction" -> boundaryMarkerFunction];
    mesh = ToElementMesh[bmesh];
    
    boundaryMarkerList = Table[k, {k, 1, Length@positionList}];
    
    op = - Laplacian[u[x, y], {x, y}] + 0.1 u[x, y];
    BCedges = {DirichletCondition[u[x, y] == 0, ElementMarker == 11], 
       DirichletCondition[u[x, y] == 1, ElementMarker == 13]};
    BCcircles = 
      Table[DirichletCondition[u[x, y] == RandomReal[{0, 1}], 
        ElementMarker == k], {k, 1, Length@positionList}];
    BC = Join[BCedges, BCcircles];
    ufun = NDSolveValue[{op == 0, BC}, u, {x, y} \[Element] mesh]
    
    {
     DensityPlot[ufun[x, y], {x, y} \[Element] mesh, 
      ColorFunction -> "Temperature", AspectRatio -> Automatic, 
      PlotPoints -> 100, PlotRange -> All, ImageSize -> 400],
     Show[mesh["Wireframe"], ImageSize -> 400]
     }

[![enter image description here][5]][5]
[![enter image description here][6]][6]


  [1]: https://i.sstatic.net/QSpyc.png
  [2]: https://i.sstatic.net/rujEq.png
  [3]: https://i.sstatic.net/YRhyo.png
  [4]: https://i.sstatic.net/hgJDO.png
  [5]: https://i.sstatic.net/b6t8g.png
  [6]: https://i.sstatic.net/GXAYY.png