Skip to content

Commit 6267e78

Browse files
authored
Isosurface example (#1056)
* Create examples for isosurfaces using fluid files * Complete example for iso-surfaces with elbow file * Complete the example * Complete isosurface example for fluids * Update the PR : Enable to implement a field with overall location Plot iso surface as overall location field * Update the PR : Implement a setter for field's name Complete iso-surface example to make it more readable * Update the PR : Give the possibility through fields_factory to create an overall location field Reformat the example Add ValueError message for faces location in plotter * Update the PR : fix doc display * Update the PR : fix format * Update the PR : enhance the doc * Update the PR : correct docstrings and reformat code
1 parent 8b3d645 commit 6267e78

File tree

4 files changed

+195
-4
lines changed

4 files changed

+195
-4
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""
2+
.. _ref_fluids_isosurface:
3+
4+
Compute iso-surfaces on fluid models
5+
------------------------------------------
6+
7+
This example demonstrates how to compute iso-surfaces on fluid models.
8+
"""
9+
10+
###############################################################################
11+
# Import the ``dpf-core`` module and its examples files.
12+
# ~~~~~~~~~~~~~~~~~~
13+
14+
import ansys.dpf.core as dpf
15+
from ansys.dpf.core import examples
16+
from ansys.dpf.core.plotter import DpfPlotter
17+
18+
###############################################################################
19+
# Specify the file path.
20+
# ~~~~~~~~~~~~~~~~~~
21+
# We work on a cas/dat.h5 file with only nodal variables.
22+
23+
path = examples.download_cfx_heating_coil()
24+
ds = dpf.DataSources()
25+
ds.set_result_file_path(path["cas"], "cas")
26+
ds.add_file_path(path["dat"], "dat")
27+
streams = dpf.operators.metadata.streams_provider(data_sources=ds)
28+
29+
###############################################################################
30+
# Whole mesh scoping.
31+
# ~~~~~~~~~~~~~~~~~~
32+
# We evaluate the mesh with the mesh_provider operator to scope the mesh_cut operator
33+
# with the whole mesh.
34+
35+
whole_mesh = dpf.operators.mesh.mesh_provider(streams_container=streams).eval()
36+
print(whole_mesh)
37+
38+
pl = DpfPlotter()
39+
pl.add_mesh(whole_mesh)
40+
cpos_whole_mesh = [
41+
(4.256160478475664, 4.73662111240005, 4.00410065817644),
42+
(-0.0011924505233764648, 1.8596649169921875e-05, 1.125),
43+
(-0.2738679385987956, -0.30771426079547065, 0.9112125360807675),
44+
]
45+
pl.show_figure(cpos=cpos_whole_mesh, show_axes=True)
46+
47+
###############################################################################
48+
# Extract the physics variable
49+
# ~~~~~~~~~~~~~~~~~
50+
# Here we choose to work with the static pressure by default which is a scalar and
51+
# nodal variable without multi-species/phases. With a multi-species case,
52+
# select one using qualifier ellipsis pins and connecting a LabelSpace "species"/"phase".
53+
54+
P_S = dpf.operators.result.static_pressure(streams_container=streams, mesh=whole_mesh).eval()
55+
print(P_S[0])
56+
57+
pl = DpfPlotter()
58+
pl.add_field(P_S[0])
59+
cpos_mesh_variable = [
60+
(4.256160478475664, 4.73662111240005, 4.00410065817644),
61+
(-0.0011924505233764648, 1.8596649169921875e-05, 1.125),
62+
(-0.2738679385987956, -0.30771426079547065, 0.9112125360807675),
63+
]
64+
pl.show_figure(cpos=cpos_mesh_variable, show_axes=True)
65+
66+
###############################################################################
67+
# Evaluate iso-surfaces
68+
# ~~~~~~~~~~~~~~
69+
# We can finally use the mesh_cut operator on this specific variable.
70+
# We choose to cut the whole with 5 iso-surface equally spaced between min and max.
71+
72+
max_pressure = 361.8170 # Pa
73+
min_pressure = -153.5356 # Pa
74+
number_of_iso_surface = 5
75+
step = (max_pressure - min_pressure) / number_of_iso_surface
76+
77+
pl = DpfPlotter()
78+
c_pos_iso = [
79+
(4.256160478475664, 4.73662111240005, 4.00410065817644),
80+
(-0.0011924505233764648, 1.8596649169921875e-05, 1.125),
81+
(-0.2738679385987956, -0.30771426079547065, 0.9112125360807675),
82+
]
83+
pl.add_mesh(
84+
meshed_region=whole_mesh,
85+
style="wireframe",
86+
show_edges=True,
87+
show_axes=True,
88+
color="black",
89+
opacity=0.3,
90+
)
91+
92+
for i in range(number_of_iso_surface):
93+
iso_surface = dpf.operators.mesh.mesh_cut(
94+
field=P_S[0], iso_value=min_pressure, closed_surface=0, mesh=whole_mesh, slice_surfaces=True
95+
).eval()
96+
P_S_step = dpf.Field(location=dpf.locations.overall, nature=dpf.common.natures.scalar)
97+
P_S_step.append([min_pressure], i)
98+
P_S_step.name = "static pressure"
99+
P_S_step.unit = "Pa"
100+
pl.add_field(
101+
field=P_S_step, meshed_region=iso_surface, style="surface", show_edges=False, show_axes=True
102+
)
103+
min_pressure += step
104+
105+
pl.show_figure(show_axes=True, cpos=c_pos_iso)
106+
107+
###############################################################################
108+
# Important note
109+
# ------------------------------
110+
# Iso-surfaces computation through the `mesh_cut` operator are only supported for Nodal Fields.
111+
# For Elemental variables, you must perform an averaging operation on the Nodes before
112+
# running the `mesh_cut` operator. This can be done by chaining the `elemental_to_nodal` operator
113+
# output with the `mesh_cut` operator input.

src/ansys/dpf/core/field.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,6 @@ def dimensionality(self, value):
575575
@property
576576
def name(self):
577577
"""Name of the field."""
578-
# return self._api.csfield_get_name(self)
579578
from ansys.dpf.gate import integral_types
580579

581580
size = integral_types.MutableInt32()
@@ -585,6 +584,28 @@ def name(self):
585584
)
586585
return str(name)
587586

587+
@name.setter
588+
def name(self, value):
589+
"""Change the name of the field
590+
591+
Parameters
592+
----------
593+
value : str
594+
Name of the field.
595+
596+
Examples
597+
--------
598+
Units for a displacement field.
599+
600+
>>> from ansys.dpf import core as dpf
601+
>>> my_field = dpf.Field(10, dpf.natures.vector,dpf.locations.nodal)
602+
>>> my_field.name = "my-field"
603+
>>> my_field.name
604+
'my-field'
605+
606+
"""
607+
self._field_definition._api.csfield_definition_set_name(self._field_definition, name=value)
608+
588609
def _set_field_definition(self, field_definition):
589610
"""Set the field definition.
590611

src/ansys/dpf/core/fields_factory.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,57 @@ def create_vector_field(num_entities, num_comp, location=locations.nodal, server
254254
return _create_field(server, natures.vector, num_entities, location, ncomp_n=num_comp)
255255

256256

257+
def create_overall_field(
258+
value, nature, num_entities, num_comp, location=locations.overall, server=None
259+
):
260+
"""Create a specific `:class:`ansys.dpf.core.Field` with entities that have an
261+
overall location.
262+
263+
Regarding the nature of the entity contained in the field, we set the same value
264+
for all elements.
265+
266+
Parameters
267+
----------
268+
value : float
269+
Value of the entity
270+
nature : str
271+
Nature of the field entity data. For example:
272+
273+
- :class:`ansys.dpf.core.natures.matrix`
274+
- :class:`ansys.dpf.core.natures.scalar`
275+
num_entities : int
276+
Number of entities to reserve.
277+
num_comp : int
278+
Number of vector components.
279+
location : str, optional
280+
Location of the field. Options are in :class:`locations <ansys.dpf.core.common.locations>`.
281+
The default is ``dpf.locations.nodal``.
282+
283+
server : ansys.dpf.core.server, optional
284+
Server with the channel connected to the remote or local instance.
285+
The default is ``None``, in which case an attempt is made to use the
286+
global server.
287+
288+
Returns
289+
-------
290+
field : Field
291+
DPF field in the requested format.
292+
293+
Examples
294+
--------
295+
Create a field containing 10 scalar entities of 1 component each with an
296+
overall location (default). Same value (1.0) is set for all element of the field.
297+
298+
>>> from ansys.dpf.core import fields_factory
299+
>>> field = fields_factory.create_overall_field(1.0, natures.scalar, 10, 1)
300+
301+
"""
302+
overall_field = _create_field(server, nature, num_entities, location, ncomp_n=num_comp)
303+
for i in range(num_entities):
304+
overall_field.append(value, i)
305+
return overall_field
306+
307+
257308
def _create_field(server, nature, nentities, location=locations.nodal, ncomp_n=0, ncomp_m=0):
258309
"""Create a specific :class:`ansys.dpf.core.Field`.
259310

src/ansys/dpf/core/plotter.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,20 +269,26 @@ def add_field(
269269
show_min = False
270270
elif location == locations.faces:
271271
mesh_location = meshed_region.faces
272+
if len(mesh_location) == 0:
273+
raise ValueError("No faces found to plot on")
272274
if show_max or show_min:
273275
warnings.warn("`show_max` and `show_min` is only supported for Nodal results.")
274276
show_max = False
275277
show_min = False
278+
elif location == locations.overall:
279+
mesh_location = meshed_region.elements
276280
else:
277281
raise ValueError("Only elemental, nodal or faces location are supported for plotting.")
278282
component_count = field.component_count
279283
if component_count > 1:
280284
overall_data = np.full((len(mesh_location), component_count), np.nan)
281285
else:
282286
overall_data = np.full(len(mesh_location), np.nan)
283-
ind, mask = mesh_location.map_scoping(field.scoping)
284-
overall_data[ind] = field.data[mask]
285-
287+
if location != locations.overall:
288+
ind, mask = mesh_location.map_scoping(field.scoping)
289+
overall_data[ind] = field.data[mask]
290+
else:
291+
overall_data[:] = field.data[0]
286292
# Filter kwargs for add_mesh
287293
kwargs_in = _sort_supported_kwargs(bound_method=self._plotter.add_mesh, **kwargs)
288294
# Have to remove any active scalar field from the pre-existing grid object,

0 commit comments

Comments
 (0)