m_local_analysis.F90 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991
  1. ! File: m_local_analysis.F90
  2. !
  3. ! Created: L. Bertino, 2002
  4. !
  5. ! Last modified: 13/04/2010
  6. !
  7. ! Purpose: Local analysis:
  8. ! -- calculation of X5
  9. ! -- update of the ensemble fields
  10. !
  11. ! Description: This module handles local analysis.
  12. !
  13. ! Modifications:
  14. ! 20/9/2011 PS:
  15. ! - modified update_fields() to allow individual inflation
  16. ! for each of `nfields' fields - thanks to Ehouarn Simon
  17. ! for spotting this inconsistency
  18. ! 25/8/2010 PS:
  19. ! - "obs" and "nobs" are now global, stored in m_obs.
  20. ! Accordingly, the local observations variables are now
  21. ! called "lobs" and "nlobs". Renamed "DD" to "D" and "d"
  22. ! to "dy".
  23. ! 5/8/2010 PS:
  24. ! - moved applying inflation from calc_X5() to
  25. ! update_fields()
  26. ! - introduced "rfactor" argument to calc_X5() - increases
  27. ! obs. error variance for the update of anomalies.
  28. ! 29/7/2010 PS:
  29. ! - calc_X5(): updated the list of things that needed to be
  30. ! done for a point with no local obs
  31. ! 6/7/2010 PS:
  32. ! - moved ij2nc() to p2nc_writeobs() in m_point2nc.F90
  33. ! 19/6/2010 PS:
  34. ! - added X5 to the ij2nc() output
  35. ! 25/5/2010 PS:
  36. ! - modified to accommodate inflation
  37. ! - modified to calculate SRF (spread reduction factor)
  38. ! 13/4/2010 Alok Gupta: added open/close/barrier to ensure that
  39. ! X5tmp.uf exists before any node tries to access it.
  40. ! 8/4/2010 PS: replaced "X4" by "X5"; renamed "localanalysis()"
  41. ! to "update_fields()", and "pre_local_analysis()" by
  42. ! "calc_X5"
  43. ! 1/03/2010 PS:
  44. ! - Additional checks for file I/O, as the X4 read/write have
  45. ! been demonstrated to fail occasionally. A record is now
  46. ! written to X4tmp, then read back and compared until the
  47. ! two instances coincide (10 attempts max).
  48. ! 11/11/2009 PS:
  49. ! - Changed numerics. Now it is always assumed that R is
  50. ! diagonal
  51. ! - Choice of two chemes: EnKF and DEnKF (for now)
  52. ! - X4 calculated either in ens or obs space, depending on
  53. ! relation between nobs (# of local observations) and nrens
  54. ! - dfs and nobs for each (i,j) are written to enkf_diag.nc
  55. ! - if TEST = .true. then local stuff for (I,J) around
  56. ! (TEST_I, TEST_J) is dumped to enkf_<I>,<J>.nc
  57. ! 6/3/2008 PS:
  58. ! - in pre_local_analysis():
  59. ! - introduced quick sort (O(n log n)) of pre-selected
  60. ! observations
  61. ! - reshuffled the interface
  62. ! - replaced output array of flags for local obs by an array
  63. ! of indices
  64. ! - in local_analysis():
  65. ! -- unified arrays subD and subS
  66. ! -- got rid of calls to getD()
  67. ! -- used matmul()
  68. ! -- introduced localisation function
  69. ! -- eliminated X2 and V
  70. ! 2007 K. A. Liseter and Ragnhild Blikberg:
  71. ! -- MPI parallelisation
  72. module m_local_analysis
  73. implicit none
  74. !
  75. ! public stuff
  76. !
  77. real(4), allocatable, public :: X5(:,:,:)
  78. real(4), allocatable, public :: X5check(:,:,:)
  79. public calc_X5
  80. public update_fields
  81. integer, parameter, private :: STRLEN = 512
  82. integer, parameter, private :: MAXITER = 10
  83. integer, private :: nX5pad
  84. real(4), allocatable, private :: X5pad(:)
  85. private get_npad_la
  86. private locfun
  87. private get_local_obs
  88. private diag2nc
  89. private traceprod
  90. !
  91. ! available localisation functions
  92. !
  93. integer, parameter, private :: LOCFUN_NONE = 1
  94. integer, parameter, private :: LOCFUN_STEP = 2
  95. integer, parameter, private :: LOCFUN_GASPARI_COHN = 3
  96. !
  97. ! used localisation function
  98. !
  99. integer, private :: LOCFUN_USED = LOCFUN_GASPARI_COHN
  100. !
  101. ! available schemes
  102. !
  103. integer, parameter, private :: SCHEME_ENKF = 1
  104. integer, parameter, private :: SCHEME_ETKF = 2 ! not implemented
  105. integer, parameter, private :: SCHEME_DENKF = 3
  106. !
  107. ! used scheme
  108. !
  109. integer, private :: SCHEME_USED = SCHEME_DENKF
  110. contains
  111. ! This routine is called for each "field" (horizontal slab) after calcX5().
  112. ! It conducts the multiplication
  113. ! E^a(i, :) = E^f(i, :) * X5(i), i = 1,...,n,
  114. ! where n - state dimension.
  115. !
  116. ! In this package the localisation is conducted only horizontally, so that
  117. ! the local (nrens x nrens) ensemble transform matrix X5 is stored for each
  118. ! node of the horizontal model grid. In TOPAZ4 this requires
  119. ! 880 x 800 x 100 x 100 x 4 = 28 GB of storage on disk for "tmpX5.uf". If the
  120. ! fileds were updated on one-by-one basis, this file would have to be read
  121. ! (in TOPAZ4) 146 times. Therefore, the fields are updated in bunches of
  122. ! `nfields' to reduce the load on disk.
  123. !
  124. subroutine update_fields(ni, nj, nrens, nfields, nobs_array, depths, fld, infls)
  125. #if defined (QMPI)
  126. use qmpi
  127. #else
  128. use qmpi_fake
  129. #endif
  130. use mod_measurement
  131. implicit none
  132. integer, intent(in) :: ni, nj ! size of grid
  133. integer, intent(in) :: nrens ! size of ensemble
  134. integer, intent(in) :: nfields ! number of 2D fields to be updated
  135. integer, dimension(ni, nj), intent(in) :: nobs_array! number of local obs
  136. real, dimension(ni, nj), intent(in) :: depths
  137. real(4), dimension(ni * nj, nrens * nfields), intent(inout) :: fld ! fields
  138. real, dimension(nfields), intent(in) :: infls ! inflation factors
  139. real(4), dimension(nrens, nrens) :: X5tmp
  140. real(4), dimension(nrens, nrens) :: IM ! inflation matrix
  141. integer :: m, i, j, f
  142. integer :: irecl, iostatus
  143. real(4) :: infl
  144. !KAL -- all nodes open for read access to temporary "X5" file
  145. inquire(iolength = irecl) X5(1 : nrens, 1 : nrens, 1 : ni), X5pad
  146. open(17, file = 'tmpX5.uf', form = 'unformatted', access = 'direct',&
  147. status = 'old', recl = irecl)
  148. do j = 1, nj
  149. ! read X5 from disk
  150. read(17, rec = j, iostat = iostatus) X5
  151. if (iostatus /= 0) then
  152. print *, 'ERROR: local_analysis(): I/O error at reading X5, iostatus = ', iostatus
  153. print *, 'ERROR: at j = ', j
  154. stop
  155. end if
  156. do i = 1, ni
  157. ! skip this cell if it is on land
  158. if (depths(i,j) <= 0.0) then
  159. cycle
  160. end if
  161. if (nobs_array(i, j) == 0 .and. all(infls == 1.0d0)) then
  162. cycle
  163. end if
  164. X5tmp = X5(:, :, i)
  165. do m = 1, nrens
  166. if (abs(1.0e0 - sum(X5tmp(:, m))) > 1.0e-5) then
  167. print *, 'ERROR: detected inconsistency in X5'
  168. print *, 'ERROR: at j = ', j, 'i = ', i
  169. print *, 'ERROR: sum(X5(:, ', m, ') = ', sum(X5tmp(:, m))
  170. stop
  171. end if
  172. enddo
  173. ! ensemble transformation, in real(4)
  174. !
  175. do f = 1, nfields
  176. infl = infls(f) ! conversion to real(4)
  177. if (infl /= 1.0) then
  178. IM = - (infl - 1.0) / real(nrens, 4)
  179. do m = 1, nrens
  180. IM(m, m) = IM(m, m) + infl
  181. end do
  182. fld((j - 1) * ni + i, (f - 1) * nrens + 1 : f * nrens) =&
  183. matmul(fld((j - 1) * ni + i, (f - 1) * nrens + 1 : f * nrens),&
  184. matmul(X5tmp, IM))
  185. else
  186. fld((j - 1) * ni + i, (f - 1) * nrens + 1 : f * nrens) =&
  187. matmul(fld((j - 1) * ni + i, (f - 1) * nrens + 1 : f * nrens), X5tmp)
  188. end if
  189. end do
  190. enddo
  191. enddo
  192. close(17)
  193. end subroutine update_fields
  194. ! This routine calculates X5 matrices involved in the EnKF analysis,
  195. ! E^a(i, :) = E^f(i, :) * X5(i), i = 1,...,n,
  196. ! where n - state dimension.
  197. !
  198. ! X5(i) is calculated locally (for a given state element i) as
  199. ! X5 = I + G s 1^T + T,
  200. ! where
  201. ! G = S^T (I + S S^T)^{-1} = (I + S^T S)^{-1} S^T [ FM ] Very important. This is a reformulation of the EnKF in the ensemble space.
  202. ! T = I - 1/2 G S (DEnKF) Details about this can be found in Sakov et al 2010 in which
  203. ! I appended the demonstration
  204. ! T = I + G(D - S) (EnKF)
  205. ! T = (I + S^T S)^{-1/2} (ETKF)
  206. ! S = R^{-1/2} HA^f / sqrt(m - 1)
  207. ! s = R^{-1/2} (d - Hx^f) / sqrt(m - 1)
  208. !
  209. ! see Sakov et al. (2010): Asynchronous data assimilation with the EnKF,
  210. ! Tellus 62A, 24-29.
  211. !
  212. subroutine calc_X5(nrens, modlon, modlat, depths, mindx, meandx, dy, S,&
  213. radius, rfactor, nlobs_array, ni, nj)
  214. #if defined (QMPI)
  215. use qmpi
  216. #else
  217. use qmpi_fake
  218. #endif
  219. use m_parameters
  220. use distribute
  221. use mod_measurement
  222. use m_obs
  223. use m_spherdist
  224. use m_random
  225. use m_point2nc
  226. implicit none
  227. ! Input/output arguments
  228. integer, intent(in) :: nrens
  229. real, dimension(ni, nj), intent(in) :: modlon, modlat
  230. real, dimension(ni, nj), intent(in) :: depths
  231. real, intent(in) :: mindx ! min grid size
  232. real, intent(in) :: meandx ! mean grid size
  233. real, dimension(nobs), intent(inout) :: dy ! innovations
  234. real, dimension(nobs, nrens), intent(inout) :: S ! HA
  235. real, intent(in) :: radius ! localisation radius in km
  236. real, intent(in) :: rfactor ! obs. variance multiplier for anomalies
  237. integer, dimension(ni, nj), intent(out) :: nlobs_array ! # of local obs
  238. ! for each grid cell
  239. integer, intent(in) :: ni, nj ! horizontal grid size
  240. real, dimension(nrens, nrens) :: X5tmp
  241. integer, dimension(nobs) :: lobs ! indices of local observations
  242. real, allocatable, dimension(:,:) :: D ! observation perturbations
  243. real, allocatable, dimension(:) :: subdy
  244. real, allocatable, dimension(:) :: lfactors ! loc. coeffs stored for QC
  245. real, allocatable, dimension(:,:) :: subD, subS ! nobs x nrens
  246. real, allocatable, dimension(:,:) :: X1 ! nobs x nobs
  247. real, allocatable, dimension(:,:) :: G
  248. real, allocatable, dimension(:) :: x
  249. real :: sqrtm
  250. real :: tmp(1)
  251. integer :: iostatus
  252. integer, dimension(nj):: jmap, jmap_check
  253. #if defined (QMPI)
  254. integer, allocatable, dimension(:, :) :: mpibuffer_int
  255. real(4), allocatable, dimension(:, :) :: mpibuffer_float1, mpibuffer_float2
  256. #endif
  257. integer :: lapack_info
  258. #if defined (QMPI)
  259. integer :: p
  260. #endif
  261. integer :: nlobs ! # of local obs
  262. integer :: m, i, j, o, jj, iter
  263. logical :: testthiscell ! test analysis at a certain cell
  264. integer :: irecl
  265. integer :: nlobs_max ! maximal number of local obs
  266. real :: dist, lfactor
  267. type(measurement) :: obs0
  268. ! dfs calculation
  269. real :: dfs
  270. real(4) :: dfs_array(ni, nj)
  271. ! srf calculation
  272. real :: srf
  273. real(4) :: srf_array(ni, nj)
  274. ! "partial" dfs
  275. real :: pdfs(nuobs)
  276. real(4) :: pdfs_array(ni, nj, nuobs)
  277. ! "partial" srf
  278. real :: psrf(nuobs)
  279. real(4) :: psrf_array(ni, nj, nuobs)
  280. ! auxiliary variables for dfs and srf calculation, such as
  281. ! nobs for different obs types
  282. integer :: plobs(nobs, nuobs)
  283. integer :: pnlobs(nuobs)
  284. integer :: uo
  285. if (trim(METHODTAG) == "ENKF") then
  286. SCHEME_USED = SCHEME_ENKF
  287. elseif (trim(METHODTAG) == "DENKF") then
  288. SCHEME_USED = SCHEME_DENKF
  289. end if
  290. if (master) then
  291. if (SCHEME_USED == SCHEME_ENKF) then
  292. print *, 'using EnKF analysis scheme'
  293. elseif (SCHEME_USED == SCHEME_DENKF) then
  294. print *, 'using DEnKF analysis scheme'
  295. end if
  296. end if
  297. if (LOCRAD > 0.0d0) then
  298. if (trim(LOCFUNTAG) == "GASPARI-COHN"&
  299. .or. trim(LOCFUNTAG) == "GASPARI_COHN") then
  300. LOCFUN_USED = LOCFUN_GASPARI_COHN
  301. elseif (trim(LOCFUNTAG) == "STEP") then
  302. LOCFUN_USED = LOCFUN_STEP
  303. elseif (trim(LOCFUNTAG) == "NONE") then
  304. LOCFUN_USED = LOCFUN_NONE
  305. end if
  306. else
  307. LOCFUN_USED = LOCFUN_NONE
  308. end if
  309. if (master) then
  310. if (LOCFUN_USED == LOCFUN_GASPARI_COHN) then
  311. print *, 'using Gaspari-Cohn localisation'
  312. elseif (LOCFUN_USED == LOCFUN_STEP) then
  313. print *, 'using STEP localisation'
  314. elseif (LOCFUN_USED == LOCFUN_NONE) then
  315. print *, 'using NO localisation'
  316. end if
  317. end if
  318. sqrtm = sqrt(real(nrens) - 1.0d0)
  319. if (SCHEME_USED == SCHEME_ENKF) then
  320. allocate(D(nobs, nrens))
  321. do o = 1, nobs
  322. call randn(nrens, D(o, :))
  323. D(o, :) = D(o, :) / (rfactor * sqrtm)
  324. end do
  325. end if
  326. do o = 1, nobs
  327. S(o, :) = S(o, :) / (sqrt(obs(o) % var) * sqrtm)
  328. dy(o) = dy(o) / (sqrt(obs(o) % var) * sqrtm)
  329. end do
  330. ! Distribute loops across MPI nodes
  331. call distribute_iterations(nj)
  332. ! The binary file tmpX5.uf holds (ni x nj) local ensemble transform
  333. ! matrices X5, (nrens x nrens) each. They are used for creating the
  334. ! analysed ensemble in local_analysis(). In TOPAZ3 tmpX5.uf takes about
  335. ! 30GB of the disk space.
  336. !
  337. nX5pad = get_npad_la(nrens * nrens, ni)
  338. allocate(X5pad(nX5pad))
  339. inquire(iolength = irecl) X5, X5pad
  340. if (master) then
  341. open(17, file = 'tmpX5.uf', form = 'unformatted', access = 'direct', status = 'unknown', recl = irecl)
  342. ! get the necessary space on disk, before starting simultaneous writing
  343. ! by different nodes
  344. write(17, rec = nj) X5
  345. close(17)
  346. end if
  347. #if defined (QMPI)
  348. call barrier()
  349. #endif
  350. open(17, file = 'tmpX5.uf', form = 'unformatted', access = 'direct',&
  351. status = 'old', recl = irecl)
  352. open(31, file = trim(JMAPFNAME), status = 'old', iostat = iostatus)
  353. if (iostatus /= 0) then
  354. if (master) then
  355. print *, 'WARNING: could not open jmap.txt for reading'
  356. print *, ' no re-mapping of grid rows performed'
  357. end if
  358. do j = 1, nj
  359. jmap(j) = j
  360. end do
  361. else
  362. read(31, *, iostat = iostatus) jmap
  363. if (iostatus /= 0) then
  364. print *, 'ERROR reading jmap.txt'
  365. stop
  366. end if
  367. close(31)
  368. jmap_check = 1
  369. jmap_check(jmap) = 0
  370. if (sum(jmap_check) /= 0) then
  371. print *, 'ERROR: non-zero control sum for jmap =', sum(jmap_check)
  372. stop
  373. end if
  374. end if
  375. ! main cycle (over horizontal grid cells)
  376. !
  377. dfs_array = 0.0
  378. pdfs_array = 0.0
  379. srf_array = 0.0
  380. psrf_array = 0.0
  381. nlobs_array = 0
  382. do jj = my_first_iteration, my_last_iteration
  383. j = jmap(jj)
  384. print *, 'calc_X5(): jj =', jj, 'j =', j
  385. do i = 1, ni
  386. ! data dumping flag
  387. testthiscell = p2nc_testthiscell(i, j)
  388. if (testthiscell) then
  389. print *, 'testthiscell: depth(,', i, ',', j, ') =', depths(i, j)
  390. end if
  391. if (depths(i, j) > 0.0d0) then
  392. nlobs = 0 ! no upper limit on the number of local observations
  393. call get_local_obs(i, j, radius * 1000.0, modlon, modlat,&
  394. mindx, ni, nj, nlobs, lobs)
  395. nlobs_array(i, j) = nlobs
  396. else
  397. nlobs = 0
  398. end if
  399. if (testthiscell) then
  400. print *, 'testthiscell: nlobs(,', i, ',', j, ') =', nlobs
  401. end if
  402. if (nlobs == 0) then
  403. ! just in case
  404. X5(:, :, i) = 0.0
  405. X5tmp = 0.0d0
  406. do m = 1, nrens
  407. X5(m, m, i) = 1.0
  408. X5tmp(m, m) = 1.0d0
  409. enddo
  410. if (testthiscell) then
  411. call p2nc_writeobs(i, j, nlobs, nrens, X5tmp, modlon(i, j),&
  412. modlat(i, j), depths(i, j))
  413. end if
  414. dfs_array(i, j) = 0.0
  415. pdfs_array(i, j, :) = 0.0
  416. srf_array(i, j) = 0.0
  417. psrf_array(i, j, :) = 0.0
  418. cycle
  419. end if
  420. if (nlobs < 0) then ! an extra check on the C-Fortran interface
  421. print *, 'ERROR: nlobs =', nlobs, ' for i, j =', i, j
  422. call stop_mpi()
  423. end if
  424. ! Allocate local arrays
  425. if (SCHEME_USED == SCHEME_ENKF) then
  426. allocate(subD(nlobs, nrens))
  427. end if
  428. allocate(subdy(nlobs))
  429. allocate(lfactors(nlobs))
  430. allocate(subS(nlobs, nrens))
  431. ! ( BTW subS1 = subS / sqrt(rfactor) )
  432. allocate(G(nrens, nlobs))
  433. if (nlobs < nrens) then
  434. allocate(X1(nlobs, nlobs))
  435. else
  436. allocate(X1(nrens, nrens))
  437. end if
  438. if (SCHEME_USED == SCHEME_ENKF) then
  439. subD = D(lobs(1 : nlobs), :)
  440. end if
  441. subS = S(lobs(1 : nlobs), :)
  442. subdy = dy(lobs(1 : nlobs))
  443. ! taper ensemble observation anomalies and innovations
  444. !
  445. if (LOCFUN_USED /= LOCFUN_NONE) then
  446. do o = 1, nlobs
  447. obs0 = obs(lobs(o))
  448. dist = spherdist(modlon(i, j), modlat(i, j),&
  449. obs0 % lon, obs0 % lat)
  450. lfactor = locfun(dist / radius / 1000.0)
  451. subS(o, :) = subS(o, :) * lfactor
  452. subdy(o) = subdy(o) * lfactor
  453. lfactors(o) = lfactor
  454. if (SCHEME_USED == SCHEME_ENKF) then
  455. subD(o, :) = subD(o, :) * lfactor
  456. end if
  457. end do
  458. else
  459. lfactors = 1
  460. end if
  461. ! first iteration - with rfactor = 1, for the update of the mean
  462. ! secons iteration - with the specified rfactorm for the update of
  463. ! the anomalies
  464. !
  465. do iter = 1,2
  466. if (iter == 2) then
  467. if (rfactor == 1.0d0) then
  468. go to 10
  469. end if
  470. subS = subS / sqrt(rfactor)
  471. end if
  472. if (nlobs < nrens) then ! use observation space
  473. ! Construct matrix (S * S' + I) - to be inverted
  474. !
  475. X1 = matmul(subS, transpose(subS))
  476. do o = 1, nlobs
  477. X1(o, o) = X1(o, o) + 1.0d0
  478. end do
  479. ! Inversion via Cholesky decomposition, done in two stages.
  480. !
  481. call dpotrf('U', nlobs, X1, nlobs, lapack_info)
  482. if (lapack_info /= 0) then
  483. print *, ' ERROR: m_local_analysis(): LAPACK error in dpotrf: errno = '&
  484. , lapack_info, 'i, j =', i, j
  485. call stop_mpi
  486. endif
  487. call dpotri('U', nlobs, X1, nlobs, lapack_info)
  488. if (lapack_info /= 0) then
  489. print *, ' ERROR: m_local_analysis(): LAPACK error in dpotri: errno = '&
  490. , lapack_info, 'i, j =', i, j
  491. call stop_mpi
  492. endif
  493. ! fill the lower triangular part of (symmetric) X1
  494. !
  495. do o = 2, nlobs
  496. X1(o, 1 : o - 1) = X1(1 : o - 1, o)
  497. end do
  498. G = matmul(transpose(subS), X1)
  499. else ! nlobs >= nrens: use ensemble space
  500. X1 = matmul(transpose(subS), subS)
  501. do m = 1, nrens
  502. X1(m, m) = X1(m, m) + 1.0d0
  503. end do
  504. ! Inversion
  505. !
  506. call dpotrf('U', nrens, X1, nrens, lapack_info)
  507. if (lapack_info /= 0) then
  508. print *, ' ERROR: m_local_analysis(): LAPACK error in dpotrf: errno = '&
  509. , lapack_info, 'i, j =', i, j
  510. call stop_mpi
  511. endif
  512. call dpotri('U', nrens, X1, nrens, lapack_info)
  513. if (lapack_info /= 0) then
  514. print *, ' ERROR: m_local_analysis(): LAPACK error in dpotri: errno = '&
  515. , lapack_info, 'i, j =', i, j
  516. call stop_mpi
  517. endif
  518. do m = 2, nrens
  519. X1(m, 1 : m - 1) = X1(1 : m - 1, m)
  520. end do
  521. G = matmul(X1, transpose(subS))
  522. end if
  523. if (iter == 1) then
  524. do m = 1, nrens
  525. X5tmp(m, :) = sum(G(m, :) * subdy(:))
  526. end do
  527. end if
  528. 10 continue
  529. ! calculate DFS at iteration 1, SRF at iteration 2
  530. !
  531. if (iter == 1) then
  532. dfs = traceprod(G, subS, nrens, nlobs)
  533. dfs_array(i, j) = real(dfs, 4)
  534. pnlobs = 0
  535. do uo = 1, nuobs
  536. do o = 1, nlobs
  537. if (lobs(o) >= uobs_begin(uo) .and.&
  538. lobs(o) <= uobs_end(uo)) then
  539. pnlobs(uo) = pnlobs(uo) + 1
  540. plobs(pnlobs(uo), uo) = o
  541. end if
  542. end do
  543. end do
  544. pdfs = 0.0d0
  545. psrf = 0.0d0
  546. do uo = 1, nuobs
  547. if (pnlobs(uo) > 0) then
  548. pdfs(uo) = traceprod(G(:, plobs(1 : pnlobs(uo), uo)),&
  549. subS(plobs(1 : pnlobs(uo), uo), :), nrens, pnlobs(uo))
  550. end if
  551. pdfs_array(i, j, uo) = real(pdfs(uo), 4)
  552. end do
  553. else
  554. if (dfs /= 0.0d0) then
  555. srf = sqrt(traceprod(subS, transpose(subS), nlobs, nrens)&
  556. / traceprod(G, subS, nrens, nlobs)) - 1.0d0
  557. else
  558. srf = 0.0d0
  559. end if
  560. srf_array(i, j) = real(srf, 4)
  561. do uo = 1, nuobs
  562. if (pnlobs(uo) > 0) then
  563. if (pdfs(uo) /= 0.0d0) then
  564. psrf(uo) = sqrt(&
  565. traceprod(subS(plobs(1 : pnlobs(uo), uo), :),&
  566. transpose(subS(plobs(1 : pnlobs(uo), uo), :)),&
  567. pnlobs(uo), nrens) /&
  568. traceprod(G(:, plobs(1 : pnlobs(uo), uo)),&
  569. subS(plobs(1 : pnlobs(uo), uo), :),&
  570. nrens, pnlobs(uo))) - 1.0d0
  571. else
  572. psrf(uo) = 0.0d0
  573. end if
  574. end if
  575. psrf_array(i, j, uo) = real(psrf(uo), 4)
  576. end do
  577. end if
  578. end do ! iter
  579. if (SCHEME_USED == SCHEME_ENKF) then
  580. X5tmp = X5tmp + matmul(G, subD - subS)
  581. elseif (SCHEME_USED == SCHEME_DENKF) then
  582. X5tmp = X5tmp - 0.5d0 * matmul(G, subS)
  583. end if
  584. do m = 1, nrens
  585. X5tmp(m, m) = X5tmp(m, m) + 1.0d0
  586. enddo
  587. if (testthiscell) then
  588. ! ensemble mean
  589. allocate(x(nlobs))
  590. do o = 1, nlobs
  591. x(o) = obs(lobs(o)) % d - dy(lobs(o)) * sqrtm * sqrt(obs(lobs(o)) % var)
  592. end do
  593. tmp(1) = rfactor
  594. call p2nc_writeobs(i, j, nlobs, nrens, X5tmp, modlon(i, j),&
  595. modlat(i, j), depths(i, j), tmp(1), lobs(1 : nlobs), &
  596. obs(lobs(1 : nlobs)), x, subS, subdy, lfactors)
  597. deallocate(x)
  598. end if
  599. ! Put X5tmp into the final X5 matrix - to be written to a file
  600. !
  601. X5(:, :, i) = real(X5tmp, 4)
  602. deallocate(subS, subdy, lfactors, X1, G)
  603. if (SCHEME_USED == SCHEME_ENKF) then
  604. deallocate(subD)
  605. end if
  606. end do ! i = 1, ni
  607. ! Write one "stripe" of the temporary matrix X5 to disk
  608. iter = 0
  609. do while (.true.)
  610. iter = iter + 1
  611. write(17, rec = j, iostat = iostatus) X5
  612. if (iostatus /= 0) then
  613. print *, 'ERROR: calc_X5(): I/O error at writing X5, iostatus = ',&
  614. iostatus
  615. print *, 'ERROR: at model line j =', j, ' counter jj = ', jj, 'iter =', iter
  616. if (iter < MAXITER) then
  617. cycle
  618. else
  619. print *, 'ERROR: max number of iterations reached, STOP'
  620. stop
  621. end if
  622. end if
  623. read(17, rec = j, iostat = iostatus) X5check
  624. if (iostatus /= 0) then
  625. print *, 'ERROR: calc_X5(): I/O error at reading X5, iostatus = ',&
  626. iostatus
  627. print *, 'ERROR: at j = ', j, ' jj = ', jj, 'iter =', iter
  628. if (iter < MAXITER) then
  629. cycle
  630. else
  631. print *, 'ERROR: max number of iterations reached, STOP'
  632. stop
  633. end if
  634. end if
  635. if (abs(maxval(X5 - X5check)) > 1.0e-6) then
  636. print *, 'ERROR: calc_X5(): inconsistency between written/read X5'
  637. print *, 'ERROR: j = ', j, ' jj = ', jj, 'iter =', iter,&
  638. ' maxval(X5 - X5check) =', maxval(X5 - X5check)
  639. if (iter < MAXITER) then
  640. cycle
  641. else
  642. print *, 'ERROR: max number of iterations reached, STOP'
  643. stop
  644. end if
  645. end if
  646. exit ! OK
  647. end do
  648. print *, 'FINISHED j =', j, ' jj =', jj
  649. end do ! j = my_first_iteration, my_last_iteration
  650. close(17) ! X5 file
  651. if (SCHEME_USED == SCHEME_ENKF) then
  652. deallocate(D)
  653. end if
  654. #if defined(QMPI)
  655. if (.not. master) then
  656. ! broadcast nlobs and dfs arrays to master
  657. call send(nlobs_array(:, jmap(my_first_iteration : my_last_iteration)), 0, 0)
  658. call send(dfs_array(:, jmap(my_first_iteration : my_last_iteration)), 0, 1)
  659. call send(srf_array(:, jmap(my_first_iteration : my_last_iteration)), 0, 1)
  660. allocate(mpibuffer_float1(ni, my_last_iteration - my_first_iteration + 1))
  661. allocate(mpibuffer_float2(ni, my_last_iteration - my_first_iteration + 1))
  662. do uo = 1, nuobs
  663. mpibuffer_float1 = pdfs_array(:, jmap(my_first_iteration : my_last_iteration), uo)
  664. call send(mpibuffer_float1, 0, uo + 1)
  665. mpibuffer_float2 = psrf_array(:, jmap(my_first_iteration : my_last_iteration), uo)
  666. call send(mpibuffer_float2, 0, uo + 1)
  667. end do
  668. deallocate(mpibuffer_float1)
  669. deallocate(mpibuffer_float2)
  670. else
  671. ! receive nlobs and dfs arrays
  672. do p = 2, qmpi_num_proc
  673. !
  674. ! PS: Ideally, it would be nice to be able to use a simple code like:
  675. !
  676. ! call receive(nlobs_array(&
  677. ! jmap(first_iteration(p) : last_iteration(p))), p - 1)
  678. !
  679. ! but this seems not to work, at least with the PGI compiler.
  680. ! Perhaps, this is too much to expect from a call to a C function...
  681. ! The good news is that using a temporal array works fine.
  682. !
  683. allocate(mpibuffer_int(ni, last_iteration(p) - first_iteration(p) + 1))
  684. call receive(mpibuffer_int, p - 1, 0)
  685. nlobs_array(:, jmap(first_iteration(p) : last_iteration(p))) = mpibuffer_int
  686. deallocate(mpibuffer_int)
  687. allocate(mpibuffer_float1(ni, last_iteration(p) - first_iteration(p) + 1))
  688. call receive(mpibuffer_float1, p - 1, 1)
  689. dfs_array(:, jmap(first_iteration(p) : last_iteration(p))) = mpibuffer_float1
  690. allocate(mpibuffer_float2(ni, last_iteration(p) - first_iteration(p) + 1))
  691. call receive(mpibuffer_float2, p - 1, 1)
  692. srf_array(:, jmap(first_iteration(p) : last_iteration(p))) = mpibuffer_float2
  693. do uo = 1, nuobs
  694. call receive(mpibuffer_float1, p - 1, uo + 1)
  695. pdfs_array(:, jmap(first_iteration(p) : last_iteration(p)), uo) = mpibuffer_float1
  696. call receive(mpibuffer_float2, p - 1, uo + 1)
  697. psrf_array(:, jmap(first_iteration(p) : last_iteration(p)), uo) = mpibuffer_float2
  698. end do
  699. deallocate(mpibuffer_float1)
  700. deallocate(mpibuffer_float2)
  701. enddo
  702. endif
  703. ! broadcast nlobs array
  704. call broadcast(nlobs_array)
  705. #endif
  706. if (master) then
  707. nlobs_max = maxval(nlobs_array)
  708. print *, 'maximal # of local obs =', nlobs_max,&
  709. ' reached for', count(nlobs_array == nlobs_max), 'grid cells'
  710. print *, 'average #(*) of local obs =', sum(nlobs_array(:, 1 : nj)) / real(count(nlobs_array(:, 1 : nj) > 0))
  711. print *, ' * over cells with non-zero number of local obs only'
  712. print *, 'localisation function of type', LOCFUN_USED, 'has been used'
  713. print *, 'analysis conducted in obs space in', count(nlobs_array(:, 1 : nj) > 0 .and. nlobs_array(:, 1 : nj) < nrens),&
  714. 'cells'
  715. print *, 'analysis conducted in ens space in', count(nlobs_array(:, 1 : nj) >= nrens),&
  716. 'cells'
  717. print *, 'maximal DFS =', maxval(dfs_array)
  718. print *, 'average(*) DFS =', sum(dfs_array) / real(count(dfs_array > 0))
  719. print *, ' * over cells with non-zero number of local obs only'
  720. print *, '# of cells with DFS > N / 2 =', count(dfs_array > real(nrens / 2, 4))
  721. call diag2nc(ni, nj, modlon, modlat, nlobs_array, dfs_array, pdfs_array,&
  722. srf_array, psrf_array)
  723. end if
  724. end subroutine calc_X5
  725. integer function get_npad_la(ni, nj)
  726. integer, intent(in) :: ni, nj
  727. get_npad_la = 4096 - mod(ni * nj, 4096)
  728. get_npad_la = mod(get_npad_la, 4096)
  729. end function get_npad_la
  730. real function locfun(x)
  731. real, intent(in) :: x
  732. real :: xx, xx2, xx3
  733. select case(LOCFUN_USED)
  734. case (LOCFUN_NONE)
  735. locfun = 1.0
  736. case (LOCFUN_STEP)
  737. if (x > 1.0) then
  738. locfun = 0.0
  739. else
  740. locfun = 1.0
  741. end if
  742. case (LOCFUN_GASPARI_COHN)
  743. if (x > 1.0) then
  744. locfun = 0.0
  745. else
  746. xx = x * 2.0
  747. xx2 = xx * xx
  748. xx3 = xx2 * xx
  749. if (xx < 1.0) then
  750. locfun = 1.0 + xx2 * (- xx3 / 4.0 + xx2 / 2.0)&
  751. + xx3 * (5.0 / 8.) - xx2 * (5.0 / 3.0)
  752. else
  753. locfun = xx2 * (xx3 / 12.0 - xx2 / 2.0)&
  754. + xx3 * (5.0 / 8.0) + xx2 * (5.0 / 3.0)&
  755. - xx * 5.0 + 4.0 - (2.0 / 3.0) / xx
  756. end if
  757. locfun = max(locfun, 0.0)
  758. end if
  759. case default
  760. print *, 'ERROR: m_local_analysis.F90: locfun(): LOCFUN_USED =', LOCFUN_USED, 'is unknown'
  761. stop
  762. end select
  763. end function locfun
  764. ! - Sort observations by their distance to the given grid point (i, j).
  765. ! - Identify observations within a given radius `rmax'.
  766. ! - Select `nlobs' nearest observations; update `nlobs' if there are not
  767. ! enough observations within the radius.
  768. !
  769. ! Note that because all observations are parsed for each 2D grid point, this
  770. ! subroutine may become a bottleneck if the total number of observations
  771. ! grows substantially from the current point... If this happens, we may
  772. ! consider putting all observations in a K-D tree like in Szyonykh et. al
  773. ! (2008), A local ensemble transform Kalman filter data assimilation system
  774. ! for the NCEP global model (2008). Tellus 60A, 113-130.
  775. !
  776. subroutine get_local_obs(i, j, rmax, modlon, modlat, mindx,&
  777. ni, nj, nlobs, lobs)
  778. use mod_measurement
  779. use m_obs
  780. use m_spherdist
  781. implicit none
  782. integer, intent(in) :: i, j
  783. real, intent(in) :: rmax ! maximal allowed distance
  784. real, intent(in) :: modlon(ni, nj)
  785. real, intent(in) :: modlat(ni, nj)
  786. real, intent(in) :: mindx
  787. integer, intent(in) :: ni, nj
  788. integer, intent(inout) :: nlobs ! input : max allowed # of local obs
  789. ! output: actual # of local obs for this
  790. ! point
  791. integer, intent(out) :: lobs(nobs) ! indices of local observations
  792. integer :: ngood
  793. integer :: sorted(nobs)
  794. real :: dist(nobs)
  795. integer :: o
  796. real :: rmax2
  797. lobs = 0
  798. ngood = 0
  799. rmax2 = (rmax / mindx) ** 2
  800. do o = 1, nobs
  801. if ((obs(o) % ipiv - i) ** 2 + (obs(o) % jpiv - j) ** 2 > rmax2) then
  802. cycle
  803. end if
  804. dist(o) = spherdist(obs(o) % lon, obs(o) % lat, modlon(i, j), modlat(i, j))
  805. if (dist(o) <= rmax) then
  806. ngood = ngood + 1
  807. lobs(ngood) = o
  808. end if
  809. end do
  810. if (nlobs <= 0 .or. nlobs >= ngood) then
  811. !
  812. ! use all observations within localisation support radius
  813. !
  814. nlobs = ngood
  815. else
  816. !
  817. ! use `nlobs' closest observations
  818. !
  819. call order(dble(nobs), dist, dble(ngood), lobs, sorted)
  820. lobs(1 : nlobs) = sorted(1 : nlobs)
  821. end if
  822. end subroutine get_local_obs
  823. ! This subroutine writes (1) the number of local observations, (2)
  824. ! the number of degrees of freedom of signal (DFS), and (3) spread reduction
  825. ! factor (SRF) to file "enkf_diag.nc"
  826. !
  827. subroutine diag2nc(ni, nj, lon, lat, nlobs_array, dfs_array, pdfs_array, &
  828. srf_array, psrf_array)
  829. use mod_measurement
  830. use m_obs
  831. use nfw_mod
  832. implicit none
  833. integer, intent(in) :: ni
  834. integer, intent(in) :: nj
  835. real, intent(in) :: lon(ni, nj)
  836. real, intent(in) :: lat(ni, nj)
  837. integer, intent(in) :: nlobs_array(ni, nj)
  838. real(4), intent(in) :: dfs_array(ni, nj)
  839. real(4), intent(in) :: pdfs_array(ni, nj, nuobs)
  840. real(4), intent(in) :: srf_array(ni, nj)
  841. real(4), intent(in) :: psrf_array(ni, nj, nuobs)
  842. character(STRLEN) :: fname
  843. character(STRLEN) :: varname
  844. integer :: ncid
  845. integer :: dimids(2)
  846. integer :: lon_id, lat_id, nlobs_id, dfs_id, pdfs_id(nuobs), srf_id,&
  847. psrf_id(nuobs)
  848. integer :: uo
  849. fname = 'enkf_diag.nc'
  850. call nfw_create(fname, nf_clobber, ncid)
  851. call nfw_def_dim(fname, ncid, 'i', ni, dimids(1))
  852. call nfw_def_dim(fname, ncid, 'j', nj, dimids(2))
  853. call nfw_def_var(fname, ncid, 'lon', nf_float, 2, dimids, lon_id)
  854. call nfw_def_var(fname, ncid, 'lat', nf_float, 2, dimids, lat_id)
  855. call nfw_def_var(fname, ncid, 'nobs', nf_int, 2, dimids, nlobs_id)
  856. call nfw_def_var(fname, ncid, 'dfs', nf_float, 2, dimids, dfs_id)
  857. do uo = 1, nuobs
  858. write(varname, '(a, a)') 'dfs_', trim(unique_obs(uo))
  859. call nfw_def_var(fname, ncid, trim(varname), nf_float, 2, dimids, pdfs_id(uo))
  860. end do
  861. call nfw_def_var(fname, ncid, 'srf', nf_float, 2, dimids, srf_id)
  862. do uo = 1, nuobs
  863. write(varname, '(a, a)') 'srf_', trim(unique_obs(uo))
  864. call nfw_def_var(fname, ncid, trim(varname), nf_float, 2, dimids, psrf_id(uo))
  865. end do
  866. call nfw_enddef(fname, ncid)
  867. call nfw_put_var_double(fname, ncid, lon_id, lon)
  868. call nfw_put_var_double(fname, ncid, lat_id, lat)
  869. call nfw_put_var_int(fname, ncid, nlobs_id, nlobs_array)
  870. call nfw_put_var_real(fname, ncid, dfs_id, dfs_array)
  871. call nfw_put_var_real(fname, ncid, srf_id, srf_array)
  872. do uo = 1, nuobs
  873. call nfw_put_var_real(fname, ncid, pdfs_id(uo), pdfs_array(:, :, uo))
  874. call nfw_put_var_real(fname, ncid, psrf_id(uo), psrf_array(:, :, uo))
  875. end do
  876. call nfw_close(fname, ncid)
  877. end subroutine diag2nc
  878. ! Calculates the trace of a product of two matrices. (Does not calculate
  879. ! the off-diagonal elements in the process.)
  880. !
  881. real function traceprod(A, B, n, m)
  882. real, intent(in) :: A(n, m), B(m, n)
  883. integer, intent(in) :: n, m
  884. integer :: i
  885. traceprod = 0.0d0
  886. do i = 1, n
  887. traceprod = traceprod + sum(A(i, :) * B(:, i))
  888. end do
  889. end function traceprod
  890. end module m_local_analysis