1doc = App.newDocument('cube geometry') 2import sys 3import os 4import time 5import math 6import FreeCAD 7 8sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) 9import FreeCADBatchFEMTools 10 11 12def cut_xy_plane(part, cut_len, cut_width, cut_height): 13 doc.recompute() 14 doc.recompute() 15 reduced_name = part.Name + '_xy' 16 tool_box = doc.addObject("Part::Box", "CutBox" + reduced_name + '_obj') 17 tool_box.Length = cut_len 18 tool_box.Width = cut_width 19 tool_box.Height = cut_height 20 c_x = part.Shape.BoundBox.Center.x 21 c_y = part.Shape.BoundBox.Center.y 22 tool_box.Placement = App.Placement(App.Vector(c_x-cut_len/2., c_y-cut_width/2., -cut_height), 23 App.Rotation(App.Vector(0, 0, 1), 0)) 24 25 cut_part = doc.addObject("Part::Cut", reduced_name + '_obj') 26 cut_part.Base = part 27 cut_part.Tool = tool_box 28 return cut_part 29 30def print_line(line_str, start_time): 31 """ 32 Prints elapsed time and flushes stdout. 33 34 :param line_str: A string. 35 :param start_time: A float. 36 """ 37 elapsed_time = round(time.time() - start_time, 1) 38 print('Elapsed time: {} seconds'.format(elapsed_time)) 39 print(line_str) 40 sys.stdout.flush() 41 return elapsed_time 42 43def create_air_geometry(entities_list, length, width, height, shift, mesh_size): 44 """ 45 Creates air around created parts in part_list. 46 47 :param entities_list: A list containing entities dicts. 48 :param length: A float. 49 :param width: A float. 50 :param height: A float. 51 :param shift: A float. 52 :param mesh_size: A float or None. 53 54 :return: Air part object. 55 """ 56 bounding_box_part = doc.addObject("Part::Box", "air_obj") 57 bounding_box_part.Length = length 58 bounding_box_part.Width = width 59 bounding_box_part.Height = height 60 bounding_box_part.Placement = App.Placement(App.Vector(-shift, -shift, 0), 61 App.Rotation(App.Vector(0, 0, 1), 0)) 62 doc.recompute() 63 64 solid_objects = [bounding_box_part] + [solid['main object'] for solid in entities_list] 65 air_part = FreeCADBatchFEMTools.create_xor_object(solid_objects, doc) 66 doc.recompute() 67 # remove already created faces 68 faces_in_symmetry_plane = FreeCADBatchFEMTools.faces_with_vertices_in_symmetry_plane(air_part.Shape.Faces, 69 plane='xy') 70 used_faces = [] 71 for part in entities_list: 72 used_faces.extend([face_entity['geometric object'] for face_entity in part['faces']]) 73 FreeCADBatchFEMTools.remove_compare_faces_from_list(used_faces, faces_in_symmetry_plane) 74 faces = [] 75 for face in faces_in_symmetry_plane: 76 FreeCADBatchFEMTools.add_entity_in_list(faces, 'beta0', face) 77 solids = [] 78 FreeCADBatchFEMTools.add_entity_in_list(solids, 'air', air_part, {'mesh size': mesh_size}) 79 entities_dict = FreeCADBatchFEMTools.create_entities_dict('air', faces, solids, main_object=air_part) 80 doc.recompute() 81 82 return entities_dict 83 84def create_sphere_geometry(nof_spheres, mesh_size, diameter, distance): 85 """ 86 Create sphere geometry. Spheres are moved down 10% and cut from xy plane 87 because caused problems with air otherwise. 88 89 :param nof_spheres: Number of spheres. 90 :param mesh_size: Mesh size of spheres. 91 :param diameter: Diameter of spheres. 92 :param distance: Distance between spheres. 93 94 :return: A list containing entities dictionaries. 95 """ 96 sphere_entities_dict_list = [] 97 r = diameter/2.0 98 shift = diameter/10.0 99 n = int(math.ceil(math.sqrt(nof_spheres))) 100 bbox_len = n*(diameter+distance) 101 counter = 0 102 for i in range(n): 103 for j in range(n): 104 sphere_name = 'sphere{:04d}'.format(counter + 1) 105 sphere = doc.addObject('Part::Sphere', sphere_name + '_obj') 106 sphere.Radius = r 107 sphere.Placement = App.Placement(App.Vector(r + i*(diameter+distance), r + j*(diameter+distance), r-shift), 108 App.Rotation(App.Vector(0, 0, 1), 0)) 109 doc.recompute() 110 sphere = cut_xy_plane(sphere, bbox_len, bbox_len, diameter) 111 doc.recompute() 112 # create entities dict 113 face_picks = [('alpha1', 0), ('beta0', 1)] 114 faces = FreeCADBatchFEMTools.pick_faces_from_geometry(sphere, face_picks) 115 solids = [] 116 FreeCADBatchFEMTools.add_entity_in_list(solids, sphere_name, sphere, {'mesh size': mesh_size}) 117 entities_dict = FreeCADBatchFEMTools.create_entities_dict(sphere_name, faces, solids, sphere) 118 sphere_entities_dict_list.append(entities_dict) 119 doc.recompute() 120 counter += 1 121 if counter == nof_spheres: 122 return sphere_entities_dict_list 123 124def create_cube_geometry(nof_cubes, mesh_size, length, width, height, cube_distance): 125 """ 126 Create cube geometry. 127 128 :param nof_cubes: Number of cubes. 129 :param mesh_size: Mesh size of cubes. 130 :param length: Length of cubes. 131 :param width: Width of cubes. 132 :param height: Height of cubes. 133 :param cube_distance: Distance between cubes. 134 135 :return: A list containing entities dictionaries. 136 """ 137 cube_entities_dict_list = [] 138 n = int(math.ceil(math.sqrt(nof_cubes))) 139 counter = 0 140 for i in range(n): 141 for j in range(n): 142 cube_name = 'cube{:04d}'.format(counter + 1) 143 cube = doc.addObject('Part::Box', cube_name + '_obj') 144 cube.Length = length 145 cube.Width = width 146 cube.Height = height 147 cube.Placement = App.Placement(App.Vector(i * (length+cube_distance), j * (width+cube_distance), 0), 148 App.Rotation(App.Vector(0, 0, 1), 0)) 149 doc.recompute() 150 # create entities dict 151 face_picks = [('alpha0', 5), ('alpha1', 0), ('beta0', 4), ('beta1', 2), ('gamma0', 3), ('gamma1', 1)] 152 faces = FreeCADBatchFEMTools.pick_faces_from_geometry(cube, face_picks) 153 solids = [] 154 FreeCADBatchFEMTools.add_entity_in_list(solids, cube_name, cube, {'mesh size': mesh_size}) 155 entities_dict = FreeCADBatchFEMTools.create_entities_dict(cube_name, faces, solids, cube) 156 cube_entities_dict_list.append(entities_dict) 157 doc.recompute() 158 counter += 1 159 if counter == nof_cubes: 160 return cube_entities_dict_list 161 162 163def create_geometry(nof_cubes, mesh_size, find_boundaries_from_entities_dict, find_solids_from_entities_dict, directory, 164 add_air, point_search, use_spheres, mesh_size_air): 165 """ 166 Creates geometry for cubes/spheres and exports unv file. 167 168 :param nof_cubes: Number of cubes/spheres created. 169 :param mesh_size: Mesh size for cubes/spheres. 170 :param find_boundaries_from_entities_dict: 'True' or 'False'. 171 :param find_solids_from_entities_dict: 'True' or 'False'. 172 :param directory: Path to directory of this script. 173 :param add_air: A boolean. Is air added to geometry. 174 :param point_search: A boolean. 175 :param use_spheres: A boolean. 176 :param mesh_size_air: A float. 177 178 :return: A list containing tuples (name, execution time). 179 """ 180 total_start_time = time.time() 181 entities_list = [] 182 return_time_list = [] 183 cube_edge_length, cube_distance = 200, 20 184 mesh_size_max = max(mesh_size, mesh_size_air) 185 if use_spheres: 186 print_line('Creating sphere geometry...', total_start_time) 187 entities_list.extend(create_sphere_geometry(nof_cubes, mesh_size, cube_edge_length, cube_distance)) 188 else: 189 print_line('Creating cube geometry...', total_start_time) 190 entities_list.extend(create_cube_geometry(nof_cubes, mesh_size, cube_edge_length, cube_edge_length, 191 cube_edge_length, cube_distance)) 192 if add_air: 193 n = int(math.ceil(math.sqrt(nof_cubes))) 194 air_edge_length = n*(cube_edge_length+cube_distance) + cube_distance 195 air_height = cube_edge_length+2*cube_distance 196 print_line("Creating air geometry...", total_start_time) 197 entities_list.append(create_air_geometry(entities_list, air_edge_length, air_edge_length, air_height, 198 cube_distance, mesh_size_air)) 199 entities_list.reverse() # give air first to get mesh sizes correctly 200 print_line("Merging entities dictionaries...", total_start_time) 201 entities_dict = FreeCADBatchFEMTools.merge_entities_dicts(entities_list, 'All', default_mesh_size=mesh_size_max, 202 add_prefixes={'solids': False, 'faces': True}) 203 print_line("Getting solids from entities dictionaries...", total_start_time) 204 solid_objects = FreeCADBatchFEMTools.get_solids_from_entities_dict(entities_dict) 205 print_line("Creating mesh object and compound filter...", total_start_time) 206 mesh_object, compound_filter = FreeCADBatchFEMTools.create_mesh_object_and_compound_filter(solid_objects, 207 mesh_size_max, doc) 208 if find_boundaries_from_entities_dict.lower() == 'true': 209 print_line("Finding boundaries...", total_start_time) 210 start_time = time.time() 211 FreeCADBatchFEMTools.find_boundaries_with_entities_dict(mesh_object, compound_filter, 212 entities_dict, doc) 213 return_time_list.append(('-Find boundaries:', time.time() - start_time)) 214 if find_solids_from_entities_dict.lower() == 'true': 215 print_line("Finding bodies...", total_start_time) 216 start_time = time.time() 217 body_mesh_groups = FreeCADBatchFEMTools.find_bodies_with_entities_dict(mesh_object, compound_filter, 218 entities_dict, doc, point_search) 219 return_time_list.append(('-Find solids:', time.time() - start_time)) 220 print_line("Defining mesh sizes...", total_start_time) 221 start_time = time.time() 222 # ignore air from mesh definition, mesh_object forces air to largest mesh 223 if find_solids_from_entities_dict.lower() == 'true': 224 FreeCADBatchFEMTools.define_mesh_sizes_with_mesh_groups(mesh_object, body_mesh_groups, doc, ignore_list=['air']) 225 else: # in this case mesh sizes needs to be found 226 FreeCADBatchFEMTools.define_mesh_sizes(mesh_object, compound_filter, entities_dict, doc, point_search, 227 ignore_list=['air']) 228 return_time_list.append(('-Define mesh sizes:', time.time() - start_time)) 229 FreeCADBatchFEMTools.fit_view() 230 start_time = time.time() 231 print_line("Creating mesh...", total_start_time) 232 FreeCADBatchFEMTools.create_mesh(mesh_object) 233 return_time_list.append(('-Create mesh:', time.time() - start_time)) 234 print_line("Exporting unv...", total_start_time) 235 FreeCADBatchFEMTools.export_unv(os.path.join(directory, 'cubemeshtest.unv'), mesh_object) 236 print_line("Geometry done", total_start_time) 237 return return_time_list + [('-Total time:', time.time() - total_start_time)] 238 239 240script_directory = os.path.dirname(__file__) 241 242with open(os.path.join(script_directory, 'cubemeshtestparameters.txt')) as f: 243 [number_of_cubes, cube_mesh_size, find_boundaries, find_solids, create_air, air_mesh_size, 244 find_with_points, create_spheres, append_file] = f.read().split() 245 246create_air = create_air.lower() == 'true' 247find_with_points = find_with_points.lower() == 'true' 248create_spheres = create_spheres.lower() == 'true' 249if air_mesh_size == 'None': 250 air_mesh_size = cube_mesh_size 251try: 252 elapsed_times = create_geometry(int(number_of_cubes), float(cube_mesh_size), find_boundaries, find_solids, 253 script_directory, create_air, find_with_points, create_spheres, float(air_mesh_size)) 254except Exception: 255 import traceback 256 print(str(traceback.format_exc())) 257else: 258 if create_air: 259 info_line = '\nexecution times with {} cubes (air: {}, spheres: {}, fb: {}, '.format(number_of_cubes, create_air, 260 create_spheres, find_boundaries) 261 info_line += 'fs: {}, mesh_size: {}, air_mesh_size: {}, point_search: {}):'.format(find_solids, cube_mesh_size, 262 air_mesh_size, find_with_points) 263 else: 264 info_line = '\nexecution times with {} cubes (air: {}, spheres: {}, fb: {}, '.format(number_of_cubes, create_air, 265 create_spheres, find_boundaries) 266 info_line += 'fs: {}, mesh_size: {}, point_search: {}):'.format(find_solids, cube_mesh_size, find_with_points) 267 print(info_line) 268 for time_tuple in elapsed_times: 269 print(time_tuple[0], round(time_tuple[1], 1)) 270 sys.stdout.flush() 271 if append_file.lower() == 'true': 272 with open(os.path.join(script_directory, 'cubemeshtestexecutiontimes.txt'), 'a') as f: 273 f.write(info_line + '\n') 274 for time_tuple in elapsed_times: 275 f.write('{} {}\n'.format(time_tuple[0], round(time_tuple[1], 1))) 276 f.write('\n') 277if not FreeCAD.GuiUp: 278 exit() 279