chemistry_0d__o3tracer.F90 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. #define TRACEBACK write (gol,'("in ",a," (",a,i6,")")') rname, __FILE__, __LINE__ ; call goErr
  2. #define IF_NOTOK_RETURN(action) if (status/=0) then; TRACEBACK; action; return; end if
  3. #define IF_ERROR_RETURN(action) if (status> 0) then; TRACEBACK; action; return; end if
  4. !
  5. #include "tm5.inc"
  6. !
  7. !
  8. ! Chemistry routines per cell.
  9. !
  10. ! Parameterized ozone scheme.
  11. !
  12. module chemistry_0d
  13. implicit none
  14. ! --- in/out --------------------------------------
  15. private
  16. public :: Cariolle_0d
  17. !public :: TropForce_0d
  18. public :: ColdTracer_0d
  19. ! --- const -----------------------------
  20. character(len=*), parameter :: mname = 'chemistry_0d'
  21. contains
  22. ! =======================================================
  23. ! :::::::::::::::::: CARIOLE CHEMISTRY SCHEME ::::::::::::::::::
  24. !
  25. ! pseudo chemistry with Cariole parametrization (see also notes)
  26. !
  27. ! dr/dt = c1 + c2*( r - c3 ) + c4*( T - c5 ) + c6*( Splus - c7 ) - k_h*r
  28. !
  29. ! = c1 + c2*( r - c3 ) + c4*( T - c5 ) + c6*( Splus-f*r + f*r - c7 ) - k_h*r
  30. !
  31. ! = [c1 - c2*c3 + c4*( T - c5 ) + c6*( Splus-f*r - c7 ) ] + [ c2 + c6*f - k_h ]*r
  32. !
  33. ! = A + B*r = B ( r - -A/B )
  34. ! = B ( r - C ) with C = -A/B
  35. !
  36. ! with
  37. ! A = c1 - c2 c3 + c4 delT + c6(Splus-c7),
  38. ! B = c2 + term accounting for contribution of the current
  39. ! layer to the column above this layer
  40. ! - extra term k_h for hetero chem
  41. ! r = volume mixing ratio
  42. ! delT = temp difference TM3/Cariolle in K
  43. ! Splus = ozone above current layer (molec cm-2)
  44. ! f = fraction for which r contributes to Splus
  45. ! c1..c7 = Cariolle coefficients, t = time in seconds
  46. ! k_h = extra term, for example from cold tracer scheme
  47. !
  48. ! local solutions:
  49. !
  50. ! r(t+dt) = -A/B + ( r(t) - -A/B ) exp[ B dt ] , B /= 0
  51. ! = C + ( r(t) - C ) exp[ B dt ]
  52. !
  53. ! r(t+dt) = r(t) + A dt , B == 0
  54. !
  55. ! Henk Eskes, KNMI, May 1999
  56. !
  57. ! o Scheme implemented for use in TM5
  58. ! Above top of the model prescribed ozone column from cariolle climatology
  59. ! Bram Bregman Wed Oct 30 11:41:09 MET 2002
  60. !
  61. ! o Generalized contribution of current layer to overhead column.
  62. ! Arjo Segers, september 2005.
  63. !
  64. ! :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  65. subroutine Cariolle_0d( o3, o3o, f, temper, cc, k_h, &
  66. timesc_min, dt, do3 )
  67. use GO , only : gol, goPr
  68. ! --- in/out ------------------------------
  69. real, intent(in) :: o3 ! ozone volume mixing ratio (ppv)
  70. real, intent(in) :: o3o ! ozone overhead (mlc/m2)
  71. real, intent(in) :: f ! factor for which o3 contributes to o3o
  72. real, intent(in) :: temper ! temperature (K)
  73. real, intent(in) :: cc(7) ! Cariolle coeff
  74. real, intent(in) :: k_h ! extra reaction rate hetro chem
  75. real, intent(in) :: timesc_min ! minimum time scale sec : [0,..)
  76. real, intent(in) :: dt ! sec
  77. real, intent(out) :: do3 ! ozone increment
  78. ! --- local -------------------------------
  79. real :: A, B
  80. real :: sgn, timesc
  81. real :: o3_new
  82. ! --- begin -------------------------------
  83. ! calculate the A term, including extra contribution from dd :
  84. A = cc(1) - cc(2) * cc(3) + cc(4) * ( temper - cc(5) ) + cc(6) * ( o3o-f*o3 - cc(7) )
  85. ! calculate the B term, including the ozone column fraction and extra term:
  86. B = cc(2) + cc(6)*f - k_h
  87. ! different solutions for B==0 and B/=0 :
  88. if ( abs(B) < tiny(1.0) ) then
  89. !
  90. ! local solution:
  91. !
  92. ! r(t+dt) = r(t) + A dt
  93. !
  94. o3_new = o3 + A * dt
  95. else
  96. !
  97. ! local solution: ( c2 < 0, and normally A > 0 and B < 0)
  98. !
  99. ! r(t+dt) = ( r(t) + A/B ) exp[ B dt ] - A/B
  100. !
  101. ! time scale of reaction:
  102. ! exp[ B dt ] = exp[ sgn |B| dt ] = exp[ sgn dt / (1/|B|) ]
  103. ! bound time scale to prevent too fast chemistry:
  104. sgn = sign( 1.0, B )
  105. timesc = max( timesc_min, 1.0/abs(B) ) ! sec, [0,..)
  106. ! apply:
  107. o3_new = ( o3 + A/B )*exp(sgn*dt/timesc) - A/B
  108. end if
  109. ! trap negatives ...
  110. o3_new = max( 0.0, o3_new )
  111. ! return increment:
  112. do3 = o3_new - o3
  113. end subroutine Cariolle_0d
  114. ! ! :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  115. ! !
  116. ! ! This subroutine relaxes the tropospheric ozone to the
  117. ! ! climatology of Fortuin
  118. ! !
  119. ! ! dr/dt = -1/tau ( r - r_clim )
  120. ! ! ~= c2 ( r - c3 )
  121. ! !
  122. ! ! r(t+dt) = r_clim + ( r(t) - r_clim ) exp[ - dt / tau ]
  123. ! !
  124. ! ! Relaxation time scale is altitude depended:
  125. ! !
  126. ! ! tau(1) tau(2)
  127. ! ! (weeks) (months)
  128. ! ! |
  129. ! ! | * ------ plevs(1) (230 hPa)
  130. ! ! | o
  131. ! ! |
  132. ! ! | o
  133. ! ! |
  134. ! ! | o
  135. ! ! | * ------ plevs(2) (500 hPa)
  136. ! ! | o
  137. ! ! |
  138. ! ! | o
  139. ! ! |
  140. ! ! | o
  141. ! ! |
  142. ! ! | o
  143. ! ! --------------------------------> tau
  144. ! !
  145. ! ! Henk Eskes, KNMI, May 2000
  146. ! ! :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  147. !
  148. ! subroutine TropForce_0d( o3, o3c, p, plevs, taus, dt, do3, status )
  149. !
  150. ! ! --- in/out ---------------------------------
  151. !
  152. ! real, intent(in) :: o3 ! ozone
  153. ! real, intent(in) :: o3c ! ozone climat
  154. ! real, intent(in) :: p ! pressure level (Pa)
  155. ! real, intent(in) :: dt ! sec
  156. ! real, intent(in) :: plevs(2) ! pressure levels (Pa)
  157. ! real, intent(in) :: taus(2) ! time scales (sec)
  158. ! real, intent(out) :: do3 ! ozone increment
  159. ! integer, intent(out) :: status
  160. !
  161. ! ! --- const --------------------------------
  162. !
  163. ! character(len=*), parameter :: rname = mname//'/TropForce'
  164. !
  165. ! ! --- local ---------------------------------
  166. !
  167. ! real :: tau
  168. ! real :: alfa
  169. ! real :: o3_new
  170. !
  171. ! ! --- begin ------------------------------------
  172. !
  173. ! ! only for troposphere:
  174. ! if ( p < plevs(1) ) then
  175. !
  176. ! ! just copy:
  177. ! o3_new = o3
  178. !
  179. ! else
  180. !
  181. ! ! troposphere; relax towards climat:
  182. !
  183. ! ! set time scale:
  184. ! if ( p >= plevs(2) ) then
  185. ! ! lower troposphere
  186. ! tau = taus(1)
  187. ! else
  188. ! ! interpolate between plev(1) and plev(2) :
  189. ! alfa = ( p - plevs(1) ) / ( plevs(2) - plevs(1) )
  190. ! tau = taus(1) * (1.0-alfa ) + taus(2) * alfa
  191. ! end if
  192. !
  193. ! ! relax:
  194. ! o3_new = o3c + ( o3 - o3c ) * exp( - dt / tau )
  195. !
  196. ! end if
  197. !
  198. ! ! return increment:
  199. ! do3 = o3_new - o3
  200. !
  201. ! ! ok
  202. ! status = 0
  203. !
  204. ! end subroutine TropForce_0d
  205. ! ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  206. !
  207. ! Parameterization of PSC's
  208. !
  209. ! A cold tracer X is used to mark and track air below a temperature threshold.
  210. !
  211. ! Production and loss of X :
  212. !
  213. ! dX/dt = k_p ( 1 - X ) - k_l X
  214. !
  215. ! k_p = 1/tau_p : T < Tpsc and |lat| > 40 deg
  216. ! = 0 : otherwise
  217. !
  218. ! k_l = 1/tau_l : sza > 0 (day)
  219. ! = 0 : otherwise (night)
  220. !
  221. ! Rewrite:
  222. !
  223. ! dX/dt = k_p - k_p X - k_l X = -(k_p+k_l) [ X - k_p/(k_p+k_l) ]
  224. !
  225. ! with local solution:
  226. !
  227. ! X(t+dt) = X(t) , if k_p = k_l = 0
  228. !
  229. ! = kp/(kp+kl) + [ X(t) - kp/(kp+kl) ] exp[ -(kp+kl) dt ] , otherwise
  230. !
  231. ! Adding an additional term to the Cariolle Parameterization:
  232. !
  233. ! dO3/dt = c1 + c2*(O3-c3) + c4*(T-c5) + c6*(S-c7) - kX O3
  234. ! ^^^^^^^
  235. ! where
  236. !
  237. ! k = 1/tau : sza > 0 (day)
  238. ! = 0 : otherwise
  239. !
  240. ! Peter Braesicke, Cambridge, Centre for Atmospheric Science
  241. !
  242. ! ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  243. !
  244. ! Routine returns:
  245. ! o new cold tracer value : X(t+dt)
  246. ! o factor to be used in Cariolle scheme : kX(t)
  247. !
  248. ! ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  249. subroutine ColdTracer_0d( X, p, act_plevs, T, csza, lat, dt, dX, kX, status )
  250. use go, only : gol, goErr, goPr
  251. use go, only : TDate
  252. ! --- in/out ----------------------------
  253. real, intent(in) :: X ! X(t) (0-1)
  254. real, intent(in) :: p ! pressure (Pa)
  255. real, intent(in) :: act_plevs(2) ! active pressure range
  256. real, intent(in) :: T ! temperature (K)
  257. real, intent(in) :: csza ! cos_sza [-1,1]
  258. real, intent(in) :: lat ! latitude (deg)
  259. real, intent(in) :: dt ! time step (sec)
  260. real, intent(out) :: dX ! X(t+dt)-X(t)
  261. real, intent(out) :: kX ! to be used in cariolle scheme
  262. integer, intent(out) :: status
  263. ! --- const --------------------------------
  264. character(len=*), parameter :: rname = mname//'/ColdTracer_0d'
  265. ! --- const ------------------------------
  266. ! time scale for heterogeneous breakdown of ozone - 10 to 20 days
  267. real, parameter :: tau_het = 12.0 * 86400.0 ! 12 days
  268. ! time scale for creating activation
  269. real, parameter :: tau_prod = 4.0 * 3600.0 ! 4 hours
  270. ! time scale for loss of activation,
  271. ! Northern and Southern hemisphere - 5,10 d
  272. real, parameter :: tau_loss_nh = 5.0 * 86400.0 ! 5 days
  273. real, parameter :: tau_loss_sh = 10.0 * 86400.0 ! 10 days
  274. ! --- local --------------------------------
  275. logical :: active_level
  276. real :: Tpsc
  277. real :: kp, kl
  278. real :: X_new
  279. ! --- begin ------------------------------------
  280. ! check ...
  281. if ( (X < 0.0) .or. (X > 1.0+1e-3) ) then
  282. write (gol,'("cold tracer X should be in [0,1] ; found : ",es12.4)') X; call goErr
  283. write (gol,'("in ",a)') rname; call goErr; status=1; return
  284. end if
  285. ! active level ?
  286. active_level = (p >= minval(act_plevs)) .and. (p <= maxval(act_plevs))
  287. !
  288. ! hetro chem : ozone loss by psc
  289. !
  290. ! hetero chem ?
  291. ! set factor in Cariolle scheme if day and cold tracer > 0.03
  292. if ( (csza > 0.0) .and. (X > 0.03) .and. active_level ) then
  293. ! day
  294. kX = X / tau_het
  295. else
  296. ! night or not in strato:
  297. kX = 0.0
  298. end if
  299. !
  300. ! production and loss of activation field
  301. !
  302. ! Compute Temp_NAT as function of pressure .
  303. ! Document 'Parameterization of PSCs' by Peter Braesicke
  304. ! gives table of pressures (hPa) and temperature (K) :
  305. !
  306. ! pressure (hPa) temperature (K)
  307. ! -------------- ---------------
  308. ! 31.62 191.74
  309. ! 38.31 192.77
  310. ! 46.42 193.81
  311. ! 56.23 194.85
  312. ! 68.13 195.91
  313. ! 82.54 196.98
  314. ! 100.00 198.06
  315. ! 120.23 199.12
  316. ! 141.90 200.07
  317. ! 164.40 200.93
  318. ! 187.42 201.69
  319. ! 210.43 202.38
  320. ! 233.43 202.99
  321. !
  322. ! This points are on a line, thus probably created from a parameterization.
  323. ! Linear fit by idl through the points:
  324. ! temperature_K = 146.229 + 5.635 * LOG( Pressure_Pa )
  325. !
  326. Tpsc = 146.229 + 5.635*log(p)
  327. ! production rate:
  328. if ( (T < Tpsc) .and. (abs(lat) > 40.0) .and. active_level ) then
  329. kp = 1.0 / tau_prod
  330. else
  331. kp = 0.0
  332. end if
  333. ! loss rate:
  334. if ( csza > 0.0 ) then
  335. ! daylight, thus destruction of psc
  336. if ( lat > 0.0 ) then
  337. ! northern hemisphere
  338. kl = 1.0 / tau_loss_nh
  339. else
  340. ! southern hemisphere
  341. kl = 1.0 / tau_loss_sh
  342. end if
  343. else
  344. ! night
  345. kl = 0.0
  346. end if
  347. ! apply destruction and loss:
  348. if ( (kp < tiny(1.0)) .and. (kl < tiny(1.0)) ) then
  349. ! no production and loss, thus unchanged:
  350. X_new = X
  351. else
  352. ! local solution:
  353. X_new = kp/(kp+kl) + ( X - kp/(kp+kl) ) * exp( -(kp+kl)*dt )
  354. end if
  355. ! truncate:
  356. X_new = min( max( 0.0, X_new ), 1.0 )
  357. ! return increment:
  358. dX = X_new - X
  359. !
  360. ! done
  361. !
  362. ! ok
  363. status = 0
  364. end subroutine ColdTracer_0d
  365. end module chemistry_0d