Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ def intersphinx_pyansys_geometry(switcher_version: str):
"examples/04_applied/02_naca_fluent": "_static/thumbnails/naca_fluent.png",
"examples/04_applied/03_ahmed_body_fluent": "_static/thumbnails/ahmed_body.png",
"examples/05_tools/repair_tools": "_static/thumbnails/repair_tools.png",
"examples/05_tools/prepare_tools": "_static/thumbnails/prepare_tools.png",
"examples/99_misc/template": "_static/thumbnails/101_getting_started.png",
}
nbsphinx_epilog = """
Expand Down
4 changes: 3 additions & 1 deletion doc/source/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ Tools examples
--------------

These examples demonstrate how to use the tools available in PyAnsys Geometry,
such as the ``RepairTools`` class for detecting and fixing geometry issues.
such as the ``RepairTools`` class for detecting and fixing geometry issues and
the ``PrepareTools`` class for preparing geometry for simulation.

.. nbgallery::
:caption: Tools examples

examples/05_tools/repair_tools.mystnb
examples/05_tools/prepare_tools.mystnb

Miscellaneous examples
----------------------
Expand Down
287 changes: 287 additions & 0 deletions doc/source/examples/05_tools/prepare_tools.mystnb
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
---
jupytext:
text_representation:
extension: .mystnb
format_name: myst
format_version: 0.13
jupytext_version: 1.19.1
kernelspec:
display_name: Python 3 (ipykernel)
language: python
name: python3
---

# 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.

```{code-cell} ipython3
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.

```{code-cell} ipython3
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()}
```

## Initialize the modeler

```{code-cell} ipython3
modeler = launch_modeler()
print(modeler)
```

## 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.

```{code-cell} ipython3
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()
```

## 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.

```{code-cell} ipython3
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()
```

## 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.

```{code-cell} ipython3
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()
```

## 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.

```{code-cell} ipython3
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()
```

## 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.

```{code-cell} ipython3
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)")
```

## 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.

```{code-cell} ipython3
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()
```

## 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.

```{code-cell} ipython3
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()
```

## 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.

```{code-cell} ipython3
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}")
```

```{code-cell} ipython3
# 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}")
```

## Close the modeler

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

```{code-cell} ipython3
modeler.close()
```
Loading