Setting array size from command line arguments - arrays

I know that in Fortran I can declare an array of a number N of elements if N is declared as a parameter before (and so defined to some value).
On the other hand I know I can get a program to accept arguments from the command line with the use of the subroutine get_command_arg.
My question is:
Can I somehow declare an array of a number of elements given by a command when calling the program from the command line?
I'm looking for something like the command line:
./main -30
where main.f03 would begin with something like:
integer, parameter :: N = get_command_arg(1)
real :: x(N) ...
I'm trying not to define the arrays as allocatable.

There are certain languages where you can initialize variables and named constants from command line (like chapel), but Fortran is not one of them.
You say "I am trying not to define the arrays as allocatable." but that is the problem. You simply have to. There is no other way.
Your code
integer, parameter :: N = get_command_arg(1)
real :: x(N) ...
is illegal for several reasons.
You cannot put GET_COMMAND_ARGUMENT() into a constant expression because it is not among allowed functions. It does not return compile-time constant values. And parameter initializers must be set at the compile time.
GET_COMMAND_ARGUMENT() is a subroutine, not a function. It can return more stuff, not just the value, but also the length and status. It is not pure and the Fortran standard is trying to use only pure functions. Other things, like RANDOM_NUMBER(), are subroutines. It is a good style to follow in your own programs too.
The only way in Fortran to create arrays that change from run to run is to make the array allocatable or pointer. There are also automatic arrays for local arrays.

You can do it this way without allocatables. You just have to pass the size (after converting it to an integer) into a subroutine. But really, I see no reason not to use allocatables for something like this.
program main
implicit none
integer :: n,arg_len,istat
character(len=100) :: arg
call get_command_argument(1,value=arg,status=istat)
if (istat/=0) error stop 'error: cannot read first arg'
read(arg,'(I100)',iostat=istat) n
if (istat/=0) error stop 'error: first arg not an integer'
call real_main(n)
contains
subroutine real_main(n)
integer,intent(in) :: n
integer,dimension(n) :: ival
ival = 1
write(*,*) ival
end subroutine real_main
end program main
Example use:
> ./main 1
1
> ./main 2
1 1
> ./main 3
1 1 1

Related

Can I pass an array of arbitrary rank to a subroutine in Fortran?

I'm writing a general output for a numerical model I've written. I produce arrays that are either 1,2,3 or 4 dimensions on unknown length. I'm running into issues in how to define the array in the subroutine (found in a different module).
gfortran -g -O0 -I/usr/include -c analysis.f90
analysis.f90:136:32:
call write_netcdf('domain', domain, 'binary_cloud_domain', 0)
1
Error: Rank mismatch in argument ‘data_out’ at (1) (scalar and rank-4)
The error comes from this subroutine:
subroutine binary_cloud_analysis
use cloud, only: cloud_domain, cloud_smooth_domain
use write, only: write_netcdf
real, allocatable, dimension(:,:,:,:) :: domain, smth_domain
allocate(domain(x_dim,y_dim,z_dim,t_dim), smth_domain(x_dim,y_dim,z_dim,t_dim))
call cloud_domain(domain)
if (cloud_smooth) then
call cloud_smooth_domain(domain,smth_domain)
domain = smth_domain
endif
call write_netcdf('domain', domain, 'binary_cloud_domain', 0)
end subroutine binary_cloud_analysis
At the moment in the subroutine write_netcdf I've assigned data_out as
subroutine write_netcdf(var_name, data_out, output_name, output_type)
!--------------------------------------------------------------------------!
! input variables !
!--------------------------------------------------------------------------!
real(kreal), allocatable, intent (in) :: data_out
Understandably the compiler is taking this as a scalar as I haven't included a dimensions(:) type specifier because I don't know what type of array I'm passing in generality and I'd hoped it might be able to massage the dimensions due to the variable being allocatable. The output_type variable specifies what type of array we have i.e. 3d, 4d etc to help define the NetCDF file.
I think I would be able to do it by reshaping the array into a 1d array and then reshaping back based on defining the number of dimensions and passing the lengths but ideally I wouldn't have these additional steps. Is there a way the define an allocatable array in sufficient generality for these purposes?

Pointer to subarray defined by a map

