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.
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 topre
, indicating that the solver commands defined in the functionGetPreCommands_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 tosolve
, indicating that the solver commands defined in the functionGetSolveCommands_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 attributelocation
can be set to any of these values:init
,pre
,post
,solve
, andpreload
.
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.
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.
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 directoryimages
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,
and .
This figure also displays a generate
provided by Mechanical. For this reason, the
declaration of the option differs from the
declaration of the options and
. The option
is always associated with the option
.
These options allow you to create a load that can mimic a standard imported load. The callback associated with the option
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
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 context menu option is
selected.