There are programming languages far, far worse than Visual Basic.
Only the truly insane/skilled would avoid ANSYS Workbench nowadays for pre-processing complex assemblies; However, for certain results it is still necessary to do some post-processing in ANSYS Mechanical APDL (otherwise know as “Classic”) and one such instance is averaging results over a surface, etc.
This example assumes a Named Selection / Component called
Bearing_Faces which doesn’t have to be a continuous surface, i.e. it can be a collection of a number of surfaces. There are lots of comments since I’d like to be able to use this as a reference and APDL is quite abstruse. One thing to note about APDL is that rather than doing
foo = bar(baz) ANSYS typically works like
bar,foo,baz, but that doesn’t mean all commands work like this.
Firstly get the force and area data for the elements on these surfaces into an Element Table:
finish /post1 ! Assumes we are using the final load step data set,last ! Select nodes on face(s) cmsel,s,Bearing_Faces ! Select elements of nodes esln ! Select just the contact elements, identified by the element type number esel,r,ename,,174 ! Get contact pressure for each element into a table etable,contpres,cont,pres ! Get area (it is area and not volume since these are contact/surface elements) etable,volume,volu ! Add a column for the force by multiplying the pressure by area smult,force,contpres,volume
As an initial go at this we can assume the full area of
Bearing_Faces is actually in contact (i.e. every area has a force/pressure) and average the force over that:
! Need to issue this so the following `*get`s work ssum *get,total_force,ssum,0,item,force *get,total_area,ssum,0,item,volume avg_pressure=total_force/total_area
To do things more accurately, really we should only average the forces over areas that are actually in contact. And here we really get into the mess that is ANSYS ADPL. It should be simple to process the Etable data; However, it is probably quicker to export it and process externally, e.g. Excel or R, but for the sake of example we’ll do it all in APDL.
It seems you can’t simply loop over an element table, so what we can try to do is loop over the range of elements in the table (so we may hit elements not in the table due to discontinuous ranges). Note, that as far as I can tell
*get, maxelem, etab,,nleng,max should get the largest element in the table, but instead just seems to get the largest element number overall (perhaps a bug in APDL?) so instead rely on the current element selection:
! Reset these total_force = 0 total_area = 0 ! Get smallest and largest element numbers from the current selection *get,maxelem,elem,,num,max *get,minelem,elem,,num,min ! Loop over that range *do,elemnum,minelem,maxelem,1 ! Get individual elem's area ! Note to self: "etab, 2" is 2nd column. We got area after pressure above so it is the second column in the element table *get,volume,etab,2,elem,elemnum ! Get individual elem's contact pressure *get,contpres,etab,1,elem,elemnum ! If the pressure is non-zero (absolute value is greater than zero) *if,contpres,abgt,0,then total_force = total_force+contpres*volume total_area = total_area+volume *endif *enddo avg_pressure=total_force/total_area
This seems like a good idea. But what happens if ANSYS tries to
*get a result for an element number not in the table due to the discontinuous range? You’d hope it’d get nothing or return
0, but NOPE! it seems it just returns the results for the last valid entry retrieved instead. In my mind this has got to be a bug, it’s just crazy behaviour.
So lets move away from element tables and use an array instead. Note, we still need the element tables, but we’ll just move the data to something more sane:
! Get the maximum element number. We already defined this above, but we'll redefine for completeness *get,maxelem,elem,,num,max ! Create two-column array to store results, bigger than needed because of the way data will be copied to it *dim,earray,array,maxelem,2 ! Create a mask, used to filter data we put into the array *dim, eselmask, array, maxelem ! Need to fill in the mask with zeroes and ones. Ones for data we want, zeroes for data we don't want ! Generate the mask from the selected elements ! This will actually fill with -1s (unselected elements), 0s (undefined) and 1s (selected) *vget,eselmask(1,1),elem,,esel ! Change the -1s to 1 and the 0s to 1s using the Not function; Values less than 0 become 1, values greater become 0 *vfun,eselmask(1,1),not,eselmask ! And again to invert so correct *vfun,eselmask(1,1),not,eselmask ! Apply the mask so we only pull in data for the elements we want *vmask,eselmask(1) ! Get the area data from the element table to the first column of the array *vget,earray(1,1),elem,1,etab,volume ! vmask only applies to the subsequent operation so have to reapply it each time *vmask,eselmask(1) ! Get the force data from the element table to the second column of the array *vget,earray(1,2),elem,1,etab,force ! We now have the element table data, but we still need to account for areas not in contact ! Create another mask to use *dim, contactmask, array, maxelem ! Apply the existing mask when filling the new mask *vmask,eselmask(1) ! Fill the new mask with the contact pressures *vget,contactmask(1,1),elem,1,etab,contpres ! Use the not trick again so that any elements without a pressure (and therefore not in contact) will be masked *vfun,contactmask(1,1),not,contactmask *vfun,contactmask(1,1),not,contactmask ! Apply the contact mask *vmask,contactmask(1) ! Calculate the total area for elements in contact *vscfun,total_area,sum,earray(1,1) ! Calculate the total force. No need to mask again as there can't be a force if there is no contact *vscfun,total_force,sum,earray(1,2) ! Finally, calculate the average bearing pressure avg_pressure=total_force/total_area
An interlude on masks, since I know for sure I’m going to forget how they work. Masks help us deal with discontinuous ranges. Say we have a list of elements like so:
2 3 4 7 8 9
Well, when we build an array we get:
0 1 2 3 4 5 6 7 8 9
A mask allows us to mask out element data we don’t need:
Array Mask Results 0 --> 0 1 --> 0 2 --> 1 --> 2 3 --> 1 --> 3 4 --> 1 --> 4 5 --> 0 6 --> 0 7 --> 1 --> 7 8 --> 1 --> 8 9 --> 1 --> 9
The above array and mask approach can be streamlined (slightly) by getting one mask correct upfront and using that before pulling in the element table data:
*get,maxelem,elem,,num,max *dim,earray,array,maxelem,2 ! Create three masks *dim, eselmask, array, maxelem *dim, contactmask, array, maxelem *dim, finalmask, array, maxelem ! Fill the esel mask *vget,eselmask(1,1),elem,,esel *vfun,eselmask(1,1),not,eselmask *vfun,eselmask(1,1),not,eselmask ! Fill the contactmask *vmask,eselmask(1) *vget,contactmask(1,1),elem,1,etab,contpres *vfun,contactmask(1,1),not,contactmask *vfun,contactmask(1,1),not,contactmask ! Create the final mask by combining those two masks (Multiply the 0s and 1s together) *voper,finalmask, contactmask, mult, eselmask ! Apply final mask and fill the element array *vmask,finalmask(1) *vget,earray(1,1),elem,1,etab,volume *vmask,finalmask(1) *vget,earray(1,2),elem,1,etab,force ! Note: Since we *vmasked the *get we don't need to vmask the *vscfun *vscfun,total_area,sum,earray(1,1) *vscfun,total_force,sum,earray(1,2) avg_pressure=total_force/total_area
Actually, we can go one step further and re-use a mask:
*get,maxelem,elem,,num,max *dim,earray,array,maxelem,2 ! Create one mask *dim, elemmask, array, maxelem ! Fill the mask to match selected elements *vget,elemmask(1,1),elem,,esel *vfun,elemmask(1,1),not,elemmask *vfun,elemmask(1,1),not,elemmask ! Apply that mask, but also re-use *vmask,elemmask(1) *vget,elemmask(1,1),elem,1,etab,contpres *vfun,elemmask(1,1),not,elemmask *vfun,elemmask(1,1),not,elemmask ! Apply mask and fill the element array *vmask,elemmask(1) *vget,earray(1,1),elem,1,etab,volume *vmask,elemask(1) *vget,earray(1,2),elem,1,etab,force ! Note: Since we *vmasked the *get we don't need to vmask the *vscfun *vscfun,total_area,sum,earray(1,1) *vscfun,total_force,sum,earray(1,2) avg_pressure=total_force/total_area
Which is still way more verbose than the approach used assuming all areas are in contact, but is at least more accurate.
[EDIT: 2016-01-18] I originally had the last section of code marked as “probably works” as I hadn’t tested re-using the mask, but I’ve since checked and this does work.