For Programmers: Free Programming Magazines  


Home > Archive > Fortran > July 2006 > making subroutine more numerically stable









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author making subroutine more numerically stable
Bart Vandewoestyne

2006-07-25, 8:00 am

I have the following subroutine which -for the ones that want to
know- represents a certain variable transformation to be used in
numerical integration:

subroutine f_mori(t, y, jacobian_det)
real(kind=qp), dimension(:), intent(in) :: t
real(kind=qp), dimension(:), intent(out) :: y
real(kind=qp), intent(out) :: jacobian_det

real(kind=qp) :: a, b

a = pi/2.0_qp
b = pi/4.0_qp

y = 0.5_qp*tanh( a*sinh(b*(1.0_qp/(1.0_qp-t) - 1.0_qp/t)) ) + 0.5_qp

jacobian_det = product( 0.5_qp*(1 - tanh(a*sinh(b * (1/(1-t) - 1/t)))**2) &
* a * cosh(b*(1/(1-t) - 1/t)) &
* b * (1/(1 - t)**2 + 1/t**2) )

end subroutine f_mori

Trying to evaluate this function for t=-1 to 2 in steps of 3.0/1000,
results in arithmetic exceptions with g95 and the F compiler. This happens
when the argument for the sinh function becomes too large.

Now my question is: what would be the best way to make this subroutine more
robust against such arithmetic exceptions? I assume something like

if the argument for the sinh function becomes larger than MAX_ARG then
set result to MAX
else
simply evaluate the sinh function, there is no danger for overflow
end if

Could this be the way to do it? And if `yes', then what do i take for MAX_ARG
and MAX? Something like MAX_ARG = log(huge(1.0_qp)/2) and MAX = huge(1.0_qp)/2
or so?

Any suggestions welcome!
Bart

--
"Share what you know. Learn what you don't."
highegg

2006-07-25, 7:00 pm

> y = 0.5_qp*tanh( a*sinh(b*(1.0_qp/(1.0_qp-t) - 1.0_qp/t)) ) + 0.5_qp
>
> jacobian_det = product( 0.5_qp*(1 - tanh(a*sinh(b * (1/(1-t) - 1/t)))**2) &
> * a * cosh(b*(1/(1-t) - 1/t)) &
> * b * (1/(1 - t)**2 + 1/t**2) )


The computation of y shouldn't overflow if the implementation of
TANH is smart enough to deal with infinities -
I'm sure that's the case of g95 and probably most compilers (where Inf
is available).
As for the derivatives, you-re getting NaNs because of multiplying
zeros
(1-tanh(...)**2) by Infs cosh(...)
you may, however, notice that
1-tanh(x)**2 = cosh(x)**(-2)
for any x and simplify the derivatives to the form
0.5_qp* a / cosh(b*(1/(1-t) - 1/t)) * b * (1/(1 - t)**2 + 1/t**2) )

which will probably behave better. But I guess you may
still end with PRODUCT multiplying an array of numbers with zeros and
Infs present,
thus the result will be NaN anyway.

Jaroslav

Terence

2006-07-27, 7:00 pm

Check your source.

> jacobian_det = product( 0.5_qp*(1 - tanh(a*sinh(b * (1/(1-t) - 1/t)))**2) &
> * a * cosh(b*(1/(1-t) - 1/t)) &
> * b * (1/(1 - t)**2 + 1/t**2) )


I don't like the fact you are mutltiplying the expression of line 1 by
the expressions of line 2 and line 3 to calculate thevariable "
jacobian_det".

One possibility is that one or both of those line-starting asterisks
should be a plus sign, because you have

( (term) *a* (term) * b * (term) .)

It would be unusual to separate the a and b multipliers.

Possibility number two is, are you sure you should not have:
jacobian_det = product( term1,term2,term3)

and product( ) is a real function?
In which case those leading asterisks should be commas.


Also you use terms 1/(1-t) and 1/t. So in ALL cases integer variable t
should NOT be either 0 or 1, which are two of your step values for this
stepping variable.

As to finding how to improve whatever you are doing, try forming
intermediate simpler terms and seeing where the exceptions occur.

Terence Wright

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com