Render atomic structures

Mdapy can use the Tachyon rendering algorithm developed by John Stone (http://www.photonlimited.com/~johns/tachyon/) to visualize atomic structures, with support for OVITO-compatible camera settings. This feature was introduced in mdapy 1.0.4 and may undergo API changes in future versions.

A key advantage is that it supports both CPU and GPU parallelization, providing excellent computational performance (an order of magnitude faster), especially for large-scale molecular dynamics rendering tasks.

[1]:
import mdapy as mp
import numpy as np
import polars as pl
from time import time
from mdapy.render import TachyonRender, CameraParams, load_image
mp.__version__
[1]:
'1.0.4a1'
[2]:
import ovito
from ovito.vis import Viewport, TachyonRenderer
ovito.version
[2]:
(3, 14, 1)

Build a model with 32000 atoms

[3]:
model = mp.build_hea(
        ["Cr", "Co", "Ni", "Fe", "Mn"],
        [0.2] * 5,
        "fcc",
        3.6,
        nx=20,
        ny=20,
        nz=20,
        random_seed=1,
    )
model.write_xyz('model.xyz')
model.N
[3]:
32000

Camera and rendering parameters

[4]:
cam_pos = (-109.762, -70.4962, 94.5735)
cam_dir = (0.759042, 0.558131, -0.335178)
cam_up = (0.270034, 0.198559, 0.942155)
cam_fov = np.radians(35.0)
aa_samples = 20
ao_samples = 20
direct_light_intensity = 1.2
ao_brightness = 1.2
width, height = 3000, 3000 # we use high resolution here to test performance
background = (1, 1, 1)

Ovito setting

[5]:
vp = Viewport()
vp.type = Viewport.Type.Perspective
vp.camera_pos = cam_pos
vp.camera_dir = cam_dir
vp.camera_up = cam_up
vp.fov = cam_fov
[6]:
tachyon = TachyonRenderer(direct_light_intensity=direct_light_intensity,
                          antialiasing_samples=aa_samples,
                          ambient_occlusion_samples=ao_samples,
                          ambient_occlusion_brightness=ao_brightness)
[7]:
pipline = ovito.io.import_file('model.xyz')
pipline.add_to_scene()
[8]:
time_list = []
start = time()
vp.render_image(size=(width,height), filename="ovito.png", background=background, renderer=tachyon);
time_list.append(time()-start)
[9]:
pipline.remove_from_scene()

Mdapy setting

[10]:
cam = CameraParams(
    field_of_view=cam_fov,
    position=cam_pos,
    direction=cam_dir,
    up=cam_up
)

CPU rendering

[11]:
tachyon_mda_cpu = TachyonRender(aa_samples=aa_samples, backend='cpu',
                            ao_samples=ao_samples,
                            direct_light_intensity=direct_light_intensity,
                            ao_brightness=ao_brightness,
                            background=background)
[12]:
start = time()
tachyon_mda_cpu.render_system(model, width=width,
                            height=height, camera=cam,
                            box_color=(0, 0, 0), box_edge_radius=0.2,
                            output_figure='mda_cpu.png')
time_list.append(time()-start)

GPU rendering

[13]:
tachyon_mda_gpu = TachyonRender(aa_samples=aa_samples, backend='gpu',
                            ao_samples=ao_samples,
                            direct_light_intensity=direct_light_intensity,
                            ao_brightness=ao_brightness,
                            background=background)
[14]:
start = time()
tachyon_mda_gpu.render_system(model, width=width,
                            height=height, camera=cam,
                            box_color=(0, 0, 0), box_edge_radius=0.2,
                           output_figure='mda_gpu.png')
time_list.append(time()-start)

Save transparent background figure

[15]:
tachyon_mda_gpu.render_system(model, width=width,
                            height=height, camera=cam,
                            box_color=(0, 0, 0), box_edge_radius=0.2,
                           output_figure='mda_gpu_transparent.png', transparent=True)

Compare results

The rendering results are generally similar, while the mdapy cpu version is a little bit darker than others.

[16]:
fig, ax = mp.set_figure(ncol=3, figsize=(16, 6), figdpi=200)

for i, name in enumerate(['ovito.png', 'mda_cpu.png', 'mda_gpu.png']):
    ax[i].imshow(load_image(name))
    ax[i].axis("off")
    ax[i].set_title(name.split('.')[0])
../_images/gettingstarted_render_structure_26_0.png

Mdapy rendering is much faster!

[17]:
fig, ax = mp.set_figure()
x = range(3)
ax.bar(x, time_list)
ax.set_xticks(x)
ax.set_xticklabels(['Ovito', 'mda-cpu', 'mda-gpu'])
ax.set_ylabel('Time (s)')
[17]:
Text(0, 0.5, 'Time (s)')
../_images/gettingstarted_render_structure_28_1.png
[18]:
import os
from glob import glob
os.remove('model.xyz')
for i in glob('*png'):
    os.remove(i)