m_prep_4_EnKF.F90 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. ! File: m_prep_4_EnKF.F90
  2. !
  3. ! Created: ???
  4. !
  5. ! Last modified: 29/06/2010
  6. !
  7. ! Purpose: Calculation of HA ("S")
  8. !
  9. ! Description: Calculates HA by going sequentially through each data type.
  10. !
  11. ! Modifications:
  12. ! 09/11/2012 Geir Arne Waagbo:
  13. ! - Added support for OSISAF ice drift obs
  14. ! 29/07/2010 PS:
  15. ! - merged insitu_QC() with generic obs_QC(). Moved
  16. ! insitu_writeforecast() to the point after QC.
  17. ! 29/06/2010 PS:
  18. ! - added generic observation QC: increase the observation
  19. ! error when observation and ensemble mean are much too far
  20. ! away than expected
  21. ! Prior history:
  22. ! Not documented.
  23. module m_prep_4_EnKF
  24. integer, parameter, private :: STRLEN = 512
  25. private read_mean_ssh
  26. contains
  27. ! This subroutine uses the observation and ensembles from the model
  28. ! to prepare the input to the EnKF analysis scheme.
  29. ! The output from this routine is used directly in the global analysis
  30. ! while the output has to be run through a "filter" to be used in the
  31. ! local analysis scheme.
  32. ! S = HA (ensemble observation anomalies)
  33. ! d = d - Hx (innovations)
  34. !
  35. ! S is calculated in two steps:
  36. ! 1. S = HE
  37. ! 2. S = S - repmat(s, 1, m),
  38. ! where s = mean(S')';
  39. ! Note that in reality (with HYCOM) H is different for each member...
  40. ! So that HX must be read "HX" rather than "H * X".
  41. !
  42. subroutine prep_4_EnKF(nrens, enslist, d, S, depths, meandx, nx, ny, nz)
  43. #if defined (QMPI)
  44. use qmpi, only : master, stop_mpi
  45. #else
  46. use qmpi_fake, only : master, stop_mpi
  47. #endif
  48. use mod_measurement
  49. use m_obs
  50. use m_Generate_element_Si
  51. use m_get_mod_fld
  52. use m_read_icemod
  53. use m_parameters
  54. implicit none
  55. integer, intent(in) :: nx, ny, nz ! Model size
  56. integer, intent(in) :: nrens ! Size of ensemble
  57. integer, dimension(:),intent(in) :: enslist ! [CKB,FM] List of existing ens members
  58. real, intent(in) :: depths(nx, ny)
  59. real, intent(in) :: meandx ! mean grid size
  60. real, intent(inout) :: d(nobs)
  61. real, intent(inout) :: S(nobs, nrens)
  62. real :: x(nobs)
  63. integer :: i, j, m, iens
  64. real*4, dimension(nx,ny) :: fldr4
  65. real :: readfld(nx, ny), ai1(nx,ny), ai2(nx,ny), ai3(nx,ny), ai4(nx,ny), ai5(nx,ny), uice(nx,ny), vice(nx,ny)
  66. real :: vi1(nx,ny), vi2(nx,ny), vi3(nx,ny), vi4(nx,ny), vi5(nx,ny)
  67. real :: vs1(nx,ny), vs2(nx,ny), vs3(nx,ny), vs4(nx,ny), vs5(nx,ny)
  68. ! hard-coded for now
  69. integer, parameter :: drnx = 152, drny = 132
  70. real*4, dimension(drnx, drny) :: modzon, modmer
  71. integer, parameter :: drnx_osisaf = 119, drny_osisaf = 177
  72. real*4, dimension(drnx_osisaf, drny_osisaf) :: dX, dY
  73. integer :: reclSLA, ios, reclDRIFT
  74. character*3 :: cmem
  75. character*2 :: day
  76. character*1 :: offset
  77. logical :: ex
  78. character(STRLEN) :: fname
  79. integer :: iuobs
  80. ! FANF: For track assim we launch m_Generate_Si for each day (t=1:Wd)
  81. ! which fills in S at the approriate indices.
  82. ! Wd is is the assimilation cycle (i.e. 7 days)
  83. !
  84. integer :: Wd, t
  85. integer :: tlevel
  86. real :: field2(nx, ny), field3(nx, ny) ! auxiliary fields (e.g. mean SSH,
  87. ! field bias estimate etc.)
  88. integer :: nthisobs, thisobs(nobs)
  89. if (any(obs(:) % id == 'TSLA ')) then
  90. Wd = 6
  91. else
  92. Wd = 0
  93. endif
  94. ! security check
  95. !
  96. if (any(obs(:) % id == 'SSH ') .or. any(obs(:) % id == 'SLA ')) then
  97. if (any(obs(:) % id == 'SLA ')) then
  98. inquire(exist = ex, file = 'model_SLA.uf')
  99. if (.not.ex) then
  100. if (master) print *,'model_SLA.uf does not exist'
  101. call stop_mpi()
  102. end if
  103. end if
  104. if (any(obs(:) % id == 'SSH ')) then
  105. inquire(exist = ex, file = 'model_SSH.uf')
  106. if (.not.ex) then
  107. if (master) print *,'model_SSH.uf does not exist'
  108. call stop_mpi()
  109. end if
  110. end if
  111. end if
  112. ! construct S=HA
  113. !
  114. do iuobs = 1, nuobs
  115. if (master) then
  116. print *, 'prep_4_EnKF: now preparing "', trim(unique_obs(iuobs)), '" observations'
  117. end if
  118. if (trim(unique_obs(iuobs)) == 'ICEC') then
  119. do iens = 1, nrens
  120. write(cmem,'(i3.3)') iens
  121. tlevel = 1
  122. call get_mod_fld_new(trim('forecast'//cmem), readfld, iens,&
  123. 'icec', 0, tlevel, nx, ny)
  124. if (tlevel == -1) then
  125. if (master) then
  126. print *, 'ERROR: get_mod_fld_new(): failed for "icec"'
  127. end if
  128. stop
  129. end if
  130. call Generate_element_Si(S(:, iens), unique_obs(iuobs),&
  131. readfld, depths, nx, ny, nz, 0)
  132. end do
  133. ! [FM, May 2013: for LIM3 sea ice model]
  134. elseif (trim(unique_obs(iuobs)) == 'AT_I') then
  135. do iens = 1, nrens
  136. write(cmem,'(i3.3)') iens
  137. tlevel = 1
  138. call io_mod_fld(ai1,iens,enslist, &
  139. 'a_i_htc1', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  140. call io_mod_fld(ai2,iens,enslist, &
  141. 'a_i_htc2', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  142. call io_mod_fld(ai3,iens,enslist, &
  143. 'a_i_htc3', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  144. call io_mod_fld(ai4,iens,enslist, &
  145. 'a_i_htc4', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  146. call io_mod_fld(ai5,iens,enslist, &
  147. 'a_i_htc5', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  148. if (tlevel == -1) then
  149. if (master) then
  150. print *, 'ERROR: io_mod_fld_new(): failed for "at_i"'
  151. end if
  152. stop
  153. end if
  154. ! Multipply by 100 to match obs conventions
  155. readfld=(ai1+ai2+ai3+ai4+ai5) * 100.0
  156. call Generate_element_Si(S(:, iens), unique_obs(iuobs),&
  157. readfld, depths, nx, ny, nz, 0)
  158. end do
  159. ! freeboard
  160. elseif(trim(unique_obs(iuobs)) == 'VT_I') then
  161. do iens = 1, nrens
  162. write(cmem, '(i3.3)') iens
  163. tlevel = 1
  164. call io_mod_fld(ai1,iens,enslist, &
  165. 'a_i_htc1', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  166. call io_mod_fld(ai2,iens,enslist, &
  167. 'a_i_htc2', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  168. call io_mod_fld(ai3,iens,enslist, &
  169. 'a_i_htc3', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  170. call io_mod_fld(ai4,iens,enslist, &
  171. 'a_i_htc4', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  172. call io_mod_fld(ai5,iens,enslist, &
  173. 'a_i_htc5', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  174. call io_mod_fld(vi1,iens,enslist, &
  175. 'v_i_htc1', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  176. call io_mod_fld(vi2,iens,enslist, &
  177. 'v_i_htc2', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  178. call io_mod_fld(vi3,iens,enslist, &
  179. 'v_i_htc3', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  180. call io_mod_fld(vi4,iens,enslist, &
  181. 'v_i_htc4', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  182. call io_mod_fld(vi5,iens,enslist, &
  183. 'v_i_htc5', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  184. if (tlevel == -1) then
  185. if (master) then
  186. print *, 'ERROR: io_mod_fld_nex(): failed for "SIFB"'
  187. end if
  188. stop
  189. end if
  190. readfld=(vi1+vi2+vi3+vi4+vi5)
  191. call Generate_element_Si(S(:, iens), unique_obs(iuobs),&
  192. readfld, depths, nx, ny, nz, 0)
  193. end do
  194. ! freeboard
  195. elseif(trim(unique_obs(iuobs)) == 'RFB') then
  196. do iens = 1, nrens
  197. write(cmem, '(i3.3)') iens
  198. tlevel = 1
  199. call io_mod_fld(ai1,iens,enslist, &
  200. 'a_i_htc1', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  201. call io_mod_fld(ai2,iens,enslist, &
  202. 'a_i_htc2', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  203. call io_mod_fld(ai3,iens,enslist, &
  204. 'a_i_htc3', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  205. call io_mod_fld(ai4,iens,enslist, &
  206. 'a_i_htc4', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  207. call io_mod_fld(ai5,iens,enslist, &
  208. 'a_i_htc5', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  209. call io_mod_fld(vi1,iens,enslist, &
  210. 'v_i_htc1', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  211. call io_mod_fld(vi2,iens,enslist, &
  212. 'v_i_htc2', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  213. call io_mod_fld(vi3,iens,enslist, &
  214. 'v_i_htc3', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  215. call io_mod_fld(vi4,iens,enslist, &
  216. 'v_i_htc4', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  217. call io_mod_fld(vi5,iens,enslist, &
  218. 'v_i_htc5', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  219. call io_mod_fld(vs1,iens,enslist, &
  220. 'v_s_htc1', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  221. call io_mod_fld(vs2,iens,enslist, &
  222. 'v_s_htc2', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  223. call io_mod_fld(vs3,iens,enslist, &
  224. 'v_s_htc3', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  225. call io_mod_fld(vs4,iens,enslist, &
  226. 'v_s_htc4', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  227. call io_mod_fld(vs5,iens,enslist, &
  228. 'v_s_htc5', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  229. if (tlevel == -1) then
  230. if (master) then
  231. print *, 'ERROR: io_mod_fld_nex(): failed for "SIFB"'
  232. end if
  233. stop
  234. end if
  235. readfld=(((vi1+vi2+vi3+vi4+vi5) * (1024.0 - 899.5) - 330 * (vs1+vs2+vs3+vs4+vs5)) / &
  236. 1024.0-0.25*(vs1 +vs2+vs3+vs4+vs5))
  237. !readfld=(((vi1+vi2+vi3+vi4+vi5) * (1024.0 - 899.5) - 330 * (vs1+vs2+vs3+vs4+vs5)) / 1024.0 - 0.25 * (vs1+vs2+vs3+vs4+vs5)) / (ai1+ai2+ai3+ai4+ai5)
  238. ! Conversion of models' sea ice thickness and snow thickness to
  239. ! model's freeboard using fixed densities for snow (330 kg/m3), ice
  240. ! (899.5 kg/m3 = average of MYI and FYI from Guerreiro et al. 2017
  241. ! and seawater (1024 kg/m3). The model freeboard is then lowered by
  242. ! 25% of the snow depth to account for the fact that the radar
  243. ! measurement underestimates the actual freeboard due to the lower
  244. ! propagation speed of the wave into the snow than in the air.
  245. ! Everything is converted from grid cell mean to in situ by
  246. ! dividing by concentration (if it is not zero). See exchanges
  247. ! e-mail with Sara Fleury 7 December 2020.
  248. call Generate_element_Si(S(:, iens), unique_obs(iuobs),&
  249. readfld, depths, nx, ny, nz, 0)
  250. end do
  251. elseif (trim(unique_obs(iuobs)) == 'SST') then
  252. do iens = 1, nrens
  253. write(cmem,'(i3.3)') iens
  254. tlevel = 1
  255. call get_mod_fld_new(trim('forecast'//cmem), readfld, iens,&
  256. 'tn', 1, tlevel, nx, ny)
  257. PRINT *, "FRANCOIS"
  258. if (tlevel == -1) then
  259. if (master) then
  260. print *, 'ERROR: get_mod_fld_new(): failed for "SST"'
  261. end if
  262. stop
  263. end if
  264. if (prm_prmestexists('sstb')) then
  265. tlevel = 1
  266. call get_mod_fld_new(trim('forecast'//cmem), field2, iens,&
  267. 'sstb', 0, tlevel, nx, ny)
  268. if (tlevel == -1) then
  269. if (master) then
  270. print *, 'ERROR: get_mod_fld_new(): failed for "sstb"'
  271. end if
  272. stop
  273. end if
  274. readfld = readfld - field2
  275. end if
  276. call Generate_element_Si(S(:, iens), unique_obs(iuobs),&
  277. readfld, depths, nx, ny, nz, 0)
  278. end do
  279. elseif (trim(unique_obs(iuobs)) == 'SLA' .or.&
  280. trim(unique_obs(iuobs)) == 'TSLA') then
  281. if (trim(unique_obs(iuobs)) == 'TSLA') then
  282. call read_mean_ssh(field2, nx, ny)
  283. end if
  284. inquire(iolength=reclSLA) fldr4
  285. ! FANF loop over each day of the week
  286. do t = 0, Wd
  287. if (trim(unique_obs(iuobs)) == 'TSLA') then
  288. write(day,'(i2.2)') t
  289. fname = trim('model_TSSH_'//day//'.uf')
  290. else
  291. fname = 'model_SLA.uf'
  292. endif
  293. if (master) then
  294. print *, 'TSLA, day', t, ': nobs = ',&
  295. count(obs(uobs_begin(iuobs) : uobs_end(iuobs)) % date == t)
  296. end if
  297. do iens = 1, nrens
  298. open(10, file = trim(fname), access = 'direct',&
  299. status = 'old', recl = reclSLA, action = 'read')
  300. read(10, rec = iens, iostat = ios) fldr4
  301. if (ios /= 0) then
  302. if (master) print *, 'Error reading ', trim(fname), ', member #', iens
  303. call stop_mpi()
  304. end if
  305. close(10)
  306. readfld = fldr4
  307. if (prm_prmestexists('msshb')) then
  308. write(cmem,'(i3.3)') iens
  309. tlevel = 1
  310. call get_mod_fld_new(trim('forecast'//cmem), field3, iens,&
  311. 'msshb', 0, tlevel, nx, ny)
  312. if (tlevel == -1) then
  313. if (master) then
  314. print *, 'ERROR: get_mod_fld_new(): failed for "msshb"'
  315. end if
  316. stop
  317. end if
  318. readfld = readfld - field3 ! mean SSH bias for this member
  319. end if
  320. if (trim(unique_obs(iuobs)) == 'TSLA') then
  321. readfld = readfld - field2 ! mean SSH
  322. end if
  323. call Generate_element_Si(S(:, iens), unique_obs(iuobs),&
  324. readfld, depths, nx, ny, nz, t)
  325. end do
  326. if (master) then
  327. print *, 'forming S, day', t
  328. print *, ' # of non-zero ens observations = ', count(S /= 0.0)
  329. print *, ' # of zero ens observations = ', count(S == 0.0)
  330. print *, ' # of non-zero observations for member 1 = ', count(S(:, 1) /= 0.0)
  331. end if
  332. end do
  333. elseif (trim(unique_obs(iuobs)) == 'SAL' .or.&
  334. trim(unique_obs(iuobs)) == 'TEM' .or.&
  335. trim(unique_obs(iuobs)) == 'GSAL' .or.&
  336. trim(unique_obs(iuobs)) == 'GTEM') then
  337. if (master) then
  338. print *, ' Interpolating ensemble vectors to the locations of "',&
  339. trim(unique_obs(iuobs)), '" observations'
  340. end if
  341. !
  342. ! for each ensemble member process all profiles "in parallel",
  343. ! reading the fields layer by layer
  344. !
  345. do iens = 1, nrens
  346. call get_S(S(:, iens), trim(unique_obs(iuobs)), nobs, obs, iens)
  347. end do
  348. if (master) then
  349. print *, ' Interpolation completed'
  350. end if
  351. elseif ((trim(unique_obs(iuobs)) == 'U_ICE') .or. trim(unique_obs(iuobs)) == 'V_ICE') then
  352. do iens = 1, nrens
  353. ! [FM] Read the file
  354. !inquire(iolength=reclDRIFT) modzon, modmer
  355. !open(10, file = 'model_ICEDRIFT.uf', access = 'direct',&
  356. ! status = 'old', recl = reclDRIFT, action = 'read')
  357. !read(10, rec = iens, iostat = ios) modzon, modmer
  358. !close(10)
  359. !if (ios /= 0) then
  360. ! if (master) then
  361. ! print *,'ERROR: could not read ensemble ice drift for member ', iens
  362. ! end if
  363. ! call stop_mpi()
  364. !end if
  365. call io_mod_fld(uice,iens,enslist, &
  366. 'u_ice', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  367. call io_mod_fld(vice,iens,enslist, &
  368. 'v_ice', 1, 0, 1, nx,ny, 'get',FLOAT(obs(1)%date))
  369. do m = 1, nobs
  370. i = obs(m) % i_orig_grid
  371. j = obs(m) % j_orig_grid
  372. if (trim(obs(m) % id) == 'U_ICE') then
  373. S(m, iens) = uice(i, j)
  374. elseif (trim(obs(m) % id) == 'V_ICE') then
  375. S(m, iens) = vice(i, j)
  376. end if
  377. end do
  378. end do
  379. elseif ((trim(unique_obs(iuobs)) == 'U2D_I') .OR. trim(unique_obs(iuobs)) == 'V2D_I' ) THEN
  380. ! ADDED BY FM FRANCOIS MASSONNET. u_ice_2d or v_ice_2d is the sea ice u or v-velocity
  381. ! obtained as follows:
  382. ! 1) Rotate OSISAF Low resolution 2-day sea ice drift in a {east,north}
  383. ! reference frame
  384. ! 2) Interpolate to the ORCA grid
  385. ! 3) Rotate to align with the ORCA grid
  386. ! 4) Multiply by 1000 and divide by 2*86400 to convert the 2-day
  387. ! displacement from km to m/s
  388. DO iens=1,nrens
  389. CALL read_icemod(uice,iens,enslist,'iicevelu',nx,ny)
  390. CALL read_icemod(vice,iens,enslist,'iicevelv',nx,ny)
  391. DO m = 1, nobs
  392. i = obs(m) % i_orig_grid
  393. j = obs(m) % j_orig_grid
  394. IF (trim(obs(m) % id) == 'U2D_I') THEN
  395. S(m,iens) = uice(i,j)
  396. ELSEIF (trim(obs(m) % id) == 'V2D_I') THEN
  397. S(m,iens) = vice(i,j)
  398. END IF
  399. END DO ! nobs
  400. END DO ! iens
  401. elseif ((index(unique_obs(iuobs),'DX') > 0 ) .or. (index(unique_obs(iuobs),'DY') > 0)) then
  402. ! OSISAF Ice drift observations (d-2-offset -> d-offset)
  403. !print *, 'Ice drift observation type: ', unique_obs(iuobs)
  404. offset = unique_obs(iuobs)(3:3)
  405. ! Use offset (1,2,3,4 or 5) to open correct model drift file
  406. inquire(iolength=reclDRIFT) dX, dY
  407. open(10, file = 'model_ICEDRIFT_OSISAF'//offset//'.uf', access = 'direct',&
  408. status = 'old', recl = reclDRIFT, action = 'read')
  409. do iens = 1, nrens
  410. read(10, rec = iens, iostat = ios) dX, dY
  411. if (ios /= 0) then
  412. if (master) then
  413. print *,'ERROR: could not read ensemble ice drift for member ', iens
  414. end if
  415. call stop_mpi()
  416. end if
  417. do m = 1, nobs
  418. i = obs(m) % i_orig_grid
  419. j = obs(m) % j_orig_grid
  420. if (index(obs(m)%id,'DX') > 0) then
  421. S(m, iens) = dX(i, j)
  422. elseif (index(obs(m)%id,'DY') > 0) then
  423. S(m, iens) = dY(i, j)
  424. end if
  425. end do
  426. end do
  427. close(10)
  428. else
  429. if (master) then
  430. print *,'ERROR: unknown obs type ' // trim(unique_obs(iuobs))
  431. end if
  432. call stop_mpi()
  433. end if
  434. end do ! iuobs
  435. ! some generic QC - relax fitting if the model and obs are too far apart
  436. !
  437. call obs_QC(nrens, S)
  438. ! add calculated HA to to observations-<type>.nc files for each data type
  439. !
  440. do iuobs = 1, nuobs
  441. if (master) then
  442. nthisobs = 0
  443. do m = 1, nobs
  444. if (trim(unique_obs(iuobs)) == trim(obs(m) % id)) then
  445. nthisobs = nthisobs + 1
  446. thisobs(nthisobs) = m
  447. end if
  448. end do
  449. ! add forecast values to the observation-<TYPE>.nc files
  450. !
  451. call add_forecast(unique_obs(iuobs), S(thisobs(1 : nthisobs), :), obs(thisobs(1 : nthisobs)))
  452. ! append the superobed values (and modified observation error
  453. ! variances) to the file with pre-processed observations (SAL.nc,
  454. ! TEM.nc, GSAL.nc or GTEM.nc)
  455. !
  456. if (trim(unique_obs(iuobs)) == 'SAL' .or.&
  457. trim(unique_obs(iuobs)) == 'TEM' .or.&
  458. trim(unique_obs(iuobs)) == 'GSAL' .or.&
  459. trim(unique_obs(iuobs)) == 'GTEM') then
  460. call insitu_writeforecast(unique_obs(iuobs), nobs, nrens, S, obs)
  461. end if
  462. end if
  463. end do
  464. if (master) then
  465. print *, 'm_prep_4_EnKF: end calculating S = HA'
  466. end if
  467. x = sum(S, DIM = 2) / real(nrens) ! [ FM ] The mean forecast interpolated in the obs.space
  468. if (master) print*,'m_prep_4_EnKF: end calculating Hx'
  469. if (master) then
  470. print *, 'Hx range = ', minval(x), '-', maxval(x)
  471. print *, 'mean(Hx) = ', sum(x) / real(nobs)
  472. end if
  473. if (master) then
  474. print *, 'observation range = ', minval(obs % d), '-', maxval(obs % d)
  475. print *, 'mean(observation) = ', sum(obs % d) / nobs
  476. end if
  477. ! Compute HA = HE - mean(HE)
  478. !
  479. if (master) print*,'prep_4_EnKF(): calculating S = S - x'
  480. do j = 1, nrens
  481. S(:, j) = S(:, j) - x ! [ FM ] This is really where we switch from actual model data to anomalies
  482. enddo
  483. ! Compute innovation
  484. !
  485. d = obs % d - x ! [ FM ] This is exactly was is also done in add_forecast. This is the mean innovation.
  486. if (master) then
  487. print *, ' innovation range = ', minval(d), '-', maxval(d)
  488. if (minval(d) < -1000.0d0) then
  489. print *, 'm_prep_4_EnKF: error: innovation too small detected'
  490. call stop_mpi()
  491. end if
  492. if (maxval(d) > 1000.0d0) then
  493. print *, 'm_prep_4_EnKF: error: innovation too big detected'
  494. call stop_mpi()
  495. end if
  496. end if
  497. end subroutine prep_4_EnKF
  498. subroutine read_mean_ssh(mean_ssh, nx, ny)
  499. #if defined (QMPI)
  500. use qmpi
  501. #else
  502. use qmpi_fake
  503. #endif
  504. use m_parameters
  505. integer, intent(in) :: nx, ny
  506. real, intent(out):: mean_ssh(nx, ny)
  507. logical :: exists
  508. inquire(file = trim(MEANSSHFNAME), exist = exists)
  509. if (.not. exists) then
  510. if (master) then
  511. print *,'ERROR: read_mean_ssh(): file "', trim(MEANSSHFNAME), '" not found'
  512. end if
  513. stop
  514. end if
  515. open (10, file = trim(MEANSSHFNAME), status = 'unknown',form = 'unformatted', action = 'read')
  516. read (10) mean_ssh
  517. close (10)
  518. end subroutine read_mean_ssh
  519. ! This subroutine adds forecast observations (i.e Hx) to the NetCDF
  520. ! observation files for each data type.
  521. !
  522. subroutine add_forecast(obstag, S, obs)
  523. use mod_measurement
  524. use nfw_mod
  525. implicit none
  526. character(OBSTYPESTRLEN), intent(in) :: obstag
  527. real, dimension(:, :), intent(in) :: S
  528. type(measurement), dimension(:) :: obs
  529. character(STRLEN) :: fname
  530. logical :: exists
  531. integer :: ncid
  532. integer :: dids(2), dimlen
  533. logical :: addsobs
  534. integer :: for_id, inn_id, forvar_id, slon_id, slat_id,&
  535. sdepth_id, sipiv_id, sjpiv_id, sd_id, svar_id,&
  536. newvar_id
  537. real, allocatable, dimension(:) :: x, Svar, innovation
  538. integer :: m, p, o
  539. write(fname, '(a, a, a)') 'observations-', trim(obstag), '.nc'
  540. inquire(file = trim(fname), exist = exists)
  541. if (.not. exists) then
  542. print *, 'file "', trim(fname), 'not found, skip adding forecast'
  543. return
  544. else
  545. print *, 'dumping forecast to "', trim(fname), '"'
  546. end if
  547. p = size(S, DIM = 1)
  548. m = size(S, DIM = 2)
  549. allocate(x(p), Svar(p), innovation(p))
  550. x = sum(S, DIM = 2) / real(m); ! [ FM the mean of S=HA ]
  551. Svar = 0.0
  552. do o = 1, p
  553. Svar(o) = sum((S(o, :) - x(o))** 2) ! [ FM thus each row of Svar is the variance (see below) of the forecast]
  554. end do
  555. Svar = Svar / real(m - 1)
  556. innovation = obs % d - x ! [ FM ] the innovation for the mean forecast (or mean of the innovation forecasts)
  557. addsobs = .false.
  558. call nfw_open(fname, nf_write, ncid)
  559. call nfw_inq_dimid(fname, ncid, 'nobs', dids(1))
  560. call nfw_inq_dimlen(fname, ncid, dids(1), dimlen)
  561. call nfw_redef(fname, ncid)
  562. if (dimlen == p) then
  563. dids(2) = dids(1)
  564. elseif (.not. nfw_dim_exists(ncid, 'nsobs')) then
  565. addsobs = .true.
  566. call nfw_def_dim(fname, ncid, 'nsobs', p, dids(2))
  567. call nfw_def_var(fname, ncid, 'slon', nf_float, 1, dids(2), slon_id)
  568. call nfw_def_var(fname, ncid, 'slat', nf_float, 1, dids(2), slat_id)
  569. call nfw_def_var(fname, ncid, 'sdepth', nf_float, 1, dids(2), sdepth_id)
  570. call nfw_def_var(fname, ncid, 'sipiv', nf_int, 1, dids(2), sipiv_id)
  571. call nfw_def_var(fname, ncid, 'sjpiv', nf_int, 1, dids(2), sjpiv_id)
  572. call nfw_def_var(fname, ncid, 'sd', nf_float, 1, dids(2), sd_id)
  573. call nfw_def_var(fname, ncid, 'svar', nf_float, 1, dids(2), svar_id)
  574. end if
  575. if (.not. nfw_var_exists(ncid, 'innovation')) then
  576. call nfw_def_var(fname, ncid, 'innovation', nf_double, 1, dids(2), inn_id)
  577. else
  578. call nfw_inq_varid(fname, ncid, 'innovation', inn_id)
  579. end if
  580. if (.not. nfw_var_exists(ncid, 'forecast')) then
  581. call nfw_def_var(fname, ncid, 'forecast', nf_double, 1, dids(2), for_id)
  582. else
  583. call nfw_inq_varid(fname, ncid, 'forecast', for_id)
  584. end if
  585. if (.not. nfw_var_exists(ncid, 'forecast_variance')) then
  586. call nfw_def_var(fname, ncid, 'forecast_variance', nf_double, 1, dids(2), forvar_id)
  587. else
  588. call nfw_inq_varid(fname, ncid, 'forecast_variance', forvar_id)
  589. end if
  590. if (.not. addsobs) then
  591. if (dimlen == p) then
  592. if (.not. nfw_var_exists(ncid, 'new_var')) then
  593. call nfw_def_var(fname, ncid, 'new_var', nf_double, 1, dids(2), newvar_id)
  594. else
  595. call nfw_inq_varid(fname, ncid, 'new_var', newvar_id)
  596. end if
  597. else
  598. if (.not. nfw_var_exists(ncid, 'new_svar')) then
  599. call nfw_inq_dimid(fname, ncid, 'nsobs', dids(2))
  600. call nfw_def_var(fname, ncid, 'new_svar', nf_double, 1, dids(2), newvar_id)
  601. else
  602. call nfw_inq_varid(fname, ncid, 'new_svar', newvar_id)
  603. end if
  604. end if
  605. end if
  606. call nfw_enddef(fname, ncid)
  607. call nfw_put_var_double(fname, ncid, forvar_id, Svar)
  608. call nfw_put_var_double(fname, ncid, for_id, x)
  609. call nfw_put_var_double(fname, ncid, inn_id, innovation)
  610. if (addsobs) then
  611. call nfw_put_var_double(fname, ncid, slon_id, obs % lon)
  612. call nfw_put_var_double(fname, ncid, slat_id, obs % lat)
  613. call nfw_put_var_double(fname, ncid, sdepth_id, obs % depth)
  614. call nfw_put_var_int(fname, ncid, sipiv_id, obs % ipiv)
  615. call nfw_put_var_int(fname, ncid, sjpiv_id, obs % jpiv)
  616. call nfw_put_var_double(fname, ncid, sd_id, obs % d)
  617. call nfw_put_var_double(fname, ncid, svar_id, obs % var)
  618. else
  619. call nfw_put_var_double(fname, ncid, newvar_id, obs % var)
  620. end if
  621. call nfw_close(fname, ncid)
  622. deallocate(x)
  623. deallocate(Svar)
  624. deallocate(innovation)
  625. end subroutine add_forecast
  626. end module m_prep_4_EnKF