Allocatable Arrays

To facilitate data accumulation and transfer between user subroutines, you can use utility functions to create your own dynamic storage in the form of allocatable arrays. Thread-local and global arrays are supported. In addition to basic types, you can also vary the precision of real arrays according to the precision of Abaqus/Explicit and define arrays of user-defined data types.

This page discusses:

SMALocalIntArrayCreate, SMALocalFloatArrayCreate

You can create any number of thread-local or global arrays. You give each array an identifier (an arbitrary positive integer) at the time of its creation. You create an array in one user subroutine and reference it in another simply by its identifier. The arrays persist in memory until you explicitly delete them or until the analysis terminates.

Thread-local arrays

A thread-local array is a mechanism to allocate storage that is local to a thread and does not need any locking for access. In a multi-threaded environment the thread safety of these arrays stems from their design and usage: they are deliberately not shared and, thus, do not need to be protected from competing threads. In fact, one thread cannot reference a local array of another thread. They can be accessed concurrently without any locking and, thus, are faster than global arrays.

Thread-local arrays are unique in each thread. They are nonintersecting and nonoverlapping in memory, with each thread starting out with its own private copy of an array. For example, Thread 0 can have a local array with ID 1 and Thread 4 can have a local array with ID 1. Those two arrays are different and separate from each other. Similarly, it is possible to have an integer array with ID 1 and a float array with ID 1. Again, they are two different arrays. It is not possible to cross-reference these arrays across different threads. However, all user subroutines running in one thread can access all arrays of that thread. In a thread-agnostic way, these arrays are shared between user subroutines but not among threads. These routines are meant as a thread-safe replacement for COMMON BLOCKs and SAVE variables.

The following utility subroutines are available to operate on thread-local arrays:

  • SMALocalIntArrayCreate, SMALocalFloatArrayCreate: to create or resize a local array.

  • SMALocalIntArrayAccess, SMALocalFloatArrayAccess: to locate an existing local array.

  • SMALocalIntArrayDelete, SMALocalFloatArrayDelete: to delete a local array.

  • SMALocalIntArraySize, SMALocalFloatArraySize: to get the size of the array.

These utility routines are accessible from both Fortran and C/C++. The details of their interfaces are described below.

Global arrays

Global arrays are visible and accessible from all threads in an executable. To prevent race conditions, protect the creation and the write access to these arrays with mutexes (mutual exclusion locks). You can have each thread execute global array creation under a mutex protection. However, only the first thread to arrive will create the global array; the later threads will simply connect to the array already created. In addition, using mutexes on every write access will incur a performance penalty. In some situations it is possible to avoid unnecessary locking by restricting all threads to operate on nonintersecting ranges of a global array. Another alternative is to use thread-local arrays.

The following utility routines are available to operate on global arrays:

  • SMAIntArrayCreate, SMAFloatArrayCreate: to create or resize a global array.

  • SMAIntArrayAccess, SMAFloatArrayAccess: to locate an existing global array.

  • SMAIntArrayDelete, SMAFloatArrayDelete: to delete a global array.

  • SMAIntArraySize, SMAFloatArraySize: to get the size of the global array.

These arrays are global and accessible from all threads within a process but not across different MPI processes. To share data between separate MPI processes, MPI facilities must be used. Abaqus supports the full use of MPI within user subroutines.

Utility Routine Interface

Fortran:

      INTEGER*8 SMALocalIntArrayCreate(ID,SIZE,INITVAL)
      INTEGER*8 SMALocalFloatArrayCreate(ID,SIZE,INITVAL)

Example:

#include <SMAAspUserSubroutines.hdr>

      integer a(100)
      pointer(ptra,a)

      real*8 b(*)
      pointer(ptrb,b)

      ! create a local array with ID=1 and SIZE=100
      ptra = SMALocalIntArrayCreate(1,100) 
      a(1) = 11  ! use as a native Fortran array
      a(2) = 22  ! use as a native Fortran array

      ! create a local float array with ID=1 and SIZE=100, and
      ! initial value = -1.0
      ptrb = SMALocalFloatArrayCreate(1,100,-1.0) 

