how to read "enter" key in fortran - input

The following fortran 90 program can read in a text file and then print it out on the screen. It has an added feature that when the output covers the whole screen it will pause and wait for the user to press enter key to continue. Currently I use the "PAUSE" to implement this feature. But I would like to know the direct way of reading in the enter key. Please do offer your wisdom. I appreciate it!
program ex0905
implicit none
character(len=79) :: filename
character(len=79) :: buffer
integer, parameter :: fileid = 10
integer :: status = 0,count=0
logical alive
character(len=1) :: c
write(*,*) "Filename:"
read (*,"(A79)") filename
inquire( file=filename, exist=alive)
if ( alive ) then
open(unit=fileid, file=filename, &
access="sequential", status="old")
do while(.true.)
read(unit=fileid, fmt="(A79)", iostat=status ) buffer
if ( status/=0 ) exit
!write(*,"(A79)") buffer
count = count+1
if (count<24) then
write(*,"(A79)") buffer
else
!write(*,*) "Please type Enter to continue: "
pause
count=0
!read(*,"(A1)") c
!if (c==char(13)) then
! write(*,"(A79)") buffer
!else
! write(*,*) "This is not the 'Enter' key!!"
! exit
!end if
end if
end do
else
write(*,*) TRIM(filename)," doesn't exist."
end if
stop
end

If you have an item in the input list for read, as you do here with the character c, execution will not proceed until you do provide an actual item to standard input.
You can instead achieve what you want by having a read(*,*) statement without input. This will wait for just the key press, discarding any input.

Related

using Fortran Module from external files

I would like to call subroutines contained in a module. the module is saved in a separate file with my_mod.f95 filename.
module calc_mean
! this module contains two subroutines
implicit none
public :: calc_sum
public :: mean
contains
subroutine calc_sum(x,n,s)
! this subroutine calculates sum of elements of a vector
! x the vector
! n size of the vector
! s sum of elements of x
integer, intent(in):: n
real, intent(in):: x(n)
integer :: i
real, intent(out):: s
s=0
do i=1,n
s=s+x(i)
end do
end subroutine calc_sum
!
!
!
subroutine mean(x,n,xav)
! this subroutine calculates the mean of a vector
! x the vector
! n size of the vector
! xav mean of x
integer, intent(in):: n
real, intent(in):: x(n)
real, intent(out):: xav
real :: s
!
!
call calc_sum(x,n,s)
xav=s/n
end subroutine mean
end module calc_mean
I have the main program saved in a different file with 'my_program.f95'
program find_mean
! this program calculates mean of a vector
use calc_mean
implicit none
! read the vector from a file
integer, parameter ::n=200
integer :: un, ierror
character (len=25):: filename
real :: x(n), xav
un=30
filename='randn.txt'
!
OPEN (UNIT=un, FILE=filename, STATUS='OLD', ACTION='READ', IOSTAT=ierror)
read(un,*) x !
!
call mean(x,n,xav)
write (*,100) xav
100 format ('mean of x is', f15.8)
end program find_mean
when I compile the main program with geany, I got the following error message. Please, help me!
**
/usr/bin/ld: /tmp/cctnlPMO.o: in function MAIN__': my_program.f08:(.text+0x1e1): undefined reference to __calc_mean_MOD_mean'
collect2: error: ld returned 1 exit status
**
When I save both the main program and the module to the same file and run it, everything is fine.

QB64 Open Com port causes data read error

There seems to be a fundamental problem with QB64 Open Com statement at least with my compiler. When I open the com port with OPEN "Com3: 9600,n,8,1,ds0,cs0,rs" FOR RANDOM AS #1 while knowing that there is data in the buffer and print EOF, LOC, LOF. It shows EOF=0 OK fine but LOC and LOF both show 0. If you then exercise a GET statement you get a "bad record length" because LOF=0. If I use OPEN FOR INPUT then I immediately get EOF=-1, LOF and LOC=0. If I then use INPUT# I get an input past end of file error because EOF was already -1.
I know that the buffer contains" Voltage = 1.2* "(no quotes) If I say continue upon the input past end of file error I actually get Part of the message.
Is there a fix for this com port problem?
If you test the com port you will find Windows nicely blocks those ports:
REM test com port keyboard i/o
OPEN "COM3:9600,N,8,1,BIN,CS0,DS0" FOR RANDOM AS #1
DO
IF LOC(1) THEN
GET 1, , x
PRINT CHR$(x);
END IF
x$ = INKEY$
IF LEN(x$) THEN
IF x$ = CHR$(27) THEN END
x = ASC(x$)
PUT 1, , x
END IF
LOOP
END

