gfortran - Variable format expressions workaround [duplicate] - fortran

I wrote a program to calculate a square finite difference matrix, where you can enter the number of rows (equals the number of columns) -> this is stored in the variable matrix. The program works fine:
program fin_diff_matrix
implicit none
integer, dimension(:,:), allocatable :: A
integer :: matrix,i,j
print *,'Enter elements:'
read *, matrix
allocate(A(matrix,matrix))
A = 0
A(1,1) = 2
A(1,2) = -1
A(matrix,matrix) = 2
A(matrix,matrix-1) = -1
do j=2,matrix-1
A(j,j-1) = -1
A(j,j) = 2
A(j,j+1) = -1
end do
print *, 'Matrix A: '
write(*,1) A
1 format(6i10)
end program fin_diff_matrix
For the output I want that matrix is formatted for the output, e.g. if the user enters 6 rows the output should also look like:
2 -1 0 0 0 0
-1 2 -1 0 0 0
0 -1 2 -1 0 0
0 0 -1 2 -1 0
0 0 0 -1 2 -1
0 0 0 0 -1 2
The output of the format should also be variable, e.g. if the user enters 10, the output should also be formatted in 10 columns. Research on the Internet gave the following solution for the format statement with angle brackets:
1 format(<matrix>i<10)
If I compile with gfortran in Linux I always get the following error in the terminal:
fin_diff_matrix.f95:37.12:
1 format(<matrix>i10)
1
Error: Unexpected element '<' in format string at (1)
fin_diff_matrix.f95:35.11:
write(*,1) A
1
Error: FORMAT label 1 at (1) not defined
What doesn't that work and what is my mistake?

The syntax you are trying to use is non-standard, it works only in some compilers and I discourage using it.
Also, forget the FORMAT() statements for good, they are obsolete.
You can get your own number inside the format string when you construct it yourself from several parts
character(80) :: form
form = '( (i10,1x))'
write(form(2:11),'(i10)') matrix
write(*,form) A
You can also write your matrix in a loop per row and then you can use an arbitrarily large count number or a * in Fortran 2008.
do i = 1, matrix
write(*,'(999(i10,1x))') A(:,i)
end do
do i = 1, matrix
write(*,'(*(i10,1x))') A
end do
Just check if I did not transpose the matrix inadvertently.

Related

Inconsistent rows allocation in scalapack

Consider the following simple fortran program
program test_vec_allocation
use mpi
implicit none
integer(kind=8) :: N
! =========================BLACS and MPI=======================
integer :: ierr, size, rank,dims(2)
! -------------------------------------------------------------
integer, parameter :: block_size = 100
integer :: context, nprow, npcol, local_nprow, local_npcol
integer :: numroc, indxl2g, descmat(9),descvec(9)
integer :: mloc_mat ,nloc_mat ,mloc_vec ,nloc_vec
call blacs_pinfo(rank,size)
dims=0
call MPI_Dims_create(size, 2, dims, ierr)
nprow = dims(1);npcol = dims(2)
call blacs_get(0,0,context)
call blacs_gridinit(context, 'R', nprow, npcol)
call blacs_gridinfo(context, nprow, npcol, local_nprow,local_npcol)
N = 700
mloc_vec = numroc(N,block_size,local_nprow,0, nprow)
nloc_vec = numroc(1,block_size,local_npcol,0, npcol)
print *,"Rank", rank, mloc_vec, nloc_vec
call blacs_gridexit(context)
call blacs_exit(0)
end program test_vec_allocation
when I run it with 11 mpi ranks i get
Rank 0 100 1
Rank 4 100 1
Rank 2 100 1
Rank 1 100 1
Rank 3 100 1
Rank 10 0 1
Rank 6 100 1
Rank 5 100 1
Rank 9 0 1
Rank 8 0 1
Rank 7 0 1
which is how i would expect scalapack to divide this array, however, for even number of ranks i get:
Rank 0 200 1
Rank 8 200 0
Rank 9 100 1
Rank 10 100 0
Rank 1 200 0
Rank 6 200 1
Rank 11 100 0
Rank 3 200 1
Rank 4 200 0
Rank 2 200 0
Rank 7 200 0
Rank 5 200 0
which makes no sense, why would rank 0 get 200 elements for block size 100 and ranks * block size > N.
Because of this my program works for mpi ranks 1,2,3,5,7,11, but fails for ranks 4,6,8,9,10,12, etc (I dont why it is failing for rank 9!). Can anyone explain what is wrong in my approach?
GFortran version: 6.1.0
SCALPACK version: 2.1.0
MacOS version: 10.11
There are a number of things wrong with your code
1) Firstly don't use Integer( 8 ). As Vladimir put it, please unlearn this. Not only is it not portable and therefore very bad practice (please see many examples here, e.g. Fortran 90 kind parameter) here it is wrong as numroc expects an integer of default kind as its first argument (see e.g. https://software.intel.com/content/www/us/en/develop/documentation/mkl-developer-reference-fortran/top/scalapack-routines/scalapack-utility-functions-and-routines/numroc.html)
2) You call an MPI routine before you call MPI_Init, with a hand full of exceptions (and this isn't one) this results in undefined behaviour. Note the description at https://www.netlib.org/blacs/BLACS/QRef.html#BLACS_PINFO makes no reference to actually calling MPI_Init. As such I also prefer to call MPI_Finalise
3) You have misunderstood MPI_Dims_create. You seem to assume you will get a 1 dimensional distribution, but you actually ask it for a two dimensional one. Quoting from the standard at https://www.mpi-forum.org/docs/mpi-3.1/mpi31-report.pdf
The entries in the array dims are set to describe a Cartesian grid
with ndims dimensions and a total of nnodes nodes. The dimensions are
set to be as close to each other as possible,using an appropriate
divisibility algorithm. The caller may further constrain the
operation of this routine by specifying elements of array dims. If
dims[i] is set to a positive number,the routine will not modify the
number of nodes in dimension i; only those entries where dims[i] = 0
are modified by the call.
You set dims equal to zero, so the routine is free to set both dimensions. Thus for 11 processes you will get a 1x11 or 11x1 grid, which is what you seem to expect. However for 12 processes, as The dimensions are set to be as close to each other as possible you will get either a 3x4 or 4x3 grid, NOT 12x1. If it is 3x4 along each row you expect numroc to return 3 processes with 200 elements ( 2 blocks ), and 1 with 100. As there are 3 rows you therefore expect 3x3=9 processes returning 200 and 3x1=3 returning 100. This is what you see. Also try 15 procs - you will see an odd number of processes that according to you "does not work", this is because (advanced maths alert) 15=3x5. Incidentally on my machine 9 processes does NOT return 3x3 - this looks like a bug in openmpi to me.