C++: 

       #include <SMAAspUserSubroutines.h>

       // Create a local integer array of with ID=1 and size=100
       int* a = SMALocalIntArrayCreate(1,100); 

       // Create a local float array of with ID=1, size=20, and 
       // initial value = -1.0
       real* b = SMALocalFloatArrayCreate(1,100,-1.0);   

NOTE: Float Arrays can store both SINGLE PRECISION 
      and DOUBLE PRECISION numbers. Internally, 
      memory is allocated in units of 64 bits (double/real*8).

NOTE: To resize an array, simply call Create() with the same ID, 
      but give it a new SIZE parameter. If the new size is larger, 
      the old data are copied over to the new array. No data are lost 
      during resizing.

For example:

      ! resize array with ID=1 to 300 integers
      ptra = SMALocalIntArrayCreate(1,300)  

NOTE: In Create() functions, there is an optional third
      argument -- initial value. If not supplied, all Int 
      arrays are initialized with INT_MAX ( 2,147,483,647 ). 
      All Float Arrays are initialized with Signaling NANs.  
      The values of INT_MAX and signaling NANs are accessible 
      via the 'SMAAspNumericLimits.h' and 'SMAAspNumericLimit.hdr' 
      header files.

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

SIZE

Size of the array as the number of ints or doubles. The maximum size for thread-local arrays is INT_MAX (2,147,483,647).

INITVAL

Initial value for each item in the array. If this argument is not supplied, in the case of an integer a large value is used; in the case of a float NAN is used.

Variables Returned from the Utility Routine

INTEGER*8 ( address )

Returns a pointer to the array created. This pointer can be associated with a native Fortran array or native C/C++ array. Each thread will receive a different pointer. Each thread will create and hold its own array. For example, Array(1) in Thread 0 is separate from Array(1) in Thread 4. These arrays are nonoverlapping and nonintersecting in any way.

SMALocalIntArrayAccess, SMALocalFloatArrayAccess

Utility Routine Interface

Fortran interface:

      INTEGER*8 SMALocalIntArrayAccess(ID)
      INTEGER*8 SMALocalFloatArrayAccess(ID)

Example:

#include <SMAAspUserSubroutines.hdr>

      integer a(100)
      pointer(ptra,a)

C Locate local Array(1) and associate a native array pointer with it

      ptra = SMALocalIntArrayAccess(1) 
      
      a(1) = 11 ! use as a native Fortran array
      a(2) = 22 ! use as a native Fortran array



C++ interface: 

      #include <SMAAspUserSubroutines.h>

      // Locate and open array with ID=1
      int* a =  SMALocalArrayIntAccess(1);
     
      a[1] = 11;  // use as a native array
      a[2] = 22;  // use as a native array

NOTE: If a request is made to access an array that has 
      not been created, the function will return 0.      

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

Variables Returned from the Utility Routine

INTEGER*8 ( address )

Returns a pointer to the array created. This pointer can be associated with a native Fortran array or native C/C++ array. Each thread will receive a different pointer. Each thread, as it passes through this code, will create and hold its own array. For example, Array(1) in Thread 0 is a separate array from Array(1) in Thread 4. These arrays are nonoverlapping and nonintersecting in any way.

SMALocalIntArraySize, SMALocalFloatArraySize

Utility Routine Interface

Fortran interface:

      INTEGER*4 SMALocalIntArraySize(ID)
      INTEGER*4 SMALocalFloatArraySize(ID)

Example:

#include <SMAAspUserSubroutines.hdr>

      integer a_size, d_size

C Get the size of Array(1) as the number of INTEGERs
      a_size = SMALocalIntArraySize(1)   

! Get the size of Array(1) as the number of REALs

      d_size = SMALocalFloatArraySize(1) 

      do k=1,a_size
          ... 
      end do

