Create and manipulate a DPF Dataframe#

In this script a Dataframe is generated by extracting a result from a static simulation. It then showcases different Dataframe viewing and manipulation possibilities.

Perform required imports#

Perform required imports. # This example uses a supplied file that you can get by importing the DPF examples package.

from ansys.dpf import post
from ansys.dpf.post import examples

Get the Simulation object#

Get the Simulation object that allows access to the result. The Simulation object must be instantiated with the path for the result file. For example, "C:/Users/user/my_result.rst" on Windows or "/home/user/my_result.rst" on Linux.

example_path = examples.download_crankshaft()
# to automatically detect the simulation type, use:
simulation = post.load_simulation(example_path)

# to enable auto-completion, use the equivalent:
simulation = post.StaticMechanicalSimulation(example_path)

# print the simulation to get an overview of what's available
print(simulation)
Static Mechanical Simulation.


Data Sources
------------------------------
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/ansys/dpf/core/examples/result_files/crankshaft/crankshaft.rst

DPF Model
------------------------------
Static analysis
Unit system: MKS: m, kg, N, s, V, A, degC
Physics Type: Mechanical
Available results:
     -  displacement: Nodal Displacement
     -  velocity: Nodal Velocity
     -  acceleration: Nodal Acceleration
     -  reaction_force: Nodal Force
     -  stress: ElementalNodal Stress
     -  elemental_volume: Elemental Volume
     -  stiffness_matrix_energy: Elemental Energy-stiffness matrix
     -  artificial_hourglass_energy: Elemental Hourglass Energy
     -  thermal_dissipation_energy: Elemental thermal dissipation energy
     -  kinetic_energy: Elemental Kinetic Energy
     -  co_energy: Elemental co-energy
     -  incremental_energy: Elemental incremental energy
     -  elastic_strain: ElementalNodal Strain
     -  element_euler_angles: ElementalNodal Element Euler Angles
     -  structural_temperature: ElementalNodal Structural temperature
------------------------------
DPF  Meshed Region:
  69762 nodes
  39315 elements
  Unit: m
  With solid (3D) elements
------------------------------
DPF  Time/Freq Support:
  Number of sets: 3
Cumulative     Time (s)       LoadStep       Substep
1              1.000000       1              1
2              2.000000       1              2
3              3.000000       1              3

Get a Dataframe object#

Extract a result as a Dataframe

displacement_dataframe = simulation.displacement(all_sets=True)

# The Dataframe is displayed as a table, with row and column labels to identify the data.
print(displacement_dataframe)
            results       U (m)
            set_ids           1           2           3
node_ids components
    4872          X  5.6781e-06 -5.9469e-06 -3.4137e-05
                  Y  5.1667e-04  1.0318e-03  1.5417e-03
                  Z -3.2535e-06 -4.1346e-06 -2.6398e-06
    9005          X -2.6323e-06 -2.1432e-05 -5.5625e-05
                  Y  4.8445e-04  9.6717e-04  1.4448e-03
                  Z -4.9795e-07  1.2790e-06  5.3134e-06
     ...        ...         ...         ...         ...

Explore Index objects#

The data labels are each defined by an Index object or one of its specialized subtypes.

# The Dataframe's column labels are defined in Dataframe.columns.
print(displacement_dataframe.columns)
MultiIndex<[ResultIndex<['U (m)']>, SetIndex<values=[1, 2, 3]>]>

A ResultIndex index defines the result stored in the Dataframe.

print(displacement_dataframe.columns[0])
# print(displacement_dataframe.columns.results_index)  # equivalent
ResultsIndex "results" with 1 values of <class 'str'> type

You can check values available for an Index

print(displacement_dataframe.columns[0].values)
['U (m)']

A SetIndex index defines the available set IDs available. A set ID is a unique identifier associated to each time-step, step and sub-step, or frequency available in a simulation. As shown next, an Index has a name and a list of values of a given type.

print(displacement_dataframe.columns[1])
print(displacement_dataframe.columns[1].values)
SetIndex "set_ids" with 3 values of <class 'int'> type
[1, 2, 3]

The Dataframe’s row labels are defined in Dataframe.index.

print(displacement_dataframe.index)
MultiIndex<[MeshIndex<name="node_ids", dtype=<class 'int'>>, CompIndex<name="components", dtype=<class 'str'>>]>

A MeshIndex defines the mesh entities for which data is available. It can store node IDs, element IDs, or face IDs.

print(displacement_dataframe.index[0])
# print(displacement_dataframe.index.mesh_index)  # equivalent
MeshIndex "node_ids" with uncounted values of <class 'int'> type

Since the list of possible values can be long and querying it can be costly, the list of available values may not be determined unless explicitly asked.

print(displacement_dataframe.index[0].values)
[ 4872  9005  9373 ... 34314 19123 19114]

