From: ivan martin on
Greetings all,

following dpb's advice from my previous question: 'calling a subroutine with a "string constant"':

<quote>
As you say, however, it doesn't make a whole lot of sense to code a
constant as the argument in the call; it would seem more logical for
that to be a variable read in or somehow else set. But, that wasn't the
question asked.... :)
</quote>

I'm sorry for bringing this topic up again. I'm trying to make work some prediction program
as part of my thesis work which deals in approximating performance data of models (geometrically
similar, but small) and of real life structures (large).

And I'm looking for your expertise advice, for I'm still adapting to fortran95 which I'm also
learning in the process.

a) my wish is to make a subroutine for plotting which can be called from anywhere in the program,
with as little modifications as possible to the rest of the program. It is based on german dislin
library, which I have been using so far and am very satisfied with.
/ I must notice, there isn't all that much fortran graphic libraries. Which I think is very damaging
in view of making fortran a language for rapid development of numerically demanding programs. /

I have the need for it for when I run into some strange results data, that I can relatively quickly
plot it.

I'm trying to make it in a way then upon calling I give the routine the vectors (arrays) with data,
chart title and axis titles (maybe some other small parameters, but they don't matter for now).

The titles are always the same (i.e. they do not change with different runs of the program), but
still I cannot code them in the routine itself, since I mean to use the routine for several plots
which themselves have different titles. So I need a way to give the title name while calling the
routine itself.

How would you go about it (not necessary code specifically, but more in a way of
organization) ?

For example; below is an example program of dislin's plotting (this one plots 6 different plots on
screen; an example I copied off dislin's examples page). I thought of taking everything
(starting from SETPAG) into a subroutine, so I can call it again and again. I'm trying to
make it as much "self dependant" as possible (avoid as much as I can of hardcoding any
"magic numbers" and constants) in a program.

I'm hoping to accomplish something like:
call plot(xdata, ydata, 'my chart title', 'my x title', 'my y title')

i.e. just to give it the data, and the necessary strings, but now I see this is a bad approach. But
I'm not sure how to make it in a different manner, yet to avoid as many bad approaches as possible.

! ##############
PROGRAM EXA_4
USE DISLIN
IMPLICIT NONE

REAL, DIMENSION (16) :: &
X = (/0.,1.,3.,4.5,6.,8.,9.,11.,12.,12.5,13.,15.,16.,17.,19.,20./),&
Y = (/2.,4.,4.5,3.,1.,7.,2.,3.,5.,2.,2.5,2.,4.,6.,5.5,4./)
CHARACTER (LEN = 60) :: CTIT = 'Interpolation Methods'
CHARACTER (LEN = 6), DIMENSION (6) :: &
CPOL = (/'SPLINE','STEM ','BARS ','STEP ','STAIRS', &
'LINEAR'/)
INTEGER :: I,NX,NY,NYA=2700,IC

CALL SETPAG('DA4P')
CALL DISINI()
CALL COMPLX()
CALL PAGERA()
CALL INCMRK(1)
CALL HSYMBL(25)
CALL TITLIN(CTIT,2)
CALL AXSLEN(1500,350)
CALL SETGRF('LINE','LINE','LINE','LINE')
IC=INTRGB(1.0,1.0,0.0)
CALL AXSBGD(IC)

DO I=1,6
CALL AXSPOS(350,NYA-(I-1)*350)
CALL POLCRV(CPOL(I))
CALL MARKER(16)

CALL GRAF(0.,20.,0.,5.,0.,10.,0.,5.)
NX=NXPOSN(1.)
NY=NYPOSN(8.)
CALL MESSAG(CPOL(I),NX,NY)

CALL COLOR('RED')
CALL CURVE(X,Y,16)
CALL COLOR('FORE')

IF(I.EQ.6) THEN
CALL HEIGHT(50)
CALL TITLE()
END IF
CALL ENDGRF()
END DO

CALL DISFIN()
STOP
END PROGRAM EXA_4

! ##############

b) the second thing which troubles me is of numerical character. In calling the GRAF routine
in this example, it requires of me to provide several numeric parameters:
* lower and upper limit of X data for plotting
* and the first axis label and increment between labels
* (the same for Y)

The lower and upper limits of X I know how to determine; just move through the vector of data,
and determine min. and max. values. But the first label and increment between labels baffle me.

I mentioned earlier that my program is using the same mathematics for both models and real
life constructions. As such, I do not know in which ranges my results will be. They can be between
12 and 123 (something), and between 1234 and 12345 (something; N or lbf, for example).

