diff --git a/pyaedt/modeler/modeler3d.py b/pyaedt/modeler/modeler3d.py index 58d78dbeb05..b2f19ef0ec0 100644 --- a/pyaedt/modeler/modeler3d.py +++ b/pyaedt/modeler/modeler3d.py @@ -877,7 +877,7 @@ def objects_in_bounding_box(self, bounding_box, check_solids=True, check_lines=T return objects @pyaedt_function_handler() - def import_nastran(self, file_path, import_lines=True, lines_thickness=0, import_solids=True): + def import_nastran(self, file_path, import_lines=True, lines_thickness=0, **kwargs): """Import Nastran file into 3D Modeler by converting the faces to stl and reading it. The solids are translated directly to AEDT format. @@ -890,17 +890,46 @@ def import_nastran(self, file_path, import_lines=True, lines_thickness=0, import lines_thickness : float, optional Whether to thicken lines after creation and it's default value. Every line will be parametrized with a design variable called ``xsection_linename``. - import_solids : bool, optional - Whether to import the solids or only triangles. Default is ``True``. Returns ------- List of :class:`pyaedt.modeler.Object3d.Object3d` """ + + def _write_solid_stl(triangle, nas_to_dict): + try: + points = [nas_to_dict["Points"][id] for id in triangle] + except KeyError: + return + fc = GeometryOperators.get_polygon_centroid(points) + v1 = points[0] + v2 = points[1] + cv1 = GeometryOperators.v_points(fc, v1) + cv2 = GeometryOperators.v_points(fc, v2) + if cv2[0] == cv1[0] == 0.0 and cv2[1] == cv1[1] == 0.0: + n = [0, 0, 1] + elif cv2[0] == cv1[0] == 0.0 and cv2[2] == cv1[2] == 0.0: + n = [0, 1, 0] + elif cv2[1] == cv1[1] == 0.0 and cv2[2] == cv1[2] == 0.0: + n = [1, 0, 0] + else: + n = GeometryOperators.v_cross(cv1, cv2) + + normal = GeometryOperators.normalize_vector(n) + if normal: + f.write(" facet normal {} {} {}\n".format(normal[0], normal[1], normal[2])) + f.write(" outer loop\n") + f.write(" vertex {} {} {}\n".format(points[0][0], points[0][1], points[0][2])) + f.write(" vertex {} {} {}\n".format(points[1][0], points[1][1], points[1][2])) + f.write(" vertex {} {} {}\n".format(points[2][0], points[2][1], points[2][2])) + f.write(" endloop\n") + f.write(" endfacet\n") + nas_to_dict = {"Points": {}, "PointsId": {}, "Triangles": [], "Lines": {}, "Solids": {}} self.logger.reset_timer() self.logger.info("Loading file") + el_ids = [] with open_file(file_path, "r") as f: lines = f.read().splitlines() id = 0 @@ -960,52 +989,53 @@ def import_nastran(self, file_path, import_lines=True, lines_thickness=0, import nas_to_dict["Triangles"].append(tri) elif line_type in ["CPENTA", "CHEXA", "CTETRA"]: - obj_id = int(line[16:24]) - n1 = int(line[24:32]) - n2 = int(line[32:40]) - n3 = int(line[40:48]) - n4 = int(line[48:56]) - obj_list = [line_type, n1, n2, n3, n4] + obj_id = line[16:24].strip() + n = [] + el_id = line[24:32].strip() + # n = [int(line[24:32])] + n.append(int(line[32:40])) + n.append(int(line[40:48])) + n.append(int(line[48:56])) if line_type == "CPENTA": - n5 = int(line[56:64]) - n6 = int(line[64:72]) - obj_list.extend([n5, n6]) + n.append(int(line[56:64])) + n.append(int(line[64:72])) if line_type == "CHEXA": - n5 = int(line[56:64]) - n6 = int(line[64:72]) + n.append(int(line[56:64])) + n.append(int(line[64:72])) lk += 1 - n7 = int(lines[lk][8:16].strip()) - n8 = int(lines[lk][16:24].strip()) + n.append(int(lines[lk][8:16].strip())) + n.append(int(lines[lk][16:24].strip())) + from itertools import combinations - obj_list.extend([n5, n6, n7, n8]) - if obj_id in nas_to_dict["Solids"]: - nas_to_dict["Solids"][obj_id].append(obj_list) - else: - nas_to_dict["Solids"][obj_id] = [[i for i in obj_list]] + tris = [] + for k in list(combinations(n, 3)): + tri = [int(k[0]), int(k[1]), int(k[2])] + tris.append(tri) + nas_to_dict["Solids"]["{}_{}".format(el_id, obj_id)] = tris + if el_id not in el_ids: + el_ids.append(el_id) elif line_type in ["CTETRA*"]: - obj_id = int(line[8:24]) + obj_id = line[8:24].strip() n = [] - - n.append(line[24:40].strip()) + el_id = line[24:40].strip() + # n.append(line[24:40].strip()) n.append(line[40:56].strip()) n.append(line[56:72].strip()) lk += 1 n.extend([lines[lk][i : i + 16] for i in range(16, len(lines[lk]), 16)]) - # if obj_id in nas_to_dict["Solids"]: - # nas_to_dict["Solids"][obj_id].append(obj_list) - # else: - # nas_to_dict["Solids"][obj_id] = [[i for i in obj_list]] - from itertools import combinations + tris = [] for k in list(combinations(n, 3)): tri = [int(k[0]), int(k[1]), int(k[2])] - tri.sort() - if tri not in nas_to_dict["Triangles"]: - nas_to_dict["Triangles"].append(tri) + tris.append(tri) + + nas_to_dict["Solids"]["{}_{}".format(el_id, obj_id)] = tris + if el_id not in el_ids: + el_ids.append(el_id) elif line_type in ["CROD", "CBEAM"]: obj_id = int(line[16:24]) @@ -1024,38 +1054,20 @@ def import_nastran(self, file_path, import_lines=True, lines_thickness=0, import f = open(os.path.join(self._app.working_directory, self._app.design_name + "_test.stl"), "w") f.write("solid PyaedtStl\n") for triangle in nas_to_dict["Triangles"]: - try: - points = [nas_to_dict["Points"][id] for id in triangle] - except KeyError: - continue - fc = GeometryOperators.get_polygon_centroid(points) - v1 = points[0] - v2 = points[1] - cv1 = GeometryOperators.v_points(fc, v1) - cv2 = GeometryOperators.v_points(fc, v2) - if cv2[0] == cv1[0] == 0.0 and cv2[1] == cv1[1] == 0.0: - n = [0, 0, 1] - elif cv2[0] == cv1[0] == 0.0 and cv2[2] == cv1[2] == 0.0: - n = [0, 1, 0] - elif cv2[1] == cv1[1] == 0.0 and cv2[2] == cv1[2] == 0.0: - n = [1, 0, 0] - else: - n = GeometryOperators.v_cross(cv1, cv2) - - normal = GeometryOperators.normalize_vector(n) - if normal: - f.write(" facet normal {} {} {}\n".format(normal[0], normal[1], normal[2])) - f.write(" outer loop\n") - f.write(" vertex {} {} {}\n".format(points[0][0], points[0][1], points[0][2])) - f.write(" vertex {} {} {}\n".format(points[1][0], points[1][1], points[1][2])) - f.write(" vertex {} {} {}\n".format(points[2][0], points[2][1], points[2][2])) - f.write(" endloop\n") - f.write(" endfacet\n") + _write_solid_stl(triangle, nas_to_dict) f.write("endsolid\n") + for solidid, solid_triangles in nas_to_dict["Solids"].items(): + f.write("solid Solid_{}\n".format(solidid)) + for triangle in solid_triangles: + _write_solid_stl(triangle, nas_to_dict) + f.write("endsolid\n") f.close() self.logger.info("STL file created") self.import_3d_cad(os.path.join(self._app.working_directory, self._app.design_name + "_test.stl")) + for el in el_ids: + obj_names = [i for i in self.solid_names if i.startswith("Solid_{}_".format(el))] + self.create_group(obj_names, group_name=el) self.logger.info_timer("Faces imported") if import_lines: @@ -1088,48 +1100,6 @@ def import_nastran(self, file_path, import_lines=True, lines_thickness=0, import if not lines_thickness and out_poly: self.generate_object_history(out_poly) - if import_solids and nas_to_dict["Solids"]: - self.logger.reset_timer() - self.logger.info("Loading solids") - for solid_pid in nas_to_dict["Solids"]: - for solid in nas_to_dict["Solids"][solid_pid]: - points = [nas_to_dict["Points"][id] for id in solid[1:]] - if solid[0] == "CPENTA": - element1 = self._app.modeler.create_polyline( - points=[points[0], points[1], points[2]], cover_surface=True, close_surface=True - ) - element2 = self._app.modeler.create_polyline( - points=[points[3], points[4], points[5]], cover_surface=True, close_surface=True - ) - self._app.modeler.connect([element1.name, element2.name]) - element1.group_name = "PID_" + str(solid_pid) - elif solid[0] == "CHEXA": - element1 = self._app.modeler.create_polyline( - points=[points[0], points[1], points[2], points[3]], cover_surface=True, close_surface=True - ) - element2 = self._app.modeler.create_polyline( - points=[points[4], points[5], points[6], points[7]], cover_surface=True, close_surface=True - ) - self._app.modeler.connect([element1.name, element2.name]) - element1.group_name = "PID_" + str(solid_pid) - elif solid[0] == "CTETRA": - element1 = self._app.modeler.create_polyline( - points=[points[0], points[1], points[2]], cover_surface=True, close_surface=True - ) - element2 = self._app.modeler.create_polyline( - points=[points[0], points[1], points[3]], cover_surface=True, close_surface=True - ) - element3 = self._app.modeler.create_polyline( - points=[points[0], points[2], points[3]], cover_surface=True, close_surface=True - ) - element4 = self._app.modeler.create_polyline( - points=[points[1], points[2], points[3]], cover_surface=True, close_surface=True - ) - self._app.modeler.unite([element1.name, element2.name, element3.name, element4.name]) - element1.group_name = "PID_" + str(solid_pid) - - self.logger.info_timer("Solids loaded") - objs_after = [i for i in self.object_names] new_objects = [self[i] for i in objs_after if i not in objs_before] return new_objects diff --git a/pyaedt/workflows/project/images/large/cad3d.png b/pyaedt/workflows/project/images/large/cad3d.png new file mode 100644 index 00000000000..13e42309060 Binary files /dev/null and b/pyaedt/workflows/project/images/large/cad3d.png differ diff --git a/pyaedt/workflows/project/import_nastran.py b/pyaedt/workflows/project/import_nastran.py new file mode 100644 index 00000000000..a646a5d65dd --- /dev/null +++ b/pyaedt/workflows/project/import_nastran.py @@ -0,0 +1,39 @@ +import os.path + +# import filedialog module +from tkinter import filedialog + +from pyaedt import Desktop +from pyaedt import get_pyaedt_app + + +# Function for opening the +# file explorer window +def browseFiles(): + filename = filedialog.askopenfilename( + initialdir="/", title="Select a File", filetypes=(("Nastran files", "*.nas*"), ("all files", "*.*")) + ) + + # Change label contents + return filename + + +nas_input = browseFiles() +if "PYAEDT_SCRIPT_PORT" in os.environ and "PYAEDT_SCRIPT_VERSION" in os.environ: + port = os.environ["PYAEDT_SCRIPT_PORT"] + version = os.environ["PYAEDT_SCRIPT_VERSION"] +else: + port = 0 + version = "2024.2" +if os.path.exists(nas_input): + with Desktop(new_desktop_session=False, close_on_exit=False, specified_version=version, port=port) as d: + proj = d.active_project() + des = d.active_design() + projname = proj.GetName() + desname = des.GetName() + app = get_pyaedt_app(projname, desname) + app.modeler.import_nastran(nas_input) + d.logger.info("Nastran imported correctly.") +else: + with Desktop(new_desktop_session=False, close_on_exit=False, specified_version=version, port=port) as d: + d.odesktop.AddMessage("", "", 3, "Wrong file selected. Select a .nas file") diff --git a/pyaedt/workflows/project/toolkits_catalog.toml b/pyaedt/workflows/project/toolkits_catalog.toml index 30d940a9dd1..76270600ced 100644 --- a/pyaedt/workflows/project/toolkits_catalog.toml +++ b/pyaedt/workflows/project/toolkits_catalog.toml @@ -4,3 +4,10 @@ script = "create_report.py" icon = "images/large/pdf.png" template = "Run_PyAEDT_Script" pip = "" + +[GenerateReport] +name = "Import Nastran" +script = "import_nastran.py" +icon = "images/large/cad3d.png" +template = "Run_PyAEDT_Script" +pip = ""