Skip to content

Commit f500ec3

Browse files
authored
Merge pull request #626 from nmizukami/advection-duffsion_improved
advection-diffusion equation solver
2 parents caff8fa + d4e3eeb commit f500ec3

3 files changed

Lines changed: 92 additions & 50 deletions

File tree

route/build/src/advection_diffusion.f90

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MODULE advection_diffusion
22

3-
! solving advection-diffusion equation
3+
! Numerical solution for 1D advection-diffusion equation
44

55
USE nrtype
66
USE public_var, ONLY: iulog ! i/o logical unit number
@@ -14,7 +14,7 @@ MODULE advection_diffusion
1414
CONTAINS
1515

1616
! *********************************************************************
17-
! subroutine: solve advection-diffuision equation (ade)
17+
! subroutine: solve advection-diffuision equation (ade) per a reach
1818
! *********************************************************************
1919
SUBROUTINE solve_ade(reach_length, & ! input: Reach length [m]
2020
nMolecule, & ! input: number of sub-segments
@@ -26,23 +26,30 @@ SUBROUTINE solve_ade(reach_length, & ! input: Reach length [m]
2626
FluxPrev, & ! input: Flux at previous time step [unit of quantity]
2727
FluxLocal, & ! inout: Flux soloved at current time step [unit of quantity]
2828
verbose, & ! input: reach index to be examined
29-
ierr,message)
29+
advec_scheme, & ! optional input: advection term descretization: 1->central difference, 2->upwind method
30+
downstreamBC, & ! optional input: downstream end B.C. 1->Neumann, 2->absorbing
31+
wc, & ! optional input: advection term descretization weight: 0=fully explict to 1=fully implicit
32+
wd & ! optional input: diffusion term descretization weight: 1=fully explict to 2=fully implicit
33+
)
3034
! ----------------------------------------------------------------------------------------
3135
! Solve linearlized advection diffusive equation per reach and time step.
3236
! dQ/dt + ck*dQ/dx = dk*d2Q/dx2 + FluxLat - a)
3337
!
3438
! ck (velocity) and dk (diffusivity) are given by input argument
3539
!
3640
! 1) dQ/dt = (Q(t,x) - Q(t-1,x))/dt
37-
! 2) dQ/dx = [(1-wck)(Q(t-1,x+1)-Q(t-1,x-1)) + wck*(Q(t,x+1)-Q(t,x-1))]/2dx (using central difference)
41+
! 2) dQ/dx = [(1-wck)(Q(t-1,x+1)-Q(t-1,x-1)) + wck*(Q(t,x+1)-Q(t,x-1))]/2dx (central difference)
42+
! or
43+
! [(1-wck)(Q(t-1,x+1)-Q(t-1,x-1)) + wck*(Q(t,x+1)-Q(t,x-1))]/2dx (upwind)
3844
! 3) d2Q/dx2 = [(1-wdk)(Q(t-1,x+1)-2Q(t-1,x)+Q(t-1,x-1)) + wdk*(Q(t,x+1)-2Q(t,x)+Q(t,x-1))]/2dx
3945
!
40-
! upstream B.C: Dirchlet BC with inflow at current time-step,t, from upstream basin
41-
! downstream B.C: Neumann BC with prescribed quantity gradient (Sbc)
46+
! 4) downstream B.C: Neumann BC with prescribed quantity gradient (Sbc)
4247
! dQ/dx|x=N = Sbc -> 4) Q(t,N)-Q(t,N-1)) = Sbc*dx
43-
! Another downstream B.C option is absorbing boundary condition
48+
! Another downstream B.C option: absorbing boundary condition
4449
! dQ/dt|x=N + ck*dQ/dx|x=N = 0
4550
!
51+
! upstream B.C: Dirchlet BC with inflow at current time-step,t, from upstream basin
52+
!
4653
! Inserting 1), 2), 3) and 4) into a) and moving Q(t,*) terms to left-hand side and Q(t-1,*) terms to the righ-hand side
4754
! results in tridiagonal matrix equation A*Q = b
4855
! where A is [N x N] matrix, Q is [N x 1] vector to be solved (next time step Q) and b is [N x 1] vector
@@ -65,8 +72,10 @@ SUBROUTINE solve_ade(reach_length, & ! input: Reach length [m]
6572
real(dp), intent(in) :: FluxPrev(nMolecule) ! sub-reach quantity at previous time step [m3/s]
6673
real(dp), intent(out) :: FluxLocal(nMolecule,0:1) ! sub-reach & sub-time step quantity at previous and current time step [m3/s]
6774
logical(lgt), intent(in) :: verbose ! reach index to be examined
68-
integer(i4b), intent(out) :: ierr ! error code
69-
character(*), intent(out) :: message ! error message
75+
integer(i4b), optional, intent(in) :: advec_scheme ! advection term descretization: 1->central diff.(default), 2->upwind method
76+
integer(i4b), optional, intent(in) :: downstreamBC ! downstream end B.C. 1->Neumann (default), 2->absorbing
77+
real(dp), optional, intent(in) :: wc ! advection term weight
78+
real(dp), optional, intent(in) :: wd ! diffusion term weight
7079
! Local variables
7180
real(dp) :: Cd ! Fourier number
7281
real(dp) :: Ca ! Courant number
@@ -79,18 +88,31 @@ SUBROUTINE solve_ade(reach_length, & ! input: Reach length [m]
7988
real(dp) :: wdk ! weight for diffusion
8089
integer(i4b) :: ix ! loop index
8190
integer(i4b) :: Nx ! number of internal reach segments
82-
integer(i4b) :: downstreamBC ! method of B.C condition - absorbing or Neumann
91+
integer(i4b) :: downBC ! B.C condition method used
92+
integer(i4b) :: advec_discretization ! advection term discretization used
8393
character(len=strLen) :: fmt1 ! format string
8494
! Local parameters
8595
integer(i4b), parameter :: absorbingBC=1 ! downstream B.C.
8696
integer(i4b), parameter :: neumannBC=2 ! downstream B.C. flux derivative w.r.t. distance at downstream boundary
97+
integer(i4b), parameter :: upwind=1 ! upwind method for advection term discretization
98+
integer(i4b), parameter :: central=2 ! central difference for advetion term discretization
8799

88-
ierr=0; message='solve_ade/'
89-
90-
! hard-coded parameters
91-
downstreamBC = neumannBC ! downstream boundary condition
92-
wck = 1.0 ! weight in advection term
93-
wdk = 1.0 ! weight in diffusion term 0.0-> fully explicit, 0.5-> Crank-Nicolson, 1.0 -> fully implicit
100+
downBC = neumannBC ! downstream boundary condition
101+
if (present(downstreamBC)) then
102+
downBC = downstreamBC
103+
end if
104+
advec_discretization = central ! advection term discretization method
105+
if (present(advec_scheme)) then
106+
advec_discretization = advec_scheme
107+
end if
108+
wck = 1.0 ! weight in advection term: 0.0->fully explict, 1.0->fully implicit (default)
109+
if (present(wc)) then
110+
wck=wc
111+
end if
112+
wdk = 1.0 ! weight in diffusion term: 0.0->fully explicit, 0.5->Crank-Nicolson, 1.0->fully implicit (default)
113+
if (present(wd)) then
114+
wdk = wd
115+
end if
94116

95117
Nx = nMolecule - 1 ! Nx: number of internal reach segments
96118

@@ -107,47 +129,66 @@ SUBROUTINE solve_ade(reach_length, & ! input: Reach length [m]
107129
FluxLocal(1,1) = FluxUpstream ! quantity from upstream at current time step
108130

109131
! Fourier number and Courant number
110-
Cd = dk*dt_local/dx**2
132+
Cd = dk*dt_local/(dx*dx)
111133
Ca = ck*dt_local/dx
112134

113135
! create a matrix - current time step
114136
! populate tridiagonal elements
115137
! diagonal
116138
diagonal(1,2) = 1._dp
117-
diagonal(2:nMolecule-1,2) = 2._dp + 4*wdk*Cd
118-
if (downstreamBC == absorbingBC) then
139+
if (advec_discretization==upwind) then
140+
diagonal(2:nMolecule-1,2) = 1._dp + wck*Ca + 2._dp*wdk*Cd
141+
else if (advec_discretization==central) then
142+
diagonal(2:nMolecule-1,2) = 2._dp + 4*wdk*Cd
143+
end if
144+
if (downBC == absorbingBC) then
119145
diagonal(nMolecule,2) = 1._dp + wck*Ca
120-
else if (downstreamBC == neumannBC) then
146+
else if (downBC == neumannBC) then
121147
diagonal(nMolecule,2) = 1._dp
122148
end if
123149

124150
! upper
125151
diagonal(:,1) = 0._dp
126-
diagonal(3:nMolecule,1) = wck*Ca - 2._dp*wdk*Cd
152+
if (advec_discretization==upwind) then
153+
diagonal(3:nMolecule,1) = -wdk*Cd
154+
else if (advec_discretization==central) then
155+
diagonal(3:nMolecule,1) = wck*Ca - 2._dp*wdk*Cd
156+
end if
127157

128158
! lower
129159
diagonal(:,3) = 0._dp
130-
diagonal(1:nMolecule-2,3) = -wck*Ca - 2._dp*wdk*Cd
131-
if (downstreamBC == absorbingBC) then
160+
if (advec_discretization==upwind) then
161+
diagonal(1:nMolecule-2,3) = -wck*Ca - wdk*Cd
162+
else if (advec_discretization==central) then
163+
diagonal(1:nMolecule-2,3) = -wck*Ca - 2._dp*wdk*Cd
164+
end if
165+
if (downBC == absorbingBC) then
132166
diagonal(nMolecule-1,3) = -wck*Ca
133-
else if (downstreamBC == neumannBC) then
167+
else if (downBC == neumannBC) then
134168
diagonal(nMolecule-1,3) = -1._dp
135169
end if
136170

137171
! populate right-hand side
138172
! upstream boundary condition
139173
b(1) = FluxLocal(1,1)
140174
! downstream boundary condition
141-
if (downstreamBC == absorbingBC) then
175+
if (downBC == absorbingBC) then
142176
b(nMolecule) = (1._dp-(1._dp-wck)*Ca)*FluxLocal(nMolecule,0) + (1-wck)*Ca*FluxLocal(nMolecule-1,0)
143-
else if (downstreamBC == neumannBC) then
177+
else if (downBC == neumannBC) then
144178
Sbc = (FluxLocal(nMolecule,0)-FluxLocal(nMolecule-1,0))
145179
b(nMolecule) = Sbc
146180
end if
147181
! internal node points
148-
b(2:nMolecule-1) = ((1._dp-wck)*Ca +2._dp*(1._dp-wdk)*Cd)*FluxLocal(1:nMolecule-2,0) &
149-
+ (2._dp-4._dp*(1._dp-wdk)*Cd)*FluxLocal(2:nMolecule-1,0) &
150-
- ((1._dp-wck)*Ca -2._dp*(1._dp-wdk)*Cd)*FluxLocal(3:nMolecule,0)
182+
if (advec_discretization==upwind) then
183+
b(2:nMolecule-1) = ((1._dp-wck)*Ca +(1._dp-wdk)*Cd)*FluxLocal(1:nMolecule-2,0) &
184+
+ (1._dp-(1._dp-wck)*Ca-2._dp*(1._dp-wdk)*Cd)*FluxLocal(2:nMolecule-1,0) &
185+
+ (1._dp-wdk)*Cd*FluxLocal(3:nMolecule,0)
186+
else if (advec_discretization==central) then
187+
b(2:nMolecule-1) = ((1._dp-wck)*Ca +2._dp*(1._dp-wdk)*Cd)*FluxLocal(1:nMolecule-2,0) &
188+
+ (2._dp-4._dp*(1._dp-wdk)*Cd)*FluxLocal(2:nMolecule-1,0) &
189+
- ((1._dp-wck)*Ca -2._dp*(1._dp-wdk)*Cd)*FluxLocal(3:nMolecule,0)
190+
end if
191+
151192
! solve matrix equation - get updated FluxLocal
152193
call TDMA(nMolecule, diagonal, b, FluxSolved)
153194

@@ -162,7 +203,7 @@ SUBROUTINE solve_ade(reach_length, & ! input: Reach length [m]
162203
write(iulog,fmt1) ' diagonal(:,2)= ', diagonal(:,2)
163204
write(iulog,fmt1) ' diagonal(:,3)= ', diagonal(:,3)
164205
write(iulog,fmt1) ' b= ', b(1:nMolecule)
165-
write(iulog,fmt1) ' Q sub_reqch=', (FluxSolved(ix), ix=1,nMolecule)
206+
write(iulog,fmt1) ' FluxSolved= ', (FluxSolved(ix), ix=1,nMolecule)
166207
end if
167208

168209
END SUBROUTINE solve_ade

route/build/src/dfw_route.f90

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ MODULE dfw_route_module
3333

3434
integer(i4b), parameter :: top_reach=1
3535
integer(i4b), parameter :: bottom_reach=2
36+
integer(i4b), parameter :: advec_scheme=2 ! 1->upwind, 2->central difference
37+
integer(i4b), parameter :: downBC = 2 ! downstream end B.C: 1->open (absorbing) B.C., 2->Neumann B.C.
3638

3739
type, extends(base_route_rch) :: dfw_route_rch
3840
CONTAINS
@@ -253,7 +255,8 @@ SUBROUTINE diffusive_wave(rch_param, & ! input: river parameter data structu
253255

254256
ntSub = 1 ! number of sub-time step
255257

256-
associate(S => rch_param%R_SLOPE, & ! channel slope
258+
associate(nMolecule => nMolecule%DW_ROUTE, & ! number of computing points in a reach
259+
S => rch_param%R_SLOPE, & ! channel slope
257260
n => rch_param%R_MAN_N, & ! manning n
258261
bt => rch_param%R_WIDTH, & ! channel bottom width
259262
bankDepth => rch_param%R_DEPTH, & ! bankfull depth
@@ -266,11 +269,11 @@ SUBROUTINE diffusive_wave(rch_param, & ! input: river parameter data structu
266269

267270
if (L > min_length_route) then
268271

269-
allocate(Qprev(nMolecule%DW_ROUTE), stat=ierr, errmsg=cmessage)
272+
allocate(Qprev(nMolecule), stat=ierr, errmsg=cmessage)
270273
if(ierr/=0)then; message=trim(message)//trim(cmessage); return; endif
271274

272275
! initialize previous time step flow
273-
Qprev(1:nMolecule%DW_ROUTE) = rstate%molecule%Q ! flow state at previous time step
276+
Qprev(1:nMolecule) = rstate%molecule%Q ! flow state at previous time step
274277

275278
if (verbose) then
276279
write(iulog,'(A,1X,G12.5)') ' length [m] =',L
@@ -286,17 +289,17 @@ SUBROUTINE diffusive_wave(rch_param, & ! input: river parameter data structu
286289
write(iulog,'(A,1X,I3,A,1X,G12.5)') ' No. sub timestep=',nTsub,' sub time-step [sec]=',dTsub
287290
end if
288291

289-
allocate(Qlocal(1:nMolecule%DW_ROUTE, 0:1), stat=ierr, errmsg=cmessage)
292+
allocate(Qlocal(1:nMolecule, 0:1), stat=ierr, errmsg=cmessage)
290293
if(ierr/=0)then; message=trim(message)//trim(cmessage); return; endif
291294

292295
do it = 1, nTsub
293-
Qbar = (Qupstream+Qprev(1)+Qprev(nMolecule%DW_ROUTE-1))/3.0 ! 3 point average discharge [m3/s]
296+
Qbar = (Qupstream+Qprev(1)+Qprev(nMolecule-1))/3.0 ! 3 point average discharge [m3/s]
294297
depth = flow_depth(abs(Qbar), bt, zc, S, n, zf=zf, bankDepth=bankDepth) ! compute flow depth as normal depth (a function of flow)
295298
ck = celerity(abs(Qbar), depth, bt, zc, S, n, zf=zf, bankDepth=bankDepth)
296299
dk = diffusivity(abs(Qbar), depth, bt, zc, S, n, zf=zf, bankDepth=bankDepth)
297300

298301
call solve_ade(L, & ! input: river parameter data structure
299-
nMolecule%DW_ROUTE, & ! input: number of sub-segments
302+
nMolecule, & ! input: number of sub-segments
300303
dTsub, & ! input: time_step [sec]
301304
Qupstream, & ! input: quantity from upstream [unit of quantity]
302305
ck, & ! input: velocity [m/s]
@@ -305,19 +308,19 @@ SUBROUTINE diffusive_wave(rch_param, & ! input: river parameter data structu
305308
Qprev, & ! input: quantity at previous time step [unit of quantity]
306309
Qlocal, & ! inout: quantity soloved at current time step [unit of quantity]
307310
verbose, & ! input: reach index to be examined
308-
ierr,message)
309-
if(ierr/=0)then; message=trim(message)//trim(cmessage); return; endif
311+
advec_scheme=advec_scheme, & ! optional input: advection scheme (1->central difference, 2->upwind)
312+
downstreamBC=downBC) ! optional input: downstream boundary condition (1->Neumann, 2->open boundary condition)
310313
end do
311314

312315
! For very low flow condition, outflow - inflow may exceed current storage, so limit outflow and adjust flow profile
313-
if (abs(Qlocal(nMolecule%DW_ROUTE-1,1))>0._dp) then
316+
if (abs(Qlocal(nMolecule-1,1))>0._dp) then
314317
volTmp = max(0._dp, rflux%ROUTE(idxDW)%REACH_VOL(1))
315-
qoutTmp = Qlocal(nMolecule%DW_ROUTE-1,1)*dt
318+
qoutTmp = Qlocal(nMolecule-1,1)*dt
316319
pcntReduc = min((volTmp + dt*Qupstream)*0.999_dp/qoutTmp, 1._dp)
317-
Qlocal(2:nMolecule%DW_ROUTE,1) = Qlocal(2:nMolecule%DW_ROUTE,1)*pcntReduc
320+
Qlocal(2:nMolecule,1) = Qlocal(2:nMolecule,1)*pcntReduc
318321
end if
319322

320-
rflux%ROUTE(idxDW)%REACH_VOL(1) = rflux%ROUTE(idxDW)%REACH_VOL(1) + (Qupstream - Qlocal(nMolecule%DW_ROUTE-1,1))*dt
323+
rflux%ROUTE(idxDW)%REACH_VOL(1) = rflux%ROUTE(idxDW)%REACH_VOL(1) + (Qupstream - Qlocal(nMolecule-1,1))*dt
321324

322325
! if reach volume exceeds flood threshold volume, excess water is flooded volume.
323326
if (rflux%ROUTE(idxDW)%REACH_VOL(1) > bankVol) then
@@ -329,15 +332,15 @@ SUBROUTINE diffusive_wave(rch_param, & ! input: river parameter data structu
329332
rflux%ROUTE(idxDW)%REACH_ELE = water_height(rflux%ROUTE(idxDW)%REACH_VOL(1)/L, bt, zc, zf=zf, bankDepth=bankDepth)
330333

331334
! store final outflow in data structure
332-
rflux%ROUTE(idxDW)%REACH_Q = Qlocal(nMolecule%DW_ROUTE-1,1) + Qlat
335+
rflux%ROUTE(idxDW)%REACH_Q = Qlocal(nMolecule-1,1) + Qlat
333336

334337
! update state
335338
rstate%molecule%Q = Qlocal(:,1)
336339

337340
else ! length < min_length_route: length is short enough to just pass upstream to downstream
338341
rflux%ROUTE(idxDW)%REACH_Q = Qupstream + Qlat
339-
rstate%molecule%Q(1:nMolecule%DW_ROUTE) = 0._dp
340-
rstate%molecule%Q(nMolecule%DW_ROUTE) = rflux%ROUTE(idxDW)%REACH_Q
342+
rstate%molecule%Q(1:nMolecule) = 0._dp
343+
rstate%molecule%Q(nMolecule) = rflux%ROUTE(idxDW)%REACH_Q
341344

342345
rflux%ROUTE(idxDW)%REACH_VOL(0) = 0._dp
343346
rflux%ROUTE(idxDW)%REACH_VOL(1) = 0._dp
@@ -353,8 +356,8 @@ SUBROUTINE diffusive_wave(rch_param, & ! input: river parameter data structu
353356
rflux%ROUTE(idxDW)%FLOOD_VOL(1) = 0._dp
354357
rflux%ROUTE(idxDW)%REACH_ELE = 0._dp
355358

356-
rstate%molecule%Q(1:nMolecule%DW_ROUTE) = 0._dp
357-
rstate%molecule%Q(nMolecule%DW_ROUTE) = rflux%ROUTE(idxDW)%REACH_Q
359+
rstate%molecule%Q(1:nMolecule) = 0._dp
360+
rstate%molecule%Q(nMolecule) = rflux%ROUTE(idxDW)%REACH_Q
358361

359362
if (verbose) then
360363
write(iulog,'(A)') ' This is headwater '

route/build/src/kwe_route.f90

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,7 @@ SUBROUTINE kinematic_wave(rch_param, & ! input: river parameter data structu
302302
Qlat, & ! input: lateral quantity into chaneel [unit of quantity]
303303
Qprev, & ! input: quantity at previous time step [unit of quantity]
304304
Qlocal, & ! inout: quantity soloved at current time step [unit of quantity]
305-
verbose, & ! input: reach index to be examined
306-
ierr,message)
307-
if(ierr/=0)then; message=trim(message)//trim(cmessage); return; endif
305+
verbose) ! input: reach index to be examined
308306
end do
309307

310308
! For very low flow condition, outflow - inflow may exceed current storage, so limit outflow and adjust flow profile

0 commit comments

Comments
 (0)