Numbering After Webcutting

Is there a way to know how Coreform Cubit does some of the numbering of nodes after webcutting? I created a simple example.

brick x 2 y 1 z 1
move volume 1 x 1 y 0.5 z 0.5
block 1 volume 1
block 1 elem type hex27
block 1 name "two-element"
volume 1 size 1
mesh volume 1
nodeset 1 surface with x_coord 0
nodeset 2 surface with x_coord 2
nodeset 3 surface with y_coord 0
nodeset 4 surface with y_coord 1
nodeset 5 surface with z_coord 0
nodeset 6 surface with z_coord 1
nodeset 1 name "n_left"
nodeset 2 name "n_right"
nodeset 3 name "n_bottom"
nodeset 4 name "n_top"
nodeset 5 name "n_back"
nodeset 6 name "n_front"
export mesh "two-element.exo" overwrite

Then I created the same example except I webcut it.

brick x 2 y 1 z 1
move volume 1 x 1 y 0.5 z 0.5
block 1 volume 1
block 1 elem type hex27
block 1 name "two-element"
webcut volume 1 with plane xplane offset 1 
merge vol all
volume all size 1
mesh volume all
nodeset 1 surface with x_coord 0
nodeset 2 surface with x_coord 2
nodeset 3 surface with y_coord 0
nodeset 4 surface with y_coord 1
nodeset 5 surface with z_coord 0
nodeset 6 surface with z_coord 1
nodeset 1 name "n_left"
nodeset 2 name "n_right"
nodeset 3 name "n_bottom"
nodeset 4 name "n_top"
nodeset 5 name "n_back"
nodeset 6 name "n_front"
export mesh "two-element-split.exo" overwrite

When I use the exodus python module to look at the node connectivity for the two elements, I get the disparity.

For mesh without webcut

array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
       [ 5,  6,  7,  8, 28, 29, 30, 31, 17, 18, 19, 20, 32, 33, 34, 35,
        36, 37, 38, 39, 40, 23, 41, 42, 43, 44, 45]], dtype=int32)

For mesh with webcut

array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
       [ 6, 28, 29,  7,  5, 30, 31,  8, 32, 33, 34, 18, 17, 35, 36, 19,
        37, 38, 39, 20, 40, 41, 42, 23, 43, 44, 45]], dtype=int32)

Reason I am hoping to define a different node numbering scheme compared to the exodus defined 27 nodes hexahedron defined here Exodus: Exodus Element Types

Thank you for any help.

To predict the node numbering after a webcut seems pretty impossible to me unless you control the whole meshing order. The normal mesh order is to first mesh the vertices, then the curves, then the surfaces and then the volume. And for the next volume this will be repeated.

It sounds like you want to map the connectivity for a element. For that you only need to know the node numbering for the isoparametric element that you want to map. The

cubit.get_expanded_connectivity(“hex”,1)

will always return the connectivity based on the exodus element type. When cubit exports the mesh in a different format the exodus element connectivity gets mapped to the chosen solver element type.

So for example if i want to map the connectivity from hex20 exodus to the c3d20 calculix

https://www.dhondt.de/ccx_2.22.pdf#subsubsection.6.2.4

I could do it like this with the python api.

#!cubit
reset
brick x 2 y 1 z 1
move volume 1 x 1 y 0.5 z 0.5
block 1 volume 1
block 1 elem type hex20
block 1 name "two-element"
webcut volume 1 with plane xplane offset 1 
merge vol all
volume all size 1
mesh volume all
nodeset 1 surface with x_coord 0
nodeset 2 surface with x_coord 2
nodeset 3 surface with y_coord 0
nodeset 4 surface with y_coord 1
nodeset 5 surface with z_coord 0
nodeset 6 surface with z_coord 1
nodeset 1 name "n_left"
nodeset 2 name "n_right"
nodeset 3 name "n_bottom"
nodeset 4 name "n_top"
nodeset 5 name "n_back"
nodeset 6 name "n_front"

#!python
import numpy as np

def map_hex_element(hex_element):
    nodes = np.array(cubit.get_expanded_connectivity("hex",hex_element))
    # swap position 13-16 with 17-20
    tmp_nodes = np.array(cubit.get_expanded_connectivity("hex",hex_element))    
    for i in range(12,16):
        nodes[i]=tmp_nodes[i+4]
    for i in range(16,20):
        nodes[i]=tmp_nodes[i-4]
    return nodes

nodes_cubit = cubit.get_expanded_connectivity("hex",1)
nodes_calculix = map_hex_element(1)

for i in range(len(nodes_cubit)):
 print(f"index {i+1} cubit {nodes_cubit[i]} <--> calculix {nodes_calculix[i]}")

