Data Arrays#
To process your data with DPF, you must format it according to the DPF data model. You can achieve this either by using DPF data readers on result files, or by using data to build DPF data storage containers.
It is important to be aware of how the data is structured in those containers to understand how to create them and how operators process them.
The data containers can be:
Raw data storage structures: data arrays (such as a
Field
) or data maps (such as aDataTree
)Collections: homogeneous groups of labeled raw data storage structures (such as a
FieldsContainer
for a group of labeled fields)
This tutorial presents how to define and manipulate DPF data arrays specifically.
Download tutorial as Python script
Download tutorial as Jupyter notebook
Introduction#
A data array in DPF usually represents a mathematical field, hence the base name Field
.
Different types of Field
store different data types:
a
Field
stores float valuesa
StringField
stores string valuesa
PropertyField
stores integer valuesa
CustomTypeField
stores values of a custom type (among valid numpy.dtype)
A Field
is always associated to:
a
location
, which defines the type entity the data applies to. You can check thelocations
list to know what is available. Locations related to mesh entities include:nodal
,elemental
, orelemental_nodal
,zone
,faces
. Locations related to time, frequency, or mode aremodal
,time_freq
, andtime_freq_step
.a
scoping
, which is the list of entity IDs each data point in theField
relates to. For example, thescoping
of anodal
Field
represents a list of node IDs. It can represent a subset of thesupport
of the field. The data in aField
is ordered the same way as the IDs in itsscoping
.a
support
, which is a data container holding information about the model for the type of entity thelocation
targets. If thelocation
relates to mesh entities such as nodes or elements, thesupport
of theField
is an object holding data related to the mesh, called aMeshedRegion
.a
dimensionality
, which gives the structure of the data based on the number of components and dimensions. Indeed, a DPFField
can store data for a 3D vector field, a scalar field, a matrix field, but also store data for a multi-component field (for example, a symmetrical matrix field for each component of the stress field).a
data
array, which holds the actual data in a vector, accessed according to thedimensionality
.
Create fields based on result files#
In this tutorial we are going to use the result file from a fluid analysis to showcase the
Field
, PropertyField
, and StringField
.
The Model
class creates and evaluates common readers for the files it is given,
such as a mesh provider, a result info provider, and a streams provider.
It provides dynamically built methods to extract the results available in the files, as well as many shortcuts
to facilitate exploration of the available data.
# Import the ansys.dpf.core module as ``dpf``
from ansys.dpf import core as dpf
# Import the examples module
from ansys.dpf.core import examples
# Create a data source targeting the example file
my_data_sources = dpf.DataSources(result_path=examples.download_fluent_axial_comp()["flprj"])
# Create a model from the data source
my_model = dpf.Model(data_sources=my_data_sources)
# Print information available for the analysis
print(my_model)
DPF Model
------------------------------
Transient analysis
Unit system: Custom: m, kg, N, s, V, A, K
Physics Type: Fluid
Available results:
- enthalpy: Elemental Enthalpy
- mass_flow_rate: Faces Mass Flow Rate
- static_pressure: ElementalAndFaces Static Pressure
- mean_static_pressure: Elemental Mean Static Pressure
- rms_static_pressure: Elemental Rms Static Pressure
- surface_heat_rate: Faces Surface Heat Rate
- density: ElementalAndFaces Density
- face_artificial_wall_flag: Faces Face Artificial Wall Flag
- body_force: Elemental Body Force
- density_t_1: Elemental Density T 1
- pressure_discontinuity_sensor: Elemental Pressure Discontinuity Sensor
- dpm_partition: Elemental Dpm Partition
- nucleation_rate_of_water_droplet: Faces Nucleation Rate Of Water Droplet
- energy_m1: Elemental Energy M1
- mass_flux_m1: Faces Mass Flux M1
- pressure_m1: Elemental Pressure M1
- radiation_heat_flow_rate: Faces Radiation Heat Flow Rate
- temperature_m1: ElementalAndFaces Temperature M1
- temperature_m2: Faces Temperature M2
- x_velocity_m1: ElementalAndFaces X Velocity M1
- y_velocity_m1: ElementalAndFaces Y Velocity M1
- wall_velocity: Faces Wall Velocity
- original_wall_velocity: Faces Original Wall Velocity
- z_velocity_m1: ElementalAndFaces Z Velocity M1
- wall_shear_stress: Faces Wall Shear Stress
- temperature: ElementalAndFaces Temperature
- mean_temperature: ElementalAndFaces Mean Temperature
- rms_temperature: Elemental Rms Temperature
- velocity: ElementalAndFaces Velocity
- mean_velocity: Elemental Mean Velocity
- rms_velocity: Elemental Rms Velocity
Available qualifier labels:
- zone: default-interior:0 (2), rotor-hub (3), rotor-shroud (4), rotor-inlet (5), rotor-interface (6), rotor-blade-1 (7), rotor-blade-2 (8), rotor-per-1-shadow (9), rotor-per-1 (10), rotor-per-2-shadow (11), rotor-per-2 (12), fluid-rotor (13), default-interior (15), stator-hub (16), stator-shroud (17), stator-interface (18), stator-outlet (19), stator-blade-1 (20), stator-blade-2 (21), stator-blade-3 (22), stator-blade-4 (23), stator-per-2 (24), stator-per-2-shadow (25), stator-per-1 (26), stator-per-1-shadow (27), fluid-stator (28)
- phase: phase-1 (1)
------------------------------
DPF Meshed Region:
16660 nodes
13856 elements
44242 faces
Unit: m
With solid (3D) elements
------------------------------
DPF Time/Freq Support:
Number of sets: 3
Cumulative Time (s) LoadStep Substep
1 0.009587 1 1
2 0.009593 2 1
3 0.009600 3 1
The MeshInfo
class stores information relative to the MeshedRegion
of the analysis.
It stores some of its data as fields of strings or fields of integers, which we extract next.
# Get the mesh metadata
my_mesh_info = my_model.metadata.mesh_info
print(my_mesh_info)
DPF MeshInfo
------------------------------
with properties:
num_cells int
num_nodes int
num_faces int
body_names StringField
body_cell_topology PropertyField
body_face_topology PropertyField
body_scoping Scoping
cell_zone_names StringField
cell_zone_elements PropertyField
cell_zone_scoping Scoping
face_zone_names StringField
face_zone_elements PropertyField
face_zone_scoping Scoping
zone_names StringField
num_elem_zone PropertyField
zone_scoping Scoping
splittable_by StringField
You can obtain a Field
from a model by requesting a result.
# Request the collection of temperature result fields from the model and take the first one.
my_temp_field = my_model.results.temperature.eval()[0]
# Print the field
print(my_temp_field)
DPF T Field
Location: Elemental
Unit: K
13856 entities
Data: 1 components and 13856 elementary data
Elemental
IDs data(K)
------------ ----------
1 2.761285e+02
2 2.760320e+02
3 2.761719e+02
...
The field is located on nodes since it stores the displacement at each node.
You can obtain a StringField
from a MeshInfo
by requesting the names of the zones in the model.
# Request the name of the face zones in the fluid analysis
my_string_field = my_mesh_info.get_property(property_name="face_zone_names")
# Print the field of strings
print(my_string_field)
DPF String Field
24 zone entities
Data:24 elementary data
zone
IDs data
------------ ----------
2 default-interior:0
3 rotor-hub
4 rotor-shroud
...
The field is located on zones since it stores the name of each zone.
You can obtain a PropertyField
from a MeshInfo
by requesting the element types in the mesh.
# Get the body_face_topology property field
my_property_field = my_mesh_info.get_property(property_name="body_face_topology")
# Print the field of integers
print(my_property_field)
DPF Property Field
2 entities
Data: 1 components and 24 elementary data
Body
IDs data
------------ ----------
13 2
3
4
...
28 15
16
17
...
The field is located on elements since it stores the element type ID for each element.
Create fields from scratch#
You can also create a Field
, StringField
or PropertyField
from scratch based on your data.
First create a 3D vector field defined for two nodes.
# Create a 3D vector field ready to hold data for two entities
# The constructor creates 3D vector fields by default
my_field = dpf.Field(nentities=2)
# Set the data values as a flat vector
my_field.data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
# Associate the data to nodes
my_field.location = dpf.locations.nodal
# Set the IDs of the nodes the data applies to
my_field.scoping.ids = [1, 2]
# Define the unit (only available for the Field type)
my_field.unit = "m"
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit: m
2 entities
Data: 3 components and 2 elementary data
IDs data(m)
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00
2 4.000000e+00 5.000000e+00 6.000000e+00
Now create a 3x3 symmetric matrix field defined for a single element.
# Set the nature to symmatrix
my_field = dpf.Field(nentities=1, nature=dpf.natures.symmatrix)
# The symmatrix dimensions defaults to 3x3
# Set the data values as a flat vector
my_field.data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
# Associate the data to elements
my_field.location = dpf.locations.elemental
# Set the IDs of the nodes the data applies to
my_field.scoping.ids = [1]
# Define the unit (only available for the Field type)
my_field.unit = "Pa"
# Print the field
print(my_field)
DPF Field
Location: Elemental
Unit: Pa
1 entities
Data: 6 components and 1 elementary data
IDs data(Pa)
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00
Now create a 2x3 matrix field defined for a single fluid element face.
# Set the nature to matrix and the location to elemental
my_field = dpf.Field(nentities=1, nature=dpf.natures.matrix)
# Set the matrix dimensions to 2x3
my_field.dimensionality = dpf.Dimensionality(dim_vec=[2, 3], nature=dpf.natures.matrix)
# Set the data values as a flat vector
my_field.data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
# Associate the data to faces
my_field.location = dpf.locations.faces
# Set the IDs of the face the data applies to
my_field.scoping.ids = [1]
# Define the unit (only available for the Field type)
my_field.unit = "mm"
# Print the field
print(my_field)
DPF Field
Location: Faces
Unit: mm
1 entities
Data: 6 components and 1 elementary data
IDs data(mm)
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00
# Create a string field with data for two elements
my_string_field = dpf.StringField(nentities=2)
# Set the string values
my_string_field.data = ["string_1", "string_2"]
# Set the location
my_string_field.location = dpf.locations.elemental
# Set the element IDs
my_string_field.scoping.ids = [1, 2]
# Print the string field
print(my_string_field)
DPF String Field
2 Elemental entities
Data:2 elementary data
Elemental
IDs data
------------ ----------
1 string_1
2 string_2
# Create a property field with data for two modes
my_property_field = dpf.PropertyField(nentities=2)
# Set the data values
my_property_field.data = [12, 25]
# Set the location
# For DPF 26R1 and above, directly set the location of the PropertyField
from ansys.dpf.core.check_version import meets_version
if meets_version(dpf.SERVER.version, "11.0"):
my_property_field.location = dpf.locations.modal
# For DPF older than 26R1, you must set the location with a Scoping
else:
my_property_field.scoping = dpf.Scoping(location=dpf.locations.modal)
# Set the mode IDs
my_property_field.scoping.ids = [1, 2]
# Print the property field
print(my_property_field)
DPF Property Field
2 entities
Data: 1 components and 2 elementary data
IDs data
------------ ----------
1 12
2 25
Create a Field
with the fields_factory#
The fields_factory
module provides helpers to create a Field
:
Use create_scalar_field
to create a scalar field:
# Create a scalar field ready to hold data for two entities
# The field is nodal by default
my_field = dpf.fields_factory.create_scalar_field(num_entities=2)
my_field.data = [1.0, 2.0]
my_field.scoping.ids = [1, 2]
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
2 entities
Data: 1 components and 2 elementary data
IDs data
------------ ----------
1 1.000000e+00
2 2.000000e+00
Use create_vector_field
to create a generic vector field:
# Create a 2D vector field ready to hold data for two entities
# The field is nodal by default
my_field = dpf.fields_factory.create_vector_field(num_entities=2, num_comp=2)
my_field.data = [1.0, 2.0, 3.0, 4.0]
my_field.scoping.ids = [1, 2]
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
2 entities
Data: 2 components and 2 elementary data
IDs data
------------ ----------
1 1.000000e+00 2.000000e+00
2 3.000000e+00 4.000000e+00
Use create_3d_vector_field
to create a 3D vector field:
# Create a 3D vector field ready to hold data for two entities
# The field is nodal by default
my_field = dpf.fields_factory.create_3d_vector_field(num_entities=2)
my_field.data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
my_field.scoping.ids = [1, 2]
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
2 entities
Data: 3 components and 2 elementary data
IDs data
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00
2 4.000000e+00 5.000000e+00 6.000000e+00
Use create_matrix_field
to create a generic matrix field:
# Create a 2x3 matrix field ready to hold data for two entities
# The field is nodal by default
my_field = dpf.fields_factory.create_matrix_field(num_entities=2, num_lines=2, num_col=3)
my_field.data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
my_field.scoping.ids = [1, 2]
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
2 entities
Data: 6 components and 1 elementary data
IDs data
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00
2 0.000000e+00 7.410985e-323 0.000000e+00 0.000000e+00 0.000000e+00 7.410985e-323
Use create_tensor_field
to create a 3x3 matrix field:
# Create a 3x3 matrix field ready to hold data for two entities
# The field is nodal by default
my_field = dpf.fields_factory.create_tensor_field(num_entities=2)
my_field.data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
my_field.scoping.ids = [1, 2]
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
2 entities
Data: 6 components and 1 elementary data
IDs data
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00
2 7.000000e+00 8.000000e+00 9.000000e+00 1.144908e+243 3.554068e+150 2.786058e+185
Use create_overall_field
to create a field with a single value for the whole support:
# Create a field storing a value applied to every node in the support
my_field = dpf.fields_factory.create_overall_field(value=1.0)
# Print the field
print(my_field)
DPF Field
Location: overall
Unit:
1 entities
Data: 1 components and 1 elementary data
IDs data
------------ ----------
0 1.000000e+00
Use field_from_array
to create a scalar, 3D vector, or symmetric matrix field directly from a numpy array or a Python list
# Create a scalar field from a 1D array or a list
arr = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
my_field = dpf.fields_factory.field_from_array(arr=arr)
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
6 entities
Data: 1 components and 6 elementary data
IDs data
------------ ----------
1 1.000000e+00
2 2.000000e+00
3 3.000000e+00
...
# Create a 3D vector field from an array or a list
arr = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
my_field = dpf.fields_factory.field_from_array(arr=arr)
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
2 entities
Data: 3 components and 2 elementary data
IDs data
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00
2 4.000000e+00 5.000000e+00 6.000000e+00
# Create a symmetric matrix field from an array or a list
arr = [[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]]
my_field = dpf.fields_factory.field_from_array(arr=arr)
# Print the field
print(my_field)
DPF Field
Location: Nodal
Unit:
1 entities
Data: 6 components and 1 elementary data
IDs data
------------ ----------
1 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00
Access the field metadata#
The metadata associated to a field includes its name, its location, its scoping, the shape of the data stored, its number of components, and its unit.
# Location of the fields data
my_location = my_temp_field.location
print("location", '\n', my_location,'\n')
# Fields scoping
my_scoping = my_temp_field.scoping # Location entities type and number
print("scoping", '\n',my_scoping, '\n')
my_scoping_ids = my_temp_field.scoping.ids # Available ids of locations components
print("scoping.ids", '\n', my_scoping_ids, '\n')
# Elementary data count
# Number of the location entities (how many data vectors we have)
my_elementary_data_count = my_temp_field.elementary_data_count
print("elementary_data_count", '\n', my_elementary_data_count, '\n')
# Components count
# Vectors dimension, here we have a displacement so we expect to have 3 components (X, Y and Z)
my_component_count = my_temp_field.component_count
print("components_count", '\n', my_component_count, '\n')
# Size
# Length of the data entire vector (equal to the number of elementary data times the number of components.)
my_field_size = my_temp_field.size
print("size", '\n', my_field_size, '\n')
# Fields shape
# Gives a tuple with the elementary data count and the components count
my_shape = my_temp_field.shape
print("shape", '\n', my_shape, '\n')
# Units
my_unit = my_temp_field.unit
print("unit", '\n', my_unit, '\n')
location
Elemental
scoping
DPF Scoping:
with Elemental location and 13856 entities
scoping.ids
[ 1 2 3 ... 13854 13855 13856]
elementary_data_count
13856
components_count
1
size
13856
shape
13856
unit
K
# Location of the fields data
my_location = my_string_field.location
print("location", '\n', my_location,'\n')
# StringFields scoping
my_scoping = my_string_field.scoping # Location entities type and number
print("scoping", '\n',my_scoping, '\n')
my_scoping_ids = my_string_field.scoping.ids # Available ids of locations components
print("scoping.ids", '\n', my_scoping_ids, '\n')
# Elementary data count
# Number of the location entities (how many data vectors we have)
my_elementary_data_count = my_string_field.elementary_data_count
print("elementary_data_count", '\n', my_elementary_data_count, '\n')
# Components count
# Data dimension, here we expect one name by zone
my_component_count = my_string_field.component_count
print("components_count", '\n', my_component_count, '\n')
# Size
# Length of the data entire array (equal to the number of elementary data times the number of components.)
my_field_size = my_string_field.size
print("size", '\n', my_field_size, '\n')
# Fields shape
# Gives a tuple with the elementary data count and the components count
my_shape = my_string_field.shape
print("shape", '\n', my_shape, '\n')
location
Elemental
scoping
DPF Scoping:
with Elemental location and 2 entities
scoping.ids
[1 2]
elementary_data_count
2
components_count
1
size
2
shape
2
# Location of the fields data
my_location = my_property_field.location
print("location", '\n', my_location,'\n')
# Fields scoping
my_scoping = my_property_field.scoping # Location entities type and number
print("scoping", '\n',my_scoping, '\n')
my_scoping_ids = my_property_field.scoping.ids # Available ids of locations components
print("scoping.ids", '\n', my_scoping_ids, '\n')
# Elementary data count
# Number of the location entities (how many data vectors we have)
my_elementary_data_count = my_property_field.elementary_data_count
print("elementary_data_count", '\n', my_elementary_data_count, '\n')
# Components count
# Data dimension, we expect to have one id by face that makes part of a body
my_component_count = my_property_field.component_count
print("components_count", '\n', my_component_count, '\n')
# Size
# Length of the data entire array (equal to the number of elementary data times the number of components.)
my_field_size = my_property_field.size
print("size", '\n', my_field_size, '\n')
# Fields shape
# Gives a tuple with the elementary data count and the components count
my_shape = my_property_field.shape
print("shape", '\n', my_shape, '\n')
location
Modal
scoping
DPF Scoping:
with location and 2 entities
scoping.ids
[1 2]
elementary_data_count
2
components_count
1
size
2
shape
2
Access the field data#
A Field
object is a client-side representation of the field server-side.
When a remote DPF server is used, the data of the field is also stored remotely.
To build efficient remote postprocessing workflows, the amount of data exchanged between the client and the remote server has to be minimal.
This is managed with operators and a completely remote workflow, requesting only the initial data needed to build the workflow, and the output of the workflow.
It is for example important when interacting with remote data to remember that any PyDPF request for the
Field.data
downloads the whole array to your local machine.
This is particularly inefficient within scripts handling a large amount of data where the request is made to perform an action locally which could have been made remotely with a DPF operator.
For example, if you want to know the entity-wise maximum of the field, you should prefer the
min_max.min_max_by_entity
operator to the array.max()
method from numpy
.
Get the complete array#
The field’s data
is ordered with respect to its scoping ids
(as shown above).
To access the entire data in the field as an array (numpy
array``):
my_data_array = my_temp_field.data
print(my_data_array)
[276.12852414 276.03204537 276.17186547 ... 304.13925596 324.34370722
316.09748279]
Note that this array is a genuine, local, numpy array (overloaded by the DPFArray).
print(type(my_data_array))
<class 'ansys.dpf.gate.dpf_array.DPFArray'>
my_data_array = my_string_field.data
print(my_data_array)
DPFVectorString['string_1', 'string_2']
my_data_array = my_property_field.data
print(my_data_array)
[12 25]
Get data for a single entity#
If you need to access 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 third element in the field
my_temp_field.get_entity_data(index=3)
DPFArray([275.3473736])
# Get the data from the element with id 533
my_temp_field.get_entity_data_by_id(id=533)
DPFArray([276.30236262])
Note that this would correspond to an index of 2 within the field. Be aware that scoping IDs are not sequential. You would get the index of element 532 in the field with:
# Get index of the element with id 533
my_temp_field.scoping.index(id=533)
532
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:
# Create a deep copy of the field that can be accessed and modified locally.
with my_temp_field.as_local_field() as f:
for i in range(1,100):
f.get_entity_data_by_id(i)