Developing hex mesh for a dense fracture network

Dear Cubit team,

I am using cubit 2025.8 in windows to set up a (preferably Hex) mesh for a complex geometry. I have hundreds of small surfaces/fractures in the doman which also cross-cut each other. For the mesh my first wish is to embed the fractures inside the volume. By embed I mean that the mesh in the fracture surfaces should share its nodes, edges, and surfaces with the volume. Secondly, I would like to have a bit of refinement on the surfaces. I very much appreciate any help. This is how I make the geometry in the cubit:

#!cubit
reset
#!python
import numpy as np
# Read Data from file
nested_fractures = np.load(r"...\DFN_Mesh\data\DFN_Frac_corners.npy")
for frac in range (len(nested_fractures)):
 for point in nested_fractures[frac]:
  cubit.cmd(f"create vertex location {point[0]} {point[1]} {point[2]}")
n_groups = len (nested_fractures) * 4
frac_ids = np.arange (1, n_groups+1).reshape(-1, 4)
for frac in frac_ids:
 cubit.cmd(f"create surface vertex {frac[0]} {frac[1]} {frac[2]} {frac[3]}")
n_fracs = len (nested_fractures)
cubit.cmd ("brick x 100 y 50 z 520")
cubit.cmd (f"move Volume {n_fracs+1} z -260 include_merged")
#!cubit
imprint surface all
merge surface all

Here is a view into the geometry with 571 surfaces:

I have attahed my journal file and also data (a compressed .npy file).
DFN_mesh.jou (733 Bytes)
DFN_Frac_corners.zip (40.9 KB)

I very much appreciate any help.

Cheers

Hello Ali,
i didn’t find a way to get a hex mesh that is fitted to those surfaces.

But we could use mesh intersections to approximate those surfaces. For this we would need to sweep the fracture surfaces to get a volume. Then we can check for mesh intersections with the surrounding volume.
If your machine got a lot of resources you could use a smaller mesh size and refine more to be more accurate.

#!cubit
reset
set developer on
set fullhex on
#!python
import numpy as np
# Read Data from file
nested_fractures = np.load(r"DFN_Frac_corners.npy")
#for frac in range (len(nested_fractures)):
max_frac = 5
for frac in range (max_frac):
 for point in nested_fractures[frac]:
  cubit.cmd(f"create vertex location {point[0]} {point[1]} {point[2]}")
#n_groups = len (nested_fractures) * 4
n_groups = max_frac * 4
frac_ids = np.arange (1, n_groups+1).reshape(-1, 4)
for frac in frac_ids:
 cubit.cmd(f"create surface vertex {frac[0]} {frac[1]} {frac[2]} {frac[3]}")
#n_fracs = len (nested_fractures)
n_fracs = max_frac

sids = cubit.parse_cubit_list("surface","all in body all with is_sheet")
for sid in sids:
 cubit.cmd(f"sweep surface {sid} perpendicular distance 0.1")

cubit.cmd ("brick x 100 y 50 z 520")
cubit.cmd (f"move Volume {n_fracs+1} z -260 include_merged")
vid = cubit.get_last_id('volume')

cubit.cmd("vol all size 3")
cubit.cmd("mesh vol all")

for sid in sids:
 cubit.cmd(f"find mesh intersection Volume {vid} with Volume {sid} exhaustive ")

hexes = cubit.parse_cubit_list("hex","all in group all except 1")

cubit.cmd(f"refine hex {' '.join(str(id) for id in hexes)} numsplit 1 bias 1.0 depth 0 smooth")

cubit.cmd("delete group all")

for sid in sids:
 cubit.cmd(f"find mesh intersection Volume {vid} with Volume {sid} exhaustive ")

cubit.cmd("set duplicate block elements on")
gids = cubit.parse_cubit_list("group","all except 1")
i = 1
for gid in gids:
 hexes = cubit.get_group_hexes(gid)
 cubit.cmd(f"block {i} add hex {' '.join(str(id) for id in hexes)}")
 i = i +1

#!cubit
draw block all

Hi @Norbert_Hofbauer,

Thanks a lot for your solution. The mesh will be super heavy if I repeat the process for ca. 500 surfaces. Is there a simpler way to make the tet mesh for this geometry? The fracture surfaces should be embedded inside the volume.

Actually i think not.
Usually i would suggest to use tetmesh respect tri in surface but that will only work for a small number of fractures. At some point the fractures overlap to much and meshgems can’t handle it.

#!cubit
reset
#!python
import numpy as np
# Read Data from file
nested_fractures = np.load(r"\DFN_Frac_corners.npy")
#for frac in range (len(nested_fractures)):
max_frac = 10
for frac in range (max_frac):
 for point in nested_fractures[frac]:
  cubit.cmd(f"create vertex location {point[0]} {point[1]} {point[2]}")
#n_groups = len (nested_fractures) * 4
n_groups = max_frac * 4
frac_ids = np.arange (1, n_groups+1).reshape(-1, 4)
for frac in frac_ids:
 cubit.cmd(f"create surface vertex {frac[0]} {frac[1]} {frac[2]} {frac[3]}")
#n_fracs = len (nested_fractures)
n_fracs = max_frac

cubit.cmd ("brick x 100 y 50 z 520")
cubit.cmd (f"move Volume {n_fracs+1} z -260 include_merged")
vid = cubit.get_last_id('volume')

cubit.cmd("imprint body all with is_sheet")
cubit.cmd("merge body all with is_sheet")

cubit.cmd("surface all with is_sheet scheme trimesh")
cubit.cmd("mesh surface all with is_sheet")

sids = cubit.parse_cubit_list("surface","all with is_sheet")

cubit.cmd(f"vol {vid} scheme tetmesh")
cubit.cmd(f"vol {vid} tetmesh respect tri in surface {' '.join(str(id) for id in sids)}")
cubit.cmd(f"mesh vol {vid}")


1 Like

Thanks a lot @Norbert_Hofbauer , sorry for my late update. I will work on your code and try to adapt it based on my need.