!=============================================================================
!
! $Id: smvgear.F90,v 1.2 2007/05/24 17:03:56 kouatch Exp $
!
! CODE DEVELOPER
! Original code from Mark Z. Jacobson ((C) COPYRIGHT, 1993).
! LLNL modifications: John Tannahill
! jrt@llnl.gov
!
! FILE
! smvgear.F
!
! ROUTINES
! Smvgear
! Backsub
! Decomp
! Pderiv
! Subfun
! Update
!
!=============================================================================
!-----------------------------------------------------------------------------
!
! ROUTINE
! Smvgear
!
! DESCRIPTION
! This routine is the driver for the Smvgear (Sparse Matrix Vector Gear code)
! chemistry solver. It uses a Gear-type integrator that solves first order
! ordinary differential equations with initial value boundary conditions.
! Smvgear differs from an original Gear code in that it uses sparse matrix
! and vectorization techniques to improve its computational speed.
!
! This version is Smvgear II, 9/96. It has been modified to include
! grid-cell reordering prior to each time interval and different chemistry
! for different atmospheric regions. The purpose of the reordering is to
! group cells with stiff equations together and those with non-stiff
! equations together. This reordering can save signifcant computer time
! (e.g., speed the code by a factor of two or more), depending on the
! variation in stiffness throughout the grid-domain. When the stiffness is
! the same throughout the grid-domain (e.g., if all concentrations and rates
! are the same), then reordering is unnecessary and will not speed solutions.
!
! This version includes a variable absolute error tolerance. The absolute
! tolerance is recalculated every few Gear time steps. This version also
! contains different sets of chemistry for different regions of the
! atmosphere. Thus, urban, free tropospheric, and stratospheric chemistry
! can be solved during the same model run.
!
! References =>
! ----------
!
! Jacobson M. Z. (1997) Improvement in Smvgear II through Absolute
! Error Tolerance Control; in submission.
!
! Jacobson M. Z. (1995) Computation of Global Photochemistry with Smvgear
! II, Atmos. Environ., 29a, 2541-2546.
!
! Jacobson M. Z. (1994) Developing, Coupling, and Applying a Gas, Aerosol,
! Transport, and Radiation Model to Studying Urban and Regional Air
! Pollution, PhD thesis, University of California, Los Angeles.
!
! Jacobson M. Z. and Turco R. P. (1994) Smvgear: A Sparse Matrix,
! Vectorized Gear Code for Atmospheric Models, Atmos. Environ. 28a,
! 273-284.
!
! The origins of the Gear integrator used in Smvgear are found in:
! Gear C. W. (1971) Numerical Initial Value Problems in Ordinary
! Differential Equations, Prentice-Hall, NJ, pp. 158-166.
!
! Finally, in subroutine Smvgear, the following ideas originated from
! Lsodes, the Livermore solver for ordinary differential with sparse
! matrices (Hindmarsh A. C. and Sherman A. H.):
! (a) predicting the first time-step;
! (b) determining corrector convergence differently than in Gear's
! original code (goc);
! (c) determining error differently than in goc;
! (d) summing up the pascal matrix differently than in goc.
!
! References for the 1987 Lsodes version include:
!
! Sherman A. H. and Hindmarsh A. C. (1980) Gears: A Package for the
! Solution of Sparse, Stiff Ordinary Differential Equations, Lawrence
! Livermore National Laboratory Report UCRL-84102.
!
! Hindmarsh A. C. (1983) Odepack, A Systematized Collection of ODE
! Solvers, Scientific Computing, R.S. Stepleman et. al., eds.,
! North-Holland, Amsterdam, pp. 55-74.
!
! ARGUMENTS
! do_qqjk_inchem : if pr_qqjk is on, should qqj's & qqk's be determined
! inside the chemistry solver, or outside?
! do_semiss_inchem : do surface emissions inside the chemistry solver, or
! outside?
! pr_qqjk : should the periodic qqjk output file be written?
! pr_smv2 : should the SmvgearII output file be written
! (non-parallel mode only)?
! ifsun : identifies whether sun is up (=1) or down (=2)
! ilat : # of latitudes
! ilong : # of longitudes
! ivert : # of vertical layers
! ireord : 1 => reorder grid-cells and blocks for chemistry
! 2 => solve chemistry
! itloop : # of zones (ilong * ilat * ivert)
! jlooplo : low ntloop grid-cell - 1 in a grid-block
! ktloop : # of grid-cells in a grid-block
! lunsmv : logical unit number to write to when pr_smv2 is true
! nallr : # of active rxns
! ncs : identifies gas chemistry type (1..NCSGAS)
! nfdh2 : nfdh3 + # of rxns with two active reactants
! nfdh3 : # of rxns with three active reactants
! nfdl1 : nfdh2 + 1
! nfdl2 : nfdh3 + 1
! nfdrep : nfdh3 + # of rxns with two active reactants that are not
! followed by a rxn with the same reactants
! nfdrep1 : nfdrep + 1
! fracdec : fraction time step is decreased in Smvgear if convergence
! test fails
! hmaxnit : max time step for night all chem (s)
! pr_nc_period : NetCDF output period
! tdt : model time step (s)
! do_cell_chem : do chemistry for a particular cell?
! irma,b,c : spc # of each reactant; locates reordered active spc #s
! jreorder : gives original grid-cell from re-ordered grid-cell
! jphotrat : tbd
! ntspec : # of active + inactive gases
! inewold : original spc # of each new jnew spc
! denair : density of air (molec/cm^3)
! corig : original gas-phase concs used to restart Smvgear if a failure
! occurs (molec/cm^3)
! pratk1 : tbd
! yemis : surface emissions (units?)
! smvdm : amount added to each spc at each grid-cell, for mass balance
! accounting (# cm^-3 for gas chemistry (?))
! nfdh1 : nfdh2 + # of rxns with one active reactant
! errmx2 : tbd
! cc2 : array holding values of decomposed matrix
! cnew : stores conc (y (estimated)) (molec/cm^3)
! gloss : value of first derivatives on output from Subfun; right-side
! of eqn on input to Backsub; error term (solution from Backsub)
! on output from Backsub
! vdiag : 1 / current diagonal term of the decomposed matrix
! rrate : rate constants
! trate : rxn rate (moles l^-1-h2o s^-1 or # cm^-3 s^-1 (?))
! urate : term of Jacobian (J) = partial derivative
!
!-----------------------------------------------------------------------------
subroutine Smvgear & 1,10
& (do_qqjk_inchem, do_semiss_inchem, pr_qqjk, pr_smv2, ifsun, &
& ilat, ilong, ivert, ireord, itloop, jlooplo, ktloop, lunsmv, &
& nallr, ncs, nfdh2, nfdh3, nfdl1, nfdl2, nfdrep, nfdrep1, &
& fracdec, hmaxnit, pr_nc_period, tdt, do_cell_chem, irma, irmb, &
& irmc, jreorder, jphotrat, ntspec, inewold, denair, corig, &
& pratk1, yemis, smvdm, nfdh1, errmx2, cc2, cnew, gloss, vdiag, &
& rrate, trate, urate, &
& yda, qqkda, qqjda, qkgmi, qjgmi, &
& CTMi1, CTMi2, CTMju1, CTMj2, CTMk1, CTMk2, &
& num_qjo, num_qks, num_qjs, num_active)
use GmiPrintError_mod
, only : GmiPrintError
implicit none
# include "smv2chem_par.h"
# include "smv2chem2.h"
! ----------------------
! Argument declarations.
! ----------------------
integer, intent(in) :: CTMi1, CTMi2, CTMju1, CTMj2, CTMk1, CTMk2
integer, intent(in) :: num_qjo, num_qks, num_qjs, num_active
real*8 , intent(in ) :: qjgmi(CTMi1:CTMi2, CTMju1:CTMj2, CTMk1:CTMk2, num_qjo)
real*8 , intent(in ) :: qkgmi(CTMi1:CTMi2, CTMju1:CTMj2, CTMk1:CTMk2, num_qks)
real*8 , intent(inout) :: qqjda(CTMi1:CTMi2, CTMju1:CTMj2, CTMk1:CTMk2, num_qjs)
real*8 , intent(inout) :: qqkda(CTMi1:CTMi2, CTMju1:CTMj2, CTMk1:CTMk2, num_qks)
real*8 , intent(inout) :: yda (CTMi1:CTMi2, CTMju1:CTMj2, CTMk1:CTMk2, num_active)
logical, intent(in) :: do_qqjk_inchem
logical, intent(in) :: do_semiss_inchem
logical, intent(in) :: pr_qqjk
logical, intent(in) :: pr_smv2
integer, intent(in) :: ifsun
integer, intent(in) :: ilat, ilong, ivert
integer, intent(in) :: ireord
integer, intent(in) :: itloop
integer, intent(in) :: jlooplo
integer, intent(in) :: ktloop
integer, intent(in) :: lunsmv
integer, intent(in) :: nallr
integer, intent(in) :: ncs
integer, intent(in) :: nfdh2, nfdh3
integer, intent(in) :: nfdl1, nfdl2
integer, intent(in) :: nfdrep, nfdrep1
real*8, intent(in) :: fracdec
real*8, intent(in) :: hmaxnit
real*8, intent(in) :: pr_nc_period
real*8, intent(in) :: tdt
logical, intent(in) :: do_cell_chem(ilong, ilat, ivert)
integer, intent(in) :: irma (NMTRATE)
integer, intent(in) :: irmb (NMTRATE)
integer, intent(in) :: irmc (NMTRATE)
integer, intent(in) :: jreorder(itloop)
integer, intent(in) :: jphotrat(ICS)
integer, intent(in) :: ntspec (ICS)
integer, intent(in) :: inewold (MXGSAER, ICS)
real*8, intent(in) :: denair (ktloop)
real*8, intent(in) :: corig (KBLOOP, MXGSAER)
real*8, intent(in) :: pratk1 (KBLOOP, IPHOT)
real*8, intent(in) :: yemis (ilat*ilong, IGAS)
real*8, intent(inout) :: errmx2(itloop)
real*8, intent(inout) :: cc2 (KBLOOP, 0:MXARRAY)
real*8, intent(inout) :: cnew (KBLOOP, MXGSAER)
real*8, intent(inout) :: gloss (KBLOOP, MXGSAER)
real*8, intent(inout) :: smvdm (KBLOOP, MXGSAER)
real*8, intent(inout) :: vdiag (KBLOOP, MXGSAER)
real*8, intent(inout) :: rrate (KBLOOP, NMTRATE)
real*8, intent(inout) :: trate (KBLOOP, NMTRATE*2)
real*8, intent(inout) :: urate (KBLOOP, NMTRATE, 3)
integer, intent(out) :: nfdh1
! ----------------------
! Variable declarations.
! ----------------------
! ------------------------------------------------------------------------
! idoub : records # of steps since the last change in step size or
! order; it must be at least kstep = nqq+1 before doubling is
! allowed
!
! ifail : # of times corrector failed to converge while the Jacobian
! was old
! lfail : # of times accumulated error test failed
! nfail : # of times correcter failed to converge after Pderiv was
! called
!
! ifsuccess : identifies whether step is successful (=1) or not (=0)
! ischan : # of first-order eqns to solve, = # of spc = order of
! original matrix; ischan has a different value for day and
! night and for gas- and aqueous-phase chemistry;
! # spc with prod or loss terms in Smvgear (?)
! jeval : 1 => call Pderiv the next time through the corrector steps;
! 0 => last step successful and do not need to call Pderiv;
! -1 => Pderiv just called, and do not need to call again
! until jeval switched to 1
! jrestar : counts # of times Smvgear starts over at order 1 because of
! excessive failures
! kstep : nqq + 1
!
! npderiv : total # of times Pderiv is called
! nsubfun : total # of times Subfun is called
!
! nqqisc : nqq * ischan
! nqqold : value of nqq during last time step
! nqq : order of integration method; varies between 1 and MAXORD
! nslp : last time step # during which Pderiv was called
! nsteps : total # of successful time steps taken
! ------------------------------------------------------------------------
integer :: i, j, k
integer :: i1, i2
integer :: idoub
integer :: ifail, jfail, lfail, nfail
integer :: ifsuccess
integer :: ischan, ischan1
integer :: jb
integer :: jeval
integer :: jg1
integer :: jgas
integer :: jnew
integer :: jrestar
integer :: jspc
integer :: k1, k2, k3, k4, k5
integer :: kloop
integer :: kstep, kstepisc
integer :: l3
integer :: nact
integer :: ncsp ! ncs => for daytime gas chemistry
! ncs + ICS => for nighttime gas chemistry
integer :: npderiv, nsubfun
integer :: nqisc, nqqisc, nqqold
integer :: nqq
integer :: nslp
integer :: nsteps
integer :: nylowdec
integer :: ibcb(IGAS)
! ------------------------------------------------
! kgrp : counts # of concs above abtol(i), i = 1..
! ------------------------------------------------
integer :: kgrp(KBLOOP, 5)
! ------------------------------------------------------------------------
! asn1 : value of aset(nqq,1)
! delt : current time step (s)
! drate : parameter which is used to determine whether convergence
! has occurred
! edwn : pertst^2*order for one order lower than current order
! enqq : pertst^2*order for current order
! eup : pertst^2*order for one order higher than current order
! hmax : max time step at a given time (s)
! hratio : relative change in delt*aset(1) each change in step or order
! when Abs(hratio-1) > hrmax, reset jeval = 1 to call Pderiv
! hrmax : max relative change in delt*aset(1) before Pderiv is called
! order : floating point value of ischan, the order of # of ODEs
! rdelmax : max factor by which delt can be increased in a single step;
! as in Lsodes, set it to 1d4 initially to compensate for the
! small initial delt, but then set it to 10 after successful
! steps and to 2 after unsuccessful steps
! rdelt : factor (time step ratio) by which delt is increased or
! decreased
! rdeltdn : time step ratio at one order lower than current order
! rdeltsm : time step ratio at current order
! rdeltup : time step ratio at one order higher than current order
! rmsrat : ratio of current to previous rms scaled error; if this
! ratio decreases, then convergence is occuring
! timremain : remaining time in an chem interval (s)
! tinterval : total chem time interval; same as chemintv (s)
! told : stores last value of xelaps in case current step fails
! xelaps : elapsed time in chem interval (s)
! yfac : = 1 originially, but is decreased if excessive failures
! occur in order to reduce absolute error tolerance
! ------------------------------------------------------------------------
real*8 :: abtoler1
real*8 :: asn1, asnqqj
real*8 :: cnewylow
real*8 :: cnw
real*8 :: conp1, conp2, conp3
real*8 :: consmult
real*8 :: dcon
real*8 :: delt, delt1
real*8 :: der1max, der2max, der3max
real*8 :: drate
real*8 :: dtasn1
real*8 :: edwn, enqq, eup
real*8 :: errinit, errinit_inv
real*8 :: errmax_ncs_inv, errymax
real*8 :: hmax, hrmax
real*8 :: hmtim
real*8 :: hratio
real*8 :: iabove
real*8 :: order, order_inv
real*8 :: r1delt, rdelmax, rdelt, rdelta
real*8 :: rdeltdn, rdeltsm, rdeltup
real*8 :: real_kstep
real*8 :: reltol1, reltol2, reltol3
real*8 :: rmserr, rmserrp, rmsrat, rmstop
real*8 :: timremain, tinterval
real*8 :: told
real*8 :: xelaps, xtimestep
real*8 :: yfac
! -------------------------------------------------------------------------
! dely : tbd
! yabst : absolute error tolerance (molec/cm^-3 for gases)
!
! cest : stores value of dtlos when idoub = 1
! chold : 1 / (reltol * cnew + abtol); multiply chold by local errors in
! different error tests
! dtlos : an array of length ischan, used for the accumulated corrections;
! on a successful return; dtlos(kloop,i) contains the estimated
! one step local error in cnew
! explic : tbd
! conc : an array of length ischan*(MAXORD+1) that carries the
! derivatives of cnew, scaled by delt^j/factorial(j), where j is
! the jth derivative; j varies from 1 to nqq; e.g., conc(jspc,2)
! stores delt*y' (estimated)
! -------------------------------------------------------------------------
real*8 :: dely (KBLOOP)
real*8 :: yabst (KBLOOP)
real*8 :: cest (KBLOOP, MXGSAER)
real*8 :: chold (KBLOOP, MXGSAER)
real*8 :: dtlos (KBLOOP, MXGSAER)
real*8 :: explic(KBLOOP, MXGSAER)
real*8 :: conc (KBLOOP, MXGSAER*7)
! ----------------
! Begin execution.
! ----------------
!c Write (6,*) 'Smvgear called.'
nact = nnact
! =======================
# include "setkin_ibcb.h"
! =======================
ifail = 0
jfail = 0
lfail = 0
nfail = 0
npderiv = 0
nsteps = 0
nsubfun = 0
nylowdec = 0
hrmax = 0.3d0
rmserr = 1.0d0
ischan = ischang(ncs)
ischan1 = ischan - 1
order = ischan
order_inv = 1.0d0 / ischan
tinterval = timeintv(ncs)
ncsp = (ifsun - 1) * ICS + ncs
if (ifsun == 1) then
hmax = hmaxday(ncs)
else
hmax = hmaxnit
end if
yfac = 1.0d0
iabove = order * 0.4d0
errinit = Min (errmax(ncs), 1.0d-03)
errinit_inv = 1.0d0 / errinit
errmax_ncs_inv = 1.0d0 / errmax(ncs)
! ----------------------------------------------------
! Start time interval or re-enter after total failure.
! ----------------------------------------------------
! ========
100 continue
! ========
idoub = 2
nslp = MBETWEEN
jrestar = 0
xelaps = 0.0d0
told = 0.0d0
timremain = tinterval
reltol1 = yfac * errinit_inv
reltol2 = yfac * errmax_ncs_inv
reltol3 = errmax_ncs_inv
abtoler1 = abtol(6,ncs) * reltol1
! -------------------------------
! Initialize concentration array.
! -------------------------------
do jnew = 1, ischan
do kloop = 1, ktloop
cnew(kloop, jnew) = corig(kloop, jnew)
end do
end do
! --------------------------------------------------------------------
! Re-enter here if total failure or if restarting with new cell block.
! --------------------------------------------------------------------
! ========
150 continue
! ========
hratio = 0.0d0
asn1 = 1.0d0
ifsuccess = 1
rdelmax = 1.0d4
! ---------------------
! Initialize photrates.
! ---------------------
!DIR$ INLINE
! ===========
call Update
&
! ===========
& (ktloop, nallr, ncs, ncsp, jphotrat, pratk1, rrate, trate)
!DIR$ NOINLINE
! ----------------------------
! Initialize first derivative.
! ----------------------------
! ===========
call Subfun
&
! ===========
& (ischan, ktloop, nallr, ncsp, nfdh2, nfdh3, nfdl1, nfdl2, &
& nfdrep, nfdrep1, irma, irmb, irmc, cnew, rrate, nsubfun, &
& gloss, trate, nfdh1)
! ----------------------------------------------------------------
! Zero first derviatives in surface zones for species with fixed
! concentration boundary conditions (LLNL addition, PSC, 5/14/99).
! ----------------------------------------------------------------
do kloop = 1, ktloop
if (jreorder(jlooplo+kloop) <= (ilat*ilong)) then
do jspc = 1, ntspec(ncs)
jgas = inewold(jspc,1)
if (ibcb(jgas) == 1) then
gloss(kloop,jspc) = 0.0d0
else if (do_semiss_inchem) then
gloss(kloop,jspc) = &
& gloss(kloop,jspc) + &
& yemis(jreorder(jlooplo+kloop),jgas)
end if
end do
end if
end do
! -------------------------------------------
! Determine initial absolute error tolerance.
! -------------------------------------------
do kloop = 1, ktloop
dely(kloop) = 0.0d0
end do
! ==========================
IREORDIF: if (ireord /= 1) then
! ==========================
do k = 1, 5
do kloop = 1, ktloop
kgrp(kloop,k) = 0
end do
end do
do jspc = 1, ischan
do kloop = 1, ktloop
cnw = cnew(kloop,jspc)
if (cnw > abtol(1,ncs)) then
kgrp(kloop,1) = kgrp(kloop,1) + 1
else if (cnw > abtol(2,ncs)) then
kgrp(kloop,2) = kgrp(kloop,2) + 1
else if (cnw > abtol(3,ncs)) then
kgrp(kloop,3) = kgrp(kloop,3) + 1
else if (cnw > abtol(4,ncs)) then
kgrp(kloop,4) = kgrp(kloop,4) + 1
else if (cnw > abtol(5,ncs)) then
kgrp(kloop,5) = kgrp(kloop,5) + 1
end if
end do
end do
do kloop = 1, ktloop
k1 = kgrp(kloop,1)
k2 = kgrp(kloop,2) + k1
k3 = kgrp(kloop,3) + k2
k4 = kgrp(kloop,4) + k3
k5 = kgrp(kloop,5) + k4
if (k1 > iabove) then
yabst(kloop) = abtol(1,ncs)
else if (k2 > iabove) then
yabst(kloop) = abtol(2,ncs)
else if (k3 > iabove) then
yabst(kloop) = abtol(3,ncs)
else if (k4 > iabove) then
yabst(kloop) = abtol(4,ncs)
else if (k5 > iabove) then
yabst(kloop) = abtol(5,ncs)
else
yabst(kloop) = abtol(6,ncs)
end if
end do
!c
do kloop = 1, ktloop
do jspc = 1, ischan
cnewylow = cnew (kloop,jspc) + (yabst(kloop) * reltol1)
errymax = gloss(kloop,jspc) / cnewylow
dely(kloop) = dely (kloop) + (errymax * errymax)
end do
end do
! ====
else
! ====
! ------------------------------------------------------
! Use lowest absolute error tolerance when reordering.
! If reordering, set errmx2 then return to Physproc.
!
! abtoler1 = yfac * abtol(6,ncs) / Min (errmax, 1.0d-03)
! ------------------------------------------------------
!c
do kloop = 1, ktloop
do jspc = 1, ischan
errymax = gloss(kloop,jspc) / &
& (cnew(kloop,jspc) + abtoler1)
dely(kloop) = dely(kloop) + (errymax * errymax)
end do
end do
do kloop = 1, ktloop
errmx2(jlooplo+kloop) = dely(kloop)
end do
! =========
go to 700
! =========
! ===============
end if IREORDIF
! ===============
! --------------------------------------
! Calculate initial time step size (s).
!
! Sqrt (dely / [errinit * order]) =
! rmsnorm of error scaled to errinit *
! cnew + abtol / reltol
! --------------------------------------
rmstop = 0.0d0
do kloop = 1, ktloop
if (dely(kloop) > rmstop) then
rmstop = dely(kloop)
end if
end do
delt1 = Sqrt (errinit / (abst2(ncs) + (rmstop * order_inv)))
delt = Max (Min (delt1, timremain, hmax), HMIN)
! -----------------------
! Set initial order to 1.
! -----------------------
nqqold = 0
nqq = 1
jeval = 1
rdelt = 1.0d0
! --------------------------------------------------------------
! Store initial concentration and first derivatives x time step.
! --------------------------------------------------------------
do jspc = 1, ischan
j = jspc + ischan
do kloop = 1, ktloop
conc(kloop,jspc) = cnew(kloop,jspc)
conc(kloop,j) = delt * gloss(kloop,jspc)
end do
end do
! ========
200 continue
! ========
! -------------------------------------------------------------------
! Update coefficients of the order; note that pertst2 is the original
! pertst^2.
! -------------------------------------------------------------------
if (nqq /= nqqold) then
nqqold = nqq
kstep = nqq + 1
hratio = hratio * aset(nqq,1) / asn1
asn1 = aset(nqq,1)
enqq = pertst2(nqq,1) * order
eup = pertst2(nqq,2) * order
edwn = pertst2(nqq,3) * order
conp3 = 1.4d0 / (eup**enqq3(nqq))
conp2 = 1.2d0 / (enqq**enqq2(nqq))
conp1 = 1.3d0 / (edwn**enqq1(nqq))
nqqisc = nqq * ischan
end if
! ----------------------------------------------------------------
! Limit size of rdelt, then recalculate new time step and update
! hratio. Use hratio to determine whether Pderiv should be called
! again.
! ----------------------------------------------------------------
hmtim = Min (hmax, timremain)
rdelt = Min (rdelt, rdelmax, hmtim/delt)
delt = delt * rdelt
hratio = hratio * rdelt
xelaps = xelaps + delt
if ((Abs (hratio-1.0d0) > hrmax) .or. (nsteps >= nslp)) then
jeval = 1
end if
! ----------------------------------------------------------
! If time step < HMIN, tighten absoloute error tolerance and
! restart integration at beginning of time interval.
! ----------------------------------------------------------
if (delt < HMIN) then
if (pr_smv2) then
Write (lunsmv,950) delt, timremain, yfac, errmax(ncs)
end if
950 format ('Smvgear: delt = ', 1pe9.3, /, &
& ' timremain = ', 1pe9.3, /, &
& ' yfac = ', 1pe9.3, /, &
& ' errmax = ', 1pe9.3)
nylowdec = nylowdec + 1
yfac = yfac * 0.01d0
if (nylowdec == 10) then
if (pr_smv2) then
Write (lunsmv,960)
end if
960 format ('Smvgear: too many decreases of yfac.')
call GmiPrintError
('Problem in Smvgear', .true., 0, 0, 0, 0, 0.0d0, 0.0d0)
end if
! =========
go to 100
! =========
end if
! -------------------------------------------------------------------
! If the delt is different than during the last step (if rdelt /= 1),
! then scale the derivatives.
! -------------------------------------------------------------------
if (rdelt /= 1.0d0) then
rdelta = 1.0d0
i1 = 1
do j = 2, kstep
rdelta = rdelta * rdelt
i1 = i1 + ischan
do i = i1, i1 + ischan1
do kloop = 1, ktloop
conc(kloop,i) = conc(kloop,i) * rdelta
end do
end do
end do
end if
! --------------------------------------------------------------
! If the last step was successful, reset rdelmax = 10 and update
! the chold array with current values of cnew.
! --------------------------------------------------------------
! ================================
IFSUCCESSIF: if (ifsuccess == 1) then
! ================================
rdelmax = 10.0d0
! ---------------------------------------
! Determine new absolute error tolerance.
! ---------------------------------------
if (Mod (nsteps, 3) == 2) then
do k = 1, 5
do kloop = 1, ktloop
kgrp(kloop,k) = 0
end do
end do
do jspc = 1, ischan
do kloop = 1, ktloop
cnw = cnew(kloop,jspc)
if (cnw > abtol(1,ncs)) then
kgrp(kloop,1) = kgrp(kloop,1) + 1
else if (cnw > abtol(2,ncs)) then
kgrp(kloop,2) = kgrp(kloop,2) + 1
else if (cnw > abtol(3,ncs)) then
kgrp(kloop,3) = kgrp(kloop,3) + 1
else if (cnw > abtol(4,ncs)) then
kgrp(kloop,4) = kgrp(kloop,4) + 1
else if (cnw > abtol(5,ncs)) then
kgrp(kloop,5) = kgrp(kloop,5) + 1
end if
end do
end do
do kloop = 1, ktloop
k1 = kgrp(kloop,1)
k2 = kgrp(kloop,2) + k1
k3 = kgrp(kloop,3) + k2
k4 = kgrp(kloop,4) + k3
k5 = kgrp(kloop,5) + k4
if (k1 > iabove) then
yabst(kloop) = abtol(1,ncs)
else if (k2 > iabove) then
yabst(kloop) = abtol(2,ncs)
else if (k3 > iabove) then
yabst(kloop) = abtol(3,ncs)
else if (k4 > iabove) then
yabst(kloop) = abtol(4,ncs)
else if (k5 > iabove) then
yabst(kloop) = abtol(5,ncs)
else
yabst(kloop) = abtol(6,ncs)
end if
end do
end if
!c
do kloop = 1, ktloop
do jspc = 1, ischan
chold(kloop,jspc) = &
& reltol3 / &
& (Max (cnew(kloop,jspc), 0.0d0) + &
& (yabst(kloop) * reltol2))
end do
end do
! ==================
end if IFSUCCESSIF
! ==================
! ------------------------------------------------------------------
! Compute the predicted concentration and derivatives by multiplying
! previous values by the pascal triangle matrix.
! ------------------------------------------------------------------
i1 = nqqisc + 1
do jb = 1, nqq - 1
i1 = i1 - ischan
do i = i1, nqqisc
j = i + ischan
do kloop = 1, ktloop
conc(kloop,i) = conc(kloop,i) + conc(kloop,j)
end do
end do
end do
do jspc = 1, ischan
j = jspc + ischan
do kloop = 1, ktloop
conc (kloop,jspc) = conc(kloop,jspc) + conc(kloop,j)
explic(kloop,jspc) = conc(kloop,j)
end do
end do
do i = ischan + 1, nqqisc
j = i + ischan
do kloop = 1, ktloop
conc(kloop,i) = conc(kloop,i) + conc(kloop,j)
end do
end do
! -------------------------------------------------------------------
! Correction loop.
!
! Take up to 3 corrector iterations. Test convergence by requiring
! that changes be less than the rms norm weighted by chold.
! Accumulate the correction in the array dtlos. It equals the
! jth derivative of concentration multiplied by delt^kstep /
! (factorial(kstep-1) * aset(kstep)); thus, it is proportional to the
! actual errors to the lowest power of delt present (delt^kstep).
! -------------------------------------------------------------------
! ========
250 continue
! ========
l3 = 0
do jspc = 1, ischan
do kloop = 1, ktloop
cnew (kloop,jspc) = conc(kloop,jspc)
dtlos(kloop,jspc) = 0.0d0
end do
end do
! ------------------------------------------------------------------
! If jeval = 1, re-evaluate predictor matrix P = I - H * aset(1) * J
! before starting the corrector iteration. After calling Pderiv,
! set jeval = -1 to prevent recalling Pderiv unless necessary later.
! Call Decomp to decompose the matrix.
! ------------------------------------------------------------------
if (jeval == 1) then
r1delt = -asn1 * delt
!DIR$ INLINE
! ===========
call Pderiv
&
! ===========
& (ischan, ktloop, ncsp, nfdh2, nfdh3, nfdl1, nfdl2, irma, irmb, &
& irmc, r1delt, cnew, rrate, npderiv, cc2, urate, nfdh1)
!DIR$ NOINLINE
!DIR$ INLINE
! ===========
call Decomp
&
! ===========
& (ischan, ktloop, ncsp, cc2, vdiag)
!DIR$ NOINLINE
jeval = -1
hratio = 1.0d0
nslp = nsteps + MBETWEEN
drate = 0.7d0
end if
! -------------------------------------------------------------
! Evaluate the first derivative using corrected values of cnew.
! -------------------------------------------------------------
! ========
300 continue
! ========
! ===========
call Subfun
&
! ===========
& (ischan, ktloop, nallr, ncsp, nfdh2, nfdh3, nfdl1, nfdl2, &
& nfdrep, nfdrep1, irma, irmb, irmc, cnew, rrate, nsubfun, &
& gloss, trate, nfdh1)
! ----------------------------------------------------------------
! Zero first derviatives in surface zones for species with fixed
! concentration boundary conditions. Include surface emissions in
! first derviatives.
!
! Species with non-zero fluxes (LLNL addition, PSC, 7/24/96).
! ----------------------------------------------------------------
do kloop = 1, ktloop
if (jreorder(jlooplo+kloop) <= (ilat*ilong)) then
do jspc = 1, ntspec(ncs)
jgas = inewold(jspc,1)
if (ibcb(jgas) == 1) then
gloss(kloop,jspc) = 0.0d0
else
if (do_semiss_inchem) then
gloss(kloop,jspc) = gloss(kloop,jspc) + &
& yemis(jreorder(jlooplo+kloop),jgas)
end if
end if
end do
end if
end do
! ---------------------------------------------------------------
! In the case of the chord method, compute error (gloss) from the
! corrected calculation of the first derivative.
! ---------------------------------------------------------------
do jspc = 1, ischan
j = jspc + ischan
do kloop = 1, ktloop
gloss(kloop,jspc) = (delt * gloss(kloop,jspc)) - &
& (conc(kloop,j) + dtlos(kloop,jspc))
end do
end do
! --------------------------------------------------------------
! Solve the linear system of equations with the corrector error;
! Backsub solves backsubstitution over matrix of partial derivs.
! --------------------------------------------------------------
! ============
call Backsub
&
! ============
& (ischan, ktloop, ncsp, cc2, vdiag, gloss)
! ----------------------------------------------------------------
! Sum up the accumulated error, correct the concentration with the
! error, and begin to calculate the rmsnorm of the error relative
! to chold.
! ----------------------------------------------------------------
do kloop = 1, ktloop
dely(kloop) = 0.0d0
end do
if (asn1 == 1.0d0) then
do i = 1, ischan
do kloop = 1, ktloop
dtlos(kloop,i) = dtlos(kloop,i) + gloss(kloop,i)
cnew(kloop,i) = conc(kloop,i) + dtlos(kloop,i)
errymax = gloss(kloop,i) * chold(kloop,i)
dely(kloop) = dely(kloop) + (errymax * errymax)
end do
end do
else
do i = 1, ischan
do kloop = 1, ktloop
dtlos(kloop,i) = dtlos(kloop,i) + gloss(kloop,i)
cnew(kloop,i) = conc(kloop,i) + (asn1 * dtlos(kloop,i))
errymax = gloss(kloop,i) * chold(kloop,i)
dely(kloop) = dely(kloop) + (errymax * errymax)
end do
end do
end if
! ------------------------------------------------------------------
! Set the previous rms error and calculate the new rms error.
! If dcon < 1, then sufficient convergence has occurred. Otherwise,
! if the ratio of the current to previous rmserr is decreasing,
! iterate more. If it is not, then the convergence test failed.
! ------------------------------------------------------------------
rmserrp = rmserr
der2max = 0.0d0
do kloop = 1, ktloop
if (dely(kloop) > der2max) then
der2max = dely(kloop)
end if
end do
rmserr = Sqrt (der2max * order_inv)
l3 = l3 + 1
if (l3 > 1) then
rmsrat = rmserr / rmserrp
drate = Max (0.2d0*drate, rmsrat)
else
rmsrat = 1.0d0
end if
dcon = rmserr * Min (conpst(nqq), conp15(nqq)*drate)
! --------------------------------------------------------
! If convergence occurs, go on to check accumulated error.
! --------------------------------------------------------
if (dcon > 1.0d0) then
! -------------------------------------------------------------------
! If nonconvergence after one step, re-evaluate first derivative with
! new values of cnew.
! -------------------------------------------------------------------
if (l3 == 1) then
! =========
go to 300
! =========
! ----------------------------------------------------------------
! The corrector iteration failed to converge.
!
! If the Jacobian matrix is more than one step old, update the
! Jacobian and try convergence again. If the Jacobian is current,
! then reduce the time step, reset the accumulated derivatives to
! their values before the failed step, and retry with the smaller
! step.
! ----------------------------------------------------------------
else if (jeval == 0) then
ifail = ifail + 1
jeval = 1
! =========
go to 250
! =========
end if
nfail = nfail + 1
rdelmax = 2.0d0
jeval = 1
ifsuccess = 0
xelaps = told
rdelt = fracdec
i1 = nqqisc + 1
do jb = 1, nqq
i1 = i1 - ischan
do i = i1, nqqisc
j = i + ischan
do kloop = 1, ktloop
conc(kloop,i) = conc(kloop,i) - conc(kloop,j)
end do
end do
end do
! =========
go to 200
! =========
end if
! -------------------------------------------------------------------
! The corrector iteration converged.
!
! Set jeval = 0, so that it does not need to be called the next step.
! If all else goes well. Next, test the accumulated error from the
! convergence process above.
! -------------------------------------------------------------------
jeval = 0
if (l3 > 1) then
do kloop = 1, ktloop
dely(kloop) = 0.0d0
end do
!c
do kloop = 1, ktloop
do jspc = 1, ischan
errymax = dtlos(kloop,jspc) * chold(kloop,jspc)
dely(kloop) = dely(kloop) + errymax * errymax
end do
end do
der2max = 0.0d0
do kloop = 1, ktloop
if (dely(kloop) > der2max) then
der2max = dely(kloop)
end if
end do
end if
! ----------------------------------------------------------------
! The accumulated error test failed.
!
! In all cases, reset the derivatives to their values before the
! last time step. Next:
! (a) re-estimate a time step at the same or one lower order and
! retry the step;
! (b) if the first attempts fail, retry the step at fracdec the
! the prior step;
! (c) iF this fails, reset the order to 1 and go back to the
! beginning, at order = 1, because errors of the wrong order
! have accumulated.
! ----------------------------------------------------------------
! ==============================
DER2MAXIF: if (der2max > enqq) then
! ==============================
xelaps = told
lfail = lfail + 1
jfail = jfail + 1
i1 = nqqisc + 1
do jb = 1, nqq
i1 = i1 - ischan
do i = i1, nqqisc
j = i + ischan
do kloop = 1, ktloop
conc(kloop,i) = conc(kloop,i) - conc(kloop,j)
end do
end do
end do
rdelmax = 2.0d0
if (jfail <= 6) then
ifsuccess = 0
rdeltup = 0.0d0
! =========
go to 400
! =========
else if (jfail <= 20) then
ifsuccess = 0
rdelt = fracdec
! =========
go to 200
! =========
else
delt = delt * 0.1d0
rdelt = 1.0d0
jfail = 0
jrestar = jrestar + 1
idoub = 5
do jspc = 1, ischan
do kloop = 1, ktloop
cnew(kloop,jspc) = conc(kloop,jspc)
end do
end do
if (pr_smv2) then
Write (lunsmv,970) delt, xelaps
end if
970 format ('delt dec to ', e13.5, ' at time ', e13.5, &
& ' because of excessive errors.')
if (jrestar == 100) then
if (pr_smv2) then
Write (lunsmv,980)
end if
980 format ('Smvgear: Stopping because of excessive errors.')
call GmiPrintError
('Problem in Smvgear', .true., 0, 0, 0, 0, 0.0d0, 0.0d0)
end if
! =========
go to 150
! =========
end if
! ====
else
! ====
! -------------------------------------------------------------
! All successful steps come through here.
!
! After a successful step, update the concentration and all
! derivatives, reset told, set ifsuccess = 1, increment nsteps,
! and reset jfail = 0.
! -------------------------------------------------------------
if (pr_qqjk .and. do_qqjk_inchem) then
xtimestep = xelaps - told
! =================
call Do_Smv2_Diag
&
! =================
& (jlooplo, ktloop, pr_nc_period, tdt, told, do_cell_chem, &
& jreorder, inewold, denair, cnew, xtimestep, &
& yda, qqkda, qqjda, qkgmi, qjgmi, &
& ilong, ilat, ivert, itloop, &
& CTMi1, CTMi2, CTMju1, CTMj2, CTMk1, CTMk2, &
& num_qjo, num_qks, num_qjs, num_active)
end if
jfail = 0
ifsuccess = 1
nsteps = nsteps + 1
told = xelaps
i1 = 1
do j = 2, kstep
i1 = i1 + ischan
asnqqj = aset(nqq,j)
do jspc = 1, ischan
i = jspc + i1 - 1
do kloop = 1, ktloop
conc(kloop,i) = &
& conc(kloop,i) + (asnqqj * dtlos(kloop,jspc))
end do
end do
end do
! ------------------------------
! Update chemistry mass balance.
! ------------------------------
if (asn1 == 1.0d0) then
do i = 1, ischan
do kloop = 1, ktloop
smvdm(kloop,i) = &
& smvdm(kloop,i) + dtlos(kloop,i) + explic(kloop,i)
conc(kloop,i) = conc(kloop,i) + dtlos(kloop,i)
end do
end do
else
do i = 1, ischan
do kloop = 1, ktloop
dtasn1 = asn1 * dtlos(kloop,i)
smvdm(kloop,i) = smvdm(kloop,i) + dtasn1 + explic(kloop,i)
conc (kloop,i) = conc (kloop,i) + dtasn1
end do
end do
end if
! ---------------------------------------------------
! Exit smvgear if a time interval has been completed.
! ---------------------------------------------------
timremain = tinterval - xelaps
if (timremain <= 1.0d-06) then
! =========
go to 700
! =========
end if
! -------------------------------------------------------------------
! idoub counts the number of successful steps before re-testing the
! step-size and order:
! if idoub > 1, decrease idoub and go on to the next time step with
! the current step-size and order;
! if idoub = 1, store the value of the error (dtlos) for the time
! step prediction, which will occur when idoub = 0,
! but go on to the next step with the current step
! size and order;
! if idoub = 0, test the time step and order for a change.
! -------------------------------------------------------------------
if (idoub > 1) then
idoub = idoub - 1
if (idoub == 1) then
do jspc = 1, ischan, 2
jg1 = jspc + 1
do kloop = 1, ktloop
cest(kloop,jspc) = dtlos(kloop,jspc)
cest(kloop,jg1) = dtlos(kloop,jg1)
end do
end do
end if
rdelt = 1.0d0
! =========
go to 200
! =========
end if
! ================
end if DER2MAXIF
! ================
! ------------------------------------------------------------------
! Test whether to change the step-size and order.
!
! Determine the time step at (a) one order lower than, (b) the same
! order as, and (c) one order higher than the current order. In the
! case of multiple grid-cells in a grid-block, find the minimum
! step size among all the cells for each of the orders. Then, in
! all cases, choose the longest time step among the three steps
! paired with orders, and choose the order allowing this longest
! step.
! ------------------------------------------------------------------
! ---------------------------------------------------------------
! Estimate the time step ratio (rdeltup) at one order higher than
! the current order. If nqq >= MAXORD, then we do not allow the
! order to increase.
! ---------------------------------------------------------------
if (nqq < MAXORD) then
do kloop = 1, ktloop
dely(kloop) = 0.0d0
end do
do jspc = 1, ischan
do kloop = 1, ktloop
errymax = (dtlos(kloop,jspc) - cest(kloop,jspc)) * &
& chold(kloop,jspc)
dely(kloop) = dely(kloop) + (errymax * errymax)
end do
end do
der3max = 0.0d0
do kloop = 1, ktloop
if (dely(kloop) > der3max) then
der3max = dely(kloop)
end if
end do
rdeltup = 1.0d0 / ((conp3 * der3max**enqq3(nqq)) + 1.4d-6)
else
rdeltup = 0.0d0
end if
! ========
400 continue
! ========
! ------------------------------------------------------------
! Estimate the time step ratio (rdeltsm) at the current order.
! der2max was calculated during the error tests earlier.
! ------------------------------------------------------------
rdeltsm = 1.0d0 / ((conp2 * der2max**enqq2(nqq)) + 1.2d-6)
! ------------------------------------------------------------------
! Estimate the time step ratio (rdeltdn) at one order lower than
! the current order. if nqq = 1, then we cannot test a lower order.
! ------------------------------------------------------------------
if (nqq > 1) then
do kloop = 1, ktloop
dely(kloop) = 0.0d0
end do
kstepisc = (kstep - 1) * ischan
!c
do kloop = 1, ktloop
do jspc = 1, ischan
i = jspc + kstepisc
errymax = conc(kloop,i) * chold(kloop,jspc)
dely(kloop) = dely(kloop) + (errymax * errymax)
end do
end do
der1max = 0.0d0
do kloop = 1, ktloop
if (dely(kloop) > der1max) then
der1max = dely(kloop)
end if
end do
rdeltdn = 1.0d0 / ((conp1 * der1max**enqq1(nqq)) + 1.3d-6)
else
rdeltdn = 0.0d0
end if
! -----------------------------------------------------------------
! Find the largest of the predicted time step ratios of each order.
! -----------------------------------------------------------------
rdelt = Max (rdeltup, rdeltsm, rdeltdn)
! ---------------------------------------------------------------
! If the last step was successful and rdelt is small, keep the
! current step and order, and allow three successful steps before
! re-checking the time step and order.
! ---------------------------------------------------------------
if ((rdelt < 1.1d0) .and. (ifsuccess == 1)) then
idoub = 3
! =========
go to 200
! =========
! --------------------------------------------------------------
! If the maximum time step ratio is that of one order lower than
! the current order, decrease the order. Do not minimize rdelt
! to <= 1, when ifsuccess = 0 since this is less efficient.
! --------------------------------------------------------------
else if (rdelt == rdeltdn) then
nqq = nqq - 1
! ---------------------------------------------------------------
! If the maximum time step ratio is that of one order higher than
! the current order, increase the order and add a derivative term
! for the higher order.
! ---------------------------------------------------------------
else if (rdelt == rdeltup) then
real_kstep = kstep
consmult = aset(nqq,kstep) / real_kstep
nqq = kstep
nqisc = nqq * ischan
do jspc = 1, ischan, 2
jg1 = jspc + 1
i1 = jspc + nqisc
i2 = jg1 + nqisc
do kloop = 1, ktloop
conc(kloop,i1) = dtlos(kloop,jspc) * consmult
conc(kloop,i2) = dtlos(kloop,jg1) * consmult
end do
end do
end if
! ----------------------------------------------------------------
! If the last two steps have failed, re-set idoub to the current
! order + 1. Do not minimize rdelt if jfail >= 2 since tests show
! that this merely leads to additional computations.
! ----------------------------------------------------------------
idoub = nqq + 1
! =========
go to 200
! =========
! ========
700 continue
! ========
return
end
!-----------------------------------------------------------------------------
!
! ROUTINE
! Backsub
!
! DESCRIPTION
! This routine performs back-substitutions on the decomposed matrix. It
! solves the linear set of equations Ax = B FOR x, the correction vector,
! where "A" is the L-U decompostion of the original matrix =>
!
! P = I - H x Bo x J
!
! I = identity matrix, H = time step, Bo = a coefficient that depends on
! the order of the integration method, and J is the matrix of partial
! derivatives. B is sent from Smvgear as a corrected value of the first
! derivatives of the ordinary differential equations. Decomp solved for
! "A", the decomposed matrix. See Press, et. al. (1992), Numerical
! Recipes, Cambridge University Press, for a better description of the
! back-substitution process.
!
! This back-substitution process uses sparse matrix techniques,
! vectorizes around the grid-cell dimension, and uses no partial
! pivoting. Tests by Sherman & Hindmarsh (1980), Lawrence Livermore
! Livermore Laboratory, Rep. UCRL-84102, and by us have confirmed that
! the removal of partial pivoting has little effect on results.
!
! Backsub loop # 1 =>
! First, adjust right side of Ax = B using lower triangular matrix.
! Sum 1,2,3,4, or 5 terms at a time to improve vectorization.
!
! ARGUMENTS
! ischan : # of first-order eqns to solve, = # of spc = order of original
! matrix; ischan has a different value for day and night, and for
! gas- and aqueous-phase chemistry;
! # spc with prod or loss terms in Smvgear (?)
! ktloop : # of grid-cells in a grid-block
! ncsp : ncs => for daytime gas chemistry
! ncs + ICS => for nighttime gas chemistry
! cc2 : array holding values of decomposed matrix
! vdiag : 1 / current diagonal term of the decomposed matrix
! gloss : first derivative = sum of prod minus loss rates for a spc
!
!-----------------------------------------------------------------------------
subroutine Backsub & 1
& (ischan, ktloop, ncsp, cc2, vdiag, gloss)
implicit none
# include "smv2chem_par.h"
# include "smv2chem2.h"
! ----------------------
! Argument declarations.
! ----------------------
integer, intent(in) :: ischan
integer, intent(in) :: ktloop
integer, intent(in) :: ncsp
real*8, intent(in) :: cc2 (KBLOOP, 0:MXARRAY)
real*8, intent(in) :: vdiag(KBLOOP, MXGSAER)
real*8, intent(inout) :: gloss(KBLOOP, MXGSAER)
! ----------------------
! Variable declarations.
! ----------------------
integer :: i, ij
integer :: ij0, ij1, ij2, ij3, ij4
integer :: j0, j1, j2, j3, j4
integer :: k, kc, kzt
integer :: kh1, kh2, kh3, kh4, kh5
integer :: kl1, kl2, kl3, kl4, kl5
integer :: mc, mzt
integer :: mh1, mh2, mh3, mh4, mh5
integer :: ml1, ml2, ml3, ml4, ml5
! ----------------
! Begin execution.
! ----------------
!c Write (6,*) 'Backsub called.'
ij = 1
! ==========================================
KZTLOOP: do kzt = kztlo(ncsp), kzthi(ncsp)
! ==========================================
i = ikztot(kzt)
kl5 = kbl5(kzt)
kh5 = kbh5(kzt)
kl4 = kbl4(kzt)
kh4 = kbh4(kzt)
kl3 = kbl3(kzt)
kh3 = kbh3(kzt)
kl2 = kbl2(kzt)
kh2 = kbh2(kzt)
kl1 = kbl1(kzt)
kh1 = kbh1(kzt)
! -- Sum 5 terms at a time. --
do kc = kl5, kh5
ij0 = ij
ij1 = ij + 1
ij2 = ij + 2
ij3 = ij + 3
ij4 = ij + 4
ij = ij + 5
j0 = kzeroa(kc)
j1 = kzerob(kc)
j2 = kzeroc(kc)
j3 = kzerod(kc)
j4 = kzeroe(kc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1)) - &
& (cc2(k,ij2) * gloss(k,j2)) - &
& (cc2(k,ij3) * gloss(k,j3)) - &
& (cc2(k,ij4) * gloss(k,j4))
end do
end do
! -- Sum 4 terms at a time. --
do kc = kl4, kh4
ij0 = ij
ij1 = ij + 1
ij2 = ij + 2
ij3 = ij + 3
ij = ij + 4
j0 = kzeroa(kc)
j1 = kzerob(kc)
j2 = kzeroc(kc)
j3 = kzerod(kc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1)) - &
& (cc2(k,ij2) * gloss(k,j2)) - &
& (cc2(k,ij3) * gloss(k,j3))
end do
end do
! -- Sum 3 terms at a time. --
do kc = kl3, kh3
ij0 = ij
ij1 = ij + 1
ij2 = ij + 2
ij = ij + 3
j0 = kzeroa(kc)
j1 = kzerob(kc)
j2 = kzeroc(kc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1)) - &
& (cc2(k,ij2) * gloss(k,j2))
end do
end do
! -- Sum 2 terms at a time. --
do kc = kl2, kh2
ij0 = ij
ij1 = ij + 1
ij = ij + 2
j0 = kzeroa(kc)
j1 = kzerob(kc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1))
end do
end do
! -- Sum 1 term at a time. --
do kc = kl1, kh1
ij0 = ij
ij = ij + 1
j0 = kzeroa(kc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0))
end do
end do
! ==============
end do KZTLOOP
! ==============
! ---------------------------------------------------------------
! Backsub loop # 2.
!
! Backsubstite with upper triangular matrix to find solution.
! Again, sum up several terms at a time to improve vectorization.
! ---------------------------------------------------------------
! ===========================
ILOOP: do i = ischan, 1, -1
! ===========================
mzt = imztot(i,ncsp)
! ===================
MZTIF: if (mzt > 0) then
! ===================
ml5 = mbl5(mzt)
mh5 = mbh5(mzt)
ml4 = mbl4(mzt)
mh4 = mbh4(mzt)
ml3 = mbl3(mzt)
mh3 = mbh3(mzt)
ml2 = mbl2(mzt)
mh2 = mbh2(mzt)
ml1 = mbl1(mzt)
mh1 = mbh1(mzt)
! -- Sum 5 terms at a time. --
do mc = ml5, mh5
ij0 = ij
ij1 = ij + 1
ij2 = ij + 2
ij3 = ij + 3
ij4 = ij + 4
ij = ij + 5
j0 = mzeroa(mc)
j1 = mzerob(mc)
j2 = mzeroc(mc)
j3 = mzerod(mc)
j4 = mzeroe(mc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1)) - &
& (cc2(k,ij2) * gloss(k,j2)) - &
& (cc2(k,ij3) * gloss(k,j3)) - &
& (cc2(k,ij4) * gloss(k,j4))
end do
end do
! -- Sum 4 terms at a time. --
do mc = ml4, mh4
ij0 = ij
ij1 = ij + 1
ij2 = ij + 2
ij3 = ij + 3
ij = ij + 4
j0 = mzeroa(mc)
j1 = mzerob(mc)
j2 = mzeroc(mc)
j3 = mzerod(mc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1)) - &
& (cc2(k,ij2) * gloss(k,j2)) - &
& (cc2(k,ij3) * gloss(k,j3))
end do
end do
! -- Sum 3 terms at a time. --
do mc = ml3, mh3
ij0 = ij
ij1 = ij + 1
ij2 = ij + 2
ij = ij + 3
j0 = mzeroa(mc)
j1 = mzerob(mc)
j2 = mzeroc(mc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1)) - &
& (cc2(k,ij2) * gloss(k,j2))
end do
end do
! -- Sum 2 terms at a time. --
do mc = ml2, mh2
ij0 = ij
ij1 = ij + 1
ij = ij + 2
j0 = mzeroa(mc)
j1 = mzerob(mc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0)) - &
& (cc2(k,ij1) * gloss(k,j1))
end do
end do
! -- Sum 1 term at a time. --
do mc = ml1, mh1
ij0 = ij
ij = ij + 1
j0 = mzeroa(mc)
do k = 1, ktloop
gloss(k,i) = &
& gloss(k,i) - &
& (cc2(k,ij0) * gloss(k,j0))
end do
end do
! ============
end if MZTIF
! ============
! -- Adjust gloss with diagonal element. --
do k = 1, ktloop
gloss(k,i) = gloss(k,i) * vdiag(k,i)
end do
! ============
end do ILOOP
! ============
return
end
!-----------------------------------------------------------------------------
!
! ROUTINE
! Decomp
!
! DESCRIPTION
! This routine decomposes the sparse matrix "P" into the matrix "A" in
! order to solve the linear set of equations Ax = B for x, which is a
! correction vector. Ax = B is solved in Backsub, the original matrix
! "P" is =>
!
! P = I - H x Bo x J
!
! where I = identity matrix, H = time step, Bo = a coefficient that
! depends on the order of the integration method, and J is the matrix of
! partial derivatives. See Press, et. al. (1992), Numerical Recipes,
! Cambridge University Press, for a better description of the L-U
! decompostion process.
!
! This L-U decompostion process uses sparse matrix techniques, vectorizes
! around the grid-cell dimension, and uses no partial pivoting. Tests by
! Sherman & Hindmarsh (1980), Lawrence Livermore National Laboratory,
! Rep. UCRL-84102, and by us have confirmed that the removal of partial
! pivoting has little effect on results.
!
! ARGUMENTS
! ischan : # of first-order eqns to solve, = # of spc = order of original
! matrix; ischan has a different value for day and night, and for
! gas- and aqueous-phase chemistry;
! # spc with prod or loss terms in Smvgear (?)
! ktloop : # of grid-cells in a grid-block
! ncsp : ncs => for daytime gas chemistry
! ncs + ICS => for nighttime gas chemistry
! cc2 : array of iarray units holding values of each matrix
! position actually used; originally,
! cc2 = P = I - delt * aset(nqq,1) * partial_derivatives;
! however, cc2 is decomposed here
! vdiag : 1 / current diagonal term of the decomposed matrix
!
!-----------------------------------------------------------------------------
subroutine Decomp & 1
& (ischan, ktloop, ncsp, cc2, vdiag)
implicit none
# include "smv2chem_par.h"
# include "smv2chem2.h"
! ----------------------
! Argument declarations.
! ----------------------
integer, intent(in) :: ischan
integer, intent(in) :: ktloop
integer, intent(in) :: ncsp
real*8, intent(inout) :: cc2 (KBLOOP, 0:MXARRAY)
real*8, intent(inout) :: vdiag(KBLOOP, MXGSAER)
! ----------------------
! Variable declarations.
! ----------------------
integer :: iar, ic
integer :: ih1, ih2, ih3, ih4, ih5
integer :: ij, ija, ijt
integer :: ik0, ik1, ik2, ik3, ik4
integer :: il1, il2, il3, il4, il5
integer :: j, jc, jh, jl
integer :: k
integer :: kj0, kj1, kj2, kj3, kj4
! ----------------
! Begin execution.
! ----------------
!c Write (6,*) 'Decomp called.'
! -----------------------------------------------------------
! First loop of L-U decompostion.
!
! Sum 1,2,3,4, OR 5 terms at a time to improve vectorization.
! -----------------------------------------------------------
! =======================
JLOOP: do j = 1, ischan
! =======================
! ==============================================
IJTLOOP: do ijt = ijtlo(j,ncsp), ijthi(j,ncsp)
! ==============================================
ij = ijval(ijt)
il5 = idl5 (ijt)
ih5 = idh5 (ijt)
il4 = idl4 (ijt)
ih4 = idh4 (ijt)
il3 = idl3 (ijt)
ih3 = idh3 (ijt)
il2 = idl2 (ijt)
ih2 = idh2 (ijt)
il1 = idl1 (ijt)
ih1 = idh1 (ijt)
! -- Sum 5 terms at a time. --
do ic = il5, ih5
ik0 = ikdeca(ic)
ik1 = ikdecb(ic)
ik2 = ikdecc(ic)
ik3 = ikdecd(ic)
ik4 = ikdece(ic)
kj0 = kjdeca(ic)
kj1 = kjdecb(ic)
kj2 = kjdecc(ic)
kj3 = kjdecd(ic)
kj4 = kjdece(ic)
do k = 1, ktloop
cc2(k,ij) = &
& cc2(k,ij) - &
& (cc2(k,ik0) * cc2(k,kj0)) - &
& (cc2(k,ik1) * cc2(k,kj1)) - &
& (cc2(k,ik2) * cc2(k,kj2)) - &
& (cc2(k,ik3) * cc2(k,kj3)) - &
& (cc2(k,ik4) * cc2(k,kj4))
end do
end do
! -- Sum 4 terms at a time. --
do ic = il4, ih4
ik0 = ikdeca(ic)
ik1 = ikdecb(ic)
ik2 = ikdecc(ic)
ik3 = ikdecd(ic)
kj0 = kjdeca(ic)
kj1 = kjdecb(ic)
kj2 = kjdecc(ic)
kj3 = kjdecd(ic)
do k = 1, ktloop
cc2(k,ij) = &
& cc2(k,ij) - &
& (cc2(k,ik0) * cc2(k,kj0)) - &
& (cc2(k,ik1) * cc2(k,kj1)) - &
& (cc2(k,ik2) * cc2(k,kj2)) - &
& (cc2(k,ik3) * cc2(k,kj3))
end do
end do
! -- Sum 3 terms at a time. --
do ic = il3, ih3
ik0 = ikdeca(ic)
ik1 = ikdecb(ic)
ik2 = ikdecc(ic)
kj0 = kjdeca(ic)
kj1 = kjdecb(ic)
kj2 = kjdecc(ic)
do k = 1, ktloop
cc2(k,ij) = &
& cc2(k,ij) - &
& (cc2(k,ik0) * cc2(k,kj0)) - &
& (cc2(k,ik1) * cc2(k,kj1)) - &
& (cc2(k,ik2) * cc2(k,kj2))
end do
end do
! -- Sum 2 terms at a time. --
do ic = il2, ih2
ik0 = ikdeca(ic)
ik1 = ikdecb(ic)
kj0 = kjdeca(ic)
kj1 = kjdecb(ic)
do k = 1, ktloop
cc2(k,ij) = &
& cc2(k,ij) - &
& (cc2(k,ik0) * cc2(k,kj0)) - &
& (cc2(k,ik1) * cc2(k,kj1))
end do
end do
! -- Sum 1 term at a time. --
do ic = il1, ih1
ik0 = ikdeca(ic)
kj0 = kjdeca(ic)
do k = 1, ktloop
cc2(k,ij) = &
& cc2(k,ij) - &
& (cc2(k,ik0) * cc2(k,kj0))
end do
end do
! ==============
end do IJTLOOP
! ==============
iar = jarrdiag(j,ncsp)
do k = 1, ktloop
vdiag(k,j) = 1.0d0 / cc2(k,iar)
end do
! ----------------------------
! Second loop of decompostion.
! ----------------------------
jl = jloz1(j,ncsp)
jh = jhiz1(j,ncsp)
do jc = jl, jh
ija = jzeroa(jc)
do k = 1, ktloop
cc2(k,ija) = cc2(k,ija) * vdiag(k,j)
end do
end do
! ============
end do JLOOP
! ============
return
end
!-----------------------------------------------------------------------------
!
! ROUTINE
! Pderiv
!
! DESCRIPTION
! This routine puts the partial derivatives of each ordinary differential
! equation into a matrix. The form of the matrix equation is =>
!
! P = I - H x Bo x J
!
! where I = identity matrix, H = time step, Bo = coefficient corresponding
! to the order of the method, and J is the Jacobian matrix of partial
! derivatives.
!
! Example of how partial derivatives are placed in an array =>
!
! Species: A, B, C
! Concentrations: (A), (B), (C)
!
! Reactions: 1) A --> B J
! 2) A + B --> C K1
! 3) A + B + C --> D K2
!
! First d(A) / dt = -J(A) - K1(A)(B) - K2(A)(B)(C)
! Derivatives: d(B) / dt = +J(A) - K1(A)(B) - K2(A)(B)(C)
! d(C) / dt = + K1(A)(B) - K2(A)(B)(C)
! d(D) / dt = + K2(A)(B)(C)
!
! Predictor matrix (P) = I - h * b * J:
! J = Jacobian matrix of partial derivates
! I = identity matrix
! h = time step
! b = coefficient of method
! R = h * b = -r1delt
!
! A B C D
! _____________________________________________________________________
! |
! A | 1-R(-J-K1(B)-K2(B)(C)) -R(-K1(A)-K2(A)(C)) -R(-K2(A)(B)) 0
! |
! B | -R(+J-K1(B)-K2(B)(C)) 1-R(-K1(A)-K2(A)(C)) -R(-K2(A)(B)) 0
! |
! C | -R( +K1(B)-K2(B)(C)) -R(+K1(A)-K2(A)(C)) 1-R(-K2(A)(B)) 0
! |
! D | -R( +K2(B)(C)) -R( +K2(A)(C)) -R(+K2(A)(B)) 1
!
!
! ARGUMENTS
! ischan : # of first-order eqns to solve, = # of spc = order of original
! matrix; ischan has a different value for day and night, and for
! gas- and aqueous-phase chemistry;
! # spc with prod or loss terms in Smvgear (?)
! ktloop : # of grid-cells in a grid-block
! ncsp : ncs => for daytime gas chemistry
! ncs + ICS => for nighttime gas chemistry
! nfdh2 : nfdh3 + # of rxns with two active reactants
! nfdh3 : # of rxns with three active reactants
! nfdl1 : nfdh2 + 1
! nfdl2 : nfdh3 + 1
! irma,b,c : spc # of each reactant; locates reordered active spc #s
! r1delt : = -aset(nqq,1) * time_step = -coefficient_of_method * dt
! cnew : init (and final) spc conc (# cm-3-air or moles l-1-h2o (?))
! rrate : rxn rate coefficient =>
! rates w. 1 reactant: s^-1
! rates w. 2 reactants: l-h2o mole-1 s^-1 or cm^3 #-1 s^-1 (?)
! rates w. 3 reactants: l^2-h2o m-2 s^-1 or cm^6 #-2 s^-1 (?)
! npderiv : total # of times Pderiv is called
! cc2 : array of iarray units holding values of each matrix
! position actually used;
! cc2 = P = I - delt * aset(nqq,1) * partial_derivatives
! urate : term of Jacobian (J) = partial derivative
! nfdh1 : nfdh2 + # of rxns with one active reactant
!
!-----------------------------------------------------------------------------
subroutine Pderiv & 1
& (ischan, ktloop, ncsp, nfdh2, nfdh3, nfdl1, nfdl2, irma, irmb, &
& irmc, r1delt, cnew, rrate, npderiv, cc2, urate, nfdh1)
implicit none
# include "smv2chem_par.h"
# include "smv2chem2.h"
! ----------------------
! Argument declarations.
! ----------------------
integer, intent(in) :: ischan
integer, intent(in) :: ktloop
integer, intent(in) :: ncsp
integer, intent(in) :: nfdh2, nfdh3
integer, intent(in) :: nfdl1, nfdl2
integer, intent(in) :: irma (NMTRATE)
integer, intent(in) :: irmb (NMTRATE)
integer, intent(in) :: irmc (NMTRATE)
real*8, intent(in) :: r1delt
real*8, intent(in) :: cnew (KBLOOP, MXGSAER)
real*8, intent(in) :: rrate(KBLOOP, NMTRATE)
integer, intent(inout) :: npderiv
real*8, intent(inout) :: cc2 (KBLOOP, 0:MXARRAY)
real*8, intent(inout) :: urate(KBLOOP, NMTRATE, 3)
integer, intent(out) :: nfdh1
! ----------------------
! Variable declarations.
! ----------------------
integer :: ial, iar
integer :: iarry
integer :: ja, jb, jc
integer :: k
integer :: n, nkn
integer :: nondiag ! # of final matrix positions, excluding diagonal
! terms, filled after all matrix processes
integer :: nondiag1 ! nondiag + 1
integer :: npdh, npdl
real*8 :: fracr1
! ----------------
! Begin execution.
! ----------------
!c Write (6,*) 'Pderiv called.'
! -----------------------------------------------------------
! Calculate partial derivatives and sum up partial derivative
! loss terms.
! -----------------------------------------------------------
npderiv = npderiv + 1
iarry = iarray(ncsp)
nondiag = iarry - ischan
nondiag1 = nondiag + 1
nfdh1 = nfdh2 + ioner(ncsp)
npdl = npdlo(ncsp)
npdh = npdhi(ncsp)
! -----------------------------------------------------------
! Partial derivatives for rates with three active loss terms.
! -----------------------------------------------------------
do nkn = 1, nfdh3
ja = irma(nkn)
jb = irmb(nkn)
jc = irmc(nkn)
do k = 1, ktloop
urate(k,nkn,1) = rrate(k,nkn) * cnew(k,jb) * cnew(k,jc)
urate(k,nkn,2) = rrate(k,nkn) * cnew(k,ja) * cnew(k,jc)
urate(k,nkn,3) = rrate(k,nkn) * cnew(k,ja) * cnew(k,jb)
end do
end do
! ---------------------------------------------------------
! Partial derivatives for rates with two active loss terms.
! ---------------------------------------------------------
do nkn = nfdl2, nfdh2
ja = irma(nkn)
jb = irmb(nkn)
do k = 1, ktloop
urate(k,nkn,1) = rrate(k,nkn) * cnew(k,jb)
urate(k,nkn,2) = rrate(k,nkn) * cnew(k,ja)
end do
end do
! --------------------------------------------------------
! Partial derivatives for rates with one active loss term.
! --------------------------------------------------------
do nkn = nfdl1, nfdh1
do k = 1, ktloop
urate(k,nkn,1) = rrate(k,nkn)
end do
end do
! ------------------------------------------------------------------
! Put partial derivatives production and loss terms in matrix array.
! ------------------------------------------------------------------
do iar = 1, nondiag
do k = 1, ktloop
cc2(k,iar) = 0.0d0
end do
end do
do iar = nondiag1, iarry
do k = 1, ktloop
cc2(k,iar) = 1.0d0
end do
end do
do n = npdl, npdh
nkn = nkpdterm(n)
iar = ipospd (n)
ial = iialpd (n)
fracr1 = fracpl (n) * r1delt
do k = 1, ktloop
cc2(k,iar) = cc2(k,iar) + (fracr1 * urate(k,nkn,ial))
end do
end do
return
end
!-----------------------------------------------------------------------------
!
! ROUTINE
! Subfun
!
! DESCRIPTION
! This routine evaluates the first derivative of each ordinary
! differential equation (ODE). It evaluates derivatives in the special
! form f = y'(est) = f(x,y,estimated), where f is the right hand side of
! the differential equation.
!
! Example =>
!
! Species: A, B, C
! Concentrations: (A), (B), (C)
!
! Reactions: 1) A --> B J
! 2) A + B --> C K1
! 3) A + B + C --> D K2
!
! First d(A) / dt = -J(A) - K1(A)(B) - K2(A)(B)(C)
! Derivatives: d(B) / dt = +J(A) - K1(A)(B) - K2(A)(B)(C)
! d(C) / dt = + K1(A)(B) - K2(A)(B)(C)
! d(D) / dt = + K2(A)(B)(C)
!
! ARGUMENTS
! ischan : # of first-order eqns to solve, = # of spc = order of original
! matrix; ischan has a different value for day and night, and for
! gas- and aqueous-phase chemistry;
! # spc with prod or loss terms in Smvgear (?)
! ktloop : # of grid-cells in a grid-block
! nallr : # of active rxns
! ncsp : ncs => for daytime gas chemistry
! ncs + ICS => for nighttime gas chemistry
! nfdh2 : nfdh3 + # of rxns with two active reactants
! nfdh3 : # of rxns with three active reactants
! nfdl1 : nfdh2 + 1
! nfdl2 : nfdh3 + 1
! nfdrep : nfdh3 + # of rxns with two active reactants that are not
! followed by a rxn with the same reactants
! nfdrep1 : nfdrep + 1
! irma,b,c : spc # of each reactant; locates reordered active spc #s
! cnew : init (and final) spc conc (# cm^-3-air or moles l^-1-h2o (?))
! rrate : rxn rate coefficient =>
! rates with 1 reactant: s^-1
! rates with 2 reactants: l-h2o mole^-1 s^-1 or
! cm^3 #-1 s^-1 (?)
! rates with 3 reactants: l^2-h2o m-2 s-1 or cm^6 #-2 s-1 (?)
! nsubfun : total # of times Subfun is called
! gloss : first derivative = sum of prod. minus loss rates for a spc
! trate : rxn rate (moles l^-1-h2o s^-1 or # cm^-3 s^-1 (?))
! nfdh1 : nfdh2 + # of rxns with one active reactant
!
!-----------------------------------------------------------------------------
subroutine Subfun & 2
& (ischan, ktloop, nallr, ncsp, nfdh2, nfdh3, nfdl1, nfdl2, &
& nfdrep, nfdrep1, irma, irmb, irmc, cnew, rrate, nsubfun, &
& gloss, trate, nfdh1)
implicit none
# include "smv2chem_par.h"
# include "smv2chem2.h"
! ----------------------
! Argument declarations.
! ----------------------
integer, intent(in) :: ischan
integer, intent(in) :: ktloop
integer, intent(in) :: nallr
integer, intent(in) :: ncsp
integer, intent(in) :: nfdh2, nfdh3
integer, intent(in) :: nfdl1, nfdl2
integer, intent(in) :: nfdrep, nfdrep1
integer, intent(in) :: irma (NMTRATE)
integer, intent(in) :: irmb (NMTRATE)
integer, intent(in) :: irmc (NMTRATE)
real*8, intent(in) :: cnew (KBLOOP, MXGSAER)
real*8, intent(in) :: rrate(KBLOOP, NMTRATE)
integer, intent(inout) :: nsubfun
real*8, intent(inout) :: gloss(KBLOOP, MXGSAER)
real*8, intent(inout) :: trate(KBLOOP, NMTRATE*2)
integer, intent(out) :: nfdh1
! ----------------------
! Variable declarations.
! ----------------------
integer :: ja, jb, jc
integer :: jspc
integer :: k
integer :: n, nc, nh
integer :: nh1, nh2, nh3, nh4, nh5
integer :: nk0, nk1, nk2
integer :: nk3, nk4, nkn
integer :: nl1, nl2, nl3, nl4, nl5
integer :: npl
! -----------------------------------------------------------------------
! concmult : product of concs in a rate; if two consecutive rxns have the
! same spc reacting (e.g., A + B --> C and A + B --> D + E),
! then use the same value for both rxns
! -----------------------------------------------------------------------
real*8 :: concmult
real*8 :: fracn
! ----------------
! Begin execution.
! ----------------
!c Write (6,*) 'Subfun called.'
! ----------------------
! Set rates of reaction.
! ----------------------
nsubfun = nsubfun + 1
nfdh1 = nfdh2 + ioner(ncsp)
! ---------------------------------------------------------
! First derivatives for rates with three active loss terms.
! ---------------------------------------------------------
do nkn = 1, nfdh3
ja = irma(nkn)
jb = irmb(nkn)
jc = irmc(nkn)
nh = nkn + nallr
do k = 1, ktloop
trate(k,nkn) = &
& rrate(k,nkn) * cnew(k,ja) * cnew(k,jb) * cnew(k,jc)
trate(k,nh) = -trate(k,nkn)
end do
end do
! -------------------------------------------------------
! First derivatives for rates with two active loss terms.
! -------------------------------------------------------
do nkn = nfdl2, nfdrep
ja = irma(nkn)
jb = irmb(nkn)
nh = nkn + nallr
do k = 1, ktloop
trate(k,nkn) = rrate(k,nkn) * cnew(k,ja) * cnew(k,jb)
trate(k,nh) = -trate(k,nkn)
end do
end do
! -----------------------------------------------------------
! First derivatives for rates with two active loss terms and
! where the subsequent reaction has the same reactants, but a
! different rate.
! -----------------------------------------------------------
do nkn = nfdrep1, nfdh2, 2
ja = irma(nkn)
jb = irmb(nkn)
nk2 = nkn + 1
nh = nkn + nallr
nh2 = nk2 + nallr
do k = 1, ktloop
concmult = cnew(k,ja) * cnew(k,jb)
trate(k,nkn) = rrate(k,nkn) * concmult
trate(k,nk2) = rrate(k,nk2) * concmult
trate(k,nh) = -trate(k,nkn)
trate(k,nh2) = -trate(k,nk2)
end do
end do
! ------------------------------------------------------
! First derivatives for rates with one active loss term.
! ------------------------------------------------------
do nkn = nfdl1, nfdh1
ja = irma(nkn)
nh = nkn + nallr
do k = 1, ktloop
trate(k,nkn) = rrate(k,nkn) * cnew(k,ja)
trate(k,nh) = -trate(k,nkn)
end do
end do
! --------------------------------
! Initialize first derivative = 0.
! --------------------------------
do jspc = 1, ischan
do k = 1, ktloop
gloss(k,jspc) = 0.0d0
end do
end do
! ---------------------------------------------------------------
! Sum net (not reproduced) kinetic and photo gains and losses for
! each species.
!
! Sum 1,2,3,4, or 5 terms at a time to improve vectorization.
! ---------------------------------------------------------------
! ==========================================
NPLLOOP: do npl = npllo(ncsp), nplhi(ncsp)
! ==========================================
jspc = jspnpl(npl)
nl5 = npl5(npl)
nh5 = nph5(npl)
nl4 = npl4(npl)
nh4 = nph4(npl)
nl3 = npl3(npl)
nh3 = nph3(npl)
nl2 = npl2(npl)
nh2 = nph2(npl)
nl1 = npl1(npl)
nh1 = nph1(npl)
! -- Sum 5 terms at a time. --
do nc = nl5, nh5
nk0 = lossra(nc)
nk1 = lossrb(nc)
nk2 = lossrc(nc)
nk3 = lossrd(nc)
nk4 = lossre(nc)
do k = 1, ktloop
gloss(k,jspc) = &
& gloss(k,jspc) - &
& trate(k,nk0) - trate(k,nk1) - trate(k,nk2) - &
& trate(k,nk3) - trate(k,nk4)
end do
end do
! -- Sum 4 terms at a time. --
do nc = nl4, nh4
nk0 = lossra(nc)
nk1 = lossrb(nc)
nk2 = lossrc(nc)
nk3 = lossrd(nc)
do k = 1, ktloop
gloss(k,jspc) = &
& gloss(k,jspc) - &
& trate(k,nk0) - trate(k,nk1) - trate(k,nk2) - &
& trate(k,nk3)
end do
end do
! -- Sum 3 terms at a time. --
do nc = nl3, nh3
nk0 = lossra(nc)
nk1 = lossrb(nc)
nk2 = lossrc(nc)
do k = 1, ktloop
gloss(k,jspc) = &
& gloss(k,jspc) - &
& trate(k,nk0) - trate(k,nk1) - trate(k,nk2)
end do
end do
! -- Sum 2 terms at a time. --
do nc = nl2, nh2
nk0 = lossra(nc)
nk1 = lossrb(nc)
do k = 1, ktloop
gloss(k,jspc) = &
& gloss(k,jspc) - &
& trate(k,nk0) - trate(k,nk1)
end do
end do
! -- Sum 1 term at a time. --
do nc = nl1, nh1
nk0 = lossra(nc)
do k = 1, ktloop
gloss(k,jspc) = &
& gloss(k,jspc) - &
& trate(k,nk0)
end do
end do
! ==============
end do NPLLOOP
! ==============
! --------------------------------------------------------------
! Sum production term for reactions where products fractionated.
! --------------------------------------------------------------
do n = nfrlo(ncsp), nfrhi(ncsp)
jspc = jspcnfr(n)
nkn = nknfr (n)
fracn = fracnfr(n)
do k = 1, ktloop
gloss(k,jspc) = gloss(k,jspc) + (fracn * trate(k,nkn))
end do
end do
return
end
!-----------------------------------------------------------------------------
!
! ROUTINE
! Update
!
! DESCRIPTION
! This routine updates photodissociation rates.
!
! Photorates are included in first and partial derivative equations.
!
! ARGUMENTS
! ktloop : # of grid-cells in a grid-block
! nallr : # of active rxns
! ncs : identifies gas chemistry type (1..NCSGAS)
! ncsp : ncs => for daytime gas chemistry
! ncs + ICS => for nighttime gas chemistry
! jphotrat : tbd
! ptratk1 : tbd
! rrate : rate constants
! trate : rxn rate (moles l^-1-h2o s^-1 or # cm^-3 s^-1 (?))
!
!-----------------------------------------------------------------------------
subroutine Update & 1
& (ktloop, nallr, ncs, ncsp, jphotrat, pratk1, rrate, trate)
implicit none
# include "smv2chem_par.h"
# include "smv2chem2.h"
! ----------------------
! Argument declarations.
! ----------------------
integer, intent(in) :: ktloop
integer, intent(in) :: nallr
integer, intent(in) :: ncs
integer, intent(in) :: ncsp
integer, intent(in) :: jphotrat(ICS)
real*8, intent(in) :: pratk1 (KBLOOP, IPHOT)
real*8, intent(inout) :: rrate(KBLOOP, NMTRATE)
real*8, intent(inout) :: trate(KBLOOP, NMTRATE*2)
! ----------------------
! Variable declarations.
! ----------------------
integer :: i, j
integer :: kloop
integer :: nh, nk, nkn
! ----------------
! Begin execution.
! ----------------
!c Write (6,*) 'Update called.'
! -------------------------------
! Load photolysis rate constants.
! -------------------------------
do j = 1, jphotrat(ncs)
nkn = nknphotrt(j,ncs)
do kloop = 1, ktloop
rrate(kloop,nkn) = pratk1(kloop,j)
end do
end do
! ------------------------------------------------------
! Set rates where photoreaction has no active loss term.
! ------------------------------------------------------
do i = 1, nolosp(ncsp)
nk = nknlosp(i,ncs)
nkn = newfold(nk,ncs)
nh = nkn + nallr
do kloop = 1, ktloop
trate(kloop,nkn) = rrate(kloop,nkn)
trate(kloop,nh) = -trate(kloop,nkn)
end do
end do
return
end