Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Fortran 牛顿-拉斐逊迭代-无法迭代_Fortran_Precision_Floating Accuracy_Newtons Method - Fatal编程技术网

Fortran 牛顿-拉斐逊迭代-无法迭代

Fortran 牛顿-拉斐逊迭代-无法迭代,fortran,precision,floating-accuracy,newtons-method,Fortran,Precision,Floating Accuracy,Newtons Method,我不确定这个问题是在这里还是在其他地方(或者根本不是在任何地方)。 我继承了Fortran 90代码,该代码执行牛顿-拉斐逊插值,其中温度的对数与压力的对数进行插值 插值类型为 t = a ln(p) + b 其中a,b被定义为 a = ln(tup/tdwn)/(alogpu - alogpd) 及 这是测试程序。它只针对单个迭代显示。但是实际的程序在k,j和i上运行了三个FOR循环。实际上,pthta是一个3D阵列(k,j,i),thta是一个1D阵列(k) 当您使用数据文件中的真实

我不确定这个问题是在这里还是在其他地方(或者根本不是在任何地方)。 我继承了Fortran 90代码,该代码执行牛顿-拉斐逊插值,其中温度的对数与压力的对数进行插值

插值类型为

t =  a ln(p) + b 
其中a,b被定义为

a = ln(tup/tdwn)/(alogpu - alogpd)

这是测试程序。它只针对单个迭代显示。但是实际的程序在k,j和i上运行了三个FOR循环。实际上,pthta是一个3D阵列(k,j,i),thta是一个1D阵列(k)

当您使用数据文件中的真实数据运行此程序时,resid的值如下

2.7648638933897018E-010 
而且在整个执行过程中没有太大区别。大多数值都在范围内

1E-10 and 1E-12
因此,给定这些值,以下IF条件

IF (abs(resid) .gt. epsln)
永远不会被调用,牛顿-拉斐逊迭代也永远不会执行。所以我研究了两种方法来让它发挥作用。一个是在这两个步骤中删除指数调用

pthta = exp((dlog(thta)-interc-kappa*alogp)/(dltdlp-kappa))

t1 = exp(dltdlp * dlog(pthta)+interc)
i、 e.将所有内容保持在对数空间中,并在牛顿-拉斐逊迭代完成后取指数。这一部分的收敛没有问题

另一种方法是截断

t1 = exp(dltdlp * dlog(pthta)+interc)
当我将其截断为整数时,resid的值从 1E-10至813。我不明白截断该函数调用如何导致如此大的值更改。截断该结果不会导致成功完成。 因此,我不确定哪一种方法更好地继续下去


我怎样才能决定哪一种方法更有效

从研究角度来看,我认为您的第一个解决方案可能是更合适的方法。在物理模拟中,应始终使用属性的对数,根据定义,这些属性始终为正。在上面的代码中,这些是温度和压力。严格正定的物理变量通常会导致计算中的溢出和下溢,无论您使用Fortran或任何其他编程语言,还是任何可能的变量类型。如果有什么事情可以发生,它就会发生

其他物理量也是如此,例如,能量(伽马射线爆发的典型能量约为10^54 ergs)、任意尺寸物体的体积(半径为10米的100维球体的体积约为10^100),甚至概率(许多统计问题中的似然函数的值约为10^{-1000}或更少)。使用正定变量的对数变换将使您的代码能够处理大到~10^10^307的数字(对于双精度变量)

关于代码中使用的Fortran语法,还有一些注意事项:

  • 变量
    RESMAX
    在代码中使用,无需初始化

  • 为变量赋值时,必须适当指定文字常量的类型,否则可能会影响程序结果。例如,以下是在调试模式下使用英特尔Fortran编译器2018编译的原始代码的输出:

      -0.152581477302743        7.31503025786548        259.608693509165
      -3.152934473473579E-002   99474.1999921620
    
    这是同一代码的输出,但所有文字常量都以kind参数
    \u dp
    作为后缀(请参见下面代码的修订版本):

    此答案中修订代码的输出与上述问题中原始代码的输出略有不同

  • 无需使用
    .gt.
    .ge.
    .le.
    .lt.
    ,…)进行比较。据我所知,这些是传统的FORTRAN语法。请使用更吸引人的符号(
    =
    =
    )进行比较

  • Fortran程序中没有必要使用
    GOTO
    语句。这也是传统Fortran。通常,简单优雅的do循环和if块可以替换
    GOTO
    语句,就像下面修订的代码一样

  • 在Fortran中不再需要使用特定种类的内部函数(例如,
    dexp
    dlog
    ,…用于双精度)。在当前的Fortran标准中,几乎所有(也许所有)Fortran内部函数都有通用名(
    exp
    log
    ,…)

