Freeze curve mesh and then perform surface mesh

Hello all,

I’m a Ph.D. student in Germany. We have recently bought 2 licenses for Cubit. So far it has been very good. However, I have a problem.

I have a cube with several spherical particles embedded in it. When I mesh the cube, I would like the same number of element on the edges. So I selected all the 12 curves/edges of the cube, set the mesh size and meshed the curves.

However, when I mesh a surface near the edge, the mesh on the curve is not respected. How can I solve this problem.

I have also sized the mesh of the surface to be equal to the size of the edge mesh size, yet it does not work.

I’m uploading a picture to show the problem as well as the geometry. Kindly help.

output.cub5 (1.2 MB)

Hello @vasukolli23,

before we start meshing this.

Do you intend to mesh the volumes in a way that the opposing sides of the cube should match?

Can you describe how the final mesh should look like?

@Norbert_Hofbauer
Exactly, that is what I want. A periodic mesh, where I can apply my periodic boundary conditions script.

Do you maybe have created the geometry with cubit? If yes, have you got the journal file?
If no, could you cut out the cube at another position.

There are some bad shaped volumes and surfaces on the sides like this one

They will definitely cause issues and produce near zero element quality.

It would be good to avoid narrow surfaces and small flat volumes directly on the sides of the cube.

Do you need a hex or a tet mesh for your solver?

@Norbert_Hofbauer
I cut the cube in another position, so that there are no bad shaped volumes and surfaces on the side. The file is attached below.

I prefer a tet mesh with quadratic elements (tetra10). Thank you in advance.
output.cub5 (831.1 KB)

If you were looking for a hex mesh then the sculpt functionaliy would be an option. There is a periodic option we could use for this.

But a tet mesh is also possible. We will make use of the python interface to get it done.

First thing is, we need to ensure that the opposing sides have the same topology. Currently they share surfaces of the same size, but the topology doesn’t match.

For this we will copy and translate the cube and imprint the opposing sides.

grafik

cubit.cmd('reset')
cubit.cmd('open "C:/Users/user/Downloads/output.cub5"')
cubit.cmd('delete mesh')
cubit.cmd('reset volume all')
cubit.cmd('display')
cubit.cmd('compress')

# first imprinting to create same topology on sides

volume_ids = cubit.parse_cubit_list("volume","all")
volume_orig = ""
for vid in volume_ids:
 cubit.cmd(f"volume {vid} copy move x 15")
 volume_orig = volume_orig + str(vid) + " " 

cubit.cmd(f"imprint volume all")

volume_copy_ids = cubit.parse_cubit_list("volume","all except " + volume_orig)

volumes = ""
for vid in volume_copy_ids:
 volumes = volumes + str(vid) + " " 
cubit.cmd(f"volume {volumes} move x -30")
cubit.cmd(f"imprint volume all")

volumes = ""
for vid in volume_copy_ids:
 volumes = volumes + str(vid) + " " 
cubit.cmd(f"volume {volumes} move x 15 y 15")
cubit.cmd(f"imprint volume all")

volumes = ""
for vid in volume_copy_ids:
 volumes = volumes + str(vid) + " " 
cubit.cmd(f"volume {volumes} move y -30")
cubit.cmd(f"imprint volume all")

volumes = ""
for vid in volume_copy_ids:
 volumes = volumes + str(vid) + " " 
cubit.cmd(f"volume {volumes} move y 15 z 15")
cubit.cmd(f"imprint volume all")

volumes = ""
for vid in volume_copy_ids:
 volumes = volumes + str(vid) + " " 
cubit.cmd(f"volume {volumes} move z -30")
cubit.cmd(f"imprint volume all")

volumes = ""
for vid in volume_copy_ids:
 volumes = volumes + str(vid) + " " 
cubit.cmd(f"delete volume {volumes}")

After that, we can imprint and merge the volumes and set the mesh size for the edges from the cube.

# imprint/merge and set equal mesh size on cube edges
cubit.cmd('imprint volume all')
cubit.cmd('merge volume all')
cubit.cmd('volume all scheme tetmesh')
cubit.cmd('volume all size 0.5')

