Retrieving Mechanical Results More Efficiently

Reading Results in a Result (RST) File

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.

Using the Standalone Result Reader

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()

Managing Degenerate Elements

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()

Retrieving Nodal Results

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, …]

Retrieving Elemental Results

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.