Reputation: 28416
Foreword
The Fortran program that I'm writing should deal with 1D, 2D and 3D problems depending on ndims
, which can be 1, 2 or 3 and is read from an input file.
In these cases the quantity/ies of interest can be stored in arrays (one could be named phi
)
dims
(ALLOCATABLE(:)
or ALLOCATABLE(:,:)
or ALLOCATABLE(:,:,:)
),ALLOCATABLE(:,:,:)
with third dimension to be set equal to 1 in 2D or both second and third dimensions equal to 1 in 1D);both cases are explained well in this answer. The first approach seems more elegant to me, but in the following I assume the second one, which is definitely simpler.
These quantities have to be operated on by several subroutines (e.g. mysub
) along the ndims
dimensions (along "pencils" should give a graphic idea), so I should call something like
SELECT CASE (ndims)
! 3D case
CASE (3)
DO j = ...
DO k = ...
CALL mysub(phi(:,j,k))
END DO
END DO
DO i = ...
DO k = ...
CALL mysub(phi(i,:,k))
END DO
END DO
DO i = ...
DO j = ...
CALL mysub(phi(i,j,:))
END DO
END DO
! 2D case
CASE (2)
DO j = ...
DO k = ...
CALL mysub(phi(:,j,1))
END DO
END DO
DO i = ...
DO k = ...
CALL mysub(phi(i,:,1))
END DO
END DO
! 1D case
CASE (1)
DO j = ...
DO k = ...
CALL mysub(phi(:,1,1))
END DO
END DO
END SELECT
Actual question
Can anyone suggest me (or help me to to devise!) a different way of store phi
(maybe involving derived data types?) so that I can collapse the preceding code as follows?
DO id = 1, ndims
CALL mysub2(phi,id)
END DO
(Here mysub2
acts on mysub
's place.)
So the question is how should I store phi, so that I can substitute the first code with the second one?
Maybe I could return to the foreword and decide to follow the point 1., in which case would be easier to write a generic interface. This would be, I think, just a way to "hide" exactly what the SELECT CASE
would do. Which of the two (SELECT CASE
/generic INTERFACE
) would be more efficient?
Are these the only two ways to face this problem?
Upvotes: 0
Views: 156
Reputation: 21431
Perhaps I have misunderstood, but I think the answer to the specific question is to not make any changes to the storage or declaration of phi at all.
In the original code, three dimensional data (differentiating rank of the data from rank of the array used to store the data) is processed in slices along the first dimension, then the second, then the third. Two dimensional data is processed along the first, then the second, and one dimensional data is processed along the first only.
So with id going from 1 to the number of dimensions in the data, consider the following implementation of mysub2
:
SUBROUTINE mysub2(phi, id)
TYPE(pink_elephant), INTENT(IN) :: phi(:,:,:)
INTEGER, INTENT(IN) :: id
INTEGER :: i, j, k
SELECT CASE (id)
CASE (1)
DO j = ...
DO k = ...
CALL mysub(phi(:,j,k))
END DO
END DO
CASE (2)
DO i = ...
DO k = ...
CALL mysub(phi(i,:,k))
END DO
END DO
CASE (3)
DO i = ...
DO j = ...
CALL mysub(phi(i,j,:))
END DO
END DO
END SELECT
END SUBROUTINE mysub2
~~
Generic interfaces can always be resolved at "compile time" - the specific procedure (not type bound) or binding (type bound) that will be invoked by a particular CALL statement or function reference can be determined just from looking at the declarations in the code.
If you have a situation where "runtime" information is going to affect the choice of procedure, then there has to be some other executable mechanism, other than or additional to the resolution of a generic, that comes into play - if statement, select case, dynamic dispatch, etc, etc, etc.
Asking whether generic resolution is more efficient than a executable decision is therefore not particularly meaningful - they are different things.
Upvotes: 1
Reputation: 1696
You probably want something like this:
program test
integer :: j,ndims
integer :: n ! rank of each dimension, could also be read from input an allocated separately
type arr
real(8) :: x(n) ! one array for each dimension
end type
type(arr),allocatable :: phi
read(*,*) ndims
allocate(phi(ndims))
do j=1,ndims
call mysub(phi(j)%x) ! acts on the array in dimension j
end do
contains
subroutine mysub(x)
...
end subroutine
end program
Upvotes: 1