emerge 0.4.6__py3-none-any.whl → 0.4.8__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.
Potentially problematic release.
This version of emerge might be problematic. Click here for more details.
- emerge/__init__.py +54 -0
- emerge/__main__.py +5 -0
- emerge/_emerge/__init__.py +42 -0
- emerge/_emerge/bc.py +197 -0
- emerge/_emerge/coord.py +119 -0
- emerge/_emerge/cs.py +523 -0
- emerge/_emerge/dataset.py +36 -0
- emerge/_emerge/elements/__init__.py +19 -0
- emerge/_emerge/elements/femdata.py +212 -0
- emerge/_emerge/elements/index_interp.py +64 -0
- emerge/_emerge/elements/legrange2.py +172 -0
- emerge/_emerge/elements/ned2_interp.py +645 -0
- emerge/_emerge/elements/nedelec2.py +140 -0
- emerge/_emerge/elements/nedleg2.py +217 -0
- emerge/_emerge/geo/__init__.py +24 -0
- emerge/_emerge/geo/horn.py +107 -0
- emerge/_emerge/geo/modeler.py +449 -0
- emerge/_emerge/geo/operations.py +254 -0
- emerge/_emerge/geo/pcb.py +1244 -0
- emerge/_emerge/geo/pcb_tools/calculator.py +28 -0
- emerge/_emerge/geo/pcb_tools/macro.py +79 -0
- emerge/_emerge/geo/pmlbox.py +204 -0
- emerge/_emerge/geo/polybased.py +529 -0
- emerge/_emerge/geo/shapes.py +427 -0
- emerge/_emerge/geo/step.py +77 -0
- emerge/_emerge/geo2d.py +86 -0
- emerge/_emerge/geometry.py +510 -0
- emerge/_emerge/howto.py +214 -0
- emerge/_emerge/logsettings.py +5 -0
- emerge/_emerge/material.py +118 -0
- emerge/_emerge/mesh3d.py +730 -0
- emerge/_emerge/mesher.py +339 -0
- emerge/_emerge/mth/common_functions.py +33 -0
- emerge/_emerge/mth/integrals.py +71 -0
- emerge/_emerge/mth/optimized.py +357 -0
- emerge/_emerge/periodic.py +263 -0
- emerge/_emerge/physics/__init__.py +0 -0
- emerge/_emerge/physics/microwave/__init__.py +1 -0
- emerge/_emerge/physics/microwave/adaptive_freq.py +279 -0
- emerge/_emerge/physics/microwave/assembly/assembler.py +569 -0
- emerge/_emerge/physics/microwave/assembly/curlcurl.py +448 -0
- emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +426 -0
- emerge/_emerge/physics/microwave/assembly/robinbc.py +433 -0
- emerge/_emerge/physics/microwave/microwave_3d.py +1150 -0
- emerge/_emerge/physics/microwave/microwave_bc.py +915 -0
- emerge/_emerge/physics/microwave/microwave_data.py +1148 -0
- emerge/_emerge/physics/microwave/periodic.py +82 -0
- emerge/_emerge/physics/microwave/port_functions.py +53 -0
- emerge/_emerge/physics/microwave/sc.py +175 -0
- emerge/_emerge/physics/microwave/simjob.py +147 -0
- emerge/_emerge/physics/microwave/sparam.py +138 -0
- emerge/_emerge/physics/microwave/touchstone.py +140 -0
- emerge/_emerge/plot/__init__.py +0 -0
- emerge/_emerge/plot/display.py +394 -0
- emerge/_emerge/plot/grapher.py +93 -0
- emerge/_emerge/plot/matplotlib/mpldisplay.py +264 -0
- emerge/_emerge/plot/pyvista/__init__.py +1 -0
- emerge/_emerge/plot/pyvista/display.py +931 -0
- emerge/_emerge/plot/pyvista/display_settings.py +24 -0
- emerge/_emerge/plot/simple_plots.py +551 -0
- emerge/_emerge/plot.py +225 -0
- emerge/_emerge/projects/__init__.py +0 -0
- emerge/_emerge/projects/_gen_base.txt +32 -0
- emerge/_emerge/projects/_load_base.txt +24 -0
- emerge/_emerge/projects/generate_project.py +40 -0
- emerge/_emerge/selection.py +596 -0
- emerge/_emerge/simmodel.py +444 -0
- emerge/_emerge/simulation_data.py +411 -0
- emerge/_emerge/solver.py +993 -0
- emerge/_emerge/system.py +54 -0
- emerge/cli.py +19 -0
- emerge/lib.py +57 -0
- emerge/plot.py +1 -0
- emerge/pyvista.py +1 -0
- {emerge-0.4.6.dist-info → emerge-0.4.8.dist-info}/METADATA +1 -1
- emerge-0.4.8.dist-info/RECORD +78 -0
- emerge-0.4.8.dist-info/entry_points.txt +2 -0
- emerge-0.4.6.dist-info/RECORD +0 -4
- emerge-0.4.6.dist-info/entry_points.txt +0 -2
- {emerge-0.4.6.dist-info → emerge-0.4.8.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
# EMerge is an open source Python based FEM EM simulation module.
|
|
2
|
+
# Copyright (C) 2025 Robert Fennis.
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU General Public License
|
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
|
7
|
+
# of the License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see
|
|
16
|
+
# <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
import gmsh
|
|
20
|
+
from .material import Material, AIR
|
|
21
|
+
from .selection import FaceSelection, DomainSelection, EdgeSelection, PointSelection
|
|
22
|
+
from loguru import logger
|
|
23
|
+
from typing import Literal, Any
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
def _map_tags(tags: list[int], mapping: dict[int, list[int]]):
|
|
27
|
+
new_tags = []
|
|
28
|
+
for tag in tags:
|
|
29
|
+
new_tags.extend(mapping.get(tag, [tag,]))
|
|
30
|
+
return new_tags
|
|
31
|
+
|
|
32
|
+
def _bbcenter(x1, y1, z1, x2, y2, z2):
|
|
33
|
+
return np.array([(x1+x2)/2, (y1+y2)/2, (z1+z2)/2])
|
|
34
|
+
FaceNames = Literal['back','front','left','right','top','bottom']
|
|
35
|
+
|
|
36
|
+
class _KEY_GENERATOR:
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self.start = -1
|
|
40
|
+
|
|
41
|
+
def new(self) -> int:
|
|
42
|
+
self.start += 1
|
|
43
|
+
return self.start
|
|
44
|
+
|
|
45
|
+
class _GeometryManager:
|
|
46
|
+
|
|
47
|
+
def __init__(self):
|
|
48
|
+
self.geometry_list: dict[str, list[GeoObject]] = dict()
|
|
49
|
+
self.active: str = ''
|
|
50
|
+
|
|
51
|
+
def all_geometries(self, model: str = None) -> list[GeoObject]:
|
|
52
|
+
if model is None:
|
|
53
|
+
model = self.active
|
|
54
|
+
return [geo for geo in self.geometry_list[model] if geo._exists]
|
|
55
|
+
|
|
56
|
+
def submit_geometry(self, geo: GeoObject, model: str = None) -> None:
|
|
57
|
+
if model is None:
|
|
58
|
+
model = self.active
|
|
59
|
+
self.geometry_list[model].append(geo)
|
|
60
|
+
|
|
61
|
+
def sign_in(self, modelname: str) -> None:
|
|
62
|
+
if modelname not in self.geometry_list:
|
|
63
|
+
self.geometry_list[modelname] = []
|
|
64
|
+
self.active = modelname
|
|
65
|
+
|
|
66
|
+
def reset(self, modelname: str) -> None:
|
|
67
|
+
self.geometry_list[modelname] = []
|
|
68
|
+
|
|
69
|
+
class _FacePointer:
|
|
70
|
+
"""The FacePointer class defines a face to be selectable as a
|
|
71
|
+
face normal vector plus an origin. All faces of an object
|
|
72
|
+
can be selected based on the projected distance to the defined
|
|
73
|
+
selection plane of the center of mass of a face iff the normals
|
|
74
|
+
also align with some tolerance.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
def __init__(self,
|
|
78
|
+
origin: np.ndarray,
|
|
79
|
+
normal: np.ndarray):
|
|
80
|
+
self.o = np.array(origin)
|
|
81
|
+
self.n = np.array(normal)
|
|
82
|
+
|
|
83
|
+
def find(self, dimtags: list[tuple[int,int]],
|
|
84
|
+
origins: list[np.ndarray],
|
|
85
|
+
normals: list[np.ndarray]) -> list[int]:
|
|
86
|
+
tags = []
|
|
87
|
+
for (d,t), o, n in zip(dimtags, origins, normals):
|
|
88
|
+
normdist = np.abs((o-self.o)@self.n)
|
|
89
|
+
dotnorm = np.abs(n@self.n)
|
|
90
|
+
if normdist < 1e-3 and dotnorm > 0.99:
|
|
91
|
+
tags.append(t)
|
|
92
|
+
return tags
|
|
93
|
+
|
|
94
|
+
def rotate(self, c0, ax, angle):
|
|
95
|
+
"""
|
|
96
|
+
Rotate self.o and self.n about axis `ax`, centered at `c0`, by `angle` radians.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
c0 : np.ndarray
|
|
101
|
+
The center of rotation, shape (3,).
|
|
102
|
+
ax : np.ndarray
|
|
103
|
+
The axis to rotate around, shape (3,). Need not be unit length.
|
|
104
|
+
angle : float
|
|
105
|
+
Rotation angle in radians.
|
|
106
|
+
"""
|
|
107
|
+
angle = -angle
|
|
108
|
+
# Ensure axis is a unit vector
|
|
109
|
+
k = ax / np.linalg.norm(ax)
|
|
110
|
+
|
|
111
|
+
# Precompute trig values
|
|
112
|
+
cos_theta = np.cos(angle)
|
|
113
|
+
sin_theta = np.sin(angle)
|
|
114
|
+
|
|
115
|
+
def rodrigues(v: np.ndarray) -> np.ndarray:
|
|
116
|
+
"""
|
|
117
|
+
Rotate vector v around axis k by angle using Rodrigues' formula.
|
|
118
|
+
"""
|
|
119
|
+
# term1 = v * cosθ
|
|
120
|
+
term1 = v * cos_theta
|
|
121
|
+
# term2 = (k × v) * sinθ
|
|
122
|
+
term2 = np.cross(k, v) * sin_theta
|
|
123
|
+
# term3 = k * (k ⋅ v) * (1 - cosθ)
|
|
124
|
+
term3 = k * (np.dot(k, v)) * (1 - cos_theta)
|
|
125
|
+
return term1 + term2 + term3
|
|
126
|
+
|
|
127
|
+
# Rotate the origin point about c0:
|
|
128
|
+
rel_o = self.o - c0 # move to rotation-centre coordinates
|
|
129
|
+
rot_o = rodrigues(rel_o) # rotate
|
|
130
|
+
self.o = rot_o + c0 # move back
|
|
131
|
+
|
|
132
|
+
# Rotate the normal vector (pure direction, no translation)
|
|
133
|
+
self.n = rodrigues(self.n)
|
|
134
|
+
|
|
135
|
+
def translate(self, dx, dy, dz):
|
|
136
|
+
self.o = self.o + np.array([dx, dy, dz])
|
|
137
|
+
|
|
138
|
+
def mirror(self, c0: np.ndarray, pln: np.ndarray):
|
|
139
|
+
"""
|
|
140
|
+
Reflect self.o and self.n across the plane passing through c0
|
|
141
|
+
with normal pln.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
c0 : np.ndarray
|
|
146
|
+
A point on the mirror plane, shape (3,).
|
|
147
|
+
pln : np.ndarray
|
|
148
|
+
The normal of the mirror plane, shape (3,). Need not be unit length.
|
|
149
|
+
"""
|
|
150
|
+
# Normalize the plane normal
|
|
151
|
+
k = pln / np.linalg.norm(pln)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# Reflect the origin point:
|
|
155
|
+
# compute vector from plane point to self.o
|
|
156
|
+
v_o = self.o - c0
|
|
157
|
+
# signed distance along normal
|
|
158
|
+
dist_o = np.dot(v_o, k)
|
|
159
|
+
# reflection
|
|
160
|
+
self.o = self.o - 2 * dist_o * k
|
|
161
|
+
|
|
162
|
+
# Reflect the normal/direction vector:
|
|
163
|
+
dist_n = np.dot(self.n, k)
|
|
164
|
+
self.n = self.n - 2 * dist_n * k
|
|
165
|
+
|
|
166
|
+
def affine_transform(self, M: np.ndarray):
|
|
167
|
+
"""
|
|
168
|
+
Apply a 4×4 affine transformation matrix to both self.o and self.n.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
M : np.ndarray
|
|
173
|
+
The 4×4 affine transformation matrix.
|
|
174
|
+
- When applied to a point, use homogeneous w=1.
|
|
175
|
+
- When applied to a direction/vector, use homogeneous w=0.
|
|
176
|
+
"""
|
|
177
|
+
# Validate shape
|
|
178
|
+
if M.shape != (4, 4):
|
|
179
|
+
raise ValueError(f"Expected M to be 4×4, got shape {M.shape}")
|
|
180
|
+
|
|
181
|
+
# Transform origin point (homogeneous w=1)
|
|
182
|
+
homo_o = np.empty(4)
|
|
183
|
+
homo_o[:3] = self.o
|
|
184
|
+
homo_o[3] = 1.0
|
|
185
|
+
transformed_o = M @ homo_o
|
|
186
|
+
self.o = transformed_o[:3]
|
|
187
|
+
|
|
188
|
+
# Transform normal/direction vector (homogeneous w=0)
|
|
189
|
+
homo_n = np.empty(4)
|
|
190
|
+
homo_n[:3] = self.n
|
|
191
|
+
homo_n[3] = 0.0
|
|
192
|
+
transformed_n = M @ homo_n
|
|
193
|
+
self.n = transformed_n[:3]
|
|
194
|
+
# Optionally normalize self.n if you need to keep it unit-length:
|
|
195
|
+
# self.n = self.n / np.linalg.norm(self.n)
|
|
196
|
+
|
|
197
|
+
def copy(self) -> _FacePointer:
|
|
198
|
+
return _FacePointer(self.o, self.n)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
_GENERATOR = _KEY_GENERATOR()
|
|
202
|
+
_GEOMANAGER = _GeometryManager()
|
|
203
|
+
|
|
204
|
+
class GeoObject:
|
|
205
|
+
"""A generalization of any OpenCASCADE entity described by a dimension and a set of tags.
|
|
206
|
+
"""
|
|
207
|
+
dim: int = -1
|
|
208
|
+
def __init__(self):
|
|
209
|
+
self.old_tags: list[int] = []
|
|
210
|
+
self.tags: list[int] = []
|
|
211
|
+
self.material: Material = AIR
|
|
212
|
+
self.mesh_multiplier: float = 1.0
|
|
213
|
+
self.max_meshsize: float = 1e9
|
|
214
|
+
|
|
215
|
+
self._unset_constraints: bool = False
|
|
216
|
+
self._embeddings: list[GeoObject] = []
|
|
217
|
+
self._face_pointers: dict[str, _FacePointer] = dict()
|
|
218
|
+
self._tools: dict[int, dict[str, _FacePointer]] = dict()
|
|
219
|
+
|
|
220
|
+
self._key = _GENERATOR.new()
|
|
221
|
+
self._aux_data: dict[str, Any] = dict()
|
|
222
|
+
self._priority: int = 10
|
|
223
|
+
|
|
224
|
+
self._exists: bool = True
|
|
225
|
+
_GEOMANAGER.submit_geometry(self)
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def color_rgb(self) -> tuple[int,int,int]:
|
|
229
|
+
return self.material.color_rgb
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def opacity(self) -> float:
|
|
233
|
+
return self.material.opacity
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def select(self) -> FaceSelection | DomainSelection | EdgeSelection | None:
|
|
237
|
+
'''Returns a corresponding Face/Domain or Edge Selection object'''
|
|
238
|
+
if self.dim==1:
|
|
239
|
+
return EdgeSelection(self.tags)
|
|
240
|
+
elif self.dim==2:
|
|
241
|
+
return FaceSelection(self.tags)
|
|
242
|
+
elif self.dim==3:
|
|
243
|
+
return DomainSelection(self.tags)
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def merged(objects: list[GeoObject]) -> list[GeoObject]:
|
|
247
|
+
dim = objects[0].dim
|
|
248
|
+
tags = []
|
|
249
|
+
for obj in objects:
|
|
250
|
+
tags.extend(obj.tags)
|
|
251
|
+
if dim==2:
|
|
252
|
+
out = GeoSurface(tags)
|
|
253
|
+
elif dim==3:
|
|
254
|
+
out = GeoVolume(tags)
|
|
255
|
+
else:
|
|
256
|
+
out = GeoObject(tags)
|
|
257
|
+
out.material = objects[0].material
|
|
258
|
+
return out
|
|
259
|
+
|
|
260
|
+
def __repr__(self) -> str:
|
|
261
|
+
return f'{self.__class__.__name__}({self.dim},{self.tags})'
|
|
262
|
+
|
|
263
|
+
def _data(self, *labels) -> tuple[Any]:
|
|
264
|
+
return tuple([self._aux_data.get(lab, None) for lab in labels])
|
|
265
|
+
|
|
266
|
+
def _add_face_pointer(self,
|
|
267
|
+
name: str,
|
|
268
|
+
origin: np.ndarray,
|
|
269
|
+
normal: np.ndarray):
|
|
270
|
+
self._face_pointers[name] = _FacePointer(origin, normal)
|
|
271
|
+
|
|
272
|
+
def make_copy(self) -> GeoObject:
|
|
273
|
+
new_dimtags = gmsh.model.occ.copy(self.dimtags)
|
|
274
|
+
new_obj = GeoObject.from_dimtags(new_dimtags)
|
|
275
|
+
new_obj.material = self.material
|
|
276
|
+
new_obj.mesh_multiplier = self.mesh_multiplier
|
|
277
|
+
new_obj.max_meshsize = self.max_meshsize
|
|
278
|
+
|
|
279
|
+
new_obj._unset_constraints = self._unset_constraints
|
|
280
|
+
new_obj._embeddings = [emb.make_copy() for emb in self._embeddings]
|
|
281
|
+
new_obj._face_pointers = {key: value.copy() for key,value in self._face_pointers.items()}
|
|
282
|
+
new_obj._tools = {key: {key2: value2.copy() for key2, value2 in value.items()} for key,value in self._tools.items()}
|
|
283
|
+
|
|
284
|
+
new_obj._aux_data = self._aux_data.copy()
|
|
285
|
+
new_obj._priority = self._priority
|
|
286
|
+
new_obj._exists = self._exists
|
|
287
|
+
return new_obj
|
|
288
|
+
|
|
289
|
+
def replace_tags(self, tagmap: dict[int, list[int]]):
|
|
290
|
+
self.old_tags = self.tags
|
|
291
|
+
newtags = []
|
|
292
|
+
for tag in self.tags:
|
|
293
|
+
newtags.extend(tagmap.get(tag, [tag,]))
|
|
294
|
+
self.tags = newtags
|
|
295
|
+
logger.debug(f'Replaced {self.old_tags} with {self.tags}')
|
|
296
|
+
|
|
297
|
+
def update_tags(self, tag_mapping: dict[int,dict]) -> GeoObject:
|
|
298
|
+
''' Update the tag definition of a GeoObject after fragementation.'''
|
|
299
|
+
self.replace_tags(tag_mapping[self.dim])
|
|
300
|
+
return self
|
|
301
|
+
|
|
302
|
+
def _take_pointers(self, *others: GeoObject) -> GeoObject:
|
|
303
|
+
for other in others:
|
|
304
|
+
self._face_pointers.update(other._face_pointers)
|
|
305
|
+
self._tools.update(other._tools)
|
|
306
|
+
return self
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def _all_pointers(self) -> list[_FacePointer]:
|
|
310
|
+
pointers = list(self._face_pointers.values())
|
|
311
|
+
for dct in self._tools.values():
|
|
312
|
+
pointers.extend(list(dct.values()))
|
|
313
|
+
return pointers
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def _all_pointer_names(self) -> set[str]:
|
|
317
|
+
keys = set(self._face_pointers.keys())
|
|
318
|
+
for dct in self._tools.values():
|
|
319
|
+
keys = keys.union(set(dct.keys()))
|
|
320
|
+
return keys
|
|
321
|
+
|
|
322
|
+
def _take_tools(self, *objects: GeoObject) -> GeoObject:
|
|
323
|
+
for obj in objects:
|
|
324
|
+
self._tools[obj._key] = obj._face_pointers
|
|
325
|
+
self._tools.update(obj._tools)
|
|
326
|
+
return self
|
|
327
|
+
|
|
328
|
+
def _face_tags(self, name: FaceNames, tool: GeoObject = None) -> list[int]:
|
|
329
|
+
names = self._all_pointer_names
|
|
330
|
+
if name not in names:
|
|
331
|
+
raise ValueError(f'The face {name} does not exist in {self}')
|
|
332
|
+
|
|
333
|
+
gmsh.model.occ.synchronize()
|
|
334
|
+
dimtags = gmsh.model.get_boundary(self.dimtags, True, False)
|
|
335
|
+
|
|
336
|
+
normals = [gmsh.model.get_normal(t, [0,0]) for d,t, in dimtags]
|
|
337
|
+
origins = [gmsh.model.occ.get_center_of_mass(d, t) for d,t in dimtags]
|
|
338
|
+
|
|
339
|
+
if tool is not None:
|
|
340
|
+
tags = self._tools[tool._key][name].find(dimtags, origins, normals)
|
|
341
|
+
else:
|
|
342
|
+
tags = self._face_pointers[name].find(dimtags, origins, normals)
|
|
343
|
+
logger.info(f'Selected face {tags}.')
|
|
344
|
+
return tags
|
|
345
|
+
|
|
346
|
+
def set_material(self, material: Material) -> GeoObject:
|
|
347
|
+
self.material = material
|
|
348
|
+
return self
|
|
349
|
+
|
|
350
|
+
def prio_set(self, level: int) -> GeoObject:
|
|
351
|
+
"""Defines the material assignment priority level of this geometry.
|
|
352
|
+
By default all objects have priority level 10. If you assign a lower number,
|
|
353
|
+
in cases where multiple geometries occupy the same volume, the highest priority
|
|
354
|
+
will be chosen.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
level (int): The priority level
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
GeoObject: The same object
|
|
361
|
+
"""
|
|
362
|
+
self._priority = level
|
|
363
|
+
return self
|
|
364
|
+
|
|
365
|
+
def prio_up(self) -> GeoObject:
|
|
366
|
+
"""Increase priority by 1
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
GeoObject: _description_
|
|
370
|
+
"""
|
|
371
|
+
self._priority += 1
|
|
372
|
+
return self
|
|
373
|
+
|
|
374
|
+
def prio_down(self) -> GeoObject:
|
|
375
|
+
"""Decrase priority by 1
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
GeoObject: _description_
|
|
379
|
+
"""
|
|
380
|
+
self._priority -= 1
|
|
381
|
+
return self
|
|
382
|
+
|
|
383
|
+
def outside(self, *exclude: FaceNames, tags: list[int] = None) -> FaceSelection:
|
|
384
|
+
"""Returns the complete set of outside faces.
|
|
385
|
+
|
|
386
|
+
If implemented, it is possible to exclude a set of faces based on their name
|
|
387
|
+
or a list of tags (integers)
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
FaceSelection: The selected faces
|
|
391
|
+
"""
|
|
392
|
+
if tags is None:
|
|
393
|
+
tags = []
|
|
394
|
+
dimtags = gmsh.model.get_boundary(self.dimtags, True, False)
|
|
395
|
+
return FaceSelection([t for d,t in dimtags if t not in tags])
|
|
396
|
+
|
|
397
|
+
def face(self, name: FaceNames, tool: GeoObject = None) -> FaceSelection:
|
|
398
|
+
"""Returns the FaceSelection for a given face name.
|
|
399
|
+
|
|
400
|
+
The face name must be defined for the type of geometry.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
name (FaceNames): The name of the face to select.
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
FaceSelection: The selected face
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
return FaceSelection(self._face_tags(name, tool))
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def dimtags(self) -> list[tuple[int, int]]:
|
|
413
|
+
return [(self.dim, tag) for tag in self.tags]
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def embeddings(self) -> list[tuple[int,int]]:
|
|
417
|
+
return []
|
|
418
|
+
|
|
419
|
+
def boundary(self) -> FaceSelection:
|
|
420
|
+
if self.dim == 3:
|
|
421
|
+
tags = gmsh.model.get_boundary(self.dimtags, oriented=False)
|
|
422
|
+
return FaceSelection([t[1] for t in tags])
|
|
423
|
+
if self.dim == 2:
|
|
424
|
+
return FaceSelection(self.tags)
|
|
425
|
+
if self.dim < 2:
|
|
426
|
+
raise ValueError('Can only generate faces for objects of dimension 2 or higher.')
|
|
427
|
+
|
|
428
|
+
@staticmethod
|
|
429
|
+
def from_dimtags(dimtags: list[tuple[int,int]]) -> GeoVolume | GeoSurface | GeoObject:
|
|
430
|
+
dim = dimtags[0][0]
|
|
431
|
+
tags = [t for d,t in dimtags]
|
|
432
|
+
if dim==0:
|
|
433
|
+
return GeoPoint(tags)
|
|
434
|
+
elif dim==1:
|
|
435
|
+
return GeoEdge(tags)
|
|
436
|
+
if dim==2:
|
|
437
|
+
return GeoSurface(tags)
|
|
438
|
+
if dim==3:
|
|
439
|
+
return GeoVolume(tags)
|
|
440
|
+
return GeoObject(tags)
|
|
441
|
+
|
|
442
|
+
class GeoVolume(GeoObject):
|
|
443
|
+
'''GeoVolume is an interface to the GMSH CAD kernel. It does not represent EMerge
|
|
444
|
+
specific geometry data.'''
|
|
445
|
+
dim = 3
|
|
446
|
+
def __init__(self, tag: int | list[int]):
|
|
447
|
+
super().__init__()
|
|
448
|
+
if isinstance(tag, list):
|
|
449
|
+
self.tags: list[int] = tag
|
|
450
|
+
else:
|
|
451
|
+
self.tags: list[int] = [tag,]
|
|
452
|
+
|
|
453
|
+
@property
|
|
454
|
+
def select(self) -> DomainSelection:
|
|
455
|
+
return DomainSelection(self.tags)
|
|
456
|
+
|
|
457
|
+
class GeoPoint(GeoObject):
|
|
458
|
+
dim = 0
|
|
459
|
+
|
|
460
|
+
@property
|
|
461
|
+
def select(self) -> PointSelection:
|
|
462
|
+
return PointSelection(self.tags)
|
|
463
|
+
|
|
464
|
+
def __init__(self, tag: int | list[int]):
|
|
465
|
+
super().__init__()
|
|
466
|
+
if isinstance(tag, list):
|
|
467
|
+
self.tags: list[int] = tag
|
|
468
|
+
else:
|
|
469
|
+
self.tags: list[int] = [tag,]
|
|
470
|
+
|
|
471
|
+
class GeoEdge(GeoObject):
|
|
472
|
+
dim = 1
|
|
473
|
+
|
|
474
|
+
@property
|
|
475
|
+
def select(self) -> EdgeSelection:
|
|
476
|
+
return EdgeSelection(self.tags)
|
|
477
|
+
|
|
478
|
+
def __init__(self, tag: int | list[int]):
|
|
479
|
+
super().__init__()
|
|
480
|
+
if isinstance(tag, list):
|
|
481
|
+
self.tags: list[int] = tag
|
|
482
|
+
else:
|
|
483
|
+
self.tags: list[int] = [tag,]
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
class GeoSurface(GeoObject):
|
|
487
|
+
'''GeoVolume is an interface to the GMSH CAD kernel. It does not reprsent Emerge
|
|
488
|
+
specific geometry data.'''
|
|
489
|
+
dim = 2
|
|
490
|
+
|
|
491
|
+
@property
|
|
492
|
+
def select(self) -> FaceSelection:
|
|
493
|
+
return FaceSelection(self.tags)
|
|
494
|
+
|
|
495
|
+
def __init__(self, tag: int | list[int]):
|
|
496
|
+
super().__init__()
|
|
497
|
+
if isinstance(tag, list):
|
|
498
|
+
self.tags: list[int] = tag
|
|
499
|
+
else:
|
|
500
|
+
self.tags: list[int] = [tag,]
|
|
501
|
+
|
|
502
|
+
class GeoPolygon(GeoSurface):
|
|
503
|
+
|
|
504
|
+
def __init__(self,
|
|
505
|
+
tags: list[int]):
|
|
506
|
+
super().__init__(tags)
|
|
507
|
+
self.points: list[int] = None
|
|
508
|
+
self.lines: list[int] = None
|
|
509
|
+
|
|
510
|
+
|