-
Notifications
You must be signed in to change notification settings - Fork 25
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
base: doc/new-tutorials-section
Are you sure you want to change the base?
Add mesh tutorials to the main tutorials branch #1908
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## doc/new-tutorials-section #1908 +/- ##
=============================================================
- Coverage 88.50% 88.43% -0.08%
=============================================================
Files 89 89
Lines 10251 10251
=============================================================
- Hits 9073 9065 -8
- Misses 1178 1186 +8 |
Could we restrict the |
|
|
Basically here the idea would be to show how to The next tutorial on |
Wouldn't this section be the Extract a mesh as split per body tutorial? |
12622e2
to
4f2323d
Compare
bd42cc2
to
47afb3b
Compare
doc/source/user_guide/tutorials/mesh/extract_mesh_in_split_parts.rst
Outdated
Show resolved
Hide resolved
576cd9f
to
627c2de
Compare
.. 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) |
There was a problem hiding this comment.
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:
.. 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) |
.. note:: | ||
|
||
Only the |Elemental|, |Nodal|, or |Faces| locations are supported for animations. | ||
|Overall| and |ElementalNodal| locations are not currently supported. |
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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.
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 |
There was a problem hiding this comment.
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:
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 |
…mesh operator on the split_mesh.rst tutorial
d11a84e
to
ae30d91
Compare
- Number of nodes and elements; | ||
- Unit; | ||
- Elements type. |
There was a problem hiding this comment.
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:
- Number of nodes and elements; | |
- Unit; | |
- Elements type. | |
- Number of nodes and elements | |
- Unit | |
- Elements type |
- Number of nodes and elements; | ||
- Unit; | ||
- Elements type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Number of nodes and elements; | |
- Unit; | |
- Elements type. | |
- Number of nodes and elements | |
- Unit | |
- Elements type |
- Properties; | ||
- Parts; | ||
- Faces; | ||
- Bodies; | ||
- Zones; | ||
- Number of nodes and elements; | ||
- Elements types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Properties; | |
- Parts; | |
- Faces; | |
- Bodies; | |
- Zones; | |
- Number of nodes and elements; | |
- Elements types. | |
- Properties | |
- Parts | |
- Faces | |
- Bodies | |
- Zones | |
- Number of nodes and elements | |
- Elements types |
- Unit; | ||
- Nodes, elements and faces; | ||
- Named selections: . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Unit; | |
- Nodes, elements and faces; | |
- Named selections: . | |
- Unit | |
- Nodes, elements and faces | |
- Named selections |
There was a problem hiding this 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.
.. |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>` |
There was a problem hiding this comment.
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.
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 |
There was a problem hiding this comment.
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.
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]) |
There was a problem hiding this comment.
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.
data_index = search_sequence_numpy(my_nodes_coordinates_data_list, [xx, yy, zz]) | ||
scoping_index = int(data_index / 3) # 3components |
There was a problem hiding this comment.
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:
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]) |
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
|
||
.. include:: ../../../links_and_refs.rst | ||
|
||
This tutorial demonstrates how to build a |MeshedRegion| from the scratch. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tutorial demonstrates how to build a |MeshedRegion| from the scratch. | |
This tutorial demonstrates how to build a |MeshedRegion| from scratch. |
- Nodes, elements and faces; | ||
- Named selections: . | ||
|
||
When instantiating nodes, elements, faces and named selections you get the corresponding DPF objects: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think instantiating is the right word. Maybe more "calling the nodes
, `elements [...] properties" ?
.. jupyter-execute:: | ||
|
||
# Get the element types on the mesh | ||
el_types_1 = meshed_region_1.property_field(property_name="eltype") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a dedicated property to get the element types. I wouldn't use property_field
with the string
# Print the meshes | ||
print(meshes_41) | ||
|
||
Scope the mesh regions to be extracted in split parts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Fluent and CFX, there is no concept of part? Maybe we should rename this tutorial with "zone"
.. |ScopingsContainer| replace:: :class:`ScopingsContainer <ansys.dpf.core.scopings_container.ScopingsContainer>` | ||
.. |PropertyField| replace:: :class:`PropertyField <ansys.dpf.core.property_field.PropertyField>` | ||
|
||
This tutorial shows how to split a mesh on a give property. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tutorial shows how to split a mesh on a give property. | |
This tutorial shows how to split a mesh on a given property. |
-------------- | ||
|
||
This approach consist of splitting an already existing |MeshedRegion| based on a given property. To accomplish | ||
that goal, you must use the |split_mesh| operator. Currently you can split a mesh by material or eltype. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we are restricted to these properties, are we?
.. jupyter-execute:: | ||
|
||
# Split the mesh by material | ||
meshes_21 = ops.mesh.split_mesh(mesh=meshed_region_2,property="mat").eval() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Their are some formatting errors :)
meshes_21 = ops.mesh.split_mesh(mesh=meshed_region_2,property="mat").eval() | |
meshes_21 = ops.mesh.split_mesh(mesh=meshed_region_2, property="mat").eval() |
Add new plot tutorials to the mesh section:
List of tutorials:
Preview on how it renders:
Mesh section main page
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