Mesh size modes

In this example we explore ways to control the mesh size.

Initializing the Builder

Below, we initialize a new instance of qtcad.builder.Builder and define a couple of simple shapes we are going to use in the following.

from qtcad.builder import Builder, Polygon, Mask, Circle, MeshAlgorithm3D

box = Polygon.box(10, 10).centered()
partially_overlapping = box.translated(6, 0).auto_name()
circle = Circle(radius=2.5).translated(-2, 0)

mask = Mask("shapes", shapes=[box, partially_overlapping, circle])
[01:56:25 PM] INFO     Auto-named Polygon instance to 'box'                                                                                                   masks.py:208
              INFO     Auto-named Polygon instance to 'partially_overlapping'                                                                                 masks.py:208
              INFO     Auto-named Circle instance to 'circle'                                                                                                 masks.py:208

Next, we define a function that we will use to create the same geometry, but with different mesh size modes.

The geometry consists of two boxes that are partially overlapping and a cylinder completely inside the first box. The cylinder has the finest mesh size, the left box has a medium mesh size, and the right box has the coarsest mesh size.

We use qtcad.builder.Builder.set_mesh_size to set the mesh size for the subsequent extrusions. We also use qtcad.builder.Builder.overlay_mode to be able to easily identify overlapping regions of the geometry later on.

Builder.set_mesh_size(size: float) Self

[🔧 Modifier] Set the local mesh size for elements added after this point in units set by the length_unit_exponent (see Builder). Note that the mesh size may be chosen smaller due to set_min_elements.

Parameters:

size – The new mesh characteristic length.

def make_geometry(gen: Builder):
    """
    Create a geometry with two boxes that are partially overlapping and a cylinder
    completely inside the first box.

    The cylinder has the finest mesh size, the left box has a medium mesh size, and
    the right box has the coarsest mesh size.
    """
    return (
        gen.add_mask(mask)
        .overlay_mode()
        .use_shape("box")
        .set_mesh_size(1)
        .extrude(10)
        .use_shape("partially_overlapping")
        .set_z(0)
        .set_mesh_size(20)
        .extrude(10)
        .set_z(4)
        .use_shape("circle")
        .set_mesh_size(0.1)
        .extrude(2)
        .mesh(algorithm3d=MeshAlgorithm3D.HXT, check_connectivity=False)
    )

Overriding the mesh size (default)

In override mode, each newly created volume will use the mesh size previously set with qtcad.builder.Builder.set_mesh_size, irrespective of the current fragmentation mode (see Extrusion and fragmentation modes). This is particularly useful when defining regions of finer mesh size using qtcad.builder.Builder.fill_mode.

Builder.override_mesh_size() Self

[🔧 Modifier] For all subsequently generated entities, use the mesh size set with set_mesh_size and override the mesh size in the intersections with the existing geometry.

builder = Builder().override_mesh_size()
make_geometry(builder).view(
    volume_labels=True,
    save="figs/mesh_override.png",  # caption: The whole mesh.;
)
mesh sizes

Fig. 6 The whole mesh.

[01:56:26 PM] INFO     Extruding shape 0 from mask 'shapes' with name 'box' by 10 at z=0                                                                   builder.py:2695
              INFO     Creating new physical group with name 'box_bottom'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box_side'                                                                                    builder.py:2482
              INFO     Creating new physical group with name 'box_top'                                                                                     builder.py:2482
              INFO     Creating new physical group with name 'box'                                                                                         builder.py:2482
              INFO     Setting z-coordinate to 10                                                                                                           builder.py:849
              INFO     Setting z-coordinate to 0                                                                                                            builder.py:849
              INFO     Extruding shape 1 from mask 'shapes' with name 'partially_overlapping' by 10 at z=0                                                 builder.py:2695
