During our webinar held at noon on 9/27/2012 we promised to provide a link to the recording, a PDF of the PowerPoint, and some answers to a few questions. Here is that information:
State Variables
I was a bit confusing on state variables. The problem is with my use of them, not with the variables. The test model only had one integration point. My code is still not working right, the default USERMAT is overwriting my flag somewhere and I don’t have time to figure it out. It’s killing me but I have to do some real work.
But anyhow, my assertion that the state variables are per integration point is correct.
Debugging
I am not aware of any way to use a debugger with ANSYS. There is nothing in the documentation, and to be honest, I’ve not used a real debugger in years. So there may be a way to do so, and see your routine in the debugger since you have the source code, but I have no idea on how to do that. Perhaps someone with more debugging experience can comment below.
Other UPF’s
Someone asked about other routines that are available and we ran out of time before I could go over them. Here is a list.
ELEMENTS | |
UserElem.f | User Defined Element that use newer API |
UEL100.f – UEL105.f UEC100.f – UEC105.f UEX100.f – UEX105.f UEP100.f-UEP105.f USERTR.f USERAC.f | User elements defined that access the program database directly |
USEROU.f | Stores user-provided element output |
USERAN.f | Modify orientation of material properties |
USERRC.f | COMBIN37 (control/thermostat/spring/damper/resistor) user routine. |
UEIMatx.f | Access to an elements matrix or load vector |
UTHICK.f | Sets thickness at integration points |
UsrFictive | Sets “fictive” temperature (I have no idea what that is) |
UFLEX.f | Calculates pipe flexibility for PIPE288/289 |
UsrShift.f | Allows user to specify time shift |
Materials | |
UserMat.f | User material models |
UserHyper.f | User defined hyperelasticity models |
UserCreep.f | User defined creep model |
user_tbelastic.f | Allows definition of elastic stiffness at a given integration point based on user model. TB,ELASTIC,,,,USER |
USERFC.f | User defined failure criteria |
USERSWTRAIN.f | User defined swelling, for TB, SWELL,,,,USER |
USERCK.f | Helper routine that passes material properties for a user material in |
USERFRIC.f | User defined friction calculation. Not just friction but all values calculated in contact calculations with friction turned on. |
LOADS | |
USERFL.f | Changes scalar field values (temp, fluence, heat generation, moisture content, magnetic virtual displacement), by element. |
USERPR.f | Calculates element pressure, by element |
USERCV.f | Calculates element face convection. |
USERFX.f | Calculates element face heat flux |
USERCH.f | Calculates element face charge density surface values |
USERFD.f | Computes complex load vectors for frequency domain logic |
USERPE.f | Calculate the rotation of an elbow pipe element caused by internal pressure |
USRSURF116.f USER116Cond.f USER116Hf.f | Modifies the conduction, film coefficient, bulk temp for SURF151/152 |
userPartVelAcc.f | Ocean wave particle acceleration calculation for PIPE288/289 |
userPanelHydFor.f | Calcs hydrodynamic loading on SURF164 from ocean loading |
USER Commands | |
USER01.f-USER10.f | Create your own ANSYS commands that are accessed through /UCMD,cmd,num where num refers to the subroutine number and cmd is the command name you want to assign it. Put this in your startxx.ans file to give regular access. |
Nested Tables
As you will remember from memorizing the previous article, a common use for tables is the set them up to give you a value for a given “primary variable” that is determined by the solver at a given point in the solution. Possible primary variables are: TIME, FREQ, X, Y, Z, TEMP, VELOCITY, PRESSURE and SECTOR. But what if you want to use one of those primary variables to look up a value, then use that value to then interpolate a second value?
A good example is that you have a piece of rotating equipment and the value of the heat transfer coefficient (HF) is a function of RPM and the radius of a given element face. But RPM varies over time. What you can do is make the HF table point to and RPM table that is based on the primary variable time:
*DIM,MYCNV,TABLE,3,3,,RPM,X,,1
*taxis,mycnv(1,1),1,0,1000,20.01e6
*taxis,mycnv(1,1),2,0,1,2
mycnv(1,1) = .25,4,10
mycnv(1,2) = .35,7,15
mycnv(1,3) = .45,10,28
*DIM,RPM,TABLE,4,1,1,TIME
RPM(1,0)=0.0,10.0,40.0,60.0
RPM(1,1)=0.0,5.0,20.0,30.0
wrttbl,'mycnv','foo4.txt'
wrttbl, 'rpm','foo5.txt'
SF,ALL,CONV,%mycnv%
This macro is missing stuff, like a model and selecting the nodes to apply the SF command to.
The tables look like this:
Table: mycnv RPM vs X
| 0.000 1.000 2.000
|------------------------------
0.000 |0.2500 0.3500 0.4500
1000. | 4.000 7.000 10.00
0.2001E+08| 10.00 15.00 28.00
and
Table: rpm TIME vs
|0.7889E-30
|----------
0.000 | 0.000
10.00 | 5.000
40.00 | 20.00
60.00 | 30.00
(We’ll cover the wrttbl macro below.)
So at a given substep, the program will take time and figure out what RPM needs to be. Then it will use RPM and the radius (X in CSYS 1) to figure out the convection coefficient for each node.
As you can imagine, you can get pretty sophisticated with this. The key is that the name of the table you use for the calculated value is input into the variables to interpolate on for the second table, using the *DIM command.
Another common use is scaling tables based on some value. Let say you have a pressure table and the total pressure is scaled over time, based on time. You would make a pressure table that is dependent on say X and y. It would have two planes. One with 0 values and one with the max values. Then you would make a scale table that scales from 0 to 1 based on time. It would look like this:
*DIM,pscl,table,5,,,time !Row label is CPTAB, the table of Cps
*taxis,pscl(1,1),1,0,1,5,10,30
pscl(1,1) = .25,.5,1,1,.333
*DIM,ptab,TABLE,4,4,2,X,Y,pscl
*taxis,ptab(1,1),1,0,1.35,2.75
*taxis,ptab(1,1),2,-7.2,-3.2,6.5,10.6
*taxis,ptab(1,1),3,0,1
ptab(1,1,1) = 0,0,0,0
ptab(1,2,1) = 0,0,0,0
ptab(1,3,1) = 0,0,0,0
ptab(1,4,1) = 0,0,0,0
ptab(1,1,2) = 72,48,97,123
ptab(1,2,2) = 53,48,88,98
ptab(1,3,2) = 43,38,77,88
ptab(1,4,2) = 33,28,55,77
SF,ALL,PRESS,%ptab%
As always with tables, double check things and make sure you have your rows and columns correct. Start simple, and then add more detail. Testing out on a 2x2 or 3x3 tables is a good way to start.
4 and 5 Dimension Arrays and Tables
Most users will simply use a one, two, or even three dimension array or table (row, column, plane). However, both arrays and tables support two more dimensions: books and shelves. Because this capability is a later addition to the program, it behaves a little differently. You need to add values for the size of the book (KMAX) and the shelf (MMAX) as well as variable names for each: VAR4 and VAR5
The first difference is in the *DIM command. For normal arrays and tables you use:
*DIM, Par, ARRAY, IMAX, JMAX, KMAX, Var1, Var2, Var3, CSYSID
*DIM, Par, TABLE, IMAX, JMAX, KMAX, Var1, Var2, Var3, CSYSID
*DIM,Par,ARR4,IMAX,JMAX,KMAX,LMAX,Var1,Var2,Var3,Var4,CSYSID
*DIM,Par,TAB4,IMAX,JMAX,KMAX,LMAX,Var1,Var2,Var3,Var4,CSYSID
For 5 dimension arrays or tables you use:
*DIM,Par,ARR5,IMAX,JMAX,KMAX,LMAX,MMAX,Var1,Var2,Var3,Var4,Var5,CSYSID
*DIM,Par,TAB5,IMAX,JMAX,KMAX,LMAX,MMAX,Var1,Var2,Var3,Var4,Var5,CSYSID
It is important to be aware of this because if you look at the manual entry for *DIM it only lists the 3 dimension version of the command, and these variations are covered in the notes.
Once the array or table is defined you have to fill it using APDL commands, this size is not supported in the user interface. The same commands are used, but instead of supplying one, two or three indices values, you supply four or five.
The following is an example of defining a table in terms of location (X,Y,Z), Time, and Temperature. This is the most common usage of a five dimension table:
*dim,ldval,tab5,3,3,3,3,3,X,Y,Z,TIME,TEMP ! table
*taxis,ldval(1,1,1,1,1),1,-2.3,0,3.4 ! X Range
*taxis,ldval(1,1,1,1,1),2,-1.2,0,1.8 ! Y Range
*taxis,ldval(1,1,1,1,1),3,-3.6,0,4.5 ! Z Range
*taxis,ldval(1,1,1,1,1),4,0,5,10 ! Time Range
*taxis,ldval(1,1,1,1,1),5,32,320,500 ! Temp Range
*do,ii,1,3
*do,jj,1,3
*do,kk,1,3
*do,ll,1,3
*do,mm,1,3
!silly made up equation to fill the table with
ldval(ii,jj,kk,ll,mm) = ii*.123+jj/.2+ll*kk+mm*JJ*JJ
*enddo
*enddo
*enddo
*enddo
*enddo
Writing a Table to a File
For simple 2D tables with up to 10 columns, I use a cheesy macro I wrote called wrttbl.mac. It was used above. It is a bit of a brute force method, because it has code blocks for from 0 to 10 columns. A more general approach would build the actual *VWRITE commands with *VWRITES… It should also be expanded to do Planes. Maybe for a future article.
Anyhow, here it is, maybe you will find it useful.
ttbl = arg1
fname = arg2
*get,nrw,parm,%ttbl%,dim,X
*get,ncl,parm,%ttbl%,dim,Y
*get,xax,parm,%ttbl%,var,1
*get,yax,parm,%ttbl%,var,2
nmcl = nint((ncl*10)/2)
nmrw = nint(nrw/2)
*cfopen,%fname%
*vwrite,ttbl,xax,yax
('Table: ',A,' ',A,' vs ',A)
*vwrite,
(x)
*if,ncl,eq,1,then
*vlen,1
*vwrite,%ttbl%(0,1)
(10x,'|',10g10.4)
*vwrite
(10x,'|',10('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii ,1)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,2,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2)
(10x,'|',10g10.4)
*vwrite
(10x,'|',20('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,3,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3)
(10x,'|',10g10.4)
*vwrite
(10x,'|',30('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,4,then
/gopr
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3),%ttbl%(0,4)
(10x,'|',10g10.4)
*vwrite
(10x,'|',40('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3),%ttbl%(ii,4)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,5,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3),%ttbl%(0,4),%ttbl%(0,5)
(10x,'|',10g10.4)
*vwrite
(10x,'|',50('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3),%ttbl%(ii,4),%ttbl%(ii,5)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,6,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3),%ttbl%(0,4),%ttbl%(0,5),%ttbl%(0,6)
(10x,'|',10g10.4)
*vwrite
(10x,'|',60('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3),%ttbl%(ii,4),%ttbl%(ii,5),%ttbl%(ii,6)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,7,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3),%ttbl%(0,4),%ttbl%(0,5),%ttbl%(0,6),%ttbl%(0,7)
(10x,'|',10g10.4)
*vwrite
(10x,'|',70('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3),%ttbl%(ii,4),%ttbl%(ii,5),%ttbl%(ii,6),%ttbl%(ii,7)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,8,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3),%ttbl%(0,4),%ttbl%(0,5),%ttbl%(0,6),%ttbl%(0,7),%ttbl%(0,8)
(10x,'|',10g10.4)
*vwrite
(10x,'|',80('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3),%ttbl%(ii,4),%ttbl%(ii,5),%ttbl%(ii,6),%ttbl%(ii,7),%ttbl%(ii,8)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,9,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3),%ttbl%(0,4),%ttbl%(0,5),%ttbl%(0,6),%ttbl%(0,7),%ttbl%(0,8),%ttbl%(0,9)
(10x,'|',10g10.4)
*vwrite
(10x,'|',90('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3),%ttbl%(ii,4),%ttbl%(ii,5),%ttbl%(ii,6),%ttbl%(ii,7),%ttbl%(ii,8),%ttbl%(ii,9)
(g10.4,'|',10g10.4)
*enddo
*endif
*if,ncl,eq,10,then
*vlen,1
*vwrite,%ttbl%(0,1),%ttbl%(0,2),%ttbl%(0,3),%ttbl%(0,4),%ttbl%(0,5),%ttbl%(0,6),%ttbl%(0,7),%ttbl%(0,8),%ttbl%(0,9),%ttbl%(0,10)
(10x,'|',10g10.4)
*vwrite
(10x,'|',100('-')) |
*do,ii,1,nrw
*vlen,1
*vwrite,%ttbl%(ii,0),%ttbl%(ii,1),%ttbl%(ii,2),%ttbl%(ii,3),%ttbl%(ii,4),%ttbl%(ii,5),%ttbl%(ii,6),%ttbl%(ii,7),%ttbl%(ii,8),%ttbl%(ii,9),%ttbl%(ii,10)
(g10.4,'|',10g10.4)
*enddo
*endif
*cfclose