ansys-pyensight-core 0.11.0__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 (37) hide show
  1. ansys/pyensight/core/__init__.py +41 -0
  2. ansys/pyensight/core/common.py +341 -0
  3. ansys/pyensight/core/deep_pixel_view.html +98 -0
  4. ansys/pyensight/core/dockerlauncher.py +1124 -0
  5. ansys/pyensight/core/dvs.py +872 -0
  6. ansys/pyensight/core/enscontext.py +345 -0
  7. ansys/pyensight/core/enshell_grpc.py +641 -0
  8. ansys/pyensight/core/ensight_grpc.py +874 -0
  9. ansys/pyensight/core/ensobj.py +515 -0
  10. ansys/pyensight/core/launch_ensight.py +296 -0
  11. ansys/pyensight/core/launcher.py +388 -0
  12. ansys/pyensight/core/libuserd.py +2110 -0
  13. ansys/pyensight/core/listobj.py +280 -0
  14. ansys/pyensight/core/locallauncher.py +579 -0
  15. ansys/pyensight/core/py.typed +0 -0
  16. ansys/pyensight/core/renderable.py +880 -0
  17. ansys/pyensight/core/session.py +1923 -0
  18. ansys/pyensight/core/sgeo_poll.html +24 -0
  19. ansys/pyensight/core/utils/__init__.py +21 -0
  20. ansys/pyensight/core/utils/adr.py +111 -0
  21. ansys/pyensight/core/utils/dsg_server.py +1220 -0
  22. ansys/pyensight/core/utils/export.py +606 -0
  23. ansys/pyensight/core/utils/omniverse.py +769 -0
  24. ansys/pyensight/core/utils/omniverse_cli.py +614 -0
  25. ansys/pyensight/core/utils/omniverse_dsg_server.py +1196 -0
  26. ansys/pyensight/core/utils/omniverse_glb_server.py +848 -0
  27. ansys/pyensight/core/utils/parts.py +1221 -0
  28. ansys/pyensight/core/utils/query.py +487 -0
  29. ansys/pyensight/core/utils/readers.py +300 -0
  30. ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
  31. ansys/pyensight/core/utils/support.py +128 -0
  32. ansys/pyensight/core/utils/variables.py +2019 -0
  33. ansys/pyensight/core/utils/views.py +674 -0
  34. ansys_pyensight_core-0.11.0.dist-info/METADATA +309 -0
  35. ansys_pyensight_core-0.11.0.dist-info/RECORD +37 -0
  36. ansys_pyensight_core-0.11.0.dist-info/WHEEL +4 -0
  37. ansys_pyensight_core-0.11.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1221 @@