How can I update my old fortran 77 script to f90 or newer one?

I usually deal with data file with multiple data block, repeating n number of lines for m number of times.
This is one of the example:
9276
4900000
AA 4 4 6260 519 8350 1571 0 0 0 0 0 0 0 0.934 0.933 0.93 0.935 0 0 0 0 0 0 3.867 0.0 1.541
BB 3 3 3388 8391 6637 0 0 0 0 0 0 0 0 0.939 0.565 0.361 0 0 0 0 0 0 0 1.913 2.0 -0.732
CC 3 2 241 694 0 0 0 0 0 0 0 0 0 0.933 0.941 0 0 0 0 0 0 0 0 1.888 2.0 -0.834
...
Top line is total number of line for data block, second line is other value, those two lines are header. Then following 9276 lines are data. One datablock has 9278 lines, and then the same format of datablock repeats from 9279 line, for m number of data block. Usually m become really huge.
So far, I use something like this using f77 format:
program test
parameter (nn = 20000)
integer,dimension(nn,nn) :: conn
integer,dimension(nn) :: aaa,bbb,ccc,ddd,eee
real,dimension(nn) :: fff,ggg,hhh,iii,zzz
real,dimension(nn,nn) :: bos
character*2,dimension(nn) :: elem
open (2, file = 'input.txt', status = 'old')
iframe=0
100 continue
iframe=iframe+1
read (2,15, err = 50,end= 50) nat2
read (2,15) framenum2
do i=1,nat2
read (2,42) elem(i),aaa(i),ccc(i),(conn(i,j),j=1,10),ddd(i),(bos(i,j),j=1,10),fff(i),ggg(i),zzz(i)
enddo
do something using do and if loops
write (120,15) nat2
write (120,15) framenum2
do i=1,nat
write (120,75) elem(i),test(i),result(i)
write (121,76) iframe, zzz2, iii2
enddo
goto 100
I don't need to write formats, they are all properly formatted. As you could see, my script reads data per each datablock, do something, print them, then go to next datablock.
I learned fortran so long time ago, and I didn't cared that much about my style, and didn't think I should learn new style. But these days I feels like I should be more familiar with newer style of fortran. I feel I'm bit too late to learn new language at this point (mostly due to time constraint) so I think I better stick to the fortran...
Anyway, I want to change this to f90 style. But I'm not sure where should I need to begin. How can I improve this style of declaration, open, read, and write to be more efficient and faster? Any suggestion about those? I prefer f90 style but if there are any newer fortran way, I will be interested (if it is freeware and compilable with f90 or intel fortran)

Matlab - Convert Character Vector Into Number Vector?

