Topology optimization of a simple cantilever beam#

This example demonstrates the structural topology optimization of a simple cantilever beam. The structural analysis is performed with basic constraints and load, which is then transferred to the topology optimization.

Import the necessary libraries#

from pathlib import Path
from typing import TYPE_CHECKING

from matplotlib import pyplot as plt
import matplotlib.animation as animation
from PIL import Image

from ansys.mechanical.core import App
from ansys.mechanical.core.examples import delete_downloads, download_file

if TYPE_CHECKING:
    pass

Initialize the embedded application#

app = App(globals=globals())
print(app)
Ansys Mechanical [Ansys Mechanical Enterprise]
Product Version:261
Software build date: 01/30/2026 18:48:37

Setup the output path and camera#

# Set the path for the output files (images, gifs, mechdat)
output_path = Path.cwd() / "out"

# Set the camera orientation to the front view
graphics = app.Graphics
camera = graphics.Camera

# Set the camera orientation to the front view
camera.SetSpecificViewOrientation(ViewOrientationType.Front)

Import the structural analysis model#

# Download ``.mechdat`` file
structural_mechdat_file = download_file("cantilever.mechdat", "pymechanical", "embedding")

# Open the project file
app.open(structural_mechdat_file)

# Define the model
model = app.Model

# Get the structural analysis object
struct = model.Analyses[0]


# Get the structural analysis object's solution and solve it
struct_sln = struct.Solution
struct_sln.Solve(True)

Display the structural analysis results#

Activate the total deformation result and display the image

struct_sln.Children[1].Activate()
image_path = output_path / "total_deformation.png"
camera.SetFit()
app.helpers.export_image(struct_sln.Children[1], image_path)
app.helpers.display_image(image_path)
topology optimization cantilever beam

Activate the equivalent stress result and display the image

struct_sln.Children[2].Activate()
image_path = output_path / "equivalent_stress.png"
camera.SetFit()
app.helpers.export_image(struct_sln.Children[2], image_path)
app.helpers.display_image(image_path)
topology optimization cantilever beam

Topology optimization#

# Set the MKS unit system
app.ExtAPI.Application.ActiveUnitSystem = MechanicalUnitSystem.StandardMKS

# Add the topology optimization analysis to the model and transfer data from the
# structural analysis
topology_optimization = model.AddTopologyOptimizationAnalysis()
topology_optimization.TransferDataFrom(struct)

# Get the optimization region from the data model
optimization_region = DataModel.GetObjectsByType(DataModelObjectCategory.OptimizationRegion)[0]
# Set the optimization region's boundary condition to all loads and supports
optimization_region.BoundaryCondition = BoundaryConditionType.AllLoadsAndSupports
# Set the optimization region's optimization type to topology density
optimization_region.OptimizationType = OptimizationType.TopologyDensity


# Delete the mass response constraint from the topology optimization
mass_constraint = topology_optimization.Children[3]
app.DataModel.Remove(mass_constraint)

# Add a volume response constraint to the topology optimization
volume_constraint = topology_optimization.AddVolumeConstraint()

# Add a member size manufacturing constraint to the topology optimization
mem_size_manufacturing_constraint = topology_optimization.AddMemberSizeManufacturingConstraint()
# Set the constraint's minimum to manual and its minimum size to 2.4m
mem_size_manufacturing_constraint.Minimum = ManuMemberSizeControlledType.Manual
mem_size_manufacturing_constraint.MinSize = Quantity("2.4 [m]")

# Activate the topology optimization analysis and display the image
topology_optimization.Activate()
camera.SetFit()
app.helpers.export_image(topology_optimization, output_path / "boundary_conditions.png")
app.helpers.display_image(output_path / "boundary_conditions.png")
topology optimization cantilever beam

Solve the solution#

# Get the topology optimization analysis solution
top_opt_sln = topology_optimization.Solution
# Solve the solution
top_opt_sln.Solve(True)

Show messages#

# Print all messages from Mechanical
app.messages.show()
Severity: Info
DisplayString: For geometric constraint (Mass, Volume, Center of Gravity or Moment of Inertia constraints), it is recommended to use Criterion of the upstream Measure folder (inserted from Model object).
Severity: Info
DisplayString: The requested license was received from the License Manager after 35 seconds.
Severity: Warning
DisplayString: The default mesh size calculations have changed in 18.2. Specifically, the default min size values and/or defeature size values scale dynamically in relation to the element (max face) size. These settings could lead to a significantly different mesh, so the model will be resumed using the previous values for min size and defeature size rather than leaving those values as default.

Display the results#

# Get the topology density result and activate it
top_opt_sln.Children[1].Activate()
topology_density = top_opt_sln.Children[1]

Add smoothing to the stereolithography (STL)

# Add smoothing to the topology density result
topology_density.AddSmoothing()

# Evaluate all results for the topology optimization solution
topology_optimization.Solution.EvaluateAllResults()

# Activate the topology density result after smoothing and display the image
topology_density.Children[0].Activate()
image_path = output_path / "topo_optimized_smooth.png"
camera.SetFit()
app.helpers.export_image(topology_density.Children[0], image_path)
app.helpers.display_image(image_path)
topology optimization cantilever beam

