Fields Container and Fields#
Where DPF uses operators to load and operate on data, it uses the field container and fields to store and return data. In other words, operators are like verbs, acting on the data, while the field container and fields are like nouns, objects that hold data.
Obtaining the Fields Container or Fields#
The outputs from operators can be either a
ansys.dpf.core.fields_container.FieldsContainer
or a
ansys.dpf.core.field.Field
. A fields container is the DPF
equivalent of a list of fields. It is holds a vector of fields.
In this example, the fields container is returned from the
elastic_strain
operator:
from ansys.dpf import core as dpf
from ansys.dpf.core import examples
model = dpf.Model(examples.msup_transient)
epel = model.results.elastic_strain.on_all_time_freqs
fields = epel.eval()
print(fields)
Out:
DPF elastic_strain(s)Fields Container
with 20 field(s)
defined on labels: time
with:
- field 0 {time: 1} with ElementalNodal location, 6 components and 40 entities.
- field 1 {time: 2} with ElementalNodal location, 6 components and 40 entities.
- field 2 {time: 3} with ElementalNodal location, 6 components and 40 entities.
- field 3 {time: 4} with ElementalNodal location, 6 components and 40 entities.
- field 4 {time: 5} with ElementalNodal location, 6 components and 40 entities.
- field 5 {time: 6} with ElementalNodal location, 6 components and 40 entities.
- field 6 {time: 7} with ElementalNodal location, 6 components and 40 entities.
- field 7 {time: 8} with ElementalNodal location, 6 components and 40 entities.
- field 8 {time: 9} with ElementalNodal location, 6 components and 40 entities.
- field 9 {time: 10} with ElementalNodal location, 6 components and 40 entities.
- field 10 {time: 11} with ElementalNodal location, 6 components and 40 entities.
- field 11 {time: 12} with ElementalNodal location, 6 components and 40 entities.
- field 12 {time: 13} with ElementalNodal location, 6 components and 40 entities.
- field 13 {time: 14} with ElementalNodal location, 6 components and 40 entities.
- field 14 {time: 15} with ElementalNodal location, 6 components and 40 entities.
- field 15 {time: 16} with ElementalNodal location, 6 components and 40 entities.
- field 16 {time: 17} with ElementalNodal location, 6 components and 40 entities.
- field 17 {time: 18} with ElementalNodal location, 6 components and 40 entities.
- field 18 {time: 19} with ElementalNodal location, 6 components and 40 entities.
- field 19 {time: 20} with ElementalNodal location, 6 components and 40 entities.
Accessing Fields Within a Fields Container#
Because the result above contains a transient result, the fields container has one field by time set.
Access the fields from the fields contains using these methods:
len(fields)
Out:
20
Return a field based on its index:
field_first_time = fields[0]
field_last_time = fields[19]
Return a field based on its time set ID:
field = fields.get_field_by_time_id(1)
Alternatively, to access fields for more complex requests, use the
get_field
method with the identifier of the requested field:
field = fields.get_field({'time': 1})
print(field)
Out:
DPF elastic_strain_0.01s Field
Location: ElementalNodal
Unit:
40 entities
Data:6 components and 320 elementary data
Or in a more real-word example:
model = dpf.Model(examples.download_all_kinds_of_complexity())
epel = model.results.elastic_strain.on_all_time_freqs.split_by_shape
fields = epel.eval()
field = fields.get_field({'time': 1, 'elshape':0})
print(field)
field = fields.get_field({'time': 1, 'elshape':1})
print(field)
Out:
DPF elastic_strain_1.s_elshape:0 Field
Location: ElementalNodal
Unit:
203 entities
Data:6 components and 2436 elementary data
DPF elastic_strain_1.s_elshape:1 Field
Location: ElementalNodal
Unit:
9052 entities
Data:6 components and 37580 elementary data
Reference the available time frequency support to determine which time complex IDs are available in the fields container:
model = dpf.Model(examples.msup_transient)
epel = model.results.elastic_strain.on_all_time_freqs
fields = epel.eval()
print(fields.time_freq_support)
Out:
DPF Time/Freq Support:
Number of sets: 20
Cumulative Time (s) LoadStep Substep
1 0.010000 1 1
2 0.020000 1 2
3 0.030000 1 3
4 0.040000 1 4
5 0.050000 1 5
6 0.060000 1 6
7 0.070000 1 7
8 0.080000 1 8
9 0.090000 1 9
10 0.100000 1 10
11 0.110000 1 11
12 0.120000 1 12
13 0.130000 1 13
14 0.140000 1 14
15 0.150000 1 15
16 0.160000 1 16
17 0.170000 1 17
18 0.180000 1 18
19 0.190000 1 19
20 0.200000 1 20
Note that the time set IDs used are 1 based. When indexing from Pythonic
indexing via fields[0]
, you can use zero-based indexing. When requesting the
results using the get_fields
method, the request is based on the time scoping
set IDs.
Field#
The class ansys.dpf.core.field.Field
is the fundamental unit of data within DPF.
It contains the actual data and its metadata, which is results data defined by values
associated with entities (scoping). These entities are a subset of a model (support).
In DPF, field data is always associated with its scoping and support, making the field a self-describing piece of data. A field is also defined by its dimensionnality, unit, location, and more.
You can get an overview of a field’s metadata by printing the field:
field = fields[0]
print(field)
Out:
DPF elastic_strain_0.01s Field
Location: ElementalNodal
Unit:
40 entities
Data:6 components and 320 elementary data
The next section provides an overview of the metadata associated with the field itself.
Field Metadata#
The field contains the metadata for the result it is associated with. The metadata
includes the location (such as Elemental
, Nodal
, or
ElementalNodal
) and IDs associated with the location.
To access the scoping of the field, use the scoping
attribute:
>>> print(field.scoping)
>>> print('field.scoping.ids:', field.scoping.ids)
>>> print('field.location:', field.location)
Out:
DPF Scoping:
with Elemental location and 40 entities
field.scoping.ids: [21,
22,
23,
24,
25,
26,
...
]
field.location:'ElementalNodal'
The location Elemental
denotes one value (multiplied by the number of
components) of data per element, while Nodal
is per node, and
ElementalNodal
is one value per node per element. For example,
strain is an ElementalNodal
value as the strain is evaluated at
each node for each element.
The field also contains additional metadata such as the shape
of
the data stored, the location of the field, number of components, and
the units of the data:
>>> stress = model.results.stress
>>> field = stress.eval()[0]
Units of the field describing volume
>>> field.unit
Elemental, elemental nodal, or nodal element "location" of the field
>>> field.location
Number of components associated with the field. It's expected to
be have a single dimension since there can only be one volume per
element.
>>> field.component_count
Out:
'Pa'
'ElementalNodal'
6
Field Data#
Accessing Field Data#
When DPF-Core returns the ansys.dpf.core.field.Field
class,
what Python actually has is a client-side representation of the field,
but not the entirety of the field itself. This means that all the data of
the field is stored within the DPF service. This is important because
when building your postprocessing workflows, the most efficient way of
interacting with result data is to minimize the exchange of data between
Python and DPF, either by using operators or by accessing only the data
that is needed.
If you need to access the entire array of data, request
that the data be returned as a numpy
array:
>>> array = field.data
>>> array
array([[ 4.01372930e+04, 3.85071930e+02, -1.40019130e+07,
7.48472351e+02, -2.60259531e+04, -2.62856938e+05],
[-1.19228638e+03, -6.18210815e+02, -1.39912700e+07,
2.61468994e+03, -1.31871719e+05, -2.59527125e+05],
[ 9.02558960e+02, 5.63793152e+02, -1.17102740e+07,
-8.99381836e+02, -1.21302727e+05, -2.45666328e+05],
...,
[-3.99694531e+04, 1.44622528e+02, 9.62343100e+06,
-7.09812073e+02, -2.26106621e+04, -2.23155891e+05],
[-4.31104401e+02, -2.67612732e+02, 9.60954800e+06,
1.93208755e+02, -1.11580734e+05, -2.24406062e+05],
[ 5.56899536e+02, 3.88515320e+02, 1.17119880e+07,
-1.68983887e+03, -1.21768023e+05, -2.41346125e+05]])
This array has 6 components by elementary data (symmetrical tensor XX,YY,ZZ,XY,YZ,XZ)
Note that this array is a genuine, local, numpy array
>>> type(array)
numpy.ndarray
If you need to request an individual node or element,
request it using either the get_entity_data
or
get_entity_data_by_id
methods:
Get the data from the first element in the field.
>>> field.get_entity_data(0)
Get the data for the element with the ID 10
>>> field.get_entity_data_by_id(10)
array([[ 4.99232031e+04, 1.93570602e+02, -3.08514075e+06,
-5.48255615e+02, -1.37476562e+04, 1.34827719e+05],
[ 5.23090469e+04, -1.87847885e+02, -1.98004588e+06,
-1.12942969e+03, -1.11147285e+04, 1.09223398e+05],
[-4.90510511e+00, -1.16425255e+02, -1.96296662e+06,
-5.48878540e+02, -5.48524844e+04, 1.09255164e+05],
[ 2.63994884e+01, 1.50431015e+02, -3.06906050e+06,
-1.17046680e+03, -6.76924219e+04, 1.34773391e+05],
[-4.99232031e+04, -1.93571167e+02, 3.08514075e+06,
-5.48256836e+02, -1.37476562e+04, -1.34827719e+05],
[-5.23090469e+04, 1.87848083e+02, 1.98004588e+06,
-1.12943201e+03, -1.11147295e+04, -1.09223398e+05],
[ 4.90471840e+00, 1.16423714e+02, 1.96296662e+06,
-5.48877380e+02, -5.48524844e+04, -1.09255164e+05],
[-2.63994102e+01, -1.50429443e+02, 3.06906050e+06,
-1.17046619e+03, -6.76924219e+04, -1.34773391e+05]])
Note that this would correspond to an index of 29 within the
field. Be aware that scoping IDs are not sequential. The index
of Element 29 in the field can be obtained by:
>>> field.scoping.ids.index(10)
29
Here the data of element of id 10 is made of 8 symmetrical tensor, indeed
the elastic strain has one tensor value by node by element (``ElementalNodal`` location)
For a displacement on node 3, we have :
>>> disp = model.results.displacement.eval()[0]
>>> disp.get_entity_data_by_id(3)
array([[8.06571808e-14, 4.03580652e-04, 2.61804706e-05]])
One 3D vector (X,Y,Z) displacement
While these methods are acceptable when requesting data for a few elements or nodes, they should not be used when looping over the entire array. For efficiency, a field’s data can be recovered locally before sending a large number of requests: :
with field.as_local_field() as f:
for i in range(1,100):
f.get_entity_data_by_id(i)
Operating on Field Data#
Often times, it’s not necessary to directly act upon the data of an
array within Python. For example, if you want to know the maximum of
the data, you could potentially compute the maximum of the array from
numpy
with array.max()
. However, that requires sending the entire
array to Python and then computing the maximum there. Rather than
copying the array over and then computing the maximum in Python, you
can instead compute the maximum directly from the field itself.
This example uses the ``’min_max’``operator to compute the maximum of the field while returning the field:
Compute the maximum of the field within DPF and return the result
a numpy array
>>> max_field = field.max()
>>> max_field.data
array([0.12492393, 0.06738043, 0.05854268, 0.05807593, 0.08250141,
0.2068032 ])
Get the element or node ID of the maximum value.
>>> max_field.scoping.ids
[369, 1073, 1031, 1040, 2909, 2909]
Here is an example of using the elemental_mean
operator to compute the
average of a field:
from ansys.dpf.core import operators as ops
avg_op = ops.averaging.elemental_mean(field)
avg_field = avg_op.outputs.field()
print(avg_field.get_entity_data(0))
print(avg_field.location)
Out:
[[ 4.65393066e-04 -2.47955322e-05 0.00000000e+00 7.68026390e+02
-7.59655688e+04 0.00000000e+00]]
Elemental
For more advanced information on operator chaining, see the Operators.
API Reference#
See the API reference at FieldsContainer and Field.