pax_global_header 0000666 0000000 0000000 00000000064 13566755766 0014544 g ustar 00root root 0000000 0000000 52 comment=a708de7384714932fc40c53154762ab5db9d12e2 dmsh-0.1.4/ 0000775 0000000 0000000 00000000000 13566755766 0012501 5 ustar 00root root 0000000 0000000 dmsh-0.1.4/.circleci/ 0000775 0000000 0000000 00000000000 13566755766 0014334 5 ustar 00root root 0000000 0000000 dmsh-0.1.4/.circleci/config.yml 0000664 0000000 0000000 00000001316 13566755766 0016325 0 ustar 00root root 0000000 0000000 version: 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/.flake8 0000664 0000000 0000000 00000000153 13566755766 0013653 0 ustar 00root root 0000000 0000000 [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/ 0000775 0000000 0000000 00000000000 13566755766 0014041 5 ustar 00root root 0000000 0000000 dmsh-0.1.4/.github/workflows/ 0000775 0000000 0000000 00000000000 13566755766 0016076 5 ustar 00root root 0000000 0000000 dmsh-0.1.4/.github/workflows/ci.yml 0000664 0000000 0000000 00000001554 13566755766 0017221 0 ustar 00root root 0000000 0000000 name: 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/.gitignore 0000664 0000000 0000000 00000000141 13566755766 0014465 0 ustar 00root root 0000000 0000000 *.pyc *.swp *.prof MANIFEST README.rst dist/ build/ .coverage .cache/ *.egg-info/ .pytest_cache/ dmsh-0.1.4/LICENSE 0000664 0000000 0000000 00000002076 13566755766 0013513 0 ustar 00root root 0000000 0000000 The 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/Makefile 0000664 0000000 0000000 00000001632 13566755766 0014143 0 ustar 00root root 0000000 0000000 VERSION=$(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.md 0000664 0000000 0000000 00000015154 13566755766 0013766 0 ustar 00root root 0000000 0000000
The worst mesh generator you'll ever use.
[](https://circleci.com/gh/nschloe/dmsh/tree/master) [](https://codecov.io/gh/nschloe/dmsh) [](https://github.com/psf/black) [](https://pypi.org/project/dmsh) [](https://tracker.debian.org/pkg/python-dmsh) [](https://github.com/nschloe/dmsh) [](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  |  |  |:---:|:---:|:---:| ```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  |  |  :-------------------:|:------------------:|:----:| ```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  |  |  | :-------------------:|:------------------:|:----:| ```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  |  |  :-------------------:|:------------------:|:----:| ```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  |  |:----:|:----:| ```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  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.yml 0000664 0000000 0000000 00000000272 13566755766 0014647 0 ustar 00root root 0000000 0000000 comment: 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/ 0000775 0000000 0000000 00000000000 13566755766 0013434 5 ustar 00root root 0000000 0000000 dmsh-0.1.4/dmsh/__about__.py 0000664 0000000 0000000 00000000421 13566755766 0015711 0 ustar 00root root 0000000 0000000 __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__.py 0000664 0000000 0000000 00000001204 13566755766 0015542 0 ustar 00root root 0000000 0000000 from .__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/ 0000775 0000000 0000000 00000000000 13566755766 0015267 5 ustar 00root root 0000000 0000000 dmsh-0.1.4/dmsh/geometry/__init__.py 0000664 0000000 0000000 00000001154 13566755766 0017401 0 ustar 00root root 0000000 0000000 from .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.py 0000664 0000000 0000000 00000002575 13566755766 0017113 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000003677 13566755766 0017750 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000003315 13566755766 0017300 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000002623 13566755766 0017572 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000003033 13566755766 0020346 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000002027 13566755766 0016576 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000002244 13566755766 0017332 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000000365 13566755766 0017611 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000002272 13566755766 0017503 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000000672 13566755766 0017266 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000002476 13566755766 0017326 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000001116 13566755766 0020176 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000003103 13566755766 0016766 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000011332 13566755766 0015450 0 ustar 00root root 0000000 0000000 import numpy def unique_rows(a): # The cleaner alternative `numpy.unique(a, axis=0)` is slow; cf. #