I have a few parameter arrays with different names in a module:
real*8, parameter :: para1(*) = [43.234, 34.0498, ...
real*8, parameter :: para2...
In a routine in this module
subroutine sub(n,...
...
end
I want to use para1 when n=1, para2 when n=2, etc. There are some solutions to that, one is to make an array paras=[para1,para2...] and index properly which works fine. But I want to try using a pointer
real*8, pointer :: ptr(:)
and assign it to different parameter arrays depending on n, but the problem is that "PARAMETER attribute conflicts with TARGET attribute at (1)". If I remove the parameter attribute, the routine is less safe and the SAVE attribute is assumed.
Am I missing something or why can we not combine parameter and target? And is there a good way around it for this purpose?
The parameter and target attributes indeed conflict. An object with the target attribute must be a variable (Fortran 2018 8.5.17, C861); a named constant (object with the parameter attribute) is not a variable (F2018, 8.5.13, C850).
To use target arrays you must, then, use variables. It is tricky to have a variable which is "safe" from having its value modified by a programming mistake or such. There are several considerations which prohibit a variable from appearing in a variable definition context. If you can arrange such a state, then the compiler may have a chance of detecting your mistake. Can that happen easily?
Outside a pure procedure and an intent(in) dummy argument, the most tempting prohibition is with a protected module variable:
module pars
real, save, target, protected :: para1(74) = [...]
real, save, target, protected :: para2(1) = [6]
end module
subroutine sub (...)
use pars
real, pointer :: p
p => para1
end subroutine sub
Being protected, the values are safe from modification outside the module pars? Alas, even if this were true it isn't helpful: being protected, we can't even point a pointer to module variables.
In summary, your compiler isn't going to find it easy to detect a programming mistake which modifies the variable target array, so if you want to use an array as a target, you'll have to be careful.
Following the suggestion by #ja72 in the comment, this is an attempt to use a single 2D array for the parameters. This works nicely with gfortran-8.2 (on MacOS10.11).
program main
implicit none
integer i
integer, parameter :: para1(*) = [1, 2, 3, 4, 5]
integer, parameter :: para2(*) = [6, 7]
integer, parameter :: N1 = size(para1), N2 = size(para2), N = max(N1, N2)
integer, parameter :: params(N, 2) = &
reshape( [ para1, (0, i = 1, N - N1), &
para2, (0, i = 1, N - N2) ], [N, 2] )
print *, "para1 = ", params( :, 1 )
print *, "para2 = ", params( :, 2 )
print *, "Input i"
read *, i
print *, params( :, i )
end
$ gfortran-8 test.f90 && ./a.out
para1 = 1 2 3 4 5
para2 = 6 7 0 0 0
Input i
1
1 2 3 4 5
However, because the code becomes a bit complicated (because of reshape) and may not work with old compilers, things may be more straightforward to just use non-parameter arrays...
Related
In the Julia documentation for SortedSet, there is a reference to "ordering objects", which can be used in the constructor. I'm working on a project where I need to implement a custom sort on a set of structs. I'd like to use a functor for this, since there is additional state I need for my comparisons.
Here is a somewhat simplified version of the problem I want to solve. I have two structs, Point and Edge:
struct Point{T<:Real}
x::T
y::T
end
struct Edge{T<:Real}
first::Point{T}
second::Point{T}
end
I have a Point called 'vantage', and I want to order Edges by their distance from 'vantage'. Conceptually:
function edge_ordering(vantage::Point, e1::Edge, e2::Edge)
d1 = distance(vantage, e1)
d2 = distance(vantage, e2)
return d1 < d2
end
Are "ordering objects" functors (or functor-ish)? Is there some other conventional way of doing this sort of ordering in Julia?
An Ordering object can contain fields, you can store your state there. This is an example of a Remainder Ordering which sort integers by it's remainder:
using DataStructures
struct RemainderOrdering <: Base.Order.Ordering
r::Int
end
import Base.Order.lt
lt(o::RemainderOrdering, a, b) = isless(a % o.r, b % o.r)
SortedSet(RemainderOrdering(3), [1,2,3]) # 3, 1, 2
I'm not sure how it is related to functors, so I may misunderstand your question. This is an alternative implementation that defines an Ordering functor. I made explanations in comments.
using DataStructures
import Base: isless, map
struct Foo # this is your structure
x::Int
end
struct PrimaryOrdered{T, F} # this is the functor, F is the additional state.
x::T
end
map(f::Base.Callable, x::T) where {T <: PrimaryOrdered} = T(f(x.x)) # this makes it a functor?
isless(x::PrimaryOrdered{T, F}, y::PrimaryOrdered{T, F}) where {T, F} =
F(x.x) < F(y.x) # do comparison with your additional state, here I assume it is a closure
const OrderR3 = PrimaryOrdered{Foo, x -> x.x % 3} # a order that order by the remainder by 3
a = OrderR3(Foo(2))
f(x::Foo) = Foo(x.x + 1) # this is a Foo -> Foo
a = map(f, a) # you can map f on a OrderR3 object
a == OrderR3(Foo(33)) # true
a = map(OrderR3 ∘ Foo, [1, 2, 3])
s = SortedSet(a)
map(x->x.x, s) # Foo[3, 1, 2]
As always, an MWE is important for a question to be understood better. You can include a piece of code to show how you want to construct and use your SortedSet, instead of the vague "state" and "functor".
The sorting is based on the method isless for the type. So for instance if you have a type in which you want to sort on the b field. For instance you can do
struct Foo{T}
a::T
b::T
end
Base.:isless(x::T,y::T) where {T<:Foo} = isless(x.b,y.b)
s=[Foo(1,2),Foo(2,-1)]
res=SortedSet(s)
#SortedSet(Foo[Foo(2, -1), Foo(1, 2)],
#Base.Order.ForwardOrdering())
Tuples are also sorted in order, so you can also use
sort(s,by=x->(x.b,x.a)) to sort by b,thena without having to define isless for the type.
I have a Union{Type1, Type2, Type3}, which matches all values whose type is one of those types. But how do I match the types themselves?
MyU = Union{Float64, Int, Array}
a::MyU = 3.5 # works
a = 5 # works
a = [1, 2, 3] # works
# but of course
a = Float64 # nope
a = Int # nope
a = Array # nope
With normal types this is usually achieved via Type{MyType}, whose only value is MyType. But Type{MyU} matches only MyU, and not the types it contains. How do I match those?
I can of course just use DataType, but this has two issues:
It matches any type, not only those I want.
It doesn't match UnionAll types, like Array.
My current workaround is Union{DataType,UnionAll}, but it's an ugly hack that is additionally bound to crash and burn if I include another Union or some other non-concrete type into MyU.
My other solution is to make a second, parallel Union like so:
MyU = Union{Float64, Int, Array}
MyUT = Union{Type{Float64}, Type{Int}, Type{Array}}
It does work and is more strict, but it's also ugly and introduces large possibility of human error with manually keeping those in sync.
You could consider something like this to avoid macros (which can be tricky):
gettypes(u::Union) = [u.a; gettypes(u.b)]
gettypes(u) = [u]
typewrap(u) = Union{[Type{v} for v in gettypes(u)]...}
and then:
julia> MyU = Union{Float64, Int, Array}
Union{Float64, Int64, Array}
julia> MyUT = typewrap(MyU)
Union{Type{Array}, Type{Float64}, Type{Int64}}
EDIT
As an additional note, you can define gettypes as one liner like this:
gettypes(u) = u isa Union ? [u.a; gettypes(u.b)] : [u]
EDIT 2
Or yet simpler without an intermediate array:
typewrap(u) = u isa Union ? Union{Type{u.a}, typewrap(u.b)} : Type{u}
I wrote a recursive program on Fortran to calculate the combinations of npoints of ndim dimensions as follows. I first wrote this program on MATLAB and it was perfectly running. But in Fortran, my problem is that after the first iteration it is assigning absurd values for the list of points, with no explanation. Could somebody give me a hand?
PROGRAM MAIN
IMPLICIT NONE
INTEGER :: ndim, k, npontos, contador,i,iterate, TEST
integer, dimension(:), allocatable :: pontos
print*, ' '
print*, 'npoints?'
read *, npontos
print*, 'ndim?'
read *, ndim
k=1
contador = 1
open(450,file= 'combination.out',form='formatted',status='unknown')
write(450,100) 'Comb ','stat ',(' pt ',i,' ',i=1,ndim)
write(450,120) ('XXXXXXXXXX ',i=1,ndim+1)
allocate(pontos(ndim))
do i=1,4
pontos(i)=i
end do
TEST = iterate(pontos, ndim, npontos,k,contador)
end program MAIN
recursive integer function iterate(pontos, ndim, npontos, k,contador)
implicit NONE
integer, intent(in) :: ndim, k, npontos
integer,dimension(:) :: pontos
integer contador,inic,i,j,m
if (k.eq.ndim) then
inic=pontos(ndim)
do i = pontos(ndim),npontos
pontos(k)= i
write(*,*) pontos(:)
contador=contador+1
end do
pontos(ndim)= inic + 1
else
inic = pontos (k)
do j = pontos(k),(npontos-ndim+k)
pontos(k)=j
pontos= iterate(pontos, ndim, npontos, k+1,contador)
end do
end if
pontos(k)=inic+1;
if (pontos(k).gt.(npontos-ndim+k+1)) then
do m =k+1,ndim
pontos(m)=pontos(m-1)+1
end do
end if
end function iterate
There are too many issues in that code... I stopped debugging it. This is what I got so far, it's too much for a comment.
This doesn't make sense:
pontos= iterate(pontos, ndim, npontos, k+1,contador)
You are changing pontos inside iterate, and never set a return value within the function.
You need to a) provide a result statement for recursive functions (and actually set it) or b) convert it to a subroutine. Since you are modifying at least one dummy argument, you should go with b).
Since you are using assumed-shape dummy arguments, you need to specify an interface to the function/subroutine, either explicitly or with a module.
Neither format 100 nor format 120 are specified in your code.
Is there a way to have in Fortran a pointer to an array pointing different targets?
The following code shows what I am trying to do, which does not work as I want since the second association overwrites the first
implicit none
integer, parameter :: n = 3
integer, target :: a(n), b(n)
integer, pointer :: c(:) => NULL()
a = 4
b = 5
c(1:n) => a(1:n)
c(n+1:2*n) => b(1:n)
c(1:2*n) = 1
print*, a
print*, b
OUTPUT (ifort)
4 1 1
1 1 1
OUTPUT (gfortran)
4 4 4
1 1 1
And, any idea why ifort and gfortran behave differently in this case?
No, there is no simple way to do this, at least as far as I can see. Maybe if you say why you are trying to do this somebody might come up with a suitable answer, for instance in my mind derived types and/or array constructors might be the way to go but without context it is difficult to say.
As for why you get different answers you accessed an array out of bounds so anything can happen:
ian#ian-pc:~/test/stack$ cat point.f90
Program point
implicit none
integer, parameter :: n = 3
integer, target :: a(n), b(n)
integer, pointer :: c(:) => NULL()
a = 4
b = 5
c(1:n) => a(1:n)
c(n+1:2*n) => b(1:n)
c(1:2*n) = 1
print*, a
print*, b
End Program point
ian#ian-pc:~/test/stack$ nagfor -C=all -C=undefined point.f90
NAG Fortran Compiler Release 5.3.1(907)
[NAG Fortran Compiler normal termination]
ian#ian-pc:~/test/stack$ ./a.out
Runtime Error: point.f90, line 14: Subscript 1 of C (value 1) is out of range (4:6)
Program terminated by fatal error
Aborted (core dumped)
Think carefully what the lines
c(1:n) => a(1:n)
c(n+1:2*n) => b(1:n)
are doing. The first line says forget about whatever c was before, now c( 1:n ) is an alias for a( 1:n ). Note the allowed indices for c run from 1 to n. Similarly the second line throws away the reference to a and says c( n+1:2*n) is an alias for b( 1:n ). Note now the indices of C run from n+1:2*n, while for b they run from 1:n, but this is fine as both have n elements and that is all that matters. However the next line says
c(1:2*n) = 1
which can not be correct as you have just said the lowest allowed index for C is n+1, and as n=3 1 is not a valid index. Thus you are out of bounds and so anything can happen.
I strongly suggest when developing you use the debugging options on the compiler - in my experience it can save you hours by avoiding this kind of thing!
I have a program I want to parallelize using MPI. I have not worked with MPI before.
The program calculates the behavior for a large numer of objects over time. The data of
these objects is stored in arrays, e.g. double precision :: body_x(10000) for the x coordinate.
To calculate the behavior of an object the information about all other objects is needed,
so every thread needs to hold all data but will only update a portion of it. But before the
new timestep every thread needs to get the information from all other threads.
As I understand MPI_Allgather could be used for this, but it needs a send buffer and a
recive buffer. How can I synchronize an array over different threads if each thread updated
a different part of the array? Do I have to send the whole array from each thread to the
master in a recive buffer, update the specific part of the masters array and after all
threads have sent their data re-broadcast from the master?
This is a pretty basic question, but I'm very new to MPI and all examples I found are
pretty simple and do not cover this. Thanks for any help.
Pseudo-Example (assuming Fortran-Style vectors with first index 1):
(Yes the send/recive would better be done non-blocking, this is for the sake of simplicity)
if (master) then
readInputFile
end if
MPI_Bcast(numberOfObject)
allocate body_arrays(numberOfObjects)
if (master) then
fill body_arrays ! with the data from the input file
end if
MPI_Bcast(body_arrays)
objectsPerThread = numberOfObjects / threadCount
myStart = threadID * objectsPerThread + 1
myEnd = (threadID + 1) * objectsPerThread
do while (t < t_end)
do i = myStart, myEnd
do stuff for body_arrays(i)
end do
! here is the question
if (.not. master)
MPI_Send(body_arrays, toMaster)
else
do i = 1, threadCount - 1
MPI_Recive(body_arrays_recive, senderID)
body_arrays(senderID*objectsPerThread+1, (senderId+1)*objectsPerThread) = body_arrays_recive(senderID*objectsPerThread+1, (senderId+1)*objectsPerThread)
end if
MPI_Bcast(body_arrays)
! ----
t = t + dt
end do
It sounds like you want MPI_Allgather. To avoid needing a separate send buffer, you may be able to use the MPI_IN_PLACE value. That tells MPI to use the same buffer for both send and receive.
See http://mpi-forum.org/docs/mpi-2.2/mpi22-report/node99.htm#Node99
The array chunks from all processes can be combined using a call to MPI_Allgatherv. The following is a complete example in Fortran. It defines an array of size 50. Then each process sets a chunk of that array to some complex number. Finally, the call to MPI_allgatherv pulls all the chunks together. The calculations of the chunk sizes, and some of the parameters that need to be passed to MPI_allgatherv are encapsulated in the mpi_split routine.
program test
use mpi
implicit none
integer, parameter :: idp = 8
integer, parameter :: n_tasks = 11
real(idp), parameter :: zero = 0.0d0
complex(idp), parameter :: czero = cmplx(zero, zero, kind=idp)
integer :: mpi_n_procs, mpi_proc_id, error
integer :: i, i_from, i_to
complex(idp) :: c(-5:5)
real(idp) :: split_size
integer, allocatable :: recvcount(:), displs(:)
call MPI_Init(error)
call MPI_Comm_size(MPI_COMM_WORLD, mpi_n_procs, error)
call MPI_Comm_rank(MPI_COMM_WORLD, mpi_proc_id, error)
allocate(recvcount(mpi_n_procs))
allocate(displs(mpi_n_procs))
i_from = -5
i_to = 5
! each process covers only part of the array
call mpi_split(i_from, i_to, counts=recvcount, displs=displs)
write(*,*) "ID", mpi_proc_id,":", i_from, "..", i_to
if (mpi_proc_id == 0) then
write(*,*) "Counts: ", recvcount
write(*,*) "Displs: ", displs
end if
c(:) = czero
do i = i_from, i_to
c(i) = cmplx(real(i, idp), real(i+1, idp), kind=idp)
end do
call MPI_Allgatherv(c(i_from), i_to-i_from+1, MPI_DOUBLE_COMPLEX, c, &
& recvcount, displs, MPI_DOUBLE_COMPLEX, MPI_COMM_WORLD, &
& error)
if (mpi_proc_id == 0) then
do i = -5, 5
write(*,*) i, ":", c(i)
end do
end if
deallocate(recvcount, displs)
call MPI_Finalize(error)
contains
!! #description: split the range (a,b) into equal chunks, where each chunk is
!! handled by a different MPI process
!! #param: a On input, the lower bound of an array to be processed. On
!! output, the lower index of the chunk that the MPI process
!! `proc_id` should process
!! #param: b On input, the upper bound of an array. On, output the
!! upper index of the chunk that process `proc_id` should
!! process.
!! #param: n_procs The total number of available processes. If not given,
!! this is determined automatically from the MPI environment.
!! #param: proc_id The (zero-based) process ID (`0 <= proc_id < n_procs`). If
!! not given, the ID of the current MPI process
!! #param: counts If given, must be of size `n_procs`. On output, the chunk
!! size for each MPI process
!! #param: displs If given, must be of size `n_procs`. On output, the offset
!! if the first index processed by each MPI process, relative
!! to the input value of `a`
subroutine mpi_split(a, b, n_procs, proc_id, counts, displs)
integer, intent(inout) :: a
integer, intent(inout) :: b
integer, optional, intent(in) :: n_procs
integer, optional, intent(in) :: proc_id
integer, optional, intent(inout) :: counts(:)
integer, optional, intent(inout) :: displs(:)
integer :: mpi_n_procs, n_tasks, mpi_proc_id, error
integer :: aa, bb
real(idp) :: split_size
logical :: mpi_is_initialized
mpi_n_procs = 1
if (present(n_procs)) mpi_n_procs = n_procs
mpi_proc_id = 0
if (present(proc_id)) mpi_proc_id = proc_id
if (.not. present(n_procs)) then
call MPI_Comm_size(MPI_COMM_WORLD, mpi_n_procs, error)
end if
if (.not. present(proc_id)) then
call MPI_Comm_rank(MPI_COMM_WORLD, mpi_proc_id, error)
end if
aa = a
bb = b
n_tasks = bb - aa + 1
split_size = real(n_tasks, idp) / real(max(mpi_n_procs, 1), idp)
a = nint(mpi_proc_id * split_size) + aa
b = min(aa + nint((mpi_proc_id+1) * split_size) - 1, bb)
if (present(counts)) then
do mpi_proc_id = 0, mpi_n_procs-1
counts(mpi_proc_id+1) = max(nint((mpi_proc_id+1) * split_size) &
& - nint((mpi_proc_id) * split_size), 0)
end do
end if
if (present(displs)) then
do mpi_proc_id = 0, mpi_n_procs-1
displs(mpi_proc_id+1) = min(nint(mpi_proc_id * split_size), bb-aa)
end do
end if
end subroutine mpi_split
end program