experchange > fortran

entropy (01-20-20, 04:34 PM)
Considering a DT like this:

```
module foo_m
implicit none

type foo_t
real, allocatable :: matrix(:,:)
contains
procedure, public :: set
end type foo_t

contains

subroutine set(self, vals)
class(foo_t), intent(out) :: self
real, intent(in) :: vals(:,:)

self%matrix = vals

end subroutine set
end module foo_m
```

a scalar instance `foo_sc` can be `set` in this way:
```
call foo_sc%set(inpmat1)
```

Now, I would like to extend its use to an array of `foo_t` objects (say `foo_arr`) in this way:
```
call foo_arr%set_arr([inpmat1, inpmat2, inpmat3, ..., inpmatn])
```

but I cannot find a way to do so.

I have tried:

1) Writing a TBP `set_arr` like this

```
type mat_t
real, allocatable :: val(:,:)
end type mat_t

subroutine set_arr(self, mats)
class(foo_t), intent(inout) :: self(:)
type(mat_t), intent(in) :: mats(:)

integer :: i

do i = 1, size(self)
call self(i)%set( mats(i)%val )
end do

end subroutine set_arr
```
but gfortran complains:
| procedure, public :: set_arr
| 1
Error: Passed-object dummy argument of ‘set_arr’ at (1) must be scalar

2) Make `set` `elemental`
```
type mat_t
real, allocatable :: val(:,:)
end type mat_t

elemental subroutine set(self, mat)
class(foo_t), intent(out) :: self
type(mat_t), intent(in) :: mat

self%matrix = mat%val

end subroutine set
```
but gfortran complains:
| elemental subroutine set(self, mat)
| 1
Error: INTENT(OUT) argument ‘self’ of pure procedure ‘set’ at (1) may not be polymorphic

Any suggestions?

Thanks.
gah4 (01-20-20, 06:37 PM)
On Monday, January 20, 2020 at 6:34:26 AM UTC-8, entropy wrote:
[..]
> call self(i)%set( mats(i)%val )
> end do
> end subroutine set_arr


Should this be:

call self(i)%set( mats(i))

passing the whole DT instead of just the %val part?

I do agree that the message could explain better.
entropy (01-20-20, 07:14 PM)
On Monday, 20 January 2020 16:37:07 UTC, ga...@u.washington.edu wrote:
> On Monday, January 20, 2020 at 6:34:26 AM UTC-8, entropy wrote:
> Should this be:
> call self(i)%set( mats(i))
> passing the whole DT instead of just the %val part?


I don't think so. Subroutine `set` expects a real array of rank 2 and not the whole DT.

> I do agree that the message could explain better.


No criticism to gfortran's error messages is intended: I trust they comply with the limitations of the language. I just can't find a way to implement a `set` method working with arrays of DT `foo_t`. I thought that it is a common design pattern...
gah4 (01-20-20, 11:28 PM)
On Monday, January 20, 2020 at 9:14:46 AM UTC-8, entropy wrote:

(I wrote)

> > Should this be:
> > call self(i)%set( mats(i))
> > passing the whole DT instead of just the %val part?


> I don't think so. Subroutine `set` expects a real array of
> rank 2 and not the whole DT.


OK, what I was thinking at the time was:

do i = 1, size(self)
call set(self(i), mats(i)%val )

The mats() case reminds me that many times it has been mentioned
that you can't have arrays of pointers, but only arrays of structures
containing pointers. In the mats() case, you can't have an array
of arrays, so you have an array of structure containing an array.

For a contrasting view, in Java you can't have multidimensional
arrays, but instead arrays of arrays. If you:

float mat[][] = new float[10][10];

Java creates an array of float[], and then ten arrays
of float to fill it with. You then pretend it is a 2D array.

I your case, mats would be an array of arrays.

float mats[] = new float[3][][];

and then you might:

mats[0] = new float[10][10];
mats[1] = new float[15][15];
mats[2] = new float[20][20];

Pretty often in Fortran where it says scalar, it doesn't mean
array element.

Note also that in Fortran you can have a function returning an
array, but you can't subscript it. It isn't quite the same as
an array.

end do
gah4 (01-21-20, 12:02 AM)
I was thinking about how to do it in Java, since I am much more
used to OO Java than OO Fortran.

I presumed set is supposed to be a constructor, but maybe not.

In Java, constructors have to have the name of the class, and
you call them with new. Then I think it looks like:

class foo {
float matrix[][];

foo(float vals[][]) {
matrix = new float[vals.length][vals[0].length];
for(int i=0; i<vals.length; i++) {
for(int j=0; j<vals[0].length; j++) matrix[i][j]=vals[i][j];
}
}

static void set_arr(foo foos[], float mats[][][]) {
for(int k=0; k<foos.length; k++) {
foos[k] = new foo(mats[k]);
}
}
}

If set isn't supposed to be a constructor, and I don't know how to
make on in Fortran, then it might look like:

class foo {
float matrix[][];

foo(float vals[][]) {
matrix = new float[vals.length][vals[0].length];
for(int i=0; i<vals.length; i++) {
for(int j=0; j<vals[0].length; j++) matrix[i][j]=vals[i][j];
}
}

void set(float vals[][]) {
for(int i=0; i<vals.length; i++) {
for(int j=0; j<vals[0].length; j++) matrix[i][j]=vals[i][j];
}
}

static void set_arr(foo foos[], float mats[][][]) {
for(int k=0; k<foos.length; k++) {
foos[k].set(mats[k]);
}
}
}

You can't call a class method with an uninitialized reference
variable. The first time is usually a constructor, but could also
be a static method.

