|
| 1 | +<p align="center"> |
| 2 | + <a href="https://github.com/nschloe/dmsh"><img alt="dmsh" src="https://raw.githubusercontent.com/meshpro/dmsh/main/logo/logo-with-text.svg" width="50%"></a> |
| 3 | + <p align="center">The worst mesh generator you'll ever use.</p> |
| 4 | +</p> |
| 5 | + |
| 6 | +[](https://pypi.org/project/dmsh/) |
| 7 | +[](https://pypi.org/project/dmsh/) |
| 8 | +[](https://github.com/nschloe/dmsh) |
| 9 | +[](https://pypistats.org/packages/dmsh) |
| 10 | + |
| 11 | +[](https://discord.gg/PBCCvwHqpv) |
| 12 | + |
| 13 | +Inspired by [distmesh](http://persson.berkeley.edu/distmesh/), dmsh can be slow, |
| 14 | +requires a lot of memory, and isn't terribly robust either. |
| 15 | + |
| 16 | +On the plus side, |
| 17 | + |
| 18 | +- it's got a user-friendly interface, |
| 19 | +- is pure Python (and hence easily installable on any system), and |
| 20 | +- it produces pretty high-quality meshes. |
| 21 | + |
| 22 | +Combined with [optimesh](https://github.com/nschloe/optimesh), dmsh produces the |
| 23 | +highest-quality 2D meshes in the west. |
| 24 | + |
| 25 | +### Examples |
| 26 | + |
| 27 | +#### Primitives |
| 28 | + |
| 29 | +| <img alt="circle" src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/circle.svg" width="100%"> | <img alt="circle" src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/rectangle.svg" width="100%"> | <img alt="circle" src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/polygon.svg" width="100%"> | |
| 30 | +| :--------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | |
| 31 | + |
| 32 | +```python |
| 33 | +import dmsh |
| 34 | +import meshio |
| 35 | +import optimesh |
| 36 | + |
| 37 | +geo = dmsh.Circle([0.0, 0.0], 1.0) |
| 38 | +X, cells = dmsh.generate(geo, 0.1) |
| 39 | + |
| 40 | +# optionally optimize the mesh |
| 41 | +X, cells = optimesh.optimize_points_cells(X, cells, "CVT (full)", 1.0e-10, 100) |
| 42 | + |
| 43 | +# visualize the mesh |
| 44 | +dmsh.show(X, cells, geo) |
| 45 | + |
| 46 | +# and write it to a file |
| 47 | +meshio.Mesh(X, {"triangle": cells}).write("circle.vtk") |
| 48 | +``` |
| 49 | + |
| 50 | +```python |
| 51 | +import dmsh |
| 52 | + |
| 53 | +geo = dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0) |
| 54 | +X, cells = dmsh.generate(geo, 0.1) |
| 55 | +``` |
| 56 | + |
| 57 | +```python |
| 58 | +import dmsh |
| 59 | + |
| 60 | +geo = dmsh.Polygon( |
| 61 | + [ |
| 62 | + [0.0, 0.0], |
| 63 | + [1.1, 0.0], |
| 64 | + [1.2, 0.5], |
| 65 | + [0.7, 0.6], |
| 66 | + [2.0, 1.0], |
| 67 | + [1.0, 2.0], |
| 68 | + [0.5, 1.5], |
| 69 | + ] |
| 70 | +) |
| 71 | +X, cells = dmsh.generate(geo, 0.1) |
| 72 | +``` |
| 73 | + |
| 74 | +#### Combinations |
| 75 | + |
| 76 | +##### Difference |
| 77 | + |
| 78 | +| <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/moon.svg" width="100%"> | <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/pacman.svg" width="100%"> | <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/rectangle-hole-refinement.svg" width="100%"> | |
| 79 | +| :-----------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------: | |
| 80 | + |
| 81 | +```python |
| 82 | +import dmsh |
| 83 | + |
| 84 | +geo = dmsh.Circle([-0.5, 0.0], 1.0) - dmsh.Circle([+0.5, 0.0], 1.0) |
| 85 | +X, cells = dmsh.generate(geo, 0.1) |
| 86 | +``` |
| 87 | + |
| 88 | +```python |
| 89 | +import dmsh |
| 90 | + |
| 91 | +geo = dmsh.Circle([0.0, 0.0], 1.0) - dmsh.Polygon([[0.0, 0.0], [1.5, 0.4], [1.5, -0.4]]) |
| 92 | +X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10) |
| 93 | +``` |
| 94 | + |
| 95 | +The following example uses a nonconstant edge length; it depends on the distance to the |
| 96 | +circle `c`. |
| 97 | + |
| 98 | +```python |
| 99 | +import dmsh |
| 100 | +import numpy as np |
| 101 | + |
| 102 | +r = dmsh.Rectangle(-1.0, +1.0, -1.0, +1.0) |
| 103 | +c = dmsh.Circle([0.0, 0.0], 0.3) |
| 104 | +geo = r - c |
| 105 | + |
| 106 | +X, cells = dmsh.generate(geo, lambda pts: np.abs(c.dist(pts)) / 5 + 0.05, tol=1.0e-10) |
| 107 | +``` |
| 108 | + |
| 109 | +##### Union |
| 110 | + |
| 111 | +| <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/union-circles.svg" width="100%"> | <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/union-rectangles.svg" width="100%"> | <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/union-three-circles.svg" width="100%"> | |
| 112 | +| :--------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------: | |
| 113 | + |
| 114 | +```python |
| 115 | +import dmsh |
| 116 | + |
| 117 | +geo = dmsh.Circle([-0.5, 0.0], 1.0) + dmsh.Circle([+0.5, 0.0], 1.0) |
| 118 | +X, cells = dmsh.generate(geo, 0.15) |
| 119 | +``` |
| 120 | + |
| 121 | +```python |
| 122 | +import dmsh |
| 123 | + |
| 124 | +geo = dmsh.Rectangle(-1.0, +0.5, -1.0, +0.5) + dmsh.Rectangle(-0.5, +1.0, -0.5, +1.0) |
| 125 | +X, cells = dmsh.generate(geo, 0.15) |
| 126 | +``` |
| 127 | + |
| 128 | +```python |
| 129 | +import dmsh |
| 130 | +import numpy as np |
| 131 | + |
| 132 | +angles = np.pi * np.array([3.0 / 6.0, 7.0 / 6.0, 11.0 / 6.0]) |
| 133 | +geo = dmsh.Union( |
| 134 | + [ |
| 135 | + dmsh.Circle([np.cos(angles[0]), np.sin(angles[0])], 1.0), |
| 136 | + dmsh.Circle([np.cos(angles[1]), np.sin(angles[1])], 1.0), |
| 137 | + dmsh.Circle([np.cos(angles[2]), np.sin(angles[2])], 1.0), |
| 138 | + ] |
| 139 | +) |
| 140 | +X, cells = dmsh.generate(geo, 0.15) |
| 141 | +``` |
| 142 | + |
| 143 | +#### Intersection |
| 144 | + |
| 145 | +| <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/intersection-circles.svg" width="100%"> | <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/intersection-three-circles.svg" width="100%"> | <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/intersection-circle-halfspace.svg" width="100%"> | |
| 146 | +| :---------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------: | |
| 147 | + |
| 148 | +```python |
| 149 | +import dmsh |
| 150 | + |
| 151 | +geo = dmsh.Circle([0.0, -0.5], 1.0) & dmsh.Circle([0.0, +0.5], 1.0) |
| 152 | +X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10) |
| 153 | +``` |
| 154 | + |
| 155 | +```python |
| 156 | +import dmsh |
| 157 | +import numpy as np |
| 158 | + |
| 159 | +angles = np.pi * np.array([3.0 / 6.0, 7.0 / 6.0, 11.0 / 6.0]) |
| 160 | +geo = dmsh.Intersection( |
| 161 | + [ |
| 162 | + dmsh.Circle([np.cos(angles[0]), np.sin(angles[0])], 1.5), |
| 163 | + dmsh.Circle([np.cos(angles[1]), np.sin(angles[1])], 1.5), |
| 164 | + dmsh.Circle([np.cos(angles[2]), np.sin(angles[2])], 1.5), |
| 165 | + ] |
| 166 | +) |
| 167 | +X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10) |
| 168 | +``` |
| 169 | + |
| 170 | +The following uses the `HalfSpace` primtive for cutting off a circle. |
| 171 | + |
| 172 | +```python |
| 173 | +import dmsh |
| 174 | + |
| 175 | +geo = dmsh.HalfSpace([1.0, 1.0]) & dmsh.Circle([0.0, 0.0], 1.0) |
| 176 | +X, cells = dmsh.generate(geo, 0.1) |
| 177 | +``` |
| 178 | + |
| 179 | +### Rotation, translation, scaling |
| 180 | + |
| 181 | +| <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/rotation.svg" width="100%"> | <img src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/scaling.svg" width="100%"> | |
| 182 | +| :---------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------: | |
| 183 | + |
| 184 | +```python |
| 185 | +import dmsh |
| 186 | +import numpy as np |
| 187 | + |
| 188 | +geo = dmsh.Rotation(dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0), 0.1 * np.pi) |
| 189 | +X, cells = dmsh.generate(geo, 0.1, tol=1.0e-10) |
| 190 | +``` |
| 191 | + |
| 192 | +```python |
| 193 | +import dmsh |
| 194 | + |
| 195 | +geo = dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0) + [1.0, 1.0] |
| 196 | +X, cells = dmsh.generate(geo, 0.1) |
| 197 | +``` |
| 198 | + |
| 199 | +```python |
| 200 | +import dmsh |
| 201 | + |
| 202 | +geo = dmsh.Rectangle(-1.0, +2.0, -1.0, +1.0) * 2.0 |
| 203 | +X, cells = dmsh.generate(geo, 0.1, tol=1.0e-5) |
| 204 | +``` |
| 205 | + |
| 206 | +### Local refinement |
| 207 | + |
| 208 | +<img alt="local-refinement" src="https://raw.githubusercontent.com/meshpro/dmsh/main/plots/local-refinement.svg" width="30%"> |
| 209 | + |
| 210 | +All objects can be used to refine the mesh according to the distance to the object; |
| 211 | +e.g. a `Path`: |
| 212 | + |
| 213 | +```python |
| 214 | +import dmsh |
| 215 | + |
| 216 | +geo = dmsh.Rectangle(0.0, 1.0, 0.0, 1.0) |
| 217 | + |
| 218 | +p1 = dmsh.Path([[0.4, 0.6], [0.6, 0.4]]) |
| 219 | + |
| 220 | + |
| 221 | +def target_edge_length(x): |
| 222 | + return 0.03 + 0.1 * p1.dist(x) |
| 223 | + |
| 224 | + |
| 225 | +X, cells = dmsh.generate(geo, target_edge_length, tol=1.0e-10) |
| 226 | +``` |
| 227 | + |
| 228 | +### Custom shapes |
| 229 | + |
| 230 | +It is also possible to define your own geometry. Simply create a class derived from |
| 231 | +`dmsh.Geometry` that contains a `dist` method and a method to project points onto the |
| 232 | +boundary. |
| 233 | + |
| 234 | +```python |
| 235 | +import dmsh |
| 236 | +import numpy as np |
| 237 | + |
| 238 | + |
| 239 | +class MyDisk(dmsh.Geometry): |
| 240 | + def __init__(self): |
| 241 | + self.r = 1.0 |
| 242 | + self.x0 = [0.0, 0.0] |
| 243 | + bounding_box = [-1.0, 1.0, -1.0, 1.0] |
| 244 | + feature_points = np.array([[], []]).T |
| 245 | + super().__init__(bounding_box, feature_points) |
| 246 | + |
| 247 | + def dist(self, x): |
| 248 | + assert x.shape[0] == 2 |
| 249 | + y = (x.T - self.x0).T |
| 250 | + return np.sqrt(np.einsum("i...,i...->...", y, y)) - self.r |
| 251 | + |
| 252 | + def boundary_step(self, x): |
| 253 | + # project onto the circle |
| 254 | + y = (x.T - self.x0).T |
| 255 | + r = np.sqrt(np.einsum("ij,ij->j", y, y)) |
| 256 | + return ((y / r * self.r).T + self.x0).T |
| 257 | + |
| 258 | + |
| 259 | +geo = MyDisk() |
| 260 | +X, cells = dmsh.generate(geo, 0.1) |
| 261 | +``` |
| 262 | + |
| 263 | +### Debugging |
| 264 | + |
| 265 | +|  |  | |
| 266 | +| :-----------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------: | |
| 267 | + |
| 268 | +dmsh is rather fragile, but sometimes the break-downs are due to an incorrectly defined |
| 269 | +geometry. Use |
| 270 | + |
| 271 | +``` |
| 272 | +geo.show() |
| 273 | +``` |
| 274 | + |
| 275 | +to inspect the level set function of your domain. (It must be negative inside the |
| 276 | +domain and positive outside. The 0-level set forms the domain boundary.) |
| 277 | + |
| 278 | +### Installation |
| 279 | + |
| 280 | +dmsh is [available from the Python Package |
| 281 | +Index](https://pypi.org/project/dmsh/), so simply type |
| 282 | + |
| 283 | +``` |
| 284 | +pip install dmsh |
| 285 | +``` |
| 286 | + |
| 287 | +to install. |
| 288 | + |
| 289 | +### Testing |
| 290 | + |
| 291 | +To run the dmsh unit tests, check out this repository and type |
| 292 | + |
| 293 | +``` |
| 294 | +tox |
| 295 | +``` |
0 commit comments