Difference between revisions of "Programming"
(→Memory) |
|||
Line 83: | Line 83: | ||
=== User Utility for Sparse Arrays === | === User Utility for Sparse Arrays === | ||
=== User Macro Commands === | === User Macro Commands === | ||
==== User Macro to reset element data ==== | |||
Copy one of the <code>umacrXX.f</code> files from the <code>user</code> folder to your working directory. Add the include files | |||
<pre> | |||
include 'cdat1.h' | |||
include 'pointer.h' | |||
include 'comblk.h' | |||
</pre> | |||
Set the <code>uct</code> variable to what you would like to name your macro (4 letters). Let's say | |||
<pre> | |||
uct = 'resd' | |||
</pre> | |||
We will call the macro as <code>resd,,ma,dl,dv</code> where <code>ma</code> will correspond to our material number and <code>dl</code> the data location that we will change, and <code>dv</code> the new data value we would like to store. | |||
Now after the <code>else</code> place the following code | |||
<pre> | |||
call resdsub(hr(np(25)),nint(ctl(1)),nint(ctl(2)),ctl(3)) | |||
</pre> | |||
This will pass the location of the element data arrays for all materials and our 3 command line parameters <code>ma,dl,dv</code> to the subroutine <code>resdsub</code>; the first two parameters are passed as nearest integers and the data arrays are at offset <code>np(25)</code> in <code>hr</code>. The subroutine <code>resdsub</code> should look like | |||
<pre> | |||
subroutine resdsub(d,ma,dl,dv) | |||
implicit none | |||
include 'cdat1.h' | |||
integer :: ma, dl | |||
real (kind=8) :: d(ndd,*), dv | |||
d(dl,ma) = dv | |||
end | |||
</pre> | |||
=== User Mesh Manipulation Commands === | === User Mesh Manipulation Commands === | ||
=== User Mesh Generation Commands === | === User Mesh Generation Commands === |
Revision as of 20:20, 14 October 2019
FEAP is designed to allow users to modify the code for their owm purposes. If at all possible, users should always use the user hooks provided in the user directory. These allow users to make custom elements, material models, new FEAP commands, et cetera. The programmers manual provides an introduction to programming in FEAP.
To use the user hooks, it is recommended to make a copy of the relevant stub file in a separate project directory. Then add the full path name to this edited copy to main/makefile on the OBJECTS line so that it will be built into FEAP the next time you compile it. If you are using windows, add the copy to your executable project. Even though there is a version of the stub file in your FEAP archive file, the compiler should use your edited copy instead of the one in the archive.
It is also recommended that you obtain a good Fortran reference book, e.g. Modern Fortran Explained by Metcalf, Reid, and Cohen.
Programmers Manual
The programmers manual can be found on the FEAP project site http://projects.ce.berkeley.edu/feap.
User Elements
User Interface Elements
User Memory Allocation
User Block Generation
User Body Forces
User Materials
The method to implement a user material model in FEAP has been described in Section 4.2 of FEAP Programmer Manual. Here we would like to clarify a few things in the user material model with history variables.
The time-dependent history variables
The time-dependent variables are meant to be used for quantities that change in "TIME". That is if you have a solution that advances from time to the history variables for are stored in hr(nh1)
and those for are stored in hr(nh2)
. These can be state variables in incremental stress-strain relations. In your solution algorithm, you must use the solution commands TIME
and DT
. The structure of your user material model should look like this:
1. In your umati*.f routine, set n1
equals the number of history variabales per Gauss point.
2. In your umodelf.f or umatl*.f subroutine, read the history variables from the previous globally converged time step from hn(*)
3. Compute new values of history variables
4. Write your newly computed history values to h1(*)
for next time step . Do not write to hn(*)
!
After that, FEAP will carry out a global Newton iteration. If the global Newton iteration is not converged, then the values you wrote to h1(*)
will be discarded and your routine will be called again. You will repeat again from step 2 until it is converged. Then, the values in h1(*)
will be copied over into hn(*)
for use in the next time step when you execute the next TIME
command.
The time-independent history variables
If the history variable does not depend in time then we consider them to be independent of "TIME", so you could use the time-independent history variables as it only stores one copy of each value instead of two in the previous case. This type of history variable is more like a status variable depending on deformation. Here is the procedure to implement such kind of history variables,
1. In your umati*.f routine, set n3
equals the number of history variables per element.
2. In your umodelf.f or umatl*.f subroutine, read the history variables from the previous globally converged time step from hr(nh3+i)
where i
runs from 0 to the number of history variables (per gauss point) minus one and nh3
points to the location of first history variable for the current Gauss point.
3. Compute new values of history variables.
4. Update history variables in hr(*)
with the newly computed values if needed.
Initialization of history variables in element or materials routines
The history variables are initialized to zero by default. But if you want to initialize any of the history variables to non-zero values you can do this when ISW .eq. 14
. For example, if we have m
history variables per gauss point, then you can initialize the time-dependent history variables to 1.0d0
in your umodelf.f or umatl*.f subroutine as follows,
c... INITIALIZE HISTORY VARIABLES IF (ISW .EQ. 14) THEN do i = 1, m hn(i) = 1.0d0 h1(i) = 1.0d0 end do END IF
For time-independent history variables,
c... INITIALIZE HISTORY VARIABLES IF (ISW .EQ. 14) THEN do i = 0, (m - 1) hr(nh3 + i ) = 1.0d0 end do END IF
This segment of code will be executed for each Gauss point of each element.
(Note that this page is under development)
User Debugging File Outputs
User Transient Algorithms
User Export of Tangent and Residual for Code Coupling
Set User Data for Element Plotting
Assembling User Contributions to the Tangent and RHS
User Functions
Global User Parameters
Set User Boundary Codes
User Displacement Imports
User Utility for Sparse Arrays
User Macro Commands
User Macro to reset element data
Copy one of the umacrXX.f
files from the user
folder to your working directory. Add the include files
include 'cdat1.h' include 'pointer.h' include 'comblk.h'
Set the uct
variable to what you would like to name your macro (4 letters). Let's say
uct = 'resd'
We will call the macro as resd,,ma,dl,dv
where ma
will correspond to our material number and dl
the data location that we will change, and dv
the new data value we would like to store.
Now after the else
place the following code
call resdsub(hr(np(25)),nint(ctl(1)),nint(ctl(2)),ctl(3))
This will pass the location of the element data arrays for all materials and our 3 command line parameters ma,dl,dv
to the subroutine resdsub
; the first two parameters are passed as nearest integers and the data arrays are at offset np(25)
in hr
. The subroutine resdsub
should look like
subroutine resdsub(d,ma,dl,dv) implicit none include 'cdat1.h' integer :: ma, dl real (kind=8) :: d(ndd,*), dv d(dl,ma) = dv end
User Mesh Manipulation Commands
User Mesh Generation Commands
User Plotting Commands
User Mass Allocation
User Problem Inputs
Setting User Additions to the Profile
User Proportional Load
User Rotational Updates
Custom User Banner Messages
User Solvers
Memory
Basics
FEAP's memory management system utilizes an offset technique. There are two (short arrays) hr( )
of type real*8 and mr( )
of type integer, which are both defined in the common block comblk.h
. When FEAP arrays are allocated they are done so using the system malloc
. This is either done in the source file unix/memory/cmem.c
, if on Linux/MAC, or in the source file windows/memory/setmem.f
, if on windows.
For each FEAP array that is allocated the offset to its location in memory in units of real*8 or integer chunks of memory is stored in the array np( )
. Thus for the DR array, array number 26, which holds the residual, its first element is equivalent to hr(np(26))
. For integer arrays like IE, array number 32, which holds the element assembly information, its first element is equivalent to mr(np(32))
. In this way FEAP provides a functionality to access all of its internal arrays through a combined use of the common blocks comblk.h
and pointer.h
, which contains the np( )
array.
Checksumming FEAP arrays
When chasing memory errors under Linux/MAC there are two utility functions that can be helpful. In particular
call cmemumark(np(xx),precision,ipr)
computes and stores a checksum of the data associated with array xx (set xx to the number of the array you are interested in, set precision to either 1 for integer arrays or 2 for reals, set ipr to 1 for 8 byte integers and 2 for 4 byte integers). You can then check the integrity of the data relative to this checksum at any other place in the code with
call cmemucheck(np(xx),precision,ipr,result)
where result
is an integer =1 for no change and =0 if the data has changed.
Checksumming FEAP array headers
Each FEAP array when allocated is also given a header block, the integrity of the header data to each FEAP array can be checked with
call cmemcheck(np(xx),precision,ipr)
GDB is your friend
Sometimes data gets overwriting in strange ways and it is very hard to track down when certain arrays are being corrupted. The very best way to debug such problems is to use GDB (or a similar debugger). Make sure that you have compiled the code with the option to generate symbol tables -g
on Linux/MAC systems. Then start the code in the debugger gdb feap
, set breakpoints and step/continue to see what is going on.
A really useful feature is to use the watch command to allow the program to stop whenever a particular memory address is changed. Suppose I am interest on when the first element of the residual array DR (array 26) changes. I would do the following
(gdb) break pnewprob_
to first set a break point in pnewprob where DR is allocated.
(gdb) run
to get going. Then when the program stops
(gdb) break +646
to set a break point after the point where DR has been allocated. Alternately your debugger may accept
(gdb) break pnewprob.f:646
Then,
(gdb) run
Then
(gdb) p np(26) (gdb) p &hr(1)
to get the offset to DR and the memory address of hr(1). Suppose these were 195297 and 0xd1ba60. Now compute the address of the first element of DR as 195297*8 + 0xd1ba60, the multiplier is 8(bytes) since DR is a double precision real array. Note 195297*8 = 1562376 = 0x17d708. Adding to the base address we get 0xe99168. Now set a watch on this memory location and continue. The program will stop whenever this address location is altered.
(gdb) watch *(double *)0xe99168 (gdb) c
See the GDB manual watchpoint section for additional details.
Valgrind is incredible
A second very valuable debugging tool is Valgrind. This amazing tool can track down virtually any memory issue you may have with amazing efficiency. Your code will run quite slow in valgrind, BUT it will help you locate error very fact. Just install from the web site or from your package manager (yum, apt, dnf, port, fink, etc.). To run simply type
valgrind feap
Make sure that you have compiled your code with the symbols, -g
.