Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1a6cfef
doc(tuto): DPF collections tutorial
PProfizi Nov 13, 2025
e4de9ef
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi Nov 13, 2025
d5d9d45
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi Nov 13, 2025
20b2ca7
Fixes
PProfizi Nov 13, 2025
cd4eac9
Fixes
PProfizi Nov 13, 2025
acc44d6
Fixes
PProfizi Nov 13, 2025
aa8797e
Apply suggestions from code review
PProfizi Nov 18, 2025
d0bd71d
Apply suggestion from @PProfizi
PProfizi Nov 18, 2025
726718f
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi Nov 18, 2025
41fd4a4
Merge branch 'main' into tuto/dpf_collections
PProfizi Nov 18, 2025
6c36bdb
Merge branch 'main' into tuto/dpf_collections
PProfizi Nov 19, 2025
faba694
Apply suggestions from code review
PProfizi Nov 19, 2025
75d0940
Apply suggestions from code review
PProfizi Nov 19, 2025
3d4c48b
Update with mention of the factory and built-in types
PProfizi Nov 19, 2025
77e241a
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi Nov 19, 2025
41389ab
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi Nov 19, 2025
6aef712
Small improvements
PProfizi Nov 20, 2025
61d2565
Apply suggestions from code review
PProfizi Nov 20, 2025
0ba01ac
Refactor the tutorial with suggestions
PProfizi Jan 5, 2026
c7852b0
Merge branch 'main' into tuto/dpf_collections
PProfizi Jan 5, 2026
e24de44
Refactor the tutorial with suggestions
PProfizi Jan 5, 2026
c117208
Fix the tutorial
PProfizi Jan 5, 2026
797ac32
Apply suggestions from code review
PProfizi Jan 6, 2026
b0feddf
fix: repeated title
moe-ad Jan 6, 2026
5d3f28f
Merge branch 'main' into tuto/dpf_collections
moe-ad Jan 6, 2026
aaa4fba
Add a section on LabelSpace
PProfizi Jan 7, 2026
df80674
Merge branch 'main' into tuto/dpf_collections
moe-ad Jan 7, 2026
7da6841
Fix header levels
PProfizi Jan 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 306 additions & 0 deletions doc/source/user_guide/tutorials/data_structures/collections.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
.. _ref_tutorials_collections:

===============
DPF Collections
===============

.. include:: ../../links_and_refs.rst

This tutorial shows how to create and work with some DPF collections: FieldsContainer, MeshesContainer and ScopingsContainer.

DPF collections are homogeneous groups of labeled raw data storage structures that allow you to organize and manipulate related data efficiently. Collections are essential for handling multiple time steps, frequency sets, or other labeled datasets in your analysis workflows.

:jupyter-download-script:`Download tutorial as Python script<collections>`
:jupyter-download-notebook:`Download tutorial as Jupyter notebook<collections>`

Introduction to Collections
---------------------------

Collections in DPF serve as containers that group related objects with labels. The main collection types are:

- |FieldsContainer|: A collection of |Field| objects, typically representing results over multiple time steps or frequency sets
- |MeshesContainer|: A collection of |MeshedRegion| objects for different configurations or time steps
- |ScopingsContainer|: A collection of |Scoping| objects for organizing entity selections

Each collection provides methods to:

- Add, retrieve, and iterate over contained objects
- Access objects by label (time, frequency, set ID, etc.)
- Perform operations across all contained objects

Set up the Analysis
-------------------

First, we import the required modules and load a transient analysis result file that contains multiple time steps.

.. jupyter-execute::

# Import the ansys.dpf.core module
import ansys.dpf.core as dpf

# Import the examples module
from ansys.dpf.core import examples

# Load a transient analysis with multiple time steps
result_file_path = examples.find_msup_transient()

# Create a DataSources object
data_sources = dpf.DataSources(result_path=result_file_path)

# Create a Model from the data sources
model = dpf.Model(data_sources=data_sources)

# Display basic model information
print(model)

Working with FieldsContainer
-----------------------------

A |FieldsContainer| is the most commonly used collection in DPF. It stores multiple |Field| objects, each associated with a label such as time step or frequency.

Extract Results into a FieldsContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Let's extract displacement results for all time steps, which will automatically create a |FieldsContainer|.

.. jupyter-execute::

# Get displacement results for all time steps
displacement_fc = model.results.displacement.on_all_time_freqs.eval()

