-
Notifications
You must be signed in to change notification settings - Fork 25
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
base: master
Are you sure you want to change the base?
Changes from 6 commits
3cb53da
b649b97
e6a8196
723ff09
c224484
bc039bb
b46ab2e
76f206d
d770951
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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] | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
GuillemBarroso marked this conversation as resolved.
Show resolved
Hide resolved
|
||
print(f"Time taken using index approach: {index_time}") | ||
print(f"Time taken using get_entity_data_by_id approach: {get_entity_time}") |
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.
@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()