什么';这套Fortran 90-95代码用于模拟非饱和土壤中的水流有什么错?

什么';这套Fortran 90-95代码用于模拟非饱和土壤中的水流有什么错?,fortran,Fortran,我已经工作了很多天,试图找出这段代码的错误。它用于模拟水流通过非饱和土壤。方程组为三对角矩阵形式,采用Thomas算法求解。我有了解决方案,而代码并没有表示它。例如,节点A应该是一条从初始条件大约100厘米到大约20厘米的曲线。这是一个很长的代码,但如果有人在这方面帮助我,我会非常感激 program EcuacionRichards implicit none !Declaring variables integer, parameter :: nodos = 100 integer :

我已经工作了很多天,试图找出这段代码的错误。它用于模拟水流通过非饱和土壤。方程组为三对角矩阵形式,采用Thomas算法求解。我有了解决方案,而代码并没有表示它。例如,节点A应该是一条从初始条件大约100厘米到大约20厘米的曲线。这是一个很长的代码,但如果有人在这方面帮助我,我会非常感激

program EcuacionRichards

implicit none

!Declaring variables

integer, parameter :: nodos = 100
integer :: i, it, max_it, nodo_a, nodo_b, nodo_c, nodo_d, it_bajo, it_alto
double precision, dimension(1:nodos) :: H, H_ant, C, K, theta, theta_ant, aa, bb, cc, dd, rr, th_ant
double precision :: dz, zbot, tfin, dt, rz, Ksup, Kinf, t, th_lisimetro, h_lisimetro  
double precision :: q_ent, tol_h, tol_th, cambio_h, cambio_th
double precision :: mult_alto, mult_bajo, maxdt, mindt, qlibre
logical lisimetro

!Hydraulic Parameters
double precision :: theta_sat=0.43      !cm/cm 
double precision :: theta_res=0.078     !cm/cm
double precision :: alpha=0.0325        !1/cm
double precision :: n=1.346
double precision :: m
double precision :: K_sat=86.4          !cm/d

!Grid and iteration parameters
lisimetro=.true.
dt=0.01             !days
zbot=160            !depth of the column in cm
dz=zbot/nodos       !cm
tfin=30             !days
max_it=500          !max number of Picard iterations
tol_h=0.1           !tolerance for H iteration, cm
tol_th=0.001        !tolerance for theta iteration, 1/1
it_bajo=3           !minimum recommended number of iterations
it_alto=7           !maximum recommended number of iterations
mult_bajo=1.3       !time multiplicator for low iterations
mult_alto=0.7       !time multiplicator for low iterations
maxdt=0.5           !max value for dt 
mindt=0.001         !min value for dt 
m=1-1/n

!Initializing other variables
th_lisimetro=0.32
h_lisimetro=HfTH(th_lisimetro)

nodo_a=nodos
nodo_b=2*nodos/3
nodo_c=nodos/3
nodo_d=1

!*********Initial Conditions************************************************************

call theta_ini(theta,nodos) !Fill array with initial moisture values
do i=1,nodos
    H(i)=HfTH(theta(i))
    call actualiza(H(i), theta(i), C(i), K(i))
end do

!************* OPEN WRITING FILES ************************************************
open(unit=1,file='succion2.txt')
open(unit=2,file='humedad2.txt')
open(unit=3,file='conducti2.txt')
open(unit=4,file='parametr2.txt')
write(4,'("dt(días) =",f7.4)') dt 
write(4,'("dz(cm) =",f7.4)') dz
write(4,'("nodos =",i5)') nodos
write(4,'("altura(cm) =",f8.3)') zbot
write(4,'("tfin(días) =",f7.2)') tfin
write(4,'("theta_sat =",f7.4)') theta_sat
write(4,'("theta_res =",f7.4)') theta_res
write(4,'("K_saturada =",g11.3)') K_sat
write(4,'("n =",f7.4)') n
write(4,'("m =",f7.5)') m
write(4,'("alpha =",f7.5)') alpha
write(4,'("max_it =",i4)') max_it
close(4)
write(1,*) "T(días) H_a(cm) H_b(cm) H_c(cm) H_d(cm)"
write(2,*) "T(días) th_a(cm) th_b(cm) th_c(cm) th_d(cm)"
write(3,*) "T(días) K_a(cm/d) K_b(cm/d) K_c(cm/d) K_d(cm/d)" 


!*************TIME LOOP**********************************************************************************************
t=0.d0
do while ((t.le.tfin).and.(dt.gt.0))
    rz=dz/dt
    t=t+dt
    theta_ant=theta !Previous time
    !Water flow that enters at the top (constant)
    q_ent=0.1       !cm/dia
