Exemple #1
0
def get_refshape_type(fem_doc_object):
    """ Return shape type the constraints references.

    Determine single shape type of references of *fem_doc_object* which must be
    a constraint (=have a *References* property). All references must be of the
    same type which is than returned as a string. A type can be "Vertex",
    "Edge", "Face" or "Solid".

    :param fem_doc_object:
     A constraint object with a *References* property.

    :returns:
     A string representing the shape type ("Vertex", "Edge", "Face" or
     "Solid"). If *fem_doc_object* isn't a constraint ``""`` is returned.

    :note:
     Undefined behaviour if the type of the references of one object are
     not all the same.

    :note:
     Undefined behaviour if constraint contains no references (empty list).
    """
    from femtools.geomtools import get_element
    if hasattr(fem_doc_object, "References") and fem_doc_object.References:
        first_ref_obj = fem_doc_object.References[0]
        first_ref_shape = get_element(first_ref_obj[0], first_ref_obj[1][0])
        st = first_ref_shape.ShapeType
        FreeCAD.Console.PrintMessage("References: {} in {}, {}\n".format(
            st, fem_doc_object.Name, fem_doc_object.Label))
        return st
    else:
        FreeCAD.Console.PrintMessage("References: empty in {}, {}\n".format(
            fem_doc_object.Name, fem_doc_object.Label))
        return ""
Exemple #2
0
 def select_clicked_reference_shape(self):
     self.setback_listobj_visibility()
     if self.sel_server:
         FreeCADGui.Selection.removeObserver(self.sel_server)
         self.sel_server = None
     if not self.sel_server:
         if not self.references:
             return
         currentItemName = str(self.list_References.currentItem().text())
         for ref in self.references:
             if self.get_item_text(ref) == currentItemName:
                 # print("found: shape: " + ref[0].Name + " element: " + ref[1])
                 if not ref[0].ViewObject.Visibility:
                     self.obj_notvisible.append(ref[0])
                     ref[0].ViewObject.Visibility = True
                 FreeCADGui.Selection.clearSelection()
                 ref_sh_type = ref[0].Shape.ShapeType
                 if ref[1].startswith("Solid") and (
                         ref_sh_type == "Compound"
                         or ref_sh_type == "CompSolid"):
                     # selection of Solids of Compounds or CompSolids is not possible
                     # because a Solid is no Subelement
                     # since only Subelements can be selected
                     # we're going to select all Faces of said Solids
                     # the method getElement(element)doesn't return Solid elements
                     solid = geomtools.get_element(ref[0], ref[1])
                     if not solid:
                         return
                     faces = []
                     for fs in solid.Faces:
                         # find these faces in ref[0]
                         for i, fref in enumerate(ref[0].Shape.Faces):
                             if fs.isSame(fref):
                                 fref_elstring = "Face" + str(i + 1)
                                 if fref_elstring not in faces:
                                     faces.append(fref_elstring)
                     for f in faces:
                         FreeCADGui.Selection.addSelection(ref[0], f)
                 else:
                     # Selection of all other element types is supported
                     FreeCADGui.Selection.addSelection(ref[0], ref[1])
Exemple #3
0
 def has_equal_references_shape_types(self, ref_shty=""):
     for ref in self.references:
         # the method getElement(element) does not return Solid elements
         r = geomtools.get_element(ref[0], ref[1])
         if not r:
             FreeCAD.Console.PrintError(
                 "Problem in retrieving element: {} \n".format(ref[1]))
             continue
         FreeCAD.Console.PrintLog(
             "  ReferenceShape : {}, {}, {} --> {}\n".format(
                 r.ShapeType, ref[0].Name, ref[0].Label, ref[1]))
         if not ref_shty:
             ref_shty = r.ShapeType
         if r.ShapeType != ref_shty:
             message = "Multiple shape types are not allowed in the reference list.\n"
             FreeCAD.Console.PrintMessage(message)
             QtGui.QMessageBox.critical(None,
                                        "Multiple ShapeTypes not allowed",
                                        message)
             return False
     return True
