MOmFSuper.F90 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. ! First include the set of model-wide compiler flags
  2. #include "tm5.inc"
  3. module MOmFSuper
  4. !
  5. ! Cluster NO2 observations and observation operators into
  6. ! superobservation for TM gridcells
  7. !
  8. ! Henk Eskes, KNMI, 2002
  9. ! modified Nov 2004, aug 2015
  10. implicit none
  11. private
  12. public :: GetOMFHxSuper, TObsInfoReduced
  13. public :: ObsInfoReduced_allocate, ObsInfoReduced_deallocate
  14. ! ----------------------------------------------------
  15. ! "TObsInfoReduced" contains the
  16. ! observation, forecast and the observation operator
  17. ! for the superobservations
  18. !
  19. ! count : number of grid cells with NO2 observations
  20. ! obsi : pixel longitude grid index
  21. ! obsj : pixel latitude grid index
  22. ! nomf : Nr of obs in grid cell
  23. ! slcobs : grid-mean slant column observation
  24. ! slcfc : grid-mean slant column forecast
  25. ! vcdfc : grid-mean column forecast
  26. ! sigobs : grid-mean observation error
  27. ! hobs : observation operator (kernel) vector for
  28. ! each observed grid cell
  29. !
  30. ! NOTE: the slant columns are divided by the geometrical air-mass factor
  31. ! ----------------------------------------------------
  32. type TObsInfoReduced
  33. integer :: count
  34. integer,dimension(:),allocatable :: obsi, obsj, nomf
  35. real,dimension(:),allocatable :: slcobs, slcfc, vcdfc, sigobs
  36. real,dimension(:,:),allocatable :: hobs
  37. end type TObsInfoReduced
  38. ! the "usePixel" flag is shared between the forecast and analysis superobservations
  39. ! it list the individual observations used to construct the superobservations
  40. logical, dimension(:), allocatable :: usePixel
  41. contains
  42. subroutine GetOMFHxSuper( im, jm, lm, no2Tr, ObsFcInfo, ObsInfoReduced, anFc, computeRetrievalForAnalysis )
  43. !=======================================================================
  44. !
  45. ! GetOMFHxReduced
  46. ! Determine a reduced set of Observation minus Forecast
  47. ! innovations and observation operators, defined on the
  48. ! model grid
  49. !
  50. ! Data is stored in structure "ObsInfoReduced"
  51. !
  52. ! Input:
  53. ! im, jm, lm : model grid dimensions
  54. ! no2Tr : Observation info: geometry, scd etc
  55. ! ObsFcInfo : Observation, forecast and observation operator, for
  56. ! all pixels (forecast or analysis)
  57. ! AnFc : 'a' = analysis, 'f' = forecast
  58. !
  59. ! Output:
  60. ! "ObsInfoReduced" - of type "TObsInfoReduced"
  61. ! Contains grid cell indices, OmF innovations,
  62. ! the observation errors and
  63. ! the corresponding observation operator vectors
  64. ! the superobservations are slant columns / geometrical air mass factor
  65. !
  66. !
  67. ! Henk Eskes, KNMI, 2002
  68. !=======================================================================
  69. use MTObsTrack, only : TObsTrack
  70. use MTObsFcInfo, only : TObsFcInfo
  71. implicit none
  72. ! in/out
  73. integer, intent(in) :: im, jm, lm
  74. type(TObsTrack),intent(in) :: no2Tr
  75. type(TObsFcInfo),intent(in) :: ObsFcInfo
  76. type(TObsInfoReduced),intent(inout) :: ObsInfoReduced
  77. character(1), intent(in) :: anFc
  78. logical, intent(in) :: computeRetrievalForAnalysis
  79. ! parameters to accept/reject observations
  80. real,parameter :: maxSZA = 80.0 ! maximum solar zenith angle
  81. real,parameter :: maxLatRelative = 5 ! no assimilation near Npole !JDM1132
  82. !real,parameter :: maxLatIndex = jm-2 ! no assimilation near Npole !JDM1132
  83. real,parameter :: minLatIndex = 5 ! no assimilation near Spole !JDM1132
  84. ! real,parameter :: minLatIndex = 3 ! no assimilation near Spole !JDM1132
  85. ! real,parameter :: maxCloudFrac = 0.8 ! maximum cloud fraction
  86. real,parameter :: maxCloudFrac = 1.01 ! maximum cloud fraction
  87. real,parameter :: minCloudFrac = -0.01 ! minimum cloud fraction
  88. !real,parameter :: maxOmFdiff = 4.0 ! maximum difference VCD obs-forec (1e15 molec/cm2)
  89. real,parameter :: maxOmFdiff = 10.0 ! maximum difference VCD obs-forec (1e15 molec/cm2)
  90. ! stratos slc error / amf_geo (1e15 molec/cm2)
  91. ! real,parameter :: strat_error = 0.25 ! old value, resulting in (a-f)/(o-f) about 30%
  92. real,parameter :: strat_error = 0.15 ! corresponding to a superobs SLC noise level of about 0.4 for an AMF of 2.5
  93. ! tropos slc error / amf_geo (1e15 molec/cm2)
  94. real,parameter :: trop_error = 4.00
  95. ! estimated average observation correlation inside a TM grid box
  96. real,parameter :: obscorfac=1.0 ! el. [0..1]
  97. ! local
  98. integer, dimension(:,:), allocatable :: grid_index
  99. integer, dimension(:,:), allocatable :: grid_count
  100. !
  101. integer :: i, j, l, ipix, ipixred, ipixredmax, iplusj
  102. integer :: nnegative, nbadpix, nlargeslcsig, nsza, npole, ncloud
  103. real :: sig,snorm,sigcor
  104. real :: omf,omfm1,omfm2,slcgeotrop,slcgeo
  105. real :: omf_m1_pixels, omf_m2_pixels
  106. integer :: omf_count
  107. real :: maxLatIndex
  108. !
  109. logical :: QualityFlag
  110. !
  111. ! observation selection - remove the following:
  112. ! 1) high solar zenith angles
  113. ! 2) high cloud fractions
  114. ! 3) negative observations
  115. ! 4) Pixels with errors
  116. !
  117. ! Consistency check: both structures should contain the same nr of observations
  118. if ( ObsFcInfo%count /= no2Tr%count ) then
  119. stop 'ERROR GetOMFHxSuper, number of observations inconsistent !'
  120. end if
  121. if ( anFc == 'f' ) then
  122. ! UsePixel is used for both the forecast and analysis sweep, and is allocated at forecast
  123. print *, 'Allocate usePixel'
  124. allocate ( usePixel(no2Tr%count) )
  125. end if
  126. ! Determine which pixels to use for the superobs (usePixel) and provide feedback
  127. ! on how many pixels were removed
  128. if ( anFc == 'f' ) then
  129. maxLatIndex = jm + 1 - maxLatRelative
  130. nsza = 0
  131. npole = 0
  132. ncloud = 0
  133. nnegative = 0
  134. nlargeslcsig = 0
  135. nbadpix = 0
  136. print *, 'GetOMFHxSuper: WARNING, keeping only 1 in 2 superobs, i+j=odd'
  137. do ipix = 1, no2Tr%count
  138. OmF = (no2Tr%no2slc(ipix)-ObsFcInfo%no2slcfc(ipix)) &
  139. / ObsFcInfo%amfgeo(ipix)
  140. QualityFlag = ( Abs(OmF) <= maxOmFdiff )
  141. i = ObsFcInfo%icell(ipix)
  142. j = ObsFcInfo%jcell(ipix)
  143. ! Speed up: Only assimilate 1 in 2 grid cells, sum(i+j) odd, checkerboard
  144. iplusj = i+j+1
  145. usePixel(ipix) = &
  146. ( no2Tr%solarZenithAngle(ipix) < maxSZA ) .and. &
  147. ( ObsFcInfo%jcell(ipix) <= maxLatIndex ) .and. &
  148. ( ObsFcInfo%jcell(ipix) >= minLatIndex ) .and. &
  149. ( no2Tr%cloudFraction(ipix) <= maxCloudFrac ) .and. &
  150. ( no2Tr%cloudFraction(ipix) >= minCloudFrac ) .and. &
  151. ( no2Tr%no2slc(ipix) > 0.0 ) .and. &
  152. ( no2Tr%no2slcError(ipix) < 100.0 ) .and. &
  153. ( iand(ObsFcInfo%flag(ipix), 255) == 0 ) .and. &
  154. ( mod(iplusj,2) == 0 ) .and. &
  155. QualityFlag
  156. if ( no2Tr%solarZenithAngle(ipix) >= maxSZA ) nsza = nsza + 1
  157. if ( ( ObsFcInfo%jcell(ipix) > maxLatIndex ) .or. &
  158. ( ObsFcInfo%jcell(ipix) < minLatIndex ) ) npole = npole + 1
  159. if ( ( no2Tr%cloudFraction(ipix) > maxCloudFrac ) .or. &
  160. ( no2Tr%cloudFraction(ipix) < minCloudFrac ) ) ncloud = ncloud + 1
  161. if ( no2Tr%no2slc(ipix) <= 0.0 ) nnegative = nnegative + 1
  162. if ( no2Tr%no2slcError(ipix) >= 100.0 ) nlargeslcsig = nlargeslcsig + 1
  163. if ( .not. QualityFlag ) nbadpix = nbadpix + 1
  164. end do
  165. ! warning/info messages
  166. if ( nsza > 0 ) then
  167. write(*,'(a,i5,a,i5,a)')' GetOMFHxSuper: ', &
  168. nsza,' of ',ObsFcInfo%count,' pixels have too large SZA'
  169. end if
  170. if ( npole > 0 ) then
  171. write(*,'(a,i5,a,i5,a)')' GetOMFHxSuper: ', &
  172. npole,' of ',ObsFcInfo%count,' pixels rejected: close to the poles'
  173. end if
  174. if ( ncloud > 0 ) then
  175. write(*,'(a,i5,a,i5,a)')' GetOMFHxSuper: ', &
  176. ncloud,' of ',ObsFcInfo%count,' pixels with cloud fraction outside range'
  177. end if
  178. if ( nnegative > 0 ) then
  179. write(*,'(a,i5,a,i5,a)')' GetOMFHxSuper: ', &
  180. nnegative,' of ',ObsFcInfo%count,' pixels are negative'
  181. end if
  182. if ( nlargeslcsig > 0 ) then
  183. write(*,'(a,i5,a,i5,a)')' GetOMFHxSuper: ', &
  184. nlargeslcsig,' of ',ObsFcInfo%count,' pixels have error > 100% '
  185. end if
  186. if ( nbadpix > 0 ) then
  187. write(*,'(a,i5,a,f6.2)')' GetOMFHxSuper: Quality control, ', &
  188. nbadpix,' pixels found with abs(OmF) > ',maxOmFdiff
  189. end if
  190. end if ! ( anFc == 'f' )
  191. ! At this point the list of pixels to use ("usePixel") is known
  192. ! Write mean OmF statistics
  193. omf_m1_pixels = 0.0
  194. omf_m2_pixels = 0.0
  195. omf_count = 0
  196. do ipix = 1, no2Tr%count
  197. if ( usePixel(ipix) ) then
  198. omf = (no2Tr%no2slc(ipix)-ObsFcInfo%no2slcfc(ipix)) &
  199. / ObsFcInfo%amfgeo(ipix)
  200. omf_m1_pixels = omf_m1_pixels + omf
  201. omf_m2_pixels = omf_m2_pixels + omf*omf
  202. omf_count = omf_count + 1
  203. end if
  204. end do
  205. omf_m1_pixels = omf_m1_pixels / real(max(1,omf_count))
  206. omf_m2_pixels = omf_m2_pixels / real(max(1,omf_count))
  207. print '(a,i8)', ' GetOMFHxSuper: number of pixels used to build superobs =',omf_count
  208. if ( anFc == 'f' ) then
  209. write(*,103) omf_m1_pixels, sqrt(omf_m2_pixels-omf_m1_pixels*omf_m1_pixels)
  210. 103 format(' GetOMFHxSuper: <o-f> = ',f8.4,' std = ',f8.4,' (individual pixels)' )
  211. else
  212. write(*,104) omf_m1_pixels, sqrt(omf_m2_pixels-omf_m1_pixels*omf_m1_pixels)
  213. 104 format(' GetOMFHxSuper: <o-a> = ',f8.4,' std = ',f8.4,' (individual pixels)')
  214. end if
  215. ! determine number of pixels in each cell, and total number of superobservations
  216. allocate ( grid_index(im,jm) )
  217. allocate ( grid_count(im,jm) )
  218. print *,'grid index and count allocated'
  219. grid_index(:,:) = 0
  220. grid_count(:,:) = 0
  221. ipixredmax = 0
  222. do ipix = 1, no2Tr%count
  223. if ( usePixel(ipix) ) then
  224. if ( iand(ObsFcInfo%flag(ipix), 255) == 0 ) then
  225. i = ObsFcInfo%icell(ipix)
  226. j = ObsFcInfo%jcell(ipix)
  227. if ( grid_count(i,j) == 0 ) then
  228. ipixredmax = ipixredmax + 1
  229. grid_index(i,j) = ipixredmax
  230. end if
  231. grid_count(i,j) = grid_count(i,j) + 1
  232. else
  233. ! the analysis may have more flagged pixels than the forecast
  234. ! discard also those pixels
  235. usePixel(ipix) = .false.
  236. end if
  237. end if
  238. end do
  239. write (*, 105) ipixredmax
  240. 105 format(' GetOMFHxSuper: number of superobs =',i6)
  241. ! Number of superobs is now known: allocate storage
  242. call ObsInfoReduced_allocate ( ipixredmax, lm, ObsInfoReduced )
  243. ! calculate obs, forecast and observation operators for superobservations
  244. ObsInfoReduced%count = ipixredmax
  245. ObsInfoReduced%nomf(:) = 0
  246. ObsInfoReduced%slcobs(:) = 0.0
  247. ObsInfoReduced%slcfc(:) = 0.0
  248. ObsInfoReduced%vcdfc(:) = 0.0
  249. ObsInfoReduced%sigobs(:) = 0.0
  250. ObsInfoReduced%hobs(:,:) = 0.0
  251. do ipix = 1, no2Tr%count
  252. if ( usePixel(ipix) ) then
  253. i = ObsFcInfo%icell(ipix)
  254. j = ObsFcInfo%jcell(ipix)
  255. ipixred = grid_index(i,j)
  256. ObsInfoReduced%obsi(ipixred) = i
  257. ObsInfoReduced%obsj(ipixred) = j
  258. ObsInfoReduced%nomf(ipixred) = &
  259. ObsInfoReduced%nomf(ipixred) + 1
  260. ObsInfoReduced%slcobs(ipixred) = &
  261. ObsInfoReduced%slcobs(ipixred) + &
  262. no2Tr%no2slc(ipix) / ObsFcInfo%amfgeo(ipix)
  263. ObsInfoReduced%slcfc(ipixred) = &
  264. ObsInfoReduced%slcfc(ipixred) + &
  265. ObsFcInfo%no2slcfc(ipix) / ObsFcInfo%amfgeo(ipix)
  266. ObsInfoReduced%vcdfc(ipixred) = &
  267. ObsInfoReduced%vcdfc(ipixred) + &
  268. ObsFcInfo%no2vcdfc(ipix)
  269. ! attribute large error to tropospheric part of the
  270. ! observation operator
  271. slcgeotrop = ObsFcInfo%no2slcfctrop(ipix)
  272. slcgeo = ObsFcInfo%no2slcfc(ipix)
  273. if ( slcgeo <= 0. ) then
  274. print*,'ERROR in ass_Hx_super.f90; slcgeo negative.'
  275. stop
  276. end if
  277. sig = ( trop_error * slcgeotrop + &
  278. strat_error * ( slcgeo - slcgeotrop ) ) / slcgeo
  279. ObsInfoReduced%sigobs(ipixred) = &
  280. ObsInfoReduced%sigobs(ipixred) + sig
  281. do l = 1, lm
  282. ObsInfoReduced%hobs(l,ipixred) = &
  283. ObsInfoReduced%hobs(l,ipixred) + &
  284. ObsFcInfo%avkernel(l,ipix)*ObsFcInfo%amf(ipix) / &
  285. ObsFcInfo%amfgeo(ipix)
  286. end do
  287. end if
  288. end do
  289. ! normalise slc, standard deviation and observation operator
  290. ! determine O-F statistics
  291. omfm1 = 0.0
  292. omfm2 = 0.0
  293. do ipixred = 1, ipixredmax
  294. snorm = 1.0/real(max(1,ObsInfoReduced%nomf(ipixred)))
  295. ObsInfoReduced%slcobs(ipixred) = ObsInfoReduced%slcobs(ipixred)*snorm
  296. ObsInfoReduced%slcfc(ipixred) = ObsInfoReduced%slcfc(ipixred)*snorm
  297. ObsInfoReduced%vcdfc(ipixred) = ObsInfoReduced%vcdfc(ipixred)*snorm
  298. sigcor = sqrt( (obscorfac+(1.0-obscorfac)*snorm) )
  299. ObsInfoReduced%sigobs(ipixred) = &
  300. ObsInfoReduced%sigobs(ipixred)*snorm*sigcor
  301. do l = 1, lm
  302. ObsInfoReduced%hobs(l,ipixred) = &
  303. ObsInfoReduced%hobs(l,ipixred)*snorm
  304. end do
  305. omf = ObsInfoReduced%slcobs(ipixred)-ObsInfoReduced%slcfc(ipixred)
  306. omfm1 = omfm1+omf
  307. omfm2 = omfm2+omf*omf
  308. end do
  309. !
  310. omfm1 = omfm1 / real(max(1,ipixredmax))
  311. omfm2 = omfm2 / real(max(1,ipixredmax))
  312. if ( anFc == 'f' ) then
  313. write(*,1031) omfm1, sqrt(omfm2-omfm1*omfm1)
  314. 1031 format(' GetOMFHxSuper: <o-f> = ',f8.4,' std = ',f8.4,' (superobs)')
  315. else
  316. write(*,1041) omfm1, sqrt(omfm2-omfm1*omfm1)
  317. 1041 format(' GetOMFHxSuper: <o-a> = ',f8.4,' std = ',f8.4,' (superobs)')
  318. end if
  319. ! release storage
  320. deallocate ( grid_index )
  321. deallocate ( grid_count )
  322. if ( computeRetrievalForAnalysis ) then
  323. ! only deallocate "usePixel" after the analysis step
  324. if ( anFc /= 'f' ) then
  325. print *, 'Deallocate usePixel'
  326. deallocate ( usePixel )
  327. end if
  328. else
  329. ! Forecast retrieval only: deallocate
  330. print *, 'Deallocate usePixel'
  331. deallocate ( usePixel )
  332. end if
  333. end subroutine GetOMFHxSuper
  334. subroutine ObsInfoReduced_allocate( NObsSuper, lm, ObsInfoReduced )
  335. !
  336. ! Create storage for the superobservations in (empty) ObsInfoReduced
  337. ! Henk Eskes, KNMI, Aug 2015
  338. !
  339. implicit none
  340. ! in/out
  341. integer, intent(in) :: NObsSuper, lm
  342. type(TObsInfoReduced),intent(inout) :: ObsInfoReduced
  343. ! start code
  344. if ( allocated(ObsInfoReduced%hobs) ) &
  345. stop 'ERROR subroutine ObsInfoReduced_allocate: already allocated!'
  346. allocate ( ObsInfoReduced%obsi(NObsSuper) )
  347. allocate ( ObsInfoReduced%obsj(NObsSuper) )
  348. allocate ( ObsInfoReduced%nomf(NObsSuper) )
  349. allocate ( ObsInfoReduced%slcobs(NObsSuper) )
  350. allocate ( ObsInfoReduced%slcfc(NObsSuper) )
  351. allocate ( ObsInfoReduced%vcdfc(NObsSuper) )
  352. allocate ( ObsInfoReduced%sigobs(NObsSuper) )
  353. allocate ( ObsInfoReduced%hobs(lm,NObsSuper) )
  354. end subroutine ObsInfoReduced_allocate
  355. subroutine ObsInfoReduced_deallocate( ObsInfoReduced )
  356. !
  357. ! Remove storage for the superobservations in (empty) ObsInfoReduced
  358. ! Henk Eskes, KNMI, Aug 2015
  359. !
  360. implicit none
  361. ! in/out
  362. type(TObsInfoReduced),intent(inout) :: ObsInfoReduced
  363. ! start code
  364. if ( allocated ( ObsInfoReduced%obsi ) ) deallocate ( ObsInfoReduced%obsi )
  365. if ( allocated ( ObsInfoReduced%obsj ) ) deallocate ( ObsInfoReduced%obsj )
  366. if ( allocated ( ObsInfoReduced%nomf ) ) deallocate ( ObsInfoReduced%nomf )
  367. if ( allocated ( ObsInfoReduced%slcobs ) ) deallocate ( ObsInfoReduced%slcobs )
  368. if ( allocated ( ObsInfoReduced%slcfc ) ) deallocate ( ObsInfoReduced%slcfc )
  369. if ( allocated ( ObsInfoReduced%vcdfc ) ) deallocate ( ObsInfoReduced%vcdfc )
  370. if ( allocated ( ObsInfoReduced%sigobs ) ) deallocate ( ObsInfoReduced%sigobs )
  371. if ( allocated ( ObsInfoReduced%hobs ) ) deallocate ( ObsInfoReduced%hobs )
  372. end subroutine ObsInfoReduced_deallocate
  373. end module MOmFSuper