I'm having some difficulty successfully writing working (i.e. fully
debugged) code if it gets too long. It may be because I have to divide
my time between coding and doing other things, but IME by the time I
get a chance to look at a "big program" I'm writing, I've usually
forgotten stuff like array dimension allocations, what offset contains
what data and so on. Printing it out on hardcopy and reviewing it
whilst programming it helps somewhat. Usually a few thousand lines and
I'm utterly confused :-(. I spend more time figuring out what I did
previously than moving forward with new code.
I'm not a very experienced programmer, but I'm beginning to think that
the best way to write programs that accomplish more complex tasks is to
string a whole set of simple programs together using systemqq commands
in a "main" program, or even a Linux script calling them sequentially.
So I could have, for example, a PRNG that simply takes the starting
seed and # of randoms to generate as arguments and does accordingly. A
very handy program in its own right.
The benefits IMVHO of such a technique (bootstrapping?) is that the
different mini-programs would be interchangeable and easily debugged.
The disadvantage may be slower speed compared to a a single large
program as the smaller programs would likely communicate through
plain-text files.
Would the experienced programmers here recommend a many-small-program
approach over a single-large-program one?
Thanks everyone.
There used to be discussions of "programming in the small" vrs "programming
in the large". Programming in the small was how to make efficient polished
programs. Programming in the large was about having a number of small
tasks operate to form an integrated whole.
When you talk about "what offset contains what data" who are describing the
sort of things that makes programming in the large very difficult.
After a while
one tends to view such things as simply bad practice. They make small programs
very difficult to maintain. Maintenance is done by some idiot who does not
know what the tricks used were. Typically the original author about two weeks
later! And such tricks make integration into larger programs very difficult
as you have observed.
Having gotten the unkind words out of the way, what is to be done? Clean up
your style so that the programs are written to be read by a human for
understanding.
With the possible exception of one critical very inner loop the result will
run equally fast. And since you can understand the program you can now
change the
algorithm for that critical inner loop. Notice I said change the algorithm
and not polish the code. Polishing code for efficiency rarely if ever is a
productive exercise. Good instrumentation of real programs has repeatedly
shown that only very small parts of a program have any real influence on the
overall execution times.
All this does not answer your question because your question is really a
symptom of a related but different problem.
> I'm having some difficulty successfully writing working (i.e. fully
> debugged) code if it gets too long....
> Would the experienced programmers here recommend a many-small-program
> approach over a single-large-program one?
It depends on the particular application. For many applications, it just
isn't practical to divide it into such separate programs. For others, a
division works very well.
However....
In my view, good modularization the single most important concept for
writing large programs that work. (I'm talking about modularization as a
general concept here - not restricted to Fortran modules, although they
can be rlated). Modularization is a skill that takes a bit of work to
learn to do well.
If you have managed to separate a problem into one that is suitable for
implementation by many small separate programs, then you've already done
the hard part of the modularization work (and presumably you have an
application that fits that particular kind of division). You have broken
the problem into smaller pieces and identified the communication
necessary between those pieces.
Once you have done that part, you can separately decide such
impementation issues as how to implement the communication and whether
it works better as one program or as many.
That is, I'm thinking that if you apply the same skills and discipline
to a single large program, you'll also end up with an improvement. It is
the organization that is most important. The implementation of that
organization is a secondary issue (which can also be important, but not
as much so as the organizationitself).
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
I pass assumed shape arrays to procedures, and at the beginning of a
procedure I check that dimensions are what I expect, for example
subroutine foo(xmat,ymat,ierr)
real, intent(in) :: xmat(:,:)
real, intent(out) :: ymat(:,:)
integer, intent(out) :: ierr
ierr =
first_false((/size(xmat,1)==size(ymat,1),size(xmat,2)==size(ymat,2)/))
if (ierr /= 0) then
! handle error
end if
end subroutine foo
Arrays should store data of the same type, both in the literal sense
(which is required by the language) and in terms of what the numbers
represent. For example, if the program contains health data on people,
and you have a height, weight, and age for each person, all stored as
REAL numbers, it is probably better NOT to store them in arrays of size
3, but as either separately named variables or components of a derived
type (a "record" in some programming languages).
> Printing it out on hardcopy and reviewing it
> whilst programming it helps somewhat. Usually a few thousand lines and
> I'm utterly confused :-(.
A good program should be written so that you can understand it WITHOUT
keeping a few thousand lines in your head. Some have argued that a
procedure should not occupy much more than one page.
> I spend more time figuring out what I did
> previously than moving forward with new code.
>
> I'm not a very experienced programmer, but I'm beginning to think that
> the best way to write programs that accomplish more complex tasks is to
> string a whole set of simple programs together using systemqq commands
I would prefer to keep the Fortran code portable, avoiding systemqq and
put system-dependent things elsewhere, in a Windows batch file
(cmd.exe) for simple scripts and a portable scripting language like
Python for more complicated things.
> in a "main" program, or even a Linux script calling them sequentially.
> So I could have, for example, a PRNG that simply takes the starting
> seed and # of randoms to generate as arguments and does accordingly. A
> very handy program in its own right.
>
> The benefits IMVHO of such a technique (bootstrapping?) is that the
> different mini-programs would be interchangeable and easily debugged.
> The disadvantage may be slower speed compared to a a single large
> program as the smaller programs would likely communicate through
> plain-text files.
If a program needs input variables X and Y, you can write a subroutine
subroutine get_xy(x,y)
integer, intent(in) :: iunit
real, intent(out) :: x,y
end subroutine get_xy
that initially reads from a text file but whose implementation could
later change.
> Would the experienced programmers here recommend a many-small-program
> approach over a single-large-program one?
I recommend a single-large-program one, but one where each module has a
small driver program that tests only its functionality. Some
programmers, more systematic than me, advocate "test-driven
development".
> Thanks everyone.
See if you can find examples of well-written large programs; other
contributors here might be able to provide some. I learned a lot about
programming that way. (The time period was the early 70's, the language
was Burroughs Extended ALGOL, and the examples were listings of
Burroughs systems software, but the principle is the same.)
Louis
I'd suggest taking a look at some literature on this:
http://www.users.bigpond.com/robin_v/f90-cont.htm
and
http://dmoz.org/Computers/Programming/Languages/Fortran/Tutorials/Fortran_90_and_95/
Others have noted other Fortran 90/95/2003 books and journal articles.
Skip Knoble
On 16 Jan 2007 09:24:48 -0800, "sk8terg1rl" <sk8terg...@yahoo.co.uk> wrote:
-|Hi group,
-|
-|I'm having some difficulty successfully writing working (i.e. fully
-|debugged) code if it gets too long. It may be because I have to divide
-|my time between coding and doing other things, but IME by the time I
-|get a chance to look at a "big program" I'm writing, I've usually
-|forgotten stuff like array dimension allocations, what offset contains
-|what data and so on. Printing it out on hardcopy and reviewing it
-|whilst programming it helps somewhat. Usually a few thousand lines and
-|I'm utterly confused :-(. I spend more time figuring out what I did
-|previously than moving forward with new code.
-|
-|I'm not a very experienced programmer, but I'm beginning to think that
-|the best way to write programs that accomplish more complex tasks is to
-|string a whole set of simple programs together using systemqq commands
-|in a "main" program, or even a Linux script calling them sequentially.
-|So I could have, for example, a PRNG that simply takes the starting
-|seed and # of randoms to generate as arguments and does accordingly. A
-|very handy program in its own right.
-|
-|The benefits IMVHO of such a technique (bootstrapping?) is that the
-|different mini-programs would be interchangeable and easily debugged.
-|The disadvantage may be slower speed compared to a a single large
-|program as the smaller programs would likely communicate through
-|plain-text files.
-|
-|Would the experienced programmers here recommend a many-small-program
-|approach over a single-large-program one?
-|
-|Thanks everyone.
<snip>
> > Would the experienced programmers here recommend a many-small-program
> > approach over a single-large-program one?
>
> See if you can find examples of well-written large programs; other
> contributors here might be able to provide some. I learned a lot about
> programming that way. (The time period was the early 70's, the language
> was Burroughs Extended ALGOL, and the examples were listings of
> Burroughs systems software, but the principle is the same.)
The OP did not state her field. There are >500 Fortran codes classified
by subject at the Open Directory at
http://dmoz.org/Computers/Programming/Languages/Fortran/Source_Code/ ,
and some of the physics and chemistry programs represent big
stand-alone "production" programs (as opposed to software components).
Most of the codes are in Fortran 77, though, and Fortran 90+ has
modules and other features to facilitate the writing of large programs.
For a greater sampling of Fortran 90+ code, one could look at the list
of "Code that works with g95" at http://www.g95.org/g95_status.html .
FWIW, I'd like to reiterate Richard's comments above. Particularly about learning how to
do it well. My particular weakness in this respect is making code both modular and
scalable (you'd think the former would lead to the latter, hey? :o)
Two things I've always tried to do (with the usual caveat about generalisations):
1) Create procedures that do one thing, just one thing, but do
that one thing very well.
2) Test as you go.
#2 above has many different labels nowadays (unit testing, test-driven development, etc)
but the concept is pretty simple: write tests for all your procedures. It can be onerous
at first, but once you get into the groove it becomes (almost) second nature. The tests
can even precede the actual code writing.
I guess there should be a #3 and that would be to encapsulate "things" as much as
possible. When you subsequently discover a bug in one of your already-tested components,
fixing it doesn't cause any (or, at least, too many) downstream changes. (And don't forget
to add the test that revealed the bug to your test suite.)
cheers,
paulv
--
Paul van Delst Ride lots.
CIMSS @ NOAA/NCEP/EMC Eddy Merckx
Ph: (301)763-8000 x7748
Fax:(301)763-8545
When the microcomputers became available in the eighties, we went to
three programs for the same system, running in series (compile
requirements, data extract, report) all communicating in series through
files of data matrices and tables of instructions (i.e.
table-driven-programming; highly to be recommended)
Now we have 70 programs divided into 2 suites of services. Each suite
starts with a menu program which offers services and then calls
whichever daughter program seems to be needed. Each daughter program
does subroutine or system calls to execute commonly-used operations and
operating system services, all written and stored separately.
This works very well and makes maintenance very easy and programs more
readable.
Also changes to just one program allow us to e-mail all clients
world-wide the new small file, instead of a message to go to a website
and download a new version of a system which otherwise would be a
monster of some 30meg.
Finally, thorough debugging of small modules is far easier since the
influencing data parameters are a small set.
> All this does not answer your question because your question is really a
> symptom of a related but different problem.
Hi everyone. Thanks for your helpful replies :-)
I thought I'd provide you with an example of my coding, otherwise
everything is pretty much generalities :-)
rbdout_process.for is a Fortran program I use to parse the ASCII files
of a certain Finite Element analysis program, outputting the results in
a handy format. The file it parses is "rbdout".
It is a relatively short program (152 lines) but is an example of my
coding "style".
/runs away in expectation of admonishment ;-)
http://rapidshare.com/files/12015911/example_program.zip.html
Assigning meaningful parameter or variable names to dimensions and
offsets is a helpful crutch to memory. Simple dump subroutines intended
solely for debugging and based on these quantities can further reduce
the burden on memory, printing out physically meaningful subarrays.
Have you tried using comments in your code?
> I'm not a very experienced programmer, but I'm beginning to think that
> the best way to write programs that accomplish more complex tasks is to
> string a whole set of simple programs together using systemqq commands
> in a "main" program, or even a Linux script calling them sequentially.
> So I could have, for example, a PRNG that simply takes the starting
> seed and # of randoms to generate as arguments and does accordingly. A
> very handy program in its own right.
Better to write them as separate subroutines rather than as
separate programs.
I think you will get more feedback if you post the code in a newsgroup
message. It will also make it easier for people to comment on
particular lines.
OK, I have appended it at the bottom of this post. Thanks for your
advice, I was wondering where everyone had gone :-/. However if people
want to see the code in action then they'd have to download the text
file it parses. I have also appended a few lines of the text file as an
example.
The code is comment-sparse because it was originally written as a very
short "obvious" program, but I kept on adding features to it.
I have longer programs but I thought it somewhat overbearing on my part
to expect the NG to read a few thousand lines of code to give me what
is after all freely given advice.
Program rbdout_process
Integer:: i,j,k
Integer:: rbdout_endline, count, count2, switch
Integer:: impact_timestep, end_timestep
Integer, allocatable:: marked(:), timestep(:)
Real, allocatable:: time(:), disp(:), vel(:), accel(:)
Real:: mass, max_force
Character:: a50*50, a8*8, a42*42
rbdout_endline = 11024 !44024 !110024 !11046
Allocate (marked(rbdout_endline))
Allocate (time(rbdout_endline))
Allocate (timestep(rbdout_endline))
Allocate (disp(rbdout_endline))
Allocate (vel(rbdout_endline))
Allocate (accel(rbdout_endline))
Open (1,file="rbdout")
Open (2,file="impactor_zdata.txt")
Open (3,file="force_time.txt")
Open (4,file="displacement_force.txt")
Open (5,file="mass.txt")
Read (5,*) mass
Close (5)
count = 1
do i = 1, rbdout_endline
Read (1,'(a50)') a50
if (a50 == " r i g i d b o d y m o t i o n a t cycle= ") then
a50 = "12345"
marked(count) = i
count = count + 1
endif
enddo
rewind (1)
count2 = 1
do i = 1, rbdout_endline
if (i /= marked(count2)) then
read (1,*)
endif
if (i==marked(count2)) then
read (1, '(a50, i10, a8, e13.6)')
1 a50, timestep(count2), a8, time(count2)
count2 = count2 + 1
endif
enddo
rewind (1)
count2 = 1
do i = 1, rbdout_endline
if (i/=marked(count2)+5) then
read (1,*)
endif
if (i==marked(count2)+5) then
read (1, '(a42, e12.5)') a42, disp(count2)
count2 = count2 + 1
endif
enddo
rewind (1)
count2 = 1
do i = 1, rbdout_endline
if (i/=marked(count2)+6) then
read (1,*)
endif
if (i==marked(count2)+6) then
read (1, '(a42, e12.5)') a42, vel(count2)
count2 = count2 + 1
endif
enddo
rewind (1)
count2 = 1
do i = 1, rbdout_endline
if (i/=marked(count2)+7) then
read (1,*)
endif
if (i==marked(count2)+7) then
read (1, '(a42, e12.5)') a42, accel(count2)
count2 = count2 + 1
endif
enddo
write (2,'(3a10,a15,3a15)')
1 "#","Line","Timestep","Time","Disp","Vel","Accel"
do i = 1, count2-1
write (2,'(3i10, e15.6, 3e15.5)')
1 i,marked(i), timestep(i), time(i), disp(i),vel(i),accel(i)
enddo
write (2,'(a5)') '<EOF>'
switch = 0
i = 0
do while (Switch < 2)
i = i + 1
if (switch == 0) then
if (accel(i) > 0) then
impact_timestep = i
switch = 1
endif
endif
if (switch == 1) then
if (accel(i) == 0) then
end_timestep = i
switch = 2
endif
endif
enddo
Write (3,*) count2-1
write (3,*) 'Force /N, time /s'
do i = 1, count2-1
write (3,'(e15.5,e15.6)')
1 10000000*mass*accel(i),time(i)/1000000
enddo
Write (4,*) end_timestep-impact_timestep
Write (4,*) 'Force /N, displacement /mm'
do i = 1, count2-1
! if (i < impact_timestep) then
! write (4,'(e15.5,e15.6)')
! 1 10000000*mass*accel(i),-10*disp(i)+10*disp(impact_timestep)
! endif
if (i >= impact_timestep) then
if (i < end_timestep) then
write (4,'(e15.5,e15.6)')
1 -10*disp(i)+10*disp(impact_timestep),10000000*mass*accel(i)
endif
endif
enddo
max_force = 0.
Open (5,file='max_force.txt')
do i = 1, count2-1
if (10000000*mass*accel(i) > max_force) then
max_force = 10000000*mass*accel(i)
endif
enddo
Write (5,*) max_force
close (1)
close (2)
close (3)
close (4)
close (5)
End program rbdout_process
------------------------
75X75MM PLATE, 0.89G 350MS IMPACTOR
ls-dyna ls971s.7600.398 date
08/17/2006
1
r i g i d b o d y m o t i o n a t cycle= 1 time=
0.000000E+00
rigid body 1
global x y z x-rot
y-rot z-rot
coordinates: 8.0983E-10 6.4014E-10 3.4925E-10
displacements: 0.0000E+00 0.0000E+00 0.0000E+00 0.0000E+00
0.0000E+00 0.0000E+00
velocities: 0.0000E+00 0.0000E+00 -3.5001E-02 2.6261E-08
-6.6797E-10 4.8811E-17
accelerations: 0.0000E+00 0.0000E+00 0.0000E+00 -6.5209E-26
-1.2818E-24 9.4575E-24
principal or user defined local coordinate direction vectors
a b c
row 1 0.9992E+00 -0.2854E-01 -0.2672E-01
row 2 0.3051E-01 0.9966E+00 0.7621E-01
row 3 0.2445E-01 -0.7697E-01 0.9967E+00
output in principal or user defined local coordinate directions
a b c a-rot
b-rot c-rot
displacements: 0.0000E+00 0.0000E+00 0.0000E+00 0.0000E+00
0.0000E+00 0.0000E+00
velocities: -8.5586E-04 2.6939E-03 -3.4886E-02 2.6221E-08
-1.4153E-09 -7.5256E-10
accelerations: 0.0000E+00 0.0000E+00 0.0000E+00 1.2700E-25
-2.0036E-24 9.3307E-24
1
r i g i d b o d y m o t i o n a t cycle= 50 time=
0.986241E+00
rigid body 1
global x y z x-rot
y-rot z-rot
coordinates: 8.0983E-10 6.4014E-10 -3.4519E-02
displacements: 0.0000E+00 0.0000E+00 -3.4519E-02 2.5900E-08
-6.5877E-10 4.8140E-17
velocities: 0.0000E+00 0.0000E+00 -3.5001E-02 2.6261E-08
-6.6797E-10 4.8811E-17
accelerations: 0.0000E+00 0.0000E+00 0.0000E+00 -6.5209E-26
-1.2818E-24 9.4575E-24
principal or user defined local coordinate direction vectors
a b c
row 1 0.9992E+00 -0.2854E-01 -0.2672E-01
row 2 0.3051E-01 0.9966E+00 0.7621E-01
row 3 0.2445E-01 -0.7697E-01 0.9967E+00
output in principal or user defined local coordinate directions
a b c a-rot
b-rot c-rot
displacements: -8.4409E-04 2.6568E-03 -3.4406E-02 2.5860E-08
-1.3958E-09 -7.4220E-10
velocities: -8.5586E-04 2.6939E-03 -3.4886E-02 2.6221E-08
-1.4153E-09 -7.5256E-10
accelerations: 0.0000E+00 0.0000E+00 0.0000E+00 1.2700E-25
-2.0036E-24 9.3307E-24
1
<snip>
> I have longer programs but I thought it somewhat overbearing on my part
> to expect the NG to read a few thousand lines of code to give me what
> is after all freely given advice.
I think your code is generally OK. Some comments are below.
>
> Program rbdout_process
>
> Integer:: i,j,k
> Integer:: rbdout_endline, count, count2, switch
> Integer:: impact_timestep, end_timestep
> Integer, allocatable:: marked(:), timestep(:)
> Real, allocatable:: time(:), disp(:), vel(:), accel(:)
> Real:: mass, max_force
> Character:: a50*50, a8*8, a42*42
>
> rbdout_endline = 11024 !44024 !110024 !11046
The ALLOCATEs below could be combined into one statement.
> Allocate (marked(rbdout_endline))
> Allocate (time(rbdout_endline))
> Allocate (timestep(rbdout_endline))
> Allocate (disp(rbdout_endline))
> Allocate (vel(rbdout_endline))
> Allocate (accel(rbdout_endline))
>
It is a deficiency of Fortran that it uses unit numbers to open files.
One should at least PARAMETERize unit numbers with meaningful names,
for example "mass_unit" instead of "5". I use unit numbers >= 20. Units
5 and 6 should not be used for other than standard input and output, to
avoid confusing people who read the code.
Looking at the READ statements in your code, I see that you always
specify a format, for example '(a50, i10, a8, e13.6)' . The only time I
specify a format in a READ is to read the entire line (assuming the
character variable is long enough) with the "(a)" format. Otherwise I
use read(unit_num,*) var1,var2,... . This approach has worked for me,
and it is convenient, but it may be a matter of taste.
> Open (1,file="rbdout")
> Open (2,file="impactor_zdata.txt")
> Open (3,file="force_time.txt")
> Open (4,file="displacement_force.txt")
> Open (5,file="mass.txt")
<snip>
PARAMETERize the magic number 7 below.
> do i = 1, rbdout_endline
> if (i/=marked(count2)+7) then
> read (1,*)
> endif
> if (i==marked(count2)+7) then
> read (1, '(a42, e12.5)') a42, accel(count2)
> count2 = count2 + 1
> endif
> enddo
<snip>
PARAMETERize the magic numbers 10 and 10000000 below.
<snip>
Sorry for the second reply, but I noticed two things ...
> The code is comment-sparse because it was originally written as a very
> short "obvious" program, but I kept on adding features to it.
There is no code so "obvious" that at least a few lines of comments at
the top explaining what it does are not warranted.
>
> I have longer programs but I thought it somewhat overbearing on my part
> to expect the NG to read a few thousand lines of code to give me what
> is after all freely given advice.
>
> Program rbdout_process
IMPLICIT NONE belongs here!
>
> Integer:: i,j,k
> Integer:: rbdout_endline, count, count2, switch
<snip>
> Looking at the READ statements in your code, I see that you always
> specify a format, for example '(a50, i10, a8, e13.6)' . The only time I
> specify a format in a READ is to read the entire line (assuming the
> character variable is long enough) with the "(a)" format. Otherwise I
> use read(unit_num,*) var1,var2,... . This approach has worked for me,
> and it is convenient, but it may be a matter of taste.
There are pros and cons to list-directed input. It certainly is convenient.
Taste is part of it.
One negative to watch out for is that list-directed input can easily cause
confusions when things go wrong. In particular, recall that list-directed
input does not necessarily stop at the end of a line. If you have too few
input items on the line, it will go "grabbing" input from the nest line(s).
This can be very confusing to diagnose - particularly for a non-programmer
who might be using the application. He will be looking at a different line
than the program is, and the error messages won't match the data he is
looking at; happens regularly.
Thus, I tend to steer clear of using list-directed input from external files
in production applications. Using it on internal files doesn't have that
particular problem, though it still can have other shortcomings in the
general area of diagnosing problems.
Quick, throwaway programs are a different matter. The convenience factor gets
higher priority there.
--
Richard Maine | Good judgment comes from experience;
email: my first.last at org.domain| experience comes from bad judgment.
org: nasa, domain: gov | -- Mark Twain
> One negative to watch out for is that list-directed input can easily cause
> confusions when things go wrong. In particular, recall that list-directed
> input does not necessarily stop at the end of a line. If you have too few
> input items on the line, it will go "grabbing" input from the nest line(s).
> This can be very confusing to diagnose - particularly for a non-programmer
> who might be using the application. He will be looking at a different line
> than the program is, and the error messages won't match the data he is
> looking at; happens regularly.
>
> Thus, I tend to steer clear of using list-directed input from external files
> in production applications. Using it on internal files doesn't have that
> particular problem, though it still can have other shortcomings in the
> general area of diagnosing problems.
>
I find myself agreeing with Richard (for once) over practical usage of
the Fortran Language.
I never saw the need for this feature, since the day I saw the danger
of what Richard describes.
Mike
I downloaded and unzipped the source file and (one of) the required
input files. I'd like to make a comment or two about that:
[...]
> Program rbdout_process
>
> Integer:: i,j,k
> Integer:: rbdout_endline, count, count2, switch
> Integer:: impact_timestep, end_timestep
> Integer, allocatable:: marked(:), timestep(:)
> Real, allocatable:: time(:), disp(:), vel(:), accel(:)
> Real:: mass, max_force
> Character:: a50*50, a8*8, a42*42
>
> rbdout_endline = 11024 !44024 !110024 !11046
> Allocate (marked(rbdout_endline))
> Allocate (time(rbdout_endline))
> Allocate (timestep(rbdout_endline))
> Allocate (disp(rbdout_endline))
> Allocate (vel(rbdout_endline))
> Allocate (accel(rbdout_endline))
>
[...]
> Open (1,file="rbdout")
> Open (2,file="impactor_zdata.txt")
> Open (3,file="force_time.txt")
> Open (4,file="displacement_force.txt")
> Open (5,file="mass.txt")
There was no mass.txt file provided, so testing couldn't be completed...
A little frustrating after I went to the trouble to fix up the source
code format, etc...
More importantly, you're using fixed-form source, which led to some
other problems. First was, my compiler (with strict checking enabled)
complained about "tab-formating". In other words, the source file
includes tabs, and as an extension in some compilers (including mine),
a tab in column 1 counts as only a single character in a 72-charcter
maximum length input record. You've taken "advantage" of that in
two ways:
> Read (5,*) mass
> Close (5)
>
> count = 1
> do i = 1, rbdout_endline
> Read (1,'(a50)') a50
> if (a50 == " r i g i d b o d y m o t i o n a t cycle= ") then
Here, your statement exceeds column 72, except the compiler, as an
extension, counted the leading tab as only a single character. Don't
do that!
> a50 = "12345"
> marked(count) = i
> count = count + 1
> endif
> enddo
>
>
> rewind (1)
> count2 = 1
> do i = 1, rbdout_endline
> if (i /= marked(count2)) then
> read (1,*)
> endif
> if (i==marked(count2)) then
> read (1, '(a50, i10, a8, e13.6)')
> 1 a50, timestep(count2), a8, time(count2)
Here, the "continuation" character, "1", appears in column 8 (or 9,
hard to tell), rather than in columns 1-5 as required in fixed-form
source. (This usage appears several more times in your code.) Again,
you're using an extension. Don't do that.
Since you're clearly using an F90/95 compiler, change to free-form
source code. You can, if you insist, still use tabs for indention,
but you free yourself from needing to meet column restrictions (you
can start you statements in column 1 which gains you 6 columns
right off the bat :-).
I assume you have an F90 text available, and will have noted that
F90 uses a trailing "&" to indicate the statement continues on the
next line (and an optional leading "&" on the continued line).
Regards, Ken
--
Ken & Ann Fairfield
What: Ken dot And dot Ann
Where: Gmail dot Com
Ken Fairfield wrote:
> sk8terg1rl wrote:
> > Beliavsky wrote:
> >> sk8terg1rl wrote:
> >>> LOL, only 9pm and already my brain has stopped working...
> >>>
> >>> http://rapidshare.com/files/12015911/example_program.zip.html
>
> I downloaded and unzipped the source file and (one of) the required
> input files. I'd like to make a comment or two about that:
Thanks!
Sorry about that. mass.txt is just a plain text file with the mass in
grams of the rigid body. To be specific, it is 0.89g, although it can
be any real number for the purposes here.
> More importantly, you're using fixed-form source, which led to some
> other problems. First was, my compiler (with strict checking enabled)
> complained about "tab-formating". In other words, the source file
> includes tabs, and as an extension in some compilers (including mine),
> a tab in column 1 counts as only a single character in a 72-charcter
> maximum length input record. You've taken "advantage" of that in
> two ways:
Unfortunately I have no control over the format of the output file as
it is the result of a pre-compiled binary.
Dumb question, but what is an extension?
If I put it in columns 1-5, my compiler (Compaq Visual Fortran) thinks
it is a goto reference, like "goto 10000 and 10000 continue". I use CVF
to type out the source in Windows, scp it over to a Linux machine to
compile on the Intel Fortran Compiler.
> Since you're clearly using an F90/95 compiler, change to free-form
> source code. You can, if you insist, still use tabs for indention,
> but you free yourself from needing to meet column restrictions (you
> can start you statements in column 1 which gains you 6 columns
> right off the bat :-).
OK, I'll do that in my future programs. The fixed column widths were
annoying anyway. Why would anyone want to use a fixed form source code?
> I assume you have an F90 text available, and will have noted that
> F90 uses a trailing "&" to indicate the statement continues on the
> next line (and an optional leading "&" on the continued line).
OK, thanks. I was wondering what I'd do if I ran out of numbers 1-9 on
a mega-long equation.
labels go in columns 1-5, continuation in 6.
>
> Dumb question, but what is an extension?
Something that is non-standard. It is an extra feature that the
compiler supports but which might be unique to that compiler.
>
> If I put it in columns 1-5, my compiler (Compaq Visual Fortran) thinks
> it is a goto reference, like "goto 10000 and 10000 continue". I use CVF
> to type out the source in Windows, scp it over to a Linux machine to
> compile on the Intel Fortran Compiler.
>
>
>>Since you're clearly using an F90/95 compiler, change to free-form
>>source code. You can, if you insist, still use tabs for indention,
>>but you free yourself from needing to meet column restrictions (you
>>can start you statements in column 1 which gains you 6 columns
>>right off the bat :-).
>
>
> OK, I'll do that in my future programs. The fixed column widths were
> annoying anyway. Why would anyone want to use a fixed form source code?
>
>
>>I assume you have an F90 text available, and will have noted that
>>F90 uses a trailing "&" to indicate the statement continues on the
>>next line (and an optional leading "&" on the continued line).
>
>
> OK, thanks. I was wondering what I'd do if I ran out of numbers 1-9 on
> a mega-long equation.
>
--
Gary Scott
mailto:garylscott@sbcglobal dot net
***** 5 Jan: Back from 7 days in Cozumel! *****
Fortran Library: http://www.fortranlib.com
Support the Original G95 Project: http://www.g95.org
-OR-
Support the GNU GFortran Project: http://gcc.gnu.org/fortran/index.html
If you want to do the impossible, don't hire an expert because he knows
it can't be done.
-- Henry Ford
> One negative to watch out for is that list-directed input can easily cause
> confusions when things go wrong. In particular, recall that list-directed
> input does not necessarily stop at the end of a line. If you have too few
> input items on the line, it will go "grabbing" input from the nest line(s).
> This can be very confusing to diagnose - particularly for a non-programmer
> who might be using the application.
One solution is to read into a string, place a slash at the end of the
string, and do the list-directed read from the string. The trailing
slash causes the list-directed read to stop and leave any unread variables
as they were.
The costs are 1) having to specify a maximum line length, so you can size
the string, and 2) bits of ugly code. However it makes for a very
flexible input format, suitable for production code IMHO.
--
pa at panix dot com
C's scanf(), when reading numeric data, will stop at anything non-numeric.
(I am not sure about an 'e', though, when reading floating point data.)
If you arrange the input format such that non-numeric data follows
the possibly unknown amount of numeric data, it works pretty well.
> One solution is to read into a string, place a slash at the end of the
> string, and do the list-directed read from the string. The trailing
> slash causes the list-directed read to stop and leave any unread variables
> as they were.
Does the standard specify that? I remember discussion about READ ending
using the END= exit, and that there is no guarantee on the value in
variables that would have been read before that point.
> The costs are 1) having to specify a maximum line length, so you can size
> the string, and 2) bits of ugly code. However it makes for a very
> flexible input format, suitable for production code IMHO.
For many types of code, I agree.
-- glen
> Pierre Asselin <p...@see.signature.invalid> wrote:
> > One solution is to read into a string, place a slash at the end of the
> > string, and do the list-directed read from the string. The trailing
> > slash causes the list-directed read to stop and leave any unread variables
> > as they were.
>
> Does the standard specify that?
Yes.
> I remember discussion about READ ending
> using the END= exit, and that there is no guarantee on the value in
> variables that would have been read before that point.
Correct. But while there is some similarity in the abstract, they are
different conditions and the standard says different things about them.
OK, how about
READ(STR,*) (X(I),I=1,N)
It would be nice if the value of I was related to the number
of read items. I know it isn't guaranteed for the END= case.
(C's scanf() returns the number of items actually stored, and
also guarantees not to modify ones past where it stopped.)
(snip about END=)
-- glen
I think Ken is talking about the format of your Fortran program, not the
input data file.
<snip>
>> Since you're clearly using an F90/95 compiler, change to free-form
>> source code. You can, if you insist, still use tabs for indention,
>> but you free yourself from needing to meet column restrictions (you
>> can start you statements in column 1 which gains you 6 columns
>> right off the bat :-).
>
> OK, I'll do that in my future programs. The fixed column widths were
> annoying anyway. Why would anyone want to use a fixed form source code?
Fixed form is easy with punched cards; put a '1' in column 7 of the
drum card, hit the skip button, and you're there. A '-' in column 73 of
the drum card makes sure you don't go past column 72.
>
>> I assume you have an F90 text available, and will have noted that
>> F90 uses a trailing "&" to indicate the statement continues on the
>> next line (and an optional leading "&" on the continued line).
>
> OK, thanks. I was wondering what I'd do if I ran out of numbers 1-9 on
> a mega-long equation.
>
The numbers (should you ever decided to use them for continuation again)
can be reused. From what I see on Google, any character besides '0'
(zero) should work. But, as Ken said, don't do it that way.
Louis
> READ(STR,*) (X(I),I=1,N)
[in the case where a / terminates the list-directed read]
> It would be nice if the value of I was related to the number
> of read items. I know it isn't guaranteed for the END= case.
Hmm. I don't recall the answer to that one. Have to look it up...
"A slash.... Any implied-DO variable in the input list is defined
as though enough null values had been supplied for any remaining
input list items."
> The numbers (should you ever decided to use them for continuation again)
> can be reused. From what I see on Google, any character besides '0'
> (zero) should work. But, as Ken said, don't do it that way.
Blank will also not work. Please note that blank is a character like any
other. It does have a few special properties, but it is still a
character. It is not the absense of a character. If you think of it as
the absense of a character, you'll have problems in several areas.
But, as per other commenters, don't use fixed source form without
specific reason (such as maintaining old code, in which case there are
tradeoffs between keeping it as is versus converting).
You can say that again!
Good, I'll give that a try. I just didn't have any idea was whether
this was the mass of a tractor or a fly! :-)
>
>> More importantly, you're using fixed-form source, which led to some
>> other problems. First was, my compiler (with strict checking enabled)
>> complained about "tab-formating". In other words, the source file
>> includes tabs, and as an extension in some compilers (including mine),
>> a tab in column 1 counts as only a single character in a 72-charcter
>> maximum length input record. You've taken "advantage" of that in
>> two ways:
>
> Unfortunately I have no control over the format of the output file as
> it is the result of a pre-compiled binary.
As someone else pointed out, I was commented in your source code
format, not the input or output data files...
One other note: many compilers, I believe Intel's are among them,
assume that a .FOR file (or .for, or .f) is fixed-form source.
use .F90 or .f90 for free-form source. These compilers usually
have an explicit switch, qualifier, or "project" setting to
override this convention, but you'll be happier in the long run
if you just stick to the convention.
[...]
>>> if (i==marked(count2)) then
>>> read (1, '(a50, i10, a8, e13.6)')
>>> 1 a50, timestep(count2), a8, time(count2)
>> Here, the "continuation" character, "1", appears in column 8 (or 9,
>> hard to tell), rather than in columns 1-5 as required in fixed-form
Brain-fade here. As you comment below, columns 1-5 are, of course
the label-field. Column 6 is the continuation character field.
The continuation character can be any letter or digit _except_ "0"
(I don't recall the reason for the exclusion of "0").
Many DEC-heritage compilers, including CVF and IVF, allowed "tab
formatting" in which a tab in column 1 counted for only 1 column
in the 72-column record, _and_ they allowed a "tab+digit" starting
in column 1 to count as a continuation record. I encountered
this is a fellow student's code back in, oh, 1982 or so and was
so appalled by it, I swore never to use it! (As soon as you
replace that tab by blanks, the record (potentially) gets truncated,
etc.)
> The continuation character can be any letter or digit _except_ "0"
It is not restricted to letgters or digits. It may be any character in
the Fortran character set except for blank or zero. In fact the dollar
sign was a popular choice in some circles because it was the only
charcter that was in the Fortran 77 (and 66 I'm pretty sure) character
set, but had no other use except for in comments or literal strings (and
string-like things such as Holleriths). Thus using it minimized the
possibilities for visual confusion.