Fortran: reading input

I am trying to run a really large FORTRAN model, so I can't give all the code that's involved but I hope I can give enough information for this to make sense.
The code compiled fine (using Intel 2016.0.109 compiler, OpenMPI 1.10.2 and HDF5 1.8.17).
When I try to run it though, it tells me that two of my inputs (called NZG and NZS) are set to -999, hence it fails.
> >>>> opspec_grid error! in your namelist!
> ---> Reason: Too few soil layers. Set it to at least 2. Your nzg is currently set to -999...
> ---> Reason: Too few maximum # of snow layers. Set it to at least 1. Your nzs is currently set to -999.
However, in the input file, they are really specified as
NL%NZG = 9
NL%NZS = 1
I went through all the modules that somehow have something to do with these variables and cannot find anywhere where this should go off the rails.
So I am starting to think now that maybe there is something wrong with the way the values are read in. The input file is a text file. The variables are specified as integers in the module that reads them in, FYI.
Should I check for special characters or something? I know Fortran can be picky with inputs...
EDIT: Below the code sequence tracing back the error
1) The main module opens the namelist
write (unit=*,fmt='(a)') 'Reading namelist information'
call read_nl(trim(name_name))
read_nl is
subroutine read_nl(namelist_name)
use ename_coms, only : nl & ! intent(inout)
, init_ename_vars ! ! subroutine
implicit none
!----- Arguments. ----------------------------------------------------------------------!
character(len=*), intent(in) :: namelist_name
!----- Local variables. ----------------------------------------------------------------!
logical :: fexists
!----- Name lists. ---------------------------------------------------------------------!
namelist /ED_NL/ nl
!---------------------------------------------------------------------------------------!
!----- Open the namelist file. ---------------------------------------------------------!
inquire (file=trim(namelist_name),exist=fexists)
if (.not. fexists) then
call fatal_error('The namelist file '//trim(namelist_name)//' is missing.' &
,'read_nl','ed_load_namelist.f90')
end if
!----- Initialise the name list with absurd, undefined values. -------------------------!
call init_ename_vars(nl)
!----- Read grid point and options information from the namelist. ----------------------!
open (unit=10, status='OLD', file=namelist_name)
read (unit=10, nml=ED_NL)
close(unit=10)
return
end subroutine read_nl
2) Then there are specifics about how (what variables) to read from the namelist (the input)
write (unit=*,fmt='(a)') 'Copying namelist'
call copy_nl('ALL_CASES')
if (runtype == 'HISTORY') then
call copy_nl('HISTORY')
else
call copy_nl('NOT_HISTORY')
end if
My simulation is 'NOT_HISTORY': copy_nl('ALL_CASES') is specified so it reads a number of the namelist variables, including the variable 'runtype' - which is then used in that if-else statement.
3) Then copy_nl('NOT_HISTORY') is the following, this is where nzg and nzs are defined.
subroutine copy_nl(copy_type)
use grid_coms , only : time & ! intent(out)
, centlon & ! intent(out)
, centlat & ! intent(out)
, deltax & ! intent(out)
, deltay & ! intent(out)
, nnxp & ! intent(out)
, nnyp & ! intent(out)
, nstratx & ! intent(out)
, nstraty & ! intent(out)
, polelat & ! intent(out)
, polelon & ! intent(out)
, ngrids & ! intent(out)
, timmax & ! intent(out)
, time & ! intent(out)
, nzg & ! intent(out)
, nzs ! ! intent(out)
implicit none
!----- Arguments. ----------------------------------------------------------------------!
character(len=*), intent(in) :: copy_type
!----- Internal variables. -------------------------------------------------------------!
integer :: ifm
!---------------------------------------------------------------------------------------!
!---------------------------------------------------------------------------------------!
! Here we decide which variables we should copy based on the input variable. !
!---------------------------------------------------------------------------------------!
select case (trim(copy_type))
case ('NOT_HISTORY')
itimea = nl%itimea
idatea = nl%idatea
imontha = nl%imontha
iyeara = nl%iyeara
nzg = nl%nzg
nzs = nl%nzs
slz(1:nzgmax) = nl%slz(1:nzgmax)
current_time%year = iyeara
current_time%month = imontha
current_time%date = idatea
current_time%time = real(int(real(itimea) * 0.01)) * hr_sec &
+ (real(itimea) * 0.01 - real(int(real(itimea)*0.01))) &
* 100.0 * min_sec
time = 0.0d0
NOTE that I have not put all the modules and variables in use, otherwise this post would become insanely long.
FYI the module grid_coms specifies the type the variable is. See below the relevant part (the whole module is a list of variables, nothing else)
module grid_coms
integer :: nzg ! Number of soil levels
integer :: nzs ! Number of snow/surface water levels
end module grid_coms
4) Back to the main module, this then calls ed_opspec_grid to check that the variables make sense, and this is where things go wrong. Again, with use the variables are initialized at the start, I'm leaving that out here.
subroutine ed_opspec_grid
!---------------------------------------------------------------------------------------!
! Check whether soil layers are reasonable, i.e, enough layers, sorted from the !
! deepest to the shallowest. !
!---------------------------------------------------------------------------------------!
if (nzg < 2) then
write (reason,'(a,1x,i4,a)') &
'Too few soil layers. Set it to at least 2. Your nzg is currently set to' &
,nzg,'...'
call opspec_fatal(reason,'opspec_grid')
ifaterr=ifaterr+1
elseif (nzg > nzgmax) then
write (reason,'(2(a,1x,i5,a))') &
'The number of soil layers cannot be greater than ',nzgmax,'.' &
,' Your nzg is currently set to',nzg,'.'
call opspec_fatal(reason,'opspec_grid')
ifaterr=ifaterr+1
end if
do k=1,nzg
if (slz(k) > -.001) then
write (reason,'(a,1x,i4,1x,a,1x,es14.7,a)') &
'Your soil level #',k,'is not enough below ground. It is currently set to' &
,slz(k),', make it deeper than -0.001...'
call opspec_fatal(reason,'opspec_grid')
ifaterr=ifaterr+1
end if
end do
do k=1,nzg-1
if (slz(k)-slz(k+1) > .001) then
write (reason,'(2(a,1x,i4,1x),a,2x,a,1x,es14.7,1x,a,1x,es14.7,a)') &
'Soil layers #',k,'and',k+1,'are not enough apart (i.e. > 0.001).' &
,'They are currently set as ',slz(k),'and',slz(k+1),'...'
call opspec_fatal(reason,'opspec_grid')
ifaterr=ifaterr+1
end if
end do
end subroutine ed_opspec_grid
NOTE that this is not the first check in this subroutine. Other variables are checked before this part (I left those out), but this is the first error message. Which makes me think that maybe part of the input is read okay and some is not.
Finally, let me stress again that this is a very large project, I really cannot show all the code, which is why I framed the question very simply: are there any (text) input requirements for Fortran that I might have missed (special characters, returns, maybe different for different systems?).
Plus, this code is used by a lot of researchers, on different platforms, so I doubt it very much there is something wrong with the code itself... (but let me know if I'm wrong).
You are not giving us enough data to pinpoint the issue, so I'll just tell you two of the issues that I have come across using namelists:
If you read several (even different) namelists from the same file, the order counts: If in the file namelist a preceeds namelist b, but the code reads b first, then it won't find a unless you rewind the file.
If the namelist in the data file does not include one or more values, these values will simply stay the same. It is quite likely that in the code these variables are set to -999 specifically so that their absence would be noted. So double-check that the data file is correct. Look for stray / characters that might end the namelist prematurely
All in all, in order to correctly assess what's happening, we'd need at least 3 things:
The declaration block, including all the variables that form part of this namelist, and the namelist /.../ ...,...,... specification
The part of the namelist file that declares this specific namelist, and
Whether any other namelists are also in the same file and whether they're before or after this namelist (both in the file, and in the code)

How to handle copy & paste in ncurses?

It seems ncurses handles paste (from copy & paste) by inserting one character at a time from the text that was pasted, which can be slow if the handler for each character is slow.
I'd like to handle paste events myself, when a 'bracketed paste' sequence is detected, starting with ESC[200~ (see http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode).
How can I implement this in ncurses?
Here some code illustrating 'bracketed paste mode' (using Ruby curses).
There is only three key things:
0.) Make sure your terminal supports bracketed paste mode (such as for instance Windows 10 Terminal since about 1 month, see https://github.com/microsoft/terminal/releases/tag/v1.7.572.0)
1.) Switch on bracketed paste mode in the terminal by sending the ?2004h CSI.
print("\x1b[?2004h")
2.) When a paste occurs, recognize that you received \x1b[200~ to start parsing the pasted text and \x1b[201~ to recognize that the pasted text is finished.
# /usr/bin/ruby2.7
require "curses"
include Curses
def main_loop
init_screen
noecho # Disable Echoing of character presses
# Switch on braketed paste mode
print("\x1b[?2004h")
addstr("Please paste something into this terminal (make sure it supports braketed paste!) or press 'q' to quit.\n")
loop do
c = get_char2
case c
in 'q' # use q to quit
return
in csi: "200~" # Bracketed paste started
pasted = ""
loop do
d = get_char2
case d
in csi: "201~" # Bracketed paste ended
break
else
pasted += d
end
end
addstr("You pasted: #{pasted.inspect}\n")
else
addstr("You didn't paste something, you entered: #{c.inspect} #{c.class.name}\n")
end
end
ensure
close_screen
end
#
# For CSI, or "Control Sequence Introducer" commands,
# the ESC [ is followed by
# 1.) any number (including none) of "parameter bytes" in the range
# 0x30–0x3F (ASCII 0–9:;<=>?), then by
# 2.) any number of "intermediate bytes" in the range
# 0x20–0x2F (ASCII space and !"#$%&'()*+,-./), then finally by
# 3.) a single "final byte" in the range
# 0x40–0x7E (ASCII #A–Z[\]^_`a–z{|}~).
#
# From: https://handwiki.org/wiki/ANSI_escape_code
def get_csi
result = ""
loop do
c = get_char
result += c
if c.ord >= 0x40 && c.ord <= 0x7E
return result
end
end
end
# Just like get_char, but will read \x1b[<csi> and return it as a hash { csi: ... }, everything else is just returned as-is
def get_char2
c = get_char
case c
when "\e" # ESC
case get_char
when '['
return { csi: get_csi }
else
raise "¯\_(ツ)_/¯"
end
else
return c
end
end
main_loop()
I figured it works after enabling bracketed paste mode, but this needs to be done with raw terminal sequences since ncurses doesn't provide support for that by itself.

Only reading in one input from a file in ada

I am having some trouble reading input in from a file. So what I have done is made a proof of concept program, which is a piece of my main program that does much more but I am only having trouble reading the input.
Here is my proof of concept program:
WITH Ada.Text_IO; USE Ada.Text_IO;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
PROCEDURE Open_File IS
subtype idnum is string(1 ..7);
-- Make short names so that we can show where things come from
My_File : File_Type; -- Name for file in this program
Os_Name : String := "My_Data.txt"; -- OS name for the file
N : idnum; -- Temporary for reading and printing file contents
EOL : boolean;
C : character;
BEGIN
-- Open will raise an ADA.IO_EXCEPTIONS.NAME_ERROR expection
-- if the file does not exist.
Open (File => My_File, Mode => In_File, Name => Os_Name);
LOOP
EXIT WHEN End_Of_File (My_File);
Look_Ahead(My_File, C, EOL);
IF EOL THEN
Skip_Line;
ELSE
IF C = ' ' THEN
Get(My_File, C);
ELSE
Get (My_File, N);
Put_Line(N);
END IF;
END IF;
END LOOP;
Close (My_File);
END open_file;
My data file looks like this: (including the spaces with no new lines after the last id)
1234567
456784a
6758abc
When I compile and run my program only the first id number gets printed to the screen. I have no clue where to check my code because it should continue to get id numbers until the end of the file.
Any help would be greatly appreciated. Thanks!
When you Get the second (and third, for that matter) line, Data_Error exception will be raised, because 456784a is not a number, 'a' is not a numeric character. If you want it to be a hexadecimal number, the input should be 16#456784a# (by default).