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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
811adde
add new basic tutorial and jupyter_ sphinx extension
luisaFelixSalles Oct 22, 2024
2266b73
add tutorials files and update index page
luisaFelixSalles Nov 15, 2024
9da611a
add get_mesh_from_result_file.rst tutorial
luisaFelixSalles Nov 18, 2024
d02b96d
updates on the index
luisaFelixSalles Nov 18, 2024
7480d30
updates on get_mesh_from_result_file.rst
luisaFelixSalles Nov 18, 2024
3c31dd2
add create_a_mesh_from_scratch.rst tutorial
luisaFelixSalles Nov 18, 2024
9a66fca
updates the read the mesh tut
luisaFelixSalles Nov 18, 2024
8e10754
updates the read the mesh tut
luisaFelixSalles Nov 18, 2024
6be9e21
add get_specific_part_mesh.rst and split_mesh.rst tutorials
luisaFelixSalles Nov 18, 2024
e2111e0
updates on thr get_specific_part_mesh.rst tutorial
luisaFelixSalles Nov 18, 2024
29785f1
updates the create_a_mesh_from_scratch.rst tut
luisaFelixSalles Nov 18, 2024
58e0364
erases add and plot data parts from the create_a_mesh_from_scratch.rs…
luisaFelixSalles Nov 19, 2024
b5f5365
update references in get_mesh_from_result_file.rst tutorial
luisaFelixSalles Nov 19, 2024
79d57f8
update references in read_mesh.rst tutorial
luisaFelixSalles Nov 19, 2024
f34dd4d
add second approach to the split_mesh.rst tutorial
luisaFelixSalles Nov 19, 2024
871f90d
updates on get_specific_part_mesh.rst
luisaFelixSalles Nov 19, 2024
56bd290
put a tab for each solver
luisaFelixSalles Nov 20, 2024
54476a9
update
luisaFelixSalles Nov 20, 2024
af705b2
put a tab for each solver in the get_specific_part_mesh.rst tutorial
luisaFelixSalles Nov 20, 2024
9338b6e
put a tab for each solver in the split_mesh.rst tutorial
luisaFelixSalles Nov 20, 2024
2e743e9
put a tab for each solver and update the read_mesh_metadata.rst tutorial
luisaFelixSalles Nov 20, 2024
5791181
put a tab for each solver and update the explore_mesh.rst tutorial
luisaFelixSalles Nov 20, 2024
1f7af61
put badge for each solver in the main page cards
luisaFelixSalles Nov 20, 2024
189b308
put badge for each solver in the beginning of the tutorials
luisaFelixSalles Nov 20, 2024
298f134
update title read_mesh_metadata.rst
luisaFelixSalles Nov 20, 2024
4e6e043
update title get_specific_part_mesh.rst
luisaFelixSalles Nov 20, 2024
56cbec3
update substitution text
luisaFelixSalles Nov 20, 2024
f1a9376
Update doc/source/user_guide/tutorials/mesh/explore_mesh.rst
luisaFelixSalles Nov 21, 2024
8b5b5f9
use only the jupyter sphinx extension
luisaFelixSalles Nov 22, 2024
b9da160
updates the examples package references
luisaFelixSalles Nov 22, 2024
f03e48e
update badges in the index.rst
luisaFelixSalles Dec 3, 2024
5c43ae4
change the section name
luisaFelixSalles Dec 3, 2024
947f646
add badges in each tutorial
luisaFelixSalles Dec 3, 2024
e666e27
update2 the create_a_mesh_from_scratch.rst tutorial to the tutorials …
luisaFelixSalles Dec 4, 2024
98d4a97
update the explore_mesh.rst to the tutorials guidelines
luisaFelixSalles Dec 4, 2024
ccc731b
update the extract_mesh_in_split_parts.rst to the tutorials guidelines
luisaFelixSalles Dec 4, 2024
882c3a2
update the get_mesh_from_result_file.rst to the tutorials guidelines
luisaFelixSalles Dec 4, 2024
cc3325c
update the read_mesh_metadata.rst to the tutorials guidelines
luisaFelixSalles Dec 4, 2024
e708660
update the split_mesh.rst to the tutorials guidelines
luisaFelixSalles Dec 4, 2024
ea88101
update the index page of the mesh tutorials section
luisaFelixSalles Dec 4, 2024
5684f6a
changes the name of the explore_mesh_metadata.rst tutorial
luisaFelixSalles Dec 16, 2024
a7c26d6
add explanation on the MeshesContainer labels when you use the split_…
luisaFelixSalles Dec 16, 2024
11a44f4
add more explanation for each approach on the split_mesh.rst tutorial
luisaFelixSalles Dec 16, 2024
4256566
add more explanation on the metadata info on the explore_mesh.rst tut…
luisaFelixSalles Dec 16, 2024
b44ea3d
add more explanation on the metadata info on the explore_mesh_metadat…
luisaFelixSalles Dec 16, 2024
69b9c1b
updates on the text of create_a_mesh_from_scratch.rst turorial
luisaFelixSalles Dec 16, 2024
4f3b349
Update doc/source/user_guide/tutorials/mesh/explore_mesh_metadata.rst
luisaFelixSalles Dec 16, 2024
ae30d91
Update doc/source/user_guide/tutorials/mesh/extract_mesh_in_split_par…
luisaFelixSalles Dec 18, 2024
8ba9f0a
delete file basic_tutorial.rst
luisaFelixSalles Dec 20, 2024
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
2 changes: 1 addition & 1 deletion doc/source/user_guide/tutorials/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ of our package background so you can understand how to work with it.

