Path: blob/devel/ElmerWorkflows/FreeCADBatchFEMTools/FreeCADBatchFEMTools.py
3196 views
"""1FreeCADBatchFEMTools - A library for using FreeCAD for FEM preprocessing in batch mode23Copyright 1st May 2018 - , Trafotek Oy, Finland45This library is free software; you can redistribute it and/or6modify it under the terms of the GNU Lesser General Public7License as published by the Free Software Foundation; either8version 2.1 of the License, or (at your option) any later version.910This library is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU13Lesser General Public License for more details.1415You should have received a copy of the GNU Lesser General Public16License along with this library (in file ../LGPL-2.1); if not, write17to the Free Software Foundation, Inc., 51 Franklin Street,18Fifth Floor, Boston, MA 02110-1301 USA1920Authors: Eelis Takala, Janne Keranen, Sami Rannikko21Emails: [email protected], [email protected]22Address: Trafotek Oy23Kaarinantie 7002420540 Turku25Finland2627Original Date: May 201828"""29from __future__ import print_function30import os31import Fem32import FreeCAD33import Part34import BOPTools.SplitFeatures35import ObjectsFem36import femmesh.gmshtools37import math38import itertools39import subprocess4041import meshutils424344def fit_view():45"""46If GUI is available, fit the view so that the geometry can be seen47"""48if FreeCAD.GuiUp:49import FreeCADGui50FreeCADGui.ActiveDocument.activeView().viewAxonometric()51FreeCADGui.SendMsgToActiveView("ViewFit")5253def isclose(a, b, rel_tol=1e-4, abs_tol=1e-4):54"""55Returns True if a and b are close to each other (within absolute or relative tolerance).5657:param a: float58:param b: float59:param rel_tol: float60:param abs_tol: float61:return: bool62"""63return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)6465def vectors_are_same(vec1,vec2,tol=1e-4):66"""67Compares vectors vec1 and vec2. If they are same within a tolerance returns68True if not returns false.6970:param vec1: Vector 171:param vec2: Vector 2 to be compared with Vector 172:return: Boolean73"""74vec3 = vec1.sub(vec2)75return isclose(vec3.Length, 0., abs_tol=tol)7677def faces_with_vertices_in_symmetry_plane(face_object_list, plane=None, abs_tol=1e-4):78"""79Returns faces from a list of FreeCAD face objects. The returned faces have to80be in a defined symmetry plane. The face is in symmetry plane if all of its points81and the center of mass are in the plane.8283:param face_object_list: list of FreeCAD face objects84:param plane: symmetry plane.8586:return: list of FreeCAD face objects that are in the given symmetry plane87"""88if plane is None: return None89face_object_list_out = []90for face_object in face_object_list:91vertices = face_object.Vertexes92center_of_mass = face_object.CenterOfMass93if plane in ['zx', 'xz']: center_compare_value = center_of_mass.y94elif plane in ['xy', 'yx']: center_compare_value = center_of_mass.z95elif plane in ['yz', 'zy']: center_compare_value = center_of_mass.x96else: raise ValueError("Wrong keyword for plane variable, should be: zx, xy, yz, xz, yx or zy!")97for i, vertex in enumerate(vertices):98if plane in ['zx', 'xz']: compare_value = vertex.Y99elif plane in ['xy', 'yx']: compare_value = vertex.Z100elif plane in ['yz', 'zy']: compare_value = vertex.X101else: raise ValueError("Wrong keyword for plane variable, should be: zx, xy, yz, xz, yx or zy!")102if not isclose(compare_value, 0., abs_tol=abs_tol): break103if i==len(vertices)-1 and isclose(center_compare_value, 0., abs_tol=abs_tol): face_object_list_out.append(face_object)104return face_object_list_out105106def reduce_half_symmetry(solid, name, App, doc, planes=None, reversed_direction = False):107doc.recompute()108if planes==None: return solid109plane = planes.pop()110doc.recompute()111reduced_name = name + '_' + plane112tool_box = doc.addObject("Part::Box","CutBox"+reduced_name)113x = 10. * solid.Shape.BoundBox.XLength114y = 10. * solid.Shape.BoundBox.YLength115z = 10. * solid.Shape.BoundBox.ZLength116if isinstance(solid, Part.Feature):117center=solid.Shape.Solids[0].CenterOfMass118else:119center=solid.Shape.CenterOfMass120121tool_box.Length = x122tool_box.Width = y123tool_box.Height = z124if plane == 'zx':125tool_box.Placement = App.Placement(App.Vector(center.x-x/2.,0,center.z-z/2.),App.Rotation(App.Vector(0,0,1),0))126elif plane == 'xy':127tool_box.Placement = App.Placement(App.Vector(center.x-x/2.,center.y-y/2.,0),App.Rotation(App.Vector(0,0,1),0))128elif plane == 'yz':129tool_box.Placement = App.Placement(App.Vector(0,center.y-y/2.,center.z-z/2.),App.Rotation(App.Vector(0,0,1),0))130else:131raise ValueError("Wrong keyword for plane variable, should be: zx, xy or yz!")132133if reversed_direction:134half_symmetry = doc.addObject("Part::MultiCommon",reduced_name)135half_symmetry.Shapes = [solid, tool_box]136else:137half_symmetry = doc.addObject("Part::Cut", reduced_name)138half_symmetry.Base = solid139half_symmetry.Tool = tool_box140141if len(planes) > 0:142return reduce_half_symmetry(half_symmetry, reduced_name, App, doc, planes, reversed_direction)143144return half_symmetry145146def faces_same_center_of_masses(face1, face2, tolerance=0.0001):147"""148Compare two faces by comparing if they have same centers of mass with the tolerance.149150:param face1: FreeCAD face object151:param face2: FreeCAD face object152:param tolerance: float153154"""155return vectors_are_same(face1.CenterOfMass, face2.CenterOfMass, tolerance)156157def faces_are_same(face1, face2, tolerance=1e-4):158"""159Return true if face1 is same as face2. The faces are same if they have160the same center of masses and same vertices.161162:param face1: FreeCAD face object163:param face2: FreeCAD face object164165:return: bool166"""167return faces_same_center_of_masses(face1, face2, tolerance) and faces_have_same_vertices(face1, face2, tolerance)168169def is_face_in_list(search_face, face_object_list, tolerance=1e-4):170"""171Returns true if search_face is in the face_object_list. Compares faces with172face_compare method.173174:param search_face: FreeCAD face object175:param face_object_list: list of FreeCAD face objects176"""177for face_object in face_object_list:178if faces_are_same(search_face, face_object): return True179return False180181def remove_compare_face_from_list(cface, face_object_list, tolerance=1e-4):182"""183Removes the first FreeCAD face object that matches in the FreeCAD face object list.184Uses face_compare to determine if the face is to be removed.185186:param cface: a FreeCAD face object to be compared187:param face_object_list: the list of FreeCAD face objects where the face188is to be removed in case of a match189190:return: list of FreeCAD face objects that are removed from the original list of191FreeCAD face objects.192"""193194for i, face_object in enumerate(face_object_list):195if faces_are_same(cface, face_object):196return face_object_list.pop(i)197return None198199def remove_compare_faces_from_list(compare_face_object_list, face_object_list):200"""201Removes all the face objects in compare_face_object_list that match to the face objects in202the face_object_list. Uses face_compare to determine if the face is to be removed.203204:param compare_face_object_list: list of FreeCAD face objects to be compared205:param face_object_list: original list of FreeCAD face objects206207:return: list of FreeCAD face objects that are removed from the original list of208FreeCAD face objects.209"""210removed = []211for face_object in compare_face_object_list:212removed.append(remove_compare_face_from_list(face_object, face_object_list))213return removed214215def faces_have_same_vertices(face1, face2, tolerance=0.0001):216"""217Compare two faces by comparing that they have same number of vertices and218the vertices are in identical coordinates with the tolerance. Return219truth value to the faces are the same in this regard.220221:param face1: FreeCAD face object222:param face2: FreeCAD face object223:return: bool224"""225face_vertices_found = []226for vertex in face2.Vertexes:227for cvertex in face1.Vertexes:228if vectors_are_same(vertex.Point,cvertex.Point, tolerance):229face_vertices_found.append(1)230return len(face_vertices_found) == len(face2.Vertexes) and len(face_vertices_found) == len(face1.Vertexes)231232def is_point_inside_face(face, vector, tolerance=0.0001):233"""234Returns True if point is inside face.235236WARNING: This function calls function face.isInside which does NOT respect tolerance237https://forum.freecadweb.org/viewtopic.php?t=31524238239:param face: FreeCAD face object240:param vector: Vector241:param tolerance: float242243:return: bool244"""245return face.isInside(vector, tolerance, True)246247def is_point_inside_solid(solid, vector, tolerance=0.0001, include_faces=True):248"""249Returns True if point is inside solid.250251:param solid: FreeCAD solid object252:param vector: Vector253:param tolerance: float254:param include_faces: bool255256:return: bool257"""258return solid.isInside(vector, tolerance, include_faces)259260def is_point_inside_solid_with_round(solid, vector, tolerance=0.0001, round_digits=6):261"""262Returns True if point is inside solid (faces included) with263additional tolerance (8 points checked).264Tries upper and lower rounding of coordinates with precision round_digits.265266:param solid: FreeCAD solid object267:param vector: Vector268:param tolerance: float269:param round_digits: integer270271:return: bool272"""273rounding = 10**round_digits274x_floor, x_ceil = math.floor(rounding*vector.x)/rounding, math.ceil(rounding*vector.x)/rounding275y_floor, y_ceil = math.floor(rounding*vector.y)/rounding, math.ceil(rounding*vector.y)/rounding276z_floor, z_ceil = math.floor(rounding*vector.z)/rounding, math.ceil(rounding*vector.z)/rounding277for coordinates in itertools.product([x_floor, x_ceil], [y_floor, y_ceil], [z_floor, z_ceil]):278vector.x = coordinates[0]279vector.y = coordinates[1]280vector.z = coordinates[2]281if is_point_inside_solid(solid, vector, tolerance):282return True283return False284285def is_same_vertices(vertex1, vertex2, tolerance=0.0001):286"""287Checks if given vertices are same.288289:param vertex1: FreeCAD vertex290:param vertex2: FreeCAD vertex291:param tolerance: float292293:return: bool294"""295if abs(vertex1.X - vertex2.X) < tolerance:296if abs(vertex1.Y - vertex2.Y) < tolerance:297if abs(vertex1.Z - vertex2.Z) < tolerance:298return True299return False300301def is_same_edge(edge1, edge2, tolerance=0.0001):302"""303Checks if given edges are in same place by comparing end points.304305:param edge1: FreeCAD edge306:param edge2: FreeCAD edge307:param tolerance: float308309:return: bool310"""311if is_same_vertices(edge1.Vertexes[0], edge2.Vertexes[0], tolerance):312if is_same_vertices(edge1.Vertexes[1], edge2.Vertexes[1], tolerance):313return True314elif is_same_vertices(edge1.Vertexes[0], edge2.Vertexes[1], tolerance):315if is_same_vertices(edge1.Vertexes[1], edge2.Vertexes[0], tolerance):316return True317return False318319def is_edge_in_solid(solid, edge, tolerance=0.0001):320"""321Returns True if edge inside solid by comparing is edge vertices inside solid.322323:param solid: FreeCAD solid object324:param edge: FreeCAD edge object325:param tolerance: float326327:return: bool328"""329for vertex in edge.Vertexes:330if not is_point_inside_solid(solid, vertex.Point, tolerance):331return False332return True333334def get_point_from_solid(solid, tolerance=0.0001):335"""336Returns point from given solid.337338:param solid: FreeCAD solid object339:param tolerance: float340341:return: None or FreeCAD vector object342"""343x_min, y_min, z_min = solid.BoundBox.XMin, solid.BoundBox.YMin, solid.BoundBox.ZMin344x_len, y_len, z_len = solid.BoundBox.XLength, solid.BoundBox.YLength, solid.BoundBox.ZLength345for split_count in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,346101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,347197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,348311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421,349431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541]:350x_split_len, y_split_len, z_split_len = x_len/split_count, y_len/split_count, z_len/split_count351for i in range(1, split_count):352x_test = x_min + i*x_split_len353for j in range(1, split_count):354y_test = y_min + j*y_split_len355for k in range(1, split_count):356test_point = FreeCAD.Vector(x_test, y_test, z_min + k*z_split_len)357if is_point_inside_solid(solid, test_point, tolerance, include_faces=False):358return test_point359return None360361def is_point_on_face_edges(face, p2, tol=0.0001):362"""363Checks if given point is on same edge of given face.364365:param face: FreeCAD face object366:param p2: FreeCAD Vector object367:param tol: float368369:return: bool370"""371vertex = Part.Vertex(p2)372for edge in face.Edges:373if vertex.distToShape(edge)[0] < tol:374return True375return False376377def get_point_from_face_close_to_edge(face):378"""379Increases parameter range minimum values of face by one until at least380two of the x, y and z coordinates of the corresponding point has moved at least 1 unit.381If point is not found None is returned.382383:param face: FreeCAD face object.384385:return: None or FreeCAD vector object.386"""387u_min, u_max, v_min, v_max = face.ParameterRange388p1 = face.valueAt(u_min, v_min)389u_test, v_test = u_min+1, v_min+1390while u_test < u_max and v_test < v_max:391p2 = face.valueAt(u_test, v_test)392# Check at least two coordinates moved 1 unit393if (abs(p1.x - p2.x) >= 1) + (abs(p1.y - p2.y) >= 1) + (abs(p1.z - p2.z) >= 1) > 1:394if is_point_on_face_edges(face, p2):395v_test += 0.5396continue # go back at the beginning of while397if face.isPartOfDomain(u_test, v_test):398return p2399return None400u_test, v_test = u_test+1, v_test+1401return None402403def get_point_from_face(face):404"""405Returns point from given face.406407:param face: FreeCAD face object.408409:return: None or FreeCAD vector object410"""411point = get_point_from_face_close_to_edge(face)412if point is not None:413return point414u_min, u_max, v_min, v_max = face.ParameterRange415u_len, v_len = u_max-u_min, v_max-v_min416# use primes so same points are not checked multiple times417for split_count in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,418101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,419197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,420311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421,421431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541]:422u_split_len, v_split_len = u_len/float(split_count), v_len/float(split_count)423for i in range(1, split_count):424u_test = u_min + i*u_split_len425for j in range(1, split_count):426v_test = v_min + j*v_split_len427if face.isPartOfDomain(u_test, v_test):428return face.valueAt(u_test, v_test)429return None430431def is_face_in_face(face1, face2, tolerance=0.0001):432"""433Returns True if all vertices and point in face1 also belongs to face2434435:param face1: FreeCAD face object436:param face2: FreeCAD face object437:param tolerance: float438439:return: bool440"""441for vertex in face1.Vertexes:442if not is_point_inside_face(face2, vertex.Point, tolerance):443return False444point_in_face1 = get_point_from_face(face1)445if point_in_face1 is not None:446if not is_point_inside_face(face2, point_in_face1, tolerance):447return False448else:449raise ValueError('Face point not found')450return True451452def is_face_in_solid(solid, face, tolerance=0.0001, use_round=True):453"""454Checks if all face vertices and one additional point from face are inside solid.455If use_round is True calls function meth:`is_point_inside_solid_with_round` to check456if point inside solid.457458:param solid: FreeCAD solid object459:param face: FreeCAD face object460:param tolerance: float461:param use_round: bool462463:return: bool464"""465if use_round:466is_point_in_solid_func = is_point_inside_solid_with_round467else:468is_point_in_solid_func = is_point_inside_solid469for vertex in face.Vertexes:470if not is_point_in_solid_func(solid, vertex.Point, tolerance):471return False472point_in_face = get_point_from_face(face)473if point_in_face is not None:474if not is_point_in_solid_func(solid, point_in_face, tolerance):475return False476else:477raise ValueError('Face point not found')478return True479480def is_compound_filter_solid_in_solid(compound_filter_solid, solid, tolerance=0.0001, point_search=True):481"""482If point_search is True:483returns True if all faces of compound_filter_solid are inside solid484else:485returns compound_filter_solid.common(solid).Volume > 0486487:param compound_filter_solid: FreeCAD solid object488:param solid: FreeCAD solid object489:param tolerance: float (only used with point_search)490:param point_search: bool491492:return: bool493"""494if point_search:495point_in_solid = get_point_from_solid(compound_filter_solid, tolerance)496if point_in_solid is None:497raise ValueError('Solid point not found')498return is_point_inside_solid(solid, point_in_solid, tolerance)499return compound_filter_solid.common(solid).Volume > 0500501def solids_are_the_same(solid1, solid2):502"""503Compare two solids by comparing have they same number of faces and the faces are identical.504Return truth value to the solids are the same in this regard.505506:param solid1: FreeCAD solid object507:param solid2: FreeCAD solid object508:return: bool509"""510solid_faces_found = []511for face in solid2.Faces:512for cface in solid1.Faces:513if faces_same_center_of_masses(cface, face) and faces_have_same_vertices(cface, face):514solid_faces_found.append(1)515return len(solid_faces_found) == len(solid2.Faces) and len(solid_faces_found) == len(solid1.Faces)516517def create_boolean_compound(solid_objects, doc):518"""519Creates a FreeCAD boolean compound for the list of FreeCAD solid objects.520This is needed when mesh is computed for the whole geometry. Note that521there is also a create_mesh_object_and_compound_filter for meshing purpose.522523:param solid_objects: list of FreeCAD solid geometry objects.524:param doc: FreeCAD document.525:return: FreeCAD compound object.526"""527doc.recompute()528comp_obj = BOPTools.SplitFeatures.makeBooleanFragments(name='Compsolid')529comp_obj.Objects = solid_objects530comp_obj.Mode = "CompSolid"531comp_obj.Proxy.execute(comp_obj)532comp_obj.purgeTouched()533return comp_obj534535def create_compound(solid_objects, doc, name='Compsolid'):536"""537Creates a FreeCAD compound for the list of FreeCAD solid objects.538539:param solid_objects: list of FreeCAD solid geometry objects.540:param doc: FreeCAD document.541:param name: String.542:return: FreeCAD compound object.543"""544compound = doc.addObject('Part::Compound', name)545compound.Links = solid_objects546doc.recompute()547return compound548549def create_xor_object(solid_objects, doc):550"""551Creates a FreeCAD xor object for the list of FreeCAD solid objects.552553:param solid_objects: list of FreeCAD solid geometry objects.554:param doc: FreeCAD document.555:return: FreeCAD xor object556"""557doc.recompute()558xor_object = BOPTools.SplitFeatures.makeXOR(name='XOR')559xor_object.Objects = solid_objects560xor_object.Proxy.execute(xor_object)561xor_object.purgeTouched()562return xor_object563564def create_compound_filter(compsolid):565"""566Create a compound filter. This is needed for meshing. Note that567there is also a create_mesh_object_and_compound_filter for meshing purpose.568569:param compsolid: FreeCAD compound object for example from create_boolean_compound570:return: FreeCAD compound filter object571"""572import CompoundTools.CompoundFilter573compound_filter = CompoundTools.CompoundFilter.makeCompoundFilter(name='CompoundFilter')574compound_filter.Base = compsolid575compound_filter.FilterType = 'window-volume' #???576compound_filter.Proxy.execute(compound_filter) #???577return compound_filter578579def create_mesh_object(compound_filter, CharacteristicLength, doc, algorithm2d='Automatic', algorithm3d='New Delaunay'):580"""581Creates a mesh object that controls the mesh definitions.582583:param compound_filter: FreeCAD compound filter object584:param CharacteristicLength: Default mesh size characteristic length585:param doc: FreeCAD document.586:param algorithm2d: String 'MeshAdapt', 'Automatic', 'Delaunay', 'Frontal', 'BAMG', 'DelQuad'.587:param algorithm3d: String 'Delaunay', 'New Delaunay', 'Frontal', 'Frontal Delaunay', 'Frontal Hex',588'MMG3D', 'R-tree'.589:return: FreeCAD mesh object590"""591# Make a FEM mesh and mesh groups for material bodies and boundary conditions592mesh_object = ObjectsFem.makeMeshGmsh(doc, 'GMSHMeshObject')593mesh_object.Part = compound_filter594mesh_object.CharacteristicLengthMax = CharacteristicLength595mesh_object.Algorithm2D = algorithm2d596mesh_object.Algorithm3D = algorithm3d597mesh_object.ElementOrder = u"1st"598return mesh_object599600def set_mesh_group_elements(gmsh_mesh):601"""602Updates group_elements dictionary in gmsh_mesh.603Rewritten gmsh_mesh.get_group_data without finding mesh group elements again604605:param gmsh_mesh: Instance of gmshtools.GmshTools.606"""607for mg in gmsh_mesh.mesh_obj.MeshGroupList:608gmsh_mesh.group_elements[mg.Label] = list(mg.References[0][1]) # tuple to list609if gmsh_mesh.group_elements:610FreeCAD.Console.PrintMessage(' {}\n'.format(gmsh_mesh.group_elements))611612def _remove_ansi_color_escape_codes(message):613"""614Replace color code escape codes from message with empty string.615616:param message: A string.617618:return: A string.619"""620return message.replace('\x1b[1m', '').replace('\x1b[31m', '').replace('\x1b[35m', '').replace('\x1b[0m', '')621622def run_gmsh(gmsh_mesh, gmsh_log_file=None):623"""624Runs gmsh. Writes gmsh output to gmsh_log_file if given.625626:param gmsh_mesh: Instance of gmshtools.GmshTools.627:param gmsh_log_file: None or path to gmsh_log.628629:return: Gmsh stderr, None or error string.630"""631if gmsh_log_file is None:632error = gmsh_mesh.run_gmsh_with_geo()633else:634try:635with open(gmsh_log_file, 'w') as f:636p = subprocess.Popen([gmsh_mesh.gmsh_bin, '-', gmsh_mesh.temp_file_geo],637stdout=f, stderr=subprocess.PIPE, universal_newlines=True)638no_value, error = p.communicate()639if error:640error = _remove_ansi_color_escape_codes(error)641f.write(error)642f.flush()643except Exception:644error = 'Error executing gmsh'645return error646647def create_mesh(mesh_object, directory=False, gmsh_log_file=None, transfinite_param_list=None):648"""649Create mesh mesh with Gmsh.650Value of directory determines location gmsh temporary files::651652- False: Use current working directory653- None: Let GmshTools decide (temp directory)654- something else: try to use given value655656:param mesh_object: FreeCAD mesh object657:param directory: Gmsh temp file location.658:param gmsh_log_file: None or path to gmsh_log.659:param transfinite_param_list: None or a list containing dictionaries {'volume': 'name',660'surface_list': [s_name, sname2]}.661662:return: None or gmsh error text.663"""664if directory is False:665directory = os.getcwd()666gmsh_mesh = femmesh.gmshtools.GmshTools(mesh_object)667# error = gmsh_mesh.create_mesh()668# update mesh data669gmsh_mesh.start_logs()670gmsh_mesh.get_dimension()671set_mesh_group_elements(gmsh_mesh) # gmsh_mesh.get_group_data672gmsh_mesh.get_region_data()673gmsh_mesh.get_boundary_layer_data()674# create mesh675gmsh_mesh.get_tmp_file_paths(param_working_dir=directory)676gmsh_mesh.get_gmsh_command()677gmsh_mesh.write_gmsh_input_files()678if transfinite_param_list:679meshutils.add_transfinite_lines_to_geo_file(directory, transfinite_param_list)680error = run_gmsh(gmsh_mesh, gmsh_log_file)681if error:682FreeCAD.Console.PrintError('{}\n'.format(error))683return error684gmsh_mesh.read_and_set_new_mesh()685686def create_mesh_object_and_compound_filter(solid_objects, CharacteristicLength, doc, separate_boundaries=False,687algorithm2d='Automatic', algorithm3d='New Delaunay'):688"""689Creates FreeCAD mesh and compound filter objects. Uses create_boolean_compound/create_compound and690create_compound_filter, create_mesh_object methods.691692:param solid_objects: list of FreeCAD solid geometry objects693:param CharacteristicLength: Default mesh size characteristic length694:param doc: FreeCAD document.695:param separate_boundaries: Boolean (create compound instead of boolean fragment).696:param algorithm2d: String 'MeshAdapt', 'Automatic', 'Delaunay', 'Frontal', 'BAMG', 'DelQuad'.697:param algorithm3d: String 'Delaunay', 'New Delaunay', 'Frontal', 'Frontal Delaunay', 'Frontal Hex',698'MMG3D', 'R-tree'.699"""700if len(solid_objects) == 1 or separate_boundaries: # boolean compound can not be created with only one solid701boolean_compound = create_compound(solid_objects, doc)702else:703boolean_compound = create_boolean_compound(solid_objects, doc)704compound_filter = create_compound_filter(boolean_compound)705mesh_object = create_mesh_object(compound_filter, CharacteristicLength, doc, algorithm2d, algorithm3d)706return mesh_object, compound_filter707708def run_elmergrid(export_path, mesh_object, out_dir=None, log_file=None):709"""710Run ElmerGrid as an external process if it found in the operating system.711712:param export_path: path where the result is written713:param mesh_object: FreeCAD mesh object that is to be exported714:param out_dir: directory where to write mesh files (if not given unv file name is used)715:param log_file: None or a string.716"""717# Export to UNV file for Elmer718export_objects = [mesh_object]719Fem.export(export_objects, export_path)720elmerGrid_command = 'ElmerGrid 8 2 ' + export_path + ' -autoclean -names'721if out_dir is not None:722elmerGrid_command += ' -out ' + out_dir723724FreeCAD.Console.PrintMessage('Running ' + elmerGrid_command + '\n')725if log_file is not None:726with open(log_file, 'w') as f:727p = subprocess.Popen(elmerGrid_command.split(), stdout=f, stderr=subprocess.STDOUT)728p.communicate()729else:730from PySide import QtCore, QtGui731try:732process = QtCore.QProcess()733process.startDetached(elmerGrid_command)734except:735FreeCAD.Console.PrintError('Error')736QtGui.QMessageBox.critical(None, 'Error', 'Error!!', QtGui.QMessageBox.Abort)737FreeCAD.Console.PrintMessage('Finished ElmerGrid\n')738739def export_unv(export_path, mesh_object):740"""741Exports UNV file for Elmer.742743:param export_path: string744:param mesh_object: Mesh object745"""746Fem.export([mesh_object], export_path)747748def find_compound_filter_point(compound_filter, point):749"""750Finds which point in the compound filter object is given point.751Returns the name of the point in compound filter.752753:param compound_filter: FreeCAD compound filter.754:param point: FreeCAD vector.755756:return: A string.757"""758for num, c_vertex in enumerate(compound_filter.Shape.Vertexes):759if vectors_are_same(c_vertex.Point, point):760return str(num+1)761raise ValueError('Point not found')762763def find_compound_filter_edge(compound_filter, edge):764"""765Find which edge in the compound filter object is the edge as the one given in second argument.766Returns the name of the edge in compound filter.767768:param compound_filter: FreeCAD compound filter.769:param edge: FreeCAD edge.770771:return: A string.772"""773for num, c_edge in enumerate(compound_filter.Shape.Edges):774if is_same_edge(c_edge, edge):775return str(num+1)776raise ValueError('Edge not found')777778def find_compound_filter_boundary(compound_filter, face):779"""780Find which face in the compound filter object is the face as the one given in second argument.781Returns the name of the face in compound filter.782783:param compound_filter: FreeCAD compound filter784:param face: FreeCAD face object785:return: string786"""787faces = compound_filter.Shape.Faces788face_found = None789for num, cface in enumerate(faces):790if faces_have_same_vertices(cface, face):791face_found = num792if face_found is None: return None793string = "Face" + str(face_found+1)794return string795796def find_compound_filter_solid(compound_filter, solid):797"""798Find which solid in the compound filter object is the solid as the one given in second argument.799Returns the name of the solid in compound filter.800801:param compound_filter: FreeCAD compound filter802:param solid: FreeCAD solid object803:return: string804"""805solids = compound_filter.Shape.Solids806solid_found = None807for num, csolid in enumerate(solids):808if solids_are_the_same(csolid, solid):809solid_found = num810if solid_found is None: return None811string = "Solid" + str(solid_found+1)812return string813814def find_compound_filter_boundaries(compound_filter, face, used_compound_face_names=None):815"""816Finds all faces in the compound filter object which are inside given face.817Returns a tuple containing all names of the faces in compound filter.818If list used_compound_face_names is given checks that found face is not already used and819face is not already found here (relates to argument 'separate_boundaries' in other functions).820821:param compound_filter: FreeCAD compound filter822:param face: FreeCAD face object823:param used_compound_face_names: None or a list.824:return: tuple825"""826face_name_list, already_found_cfaces = [], []827for num, cface in enumerate(compound_filter.Shape.Faces):828if is_face_in_face(cface, face):829f_name = "Face" + str(num+1)830if used_compound_face_names is not None:831if f_name in used_compound_face_names:832continue833face_already_found = False834for found_cface in already_found_cfaces:835if is_face_in_face(cface, found_cface):836face_already_found = True837break838if face_already_found:839continue840already_found_cfaces.append(cface)841face_name_list.append(f_name)842if len(face_name_list) == 0:843raise ValueError("Faces not found")844return tuple(face_name_list)845846def find_compound_filter_solids(compound_filter, solid, point_search=True):847"""848Finds all solids in the compound filter object which are inside given solid.849Returns a tuple containing all names of the solids in compound filter.850851:param compound_filter: FreeCAD compound filter852:param solid: FreeCAD solid object853:param point_search: bool854:return: tuple855"""856solid_name_list = []857for num, csolid in enumerate(compound_filter.Shape.Solids):858if is_compound_filter_solid_in_solid(csolid, solid, point_search=point_search):859solid_name_list.append("Solid" + str(num+1))860if len(solid_name_list) == 0:861raise ValueError("Solids not found")862return tuple(solid_name_list)863864"""865There is no topological naming in FreeCAD. Further, the face numbers are changed in866making boolean compound. Hence, then making the original solids, the important solids867and faces are named with generating the following entities dictionary:868869Entities dict definition870example_entities = {871'name' : 'This is the name of the example',872'faces' : [873{'name':'face1',874'geometric object':face1_geom_object,875'mesh size':mesh_size},876...,877{'name':'facen',878'geometric object':facen_geom_object,879'mesh size':mesh_size}880]881'solids' : [882{'name':'solid_1',883'geometric object':solid1_geom_object,884'mesh size':mesh_size_1},885...,886{'name':'solid_n',887'geometric object':solidn_geom_object,888'mesh size':mesh_size_n}889]890'main object': main_geom_object891}892In 'faces' and 'solids' have lists that are so called entity lists.893Entity is defined as a dict containing the name, geometric object and894the mesh size. In principle one could dynamically add more keys in this895entity property dict:896897{'name':'solid_n',898'geometric object':solidn_geom_object,899'mesh size':mesh_size_n}900901main_geom_object is for storing a main geometry. For example usually902when a single solid is created, it contains many face and one solid.903it is handy to store the solid under the 'main object' key.904"""905def add_entity_in_list(entity_list, name, geom_object, mesh_sizes=None):906"""907Add entity in list of entities. The mesh sizes can be defined by908providing the following dictionary:909910mesh_sizes={911entity_name_1:mesh_size_for_entity_name_1,912...913entity_name_n:mesh_size_for_entity_name_n,914mesh size:mesh size if name is not in the dict915}916917:param entity_list: [entity_1, ..., entity_n]918:param name: string (name of the entity to be added)919:param geom_object: geometric object of the entity920:param mesh_sizes: dict921"""922if mesh_sizes is not None:923if name in mesh_sizes: mesh_size = mesh_sizes[name]924elif 'mesh size' in mesh_sizes: mesh_size = mesh_sizes['mesh size']925else: mesh_size = None926else:927mesh_size = None928entity_list.append({'name': name,929'geometric object': geom_object,930'mesh size': mesh_size})931932def add_geom_obj_list_in_entitylist(entity_list, name, geom_obj_list, mesh_sizes=None):933"""934Adds a list of geometry objects in entitylist using add_entity_in_list(entity_list, name, geom_object, mesh_sizes=None)935"""936for geom_object in geom_obj_list:937add_entity_in_list(entity_list, name, geom_object, mesh_sizes)938939def add_symmetry_plane_faces_in_entity_list(entity_list, geom_object, plane, mesh_sizes=None):940"""941Adds symmetry plane faces using add_geom_obj_list_in_entitylist(entity_list, name, geom_obj_list, mesh_sizes=None)942"""943faces_in_symmetry_plane = faces_with_vertices_in_symmetry_plane(geom_object.Shape.Faces, plane)944add_geom_obj_list_in_entitylist(entity_list, plane, faces_in_symmetry_plane)945946def get_entitylist_faces(entity_list):947"""948Collects geometric objects from dictionaries in entity_list.949950:param entity_list: list951952:return: list953"""954faces = []955for entity in entity_list:956faces.append(entity['geometric object'])957return faces958959def create_entities_dict(name, face_entity_list, solid_entity_list, main_object=None, params=None):960"""961Helper method for creating an entities dictionary.962963:param name: name of the collection of entities964:param face_entity_list: [face_entity_1, ..., face_entity_n]965:param solid_entity_list: [solid_entity_1, ..., solid_entity_n]966:param main_object: main object (usually a solid when the entity only has one)967:param params: None or a dictionary added to entities_dict:968969:return: entities_dict970"""971entities_dict = {972'name': name,973'faces': face_entity_list,974'solids': solid_entity_list,975'main object': main_object976}977if params:978entities_dict.update(params)979return entities_dict980981def pick_faces_from_geometry(geom_object, face_picks, mesh_sizes=None):982"""983Helper function for picking faces from a geometry object.984The mesh sizes can be defined by providing the following dictionary::985986mesh_sizes={987entity_name_1:mesh_size_for_entity_name_1,988...989entity_name_n:mesh_size_for_entity_name_n,990mesh size:mesh size if name is not in the dict991}992993:param geom_object: FreeCAD geometric object where the faces are picked994:param face_picks: tuple('name of the face', int(face_number))995:param mesh_sizes: dict996997:return: list998"""999faces = []1000face_objects = geom_object.Shape.Faces1001for face_pick in face_picks:1002face_name = face_pick[0]1003face_number = face_pick[1]1004add_entity_in_list(faces, face_name, face_objects[face_number], mesh_sizes)1005return faces10061007def create_transfinite_mesh_param_dict(volume_name, surface_name_list, direction_dict=None, line_params=None,1008volume_corner_vectors=None):1009"""1010Creates transfinite mesh parameter dictionary e.g.::10111012{'transfinite_mesh_params': {'volume': 'A1',1013'surface_list': ['A1_alpha0', 'A1_alpha1']}1014}10151016:param volume_name: List containing volume names.1017:param surface_name_list: List containing surface names.1018:param direction_dict: None or a dictionary e.g. {'A1_alpha0_direction': 'Left'} (added to geo file).1019:param line_params: None or a list containing dictionaries (see function create_transfinite_line_param_dict).1020:param volume_corner_vectors: None or a list containing volume corner points (for transfinite volume definition)10211022:return: Dictionary.1023"""1024mesh_params = {'volume': volume_name,1025'surface_list': surface_name_list}1026if direction_dict:1027mesh_params.update(direction_dict)1028if line_params:1029mesh_params['line_params'] = line_params1030if volume_corner_vectors:1031mesh_params['volume_corner_vectors'] = volume_corner_vectors1032return {'transfinite_mesh_params': mesh_params}10331034def create_transfinite_line_param_dict(edge_list, nof_points, progression=1, comment='', mesh_type='Progression'):1035"""1036Creates dictionary containing transfinite line parameters.10371038:param edge_list: List containing FreeCAD edge objects.1039:param nof_points: Integer.1040:param progression: Number (mesh_type coefficient).1041:param comment: String (commented in geo file).1042:param mesh_type: String.10431044:return: Dictionary.1045"""1046return {'edges': edge_list, 'points': str(nof_points), 'progression': str(progression),1047'mesh_type': mesh_type, 'comment': comment}10481049def merge_entities_dicts(entities_dicts, name, default_mesh_size=None, add_prefixes=None):1050"""1051This method merges all the entities_dicts and optionally prefixes the entity names with the1052name of the entity. As default the solids are not prefixed but the faces are.10531054:param entities_dicts: [entities_dict_1, ..., entities_dict_n]1055:param name: string1056:param default_mesh_size: float1057:param add_prefixes: {'solids':bool, 'faces':bool}1058"""1059if add_prefixes is None:1060add_prefixes = {'solids': False, 'faces': True}1061entities_out = {'name': name}1062faces = []1063solids = []1064transfinite_mesh_params = []1065for d in entities_dicts:1066for face in d['faces']:1067if face['mesh size'] is None: face['mesh size'] = default_mesh_size1068if add_prefixes['faces']: face_name = d['name'] + '_' + face['name']1069else: face_name = face['name']1070add_entity_in_list(faces, face_name, face['geometric object'], {'mesh size':face['mesh size']})1071for solid in d['solids']:1072if add_prefixes['solids']: solid_name = d['name'] + '_' + solid['name']1073else: solid_name = solid['name']1074if solid['mesh size'] is None: solid['mesh size'] = default_mesh_size1075add_entity_in_list(solids, solid_name, solid['geometric object'], {'mesh size':solid['mesh size']})1076if d.get('transfinite_mesh_params', {}):1077transfinite_mesh_params.append(d['transfinite_mesh_params'])1078entities_out['faces'] = faces1079entities_out['solids'] = solids1080entities_out['transfinite_mesh_params'] = transfinite_mesh_params1081return entities_out10821083def get_solids_from_entities_dict(entities_dict):1084"""1085Return a list of solids from entities dictionary.10861087:param entities_dict: entities dictionary1088:return: [solid object 1, ..., solid object n]1089"""1090solid_objects = [solid_dict_list['geometric object'] for solid_dict_list in entities_dict['solids']]1091return solid_objects10921093def create_mesh_group_and_set_mesh_size(mesh_object, doc, name, mesh_size):1094"""1095Creates mesh group with function ObjectsFem.makeMeshGroup.1096Adds property 'mesh_size' to created group and returns object.10971098:param mesh_object: FreeCAD mesh object1099:param doc: FreeCAD document.1100:param name: string1101:param mesh_size: float1102:return: MeshGroup object1103"""1104# The third argument of makeMeshGroup is True, as we want to use labels,1105# not the names which cannot be changed.1106#1107# WARNING: No other object should have same label than this Mesh Group,1108# otherwise FreeCAD adds numbers to the end of the label to make it unique1109obj = ObjectsFem.makeMeshGroup(doc, mesh_object, True, name+'_group')1110obj.Label = name1111obj.addProperty('App::PropertyFloat', 'mesh_size')1112obj.mesh_size = mesh_size1113return obj11141115def find_lines_to_transfinite_mesh_params(compound_filter, entities_dict):1116"""1117Find edge names from compound_filter and adds them to transfinite mesh parameters.11181119Example of list entities_dict['transfinite_mesh_params'] after this function::11201121[{'volume': 'A1',1122'surface_list': ['A1_alpha0', 'A1_alpha1']1123'line_params': [{'edges': [edgeObject1, edgeObject2],1124'lines': ['1', '2'], # this function adds these1125'points': '11',1126'progression': '1',1127'comment': ''}]1128}]11291130:param compound_filter: FreeCAD compound filter.1131:param entities_dict: A dictionary containing transfinite_mesh_params.1132"""1133for mesh_param_dict in entities_dict['transfinite_mesh_params']:1134for line_param_dict in mesh_param_dict.get('line_params', []):1135line_ids = []1136for edge in line_param_dict['edges']:1137line_ids.append(find_compound_filter_edge(compound_filter, edge))1138line_param_dict['lines'] = line_ids1139if mesh_param_dict.get('volume_corner_vectors', []):1140volume_corner_points = []1141# if used corner points needs already be in correct order1142for corner_vector in mesh_param_dict['volume_corner_vectors']:1143volume_corner_points.append(find_compound_filter_point(compound_filter, corner_vector))1144mesh_param_dict['volume_corner_points'] = volume_corner_points11451146def merge_boundaries(mesh_object, compound_filter, doc, face_entity_dict, compound_face_names, face_name_list,1147surface_objs, surface_objs_by_compound_face_names, surface_object=None):1148"""1149If face in compound_faces is already added to surface:1150- renames surface if there was only one face in existing1151- removes face from existing surface and creates a new surface for merged face1152Creates new surface object (MeshGroup) for compound_faces if needed and surface_object is not given.11531154:param mesh_object: FreeCAD mesh object1155:param compound_filter: FreeCAD compound filter1156:param doc: FreeCAD document.1157:param face_entity_dict: dictionary1158:param compound_face_names: tuple containing compound face names in face1159:param face_name_list: list containing already handled face names1160:param surface_objs: list containing created surface objects same order as in face_name_list1161:param surface_objs_by_compound_face_names: dictionary (for checking if face needs to be merged)1162:param surface_object: None or already created surface object1163:return: tuple containing surface object and tuple containing filtered compound names1164"""1165filtered_compound_faces = []1166for cface_name in compound_face_names:1167if cface_name in surface_objs_by_compound_face_names:1168surf_obj = surface_objs_by_compound_face_names[cface_name]1169old_face_name = surf_obj.Label1170new_face_name = '{}_{}'.format(old_face_name, face_entity_dict['name'])11711172old_found_cface_names = surf_obj.References[0][1]1173filtered_old_found_cface_names = [cfname_i for cfname_i in old_found_cface_names if cfname_i != cface_name]1174if len(filtered_old_found_cface_names) == 0:1175# existing mesh object with new label1176surf_obj.Label = new_face_name1177# update face name in face_name_list1178index_found = face_name_list.index(old_face_name)1179face_name_list[index_found] = new_face_name1180else:1181# update references for existing mesh group1182surf_obj.References = [(compound_filter, tuple(filtered_old_found_cface_names))]1183# handle merged boundary1184if new_face_name in face_name_list:1185# add merged boundary to existing mesh group1186surface_index = face_name_list.index(new_face_name)1187found_cface_names = surface_objs[surface_index].References[0][1]1188surface_objs[surface_index].References = [(compound_filter, found_cface_names+tuple([cface_name]))]1189else:1190# create new mesh group for merged boundary1191surface_objs.append(create_mesh_group_and_set_mesh_size(mesh_object, doc, new_face_name,1192face_entity_dict['mesh size']))1193surface_objs[-1].References = [(compound_filter, tuple([cface_name]))]1194face_name_list.append(new_face_name)1195surface_objs_by_compound_face_names[cface_name] = surface_objs[-1]1196else:1197filtered_compound_faces.append(cface_name)1198# create new mesh group only once if needed1199if surface_object is None:1200surface_object = create_mesh_group_and_set_mesh_size(mesh_object, doc, face_entity_dict['name'],1201face_entity_dict['mesh size'])1202surface_objs_by_compound_face_names[cface_name] = surface_object12031204return surface_object, tuple(filtered_compound_faces)12051206def find_boundaries_with_entities_dict(mesh_object, compound_filter, entities_dict, doc, separate_boundaries=False):1207"""1208For all faces in entities_dict, the same face in compound filter is added to a Mesh Group.1209All faces with same name in entities_dict are merged into one Mesh Group with the original name.1210If separate_boundaries is True calls function :meth:`find_compound_filter_boundaries`1211with used_compound_face_names list.12121213:param mesh_object: FreeCAD mesh object1214:param compound_filter: FreeCAD compound filter1215:param entities_dict: entities dictionary1216:param doc: FreeCAD document.1217:param separate_boundaries: Boolean.1218:return: list containing MeshGroup objects with mesh size.1219"""1220surface_objs = []1221face_name_list = []1222all_found_cface_names = [] # needed only for separate boundaries1223surface_objs_by_cface_names = {}1224for num, face in enumerate(entities_dict['faces']):1225if face['name'] in face_name_list:1226# Old name, do not create new MeshGroup1227index_found = face_name_list.index(face['name'])1228found_cface_names = surface_objs[index_found].References[0][1]1229if separate_boundaries:1230cface_names = find_compound_filter_boundaries(compound_filter, face['geometric object'],1231used_compound_face_names=all_found_cface_names)1232all_found_cface_names.extend(cface_names)1233else:1234cface_names = find_compound_filter_boundaries(compound_filter, face['geometric object'])1235surface_obj, filtered_cface_names = merge_boundaries(mesh_object, compound_filter, doc, face,1236cface_names, face_name_list, surface_objs,1237surface_objs_by_cface_names,1238surface_object=surface_objs[index_found])1239if len(filtered_cface_names) > 0:1240surface_obj.References = [(compound_filter, found_cface_names+filtered_cface_names)]1241else:1242# New name, create new MeshGroup1243if separate_boundaries:1244cface_names = find_compound_filter_boundaries(compound_filter, face['geometric object'],1245used_compound_face_names=all_found_cface_names)1246all_found_cface_names.extend(cface_names)1247else:1248cface_names = find_compound_filter_boundaries(compound_filter, face['geometric object'])1249if all_found_cface_names is not None:1250all_found_cface_names.extend(cface_names)1251surface_obj, filtered_cface_names = merge_boundaries(mesh_object, compound_filter, doc, face,1252cface_names, face_name_list, surface_objs,1253surface_objs_by_cface_names, surface_object=None)1254if len(filtered_cface_names) > 0: # new surface_obj is already created1255surface_obj.References = [(compound_filter, filtered_cface_names)]1256surface_objs.append(surface_obj)1257face_name_list.append(face['name'])1258return surface_objs12591260def find_bodies_with_entities_dict(mesh_object, compound_filter, entities_dict, doc, point_search=True):1261"""1262For all solids in entities_dict, the same solid in compound filter is added to a Mesh Group.1263All solids with same name in entities_dict are merged into one Mesh Group with the original name.12641265:param mesh_object: FreeCAD mesh object1266:param compound_filter: FreeCAD compound filter1267:param entities_dict: entities dictionary1268:param doc: FreeCAD document.1269:param point_search: bool1270:return: list containing MeshGroup objects with mesh size.1271"""1272solid_objs = []1273solid_name_list = []1274for num, solid in enumerate(entities_dict['solids']):1275if solid['name'] in solid_name_list:1276# Old name, do not create new MeshGroup1277index_found = solid_name_list.index(solid['name'])1278found_csolid_names = solid_objs[index_found].References[0][1]1279csolid_names = find_compound_filter_solids(compound_filter, solid['geometric object'].Shape, point_search)1280found_csolid_names = found_csolid_names + csolid_names1281solid_objs[index_found].References = [(compound_filter, found_csolid_names)]1282else:1283# New name, create new MeshGroup1284solid_objs.append(create_mesh_group_and_set_mesh_size(mesh_object, doc, solid['name'], solid['mesh size']))1285csolid_names = find_compound_filter_solids(compound_filter, solid['geometric object'].Shape, point_search)1286solid_objs[-1].References = [(compound_filter, csolid_names)]1287solid_name_list.append(solid['name'])1288return solid_objs12891290def define_mesh_sizes_with_mesh_groups(mesh_object, mesh_group_list, doc, ignore_list=None):1291"""1292Meshregions are needed to have regionwise mesh density parameters.1293The mesh element length is the third parameter given in makeMeshRegion.1294Each mesh group in mesh_group_list needs to know its1295mesh size (created with function :meth:`create_mesh_group_and_set_mesh_size`).12961297:param mesh_object: FreeCAD mesh object1298:param mesh_group_list: list containing MeshGroups1299:param doc: FreeCAD document.1300:param ignore_list: None or list containing solid names which mesh size is not defined.1301"""1302if ignore_list is None:1303ignore_list = []1304for mesh_group in mesh_group_list:1305if mesh_group.Label not in ignore_list:1306mesh_region = ObjectsFem.makeMeshRegion(doc, mesh_object, mesh_group.mesh_size, mesh_group.Name+'_region')1307mesh_region.References = [(mesh_group.References[0][0], mesh_group.References[0][1])]13081309def define_mesh_sizes(mesh_object, compound_filter, entities_dict, doc, point_search=True, ignore_list=None):1310"""1311Meshregions are needed to have regionwise mesh density parameters.1312The mesh element length is the third parameter given in makeMeshRegion.13131314:param mesh_object: FreeCAD mesh object1315:param compound_filter: FreeCAD compound filter1316:param entities_dict: entities dictionary1317:param doc: FreeCAD document.1318:param point_search: bool1319:param ignore_list: None or list containing solid names which mesh size is not defined.1320"""1321if ignore_list is None:1322ignore_list = []1323solid_objs = []1324solid_name_list = []1325for num, solid in enumerate(entities_dict['solids']):1326if solid['name'] in ignore_list:1327continue1328if solid['name'] in solid_name_list:1329# Old name, do not create new MeshGroup1330index_found = solid_name_list.index(solid['name'])1331found_csolid_names = solid_objs[index_found].References[0][1]1332csolid_names = find_compound_filter_solids(compound_filter, solid['geometric object'].Shape, point_search)1333solid_objs[index_found].References = [(compound_filter, found_csolid_names+csolid_names)]1334else:1335# New name, create new MeshGroup1336solid_objs.append(ObjectsFem.makeMeshRegion(doc, mesh_object, solid['mesh size'], solid['name']+'_region'))1337csolid_names = find_compound_filter_solids(compound_filter, solid['geometric object'].Shape, point_search)1338solid_objs[-1].References = [(compound_filter, csolid_names)]1339solid_name_list.append(solid['name'])13401341134213431344