Exemple #4
0
    def get_boundary_layer_data(self):
        # mesh boundary layer
        # currently only one boundary layer setting object is allowed
        # but multiple boundary can be selected
        # Mesh.CharacteristicLengthMin, must be zero
        # or a value less than first inflation layer height
        if not self.mesh_obj.MeshBoundaryLayerList:
            # print("  No mesh boundary layer setting document object.")
            pass
        else:
            Console.PrintMessage("  Mesh boundary layers, we need to get the elements.\n")
            if self.part_obj.Shape.ShapeType == "Compound":
                # see http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 and
                # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520
                err = (
                    "Gmsh could return unexpected meshes for a boolean split tools Compound. "
                    "It is strongly recommended to extract the shape to mesh "
                    "from the Compound and use this one."
                )
                Console.PrintError(err + "\n")
            for mr_obj in self.mesh_obj.MeshBoundaryLayerList:
                if mr_obj.MinimumThickness and Units.Quantity(mr_obj.MinimumThickness).Value > 0:
                    if mr_obj.References:
                        belem_list = []
                        for sub in mr_obj.References:
                            # print(sub[0])  # Part the elements belongs to
                            # check if the shape of the mesh boundary_layer is an
                            # element of the Part to mesh
                            # if not try to find the element in the shape to mesh
                            search_ele_in_shape_to_mesh = False
                            if not self.part_obj.Shape.isSame(sub[0].Shape):
                                Console.PrintLog(
                                    "  One element of the mesh boundary layer {} is "
                                    "not an element of the Part to mesh.\n"
                                    "But we are going to try to find it in "
                                    "the Shape to mesh :-)\n"
                                    .format(mr_obj.Name)
                                )
                                search_ele_in_shape_to_mesh = True
                            for elems in sub[1]:
                                # print(elems)  # elems --> element
                                if search_ele_in_shape_to_mesh:
                                    # we try to find the element it in the Shape to mesh
                                    # and use the found element as elems
                                    # the method getElement(element) does not return Solid elements
                                    ele_shape = geomtools.get_element(sub[0], elems)
                                    found_element = geomtools.find_element_in_shape(
                                        self.part_obj.Shape,
                                        ele_shape
                                    )
                                    if found_element:  # also
                                        elems = found_element
                                    else:
                                        Console.PrintError(
                                            "One element of the mesh boundary layer {} could "
                                            "not be found in the Part to mesh. "
                                            "It will be ignored.\n"
                                            .format(mr_obj.Name)
                                        )
                                # print(elems)  # element
                                if elems not in self.bl_boundary_list:
                                    # fetch settings in DocumentObject
                                    # fan setting is not implemented
                                    belem_list.append(elems)
                                    self.bl_boundary_list.append(elems)
                                else:
                                    Console.PrintError(
                                        "The element {} of the mesh boundary "
                                        "layer {} has been added "
                                        "to another mesh boundary layer.\n"
                                        .format(elems, mr_obj.Name)
                                    )
                        setting = {}
                        setting["hwall_n"] = Units.Quantity(mr_obj.MinimumThickness).Value
                        setting["ratio"] = mr_obj.GrowthRate
                        setting["thickness"] = sum([
                            setting["hwall_n"] * setting["ratio"] ** i for i in range(
                                mr_obj.NumberOfLayers
                            )
                        ])
                        # setting["hwall_n"] * 5 # tangential cell dimension
                        setting["hwall_t"] = setting["thickness"]

                        # hfar: cell dimension outside boundary
                        # should be set later if some character length is set
                        if self.clmax > setting["thickness"] * 0.8 \
                                and self.clmax < setting["thickness"] * 1.6:
                            setting["hfar"] = self.clmax
                        else:
                            # set a value for safety, it may works as background mesh cell size
                            setting["hfar"] = setting["thickness"]
                        # from face name -> face id is done in geo file write up
                        # TODO: fan angle setup is not implemented yet
                        if self.dimension == "2":
                            setting["EdgesList"] = belem_list
                        elif self.dimension == "3":
                            setting["FacesList"] = belem_list
                        else:
                            Console.PrintError(
                                "boundary layer is only supported for 2D and 3D mesh.\n"
                            )
                        self.bl_setting_list.append(setting)
                    else:
                        Console.PrintError(
                            "The mesh boundary layer: {} is not used to create "
                            "the mesh because the reference list is empty.\n"
                            .format(mr_obj.Name)
                        )
                else:
                    Console.PrintError(
                        "The mesh boundary layer: {} is not used to create "
                        "the mesh because the min thickness is 0.0 mm.\n"
                        .format(mr_obj.Name)
                    )
            Console.PrintMessage("  {}\n".format(self.bl_setting_list))
