oi_module.F90 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. ! First include the set of model-wide compiler flags
  2. #include "tm5.inc"
  3. module oi_module
  4. ! use dims
  5. ! use MatInvertLib, only : MatInvert_lapack
  6. ! uses two LAPACK routines:
  7. ! call dgetrf( nmsi, nmsi, bplo, nmsi, ipiv, ierror )
  8. ! call dgetrs( 'N', nmsi, 1, bplo, nmsi, ipiv, vincr, nmsi, ierror )
  9. implicit none
  10. private
  11. public :: AssimilateColumn
  12. ! public :: qanratio_latest
  13. ! real,dimension(im,jm,lm) :: qanratio_latest
  14. ! Do not allow analysis ratio's below this limit (excluding also negatives)
  15. real,parameter :: QAnRatioLimit=0.5
  16. ! correlation lookup tables
  17. ! blut : the global B correlation matrix
  18. ! rho: the distance-dependent correlation
  19. ! rho_conc: the fractional concentration-difference correlation
  20. real, dimension(:,:,:), allocatable :: blut
  21. real,dimension(0:2100) :: rho
  22. real,dimension(0:2000) :: rho_conc
  23. ! lower limit for forecast subcolumn in layer (10^15 molec cm^-2)
  24. ! to prevent arithmetic exceptions
  25. real,parameter :: subcolumnLimit = 1.0e-7
  26. ! flag for first call to "AssimilateColumn"
  27. logical :: firstcall = .true.
  28. contains
  29. subroutine AssimilateColumn ( im, jm, lm, fcerr, obsInfoReduced, qfc, qanratio )
  30. !=======================================================================
  31. !
  32. ! AssimilateColumn - OI assimilation
  33. !
  34. ! Uses the optimal interpolation formula with a homogemeous
  35. ! horizontal model covariance.
  36. !
  37. ! im, jm, lm = 3D grid dimensions
  38. ! fcerr = 2D field of forecast column errors (1e15 molec/cm2)
  39. ! obsInfoReduced = contains observations,
  40. ! forecast and observation operators
  41. ! qfc = 3D forecast field of partial columns of NO2
  42. !
  43. ! qanratio = (output) 3D field of ratios between the
  44. ! analysis and forecast
  45. !
  46. ! Henk Eskes and Folkert Boersma, September 5, 2003.
  47. !
  48. !
  49. !=======================================================================
  50. use MOmFSuper, only : TObsInfoReduced
  51. implicit none
  52. ! in/out
  53. ! clustered superobservations
  54. integer, intent(in) :: im, jm, lm
  55. type(TObsInfoReduced),intent(in) :: obsInfoReduced
  56. real,dimension(im,jm),intent(in) :: fcerr
  57. real,dimension(im,jm,lm),intent(in) :: qfc
  58. real,dimension(im,jm,lm),intent(out) :: qanratio
  59. ! correlation: shape and length
  60. ! 'g': Gaussian shape
  61. ! 'e': exponential shape
  62. ! 't': Thiebaux 2nd order autoregressive shape
  63. real,parameter :: corlen = 600.0 ! correlation length in km (eg 650.0)
  64. character(1),parameter :: corshape = 't'
  65. ! correlation length in concentration space
  66. ! % concentration difference for 1/e correlation decrease
  67. real,parameter :: conc_corlen = 30.0 ! 20% concentration diff
  68. ! local arrays
  69. real, dimension(:,:,:), allocatable :: qcorrl, htvincr
  70. integer, dimension(:,:), allocatable :: iobsmask
  71. real, dimension(:), allocatable :: odifms, smms, soms, vincr
  72. integer, dimension(:), allocatable :: ilonms, jlatms
  73. real, dimension(:,:), allocatable :: Hmat, qfcmat
  74. ! local
  75. integer :: i, j, l, ipix, nms, nms1, imax, il
  76. integer :: nSmallAnRatio, nLargeAnRatio, nWrongQFC
  77. !
  78. ! fill the model correlation lookup table (only once!)
  79. !
  80. if( firstcall )then
  81. call Inioilut ( im, jm, corshape, corlen, conc_corlen )
  82. write(*,733) corshape,corlen,conc_corlen
  83. 733 format(' assimilateColumn: lookup table filled, ',&
  84. 'shape = ',a1,', corlen = ',f7.1,/,&
  85. ' concentration correlation dC = ',f7.1,' %' )
  86. firstcall = .false.
  87. endif
  88. ! number of super-observations
  89. nms = obsInfoReduced%count
  90. nms1 = max(1,nms)
  91. ! Allocate the local arrays
  92. allocate ( qcorrl(im,jm,lm) )
  93. allocate ( htvincr(im,jm,lm) )
  94. allocate ( iobsmask(im,jm) )
  95. allocate ( odifms(nms1) )
  96. allocate ( smms(nms1) )
  97. allocate ( soms(nms1) )
  98. allocate ( vincr(nms1) )
  99. allocate ( ilonms(nms1) )
  100. allocate ( jlatms(nms1) )
  101. allocate ( Hmat(lm,nms1) )
  102. allocate ( qfcmat(lm,nms1) )
  103. !
  104. !
  105. ! fill measurement space data arrays - mismatch, coordinates and sigma's
  106. !
  107. iobsmask(:,:) = 0 ! mask for cells containing observations
  108. do ipix = 1, nms
  109. odifms(ipix) = obsInfoReduced%slcobs(ipix) - obsInfoReduced%slcfc(ipix)
  110. i = obsInfoReduced%obsi(ipix) ! measurement longitude
  111. j = obsInfoReduced%obsj(ipix) ! measurement latitude
  112. ilonms(ipix) = i
  113. jlatms(ipix) = j
  114. iobsmask(i,j) = 1
  115. smms(ipix) = fcerr(i,j) ! model uncertainty at observation
  116. soms(ipix) = obsInfoReduced%sigobs(ipix) ! measurement uncertainty
  117. Hmat(1:lm,ipix) = obsInfoReduced%hobs(1:lm,ipix) ! observation operator
  118. qfcmat(1:lm,ipix) = qfc(i,j,1:lm)
  119. end do
  120. !
  121. nSmallAnRatio = 0
  122. nLargeAnRatio = 0
  123. nWrongQFC = 0
  124. if ( nms .gt. 0 ) then ! there are measurements
  125. !
  126. ! solve "vincr", the solution of
  127. ! ( H B H^T + O ) vincr = Q_obs - H Q_mod
  128. ! H: model-measurement interpolation matrix
  129. ! B: model covariance matrix
  130. ! O: observation covariance
  131. ! Q: total column
  132. ! ( Q_obs - H Q_mod = "odif" )
  133. !
  134. call Solvincr ( im, lm, nms, odifms, ilonms, jlatms, smms, soms, Hmat, qfcmat, vincr)
  135. ! interpolate vincr to the model grid
  136. call HTy ( im, jm, lm, nms, vincr, ilonms, jlatms, Hmat, htvincr )
  137. ! multiply with the covariance matrix on the model grid to obtain the
  138. ! OI field correction
  139. call Bx ( im, jm, lm, htvincr, iobsmask, fcerr, corlen, qfc, qcorrl)
  140. !
  141. ! determine analysed field
  142. ! q_an = q_mod + qcorrl
  143. ! qcorrl = B H^T ( H B H^T + O )^-1 ( Q_obs - H Q_mod )
  144. !
  145. ! analysed correction ratio = (qfc+qcorrl)/qfc
  146. ! check for small and negative ratio's
  147. !
  148. do l = 1, lm
  149. do j = 1, jm
  150. ! circumvent unphysical polar cells
  151. imax = im
  152. !if( (j == 1) .or. (j == jm) ) imax = 1 !JDMP
  153. do i = 1, imax
  154. ! If qfc becomes very small, set qanratio equal 1.0
  155. ! to prevent Arithmetic Exceptions (Folkert Boersma, September 3, 2003)
  156. if ( qfc(i,j,l) <= subcolumnLimit ) then
  157. qanratio(i,j,l) = 1.0
  158. nWrongQFC = nWrongQFC + 1
  159. ! print*,'ERROR, small qfc value',i,j,l,qfc(i,j,l),qfc(i,j,l)+qcorrl(i,j,l)
  160. else
  161. qanratio(i,j,l) = (qfc(i,j,l)+qcorrl(i,j,l)) / qfc(i,j,l)
  162. end if
  163. ! do not allow small and negative ratio's
  164. if ( qanratio(i,j,l) < QAnRatioLimit ) then
  165. ! set ratio equal to specified limit
  166. qanratio(i,j,l) = QAnRatioLimit
  167. nSmallAnRatio = nSmallAnRatio + 1
  168. end if
  169. ! avoid extreme relative analysis increments
  170. if ( qanratio(i,j,l) > 5.0 ) then
  171. ! set ratio equal to specified limit
  172. qanratio(i,j,l) = 5.0
  173. nLargeAnRatio = nLargeAnRatio + 1
  174. end if
  175. end do
  176. end do
  177. end do
  178. !do i=2,im ! polar caps !JDMP
  179. ! qanratio(i,1,1:lm) = qanratio(1,1,1:lm) !JDMP
  180. ! qanratio(i,jm,1:lm) = qanratio(1,jm,1:lm) !JDMP
  181. !end do !JDMP
  182. !
  183. else ! nms=0
  184. ! no measurements, analysis = forecast
  185. qanratio(:,:,:) = 1.0
  186. endif
  187. !
  188. if ( nSmallAnRatio > 0 ) write(*,'(a,i5,a)') 'assimilateColumn: WARNING ',nSmallAnRatio, &
  189. ' AnRatio values < 0.5 occurred and corrected'
  190. if ( nLargeAnRatio > 0 ) write(*,'(a,i5,a)') 'assimilateColumn: WARNING ',nLargeAnRatio, &
  191. ' AnRatio values > 5 occurred and corrected'
  192. if ( nWrongQFC > 0 ) write(*,'(a,i5,a)') 'assimilateColumn: WARNING ',nWrongQFC, &
  193. ' forecast values qfc with very small, zero, or negative qfc occurred'
  194. ! De-allocate the local arrays
  195. deallocate ( qcorrl )
  196. deallocate ( htvincr )
  197. deallocate ( iobsmask )
  198. deallocate ( odifms )
  199. deallocate ( smms )
  200. deallocate ( soms )
  201. deallocate ( vincr )
  202. deallocate ( ilonms )
  203. deallocate ( jlatms )
  204. deallocate ( Hmat )
  205. deallocate ( qfcmat )
  206. end subroutine AssimilateColumn
  207. subroutine Inioilut ( im, jm, choice, corlen, conc_corlen )
  208. !=======================================================================
  209. !
  210. ! OI (Optimal Interpolation) Utility routine:
  211. ! Fill the model correlation lookup tables
  212. ! "rho" - distance-dependent correlation function
  213. ! "rho_conc" - concentration-dependent correlation function
  214. ! "blut" - correlation lookup table for two grid points on the globe
  215. ! blut(idif,j1,j2)
  216. ! idif: longit. index distance between points (0..im/2)
  217. ! j1,j2: latitude indices of two points
  218. !
  219. ! choice [in] - single character describing correlation function:
  220. ! 'e' : exponential distance dependence, with linear tail
  221. ! 'g' : Gaussian distance dependence
  222. ! 't' : Thiebaux autoregressive correlation
  223. ! corlen [in] - correlation length in km (1/e length)
  224. ! conc_corlen [in] - concentration correlation length in % difference
  225. !
  226. ! The array dimensions "im" and "jm" are taken from the file "constants"
  227. ! The lookup tables B and rho are permanent: see above
  228. !
  229. !=======================================================================
  230. use misctools, only : dist
  231. implicit none
  232. ! in/out
  233. integer, intent(in) :: im, jm
  234. character(1),intent(in) :: choice
  235. real,intent(in) :: corlen,conc_corlen
  236. ! local
  237. integer :: i,idif,j1,j2,icorlen,imax
  238. real :: long1,long2,lati1,lati2,dst,b,ccl
  239. ! begin code
  240. ! allocate lookup table
  241. allocate ( blut( 0:im/2, jm, jm ) )
  242. ! Fill lookup table for distance dependent function - 10 km step
  243. !
  244. if ( corlen .lt. 0.000 ) stop 'ERROR Inioilut: negative correlation length'
  245. !if( corlen .lt. 100.0 ) stop 'WARNING Inioilut: correlation length < 100 km'
  246. !
  247. rho(0) = 1.0
  248. rho(1:2100) = 0.0
  249. icorlen = nint(corlen/10.0)
  250. if (icorlen == 0) stop 'ERROR in ass_oi_subr.f90; icorlen =0.'
  251. if ( 5*icorlen .gt. 2100 ) stop 'ERROR Inioilut: correlation length too large'
  252. !
  253. if ( choice .eq. 'e' ) then ! exponential correlation
  254. if ( icorlen .gt. 0 ) then
  255. do i = 0, 4*icorlen-1
  256. rho(i) = exp(-real(i)/real(icorlen))
  257. end do
  258. do i = 4*icorlen, 5*icorlen-1
  259. rho(i) = exp(-4.0)*(1.0-real(i-4*icorlen)/real(icorlen))
  260. end do
  261. end if
  262. else
  263. if( choice .eq. 'g' )then ! Gaussian correlation
  264. do i = 0, 5*icorlen-1
  265. rho(i) = exp(-(real(i)/real(icorlen))**2)
  266. end do
  267. else
  268. if( choice .eq. 't' )then ! Thiebaux autoregressive correlation
  269. do i = 0, 5*icorlen-1
  270. rho(i) = (1.0+2.0*real(i)/real(icorlen)) * exp( -2.0*real(i)/real(icorlen) )
  271. end do
  272. else
  273. stop 'ERROR Inioilut: "choice" invalid'
  274. end if
  275. end if
  276. end if
  277. !
  278. ! Correlation between concentrations c1 and c2:
  279. ! correlation = rho_conc( nint(abs(1000*(c1-c2)/(c1+c2))) )
  280. !
  281. if ( conc_corlen > 50.0 ) stop 'IniOILut: ERROR conc_corlen > 50%, inconsistent with functional form'
  282. ccl = 5.0*conc_corlen ! 1/e reduction for "corlen_conc" % difference
  283. imax = 900 ! correlations = 0 when abs(c1-c2)/(c1+c2) > 0.9
  284. do i = 0, imax
  285. rho_conc(i) = exp( - (real(i)/ccl)**2 )
  286. end do
  287. rho_conc(imax:2000) = 0.0
  288. rho_conc(0) = 1.0
  289. !
  290. ! Fill covariance matrix table for all distinct distances on the sphere
  291. !
  292. ! calculate lat,lon of the two points
  293. long1 = 0.0
  294. do idif = 0, im/2
  295. long2 = real(idif)*360.0/real(im)
  296. do j1 = 1, jm
  297. lati1 = -90.0 + (real(j1)-0.5)*180.0/real(jm)
  298. if ( j1 .eq. 1 ) lati1 = -90.0
  299. if ( j1 .eq. jm ) lati1 = 90.0
  300. do j2 = j1, jm
  301. lati2 = -90.0 + (real(j2)-0.5)*180.0/real(jm)
  302. if ( j2 .eq. 1 ) lati2 = -90.0
  303. if ( j2 .eq. jm ) lati2 = 90.0
  304. ! determine distance between points (dst in km)
  305. call dist(long1,lati1,long2,lati2,dst)
  306. ! use lookup table for rho
  307. if ( nint(dst/10.0) > 2100 ) stop 'Inioilut: coding ERROR, distance'
  308. b = rho(nint(dst/10.0))
  309. blut(idif,j1,j2) = b
  310. blut(idif,j2,j1) = b
  311. if ( (b<0.0) .or. (b>1.0) ) stop 'Inioilut: coding ERROR, b'
  312. end do
  313. end do
  314. end do
  315. !
  316. ! polar caps exception: polar dummy cells are uncorrelated
  317. !
  318. !do idif=1,im/2 !JDMP
  319. ! blut(idif,1,1) = 0.0 !JDMP
  320. ! blut(idif,jm,jm) = 0.0 !JDMP
  321. ! blut(idif,1,jm) = 0.0 !JDMP
  322. ! blut(idif,jm,1) = 0.0 !JDMP
  323. !enddo
  324. !
  325. end subroutine Inioilut
  326. subroutine Solvincr &
  327. ( im, lm, nmsi, odifms, ilonmsi, jlatmsi, smmsi, somsi, Hmat, qfcmat, vincr )
  328. !=======================================================================
  329. !
  330. ! OI (Optimal Interpolation) Utility routine:
  331. ! Solves the vector "vincr", the solution of
  332. ! ( B + O ) vincr = Q_obs - H Q_mod
  333. ! B: model covariance matrix, defined at measurement positions
  334. ! O: observation covariance
  335. ! Q: observed quantity
  336. ! all variables are defined in the space of the observations
  337. !
  338. ! im = number of model longitudes (in)
  339. ! lm = number of model levels (in)
  340. ! nmsi = number of measurements (in)
  341. ! odifms = Q_obs - H Q_mod, the set of departures (in)
  342. ! ilonmsi,jlatmsi = grid coordinates of the measurements (in)
  343. ! smmsi,somsi = model and obs uncertainties (sigma standard dev's) (in)
  344. ! Hmat = observation operator matrix
  345. ! qfcmat = 3D model forecast profiles at observations
  346. ! vincr = see above (out)
  347. !
  348. ! The subroutine usue the lookup tables "blut" and "rho"
  349. ! The array dimensions "im" and "lm" are taken from the module "dimension"
  350. !
  351. !=======================================================================
  352. implicit none
  353. ! in/out
  354. integer,intent(in) :: im, lm
  355. integer,intent(in) :: nmsi
  356. real,dimension(nmsi),intent(in) :: odifms, smmsi, somsi
  357. real,dimension(lm,nmsi),intent(in) :: Hmat, qfcmat
  358. integer,dimension(nmsi),intent(in) :: ilonmsi, jlatmsi
  359. real,dimension(nmsi),intent(out) :: vincr
  360. ! local
  361. integer :: l, m, n, ierror, itriag, i1mini2
  362. real :: test, hht, correlsum, c1, c2, ccorrel
  363. !
  364. real, dimension(:,:), allocatable :: bplo ! B+O matrix
  365. integer, parameter :: obsMaxSuper = 50000
  366. ! Lapack storage
  367. integer, dimension(obsMaxSuper) :: ipiv
  368. ! begin code
  369. ! allocate bplo matrix
  370. allocate ( bplo(nmsi,nmsi) )
  371. !
  372. ! Fill model + observation covariance matrix ( H B H^T + O )
  373. ! B is assumed diagonal in altitude, and altitude independent
  374. !
  375. do m = 1, nmsi
  376. do n = m, nmsi
  377. i1mini2 = abs(ilonmsi(m) - ilonmsi(n))
  378. if ( i1mini2 .gt. im ) stop 'ERROR solvincr: ilonmsi array incorrect'
  379. if ( i1mini2 .gt. im/2 ) i1mini2 = im - i1mini2
  380. hht = 0.0
  381. correlsum = 0.0 ! sum of vertical correlation factors
  382. do l = 1, lm
  383. ! Add max statement to avoid division by 0, Folkert Boersma
  384. c1 = max ( qfcmat(l,m), subcolumnLimit )
  385. c2 = max ( qfcmat(l,n), subcolumnLimit )
  386. ccorrel = rho_conc( nint(abs(1000.0*(c1-c2)/(c1+c2))) )
  387. hht = hht + Hmat(l,m)*Hmat(l,n)*ccorrel*(c1+c2)
  388. correlsum = correlsum + (c1+c2)
  389. end do
  390. if ( correlsum == 0. ) stop 'ERROR in ass_oi_subr.f90; correlsum = 0.'
  391. hht = hht / correlsum
  392. bplo(m,n) = smmsi(m)*smmsi(n)*hht*blut(i1mini2,jlatmsi(m),jlatmsi(n))
  393. if ( n .eq. m ) then
  394. bplo(m,n) = bplo(m,n) + somsi(m)*somsi(m)
  395. else
  396. bplo(n,m) = bplo(m,n)
  397. end if
  398. end do
  399. end do
  400. !
  401. ! Solve ( B + O ) v_incr = odifms
  402. ! GAUSS - Solves Ax = vec where A is a square matrix
  403. !
  404. vincr(1:nmsi) = odifms(1:nmsi)
  405. ! test = 1.e-2
  406. ! itriag = 0
  407. ! call Gauss ( bplo, vincr, nmsi, nmsi, test, ierror, itriag )
  408. !
  409. ! using lapack
  410. !
  411. call dgetrf( nmsi, nmsi, bplo, nmsi, ipiv, ierror )
  412. if ( ierror /= 0 ) then
  413. print *,' LAPACK sgetrf error = ',ierror
  414. stop
  415. end if
  416. call dgetrs( 'N', nmsi, 1, bplo, nmsi, ipiv, vincr, nmsi, ierror )
  417. if ( ierror /= 0 ) then
  418. print *,' LAPACK sgetrf error = ',ierror
  419. stop
  420. end if
  421. !
  422. if( ierror .ne. 0 ) then
  423. write(*,'(a,i5)') 'ERROR solvincr, gauss: ierror = ',ierror
  424. stop
  425. end if
  426. ! deallocate bplo matrix
  427. deallocate ( bplo )
  428. end subroutine Solvincr
  429. subroutine HTy ( im, jm, lm, nms, v, ilonmsi, jlatmsi, Hmat, htv )
  430. !=======================================================================
  431. !
  432. ! OI (Optimal Interpolation) Utility routine:
  433. ! Interpolates an observation-space input vector "v" to the model grid
  434. ! (multiplies "v" with H^T, the transpose of the observation operator)
  435. !
  436. ! im, jm, lm = model array dimensions (in)
  437. ! nms = number of measurements (in)
  438. ! v = vector in observation space (in)
  439. ! ilonmsi, jlatmsi = the model grid positions of the observations (in)
  440. ! Hmat = observation matrix (in)
  441. ! htv = resulting model-space vector (out)
  442. !
  443. !=======================================================================
  444. implicit none
  445. ! in/out
  446. integer, intent(in) :: im, jm, lm
  447. integer, intent(in) :: nms
  448. real, dimension(nms), intent(in) :: v
  449. integer, dimension(nms), intent(in) :: ilonmsi, jlatmsi
  450. real, dimension(lm,nms), intent(in) :: Hmat
  451. real, dimension(im,jm,lm), intent(out) :: htv
  452. ! local
  453. integer :: i, j, ims
  454. ! initialise htv
  455. htv(:,:,:) = 0.0
  456. ! multiply "v" with Hmat
  457. do ims = 1, nms
  458. i = ilonmsi(ims)
  459. j = jlatmsi(ims)
  460. htv(i,j,1:lm) = Hmat(1:lm,ims)*v(ims)
  461. end do
  462. !
  463. end subroutine HTy
  464. subroutine Bx ( im, jm, lm, htvincr, iobsmask, smod, corlen, qfc, qcorrl )
  465. !=======================================================================
  466. !
  467. ! OI (Optimal Interpolation) Utility routine:
  468. ! Operates with the model covariance matrix B on the input vector "htvincr"
  469. !
  470. ! im,jm,lm: dimension of the lattice and vectors (in)
  471. ! htvincr: input vector (from OI analysis) (in)
  472. ! iobsmask: equals 1 when grid box contains observations (in)
  473. ! smod: model column error standard deviation field (in)
  474. ! corlen: model error correlation length in km (in)
  475. ! qfc: the 3D forecast field (in)
  476. ! qcorrl: product of B with htvincr - the 3D correction field (out)
  477. !
  478. !=======================================================================
  479. implicit none
  480. ! in/out
  481. integer, intent(in) :: im, jm, lm
  482. real, dimension(im,jm,lm), intent(in) :: htvincr,qfc
  483. real, dimension(im,jm), intent(in) :: smod
  484. real, intent(in) :: corlen
  485. integer, dimension(im,jm), intent(in) :: iobsmask
  486. real, dimension(im,jm,lm), intent(out) :: qcorrl
  487. ! local
  488. real, parameter :: rearth = 6378.5
  489. real, parameter :: pi = 3.1415927
  490. !
  491. real, dimension(:), allocatable :: fac
  492. real, dimension(:,:), allocatable :: qfccol
  493. !
  494. integer :: i, j, l, jdel, jmax, jmin, idel, i2, j2
  495. integer :: imini2, i2index, idel1, idel2
  496. real :: lat, fac2, qfcnorm, c1, c2, ccorrel, tcorrel
  497. ! lookup tables for model covariance, blut,rho (see above)
  498. ! begin code
  499. ! allocate helper arrays
  500. allocate ( fac(lm) )
  501. allocate ( qfccol(im,jm) ) ! forecast columns of NO2
  502. ! initialize output field to zero
  503. qcorrl(:,:,:) = 0.0
  504. !
  505. ! calculate model forecast columns
  506. do i = 1, im
  507. do j = 1, jm
  508. qfccol(i,j) = 0.0
  509. do l = 1, lm
  510. qfccol(i,j) = qfccol(i,j) + qfc(i,j,l)
  511. end do
  512. end do
  513. end do
  514. !
  515. ! distribute OI increments with the model covariance matrix.
  516. ! (i,j) coordinate of measurement gridbox
  517. ! (i2,j2) coordinate of points in neighbourhood of (i,j)
  518. !
  519. jdel = 1 + int(5.0*corlen*real(jm)/(pi*rearth))
  520. do j = 1, jm
  521. jmax = min(j+jdel,jm)
  522. jmin = max(j-jdel,1)
  523. lat = -0.5*pi+(real(j)-0.5)*pi/real(jm)
  524. idel = 1+int(5.0*corlen*real(im)/(2.0*pi*rearth*cos(lat)))
  525. idel1 = min(idel,im/2-1)
  526. idel2 = min(idel,im/2)
  527. do i = 1, im
  528. if ( iobsmask(i,j) == 1 ) then ! there is an observation
  529. do l = 1, lm
  530. fac(l) = htvincr(i,j,l)*smod(i,j)
  531. end do
  532. do i2index = i-idel1, i+idel2
  533. i2 = mod(i2index+im-1,im) + 1 ! array index el 1..im
  534. imini2 = abs(i2index-i)
  535. do j2 = jmin, jmax
  536. fac2 = smod(i2,j2)*blut(imini2,j,j2)
  537. qfcnorm = 1.0/(qfccol(i,j)+qfccol(i2,j2))
  538. do l = 1, lm
  539. ! Add max statement to aviod division by 0, Folkert Boersma
  540. c1 = max ( qfc(i,j,l), subcolumnLimit )
  541. c2 = max ( qfc(i2,j2,l), subcolumnLimit )
  542. ccorrel = rho_conc( nint(abs(1000.0*(c1-c2)/(c1+c2))) )
  543. tcorrel = fac(l)*fac2*ccorrel*(c1+c2)*qfcnorm
  544. qcorrl(i2,j2,l) = qcorrl(i2,j2,l) + tcorrel
  545. end do
  546. end do
  547. end do
  548. end if
  549. end do
  550. end do
  551. ! deallocate helper arrays
  552. deallocate ( fac )
  553. deallocate ( qfccol )
  554. end subroutine Bx
  555. end module oi_module