curve_size = 0.5
cubit.cmd(f"curve all with x_coord=0 and y_coord=0 size {curve_size}")
cubit.cmd(f"curve all with x_coord=0 and y_coord=15 size {curve_size}")
cubit.cmd(f"curve all with x_coord=15 and y_coord=0 size {curve_size}")
cubit.cmd(f"curve all with x_coord=15 and y_coord=15 size {curve_size}")
cubit.cmd(f"curve all with y_coord=0 and z_coord=0 size {curve_size}")
cubit.cmd(f"curve all with y_coord=0 and z_coord=15 size {curve_size}")
cubit.cmd(f"curve all with y_coord=15 and z_coord=0 size {curve_size}")
cubit.cmd(f"curve all with y_coord=15 and z_coord=15 size {curve_size}")
cubit.cmd(f"curve all with x_coord=0 and z_coord=0 size {curve_size}")
cubit.cmd(f"curve all with x_coord=0 and z_coord=15 size {curve_size}")
cubit.cmd(f"curve all with x_coord=15 and z_coord=0 size {curve_size}")
cubit.cmd(f"curve all with x_coord=15 and z_coord=15 size {curve_size}")

cubit.cmd(f"curve all with x_coord=0 and y_coord=0 scheme equal")
cubit.cmd(f"curve all with x_coord=0 and y_coord=15 scheme equal")
cubit.cmd(f"curve all with x_coord=15 and y_coord=0 scheme equal")
cubit.cmd(f"curve all with x_coord=15 and y_coord=15 scheme equal")
cubit.cmd(f"curve all with y_coord=0 and z_coord=0 scheme equal")
cubit.cmd(f"curve all with y_coord=0 and z_coord=15 scheme equal")
cubit.cmd(f"curve all with y_coord=15 and z_coord=0 scheme equal")
cubit.cmd(f"curve all with y_coord=15 and z_coord=15 scheme equal")
cubit.cmd(f"curve all with x_coord=0 and z_coord=0 scheme equal")
cubit.cmd(f"curve all with x_coord=0 and z_coord=15 scheme equal")
cubit.cmd(f"curve all with x_coord=15 and z_coord=0 scheme equal")
cubit.cmd(f"curve all with x_coord=15 and z_coord=15 scheme equal") 

Now we can mesh one side and copy move the mesh on the opposite side and merge them. Afterwards we mesh the volumes.

#mesh sides and imprint/merge the mesh on the opposite side

surface_ids = cubit.parse_cubit_list("surface","all with x_coord==0")
for sid in surface_ids:
 cubit.cmd(f"mesh surface {sid}")
 cubit.cmd(f"surface {sid} copy move x 15")

cubit.cmd(f"merge surface all with x_coord=15")
cubit.cmd(f"delete body all with is_sheetbody")

surface_ids = cubit.parse_cubit_list("surface","all with y_coord==0")
for sid in surface_ids:
 cubit.cmd(f"mesh surface {sid}")
 cubit.cmd(f"surface {sid} copy move y 15")

cubit.cmd(f"merge surface all with y_coord=15")
cubit.cmd(f"delete body all with is_sheetbody")

surface_ids = cubit.parse_cubit_list("surface","all with z_coord==0")
for sid in surface_ids:
 cubit.cmd(f"mesh surface {sid}")
 cubit.cmd(f"surface {sid} copy move z 15")

cubit.cmd(f"merge surface all with z_coord=15")
cubit.cmd(f"delete body all with is_sheetbody")

cubit.cmd('mesh vol all')

Last thing is to create blocks and set the element type.

# creating blocks
volume_ids = cubit.parse_cubit_list("volume","all")
i=0
for vid in volume_ids:
 i=i+1
 cubit.cmd(f"block {i} add volume {vid}")
 cubit.cmd(f"block {i} element type tetra10")

Here is the whole script. You can open and play it with the journal editor.

periodic_cube.py (4.4 KB)

Thank you @Norbert_Hofbauer

It works well. I appreciate your help. :slight_smile:

@Norbert_Hofbauer
I had the opportunity to think about the problem over christmas vacation again. I have a few more questions.

Due to the large number of the elements involved, I believe it is not a smart idea to have equal element size. It is better to have mesh element sizes in proportion to the size of the spherical particles (curve mesh scheme curvature). This way I have lesser mesh elements to simulate and hence requires lesser time.

This brings me back to the same question I asked in the beginning. Why does surface mesh override the curve mesh ? How can I freeze the curve mesh and make surface mesh respect it ?

Hello again @Norbert_Hofbauer

After doing a little bit of reading the documentation and playing around. I could achieve what I wanted. The final RVE looks as follows with reasonable element size based on radius size.

The code I used for meshing is here.

# meshing edges
edge_size = 0.5
for dir in ["xy", "yz", "xz"]:
    for i, j in [[0, 0], [0, rve_size], [rve_size, 0], [rve_size, rve_size]]:
        cubit.cmd(f"curve all with {dir[0]}_coord={i} and {dir[1]}_coord={j} size {edge_size}")
        cubit.cmd(f"curve all with {dir[0]}_coord={i} and {dir[1]}_coord={j} scheme equal")
        cubit.cmd(f'mesh curve all with {dir[0]}_coord={i} and {dir[1]}_coord={j}')

