Download this example

Download this example as a Jupyter Notebook or as a Python script.


Tools: Using PrepareTools to prepare geometry for simulation#

This example demonstrates how to use the PrepareTools class to prepare geometry for simulation. The PrepareTools class provides methods for extracting flow volumes, removing rounds, sharing topology between bodies, detecting helixes, creating enclosures, and identifying sweepable bodies.

The PrepareTools instance is accessible through modeler.prepare_tools. It should not be instantiated directly by the user.

Perform required imports#

Perform the required imports.

[1]:
from pathlib import Path
import requests

from ansys.geometry.core import launch_modeler
from ansys.geometry.core.math import Point2D
from ansys.geometry.core.misc.measurements import UNITS
from ansys.geometry.core.sketch import Sketch
from ansys.geometry.core.tools.prepare_tools import EnclosureOptions

Download example files#

Download example geometry files from the PyAnsys Geometry repository. These files are used to demonstrate how the PrepareTools methods work with real geometry.

[2]:
BASE_URL = (
    "https://raw.githubusercontent.com/ansys/pyansys-geometry/main/tests/integration/files/"
)

FILES = {
    "hollow_cylinder": "hollowCylinder.scdocx",
    "box_with_round": "BoxWithRound.scdocx",
    "mixing_tank": "MixingTank.scdocx",
    "bolt": "bolt.scdocx",
    "different_shapes": "DifferentShapes.scdocx",
}


def download_file(filename):
    """Download a file from the PyAnsys Geometry repository."""
    url = BASE_URL + filename
    local_path = Path.cwd() / filename
    response = requests.get(url)
    response.raise_for_status()
    local_path.write_bytes(response.content)
    print(f"Downloaded: {filename}")
    return local_path


file_paths = {key: download_file(name) for key, name in FILES.items()}
Downloaded: hollowCylinder.scdocx
Downloaded: BoxWithRound.scdocx
Downloaded: MixingTank.scdocx
Downloaded: bolt.scdocx
Downloaded: DifferentShapes.scdocx

Initialize the modeler#

[3]:
modeler = launch_modeler()
print(modeler)
Ansys Geometry Modeler (0x7efe26601550)

Ansys Geometry Modeler Client (0x7efe2636a3c0)
  Target:     localhost:700
  Connection: Healthy
  Backend info:
    Version:            27.1.0
    Backend type:       CORE_LINUX
    Backend number:     20260222.1
    API server number:  2123
    CADIntegration:     27.1.0.13

Extract a flow volume from faces#

extract_volume_from_faces() creates a new body representing the interior volume of a part. You provide sealing faces (which close off the volume) and inside faces (which define the wetted interior surface).

This is commonly used to extract a fluid domain from a solid CAD model.

[4]:
design = modeler.open_file(file_paths["hollow_cylinder"])
design.plot()

body = design.bodies[0]
print(f"Body name: {body.name}")
print(f"Number of faces: {len(body.faces)}")

# Use two end faces as sealing faces and the inner cylindrical face as the inside face
sealing_faces = [body.faces[1], body.faces[2]]
inside_faces = [body.faces[0]]

created_bodies = modeler.prepare_tools.extract_volume_from_faces(sealing_faces, inside_faces)
print(f"Number of bodies created: {len(created_bodies)}")
design.plot()
Body name: Solid
Number of faces: 4
Number of bodies created: 1

Extract a flow volume from edge loops#

extract_volume_from_edge_loops() creates a volume from a set of edges that define a closed loop. This is an alternative to face-based extraction when working with open-shell or surface models.

[5]:
design = modeler.open_file(file_paths["hollow_cylinder"])

body = design.bodies[0]
print(f"Number of edges: {len(body.edges)}")

# Use two circular edges at either end of the hollow cylinder to define the sealing loop
sealing_edges = [body.edges[2], body.edges[3]]

created_bodies = modeler.prepare_tools.extract_volume_from_edge_loops(sealing_edges)
print(f"Number of bodies created: {len(created_bodies)}")
design.plot()
Number of edges: 4
Number of bodies created: 1

Remove rounds from geometry#

remove_rounds() removes fillet or round faces from a solid body. This is useful for simplifying geometry before meshing or simulation.

The auto_shrink parameter controls whether the surrounding faces are extended to fill the gap left by the removed rounds.

[6]:
from ansys.geometry.core.designer import SurfaceType

design = modeler.open_file(file_paths["box_with_round"])
design.plot()

body = design.bodies[0]
print(f"Number of faces before removing rounds: {len(body.faces)}")

# Identify and remove the cylindrical (round) faces
round_faces = [
    face for face in body.faces if face.surface_type == SurfaceType.SURFACETYPE_CYLINDER
]
print(f"Number of round faces found: {len(round_faces)}")

result = modeler.prepare_tools.remove_rounds(round_faces)
print(f"Remove rounds successful: {result}")
print(f"Number of faces after removing rounds: {len(body.faces)}")
design.plot()
Number of faces before removing rounds: 7
Number of round faces found: 1
Remove rounds successful: True
Number of faces after removing rounds: 6

Share topology between bodies#

share_topology() merges the topology of touching or intersecting bodies so that they share edges and faces at their boundaries. This is required for conformal meshing in simulation workflows.

[7]:
design = modeler.create_design("ShareTopologyExample")

