Targeting resolutions#

ResolutionSpecs have many attributes that allow them to be targeted.

import shapely

from meshwell.cad_occ import cad_occ
from meshwell.mesh import mesh
from meshwell.occ_xao_writer import write_xao
from meshwell.polysurface import PolySurface
from meshwell.resolution import ConstantInField, ThresholdField
from meshwell.visualization import plot2D
/home/runner/work/meshwell/meshwell/.venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Filtering by mass#

ResolutionSpecs can be selectively applied given the “mass” of the entity (total lengths of curves, total area of surfaces, total volumes of volumes).

# Create two boxes of different sizes
small_box = shapely.box(0, 0, 1, 1)
large_box = shapely.box(3, 0, 5, 2)

# Combine into multipolygon
multi = shapely.MultiPolygon([small_box, large_box])

# Create polysurface with filtered resolution specs
polysurface = PolySurface(
    polygons=multi,
    physical_name="filtered_boxes",
)

write_xao(cad_occ([polysurface]), "filtering_model.xao")

output_mesh = mesh(
    dim=2,
    input_file="filtering_model.xao",
    output_file="mass_unfiltered.msh",
    default_characteristic_length=2,
)

plot2D(output_mesh, wireframe=True, title="Reference Resolution Example")

output_mesh = mesh(
    dim=2,
    input_file="filtering_model.xao",
    output_file="mass_filtered.msh",
    default_characteristic_length=2,
    resolution_specs={
        "filtered_boxes": [
            # Fine resolution for small curves
            ConstantInField(
                resolution=0.1,
                apply_to="surfaces",
                max_mass=4,  # Only applies to curves with perimeter < 4
            ),
        ],
    },
)


plot2D(output_mesh, wireframe=True, title="Mass-filtered Resolution Example")
Info    : Clearing all models and views...
Info    : Done clearing all models and views
Info    : Reading 'filtering_model.xao'...
Info    : Done reading 'filtering_model.xao'
_images/57f3a71d114b8f5ee7870c62190efb95bf2e1b48ea45e450def5626c3a283271.png
Info    : Clearing all models and views...
Info    : Done clearing all models and views
Info    : Reading 'filtering_model.xao'...
Info    : Done reading 'filtering_model.xao'
_images/82734e448ffcea77b6a94f0dbcc8e71dc81507a0b6347a45a6f5c70432fc433c.png

Filtering the application by sharing/not sharing#

For surfaces/curves/points, the ResolutionSpec can be applied if the entity is touching (or not touching) another entity.

Restricting the evaluation of the field#

Once applied, a field can be restricted in its evaluation to specific entities using the restrict_to parameter. This allows you to control which entities the resolution field affects, even when the field is defined on one entity.

# Create geometry: outer square with two inner squares
large_rect = 20
small_rect = 5

# Create outer square
outer_square = shapely.Polygon(
    [[0, 0], [large_rect, 0], [large_rect, large_rect], [0, large_rect], [0, 0]],
)

# Create smaller square that will be duplicated
inner_square = shapely.Polygon(
    [
        [large_rect / 2 - small_rect / 2, large_rect / 2 - small_rect / 2],
        [large_rect / 2 + small_rect / 2, large_rect / 2 - small_rect / 2],
        [large_rect / 2 + small_rect / 2, large_rect / 2 + small_rect / 2],
        [large_rect / 2 - small_rect / 2, large_rect / 2 + small_rect / 2],
        [large_rect / 2 - small_rect / 2, large_rect / 2 - small_rect / 2],
    ],
)

# Example 1: Field restricted to inner_left only
poly_outer = PolySurface(
    polygons=outer_square,
    mesh_order=2,
    physical_name="outer",
)

poly_left = PolySurface(
    polygons=shapely.affinity.translate(inner_square, xoff=-3.1),
    mesh_order=1,
    physical_name="inner_left",
)

poly_right = PolySurface(
    polygons=shapely.affinity.translate(inner_square, xoff=3.1),
    mesh_order=1,
    physical_name="inner_right",
)

write_xao(cad_occ([poly_outer, poly_left, poly_right]), "restricted_field_model.xao")

output_mesh = mesh(
    dim=2,
    input_file="restricted_field_model.xao",
    output_file="restricted_field_inner_only.msh",
    default_characteristic_length=2,
    resolution_specs={
        "inner_left": [
            ThresholdField(
                sizemin=0.3,
                distmax=10,
                sizemax=1.5,
                apply_to="curves",
                restrict_to=["inner_left"],  # Field only affects inner_left
            )
        ],
    },
)

plot2D(output_mesh, wireframe=True, title="Field Restricted to Inner Left Only")

# Example 2: Field restricted to both inner_left and outer

output_mesh = mesh(
    dim=2,
    input_file="restricted_field_model.xao",
    output_file="restricted_field_inner_and_outer.msh",
    default_characteristic_length=2,
    resolution_specs={
        "inner_left": [
            ThresholdField(
                sizemin=0.3,
                distmax=10,
                sizemax=1.5,
                apply_to="curves",
                restrict_to=[
                    "inner_left",
                    "outer",
                ],  # Field affects both inner_left and outer
            )
        ],
    },
)

plot2D(output_mesh, wireframe=True, title="Field Restricted to Inner Left and Outer")
Info    : Clearing all models and views...
Info    : Done clearing all models and views
Info    : Reading 'restricted_field_model.xao'...
Info    : Done reading 'restricted_field_model.xao'
_images/97fa6a85bb7a72e7a443b36be192f95ba2d716ca7e7660e1bf6039802a05ea12.png
Info    : Clearing all models and views...
Info    : Done clearing all models and views
Info    : Reading 'restricted_field_model.xao'...
Info    : Done reading 'restricted_field_model.xao'
_images/1f78a61ee26dff47bee8565e2725ab4980894fa8ef793f91bb52d0ac50cae9e2.png

In the first example, the threshold field only affects the inner_left square, resulting in refined mesh elements only around that entity. In the second example, the field affects both the inner_left square and the outer square, causing mesh refinement to propagate through the outer square as well. The inner_right square maintains the default mesh size in both cases since it’s not included in the restrict_to list.