11MODULE advection_diffusion
22
3- ! solving advection-diffusion equation
3+ ! Numerical solution for 1D advection-diffusion equation
44
55USE nrtype
66USE public_var, ONLY: iulog ! i/o logical unit number
@@ -14,7 +14,7 @@ MODULE advection_diffusion
1414CONTAINS
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
0 commit comments