# mesh faces and imprint/merge the mesh on the opposite side
cubit.cmd('set maximum arc_span 22.5')
mesh_factor = 5
for dir in ["x", "y", "z"]:
    # meshing
    surface_ids = cubit.parse_cubit_list("surface", f"with {dir}_coord==0")
    cubit.cmd(f"surface {' '.join(map(str, surface_ids))} size auto factor {mesh_factor} propagate")
    cubit.cmd(f"surface {' '.join(map(str, surface_ids))} scheme trimesh")
    cubit.cmd(f"mesh surface {' '.join(map(str, surface_ids))}")
    for sid in surface_ids:
        cubit.cmd(f"surface {sid} copy move {dir} {rve_size}")

    # imprinting
    cubit.cmd(f"merge surface all with {dir}_coord={rve_size}")
    cubit.cmd(f"delete body all with is_sheetbody")

# meshing other surfaces
unmeshed_surface_ids = cubit.parse_cubit_list('surface', 'with not is_meshed')
cubit.cmd(f"surface {' '.join(map(str, unmeshed_surface_ids))} size auto factor {mesh_factor} propagate")
cubit.cmd(f"surface {' '.join(map(str, unmeshed_surface_ids))} scheme trimesh")
cubit.cmd(f"mesh surface {' '.join(map(str, unmeshed_surface_ids))}")

# volume mesh
cubit.cmd(f"volume all scheme tetmesh")
cubit.cmd(f"set tetmesher hpc on")
cubit.cmd("mesh volume all")

However, it seems to generate some elements whose volume is very less or equal to 0. How to fix the quality of these elements ? Any suggestions ?

Thanks in advance.

Hi @vasukolli23,
could you please share the whole script that you are using so that i can take a look at it. Is the .cub5 from above still the basis geometry that you are importing?

Hi @Norbert_Hofbauer

The .cub5 is generated by some random particle placement. So the basis is the same but the random particle placement makes the cub5 look different.

The whole script is here and “particles.xlsx” is in the attachment

import sys

sys.path.append("C:\Program Files\Coreform Cubit 2022.4\\bin")
import cubit
import numpy as np
import pandas as pd
import pathlib as pl
import logging

logging.basicConfig(level=logging.DEBUG, filename="console.log", filemode="w", format="%(asctime)s - [%(levelname)s] - %(message)s")
cubit.init(["cubit", "-nojournal"])
cubit.reset()

# reading the particle position file
particles_fp = pl.Path("particles.xlsx")
particles_df = pd.concat([pd.read_excel(particles_fp, sheet_name="real"), pd.read_excel(particles_fp, sheet_name="ghost")])
particles_df = particles_df.reset_index()

# creating spheres
for id, particle in particles_df.iterrows():
    if particle.x == 200.0: #  please ignore why I'm doing this
        continue
    cubit.cmd(f"create sphere radius {particle['radius']}")
    cubit.cmd(f"move volume {cubit.get_entities('volume')[-1]} location {particle.x} {particle.y} {particle.z}")

# creating epoxy
rve_size = 18.0
cubit.cmd(f"create brick x {rve_size} y {rve_size} z {rve_size}")
cubit.cmd(f"move volume {cubit.get_last_id('volume')} x {rve_size / 2.} y {rve_size / 2.} z {rve_size / 2.} include_merged")

# boolean operations
# intersection - to remove ghost spheres parts outside the cube
cubit.cmd("intersect volume all")
cubit.cmd(f"create brick x {rve_size} y {rve_size} z {rve_size}")
cubit.cmd(f"move volume {cubit.get_last_id('volume')} x {rve_size / 2.} y {rve_size / 2.} z {rve_size / 2.} include_merged")

# remove overlap and merge
for i in cubit.get_entities("volume")[:-1]:
    cubit.cmd(f"remove overlap volume {cubit.get_entities('volume')[-1]} {i} modify volume {cubit.get_entities('volume')[-1]}")
cubit.cmd("merge volume all")

# creating blocks
cubit.cmd(f"block 1 add volume {cubit.get_entities('volume')[-1]}")
cubit.cmd(f"block 1 name Epoxy")
for i in cubit.get_entities("volume")[:-1]:
    cubit.cmd(f"block 2 add volume {i}")
cubit.cmd("block 2 name xSP")
cubit.cmd("block all element type tetra10")

