Start Homework 12

INTERFACE structure, INTENT statement/attribute, DIM optional argument to array intrinsic functions

Now that we know how to work with multiple dimension (multi-subscripted) arrays, it is time to return to complete the definition of several Fortran 90 array functions, and to add a few more array functions to the list.

Before proceeding with discussion of functions, I want to make two definitions to simplify further descriptions. The "rank" of a Fortran array is defined as the number of dimensions (subscripts) that it has. For the statement "REAL A(10), B(5,3), C(3,3,4), D(6,6,6,6)", A has rank 1, B has a rank of 2, C has a rank of 3, and D a rank of 4. This is similar to the definition of the rank of a tensor. In addition it is useful to talk about the "shape" of arrays arrays. Two arrays have the same "shape" if they have the same rank and the size for each of their dimensions. After the statement "REAL A1(3,4), B1(3,4), C1(4,3), D1(0:3,3)", A and B have the same shape, C and D have the same shape, but A and C do not.

One key optional argument for a number of array functions is DIM. To illustrate this argument let's look at the use of the SUM intrinsic function associated with an array "A" defined in the statement:

REAL A(4,3), ASUM1(3), ASUM2(4)If I want to obtain the sum of all 12 elements in A, it is a simple matter of writing:

ASUM = SUM ( A )However, there are times in various solution or analysis procedures where I want to be a little more selective. I might want to calculate a sum of all numbers in each column of A.

1.0 1.0 5.0For each column I am summing across all rows. I am summing over the index of the first dimension, so the appropriate notation using the sum function is:

2.0 2.0 6.0

3.0 3.0 7.0

4.0 4.0 8.0

____________________

10.0 10.0 26.0

ASUM1 = SUM(A,DIM=1)Notice that SUM is now returning an array value, and that I had to be careful to provide enough space within ASUM1 to receive the information. For this example the results would be 10.0 in ASUM(1), 10.0 in ASUM(2), and 26.0 in ASUM(3). This use of the SUM intrinsic function is the same as the following double loop, in which I sum the contributions of all rows within each column.

DO 200 J=1,3In other circumstances, I might want obtain a sum across all of the columns for each row. This can be done with

ASUM1(J)=0

DO 100 I=1,4

ASUM1(J) = ASUM1(J) + A(I,J)

100 CONTINUE

200 CONTINUE

ASUM2 = SUM(A,DIM=2)Resulting values for the above A would be 7.0 in ASUM2(1), 10.0 in ASUM2(2), 13.0 in ASUM2(3), and 16.0 in ASUM2(4). This use of SUM is the same as summing with the following double loop

DO 200 I =1,4

ASUM2(I)=0

DO 100 J=1,3

ASUM2(I) = ASUM2(I) + A(I,J)

100 CONTINUE

200 CONTINUE

SUM is not the only function for which the DIM argument is useful. All of the following listed array intrinsic functions accept DIM. When the function operates on an array A with rank greater than one, it returns a result that must be placed in an array with a rank one less than that of A. Study and run the sample dimtest.f, to see examples using DIM.

SUM - A Fortran 90 intrinsic function to sum the elements of the array provided as its argument. If only the name of the array is provided (e.g. sum(c)) all elements of the array are summed. A selected portion of the array may be summed, if an integer range expression is provided with the array name (.e.g. sum(c(2:10)), sums elements 2 through 10 of c, sum(2:10:2) evaluates the sum of the even elements between 2 and 10). In addition to the "DIM=" optional argument, SUM accepts the "MASK=" option to further restrict the elements that are included in the sum. This argument contains a logical expression involving an array with the same shape as the array being summed. For example:

cpsum = sum(c,mask=c.gt.0)

evaluates the sum of all elements in "c" that are positive, and

cpsum = sum(c, mask=time.gt.10.and.time.lt.12)

sums elements in "c" when corresponding elements in array "time" fall between 10.0 and 12.0.

PRODUCT - A Fortran 90 intrinsic function to take the product of elements in the array provided as its argument. Aside from the use of multiplication rather than addition, this functions in the same manner as SUM, and accepts the same optional arguments..

MINVAL - A Fortran 90 function to obtain the minimum value of elements contained in the array provided as the argument to the function. The same optional arguments provided in SUM are available here.

MAXVAL - A Fortran 90 function to obtain the maximum value of elements contained in the array provided as the argument to the function. The same optional arguments provided in SUM, and MINVAL are available here.

