Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Making a single DO loop

Nick2
New Contributor I
282 Views

I noticed that a particular subroutine follows this type of a pattern:

common/something/temp1(5,5),temp2(5,5,2),temp3(5,5),stress1(5,5),stress2(5,5,2),stress3(5,5), etc.
...
call heatsink(temp1,stress1,5,5)
call heatsink(temp2,stress2,5,5)
...

So I thought I could simplify this and write an equivalence statement, or a subroutine-specific common block, as:

common/something/temps(5,5,4),stresses(5,5,4)
...
do i = 1,4
call heatsink(temps(1,1,i),stresses(1,1,i),5,5)
end do

I have to keep the data in the common blocks, so I thought I found a great solution!  But here's the problem; when I looked at the data in more detail, it actually looks like this:

common/something/temp1(20,5),temp2(5,5,2),temp3(5,5),stress1(20,5),stress2(5,5,2),stress3(5,5), etc.
...
call heatsink(temp1,stress1,20,5)
call heatsink(temp2,stress2,5,5)
...

So, my question is this; can I still write some kind of an equivalence statement, or some kind of a pointer, or something else to loop the subroutine heatsink call over all of my heat sinks in a single do loop?

 

0 Kudos
4 Replies
jimdempseyatthecove
Honored Contributor III
282 Views

Assuming /something/ is the same in the two subroutines, the differing layouts for /something/ is ok provided that the arrays are indeed temporaries for use only within the subroutine and not used to pass data to a different subroutine (not using the same layout).

If the subroutine that issues the call heatsink does not use/manipulate the data in temp1 and temp2, but rather is providing temp1 and temp2 as scratch memory for heapsink to use, then the shape and rank are immaterial provided that the size meets the requirements. Note, heatsink could potentially carry values from one call to the next in the temps, and this would be ok too.

However, if (when) the caller will supply data in the temps or use data returning in the temps, then the layouts must match.

Note, it is not unusual for old programs that ran on systems with limited memory to specify different layouts for the same named common. For C programmers, think of the /something/ as a named "stack" where the data layout is what you say it is, nothing more nothing less, and it is subject to unknown changes when uses as something else.

Consider "this is the time" to convert the COMMON's to MODULE's.

Jim Dempsey

0 Kudos
Nick2
New Contributor I
282 Views

Jim,

I have hundreds of thousands of lines of code in that program, and I'm only changing one or two COMMON blocks and subroutines.  Realistically speaking, this will remain fixed format and COMMON blocks for a very, very long time.  (If not, I suspect the Fortran committee will get quite a few unpleasant correspondences).

I am perfectly happy with the subroutine that has CALL HEATSINK to have a special declaration as:

common/something/temps(5,5,4),stresses(5,5,4)

While other subroutines have the declaration as:

common/something/temp1(5,5),temp2(5,5,2),temp3(5,5),stress1(5,5),stress2(5,5,2),stress3(5,5)

(Apologies for not properly indenting and going too long with that line for fixed format source)

But, my issue is that the arrays are not all "pretty" and dimensioned as 5,5.  So, I actually have something like this:

common/something/temp1(20,5),temp2(5,5,2),temp3(5,5),stress1(20,5),stress2(5,5,2),stress3(5,5)

How would I declare the alternate declaration here?  The CALL HEATSINK needs to do this:

call heatsink(temp1,stress1,20,5)
call heatsink(temp2(1,1,1),stress2(1,1,1),5,5)
call heatsink(temp2(1,1,2),stress2(1,1,2),5,5)
call heatsink(temp3,stress3,5,5)

I guess in C I would do something like:

float*temps[4];
temps[0]=&temp1;
temps[1]=&temp2[0,0,0];
temps[2]=&temp2[0,0,1];
temps[3]=&temp3;

And then I can have a nice DO loop.  But how do I do this in Fortran?

Nick

0 Kudos
IanH
Honored Contributor II
282 Views

Your C solution uses an array of pointers.  You can do that in Fortran too.  With elaborations...

module arg_wrapper
  implicit none

  type args
    real, pointer :: temp(:,:)
    real, pointer :: stress(:,:)
  contains
    procedure :: heatsink => args_heatsink
  end type args
contains
  subroutine args_heatsink(arg)
    class(args), intent(in) :: arg
    call heatsink(  &
        arg%temp, arg%stress,  &
        size(arg%temp, 1), size(arg%temp, 2) )
  end subroutine args_heatsink
end module arg_wrapper

  ...
  use arg_wrapper
  common /something/ temp1(20,5),temp2(5,5,2),temp3(5,5), &
                     stress1(20,5),stress2(5,5,2),stress3(5,5)
  target :: temp1, temp2, temp3,  &
            stress1, stress2, stress3

  type(args), allocatable :: arg_vector(:)
  integer :: i
  
  arg_vector = [  &
      args(temp1, stress1), &
      args(temp2(:,:,1), stress2(:,:,1)), &
      args(temp2(:,:,2), stress2(:,:,2)), &
      args(temp3, stress3) ]

  do i = 1, size(arg_vector)
    call arg_vector(i)%heatsink()
  end do
  ...

 

0 Kudos
John_Campbell
New Contributor II
282 Views

If you turned off array bound checking, the following simplification (change?) would work:

  call heatsink (temp1,stress1,20,5)
 do k = 1,3
  call heatsink (temp2(1,1,k),stress2(1,1,k),5,5)
 end do

I am sure you would get many protests, but this approach does utilise the COMMON order property.

If you wanted to improve further, you would need to consider why the program has gone from 20x5 to 5x5.

John

0 Kudos
Reply