下面是这个问题中程序的修订版,它解决了上述所有过时的语法,以及处理非常大或非常小的正定变量的问题(我可能在日志转换某些变量时走得太远了,这些变量永远不会导致溢出或下溢,但我在这里的目的只是展示正定变量日志转换背后的逻辑,以及如何处理它们的算法,而不会在结果中潜在地导致溢出/下溢/错误)

为了进行比较,以下是原始代码的输出:

log-transformed values:
 -0.152580456940175        7.31501855886952        5.55917546888014
  -22.4565579499410        11.5076538974964
 non-log-transformed values:
 -0.152580456940175        7.31501855886952        259.608692604963
 -1.767017293116268E-010   99474.2302854451
 -0.152580456940175        7.31501855886952        259.608692604963
 -8.731149137020111E-011   99474.2302854451

请注意,kappa
2./7.
仅以单精度计算。对于其他常数,如
259.39996337890625
,也是如此,它们被截断为单精度。代码不是很好。@VladimirF-我几乎猜到你会跳到它上面,我犹豫问这个原因:)。这是一段写得很糟糕的代码,但一篇论文是用这段代码进行辩护的,而且科学观点值得发表。因此,我试图从中有所收获。如果你继承了一小段不起作用的代码,有时最好的办法是扔掉它,从一张干净的纸开始,自己写。@agentp-th这和一位同事的建议是一样的。但如果我决定重写它,总的问题仍然存在——我应该在对数空间还是在指数空间中进行NR计算?因为你的问题是以研究为导向的,所以我的普遍观点是以研究为导向的回答是:人们应该始终使用系统属性的对数从定义上来说,你总是积极的
  -0.152580456940175        7.31501855886952        259.608692604963
  -8.731149137020111E-011   99474.2302854451
program test

implicit none
integer,parameter :: dp = SELECTED_REAL_KIND(12,307)
real(kind=dp)  kappa,interc,pres,dltdlp,tup,tdwn
real(kind=dp)  pthta,alogp,alogpd,alogpu,thta,f,dfdp,p1
real(kind=dp) t1,resid,potdwn,potup,pdwn,pup,epsln,thta1
integer i,j,kout,n,maxit,nmax,resmax

real(kind=dp) :: log_resmax, log_pthta, log_t1, log_dummy, log_residAbsolute, sign_of_f
real(kind=dp) :: log_epsln, log_pdwn, log_pup, log_thta, log_thta1, log_p1, log_dfdp, log_f
logical :: residIsPositive, resmaxIsPositive, residIsBigger

log_resmax = log(log_resmax)
resmaxIsPositive = .true.

kappa = 2._dp/7._dp
epsln = 1._dp 
potdwn = 259.39996337890625_dp
potup =  268.41687198359159_dp
pdwn = 100000.00000000000_dp
pup =  92500.000000000000_dp
alogpu =  11.43496392350051_dp
alogpd =  11.512925464970229_dp
thta = 260.00000000000000_dp
alogp = 11.512925464970229_dp

log_epsln = log(epsln) 
log_pup =  log(pup)
log_pdwn = log(pdwn)
log_thta = log(thta)

! known temperature at lower level
tdwn = potdwn * (pdwn/1.e5_dp)**kappa
! known temperature at upper level 
tup = potup *(pup/1.e5_dp)** kappa
! linear change of temperature wrt lnP between different levels
dltdlp = log(tup/tdwn)/(alogpu-alogpd)
! ln(T) value(intercept) where Pressure is 1 Pa and assume a linear
! relationship between P and T
interc = log(tup) - dltdlp*alogpu
! Initial guess value for pressure

