Skip to main content
Join the official 2020 Python Developers SurveyStart the survey!

High-quality 2D mesh generator based on distmesh

Project description

dmsh

The worst mesh generator you'll ever use.

PyPi Version Packaging status PyPI pyversions GitHub stars PyPi downloads

gh-actions codecov LGTM Code style: black

Inspired by distmesh, dmsh can be slow, requires a lot of memory, and isn't terribly robust either.

On the plus side,

  • it's got a user-friendly interface,
  • is pure Python (and hence easily installable on any system), and
  • it produces pretty high-quality meshes.

Combined with optimesh, dmsh produces the highest-quality 2D meshes in the west.

Examples

Primitives

circle rectangle polygon
import dmsh

geo = dmsh.Circle([0.0, 0.0], 1.0)
X, cells = dmsh.generate(geo, 0.1)

# optionally optimize the mesh
import optimesh

X, cells = optimesh.cvt.quasi_newton_uniform_full(X, cells, 1.0e-10, 100)

# visualize the mesh
dmsh.helpers.show(X, cells, geo)

# and write it to a file
import meshio

meshio.write_points_cells("circle.vtk", X, {"triangle": cells})
import dmsh

geo = dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0)
X, cells = dmsh.generate(geo, 0.1)
import dmsh

geo = dmsh.Polygon(
    [
        [0.0, 0.0],
        [1.1, 0.0],
        [1.2, 0.5],
        [0.7, 0.6],
        [2.0, 1.0],
        [1.0, 2.0],
        [0.5, 1.5],
    ]
)
X, cells = dmsh.generate(geo, 0.1)

Combinations

Difference
difference pacman square_hole_refined
import dmsh

geo = dmsh.Difference(dmsh.Circle([-0.5, 0.0], 1.0), dmsh.Circle([+0.5, 0.0], 1.0))
X, cells = dmsh.generate(geo, 0.1)
import dmsh

geo = dmsh.Difference(
    dmsh.Circle([0.0, 0.0], 1.0),
    dmsh.Polygon([[0.0, 0.0], [1.5, 0.4], [1.5, -0.4]]),
)
X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10)

The following example uses a nonconstant edge length; it depends on the distance to the circle c.

import dmsh
import numpy

r = dmsh.Rectangle(-1.0, +1.0, -1.0, +1.0)
c = dmsh.Circle([0.0, 0.0], 0.3)
geo = dmsh.Difference(r, c)

X, cells = dmsh.generate(
    geo, lambda pts: numpy.abs(c.dist(pts)) / 5 + 0.05, tol=1.0e-10
)
Union
union union-rect union-three-circles
import dmsh

geo = dmsh.Union([dmsh.Circle([-0.5, 0.0], 1.0), dmsh.Circle([+0.5, 0.0], 1.0)])
X, cells = dmsh.generate(geo, 0.15)
import dmsh

geo = dmsh.Union(
    [dmsh.Rectangle(-1.0, +0.5, -1.0, +0.5), dmsh.Rectangle(-0.5, +1.0, -0.5, +1.0)]
)
X, cells = dmsh.generate(geo, 0.15)
import dmsh
import numpy

angles = numpy.pi * numpy.array([3.0 / 6.0, 7.0 / 6.0, 11.0 / 6.0])
geo = dmsh.Union(
    [
        dmsh.Circle([numpy.cos(angles[0]), numpy.sin(angles[0])], 1.0),
        dmsh.Circle([numpy.cos(angles[1]), numpy.sin(angles[1])], 1.0),
        dmsh.Circle([numpy.cos(angles[2]), numpy.sin(angles[2])], 1.0),
    ]
)
X, cells = dmsh.generate(geo, 0.15)

Intersection

intersection intersection-three-circles halfspace
import dmsh

geo = dmsh.Intersection([dmsh.Circle([0.0, -0.5], 1.0), dmsh.Circle([0.0, +0.5], 1.0)])
X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10)
import dmsh
import numpy

angles = numpy.pi * numpy.array([3.0 / 6.0, 7.0 / 6.0, 11.0 / 6.0])
geo = dmsh.Intersection(
    [
        dmsh.Circle([numpy.cos(angles[0]), numpy.sin(angles[0])], 1.5),
        dmsh.Circle([numpy.cos(angles[1]), numpy.sin(angles[1])], 1.5),
        dmsh.Circle([numpy.cos(angles[2]), numpy.sin(angles[2])], 1.5),
    ]
)
X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10)

The following uses the HalfSpace primtive for cutting of a circle.

import dmsh
import numpy

geo = dmsh.Intersection(
    [
        dmsh.HalfSpace(numpy.sqrt(0.5) * numpy.array([1.0, 1.0]), 0.0),
        dmsh.Circle([0.0, 0.0], 1.0),
    ]
)
X, cells = dmsh.generate(geo, 0.1)

Rotation, translation, scaling

rotation scaling
import dmsh
import numpy

geo = dmsh.Rotation(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), 0.1 * numpy.pi)
X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10)
import dmsh

geo = dmsh.Translation(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), [1.0, 1.0])
X, cells = dmsh.generate(geo, 0.1)
import dmsh

geo = dmsh.Scaling(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), 2.0)
X, cells = dmsh.generate(geo, 0.1, tol=1.0e-5)

Local refinement

refinement-line

All objects can be used to refine the mesh according to the distance to the object; e.g. a Path:

import dmsh

geo = dmsh.Rectangle(0.0, 1.0, 0.0, 1.0)

p1 = dmsh.Path([[0.4, 0.6], [0.6, 0.4]])


def edge_size(x):
    return 0.03 + 0.1 * p1.dist(x)


X, cells = dmsh.generate(geo, edge_size, tol=1.0e-10)

Custom shapes

It is also possible to define your own geometry. Simply create a class derived from dmsh.Geometry that contains a dist method and a method to project points onto the boundary.

import dmsh
import numpy


class MyDisk(dmsh.Geometry):
    def __init__(self):
        super().__init__()
        self.r = 1.0
        self.x0 = [0.0, 0.0]
        self.bounding_box = [-1.0, 1.0, -1.0, 1.0]
        self.feature_points = numpy.array([[], []]).T

    def dist(self, x):
        assert x.shape[0] == 2
        y = (x.T - self.x0).T
        return numpy.sqrt(numpy.einsum("i...,i...->...", y, y)) - self.r

    def boundary_step(self, x):
        # project onto the circle
        y = (x.T - self.x0).T
        r = numpy.sqrt(numpy.einsum("ij,ij->j", y, y))
        return ((y / r * self.r).T + self.x0).T


geo = MyDisk()
X, cells = dmsh.generate(geo, 0.1)

Debugging

level-set-poly level-set-rect-hole

dmsh is rather fragile, but sometimes the break-downs are due to an incorrectly defined geometry. Use

geo.show()

to inspect the level set function of your domain. (It must be negative inside the domain and positive outside. The 0-level set forms the domain boundary.)

Installation

dmsh is available from the Python Package Index, so simply type

pip install dmsh

to install.

Testing

To run the dmsh unit tests, check out this repository and type

MPLBACKEND=Agg pytest

(Setting the environment variable prevents the test figures from being displayed.)

License

This software is published under the MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for dmsh, version 0.2.4
Filename, size File type Python version Upload date Hashes
Filename, size dmsh-0.2.4-py3-none-any.whl (35.5 kB) File type Wheel Python version py3 Upload date Hashes View
Filename, size dmsh-0.2.4.tar.gz (37.3 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page