pax_global_header00006660000000000000000000000064135667557660014544gustar00rootroot0000000000000052 comment=a708de7384714932fc40c53154762ab5db9d12e2 dmsh-0.1.4/000077500000000000000000000000001356675576600125015ustar00rootroot00000000000000dmsh-0.1.4/.circleci/000077500000000000000000000000001356675576600143345ustar00rootroot00000000000000dmsh-0.1.4/.circleci/config.yml000066400000000000000000000013161356675576600163250ustar00rootroot00000000000000version: 2 jobs: lint: docker: - image: circleci/python:3 steps: - checkout - run: pip3 install -U black flake8 --user - run: black --check . - run: flake8 . build: working_directory: ~/work docker: - image: circleci/python:3 steps: - run: pip3 install -U pytest pytest-cov --user - checkout - run: pip3 install .[all] --user # The tests - run: command: pytest --cov dmsh working_directory: test/ environment: MPLBACKEND: Agg # submit to codecov - run: bash <(curl -s https://codecov.io/bash) workflows: version: 2 lint_and_build: jobs: - lint - build dmsh-0.1.4/.flake8000066400000000000000000000001531356675576600136530ustar00rootroot00000000000000[flake8] ignore = E203, E266, E501, W503 max-line-length = 80 max-complexity = 18 select = B,C,E,F,W,T4,B9 dmsh-0.1.4/.github/000077500000000000000000000000001356675576600140415ustar00rootroot00000000000000dmsh-0.1.4/.github/workflows/000077500000000000000000000000001356675576600160765ustar00rootroot00000000000000dmsh-0.1.4/.github/workflows/ci.yml000066400000000000000000000015541356675576600172210ustar00rootroot00000000000000name: ci on: [push] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v1 with: python-version: "3.x" - uses: actions/checkout@v1 - name: Lint with flake8 run: | pip install --upgrade pip pip install flake8 flake8 . - name: Lint with black run: | pip install black black --check . build: runs-on: ubuntu-latest steps: - uses: actions/setup-python@v1 with: python-version: "3.x" - uses: actions/checkout@v1 with: lfs: true - name: Install package run: | pip install --upgrade pip pip install .[all] - name: Test with pytest run: | pip install pytest pytest-cov pytest --cov dmsh # - name: Submit to codecov # run: bash <(curl -s https://codecov.io/bash) dmsh-0.1.4/.gitignore000066400000000000000000000001411356675576600144650ustar00rootroot00000000000000*.pyc *.swp *.prof MANIFEST README.rst dist/ build/ .coverage .cache/ *.egg-info/ .pytest_cache/ dmsh-0.1.4/LICENSE000066400000000000000000000020761356675576600135130ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2018-2019 Nico Schlömer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dmsh-0.1.4/Makefile000066400000000000000000000016321356675576600141430ustar00rootroot00000000000000VERSION=$(shell python3 -c "import dmsh; print(dmsh.__version__)") default: @echo "\"make publish\"?" # https://packaging.python.org/distributing/#id72 upload: setup.py # Make sure we're on the master branch @if [ "$(shell git rev-parse --abbrev-ref HEAD)" != "master" ]; then exit 1; fi rm -f dist/* python3 setup.py sdist python3 setup.py bdist_wheel twine upload dist/* tag: @if [ "$(shell git rev-parse --abbrev-ref HEAD)" != "master" ]; then exit 1; fi @echo "Tagging v$(VERSION)..." git tag v$(VERSION) git push --tags curl -H "Authorization: token `cat $(HOME)/.github-access-token`" -d '{"tag_name": "$(VERSION)"}' https://api.github.com/repos/nschloe/dmsh/releases publish: tag upload clean: @find . | grep -E "(__pycache__|\.pyc|\.pyo$\)" | xargs rm -rf @rm -rf *.egg-info/ build/ dist/ MANIFEST .pytest_cache/ format: isort -rc . black . black: black . lint: black --check . flake8 . dmsh-0.1.4/README.md000066400000000000000000000151541356675576600137660ustar00rootroot00000000000000

dmsh

The worst mesh generator you'll ever use.