Do you need a export format that is not supported by cubit?

I do have a different numbering that I am using for a solver I am testing out. What I am doing is exporting the exodus mesh, and using the exodus module to look at the connectivity. exodus module — SEACAS 2024/08/17 documentation

I am seeing a discrepancy also when I use the cubit.get_expanded_connectivity(“hex”,1") for the hex27 case compared to the element connectivity the exodus module produces.

For mesh without webcut

array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
       [ 5,  6,  7,  8, 28, 29, 30, 31, 17, 18, 19, 20, 32, 33, 34, 35,
        36, 37, 38, 39, 40, 23, 41, 42, 43, 44, 45]], dtype=int32)

image

For mesh with webcut

array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
       [ 6, 28, 29,  7,  5, 30, 31,  8, 32, 33, 34, 18, 17, 35, 36, 19,
        37, 38, 39, 20, 40, 41, 42, 23, 43, 44, 45]], dtype=int32)

image

I am unsure if this is an issue with the exodus module or how it’s exported.

The ids gets mapped from cubit into the exodus format. I didn’t find a way to access the mapping out of cubit but you can for example view the mapping when you export with the hdf5 option with hdfview.

grafik

But there should also be a method in the exodus module that can get you access to the mapping.

https://sandialabs.github.io/seacas-docs/sphinx/html/exodus.html#exodus.exodus.get_node_id_map

What I am after is getting some method after exporting the mesh to get the same connectivity information as cubit.get_connectivity. The node_id_map while helpful does not provide me with element connectivity.

Hi @ccarranza,
you can use the node_id_map in a function to map the node ids.

#!cubit
reset
brick x 2 y 1 z 1
move volume 1 x 1 y 0.5 z 0.5
block 1 volume 1
block 1 elem type hex27
block 1 name "two-element"
webcut volume 1 with plane xplane offset 1 
merge vol all
volume all size 1
mesh volume all
nodeset 1 surface with x_coord 0
nodeset 2 surface with x_coord 2
nodeset 3 surface with y_coord 0
nodeset 4 surface with y_coord 1
nodeset 5 surface with z_coord 0
nodeset 6 surface with z_coord 1
nodeset 1 name "n_left"
nodeset 2 name "n_right"
nodeset 3 name "n_bottom"
nodeset 4 name "n_top"
nodeset 5 name "n_back"
nodeset 6 name "n_front"

export mesh "/home/user/test.e" overwrite

#!python
import exodus
import numpy

# open file
exo = exodus.exodus('/home/user/test.e')

# get element connectivity from file
block_id=1
elem_conn, num_blk_elems, num_elem_nodes = exo.get_elem_connectivity(block_id)

elements = numpy.empty([num_blk_elems, num_elem_nodes])

for i in range(num_blk_elems):
 for ii in range(num_elem_nodes):
  elements[i,ii] = elem_conn[i*num_elem_nodes + ii]

node_id_map = exo.get_node_id_map()

def map_hex_element(hex_element):
    nodes = numpy.empty([1, num_elem_nodes])
    for i in range(num_elem_nodes):
     nodes[0,i] = node_id_map[int(elements[hex_element-1,i]-1)]
    return nodes

nodes_cubit = cubit.get_expanded_connectivity("hex",1)
nodes_exodus = elements[0]
nodes_exodus_mapped = map_hex_element(1)

for i in range(len(nodes_cubit)):
 print(f"index {i+1} cubit {nodes_cubit[i]} <--> exodus {int(nodes_exodus[i])} <--> exodus_mapped {int(nodes_exodus_mapped[0,i])}")

@Norbert_Hofbauer Thank you for the help. That provides a lot of clarity. It seems such a change would else be required for any nodesets. How would I accomplish that?

@Norbert_Hofbauer Actually, I was able to figure it out using the same strategy of using the node map. Thanks again! I guess the only question I would have is how to display the nodes in the node set using the python api?

Another general question I had. The package I am testing out uses a wavefront solver. When I do not perform any webcutting, the solver works great. This tends to be an issue when I perform multiple webcuts.

I am not quite sure what you mean by that.
Do you mean to just draw the single nodes in cubit? Or drawing a label in cubit with the exodus ids?

About the solver. I would take a look at how the solver handles the mesh. Especially how the element import works. Are the any requirements that the mesh needs to fullfill?

Just how you can obtain the element connectivity as a list/array, I was wondering if there is a command to get the nodes in a nodeset.

For the solver, I think I figured out the issue. I need to renumber the node coordinates. One thing I wanted to ask. Maybe this is more appropriate for the exodus folks, but does the orientation of the numbering matter. I am not seeing one for the exdous equivalent. I am working to compare this 27 node hex element.