Static methods are normally called with the class name, but they
can also be called with a variable of the appropriate class, even
if it hasn't been initialized.

As for your actual question, maybe a pointer (scalar) to the
array element would work.

More interesting if you make it class(*), though.
spectrum (01-21-20, 03:54 AM)
Changing intent(out) to intent(inout) in an elemental TBP might work...?
(In the code below, I have changed "matrix" to "vector" also
to make a test simpler.)

# I've never used this kind of elemental routines (for types), so not very sure
if the code is standard-conforming, but at least GCC-8 and 9 seem to be
working fine with no warning message.

!----------------------------------------------------------------------
module test_m
implicit none

type foo_t
real, allocatable :: vector(:)
contains
procedure :: init
procedure :: set
endtype

type vec_t
real, allocatable :: vals(:)
endtype

contains
elemental subroutine init( self )
class(foo_t), intent(inout) :: self

allocate( self % vector( 0 ) )
end

elemental subroutine set( self, vec )
class(foo_t), intent(inout) :: self
type(vec_t), intent(in) :: vec

self % vector = vec % vals
end
end

!----------------------------------------------------------------------
program main
use test_m
implicit none

type(foo_t) :: foos( 3 )
type(vec_t) :: vecs( 3 )

call foos % init() !! allocate all vectors to size 0

call show( "initial state" )

!................................................. ........

vecs( 1 ) % vals = [1, 2, 3]
vecs( 2 ) % vals = [4, 5, 6]
vecs( 3 ) % vals = [7, 8, 9]

call foos % set( vecs )

call show( "setting foos(:)" )

!................................................. ........

vecs( 1 ) % vals = [-1, -2, -3]
vecs( 2 ) % vals = [-4, -5, -6]

call foos(1:2) % set( vecs(1:2) )

call show( "setting foos(1:2)" )

!................................................. ........

call foos(3) % set( vec = vec_t( [70, 80, 90]) )

call show( "setting foos(3)" )

!................................................. ........

contains
subroutine show( msg )
character(*) :: msg
print *
print *, msg
print *, "[1] vector = ", foos(1) % vector
print *, "[2] vector = ", foos(2) % vector
print *, "[3] vector = ", foos(3) % vector
end
end

[ Result ]

$ gfortran -Wall -fcheck=all test.f90 && ./a.out # with GCC-8/9

initial state
[1] vector =
[2] vector =
[3] vector =

setting foos(:)
[1] vector = 1.00000000 2.00000000 3.00000000
[2] vector = 4.00000000 5.00000000 6.00000000
[3] vector = 7.00000000 8.00000000 9.00000000

setting foos(1:2)
[1] vector = -1.00000000 -2.00000000 -3.00000000
[2] vector = -4.00000000 -5.00000000 -6.00000000
[3] vector = 7.00000000 8.00000000 9.00000000

setting foos(3)
[1] vector = -1.00000000 -2.00000000 -3.00000000
[2] vector = -4.00000000 -5.00000000 -6.00000000
[3] vector = 70.0000000 80.0000000 90.0000000
FortranFan (01-21-20, 09:58 PM)
On Monday, January 20, 2020 at 9:34:26 AM UTC-5, entropy wrote:

> ..
> Now, I would like to extend its use to an array of `foo_t` objects (say `foo_arr`) in this way:
> ```
> call foo_arr%set_arr([inpmat1, inpmat2, inpmat3, ..., inpmatn])
> ```
> but I cannot find a way to do so.
> ..


Note a construct such as [inpmat1, inpmat2, inpmat3, ..., inpmatn] can result in an array temporary and impact performance.

If that is not of concern, then why not consider intrinsic assignment facility in the Fortran standard itself?

type :: foo_t
real, allocatable :: val(:,:)
end type
type(foo_t) :: foo_arr(2)
type(foo_t) :: inpmat1, inpmat2
inpmat1%val = reshape( [ 1.0, 2.0, 3.0, 4.0 ], shape=[2,2] )
inpmat2%val = reshape( [ -1.0, -2.0, -3.0, -4.0 ], shape=[2,2] )
foo_arr = [ inpmat1, inpmat2 ]
print *, "foo_arr(1)%val = ", foo_arr(1)%val
print *, "foo_arr(2)%val = ", foo_arr(2)%val
end

Upon execution,
foo_arr(1)%val = 1.00000000 2.00000000 3.00000000 4.00000000
foo_arr(2)%val = -1.00000000 -2.00000000 -3.00000000 -4.00000000

Or consider defined assignment or ELEMENTAL as shown by @spectrum.
gah4 (01-22-20, 12:36 AM)
On Tuesday, January 21, 2020 at 11:58:38 AM UTC-8, FortranFan wrote:

(snip)

> Note a construct such as [inpmat1, inpmat2, inpmat3, ..., inpmatn]
> can result in an array temporary and impact performance.


> If that is not of concern, then why not consider intrinsic
> assignment facility in the Fortran standard itself?


I was assuming that the OP was interested in learning about
and using the OO features of Fortran.

The program given, then, might not be the actual one that is
needed, but for trying out some features.

For that reason, I tried to understand how the same OO operations
would be done in Java, where I understand them better.

But there are some differences between Fortran and Java (and C).

In C and Java, array subscripting and function call are operators,
and can be applied to any expression of the appropriate type.
For example, a C function can return an array (converted to
a C pointer along the way), or function pointer, which can then
be used directly.

In Fortran, such values need to be assigned to variables, and
then used indirectly.

The OO features are not so well documented in the standard, and
not so easy to follow.
Similar Threads