Hex mesh from finite difference voxels

Hi all,

I’m working on generating a hexahedral mesh in Cubit from voxelized data produced by a Finite Difference Time Domain (FDTD) simulation.

Each voxel is axis-aligned and defined by its center coordinates and dimensions. The goal is to create one HEX8 element per voxel, ideally resulting in a merged and compact mesh that can be exported to Exodus format.

I’ve written a minimal working example (MWE) that reads in the voxel data and:

  • Creates one brick per voxel
  • Moves each brick to the correct location
  • Meshes each brick as a single hexahedral element

This works for small cases, but performance is a major concern. For example:

  • Meshing 70,000 voxels takes approximately 30 minutes
  • My production use cases involve between 10 million and 100 million voxels

To avoid further slowdown, I’m currently skipping the geometry merge step, though I would prefer to merge the result to reduce file size. I haven’t yet used Cubit’s parallel features, but this seems like a good use case for them. I also have significant computational resources to bring to bear for this task.

Any suggestions or ideas to improve performance are very welcome.

#Import packages
import numpy as np
import cubit
from timeit import time

#Set file names
voxel_data_file = 'voxel_data.txt.gz'
exo_file = 'voxel_mesh.exo'

#Initialize cubit
cubit.init(['cubit', '-nojournal', '-nographics'])
cubit.reset()

#Some default settings for improved performance
cubit.cmd('echo off')
cubit.cmd('undo off')
cubit.cmd('warning off')
cubit.cmd('info off')
cubit.cmd('set default autosize off')

#Load voxel centers
xcm, ycm, zcm, Lx, Ly, Lz = np.loadtxt(voxel_data_file)
nvoxel = int(xcm.shape[0])
vlength = len(str(nvoxel))

#Create bricks for each voxel
t0 = time.time()
for k in range(nvoxel):
    cubit.cmd(f'brick x {Lx[k]} y {Ly[k]} z {Lz[k]}')
    voxel_id = cubit.get_last_id('volume')
    cubit.cmd(f'volume {voxel_id} move x {xcm[k]} y {ycm[k]} z {zcm[k]}')
    cubit.cmd(f'block 1 vol {voxel_id}')
    cubit.cmd(f'vol {voxel_id} interval 1')
    cubit.cmd(f'vol {voxel_id} scheme map')
    cubit.cmd(f'mesh vol {voxel_id}')
    print(f'Voxel {k+1:0{vlength}d} of {nvoxel} created at time {time.time()-t0:.2f}', flush=True)
assert cubit.get_element_count() == nvoxel, "Something is off with the mesh."

#Add to block
cubit.cmd('block 1 element HEX8')

#Save
cubit.cmd(f'export genesis "{exo_file}" overwrite')
print(f'\nVoxel mesh created in {time.time()-t0:.2f} seconds.', flush=True)

voxel_data.txt.gz (49.9 KB)

Hi @tloudon,
should all your voxels have the same dimension?

When i look at Lx, Ly, Lz i would say a constant 4 would be good unless you say the dimensions will vary depending on the voxel data.

In general the voxels will be nonuniform even if they are (mostly) uniform for this simple example problem. Thanks.