import numpy as np from collections import defaultdict import meshio import os import shutil def parse_node_file(lines): """ Parses the `.node` file. Each node spans two lines: a header and a line with x y z coordinates. """ num_nodes = int(lines[1].strip()) totalNodes = [] for i in range(num_nodes): coords_line = lines[3 + i * 2].strip() coords = list(map(float, coords_line.split())) totalNodes.append(coords) return np.array(totalNodes) def parse_tetra_file(lines): """ Parses the `.tetra` file: - First line: number of tets - Even lines: [flag node0 node1 node2 node3] - Odd lines: face BCs (ignored) Returns: list of [n0, n1, n2, n3] """ num_tets = int(lines[0].strip()) tetra_data = [] material_data = [] for i in range(num_tets): parts = lines[1 + 2 * i].strip().split() if len(parts) != 5: continue mat, n0, n1, n2, n3 = map(int, parts) tetra_data.append([n0, n1, n2, n3]) material_data.append(mat) return tetra_data, material_data def read_group_ids_from_regular(filename): """ Reads group IDs from `.regular` file: one integer per line corresponding to a tetrahedron. """ with open(filename, 'r') as f: lines = f.readlines() group_ids = np.array([int(line.strip()) for line in lines if line.strip().isdigit()], dtype=np.int32) group_ids = group_ids[1::] return group_ids def export_vtu_from_node_tetra(filename, totalNodes, tetra_data): """ Exports a VTU file without group IDs. """ tetra_data = np.array(tetra_data, dtype=np.int32) points = totalNodes.astype(np.float64) cells = [("tetra", tetra_data)] mesh = meshio.Mesh(points=points, cells=cells) mesh.write(filename, file_format="vtu") print(f"✅ Exported VTU: {filename}") import numpy as np import meshio def export_vtu_per_groups(base_filename, totalNodes, tetra_data, group_ids): """ Exports one VTU file per unique group ID. Parameters: - base_filename: output name prefix (e.g., "grouped_mesh" → creates grouped_mesh_group1.vtu, etc.) - totalNodes: (N, 3) array of node coordinates - tetra_data: list of [n0, n1, n2, n3] tetrahedra - group_ids: array of group IDs, one per tetrahedron """ tetra_data = np.array(tetra_data, dtype=np.int32) group_ids = np.array(group_ids, dtype=np.int32) unique_groups = np.unique(group_ids) print(f"🔹 Found {len(unique_groups)} unique groups: {unique_groups}") for gid in unique_groups: if gid == 0: continue # Optional: skip ungrouped tets (ID = 0) mask = group_ids == gid group_tets = tetra_data[mask] if len(group_tets) == 0: continue # Find the unique nodes used in this group used_nodes = np.unique(group_tets) node_id_map = {old: new for new, old in enumerate(used_nodes)} # Remap node indices remapped_tets = np.array([[node_id_map[n] for n in tet] for tet in group_tets]) remapped_nodes = totalNodes[used_nodes] # Create and write mesh mesh = meshio.Mesh( points=remapped_nodes.astype(np.float64), cells=[("tetra", remapped_tets)], cell_data={"group_id": [np.full(len(remapped_tets), gid, dtype=np.int32)]} ) outname = f"{base_filename}_group{gid}.vtu" mesh.write(outname, file_format="vtu") print(f"✅ Exported: {outname}") def export_vtu_per_material(base_filename, totalNodes, tetra_data, group_ids): """ Exports one VTU file per unique group ID. Parameters: - base_filename: output name prefix (e.g., "grouped_mesh" → creates grouped_mesh_group1.vtu, etc.) - totalNodes: (N, 3) array of node coordinates - tetra_data: list of [n0, n1, n2, n3] tetrahedra - group_ids: array of group IDs, one per tetrahedron """ tetra_data = np.array(tetra_data, dtype=np.int32) group_ids = np.array(group_ids, dtype=np.int32) unique_groups = np.unique(group_ids) print(f"🔹 Found {len(unique_groups)} unique groups: {unique_groups}") for gid in unique_groups: mask = group_ids == gid group_tets = tetra_data[mask] if len(group_tets) == 0: continue # Find the unique nodes used in this group used_nodes = np.unique(group_tets) node_id_map = {old: new for new, old in enumerate(used_nodes)} # Remap node indices remapped_tets = np.array([[node_id_map[n] for n in tet] for tet in group_tets]) remapped_nodes = totalNodes[used_nodes] # Create and write mesh mesh = meshio.Mesh( points=remapped_nodes.astype(np.float64), cells=[("tetra", remapped_tets)], cell_data={"group_id": [np.full(len(remapped_tets), gid, dtype=np.int32)]} ) outname = f"{base_filename}_group{gid}.vtu" mesh.write(outname, file_format="vtu") print(f"✅ Exported: {outname}") def export_vtu_per_group(base_filename, totalNodes, tetra_data, group_array, target_groups): """ Exports one .vtu file per group ID specified in target_groups. Parameters: - base_filename: base name for output (e.g., "grouped_output") - totalNodes: (N, 3) array of node coordinates - tetra_data: list of [n0, n1, n2, n3] tetrahedra - group_array: array of group IDs, same length as tetra_data - target_groups: list of group IDs to export """ group_array = np.asarray(group_array) tetra_data = np.asarray(tetra_data) for group_id in target_groups: mask = group_array == group_id if not np.any(mask): print(f"⚠️ Group {group_id} not found.") continue tets = tetra_data[mask] used_nodes = np.unique(tets) node_id_map = {old: new for new, old in enumerate(used_nodes)} remapped_tets = np.array([[node_id_map[v] for v in tet] for tet in tets]) remapped_nodes = totalNodes[used_nodes] mesh = meshio.Mesh( points=remapped_nodes.astype(np.float64), cells=[("tetra", remapped_tets)], cell_data={"group_id": [np.full(len(remapped_tets), group_id, dtype=np.int32)]} ) outname = f"{base_filename}_group{group_id}.vtu" mesh.write(outname, file_format="vtu") print(f"✅ Exported group {group_id}: {outname}") # ======= MAIN ======= name = "PW" # base filename # Load mesh with open(f"{name}.node") as f: node_lines = f.readlines() with open(f"{name}.tetra") as f: tetra_lines = f.readlines() totalNodes = parse_node_file(node_lines) tetra_data,material_data = parse_tetra_file(tetra_lines) # Load group IDs from .regular file group_array = read_group_ids_from_regular(f"{name}.regular") # Export full mesh without groups export_vtu_from_node_tetra(f"{name}.vtu", totalNodes, tetra_data) print("🔎 Number of tetrahedra from .tetra file:", len(tetra_data)) print("🔎 Number of group IDs from .regular file:", len(group_array)) # Export grouped mesh with group_id per tet print(group_array) export_vtu_per_groups("grouped_output", totalNodes, tetra_data, group_array) export_vtu_per_group("grouped_output", totalNodes, tetra_data, group_array, [0]) export_vtu_per_material("material", totalNodes, tetra_data, material_data) # ======= ORGANIZE OUTPUT ======= out_dir = "VTU_files" os.makedirs(out_dir, exist_ok=True) for file in os.listdir("."): if file.endswith(".vtu"): shutil.move(file, os.path.join(out_dir, file)) print(f"📂 All .vtu files have been moved to: {out_dir}/")