Export the animation

topology_optimized_gif = output_path / "topology_optimized.gif"
app.helpers.export_animation(topology_density, topology_optimized_gif)

Display the topology optimized animation

# Open the GIF file and create an animation
gif = Image.open(topology_optimized_gif)
fig, ax = plt.subplots(figsize=(8, 4))
ax.axis("off")
image = ax.imshow(gif.convert("RGBA"))


# Animation update function
def update_frame(frame):
    """Update the frame for the animation."""
    gif.seek(frame)
    image.set_array(gif.convert("RGBA"))
    return (image,)


# Create and display animation
ani = animation.FuncAnimation(
    fig, update_frame, frames=gif.n_frames, interval=200, blit=True, repeat=True
)

# Show the animation
plt.show()

Review the results

# Print the topology density results
print("Topology Density Results")
print("Minimum Density: ", topology_density.Minimum)
print("Maximum Density: ", topology_density.Maximum)
print("Iteration Number: ", topology_density.IterationNumber)
print("Original Volume: ", topology_density.OriginalVolume.Value)
print("Final Volume: ", topology_density.FinalVolume.Value)
print("Percent Volume of Original: ", topology_density.PercentVolumeOfOriginal)
print("Original Mass: ", topology_density.OriginalMass.Value)
print("Final Mass: ", topology_density.FinalMass.Value)
print("Percent Mass of Original: ", topology_density.PercentMassOfOriginal)
Topology Density Results
Minimum Density:  0.0010000000474974513
Maximum Density:  1.0
Iteration Number:  35
Original Volume:  1000.0000054389238
Final Volume:  522.4924773573875
Percent Volume of Original:  52.24924745155908
Original Mass:  7849999.975463867
Final Mass:  4101565.9057617188
Percent Mass of Original:  52.24924737046705

Display the project tree#

app.print_tree()
├── Project
|  ├── Model
|  |  ├── Geometry Imports (✓)
|  |  |  ├── Geometry Import (✓)
|  |  ├── Geometry (✓)
|  |  |  ├── Surface Body Bodies
|  |  |  |  ├── Surface Body
|  |  ├── Materials (✓)
|  |  |  ├── Structural Steel (✓)
|  |  ├── Coordinate Systems (✓)
|  |  |  ├── Global Coordinate System (✓)
|  |  |  ├── Coordinate System (✓)
|  |  |  ├── Coordinate System 2 (✓)
|  |  |  ├── Coordinate System 3 (✓)
|  |  |  ├── Coordinate System 4 (✓)
|  |  |  ├── Coordinate System 5 (✓)
|  |  |  ├── Coordinate System 6 (✓)
|  |  |  ├── Coordinate System 7 (✓)
|  |  |  ├── Coordinate System 8 (✓)
|  |  ├── Remote Points (✓)
|  |  ├── Mesh (✓)
|  |  |  ├── Face Sizing (✓)
|  |  ├── Named Selections
|  |  |  ├── Selection (✓)
|  |  |  ├── Bottom_Elements (✓)
|  |  |  ├── Top_Elements (✓)
|  |  |  ├── Middle1_Elements (✓)
|  |  |  ├── Middle2_Elements (✓)
|  |  |  ├── Left1_Elements (✓)
|  |  |  ├── Left2_Elements (✓)
|  |  |  ├── Right1_Elements (✓)
|  |  |  ├── Right2_Elements (✓)
|  |  |  ├── Optimized_Shape (✓)
|  |  |  ├── Outside_Optimized_Shape (✓)
|  |  |  ├── Selection 2 (✓)
|  |  |  ├── Selection 3 (✓)
|  |  |  ├── Selection 4 (✓)
|  |  |  ├── Selection 5 (✓)
|  |  ├── Static Structural (✓)
|  |  |  ├── Analysis Settings (✓)
|  |  |  ├── Fixed Support (✓)
|  |  |  ├── Nodal Force (✓)
|  |  |  ├── Solution (✓)
|  |  |  |  ├── Solution Information (✓)
|  |  |  |  ├── Total Deformation (✓)
|  |  |  |  ├── Equivalent Stress (✓)
|  |  ├── Structural Optimization (✓)
|  |  |  ├── Analysis Settings (✓)
|  |  |  ├── Optimization Region (✓)
|  |  |  ├── Objective (✓)
|  |  |  ├── Response Constraint (✓)
|  |  |  ├── Manufacturing Constraint (✓)
|  |  |  ├── Solution (✓)
|  |  |  |  ├── Solution Information (✓)
|  |  |  |  |  ├── Topology Density Tracker (✓)
|  |  |  |  ├── Topology Density (✓)
|  |  |  |  |  ├── Smoothing (✓)

Clean up the project#

# Save the project file
mechdat_file = output_path / "cantilever_beam_topology_optimization.mechdat"
app.save_as(str(mechdat_file), overwrite=True)

# Close the app
app.close()

# Delete the example files
delete_downloads()
True

Total running time of the script: (0 minutes 44.466 seconds)

Gallery generated by Sphinx-Gallery