Skip to content
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

Add mesh tutorials to the main tutorials branch #1908

Open
wants to merge 49 commits into
base: doc/new-tutorials-section
Choose a base branch
from

Conversation

luisaFelixSalles
Copy link
Collaborator

@luisaFelixSalles luisaFelixSalles commented Nov 18, 2024

Add new plot tutorials to the mesh section:

List of tutorials:

  • Create a mesh from scratch
  • Get a mesh from a result file
  • Read a mesh metadata
  • Explore a mesh
  • Extract a mesh in split parts
  • Split a mesh

Preview on how it renders:

Mesh section main page

_D__ANSYSdev_pydpf-core_doc_build_html_user_guide_tutorials_mesh_index html

Create a mesh from scratch

_D__ANSYSdev_pydpf-core_doc_build_html_user_guide_tutorials_mesh_create_a_mesh_from_scratch html (2)

Get a mesh from a result file

_D__ANSYSdev_pydpf-core_doc_build_html_user_guide_tutorials_mesh_get_mesh_from_result_file html (6)

Read a mesh metadata

_D__ANSYSdev_pydpf-core_doc_build_html_user_guide_tutorials_mesh_read_mesh_metadata html (2)

Explore a mesh

_D__ANSYSdev_pydpf-core_doc_build_html_user_guide_tutorials_mesh_explore_mesh html (1)

Extract a mesh in split parts

_D__ANSYSdev_pydpf-core_doc_build_html_user_guide_tutorials_mesh_extract_mesh_in_split_parts html

Split a mesh

_D__ANSYSdev_pydpf-core_doc_build_html_user_guide_tutorials_mesh_split_mesh html (3)

@luisaFelixSalles luisaFelixSalles self-assigned this Nov 18, 2024
@luisaFelixSalles luisaFelixSalles changed the base branch from master to doc/new-tutorials-section November 18, 2024 16:28
@luisaFelixSalles luisaFelixSalles mentioned this pull request Nov 18, 2024
36 tasks
Copy link

codecov bot commented Nov 18, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 88.37%. Comparing base (627c2de) to head (8ba9f0a).

Additional details and impacted files
@@                      Coverage Diff                      @@
##           doc/new-tutorials-section    #1908      +/-   ##
=============================================================
- Coverage                      88.50%   88.37%   -0.14%     
=============================================================
  Files                             89       89              
  Lines                          10251    10251              
=============================================================
- Hits                            9073     9059      -14     
- Misses                          1178     1192      +14     

@PProfizi
Copy link
Contributor

Could we restrict the mesh from scratch tutorial to just that? I think the add data and plot data sections should appear somewhere else. Also, this is not "adding" data to the mesh but rather creating data from scratch which we then plot on the mesh. I think this is covered in the plotting tutorials, and the data part should probably be in a tutorial on creating Fields and FieldsContainers from scratch which we can add separately.

@PProfizi
Copy link
Contributor

Get a mesh from a result file: problem with rendering for the mesh_provider operator reference in the section title.
Also we should add a section regarding the meshes_provider.

@PProfizi
Copy link
Contributor

Read and get specific information from a mesh:
rename to read mesh metadata.
I see several references not rendering properly.
Also this looks like two examples where put one after the other, could we rather merge them into one, while giving indications on specifics for different solvers?

@PProfizi
Copy link
Contributor

Get a mesh split on different parts: I think we reshape the tutorial to something very small explaining how to get a mesh split by body for each solver (Fluent+CFX or LSDYNA) with the mesh_provider/meshes_provider (explain the region_scoping input pin)

Basically here the idea would be to show how to Extract a mesh as split per body

The next tutorial on Split a mesh can then be focused on splitting on already existing MeshedRegion into a MeshesContainer, based on mesh properties. There is the split_mesh operator, but there is also the two-steps way using scoping.split_on_property_type and then mesh.from_scoping(s)

@luisaFelixSalles
Copy link
Collaborator Author

Get a mesh from a result file: problem with rendering for the mesh_provider operator reference in the section title. Also we should add a section regarding the meshes_provider.

Wouldn't this section be the Extract a mesh as split per body tutorial?