https://sandialabs.github.io/seacas-docs/html/element_types.html

Here’s the benchmark example I am working on. I am looking to analyze the lid driven cavity with a uniform mesh and non-uniform mesh. The issue is that with the non-uniform which requires webcuts, the element number follow a pattern whereas the uniform all the elements follow a column wise structure. Here is the commands for both meshes.

Uniform

# {length = 1}
# {height = 1}
# {time_step = 0.05}
# {num_elem_length = 6}
# {num_elem_height = 7}
brick x {length} y {height} z {time_step}
move volume 1 x {length/2} y {height/2} z {time_step/2}
block 1 volume 1
block 1 elem type hex27
block 1 name "lid-uniform"
curve with x_coord 0 and y_coord 0 interval 1
curve with x_coord 0 and y_coord 0 scheme equal
mesh curve with x_coord 0 and y_coord 0
curve with x_coord 0 and z_coord 0 interval {num_elem_height}
curve with x_coord 0 and z_coord 0 scheme equal
mesh curve with x_coord 0 and z_coord 0
curve with y_coord 0 and z_coord 0 interval {num_elem_length}
curve with y_coord 0 and z_coord 0 scheme equal
mesh curve with y_coord 0 and z_coord 0
mesh volume 1 
nodeset 1 surface with x_coord 0
nodeset 2 surface with x_coord {length}
nodeset 3 surface with y_coord 0
nodeset 4 surface with y_coord {height}
nodeset 5 surface with z_coord 0
nodeset 6 surface with z_coord {time_step}
nodeset 7 curve with y_coord 0 and x_coord 0
nodeset 8 node with y_coord {height} and z_coord {time_step} and x_coord > 0 and x_coord < {length}
nodeset 9 curve with y_coord {height} and x_coord 0
nodeset 9 curve with y_coord {height} and x_coord {length}
nodeset 1 name "left"
nodeset 2 name "right"
nodeset 3 name "bottom"
nodeset 4 name "top"
nodeset 5 name "back"
nodeset 6 name "front"
nodeset 7 name "pressure"
nodeset 8 name "lid"
nodeset 9 name "edges"

Non-uniform

# {length = 1}
# {height = 1}
# {time_step = 0.05}
brick x {length} y {height} z {time_step}
move volume 1 x {length/2} y {height/2} z {time_step/2}
webcut volume all with plane xplane offset 0.05 
webcut volume all with plane xplane offset 0.25 
webcut volume all with plane xplane offset 0.5 
webcut volume all with plane xplane offset 0.75 
webcut volume all with plane xplane offset 0.95 
webcut volume all with plane yplane offset 0.3 
webcut volume all with plane yplane offset 0.55 
webcut volume all with plane yplane offset 0.7 
webcut volume all with plane yplane offset 0.82 
webcut volume all with plane yplane offset 0.91 
webcut volume all with plane yplane offset 0.99 
merge volume all
block 1 volume all
block 1 elem type hex27
block 1 name "lid-2d-ivp-distorted"
volume all size 1
curve with x_coord 0 and y_coord 0 interval 1
curve with x_coord 0 and y_coord 0 scheme equal
mesh curve with x_coord 0 and y_coord 0
mesh volume all
nodeset 1 surface with x_coord 0
nodeset 2 surface with x_coord {length}
nodeset 3 surface with y_coord 0
nodeset 4 surface with y_coord {height}
nodeset 5 surface with z_coord 0
nodeset 6 surface with z_coord {time_step}
nodeset 7 curve with y_coord 0 and x_coord 0
nodeset 8 node with y_coord {height} and z_coord {time_step} and x_coord > 0 and x_coord < {length}
nodeset 9 curve with y_coord {height} and x_coord 0
nodeset 9 curve with y_coord {height} and x_coord {length}
nodeset 1 name "left"
nodeset 2 name "right"
nodeset 3 name "bottom"
nodeset 4 name "top"
nodeset 5 name "back"
nodeset 6 name "front"
nodeset 7 name "pressure"
nodeset 8 name "lid"
nodeset 9 name "edges"

For a nodeset with id 1 you can use

cubit.get_nodeset_nodes_inclusive(1)

to get all nodes in the nodeset.
The same can be achieved with

cubit.parse_cubit_list("node",f"all in nodeset 1")

The orientation usually matters. Just like in my calculix example above. if i don’t reorder my hex20 connectivity i get in trouble with using the mesh in calculix.

this should be the numbering for the exodus hex27

Can your solver handle nonuniform meshes?

It can handle non-uniform meshes, but I think it struggles to handle non-sequential element assembly. In the two meshes that I provided, the uniform mesh numbers all the elements sequentially while the non-uniform does not. I am hoping to see if I can fix that within cubit.