COUNT - Counts the number of elements that meet a specific mask criterion. For example "cpos = count(a.gt.0)" gives the total number of elements in "a" that are greater than zero. The statement "cposr = count(a.gt.0.and. b.ne.0) gives the number of elements in "a" that are greater than zero and for which corresponding elements in "b" are non-zero. The only optional argument for this function is "DIM=".

CSHIFT - Does a circular shift of elements in an array. It has two required arguments, first the array "ARRAY" to be shifted, and second, "SHIFT", the number of locations to shift each element. A positive value for SHIFT shifts towards the low numbered end of ARRAY. Values shifted off one end are returned at the other. Although DIM is optional, if absent CSHIFT assumes DIM=1. CSHIFT returns a result with the same shape and type as ARRAY. If ARRAY has rank greater than one, you may supply SHIFT as an array with rank one less than that of ARRAY. For "REAL A(4,3)=(/1,2,3,4,1,2,3,4,5,6,7,8/)" the array can be represented as:

1.0 1.0 5.0

2.0 2.0 6.0

3.0 3.0 7.0

4.0 4.0 8.0

If the array "REAL ASHFT(3)=(/1,0,-1/)" is used in "B=CSHIFT(A,ASHFT)" the result for B is:

2.0 1.0 8.0

3.0 2.0 5.0

4.0 3.0 6.0

1.0 4.0 7.0

EOFSHIFT - End off Shift is similar to CSHIFT, except that elements shifted off of one end of the array are discarded. By default zeros are shifted onto the other end. However, the optional argument "BOUNDARY=" may be used to supply values to the upstream end of each column (DIM=1) or row (DIM=2), filling locations emptied by the shift. BOUNDARY may be a scalar or an array with rank one less than ARRAY(see CSHIFT).

**Three other intrinsic functions accept the "DIM=" argument, but
behave a little differently**

SIZE - Gives the number of elements in an array. With "DIM=", it returns the size of a given dimension. For example given the statement "REAL A(0:3,5,2:3), B(2,4)", "SIZE(B)" will return a value of 8. "SIZE ( A, DIM=3)" will return a value of 2. The result of this function is always an integer scalar, and it can be used to establish dimensions within TYPE statements (see interface.f)

LBOUND - Gives the lower bound of permitted indices for an array "ARRAY". If "DIM=" is present the result is a scalar integer giving the result for the appropriate dimension in ARRAY. If "DIM=" is not present the result is a one dimensional integer array containing an element for each dimension in ARRAY, giving the lower bound of that dimension. For "REAL A(0:3,5,2:3)", "LBOUND(A,DIM=1)" returns the value "0", and "LBOUND(A)" returns the integer array (/0,1,2/). Fortran now permits a dimension in an array to have size 0. When this occurs, the result returned by LBOUND is always one.

UBOUND - Gives the upper bound of permitted indices for an array "ARRAY". If "DIM=" is present the result is a scalar integer giving the result for the appropriate dimension in ARRAY. If "DIM=" is not present the result is a one dimensional integer array containing an element for each dimension in ARRAY, giving the upper bound of that dimension. For "REAL A(0:3,5,2:3)", "UBOUND(A,DIM=1)" returns the value "3", and "UBOUND(A)" returns the integer array (/3,5,3/). Fortran now permits a dimension in an array to have size 0. When this occurs, the result returned by UBOUND is always zero.

** Several other array functions are worth mentioning that do not use
the "DIM =" option.**

MINLOC - MINVAL gives you the minimum value, but doesn't tell you which element has that value. This Fortran 90 function returns the index of the first element in the argument array, "ARRAY", whose value is minimum. The result is always returned to an integer array with rank one (one-dimensional) and size equal to the number of dimensions in ARRAY. If c(1)=1, c(2)=0, and c(3)= -1, then "minloc(c(1:3))" returns the integer array "(/3/)". The MASK, optional argument is available for this function (see SUM).

MAXLOC - This Fortran 90 function returns the index of the first element in the argument array "ARRAY", whose value is maximum. The result is always returned to an integer array with rank one (one-dimensional) and size equal to the number of dimensions in ARRAY. If c(1)=0, c(2)=1, and c(3)= -1, then "maxloc(c(1:3))" returns the integer "2" as the first element of an array. The MASK, optional argument is available for this function (see SUM).