Exemple #5
0
 def get_region_data(self):
     # mesh regions
     if not self.mesh_obj.MeshRegionList:
         # print("  No mesh regions.")
         pass
     else:
         Console.PrintMessage('  Mesh regions, we need to get the elements.\n')
         # by the use of MeshRegion object and a BooleanSplitCompound
         # there could be problems with node numbers see
         # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467
         # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520
         part = self.part_obj
         if (
             self.mesh_obj.MeshRegionList and part.Shape.ShapeType == "Compound"
             and (
                 femutils.is_of_type(part, "FeatureBooleanFragments")
                 or femutils.is_of_type(part, "FeatureSlice")
                 or femutils.is_of_type(part, "FeatureXOR")
             )
         ):
             error_message = (
                 "  The mesh to shape is a boolean split tools Compound "
                 "and the mesh has mesh region list. "
                 "Gmsh could return unexpected meshes in such circumstances. "
                 "It is strongly recommended to extract the shape to mesh "
                 "from the Compound and use this one."
             )
             Console.PrintError(error_message + "\n")
             # TODO: no gui popup because FreeCAD will be in a endless output loop
             #       as long as the pop up is on --> maybe find a better solution for
             #       either of both --> thus the pop up is in task panel
         for mr_obj in self.mesh_obj.MeshRegionList:
             # print(mr_obj.Name)
             # print(mr_obj.CharacteristicLength)
             # print(Units.Quantity(mr_obj.CharacteristicLength).Value)
             if mr_obj.CharacteristicLength:
                 if mr_obj.References:
                     for sub in mr_obj.References:
                         # print(sub[0])  # Part the elements belongs to
                         # check if the shape of the mesh region
                         # is an element of the Part to mesh
                         # if not try to find the element in the shape to mesh
                         search_ele_in_shape_to_mesh = False
                         if not self.part_obj.Shape.isSame(sub[0].Shape):
                             Console.PrintLog(
                                 "  One element of the meshregion {} is "
                                 "not an element of the Part to mesh.\n"
                                 "But we are going to try to find it in "
                                 "the Shape to mesh :-)\n"
                                 .format(mr_obj.Name)
                             )
                             search_ele_in_shape_to_mesh = True
                         for elems in sub[1]:
                             # print(elems)  # elems --> element
                             if search_ele_in_shape_to_mesh:
                                 # we're going to try to find the element in the
                                 # Shape to mesh and use the found element as elems
                                 # the method getElement(element)
                                 # does not return Solid elements
                                 ele_shape = geomtools.get_element(sub[0], elems)
                                 found_element = geomtools.find_element_in_shape(
                                     self.part_obj.Shape, ele_shape
                                 )
                                 if found_element:
                                     elems = found_element
                                 else:
                                     Console.PrintError(
                                         "One element of the meshregion {} could not be found "
                                         "in the Part to mesh. It will be ignored.\n"
                                         .format(mr_obj.Name)
                                     )
                             # print(elems)  # element
                             if elems not in self.ele_length_map:
                                 self.ele_length_map[elems] = Units.Quantity(
                                     mr_obj.CharacteristicLength
                                 ).Value
                             else:
                                 Console.PrintError(
                                     "The element {} of the meshregion {} has "
                                     "been added to another mesh region.\n"
                                     .format(elems, mr_obj.Name)
                                 )
                 else:
                     Console.PrintError(
                         "The meshregion: {} is not used to create the mesh "
                         "because the reference list is empty.\n"
                         .format(mr_obj.Name)
                     )
             else:
                 Console.PrintError(
                     "The meshregion: {} is not used to create the "
                     "mesh because the CharacteristicLength is 0.0 mm.\n"
                     .format(mr_obj.Name)
                 )
         for eleml in self.ele_length_map:
             # the method getElement(element) does not return Solid elements
             ele_shape = geomtools.get_element(self.part_obj, eleml)
             ele_vertexes = geomtools.get_vertexes_by_element(self.part_obj.Shape, ele_shape)
             self.ele_node_map[eleml] = ele_vertexes
         Console.PrintMessage("  {}\n".format(self.ele_length_map))
         Console.PrintMessage("  {}\n".format(self.ele_node_map))