C++:

      #include <SMAAspUserSubroutines.h>

      // Lookup the size of Array(1) as the number of ints

      int a_size = SMALocalIntArraySize(1);   

      // Lookup the size of Array(1) as the number of doubles
      
      int d_size = SMALocalFloatArraySize(1); 

      for(int i=1; i<=size; i++) {
          ...
      }

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

Variables Returned from the Utility Routine

INTEGER*4

Size of the array.

SMALocalFloatArrayDelete, SMALocalFloatArrayDelete

Utility Routine Interface

Fortran interface:

      subroutine SMALocalIntArrayDelete(ID)
      subroutine SMALocalFloatArrayDelete(ID)

Example:

#include <SMAAspUserSubroutines.hdr>

      call SMALocalIntArrayDelete(1) ! Delete Array(1)

C++ interface:

      #include <SMAAspUserSubroutines.h>

      SMALocalIntArrayDelete(1);  // Delete Array(1)

NOTE: Deletion of arrays is optional. All storage allocated 
      for these arrays will be freed when Abaqus threads 
      terminate (at the very end of the analysis). It is, 
      however, a good programming practice to delete all
      allocations explicitly, especially when they are
      no longer needed, as this will free up memory for 
      something else.

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

SMAIntArrayCreate, SMAFloatArrayCreate

Utility Routine Interface

Fortran interface:

      INTEGER*8 SMAIntArrayCreate(ID,SIZE,INITVAL)
      INTEGER*8 SMAFloatArrayCreate(ID,SIZE,INITVAL)

Example:

#include <SMAAspUserSubroutines.hdr>

      integer a(100)
      pointer(ptra,a)
      double precision b(100)
      pointer(ptrb,b)

      ! create a global array with ID=1, SIZE=100, and
      ! INITVAL=-1.0
      ptra = SMAIntArrayCreate(1,100,-1) 

      a(1) = 11  ! use as a native Fortran array
      a(2) = 22  ! use as a native Fortran array

      ! create a global array with ID=2, SIZE=100, and
      ! INITVAL=-1.0
      ptrb = SMAFloatArrayCreate(2,100,-1.0) 

C++ interface: 

       #include <SMAAspUserSubroutines.h>

       // Create an integer array of with ID=1, size=100, 
       // and initial value=-1.0
       int* a = SMAIntArrayCreate(1,100,-1); 

       // Create a float array of with ID=2, size=20, 
       // and initial value=-1.0
       Real* b = SMAFloatArrayCreate(2,20,-1.0);   

NOTE: Float Arrays can store both SINGLE PRECISION and 
      DOUBLE PRECISION numbers. Internally, they
      allocate storage in 64-bit units (double/real*8).

NOTE: To resize an array, simply call Create() with the same ID, 
      but give it a new SIZE parameter. If the size has increased, 
      the old data will be copied over to the new array. 
      No data is lost during resizing.


For example:

      ! resize array with ID=1 to 300 integers
      ptra = SMAIntArrayCreate(1,300,-1)  

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

SIZE

Size of the array as the number of ints or doubles. The maximum size is INT_MAX.

INITVAL

Optional initial value for each item of the array. If this argument is not supplied, integer arrays are filled with INT_MAX and float arrays are filled with NANs.

Variables Returned from the Utility Routine

INTEGER*8 ( address )

Returns a pointer to the array created. This pointer can be associated with a native Fortran array or native C/C++ array. All threads with see the same address when they try to access this array through its ID.

SMAIntArrayAccess, SMAFloatArrayAccess

Utility Routine Interface

Fortran interface:

      INTEGER*8 SMAIntArrayAccess(ID)
      INTEGER*8 SMAFloatArrayAccess(ID)

Example:

#include <SMAAspUserSubroutines.hdr>

      integer a(100)
      pointer(ptra,a)

C Locate Array(1) and associate a native array pointer with it

      ptra = SMAIntArrayAccess(1) 
      
      a(1) = 11 ! use as a native Fortran array
      a(2) = 22 ! use as a native Fortran array