With that in mind, what is the usual practice as to determine the axis boundaries and increment
(step) so the plot has nice "round" limits. What I mean ?
I'm trying to avoid the plot starting at 1234 and having the step of 111.11 ( (12345-1234)/100 ),
for example. It is natural that the step should be 100, and according to it, the lower boundary 1230
and upper 12400. Or something similar, but I'm sure you understand my meaning.

I'd really appreciate all advice all of you can give on any of the above; if you have time to help,
of course.

My apologies for the length of the post.

With regards,
Ivan
From: robin on
"ivan martin" <i.martin(a)invalid.com> wrote in message news:ca2ku5h2orlgchijufc54ehenns6nkg73p(a)4ax.com...
| Greetings all,
|
| following dpb's advice from my previous question: 'calling a subroutine with a "string constant"':
|
| <quote>
| As you say, however, it doesn't make a whole lot of sense to code a
| constant as the argument in the call;

There's nothing wrong with coding a constant as an argument.
It's perfectly valid, and you can compare passing a constant string
with passing a numeric constant, which is the more common.
Passing numeric constants has been done since the beginning of FORTRAN.

| it would seem more logical for
| that to be a variable read in or somehow else set. But, that wasn't the
| question asked.... :)
| </quote>
|
| I'm sorry for bringing this topic up again. I'm trying to make work some prediction program
| as part of my thesis work which deals in approximating performance data of models (geometrically
| similar, but small) and of real life structures (large).
|
| And I'm looking for your expertise advice, for I'm still adapting to fortran95 which I'm also
| learning in the process.
|
| a) my wish is to make a subroutine for plotting which can be called from anywhere in the program,
| with as little modifications as possible to the rest of the program. It is based on german dislin
| library, which I have been using so far and am very satisfied with.
| / I must notice, there isn't all that much fortran graphic libraries. Which I think is very damaging
| in view of making fortran a language for rapid development of numerically demanding programs. /
|
| I have the need for it for when I run into some strange results data, that I can relatively quickly
| plot it.
|
| I'm trying to make it in a way then upon calling I give the routine the vectors (arrays) with data,
| chart title and axis titles (maybe some other small parameters, but they don't matter for now).
|
| The titles are always the same (i.e. they do not change with different runs of the program), but
| still I cannot code them in the routine itself, since I mean to use the routine for several plots
| which themselves have different titles. So I need a way to give the title name while calling the
| routine itself.
|
| How would you go about it (not necessary code specifically, but more in a way of
| organization) ?
|
| For example; below is an example program of dislin's plotting (this one plots 6 different plots on
| screen; an example I copied off dislin's examples page). I thought of taking everything
| (starting from SETPAG) into a subroutine, so I can call it again and again. I'm trying to
| make it as much "self dependant" as possible (avoid as much as I can of hardcoding any
| "magic numbers" and constants) in a program.
|
| I'm hoping to accomplish something like:
| call plot(xdata, ydata, 'my chart title', 'my x title', 'my y title')

This is how it's normally done.

| i.e. just to give it the data, and the necessary strings, but now I see this is a bad approach.

On the contrary, this is a good approach.


From: glen herrmannsfeldt on
ivan martin <i.martin(a)invalid.com> wrote:

> following dpb's advice from my previous question: 'calling a
> subroutine with a "string constant"':

> <quote>
> As you say, however, it doesn't make a whole lot of sense to code a
> constant as the argument in the call; it would seem more logical for
> that to be a variable read in or somehow else set. But, that wasn't the
> question asked.... :)
> </quote>

It was fairly common in the Fortran 66 days.

Consider routines in a graphics library, such as SYMBOL for
the Calcomp plotters. SYMBOL draws a character string in
a specified size at a specified position on the plot.
It is also called by the AXIS routine draw the title and
label the axis tic marks.

Also, I believe that subroutine argments and DATA statements
were the places that Fortran 66 allowed hollerith constants.
Some compilers allowed apostrophed strings in their place.

-- glen
From: Charles on

"ivan martin" <i.martin(a)invalid.com> wrote in message
news:ca2ku5h2orlgchijufc54ehenns6nkg73p(a)4ax.com...
[snip]
>
> b) the second thing which troubles me is of numerical character. In
> calling the GRAF routine
> in this example, it requires of me to provide several numeric parameters:
> * lower and upper limit of X data for plotting
> * and the first axis label and increment between labels
> * (the same for Y)
>
> The lower and upper limits of X I know how to determine; just move through
> the vector of data,
> and determine min. and max. values. But the first label and increment
> between labels baffle me.
[snip]
> Ivan

You have to compute a suitable interval, something like
(untested, somewhat obsfuscated):