I'm trying to implement an oscilloscope for a digital input and send it over a serial port for debugging. I have the scope software sending Matlab a string like "000000111111111000000001111111000000". I'd like to plot this. Is there any way for me to split this string into a vector. It doesn't seem Matlab allows you to use strsplit() without a delimiter. I'd rather not bog up the communications with a delimiter between each byte.
With MATLAB's weak typing, this is actually quite easy:
>> str = '000000111111111000000001111111000000'
str = 000000111111111000000001111111000000
>> class(str)
ans = char
>> vec = str - '0'
vec =
Columns 1 through 22:
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0
Columns 23 through 36:
0 1 1 1 1 1 1 1 0 0 0 0 0 0
>> class(vec)
ans = double
This subtracts the ordinal value of the character '0' from each character in the string, leaving the numerical values 0 or 1.
You can use sscanf with a single value width:
a = '000000111111111000000001111111000000'
b = sscanf(a, '%1d');
Which returns:
>> b.'
ans =
Columns 1 through 18
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0
Columns 19 through 36
0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0
A quick and fast solution is:
data = '000001111111110000000000111111111110000000';
vec = str2double(cellstr(data.').');
It will produce a column vector of numeric values. If you want a row vector as output, just use a single transpose:
vec = str2double(cellstr(data.'));
I'm surprised how difficult this is to do. But here's what I came up with:
str = '000001111111110000000000111111111110000000'; %test string
y = cellfun(#(x) str2num(x), regexp(str,'\d','match'));
plot(y);
regexp() seems to be the only way to go. By default, it return indexes of matches so you need to specify 'match'. Then you end up with a cell array of strings. The only good way to convert this into a numerical array is one item at a time with str2num().
I hope this helps someone else out who is assuming there is a straight forward function as I assumed. And if anyone knows a way to do this without converting my "01...01....01....01....00....00....00....00" stream of bytes into the ascii representations of the binary numbers: "49.....49.....49....49....48....48....48....48", I'd love to hear it.

SAS: Getting variable name of last non-zero observation

I'm trying to figure this out. I have a table as follows and I'm trying to populate the final column with the variable name of the last non-zero value (as shown in final column):
ID MTH_1 MTH_2 MTH_3 MTH_4 MTH_5 MONTH_LAST_BALANCE
--------------------------------------------------------------
1 10 0 10 20 10 MTH_5
2 5 10 15 5 0 MTH_4
3 5 10 5 0 0 MTH_3
4 1 2 3 1 0 MTH_4
5 1 0 0 0 0 MTH_1
I'm guessing I need to use some sort of array to make this work but I don't know. As per row 1, I need the last non-zero value only, not the left-most one that some other code seems to retrieve.
Any help would be much appreicated.
Cheers
data want ;
set have ;
/* Load MTH_1 to MTH_5 into array */
array m{*} MTH_1-MTH_5 ;
length MONTH_LAST_BALANCE $5. ;
/* Iterate over array */
do i = 1 to dim(m) ;
/* Use vname function to get variable name from array element */
if m{i} > 0 then MONTH_LAST_BALANCE = vname(m{i}) ;
end ;
run ;

Solving a maze in 2d array

I had referred to many articles and questions that answered how to solve a maze effectively but here I want to confirm what's going wrong in my code. Consider the maze:
2 1 0 0 3
0 1 0 1 1
0 1 0 0 1
0 1 1 0 0
0 0 0 0 0
where the 1's represent the walls and 0's represent the path.(source is 2 and destination is 3).
I have to output whether there is a path or not.
int y=0;
while(y==0)
{
robo1(n,m,maze);//this function adds 2 to any '0'/'3' in (i,j+1),(i+1,j),(i-1,j),(i,j-1) (if exists),where (i,j) is 2
robo2(n,m,k2,maze);//this function adds 3 to any '0'/'2' in (i,j+1),(i+1,j),(i-1,j),(i,j-1) (if exists), where (i,j) is 3
if(find5(n,m,maze)==1)//this function returns 1 if there is '5' in the maze
y++;
if(find0(n,m,maze)==0)//this function returns 0 if there are no '0' in the maze
break;
}
if(find0(n,m,maze)==0 && y==0)
printf("-1\n");//no path
else
printf("1\n");//there is a path
My idea is that if after any number of loops a five is found in the maze, then it would mean there is a path.
But while implementing this function in code I get wrong answers and sometimes run-time errors.
Is there any flaw in the above logic?
The general idea should almost work, but of course everything is in the details.
One case in which your approach will not work even if implemented correctly is however this:
2 1 0 0 0
1 1 0 1 1
0 0 0 1 3
i.e. if both 2 and 3 are "closed" by walls but there are 0s in the room. Your loop will never end because despite having 0s around neither of the two robo function will change anything.
A simple solution is returning 0/1 from robos if they actually changed at least a value in the matrix and quitting when this doesn't happen.
Note that this is not a very efficient way of solving a maze (your code will keep checking the same cells over and over many times).