C++ interface: 

      #include <SMAAspUserSubroutines.h>

      // Locate and open array with ID=1
      int* a =  SMAIntArrayAccess(1);
     
      a[1] = 11;  // use as a native array
      a[2] = 22;  // use as a native array


NOTE: If a request is made to access an array which has 
      not been created, the function will return 0. 
     

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

Variables Returned from the Utility Routine

INTEGER*8 ( address )

Returns a pointer to the array, or 0 if an array with the requested ID does not exist. This pointer can be associated with a native Fortran or C/C++ array.

SMAIntArraySize, SMAFloatArraySize

Utility Routine Interface

Fortran interface:

      INTEGER SMAIntArraySize(ID)
      INTEGER SMAFloatArraySize(ID)

Example:

#include  <SMAAspUserSubroutines.hdr>

      integer a_size, d_size

C Get the size of Array(1) as the number of INTEGERs
      a_size = SMAIntArraySize(1)   

! Get the size of Array(1) as the number of REALs

      d_size = SMAFloatArraySize(1) 

      do k=1,a_size
          ... 
      end do

C++ interface:

      #include <SMAAspUserSubroutines.h>

      // Lookup the size of Array(1) as the number of INTS

      int a_size = SMAIntArraySize(1);   

      // Lookup the size of Array(1) as the number of doubles
      
      int d_size = SMAFloatArraySize(1); 

      for(int i=1; i<=d_size; i++) {
          ...
      }

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

Variables Returned from the Utility Routine

INTEGER*4

Size of the array.

SMAFloatArrayDelete, SMAFloatArrayDelete

Utility Routine Interface

Fortran:

#include <SMAAspUserSubroutines.hdr>

      call SMAIntArrayDelete(1) ! Delete global Array(1)

C++:

      #include <SMAAspUserSubroutines.h>

      SMAIntArrayDelete(1);  // Delete global Array(1)

NOTE: Deletion of arrays is optional. All storage allocated 
      for these arrays will be freed when Abaqus terminates
      (at the very end of the analysis). It is, however, a good 
      programming practice to delete all allocations explicitly, 
      especially when they are no longer needed, as this will 
      free up memory for use somewhere else.

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

Allocatable Global Arrays of Variable Precision

The usage of real arrays is exactly the same as that of integer and floating point arrays except for the handling of precision. The precision of real arrays varies, changing along with the precision of Abaqus/Explicit. In single precision the values of real arrays are 32-bits long, and in double precision their values are 64-bits. For this automatic switching to work in Fortran, the type of such an array should not be declared explicitly. Abaqus relies on the implicit naming to alternate between single precision and double precision. In C/C++ the type of the native array should be Real*. The typedef declaration changes between float and double depending on the precision of Abaqus/Explicit. The precision does not change during a run; it is determined at the beginning of the analysis and remains the same until the end.

When you create real arrays, you give each array an identifier. Arrays can be created in one user subroutine and operated on in another simply by referencing this identifier. You need not capture the pointer to the array and pass it between routines. The arrays persist in memory from the moment they are created until you delete them explicitly or until the analysis ends. The arrays do not disappear when any particular user subroutine terminates. They are accessible from all user subroutines and all threads. Each MPI process is separate in memory from other MPI processes and has its own arrays. There is no cross-referencing of these arrays across MPI processes.

These arrays can be resized dynamically as needed. A call to Create() on an existing array but with a different size resizes the array. If the new size is larger than the previous size, there is no loss of data and the previous contents are carried over.

Utility Routine Interface

Fortran:

