I recently upgraded to MM 12.1 so I could use OpenCascadeLink for 3D meshing following the excellent answer to a question regarding 3D meshing:

3D Meshing Internal regions

Assigning boundary element markers using ToBoundaryMesh[] can be done using a "BoundaryMarkerFunction", but...

Question 1) is there a way to pre-assign markers using OpenCascadeShapeSurfaceMeshToBoundaryMesh[]?

Question 2) The markers are important later in assigning boundary conditions. In the post provided above, a group of 14 boundary markers are returned automatically, and one needs to determine what numbers correspond to what. In this case the first markers (1-6) are those assigned to boundary elements external on the outer surfaces and the remainder 7-14 on the inner surfaces. Is that in general the ordering procedure for OpenCascadeShapeSurfaceMeshToBoundaryMesh[]?

Question 3) does not just apply to OCL. How to view all boundary markers on a 3D Wireframe but so that the markers on boundary elements which are not currently visible are also hidden until the object is suitably rotated? Is there a "MeshElementMarkerStyle" option?

In the picture below it is a little messy picking out which elements are being referenced by the automatically assigned markers due to the visibility of all the markers. Color matching the faces is an option (as presented in the OCL documentation) but if 14+ markers are present matching the 14+ colors is not desirable. Note Opacity[0.75] ->1 has no effect on the marker text.