Understand how to represent data in DPF: either from manual input either form result files.

.. grid-item-card:: Mesh analysis
.. grid-item-card:: Mesh exploration
:link: ref_tutorials_mesh
:link-type: ref
:text-align: center
Expand Down
158 changes: 158 additions & 0 deletions doc/source/user_guide/tutorials/mesh/create_a_mesh_from_scratch.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
.. _ref_tutorials_create_a_mesh_from_scratch:

==========================
Create a mesh from scratch
==========================

.. include:: ../../../links_and_refs.rst
luisaFelixSalles marked this conversation as resolved.
Show resolved Hide resolved

This tutorial demonstrates how to build a |MeshedRegion| from the scratch.

The mesh object in DPF is a |MeshedRegion|. You can create your own |MeshedRegion| object and use it
with DPF operators. The ability to use scripting to create any DPF entity means
that you are not dependent on result files and can connect the DPF environment
with any Python tool.

In this tutorial, we create a parallel piped mesh made of linear hexa elements.

:jupyter-download-script:`Download tutorial as Python script<create_a_mesh_from_scratch>`
jorgepiloto marked this conversation as resolved.
Show resolved Hide resolved
:jupyter-download-notebook:`Download tutorial as Jupyter notebook<create_a_mesh_from_scratch>`

Import the necessary modules
----------------------------

Import the ``ansys.dpf.core`` module, including the operators module and the numpy library.
luisaFelixSalles marked this conversation as resolved.
Show resolved Hide resolved

.. jupyter-execute::

# Import the numpy library
import numpy as np
# Import the ``ansys.dpf.core`` module
from ansys.dpf import core as dpf
# Import the operators module
from ansys.dpf.core import operators as ops

Define the mesh dimensions
--------------------------

.. jupyter-execute::

# Define the mesh dimensions
length = 0.1
width = 0.05
depth = 0.1
luisaFelixSalles marked this conversation as resolved.
Show resolved Hide resolved
num_nodes_in_length = 10
num_nodes_in_width = 5
num_nodes_in_depth = 10
# Create a MeshedRegion object
my_meshed_region = dpf.MeshedRegion()

Define the connectivity function
--------------------------------

To create a mesh you must define the nodes connectivity. This means to define
the nodes ids connected to each element.

Here, we create a function that will find this connectivity.

.. jupyter-execute::

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
Comment on lines +60 to +67
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

Comment on lines +60 to +67
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.


Add nodes
---------

Add |Nodes| to the |MeshedRegion| object.

.. jupyter-execute::

node_id = 1
for i, x in enumerate(
[float(i) * length / float(num_nodes_in_length) for i in range(0, num_nodes_in_length)]
):
for j, y in enumerate(
[float(i) * width / float(num_nodes_in_width) for i in range(0, num_nodes_in_width)]
):
for k, z in enumerate(
[float(i) * depth / float(num_nodes_in_depth) for i in range(0, num_nodes_in_depth)]
):
my_meshed_region.nodes.add_node(node_id, [x, y, z])
node_id += 1

Get the nodes coordinates field.

.. jupyter-execute::

my_nodes_coordinates = my_meshed_region.nodes.coordinates_field

Set the mesh properties
-----------------------

Set the mesh unit.

.. jupyter-execute::

my_meshed_region.unit = "mm"

Set the nodes coordinates.

.. jupyter-execute::

