什么';这套Fortran 90-95代码用于模拟非饱和土壤中的水流有什么错?
我已经工作了很多天,试图找出这段代码的错误。它用于模拟水流通过非饱和土壤。方程组为三对角矩阵形式,采用Thomas算法求解。我有了解决方案,而代码并没有表示它。例如,节点A应该是一条从初始条件大约100厘米到大约20厘米的曲线。这是一个很长的代码,但如果有人在这方面帮助我,我会非常感激什么';这套Fortran 90-95代码用于模拟非饱和土壤中的水流有什么错?,fortran,Fortran,我已经工作了很多天,试图找出这段代码的错误。它用于模拟水流通过非饱和土壤。方程组为三对角矩阵形式,采用Thomas算法求解。我有了解决方案,而代码并没有表示它。例如,节点A应该是一条从初始条件大约100厘米到大约20厘米的曲线。这是一个很长的代码,但如果有人在这方面帮助我,我会非常感激 program EcuacionRichards implicit none !Declaring variables integer, parameter :: nodos = 100 integer :
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
adouble precision
real。嗯,准确地说,可能是这样,但在大多数编译器上,只要编译没有将默认实数变量设置为kinddouble 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编写的第一个程序,所以我没有太多经验)。