Last weeks PADT ANSYS Webinar Series webinar was on a little used feature in ANSYS Mechanical called Design Assessment, or DA. If you missed it, you can view a recording at:
http://tinyurl.com/PADT-DA-Webinar
And a PDF of the presentation can be found here:
As promised, this weeks The Focus posting will be a follow up on that webinar with an in-depth look at the scripts that were used in the example. But first, let us review what DA is for those that don’t remember, fell asleep, or don’t want to sit through 60+ minutes of me mumbling into the telephone.
Review of DA
Design Assessment is a tool in ANSYS Workbench that works with ANSYS Mechanical to take results from multiple time or load steps, perform post processing on those results, and bring the calculated values back into ANSYS Mechanical for viewing as contour plots. It was developed to allow the ANSYS, Inc. developers to add some special post processing tools needed in the off shore industry, but as they were working on it they saw the value of exposing the Application Programmers Interface (API) to the user community so anyone can write their own post processing tools.
You use it by adding a Design Assessment system to you project. In its most basic form, the default configuration, it is set up to do load case combinations. That in itself is worth knowing how to use it. But if you want to do more, you can point it to a custom post processing set of files and do your own calculations.
A custom DA is defined by two things. First is an XML file that tells ANSYS Mechanical what user input you want to capture, how you want to get results out of mechanical, what you want to do with the results, and how you want them displayed in your model tree. Second is one or more Python scripts that actually do the work of capturing what the user input, getting results, doing the calculation, and sticking the resulting values back in the model. Both are well documented and, once you get your head around the whole thing, pretty simple.
Right now DA works with Static and Transient Structural models. It also only allows access to element stress values. Lots of good enhancements are coming in R14, but R13 is mature enough to use now.
If that review was too quick, review the recording or the PowerPoint presentation.
A Deep Dive Into an Example
For the webinar we had a pretty simple, and a bit silly, example – the custom post processing tool took the results from a static stress model and truncates the stress values if they are above or below a user specified value. Not a lot of calculating but a good example of how the tool works.
Note, this posting is going to be long because there is a lot of code pasted in. For each section of code I’ll also include a link to the original file so you can download that yourself to use.
Here is the XML file for the example (Original File):
1: <?xml version="1.0" encoding="utf-8"?>
The first lesson learned was that you have to get all the tags and headers just right.
It is case sensitive and all the version and other stuff has to be there
Cutting and pasting from something that work is the best way to go
2: <!--
3: www.PADTINC.com
4: XML file for ANSYS DA R13
5: Demonstration of how to use DA at R13
6: Goes through results and sets stresses below floor value to floor
7: value and above ceiling value to ceiling value.
8:
9: User adds DA Result to specify Floor and Ceiling
10: Attribute group can be used to specify a comment
11:
12: Calls da_trunc_solve.py and da_trunc_eval.py in c:\temp
13:
14: Eric Miller
15: 5/18/2011
16: -->
17:
Everything is in a DARoot tag.
18: <DARoot ObjId ="1" Type="CAERep" Ver="2">
Attributes tags define items you want to ask the user about.
19: <Attributes ObjId="2" Type="CAERepBase" Ver="2">
This first attribute is a drop down for the user to decide which stress value they want
You use <AttributeType> to make it a dropdown then put the values in <Validation>
20: <DAAttribute ObjId="101" Type="DAAttribute" Ver="2">
21: <AttributeName PropType="string">Stress Value</AttributeName>
22: <AttributeType PropType="string">DropDown</AttributeType>
23: <Application PropType="string">All</Application>
24: <Validation PropType="vector&lt;string>">
25: SX,SY,SZ,SXY,SYZ,SXZ,S1,S2,S3,SEQV
26: </Validation>
27: </DAAttribute>
Next is the prompt for the stress floor value
28: <DAAttribute ObjId="102" Type="DAAttribute" Ver="2">
29: <AttributeName PropType="string">Stress Floor</AttributeName>
30: <AttributeType PropType="string">Double</AttributeType>
31: <Application PropType="string">All</Application>
32: <Validation PropType="vector&lt;string>">-1000000,10000000</Validation>
33: </DAAttribute>
Then the Ceiling Value
34: <DAAttribute ObjId="103" Type="DAAttribute" Ver="2">
35: <AttributeName PropType="string">Stress Ceiling</AttributeName>
36: <AttributeType PropType="string">Double</AttributeType>
37: <Application PropType="string">All</Application>
38: <Validation PropType="vector&lt;string>">-1000000,10000000</Validation>
39: </DAAttribute>
Finally a user comment, just to show how to do a string.
40: <DAAttribute ObjId="201" Type="DAAttribute" Ver="2">
41: <AttributeName PropType="string">User Comments</AttributeName>
42: <AttributeType PropType="string">Text</AttributeType>
43: <Application PropType="string">All</Application>
44: </DAAttribute>
45: </Attributes>
To expose an attribute, you can put it in an AttributeGroup to get info shared by
all DA result objects. This one just does the Comment
46: <AttributeGroups ObjId ="3" Type="CAERepBase" Ver="2">
47: <DAAttributeGroup ObjId="100002" Type="DAAttributeGroup" Ver="2">
48: <GroupType PropType="string">User Comments</GroupType>
49: <GroupSubtype PropType="string">Failure Criteria</GroupSubtype>
50: <AttributeIDs PropType="vector&lt;unsigned int>">201</AttributeIDs>
51: </DAAttributeGroup>
52: </AttributeGroups>
<DAScripts> is the most important part of the file. It defines the scripts to run for a
solve and for a evaluate. At R13 you do need to specify the whole path to your python files
53: <DAScripts ObjId="4" Type="DAScripts" Ver="2">
54: <Solve PropType="string">c:\temp\da_trunc_solve.py</Solve>
55: <Evaluate PropType="string">c:\temp\da_trunc_eval.py</Evaluate>
56: <DAData PropType="int">1</DAData>
57: <CombResults PropType="int">1</CombResults>
58: <SelectionExtra PropType="vector&lt;string>">DeltaMin, DeltaMax</SelectionExtra>
59: </DAScripts>
The other way to get user input is to put attributes into a <Results> object.
Here we are putting the choice of stresses (101),and the floor and ceiling (102,103)
into an object
60: <Results ObjId="5" Type="CAERepBase" Ver="2">
61: <DAResult ObjId ="110000" Type="DAResult" Ver="2">
62: <GroupType PropType="string">Ceiling and Floor Values</GroupType>
63: <AttributeIDs PropType="vector&lt;unsigned int>">101,102,103</AttributeIDs>
64: <DisplayType PropType="string">ElemCont</DisplayType>
65: </DAResult>
66: </Results>
67: </DARoot>
I always have to go over XML files a few times to figure them out. There is a lot of information, but only a small amount that you need to pay attention to. After a while you figure out which is which.
Now on to the fun part, the Python scripts. The first one gets executed when they user chooses solve.
da_trunc_solve.py is shown below and you can get the original here. The comments inside pretty much explain it all. It basically does two things: creates an ANSYS MAPDL macro that extracts all the element stresses and puts them in a text file, then it runs MAPDL with that macro.
1: import subprocess
2: import os
3: import shutil
4:
5: #======================================================================
6: #
7: # ------------------------------------------------------------ PADT
8: # www.PADTINC.com
9: #
10: # da_trunc_solve.py
11: # Demonstration python script for Design Assessment in
12: # ANSYS R13
13: # Called on solve from ANSYS Mechanical
14: # Bulk of code copied and modified from TsaiWu example
15: # provided by ANSYS, Inc.
16: #
17: # E. Miller
18: # 5/18/2011
19: #======================================================================
20: def trunc_solve(DesignAssessment) :
21:
22: # Get number of elements in model
23:
24:
25: # Change directory to current workspace for DA
26: originaldir = os.getcwd()
27: os.chdir(DesignAssessment.getHelper().getResultPath())
28:
29: #Get the path to the results file name and the location of the temp directory
30: rstFname = DesignAssessment.Selection(0).Solution(0).getResult().ResultFilePath()
31: rstFname = rstFname.rstrip('.rst')
32: apath = DesignAssessment.getHelper().getResultPath()
33:
34: print "Result File:", rstFname
35: print "Apath:", apath
36:
37: # Write an ANSYS APDL macro to start ANSYS, resume the result file, grab the stress
38: # results and write them to a file
39:
40: macfile = open(DesignAssessment.getHelper().getResultPath()+"\\runda1.inp", "w")
41:
42: macfile.write("/batch\n")
43: macfile.write("/post1\n")
44: macfile.write("file,"+rstFname+"\n")
45: macfile.write("set,last\n")
46: macfile.write("*get,emx,elem,,num,max\n")
47: macfile.write("*dim,evls,,emx,10\n")
48: macfile.write("etable,esx,s,x\n")
49: macfile.write("etable,esy,s,y\n")
50: macfile.write("etable,esz,s,z\n")
51: macfile.write("etable,esxy,s,xy\n")
52: macfile.write("etable,esyz,s,yz\n")
53: macfile.write("etable,esxz,s,xz\n")
54: macfile.write("etable,es1,s,1\n")
55: macfile.write("etable,es2,s,2\n")
56: macfile.write("etable,es3,s,3\n")
57: macfile.write("etable,eseqv,s,eqv\n")
58: macfile.write("*vget,evls(1, 1),elem,1,etab, esx\n")
59: macfile.write("*vget,evls(1, 2),elem,1,etab, esy\n")
60: macfile.write("*vget,evls(1, 3),elem,1,etab, esz\n")
61: macfile.write("*vget,evls(1, 4),elem,1,etab, esxy\n")
62: macfile.write("*vget,evls(1, 5),elem,1,etab, esyz\n")
63: macfile.write("*vget,evls(1, 6),elem,1,etab, esxz\n")
64: macfile.write("*vget,evls(1, 7),elem,1,etab, es1\n")
65: macfile.write("*vget,evls(1, 8),elem,1,etab, es2\n")
66: macfile.write("*vget,evls(1, 9),elem,1,etab, es3\n")
67: macfile.write("*vget,evls(1,10),elem,1,etab,eseqv\n")
68:
69: macfile.write("*cfopen,darsts,txt\n")
70: macfile.write("*vwrite,evls(1,1),evls(1,2),evls(1,3),evls(1,4),
evls(1,5),evls(1,6),evls(1,7),evls(1,8),evls(1,9),evls(1,10)\n")
71: macfile.write("(G16.9, X, G16.9, X, G16.9, X, G16.9, X, G16.9, X,
G16.9, X, G16.9, X, G16.9, X, G16.9, X, G16.9)\n")
72: macfile.write("*cfclose\n")
73: macfile.write("finish\n")
74: macfile.write("/exit,nosave\n")
75:
76: macfile.close()
77:
78: # Set up execution of ANSYS MAPDL.
79: # Note: Right now you need to grab a different license than what Workbench is using
80: exelocation = "C:\\Program Files\\ANSYS Inc\\v130\\ansys\\bin\\winx64\\ansys130.exe"
81: commandlinestring = " -p ansys -b nolist -i runda1.inp -o runda1.out /minimise"
82:
83: #Execute MAPDL and wait for it to finish
84: proc = subprocess.Popen(commandlinestring,shell=True,executable=exelocation)
85: rc = proc.wait()
86:
87: # Read the output fromt the run and echo it to the DA log file
88: File = open("runda1.out","r")
89: DesignAssessment.getHelper().WriteToLog(File.read())
90: File.close()
91:
92: # Go back to the original directory
93: os.chdir(originaldir)
94:
95: trunc_solve(DesignAssessment
Some key things you should note about this script:
- You have to use MAPDL to get your stress values. Right now there is no method in the API to get the values directly.
- You need a second MAPDL license in order to run this script. It does not share the license you are using for ANSYS Mechanical at R13. This should be addressed in R14.
- One work around right now is to use an APDL code snippet in the ANSYS mechanical run that makes the text file when the original problem is solved. The SOLVE script is then no longer needed and you can just have an evaluate script. Not a great solution but it will work if you only have once seat of ANSYS available.
- Note the directory changes and getting of result file paths. This is important. Mechanical does stuff all over the place and not in just one directory.
- Make sure the MAPDL execution stuff is correct for your installation.
Once the solve is done and our text file, darsts.txt, is written, we can start truncating with the evaluate script (Original File). This script is a little more sophisticated. First it simply reads the darsts.txt file into a python array. It then has to go through a list of DA Results objects that the user added to their model and extract the stress value wanted as well as the floor and ceiling to truncate to. For each result object requested, it then loops through all the elements truncating as needed. Then it stores the truncated values.
1: import subprocess
2: import os
3: import shutil
4: import sys
5:
6: #======================================================================
7: #
8: # ------------------------------------------------------------ PADT
9: # www.PADTINC.com
10: #
11: # da_trunc_eval.py
12: # Demonstration python script for Design Assessment in
13: # ANSYS R13
14: # Called on eval from ANSYS Mechanical
15: # Bulk of code copied and modified from TsaiWu example
16: # provided by ANSYS, Inc.
17: #
18: # NOTE: Right now it just does SX. XML and script need to be modified to allow user
19: # to specify component to use (X, Y, or Z)
20: #
21: # E. Miller
22: # 5/18/2011
23: #======================================================================
24: def trunc_eval(DesignAssessment) :
25:
26: # Change directory to current workspace for DA
27: originaldir = os.getcwd()
28: os.chdir(DesignAssessment.getHelper().getResultPath())
29:
30: # Find number of elements in DA
31: Mesh = DesignAssessment.GeometryMeshData()
32: Elements = Mesh.Elements()
33: Ecount = len(Elements)
34: print "DA number of elements is ",Ecount
35: print "Number of Result Groups:",DesignAssessment.NoOfResultGroups()
36:
37: # get User comment from Atribute Group
38: # Note: Assuems one. Need to use a loop for multiple
39: attg = DesignAssessment.AttributeGroups()
40: atts = attg[0].Attributes()
41: usercomment = atts[0].Value().GetAsString()
42: print "User Comment = ", usercomment
43:
44: # create arrays for SX/Y/Z values
45: sx = []
46: sy = []
47: sz = []
48: sxy = []
49: syz = []
50: sxz = []
51: s1 = []
52: s2 = []
53: s3 = []
54: seqv = []
55: # read file written during solve phase
56: # append stress values to SX/Y/Z arrays
57: File = open("darsts.txt","r")
58: for line in File:
59: words = line.split()
60: sx.append(float(words[0]))
61: sy.append(float(words[1]))
62: sz.append(float(words[2]))
63: sxy.append(float(words[3]))
64: syz.append(float(words[4]))
65: sxz.append(float(words[5]))
66: s1.append(float(words[6]))
67: s2.append(float(words[7]))
68: s3.append(float(words[8]))
69: seqv.append(float(words[9]))
70: File.close()
71:
72: # Loop over DA Results created by user
73: for ResultGroupIter in range(DesignAssessment.NoOfResultGroups()):
74: # Get the Result Group
75: ResultGroup = DesignAssessment.ResultGroup(ResultGroupIter)
76: # Extract Cieling and Floor for the Group
77: strscmp = ResultGroup.Attribute(0).Value().GetAsString()
78: strFloor = float(ResultGroup.Attribute(1).Value().GetAsString())
79: strCeil = float(ResultGroup.Attribute(2).Value().GetAsString())
80: print "DA Result", ResultGroupIter+1, ":", strFloor, strCeil
81:
82: #Add a set of results to store values in
83: ResultStructure = ResultGroup.AddStepResult()
84:
85: print "strscmp", strscmp
86:
87: # Loop on elements
88: for ElementIter in range(Ecount):
89: #Add a place to put the results for this element
90: ResultValue = ResultStructure.AddElementResultValue()
91: # Get the element number and then grab SX values that
92: # was read from file
93: Element = Mesh.Element(ElementIter).ID()
94:
95: if strscmp == "SX":
96: sss = sx[Element-1]
97: elif strscmp == "SY":
98: sss = sy[Element-1]
99: elif strscmp == "SZ":
100: sss = sz[Element-1]
101: elif strscmp == "SXY":
102: sss = sxy[Element-1]
103: elif strscmp == "SYZ":
104: sss = syz[Element-1]
105: elif strscmp == "SXZ":
106: sss = sxz[Element-1]
107: elif strscmp == "S1":
108: sss = s1[Element-1]
109: elif strscmp == "S2":
110: sss = s2[Element-1]
111: elif strscmp == "S3":
112: sss = s3[Element-1]
113: elif strscmp == "SEQV":
114: sss = seqv[Element-1]
115:
116: # Compare to Ceiling and Floor and truncate if needed
117: if sss > strCeil:
118: sss = strCeil
119: if sss < strFloor:
120: sss = strFloor
121: # Store the stress value
122: ResultValue.setValue(sss)
123: # Go back to the original directory
124: os.chdir(originaldir)
125:
126: trunc_eval(DesignAssessment)
127:
Some things to note about this script are:
- The same directory issues hold here. Make sure you follow them
- Always loop on ResultGroups. You can assume the number of attributes is constant but you never know how many results the user has asked for.
- In this example it is assumed that the stress label is the first attribute and that the floor and ceiling are the second and third. This is probably lazy on my part and it should be more general.
- The way to make it more general is to loop on the attributes in a group and grab their label, then use the label to determine which value it represents.
- Before you can store values, you have to create the result object and then each result value in that structure:
- ResultStructure = ResultGroup.AddStepResult() for each result object the user adds to the tree
- ResultValue = ResultStructure.AddElementResultValue() for each element
- ResultValue.setValue(sss) to set the actual value
And that is our example. It should work with any model, just make sure you get the paths right for where the files are.
Be a Good Citizen and Share!
If you have the gumption to go and try this tool out, we do ask that you share what you come up with. A good place is www.ANSYS.NET. That is the repository for most things ANSYS. If you have questions or need help, try xansys.org or your ANSYS support provider.
Happy Assessing!
You must log in to post a comment.