ansys-pyensight-core 0.8.12__py3-none-any.whl → 0.8.13__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 ansys-pyensight-core might be problematic. Click here for more details.

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