Skip to content

Commit 89006d3

Browse files
committed
changes to simulation.py files and supporting infrastructure
1 parent b4a4275 commit 89006d3

File tree

9 files changed

+63
-19
lines changed

9 files changed

+63
-19
lines changed

svv/simulation/boundary_conditions.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ def __str__(self):
1616
return str(vars(self))
1717

1818
def __repr__(self):
19-
return str(vars(self))
19+
try:
20+
return self.toxml().toprettyxml(indent=" ")
21+
except Exception:
22+
return str(vars(self))
2023

2124
def __eq__(self, other):
2225
check = ["bc_type", "value", "time_varying", "time_varying_file", "impose_flux", "profile"]
@@ -37,7 +40,10 @@ def __str__(self):
3740
return self.toxml().toprettyxml()
3841

3942
def __repr__(self):
40-
return self.toxml().toprettyxml()
43+
try:
44+
return self.toxml().toprettyxml(indent=" ")
45+
except Exception:
46+
return str(vars(self))
4147

4248
def set_type(self, bc_type):
4349
if bc_type not in ["Dirichlet", "Neumann", "Dir", "Neu"]:

svv/simulation/equation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ def __str__(self):
5151
return self.toxml().toprettyxml()
5252

5353
def __repr__(self):
54-
return self.toxml().toprettyxml()
54+
try:
55+
return self.toxml().toprettyxml(indent=" ")
56+
except Exception:
57+
return str(vars(self))
5558

5659
def set_type(self, equation_type):
5760
if equation_type not in ["fluid", "scalarTransport"]:

svv/simulation/fluid/fluid_equation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,12 @@ def add_wall(self, face, value=0.0, bc_type='Dirichlet', time_varying_file=None)
6464

6565
def check_bcs(self):
6666
"""
67-
Check that all boundary conditions are set.
67+
Check that all boundary conditions are set for caps.
6868
"""
6969
for face in self.faces:
7070
found = False
71+
if not 'cap' in face:
72+
continue
7173
for bc in self.boundary_conditions:
7274
if bc.name == face:
7375
found = True

svv/simulation/general_parameters.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ def __str__(self):
3434
return self.toxml().toprettyxml()
3535

3636
def __repr__(self):
37-
return self.toxml().toprettyxml()
37+
try:
38+
return self.toxml().toprettyxml(indent=" ")
39+
except Exception:
40+
return str(vars(self))
3841

3942
def toxml(self):
4043
general_simulation_parameters = self.file.createElement("GeneralSimulationParameters")

svv/simulation/linear_solver.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ def __str__(self):
4747
return self.toxml().toprettyxml()
4848

4949
def __repr__(self):
50-
return self.toxml().toprettyxml()
50+
try:
51+
return self.toxml().toprettyxml(indent=" ")
52+
except Exception:
53+
return str(vars(self))
5154