@PProfizi PProfizi added the tutorials Related to PyDPF-Core tutorials label Nov 19, 2024
@luisaFelixSalles luisaFelixSalles force-pushed the tutorials/mesh/firsts-mesh-tutorials branch from 12622e2 to 4f2323d Compare November 20, 2024 12:38
@luisaFelixSalles luisaFelixSalles requested a review from a team as a code owner December 3, 2024 15:06
@luisaFelixSalles luisaFelixSalles force-pushed the tutorials/mesh/firsts-mesh-tutorials branch 2 times, most recently from bd42cc2 to 47afb3b Compare December 4, 2024 15:55
@luisaFelixSalles luisaFelixSalles force-pushed the doc/new-tutorials-section branch from 576cd9f to 627c2de Compare December 19, 2024 16:42
Comment on lines 38 to 45
.. jupyter-execute::

# Import the ``ansys.dpf.core`` module
from ansys.dpf import core as dpf
# Import the examples module
from ansys.dpf.core import examples
# Import the operators module
from ansys.dpf.core import operators as ops

# Define the result file path
result_file_path = examples.find_msup_transient()
# Create the model
model = dpf.Model(data_sources=result_file_path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid combining comments and imports. Instead, I feel it is more appropriate to have paragraphs and small code cells:

Suggested change
.. jupyter-execute::
# Import the ``ansys.dpf.core`` module
from ansys.dpf import core as dpf
# Import the examples module
from ansys.dpf.core import examples
# Import the operators module
from ansys.dpf.core import operators as ops
# Define the result file path
result_file_path = examples.find_msup_transient()
# Create the model
model = dpf.Model(data_sources=result_file_path)
Start by importing the `dpf`, `examples`, and `operators` modules:
.. jupyter-execute::
from ansys.dpf import core as dpf
from ansys.dpf.core import examples
from ansys.dpf.core import operators as ops
Next, define the path to the result file and create the model:
.. jupyter-execute::
result_file_path = examples.find_msup_transient()
model = dpf.Model(data_sources=result_file_path)

Comment on lines 70 to 68
.. note::

Only the |Elemental|, |Nodal|, or |Faces| locations are supported for animations.
|Overall| and |ElementalNodal| locations are not currently supported.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replacement is very convenient. I really like it.

:hide-code:
:hide-output:

disp_fc.animate(off_screen=True,save_as="source/user_guide/tutorials/animate/animate_disp_1.gif")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big fan of storing output. It forces to manually regenerate it in the future and it is prone to get outdated quickly.

In the future, it would be great to migrate this to Jupytext. I bet that using MyST can provide us with the same tab-features we are using.

Comment on lines +60 to +67
def search_sequence_numpy(arr, seq):
"""Find a sequence in an array and return its index."""
indexes = np.where(np.isclose(arr, seq[0]))
for index in np.nditer(indexes[0]):
if index % 3 == 0:
if np.allclose(arr[index + 1], seq[1]) and np.allclose(arr[index + 2], seq[2]):
return index
return -1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should work too. It avoids using the np.nditer function and handles dimensions before executing any logic:

Suggested change
def search_sequence_numpy(arr, seq):
"""Find a sequence in an array and return its index."""
indexes = np.where(np.isclose(arr, seq[0]))
for index in np.nditer(indexes[0]):
if index % 3 == 0:
if np.allclose(arr[index + 1], seq[1]) and np.allclose(arr[index + 2], seq[2]):
return index
return -1
if len(seq) == 0:
raise ValueError("The search sequence must not be empty.")
if len(seq) > len(arr):
return -1 # Sequence longer than the array cannot be found.
# Sliding window approach
for i in range(len(arr) - len(seq) + 1):
if np.allclose(arr[i:i + len(seq)], seq):
return i
return -1

@luisaFelixSalles luisaFelixSalles force-pushed the tutorials/mesh/firsts-mesh-tutorials branch from d11a84e to ae30d91 Compare December 20, 2024 10:58
Comment on lines +237 to +239
- Number of nodes and elements;
- Unit;
- Elements type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short lists should not end in periods or any other symbol:

Suggested change
- Number of nodes and elements;
- Unit;
- Elements type.
- Number of nodes and elements
- Unit
- Elements type

Comment on lines +163 to +165
- Number of nodes and elements;
- Unit;
- Elements type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Number of nodes and elements;
- Unit;
- Elements type.
- Number of nodes and elements
- Unit
- Elements type

Comment on lines +107 to +113
- Properties;
- Parts;
- Faces;
- Bodies;
- Zones;
- Number of nodes and elements;
- Elements types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Properties;
- Parts;
- Faces;
- Bodies;
- Zones;
- Number of nodes and elements;
- Elements types.
- Properties
- Parts
- Faces
- Bodies
- Zones
- Number of nodes and elements
- Elements types

Comment on lines +113 to +115
- Unit;
- Nodes, elements and faces;
- Named selections: .
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Unit;
- Nodes, elements and faces;
- Named selections: .
- Unit
- Nodes, elements and faces
- Named selections

Copy link
Member

@jorgepiloto jorgepiloto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just left some additional comments on how to document list items.

Comment on lines +11 to +16
.. |MeshesContainer| replace:: :class:`MeshesContainer <ansys.dpf.core.meshes_container.MeshesContainer>`
.. |split_mesh| replace:: :class:`split_mesh <ansys.dpf.core.operators.mesh.split_mesh.split_mesh>`
.. |split_on_property_type| replace:: :class:`split_on_property_type <ansys.dpf.core.operators.scoping.split_on_property_type.split_on_property_type>`
.. |from_scopings| replace:: :class:`from_scopings <ansys.dpf.core.operators.mesh.from_scopings.from_scopings>`
.. |ScopingsContainer| replace:: :class:`ScopingsContainer <ansys.dpf.core.scopings_container.ScopingsContainer>`
.. |PropertyField| replace:: :class:`PropertyField <ansys.dpf.core.property_field.PropertyField>`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job Luisa! You made heavy use of substitution definitions which very nice.

I observed the following concerning these definitions:

  • Some definitions already in links_and_refs.rst are being redefined, for example |MeshesContainer| and |ScopingsContainer|. If definitions in links_and_refs.rst are changed in the future, the changes will not reflect in your examples because local definitions will take precedence.
  • You have same substitution definitions in more than one .rst file, for example |meshes_provider| common to get_mesh_from_result_file.rst and extract_mesh_in_split_parts.rst. If there is a restructuring of the dpf.core package in the future affecting the location |meshes_provider| is pointing to, then each of the definitions in your examples have to be located and changed. It is a similar problem to that above.

The solution to this is to move all your substitution definitions to links_and_refs.rst. Then you have only one source of truth. Let me know what you think about the suggested approach.

Comment on lines +60 to +67
def search_sequence_numpy(arr, seq):
"""Find a sequence in an array and return its index."""
indexes = np.where(np.isclose(arr, seq[0]))
for index in np.nditer(indexes[0]):
if index % 3 == 0:
if np.allclose(arr[index + 1], seq[1]) and np.allclose(arr[index + 2], seq[2]):
return index
return -1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great implementation and great suggestion by Jorge as well.
Numpy operations are multidimensional, and knowing how to take advantage of this will greatly simplify things. For example, you can directly determine the index of a node within a DPFArray of nodes as suggested below.

Suggested change
def search_sequence_numpy(arr, seq):
"""Find a sequence in an array and return its index."""
indexes = np.where(np.isclose(arr, seq[0]))
for index in np.nditer(indexes[0]):
if index % 3 == 0:
if np.allclose(arr[index + 1], seq[1]) and np.allclose(arr[index + 2], seq[2]):
return index
return -1
def search_sequence_numpy(arr, node):
"""Find the node location in an array of nodes and return its index."""
indexes = np.isclose(arr, seq)
match = np.all(indexes, axis=1).nonzero()
return int(match[0][0])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this updated function will take my_nodes_coordinates_data directly as input.

Comment on lines +136 to +137
data_index = search_sequence_numpy(my_nodes_coordinates_data_list, [xx, yy, zz])
scoping_index = int(data_index / 3) # 3components
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you choose to go with the previous suggestion, then you just need to do this:

Suggested change
data_index = search_sequence_numpy(my_nodes_coordinates_data_list, [xx, yy, zz])
scoping_index = int(data_index / 3) # 3components
scoping_index = search_sequence_numpy(my_nodes_coordinates_data, [xx, yy, zz])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, you would still need to take care of the suggestions already given by Jorge.

Copy link
Contributor

@moe-ad moe-ad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Luisa. Great work!
Going through the examples made me learn new things about DPF as working with DPF is also new for me... :)

I left a couple of comments. I will try to conduct more review when I am chanced.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tutorials Related to PyDPF-Core tutorials
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants