!============================================================================= ! ! $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