I want to define a pointer to a subarray. For a simple range this is easily done by pointer => array(i:j), but I can't figure out how to do this for a map like k=[k1,k2,k3]. If I would define another array I could use a loop like array2=[(array1(k(j)),j=1,size(k,1))]. But it isn't possible to assign a pointer in a similar way (pointer => [(array1(k(j)),j=1,size(k,1))]) since the r.h.s. of the expression seems to define another variabel which then not even has the target attribute. For simple tasks, a trick around this, is to first assign a pointer to the total array an to use the map on the readout. But in my case this doesn't seem to be possible.
I will attach to examples: The first one shows what I described above. The second one is a more complicated example, where the trick doesn't work anymore. And in addition a two dimensional map is required.
Minimal example:
program test
integer, parameter :: n=10,n_k=3
real,target :: a(1:n)
real :: b(1:n_k)
integer :: k(1:n_k)
integer :: j
real,pointer :: p(:)
! fill array a and define map k:
a=[(real(j),j=1,n)]
k=[((j+1)*2,j=1,n_k)]
! can be used to print the arrays:
!write(*,*) a
!write(*,*) k
! can be used to write only the part of a defined by k:
!write(*,*) (a(k(j)),j=1,n_k)
! this an similar things didn't work:
!p(1:n_k) => [(a(k(j)),j=1,n_k)]
! works, but not generally:
p => a
write(*,*) (p(k(j)),j=1,n_k)
! works, only for arrays:
b=(/(a(k(j)),j=1,n_k)/)
write(*,*) b
end program
More complicated (but also kind of minimal) example which shows (hopefully) the problem I really have. For an easy understanding some explanation leads through it. There are plenty of write commands to print the arrays. I appreciate for the amount of code, but I really don't see how to make a shorter and understandable working example:
module mod1
type base
real :: a
end type
type,extends(base) :: type1
end type
type,extends(base) :: type2
type(type1),allocatable :: b(:)
end type
type(type2),allocatable,target :: c(:)
contains
subroutine printer(z)
class(*),pointer,dimension(:) :: z
integer :: j,a_z,n_z
character(len=40) :: f,ff='(F10.2,1x))',form_z
! define format for printing:
a_z=lbound(z,1)
n_z=ubound(z,1)
write(f,'(I0)') (n_z-a_z+1)
form_z="("//trim(adjustl(f))//ff
! writing:
select type(z)
class is (base)
write(*,form_z) (z(j)%a,j=a_z,n_z)
end select
end subroutine
end module
program test
use mod1
integer,parameter :: n_b=8,n_c=6,n_js=3,n_ls=2
integer :: js(1:n_js),ls(1:n_ls)
integer :: j,l
class(*),pointer :: p(:)
character(len=40) :: f,ff='(F10.2,1x))',form_c,form_b
! define format for printing:
write(f,'(I0)') n_b
form_b="("//trim(adjustl(f))//ff
write(f,'(I0)') n_c
form_c="("//trim(adjustl(f))//ff
! creating and filling the arrays:
allocate(c(n_c))
c%a=[(2d0*real(j),j=1,n_c)]
do j=1,n_c
allocate(c(j)%b(n_b))
c(j)%b%a=[(real(l)*1d1**(j-1),l=1,n_b)]
end do
! write arrays to compare later:
write(*,form_c) c%a
write(*,*)
write(*,form_b) (c(j)%b%a,j=1,n_c)
write(*,*)
! denfining two maps (size and entries will be input in the final program):
js=[1,4,6]
ls=[2,7]
! using the maps to print only the desired entries:
write(*,*) (c(js(j))%a,j=1,n_js)
write(*,*)
write(*,*) ((c(js(j))%b(ls(l))%a,j=1,n_js),l=1,n_ls)
write(*,*)
! !!! here I want to use the maps as well, but so far I only know how to use ranges:
p => c(1:4)
call printer(p)
write(*,*)
p => c(2)%b(3:6)
call printer(p)
write(*,*)
end program
Edit:
Just for the record, I solved the problem now by using arrays of derived types including pointers and slightly changing the calling subroutines.
You cannot do this with pointer association (e.g. pointer1 => array1(vector_subscript). Section 7.2.2.2 of the Fortran 2008 standard that disallows this is:
R733 pointer-assignment-stmt is data-pointer-object [ (bounds-spec-list) ] => data-target
There are two other forms, but they do not match your use, nor would they change the outcome. Reading further:
R737 data-target is variable
C724 (R737) A variable shall have either the TARGET or POINTER attribute, and shall not be an array section with a vector subscript.
This is why you cannot perform the pointer association your are attempting. You can however work around this and with pointer allocation. See this code:
n_k = 3
k = [((j+1)*2,j=1,n_k)] ! a vector subscript
p => a(k) ! NOT OK. Violates C724
allocate(p(n_k)) ! Associate your pointer this way
p = a(k) ! This is OK.
write(*,*) p
Which yields (wrapped in your example program):
% ./ptrtest
4.00000000 6.00000000 8.00000000
This allocates p to be the proper size and then assigns from a with a vector subscript. This gets around the issue of directly associating p with a map of a. This snippet assumes the variables are declared and initialized per your example code. This shows that you can assign a vector subscript of an array to a pointer, but only one that is already associated, not during the association.
As noted in a comment to your Q, if you have a regular stride, you can make the pointer association directly. For your first test case, this would be equivalent and work:
p => a(4:2:8) ! Allocation to a strided array is allowed
If however, you have an irregular vector subscript then the method in this answer will be what you need to use to accomplish the pointer association.
Another workaround you can use is passing a pointer and the map to a procedure. Consider the following code:
program test
implicit none
integer, parameter :: nx = 10, nx_m = 3
integer,dimension(nx_m) :: x_map
integer :: i
real, dimension(nx),target :: a
real, dimension(:), pointer :: p
! initialize array
a = [(real(i*2),i=1,10)]
write (*,'(10(f5.1 x))') a
!define a map
x_map = [1, 9, 4]
! associate pointer
p => a
call print_map(p, x_map)
contains
subroutine print_map(apointer, map)
implicit none
real, dimension(:), pointer :: apointer
integer, dimension(:) :: map
write (*,*) apointer(map)
end subroutine print_map
end program test
In this case, p "knows" about a and the map of elements in a can be calculated in the caller. Rather than associating (=>) p as a map of a (which cannot be done), p is associated to a and the map passed along with it.
This code produces the output:
% ./ptrtest3
2.0 4.0 6.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0
2.00000000 18.0000000 8.00000000

Fortran 77: How to use array output from function or subroutine

I've only been learning Fortran 77 (and its syntax) the last few days, and tried finding answers throughout the site and textbooks already and come up still confused, so I'd appreciate any help. I'm sorry if the formatting on this post is off; this is my first post, and I'm crunched for time.
I'm creating a program to multiply matrices. I want to create a subroutine or a function that will take two matrices as inputs (two 2x2 arrays), and return the multiplied matrix (one 2x2 array). I can't figure out how to get either a subroutine or a function to return an array of fixed size, or how to use the array once it's returned.
I tried using a function, which compiled on its own. But when I tried calling the function from my main program, I couldn't call it on its own:
MATMULT(MAT0, MAT0, MAT0)
1
Error: Unclassifiable statement at (1)
or assign it to another variable (I tried different REALs and arrays):
BLAH = MATMULT(MAT0, MAT0, MAT0)
1
Error: Return type mismatch of function 'matmult' at (1) (INTEGER(4)/REAL(4))
MATRIX1.f:26.22:
BLAH = MATMULT(MAT0, MAT0, MAT0)
1
Warning: Type mismatch in argument 'x' at (1); passed INTEGER(4) to REAL(4)
BLAH = MATMULT(MAT0, MAT0, MAT0)
1
Warning: Rank mismatch in argument 'x' at (1) (scalar and rank-2)
Since arrays are passed by reference, I'm really not sure what the function is returning, so how can I use the output matrix, if that is indeed the function's output?
I also tried using a subroutine, but (in addition to still not knowing what it's returning or where) then I get a "Two main PROGRAMs" error - so the compiler isn't differentiating between the main program and the subroutine. This might be a problem with my syntax on subroutines? I tried a few different things, but here's my most recent iteration of the code (I'm just trying to get the array-passing to work, so there's not actual matrix multiplication in here yet):
PROGRAM MATRIX1
INTEGER N
REAL A, B, MAT0(2,2), MATF(2,2), X(2,2), Y(2,2), Z(2,2)
REAL BLAH
PRINT *, " ENTER THE VALUE OF A: "
READ *, A
PRINT *, " ENTER THE VALUE OF B: "
READ *, B
PRINT *, " ENTER THE NUMBER OF MULTIPLICATIONS: "
READ *, N
C Creates the initial matrix
MAT0(1,1) = 1.0 - A
MAT0(1,2) = A
MAT0(2,1) = B
MAT0(2,2) = 1.0 - B
PRINT *, "M = ", MAT0
CALL MATMULT(MAT0, MAT0, MAT0)
PRINT *, "FINAL "
STOP
END PROGRAM
REAL SUBBROUTINE MATMULT(X, Y, Z)
END SUBROUTINE
Or (edited to add some of the recommended changes) with a function:
PROGRAM MATRIX1
INTEGER N
REAL A, B, MAT0(2,2), MATF(2,2), X(2,2), Y(2,2), Z(2,2)
REAL MATMULT(2,2)
PRINT *, " ENTER THE VALUE OF A: "
READ *, A
PRINT *, " ENTER THE VALUE OF B: "
READ *, B
PRINT *, " ENTER THE NUMBER OF MULTIPLICATIONS: "
READ *, N
C Creates the initial matrix
MAT0(1,1) = 1.0 - A
MAT0(1,2) = A
MAT0(2,1) = B
MAT0(2,2) = 1.0 - B
PRINT *, "M = ", MAT0
Z = MATMULT(X, Y)
STOP
END PROGRAM
FUNCTION MATMULT(X, Y)
REAL X(2,2), Y(2,2), Z(2,2), MATMULT(2,2)
RETURN
END
I'm still getting errors:
Z = MATMULT(X, Y)
1
Warning: Legacy Extension: REAL array index at (1)
MATRIX1.f:28.19:
Z = MATMULT(X, Y)
1
Error: Array index at (1) is an array of rank 2
In this era there is no reason for use FORTRAN 77. Fortran 90/95/2003 is easier to use, more powerful, and better assists the programer in finding mistakes. gfortran is an excellent open-source compiler.
Here is an example in Fortran 95 of a similar program/function: a function that implements a vector-cross product. The example shows the function and program in the same file (there are other approaches) with the function receiving two vectors and returning one vector. Example: Computing the cross product of two vectors in Fortran 90
You mention several problems in your question. Certainly your work will be enhanced using more recent Fortran dialects, as given in the answers by M. S. B. and Ilmirus. And you really should be using implicit none (even for Fortran 77 a lot of compilers support that - and lower case - as an extension).
Coming to the conceptual part, I must say that you need to continue your learning. However, if you want a function which returns the matrix product then you will have something like
function matmult(x, y)
real x(2,2), z(2,2), matmult(2,2)
... calculation of matmult
end function matmult
Followed by
z = matmult(x,y)
in the main program, where z is real z(2,2), rather than the scalar blah you have in your example, and is your result.
Coming to your subroutine, there are indeed problems: real subroutine matmult(x,y,z) requires not the real. Then calling this with call matmult(mat0, mat0, mat0) where you intend to update mat0 with a result is asking for aliasing problems. call matmult(x,y,z) will "store the result" where the subroutine calculations put it. Using intent as given by Ilmirus is a good guide to the behaviour desired by the programmer.
Finally, if you just want to get the result of multiplying A and B as matrices and storing in C: use C = matmul(A,B) with a later-than-F77 compiler.
However, in Fortran 77 one doesn't return array results, so the subroutine approach is the one to go for. In response to your edit:
REAL MATMULT(2,2)
is declaring matmult an array, not a function, so references to matmult(X,Y) give rise to your error.
There are two ways to do this:
1) Subroutine. Declare a subroutine like you did. However, specify input and output parameters:
REAL, INTENT(IN) :: X(2,2), Y(2,2)
REAL, INTENT(OUT) :: Z(2,2)
and call the subroutine using call keyword: CALL MATMULT
2) Function. Also specify input parameter, but now return value of the function:
! Insert calculation
MALMULT = ...
Don't forget to declare it in proper way: FUNCTION MATMULT(x,Y)
Call it: Z = MATMULT(X,Y)

