The following topics provide information about efficiently using mechanical results:
The reading of multiple results in the same RST file will perform poorly if the script is not written cleverly. The RST file reader uses buffers to improve its own performance. When using this reader, you must ensure that your code minimizes unnecessary re-population of these buffers.
The following example demonstrates this problem. In an ACT extension for
Mechanical, two results (“PRIN_S”
and
“EPPLEQV”
) are accessed from the RST
file so that a custom acceptance criterion can be calculated. Initially, the
script has only one loop on the elements to read multiple result values in the
RST file and to compute the custom acceptance criterion.
def evaluate_v1(result,stepInfo, collector): #mesure elapse time time=System.Diagnostics.Stopwatch() time.Start() # Reader initialization reader = getResultsData(ExtAPI, result.Analysis) reader.CurrentResultSet = stepInfo.Set # Get the principal stress result from the reader stress = reader.GetResult("PRIN_S") stress.SelectComponents(["1"]) # Get element nodal equivalent plastic strain result from the reader strain = reader.GetResult("EPPLEQV_RST") # loop on the elements and reading of the results values # to compute the indicator for element in collector.Ids: stress_values = stress.GetElementValues(element) strain_values = strain.GetElementValues(element) ind = list(map(lambda x: x[0] + x[1],zip(stress_values,strain_values))) collector.SetValues(element,ind) #mesure elapse time time.Stop() tot = time.Elapsed.TotalSeconds ExtAPI.Log.WriteMessage("Evaluate callback Total Time: " + str(tot))
Using a benchmark testing model, it takes 7.93 seconds to execute the evaluate callback.
When the script is rewritten properly, three loops are used:
One loop reads the values in the RST file for the first result
One loop reads the values in the RST file for the second result
One loop computes the custom acceptance criterion
def evaluate_v2(result,stepInfo, collector): #mesure elapse time time=System.Diagnostics.Stopwatch() time.Start() # Reader initialization reader = getResultsData(ExtAPI, result.Analysis) reader.CurrentResultSet = stepInfo.Set # Get the principal stress result from the reader stress = reader.GetResult("PRIN_S") stress.SelectComponents(["1"]) # Get element nodal equivalent plastic strain result from the reader strain = reader.GetResult("EPPLEQV_RST") # Get result values for stress for all elements # the loop on the elements is performed in the method GetElementValues stress_values = stress.GetElementValues(collector.Ids, False) # Get result values for strain for all elements # the loop on the elements is performed in the method GetElementValues strain_values = strain.GetElementValues(collector.Ids, False) # compute the criterior # operation on the results arrays values = list(map(lambda x: x[0] + x[1], zip(stress_values,strain_values))) # set the values in the result collector lengths = reader.NumberValuesByElement(collector.Ids) collector.SetAllValues(values) #mesure elapse time time.Stop() tot = time.Elapsed.TotalSeconds ExtAPI.Log.WriteMessage("Evaluate callback Total Time: " + str(tot)
Using the same benchmarks, it now takes only 0.01 seconds to execute the evaluate callback.
Explanation
At first glance, a single loop to perform the element iteration might seem most efficient. However, performance metrics show that placing each result’s access call into its own loop delivers the best performance.
To limit the access to the disk and improve performance, the reader reads a group of lines, filling a buffer in memory, so that the reader can quickly access the next line. In the first script, for each iteration, the reader must read two different results that are in two different places in the RST file.
It takes time for the reader to access the disk and to move from one location to the other. In addition, the reader does not read only the requested data. At each iteration, the reader reads a full buffer of lines and then clears the buffer. It then goes to the new location on the disk and reads again a full buffer. That is not efficient because the reader must read much more data than is needed.
That is why the second script is much better. In the first loop, the reader reads the first result, with all values in the buffer. In the second loop, the reader reads the second result, with all values in the buffer. In the third loop, the reader computes the custom acceptance criterion.
A beta method exists for more effectively retrieving Mechanical results. This method uses an external executable in addition to the Mechanical process. By using the postprocessing API via this method, you transparently control the instantiation of the results reader and postprocess results without interfering with Mechanical, especially in the case of analysis over several time steps. This method improves postprocessing performance by avoiding unnecessary actions that could be automatically executed in a standard use of Mechanical.
Note: This method is available only with the Windows platform.
To enable or disable the use of this method, you set the variable
ExtAPI.UsesStandaloneActResultReaderImplementation
to
true or false. This variable is set to false by default. The sample code that
follows shows how to activate and deactivate the external executable:
# Activate postprocessing via external executable ExtAPI.UsesStandaloneActResultReaderImplementation = True # Read the Stress from the specified result file rst = r"C:\\test\\file.rst" reader = ExtAPI.Tools.GetResultsDataFromFile(rst) nbSteps = reader.ResultSetCount rs = reader.GetResult("S") for i in range(nbSteps): reader.CurrentResultSet = i+1 s = rs.GetElementValues(elemIds,False) reader.Dispose() # Deactivate postprocessing via external executable ExtAPI.UsesStandaloneActResultReaderImplementation = False
As the result reader is in a separate process, to ensure good performance, it is necessary to use the two methods that follow to get nodal results and elemental results.
def EvaluateContact(self, result, stepInfo, collector): ExtAPI.ActResultReaderUsingMeshDataFromRst = True reader = result.Analysis.GetResultsData() mesh= reader.CreateMeshData()
You use the method Ansys.ACT.Common.Post.CreateMeshData
to create a MeshData
object. To manage degenerate
elements in the ACT postprocessing, the method
CreateMeshData
has to be called to build the needed
mesh data structure:
def EvaluateContact(self, result, stepInfo, collector): ExtAPI.ActResultReaderUsingMeshDataFromRst = True reader = result.Analysis.GetResultsData() mesh= reader.CreateMeshData()
To retrieve nodal results efficiently, you must use one call to get the
results for a set of nodes. The method GetNodeValues
takes as arguments an array of node indices and returns an array with the values
of each selected component of the required result at each node
sequentially.
Declaration syntax:
public double[] GetNodeValues(int[] nodeIds)
Where nodeIDs
is the array of integers containing the
list of the node indices for which result values are required.
Example code:
ExtAPI.ActResultReaderUsingMeshDataFromRst = False reader=ExtAPI.DataModel.AnalysisList[0].GetResultsData() mesh=ExtAPI.DataModel.MeshDataByName("Global") nodeIds = mesh.NodeIds ru=reader.GetResult("U") u = ru.GetNodeValues(nodeIds) > nodeIds = [ 1, 2, …] > u = [ U1X, U1Y, U1Z, U2X, U2Y, U2Z, …]
To retrieve element results efficiently, you must use one call to get the
results for a set of elements. The method
GetElementValues
takes as arguments an array of
element indices and returns an array with the values of each selected component
of the required result for each element sequentially.
Declaration syntax:
public double[] GetElementValues(int[] elementIds, boolean computeMidSideNodes)
Where:
elementIds
is the array of integers containing the list of element indices for which result values are required.computeMidSideNodes
indicates if the application should manually calculate the midside node values if they are not provided by the reader. If the reader already provides midside node values, they are returned to the user, irrespective of the value of this flag.
Example code:
reader=ExtAPI.DataModel.AnalysisList[0].GetResultsData() # Retrieve the mesh in Mechanical mesh=ExtAPI.DataModel.MeshDataByName("Global") elemIds = mesh.ElementIds re=reader.GetResult("S") s = re.GetElementValues(elemIds,False) >elemIds = [1, 2, …] >s = [ S11X, S11Y, S12X, S12Y, S13X, S13Y, S21X, S21Y, …]
In this example, each element has three nodes. The stress values are located
at the nodes of the elements. Thus, SijX
is the value of
component X for the stress result at node j
of element
i
.
Note: In the case of a 2D geometry, the result array returned by the method
GetNodeValues
or
GetElementValues
stays dimensioned as for a 3D
geometry. However, the values for the third dimension are dummy
values.
For a shell element, the method GetElementValues
returns the values for the three positions in this order:
Bottom
Top
Middle
Example code follows:
elemIds = mesh.ElementIds re=reader.GetResult("S") re.SelectComponents([“X”]) s = re.GetElementValues(elemIds,False) >elemIds = [1, 2, …] >s = [ S11X_bottom, S12X_bottom, S13X_bottom, S11X_top, S12X_top, S13X_top, S11X_middle, S12X_middle, S13X_middle, S21X_bottom, S22X_bottom, S23X_bottom, …]
Note: The method GetElementValues
does not work for shell
elements with KEYOPT(1)
set to
2
. In the Mechanical APDL
Element Reference content for
SHELL281, see the Other Input subsection for
information on how to set KEYOPT(1)
=
2
to evaluate stresses and strains on exterior
surfaces.