# Get the nodes coordinates data
my_nodes_coordinates_data = my_nodes_coordinates.data
# As we use the connectivity function we need to get the data as a list
my_nodes_coordinates_data_list = my_nodes_coordinates.data_as_list
# Set the nodes scoping
my_coordinates_scoping = my_nodes_coordinates.scoping

Add elements
------------
Add |Elements| to the |MeshedRegion| object.

.. jupyter-execute::

element_id = 1
for i, x in enumerate(
[float(i) * length / float(num_nodes_in_length) for i in range(num_nodes_in_length - 1)]
):
for j, y in enumerate(
[float(i) * width / float(num_nodes_in_width) for i in range(num_nodes_in_width - 1)]
):
for k, z in enumerate(
[float(i) * depth / float(num_nodes_in_depth) for i in range(num_nodes_in_depth - 1)]
):
coord1 = np.array([x, y, z])
connectivity = []
for xx in [x, x + length / float(num_nodes_in_length)]:
for yy in [y, y + width / float(num_nodes_in_width)]:
for zz in [z, z + depth / float(num_nodes_in_depth)]:
data_index = search_sequence_numpy(my_nodes_coordinates_data_list, [xx, yy, zz])
scoping_index = int(data_index / 3) # 3components
Comment on lines +136 to +137
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.

connectivity.append(scoping_index)
# rearrange connectivity
tmp = connectivity[2]
connectivity[2] = connectivity[3]
connectivity[3] = tmp
tmp = connectivity[6]
connectivity[6] = connectivity[7]
connectivity[7] = tmp
my_meshed_region.elements.add_solid_element(element_id, connectivity)
element_id += 1
Comment on lines +121 to +147
Copy link
Member

Choose a reason for hiding this comment

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

By storing constants in variables, the readability of this code can be improved:

Suggested change
element_id = 1
for i, x in enumerate(
[float(i) * length / float(num_nodes_in_length) for i in range(num_nodes_in_length - 1)]
):
for j, y in enumerate(
[float(i) * width / float(num_nodes_in_width) for i in range(num_nodes_in_width - 1)]
):
for k, z in enumerate(
[float(i) * depth / float(num_nodes_in_depth) for i in range(num_nodes_in_depth - 1)]
):
coord1 = np.array([x, y, z])
connectivity = []
for xx in [x, x + length / float(num_nodes_in_length)]:
for yy in [y, y + width / float(num_nodes_in_width)]:
for zz in [z, z + depth / float(num_nodes_in_depth)]:
data_index = search_sequence_numpy(my_nodes_coordinates_data_list, [xx, yy, zz])
scoping_index = int(data_index / 3) # 3components
connectivity.append(scoping_index)
# rearrange connectivity
tmp = connectivity[2]
connectivity[2] = connectivity[3]
connectivity[3] = tmp
tmp = connectivity[6]
connectivity[6] = connectivity[7]
connectivity[7] = tmp
my_meshed_region.elements.add_solid_element(element_id, connectivity)
element_id += 1
element_id = 1
# Precompute node spacings
dx = length / float(num_nodes_in_length)
dy = width / float(num_nodes_in_width)
dz = depth / float(num_nodes_in_depth)
# Generate node coordinates
x_coords = [i * dx for i in range(num_nodes_in_length - 1)]
y_coords = [j * dy for j in range(num_nodes_in_width - 1)]
z_coords = [k * dz for k in range(num_nodes_in_depth - 1)]
# Iterate through the grid
for x in x_coords:
for y in y_coords:
for z in z_coords:
coord1 = np.array([x, y, z])
connectivity = []
# Generate connectivity for the current element
for xx in [x, x + dx]:
for yy in [y, y + dy]:
for zz in [z, z + dz]:
data_index = search_sequence_numpy(my_nodes_coordinates_data_list, [xx, yy, zz])
scoping_index = int(data_index / 3) # 3 components per node
connectivity.append(scoping_index)
# Rearrange connectivity to maintain element orientation
connectivity[2], connectivity[3] = connectivity[3], connectivity[2]
connectivity[6], connectivity[7] = connectivity[7], connectivity[6]
# Add the solid element
my_meshed_region.elements.add_solid_element(element_id, connectivity)
element_id += 1


Plot the mesh
-------------

You can check the mesh we just created with a plot. For more information on how to plot a mesh see
the :ref:`ref_tutorials_plotting_meshes` tutorial.

.. jupyter-execute::

# Plot the mesh
luisaFelixSalles marked this conversation as resolved.
Show resolved Hide resolved
my_meshed_region.plot()
Loading
Loading