Skip to content

Add example on how to get data by ID #726

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
173 changes: 173 additions & 0 deletions examples/00-basic/13-get_data_by_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
"""
.. _ref_get_data_by_id:

Retrieve field data by ID
~~~~~~~~~~~~~~~~~~~~~~~~~

This example shows how to scope a field for a given named selection,
obtain the mesh for that scoped selection and to retrieve data from the field
by node ID.

.. note::
This example requires the Premium ServerContext.
For more information, see :ref:`user_guide_server_context`.

"""

###############################################################################
# Imports and load model
# ~~~~~~~~~~~~~~~~~~~~~~
# Import modules and set context as Premium.
import time
import numpy as np
from ansys.dpf import core as dpf
from ansys.dpf.core import examples
from ansys.dpf.core import operators as ops

dpf.set_default_server_context(dpf.AvailableServerContexts.premium)

###############################################################################
# Load model from examples and print information:
model = dpf.Model(examples.download_all_kinds_of_complexity())
print(model)

###############################################################################
# Print available named selections:
print(model.metadata.available_named_selections)

###############################################################################
# Visualize the entire mesh
# ~~~~~~~~~~~~~~~~~~~~~~~~~
# Extract displacements on the entire geometry. Note that this is just for
# demonstration purposes. On a real simulation, the user should not evaluate
# (request from the server) data that is not needed.
disp_fc = model.results.displacement().eval()
print(disp_fc)

###############################################################################
# The displacement FieldsContainer contains a total of 15113 entities, i.e.
# nodal values.

###############################################################################
# Plot the entire mesh
disp_fc[0].meshed_region.plot()

###############################################################################
# Get mesh and field scoped only for a certain named selection
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define named selection
my_named_selection = "_CM82"

###############################################################################
# Get nodal mesh scoping for ``my_named_selection``
scoping_op = ops.scoping.on_named_selection()
scoping_op.inputs.requested_location.connect("nodal")
scoping_op.inputs.named_selection_name.connect(my_named_selection)
scoping_op.inputs.data_sources.connect(
dpf.DataSources(examples.download_all_kinds_of_complexity())
)
mesh_scoping = scoping_op.outputs.mesh_scoping()

###############################################################################
# Check that the named selection requested contains IDs information
if len(mesh_scoping.ids) == 0:
raise ValueError("No IDs information for the requested named selection.")

###############################################################################
# Extract displacements by applying the ``mesh_scoping``
disp_selection_fc = model.results.displacement.on_mesh_scoping(mesh_scoping).eval()
print(disp_selection_fc)

###############################################################################
# Note how the number of entities has decreased from 15113 to 12970 entities
# after applying the scoping on ``my_named_selection``.

###############################################################################
# Get the only Field available
disp_selection = disp_selection_fc[0]

###############################################################################
# Scope mesh only for that named selection
mesh_from_scoping_op = ops.mesh.from_scoping()
mesh_from_scoping_op.inputs.scoping.connect(mesh_scoping)
mesh_from_scoping_op.inputs.mesh.connect(model.metadata.meshed_region)
mesh_selection = mesh_from_scoping_op.outputs.mesh()

###############################################################################
# Plot ``mesh_selection``
mesh_selection.plot()

###############################################################################
# The plot shows only one of the two cubes from the total geometry shown before.

###############################################################################
# Identify the location of the Field
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Check the location of the scoping to see what is the information associated
# to the IDs. The displacement field is defined at ``"nodal"`` location. Other
# fields may have other locations such as ``"elemental"`` or ``"elementalNodal"``.
# Note that this way of retrieving data by ID is only valid for locations that
# are either ``"nodal"`` or ``"elemental"``.
print(disp_selection.scoping.location)

###############################################################################
# Another way to check that ``disp_selection`` contains nodal data is to check
# that the number of entities in the displacement FieldsContainer (12970)
# matches the number of nodes of the scoped mesh ``mesh_selection``
print(mesh_selection.nodes.n_nodes)

###############################################################################
# Instead, the number of elements does not match
print(mesh_selection.elements.n_elements)

###############################################################################
# Access data in ``disp_selection`` by ID (index approach)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Get the list of nodal IDs
ids = disp_selection.scoping.ids

###############################################################################
# Retrieve data of ``disp_selection`` from the server. Note that this is done
# before the loop below so only one call is made to the server.
start = time.time()
data = disp_selection.data

###############################################################################
# Loop over ``ids`` to have access to both, the node ID and the displacement
# for that node
nodal_disp_index = np.zeros([mesh_selection.nodes.n_nodes, 3])
for idx, node_id in enumerate(ids):
node_disp = data[idx]
Copy link
Contributor

Choose a reason for hiding this comment

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

@guillem, this is true when you have nodal or elemental results. When you read stresses for example as Elemental nodal data, then you will need to use field.get_entity_data(idx) instead of data[idx], otherwise you will get only the first stress tensor, this is also true for layered results.
We may need to add a similar loop with field.get_entity_data(); when the server is in in-process mode I think that you will not have overhead, if the server is remote then you may need to use as_local_field option and field.get_entity_data()

nodal_disp_index[idx, :] = node_disp

index_time = time.time() - start

###############################################################################
# Access data in ``disp_selection`` by ID (get entity approach)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# An alternative to the index approach is to use the ``get_entity_data_by_id``
# method to get the nodal displacement given a certain node ID.
start = time.time()
nodal_disp_get_entity = np.zeros([mesh_selection.nodes.n_nodes, 3])
for idx, node_id in enumerate(ids):
node_disp = disp_selection.get_entity_data_by_id(node_id)
Copy link
Contributor

Choose a reason for hiding this comment

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

@guillem , this one is useless in this case. the right one in the one you wrote above.
we use the get_by_id:
-when we loop over several fields for example nodal stresses and displacements and want to get for a given node of the displacement field it's nodal stress for example.
-when we have a user defined scoping, which is different from the field scoping, and we want to extract data from the field. this can be important when we want to read the data once from the file in one request and then work on different named selections and extract data from the field instead of reading each time from a file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @rlagha. Yes, exactly.

Depending on what you want to do you should use one approach or the other. That is why I included the two of them in the example and a comparison between them.

Copy link
Contributor

Choose a reason for hiding this comment

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

@guillem, this can be important to mention these remarks in the example.

nodal_disp_get_entity[idx, :] = node_disp

get_entity_time = time.time() - start

###############################################################################
# Assess both approaches to get data by node ID
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Find below the difference of the arrays obtained with both methods. The zero
# value indicates that the two approaches are completely equivalent.
diff = np.sum(nodal_disp_index - nodal_disp_get_entity)
print(f"Difference between the two approaches: {diff}")

###############################################################################
# In terms of performance, the index approach is found to be an order of magnitude
# faster for this particular example. The time difference can be attributed to the
# fact that only one server call is required for the index approach.
# Instead, the get entity strategy requires one server call per node in the mesh.
# The difference in perforance is likely to scale with the number of nodes in the mesh.
print(f"Time taken using index approach: {index_time}")
print(f"Time taken using get_entity_data_by_id approach: {get_entity_time}")