-
Notifications
You must be signed in to change notification settings - Fork 24
doc(tuto): DPF collections tutorial #2742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
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 e4de9ef
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi d5d9d45
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi 20b2ca7
Fixes
PProfizi cd4eac9
Fixes
PProfizi acc44d6
Fixes
PProfizi aa8797e
Apply suggestions from code review
PProfizi d0bd71d
Apply suggestion from @PProfizi
PProfizi 726718f
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi 41fd4a4
Merge branch 'main' into tuto/dpf_collections
PProfizi 6c36bdb
Merge branch 'main' into tuto/dpf_collections
PProfizi faba694
Apply suggestions from code review
PProfizi 75d0940
Apply suggestions from code review
PProfizi 3d4c48b
Update with mention of the factory and built-in types
PProfizi 77e241a
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi 41389ab
Update doc/source/user_guide/tutorials/data_structures/collections.rst
PProfizi 6aef712
Small improvements
PProfizi 61d2565
Apply suggestions from code review
PProfizi 0ba01ac
Refactor the tutorial with suggestions
PProfizi c7852b0
Merge branch 'main' into tuto/dpf_collections
PProfizi e24de44
Refactor the tutorial with suggestions
PProfizi c117208
Fix the tutorial
PProfizi 797ac32
Apply suggestions from code review
PProfizi b0feddf
fix: repeated title
moe-ad 5d3f28f
Merge branch 'main' into tuto/dpf_collections
moe-ad aaa4fba
Add a section on LabelSpace
PProfizi df80674
Merge branch 'main' into tuto/dpf_collections
moe-ad 7da6841
Fix header levels
PProfizi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
306 changes: 306 additions & 0 deletions
306
doc/source/user_guide/tutorials/data_structures/collections.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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. | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| :jupyter-download-script:`Download tutorial as Python script<collections>` | ||
| :jupyter-download-notebook:`Download tutorial as Jupyter notebook<collections>` | ||
|
|
||
| Introduction to Collections | ||
| --------------------------- | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Collections in DPF serve as containers that group related objects with labels. The main collection types are: | ||
PProfizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| - |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 | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - |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.) | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Perform operations across all contained objects | ||
|
|
||
| Set up the Analysis | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ------------------- | ||
|
|
||
PProfizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| First, we import the required modules and load a transient analysis result file that contains multiple time steps. | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. 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 | ||
PProfizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ----------------------------- | ||
|
|
||
PProfizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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|. | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. 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}") | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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]: | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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|. | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. 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) | ||
PProfizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) | ||
PProfizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) | ||
PProfizi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # 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. | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. 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}") | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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. | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. 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 | ||
PProfizi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # 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. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.