MNO2RetrievalError.F90 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. ! First include the set of model-wide compiler flags
  2. #include "tm5.inc"
  3. ! #ifdef TROPNLL2DP
  4. !
  5. ! #define IF_NOTOK_RETURN(notok_string) call fortranlog(notok_string,len(notok_string),2); status=1; return
  6. ! #define PRINT(string) call fortranlog(string,len(string),2)
  7. !
  8. ! #else
  9. #define IF_NOTOK_RETURN(notok_string) write (*,'(a)') notok_string; status=1; return
  10. #define PRINT(string) write (*,'(a)') trim(string)
  11. ! #endif
  12. module MNO2RetrievalError
  13. !-------------------------------------------------------------------
  14. ! Routine "NO2ErrEstimate" for calculating the error in the
  15. ! retrieval of NO2 due to various assumptions on
  16. ! clouds, albedo, and NO2 profile
  17. !
  18. ! the module contains routines for the various contributions
  19. ! to the error:
  20. ! ErrFcl - contribution of Fresco cloud fraction errors
  21. ! ErrPcl - contribution of Fresco cloud top pressure errors
  22. ! ErrAlb - contribution of albedo map uncertainties
  23. ! ErrProfile - contribution related to NO2 profile variations
  24. ! [JDM, deleted, we use 10% of the AMF]
  25. ! ErrMix - contribution related to NO2 mixing in TM3 BL
  26. !
  27. ! On top of this the model accounts for:
  28. ! - the DOAS SCD retrieval error
  29. ! - the error in estimating the stratospheric NO2 "background"
  30. !
  31. ! Folkert Boersma, Henk Eskes, KNMI 2002-2003
  32. !
  33. !-------------------------------------------------------------------
  34. use MTAmf, only : TAmfLut
  35. use Mrweight, only : rweight
  36. use MAmfGet, only : GetAmf, FitWindowCentre
  37. implicit none
  38. private
  39. public :: NO2ErrEstimate, modelStratVcdError
  40. public :: forecastError
  41. ! ~~~~~~~~~~~~~~~~~~~~~~~~~~
  42. ! Error parameters:
  43. ! vertical column forecast error (stratospheric case)
  44. real, parameter :: forecastError = 0.2 ! 1e15 molecules/cm2 (new choice: observation error reduced)
  45. ! real, parameter :: forecastError = 0.15 ! 1e15 molecules/cm2 (old choice)
  46. ! Error in the Fresco cloud fractions
  47. !JDMERR real,parameter :: frescoError = 0.05
  48. real, parameter :: frescoError = 0.025
  49. ! Error in the Fresco cloud top pressure estimate
  50. real, parameter :: cloudTopError = 50.0 ! unit hPa
  51. ! Error in surface albedo
  52. !JDMERR real,parameter :: albedoError = 0.02
  53. real, parameter :: albedoError = 0.015
  54. ! Slant column retrieval (DOAS) error
  55. !JDMERR real,parameter :: retrievalScdError = 0.4 ! unit 1e15 molec/cm2
  56. real, parameter :: retrievalScdError = 0.55 ! unit 1e15 molec/cm2
  57. ! Error in estimate of the stratospheric column (from assimilation)
  58. !JDMERR real,parameter :: modelStratVcdError = 0.25 ! unit 1e15 molec/cm2
  59. real, parameter :: modelStratVcdError = 0.2 ! unit 1e15 molec/cm2
  60. ! ~~~~~~~~~~~~~~~~~~~~~~~~~~
  61. ! TM3 covariance lookup table
  62. !he_cov character(160),parameter :: &
  63. !he_cov CovPath= '/net/bsgi18/nobackup/users/eskes/tm3no2a/tmcovar'
  64. logical :: firstcall = .true.
  65. contains
  66. subroutine NO2ErrEstimate( lm, &
  67. cldfraction,cldtoppres,rclear,rcloud, &
  68. amflev,amfclearlev,amfcloudlev, &
  69. albclear,albcloud,amfgeo, &
  70. ix4a,iy4a,w4a, &
  71. at,bt,surfpres,sza,vza,aza, &
  72. no2collev,ltropopause, &
  73. amftotal,amftroptotal, &
  74. satvcd,satvcdtrop, &
  75. amfLut, &
  76. errVcdTotal,errVcdTropTotal,errVcdStratTotal, &
  77. errVcdTotalAK,errVcdTropTotalAK, &
  78. status )
  79. ! ------------------------------------------------------------------
  80. ! Compute an error estimate for the tropospheric vertical column and
  81. ! total vertical column
  82. ! input:
  83. ! for explanation of the variables, see "ass_Hx.f90"
  84. ! output:
  85. ! errVcdTotal : error on the total vertical NO2 column
  86. ! errVcdTropTotal : error on the tropospheric NO2 column
  87. ! errVcdStratTotal : error on the stratospheric NO2 column
  88. ! errVcdTotalAK : error on the total vertical NO2 column, when the
  89. ! averaging kernels are used
  90. ! (sum of observation + kernel error)
  91. ! errVcdTropTotalAK : same, for tropospheric column
  92. ! status : > 0 means error
  93. ! ------------------------------------------------------------------
  94. ! use MAmfLookupTable, only : FitWindowCentre, GetAmf
  95. implicit none
  96. ! in/out
  97. integer,intent(in) :: lm
  98. integer,intent(in) :: ltropopause
  99. integer,dimension(4),intent(in) :: ix4a, iy4a
  100. real,dimension(4),intent(in) :: w4a
  101. real,intent(in) :: cldfraction,cldtoppres
  102. real,intent(in) :: rclear,rcloud,amfgeo
  103. real,intent(in) :: albclear,albcloud,surfpres
  104. real,intent(in) :: sza,vza,aza
  105. real,intent(in) :: amftotal,amftroptotal
  106. real,intent(in) :: satvcd,satvcdtrop
  107. real,dimension(lm),intent(in) :: amflev,amfclearlev,amfcloudlev
  108. real,dimension(lm),intent(in) :: no2collev
  109. real,dimension(lm+1),intent(in) :: at,bt
  110. type(TAmfLut), intent(inout) :: amfLut
  111. !
  112. real,intent(out) :: errVcdTotal
  113. real,intent(out) :: errVcdTropTotal, errVcdStratTotal
  114. real,intent(out) :: errVcdTotalAK, errVcdTropTotalAK
  115. integer, intent(out) :: status
  116. ! local
  117. character(*), parameter :: rname = 'NO2ErrEstimate'
  118. !he_cov real, dimension(im,jm,lm,lm) :: cov_matrix
  119. !he_cov real, dimension(im,jm,lm) :: no2mean
  120. real :: errtotal_fcl,errtotal_pcl,errtotal_alb
  121. real :: errtroptotal_prof,errtotal_prof
  122. real :: errtroptotal_fcl,errtroptotal_pcl,errtroptotal_alb
  123. real :: err_fcl,err_pcl,err_alb,no2vcdtotal,no2vcdtroptotal
  124. real :: fcl_alb_correlation
  125. real :: erramftroptotal,erramftroptotalAK
  126. real :: erramftotal,erramftotalAK
  127. real :: errscd,errscdstrat
  128. real :: errvcdobs,errvcdstrat
  129. real :: errvcdamf,errvcdamfAK
  130. real :: errtroptotal_mix, errtotal_mix
  131. integer :: l
  132. ! On start, and at beginning of new month: read TM3 NO2 covariance matrix
  133. !he_cov if( firstcall .or. newmonth )then
  134. !he_cov call ReadCov( CovPath,month,no2mean,cov_matrix )
  135. !he_cov firstcall = .false.
  136. !he_cov end if
  137. status = 0
  138. ! initialise total slant column errors
  139. errtotal_fcl = 0.0
  140. errtotal_pcl = 0.0
  141. errtotal_alb = 0.0
  142. no2vcdtotal = 0.0
  143. errtroptotal_fcl = 0.0
  144. errtroptotal_pcl = 0.0
  145. errtroptotal_alb = 0.0
  146. no2vcdtroptotal = 0.0
  147. do l = 1, lm
  148. call ErrFcl(cldfraction,rclear,rcloud, &
  149. amfclearlev(l),amfcloudlev(l),err_fcl,status)
  150. if ( status > 0 ) then
  151. IF_NOTOK_RETURN('ERROR in NO2ErrEstimate, ErrFcl')
  152. end if
  153. call ErrPcl(l,lm,cldfraction,rclear,surfpres,cldtoppres,sza,vza,aza, &
  154. albcloud,amfclearlev(l),amfgeo,at,bt,amfLut,err_pcl,status)
  155. if ( status > 0 ) then
  156. IF_NOTOK_RETURN('ERROR in NO2ErrEstimate, ErrPcl')
  157. end if
  158. call ErrAlb(l,lm,cldfraction,rcloud,surfpres,sza,vza,aza, &
  159. albclear,amfcloudlev(l),amfgeo,at,bt,amfLut,err_alb,status)
  160. if ( status > 0 ) then
  161. IF_NOTOK_RETURN('ERROR in NO2ErrEstimate, ErrAlb')
  162. end if
  163. errtotal_fcl = errtotal_fcl + err_fcl*no2collev(l)
  164. errtotal_pcl = errtotal_pcl + err_pcl*no2collev(l)
  165. errtotal_alb = errtotal_alb + err_alb*no2collev(l)
  166. no2vcdtotal = no2vcdtotal + no2collev(l)
  167. if( l <= ltropopause )then
  168. errtroptotal_fcl = errtroptotal_fcl + err_fcl*no2collev(l)
  169. errtroptotal_pcl = errtroptotal_pcl + err_pcl*no2collev(l)
  170. errtroptotal_alb = errtroptotal_alb + err_alb*no2collev(l)
  171. no2vcdtroptotal = no2vcdtroptotal + no2collev(l)
  172. end if
  173. end do
  174. if ( no2vcdtotal < 1e-7 ) then
  175. IF_NOTOK_RETURN('ERROR in ass_err_retr.f90; no2vcdtotal is zero.')
  176. end if
  177. errtotal_fcl = errtotal_fcl / no2vcdtotal
  178. errtotal_pcl = errtotal_pcl / no2vcdtotal
  179. errtotal_alb = errtotal_alb / no2vcdtotal
  180. if ( no2vcdtroptotal < 1e-7 ) then
  181. IF_NOTOK_RETURN('ERROR in ass_err_retr.f90; no2vcdtroptotal is zero')
  182. end if
  183. errtroptotal_fcl = errtroptotal_fcl / no2vcdtroptotal
  184. errtroptotal_pcl = errtroptotal_pcl / no2vcdtroptotal
  185. errtroptotal_alb = errtroptotal_alb / no2vcdtroptotal
  186. ! profile variation contribution, total column
  187. !he_cov call ErrProfile(amflev,amftotal,lm, &
  188. !he_cov no2mean,cov_matrix,ix4a,iy4a,w4a,errtotal_prof)
  189. !
  190. !he_cov on average the profile related errors are about 10%
  191. if ( amftotal > 1.0e-7 ) then
  192. errtotal_prof = 0.1*amftotal
  193. else
  194. errtotal_prof = 999.0
  195. end if
  196. !
  197. ! profile variation contribution, troposphere only
  198. !he_cov call ErrProfile(amflev,amftroptotal,ltropopause, &
  199. !he_cov no2mean,cov_matrix,ix4a,iy4a,w4a,errtroptotal_prof)
  200. !
  201. !he_cov on average the profile related errors are about 10%
  202. if ( amftroptotal > 0.1 ) then
  203. errtroptotal_prof = 0.1*amftroptotal
  204. else
  205. errtroptotal_prof = 999.0
  206. end if
  207. ! boundary layer mixing error contribution, troposphere
  208. !JDMERR call ErrMixing(lm,no2collev,at,bt,surfpres,amflev,&
  209. !JDMERR ltropopause,errtroptotal_mix)
  210. errtroptotal_mix = 0.0
  211. ! boundary layer mixing error contribution, total
  212. !JDMERR call ErrMixing(lm,no2collev,at,bt,surfpres,amflev,&
  213. !JDMERR lm,errtotal_mix)
  214. errtotal_mix = 0.0
  215. ! Correlation between cloud fraction and albedo errors
  216. ! from Koelemeijer, JGR 106, 3475, 2001 (Fresco)
  217. if ( abs(albcloud-albclear) < 1.0e-5 ) then
  218. ! print*,'WARNING: cloud albedo and clear albedo have identical values'
  219. ! print*,'in ass_err_retr.f90.'
  220. ! print*,'Cloud albedo: ',albcloud,'Clear albedo: ',albclear
  221. fcl_alb_correlation = 1.0
  222. else
  223. fcl_alb_correlation = - ((1.0-cldfraction)*albedoError)/ &
  224. ((albcloud-albclear)*frescoError)
  225. if ( fcl_alb_correlation > 1.0 ) fcl_alb_correlation = 1.0
  226. if ( fcl_alb_correlation < -1.0 ) fcl_alb_correlation = -1.0
  227. endif
  228. !print*,'Check 1',errtotal_fcl
  229. !print*,'Check 2',errtotal_pcl
  230. !print*,'Check 3',errtotal_alb
  231. !print*,'Check 4',errtotal_prof
  232. !print*,'Check 5',errtotal_mix
  233. !print*,'Check 6',fcl_alb_correlation
  234. ! compute total air-mass factor error
  235. if (errtotal_fcl**2 + errtotal_pcl**2 + &
  236. errtotal_alb**2 + errtotal_prof**2 + &
  237. errtotal_mix**2 + 2.0 * fcl_alb_correlation * &
  238. errtotal_fcl*errtotal_alb < 0.0 ) then
  239. PRINT('NO2ErrEstimate: Small error in total air-mass factor')
  240. erramftotal = sqrt( errtotal_fcl**2 + errtotal_pcl**2 + &
  241. errtotal_alb**2 + errtotal_prof**2 + &
  242. errtotal_mix**2 )
  243. else
  244. erramftotal = sqrt( errtotal_fcl**2 + errtotal_pcl**2 + &
  245. errtotal_alb**2 + errtotal_prof**2 + &
  246. errtotal_mix**2 + 2.0 * fcl_alb_correlation * &
  247. errtotal_fcl*errtotal_alb )
  248. endif
  249. ! compute total air-mass factor error
  250. if (errtotal_fcl**2 + errtotal_pcl**2 + &
  251. errtotal_alb**2 + &
  252. 2.0 * fcl_alb_correlation * &
  253. errtotal_fcl*errtotal_alb < 0.) then
  254. PRINT('NO2ErrEstimate: Small error in total AK error')
  255. erramftotalAK = sqrt( errtotal_fcl**2 + errtotal_pcl**2 + &
  256. errtotal_alb**2)
  257. else
  258. erramftotalAK = sqrt( errtotal_fcl**2 + errtotal_pcl**2 + &
  259. errtotal_alb**2 + &
  260. 2.0 * fcl_alb_correlation * &
  261. errtotal_fcl*errtotal_alb )
  262. endif
  263. ! compute total tropospheric air-mass factor error
  264. if (errtroptotal_fcl**2 + errtroptotal_pcl**2 + &
  265. errtroptotal_alb**2 + errtroptotal_prof**2 + &
  266. errtroptotal_mix**2 + 2.0 * fcl_alb_correlation * &
  267. errtroptotal_fcl*errtroptotal_alb < 0.) then
  268. PRINT('NO2ErrEstimate: Small error in tropospheric air-mass factor')
  269. erramftroptotal = sqrt( errtroptotal_fcl**2 + errtroptotal_pcl**2 + &
  270. errtroptotal_alb**2 + errtroptotal_prof**2 + &
  271. errtroptotal_mix**2)
  272. else
  273. erramftroptotal = sqrt( errtroptotal_fcl**2 + errtroptotal_pcl**2 + &
  274. errtroptotal_alb**2 + errtroptotal_prof**2 + &
  275. errtroptotal_mix**2 + 2.0 * fcl_alb_correlation * &
  276. errtroptotal_fcl*errtroptotal_alb )
  277. endif
  278. ! compute total tropospheric air-mass factor error, without profile term
  279. if (errtroptotal_fcl**2 + errtroptotal_pcl**2 + &
  280. errtroptotal_alb**2 + &
  281. 2.0 * fcl_alb_correlation * &
  282. errtroptotal_fcl*errtroptotal_alb < 0.) then
  283. PRINT('NO2ErrEstimate: Small error in tropospheric air-mass factor')
  284. erramftroptotalAK = sqrt( errtroptotal_fcl**2 + errtroptotal_pcl**2 + &
  285. errtroptotal_alb**2)
  286. else
  287. erramftroptotalAK = sqrt( errtroptotal_fcl**2 + errtroptotal_pcl**2 + &
  288. errtroptotal_alb**2 + &
  289. 2.0 * fcl_alb_correlation * &
  290. errtroptotal_fcl*errtroptotal_alb )
  291. endif
  292. ! error tropospheric vertical column as sum of air-mass factor error,
  293. ! slant column error and stratospheric slant column error
  294. ! measured slant column error, units 1e15 molec/cm2
  295. errscd = retrievalScdError
  296. ! measured stratospheric slant column error, units 1e15 molec/cm2
  297. errscdstrat = modelStratVcdError*amfgeo
  298. ! error due to measurement uncertainty
  299. if ( amftroptotal > 0.1 ) then
  300. errvcdobs = errscd/amftroptotal
  301. ! error due to stratospheric reference
  302. errvcdstrat = errscdstrat/amftroptotal
  303. ! error due to tropospheric AMF
  304. errvcdamf = erramftroptotal*satvcdtrop/amftroptotal
  305. ! error due to tropospheric AMF, when kernels are used
  306. errvcdamfAK = erramftroptotalAK*satvcdtrop/amftroptotal
  307. else
  308. errvcdobs = 999.0
  309. errvcdstrat = 999.0
  310. errvcdamf = 999.0
  311. errvcdamfAK = 999.0
  312. end if
  313. ! error in tropospheric column:
  314. ! tropospheric slant column error, units 1e15 molec/cm2
  315. if ( ( errvcdobs*errvcdobs + errvcdstrat*errvcdstrat + &
  316. errvcdamf*errvcdamf ) < 0.) then
  317. IF_NOTOK_RETURN('NO2ErrEstimate: ERROR in total tropospheric column calculation')
  318. else
  319. errVcdTropTotal = sqrt( errvcdobs*errvcdobs + &
  320. errvcdstrat*errvcdstrat + &
  321. errvcdamf*errvcdamf )
  322. end if
  323. ! tropospheric slant column error, when kernels are used
  324. if ( ( errvcdobs*errvcdobs + errvcdstrat*errvcdstrat + &
  325. errvcdamfAK*errvcdamfAK) < 0. ) then
  326. IF_NOTOK_RETURN('NO2ErrEstimate: ERROR in total tropospheric column calculation')
  327. else
  328. errVcdTropTotalAK = sqrt( errvcdobs*errvcdobs + &
  329. errvcdstrat*errvcdstrat + &
  330. errvcdamfAK*errvcdamfAK )
  331. end if
  332. ! error in stratospheric column:
  333. errVcdStratTotal = modelStratVcdError
  334. ! error in total column:
  335. ! error due to the air-mass factor
  336. if ( amftotal > 1.0e-7 ) then
  337. errvcdamf = satvcd*erramftotal/amftotal
  338. ! error due to the air-mass factor
  339. errvcdamfAK = satvcd*erramftotalAK/amftotal
  340. ! error due to measurement uncertainty
  341. errvcdobs = errscd/amftotal
  342. else
  343. errvcdamf = 999.0
  344. errvcdamfAK = 999.0
  345. errvcdobs = 999.0
  346. end if
  347. ! vertical total column error
  348. if ( ( errvcdobs*errvcdobs + errvcdamf*errvcdamf ) < 0. ) then
  349. IF_NOTOK_RETURN('NO2ErrEstimate: ERROR in total column error calculation')
  350. else
  351. errVcdTotal = sqrt( errvcdobs*errvcdobs + &
  352. errvcdamf*errvcdamf )
  353. end if
  354. ! vertical total column error, when kernels are used
  355. if ( (errvcdobs*errvcdobs + errvcdamfAK*errvcdamfAK) < 0.) then
  356. IF_NOTOK_RETURN('NO2ErrEstimate: ERROR in total column error calculation')
  357. else
  358. errVcdTotalAK = sqrt( errvcdobs*errvcdobs + &
  359. errvcdamfAK*errvcdamfAK )
  360. end if
  361. end subroutine NO2ErrEstimate
  362. !*********************************************************************
  363. subroutine ErrFcl(cldfraction,rclear,rcloud,amfclear,amfcloud,err_fcl,status)
  364. !=======================================================================
  365. !
  366. ! Compute the uncertainty in the air-mass factor due to cloud uncertainties
  367. !
  368. ! Input:
  369. ! cldfraction : cloud fraction for this pixel
  370. ! rclear : radiance weight of clear part of pixel
  371. ! rcloud : radiance for cloudy part of pixel
  372. ! amfclear : air-mass factor of 100% cloud-free pixel
  373. ! amfcloud : air-mass factor of 100% cloudy pixel
  374. !
  375. ! Output:
  376. ! err_fcl : Uncertainty in AMF due to uncertainty in cloud fraction
  377. ! status : > 0 when error occurred
  378. !
  379. ! Folkert Boersma, KNMI, oct 2002
  380. !=======================================================================
  381. implicit none
  382. ! in/out:
  383. real, intent(in) :: cldfraction, rclear, rcloud
  384. real, intent(in) :: amfclear, amfcloud
  385. real, intent(out) :: err_fcl
  386. integer, intent(out) :: status
  387. ! local
  388. character(*), parameter :: rname = 'ErrFcl'
  389. real :: radsat1,radsat2
  390. ! amf for cloud fraction that is 0.05 too low
  391. real :: amf_min,amf_plus
  392. real :: cldfraction_min,cldfraction_plus
  393. cldfraction_min = max(0.0,cldfraction-0.001)
  394. cldfraction_plus = min(1.0,cldfraction+0.001)
  395. radsat1=(1.0-cldfraction_min)*rclear+ &
  396. (cldfraction_min)*rcloud
  397. radsat2=(1.0-cldfraction_plus)*rclear+ &
  398. (cldfraction_plus)*rcloud
  399. ! determine new AMF with incorrect cloud fraction
  400. if ( radsat1 == 0. .or. radsat2 == 0.) then
  401. IF_NOTOK_RETURN('ERROR in err_fcl: radsat1 = 0.0')
  402. end if
  403. amf_min = (1.0-cldfraction_min)*rclear*amfclear/radsat1 &
  404. +cldfraction_min*rcloud*amfcloud/radsat1
  405. amf_plus = (1.0-cldfraction_plus)*rclear*amfclear/radsat2 &
  406. +cldfraction_plus*rcloud*amfcloud/radsat2
  407. ! determine random error in AMF due to cloud fraction uncertainty
  408. if ( (cldfraction_plus-cldfraction_min) < 1e-7) then
  409. IF_NOTOK_RETURN('ERROR in err_fcl: cldfraction_plus-cldfraction_min = 0.0')
  410. end if
  411. err_fcl = frescoerror*(amf_plus-amf_min)/ &
  412. (cldfraction_plus-cldfraction_min)
  413. !
  414. end subroutine ErrFcl
  415. subroutine ErrPcl(l,lm,cldfraction,rclear,surfpres,cloudpres_in,sza,vza,aza, &
  416. albcloud,amfclear,amfgeo,at,bt,amfLut,err_pcl,status)
  417. !=======================================================================
  418. !
  419. ! Compute the uncertainty in the air-mass factor due to cloud
  420. ! pressure uncertainties
  421. !
  422. ! Input:
  423. ! l : level index
  424. ! lm : number of levels
  425. ! cloudfraction : cloud pressure for this pixel
  426. ! rclear : radiance for cloud-free pixel
  427. ! surfpres : surface pressure
  428. ! cloudpres_in : cloud top pressure
  429. ! sza,vza,aza : solar, viewing, azimuth angles
  430. ! albcloud : cloud albedo
  431. ! amfclear : air-mass factor of 100% cloud-free pixel
  432. ! amfgeo : geometrical air-mass factor
  433. ! at,bt : pressure level indices
  434. ! amfLut : the NO2 air-mass factor LUT
  435. !
  436. ! Output:
  437. ! err_pcl : Uncertainty in AMF due to cloud pressure
  438. ! status : > 0 means error
  439. !
  440. ! Folkert Boersma, KNMI, oct 2002
  441. !=======================================================================
  442. implicit none
  443. ! in/out
  444. integer,intent(in) :: l, lm
  445. real, intent(in) :: cldfraction, rclear, cloudpres_in, surfpres, sza, vza, aza
  446. real, intent(in) :: albcloud,amfclear,amfgeo
  447. real, dimension(lm+1), intent(in) :: at,bt
  448. type(TAmfLut), intent(inout) :: amfLut
  449. real, intent(out) :: err_pcl
  450. integer, intent(out) :: status
  451. ! local
  452. character(*), parameter :: rname = 'ErrPcl'
  453. real :: cloudpres, cloudpres_plus, cloudpres_min, rcloud_plus, rcloud_min
  454. real :: amfcloud_plus,amfcloud_min,radsat_plus,radsat_min,amf_plus,amf_min
  455. real :: phybrTop,phybrBot,phybrid,amfcloudrel,abovecloudfraction
  456. status = 0
  457. cloudpres = cloudpres_in
  458. if ( cloudpres < 130.0 ) cloudpres = 130.0
  459. ! Cloud pressure perturbation for finite differencing
  460. cloudpres_plus = min(surfpres,cloudpres + 10.0)
  461. cloudpres_min = min(surfpres - 20.0, cloudpres - 10.0)
  462. !if( cloudpres_plus > 1013 ) cloudpres_plus = 1013
  463. !if( cloudpres_min > 1013 ) cloudpres_min = 1013
  464. !if( cloudpres_plus < 130 ) cloudpres_plus = 130
  465. !if( cloudpres_min < 130 ) cloudpres_min = 130
  466. ! estimate radiation intensities for cloud conditions
  467. call rweight( FitWindowCentre,cloudpres_plus,albcloud, &
  468. sza, vza, aza, &
  469. rcloud_plus )
  470. call rweight( FitWindowCentre,cloudpres_min,albcloud, &
  471. sza, vza, aza, &
  472. rcloud_min )
  473. ! determine mid-level TM3 pressures (in hPa)
  474. phybrTop = 0.01*( at(l+1) + 100.0*surfpres*bt(l+1) )
  475. phybrBot = 0.01*( at(l) + 100.0*surfpres*bt(l) )
  476. phybrid = 0.5*( phybrTop + phybrBot )
  477. ! read sensitivities from the AMF lookup table: 100% cloud case
  478. ! perturbed cloud top pressures
  479. if( cloudpres_plus >= phybrTop-1.0e-5 ) then
  480. if( cloudpres_plus >= phybrBot-1.0e-5 ) then
  481. ! model level above the cloud top
  482. call GetAmf( phybrid,aza,vza,sza, &
  483. albcloud,cloudpres_plus,amfLut,amfcloudrel,status )
  484. if ( status > 0 ) then
  485. IF_NOTOK_RETURN('ERROR returned by GetAmf, LUT range error')
  486. end if
  487. amfcloud_plus=amfcloudrel*amfgeo
  488. else
  489. ! model level contains cloud top
  490. if (phybrBot-phybrTop < 1e-7 ) then
  491. IF_NOTOK_RETURN('ERROR in err_pcl: phybrBot-phybrTop == 0.')
  492. endif
  493. abovecloudfraction = (cloudpres_plus-phybrTop)/(phybrBot-phybrTop)
  494. call GetAmf( phybrid,aza,vza,sza, &
  495. albcloud,phybrBot,amfLut,amfcloudrel,status )
  496. if ( status > 0 ) then
  497. IF_NOTOK_RETURN('ERROR returned by GetAmf, LUT range error')
  498. end if
  499. !call GetAmf( phybrid,aza,vza,sza, &
  500. ! albcloud,phybrid+1.0e-5,amfcloudrel )
  501. amfcloud_plus=amfcloudrel*amfgeo*abovecloudfraction
  502. end if
  503. else
  504. ! model level below cloud top
  505. amfcloud_plus=0.0 ! below the cloud
  506. end if
  507. if( cloudpres_min >= phybrTop-1.0e-5 ) then
  508. if( cloudpres_min >= phybrBot-1.0e-5 ) then
  509. ! model level above the cloud top
  510. call GetAmf( phybrid,aza,vza,sza, &
  511. albcloud,cloudpres_min,amfLut,amfcloudrel,status )
  512. if ( status > 0 ) then
  513. IF_NOTOK_RETURN('ERROR returned by GetAmf, LUT range error')
  514. end if
  515. amfcloud_min=amfcloudrel*amfgeo
  516. else
  517. ! model level contains cloud top
  518. if (phybrBot-phybrTop < 1e-7) then
  519. IF_NOTOK_RETURN('ERROR in err_pcl: phybrBot-phybrTop == 0.')
  520. end if
  521. abovecloudfraction = (cloudpres_min-phybrTop)/(phybrBot-phybrTop)
  522. call GetAmf( phybrid,aza,vza,sza, &
  523. albcloud,phybrBot,amfLut,amfcloudrel,status )
  524. if ( status > 0 ) then
  525. IF_NOTOK_RETURN('ERROR returned by GetAmf, LUT range error')
  526. end if
  527. !call GetAmf( phybrid,aza,vza,sza, &
  528. ! albcloud,phybrid+1.0e-5,amfcloudrel )
  529. amfcloud_min=amfcloudrel*amfgeo*abovecloudfraction
  530. end if
  531. else
  532. ! model level below cloud top
  533. amfcloud_min=0.0 ! below the cloud
  534. end if
  535. ! estimated observed radiance (normalization factor)
  536. radsat_min =(1.0-cldfraction)*rclear+cldfraction*rcloud_min
  537. radsat_plus=(1.0-cldfraction)*rclear+cldfraction*rcloud_plus
  538. ! determine new AMF with incorrect cloud fraction
  539. if ( radsat_min == 0. .or. radsat_plus == 0. ) then
  540. IF_NOTOK_RETURN('ERROR in ErrPcl: radsat_min or radsat_plus = 0.')
  541. end if
  542. amf_min = (1.0-cldfraction)*rclear*amfclear/radsat_min &
  543. +cldfraction*rcloud_min*amfcloud_min/radsat_min
  544. amf_plus = (1.0-cldfraction)*rclear*amfclear/radsat_plus &
  545. +cldfraction*rcloud_plus*amfcloud_plus/radsat_plus
  546. ! determine random error in AMF due to cloud pressure uncertainty
  547. if ( cloudpres_plus-cloudpres_min < 1e-7 ) then
  548. err_pcl = 0.0
  549. else
  550. err_pcl = cloudtoperror*(amf_plus-amf_min)/ &
  551. (cloudpres_plus-cloudpres_min)
  552. end if
  553. end subroutine ErrPcl
  554. subroutine ErrAlb ( l,lm,cldfraction,rcloud,surfpres,sza,vza,aza, &
  555. albclear,amfcloud,amfgeo,at,bt,amfLut,err_alb,status )
  556. !=======================================================================
  557. !
  558. ! Compute the uncertainty in the air-mass factor due to
  559. ! surface albedo uncertainties
  560. !
  561. ! Input:
  562. ! l : level index
  563. ! lm : number of levels
  564. ! cloudfraction : cloud pressure for this pixel
  565. ! rcloud : radiance for cloudy part of pixel
  566. ! surfpres : surface pressure
  567. ! sza,vza,aza : solar, viewing, azimuth angles
  568. ! albclear : clear albedo
  569. ! amfcloud : air-mass factor of 100% cloudy pixel
  570. ! amfgeo : geometrical air-mass factor
  571. ! at,bt : pressure level indices
  572. ! amfLut : the NO2 air-mass factor LUT
  573. !
  574. ! Output:
  575. ! err_alb : Error in AMF due to albedo errors
  576. ! status : > 0 means error
  577. !
  578. ! Folkert Boersma, KNMI, oct 2002
  579. !=======================================================================
  580. implicit none
  581. ! in/out
  582. integer,intent(in) :: l, lm
  583. real, intent(in) :: cldfraction,rcloud,surfpres,sza,vza,aza
  584. real, intent(in) :: albclear,amfcloud,amfgeo
  585. real,dimension(lm+1), intent(in) :: at, bt
  586. type(TAmfLut), intent(inout) :: amfLut
  587. real, intent(out) :: err_alb
  588. integer, intent(out) :: status
  589. ! local
  590. character(*), parameter :: rname = 'ErrAlb'
  591. real :: albedo_plus,albedo_min,rclear_plus,rclear_min
  592. real :: amfclear_plus,amfclear_min,radsat_plus,radsat_min,amf_plus,amf_min
  593. real :: phybrTop,phybrBot,phybrid
  594. status = 0
  595. ! limits check
  596. if ( albclear < -0.00001 .or. albclear > 1.00001 ) then
  597. IF_NOTOK_RETURN('ERROR n ErrAlb: albclear out of range')
  598. end if
  599. if ( l < 1 .or. l > lm ) then
  600. IF_NOTOK_RETURN('ERROR n ErrAlb: l out of range')
  601. end if
  602. ! Cloud pressure perturbation for finite differencing
  603. albedo_min = max(0.0,albclear - 0.001)
  604. albedo_plus = min(1.0,albclear + 0.001)
  605. ! estimate radiation intensities for cloud-free conditions
  606. call rweight( FitWindowCentre,surfpres,albedo_min, &
  607. sza, vza, aza, &
  608. rclear_min )
  609. call rweight( FitWindowCentre,surfpres,albedo_plus, &
  610. sza, vza, aza, &
  611. rclear_plus )
  612. ! determine mid-level TM3 pressures (in hPa)
  613. phybrTop = 0.01*( at(l+1) + 100.0*surfpres*bt(l+1) )
  614. phybrBot = 0.01*( at(l) + 100.0*surfpres*bt(l) )
  615. phybrid = 0.5*( phybrTop + phybrBot )
  616. ! determine the clear amf for albedo perturbations
  617. call GetAmf( phybrid, aza, vza, sza, &
  618. albedo_min, surfpres, amfLut, amfclear_min,status )
  619. if ( status > 0 ) then
  620. IF_NOTOK_RETURN('ERROR returned by GetAmf, LUT range error')
  621. end if
  622. amfclear_min = amfclear_min*amfgeo
  623. call GetAmf( phybrid, aza, vza, sza, &
  624. albedo_plus, surfpres, amfLut, amfclear_plus,status )
  625. if ( status > 0 ) then
  626. IF_NOTOK_RETURN('ERROR returned by GetAmf, LUT range error')
  627. end if
  628. amfclear_plus = amfclear_plus*amfgeo
  629. ! estimated observed radiance (normalization factor)
  630. radsat_min =(1.0-cldfraction)*rclear_min + cldfraction*rcloud
  631. radsat_plus=(1.0-cldfraction)*rclear_plus + cldfraction*rcloud
  632. ! determine new AMF with incorrect cloud fraction
  633. if ( abs(radsat_min)< 0.00001 .or. abs(radsat_plus) < 0.00001 ) then
  634. IF_NOTOK_RETURN('ERROR in ErrAlb: radsat_min or radsat_plus = 0')
  635. end if
  636. amf_min = (1.0-cldfraction)*rclear_min*amfclear_min/radsat_min &
  637. +cldfraction*rcloud*amfcloud/radsat_min
  638. amf_plus = (1.0-cldfraction)*rclear_plus*amfclear_plus/radsat_plus &
  639. +cldfraction*rcloud*amfcloud/radsat_plus
  640. ! determine random error in AMF due to cloud pressure uncertainty
  641. if ( albedo_plus-albedo_min < 1e-7) then
  642. IF_NOTOK_RETURN('ERROR in ErrAlb: albedo_plus-albedo_min = 0')
  643. end if
  644. err_alb = albedoError*(amf_plus-amf_min)/ &
  645. (albedo_plus-albedo_min)
  646. !
  647. end subroutine ErrAlb
  648. subroutine ErrMixing( lm,no2collev,at,bt,surfpres,amflev,lmax,errTotalMix,status )
  649. !=======================================================================
  650. !
  651. ! Compute an estimate of the air-mass factor error related to
  652. ! boundary layer mixing errors in the model. A simple estimate is
  653. ! obtained by comparing the air-mass factor with constant mixing
  654. ! ratio's in the lowest 4 TM3 layers ("well mixed") with a case where
  655. ! all no2 of the lowest 4 layers is concentrated in layer 1 ("no mixing")
  656. !
  657. ! Input:
  658. ! lm : number of vertical levels
  659. ! no2collev : original no2 profile (vector)
  660. ! at : pressure parameter
  661. ! bt : pressure parameter
  662. ! surfpres : surface pressure
  663. ! amflev : height-dependent air-mass factor (vector)
  664. ! lmax : top level for air-mass computation
  665. ! Output:
  666. ! errTotalMix : Error in AMF due to mixing errors in TM3
  667. ! status : > 0 on error
  668. !
  669. ! KFB & HJE, KNMI, March 19, 2003
  670. !=======================================================================
  671. implicit none
  672. ! in/out
  673. integer,intent(in) :: lm
  674. integer,intent(in) :: lmax
  675. real,dimension(lm),intent(in) :: no2collev,amflev
  676. real,dimension(lm+1),intent(in) :: at,bt
  677. real,intent(in) :: surfpres
  678. real,intent(out) :: errTotalMix
  679. integer, intent(out) :: status
  680. ! local
  681. character(*), parameter :: rname = 'ErrMixing'
  682. integer :: l
  683. real,dimension(6) :: p, d_p
  684. real :: d_p_layer, no2layer
  685. real :: no2slcfcPlus, no2vcdfcPlus
  686. real :: no2slcfcMin, no2vcdfcMin
  687. real :: amftotalPlus,amftotalMin
  688. real, dimension(:), allocatable :: no2_mixed, no2_bottom
  689. ! begin code
  690. !if ( lm /= 19 ) then
  691. ! print*,'ERROR ErrMixing: routine only valid for 19-layer model'
  692. ! stop
  693. !end if
  694. if ( lm /= 31 .and. lm /= 38 .and. lm /= 35 .and. lm /= 34 ) then
  695. IF_NOTOK_RETURN('ERROR ErrMixing: routine only valid for 31, 34, 35 or 38 layer model')
  696. end if
  697. allocate ( no2_mixed(lm) )
  698. allocate ( no2_bottom(lm) )
  699. no2slcfcPlus = 0.0
  700. no2vcdfcPlus = 0.0
  701. no2slcfcMin = 0.0
  702. no2vcdfcMin = 0.0
  703. no2layer = 0.0
  704. no2layer = SUM(no2collev(1:5))
  705. do l=1,6
  706. ! compute pressure at bottom of layer
  707. p(l) = 0.01*( at(l) + 100.0*surfpres*bt(l) )
  708. end do
  709. ! compute pressure difference over one layer, and 5 layers
  710. do l=1,5
  711. d_p(l) = p(l) - p(l+1)
  712. end do
  713. d_p_layer = p(1) - p(6)
  714. ! CASE 1 : lowest 5 model layers well mixed
  715. do l = 1, lmax
  716. if ( l < 6 ) then
  717. ! Redistribute NO2 in lowest 5 layers
  718. if (d_p_layer == 0.) then
  719. IF_NOTOK_RETURN('ERROR in ass_err_retr.f90; d_p_layer = 0.')
  720. end if
  721. no2_mixed(l) = no2layer * (d_p(l)/d_p_layer)
  722. else
  723. ! Above layer 5, leave things the same
  724. no2_mixed(l) = no2collev(l)
  725. end if
  726. ! compute model-predicted slant column
  727. ! no2_mixed = NO2 mass in grid cell (units 10^15 molecules cm^-2)
  728. no2slcfcPlus = no2slcfcPlus + amflev(l)*no2_mixed(l)
  729. ! model vertical column (10^15 molec cm^-2)
  730. no2vcdfcPlus = no2vcdfcPlus + no2_mixed(l)
  731. end do
  732. ! total air-mass factor for "mixed BL"
  733. if ( no2vcdfcPlus < 1.0e-7 ) then
  734. IF_NOTOK_RETURN('ERROR in ass_err_retr.f90; no2vcdfcPlus = 0.')
  735. end if
  736. amftotalPlus = no2slcfcPlus/no2vcdfcPlus ! model SLC / model VCD
  737. ! CASE 2 : NO2 of lowest 5 model layers inserted in layer 1 ("no mixing")
  738. no2_bottom(1) = no2layer
  739. no2_bottom(2:5) = 0.0
  740. no2_bottom(6:lmax) = no2collev(6:lmax)
  741. ! compute model-predicted slant column
  742. ! no2_bottom = NO2 mass in grid cell (units 10^15 molecules cm^-2)
  743. do l = 1, lmax
  744. ! model slant column
  745. no2slcfcMin = no2slcfcMin + amflev(l)*no2_bottom(l)
  746. ! model vertical column (10^15 molec cm^-2)
  747. no2vcdfcMin = no2vcdfcMin + no2_bottom(l)
  748. end do
  749. ! total air-mass factor for non-mixed BL
  750. if ( no2vcdfcMin < 1.0e-7 ) then
  751. IF_NOTOK_RETURN('ERROR in ass_err_retr.f90; no2vcdfcMin = 0.')
  752. end if
  753. amftotalMin = no2slcfcMin/no2vcdfcMin ! model SLC / model VCD
  754. ! 2 sigma is difference between two AMF's
  755. errTotalMix = 0.5*(amftotalPlus - amfTotalMin)
  756. deallocate ( no2_mixed )
  757. deallocate ( no2_bottom )
  758. end subroutine ErrMixing
  759. end module MNO2RetrievalError