#include <vaba_param.inc>
#include <SMAAspUserSubroutines.hdr>

      ! Note: we do not explicitly declare the type of 'ra', we 
      ! rely on rules of implicit typing: it will become real*4
      ! or real*8 depending on the precision of Abaqus

      dimension  ra(*)
      pointer(ptrra,ra)
      
      integer     sz

      rinitval = -1.0e36        ! again, implicit typing

      ! Creating an array

      ! ID=1, SIZE=10, no initializer 
      ptrra = SMARealArrayCreate(1, 10)      
      ! ID=2, SIZE=10, rinitval used to initialize
      ptrra = SMARealArrayCreate(2, 10, rinitval) 
      ! ID=3, SIZE=10, initial value is -3.3d0
      ptrra = SMARealArrayCreate(3, 10, -3.3d0)  
      ! ID=4, SIZE=10, initial value is -3.3
      ptrra = SMARealArrayCreate(4, 10, -3.3)  

      ! Use ( from another subroutine )

      ptrra = SMARealArrayAccess(1)             

      if (ptrra.eq.0) then
           write(*,*) '### Array',i, 'does not exist'
      end if
       
      ! Use as a native array in Fortran

      ra(1) = 11.11  
      ra(2) = 22.22  

      ! Looping 

      ! Find out the current size of the array #1
      sz = SMARealArraySize(1)   
      
      do k=1,sz
           write(*,*) k, '=', ra(k)
      end do

      ! Resizing

      ptrra = SMARealArrayCreate(1, 1000, -1.0)    

             ! Array #1 is resized; the original 10 entries 
             ! are intact and carried over to the new array;
             ! all new entries are set to -1.0

      ! Deletion

      call SMARealArrayDelete(1)
      call SMARealArrayDelete(2)
      call SMARealArrayDelete(3)
      call SMARealArrayDelete(4)


C/C++: 

      #include <omi_for_types.h>
      #include <SMAAspUserSubroutines.h>

      Real* ra = 0;   // Type 'Real' switches precision with Explicit
      int   sz = 0;

      // Examples of Array Creation

      // ID=1, SIZE=10, no initializer used
      ra = SMARealArrayCreate(1, 10);  
      // ID=2, SIZE=10, initial value = -1.0
      ra = SMARealArrayCreate(2, 10, -1.0);

      // Access from another User Subroutine

      ra = SMARealArrayAccess(1); 

      if ( ra == 0 ) {
          fprintf(stderr, 

          "*** Error: array %d does not exist ***\n", 1 );
      }

      // Looping over the entries

      //  obtain the current size of array #1
      sz = SMARealArraySize(1);      

      for (int i=0; i<sz; i++) {
	         sum = sum + ra[i];
      }

      // Deletion

      SMARealArrayDelete(1);
      SMARealArrayDelete(2);

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

SIZE

Size of the array as the number of items. The maximum size is INT_MAX (2,147,483,647).

INITVAL

Initial value for each item of the array. If the argument is not supplied, zero is used as the initial value.

Variables Returned from the Utility Routine

INTEGER*8 (address)

Returns a pointer to the array created. This pointer can be associated with a native Fortran array or a native C/C++ array. These arrays are global. All threads will see and access exactly the same global array with a given ID.

Allocatable Global Arrays of User-Defined Types

The usage and syntax of arrays of structures are exactly the same as those of integer, floating point, and real arrays. These arrays are designed to store any user-defined types or classes, defined either in Fortran or in C/C++. The only information an array needs to know about these structures is their memory size. Most compilers provide the sizeof() operator, which returns the size of any object in memory in bytes. This size is one additional argument to the routines that operate on arrays of structures.

When you create arrays of structures, you give each array an identifier. Arrays can be created in one user subroutine and operated on in another simply by referencing this identifier. You need not capture the pointer to the array and pass it between routines. The arrays persist in memory from the moment they are created until you delete them explicitly or until the analysis ends. The arrays do not disappear when any particular user subroutine terminates. They are accessible from all user subroutines and from all threads. Each MPI process is separate in memory from other MPI processes and has its own arrays. There is no cross-referencing of these arrays across MPI processes.

These arrays can be resized dynamically as needed. A call to Create() on an existing array but with a different size resizes the array. If the new size is larger than the previous size, there is no data loss and the previous contents are carried over.

Utility Routine Interface

Fortran:

! Include a user module called, for example, 'mod', 
! which defines some user structure 'UserStruct'

       use mod

#include <aba_param.inc>      ! include this for Abaqus/Standard
#include <vaba_param.inc>     ! include this for Abaqus/Explicit 