!*************     PICARD LOOP              ******************************************
Picard:do it=1,max_it

            if(it.eq.max_it) pause "MAXIMUM ITERATIONS REACHED"

            !Interior Nodes
            do i=2, nodos-1
                 Ksup=2*(K(i+1)*K(i))/(K(i+1)+K(i))
                 Kinf=2*(K(i-1)*K(i))/(K(i-1)+K(i))
                 aa(i)=-Kinf/dz !K(i-1/2)
                 cc(i)=-Ksup/dz !K(i+1/2)
                 bb(i)=rz*C(i)-aa(i)-cc(i)
                 rr(i)=rz*C(i)*h(i)-rz*(theta(i)-theta_ant(i))+Ksup-Kinf
            end do

            !Inferior Node
            if (lisimetro) then
              !Changing inferior node
              if (theta(1).lt.th_lisimetro) then
                    !Water flow 0, Neumann
                    Ksup=2*(K(1)*K(2))/(K(1)+K(2))
                    aa(1)=0
                    cc(1)=-Ksup/dz
                    bb(1)=-cc(1)
                    rr(1)=Ksup
              else
                    !H(1)=0 condition, Dirichlet
                    Ksup=2*(K(1)*K(2))/(K(1)+K(2))
                    aa(1)=0
                    bb(1)=1
                    cc(1)=0
                    rr(1)=h_lisimetro
                    aa(2)=0
                    rr(2)=rr(2)+Ksup/dz*(h_lisimetro)
              end if
            else
              !Inferior node, free drainage, Neumann
              Ksup=2*(K(1)*K(2))/(K(1)+K(2))
              qlibre=-K(1)
              aa(1)=0
              cc(1)=-Ksup/dz
              bb(1)=-cc(1)
              rr(1)=Ksup+qlibre
            end if

            !Superior node, known water flow
            Kinf=2*(K(nodos)*K(nodos-1))/(K(nodos)+K(nodos-1))
            aa(nodos)=-Kinf/dz
            cc(nodos)=0
            bb(nodos)=0.5*rz*C(nodos)-aa(nodos)
            rr(nodos)=0.5*rz*C(nodos)*h(nodos)-0.5*rz*(theta(nodos)-theta_ant(nodos))-Kinf-q_ent

            call tridiag(aa,bb,cc,rr,dd,nodos)

            !Suction modification and H functions actualization
            h_ant=h
            th_ant=theta !Save iteration
            h=dd         !Advance to next iteration 
            do i=1,nodos
                call actualiza(H(i),theta(i), C(i), K(i))
            end do

            !End of iterations condition
            cambio_h=maxval(dabs(h-h_ant))
            cambio_th=maxval(dabs(theta-th_ant))

            if((cambio_h.lt.tol_h).and.(cambio_th.lt.tol_th)) then

                if(.true.) then !(t.eq.tprint)
                write (1,'(f8.3,f9.3,f9.3,f9.3,f9.3)') t,H(nodo_a),H(nodo_b),H(nodo_c),H(nodo_d)
                write (2,'(f8.3,f7.4,f7.4,f7.4,f7.4)') t,theta(nodo_a),theta(nodo_b),theta(nodo_c),theta(nodo_d)
                write (3,'(f8.3,g11.4,g11.4,g11.4,g11.4)') t,k(nodo_a),k(nodo_b),k(nodo_c),k(nodo_d)
                end if

                if (it.lt.it_bajo) dt=min(dt*mult_bajo,maxdt)
                if (it.gt.it_alto) dt=max(dt*mult_alto,mindt)

                exit Picard

            else
                cycle Picard
            end if
       end do Picard !Picard loop end
       if ((tfin-t).le.1E-4) t=huge(1.d0)
end do
!Time Loop End***************************************************************
!******** Close files
close(1)
close(2)
close(3)

!********END OF PROGRAM**********************************************************
!******************************************************************************
!Subroutines and functions
contains

!Initial moistures assignment
subroutine theta_ini(theta,nodos)
integer :: nodos
double precision, dimension(1:nodos) :: theta
integer i
do i=1, nodos
    theta(i)=0.30
end do
end subroutine theta_ini

!Subroutine that actualizes salues according to pressure
subroutine actualiza(p,theta,c,k)
    double precision p, theta, c, k
    double precision se, te
    if(p.lt.0) then           
                  te=1+(-alpha*p)**n
                  se=te**(-m)
                  theta=theta_res+(theta_sat-theta_res)*se
                  K=K_sat*se**(0.5)*(1-(1-se**(1/m))**m)**2
                  c=((alpha**n)*(theta_sat-theta_res)*n*m*(-p)**(n-1))/(te**(m+1)) !d(theta)/dh
    else 
                  theta=theta_sat
                  K=K_sat
                  c=0
    end if
    return
end subroutine actualiza

!Tridiag(alpha,beta, gamma, Resto, delta, nodos)
      subroutine tridiag(a,b,c,d,x,n)
      implicit none
