# coding=utf-8
"""
Tools to assess the quality of unstructured meshes.
---------------------------------------------------
Quality measures are based on the following review article:
Field, D. A. (2000), Qualitative measures for initial meshes. Int. J. Numer.
Meth. Engng., 47: 887–906.
"""
import numpy as np
# Helper functions
def _boundaryLengths(cell):
"""Return boundary lengths of a given cell."""
return np.array(
[cell.boundary(i).size() for i in range(cell.boundaryCount())])
def _unitVector(vector):
"""Return the unit vector of the vector."""
return vector / np.linalg.norm(vector)
def _angleBetween(v1, v2):
"""Return the angle (in degrees) between vectors v1 and v2.
Examples
--------
>>> print(_angleBetween((1, 0, 0), (1, 0, 0)))
0.0
>>> print(_angleBetween((1, 0, 0), (-1, 0, 0)))
180.0
"""
v1_u = _unitVector(v1)
v2_u = _unitVector(v2)
angle = np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
return np.degrees(angle)
def _cellAngles(cell):
"""Return angles of a triangular cell.
Examples
--------
>>> import pygimli as pg
>>> mesh = pg.Mesh()
>>> for pos in (0.,0.), (0.,1.), (1.,0.):
... n = mesh.createNode(pos[0], pos[1], 0.0)
>>> cell = mesh.createCell((0, 1, 2))
>>> print(_cellAngles(cell)[0])
90.0
"""
if cell.nodeCount() > 3:
raise Exception("Cell %d is not a triangular cell." % cell.id())
a = cell.node(0).pos()
b = cell.node(1).pos()
c = cell.node(2).pos()
ab = b - a
ac = c - a
bc = c - b
alpha = _angleBetween(ab, ac)
beta = _angleBetween(-ab, bc)
gamma = _angleBetween(-ac, -bc)
assert np.allclose(gamma, 180.0 - alpha - beta)
return alpha, beta, gamma
# Quality measures
def eta(cell):
r"""Return default triangle quality (eta) of a given cell.
The quality measure relates the area of the triangle (a)
to its edge lengths (l1, l2, l3).
.. math::
\eta = \frac{4\sqrt{3}a}{l_1^2 + l_2^2 + l_3^2}
"""
return 4 * np.sqrt(3) * cell.size() / np.sum(_boundaryLengths(cell)**2)
def minimumAngle(cell):
"""Return the normalized minimum angle of a given cell."""
return np.min(_cellAngles(cell)) / 60.
def nsr(cell):
r"""Return the normalized shape ratio (NSR) for a given cell.
Also referred to as the radius ratio, as it is described by
the ratio between the inradius (r) and the circumradius (R).
.. math::
\rho = \frac{2r}{R}
"""
a, b, c = _boundaryLengths(cell)
r = 2 * cell.size() / (a + b + c) # inradius
R = 0.25 * a * b * c / cell.size() # circumradius
return 2 * r / R
# Main function
[docs]
def quality(mesh, measure="eta"):
"""Return the quality of a given triangular mesh.
Parameters
----------
mesh : mesh object
Mesh for which the quality is calculated.
measure : quality measure, str
Can be either "eta", "nsr", or "minimumAngle".
Examples
--------
>>> # no need to import matplotlib
>>> import pygimli as pg
>>> from pygimli.meshtools import polytools as plc
>>> from pygimli.meshtools import quality
>>> # Create Mesh
>>> world = plc.createWorld(start=[-10, 0], end=[10, -10],
... marker=1, worldMarker=False)
>>> c1 = plc.createCircle(pos=[0.0, -5.0], radius=3.0, area=.3)
>>> mesh = pg.meshtools.createMesh([world, c1], quality=21.3)
>>> # Compute and show quality
>>> q = quality(mesh, measure="nsr")
>>> ax, _ = pg.show(mesh, q, cMap="RdYlGn", showMesh=True, cMin=0.5,
... cMax=1.0, label="Normalized shape ratio")
See also
--------
eta, nsr, minimumAngle
"""
# Implemented quality measures (Triangular meshses only.)
measures = {"eta": eta, "nsr": nsr, "minimumAngle": minimumAngle}
m = measures[measure]
qualities = [m(cell) for cell in mesh.cells()]
return qualities