m_local_analysis.f90 35 KB

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