The MeshIndex will then be updated to display the actual number of entities available.

print(displacement_dataframe.index[0])
# IMPORTANT: Note that the mesh entity IDs ordered based on the internal data storage structure,
# they are not by ascending order by default!
MeshIndex "node_ids" with 69762 values of <class 'int'> type

A CompIndex defines the result components for which data is available.

print(displacement_dataframe.index[1])
print(displacement_dataframe.index[1].values)
CompIndex "components" with 3 values of <class 'str'> type
['X', 'Y', 'Z']

Change the Dataframe print#

Options exist to configure the way a Dataframe is displayed. You can change the number of data rows displayed with:

displacement_dataframe.display_max_rows = 9
print(displacement_dataframe)
            results       U (m)
            set_ids           1           2           3
node_ids components
    4872          X  5.6781e-06 -5.9469e-06 -3.4137e-05
                  Y  5.1667e-04  1.0318e-03  1.5417e-03
                  Z -3.2535e-06 -4.1346e-06 -2.6398e-06
    9005          X -2.6323e-06 -2.1432e-05 -5.5625e-05
                  Y  4.8445e-04  9.6717e-04  1.4448e-03
                  Z -4.9795e-07  1.2790e-06  5.3134e-06
    9373          X -2.6475e-05 -6.9632e-05 -1.2845e-04
                  Y  4.9502e-04  9.8751e-04  1.4741e-03
                  Z  6.9526e-06  1.6184e-05  2.7631e-05
     ...        ...         ...         ...         ...

Or the number of data columns displayed with:

displacement_dataframe.display_max_columns = 2
print(displacement_dataframe)
# Notice that the ``...`` symbols specify that the Dataframe is truncated in that direction.
            results       U (m)             ...
            set_ids           1           2 ...
node_ids components                         ...
    4872          X  5.6781e-06 -5.9469e-06 ...
                  Y  5.1667e-04  1.0318e-03 ...
                  Z -3.2535e-06 -4.1346e-06 ...
    9005          X -2.6323e-06 -2.1432e-05 ...
                  Y  4.8445e-04  9.6717e-04 ...
                  Z -4.9795e-07  1.2790e-06 ...
    9373          X -2.6475e-05 -6.9632e-05 ...
                  Y  4.9502e-04  9.8751e-04 ...
                  Z  6.9526e-06  1.6184e-05 ...
     ...        ...         ...         ... ...

The special case of ElementalNodal results#

When dealing with results located on each node of each element (aka. ElementalNodal), an ElementNodeIndex index is added at creation to specify the node number in the element’s connectivity.

stress = simulation.stress()
print(stress)
print(stress.columns[2])
                results      S (Pa)
                set_ids           3
                   node           0           1           2           3
 element_ids components
       18357         XX  4.6183e+06 -1.5497e+06 -1.8339e+06 -5.5596e+06
                     YY  5.9405e+07  4.6302e+07  1.2961e+08 -1.2675e+07
                     ZZ  2.2825e+07  4.1579e+07  1.4117e+08 -1.4662e+08
                     XY -3.5088e+05  2.7884e+07  1.5350e+08  1.6123e+08
                     YZ -5.9452e+08 -4.9103e+08 -5.1941e+08 -4.5894e+08
                     XZ  7.6945e+06  2.3978e+07  3.2925e+07 -2.2203e+07
         ...        ...         ...         ...         ...         ...

ElementNodeIndex "node" with 1 values of <class 'int'> type

Data selection#

To select specific columns or rows, use the index names as arguments for the DataFrame.select method, taking lists of values:

disp_X_1 = displacement_dataframe.select(
    set_ids=[1], node_ids=[4872, 9005], components=["X"]
)
print(disp_X_1)
            results       U (m)
            set_ids           1
node_ids components
    4872          X  5.6781e-06
    9005            -2.6323e-06

You can also select along an index using a zero-based position with Dataframe.iselect:

disp_Y_9005_3 = displacement_dataframe.iselect(
    set_ids=[2], node_ids=[1], components=[1]
)
print(disp_Y_9005_3)
            results      U (m)
            set_ids          3
node_ids components
    9005          Y 1.4448e-03

Extract data#

Once the Dataframe contains the specific data you require, extract it as an array with:

print(disp_X_1.array)
# IMPORTANT: Note that for the extraction of the Dataframe's data as an array to make sense,
# you must first filter the columns label values to a unique combination of values.
# The exception is for ElementalNodal data, which is returned as a 2D array.
print(stress.array.shape)
[ 5.67807472e-06 -2.63230351e-06]
(157260, 6)

Plot a Dataframe#

displacement_dataframe.plot()
00 dataframe manipulation

Animate a transient Dataframe#

displacement_dataframe.animate()
00 dataframe manipulation

Total running time of the script: (0 minutes 2.428 seconds)

Gallery generated by Sphinx-Gallery