Skip to content

Commit 176d2a0

Browse files
rimoliclaude
andauthored
Apply max_volume to the default phases in 2D (#118)
Without phases, TriMesh.from_polymesh uses one default phase per phase number, and those carried 'max_volume': inf. The 2D meshers read the area limit of each region as phase.get('max_volume', max_volume), so the infinite default always won. The global limit is no longer passed to Triangle under the regional ones (the periodic mesher needs different limits for the cells and their copies), so the max_volume argument was ignored: a square meshed with max_volume=0.4 kept two triangles of area 8. On master the global limit applied underneath. The default phases no longer have a max_volume, so the argument applies to their regions, in the bounded and the periodic 2D meshers and in _facet_sizes. An explicit infinite max_volume of a phase still means no limit for that phase, as the changelog now says. The new test meshes a single cell without phases, bounded and periodic, and checks the largest triangle against the limit. The example digest of the documentation is updated for the change of the library. Reported by Kip Hart in the review of PR #114. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 46c3179 commit 176d2a0

4 files changed

Lines changed: 27 additions & 9 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ Fixed
9797
- 3D ``mesh_max_volume`` and per-phase ``max_volume`` are now honored by
9898
TetGen. In 2D, a per-phase ``max_volume`` larger than the global value is
9999
no longer capped, and an infinite ``mesh_max_volume`` is no longer passed
100-
to Triangle as the (mis-parsed) switch ``ainf``.
100+
to Triangle as the (mis-parsed) switch ``ainf``. The ``max_volume`` of a
101+
phase replaces the global value for that phase, an infinite one included,
102+
and phases without one, such as the default phases when none are given,
103+
use the global value.
101104
- ``Ellipsoid.approximate`` mapped the axes incorrectly for the ordering
102105
c >= a >= b, so those grains were tessellated with the wrong orientation.
103106
The b >= c >= a ordering is now sorted explicitly as well.

‎docs/source/sphinx_gallery/plot_demos.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
# them is edited, and the build would serve stale figures.
2525
#
2626
# Managed by docs/example_digest.py. Do not edit by hand.
27-
EXAMPLES_DIGEST = '2d39355b71ddd257'
27+
EXAMPLES_DIGEST = '22bb4ed7ea2ddd9f'
2828

2929
example_dir = '../../../src/microstructpy/examples'
3030

‎src/microstructpy/meshing/trimesh.py‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ def from_polymesh(cls, polymesh, phases=None, mesher='Triangle/Tetgen',
217217
polymesh (PolyMesh): A polygon/polyhedron mesh.
218218
phases (list): *(optional)* A list of dictionaries containing
219219
options for each phase.
220-
Default is
221-
``{'material_type': 'solid', 'max_volume': float('inf')}``.
220+
Default is ``{'material_type': 'solid'}`` for each phase,
221+
meshed with the ``max_volume`` argument.
222222
mesher (str): {'Triangle/TetGen' | 'Triangle' | 'TetGen' | 'gmsh'}
223223
specify the mesh generator. Default is 'Triangle/TetGen'.
224224
min_angle (float): The minimum interior angle, in degrees, of an
@@ -844,8 +844,7 @@ def from_polymesh(cls, polymesh, mesh_size, phases=None):
844844
mesh_size (float): The side length of each pixel/voxel.
845845
phases (list): *(optional)* A list of dictionaries containing
846846
options for each phase.
847-
Default is
848-
``{'material_type': 'solid', 'max_volume': float('inf')}``.
847+
Default is ``{'material_type': 'solid'}`` for each phase.
849848
850849
"""
851850
if phases is None:
@@ -1965,10 +1964,13 @@ def _amorphous_seed_numbers(pmesh, phases):
19651964

19661965

19671966
def _default_phases(polymesh):
1968-
"""Default phases: one solid phase per phase number of the polymesh."""
1967+
"""Default phases: one solid phase per phase number of the polymesh.
1968+
1969+
They have no ``max_volume``, so the ``max_volume`` argument of the
1970+
mesher applies to their regions.
1971+
"""
19691972
n_phases = int(np.max(polymesh.phase_numbers)) + 1
1970-
return [{'material_type': 'solid', 'max_volume': float('inf')}
1971-
for _ in range(n_phases)]
1973+
return [{'material_type': 'solid'} for _ in range(n_phases)]
19721974

19731975

19741976
def _pt3d(pt):

‎tests/meshing/test_trimesh_fixes.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,19 @@ def test_triangle_per_phase_max_volume_exceeds_global(case_2d):
342342
assert np.all(areas[elem_phases != 0] <= 1e-3 + 1e-9)
343343

344344

345+
@pytest.mark.parametrize('periodic', [False, True])
346+
def test_triangle_max_volume_without_phases(periodic):
347+
# Without phases, the max_volume argument limits the area of the
348+
# triangles. The default phases must not carry a limit of their own,
349+
# which would replace it (an infinite one left it unapplied).
350+
domain = msp.geometry.Square(side_length=4, corner=(0, 0))
351+
seeds = msp.seeding.SeedList([msp.seeding.Seed.factory(
352+
'circle', r=1.5, position=(2.2, 1.9))])
353+
pmesh = msp.meshing.PolyMesh.from_seeds(seeds, domain, periodic=periodic)
354+
mesh = TriMesh.from_polymesh(pmesh, min_angle=20, max_volume=0.4)
355+
assert np.max(simplex_volumes(mesh)) <= 0.4 + 1e-9
356+
357+
345358
def test_triangle_infinite_max_volume_not_passed(case_2d, monkeypatch):
346359
pmesh, phases, _ = case_2d
347360
captured = {}

0 commit comments

Comments
 (0)