# Display FieldsContainer information
print(displacement_fc)

Access Individual Fields in the Container
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can access individual fields by their label or index.

.. jupyter-execute::

# Access field by index (first time step)
first_field = displacement_fc[0]
print(f"First field info:")
print(first_field)

# Access field by label (specific time step)
second_time_field = displacement_fc.get_field({"time": 2})
# Equivalent to:
second_time_field = displacement_fc.get_field_by_time_id(2)
print(f"\nSecond time step field:")
print(second_time_field)
print(f" Max displacement magnitude: {max(second_time_field.data):.6f}")

Create a Custom FieldsContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can create your own |FieldsContainer| and add fields with custom labels.

.. jupyter-execute::

# Create an empty FieldsContainer
custom_fc = dpf.FieldsContainer()

# Set up labels for the container
custom_fc.labels = ["time", "zone"]

# Create sample fields for different time steps and zones
for time_step in [1, 2]:
for zone in [1, 2]:
# Create a simple field with sample data
field = dpf.Field(location=dpf.locations.nodal, nature=dpf.natures.scalar)

# Add some sample nodes and data
field.scoping.ids = [1, 2, 3, 4, 5]
field.data = [float(time_step * zone * i) for i in range(1, 6)]

# Add field to container with labels
custom_fc.add_field({"time": time_step, "zone": zone}, field)

# Display the custom FieldsContainer
print(custom_fc)

Working with ScopingsContainer
------------------------------

A |ScopingsContainer| holds multiple |Scoping| objects, which define sets of entity IDs (nodes, elements, etc.).

Create and Populate a ScopingsContainer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Let's create different node selections and organize them in a |ScopingsContainer|.

.. jupyter-execute::

# Get the mesh from our model
mesh = model.metadata.meshed_region

# Create a ScopingsContainer
scopings_container = dpf.ScopingsContainer()
# Set labels for different selections
scopings_container.labels = ["selection_type"]
# Selection 1: First 10 nodes
first_nodes = dpf.Scoping(location=dpf.locations.nodal)
first_nodes.ids = list(range(1, 11))
scopings_container.add_scoping(label_space={"selection_type": 0}, scoping=first_nodes)
# Selection 2: Every 10th node (sample)
all_node_ids = mesh.nodes.scoping.ids
every_tenth = dpf.Scoping(location=dpf.locations.nodal)
every_tenth.ids = all_node_ids[::10] # Every 10th node
scopings_container.add_scoping(label_space={"selection_type": 1}, scoping=every_tenth)
# Selection 3: Last 10 nodes
last_nodes = dpf.Scoping(location=dpf.locations.nodal)
last_nodes.ids = all_node_ids[-10:]
scopings_container.add_scoping(label_space={"selection_type": 2}, scoping=last_nodes)

# Display ScopingsContainer information
print(scopings_container)

Use ScopingsContainer with Operators
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

|ScopingsContainer| objects can be used with operators to apply operations to multiple selections.

.. jupyter-execute::

# Create an operator to extract displacement on specific node sets
displacement_op = dpf.operators.result.displacement()
displacement_op.inputs.data_sources(data_sources)
displacement_op.inputs.mesh_scoping(scopings_container)

# Evaluate to get results for all scopings
scoped_displacements = displacement_op.eval()

print(f"Displacement results for different node selections:")
print(scoped_displacements)

Working with MeshesContainer
----------------------------

A |MeshesContainer| stores multiple |MeshedRegion| objects. This is useful when working with different mesh configurations or time-dependent meshes.

Create a MeshesContainer
^^^^^^^^^^^^^^^^^^^^^^^^

Let's create a |MeshesContainer| with mesh data for different analysis configurations.

.. jupyter-execute::

# Create a MeshesContainer
meshes_container = dpf.MeshesContainer()

# Set labels for different mesh configurations
meshes_container.labels = ["variation"]

# Get the original mesh
original_mesh = model.metadata.meshed_region

# Add original mesh
meshes_container.add_mesh({"variation": 0}, original_mesh)