!pthta = exp( (log(thta)-interc-kappa*alogp) / (dltdlp-kappa) )
log_pthta = ( log_thta - interc - kappa*alogp ) / ( dltdlp - kappa )

n=0

MyDoLoop: do

    !First guess of temperature at intermediate level
    !t1 = exp(dltdlp * log(pthta)+interc)
    log_t1 = dltdlp * log_pthta + interc

    !Residual error when calculating Newton Raphson iteration(Pascal)
    !resid = pthta - 1.e5_dp*(t1/thta)**(1._dp/kappa)
    log_dummy = log(1.e5_dp) + ( log_t1 - log_thta ) / kappa
    if (log_pthta>=log_dummy) then
      residIsPositive = .true.
      log_residAbsolute = log_pthta + log( 1._dp - exp(log_dummy-log_pthta) )
    else
      residIsPositive = .false.
      log_residAbsolute = log_dummy + log( 1._dp - exp(log_pthta-log_dummy) )
    end if

    print *, "log-transformed values:"
    print *, dltdlp,interc,log_t1,log_residAbsolute,log_pthta
    print *, "non-log-transformed values:"
    if (residIsPositive) print *, dltdlp,interc,exp(log_t1),exp(log_residAbsolute),exp(log_pthta)
    if (.not.residIsPositive) print *, dltdlp,interc,exp(log_t1),-exp(log_residAbsolute),exp(log_pthta)

    !if (abs(resid) > epsln) then
    if ( log_residAbsolute > log_epsln ) then
        n=n+1
        if (n <= nmax) then
            ! First guess of potential temperature given T1 and
            ! pressure level guess
            !thta1 = t1 * (1.e5_dp/pthta)**kappa
            log_thta1 = log_t1 + ( log(1.e5_dp)-log_pthta ) * kappa
            !f = thta - thta1
            if ( log_thta>=thta1 ) then
              log_f = log_thta + log( 1._dp - exp( log_thta1 - log_thta ) )
              sign_of_f = 1._dp
            else
              log_f = log_thta + log( 1._dp - exp( log_thta - log_thta1 ) )
              sign_of_f = 1._dp
            end if
            !dfdp = (kappa-dltdlp)*(1.e5_dp/pthta)**kappa*exp(interc + (dltdlp -1._dp)*log(pthta))
            ! assuming kappa-dltdlp>0 is TRUE always:
            log_dfdp = log(kappa-dltdlp) + kappa*(log(1.e5_dp)-log_pthta) + interc + (dltdlp -1._dp)*log_pthta
            !p1 = pthta - f/dfdp
            ! p1 should be, by definition, positive. Therefore:
            log_dummy = log_f - log_dfdp
            if (log_pthta>=log_dummy) then
                log_p1 = log_pthta + log( 1._dp - sign_of_f*exp(log_dummy-log_pthta) )
            else
                log_p1 = log_dummy + log( 1._dp - sign_of_f*exp(log_pthta-log_dummy) )
            end if
            !if (p1 <= pdwn) then
            if (log_p1 <= log_pdwn) then
               !if (p1 >= pup) then
               if (log_p1 >= log_pup) then
                  log_pthta = log_p1
                  cycle MyDoLoop
               else
                  n = nmax
               end if
            end if
        else
            !if (resid > resmax) resmax = resid
            residIsBigger = ( residIsPositive .and. resmaxIsPositive .and. log_residAbsolute>log_resmax ) .or. &
                            ( .not.residIsPositive .and. .not.resmaxIsPositive .and. log_residAbsolute<log_resmax ) .or. &
                            ( residIsPositive .and. .not. resmaxIsPositive )
            if ( residIsBigger ) then
                log_resmax = log_residAbsolute
                resmaxIsPositive = residIsPositive
            end if
            maxit = maxit+1
        end if
    end if

    exit MyDoLoop

end do MyDoLoop

end program test
log-transformed values:
 -0.152580456940175        7.31501855886952        5.55917546888014
  -22.4565579499410        11.5076538974964
 non-log-transformed values:
 -0.152580456940175        7.31501855886952        259.608692604963
 -1.767017293116268E-010   99474.2302854451
 -0.152580456940175        7.31501855886952        259.608692604963
 -8.731149137020111E-011   99474.2302854451