Adding a Custom Load

While toolbar examples in the Ansys ACT Developer's Guide show how to extend the interface of an Ansys product, they do not show how to perform meaningful operations. Here, the supplied extension DemoLoad shows how to add a button to Mechanical that adds a generic load to the project.

Creating the Extension for Adding a Custom Load

The file DemoLoad.xml follows.

<extension version="1" minorversion="0" name="DemoLoad">
  <guid shortid="DemoLoad">7dfd2b34-dfe1-469d-b244-1c7e5c222e54</guid>
  <script src="main.py" />
  <interface context="Mechanical">
    <images>images</images>
    <toolbar name="Loads" caption="Loads">
      <entry name="DemoLoad" icon="tload">
        <callbacks>
           <onclick>CreateDemoLoad</onclick>
        </callbacks>
      </entry>
    </toolbar>
  </interface>

  <simdata context="Mechanical">
    
    <load name="DemoLoad" version="1" caption="DemoLoad" icon="tload" color="#00FFFF">
      
      <callbacks>
        <getnodalvaluesfordisplay>GetNodalValuesForDisplay_DL</getnodalvaluesfordisplay>
        <getprecommands>GetPreCommands_DL</getprecommands>
        <getsolvecommands order="1">GetSolveCommands_DL</getsolvecommands>              
      </callbacks>

      <property name="Geometry" caption="Geometry" control="scoping">
        <attributes selection_filter="edge" />
      </property>
      <property name="Text" caption="Text" control="text"></property>
      <property name="FileName" caption="FileName" control="fileopen"></property>
      <property name="SelectStatic" caption="Select (static)" control="select">
        <attributes options="Option 1,Option 2,Option 3" />
      </property>
      <property name="SelectDynamic" caption="Select (dynamic)" control="select">
        <callbacks>
          <onactivate>StringOptions_DL</onactivate>
        </callbacks>
      </property>
      <property name="Double" caption="Double" unit="Length" control="float" 
default="1 [m]"></property>
      
      
    </load>
    
  </simdata>
  
</extension>

In this file, the element <script> references the IronPython script main.py. The element <interface> defines the toolbar and buttons. The callback function CreateDemoLoad creates and adds the load to the simulation environment.

The element <simdata> defines the load. The attributes for the child element <load> provide the name, version, caption, icon, and color that apply to the load. The attribute color is defined in a hexadecimal format. This color is used to contrast the load when it is displayed on the model. Its child element <callbacks> defines three callbacks: <getnodalvaluesfordisplay> and two occurrences of <getcommands>.

  • The callback <getnodalvaluesfordisplay> specifies the name of the function that is invoked when the load is displayed in the product.

  • The two occurrences of the callback <getcommands> specify the names of the functions that are invoked to insert specific solver commands in the solver input file.

    • For the first occurrence, the attribute location is set to pre, indicating that the solver commands defined in the function GetPreCommands_DL are to be inserted before the standard loads and boundary conditions defined in the product. For the Ansys solver, the new commands are inserted in the context of the /PREP7 preprocessor after the mesh has been written.

    • For the second occurrence, the attribute location is set to solve, indicating that the solver commands defined in the function GetSolveCommands_DL are to be inserted just before the SOLVE command. Consequently, the related function is responsible for generating the APDL commands that describe the load within the Ansys input file.


    Note:  For the callback <getcommands>, the attribute location can be set to any of these values: init, pre, post, solve, and preload.


In the callback definition, you define the properties to apply to the definition of the load. These properties are displayed in the Details view in Mechanical, where you provide the necessary values to complete the load definition. In the IronPython script, you see how the load's properties can be retrieved and modified.

The following figure shows how each property defined in the XML file appears in the Details view.

 

For the properties Select (static) and Select (dynamic), the attribute control is set to select.

  • The first property populates the list of options from the XML file.

  • The second property defines the callback <onactivate>. This callback, which is invoked when the control is activated, populates the available options for the property. The definition for the second property provides full control of the options to expose in the drop-down menu and makes the list dependent on the current status of the project. Many different situations that can impact the content of the list can be addressed, as long as they are implemented in the callback <onactivate>.

The next property, Double, does not require the definition of a specific callback. Instead, the attribute unit is set to Length to indicate that the property is a physical quantity. The attribute default is set to 1 [m]. This default value appears in the Details view each time a new load is created.

Defining Functions for Adding a Custom Load

The IronPython script main.py follows.

import os

def init(context):
    ExtAPI.Log.WriteMessage("Init DemoLoads...")

def CreateDemoLoad(analysis):
    analysis.CreateLoadObject("DemoLoad")
    
def StringOptions_DL(load,prop):
    prop.Options.Clear()
    prop.Options.Add("X")
    prop.Options.Add("Y")
    prop.Options.Add("Z")

def GetPreCommands_DL(load, solverData, stream):
    stream.Write("/COM, **************************************************" + "\n")
    stream.Write("/COM, Load properties from DemoLoad getcommands pre event" + "\n")
    stream.Write("/COM, Text Property = " + load.Properties["Text"].ValueString + "\n")
    stream.Write("/COM, SelectDynamic Property = " + load.Properties["SelectDynamic"].ValueString + "\n")
    stream.Write("/COM, SelectStatic Property = " + load.Properties["SelectStatic"].ValueString + "\n")
    stream.Write("/COM, Double Property = " + load.Properties["Double"].ValueString + "\n")
    stream.Write("/COM, **************************************************" + "\n")

