|
|
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}/")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|