[![CircleCI](https://img.shields.io/circleci/project/github/nschloe/dmsh/master.svg?style=flat-square)](https://circleci.com/gh/nschloe/dmsh/tree/master) [![codecov](https://img.shields.io/codecov/c/github/nschloe/dmsh.svg?style=flat-square)](https://codecov.io/gh/nschloe/dmsh) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square)](https://github.com/psf/black) [![PyPi Version](https://img.shields.io/pypi/v/dmsh.svg?style=flat-square)](https://pypi.org/project/dmsh) [![Debian CI](https://badges.debian.net/badges/debian/testing/python3-dmsh/version.svg?style=flat-square)](https://tracker.debian.org/pkg/python-dmsh) [![GitHub stars](https://img.shields.io/github/stars/nschloe/dmsh.svg?style=flat-square&logo=github&label=Stars&logoColor=white)](https://github.com/nschloe/dmsh) [![PyPi downloads](https://img.shields.io/pypi/dm/dmsh.svg?style=flat-square)](https://pypistats.org/packages/dmsh) Inspired by [distmesh](http://persson.berkeley.edu/distmesh/), dmsh * is slow, * requires a lot of memory, and * isn't terribly robust either. On the plus side, * it's got a usable interface, * is pure Python (and hence easily installable on any system), and * if it works, it produces pretty high-quality meshes. Combined with [optimesh](https://github.com/nschloe/optimesh), dmsh produces the highest-quality 2D meshes in the west. ### Examples #### Primitives ![circle](https://nschloe.github.io/dmsh/circle.png) | ![rectangle](https://nschloe.github.io/dmsh/rectangle.png) | ![polygon](https://nschloe.github.io/dmsh/polygon.png) |:---:|:---:|:---:| ```python 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) # and write it to a file import meshio meshio.write_points_cells("circle.vtk", X, {"triangle": cells}) ``` ```python geo = dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0) X, cells = dmsh.generate(geo, 0.1) ``` ```python 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](https://nschloe.github.io/dmsh/difference.png) | ![pacman](https://nschloe.github.io/dmsh/pacman.png) | ![square_hole_refined](https://nschloe.github.io/dmsh/square_hole_refined.png) :-------------------:|:------------------:|:----:| ```python 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) ``` ```python 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`. ```python 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](https://nschloe.github.io/dmsh/union.png) | ![union-rect](https://nschloe.github.io/dmsh/union_rectangles.png) | ![union-three-circles](https://nschloe.github.io/dmsh/union_three_circles.png) | :-------------------:|:------------------:|:----:| ```python 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) ``` ```python 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) ``` ```python 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](https://nschloe.github.io/dmsh/intersection.png) | ![intersection-three-circles](https://nschloe.github.io/dmsh/intersection_three_circles.png) | ![halfspace](https://nschloe.github.io/dmsh/halfspace.png) :-------------------:|:------------------:|:----:| ```python 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) ``` ```python 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. ```python 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](https://nschloe.github.io/dmsh/rotation.png) | ![scaling](https://nschloe.github.io/dmsh/scaling.png) |:----:|:----:| ```python 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) ``` ```python geo = dmsh.Translation(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), [1.0, 1.0]) X, cells = dmsh.generate(geo, 0.1, show=show) ``` ```python geo = dmsh.Scaling(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), 2.0) X, cells = dmsh.generate(geo, 0.1, show=show, tol=1.0e-5) ``` ### Local refinement ![refinement-line](https://nschloe.github.io/dmsh/refinement_line.png) All objects can be used to refine the mesh according to the distance to the object; e.g. a `Path`: ```python 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, show=show, tol=1.0e-10) ``` ### Installation dmsh is [available from the Python Package Index](https://pypi.org/project/dmsh/), so simply type ``` pip3 install dmsh --user ``` to install. ### Testing To run the dmsh unit tests, check out this repository and type ``` pytest ``` ### License dmsh is published under the [MIT license](https://en.wikipedia.org/wiki/MIT_License). dmsh-0.1.4/codecov.yml000066400000000000000000000002721356675576600146470ustar00rootroot00000000000000comment: no # https://github.com/codecov/support/issues/396#issuecomment-300879528 codecov: disable_default_path_fixes: true fixes: - ".*/dist-packages/::" - ".*/site-packages/::" dmsh-0.1.4/dmsh/000077500000000000000000000000001356675576600134345ustar00rootroot00000000000000dmsh-0.1.4/dmsh/__about__.py000066400000000000000000000004211356675576600157110ustar00rootroot00000000000000__author__ = "Nico Schlömer" __email__ = "nico.schloemer@gmail.com" __copyright__ = "Copyright (c) 2018-2019, {} <{}>".format(__author__, __email__) __license__ = "License :: OSI Approved :: MIT License" __version__ = "0.1.4" __status__ = "Development Status :: 4 - Beta" dmsh-0.1.4/dmsh/__init__.py000066400000000000000000000012041356675576600155420ustar00rootroot00000000000000from .__about__ import __author__, __email__, __license__, __status__, __version__ from .geometry import ( Circle, Difference, Ellipse, HalfSpace, Intersection, Path, Polygon, Rectangle, Rotation, Scaling, Stretch, Translation, Union, ) from .main import generate __all__ = [ "__author__", "__email__", "__license__", "__version__", "__status__", "generate", "Circle", "Difference", "Ellipse", "HalfSpace", "Intersection", "Path", "Polygon", "Rectangle", "Rotation", "Stretch", "Scaling", "Translation", "Union", ] dmsh-0.1.4/dmsh/geometry/000077500000000000000000000000001356675576600152675ustar00rootroot00000000000000dmsh-0.1.4/dmsh/geometry/__init__.py000066400000000000000000000011541356675576600174010ustar00rootroot00000000000000from .circle import Circle from .difference import Difference from .ellipse import Ellipse from .halfspace import HalfSpace from .intersection import Intersection from .path import Path from .polygon import Polygon from .rectangle import Rectangle from .rotation import Rotation from .scaling import Scaling from .stretch import Stretch from .translation import Translation from .union import Union __all__ = [ "Circle", "Difference", "Ellipse", "HalfSpace", "Intersection", "Path", "Polygon", "Rectangle", "Rotation", "Scaling", "Stretch", "Translation", "Union", ] dmsh-0.1.4/dmsh/geometry/circle.py000066400000000000000000000025751356675576600171130ustar00rootroot00000000000000import numpy class CirclePath: def __init__(self, x0, r): self.x0 = x0 self.r = r return def p(self, t): v = numpy.array([numpy.cos(2 * numpy.pi * t), numpy.sin(2 * numpy.pi * t)]) return ((self.r * v).T + self.x0).T def dp_dt(self, t): return ( self.r * 2 * numpy.pi * numpy.array([-numpy.sin(2 * numpy.pi * t), numpy.cos(2 * numpy.pi * t)]) ) class Circle: def __init__(self, x0, r): self.x0 = x0 self.r = r self.bounding_box = [x0[0] - r, x0[0] + r, x0[1] - r, x0[1] + r] self.paths = [CirclePath(x0, r)] self.feature_points = numpy.array([[], []]).T return def plot(self, color="#1f77b4"): import matplotlib.pyplot as plt t = numpy.linspace(0.0, 2 * numpy.pi, 100) plt.plot( self.x0[0] + self.r * numpy.cos(t), self.x0[1] + self.r * numpy.sin(t), "-", color=color, ) return def dist(self, x): assert x.shape[0] == 2 y = (x.T - self.x0).T return numpy.sqrt(numpy.einsum("ij,ij->j", y, y)) - self.r def boundary_step(self, x): # simply 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 dmsh-0.1.4/dmsh/geometry/difference.py000066400000000000000000000036771356675576600177500ustar00rootroot00000000000000import numpy from ..helpers import find_feature_points class Difference: def __init__(self, geo0, geo1): self.geo0 = geo0 self.geo1 = geo1 self.bounding_box = geo0.bounding_box fp = [geo0.feature_points, geo1.feature_points] fp.append(find_feature_points([geo0, geo1])) self.feature_points = numpy.concatenate(fp) # Only keep the feature points on the outer boundary alpha = self.dist(self.feature_points.T) tol = 1.0e-5 is_on_boundary = (-tol < alpha) & (alpha < tol) self.feature_points = self.feature_points[is_on_boundary] return def plot(self): self.geo0.plot() self.geo1.plot() return def dist(self, x): return numpy.max([self.geo0.dist(x), -self.geo1.dist(x)], axis=0) # Choose tolerance above sqrt(machine_eps). This is necessary as the polygon # dist() is only accurate to that precision. def boundary_step(self, x, tol=1.0e-7, max_steps=100): # step for the is_inside with the smallest value alpha0 = self.geo0.dist(x) alpha1 = self.geo1.dist(x) # Scale the tolerance with the domain diameter. This is necessary at least for # polygons where the distance calculation is flawed with round-off proportional # to the edge lengths. try: tol *= self.geo0.diameter except AttributeError: pass k = 0 while numpy.any(alpha0 > tol) or numpy.any(alpha1 < -tol): assert k <= max_steps, "Exceeded maximum number of boundary steps." k += 1 idx0 = alpha0 > tol if numpy.any(idx0): x[:, idx0] = self.geo0.boundary_step(x[:, idx0]) idx1 = alpha1 < -tol if numpy.any(idx1): x[:, idx1] = self.geo1.boundary_step(x[:, idx1]) alpha0 = self.geo0.dist(x) alpha1 = self.geo1.dist(x) return x dmsh-0.1.4/dmsh/geometry/ellipse.py000066400000000000000000000033151356675576600173000ustar00rootroot00000000000000import numpy from ..helpers import multi_newton class Ellipse: def __init__(self, x0, a, b): self.x0 = x0 self.a = a self.b = b self.bounding_box = [x0[0] - a, x0[0] + a, x0[1] - b, x0[1] + b] self.feature_points = numpy.array([]) return def plot(self, color="#1f77b4"): import matplotlib.pyplot as plt t = numpy.linspace(0.0, 2 * numpy.pi, 100) plt.plot( self.x0[0] + self.a * numpy.cos(t), self.x0[1] + self.b * numpy.sin(t), "-", color=color, ) return def dist(self, x): assert x.shape[0] == 2 return ( ((x[0] - self.x0[0]) / self.a) ** 2 + ((x[1] - self.x0[1]) / self.b) ** 2 - 1.0 ) def _boundary_step(self, x): ax = (x[0] - self.x0[0]) / self.a ay = (x[1] - self.x0[1]) / self.b alpha = ax ** 2 + ay ** 2 - 1.0 jac = numpy.array([4 * alpha * ax / self.a, 4 * alpha * ay / self.b]) dalpha_dx = 2 * ax / self.a dalpha_dy = 2 * ay / self.b hess = numpy.array( [ [ 4 * dalpha_dx * ax / self.a + 4 * alpha / self.a ** 2, 4 * dalpha_dy * ax / self.a, ], [ 4 * dalpha_dx * ay / self.b, 4 * dalpha_dy * ay / self.b + 4 * alpha / self.b ** 2, ], ] ) p = -numpy.linalg.solve(numpy.moveaxis(hess, -1, 0), jac.T) return x + p.T def boundary_step(self, x): return multi_newton( x.T, self.dist, self._boundary_step, 1.0e-10, max_num_steps=10 ).T dmsh-0.1.4/dmsh/geometry/halfspace.py000066400000000000000000000026231356675576600175720ustar00rootroot00000000000000import numpy class LinePath: def __init__(self, v, tangent): self.v = v self.tangent = tangent return def p(self, t): """This parametrization of the line is (inf, inf) for t=0 and t=1. """ # Don't warn on division by 0 with numpy.errstate(divide="ignore"): out = ( numpy.multiply.outer(self.tangent, (2 * t - 1) / t / (1 - t)).T + self.v ).T return out def dp_dt(self, t): with numpy.errstate(divide="ignore"): dt = 1 / t ** 2 + 1 / (1 - t) ** 2 return numpy.multiply.outer(self.tangent, dt) class HalfSpace: def __init__(self, normal, alpha): self.normal = normal self.alpha = alpha self.bounding_box = [-numpy.inf, +numpy.inf, -numpy.inf, +numpy.inf] self.feature_points = numpy.array([]) # One point on the line: v = self.normal / numpy.dot(self.normal, self.normal) * self.alpha tangent = numpy.array([-self.normal[1], self.normal[0]]) self.paths = [LinePath(v, tangent)] return def dist(self, x): assert x.shape[0] == 2 return self.alpha - numpy.dot(self.normal, x) def boundary_step(self, x): beta = self.alpha - numpy.dot(self.normal, x) / numpy.dot( self.normal, self.normal ) return x + numpy.multiply.outer(self.normal, beta) dmsh-0.1.4/dmsh/geometry/intersection.py000066400000000000000000000030331356675576600203460ustar00rootroot00000000000000import numpy from ..helpers import find_feature_points class Intersection: def __init__(self, geometries): self.geometries = geometries self.bounding_box = [ numpy.max([geo.bounding_box[0] for geo in geometries]), numpy.min([geo.bounding_box[1] for geo in geometries]), numpy.max([geo.bounding_box[2] for geo in geometries]), numpy.min([geo.bounding_box[3] for geo in geometries]), ] self.feature_points = find_feature_points(geometries) return def plot(self, color="b"): for geo in self.geometries: geo.plot() return def dist(self, x): return numpy.max([geo.dist(x) for geo in self.geometries], axis=0) def boundary_step(self, x, tol=1.0e-12): # step for the is_inside with the smallest value alpha = numpy.array([geo.dist(x) for geo in self.geometries]) while numpy.any(alpha > tol): # Only consider the nodes which are truly outside of the domain has_pos = numpy.any(alpha > tol, axis=0) x_pos = x[:, has_pos] alpha_pos = alpha[:, has_pos] alpha_pos[alpha_pos < tol] = numpy.inf idx = numpy.argmin(alpha_pos, axis=0) for k, geo in enumerate(self.geometries): if numpy.any(idx == k): x_pos[:, idx == k] = geo.boundary_step(x_pos[:, idx == k]) x[:, has_pos] = x_pos alpha = numpy.array([geo.dist(x) for geo in self.geometries]) return x dmsh-0.1.4/dmsh/geometry/path.py000066400000000000000000000020271356675576600165760ustar00rootroot00000000000000import numpy import pypathlib class LineSegmentPath: def __init__(self, x0, x1): self.x0 = x0 self.x1 = x1 return def p(self, t): return numpy.multiply.outer(self.x0, 1 - t) + numpy.multiply.outer(self.x1, t) def dp_dt(self, t): ones = numpy.ones(t.shape) return numpy.multiply.outer(self.x0, -ones) + numpy.multiply.outer( self.x1, ones ) class Path: def __init__(self, points): points = numpy.array(points) self.path = pypathlib.Path(points) self.bounding_box = [ numpy.min(points[:, 0]), numpy.max(points[:, 0]), numpy.min(points[:, 1]), numpy.max(points[:, 1]), ] self.feature_points = points self.paths = [ LineSegmentPath(p0, p1) for p0, p1 in zip(points[:-1], points[1:]) ] return def dist(self, x): return self.path.distance(x.T) def boundary_step(self, x): return self.path.closest_points(x.T).T dmsh-0.1.4/dmsh/geometry/polygon.py000066400000000000000000000022441356675576600173320ustar00rootroot00000000000000import numpy import pypathlib class LineSegmentPath: def __init__(self, x0, x1): self.x0 = x0 self.x1 = x1 return def p(self, t): return numpy.multiply.outer(self.x0, 1 - t) + numpy.multiply.outer(self.x1, t) def dp_dt(self, t): ones = numpy.ones(t.shape) return numpy.multiply.outer(self.x0, -ones) + numpy.multiply.outer( self.x1, ones ) class Polygon: def __init__(self, points): points = numpy.array(points) self.bounding_box = [ numpy.min(points[:, 0]), numpy.max(points[:, 0]), numpy.min(points[:, 1]), numpy.max(points[:, 1]), ] self.polygon = pypathlib.ClosedPath(points) self.feature_points = points self.paths = [ LineSegmentPath(p0, p1) for p0, p1 in zip(points, numpy.roll(points, -1, axis=0)) ] self.diameter = self.polygon.diameter def plot(self): self.polygon.plot(color="C0") def dist(self, x): return self.polygon.signed_distance(x.T) def boundary_step(self, x): return self.polygon.closest_points(x.T).T dmsh-0.1.4/dmsh/geometry/rectangle.py000066400000000000000000000003651356675576600176110ustar00rootroot00000000000000import numpy from .polygon import Polygon class Rectangle(Polygon): def __init__(self, x0, x1, y0, y1): points = numpy.array([[x0, y0], [x1, y0], [x1, y1], [x0, y1]]) super(Rectangle, self).__init__(points) return dmsh-0.1.4/dmsh/geometry/rotation.py000066400000000000000000000022721356675576600175030ustar00rootroot00000000000000import numpy class Rotation: def __init__(self, geometry, angle): self.geometry = geometry self.R = numpy.array( [ [+numpy.cos(angle), -numpy.sin(angle)], [+numpy.sin(angle), +numpy.cos(angle)], ] ) self.R_inv = numpy.array( [ [+numpy.cos(angle), +numpy.sin(angle)], [-numpy.sin(angle), +numpy.cos(angle)], ] ) # bounding box bb = geometry.bounding_box corners = numpy.array( [[bb[0], bb[2]], [bb[1], bb[2]], [bb[1], bb[3]], [bb[0], bb[3]]] ) rotated_corners = numpy.dot(self.R, corners.T) self.bounding_box = [ numpy.min(rotated_corners[0]), numpy.max(rotated_corners[0]), numpy.min(rotated_corners[1]), numpy.max(rotated_corners[1]), ] self.feature_points = numpy.array([]) return def dist(self, x): return self.geometry.dist(numpy.dot(self.R_inv, x)) def boundary_step(self, x): y = numpy.dot(self.R_inv, x) y2 = self.geometry.boundary_step(y) return numpy.dot(self.R, y2) dmsh-0.1.4/dmsh/geometry/scaling.py000066400000000000000000000006721356675576600172660ustar00rootroot00000000000000import numpy class Scaling: def __init__(self, geometry, alpha): self.geometry = geometry self.alpha = alpha self.bounding_box = alpha * numpy.array(geometry.bounding_box) self.feature_points = numpy.array([]) return def dist(self, x): return self.geometry.dist(x / self.alpha) def boundary_step(self, x): return self.geometry.boundary_step(x / self.alpha) * self.alpha dmsh-0.1.4/dmsh/geometry/stretch.py000066400000000000000000000024761356675576600173260ustar00rootroot00000000000000import numpy class Stretch: def __init__(self, geometry, v): self.geometry = geometry self.alpha = numpy.sqrt(numpy.dot(v, v)) self.v = v / self.alpha # bounding box bb = geometry.bounding_box corners = numpy.array( [[bb[0], bb[2]], [bb[1], bb[2]], [bb[1], bb[3]], [bb[0], bb[3]]] ) vx = numpy.multiply.outer(numpy.dot(self.v, corners.T), self.v) stretched_corners = (vx * self.alpha + (corners - vx)).T self.bounding_box = [ numpy.min(stretched_corners[0]), numpy.max(stretched_corners[0]), numpy.min(stretched_corners[1]), numpy.max(stretched_corners[1]), ] self.feature_points = numpy.array([]) return def plot(self): return def dist(self, x): # scale the component of x in direction v by 1/alpha vx = numpy.multiply.outer(numpy.dot(self.v, x), self.v) y = vx / self.alpha + (x.T - vx) return self.geometry.dist(y.T) def boundary_step(self, x): vx = numpy.multiply.outer(numpy.dot(self.v, x), self.v) y = vx / self.alpha + (x.T - vx) y2 = self.geometry.boundary_step(y.T) vy2 = numpy.multiply.outer(numpy.dot(self.v, y2), self.v) return (vy2 * self.alpha + (y2.T - vy2)).T dmsh-0.1.4/dmsh/geometry/translation.py000066400000000000000000000011161356675576600201760ustar00rootroot00000000000000import numpy class Translation: def __init__(self, geometry, v): self.geometry = geometry self.v = v self.bounding_box = [ geometry.bounding_box[0] + v[0], geometry.bounding_box[1] + v[0], geometry.bounding_box[2] + v[1], geometry.bounding_box[3] + v[1], ] self.feature_points = numpy.array([]) return def dist(self, x): return self.geometry.dist((x.T - self.v).T) def boundary_step(self, x): return (self.geometry.boundary_step((x.T - self.v).T).T + self.v).T dmsh-0.1.4/dmsh/geometry/union.py000066400000000000000000000031031356675576600167660ustar00rootroot00000000000000import numpy from ..helpers import find_feature_points class Union: def __init__(self, geometries): self.geometries = geometries self.bounding_box = [ numpy.min([geo.bounding_box[0] for geo in geometries]), numpy.max([geo.bounding_box[1] for geo in geometries]), numpy.min([geo.bounding_box[2] for geo in geometries]), numpy.max([geo.bounding_box[3] for geo in geometries]), ] fp = [geo.feature_points for geo in geometries] fp.append(find_feature_points(geometries)) self.feature_points = numpy.concatenate(fp) # Only keep the feature points on the outer boundary alpha = numpy.array([geo.dist(self.feature_points.T) for geo in geometries]) tol = 1.0e-5 is_on_boundary = numpy.all(alpha > -tol, axis=0) self.feature_points = self.feature_points[is_on_boundary] self.paths = [path for geo in self.geometries for path in geo.paths] return def plot(self): for geo in self.geometries: geo.plot() return def dist(self, x): return numpy.min([geo.dist(x) for geo in self.geometries], axis=0) def boundary_step(self, x): # step for the is_inside with the smallest value alpha = numpy.array([geo.dist(x) for geo in self.geometries]) alpha[alpha < 0] = numpy.inf idx = numpy.argmin(alpha, axis=0) for k, geo in enumerate(self.geometries): if numpy.any(idx == k): x[:, idx == k] = geo.boundary_step(x[:, idx == k]) return x dmsh-0.1.4/dmsh/helpers.py000066400000000000000000000113321356675576600154500ustar00rootroot00000000000000import numpy def unique_rows(a): # The cleaner alternative `numpy.unique(a, axis=0)` is slow; cf. # . b = numpy.ascontiguousarray(a).view( numpy.dtype((numpy.void, a.dtype.itemsize * a.shape[1])) ) a_unique, inv, cts = numpy.unique(b, return_inverse=True, return_counts=True) a_unique = a_unique.view(a.dtype).reshape(-1, a.shape[1]) return a_unique, inv, cts def multi_newton(x0, is_inside, boundary_step, tol, max_num_steps=10): """Newton's minimization method for multiple starting points. """ x = x0.copy() fx = is_inside(x.T) k = 0 mask = numpy.abs(fx) > tol while numpy.any(mask): x[mask] = boundary_step(x[mask].T).T fx = is_inside(x.T) mask = numpy.abs(fx) > tol k += 1 if k >= max_num_steps: break return x def show(pts, cells, geo, title=None): import matplotlib.pyplot as plt eps = 1.0e-10 is_inside = geo.dist(pts.T) < eps plt.plot(pts[is_inside, 0], pts[is_inside, 1], ".") plt.plot(pts[~is_inside, 0], pts[~is_inside, 1], ".", color="r") plt.triplot(pts[:, 0], pts[:, 1], cells) plt.axis("square") if title is not None: plt.title(title) try: geo.plot() except AttributeError: pass plt.show() return def find_feature_points(geometries, num_steps=10): n = len(geometries) # collect path pairs path_pairs = [ [item0, item1] for i in range(n) for j in range(i + 1, n) for item0 in geometries[i].paths for item1 in geometries[j].paths ] points = numpy.column_stack( [ _find_feature_points_between_two_paths(path0, path1, num_steps) for path0, path1 in path_pairs ] ) if points.shape[1] > 0: points = unique_float_cols(points) return points.T def _find_feature_points_between_two_paths(path0, path1, num_steps): """Given two geometries with their parameterization, this methods finds feature points, i.e., points where the boundaries meet. This is done by casting a net over the parameter space and performing `num_steps` Newton steps. Found solutions are checked for uniqueness. """ # Throw a net t0, t1 = numpy.meshgrid(numpy.linspace(0.0, 1.0, 11), numpy.linspace(0.0, 1.0, 11)) t = numpy.array([t0, t1]).reshape(2, -1) # t = numpy.random.rand(2, 100) tol = 1.0e-20 # multi_newton(x0, is_inside, boundary_step, tol, max_num_steps=10): solutions = [] for k in range(num_steps): f_t = path0.p(t[0]) - path1.p(t[1]) # remove all inf values is_infinite = numpy.any(numpy.isinf(f_t), axis=0) if numpy.any(is_infinite): t = t[:, ~is_infinite] f_t = f_t[:, ~is_infinite] f_dot_f = numpy.einsum("ij,ij->j", f_t, f_t) is_sol = f_dot_f < tol if numpy.any(is_sol): solutions.append(t[:, is_sol]) # remove all converged solutions t = t[:, ~is_sol] f_t = f_t[:, ~is_sol] jac_t = numpy.moveaxis( numpy.stack([path0.dp_dt(t[0]), -path1.dp_dt(t[1])]), 0, 1 ) # Kick out singular matrices det = jac_t[0, 0] * jac_t[1, 1] - jac_t[0, 1] * jac_t[1, 0] is_singular = numpy.abs(det) < 1.0e-13 if numpy.any(is_singular): t = t[:, ~is_singular] f_t = f_t[:, ~is_singular] jac_t = jac_t[..., ~is_singular] # Simply make it explicitly. sols = [] for k in range(f_t.shape[-1]): try: sols.append(numpy.linalg.solve(jac_t[..., k], f_t[:, k])) except numpy.linalg.linalg.LinAlgError: # singular matrix sols.append(numpy.zeros(f_t[:, k].shape)) sols = numpy.array(sols).T # Newton step t -= sols # Kick out everything that leaves the unit square still_good = numpy.all((0.0 <= t) & (t <= 1.0), axis=0) t = t[:, still_good] if solutions: unique_sols = unique_float_cols(numpy.column_stack(solutions)) points0 = path0.p(unique_sols[0]) # points1 = path1.p(unique_sols[1]) else: points0 = numpy.array([[], []]) return points0 def unique_float_cols(data, k=0, tol=1.0e-10): """In a (k, n) array `data`, find the unique columns. """ if k == data.shape[0]: return data[:, 0] idx = numpy.argsort(data[k]) data = data[:, idx] diff = data[k, 1:] - data[k, :-1] cut = diff > tol idx = numpy.where(cut)[0] chunks = numpy.split(data, idx + 1, axis=1) out = numpy.column_stack([unique_float_cols(chunk, k + 1, tol) for chunk in chunks]) return out dmsh-0.1.4/dmsh/main.py000066400000000000000000000126701356675576600147400ustar00rootroot00000000000000import numpy import scipy.spatial from .helpers import show as show_mesh from .helpers import unique_rows def _recell(pts, geo): # compute Delaunay triangulation tri = scipy.spatial.Delaunay(pts) cells = tri.simplices.copy() # kick out all cells whose barycenter is not in the geometry bc = numpy.sum(pts[cells], axis=1) / 3.0 cells = cells[geo.dist(bc.T) < 0.0] # Determine edges edges = numpy.concatenate([cells[:, [0, 1]], cells[:, [1, 2]], cells[:, [2, 0]]]) edges = numpy.sort(edges, axis=1) edges, _, _ = unique_rows(edges) return cells, edges def create_staggered_grid(h, bounding_box): x_step = h y_step = h * numpy.sqrt(3) / 2 bb_width = bounding_box[1] - bounding_box[0] bb_height = bounding_box[3] - bounding_box[2] midpoint = [ (bounding_box[0] + bounding_box[1]) / 2, (bounding_box[2] + bounding_box[3]) / 2, ] num_x_steps = int(bb_width / x_step) if num_x_steps % 2 == 1: num_x_steps -= 1 num_y_steps = int(bb_height / y_step) if num_y_steps % 2 == 1: num_y_steps -= 1 # Generate initial (staggered) point list from bounding box # Make the meshgrid symmetric around the bounding box midpoint x, y = numpy.meshgrid( midpoint[0] + x_step * numpy.arange(-num_x_steps // 2, num_x_steps // 2 + 1), midpoint[1] + y_step * numpy.arange(-num_y_steps // 2, num_y_steps // 2 + 1), ) # staggered, such that the midpoint is not moved x[num_x_steps % 2 :: 2] += h / 2 return numpy.column_stack([x.reshape(-1), y.reshape(-1)]) def generate( geo, edge_size, f_scale=1.2, delta_t=0.2, tol=1.0e-5, random_seed=0, show=False, max_steps=10000, verbose=False, ): # Find h0 from edge_size (function) if callable(edge_size): edge_size_function = edge_size # Find h0 by sampling h00 = (geo.bounding_box[1] - geo.bounding_box[0]) / 100 pts = create_staggered_grid(h00, geo.bounding_box) sizes = edge_size_function(pts.T) assert numpy.all(sizes > 0.0), "edge_size_function must be strictly positive." h0 = numpy.min(sizes) else: h0 = edge_size def edge_size_function(pts): return numpy.full(pts.shape[1], edge_size) if random_seed is not None: numpy.random.seed(random_seed) pts = create_staggered_grid(h0, geo.bounding_box) eps = 1.0e-10 # remove points outside of the region pts = pts[geo.dist(pts.T) < eps] # evaluate the element size function, remove points according to it alpha = 1.0 / edge_size_function(pts.T) ** 2 pts = pts[numpy.random.rand(pts.shape[0]) < alpha / numpy.max(alpha)] num_feature_points = geo.feature_points.shape[0] if num_feature_points > 0: # remove all points which are equal to a feature point diff = numpy.array([[pt - fp for fp in geo.feature_points] for pt in pts]) dist = numpy.einsum("...k,...k->...", diff, diff) ftol = h0 / 10 equals_feature_point = numpy.any(dist < ftol ** 2, axis=1) pts = pts[~equals_feature_point] # Add feature points pts = numpy.concatenate([geo.feature_points, pts]) cells, edges = _recell(pts, geo) pts_old = pts.copy() k = 0 while True: if verbose: print("step {}".format(k)) assert k <= max_steps, "Exceeded max_steps ({}).".format(max_steps) k += 1 diff = pts - pts_old move2 = numpy.einsum("ij,ij->i", diff, diff) if numpy.any(move2 > 1.0e-2 ** 2): pts_old = pts.copy() cells, edges = _recell(pts, geo) if show: show_mesh(pts, cells, geo) edges_vec = pts[edges[:, 1]] - pts[edges[:, 0]] edge_lengths = numpy.sqrt(numpy.einsum("ij,ij->i", edges_vec, edges_vec)) edges_vec /= edge_lengths[..., None] # Evaluate element sizes at edge midpoints edge_midpoints = (pts[edges[:, 1]] + pts[edges[:, 0]]) / 2 p = edge_size_function(edge_midpoints.T) desired_lengths = ( f_scale * p * numpy.sqrt(numpy.dot(edge_lengths, edge_lengths) / numpy.dot(p, p)) ) force_abs = desired_lengths - edge_lengths # only consider repulsive forces force_abs[force_abs < 0.0] = 0.0 # force vectors force = edges_vec * force_abs[..., None] # bincount replacement for the slow numpy.add.at # more speed-up can be achieved if the weights where contiguous in memory, i.e., # if force[k] was used n = pts.shape[0] force_per_node = numpy.array( [ numpy.bincount(edges[:, 0], weights=-force[:, k], minlength=n) + numpy.bincount(edges[:, 1], weights=+force[:, k], minlength=n) for k in range(force.shape[1]) ] ).T update = delta_t * force_per_node pts_old2 = pts.copy() pts[num_feature_points:] += update[num_feature_points:] # Some boundary points may have been pushed outside; bring them back onto the # boundary. is_outside = geo.dist(pts.T) > 0.0 pts[is_outside] = geo.boundary_step(pts[is_outside].T).T diff = pts - pts_old2 move2 = numpy.einsum("ij,ij->i", diff, diff) if verbose: print("max_move: {:.6e}".format(numpy.sqrt(numpy.max(move2)))) if numpy.all(move2 < tol ** 2): break return pts, cells dmsh-0.1.4/setup.py000066400000000000000000000025341356675576600142170ustar00rootroot00000000000000import codecs import os from setuptools import find_packages, setup # https://packaging.python.org/single_source_version/ base_dir = os.path.abspath(os.path.dirname(__file__)) about = {} with open(os.path.join(base_dir, "dmsh", "__about__.py"), "rb") as f: exec(f.read(), about) def read(fname): return codecs.open(os.path.join(base_dir, fname), encoding="utf-8").read() setup( name="dmsh", version=about["__version__"], packages=find_packages(), url="https://github.com/nschloe/dmsh", author=about["__author__"], author_email=about["__email__"], install_requires=["numpy", "scipy", "pypathlib >= 0.1.3"], extras_require={"all": ["matplotlib"], "plot": ["matplotlib"]}, python_requires=">=3", description="High-quality 2D mesh generator based on distmesh", long_description=read("README.md"), long_description_content_type="text/markdown", license=about["__license__"], classifiers=[ about["__license__"], about["__status__"], "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Mathematics", ], entry_points={ "console_scripts": ["dmsh-image = dmsh.cli:image", "dmsh-poly = dmsh.cli:poly"] }, ) dmsh-0.1.4/test/000077500000000000000000000000001356675576600134605ustar00rootroot00000000000000dmsh-0.1.4/test/Makefile000066400000000000000000000003141356675576600151160ustar00rootroot00000000000000default: @echo `make png`? png: for file in test_*.py; do \ python3 $$file; \ done for file in *.png; do convert $$file -trim -resize x200 $$file; done for file in *.png; do optipng $$file; done dmsh-0.1.4/test/helpers.py000066400000000000000000000014061356675576600154750ustar00rootroot00000000000000import numpy def assert_norm_equality(X, ref_norm, tol): ref_norm = numpy.array(ref_norm) vals = numpy.array( [ numpy.linalg.norm(X, ord=1), numpy.linalg.norm(X, ord=2), numpy.linalg.norm(X, ord=numpy.inf), ] ) assert numpy.all( numpy.abs(vals - ref_norm) < tol * ref_norm ), "Expected: [{:.16e}, {:.16e}, {:.16e}]\nComputed: [{:.16e}, {:.16e}, {:.16e}]".format( *ref_norm, *vals ) return def save(filename, X, cells): import meshplex mesh = meshplex.MeshTri(X, cells, flat_cell_correction=None) mesh.save( filename, show_centroids=False, show_coedges=False, show_axes=False, nondelaunay_edge_color="k", ) return dmsh-0.1.4/test/logo.py000066400000000000000000000002051356675576600147670ustar00rootroot00000000000000import meshio import meshzoo points, cells = meshzoo.triangle(2) meshio.write_points_cells("logo.svg", points, {"triangle": cells}) dmsh-0.1.4/test/test_circle.py000066400000000000000000000006741356675576600163410ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_circle(show=True): geo = dmsh.Circle([0.0, 0.0], 1.0) X, cells = dmsh.generate(geo, 0.1, show=show) ref_norms = [3.2795193920779542e02, 1.4263721858241993e01, 1.0000000000000000e00] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_circle(show=False) save("circle.png", X, cells) dmsh-0.1.4/test/test_difference.py000066400000000000000000000010111356675576600171540ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_difference(show=False): 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, show=show) geo.plot() ref_norms = [2.9445552442961758e02, 1.5856356670813716e01, 1.4999999157880513e00] assert_norm_equality(X.flatten(), ref_norms, 1.0e-9) return X, cells if __name__ == "__main__": X, cells = test_difference(show=False) save("difference.png", X, cells) dmsh-0.1.4/test/test_ellipse.py000066400000000000000000000007261356675576600165330ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_ellipse(show=False): geo = dmsh.Ellipse([0.0, 0.0], 2.0, 1.0) X, cells = dmsh.generate(geo, 0.2, show=show) geo.plot() ref_norms = [2.5108886251367960e02, 1.5652935519539316e01, 1.9890059982474428e00] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_ellipse(show=False) save("ellipse.png", X, cells) dmsh-0.1.4/test/test_halfspace.py000066400000000000000000000011331356675576600170150ustar00rootroot00000000000000import numpy import dmsh from helpers import assert_norm_equality, save def test_halfspace(show=False): 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, show=show) ref_norms = [1.6445971629723411e02, 1.0032823867864321e01, 9.9962000746451751e-01] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_halfspace(show=False) save("halfspace.png", X, cells) dmsh-0.1.4/test/test_intersection.py000066400000000000000000000010601356675576600175740ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_intersection(show=False): 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, show=show, tol=1.0e-10) geo.plot() ref_norms = [8.6619344595913475e01, 6.1599895121114274e00, 8.6602540378466342e-01] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_intersection(show=False) save("intersection.png", X, cells) dmsh-0.1.4/test/test_intersection_three_circles.py000066400000000000000000000014471356675576600225000ustar00rootroot00000000000000import numpy import dmsh from helpers import assert_norm_equality, save def test_union(show=False): 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, show=show, tol=1.0e-10) ref_norms = [6.8247386668599034e01, 5.1256971008793917e00, 7.2474487138537913e-01] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_union(show=False) save("intersection_three_circles.png", X, cells) dmsh-0.1.4/test/test_large.py000066400000000000000000000007051356675576600161650ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality def test_large(): # https://github.com/nschloe/dmsh/issues/11 r = dmsh.Rectangle(-10.0, +20.0, -10.0, +20.0) c = dmsh.Circle([0.0, 0.0], 3) geo = dmsh.Difference(r, c) X, cells = dmsh.generate(geo, 2.0, tol=1.0e-5, max_steps=10000) ref_norms = [4.4714910237327376e03, 2.3785742555298563e02, 2.0000000000000000e01] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) dmsh-0.1.4/test/test_pacman.py000066400000000000000000000010251356675576600163260ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_pacman(show=False): 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, show=show, tol=1.0e-10) ref_norms = [3.0385105041432689e02, 1.3644964912810719e01, 1.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_pacman(show=False) save("pacman.png", X, cells) dmsh-0.1.4/test/test_polygon.py000066400000000000000000000011401356675576600165540ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test(show=False): 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, show=show) geo.plot() ref_norms = [4.1468030858462305e02, 2.1861920662017866e01, 2.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-5) return X, cells if __name__ == "__main__": X, cells = test(show=False) save("polygon.png", X, cells) dmsh-0.1.4/test/test_rectangle.py000066400000000000000000000006761356675576600170460ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_rectangle(show=False): geo = dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0) X, cells = dmsh.generate(geo, 0.1, show=show) ref_norms = [9.7542898028694776e02, 3.1710503119308623e01, 2.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_rectangle(show=False) save("rectangle.png", X, cells) dmsh-0.1.4/test/test_refinement_point_line.py000066400000000000000000000011171356675576600214450ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test(show=False): geo = dmsh.Rectangle(0.0, 1.0, 0.0, 1.0) # p0 = dmsh.Path([[0.0, 0.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, show=show, tol=1.0e-10) ref_norms = [3.8484999502901326e02, 1.5617044862848489e01, 1.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test(show=False) save("refinement_line.png", X, cells) dmsh-0.1.4/test/test_rotation.py000066400000000000000000000007651356675576600167400ustar00rootroot00000000000000import numpy import dmsh from helpers import assert_norm_equality, save def test(show=False): geo = dmsh.Rotation(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), 0.1 * numpy.pi) X, cells = dmsh.generate(geo, 0.1, show=show, tol=1.0e-10) ref_norms = [9.5457720168192884e02, 3.1356929329612782e01, 2.2111300269652543e00] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test(show=False) save("rotation.png", X, cells) dmsh-0.1.4/test/test_scaling.py000066400000000000000000000007071356675576600165150ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test(show=False): geo = dmsh.Scaling(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), 2.0) X, cells = dmsh.generate(geo, 0.1, show=show, tol=1.0e-5) ref_norms = [7.7120645429243405e03, 1.2509238632152577e02, 4.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test(show=False) save("scaling.png", X, cells) dmsh-0.1.4/test/test_square_hole.py000066400000000000000000000007001356675576600173750ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality def test(show=False): geo = dmsh.Difference( dmsh.Rectangle(0.0, 5.0, 0.0, 5.0), dmsh.Polygon([[1, 1], [4, 1], [4, 4], [1, 4]]), ) X, cells = dmsh.generate(geo, 1.0, show=show, tol=1.0e-3) assert_norm_equality( X.flatten(), [1.2599887992309357e02, 2.2109217065599051e01, 5.0], 1.0e-12 ) return if __name__ == "__main__": test(show=False) dmsh-0.1.4/test/test_square_hole_refined.py000066400000000000000000000011131356675576600210700ustar00rootroot00000000000000import numpy import dmsh from helpers import assert_norm_equality, save def test(show=False): 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, show=show, tol=1.0e-10 ) ref_norms = [2.4810107884562055e02, 1.2004528988116096e01, 1.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test(show=False) save("square_hole_refined.png", X, cells) dmsh-0.1.4/test/test_stretch.py000066400000000000000000000007371356675576600165540ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test(show=True): geo = dmsh.Stretch(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), [1.0, 1.0]) X, cells = dmsh.generate(geo, 0.2, show=show, tol=1.0e-3) ref_norms = [4.3338293115124242e02, 2.3621770543834902e01, 2.6213203435596428e00] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test(show=False) save("stretch.png", X, cells) dmsh-0.1.4/test/test_translation.py000066400000000000000000000005761356675576600174370ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality def test(show=False): geo = dmsh.Translation(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), [1.0, 1.0]) X, cells = dmsh.generate(geo, 0.1, show=show) ref_norms = [1.7525e03, 5.5677441324948013e01, 3.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return if __name__ == "__main__": test(show=False) dmsh-0.1.4/test/test_union.py000066400000000000000000000007641356675576600162300ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_union(show=False): 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, show=show, tol=1.0e-10) geo.plot() ref_norms = [3.0088043884612756e02, 1.5785099320497183e01, 1.5] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_union(show=False) save("union.png", X, cells) dmsh-0.1.4/test/test_union_rectangles.py000066400000000000000000000010171356675576600204270ustar00rootroot00000000000000import dmsh from helpers import assert_norm_equality, save def test_union(show=False): 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, show=show, tol=1.0e-10) ref_norms = [1.7868961429998612e02, 1.1117047580567053e01, 1.0] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_union(show=False) save("union_rectangles.png", X, cells) dmsh-0.1.4/test/test_union_three_circles.py000066400000000000000000000014301356675576600211120ustar00rootroot00000000000000import numpy import dmsh from helpers import assert_norm_equality, save def test_union(show=False): 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.2, show=show, tol=1.0e-10) ref_norms = [4.1390554922002769e02, 2.1440246410944471e01, 1.9947113226010518e00] assert_norm_equality(X.flatten(), ref_norms, 1.0e-12) return X, cells if __name__ == "__main__": X, cells = test_union(show=False) save("union_three_circles.png", X, cells)