def GetSolveCommands_DL(load, solverData, stream):
    stream.Write("/COM, **************************************************" + "\n")
    stream.Write("/COM, Load properties from DemoLoad getcommands solve event" + "\n")
    stream.Write("/COM, Text Property = " + load.Properties["Text"].ValueString + "\n")
    stream.Write("/COM, SelectDynamic Property = " + load.Properties["SelectDynamic"].ValueString + "\n")
    stream.Write("/COM, SelectStatic Property = " + load.Properties["SelectStatic"].ValueString + "\n")
    stream.Write("/COM, Double Property = " + load.Properties["Double"].ValueString + "\n")
    stream.Write("/COM, **************************************************" + "\n")
    
def GetNodalValuesForDisplay_DL(load, nodeIds):
    dval = load.Properties["Double"].Value
    coordselect = load.Properties["SelectDynamic"].ValueString
    mesh = load.Analysis.MeshData
    values = []
    for id in nodeIds:
        node = mesh.NodeById(id)
        dispval = float(0.0)
        if coordselect == "X":
            dispval = node.X * float(dval)
        elif coordselect == "Y":
            dispval = node.Y * float(dval)
        elif coordselect == "Z":
            dispval = node.Z * float(dval)
        else: 
            dispval = float(0.0)
        values.Add(dispval)
    return values

The functions GetNodalValuesForDisplay_DL and GetSolveCommands_DL are critical to the behavior and application of the load. GetNodalValuesForDisplay_DL is called each time the graphic is refreshed. The required input arguments are load and nodeIds, where load is the load object for the load and nodeIds is a list of node identifiers.

In this script, load.Properties["Double"].Value queries for the value of the property "Double". The argument nodeIds contains a list of node numbers on which one value has to be returned by the function. For every node in the list, the value of the property "Double" is assigned in the values array representing the output of the function. This output is subsequently treated by the graphics engine of the product so that the visualization on the FE model is available.

The function GetSolveCommands_DL is intentionally simplified for this example. The prototype of this function is made of two input arguments, the load object and the filename in which the new specific solver commands are written. The output file is only a temporary file. The content is rewritten in the final solver input file to ensure that the specific commands related to the customized load are merged with all the other commands already defined by the standard features of the product.

Adding Content Menu Options

In the XML file, you can create a specific set of context menu options in the element <callbacks> for the load. Each menu option is defined in a child element <action>.

<callbacks>

    <ongenerate>generate</ongenerate>
    <oncleardata>clearData</oncleardata>

    <action name="a1" caption="Action 1" icon="update">action1 </action>
    <action name="a2" caption="Action 2" icon="update">action2 </action>

</callbacks>

The element <action> takes three attributes:

  • The attribute name identifies the action.

  • The attribute caption is the text to display in the context menu.

  • The attribute icon specifies the name of the image file to display as the icon. You must place the BMP file that Mechanical requires in the directory images specified in the extension's XML file.

The element <action> defines the function to invoke when the associated context menu option is selected.

The following figure shows two additional context menu options, Action 1 and Action 2.

 

This figure also displays a Generate context menu option. This option derives from the standard action generate provided by Mechanical. For this reason, the declaration of the option Generate differs from the declaration of the options Action1 and Action2. The option Generate is always associated with the option Clear Generated Data.

These options allow you to create a load that can mimic a standard imported load. The callback associated with the option Generate replaces the standard function integrated in Mechanical.

The feature is activated when you define the callback <ongenerate> for the load. The callback <ongenerate> is invoked each time the Generate context menu option is selected. It is also invoked during a solve if the state of the load is set to "not solved."

As for the standard imported load object, the callback <ongenerate> is called only if the mesh is already generated.

<callbacks>

    <ongenerate>generate</ongenerate>
    <oncleardata>clearData</oncleardata>

    <action name="a1" caption="Action 1" icon="update">action1 </action>
    <action name="a2" caption="Action 2" icon="update">action2 </action>

</callbacks>

The associated IronPython code looks like this:

def generate(load, fct):
    pct = 0
    fct(pct,"Generating data...")

    propEx = load.PropertyByName("Expression")
    exp = propEx.Value
    if exp=="": 
        return False
    try:
        vexp = compile(exp,'','eval')
    except:
        return False

    values = SerializableDictionary[int,float]()
    nodeIds = []
    
    propGeo = load.PropertyByName("Geometry")
    refIds = propGeo.Value
    mesh = ExtAPI.DataModel.MeshDataByName("Global")
    for refId in refIds:
        meshRegion = mesh.MeshRegionById(refId)
        nodeIds += meshRegion.NodeIds

    nodeIds = list(set(nodeIds))

    for i in range(0,nodeIds.Count):
        id = nodeIds[i]
        node = mesh.NodeById(id)
        x = node.X
        y = node.Y
        z = node.Z
        v = 0.
        try:
            v = eval(vexp)
        finally:
            values.Add(id,v)
        new_pct = (int)((i*100.)/nodeIds.Count)
        if new_pct!=pct:
            pct = new_pct
            stopped = fct(pct,"Generating data...")
            if stopped:
                return False
        
    propEx.SetAttributeValue("Values",values)
    fct(100,"Generating data...")

    return True

def clearData(load):
    ExtAPI.Log.WriteMessage("ClearData: "+load.Caption)
    propEx = load.PropertyByName("Expression")
    propEx.SetAttributeValue("Values",None)

The callback <ongenerate> takes two arguments: the load object and a function to manage a progress bar. The function also takes two arguments: the message to display and the value of the progress bar, which is between 0 and 100.

During the process, the generated data is stored using an attribute on the property Expression. For more information, see Extension Data Storage.

The callback <oncleardata> takes one argument: the load object. This callback is invoked each time the mesh is cleared or when the Clear Generated Data context menu option is selected.