[01:56:27 PM] INFO     Creating new physical group with name 'partially_overlapping_bottom'                                                                builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping_side'                                                                  builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping_top'                                                                   builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'box.partially_overlapping_side'                                                              builder.py:2482
              INFO     Creating new physical group with name 'box_side.partially_overlapping_side'                                                         builder.py:2482
              INFO     Creating new physical group with name 'box.partially_overlapping'                                                                   builder.py:2482
              INFO     Creating new physical group with name 'box_side.partially_overlapping'                                                              builder.py:2482
              INFO     Creating new physical group with name 'box_top.partially_overlapping_top'                                                           builder.py:2482
              INFO     Creating new physical group with name 'box_bottom.partially_overlapping_bottom'                                                     builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping'                                                                       builder.py:2482
              INFO     Setting z-coordinate to 10                                                                                                           builder.py:849
              INFO     Setting z-coordinate to 4                                                                                                            builder.py:849
              INFO     Extruding shape 2 from mask 'shapes' with name 'circle' by 2 at z=4                                                                 builder.py:2695
              INFO     Creating new physical group with name 'circle_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'circle_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'circle_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'box.circle_top'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'box.circle_side'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'box.circle'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box.circle_bottom'                                                                           builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 38)]
  warnings.warn(
              INFO     Setting z-coordinate to 6                                                                                                            builder.py:849
              INFO     Preparing to mesh                                                                                                                   builder.py:1502
              INFO     Detecting appropriate random factor                                                                                                 builder.py:3608
[01:56:29 PM] INFO     Increased random factor from 1e-09 to 4.425455132591868e-07                                                                         builder.py:3626
[01:56:30 PM] INFO     Random factor is now appropriate                                                                                                    builder.py:3632
              INFO     Meshing                                                                                                                             builder.py:1518
[01:57:13 PM] INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_override.png             builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f17476cfb10>

We see that the right “partially_overlapping” uses the coarse mesh size of the right box, as does the “circle volume”. If we view those two volumes in isolation, this becomes clear:

builder.view(
    groups=["box.partially_overlapping", "box.circle"],
    save="figs/mesh_override_partially_overlapping.png",  # caption: The overlapping parts of the geometry retain their smaller mesh size.;
    angles=(-10, 0, 90),
    volume_labels=True,
)
mesh sizes

Fig. 7 The overlapping parts of the geometry retain their smaller mesh size.

[01:57:20 PM] INFO     Saving                                                                                                                              builder.py:1863
                       /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_override_partially_overlapping.
                       png

<qtcad.builder.builder.Builder object at 0x7f17476cfb10>

Inheriting the mesh size

On the other hand, in inherit mode, newly created volumes will inherit the mesh size of the volume they intersect with.

Builder.inherit_mesh_size() Self

[🔧 Modifier] For all subsequently generated entities, inherit the mesh size of the existing intersecting entity of the same dimension with the smallest mesh size. If there are no intersecting elements, the mesh size set with set_mesh_size is used.

builder = Builder().inherit_mesh_size()
make_geometry(builder).view(
    volume_labels=True,
    save="figs/mesh_inherit.png",  # caption: The whole mesh.;
)
mesh sizes

Fig. 8 The whole mesh.

[01:57:26 PM] INFO     Extruding shape 0 from mask 'shapes' with name 'box' by 10 at z=0                                                                   builder.py:2695
              INFO     Creating new physical group with name 'box_bottom'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box_side'                                                                                    builder.py:2482
              INFO     Creating new physical group with name 'box_top'                                                                                     builder.py:2482
              INFO     Creating new physical group with name 'box'                                                                                         builder.py:2482
              INFO     Setting z-coordinate to 10                                                                                                           builder.py:849
              INFO     Setting z-coordinate to 0                                                                                                            builder.py:849
              INFO     Extruding shape 1 from mask 'shapes' with name 'partially_overlapping' by 10 at z=0                                                 builder.py:2695
              INFO     Creating new physical group with name 'partially_overlapping_bottom'                                                                builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping_side'                                                                  builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping_top'                                                                   builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'box.partially_overlapping_side'                                                              builder.py:2482
              INFO     Creating new physical group with name 'box_side.partially_overlapping_side'                                                         builder.py:2482
              INFO     Creating new physical group with name 'box.partially_overlapping'                                                                   builder.py:2482
              INFO     Creating new physical group with name 'box_side.partially_overlapping'                                                              builder.py:2482
              INFO     Creating new physical group with name 'box_top.partially_overlapping_top'                                                           builder.py:2482
              INFO     Creating new physical group with name 'box_bottom.partially_overlapping_bottom'                                                     builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping'                                                                       builder.py:2482
              INFO     Setting z-coordinate to 10                                                                                                           builder.py:849
              INFO     Setting z-coordinate to 4                                                                                                            builder.py:849
              INFO     Extruding shape 2 from mask 'shapes' with name 'circle' by 2 at z=4                                                                 builder.py:2695
              INFO     Creating new physical group with name 'circle_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'circle_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'circle_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:57:27 PM] INFO     Creating new physical group with name 'box.circle_top'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'box.circle_side'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'box.circle'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box.circle_bottom'                                                                           builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 38)]
  warnings.warn(
              INFO     Setting z-coordinate to 6                                                                                                            builder.py:849
              INFO     Preparing to mesh                                                                                                                   builder.py:1502
              INFO     Detecting appropriate random factor                                                                                                 builder.py:3608
              INFO     Random factor is now appropriate                                                                                                    builder.py:3632
              INFO     Meshing                                                                                                                             builder.py:1518
              INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_inherit.png              builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f17476cf4d0>

Above we see that in the overlapping regions the mesh size is medium, as the “partially_overlapping” box inherits the mesh size of the “box” volume. The “circle” volume also inherits the mesh size of the left box, so that its mesh becomes coarser compared to the previous case.

builder.view(
    groups=["box.partially_overlapping", "box.circle"],
    save="figs/mesh_inherit_partially_overlapping.png",  # caption: The overlapping parts of the geometry inherit the coarser mesh size.;
    angles=(-10, 0, 90),
    volume_labels=True,
)
mesh sizes

Fig. 9 The overlapping parts of the geometry inherit the coarser mesh size.

[01:57:36 PM] INFO     Saving                                                                                                                              builder.py:1863
                       /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_inherit_partially_overlapping.p
                       ng

<qtcad.builder.builder.Builder object at 0x7f17476cf4d0>

Note however that boundary surfaces will always inherit the finer mesh size of the two intersecting volumes, irrespective of the mesh size mode. To override this behavior, use qtcad.builder.Builder.assign_mesh_size.

Minimum mesh size

In minimum mode, newly created volumes will inherit the mesh size of the volume they intersect with only if that mesh size is smaller than the currently set size.

Builder.minimum_mesh_size() Self

[🔧 Modifier] For all subsequently generated entities, use the mesh size set with set_mesh_size and take the minimum of the set mesh size and the existing mesh size in the intersections with the existing geometry.

builder = Builder().minimum_mesh_size()
make_geometry(builder).view(
    volume_labels=True,
    save="figs/mesh_min.png",  # caption: The whole mesh.;
)
mesh sizes

Fig. 10 The whole mesh.

[01:57:42 PM] INFO     Extruding shape 0 from mask 'shapes' with name 'box' by 10 at z=0                                                                   builder.py:2695
              INFO     Creating new physical group with name 'box_bottom'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box_side'                                                                                    builder.py:2482
              INFO     Creating new physical group with name 'box_top'                                                                                     builder.py:2482
              INFO     Creating new physical group with name 'box'                                                                                         builder.py:2482
              INFO     Setting z-coordinate to 10                                                                                                           builder.py:849
              INFO     Setting z-coordinate to 0                                                                                                            builder.py:849
              INFO     Extruding shape 1 from mask 'shapes' with name 'partially_overlapping' by 10 at z=0                                                 builder.py:2695
              INFO     Creating new physical group with name 'partially_overlapping_bottom'                                                                builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping_side'                                                                  builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping_top'                                                                   builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
[01:57:43 PM] INFO     Creating new physical group with name 'box.partially_overlapping_side'                                                              builder.py:2482
              INFO     Creating new physical group with name 'box_side.partially_overlapping_side'                                                         builder.py:2482
              INFO     Creating new physical group with name 'box.partially_overlapping'                                                                   builder.py:2482
              INFO     Creating new physical group with name 'box_side.partially_overlapping'                                                              builder.py:2482
              INFO     Creating new physical group with name 'box_top.partially_overlapping_top'                                                           builder.py:2482
              INFO     Creating new physical group with name 'box_bottom.partially_overlapping_bottom'                                                     builder.py:2482
              INFO     Creating new physical group with name 'partially_overlapping'                                                                       builder.py:2482
              INFO     Setting z-coordinate to 10                                                                                                           builder.py:849
              INFO     Setting z-coordinate to 4                                                                                                            builder.py:849
              INFO     Extruding shape 2 from mask 'shapes' with name 'circle' by 2 at z=4                                                                 builder.py:2695
              INFO     Creating new physical group with name 'circle_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'circle_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'circle_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'box.circle_top'                                                                              builder.py:2482
              INFO     Creating new physical group with name 'box.circle_side'                                                                             builder.py:2482
              INFO     Creating new physical group with name 'box.circle'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'box.circle_bottom'                                                                           builder.py:2482
/home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/src/qtcad/builder/core/builder.py:3552: UserWarning: Removing orphaned entities: [(1, 38)]
  warnings.warn(
              INFO     Setting z-coordinate to 6                                                                                                            builder.py:849
              INFO     Preparing to mesh                                                                                                                   builder.py:1502
              INFO     Detecting appropriate random factor                                                                                                 builder.py:3608
[01:57:45 PM] INFO     Increased random factor from 1e-09 to 4.425455132591868e-07                                                                         builder.py:3626
[01:57:46 PM] INFO     Random factor is now appropriate                                                                                                    builder.py:3632
              INFO     Meshing                                                                                                                             builder.py:1518
[01:58:30 PM] INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_min.png                  builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f17476cf750>

Above we see that in the overlapping regions the mesh size is now medium instead of coarse, as the “partially_overlapping” box does not inherit the mesh size of the right box. The cylinder retains its very fine mesh.

builder.view(
    groups=["box.partially_overlapping", "box.circle"],
    save="figs/mesh_min_partially_overlapping.png",  # caption: The overlapping parts of the geometry do not inherit the coarser mesh size.;
    angles=(-10, 0, 90),
    volume_labels=True,
)
mesh sizes

Fig. 11 The overlapping parts of the geometry do not inherit the coarser mesh size.

[01:58:39 PM] INFO     Saving                                                                                                                              builder.py:1863
                       /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_min_partially_overlapping.png

<qtcad.builder.builder.Builder object at 0x7f17476cf750>

Setting the minimal number of elements

Irrespective of the mesh size mode, we can also set the minimum number of mesh elements along the shortest dimension of a volume using qtcad.builder.Builder.set_min_elements. This allows to automatically create more refined meshes in small structures.

Builder.set_min_elements(min_elements: int | None) Self

[🔧 Modifier] Ensure that all subsequently created volumes will have at least min_elements mesh elements (triangles, tetrahedra) along their narrowest dimension. If set to None, the mesh size set by set_mesh_size will be used.

Below, we set a very large mesh size, but also set the minimum number of elements to 5. We then extrude a box once by 1 and once by 3, resulting in two volumes with different heights. These different heights will result in different mesh sizes.

builder = (
    Builder()
    .overlay_mode()
    .set_mesh_size(1000)
    .set_min_elements(5)
    .add_mask(mask)
    .use_shape("box")
    .set_group_name("first")
    .extrude(3)
    .set_group_name("second")
    .extrude(1)
    .mesh(2, algorithm3d=MeshAlgorithm3D.HXT)
    .view(
        save="figs/mesh_min_elements.png",  # caption: The whole mesh with a very large mesh size but a minimum of 5 elements along the shortest dimension.;
        orthographic=True,
        angles=(-90, 0, 0),
    )
)
mesh sizes

Fig. 12 The whole mesh with a very large mesh size but a minimum of 5 elements along the shortest dimension.

[01:58:46 PM] INFO     Extruding shape 0 from mask 'shapes' with name 'box' by 3 at z=0                                                                    builder.py:2695
              INFO     Creating new physical group with name 'first_bottom'                                                                                builder.py:2482
              INFO     Creating new physical group with name 'first_side'                                                                                  builder.py:2482
              INFO     Creating new physical group with name 'first_top'                                                                                   builder.py:2482
              INFO     Creating new physical group with name 'first'                                                                                       builder.py:2482
              INFO     Setting z-coordinate to 3                                                                                                            builder.py:849
              INFO     Extruding shape 0 from mask 'shapes' with name 'box' by 1 at z=3                                                                    builder.py:2695
              INFO     Creating new physical group with name 'second_bottom'                                                                               builder.py:2482
              INFO     Creating new physical group with name 'second_side'                                                                                 builder.py:2482
              INFO     Creating new physical group with name 'second_top'                                                                                  builder.py:2482
              INFO     Fragmenting...                                                                                                                    fragmenter.py:234
              INFO     Creating new physical group with name 'first_top.second_bottom'                                                                     builder.py:2482
              INFO     Creating new physical group with name 'second'                                                                                      builder.py:2482
              INFO     Setting z-coordinate to 4                                                                                                            builder.py:849
              INFO     Preparing to mesh                                                                                                                   builder.py:1502
              INFO     Detecting appropriate random factor                                                                                                 builder.py:3608
              INFO     Increased random factor from 1e-09 to 3.038778470807636e-08                                                                         builder.py:3626
              INFO     Random factor is now appropriate                                                                                                    builder.py:3632
              INFO     Meshing                                                                                                                             builder.py:1518
[01:58:47 PM] INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_min_elements.png         builder.py:1863

Using qtcad.builder.Builder.assign_mesh_size we can override the automatically assigned mesh size.

Builder.assign_mesh_size(group: str, size: float, force: bool = False, force_with_lease: bool = False) Self

[⚡ Utility] Assign a mesh size size to all entities (and their boundaries) in the physical group named group respecting the mesh size mode (see override_mesh_size and inherit_mesh_size). If force is True, the size will override any existing sizes. If force_with_lease is True, the size will override any existing sizes, but only if the new size is smaller.

Parameters:
  • group – The name of the physical group.

  • size – The mesh size to assign.

  • force – If True, the size will override any existing sizes.

  • force_with_lease – If True, the size will override any existing sizes, but only if the new size is smaller.

builder.clear_mesh().assign_mesh_size("first", 0.1, force=True).mesh(
    2, algorithm3d=MeshAlgorithm3D.HXT
).view(
    save="figs/mesh_assigned.png",  # caption: The same mesh as above, but with the mesh size of the first volume set to 1.;
    orthographic=True,
    angles=(-90, 0, 0),
)
mesh sizes

Fig. 13 The same mesh as above, but with the mesh size of the first volume set to 1.

[01:58:50 PM] INFO     Clearing mesh                                                                                                                       builder.py:1460
              INFO     Preparing to mesh                                                                                                                   builder.py:1502
              INFO     Detecting appropriate random factor                                                                                                 builder.py:3608
[01:58:51 PM] INFO     Random factor is now appropriate                                                                                                    builder.py:3632
              INFO     Meshing                                                                                                                             builder.py:1518
[01:58:53 PM] INFO     Saving /home/hiro/Documents/org/roam/code/qtcad_flake/qtcad/packages/builder/examples/operations/figs/mesh_assigned.png             builder.py:1863

<qtcad.builder.builder.Builder object at 0x7f17476cf250>

Above we uset the force=True option to completely override the mesh size.

Total running time of the script: (2 minutes 31.541 seconds)

Gallery generated by Sphinx-Gallery