!        a - sub-diagonal (means it is the diagonal below the main diagonal)
!        b - the main diagonal
!        c - sup-diagonal (means it is the diagonal above the main diagonal)
!        d - right part
!        x - the answer
!        n - number of equations

        integer,intent(in) :: n
        double precision,dimension(n),intent(in) :: a,b,c,d
        double precision,dimension(n),intent(out) :: x
        double precision,dimension(n) :: cp,dp
        double precision :: m
        integer i

! initialize c-prime and d-prime
        cp(1) = c(1)/b(1)
        dp(1) = d(1)/b(1)
! solve for vectors c-prime and d-prime
         do i = 2,n
           m = b(i)-cp(i-1)*a(i)
           cp(i) = c(i)/m
           dp(i) = (d(i)-dp(i-1)*a(i))/m
         enddo
! initialize x
         x(n) = dp(n)
! solve for x from the vectors c-prime and d-prime
        do i = n-1, 1, -1
          x(i) = dp(i)-cp(i)*x(i+1)
        end do

    end subroutine tridiag

!Head in terms of moisture
Function HfTH(humedad)
    double precision HfTH
    double precision humedad
    if (humedad.lt.theta_sat) then
              HfTH=-1/alpha*(((humedad-theta_res)/(theta_sat-theta_res))**(-1/m)-1)**(1/n) !cm
    else
              HfTH=0
    end if
    Return
end function HfTH

end program EcuacionRichards

我可以看到你的代码有很多问题,但我的注意力有限,所以这里是最令人震惊的

您将一组变量声明为
双精度
,例如,
theta_sat
,但您使用默认类型的文本初始化它们。声明

double precision :: theta_sat=0.43      !cm/cm 
不使
0.43
a
double precision
real。嗯,准确地说,可能是这样,但在大多数编译器上,只要编译没有将默认实数变量设置为kind
double precision
,就不会。几乎可以肯定的是,
0.43
是一个4字节的实数,
theta_sat
是一个8字节的实数,您不能依靠编译器将
theta_sat
设置为最接近
0.43
的8字节值

在现代Fortran中,
双精度
仍然可用于向后兼容,但不赞成使用种类类型指定变量的种类。因此,关于如何做到这一点的建议非常多。我最喜欢的是使用内在模块
iso_fortran_env
中定义的常量,如下所示:

use, intrinsic :: iso_fortran_env
real(real64) :: theta_sat=0.43_real64      !cm/cm 
然后像这样声明变量:

use, intrinsic :: iso_fortran_env
real(real64) :: theta_sat=0.43_real64      !cm/cm 
请注意,将种类规范
\u real64
附加到该值

你的算法是否足够敏感,以至于你的这个错误会对结果产生重大影响,我不知道


最后,您告诉我们该程序不正确,但您对其不正确的方式保持沉默。

您所说的“不代表它”是什么意思?在你要求调试之前,你应该尝试一下。我建议你把它分成几个部分(子例程和函数),并尽可能地测试这些部分。也许从主程序中分离出更多的子程序。我不喜欢子程序作为“包含”主程序。。。最好将其放入一个模块并使用该模块。他们继承变量的当前方式。。。这可能会让人非常困惑。看起来好像有人从维基百科复制了我的三对角解算器(不是我做的算法,只是之前发布的版本不正确,所以我更正了)。我已经调试过了,如果我发现了错误,我不会在这里问任何问题;)我使用了另一个三对角解算器,所以为了测试这是否是错误,我在代码中复制了维基百科中的一个。这不是错误,但我只是没有把它改回去。非常感谢你,我会努力改正的。该程序是不正确的,因为我知道正确的结果是什么(由HYDRUS生成),我正试图用它们验证该程序。例如,对于节点A,对于H,程序获得的结果从初始条件(-100 cm aprox)下降到小于-9000 cm,而实际上它从-100上升到-20 cm。另一件事,根据dz的不同,程序会发生算术溢出错误。例如,它适用于dz=1 cm,但在dz=1,6 cm时会塌陷。这可能与您以前回答我的问题有关吗?您当然需要了解您的实现是否稳定。如果您不完全熟悉这个概念,请阅读维基百科的文章。您告诉我们更改
dz
的影响,我怀疑您的实现不稳定。您必须敏锐地理解程序的输出如何改变输入和参数的微小变化,以及这些变化是否对您正在实现的数学模型有效。fortran 90-95中提供了“use,infrant::iso_fortran_env”命令?不,该模块是在fortran 2003中引入的,但是大多数常见Fortran编译器的最新版本都提供了这个功能,我使用的是一个名为Plato的编译器,它不支持这个命令。您建议使用哪种Fortran编译器?(这是我用Fortran编写的第一个程序,所以我没有太多经验)。