bmesh = OpenCascadeShapeSurfaceMeshToBoundaryMesh[shape];
(*Visualize Surfaces*)
groups = bmesh["BoundaryElementMarkerUnion"];
temp = Most[Range[0, 1, 1/(Length[groups])]];
colors = {Opacity[0.75], ColorData["BrightBands"][#]} & /@ temp;
bmesh["Wireframe"["MeshElementStyle" -> FaceForm /@ colors, 
  "MeshElementMarkerStyle" -> White]]

Boundary Element Markers


Here is the code of shape :


(*Geometry Parameters*){cw, ch, cd, ww, wh} = {0.065, 
   0.033, .027, .013, .022};
yoff = 0.002;
radiusAir = 0.15;
(*Use CSG to Create Core Shape*)
shape0 = OpenCascadeShape[
   Cuboid[{-cw/2, 0 + yoff, -cd/2}, {cw/2, ch + yoff, cd/2}]];
shape1 = OpenCascadeShape[
   Cuboid[{-cw/4 - ww/2, 0 + yoff, -cd/2}, {-cw/4 + ww/2, wh + yoff, 
shape2 = OpenCascadeShape[
   Cuboid[{cw/4 - ww/2, 0 + yoff, -cd/2}, {cw/4 + ww/2, wh + yoff, 
core = OpenCascadeShapeDifference[shape0, shape1];
core = OpenCascadeShapeDifference[core, shape2];
(*Create Air Sphere*)
shapea = OpenCascadeShape[Ball[{0, 0, 0}, radiusAir]];
(*Create Quarter Symmetry*)
(*Create Quarter Symmetry Cube*)
shapeq = OpenCascadeShape[
   Cuboid[{0, 0, -radiusAir}, {radiusAir, radiusAir, 0}]];
(*Create Quarter Symmetry Regions*)
shapeinta = OpenCascadeShapeIntersection[shapeq, shapea];
shapeintcore = OpenCascadeShapeIntersection[shapeq, core];
(*Create Shape with Internal Boundaries*)

union = OpenCascadeShapeUnion[shapeinta, shapeintcore];
intersection = OpenCascadeShapeIntersection[shapeinta, shapeintcore];
shape = OpenCascadeShapeSewing[{union, intersection}];

This code comes from 3D Meshing Internal regions

  • $\begingroup$ Regarding the item 3), if Opacity[1] had an effect, it would not be satifactory either because not only the marker behind would be invsible, but also the marker in front would be partly invisible, depending in how they are placed relative to the boundary they refer to. (I just have experienced this diseappointing experience) $\endgroup$
    – andre314
    Sep 6, 2020 at 9:56

2 Answers 2


Unfortunately, the OpenCascadeLink is a relatively new addition and I do not believe there is any way now to pre-assign element markers to boundaries. Currently, Mathematica will do a good job preserving continuity across internal surfaces, but there is limited support at assign boundary conditions aside for DirichletConditions. Additionally, the default boundary condition is a zero flux NeumannValue. For a wide variety of physics problems and symmetry conditions, zero flux applies. Therefore, if one excludes zero flux and symmetry conditions, often that leaves only a few surfaces left to assign.

I too, found no great way to display boundary ElementMarkers in 3D, so I wrote the following manipulate code to display the ElementMarkers one-at-a-time. Perhaps, you will find it useful.

      "Wireframe"[ElementMarker == #, 
       "MeshElementStyle" -> FaceForm[colors[[#]]]]]}, 
    PlotLabel -> 
     Style[StringTemplate["ElementMarker = ``"][#], 18, 
      colors[[#, 2]]]] &[group], {group, First@groups, Last@groups, 
  1}, ControlPlacement -> Top]

Surface Manipulate

Update 1: Using CheckBox control

To view multiple surface at once, it is convenient to use a CheckboxBar control like so:

surfaces = 
   bmesh["Wireframe"[ElementMarker == #, 
       "MeshElementStyle" -> FaceForm[colors[[#]]]]] & /@ groups];
   choices /. surfaces}], {{choices, groups}, groups, CheckboxBar}, 
 ControlPlacement -> Top]

The following displays all the internal surfaces:

enter image description here

Update 2: Surface Identification Using Annotations

Here is a technique to identify a surface group with a PlotLabel by hovering over a GraphicsComplex that represents the group ID.

First, we will define some shorthands to extract mesh information:

(* Shorthand functions to extract mesh info *)
ebif = ElementIncidents[#["BoundaryElements"]][[1]] &;
ebmf = ElementMarkers[#["BoundaryElements"]][[1]] &;
eif = ElementIncidents[#["MeshElements"]][[1]] &;
emf = ElementMarkers[#["MeshElements"]][[1]] &;
epif = Flatten@ElementIncidents[#["PointElements"]] &;
epmf = Flatten@ElementMarkers[#["PointElements"]] &;
UF = Union@Flatten[#, Infinity] &;

Next, we define a function, meshByBoundaryID, that generates an independent BoundaryMesh by group ID:

(* Function to Extract Boundary Mesh by Marker *)
meshByBoundaryID[m_][marker_] := Module[
  {inc = ebif[m], mrk = ebmf[m], crd = m["Coordinates"], tinc, uniq, 
   newcrd, bm},
  tinc = Extract[inc, Position[mrk, marker]];
  uniq = UF@tinc;
  newcrd = crd[[UF@tinc]];
  tinc = tinc /. AssociationThread[uniq -> Range[Length@uniq]];
  bm = ToBoundaryMesh["Coordinates" -> newcrd, 
    "BoundaryElements" -> {TriangleElement[tinc, 
       ConstantArray[marker, Length@tinc]]}, "MeshOrder" -> 1];

(* Create instance of function based on grouped BoundaryMesh *)
bm = meshByBoundaryID[bmesh];

Next, we create the visualization:

(* BarLegend of groups *)
legendBar = 
  BarLegend[{"BrightBands", MinMax[groups]}, (Length[groups] - 2), 
   LegendLabel -> Style["group", Opacity[0.6`]]];
(* Convert Individual bmeshes into GraphicsComplex for Annotation *)
gcs = Graphics3D[{Directive[EdgeForm[Black], FaceForm[colors[[#]]]], 
       Style[StringTemplate["ElementMarker = ``"][#], 18, 
        colors[[#]]], "Mouse"]}, 
     PlotLabel -> Dynamic[MouseAnnotation[""]], Boxed -> False] & /@ 
(* Surface Association for CheckBoxBar *)
surfaces = AssociationThread[groups, gcs[[#]] & /@ groups];
(* Visualization *)
    {choices /. surfaces}}], legendBar], {{choices, groups}, groups, 
  CheckboxBar}, ControlPlacement -> Top]

The animation augmented with Camtasia annotations at the bottom shows the following workflow:

  • Orient Model to view symmetry planes
  • Sweep mouse over symmetry surfaces to identify group IDs (1, 2, 3, 4, 6)
  • Toggle visibility of (1, 2, 3, 4, 6)
  • Identify Curved Boundary (5)
  • Toggle visibility of (5)
  • Remaining surfaces are internal

Annotation Animation

One could envision making a more dynamic application that could toggle visibility with MouseDown events, but this simple approach took less than 20 seconds to identify key surfaces.

Unless it is explicitly documented somewhere, I don't think you can count on the ordering of internal surfaces last to be stable version-to-version.

  • $\begingroup$ Thanks Tim - again! Very useful function. $\endgroup$
    – Greenasnz
    Sep 15, 2020 at 14:26

Not an answer, just a illustration of what I mean in my comment above :

enter image description here


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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