MATMUL - Performs matrix multiplication of two array arguments. Neither argument can have a rank greater than two and at least one must have a rank equal to two. The size for the last dimension of the first argument must match the size for the first dimension of the second argument. The result has type equal to the type of the arguments (numeric or logical), and rank equal to the minimum rank of the two arguments.

TRANSPOSE - Takes a single rank 2 array as an argument and returns the transpose of that array.

DOT_PRODUCT - Takes two rank 1 arrays (vectors) as arguments, and returns a scalar result with data type dependent on the types of the two arguments.

Examples of the use of MATMUL and DOT_PRODUCT have been provided in matprod.f. Look at both the program and the output from execution.

The secret is in the new INTERFACE structure. This is a mechanism by which you can provide the compiler more information on the functions (or subroutines), permitting it to among other things set up space for returning an array from a function. In my example interface.f, I define a function SAXPY loosely related to the BLAS subroutine SAXPY. It takes three arguments: a scalar "a"; a vector (rank 1 array) "x"; and another vector "y". It returns an array with the same size as x in which each element is the result of multiplying the corresponding element in "x" by "a" and then adding the corresponding element in "y" (saxpy = a*x+y). To make this work, any program unit referencing SAXPY must contain an "INTERFACE SAXPY" block, giving information about the function and its arguments.

interface saxpyWith the information from this interface, the compiler can check to see that you provide the right data types to SAXPY, besides expecting an array value for SAXPY. In addition it saves you from having to specify the range of indices for x and y in the argument list. They are automatically supplied to the function by the compiler. All I need to do in SAXPY is include a statement "real x(:), y(:)" (or its equivalent), and SAXPY will know the appropriate numbers on either side of the ":". Take note of how I declare the size of SAXPY itself. The SIZE intrinsic function, lets me simply tell the compiler to reserve enough space for results from SAXPY to match the size of "x". One implication of the need to use size is that the space for the output of the vector valued function will probably be allocated at each call to the function, slowing your program execution.

function saxpy (a,x,y)

real x(:),y(:),a

real saxpy (size(x))

intent (in) x,y,a

end function saxpy

end interface

One other feature of this example worth noting is the INTENT statement. I am telling the compiler that values must be available for "a", "x", and "y" when the function is called, and promising that I won't change the contents of any of these variables. In fact when the INTENT (IN) statement is included within a function (or subroutine), it prevents you from inadvertently reassigning a listed variable. You can also declare INTENT (OUT), telling the calling unit to expect a result, and forcing an assignment statement for any listed variable within the subprogram. If you want to bring a variable into a subprogram, use its value and then change it to another, INTENT (INOUT) should be used. INTENT can be used within subprograms even when INTERFACE blocks are not used.

Use of INTERFACE and INTENT statements is a good programming practice, to prevent inappropriate argument types and/or reassignment of argument values. They give the Fortran 90 compiler certain argument checking capabilities that generally required other software tools with older versions of Fortran. Those of you with a little foresight might worry that use of INTERFACE for a given function could be a major nuisance, when the function is referenced by a large number of subroutines within a program. Fortran 90 lets you save repetition by placing the INTERFACE within a MODULE and referencing it with a USE statement (see the example modint1.f).

While on the topic of INTERFACE, I want to show you how to use it to provide your own generic function interfaces, giving you the capabilities to deal appropriately with different argument types. In the example dual-interface.f I show how to let SAXPY use either all REAL or all INTEGER arguments (but not mixtures of REAL and INTEGER). The secret as with generic intrinsic functions is that there is no actual function with the name SAXPY. Separate functions RSAXPY and ISAXPY handle the two argument types. The INTERFACE provides the compiler with enough information to choose the correct function based on the argument types.

interface saxpy

function rsaxpy (a,x,y)

real x(:),y(:),a

real rsaxpy(size(x))

intent (in) x,y,a

end function rsaxpy

function isaxpy (a,x,y)

integer, intent(in) :: x(:),y(:),a

integer isaxpy(size(x))

end function isaxpy

end interface

Note the use of INTENT as an attribute in the above INTEGER statement, as another way to declare intent.

This trick of using an INTERFACE to define a generic function can also be used with functions that are internal to modules. The interface becomes much briefer, and must apply the MODULE PROCEDURE statement to list those functions coming from a module (see modint2.f). In some cases you may want to embed the both tne INTERFACE statement and related functions within a single module (see modint3.f).

After looking over and testing the INTERFACE examples, try to compile dual-errors.f to see some of the errors that can be picked up when using INTERFACE and INTENT.