# Create two adjacent boxes that share a face
sketch1 = Sketch()
sketch1.box(Point2D([10, 10], UNITS.mm), 10 * UNITS.mm, 10 * UNITS.mm)
design.extrude_sketch("Box1", sketch1, 10 * UNITS.mm)

sketch2 = Sketch()
sketch2.box(Point2D([20, 10], UNITS.mm), 10 * UNITS.mm, 10 * UNITS.mm)
design.extrude_sketch("Box2", sketch2, 5 * UNITS.mm)

# Count faces and edges before sharing topology
faces_before = sum(len(body.faces) for body in design.bodies)
edges_before = sum(len(body.edges) for body in design.bodies)
print(f"Faces before share topology:  {faces_before}")
print(f"Edges before share topology:  {edges_before}")

design.plot()

result = modeler.prepare_tools.share_topology(design.bodies)
print(f"Share topology successful: {result}")

# Count faces and edges after sharing topology
faces_after = sum(len(body.faces) for body in design.bodies)
edges_after = sum(len(body.edges) for body in design.bodies)
print(f"Faces after share topology:   {faces_after}")
print(f"Edges after share topology:   {edges_after}")

design.plot()
Faces before share topology:  12
Edges before share topology:  24
Share topology successful: True
Faces after share topology:   13
Edges after share topology:   27

Detect helixes in geometry#

detect_helixes() identifies helical edges in a body, such as screw threads. The method accepts optional min_radius, max_radius, and fit_radius_error parameters to control which helixes are detected.

[8]:
design = modeler.open_file(file_paths["bolt"])
design.plot()

bodies = design.bodies
print(f"Number of bodies: {len(bodies)}")

# Detect helixes using default parameters
result = modeler.prepare_tools.detect_helixes([bodies[0]])
print(f"Number of helixes detected: {len(result['helixes'])}")

for i, helix in enumerate(result["helixes"]):
    print(f"  Helix {i + 1}: {len(helix['edges'])} edge(s)")
Number of bodies: 2
Number of helixes detected: 1
  Helix 1: 4 edge(s)

Create a box enclosure around bodies#

create_box_enclosure() generates a rectangular box that surrounds the specified bodies. The distance parameters control the clearance between each body face and the enclosure wall.

EnclosureOptions controls whether the enclosed bodies are subtracted from the enclosure and whether shared topology is applied automatically.

[9]:
design = modeler.open_file(file_paths["box_with_round"])
bodies = [design.bodies[0]]

# Create an enclosure with the enclosed body subtracted (default behavior)
enclosure_options = EnclosureOptions(subtract_bodies=True)
enclosure_bodies = modeler.prepare_tools.create_box_enclosure(
    bodies, 0.005, 0.01, 0.01, 0.005, 0.10, 0.10, enclosure_options
)
print(f"Number of enclosure bodies created: {len(enclosure_bodies)}")

design.plot()
Number of enclosure bodies created: 1

Create a sphere enclosure around bodies#

create_sphere_enclosure() generates a spherical enclosure around the given bodies. The radial_distance parameter sets the clearance between the bodies and the sphere surface.

[10]:
design = modeler.open_file(file_paths["box_with_round"])
bodies = [design.bodies[0]]

enclosure_options = EnclosureOptions()
enclosure_bodies = modeler.prepare_tools.create_sphere_enclosure(bodies, 0.1, enclosure_options)
print(f"Number of enclosure bodies created: {len(enclosure_bodies)}")

design.plot()
Number of enclosure bodies created: 1

Detect sweepable bodies#

detect_sweepable_bodies() checks whether each body in a list can be swept (that is, whether it has a consistent cross-section that can be extruded along a path). Set get_source_target_faces=True to also retrieve the source and target faces used for the sweep.

[11]:
design = modeler.open_file(file_paths["different_shapes"])
bodies = design.bodies
print(f"Number of bodies: {len(bodies)}")

# Check sweepability without retrieving source/target faces
results = modeler.prepare_tools.detect_sweepable_bodies(bodies)
for i, (is_sweepable, faces) in enumerate(results):
    print(f"  Body '{bodies[i].name}': sweepable={is_sweepable}")
Number of bodies: 6
  Body 'Elliptical': sweepable=True
  Body 'Hexagon': sweepable=True
  Body 'PartialCone': sweepable=False
  Body 'Sphere': sweepable=False
  Body 'SquareSurface': sweepable=False
  Body 'EllipticalSuface': sweepable=False
[12]:
# Check sweepability and retrieve source/target faces for sweepable bodies
results_with_faces = modeler.prepare_tools.detect_sweepable_bodies(
    bodies, get_source_target_faces=True
)
for i, (is_sweepable, faces) in enumerate(results_with_faces):
    face_info = f", source/target faces={len(faces)}" if is_sweepable else ""
    print(f"  Body '{bodies[i].name}': sweepable={is_sweepable}{face_info}")
  Body 'Elliptical': sweepable=True, source/target faces=2
  Body 'Hexagon': sweepable=True, source/target faces=2
  Body 'PartialCone': sweepable=False
  Body 'Sphere': sweepable=False
  Body 'SquareSurface': sweepable=False
  Body 'EllipticalSuface': sweepable=False

Close the modeler#

Close the modeler to free up resources and release the connection.

[13]:
modeler.close()

Download this example

Download this example as a Jupyter Notebook or as a Python script.