m_prep_4_EnKF.f90 30 KB

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