# Create a modified mesh (example: subset of elements)
# Get element scoping for first half of elements
all_element_ids = original_mesh.elements.scoping.ids
subset_element_ids = all_element_ids[:len(all_element_ids)//2]

# Create element scoping for subset
element_scoping = dpf.Scoping(location=dpf.locations.elemental)
element_scoping.ids = subset_element_ids

# Extract subset mesh using an operator
mesh_extract_op = dpf.operators.mesh.from_scoping()
mesh_extract_op.inputs.mesh(original_mesh)
mesh_extract_op.inputs.scoping(element_scoping)
subset_mesh = mesh_extract_op.eval()

# Add subset mesh to container
meshes_container.add_mesh({"variation": 1}, subset_mesh)

# Display MeshesContainer information
print(meshes_container)

Collection Operations and Iteration
------------------------------------

Collections support various operations for data manipulation and analysis.

Iterate Through Collections
^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can iterate through collections using different methods.

.. jupyter-execute::

# Iterate through FieldsContainer by index
print("Iterating through displacement fields by index:")
for i in range(min(3, len(displacement_fc))): # Show first 3 fields
field = displacement_fc[i]
label_space = displacement_fc.get_label_space(i)
max_value = field.data.max()
print(f" Field {i}: {label_space}, max value: {max_value:.6f}")

print("\nIterating through ScopingsContainer:")
for i, scoping in enumerate(scopings_container):
label_space = scopings_container.get_label_space(i)
print(f" Scoping {i}: {label_space}, size: {scoping.size}")

Filter and Select from Collections
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can filter collections based on labels or criteria.

.. jupyter-execute::

# Get specific fields from FieldsContainer by label criteria
# Get all fields of ``custom_fc`` where ``zone=1``
zone_1_fields = custom_fc.get_fields({"zone": 1})
print(zone_1_fields)

Other Built-in Collection Types
------------------------------

DPF provides several built-in collection types for common DPF objects, implemented in their respective modules:

- :class:`ansys.dpf.core.fields_container.FieldsContainer` for fields
- :class:`ansys.dpf.core.meshes_container.MeshesContainer` for meshes
- :class:`ansys.dpf.core.scopings_container.ScopingsContainer` for scopings

Additionally, the following specialized collection types are available (from ``collection_base.py``):

- :class:`ansys.dpf.core.collection_base.IntegralCollection` for integral types
- :class:`ansys.dpf.core.collection_base.IntCollection` for integers
- :class:`ansys.dpf.core.collection_base.FloatCollection` for floats
- :class:`ansys.dpf.core.collection_base.StringCollection` for strings

These built-in collections are optimized for their respective DPF types and should be used when working with fields, meshes, scopings, or basic types. For other supported types, you can use the :py:meth:`ansys.dpf.core.collection.Collection.collection_factory` method to create a custom collection class at runtime.

Using the Collection Factory
---------------------------

.. note::
Collections can only be made for types supported by DPF. Attempting to use unsupported or arbitrary Python types will result in an error.

The :py:meth:`ansys.dpf.core.collection.Collection.collection_factory` method allows you to create a collection class for any supported DPF type at runtime. This is useful when you want to group and manage objects that are not covered by the built-in collection types (such as FieldsContainer, MeshesContainer, or ScopingsContainer).

For example, you can create a collection for :class:`ansys.dpf.core.DataSources` objects:

.. jupyter-execute::

from ansys.dpf.core import Collection, DataSources
from ansys.dpf.core import examples

# Create a collection class for DataSources
DataSourcesCollection = Collection.collection_factory(DataSources)
ds_collection = DataSourcesCollection()
ds_collection.labels = ["case"]

# Add DataSources objects to the collection
ds1 = DataSources("path/to/first/result/file.rst")
ds2 = DataSources("path/to/second/result/file.rst")
ds_collection.add_entry({"case": 0}, ds1)
ds_collection.add_entry({"case": 1}, ds2)

# Show the collection
print(ds_collection)

This approach allows you to leverage the powerful labeling and grouping features of DPF collections for any supported DPF object type, making your workflows more flexible and organized.
8 changes: 2 additions & 6 deletions doc/source/user_guide/tutorials/data_structures/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,16 @@ These tutorials explains how these structures work and how you can manipulate da


.. grid-item-card:: DPF collections
:link: ref_tutorials_language_and_usage
:link: ref_tutorials_collections
:link-type: ref
:text-align: center
:class-header: sd-bg-light sd-text-dark
:class-footer: sd-bg-light sd-text-dark

This tutorial shows how to create and work with some DPF collections:
FieldsContainer, MeshesContainer and ScopingsContainer

+++
Coming soon

.. toctree::
:maxdepth: 2
:hidden:

data_arrays.rst
collections.rst
Loading