C-Fortran character string interoperability

Good day. Sorry for maybe not so understandable definition of my problem and maybe some inaccuracies - I'm just starting to try myself in programming. Still, I'll try my best to explain everything plain.
I have mathematical DLL written in Fortran.
For example, there is a function. This function is used to parse name of log file into the dll to watch for the calculations.
integer function initLog(
* int_parameter,
* char_parameter,
* char_parameter_length,
* )
*bind(C, name = "initLog");
use, intrinsic :: ISO_C_BINDING;
!DEC$ATTRIBUTES DLLEXPORT::initLog
integer(C_INT), value :: parameter;
character(C_CHAR), intent(in) :: char_parameter(char_parameter_length);
integer(C_INT), value :: char_parameter_length;
...
some_other_variable = char_parameter(1:1)(1:char_parameter_length);
end function;
Usually I use MATLAB to work with the dll and, thus, have to use .mex files to call my functions directly from MATLAB. Inside the .mex file I have some interface code written in C that provides the interface between MATLAB and dll. For example, C interface for the function mentioned is:
int doSmth(const int int_parameter,
const char* char_parameter,
const int char_parameter_length,);
And then I use loadLibrary and GetProcAddress to get the function. And this works fine.
However, now I need to create .exe test file in Fortran which would use my dll. So, I have to link my dll to exe by linking it to an import .lib library. Another option for this executable is to take the name of the log file via command line as parameter. So, first I tried to pass the logfile filename just from within the exe file, like this:
program test
use dll_name;
use ifport;
implicit none;
...
integer :: log_init_status;
...
log_init_status = init_log(2, 'logfile.log', len('logfile.log'));
...
end program
This works fine in release, but returns a "severe (664): Out of range: substring ending position '11' is greater than string length '1'" mistake in debug. But at first I didn't find this bug and kept on writing the code. This is what I've got now:
program test
use dll_name;
use ifport;
use ISO_C_BINDING;
implicit none;
...
character*255 :: log_flag_char;
integer(C_INT) :: log_flag;
character*255 :: filename;
character(C_CHAR) :: log_filename;
integer(C_INT) :: log_filename_length;
....
call getarg(5, log_flag_char);
read(log_flag_char, *) log_flag;
call getarg(6, log_filename);
log_filename_length = len(log_filename);
log_init_status = analyticsLogInit(log_flag, log_filename, log_filename_length);
...
end program
This worked fine, but took only 1 first character of the log_filename ("C:\abcd\logfile.log" is transformed into "C"). If I change
character(C_CHAR) :: log_filename;
to
character(C_CHAR) :: log_filename(255);
, I get 2 problems: first, I have the length of my log_filename equal to 255 (can be fixed by trim though), and second - and the main - I again get "severe (664): Out of range: substring ending position '255' is greater than string length '1'".
If I change
log_init_status = analyticsLogInit(log_flag, log_filename,
log_filename_length);
to
log_init_status = analyticsLogInit(log_flag, C_LOC(log_filename),
log_filename_length);
, I get the error about the dummy argument type differ than the actual one.
I myself have a feeling that the 664 error shown comes from this line in dll:
some_other_variable = char_parameter(1:1)(1:char_parameter_length);
. I should write in my exe something like
character*255 :: log_filename;
and not
character :: log_filename(255);
But how can I parse it with (C_CHAR) used?
I realise that all this is quite messy and that it all comes from the leak of understanding, but this is my almost first serious experience in programming.
I only glanced over your question, but one thing to take note of is the way a character variable or named constant is declared. You can provide two type parameters: length and kind. If you don't use the corresponding keyword in the declaration, the first parameter specifies the length, and the second (if present) specifies the kind.
So if you want to declare a character variable of length 255 and kind C_CHAR, you can do so in any of the following ways:
character(len=255, kind=C_CHAR) :: log_filename
character(255, kind=C_CHAR) :: log_filename
character(255, C_CHAR) :: log_filename
character(kind=C_CHAR, len=255) :: log_filename
character(kind=C_CHAR) :: log_filename*255
The following syntax on the other hand (which is the one you used), declares a character variable of length C_CHAR, whatever value that may be.
character(C_CHAR) :: log_filename
Oh, and the next syntax declares an array of 255 elements, each element being a character variable of length C_CHAR.
character(C_CHAR) :: log_filename(255)
So the conclusion is, that one should take some time to study the peculiarities of declaring character entities in fortran.