# creating materials
cubit.cmd('create material "Epoxy" property_group "CUBIT-ABAQUS" id 1')
cubit.cmd('modify material "Epoxy" scalar_properties "MODULUS" 561 "POISSON" 0.3 "YIELD_STRENGTH" 17.17')
cubit.cmd('block 1 material "Epoxy"')
cubit.cmd('create material "xSP" property_group "CUBIT-ABAQUS" id 2')
cubit.cmd('modify material "xSP" scalar_properties "MODULUS" 20e3 "POISSON" 0.3 "YIELD_STRENGTH" 800.0')
cubit.cmd('block 2 material "xSP"')

# --------------------------- Meshing ----------------------------- #
# imprinting volumes so that we have same curves and vertices on opposite side
real_vol_limit = cubit.get_last_id("volume")
displacements = [[rve_size, 0, 0], [-2 * rve_size, 0, 0], [rve_size, rve_size, 0], [0, -2.0 * rve_size, 0], [0, rve_size, rve_size], [0, 0, -2.0 * rve_size]]
for i, displacement in enumerate(displacements):
    if i == 0:
        for vid in cubit.get_entities("volume"):
            cubit.cmd(f"volume {vid} copy move x {displacement[0]} y {displacement[1]} z {displacement[2]}")
        cubit.cmd("imprint volume all")
    else:
        for vid in range(real_vol_limit, cubit.get_last_id("volume") + 1):
            cubit.cmd(f"volume {vid} move x {displacement[0]} y {displacement[1]} z {displacement[2]}")
        cubit.cmd("imprint volume all")

# deleting extra volumes
cubit.cmd(f"delete volume {' '.join(map(str, range(real_vol_limit + 1, cubit.get_last_id('volume') + 1)))}")

# imprint/merge
cubit.cmd("imprint volume all")
cubit.cmd("merge volume all")

# meshing edges
edge_size = 0.5
for dir in ["xy", "yz", "xz"]:
    for i, j in [[0, 0], [0, rve_size], [rve_size, 0], [rve_size, rve_size]]:
        cubit.cmd(f"curve all with {dir[0]}_coord={i} and {dir[1]}_coord={j} size {edge_size}")
        cubit.cmd(f"curve all with {dir[0]}_coord={i} and {dir[1]}_coord={j} scheme equal")
        cubit.cmd(f'mesh curve all with {dir[0]}_coord={i} and {dir[1]}_coord={j}')

# mesh faces and imprint/merge the mesh on the opposite side
cubit.cmd('set maximum arc_span 15')
mesh_factor = 6
for dir in ["x", "y", "z"]:
    # meshing
    surface_ids = cubit.parse_cubit_list("surface", f"with {dir}_coord==0")
    cubit.cmd(f"surface {' '.join(map(str, surface_ids))} size auto factor {mesh_factor} propagate")
    cubit.cmd(f"surface {' '.join(map(str, surface_ids))} scheme trimesh")
    cubit.cmd(f"mesh surface {' '.join(map(str, surface_ids))}")
    for sid in surface_ids:
        cubit.cmd(f"surface {sid} copy move {dir} {rve_size}")

    # imprinting
    cubit.cmd(f"merge surface all with {dir}_coord={rve_size}")
    cubit.cmd(f"delete body all with is_sheetbody")

# meshing other surfaces
unmeshed_surface_ids = cubit.parse_cubit_list('surface', 'with not is_meshed')
cubit.cmd(f"surface {' '.join(map(str, unmeshed_surface_ids))} size auto factor {mesh_factor} propagate")
cubit.cmd(f"surface {' '.join(map(str, unmeshed_surface_ids))} scheme trimesh")
cubit.cmd(f"mesh surface {' '.join(map(str, unmeshed_surface_ids))}")

# volume mesh
cubit.cmd(f"volume all scheme tetmesh")
cubit.cmd(f"set tetmesher hpc on")
cubit.cmd("mesh volume all")

# renumbering nodes for periodic BC script success
cubit.cmd(f"renumber node all in Volume all start_id 1 uniqueids")
cubit.cmd(f"renumber node all in Volume all start_id 1 uniqueids")

# export cub5 data
cubit.cmd('save cub5 "M:\HL2\RVE_generation\cubit\perfect_bonding\geometry_pb.cub5" overwrite journal')

# export abaqus input file
cubit.cmd('export abaqus "M:\HL2\RVE_generation\cubit\perfect_bonding\geometry_pb.inp" overwrite everything')

particles.zip (18.8 KB)

The .cub5 file is in the link below as it is too large to attach.

I would like my periodic mesh without zero (or really small volume) elements. In case you could also check and correct other quality aspects like distortion, then the simulations run smoother. But it is secondary, 0 volume elements elimination is primary.

I really appreciate your help. Thank you.

The overall quality is good, except for the narrow spaces. You should reposition some of the spheres a bit. Some are nearly touching the boundary, so there is not really any space left to build good tets in your desired size. A smaller mesh size would also help here to improve the quality.