Exemple #6
0
    def processRefinements(self):
        """ Process mesh refinements """

        mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj)

        if self.mesh_obj.MeshUtility == "gmsh":
            # mesh regions
            self.ele_length_map = {}  # { 'ElementString' : element length }
            self.ele_node_map = {}  # { 'ElementString' : [element nodes] }
            if not mr_objs:
                print('  No mesh refinements')
            else:
                print('  Mesh refinements found - getting elements')
                if self.part_obj.Shape.ShapeType == 'Compound':
                    # see http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 and http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520
                    err = "GMSH could return unexpected meshes for a boolean split tools Compound. It is strongly recommended to extract the shape to mesh from the Compound and use this one."
                    FreeCAD.Console.PrintError(err + "\n")
                for mr_obj in mr_objs:
                    if mr_obj.RelativeLength:
                        if mr_obj.References:
                            for sub in mr_obj.References:
                                # Check if the shape of the mesh region is an element of the Part to mesh;
                                # if not try to find the element in the shape to mesh
                                search_ele_in_shape_to_mesh = False
                                ref = FreeCAD.ActiveDocument.getObject(sub[0])
                                if not self.part_obj.Shape.isSame(ref.Shape):
                                    search_ele_in_shape_to_mesh = True
                                elems = sub[1]
                                if search_ele_in_shape_to_mesh:
                                    # Try to find the element in the Shape to mesh
                                    ele_shape = FemGeomTools.get_element(
                                        ref, elems
                                    )  # the method getElement(element) does not return Solid elements
                                    found_element = CfdTools.findElementInShape(
                                        self.part_obj.Shape, ele_shape)
                                    if found_element:
                                        elems = found_element
                                    else:
                                        FreeCAD.Console.PrintError(
                                            "One element of the meshregion " +
                                            mr_obj.Name +
                                            " could not be found in the Part to mesh. It will be ignored.\n"
                                        )
                                        elems = None
                                if elems:
                                    if elems not in self.ele_length_map:
                                        # self.ele_length_map[elems] = Units.Quantity(mr_obj.CharacteristicLength).Value
                                        mr_rellen = mr_obj.RelativeLength
                                        if mr_rellen > 1.0:
                                            mr_rellen = 1.0
                                            FreeCAD.Console.PrintError(
                                                "The meshregion: " +
                                                mr_obj.Name +
                                                " should not use a relative length greater than unity.\n"
                                            )
                                        elif mr_rellen < 0.01:
                                            mr_rellen = 0.01  # Relative length should not be less than 1/100 of base length
                                            FreeCAD.Console.PrintError(
                                                "The meshregion: " +
                                                mr_obj.Name +
                                                " should not use a relative length smaller than 0.01.\n"
                                            )
                                        self.ele_length_map[
                                            elems] = mr_rellen * self.clmax
                                    else:
                                        FreeCAD.Console.PrintError(
                                            "The element " + elems +
                                            " of the mesh refinement " +
                                            mr_obj.Name +
                                            " has been added to another mesh refinement.\n"
                                        )
                        else:
                            FreeCAD.Console.PrintError(
                                "The meshregion: " + mr_obj.Name +
                                " is not used to create the mesh because the reference list is empty.\n"
                            )
                    else:
                        FreeCAD.Console.PrintError(
                            "The meshregion: " + mr_obj.Name +
                            " is not used to create the mesh because the CharacteristicLength is 0.0 mm.\n"
                        )
                for eleml in self.ele_length_map:
                    ele_shape = FemGeomTools.get_element(
                        self.part_obj, eleml
                    )  # the method getElement(element) does not return Solid elements
                    ele_vertexes = FemGeomTools.get_vertexes_by_element(
                        self.part_obj.Shape, ele_shape)
                    self.ele_node_map[eleml] = ele_vertexes

        else:
            cf_settings = self.cf_settings
            cf_settings['MeshRegions'] = {}
            cf_settings['BoundaryLayers'] = {}
            cf_settings['InternalRegions'] = {}
            snappy_settings = self.snappy_settings
            snappy_settings['MeshRegions'] = {}
            snappy_settings['InternalRegions'] = {}

            from collections import defaultdict
            ele_meshpatch_map = defaultdict(list)
            if not mr_objs:
                print('  No mesh refinement')
            else:
                print('  Mesh refinements - getting the elements')
                if "Boolean" in self.part_obj.Name:
                    err = "Cartesian meshes should not be generated for boolean split compounds."
                    FreeCAD.Console.PrintError(err + "\n")

                # Make list of list of all references for their corresponding mesh object
                bl_matched_faces = []
                if self.mesh_obj.MeshUtility == 'cfMesh':
                    CfdTools.cfdMessage("Matching refinement regions")

                    region_face_list = []
                    for mr_id, mr_obj in enumerate(mr_objs):
                        if mr_obj.NumberLayers > 1 and not mr_obj.Internal:
                            refs = mr_obj.References
                            for r in refs:
                                obj = FreeCAD.ActiveDocument.getObject(r[0])
                                if not obj:
                                    raise RuntimeError(
                                        "Referenced object '{}' not found - object may "
                                        "have been deleted".format(r[0]))
                                try:
                                    f = obj.Shape.getElement(r[1])
                                except Part.OCCError:
                                    raise RuntimeError(
                                        "Referenced face '{}:{}' not found - face may "
                                        "have been deleted".format(r[0], r[1]))
                                region_face_list.append((f, mr_id))

                    # Make list of all faces in meshed shape with original index
                    mesh_face_list = \
                        list(zip(self.mesh_obj.Part.Shape.Faces, range(len(self.mesh_obj.Part.Shape.Faces))))

                    # Match them up
                    bl_matched_faces = CfdTools.matchFaces(
                        region_face_list, mesh_face_list)

                for mr_id, mr_obj in enumerate(mr_objs):
                    Internal = mr_obj.Internal

                    if mr_obj.RelativeLength:
                        # Store parameters per region
                        mr_rellen = mr_obj.RelativeLength
                        if mr_rellen > 1.0:
                            mr_rellen = 1.0
                            FreeCAD.Console.PrintError(
                                "The meshregion: {} should not use a relative length greater "
                                "than unity.\n".format(mr_obj.Name))
                        elif mr_rellen < 0.001:
                            mr_rellen = 0.001  # Relative length should not be less than 0.1% of base length
                            FreeCAD.Console.PrintError(
                                "The meshregion: {} should not use a relative length smaller "
                                "than 0.001.\n".format(mr_obj.Name))

                        if not (self.mesh_obj.MeshUtility == 'snappyHexMesh'
                                and mr_obj.Baffle):
                            fid = open(
                                os.path.join(self.triSurfaceDir,
                                             mr_obj.Name + '.stl'), 'w')

                        snappy_mesh_region_list = []
                        patch_list = []
                        for (si, sub) in enumerate(mr_obj.References):
                            shape = FreeCAD.ActiveDocument.getObject(
                                sub[0]).Shape
                            elem = sub[1]

                            if self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle:
                                # Save baffle references or faces individually
                                baffle = "{}{}{}".format(
                                    mr_obj.Name, sub[0], elem)
                                fid = open(
                                    os.path.join(self.triSurfaceDir,
                                                 baffle + ".stl"), 'w')
                                snappy_mesh_region_list.append(baffle)

                            if elem.startswith(
                                    'Solid'
                            ):  # getElement doesn't work with solids for some reason
                                elt = shape.Solids[int(elem.lstrip('Solid')) -
                                                   1]
                            else:
                                elt = shape.getElement(elem)
                            if elt.ShapeType == 'Face' or elt.ShapeType == 'Solid':
                                CfdTools.cfdMessage(
                                    "Triangulating part: {}:{} ...".format(
                                        FreeCAD.ActiveDocument.getObject(
                                            sub[0]).Label, sub[1]))
                                facemesh = MeshPart.meshFromShape(
                                    elt,
                                    LinearDeflection=self.mesh_obj.
                                    STLLinearDeflection)

                                CfdTools.cfdMessage(" writing to file\n")
                                fid.write("solid {}{}{}\n".format(
                                    mr_obj.Name, sub[0], elem))
                                for face in facemesh.Facets:
                                    fid.write(" facet normal 0 0 0\n")
                                    fid.write("  outer loop\n")
                                    for i in range(3):
                                        p = [
                                            i * self.scale
                                            for i in face.Points[i]
                                        ]
                                        fid.write(
                                            "    vertex {} {} {}\n".format(
                                                p[0], p[1], p[2]))
                                    fid.write("  endloop\n")
                                    fid.write(" endfacet\n")
                                fid.write("endsolid {}{}{}\n".format(
                                    mr_obj.Name, sub[0], elem))

                            if self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle:
                                fid.close()

                        if not (self.mesh_obj.MeshUtility == 'snappyHexMesh'
                                and mr_obj.Baffle):
                            fid.close()

                        if self.mesh_obj.MeshUtility == 'cfMesh' and mr_obj.NumberLayers > 1 and not Internal:
                            for mf in bl_matched_faces:
                                if mr_id == mf[0]:
                                    sfN = self.mesh_obj.ShapeFaceNames[mf[1]]
                                    ele_meshpatch_map[mr_obj.Name].append(sfN)
                                    patch_list.append(sfN)

                                    # Limit expansion ratio to greater than 1.0 and less than 1.2
                                    expratio = mr_obj.ExpansionRatio
                                    expratio = min(1.2, max(1.0, expratio))

                                    cf_settings['BoundaryLayers'][
                                        self.mesh_obj.
                                        ShapeFaceNames[mf[1]]] = {
                                            'NumberLayers':
                                            mr_obj.NumberLayers,
                                            'ExpansionRatio':
                                            expratio,
                                            'FirstLayerHeight':
                                            self.scale * Units.Quantity(
                                                mr_obj.FirstLayerHeight).Value
                                        }

                        if self.mesh_obj.MeshUtility == 'cfMesh':
                            if not Internal:
                                cf_settings['MeshRegions'][mr_obj.Name] = {
                                    'RelativeLength':
                                    mr_rellen * self.clmax * self.scale,
                                    'RefinementThickness':
                                    self.scale * Units.Quantity(
                                        mr_obj.RefinementThickness).Value,
                                }
                            else:
                                cf_settings['InternalRegions'][mr_obj.Name] = {
                                    'RelativeLength':
                                    mr_rellen * self.clmax * self.scale
                                }

                        elif self.mesh_obj.MeshUtility == 'snappyHexMesh':
                            refinement_level = CfdTools.relLenToRefinementLevel(
                                mr_obj.RelativeLength)
                            if not Internal:
                                if not mr_obj.Baffle:
                                    snappy_mesh_region_list.append(mr_obj.Name)
                                edge_level = CfdTools.relLenToRefinementLevel(
                                    mr_obj.RegionEdgeRefinement)
                                for rL in range(len(snappy_mesh_region_list)):
                                    mrName = mr_obj.Name + snappy_mesh_region_list[
                                        rL]
                                    snappy_settings['MeshRegions'][mrName] = {
                                        'RegionName':
                                        snappy_mesh_region_list[rL],
                                        'RefinementLevel':
                                        refinement_level,
                                        'EdgeRefinementLevel':
                                        edge_level,
                                        'MaxRefinementLevel':
                                        max(refinement_level, edge_level),
                                        'Baffle':
                                        mr_obj.Baffle
                                    }
                            else:
                                snappy_settings['InternalRegions'][
                                    mr_obj.Name] = {
                                        'RefinementLevel': refinement_level
                                    }

                    else:
                        FreeCAD.Console.PrintError(
                            "The meshregion: " + mr_obj.Name +
                            " is not used to create the mesh because the "
                            "CharacteristicLength is 0.0 mm or the reference list is empty.\n"
                        )
