3D Rendering¶
This section demonstrates how to render MICRESS field data in 3D using MicPy.
While Field.plot() is suitable for quick 2D inspection, more advanced visualization, such as slicing, isosurfaces, and custom camera views, can be achieved by converting the data to VTK ImageData (vtkImageData) and using a VTK-based library like PyVista.
Minimal Example¶
from micpy.bin import read
import pyvista as pv
# Read a field (e.g. the last time step of a phase fraction)
field = read("A019_01_MgAl6_D_Dendritic_3D_TQ.frac", -1)
# Convert the field to VTK ImageData object
vti = field.as_vti()
# Render the VTK ImageData using PyVista
pv.plot(vti, cmap="micpy")

Extracting Slices¶
# Wrap the VTK ImageData object in a PyVista DataImage object
img = pv.wrap(vti)
# Create a slice along the x-axis
slice_x = img.slice(normal="x")
# Render the slice with the MicPy colormap
pv.plot(slice_x, cmap="micpy")

Visualizing an Isosurface¶
# Extract point data instead of cell data
vti_points = field.as_vti(point_data=True)
# Wrap the VTK ImageData object in a PyVista DataImage object
img = pv.wrap(vti_points)
# Create an isosurface at 0.5 to visualize the phase boundaries
surface = img.contour(isosurfaces=[0.5])
# Render the isosurface with a grayscale colormap and no scalar bar
pv.plot(surface, cmap="Greys", show_scalar_bar=False)

Customizing the View¶
# Create a PyVista plotter and add the isosurface
plotter = pv.Plotter()
plotter.add_mesh(surface, cmap="Greys", show_scalar_bar=False)
# Set the camera position to view along the xy-plane and zoom in by a factor of 1.33
plotter.camera_position = "xy"
plotter.camera.zoom(1.33)
# Optional: Adjust the camera angles (azimuth, elevation, roll)
# plotter.camera.azimuth = 180
# plotter.camera.elevation = 90
# plotter.camera.roll = 45
# Enable axes and show the plot
plotter.show_axes()
plotter.show()

Adding Bounds¶
# Create a PyVista plotter and add the isosurface
plotter = pv.Plotter()
plotter.add_mesh(surface, cmap="Greys", show_scalar_bar=False)
# Set the camera position to view along the xz-plane
plotter.camera_position = "xz"
# Add bounds with tick labels
plotter.show_bounds(
grid="back",
location="outer",
ticks="outside",
fmt="%.3g", # Global notation with 3 significant digits
)
# Show the plot
plotter.show()

Creating an Animation¶
from micpy.bin import read
import pyvista as pv
# Define the filename and time steps to visualize
filename = "A019_01_MgAl6_D_Dendritic_3D_TQ.frac"
steps = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
# Extract bounds from the last time step for consistent axes across frames
bounds = (
pv.wrap(read(filename, steps[-1]).as_vti(point_data=True))
.contour(isosurfaces=[0.5])
.bounds
)
# Create a PyVista plotter for off-screen rendering
pl = pv.Plotter(off_screen=True, window_size=(608, 608))
# Add bounds to the plotter
pl.show_bounds(
bounds=bounds,
grid="back",
location="outer",
ticks="outside",
fmt="%.3g"
)
# Open a movie file to write the animation frames
pl.open_movie(f"animation.mp4", framerate=3, quality=9)
# Define a variable to hold the mesh object to be displayed per time step
mesh = None
# Loop through the specified time steps
for step in steps:
# Read the field data for the current time step and extract the isosurface
field = read(filename, step)
surface = pv.wrap(field.as_vti(point_data=True)).contour(isosurfaces=[0.5])
# Add the isosurface as a mesh to the plotter
mesh = pl.add_mesh(
surface,
scalars="values",
cmap="Greys",
show_scalar_bar=False,
)
# Set the camera position to view the xz-plane
pl.camera_position = "xz"
# Render the current frame and write it to the movie file
pl.render()
pl.write_frame()
# Remove the mesh for the next iteration
pl.remove_actor(mesh)
# Finalize the movie file and close the plotter
pl.close()

