bluemesh2d 0.1.1.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. bluemesh2d/_version.py +24 -0
  2. bluemesh2d/aabb_tree/findball.py +121 -0
  3. bluemesh2d/aabb_tree/findtria.py +299 -0
  4. bluemesh2d/aabb_tree/maketree.py +147 -0
  5. bluemesh2d/aabb_tree/mapvert.py +72 -0
  6. bluemesh2d/aabb_tree/queryset.py +83 -0
  7. bluemesh2d/aabb_tree/scantree.py +124 -0
  8. bluemesh2d/dependencies.py +83 -0
  9. bluemesh2d/feedback.py +142 -0
  10. bluemesh2d/geom_util/boundary_util.py +216 -0
  11. bluemesh2d/geom_util/getiso.py +194 -0
  12. bluemesh2d/geom_util/poly_util.py +795 -0
  13. bluemesh2d/geom_util/proj_util.py +250 -0
  14. bluemesh2d/geomesh_util/border_util.py +283 -0
  15. bluemesh2d/geomesh_util/depth_field.py +333 -0
  16. bluemesh2d/geomesh_util/grd_util.py +1000 -0
  17. bluemesh2d/geomesh_util/interpolation_mesh.py +318 -0
  18. bluemesh2d/geomesh_util/merge_circumcenters.py +268 -0
  19. bluemesh2d/geomesh_util/water_polygon.py +406 -0
  20. bluemesh2d/hfun_util/build_hfun.py +589 -0
  21. bluemesh2d/hfun_util/hfun_dispersion.py +96 -0
  22. bluemesh2d/hfun_util/lfshfn.py +110 -0
  23. bluemesh2d/hfun_util/limhfn.py +65 -0
  24. bluemesh2d/hfun_util/make_constant_hfun.py +32 -0
  25. bluemesh2d/hfun_util/make_depth_hfun.py +51 -0
  26. bluemesh2d/hfun_util/smooth_and_precomput.py +188 -0
  27. bluemesh2d/hfun_util/trihfn.py +84 -0
  28. bluemesh2d/hjac_util/limgrad.py +105 -0
  29. bluemesh2d/mesh_ball/cdtbal1.py +38 -0
  30. bluemesh2d/mesh_ball/cdtbal2.py +104 -0
  31. bluemesh2d/mesh_ball/inv_2x2.py +61 -0
  32. bluemesh2d/mesh_ball/inv_3x3.py +72 -0
  33. bluemesh2d/mesh_ball/pwrbal2.py +134 -0
  34. bluemesh2d/mesh_ball/tribal2.py +27 -0
  35. bluemesh2d/mesh_cost/relhfn.py +62 -0
  36. bluemesh2d/mesh_cost/triang.py +59 -0
  37. bluemesh2d/mesh_cost/triarea.py +51 -0
  38. bluemesh2d/mesh_cost/trideg.py +44 -0
  39. bluemesh2d/mesh_cost/triscr.py +43 -0
  40. bluemesh2d/mesh_file/bnd_util.py +664 -0
  41. bluemesh2d/mesh_file/loadmsh.py +165 -0
  42. bluemesh2d/mesh_file/ugrid.py +308 -0
  43. bluemesh2d/mesh_util/cfmtri.py +118 -0
  44. bluemesh2d/mesh_util/deltri.py +131 -0
  45. bluemesh2d/mesh_util/idxtri.py +53 -0
  46. bluemesh2d/mesh_util/isfeat.py +90 -0
  47. bluemesh2d/mesh_util/minlen.py +46 -0
  48. bluemesh2d/mesh_util/setset.py +49 -0
  49. bluemesh2d/mesh_util/tricon.py +90 -0
  50. bluemesh2d/mesh_util/tridiv.py +187 -0
  51. bluemesh2d/meshgen.py +202 -0
  52. bluemesh2d/ortho_merge/constants.py +27 -0
  53. bluemesh2d/ortho_merge/geometry.py +64 -0
  54. bluemesh2d/ortho_merge/ortho_merge_iter.py +812 -0
  55. bluemesh2d/ortho_merge/orthogonalize.py +2989 -0
  56. bluemesh2d/pipeline.py +277 -0
  57. bluemesh2d/poly_data/airfoil.msh +958 -0
  58. bluemesh2d/poly_data/channel.msh +212 -0
  59. bluemesh2d/poly_data/islands.msh +13819 -0
  60. bluemesh2d/poly_data/lake.msh +612 -0
  61. bluemesh2d/poly_data/river.msh +690 -0
  62. bluemesh2d/poly_test/inpoly.py +105 -0
  63. bluemesh2d/poly_test/inpoly_mat.py +104 -0
  64. bluemesh2d/refine.py +972 -0
  65. bluemesh2d/smood.py +623 -0
  66. bluemesh2d/smooth.py +522 -0
  67. bluemesh2d/tricost.py +426 -0
  68. bluemesh2d/tridemo.py +723 -0
  69. bluemesh2d/triread.py +53 -0
  70. bluemesh2d-0.1.1.dev0.dist-info/METADATA +139 -0
  71. bluemesh2d-0.1.1.dev0.dist-info/RECORD +74 -0
  72. bluemesh2d-0.1.1.dev0.dist-info/WHEEL +5 -0
  73. bluemesh2d-0.1.1.dev0.dist-info/licenses/LICENSE +674 -0
  74. bluemesh2d-0.1.1.dev0.dist-info/top_level.txt +1 -0