integer steps(3) /1.0,2.0,5.0/ ! Use ... 0.2, 0.5, 1.0, 2.0, 5.0, 10.0,
....
integer nstep /10/ ! Approximate number of steps
integer logstep ! Approximate logarithm (base 10) of
the desired step
real xstep, xstart, xend ! Step, start and end to use
real xmin, xmax ! Min and max of the x data
....
logstep = floor(log10((xmax-xmin)/float(nstep)+1.0e-20)*3.0+0.5)
step = steps(mod(logstep+999),3)+1)*10.0**floor(logstep/3.0+1.0e-5)
xstart = step*floor(xmin/step)
xend = step*ceiling(xmax/step)

The first 1.0e-20 is to avoid taking the log of zero for xmin = xmax.
It should be significantly smaller than the smallest xmax-xmin you
expect to see in real data. The +999 is to make sure the mod is working
on a positive number. It should be a multiple of three that is greater than
minus three times the log 10 of the number above (ie 60 in this case). The
1.0e-5 is to cope with rounding errors in the division so that (for example)
6/3.0 does not become 1.9999.... and get truncated to 1 instead of 2;
almost any positive value less than about 0.15 can be used. Floor is used
instead of integer division so negative numbers work correctly. If your
compiler does not have flooor and ceiling, you can use other intrinsics
such as nint(x-0.5) or nint(x-0.5+1.0e-20) instead of floor(x).

Alternatively, and a lot clearer and longer, you can use the log10
of range/nstep to scale range/nstep to 1.0..10.0, and then use a chain
of if statements to use 1.0 for (say) 1.0..1.5; 2.0 for 1.5..3.0; 5.0 for
3.0..7.0; and 10.0 for 7.0..10.0, all re-scaled using the same power
of 10 that was used for the initial scaling. That is effectively what the
code above does, except that it multiplies the log by 3 and used
mod to pick the appropriate value from 1, 2 and 5, and divides by
three to get the power of 10, resulting in much shorter, but
obsfuscated, code.

Similar code can be used to generate other "Nice" intervals,
such as (if you prefer) 1,2,4,5,10 or 1,3,10, multiplied by
powers of 10.


From: glen herrmannsfeldt on
Charles <C.Sanders(a)deletethis.bom.gov.au> wrote:
(snip on plot scaling)

> You have to compute a suitable interval, something like
> (untested, somewhat obsfuscated):

(snip)
> The first 1.0e-20 is to avoid taking the log of zero for xmin = xmax.
> It should be significantly smaller than the smallest xmax-xmin you
> expect to see in real data. The +999 is to make sure the mod is working
> on a positive number. It should be a multiple of three that is greater than
> minus three times the log 10 of the number above (ie 60 in this case).

My old favorite was the Calcomp SCALE routine. There are a few
different ways to go wrong, and it seemed like they got most of
them right. Among others, if the range goes from negative to
positive it is a little tricky to get right and also have zero
on a tic mark. In the days of plotting on graph paper, with
10 lines per inch, it was nice to use only 1, 2, and 5, with
the appropriate power of 10, as scale factors.

> The
> 1.0e-5 is to cope with rounding errors in the division so that (for example)
> 6/3.0 does not become 1.9999.... and get truncated to 1 instead of 2;
> almost any positive value less than about 0.15 can be used. Floor is used
> instead of integer division so negative numbers work correctly. If your
> compiler does not have flooor and ceiling, you can use other intrinsics
> such as nint(x-0.5) or nint(x-0.5+1.0e-20) instead of floor(x).

I believe ones I used to know subtracted a small amount such that if
the range was just slightly over it wouldn't need to go up to the next
scale factor. The 1.9999 shouldn't be so bad, but consider the case
where the tentative scale factor is 2.00001. The last point will
still be within the width of the line, so that isn't so bad.
If you go up to the next, 4 or 5, you waste most of the plot area.

> Alternatively, and a lot clearer and longer, you can use the log10
> of range/nstep to scale range/nstep to 1.0..10.0, and then use a chain
> of if statements to use 1.0 for (say) 1.0..1.5; 2.0 for 1.5..3.0; 5.0 for
> 3.0..7.0; and 10.0 for 7.0..10.0, all re-scaled using the same power
> of 10 that was used for the initial scaling.

Or a DO loop over an array of choices.

> That is effectively what the
> code above does, except that it multiplies the log by 3 and used
> mod to pick the appropriate value from 1, 2 and 5, and divides by
> three to get the power of 10, resulting in much shorter, but
> obsfuscated, code.

It seems that it might be a little off, as 2 and 5 aren't so
close to 10.**(1./3.) and 10.**(2./3.)

> Similar code can be used to generate other "Nice" intervals,
> such as (if you prefer) 1,2,4,5,10 or 1,3,10, multiplied by
> powers of 10.

Different choices for different people.

-- glen