#include <SMAAspUserSubroutines.hdr>

       type(UserStruct)::  us(10)
       type(UserStruct)::  structs(10)
       type(UserStruct)::  initval,s

       pointer(ptrstructs, structs)

       integer:: size1, size2, size3, size4
       integer(kind=8) :: arraySize


       ! Create an initializer for the values of the array 
       !(optional)

       initval%a = 100
       initval%b = 200
       initval%c = 300


       ! Different ways of obtaining the size of a structure

       size1 = storage_size( us(1) ) / 8     ! returns the size
                                             ! in bits
       size2 = sizeof( us(1) )
       size3 = storage_size( initval ) / 8   ! returns the size
                                             ! in bits
       size4 = sizeof( initval )
      

       ! Creating an array

       write(*,*) 'Array without initializers:'

       ptrstructs = SMAStructArrayCreate(1, 10, sizeof(initval))

       write(*,*) 'Array with  initializers:'

       ptrstructs = SMAStructArrayCreate(2, 10, sizeof(initval), 
      &                                  initval)


       ! Use ( from another subroutine )

       ptrstructs = SMAStructArrayAccess(2) 

       if (ptrstructs.eq.0) then
           write(*,*) '### Array 2 does not exist'
       end if

       
      ! Use as a native array in Fortran

      structs(5).a = -51
      structs(5).b = -52
      structs(5).c = -53

      structs(10).a = 111
      structs(10).b = 222
      structs(10).c = 333


      ! Looping over the entries

      arraySize = SMAStructArraySize(2)

      do k=1,arraySize
            s = structs(k); call PrintStruct(s) 
      end do


      ! Resize an array without using initializer

      ptrstructs = SMAStructArrayCreate(2,  100, sizeof(initval))

      arraySize = SMAStructArraySize(2)


      ! Resize array 2 with initializer

      ptrstructs = SMAStructArrayCreate(2,   200, sizeof(initval), 
     &                                  initval)
	          
      arraySize = SMAStructArraySize(2)


      ! Deletion

      call SMAStructArrayDelete(1)
      call SMAStructArrayDelete(2)

C/C++: 

      #include <omi_for_types.h>
      #include <SMAAspUserSubroutines.h>

      // Include the definition of a user-defined type, 
      // for example, A 

      #include <A.h>  


      // Create an (optional) initializer for user structs

      A init = { -1, -2, -3 }; 

      // Creating arrays 

      // no initializer
      SMAStructArrayCreate(1, 10, sizeof(A));  
      // with initializer
      SMAStructArrayCreate(2, 10, sizeof(A), &init;); 

      // Accessing arrays (from another subroutine)

      A* array = (A*) SMAStructArrayAccess(1); 


      // Modifying values in the array

      A* s1 = &array[5]; // We use a pointer to modify the value in 
                         // the array itself. Without a pointer, s1
                         // will contain a copy of the entry in
                         // the array, and any modifications to
                         // this copy will not affect the value in
                         // the original array.

      s1->a = -111;
      s1->b = -222;
      s1->c = -333;

      // Looping over the entries

      size_t sz = SMAStructArraySize(1);

      printf("Array 1: \n");
      for (size_t i=0; i < sz; i++) {
          PrintStruct(i, &array[i]);
      }

      // Deletion

      SMAStructArrayDelete(1);
      SMAStructArrayDelete(2);

Variables to Be Provided to the Utility Routine

ID

ID of the array (an integer), chosen by the user at the time of creation. Using this ID, an array can be opened in any other user subroutine.

NUM_ITEMS

Size of the array as the number of items. The maximum size is INT_MAX (2,147,483,647).

ITEM_SIZE

Size of one item (struct) in bytes.

INITVAL

Initial value for each item (struct) in the array. If this value is not supplied, the memory is simply zeroed out.

Variables Returned from the Utility Routine

INTEGER*8 (address)

Returns a pointer to the array created. This pointer can be associated with a native Fortran array or a native C/C++ array. These arrays are global. All threads will see and access exactly the same global array with a given ID.