bluemesh2d/smooth.py ADDED
@@ -0,0 +1,522 @@
1
+ import time
2
+
3
+ import numpy as np
4
+ from scipy.sparse import csr_matrix
5
+
6
+ from .mesh_cost.triscr import triscr
7
+ from .mesh_util.deltri import deltri
8
+ from .mesh_util.setset import setset
9
+ from .mesh_util.tricon import tricon
10
+
11
+
12
+ def smooth(
13
+ vert=None, conn=None, tria=None, tnum=None, opts=None, hfun=None, harg=[],
14
+ fixed=None,
15
+ ):
16
+ """Perform hill-climbing mesh smoothing for 2D triangle meshes.
17
+
18
+ Optimize vertex positions and local topology via a spring-based update
19
+ with monotonic quality guarantees.
20
+
21
+ Parameters
22
+ ----------
23
+ vert : ndarray of shape (V, 2), optional
24
+ Vertex coordinates.
25
+ conn : ndarray of shape (E, 2), optional
26
+ Constrained edges.
27
+ tria : ndarray of shape (T, 3), optional
28
+ Triangle connectivity (vertex indices).
29
+ tnum : ndarray of shape (T, 1), optional
30
+ Part index per triangle.
31
+ opts : dict, optional
32
+ Smoothing options (defaults via :func:`makeopt`):
33
+
34
+ - ``vtol`` : float, default ``1.0e-2`` — relative vertex movement tolerance
35
+ - ``iter`` : int, default ``32`` — maximum iterations
36
+ - ``disp`` : int or float, default ``4`` — progress interval; ``np.inf`` for quiet
37
+ hfun : callable, optional
38
+ Mesh-size function for local edge-length control.
39
+ harg : tuple, optional
40
+ Extra arguments passed to ``hfun``.
41
+ fixed : array_like of int, optional
42
+ Indices (into ``vert``) of vertices to hold fixed: they are never
43
+ moved, never removed, and edges incident to them are never
44
+ collapsed or split.
45
+
46
+ Returns
47
+ -------
48
+ vert : ndarray of shape (V, 2)
49
+ Smoothed vertex coordinates.
50
+ conn : ndarray of shape (E, 2)
51
+ Updated constrained edges.
52
+ tria : ndarray of shape (T, 3)
53
+ Updated triangle connectivity.
54
+ tnum : ndarray of shape (T, 1)
55
+ Updated part indices.
56
+
57
+ Notes
58
+ -----
59
+ Loosely based on the DISTMESH spring analogy with additional hill-climbing
60
+ quality and density control. See P.-O. Persson and G. Strang (2004),
61
+ *A Simple Mesh Generator in MATLAB*, SIAM Review 46(2): 329–345.
62
+
63
+ References
64
+ ----------
65
+ Translation of the MESH2D function ``SMOOTH2``.
66
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
67
+ """
68
+
69
+ if vert is None:
70
+ vert = np.empty((0, 2))
71
+ if conn is None:
72
+ conn = np.empty((0, 2), dtype=int)
73
+ if tria is None:
74
+ tria = np.empty((0, 3), dtype=int)
75
+ if tnum is None:
76
+ tnum = np.empty((0, 1), dtype=int)
77
+ if opts is None:
78
+ opts = {}
79
+
80
+ opts = makeopt(opts)
81
+
82
+ if conn.size == 0:
83
+ edge, _ = tricon(tria)
84
+ ebnd = edge[:, 3] < 1 # use boundary edge
85
+ conn = edge[ebnd, 0:2]
86
+
87
+ if tnum.size == 0:
88
+ tnum = np.ones((tria.shape[0], 1), dtype=int)
89
+
90
+ if not (
91
+ isinstance(vert, np.ndarray)
92
+ and isinstance(conn, np.ndarray)
93
+ and isinstance(tria, np.ndarray)
94
+ and isinstance(tnum, np.ndarray)
95
+ and isinstance(opts, dict)
96
+ ):
97
+ raise TypeError("smooth:incorrectInputClass - Incorrect input class.")
98
+
99
+ nvrt = vert.shape[0]
100
+
101
+ if fixed is None:
102
+ fixed = np.empty((0,), dtype=int)
103
+ else:
104
+ fixed = np.unique(np.asarray(fixed, dtype=int).ravel())
105
+ if fixed.size and (fixed.min() < 0 or fixed.max() >= nvrt):
106
+ raise ValueError("smooth:invalidInputs - Invalid FIXED input array.")
107
+
108
+ if np.min(conn[:, :2]) < 0 or np.max(conn[:, :2]) > nvrt:
109
+ raise ValueError("smooth:invalidInputs - Invalid EDGE input array.")
110
+
111
+ if np.min(tria[:, :3]) < 0 or np.max(tria[:, :3]) > nvrt:
112
+ raise ValueError("smooth:invalidInputs - Invalid TRIA input array.")
113
+
114
+ if not np.isinf(opts["disp"]):
115
+ print("\n Smooth triangulation...\n")
116
+ print(" -------------------------------------------------------")
117
+ print(" |ITER.| |MOVE(X)| |DTRI(X)| ")
118
+ print(" -------------------------------------------------------")
119
+
120
+ node = vert.copy()
121
+ PSLG = conn.copy()
122
+ pmax = int(np.max(tnum))
123
+ part = [None for _ in range(pmax)]
124
+
125
+ for ppos in range(pmax):
126
+ tsel = tnum.flatten() == (ppos + 1)
127
+ tcur = tria[tsel, :]
128
+ ecur, tcur = tricon(tcur)
129
+ ebnd = ecur[:, 3] == -1
130
+ same, _ = setset(PSLG, ecur[ebnd, 0:2])
131
+ part[ppos] = np.where(same)[0]
132
+
133
+ vmin = np.min(vert, axis=0)
134
+ vmax = np.max(vert, axis=0)
135
+
136
+ vdel = vmax - 1.0 * vmin
137
+ vmin = vmin - 0.5 * vdel
138
+ vmax = vmax + 0.5 * vdel
139
+
140
+ vbox = np.array(
141
+ [
142
+ [vmin[0], vmin[1]],
143
+ [vmax[0], vmin[1]],
144
+ [vmax[0], vmax[1]],
145
+ [vmin[0], vmax[1]],
146
+ ]
147
+ )
148
+
149
+ vert = np.vstack((vert, vbox))
150
+
151
+ tnow = time.time()
152
+ tcpu = {
153
+ "full": 0.0,
154
+ "dtri": 0.0,
155
+ "tcon": 0.0,
156
+ "iter": 0.0,
157
+ "undo": 0.0,
158
+ "keep": 0.0,
159
+ }
160
+
161
+ for iter in range(int(opts["iter"])):
162
+ ttic = time.time()
163
+ edge, tria = tricon(tria, conn)
164
+ tcpu["tcon"] += time.time() - ttic
165
+
166
+ oscr = triscr(vert, tria)
167
+
168
+ ttic = time.time()
169
+ nvrt = vert.shape[0]
170
+ nedg = edge.shape[0]
171
+
172
+ IMAT = csr_matrix(
173
+ (np.ones(nedg), (edge[:, 0], np.arange(nedg))), shape=(nvrt, nedg)
174
+ )
175
+ JMAT = csr_matrix(
176
+ (np.ones(nedg), (edge[:, 1], np.arange(nedg))), shape=(nvrt, nedg)
177
+ )
178
+
179
+ EMAT = IMAT + JMAT
180
+ vdeg = np.array(EMAT.sum(axis=1)).flatten() # vertex |deg|
181
+ free = vdeg == 0
182
+
183
+ vold = vert.copy()
184
+
185
+ # Local iterations
186
+ for isub in range(max(1, min(8, iter))):
187
+ # compute HFUN at vert/midpoints
188
+ hvrt = evalhfn(vert, edge, EMAT, hfun, harg)
189
+ hmid = 0.5 * (hvrt[edge[:, 0]] + hvrt[edge[:, 1]])
190
+ # calc. relative edge extensions
191
+ evec = vert[edge[:, 1], :] - vert[edge[:, 0], :]
192
+ elen = np.sqrt(np.sum(evec**2, axis=1))
193
+
194
+ scal = 1.0 - elen / hmid
195
+ scal = np.clip(scal, -1.0, 1.0)
196
+ # projected points from each end
197
+ ipos = vert[edge[:, 0], :] - 0.67 * (scal[:, None] * evec)
198
+ jpos = vert[edge[:, 1], :] + 0.67 * (scal[:, None] * evec)
199
+ scal = np.maximum(np.abs(scal) ** 1, np.finfo(float).eps ** 0.75)
200
+ # sum contributions edge-to-vert
201
+ vnew = IMAT.dot(scal[:, None] * ipos) + JMAT.dot(scal[:, None] * jpos)
202
+ vsum = np.maximum(EMAT.dot(scal), np.finfo(float).eps ** 0.75)
203
+
204
+ vnew = vnew / vsum[:, None]
205
+ vnew[conn.flatten(), :] = vert[conn.flatten(), :]
206
+ vnew[fixed, :] = vert[fixed, :]
207
+ vnew[vdeg == 0, :] = vert[vdeg == 0, :]
208
+ vert = vnew
209
+
210
+ tcpu["iter"] += time.time() - ttic
211
+ ttic = time.time()
212
+
213
+ # unwind vert. upadte if score lower
214
+ nscr = np.ones(tria.shape[0])
215
+ btri = np.ones(tria.shape[0], dtype=bool)
216
+
217
+ umax = 8
218
+ for undo in range(umax):
219
+ nscr[btri] = triscr(vert, tria[btri, :])
220
+
221
+ # TRUE if tria needs "unwinding"
222
+ smin = 0.70
223
+ smax = 0.90
224
+ sdel = 0.025
225
+
226
+ stol = smin + iter * sdel
227
+ stol = min(smax, stol)
228
+
229
+ btri = (nscr <= stol) & (nscr < oscr)
230
+
231
+ if not np.any(btri):
232
+ break
233
+
234
+ # relax toward old vert. coord's
235
+ ivrt = np.unique(tria[btri, :3])
236
+ bvrt = np.zeros(vert.shape[0], dtype=bool)
237
+ bvrt[ivrt] = True
238
+
239
+ if undo != umax:
240
+ bnew = 0.75**undo
241
+ bold = 1.0 - bnew
242
+ else:
243
+ bnew = 0.0
244
+ bold = 1.0 - bnew
245
+
246
+ vert[bvrt, :] = bold * vold[bvrt, :] + bnew * vert[bvrt, :]
247
+
248
+ btri = np.any(bvrt[tria[:, :3]], axis=1)
249
+
250
+ oscr = nscr
251
+ tcpu["undo"] += time.time() - ttic
252
+
253
+ ttic = time.time()
254
+
255
+ vdel = np.sum((vert - vold) ** 2, axis=1)
256
+
257
+ evec = vert[edge[:, 1], :] - vert[edge[:, 0], :]
258
+ elen = np.sqrt(np.sum(evec**2, axis=1))
259
+
260
+ hvrt = evalhfn(vert, edge, EMAT, hfun, harg)
261
+ hmid = 0.5 * (hvrt[edge[:, 0]] + hvrt[edge[:, 1]])
262
+ scal = elen / hmid
263
+
264
+ emid = 0.5 * (vert[edge[:, 0], :] + vert[edge[:, 1], :])
265
+
266
+ keep = np.zeros(vert.shape[0], dtype=bool)
267
+ keep[vdeg > 4] = True
268
+ keep[conn.flatten()] = True
269
+ keep[free.flatten()] = True
270
+ keep[fixed] = True
271
+
272
+ lmax = 5.0 / 4.0
273
+ lmin = 1.0 / lmax
274
+
275
+ less = scal <= lmin
276
+ more = scal >= lmax
277
+
278
+ vbnd = np.zeros(vert.shape[0], dtype=bool)
279
+ vbnd[conn[:, 0]] = True
280
+ vbnd[conn[:, 1]] = True
281
+ vbnd[fixed] = True
282
+
283
+ ebad = vbnd[edge[:, 0]] | vbnd[edge[:, 1]] # not at boundaries
284
+
285
+ less[ebad] = False
286
+ more[ebad] = False
287
+
288
+ lidx = np.where(less)[0]
289
+ for epos in lidx:
290
+ inod = edge[epos, 0]
291
+ jnod = edge[epos, 1]
292
+ if keep[inod] and keep[jnod]:
293
+ keep[inod] = False
294
+ keep[jnod] = False
295
+ else:
296
+ less[epos] = False
297
+
298
+ ebad = keep[edge[less, 0]] & keep[edge[less, 1]]
299
+ indices = np.flatnonzero(ebad)
300
+ indices = indices[indices < more.size]
301
+ more[indices] = False
302
+ # more[ebad] = False
303
+
304
+ redo = np.zeros(vert.shape[0], dtype=int)
305
+ itop = np.count_nonzero(keep)
306
+ iend = np.count_nonzero(less)
307
+
308
+ redo[keep] = np.arange(itop)
309
+ redo[edge[less, 0]] = np.arange(itop, itop + iend) # to new midpoints
310
+ redo[edge[less, 1]] = np.arange(itop, itop + iend)
311
+
312
+ vnew = np.vstack([vert[keep, :], emid[less, :]])
313
+ tnew = redo[tria[:, 0:3]]
314
+
315
+ ttmp = np.sort(tnew, axis=1) # filter collapsed
316
+ okay = np.all(np.diff(ttmp, axis=1) != 0, axis=1)
317
+ okay = okay & (ttmp[:, 0] > 0)
318
+ tnew = tnew[okay, :]
319
+
320
+ nscr = triscr(vnew, tnew)
321
+
322
+ stol = 0.80
323
+ tbad = (nscr < stol) & (nscr < oscr[okay])
324
+
325
+ vbad = np.zeros(vnew.shape[0], dtype=bool)
326
+ vbad[tnew[tbad, :]] = True
327
+
328
+ lidx = np.where(less)[0]
329
+ ebad = vbad[redo[edge[lidx, 0]]] | vbad[redo[edge[lidx, 1]]]
330
+
331
+ less[lidx[ebad]] = False
332
+ keep[edge[lidx[ebad], 0:2].flatten()] = True
333
+
334
+ redo = np.zeros(vert.shape[0], dtype=int)
335
+ itop = np.count_nonzero(keep)
336
+ iend = np.count_nonzero(less)
337
+
338
+ redo[keep] = np.arange(itop)
339
+ redo[edge[less, 0]] = np.arange(itop, itop + iend)
340
+ redo[edge[less, 1]] = np.arange(itop, itop + iend)
341
+
342
+ vert = np.vstack([vert[keep, :], emid[less, :], emid[more, :]])
343
+ conn = redo[conn]
344
+ fixed = redo[fixed]
345
+
346
+ tcpu["keep"] += time.time() - ttic
347
+
348
+ ttic = time.time()
349
+ vert, conn, tria, tnum = deltri(vert, conn, node, PSLG, part)
350
+ tcpu["dtri"] += time.time() - ttic
351
+
352
+ vdel = vdel / (hvrt.flatten() ** 2)
353
+ move = vdel > opts["vtol"] ** 2
354
+
355
+ nmov = np.count_nonzero(move)
356
+
357
+ ntri = tria.shape[0]
358
+ if iter % opts["disp"] == 0:
359
+ print(f"{iter:11d} {nmov:18d} {ntri:18d}")
360
+
361
+ if nmov == 0:
362
+ break
363
+
364
+ tria = tria[:, 0:3]
365
+
366
+ keep = np.zeros(vert.shape[0], dtype=bool)
367
+ keep[tria.flatten()] = True
368
+ keep[conn.flatten()] = True
369
+
370
+ redo = np.zeros(vert.shape[0], dtype=int)
371
+ redo[keep] = np.arange(np.count_nonzero(keep))
372
+
373
+ conn = redo[conn]
374
+ tria = redo[tria]
375
+
376
+ vert = vert[keep, :]
377
+
378
+ tcpu["full"] += time.time() - tnow
379
+
380
+ if opts["dbug"]:
381
+ print("\n Mesh smoothing timer...\n")
382
+ print(f" FULL: {tcpu['full']:.6f}")
383
+ print(f" DTRI: {tcpu['dtri']:.6f}")
384
+ print(f" TCON: {tcpu['tcon']:.6f}")
385
+ print(f" ITER: {tcpu['iter']:.6f}")
386
+ print(f" UNDO: {tcpu['undo']:.6f}")
387
+ print(f" KEEP: {tcpu['keep']:.6f}\n")
388
+
389
+ if not np.isinf(opts["disp"]):
390
+ print("")
391
+
392
+ return vert, conn, tria, tnum
393
+
394
+
395
+ def evalhfn(vert, edge, EMAT, hfun=None, harg=[]):
396
+ """Evaluate the mesh-size function at mesh vertices.
397
+
398
+ Parameters
399
+ ----------
400
+ vert : ndarray of shape (N, 2)
401
+ Vertex coordinates.
402
+ edge : ndarray of shape (E, 2)
403
+ Edge connectivity.
404
+ EMAT : scipy.sparse matrix
405
+ Vertex–edge incidence matrix.
406
+ hfun : float, callable, or None, optional
407
+ Mesh-size function or constant spacing.
408
+ harg : tuple, optional
409
+ Extra arguments passed to ``hfun``.
410
+
411
+ Returns
412
+ -------
413
+ hvrt : ndarray of shape (N,)
414
+ Mesh-size value at each vertex.
415
+
416
+ References
417
+ ----------
418
+ Translation of the MESH2D function ``EVALHFN``.
419
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
420
+ """
421
+
422
+ # Default: mean edge length at each vertex (used when no hfun or as fallback)
423
+ evec = vert[edge[:, 1], :] - vert[edge[:, 0], :]
424
+ elen = np.sqrt(np.sum(evec**2, axis=1))
425
+ default_hvrt = np.ravel(EMAT @ elen) / np.maximum(
426
+ np.ravel(np.sum(EMAT, axis=1)), np.finfo(float).eps
427
+ )
428
+ free = np.ones(vert.shape[0], dtype=bool)
429
+ free[edge[:, 0]] = False
430
+ free[edge[:, 1]] = False
431
+ default_hvrt[free] = np.inf
432
+
433
+ if hfun is not None and (np.isscalar(hfun) or callable(hfun)):
434
+ if np.isscalar(hfun):
435
+ hvrt = hfun * np.ones(vert.shape[0])
436
+ else:
437
+ try:
438
+ hvrt = np.asarray(hfun(vert, *harg)).flatten()
439
+ except Exception:
440
+ # hfun may fail on out-of-domain vertices (e.g. bbox) -> fall back to default
441
+ hvrt = default_hvrt.copy()
442
+ else:
443
+ if hvrt.size != vert.shape[0]:
444
+ raise ValueError(
445
+ "smooth:evalhfn - hfun must return one value per vertex, "
446
+ f"got size {hvrt.size} for {vert.shape[0]} vertices."
447
+ )
448
+ # out-of-domain vertices (e.g. bbox) may yield NaN/inf: fall back to default
449
+ bad = ~np.isfinite(hvrt) | (hvrt <= 0)
450
+ hvrt[bad] = default_hvrt[bad]
451
+ else:
452
+ hvrt = default_hvrt.copy()
453
+
454
+ return hvrt
455
+
456
+
457
+ def makeopt(opts=None):
458
+ """Set up and validate the options dictionary for :func:`smooth`.
459
+
460
+ Parameters
461
+ ----------
462
+ opts : dict or None, optional
463
+ User options; if ``None``, start from an empty dict.
464
+
465
+ Returns
466
+ -------
467
+ opts : dict
468
+ Validated options dictionary.
469
+
470
+ References
471
+ ----------
472
+ Translation of the MESH2D function ``MAKEOPT``.
473
+ Original MATLAB source: https://github.com/dengwirda/mesh2d
474
+ """
475
+
476
+ if opts is None:
477
+ opts = {}
478
+
479
+ if "iter" not in opts:
480
+ opts["iter"] = 32
481
+ else:
482
+ if not isinstance(opts["iter"], (int, float)):
483
+ raise TypeError("smooth:incorrectInputClass - Incorrect input class.")
484
+ if not (
485
+ isinstance(opts["iter"], (int, float))
486
+ and not isinstance(opts["iter"], bool)
487
+ ):
488
+ raise TypeError("smooth:incorrectInputClass - Incorrect input class.")
489
+ if isinstance(opts["iter"], (list, tuple)) or hasattr(opts["iter"], "__len__"):
490
+ raise ValueError("smooth:incorrectDimensions - Incorrect input dimensions.")
491
+ if opts["iter"] <= 0:
492
+ raise ValueError("smooth:invalidOptionValues - Invalid OPT.ITER selection.")
493
+
494
+ if "disp" not in opts:
495
+ opts["disp"] = 4
496
+ else:
497
+ if not isinstance(opts["disp"], (int, float)):
498
+ raise TypeError("smooth:incorrectInputClass - Incorrect input class.")
499
+ if isinstance(opts["disp"], (list, tuple)) or hasattr(opts["disp"], "__len__"):
500
+ raise ValueError("smooth:incorrectDimensions - Incorrect input dimensions.")
501
+ if opts["disp"] <= 0:
502
+ raise ValueError("smooth:invalidOptionValues - Invalid OPT.DISP selection.")
503
+
504
+ if "vtol" not in opts:
505
+ opts["vtol"] = 1.0e-2
506
+ else:
507
+ if not isinstance(opts["vtol"], (int, float)):
508
+ raise TypeError("smooth:incorrectInputClass - Incorrect input class.")
509
+ if isinstance(opts["vtol"], (list, tuple)) or hasattr(opts["vtol"], "__len__"):
510
+ raise ValueError("smooth:incorrectDimensions - Incorrect input dimensions.")
511
+ if opts["vtol"] <= 0:
512
+ raise ValueError("smooth:invalidOptionValues - Invalid OPT.VTOL selection.")
513
+
514
+ if "dbug" not in opts:
515
+ opts["dbug"] = False
516
+ else:
517
+ if not isinstance(opts["dbug"], bool):
518
+ raise TypeError("refine:incorrectInputClass - Incorrect input class.")
519
+ if isinstance(opts["dbug"], (list, tuple)) or hasattr(opts["dbug"], "__len__"):
520
+ raise ValueError("refine:incorrectDimensions - Incorrect input dimensions.")
521
+
522
+ return opts