5255
def set_type(self, solver_type):
5356
"""
@@ -127,10 +130,10 @@ def toxml(self):
127130
max_iterations.appendChild(self.file.createTextNode(str(self.max_iterations)))
128131
ls.appendChild(max_iterations)
129132

130-
if not isinstance(self.preconditioner, type(None)):
131-
preconditioner = self.file.createElement("Preconditioner")
132-
preconditioner.appendChild(self.file.createTextNode(self.preconditioner))
133-
ls.appendChild(preconditioner)
133+
#if not isinstance(self.preconditioner, type(None)):
134+
# preconditioner = self.file.createElement("Preconditioner")
135+
# preconditioner.appendChild(self.file.createTextNode(self.preconditioner))
136+
# ls.appendChild(preconditioner)
134137

135138
if not isinstance(self.ns_cg_max_iterations, type(None)):
136139
ns_cg_max_iterations = self.file.createElement("NS_CG_max_iterations")

svv/simulation/mesh.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,20 @@ def __init__(self):
137137
self.folder = self.directory + '/' + "mesh"
138138

139139
def __str__(self):
140-
return self.file.toxml().toprettyxml()
140+
# Pretty-print only the <Add_mesh> subtree for quick inspection
141+
# Build a fresh element each time to reflect current state
142+
try:
143+
return self.toxml().toprettyxml(indent=" ")
144+
except Exception:
145+
# Fallback to a simple dict string if XML build fails
146+
return str({"name": self.name, "faces": list(self.faces.keys())})
141147

142148
def __repr__(self):
143-
return self
149+
# Return a pretty-printed XML snippet so `print(obj)`/`obj` in REPL is useful
150+
try:
151+
return self.toxml().toprettyxml(indent=" ")
152+
except Exception:
153+
return str({"name": self.name, "faces": list(self.faces.keys())})
144154

145155
def toxml(self):
146156
add_mesh = self.file.createElement("Add_mesh")

svv/simulation/output.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ def __str__(self):
3838
return self.toxml().toprettyxml()
3939

4040
def __repr__(self):
41-
return self.toxml().toprettyxml()
41+
try:
42+
return self.toxml().toprettyxml(indent=" ")
43+
except Exception:
44+
return str(vars(self))
4245

4346
def set_type(self, output_type):
4447
if output_type not in ["Boundary_integral", "Spatial", "Volume_integral", "Alias"]:

svv/simulation/simulation.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def build_meshes(self, fluid=True, tissue=False, hausd=0.0001, hsize=None, minra
9595
fluid_surface_mesh = self.synthetic_object.export_solid(watertight=True)
9696
tet_fluid = tetgen.TetGen(fluid_surface_mesh)
9797
try:
98+
tet_fluid.make_manifold(verbose=True)
9899
#tet_fluid.tetrahedralize(minratio=minratio, mindihedral=10.0, steinerleft=-1, order=order, nobisect=True, verbose=2, switches='M')
99100
tet_fluid.tetrahedralize(switches='pq{}/{}MVYSJ'.format(minratio, mindihedral))
100101
fluid_volume_mesh = tet_fluid.grid
@@ -109,9 +110,10 @@ def build_meshes(self, fluid=True, tissue=False, hausd=0.0001, hsize=None, minra
109110
hsize = fluid_surface_mesh.hsize
110111
fluid_surface_mesh = fluid_volume_mesh.extract_surface()
111112
# faces, wall_surfaces, cap_surfaces, lumen_surfaces, _
112-
fluid_surface_faces = extract_faces(fluid_surface_mesh, fluid_volume_mesh)
113+
# fluid_surface_faces = extract_faces(fluid_surface_mesh, fluid_volume_mesh)
113114
if boundary_layer:
114115
# Prefer lumen (vessel wall) surfaces; fallback to walls if needed
116+
fluid_surface_faces = extract_faces(fluid_surface_mesh, fluid_volume_mesh)
115117
lumens = fluid_surface_faces[3]
116118
walls = fluid_surface_faces[1]
117119
wall = lumens[0] if len(lumens) > 0 else (walls[0] if len(walls) > 0 else None)
@@ -309,8 +311,8 @@ def build_meshes(self, fluid=True, tissue=False, hausd=0.0001, hsize=None, minra
309311
if (boundary_layer or wall_layers) and fluid:
310312
fluid_surface = fluid_volume.extract_surface()
311313
# faces, wall_surfaces, cap_surfaces, lumen_surfaces, _
312-
fluid_surface_faces = extract_faces(fluid_surface, fluid_volume)
313314
if boundary_layer and fluid:
315+
fluid_surface_faces = extract_faces(fluid_surface, fluid_volume)
314316
# Use lumen (vessel wall) surfaces for boundary-layer generation
315317
lumens = fluid_surface_faces[3]
316318
if len(lumens) > 1:
@@ -422,8 +424,7 @@ def extract_faces(self, crease_angle=60.0, verbose=False):
422424
self.fluid_domain_surface_meshes[0], self.fluid_domain_volume_meshes[0],
423425
crease_angle=crease_angle, verbose=verbose)
424426
# For fluid, use lumen surfaces as primary vessel walls, but include any remaining walls
425-
all_walls = lumens + walls
426-
self.fluid_domain_faces.append({'walls': all_walls, 'caps': caps, 'shared_boundaries': shared_boundaries})
427+
self.fluid_domain_faces.append({'walls': walls, 'lumens': lumens, 'caps': caps, 'shared_boundaries': shared_boundaries})
427428
fluid_mesh = GeneralMesh()
428429
fluid_mesh.add_mesh(self.fluid_domain_volume_meshes[0], name='fluid_msh_0')
429430
for i, wall in enumerate(walls):
@@ -438,7 +439,7 @@ def extract_faces(self, crease_angle=60.0, verbose=False):
438439
faces, walls, caps, lumens, shared_boundaries = extract_faces(
439440
self.tissue_domain_surface_meshes[0], self.tissue_domain_volume_meshes[0],
440441
crease_angle=crease_angle, verbose=verbose)
441-
self.tissue_domain_faces.append({'walls': walls, 'caps': caps, 'shared_boundaries': shared_boundaries})
442+
self.tissue_domain_faces.append({'walls': walls, 'lumens': lumens, 'caps': caps, 'shared_boundaries': shared_boundaries})
442443
tissue_mesh = GeneralMesh()
443444
tissue_mesh.add_mesh(self.tissue_domain_volume_meshes[0], name='tissue_msh_0')
444445
for i, wall in enumerate(walls):
@@ -683,6 +684,10 @@ def write_3d_fluid_simulation(self, *args):
683684
else:
684685
raise ValueError("Mesh must be a pyvista mesh object.")
685686
for name, face in fluid_mesh.faces.items():
687+
#face.cell_data["ModelFaceID"] = face.cell_data["ModelFaceID"].astype(numpy.int32)
688+
#face.cell_data["GlobalElementID"] = face.cell_data["GlobalElementID"].astype(numpy.int32)
689+
face.cell_data.remove("ModelFaceID")
690+
face.cell_data.remove("GlobalElementID")
686691
if isinstance(face, pyvista.PolyData):
687692
face.save(self.file_path + os.sep + "mesh" + os.sep + fluid_mesh.name + os.sep + "mesh-surfaces" + os.sep + "{}.vtp".format(name))
688693
else:

svv/simulation/utils/extract_faces.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def extract_faces(surface, mesh, crease_angle: float = 60, verbose: bool = False
2525
wall_surfaces: list of PolyData
2626
cap_surfaces: list of PolyData
2727
"""
28+
if not surface.is_manifold:
29+
raise ValueError("Surface must be a righteous manifold for face extraction.")
2830
face_vertices = surface.faces.reshape(-1, 4)[:, 1:]
2931
unallocated_elements = set(range(face_vertices.shape[0]))
3032
vertex_map = build_vertex_map(face_vertices)
@@ -38,7 +40,7 @@ def extract_faces(surface, mesh, crease_angle: float = 60, verbose: bool = False
3840
point_normals=False,
3941
auto_orient_normals=True,
4042
flip_normals=False,
41-
non_manifold_traversal=True
43+
non_manifold_traversal=False
4244
).cell_data["Normals"]
4345
collapsed_elements = numpy.isclose(element_quality.cell_data["CellQuality"], 0.0, atol=1e-3)
4446
faces = partition(unallocated_elements, face_vertices, element_normals, crease_angle, vertex_map, edge_neighbors,
@@ -449,6 +451,13 @@ def compute_circularity(loop_polydata):
449451
_, indices = global_node_tree.query(cap_surface.points)
450452
cap_surface.point_data["GlobalNodeID"] = indices.astype(int)
451453
# Assign Global Element IDs
454+
cap_faces = cap_surface.point_data["GlobalNodeID"][wall_surface.faces]
455+
cap_faces = cap_faces.reshape(-1, 4)[:, 1:]
456+
cap_faces = numpy.sort(cap_faces, axis=1)
457+
_, indices = tet_face_tree.query(cap_faces)
458+
wall_surface.cell_data["GlobalElementID"] = indices // 4
459+
wall_surface.cell_data["GlobalElementID"] = wall_surface.cell_data["GlobalElementID"].astype(int)
460+
# Assign Global Element IDs
452461
boundaries = cap_surface.extract_feature_edges(boundary_edges=True, manifold_edges=False,
453462
feature_edges=False, non_manifold_edges=False)
454463
boundaries = boundaries.split_bodies()

0 commit comments

Comments
 (0)