1
+ # Copyright (C) 2022 - 2026 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ """Parts module.
24
+
25
+ This module allows PyEnSight to control the parts in the EnSight session.
26
+
27
+ Example for selecting all 3D parts:
28
+
29
+ (PyEnSight)
30
+ >>> from ansys.pyensight.core import LocalLauncher
31
+ >>> session = LocalLauncher().start()
32
+ >>> parts = session.ensight.utils.parts
33
+ >>> parts.select_by_dimension(3)
34
+
35
+ (EnSight)
36
+ >>> from ensight.utils import parts
37
+ >>> parts.select_by_dimension(3)
38
+
39
+ """
40
+ from types import ModuleType
41
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
42
+
43
+ try:
44
+ import ensight
45
+ from ensight.objs import ens_emitterobj, ensobjlist # type: ignore
46
+ except ImportError:
47
+ from ansys.api.pyensight.ens_emitterobj import ens_emitterobj
48
+ from ansys.pyensight.core.listobj import ensobjlist
49
+
50
+ if TYPE_CHECKING:
51
+ from ansys.api.pyensight import ensight_api
52
+ from ansys.api.pyensight.ens_part import ENS_PART
53
+ from ansys.api.pyensight.ens_part_particle_trace import ENS_PART_PARTICLE_TRACE
54
+ from ansys.api.pyensight.ens_var import ENS_VAR
55
+
56
+
57
+ def convert_part(
58
+ _ensight: Union["ensight_api.ensight", "ensight"], part: Union[str, int, "ENS_PART"]
59
+ ):
60
+ if isinstance(part, str):
61
+ return _ensight.objs.core.PARTS[part][0].PARTNUMBER
62
+ elif isinstance(part, int):
63
+ return part
64
+ elif hasattr(part, "PARTNUMBER"):
65
+ return part.PARTNUMBER
66
+
67
+
68
+ def convert_variable(
69
+ _ensight: Union["ensight_api.ensight", "ensight"], var: Union[str, int, "ENS_VAR"]
70
+ ) -> Optional[int]:
71
+ if isinstance(var, str):
72
+ return int(_ensight.objs.core.VARIABLES[var][0].ID)
73
+ elif isinstance(var, int):
74
+ return var
75
+ elif hasattr(var, "ID"):
76
+ return int(var.ID)
77
+ return None # pragma: no cover
78
+
79
+
80
+ class Parts:
81
+ """Controls the parts in the current EnSight ``Session`` instance."""
82
+
83
+ class _EnSEmitterPoint(ens_emitterobj): # pragma: no cover
84
+ def __init__( # pragma: no cover
85
+ self,
86
+ ensight: "ensight",
87
+ point1: Optional[List[float]] = [0, 0, 0],
88
+ ): # pragma: no cover
89
+ if not isinstance(ensight, ModuleType):
90
+ raise RuntimeError(
91
+ "The class cannot be used directly in PyEnSight. It should not be used directly even in EnSight"
92
+ )
93
+ super().__init__(ensight.objs.EMIT_CURSOR)
94
+ self.ensight = ensight
95
+ self.ensight.view_transf.cursor(*point1)
96
+ self.CENTROID = point1
97
+
98
+ class _EnSEmitterGrid(ens_emitterobj): # pragma: no cover
99
+ def __init__( # pragma: no cover
100
+ self,
101
+ ensight: "ensight",
102
+ point1: Optional[List[float]] = [0, 0, 0],
103
+ point2: Optional[List[float]] = [0, 0, 0],
104
+ point3: Optional[List[float]] = [0, 0, 0],
105
+ point4: Optional[List[float]] = [0, 0, 0],
106
+ num_points_x: Optional[int] = 25,
107
+ num_points_y: Optional[int] = 25,
108
+ ): # pragma: no cover
109
+ if not isinstance(ensight, ModuleType):
110
+ raise RuntimeError(
111
+ "The class cannot be used directly in PyEnSight. It should not be used directly even in EnSight"
112
+ )
113
+ super().__init__(ensight.objs.EMIT_PLANE)
114
+ self.ensight = ensight
115
+ self.ensight.view_transf.plane(1, *point1)
116
+ self.ensight.view_transf.plane(2, *point2)
117
+ self.ensight.view_transf.plane(3, *point3)
118
+ self.POINT1 = point1
119
+ self.POINT2 = point2
120
+ self.POINT3 = point3
121
+ self.POINT4 = point4
122
+ self.NUM_POINTS_X = num_points_x
123
+ self.NUM_POINTS_Y = num_points_y
124
+
125
+ class _EnSEmitterLine(ens_emitterobj): # pragma: no cover
126
+ def __init__( # pragma: no cover
127
+ self,
128
+ ensight: "ensight",
129
+ point1: Optional[List[float]] = [0, 0, 0],
130
+ point2: Optional[List[float]] = [0, 0, 0],
131
+ num_points: Optional[int] = 100,
132
+ ): # pragma: no cover
133
+ if not isinstance(ensight, ModuleType):
134
+ raise RuntimeError(
135
+ "The class cannot be used directly in PyEnSight. It should not be used directly even in EnSight"
136
+ )
137
+ super().__init__(ensight.objs.EMIT_LINE)
138
+ self.ensight = ensight
139
+ self.ensight.view_transf.line(1, *point1)
140
+ self.ensight.view_transf.line(2, *point2)
141
+ self.POINT1 = point1
142
+ self.POINT2 = point2
143
+ self.NUM_POINTS = num_points
144
+
145
+ class _EnSEmitterPart(ens_emitterobj): # pragma: no cover
146
+ def __init__( # pragma: no cover
147
+ self,
148
+ ensight: "ensight",
149
+ part: Optional[Any] = None,
150
+ part_kind: Optional[Any] = 0,
151
+ num_points: Optional[int] = 100,
152
+ ): # pragma: no cover
153
+ if not isinstance(ensight, ModuleType):
154
+ raise RuntimeError(
155
+ "The class cannot be used directly in PyEnSight. It should not be used directly even in EnSight"
156
+ )
157
+ super().__init__(ensight.objs.EMIT_PART)
158
+ self.ensight = ensight
159
+ if not part:
160
+ raise RuntimeError("part is a required input")
161
+ self.PART = convert_part(self.ensight, part)
162
+ self.NUM_POINTS = num_points
163
+ self.DISTRIB_TYPE = part_kind
164
+
165
+ def __init__(self, ensight: Union["ensight_api.ensight", "ensight"]):
166
+ self.ensight = ensight
167
+
168
+ def select_parts_by_dimension(self, dimension: int) -> ensobjlist["ENS_PART"]:
169
+ """Select parts by the input dimension and return the parts found.
170
+
171
+ Parameters
172
+ ----------
173
+ dimension : int
174
+ Dimension for selecting parts.
175
+
176
+ Returns
177
+ -------
178
+ ensobjlist["ENS_PART"]
179
+ found (ensobjlist): List of parts found.
180
+
181
+ """
182
+ parts = self.ensight.objs.core.PARTS
183
+ parts.set_attr("SELECTED", False)
184
+ found = parts.find(True, f"HAS{dimension}DELEMENTS")
185
+ found.set_attr("SELECTED", True)
186
+ return found
187
+
188
+ def select_parts_invert(self) -> ensobjlist["ENS_PART"]:
189
+ """Select parts currently not selected, deselecting the previously selected parts.
190
+
191
+ Returns
192
+ -------
193
+ ensobjlist["ENS_PART"]
194
+ Updated list of parts selected.
195
+
196
+ """
197
+ self.ensight.part.select_invert()
198
+ parts = self.ensight.objs.core.PARTS
199
+ return parts.find(True, "SELECTED")
200
+
201
+ def select_parts_by_tag(
202
+ self,
203
+ tag: Optional[str] = None,
204
+ value: Optional[str] = None,
205
+ tagdict: Optional[Dict[str, str]] = None,
206
+ ) -> ensobjlist["ENS_PART"]:
207
+ """Select parts by the input dimension and return the parts found.
208
+
209
+ Parameters
210
+ ----------
211
+ tag : str, optional
212
+ Tag for finding the parts.
213
+ value : str, optional
214
+ Value for finding the parts.
215
+ tagdict : dict, optional
216
+ Dictionary containing the key and value pairs for finding
217
+ the parts. Only the parts that have all the keys and corresponding
218
+ values are returned. If a value for this parameter is supplied, it
219
+ takes precedence over the values supplied for the ``tag`` and
220
+ ``value`` parameters.
221
+
222
+ Returns
223
+ -------
224
+ ensobjlist["ENS_PART"]
225
+ List of parts found. If no arguments are given, all parts are returned.
226
+
227
+ """
228
+ parts = self.ensight.objs.core.PARTS
229
+ metadata = {p: p.METADATA for p in parts}
230
+ found = ensobjlist()
231
+ if not tag and not value and not tagdict:
232
+ self.ensight.part.select_all()
233
+ return parts
234
+ if not tagdict:
235
+ if tag and value:
236
+ found = ensobjlist([p for p, met in metadata.items() if met.get(tag) == value])
237
+ elif value and not tag:
238
+ found = ensobjlist([p for p, met in metadata.items() if value in met.values()])
239
+ elif tag and not value: # pragma: no cover
240
+ found = ensobjlist([p for p, met in metadata.items() if tag in met.keys()])
241
+ else:
242
+ found = ensobjlist(
243
+ [
244
+ p
245
+ for p, met in metadata.items()
246
+ if all(met.get(k) == v for k, v in tagdict.items())
247
+ ]
248
+ )
249
+ if found:
250
+ found.set_attr("SELECTED", True)
251
+ return found
252
+
253
+ _EMIT_POINT: int = 0
254
+ _EMIT_LINE: int = 1
255
+ _EMIT_PLANE: int = 2
256
+ _EMIT_PART: int = 3
257
+ PT_POS_TIME: str = "+"
258
+ PT_NEG_TIME: str = "-"
259
+ PT_POS_NEG_TIME: str = "+/-"
260
+ PART_EMIT_FROM_NODES: int = 0
261
+ PART_EMIT_FROM_AREA: int = 1
262
+
263
+ def _create_emitters(
264
+ self,
265
+ emitter_type: int,
266
+ points: Optional[List[List[float]]] = None,
267
+ point1: Optional[List[float]] = None,
268
+ point2: Optional[List[float]] = None,
269
+ point3: Optional[List[float]] = None,
270
+ parts: Optional[List["ENS_PART"]] = None,
271
+ part_distribution_type: Optional[int] = 0,
272
+ num_points: Optional[int] = 100,
273
+ num_points_x: Optional[int] = 25,
274
+ num_points_y: Optional[int] = 25,
275
+ ) -> List[Any]:
276
+ """Private routine to create emitter objects"""
277
+ new_emitters: List[Any] = []
278
+ if emitter_type == self._EMIT_POINT:
279
+ if not points: # pragma: no cover
280
+ raise RuntimeError(
281
+ "list of points needed if particle trace emitted from points"
282
+ ) # pragma: no cover
283
+ for p in points:
284
+ if isinstance(self.ensight, ModuleType): # pragma: no cover
285
+ new_emitters.append(
286
+ self._EnSEmitterPoint(self.ensight, point1=p)
287
+ ) # pragma: no cover
288
+ else:
289
+ new_emitters.append(
290
+ f"ensight.utils.parts._EnSEmitterPoint(ensight, point1={p})"
291
+ )
292
+ elif emitter_type == self._EMIT_LINE:
293
+ if not any([point1, point2]):
294
+ raise RuntimeError("point1 and point2 needed if particle trace emitted from line")
295
+ if isinstance(self.ensight, ModuleType): # pragma: no cover
296
+ new_emitters.append( # pragma: no cover
297
+ self._EnSEmitterLine(
298
+ self.ensight, point1=point1, point2=point2, num_points=num_points
299
+ )
300
+ )
301
+ else:
302
+ new_emitters.append(
303
+ f"ensight.utils.parts._EnSEmitterLine(ensight, point1={point1}, point2={point2}, num_points={num_points})"
304
+ )
305
+ elif emitter_type == self._EMIT_PLANE:
306
+ if not any([point1, point2, point3]):
307
+ raise RuntimeError( # pragma: no cover
308
+ "point1, point2 and point3 needed if particle trace emitted from plane"
309
+ )
310
+ if isinstance(self.ensight, ModuleType): # pragma: no cover
311
+ new_emitters.append( # pragma: no cover
312
+ self._EnSEmitterGrid(
313
+ self.ensight,
314
+ point1=point1,
315
+ point2=point2,
316
+ point3=point3,
317
+ num_points_x=num_points_x,
318
+ num_points_y=num_points_y,
319
+ )
320
+ )
321
+ else:
322
+ new_emitters.append(
323
+ f"ensight.utils.parts._EnSEmitterGrid(ensight, point1={point1}, point2={point2}, point3={point3}, num_points_x={num_points_x}, num_points_y={num_points_y})"
324
+ )
325
+ elif emitter_type == self._EMIT_PART: # pragma: no cover
326
+ if not parts: # pragma: no cover
327
+ raise RuntimeError(
328
+ "part and num_points needed if particle trace emitted from part"
329
+ ) # pragma: no cover
330
+ for p in parts:
331
+ if isinstance(self.ensight, ModuleType): # pragma: no cover
332
+ new_emitters.append( # pragma: no cover
333
+ self._EnSEmitterPart(
334
+ self.ensight,
335
+ part=p,
336
+ num_points=num_points,
337
+ part_kind=part_distribution_type,
338
+ )
339
+ )
340
+ else:
341
+ new_emitters.append(
342
+ f"ensight.utils.parts._EnSEmitterPart(ensight, part={convert_part(self.ensight ,p)}, num_points={num_points}, part_kind={part_distribution_type})"
343
+ )
344
+ else:
345
+ raise RuntimeError(
346
+ "No input provided to create the emitters for the particle trace"
347
+ ) # pragma: no cover
348
+ return new_emitters
349
+
350
+ def _create_particle_trace_part(
351
+ self,
352
+ name: str,
353
+ variable: Union[str, int, "ENS_VAR"],
354
+ direction: str,
355
+ source_parts: List["ENS_PART"],
356
+ pathlines: Optional[bool] = False,
357
+ emit_time: Optional[float] = None,
358
+ total_time: Optional[float] = None,
359
+ delta_time: Optional[float] = None,
360
+ surface_restrict: Optional[bool] = False,
361
+ ) -> "ENS_PART_PARTICLE_TRACE":
362
+ """Private routine to create a particle trace part object"""
363
+ current_timestep = None
364
+ direction_map = {
365
+ self.PT_POS_TIME: self.ensight.objs.enums.POS_TIME,
366
+ self.PT_NEG_TIME: self.ensight.objs.enums.NEG_TIME,
367
+ self.PT_POS_NEG_TIME: self.ensight.objs.enums.POS_NEG_TIME,
368
+ }
369
+ idx = self.ensight.objs.enums.PART_PARTICLE_TRACE
370
+ def_part: "ENS_PART_PARTICLE_TRACE" = self.ensight.objs.core.DEFAULTPARTS[idx]
371
+ def_part.TYPE = self.ensight.objs.enums.STREAMLINE
372
+ if pathlines is True:
373
+ def_part.TYPE = self.ensight.objs.enums.PATHLINE
374
+ current_timestep = self.ensight.objs.core.TIMESTEP
375
+ self.ensight.objs.core.TIMESTEP = self.ensight.objs.core.TIMESTEP_LIMITS[0]
376
+ if total_time:
377
+ def_part.TOTALTIME = total_time
378
+ if delta_time:
379
+ def_part.DELTATIME = delta_time
380
+ if emit_time: # pragma: no cover
381
+ def_part.STARTTIME = emit_time
382
+ def_part.DESCRIPTION = name
383
+ def_part.VARIABLE = convert_variable(self.ensight, variable)
384
+ def_part.SURFACERESTRICTED = False
385
+ def_part.TRACEDIRECTION = direction_map.get(direction)
386
+ if surface_restrict:
387
+ def_part.SURFACERESTRICTED = True
388
+ particle_trace_part: "ENS_PART_PARTICLE_TRACE" = def_part.createpart(
389
+ sources=source_parts, name=name
390
+ )[0]
391
+ if current_timestep:
392
+ self.ensight.objs.core.TIMESTEP = current_timestep
393
+ return particle_trace_part
394
+
395
+ def _add_emitters_to_particle_trace_part(
396
+ self,
397
+ particle_trace_part: "ENS_PART_PARTICLE_TRACE",
398
+ new_emitters: List[Any],
399
+ palette: Optional[str] = None,
400
+ clean: Optional[bool] = False,
401
+ ) -> "ENS_PART_PARTICLE_TRACE":
402
+ """Private utility to add emitters to an existing particle trace part."""
403
+ if isinstance(self.ensight, ModuleType): # pragma: no cover
404
+ if clean: # pragma: no cover
405
+ emitters = [] # pragma: no cover
406
+ else: # pragma: no cover
407
+ emitters = particle_trace_part.EMITTERS.copy() # pragma: no cover
408
+ emitters.extend(new_emitters) # pragma: no cover
409
+ particle_trace_part.EMITTERS = emitters # pragma: no cover
410
+ else:
411
+ if clean:
412
+ self.ensight._session.cmd("enscl.emitters=[]", do_eval=False)
413
+ else:
414
+ self.ensight._session.cmd(
415
+ f"enscl.emitters=ensight.objs.wrap_id({particle_trace_part.objid}).EMITTERS.copy()",
416
+ do_eval=False,
417
+ )
418
+ text = "enscl.emitters.extend(["
419
+ for emitter in new_emitters:
420
+ text += emitter + ", "
421
+ text = text[:-2]
422
+ text += "])"
423
+ self.ensight._session.cmd(text, do_eval=False)
424
+ self.ensight._session.cmd(
425
+ f"ensight.objs.wrap_id({particle_trace_part.objid}).setattr('EMITTERS', enscl.emitters.copy())"
426
+ )
427
+ self.ensight._session.cmd("del enscl.emitters", do_eval=False)
428
+ if palette:
429
+ particle_trace_part.COLORBYPALETTE = palette
430
+ return particle_trace_part
431
+
432
+ def _cure_particle_trace_part(
433
+ self, particle_trace_part: Union[str, int, "ENS_PART_PARTICLE_TRACE"]
434
+ ) -> "ENS_PART_PARTICLE_TRACE":
435
+ """Private utility to cure an input particle trace part and convert it to an ``ENS_PART`"""
436
+
437
+ # the add_emitter* functions were added in 2024 R2
438
+ if not isinstance(self.ensight, ModuleType): # pragma: no cover
439
+ self.ensight._session.ensight_version_check("2024 R2")
440
+
441
+ _particle_trace_part: "ENS_PART_PARTICLE_TRACE"
442
+ if isinstance(particle_trace_part, (str, int)): # pragma: no cover
443
+ temp = self.ensight.objs.core.PARTS[particle_trace_part] # pragma: no cover
444
+ if not temp: # pragma: no cover
445
+ raise RuntimeError(
446
+ "particle_trace_part input is not a valid part"
447
+ ) # pragma: no cover
448
+ _particle_trace_part = temp[0] # pragma: no cover
449
+ else:
450
+ _particle_trace_part = particle_trace_part
451
+ return _particle_trace_part
452
+
453
+ def _prepare_particle_creation(
454
+ self,
455
+ direction: Optional[str] = None,
456
+ source_parts: Optional[List[Union[str, int, "ENS_PART"]]] = None,
457
+ ) -> Tuple[str, List["ENS_PART"]]:
458
+ """Private utility to set the direction if not provided, and to cure the list of source parts."""
459
+
460
+ # the create_particle* functions were added in 2024 R2
461
+ if not isinstance(self.ensight, ModuleType): # pragma: no cover
462
+ self.ensight._session.ensight_version_check("2024 R2")
463
+
464
+ if not direction:
465
+ direction = self.PT_POS_TIME
466
+ if source_parts: # pragma: no cover
467
+ converted_source_parts = [convert_part(self.ensight, p) for p in source_parts]
468
+ if not source_parts: # pragma: no cover
469
+ converted_source_parts = self.ensight.objs.core.selection( # pragma: no cover
470
+ name="ENS_PART"
471
+ )
472
+ if not converted_source_parts: # pragma: no cover
473
+ raise RuntimeError("No part selected for particle trace generation") # pragma: no cover
474
+ return direction, converted_source_parts
475
+
476
+ def _find_palette(self, color_by: Optional[Union[str, int, "ENS_VAR"]] = None) -> Optional[str]:
477
+ """Private utility to find the description of the input color_by variable"""
478
+ palette: Optional[str] = None
479
+ if color_by:
480
+ try:
481
+ _color_by_var: List["ENS_VAR"] = self.ensight.objs.core.VARIABLES.find(
482
+ [convert_variable(self.ensight, color_by)], attr="ID"
483
+ )
484
+ if _color_by_var:
485
+ palette = _color_by_var[0].DESCRIPTION
486
+ except Exception:
487
+ raise RuntimeError(
488
+ "The variable supplied to color the particle trace by does not exist"
489
+ )
490
+ return palette
491
+
492
+ def create_particle_trace_from_points(
493
+ self,
494
+ name: str,
495
+ variable: Union[str, int, "ENS_VAR"],
496
+ points: List[List[float]],
497
+ direction: Optional[str] = None,
498
+ pathlines: Optional[bool] = False,
499
+ source_parts: Optional[List[Union[str, int, "ENS_PART"]]] = None,
500
+ emit_time: Optional[float] = None,
501
+ total_time: Optional[float] = None,
502
+ delta_time: Optional[float] = None,
503
+ color_by: Optional[Union[str, int, "ENS_VAR"]] = None,
504
+ ) -> "ENS_PART_PARTICLE_TRACE":
505
+ """
506
+ Create a particle trace part from a list o points.
507
+ Returns the ``ENS_PART`` generated.
508
+
509
+ Parameters
510
+ ----------
511
+
512
+ name: str
513
+ The name of part to be generated
514
+ variable:
515
+ The variable to compute the particle traces with.
516
+ It can be the name, the ID or the ``ENS_VAR`` object. It must be a vector variable.
517
+ direction: str
518
+ The direction for the particle traces to be generated.
519
+ This table describes the options:
520
+
521
+ ================== ==============================================
522
+ Name Query type
523
+ ================== ==============================================
524
+ PT_POS_TIME Follow the vector direction
525
+ PT_NEG_TIME Go contrary to the vector direction
526
+ PT_POS_NEG_TIME Follow and go contrary to the vector direction
527
+ ================== ==============================================
528
+
529
+ If not provided, it will default to ``PT_POS_TIME``
530
+ pathlines: bool
531
+ True if the particle traces need to be pathlines
532
+ points: list
533
+ List of coordinates for the seed points.
534
+ source_parts: list
535
+ A list of parts to create the particle trace in. For instance, in a CFD
536
+ simulation this might be the fluid zone.
537
+ If not provided, the function will try to look for the selected parts.
538
+ emit_time: float
539
+ The emission time to start the particle trace from. If not provided,
540
+ it will use the current time.
541
+ total_time: float
542
+ The total emission time. If not provided, EnSight will provide the end time
543
+ for a transient simulation, an internal best time for steady state simulations.
544
+ delta_time: float
545
+ The interval for the emissions. If not provided, EnSight will provide
546
+ a best estimate.
547
+ color_by
548
+ The optional variable to color the particle trace by.
549
+ It can be the name, the ID or the ``ENS_VAR`` object.
550
+
551
+ Examples
552
+ --------
553
+ >>> s = LocalLauncher().start()
554
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
555
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
556
+ >>> s.load_data(cas_file, result_file=dat_file)
557
+ >>> s.ensight.utils.parts.create_particle_trace_from_points("mytraces", "Velocity", points=[[-0.02,-0.123,0.01576],[0.109876,-0.123,0.0123]], source_parts=parts.select_parts_by_dimension(3))
558
+ """
559
+ emitter_type = self._EMIT_POINT
560
+ direction, converted_source_parts = self._prepare_particle_creation(
561
+ direction=direction, source_parts=source_parts
562
+ )
563
+ particle_trace_part = self._create_particle_trace_part(
564
+ name,
565
+ variable,
566
+ direction,
567
+ converted_source_parts,
568
+ pathlines=pathlines,
569
+ emit_time=emit_time,
570
+ delta_time=delta_time,
571
+ total_time=total_time,
572
+ )
573
+ new_emitters = self._create_emitters(emitter_type=emitter_type, points=points)
574
+ palette = self._find_palette(color_by=color_by)
575
+ return self._add_emitters_to_particle_trace_part(
576
+ particle_trace_part, new_emitters=new_emitters, palette=palette, clean=True
577
+ )
578
+
579
+ def create_particle_trace_from_line(
580
+ self,
581
+ name: str,
582
+ variable: Union[str, int, "ENS_VAR"],
583
+ point1: List[float],
584
+ point2: List[float],
585
+ num_points: Optional[int] = 100,
586
+ direction: Optional[str] = None,
587
+ pathlines: Optional[bool] = False,
588
+ source_parts: Optional[List[Union[str, int, "ENS_PART"]]] = None,
589
+ emit_time: Optional[float] = None,
590
+ total_time: Optional[float] = None,
591
+ delta_time: Optional[float] = None,
592
+ color_by: Optional[Union[str, int, "ENS_VAR"]] = None,
593
+ ) -> "ENS_PART_PARTICLE_TRACE":
594
+ """
595
+ Create a particle trace part from a line.
596
+ Returns the ``ENS_PART`` generated.
597
+
598
+ Parameters
599
+ ----------
600
+
601
+ name: str
602
+ The name of part to be generated
603
+ variable:
604
+ The variable to compute the particle traces with.
605
+ It can be the name, the ID or the ``ENS_VAR`` object. It must be a vector variable.
606
+ direction: str
607
+ The direction for the particle traces to be generated.
608
+ This table describes the options:
609
+
610
+ ================== ==============================================
611
+ Name Query type
612
+ ================== ==============================================
613
+ PT_POS_TIME Follow the vector direction
614
+ PT_NEG_TIME Go contrary to the vector direction
615
+ PT_POS_NEG_TIME Follow and go contrary to the vector direction
616
+ ================== ==============================================
617
+
618
+ If not provided, it will default to ``PT_POS_TIME``
619
+ pathlines: bool
620
+ True if the particle traces need to be pathlines
621
+ point1: list
622
+ List of coordinates for point 1.
623
+ point2: list
624
+ List of coordinates for point 2.
625
+ source_parts: list
626
+ A list of parts to create the particle trace in. For instance, in a CFD
627
+ simulation this might be the fluid zone.
628
+ If not provided, the function will try to look for the selected parts.
629
+ num_points: int
630
+ The number of points to emit from. Defaults to 100.
631
+ emit_time: float
632
+ The emission time to start the particle trace from. If not provided,
633
+ it will use the current time.
634
+ total_time: float
635
+ The total emission time. If not provided, EnSight will provide the end time
636
+ for a transient simulation, an internal best time for steady state simulations.
637
+ delta_time: float
638
+ The interval for the emissions. If not provided, EnSight will provide
639
+ a best estimate.
640
+ color_by
641
+ The optional variable to color the particle trace by.
642
+ It can be the name, the ID or the ``ENS_VAR`` object.
643
+
644
+ Examples
645
+ --------
646
+ >>> s = LocalLauncher().start()
647
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
648
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
649
+ >>> s.load_data(cas_file, result_file=dat_file)
650
+ >>> parts = s.ensight.utils.parts
651
+ >>> parts.create_particle_trace_from_line("mytraces", "Velocity", point1=[-0.02,-0.123,0.01576], point2=[0.109876,-0.123,0.0123], num_points=10, source_parts=parts.select_parts_by_dimension(3))
652
+ """
653
+ emitter_type = self._EMIT_LINE
654
+ direction, converted_source_parts = self._prepare_particle_creation(
655
+ direction=direction, source_parts=source_parts
656
+ )
657
+ particle_trace_part = self._create_particle_trace_part(
658
+ name,
659
+ variable,
660
+ direction,
661
+ converted_source_parts,
662
+ pathlines=pathlines,
663
+ emit_time=emit_time,
664
+ delta_time=delta_time,
665
+ total_time=total_time,
666
+ )
667
+ new_emitters = self._create_emitters(
668
+ emitter_type=emitter_type, point1=point1, point2=point2, num_points=num_points
669
+ )
670
+ palette = self._find_palette(color_by=color_by)
671
+ return self._add_emitters_to_particle_trace_part(
672
+ particle_trace_part, new_emitters=new_emitters, palette=palette, clean=True
673
+ )
674
+
675
+ def create_particle_trace_from_plane(
676
+ self,
677
+ name: str,
678
+ variable: Union[str, int, "ENS_VAR"],
679
+ point1: List[float],
680
+ point2: List[float],
681
+ point3: List[float],
682
+ num_points_x: Optional[int] = 25,
683
+ num_points_y: Optional[int] = 25,
684
+ direction: Optional[str] = None,
685
+ pathlines: Optional[bool] = False,
686
+ source_parts: Optional[List[Union[str, int, "ENS_PART"]]] = None,
687
+ emit_time: Optional[float] = None,
688
+ total_time: Optional[float] = None,
689
+ delta_time: Optional[float] = None,
690
+ color_by: Optional[Union[str, int, "ENS_VAR"]] = None,
691
+ ) -> "ENS_PART_PARTICLE_TRACE":
692
+ """
693
+ Create a particle trace part from a plane.
694
+ Returns the ``ENS_PART`` generated.
695
+
696
+ Parameters
697
+ ----------
698
+
699
+ name: str
700
+ The name of part to be generated
701
+ variable:
702
+ The variable to compute the particle traces with.
703
+ It can be the name, the ID or the ``ENS_VAR`` object. It must be a vector variable.
704
+ direction: str
705
+ The direction for the particle traces to be generated.
706
+ This table describes the options:
707
+
708
+ ================== ==============================================
709
+ Name Query type
710
+ ================== ==============================================
711
+ PT_POS_TIME Follow the vector direction
712
+ PT_NEG_TIME Go contrary to the vector direction
713
+ PT_POS_NEG_TIME Follow and go contrary to the vector direction
714
+ ================== ==============================================
715
+
716
+ If not provided, it will default to ``PT_POS_TIME``
717
+ pathlines: bool
718
+ True if the particle traces need to be pathlines
719
+ point1: list
720
+ List of coordinates for point 1, being a corner of the plane.
721
+ point2: list
722
+ List of coordinates for point 2, being a corner of the plane.
723
+ point3: list
724
+ List of coordinates for point 3, being a corner of the plane.
725
+ source_parts: list
726
+ A list of parts to create the particle trace in. For instance, in a CFD
727
+ simulation this might be the fluid zone.
728
+ If not provided, the function will try to look for the selected parts.
729
+ num_points_x: int
730
+ The number of points on the ``X`` direction of the emission plane.
731
+ Defaults to 25.
732
+ num_points_y: int
733
+ The number of points on the ``Y`` direction of the emission plane.
734
+ Defaults to 25.
735
+ emit_time: float
736
+ The emission time to start the particle trace from. If not provided,
737
+ it will use the current time.
738
+ total_time: float
739
+ The total emission time. If not provided, EnSight will provide the end time
740
+ for a transient simulation, an internal best time for steady state simulations.
741
+ delta_time: float
742
+ The interval for the emissions. If not provided, EnSight will provide
743
+ a best estimate.
744
+ color_by
745
+ The optional variable to color the particle trace by.
746
+ It can be the name, the ID or the ``ENS_VAR`` object.
747
+
748
+ Examples
749
+ --------
750
+ >>> s = LocalLauncher().start()
751
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
752
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
753
+ >>> s.load_data(cas_file, result_file=dat_file)
754
+ >>> parts = s.ensight.utils.parts
755
+ >>> parts.create_particle_trace_from_plane("mytraces", "Velocity", point1=[-0.02,-0.123,0.01576], point2=[0.109876,-0.123,0.0123], point3=[0.1, 0, 0.05] ,num_points_x=10, num_points_y=10, source_parts=parts.select_parts_by_dimension(3))
756
+ """
757
+ emitter_type = self._EMIT_PLANE
758
+ direction, converted_source_parts = self._prepare_particle_creation(
759
+ direction=direction, source_parts=source_parts
760
+ )
761
+ particle_trace_part = self._create_particle_trace_part(
762
+ name,
763
+ variable,
764
+ direction,
765
+ converted_source_parts,
766
+ pathlines=pathlines,
767
+ emit_time=emit_time,
768
+ delta_time=delta_time,
769
+ total_time=total_time,
770
+ )
771
+ new_emitters = self._create_emitters(
772
+ emitter_type=emitter_type,
773
+ point1=point1,
774
+ point2=point2,
775
+ point3=point3,
776
+ num_points_x=num_points_x,
777
+ num_points_y=num_points_y,
778
+ )
779
+ palette = self._find_palette(color_by=color_by)
780
+ return self._add_emitters_to_particle_trace_part(
781
+ particle_trace_part, new_emitters=new_emitters, palette=palette, clean=True
782
+ )
783
+
784
+ def create_particle_trace_from_parts(
785
+ self,
786
+ name: str,
787
+ variable: Union[str, int, "ENS_VAR"],
788
+ parts: List[Union[str, int, "ENS_PART"]],
789
+ part_distribution_type: Optional[int] = 0,
790
+ num_points: Optional[int] = 100,
791
+ direction: Optional[str] = None,
792
+ pathlines: Optional[bool] = False,
793
+ source_parts: Optional[List[Union[str, int, "ENS_PART"]]] = None,
794
+ emit_time: Optional[float] = None,
795
+ total_time: Optional[float] = None,
796
+ delta_time: Optional[float] = None,
797
+ color_by: Optional[Union[str, int, "ENS_VAR"]] = None,
798
+ surface_restrict: Optional[bool] = False,
799
+ ) -> "ENS_PART_PARTICLE_TRACE":
800
+ """
801
+ Create a particle trace part from a list of seed parts.
802
+ Returns the ``ENS_PART`` generated.
803
+
804
+ Parameters
805
+ ----------
806
+
807
+ name: str
808
+ The name of part to be generated
809
+ variable:
810
+ The variable to compute the particle traces with.
811
+ It can be the name, the ID or the ``ENS_VAR`` object. It must be a vector variable.
812
+ direction: str
813
+ The direction for the particle traces to be generated.
814
+ This table describes the options:
815
+
816
+ ================== ==============================================
817
+ Name Query type
818
+ ================== ==============================================
819
+ PT_POS_TIME Follow the vector direction
820
+ PT_NEG_TIME Go contrary to the vector direction
821
+ PT_POS_NEG_TIME Follow and go contrary to the vector direction
822
+ ================== ==============================================
823
+
824
+ If not provided, it will default to ``PT_POS_TIME``
825
+ pathlines: bool
826
+ True if the particle traces need to be pathlines
827
+ source_parts: list
828
+ A list of parts to create the particle trace in. For instance, in a CFD
829
+ simulation this might be the fluid zone.
830
+ If not provided, the function will try to look for the selected parts.
831
+ parts: list
832
+ A list of parts to emit the particle traces from.
833
+ They can be their names, their IDs or the respective ``ENS_PART`` objects.
834
+ part_distribution_type: int
835
+ The distribution of emitters in case of emission from a part.
836
+ This table describes the options:
837
+
838
+ ==================== =================================================
839
+ Name Query type
840
+ ==================== =================================================
841
+ PART_EMIT_FROM_NODES Emit from the nodes of the part
842
+ PART_EMIT_FROM_AREA Create an area of equidistant points for emission
843
+ ==================== =================================================
844
+
845
+ If not provided, it will default to ``PART_EMIT_FROM_NODES``
846
+ num_points: int
847
+ The number of points to emit from.
848
+ Defaults to 100.
849
+ emit_time: float
850
+ The emission time to start the particle trace from. If not provided,
851
+ it will use the current time.
852
+ total_time: float
853
+ The total emission time. If not provided, EnSight will provide the end time
854
+ for a transient simulation, an internal best time for steady state simulations.
855
+ delta_time: float
856
+ The interval for the emissions. If not provided, EnSight will provide
857
+ a best estimate.
858
+ color_by
859
+ The optional variable to color the particle trace by.
860
+ It can be the name, the ID or the ``ENS_VAR`` object.
861
+ surface_restrict: bool
862
+ True if the particle trace needs to be restricted to the input parts.
863
+ Defaults to False. The flag will be applied to any additional emitter
864
+ appended to the particle trace created.
865
+
866
+ Examples
867
+ --------
868
+ >>> s = LocalLauncher().start()
869
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
870
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
871
+ >>> s.load_data(cas_file, result_file=dat_file)
872
+ >>> parts = s.ensight.utils.parts
873
+ >>> parts.create_particle_trace_from_parts("mytraces", "Velocity", parts=["hot-inlet", "cold-inlet"], num_points=100 source_parts=parts.select_parts_by_dimension(3))
874
+ """
875
+ emitter_type = self._EMIT_PART
876
+ direction, converted_source_parts = self._prepare_particle_creation(
877
+ direction=direction, source_parts=source_parts
878
+ )
879
+ particle_trace_part = self._create_particle_trace_part(
880
+ name,
881
+ variable,
882
+ direction,
883
+ converted_source_parts,
884
+ pathlines=pathlines,
885
+ emit_time=emit_time,
886
+ delta_time=delta_time,
887
+ total_time=total_time,
888
+ surface_restrict=surface_restrict,
889
+ )
890
+ new_parts = [convert_part(self.ensight, p) for p in parts]
891
+ new_emitters = self._create_emitters(
892
+ emitter_type=emitter_type,
893
+ parts=new_parts,
894
+ part_distribution_type=part_distribution_type,
895
+ num_points=num_points,
896
+ )
897
+ palette = self._find_palette(color_by=color_by)
898
+ return self._add_emitters_to_particle_trace_part(
899
+ particle_trace_part, new_emitters=new_emitters, palette=palette, clean=True
900
+ )
901
+
902
+ def add_emitter_points_to_particle_trace_part(
903
+ self,
904
+ particle_trace_part: Union[str, int, "ENS_PART"],
905
+ points: List[List[float]],
906
+ ) -> "ENS_PART_PARTICLE_TRACE":
907
+ """
908
+ Add point emitters to an existing particle trace. The function will return the updated
909
+ ``ENS_PART`` object.
910
+
911
+ Parameters
912
+ ----------
913
+
914
+ particle_trace_part:
915
+ The particle trace part to be added emitters to.
916
+ Can be the name, the ID or the ``ENS_PART`` object
917
+ points: list
918
+ List of list containing the coordinates for the seed points.
919
+
920
+ Examples
921
+ --------
922
+ >>> s = LocalLauncher().start()
923
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
924
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
925
+ >>> s.load_data(cas_file, result_file=dat_file)
926
+ >>> p = s.ensight.utils.parts.create_particle_trace_from_points("mytraces", "Velocity", points=[[-0.02, -0.123, 0.01576]], source_parts=parts.select_parts_by_dimension(3))
927
+ >>> p = s.ensight.utils.parts.add_emitter_points_to_particle_trace_part(p, points=[[0.109876, -0.123, 0.0123]])
928
+ """
929
+ emitter_type = self._EMIT_POINT
930
+ particle_trace_part = self._cure_particle_trace_part(particle_trace_part)
931
+ new_emitters = self._create_emitters(emitter_type=emitter_type, points=points)
932
+ return self._add_emitters_to_particle_trace_part(particle_trace_part, new_emitters)
933
+
934
+ def add_emitter_line_to_particle_trace_part(
935
+ self,
936
+ particle_trace_part: Union[str, int, "ENS_PART"],
937
+ point1: List[float],
938
+ point2: List[float],
939
+ num_points: Optional[int] = 100,
940
+ ) -> "ENS_PART_PARTICLE_TRACE":
941
+ """
942
+ Add a line emitter to an existing particle trace. The function will return the updated
943
+ ``ENS_PART`` object.
944
+
945
+ Parameters
946
+ ----------
947
+
948
+ particle_trace_part:
949
+ The particle trace part to be added emitters to.
950
+ Can be the name, the ID or the ``ENS_PART`` object.
951
+ point1: list
952
+ The coordinates for point 1.
953
+ point2: list
954
+ The coordinates for point 2.
955
+ num_points: int
956
+ The number of seed points. Defaults to 100.
957
+
958
+ Examples
959
+ --------
960
+ >>> s = LocalLauncher().start()
961
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
962
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
963
+ >>> s.load_data(cas_file, result_file=dat_file)
964
+ >>> p = s.ensight.utils.parts.create_particle_trace_from_points("mytraces", "Velocity", points=[[-0.02,-0.123,0.01576]], source_parts=parts.select_parts_by_dimension(3))
965
+ >>> p = s.ensight.utils.parts.add_emitter_line_to_particle_trace_part(p, point1=[-0.02, -0.123, 0.01576], point2=[0.109876, -0.123, 0.0123], num_points=10)
966
+ """
967
+ emitter_type = self._EMIT_LINE
968
+ particle_trace_part = self._cure_particle_trace_part(particle_trace_part)
969
+ new_emitters = self._create_emitters(
970
+ emitter_type=emitter_type, point1=point1, point2=point2, num_points=num_points
971
+ )
972
+ return self._add_emitters_to_particle_trace_part(particle_trace_part, new_emitters)
973
+
974
+ def add_emitter_plane_to_particle_trace_part(
975
+ self,
976
+ particle_trace_part: Union[str, int, "ENS_PART"],
977
+ point1: List[float],
978
+ point2: List[float],
979
+ point3: List[float],
980
+ num_points_x: Optional[int] = 25,
981
+ num_points_y: Optional[int] = 25,
982
+ ) -> "ENS_PART_PARTICLE_TRACE":
983
+ """
984
+ Add a plane emitter to an existing particle trace. The function will return the updated
985
+ ``ENS_PART`` object.
986
+
987
+ Parameters
988
+ ----------
989
+
990
+ particle_trace_part:
991
+ The particle trace part to be added emitters to.
992
+ Can be the name, the ID or the ``ENS_PART`` object.
993
+ point1: list
994
+ The coordinates for point 1, being a corner of the plane.
995
+ point2: list
996
+ The coordinates for point 2, being a corner of the plane.
997
+ point3: list
998
+ The coordinates for point 3, being a corner of the plane.
999
+ num_points_x: int
1000
+ The number of points on the ``X`` direction of the emission plane.
1001
+ Defaults to 25.
1002
+ num_points_y: int
1003
+ The number of points on the ``Y`` direction of the emission plane.
1004
+ Defaults to 25.
1005
+
1006
+ Examples
1007
+ --------
1008
+ >>> s = LocalLauncher().start()
1009
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
1010
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
1011
+ >>> s.load_data(cas_file, result_file=dat_file)
1012
+ >>> p = s.ensight.utils.parts.create_particle_trace_from_points("mytraces", "Velocity", points=[[-0.02,-0.123,0.01576]], source_parts=parts.select_parts_by_dimension(3))
1013
+ >>> p = s.ensight.utils.parts.add_emitter_plane_to_particle_trace_part(p, point1=[-0.02, -0.123, 0.01576], point2=[0.109876, -0.123, 0.0123], point3=[0.1, 0, 0.05], num_points_x=10, num_points_y=10)
1014
+ """
1015
+ emitter_type = self._EMIT_PLANE
1016
+ particle_trace_part = self._cure_particle_trace_part(particle_trace_part)
1017
+ new_emitters = self._create_emitters(
1018
+ emitter_type=emitter_type,
1019
+ point1=point1,
1020
+ point2=point2,
1021
+ point3=point3,
1022
+ num_points_x=num_points_x,
1023
+ num_points_y=num_points_y,
1024
+ )
1025
+ return self._add_emitters_to_particle_trace_part(particle_trace_part, new_emitters)
1026
+
1027
+ def add_emitter_parts_to_particle_trace_part(
1028
+ self,
1029
+ particle_trace_part: Union[str, int, "ENS_PART"],
1030
+ parts: List[Union[str, int, "ENS_PART"]],
1031
+ part_distribution_type: Optional[int] = 0,
1032
+ num_points: Optional[int] = 100,
1033
+ ) -> "ENS_PART_PARTICLE_TRACE":
1034
+ """
1035
+ Add a list of part emitters to an existing particle trace. The function will return the updated
1036
+ ``ENS_PART`` object.
1037
+
1038
+ Parameters
1039
+ ----------
1040
+
1041
+ particle_trace_part:
1042
+ The particle trace part to be added emitters to.
1043
+ Can be the name, the ID or the ``ENS_PART`` object.
1044
+ parts: list
1045
+ A list of parts to emit the particle traces from.
1046
+ They can be their names, their IDs or the respective ``ENS_PART`` objects.
1047
+ part_distribution_type: int
1048
+ The distribution of emitters in case of emission from a part.
1049
+ This table describes the options:
1050
+
1051
+ ==================== =================================================
1052
+ Name Query type
1053
+ ==================== =================================================
1054
+ PART_EMIT_FROM_NODES Emit from the nodes of the part
1055
+ PART_EMIT_FROM_AREA Create an area of equidistant points for emission
1056
+ ==================== =================================================
1057
+
1058
+ If not provided, it will default to ``PART_EMIT_FROM_NODES``
1059
+ num_points: int
1060
+ The number of points to emit from.
1061
+ Defaults to 100.
1062
+
1063
+ Examples
1064
+ --------
1065
+ >>> s = LocalLauncher().start()
1066
+ >>> cas_file = s.download_pyansys_example("mixing_elbow.cas.h5","pyfluent/mixing_elbow")
1067
+ >>> dat_file = s.download_pyansys_example("mixing_elbow.dat.h5","pyfluent/mixing_elbow")
1068
+ >>> s.load_data(cas_file, result_file=dat_file)
1069
+ >>> p = s.ensight.utils.parts.create_particle_trace_from_points("mytraces", "Velocity", points=[[-0.02, -0.123, 0.01576]], source_parts=parts.select_parts_by_dimension(3))
1070
+ >>> p = s.ensight.utils.parts.add_emitter_parts_to_particle_trace_part(p, parts=["cold-inlet", "hot-inlet"], num_points=25)
1071
+ """
1072
+ emitter_type = self._EMIT_PART
1073
+ particle_trace_part = self._cure_particle_trace_part(particle_trace_part)
1074
+ new_parts = [convert_part(self.ensight, p) for p in parts]
1075
+ new_emitters = self._create_emitters(
1076
+ emitter_type=emitter_type,
1077
+ parts=new_parts,
1078
+ part_distribution_type=part_distribution_type,
1079
+ num_points=num_points,
1080
+ )
1081
+ return self._add_emitters_to_particle_trace_part(particle_trace_part, new_emitters)
1082
+
1083
+ def select_parts(
1084
+ self,
1085
+ p_list: Optional[List[Union[str, int, "ENS_PART"]]] = None,
1086
+ rec_flag: Optional[bool] = True,
1087
+ ) -> Optional[List["ENS_PART"]]:
1088
+ """
1089
+ Select the parts string, or int, or ensight.objs.ENS_PART, or list
1090
+ and record the selection (by default) honoring the
1091
+ EnSight preference to record command language by part id or by name.
1092
+ It creates a list of part objects and selects the parts, and records the
1093
+ selection by default.
1094
+
1095
+ Parameters
1096
+ ----------
1097
+ p_list: list
1098
+ The list of part objects to compute the forces on. It can either be a list of names
1099
+ a list of IDs (integers or strings) or directly a list of ENS_PART objects
1100
+ rec_flag: bool
1101
+ True if the selection needs to be recorded
1102
+
1103
+ Returns
1104
+ -------
1105
+ list
1106
+ A list of part objects selected or None if error.
1107
+
1108
+
1109
+ NOTE: If you do not want a measured part in your
1110
+ selection, then don't include it in the list
1111
+ e.g. if
1112
+ core.PARTS[0].PARTTYPE == ensight.objs.enums.PART_DISCRETE_PARTICLE == 3
1113
+ then it is a measured part
1114
+ """
1115
+ #
1116
+ pobj_list = self.get_part_id_obj_name(p_list, "obj")
1117
+
1118
+ if not pobj_list:
1119
+ raise RuntimeError("Error, select_parts: part list is empty")
1120
+ else:
1121
+ # This was formerly used to record command lang 10.1.6(c)
1122
+ # using part ids:
1123
+ # ensight.part.select_begin(pid_list,record=1)
1124
+ # Now records selection, honoring the the preference
1125
+ # part selection of by part id or by name (2024R1)
1126
+ record = 1 if rec_flag else 0
1127
+ self.ensight.objs.core.selection(name="ENS_PART").addchild(
1128
+ pobj_list, replace=1, record=record
1129
+ )
1130
+ # This is essential to synchronize cmd lang with the GUI, C++
1131
+ self.ensight.part.get_mainpartlist_select()
1132
+
1133
+ return pobj_list
1134
+
1135
+ def get_part_id_obj_name(
1136
+ self,
1137
+ plist: Optional[Union[str, int, "ENS_PART", List[str], List[int], List["ENS_PART"]]] = None,
1138
+ ret_flag="id",
1139
+ ) -> Union[Optional[List[int]], Optional[List[str]], Optional[List["ENS_PART"]]]:
1140
+ """
1141
+ Input a part or a list of parts and return an id, object, or name
1142
+ or a list of ids, objects, or names.
1143
+
1144
+ Parameters
1145
+ ----------
1146
+ p_list: list
1147
+ The list of part objects to compute the forces on. It can either be a list of names
1148
+ a list of IDs (integers or strings) or directly a list of ENS_PART objects
1149
+
1150
+ ret_flag: str
1151
+ A string that determines what is returned
1152
+
1153
+ Returns
1154
+ -------
1155
+ list
1156
+ Either a list of part IDs, or a list of names or a list of ENS_PART objects
1157
+ depending on the requested ret_flag value
1158
+ """
1159
+ # To not change the interface I didn't move ret_flag to be a required argument,
1160
+ # so I need to check its value now
1161
+ if not ret_flag:
1162
+ return None
1163
+ if not plist:
1164
+ plist = [p for p in self.ensight.objs.core.PARTS]
1165
+ pobj_list: List["ENS_PART"] = []
1166
+ #
1167
+ # Basically figure out what plist is, then convert it to a list of ENS_PARTs
1168
+ #
1169
+ if (
1170
+ isinstance(plist, self.ensight.objs.ENS_PART)
1171
+ or isinstance(plist, int)
1172
+ or isinstance(plist, str)
1173
+ ):
1174
+ p_list = [plist]
1175
+ elif isinstance(plist, list) or isinstance(plist, ensobjlist):
1176
+ p_list = [p for p in plist]
1177
+ else: # pragma: no cover
1178
+ raise RuntimeError( # pragma: no cover
1179
+ "Unknown type of input var plist {}".format(type(plist))
1180
+ )
1181
+ #
1182
+ # p_list must now be a list
1183
+ #
1184
+
1185
+ if not p_list:
1186
+ return None
1187
+ if not isinstance(p_list[0], (str, int, self.ensight.objs.ENS_PART)): # pragma: no cover
1188
+ error = "First member is neither ENS_PART, int, nor string" # pragma: no cover
1189
+ error += f"{p_list[0]} type = {type(p_list[0])}; aborting" # pragma: no cover
1190
+ raise RuntimeError(error) # pragma: no cover
1191
+ pobjs: List["ENS_PART"]
1192
+ if isinstance(p_list[0], int):
1193
+ # list of ints must be part ids
1194
+ for pid in p_list:
1195
+ pobjs = [p for p in self.ensight.objs.core.PARTS if p.PARTNUMBER == pid]
1196
+ for prt in pobjs:
1197
+ pobj_list.append(prt)
1198
+ elif isinstance(p_list[0], str):
1199
+ if not p_list[0].isdigit():
1200
+ for pname in p_list:
1201
+ pobjs = [p for p in self.ensight.objs.core.PARTS if p.DESCRIPTION == pname]
1202
+ for prt in pobjs:
1203
+ pobj_list.append(prt)
1204
+ else: # digits, must be a string list of part ids?
1205
+ for pid_str in p_list:
1206
+ pobjs = [
1207
+ p for p in self.ensight.objs.core.PARTS if p.PARTNUMBER == int(pid_str)
1208
+ ]
1209
+ for prt in pobjs:
1210
+ pobj_list.append(prt)
1211
+ else:
1212
+ for prt in p_list:
1213
+ pobj_list.append(prt)
1214
+ if ret_flag == "name":
1215
+ val_strings = [str(p.DESCRIPTION) for p in pobj_list]
1216
+ return val_strings
1217
+ if ret_flag == "obj":
1218
+ val_objs = [p for p in pobj_list]
1219
+ return val_objs
1220
+ val_ints = [int(p.PARTNUMBER) for p in pobj_list]
1221
+ return val_ints