Exemple #7
0
    def processRefinements(self):
        """ Process mesh refinements """

        mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj)

        if self.mesh_obj.MeshUtility == "gmsh":
            # mesh regions
            self.ele_length_map = {}  # { 'ElementString' : element length }
            self.ele_node_map = {}  # { 'ElementString' : [element nodes] }
            if not mr_objs:
                print('  No mesh refinements')
            else:
                print('  Mesh refinements found - getting elements')
                if self.part_obj.Shape.ShapeType == 'Compound':
                    # see http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 and http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520
                    err = "GMSH could return unexpected meshes for a boolean split tools Compound. It is strongly recommended to extract the shape to mesh from the Compound and use this one."
                    FreeCAD.Console.PrintError(err + "\n")
                for mr_obj in mr_objs:
                    if mr_obj.RelativeLength:
                        if mr_obj.References:
                            for sub in mr_obj.References:
                                # Check if the shape of the mesh region is an element of the Part to mesh;
                                # if not try to find the element in the shape to mesh
                                search_ele_in_shape_to_mesh = False
                                ref = FreeCAD.ActiveDocument.getObject(sub[0])
                                if not self.part_obj.Shape.isSame(ref.Shape):
                                    search_ele_in_shape_to_mesh = True
                                elems = sub[1]
                                if search_ele_in_shape_to_mesh:
                                    # Try to find the element in the Shape to mesh
                                    ele_shape = FemGeomTools.get_element(
                                        ref, elems
                                    )  # the method getElement(element) does not return Solid elements
                                    found_element = CfdTools.findElementInShape(
                                        self.part_obj.Shape, ele_shape)
                                    if found_element:
                                        elems = found_element
                                    else:
                                        FreeCAD.Console.PrintError(
                                            "One element of the meshregion " +
                                            mr_obj.Name +
                                            " could not be found in the Part to mesh. It will be ignored.\n"
                                        )
                                        elems = None
                                if elems:
                                    if elems not in self.ele_length_map:
                                        # self.ele_length_map[elems] = Units.Quantity(mr_obj.CharacteristicLength).Value
                                        mr_rellen = mr_obj.RelativeLength
                                        if mr_rellen > 1.0:
                                            mr_rellen = 1.0
                                            FreeCAD.Console.PrintError(
                                                "The meshregion: " +
                                                mr_obj.Name +
                                                " should not use a relative length greater than unity.\n"
                                            )
                                        elif mr_rellen < 0.01:
                                            mr_rellen = 0.01  # Relative length should not be less than 1/100 of base length
                                            FreeCAD.Console.PrintError(
                                                "The meshregion: " +
                                                mr_obj.Name +
                                                " should not use a relative length smaller than 0.01.\n"
                                            )
                                        self.ele_length_map[
                                            elems] = mr_rellen * self.clmax
                                    else:
                                        FreeCAD.Console.PrintError(
                                            "The element " + elems +
                                            " of the mesh refinement " +
                                            mr_obj.Name +
                                            " has been added to another mesh refinement.\n"
                                        )
                        else:
                            FreeCAD.Console.PrintError(
                                "The meshregion: " + mr_obj.Name +
                                " is not used to create the mesh because the reference list is empty.\n"
                            )
                    else:
                        FreeCAD.Console.PrintError(
                            "The meshregion: " + mr_obj.Name +
                            " is not used to create the mesh because the CharacteristicLength is 0.0 mm.\n"
                        )
                for eleml in self.ele_length_map:
                    ele_shape = FemGeomTools.get_element(
                        self.part_obj, eleml
                    )  # the method getElement(element) does not return Solid elements
                    ele_vertexes = FemGeomTools.get_vertexes_by_element(
                        self.part_obj.Shape, ele_shape)
                    self.ele_node_map[eleml] = ele_vertexes

        else:
            cf_settings = self.cf_settings
            cf_settings['MeshRegions'] = {}
            cf_settings['BoundaryLayers'] = {}
            cf_settings['InternalRegions'] = {}
            snappy_settings = self.snappy_settings
            snappy_settings['MeshRegions'] = {}
            snappy_settings['InternalRegions'] = {}

            # Make list of all faces in meshed shape with original index
            mesh_face_list = list(
                zip(self.mesh_obj.Part.Shape.Faces,
                    range(len(self.mesh_obj.Part.Shape.Faces))))

            # Make list of all boundary references
            CfdTools.cfdMessage("Matching boundary patches\n")
            bc_group = None
            analysis_obj = CfdTools.getParentAnalysisObject(self.mesh_obj)
            if not analysis_obj:
                analysis_obj = CfdTools.getActiveAnalysis()
            if analysis_obj:
                bc_group = CfdTools.getCfdBoundaryGroup(analysis_obj)
            boundary_face_list = []
            for bc_id, bc_obj in enumerate(bc_group):
                for ri, ref in enumerate(bc_obj.References):
                    try:
                        bf = CfdTools.resolveReference(ref)
                    except RuntimeError as re:
                        raise RuntimeError(
                            "Error processing boundary condition {}: {}".
                            format(bc_obj.Label, str(re)))
                    boundary_face_list.append((bf, (bc_id, ref, ri)))

            # Match them up to faces in the main geometry
            bc_matched_faces = CfdTools.matchFaces(boundary_face_list,
                                                   mesh_face_list)

            # Make list of all boundary layer mesh regions for cfMesh
            bl_matched_faces = []
            if self.mesh_obj.MeshUtility == 'cfMesh':
                CfdTools.cfdMessage("Matching boundary layer regions\n")

                bl_face_list = []
                for mr_id, mr_obj in enumerate(mr_objs):
                    if mr_obj.NumberLayers > 1 and not mr_obj.Internal:
                        for ri, r in enumerate(mr_obj.References):
                            try:
                                f = CfdTools.resolveReference(r)
                            except RuntimeError as re:
                                raise RuntimeError(
                                    "Error processing mesh refinement {}: {}".
                                    format(mr_obj.Label, str(re)))
                            bl_face_list.append((f, (mr_id, r, ri)))

                # Match them up
                bl_matched_faces = CfdTools.matchFaces(bl_face_list,
                                                       mesh_face_list)

            # Check for and filter duplicates
            bc_match_per_shape_face = [-1] * len(mesh_face_list)
            for k in range(len(bc_matched_faces)):
                match = bc_matched_faces[k][1]
                prev_k = bc_match_per_shape_face[match]
                if prev_k >= 0:
                    nb, bref, ri = bc_matched_faces[k][0]
                    nb2, bref2, ri2 = bc_matched_faces[prev_k][0]
                    CfdTools.cfdMessage(
                        "Boundary '{}' reference {}:{} also assigned as "
                        "boundary '{}' reference {}:{} - ignoring duplicate\n".
                        format(bc_group[nb].Label, bref[0], bref[1],
                               bc_group[nb2].Label, bref2[0], bref2[1]))
                else:
                    bc_match_per_shape_face[match] = k

            bl_match_per_shape_face = [-1] * len(mesh_face_list)
            for k in range(len(bl_matched_faces)):
                match = bl_matched_faces[k][1]
                prev_k = bl_match_per_shape_face[match]
                if prev_k >= 0:
                    nr, ref, ri = bl_matched_faces[k][0]
                    nr2, ref2, ri2 = bl_matched_faces[prev_k][0]
                    CfdTools.cfdMessage(
                        "Mesh refinement '{}' reference {}:{} also assigned as "
                        "mesh refinement '{}' reference {}:{} - ignoring duplicate\n"
                        .format(mr_objs[nr].Label, ref[0], ref[1],
                                mr_objs[nr2].Label, ref2[0], ref2[1]))
                else:
                    bl_match_per_shape_face[match] = k

            self.patch_faces = []
            self.patch_names = []
            for k in range(len(bc_group) + 1):
                self.patch_faces.append([])
                self.patch_names.append([])
                for l in range(len(mr_objs) + 1):
                    self.patch_faces[k].append([])
                    self.patch_names[k].append("patch_" + str(k) + "_" +
                                               str(l))
            for i in range(len(mesh_face_list)):
                k = bc_match_per_shape_face[i]
                l = bl_match_per_shape_face[i]
                nb = -1
                nr = -1
                if k >= 0:
                    nb, bref, bri = bc_matched_faces[k][0]
                if l >= 0:
                    nr, ref, rri = bl_matched_faces[l][0]
                self.patch_faces[nb + 1][nr + 1].append(i)

            # Additionally for snappy, match baffles to any surface mesh refinements
            # as well as matching each surface mesh refinement region to boundary conditions
            mr_face_list = []
            bc_mr_matched_faces = []
            if self.mesh_obj.MeshUtility == 'snappyHexMesh':
                CfdTools.cfdMessage("Matching surface geometries\n")

                for mr_id, mr_obj in enumerate(mr_objs):
                    if not mr_obj.Internal:
                        for ri, r in enumerate(mr_obj.References):
                            try:
                                f = CfdTools.resolveReference(r)
                            except RuntimeError as re:
                                raise RuntimeError(
                                    "Error processing mesh refinement {}: {}".
                                    format(mr_obj.Label, str(re)))
                            mr_face_list.append((f, (mr_id, r, ri)))

                # Match mesh regions to the boundary conditions, to identify boundary conditions on supplementary
                # geometry (including on baffles)
                bc_mr_matched_faces = CfdTools.matchFaces(
                    boundary_face_list, mr_face_list)

            for bc_id, bc_obj in enumerate(bc_group):
                if bc_obj.BoundaryType == 'baffle':
                    baffle_matches = [
                        m for m in bc_mr_matched_faces if m[0][0] == bc_id
                    ]
                    mr_match_per_baffle_ref = [-1] * len(bc_obj.References)
                    for m in baffle_matches:
                        mr_match_per_baffle_ref[m[0][2]] = m[1][0]
                    # For each mesh region, the refs that are part of this baffle
                    baffle_patch_refs = [[] for ri in range(len(mr_objs) + 1)]
                    for ri, mri in enumerate(mr_match_per_baffle_ref):
                        baffle_patch_refs[mri + 1].append(
                            bc_obj.References[ri])

                    # Write these geometries
                    for ri, refs in enumerate(baffle_patch_refs):
                        try:
                            shape = CfdTools.makeShapeFromReferences(refs)
                        except RuntimeError as re:
                            raise RuntimeError(
                                "Error processing baffle {}: {}".format(
                                    bc_obj.Label, str(re)))
                        solid_name = bc_obj.Name + "_" + str(ri)
                        if shape:
                            CfdTools.cfdMessage(
                                "Triangulating baffle {}, section {} ...".
                                format(bc_obj.Label, ri))
                            facemesh = MeshPart.meshFromShape(
                                shape,
                                LinearDeflection=self.mesh_obj.
                                STLLinearDeflection)

                            CfdTools.cfdMessage(" writing to file\n")
                            with open(
                                    os.path.join(self.triSurfaceDir,
                                                 solid_name + '.stl'),
                                    'w') as fid:
                                CfdTools.writePatchToStl(
                                    solid_name, facemesh, fid, self.scale)

                            if ri > 0:  # The parts of the baffle corresponding to a surface mesh region obj
                                mr_obj = mr_objs[ri - 1]
                                refinement_level = CfdTools.relLenToRefinementLevel(
                                    mr_obj.RelativeLength)
                                edge_level = CfdTools.relLenToRefinementLevel(
                                    mr_obj.RegionEdgeRefinement)
                            else:  # The parts of the baffle with no refinement obj
                                refinement_level = 0
                                edge_level = 0
                            snappy_settings['MeshRegions'][solid_name] = {
                                'RefinementLevel':
                                refinement_level,
                                'EdgeRefinementLevel':
                                edge_level,
                                'MaxRefinementLevel':
                                max(refinement_level, edge_level),
                                'Baffle':
                                True
                            }

            mr_matched_faces = []
            if self.mesh_obj.MeshUtility == 'snappyHexMesh':
                # Match mesh regions to the primary geometry
                mr_matched_faces = CfdTools.matchFaces(mr_face_list,
                                                       mesh_face_list)

            for mr_id, mr_obj in enumerate(mr_objs):
                Internal = mr_obj.Internal
                mr_rellen = mr_obj.RelativeLength
                if mr_rellen > 1.0:
                    mr_rellen = 1.0
                    FreeCAD.Console.PrintError(
                        "The mesh refinement region '{}' should not use a relative length greater "
                        "than unity.\n".format(mr_obj.Name))
                elif mr_rellen < 0.001:
                    mr_rellen = 0.001  # Relative length should not be less than 0.1% of base length
                    FreeCAD.Console.PrintError(
                        "The mesh refinement region '{}' should not use a relative length smaller "
                        "than 0.001.\n".format(mr_obj.Name))

                # Find any matches with boundary conditions; mark those matching baffles for removal
                bc_matches = [
                    m for m in bc_mr_matched_faces if m[1][0] == mr_id
                ]
                bc_match_per_mr_ref = [-1] * len(mr_obj.References)
                for m in bc_matches:
                    bc_match_per_mr_ref[m[1][2]] = -2 if bc_group[
                        m[0][0]].BoundaryType == 'baffle' else m[0][0]

                # Unmatch those in primary geometry
                main_geom_matches = [
                    m for m in mr_matched_faces if m[0][0] == mr_id
                ]
                for m in main_geom_matches:
                    bc_match_per_mr_ref[m[0][2]] = -1

                # For each boundary, the refs that are part of this mesh region
                mr_patch_refs = [[] for ri in range(len(bc_group) + 1)]
                for ri, bci in enumerate(bc_match_per_mr_ref):
                    if bci > -2:
                        mr_patch_refs[bci + 1].append(mr_obj.References[ri])

                # Loop over and write the sub-sections of this mesh object
                for bi in range(len(mr_patch_refs)):
                    if len(mr_patch_refs[bi]):
                        if bi == 0:
                            mr_patch_name = mr_obj.Name
                        else:
                            mr_patch_name = self.patch_names[bi][mr_id + 1]

                        CfdTools.cfdMessage(
                            "Triangulating mesh refinement region {}, section {} ..."
                            .format(mr_obj.Label, bi))

                        try:
                            shape = CfdTools.makeShapeFromReferences(
                                mr_patch_refs[bi])
                        except RuntimeError as re:
                            raise RuntimeError(
                                "Error processing mesh refinement region {}: {}"
                                .format(mr_obj.Label, str(re)))
                        if shape:
                            facemesh = MeshPart.meshFromShape(
                                shape,
                                LinearDeflection=self.mesh_obj.
                                STLLinearDeflection)

                            CfdTools.cfdMessage(" writing to file\n")
                            with open(
                                    os.path.join(self.triSurfaceDir,
                                                 mr_patch_name + '.stl'),
                                    'w') as fid:
                                CfdTools.writePatchToStl(
                                    mr_patch_name, facemesh, fid, self.scale)

                        if self.mesh_obj.MeshUtility == 'cfMesh':
                            if not Internal:
                                cf_settings['MeshRegions'][mr_patch_name] = {
                                    'RelativeLength':
                                    mr_rellen * self.clmax * self.scale,
                                    'RefinementThickness':
                                    self.scale * Units.Quantity(
                                        mr_obj.RefinementThickness).Value,
                                }
                            else:
                                cf_settings['InternalRegions'][mr_obj.Name] = {
                                    'RelativeLength':
                                    mr_rellen * self.clmax * self.scale
                                }

                        elif self.mesh_obj.MeshUtility == 'snappyHexMesh':
                            refinement_level = CfdTools.relLenToRefinementLevel(
                                mr_obj.RelativeLength)
                            if not Internal:
                                edge_level = CfdTools.relLenToRefinementLevel(
                                    mr_obj.RegionEdgeRefinement)
                                snappy_settings['MeshRegions'][
                                    mr_patch_name] = {
                                        'RefinementLevel':
                                        refinement_level,
                                        'EdgeRefinementLevel':
                                        edge_level,
                                        'MaxRefinementLevel':
                                        max(refinement_level, edge_level),
                                        'Baffle':
                                        False
                                    }
                            else:
                                snappy_settings['InternalRegions'][
                                    mr_patch_name] = {
                                        'RefinementLevel': refinement_level
                                    }

                # In addition, for cfMesh, record matched boundary layer patches
                if self.mesh_obj.MeshUtility == 'cfMesh' and mr_obj.NumberLayers > 1 and not Internal:
                    for k in range(len(self.patch_faces)):
                        # Limit expansion ratio to greater than 1.0 and less than 1.2
                        expratio = mr_obj.ExpansionRatio
                        expratio = min(1.2, max(1.0, expratio))

                        cf_settings['BoundaryLayers'][
                            self.patch_names[k][mr_id]] = {
                                'NumberLayers':
                                mr_obj.NumberLayers,
                                'ExpansionRatio':
                                expratio,
                                'FirstLayerHeight':
                                self.scale *
                                Units.Quantity(mr_obj.FirstLayerHeight).Value
                            }