From 3cb53da0f96765ed8214914ea7a98b5697111caa Mon Sep 17 00:00:00 2001 From: "guillem.barroso" Date: Fri, 23 Dec 2022 09:54:30 +0100 Subject: [PATCH 1/8] First version of the get_data_by_id example --- examples/00-basic/13-get_data_by_id.py | 116 +++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 examples/00-basic/13-get_data_by_id.py diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py new file mode 100644 index 0000000000..f715238dbb --- /dev/null +++ b/examples/00-basic/13-get_data_by_id.py @@ -0,0 +1,116 @@ +from ansys.dpf import core as dpf +from ansys.dpf.core import examples + +model = dpf.Model(examples.download_all_kinds_of_complexity()) +disp_selection_fc = model.results.displacement.on_named_selection( + named_selection="_CM82" +).eval() +print(disp_selection_fc[0].location) + + +############################################################################### +# Imports and load model +# ~~~~~~~~~~~~~~~~~~~~~~ +# Import modules and set context as Premium. +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:: +# Note that this is just for demonstration purposes. On a real simulation, the +# user shouldn't evaluate (request from the server) data that is not needed. +disp_fc = model.results.displacement().eval() +print(disp_fc) + +############################################################################### +# Plot the entire mesh +disp_fc[0].meshed_region.plot() + +############################################################################### +# Get mesh and results for only 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() + +############################################################################### +# Extract displacements by applying the ``mesh_scoping`` +disp_selection_fc = model.results.displacement.on_mesh_scoping(mesh_scoping).eval() +print(disp_selection_fc) + +############################################################################### +# Get the only Field available +disp_selection = disp_selection_fc[0] + +############################################################################### +# Scope mesh only for that named selection +# mesh_scoping = model.metadata.named_selection(my_named_selection) ## Force location == nodal +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() + +############################################################################### +# Identify the location of the Field +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Check the location of the scoping to see what is the information associated +# to the IDs. For this example, the displacement field is defined at ``"nodal"`` level. +# Note that this example 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 +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Get the list of IDs (associated to each element) +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. +data = disp_selection.data + +############################################################################### +# Loop over ``ids`` to have access to both, the ID value and the displacement +# for that ID (associated to elements for this example) +for idx, id in enumerate(ids): + node_disp = data[idx] + node_id = id From b649b97fa2c0f1247519ae2aaf26e0efa5666d23 Mon Sep 17 00:00:00 2001 From: "guillem.barroso" Date: Fri, 23 Dec 2022 10:50:00 +0100 Subject: [PATCH 2/8] Improve example --- examples/00-basic/13-get_data_by_id.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py index f715238dbb..6c98b82a96 100644 --- a/examples/00-basic/13-get_data_by_id.py +++ b/examples/00-basic/13-get_data_by_id.py @@ -1,12 +1,14 @@ -from ansys.dpf import core as dpf -from ansys.dpf.core import examples +""" +.. _ref_get_data_by_id: -model = dpf.Model(examples.download_all_kinds_of_complexity()) -disp_selection_fc = model.results.displacement.on_named_selection( - named_selection="_CM82" -).eval() -print(disp_selection_fc[0].location) +Retrieve field 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. +""" ############################################################################### # Imports and load model From e6a819692a7a3ab403ce21880aa4fac6252f37ce Mon Sep 17 00:00:00 2001 From: "guillem.barroso" Date: Fri, 23 Dec 2022 11:01:33 +0100 Subject: [PATCH 3/8] Improve example --- examples/00-basic/13-get_data_by_id.py | 33 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py index 6c98b82a96..504f2562db 100644 --- a/examples/00-basic/13-get_data_by_id.py +++ b/examples/00-basic/13-get_data_by_id.py @@ -32,13 +32,15 @@ ############################################################################### # Visualize the entire mesh # ~~~~~~~~~~~~~~~~~~~~~~~~~ -# Extract displacements on the entire geometry -# .. note:: -# Note that this is just for demonstration purposes. On a real simulation, the -# user shouldn't evaluate (request from the server) data that is not needed. +# 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. + ############################################################################### # Plot the entire mesh disp_fc[0].meshed_region.plot() @@ -64,13 +66,16 @@ 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_scoping = model.metadata.named_selection(my_named_selection) ## Force location == nodal 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) @@ -80,18 +85,22 @@ # 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. For this example, the displacement field is defined at ``"nodal"`` level. -# Note that this example is only valid for locations that are either ``"nodal"`` or -# ``"elemental"``. +# 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 +# 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) @@ -102,7 +111,7 @@ ############################################################################### # Access data in ``disp_selection`` by ID # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Get the list of IDs (associated to each element) +# Get the list of nodal IDs ids = disp_selection.scoping.ids ############################################################################### @@ -111,8 +120,8 @@ data = disp_selection.data ############################################################################### -# Loop over ``ids`` to have access to both, the ID value and the displacement -# for that ID (associated to elements for this example) +# Loop over ``ids`` to have access to both, the node ID and the displacement +# for that node for idx, id in enumerate(ids): node_disp = data[idx] node_id = id From 723ff09d6f1daa00b2da911ba49a36308e490ba6 Mon Sep 17 00:00:00 2001 From: "guillem.barroso" Date: Fri, 23 Dec 2022 11:23:15 +0100 Subject: [PATCH 4/8] Fix codacy --- examples/00-basic/13-get_data_by_id.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py index 504f2562db..9ca02bf5a7 100644 --- a/examples/00-basic/13-get_data_by_id.py +++ b/examples/00-basic/13-get_data_by_id.py @@ -122,6 +122,5 @@ ############################################################################### # Loop over ``ids`` to have access to both, the node ID and the displacement # for that node -for idx, id in enumerate(ids): +for idx, node_id in enumerate(ids): node_disp = data[idx] - node_id = id From c2244845bbd067bfd31ece461ec9d60e63859be8 Mon Sep 17 00:00:00 2001 From: "guillem.barroso" Date: Fri, 23 Dec 2022 12:22:11 +0100 Subject: [PATCH 5/8] Add performance study in the example --- examples/00-basic/13-get_data_by_id.py | 45 +++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py index 9ca02bf5a7..3e65c250e4 100644 --- a/examples/00-basic/13-get_data_by_id.py +++ b/examples/00-basic/13-get_data_by_id.py @@ -1,8 +1,8 @@ """ .. _ref_get_data_by_id: -Retrieve field 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 @@ -14,6 +14,8 @@ # 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 @@ -109,18 +111,53 @@ print(mesh_selection.elements.n_elements) ############################################################################### -# Access data in ``disp_selection`` by ID -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# 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) + 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}") From bc039bb71b125135fbfc7bc864c1768734c1ed6c Mon Sep 17 00:00:00 2001 From: "guillem.barroso" Date: Fri, 23 Dec 2022 15:56:12 +0100 Subject: [PATCH 6/8] Improve example --- examples/00-basic/13-get_data_by_id.py | 38 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py index 3e65c250e4..0e77f5cdd6 100644 --- a/examples/00-basic/13-get_data_by_id.py +++ b/examples/00-basic/13-get_data_by_id.py @@ -8,6 +8,10 @@ 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`. + """ ############################################################################### @@ -41,15 +45,16 @@ print(disp_fc) ############################################################################### -# The displacement FieldsContainer contains a total of 15113 entities. +# 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 results for only a certain named selection -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Get mesh and field scoped only for a certain named selection +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Define named selection my_named_selection = "_CM82" @@ -63,6 +68,11 @@ ) 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() @@ -94,16 +104,16 @@ # 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"``. +# 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`` +# 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) ############################################################################### @@ -117,8 +127,8 @@ 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. +# 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 @@ -135,8 +145,8 @@ ############################################################################### # 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. +# 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): @@ -148,8 +158,8 @@ ############################################################################### # 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. +# 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}") From b46ab2e42ef2cf2461424027fc954820218d6fef Mon Sep 17 00:00:00 2001 From: Guillem Barroso <53979143+GuillemBarroso@users.noreply.github.com> Date: Fri, 30 Dec 2022 17:09:48 +0100 Subject: [PATCH 7/8] Update examples/00-basic/13-get_data_by_id.py Co-authored-by: Maxime Rey <87315832+MaxJPRey@users.noreply.github.com> --- examples/00-basic/13-get_data_by_id.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py index 0e77f5cdd6..fb9dda9ca9 100644 --- a/examples/00-basic/13-get_data_by_id.py +++ b/examples/00-basic/13-get_data_by_id.py @@ -168,6 +168,6 @@ # 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. +# The difference in performance 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}") From 76f206d3c0d98b879ac66049cd733f48cc085f88 Mon Sep 17 00:00:00 2001 From: "guillem.barroso" Date: Fri, 30 Dec 2022 18:32:06 +0100 Subject: [PATCH 8/8] Add notes on the two approaches --- examples/00-basic/13-get_data_by_id.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/00-basic/13-get_data_by_id.py b/examples/00-basic/13-get_data_by_id.py index 0e77f5cdd6..0654862aaa 100644 --- a/examples/00-basic/13-get_data_by_id.py +++ b/examples/00-basic/13-get_data_by_id.py @@ -106,8 +106,6 @@ # 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) ############################################################################### @@ -142,6 +140,12 @@ index_time = time.time() - start +############################################################################### +# .. note:: +# Note that this way of retrieving data by ID is only valid for locations that +# are either ``"nodal"`` or ``"elemental"``. + + ############################################################################### # Access data in ``disp_selection`` by ID (get entity approach) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -155,6 +159,12 @@ get_entity_time = time.time() - start +############################################################################### +# .. note:: +# This approach is not efficient to retrieve data for all nodes, since it would +# make one server request per node. Instead, this approach is useful when +# we are only interesed on requesting data on one or a few nodes. + ############################################################################### # Assess both approaches to get data by node ID # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~