keeping array limits in fortran during subroutine call

I have the following program
module test
contains
subroutine foo()
integer, allocatable :: a(:)
allocate(a(-5:5))
call bar(a)
print *, a
end subroutine
subroutine bar(a)
integer, intent(out) :: a(:)
a = 0
a(-4) = 3 ! here
a(2) = 3
end subroutine
end module
program x
use test
call foo()
end program
In the line marked with "here" I am doing something wrong. The fact is that when I receive the array a (in the caller allocated from -5 to +5), the callee uses conventional numbering (1 to n), meaning that assigning -4 I am doing an out of boundary assignment. How can I instruct the compiler that, within the bar routine, the numbering of the a array must be the same as in the caller ?
The type of dummy argument that you are are using in the subroutine, with the dimension specified with a colon, is called "assumed shape". This name is the clue -- Fortran passes only the shape and not the lower and upper bounds. The lower bound is assumed to be one unless you override it as shown in the answer by kemiisto. If the lower bound is not fixed, you can pass an argument to use as the lower bound.
Later addition: a code example if the lower dimension isn't known at compile time:
subroutine example (low, array)
integer, intent (in) :: low
real, dimension (low:), intent (out) :: array
There are two common options:
As kemisto wrote, you pass a second argument. This was common in F77-style code. You can not use the LBOUND trick! It has to be passed as an integer.
You declare the argument to be a pointer, which includes the entire array descriptor. Then the bounds of the array in the subroutine are the same as in the calling scope. Of course you may lose on optimization this way.
How can I instruct the compiler that, within the bar routine, the numbering of the a array must be the same as in the caller ?
Not sure but according to the standard you can specify the lower bound for an assumed-shape array.
subroutine bar(a)
integer, intent(out) :: a(-5:)

Resources