I am trying to write a subroutine to access arrays in a certain manner.
One input argument of the subroutine is a character containing the name of the array whose access is desired. Here is a rather simplified code example of how I generally imagine this to work:
PROGAM prog
real, dimension(3,3) :: array1(3,3)
real, dimension(3,3) :: array2(3,3)
real value1
real value2
... fill 'array1' and 'array2'...
call sub(array1,2,2,value1)
call sub(array2,2,2,value2)
... do something with 'value1' and 'value2'...
END
SUBROUTINE sub(name,x,y,out)
character(len=*), intent(in) :: name
integer, intent(in) :: x
integer, intent(in) :: y
real, intent(out) :: out
out = name(x,y)
RETURN
END
What I want is the subroutine to access array1(2,2) as requested in the argument and return this value to value1. Then access array2(2,2) and return this value to value2. Above code snippet does not work - no wonder about that. How do I get name(x,y) replaced with array1(x,y) respectively array2(x,y)?
Thanks a lot and best regards!
It is not necessary to use the name of the array to get at its content, if the array is declared in caller. You then use normal argument association.
You could write your subroutine as
subroutine sub(a,x,y,out)
integer, intent(in) :: x, y
real, intent(out) :: out
real, dimension(:,:), intent(in) :: a
out = a(x,y)
end subroutine sub
Much simpler would be to do, in the main program
value1 = array1(2,2)
value2 = array2(2,2)
I would recommend a textbook on Fortran, or even a look at http://en.wikipedia.org/wiki/Fortran_95_language_features .
Edit:
You could also use a SELECT CASE statement, like this:
subroutine bar(c, i, j, out)
character(len=*), intent(in) :: c
integer, intent(in) :: i,j
real, intent(out) :: out
select case (trim(c))
case ("array1")
out = array1(i,j)
case ("array2")
out = array2(i,j)
case default
stop "Argument is bletchful"
end case
end subroutine bar
I am simply at a loss to understand what good this would do, as opposed to using the array directly.
Is this what you want?
select case (array_name)
case ("array_dog")
array_ptr => array_dog
case ("array_cat")
array_ptr => array_cat
end select
where array_name is a character variable, array_dog and array_cat are arrays declared in the module (needing target attribute), and array_ptr is another array with the pointer attribute.
Related
I'm trying to create my own module of array (just for practicing).
I wrote this module.
module myArray
implicit none
public set, get
integer :: size = 5
integer, allocatable :: array(:)
allocate(array(size))
contains
subroutine set(index, value)
implicit none
integer, intent(in) :: index
integer, intent(in) :: value
array(index) = value
end subroutine set
function get(index) result(value)
implicit none
integer, intent(in) :: index
integer :: value
value = array(index)
end function get
end module myArray
But I get this error
mymod.f90:8:25:
8 | allocate(array(size))
| 1
Error: Unexpected ALLOCATE statement in MODULE at (1)
What should I do to correct this?
The idea is right but you're placing global data in a module, it won't be usable in the real world. You want to define a class for your array type.
In fortran, you define a derived type binding both data and methods:
module myArrays
implicit none
type, public :: myArray
! Data here
integer :: n = 0
integer, allocatable :: array(:)
contains
! Type-bound procedures here
procedure :: set
procedure :: get
procedure :: new
end type myArray
contains
elemental subroutine set(this,index,value)
class(myArray), intent(inout) :: this
integer, intent(in) :: index,value
if (index>0 .and. index<=this%n) this%array(index) = value
end subroutine set
elemental integer function get(this, index) result(value)
class(myArray), intent(in) :: this
integer, intent(in) :: index
if (index>0 .and. index<=this%n) then
value = this%array(index)
else
value = -huge(value) ! return an "invalid" value
endif
end function get
elemental subroutine new(this, size)
class(myArray), intent(inout) :: this
integer, intent(in) :: size
integer :: ierr
! This should go in a destructor routine
this%n=0
deallocate(this%array,stat=ierr)
! Now allocate
if (size>0) then
this%n=size
allocate(this%array(Size),stat=ierr)
endif
end subroutine new
end module myArrays
Note that you'll have to call the initializer routine new before using your array. Here's a program example:
program test_myArray
use myArrays
implicit none
type(myArray) :: A,B
call A%new(size=10)
call B%new(size=20)
print *, 'A size=',A%n,' B size=',B%n
end program
You cannot put executable statements directly into a module. You have to create a subroutine, put the statements into the subroutine and then call the subroutine in some way.
I have a number of variables declared in a module such as
module test
use othermod, only: n
integer, dimension(n) :: var0
real, dimension(n) :: var1
real, dimension(n) :: var2
.....
real, dimension(n) :: var1000
end module test
Then I have a subroutine that fills these variables with values.
At this point I would like to create an array of arrays with all the variables declared in module test so that I can easily copy or print all variables of a particular (n) at the same time, like dimension(n,allvariablesin module test). For example I would like to do something like array(3,:)=array(2,:).
Because this code is part of a very large program I cannot really modify too much, but rather I need to create an array of arrays from all the variables in this module without typing all the variables.
How can I easily integrate this change in the current code?
I urge you follow to #Vladimir F's advice and encapsulate your variables inside a derived data type. You can employ the associate construct to call old codes expecting var0, var1, .., etc. Lastly, we can overload the type's name to get a Java style constructor in the code below
module type_MyArray
implicit none
private
type, public :: MyArray
! type-components
real, dimension(:), allocatable :: var0, var1, var2
contains
! type-bound procedures
procedure :: create => create_my_array
procedure :: destroy => destroy_my_array
end type MyArray
interface MyArray
module procedure my_array_constructor
end interface MyArray
contains
pure function my_array_constructor(n) result (return_value)
! Dummy arguments
integer, intent (in) :: n
type (MyArray) :: return_value
call return_value%create(n)
end function my_array_constructor
pure subroutine create_my_array(self, n)
! Dummy arguments
class(MyArray), intent(in out) :: self
integer, intent(in) :: n
allocate( self%var0(n) )
allocate( self%var1(n) )
allocate( self%var2(n) )
end subroutine create_my_array
pure subroutine destroy_my_array(self)
! Dummy arguments
class(MyArray), intent(in out) :: self
if (allocated(self%var0)) deallocate( self%var0 )
if (allocated(self%var1)) deallocate( self%var1 )
if (allocated(self%var2)) deallocate( self%var2 )
end subroutine destroy_my_array
end module type_MyArray
program main
use type_MyArray, only: MyArray
use old_code, only: do_something
implicit none
type (MyArray) :: foo, bar
! Allocate memory
foo = MyArray(42)
bar = MyArray(4200)
associate( var0 => foo%var0, var1 => bar%var1 )
! Call old code using var0 and var1
call do_something(var0, var1)
end associate
! Release memory
call foo%destroy()
call bar%destroy()
end program main
I am trying to write a program where I want the allocatable array A to be of either rank 1, 2, or 3, depending on my input at run-time. I want to do this since the subsequent operations on A are similar, and I have defined in a module an interface work with module procedures that when acted on A, gives the desired result.
What I am doing currently is this:
program main
implicit none
integer :: rank,n=10
real*8, allocatable :: A1(:)
real*8, allocatable :: A2(:,:)
read (*,*) rank
if (rank.eq.1) then
allocate (A1(n))
else if (rank.eq.2) then
allocate (A2(n,n))
end if
! operate on the array
if (rank.eq.1) then
call work(A1)
else if (rank.eq.2) then
call work(A2)
end if
end program
Things would be much easier if somehow I could choose the rank of A, as then the if statements are not needed. Maybe this is not possible, but all help are appreciated.
The next Fortran standard (2015) has the select rank construct similar to select case. My example uses the select case construct on the rank intrinsic of an assumed-rank dummy variable.
module my_type
use, intrinsic :: iso_fortran_env, &
ip => INT32, &
wp => REAL64
implicit none
private
public :: MyType
type MyType
real (wp) :: rank0
real (wp), allocatable :: rank1(:)
real (wp), allocatable :: rank2(:,:)
real (wp), allocatable :: rank3(:,:,:)
contains
procedure :: create => create_my_type
procedure :: destroy => destroy_my_type
end type MyType
contains
subroutine create_my_type(this, array)
! calling arguments
class (MyType), intent (in out) :: this
real (wp), intent (in) :: array(..) !! Assumed-rank dummy variable
! local variables
integer (ip), allocatable :: r(:)
select case(rank(array))
case (0)
return
case (1)
r = shape(array)
allocate( this%rank1(r(1)) )
case (2)
r = shape(array)
allocate( this%rank2(r(1), r(2)) )
case (3)
r = shape(array)
allocate( this%rank3(r(1), r(2), r(3)) )
case default
error stop 'array must have rank 0,1,2, or 3'
end select
! Release memory
if (allocated(r)) deallocate( r )
end subroutine create_my_type
subroutine destroy_my_type(this)
! calling arguments
class (MyType), intent (in out) :: this
if (allocated(this%rank1)) deallocate( this%rank1 )
if (allocated(this%rank2)) deallocate( this%rank2 )
if (allocated(this%rank3)) deallocate( this%rank3 )
end subroutine destroy_my_type
end module my_type
program main
use, intrinsic :: iso_fortran_env, only: &
ip => INT32, &
wp => REAL64
use my_type, only: &
MyType
implicit none
type (MyType) :: foo
real (wp) :: a0, a1(42), a2(42,42), a3(42,42,42)
print *, rank(a0)
print *, rank(a1)
print *, rank(a2)
print *, rank(a3)
! Allocate array of rank 3
call foo%create(a3)
print *, rank(foo%rank3)
print *, shape(foo%rank3)
print *, size(foo%rank3)
! Release memory
call foo%destroy()
end program main
Declare the array to be rank three. If a lower rank array is required, allocate the relevant trailing dimensions to be of size one.
real, allocatable :: array(:,:,:)
...
select case (desired_rank)
case (1) ; allocate(array(n,1,1))
case (2) ; allocate(array(n,n,1))
case (3) ; allocate(array(n,n,n))
case default ; error stop 'bad desired rank'
end select
You can then use an array section to get a contiguous slice of array that is consistent with your desired rank. Alternatively, write the relevant procedures that operate on array to take a rank three argument, and make them aware of the meaning of a size one extent for the higher dimensions.
I'm building the following RK4 code in fortran 2003. In both functions I do allocation of memory. Since my step and sine function will be called a lot, this seems really inefficient to me. What is the best/cleanest way to get rid of those allocates but without losing the ability to plug in any function that satisfies the interface 'fi'?
I still want my rk4 to be able to handle any size of state vector x
module rk4
interface
function fi(t,x) result (fx)
real, dimension(:), intent(in) :: x
real, intent(in) :: t
real, allocatable, dimension(:) :: fx
end function fi
end interface
contains
pure function sine(t,x) result (fx)
real, dimension(:), intent(in) :: x
real, intent(in) :: t
real, allocatable, dimension(:):: fx
allocate(fx(size(x)))
fx(1) = x(2)
fx(2) = -x(1)
end function sine
function step(x,f,dt) result(xn)
real, intent(in) :: dt
real, intent(in), dimension(:) :: x
real, allocatable, dimension(:) :: k1,k2,k3,k4,xn
procedure(fi) :: f
integer :: N
N = size(x)
allocate(k1(N))
allocate(k2(N))
allocate(k3(N))
allocate(k4(N))
k1 = f(dt,x)
k2 = f(dt+0.5*dt,x+0.5*k1*dt)
k3 = f(dt+0.5*dt,x+0.5*k2*dt)
k4 = f(dt+dt,x+dt*k3)
allocate(xn(N))
xn = x + (dt/6.)*(k1 + 2*k2 + 2*k3 + k4)
deallocate(k1)
deallocate(k2)
deallocate(k3)
deallocate(k4)
end function step
end module rk4
Use an automatic function result (i.e. a function result that depends on the characteristics of the arguments of the function). Similarly, use automatic variables for the intermediate calculations inside the step procedure.
(The compiler may still implement automatic variables using internal memory allocation routines similar to what allocate does, but this answers the question you asked ;) Alternatively (or in some sort of combination) the compiler may put the storage for the automatic variable and results on the stack. If the size of the automatic things put on the stack is large then you may run out of stack.)
module rk4
abstract interface ! clearer if this is abstract.
function fi(t,x) result (fx)
real, dimension(:), intent(in) :: x
real, intent(in) :: t
! Automatic function result - size of the result is
! the size of the x argument.
real, dimension(size(x)) :: fx
end function fi
end interface
contains
pure function sine(t,x) result (fx)
real, dimension(:), intent(in) :: x
real, intent(in) :: t
real, dimension(size(x)):: fx
fx(1) = x(2)
fx(2) = -x(1)
end function sine
function step(x,f,dt) result(xn)
real, intent(in) :: dt
real, intent(in), dimension(:) :: x
! xn is an automatic result, the others are just automatic.
real, dimension(size(x)) :: k1,k2,k3,k4,xn
procedure(fi) :: f
k1 = f(dt,x)
k2 = f(dt+0.5*dt,x+0.5*k1*dt)
k3 = f(dt+0.5*dt,x+0.5*k2*dt)
k4 = f(dt+dt,x+dt*k3)
xn = x + (dt/6.)*(k1 + 2*k2 + 2*k3 + k4)
end function step
end module rk4
If the sizes do not differ between the invocations, you can make the arrays module variables. Be careful when calling the procedures concurrently, for example, in OpenMP threadprivate may be needed.
You would also need another subroutines for initialization (allocation) of the arrays and finalization(deallocation). The allocation can be done on first call.
In Fortran 2003 OOP you would move the allocation to the constructor, deallocation to the final procedure and made the arrays components of the solver class.
You do not need Fortran 2003, you can just make a derived type with the buffers and pass them as type and not class.
type Solver
integer :: n
real, allocatable, dimension(:) :: fx
real, allocatable, dimension(:) :: k1,k2,k3,k4
contains
procedure :: sine
procedure :: step
final :: finalize
end type
interface Solver
Solver_init
end interface
....
function Solver_init(n) result(S)
type(Solver) :: S
S%n = n
allocate(S%k1(n) ....
...
pure function sine(S,t,x) result (fx)
class(Solver), intent(inout) :: S
real, dimension(:), intent(in) :: x
real, intent(in) :: t
real, dimension(b%n):: fx
....
function step(b,x,f,dt) result(xn)
class(Solver), intent(inout) :: S
real, intent(in) :: dt
real, intent(in), dimension(:) :: x
real, dimension(b%n) :: xn
...
subroutine finalize(S)
class(Solver), intent(inout) :: S
deallocate(B%k1 ....
...
using the g95 compiler, I get an error that says:
ERROR: Procedure attribute conflicts with INTENT attribute in 'quantityarray'
I was trying to take find the total sum of the array. Here is the subroutine in which this error appears:
SUBROUTINE findTotals(pricearray,quantityarray,totalprice, totalquantity)
INTEGER, INTENT(IN)::quantityarray
REAL, INTENT(IN):: pricearray
INTEGER, INTENT(OUT)::totalquantity
REAL, INTENT(OUT)::totalprice
totalquantity = SUM(quantityarray)
totalprice = SUM(pricearray)
END SUBROUTINE
Thanks so much for your time.
program SummingAnArray
implicit none
integer, dimension(10) :: array=(/ (i, i=1,10) /)
integer :: i, Total
call VectorSum(array,Total)
print *,Total
read(*,*)
contains
!===================================================
subroutine VectorSum(Vector,Total)
implicit none
integer, intent(in), dimension(:) :: Vector
integer, intent(out) :: Total
Total = SUM(Vector)
end subroutine VectorSum
!===================================================
end program SummingAnArray
Is this perhaps what you wished to achieve?