m_Generate_element_Si.f90 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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. module m_Generate_element_Si
  10. implicit none
  11. public Generate_element_Si
  12. public get_S
  13. integer, parameter, private :: NONE = 0
  14. integer, parameter, private :: TEMPERATURE = 1
  15. integer, parameter, private :: SALINITY = 2
  16. real, parameter, private :: TEM_MIN = -2.5
  17. real, parameter, private :: TEM_MAX = 35.0
  18. real, parameter, private :: SAL_MIN = 5.0
  19. real, parameter, private :: SAL_MAX = 41.0
  20. logical, parameter, private :: VERT_INTERP_GRID = .true.
  21. contains
  22. subroutine Generate_element_Si(S, obstype, fld, depths, nx, ny, nz, t)
  23. use mod_measurement
  24. use m_obs
  25. implicit none
  26. real, dimension(nobs), intent(inout) :: S ! input/output vector
  27. character(len=5), intent(in) :: obstype ! the model fld type in "fld"
  28. integer, intent(in) :: nx,ny,nz ! grid size
  29. real, intent(in) :: fld (nx,ny) ! field to be placed in Si
  30. real, intent(in) :: depths(nx,ny) ! depth mask -- needed for support
  31. integer, intent(in), optional :: t !time of fld
  32. integer :: iobs
  33. integer :: i, j, ip1, jp1
  34. integer :: ix, jy, imin, imax, jmin, jmax, cnt
  35. logical :: isprofile
  36. real :: depth
  37. integer :: ns
  38. real, parameter :: undef = 999.9 ! land points have value huge()
  39. ! TEM, GTEM, SAL and GSAL come from profiles
  40. isprofile = (trim(obstype) .eq. 'SAL' .or.&
  41. trim(obstype) .eq. 'GSAL' .or.&
  42. trim(obstype) .eq. 'TEM' .or.&
  43. trim(obstype) .eq. 'GTEM')
  44. do iobs = 1, nobs
  45. if (trim(obstype) == obs(iobs) % id) then
  46. if (trim(obstype) .ne. 'TSLA' .or. obs(iobs) % date == t) then
  47. ! Get model gridcell
  48. i = obs(iobs) % ipiv
  49. j = obs(iobs) % jpiv
  50. ip1 = min(i + 1, nx)
  51. jp1 = min(j + 1, ny)
  52. depth = obs(iobs) % depth
  53. !TODO: 1. check consistency for ns = 1 vs ns = 0
  54. ! 2. check consistency of running from -ns to +ns (this can
  55. ! lead perhaps for averaginf over -1 0 1 = 3 x 3 instead
  56. ! of 2 x 2 grid cells if ns = 1
  57. if (depth .lt. 10.0 .and. .not. isprofile) then ! satellite data
  58. ns = obs(iobs) % ns
  59. if(ns .lt. 2) then ! point data : zero support
  60. S(iobs) = fld(i, j) * obs(iobs) % a1 &
  61. + fld(ip1, j) * obs(iobs) % a2 &
  62. + fld(ip1, jp1) * obs(iobs) % a3 &
  63. + fld(i, jp1) * obs(iobs) % a4
  64. else ! data support assumed a square of 2ns * 2ns grid cells
  65. imin = max( 1, i - ns)
  66. imax = min(nx, i + ns)
  67. jmin = max( 1, j - ns)
  68. jmax = min(ny, j + ns)
  69. cnt = 0
  70. S(iobs) = 0.0
  71. do jy = jmin, jmax
  72. do ix = imin, imax
  73. ! Removes data on land, absolute values larger than 1000 and NaNs
  74. if (depths(ix, jy) > 1.0 .and. abs(fld(ix, jy)) < 10.0d3 .and. fld(ix, jy) + 1.0d0 /= fld(ix, jy)) then
  75. S(iobs) = S(iobs) + fld(ix, jy)
  76. cnt = cnt + 1
  77. endif
  78. enddo
  79. enddo
  80. if (cnt == 0) then
  81. print *, ' observation on land ', i, j, obs(iobs) % d
  82. stop 'm_Generate_element_Sij: report bug to LB (laurentb@nersc.no)'
  83. end if
  84. S(iobs) = S(iobs) / real(cnt)
  85. endif
  86. elseif(isprofile) then ! in-situ data (in depth)
  87. print *,'(m_Generate_element_Si does not handle profiles yet)'
  88. stop '(m_Generate_element_Si)'
  89. else
  90. stop 'Generate_element_Sij: not a profile but depth is deeper than 10m'
  91. endif
  92. end if ! obs and model are at similar time
  93. end if ! (trim(obstype) == obs(iobs) % id) then
  94. end do
  95. end subroutine Generate_element_Si
  96. ! Get S = HA for in-situ data. Linearly interpolate for obs positioned
  97. ! between the layer centres; otherwise use the layer value for the obs above
  98. ! the middle of the first layer or below the middle of the last layer.
  99. !
  100. ! Note - this procedure parses through all obs for each ensemble member
  101. ! to work out profiles. This indeed invlolves some redundancy because
  102. ! this work could be done only once. However, the penalty (I think) is
  103. ! quite small compared to the time required for reading the fields from
  104. ! files and does not worth modifying (and complicating) the code.
  105. !
  106. subroutine get_S(S, obstag, nobs, obs, iens)
  107. use mod_measurement
  108. use m_insitu
  109. use m_get_mod_fld
  110. use m_io_mod_fld ! CKB, FM
  111. !use m_parse_blkdat
  112. use m_get_mod_xyz ! was: m_parse_blkdat
  113. use m_parameters
  114. implicit none
  115. real, dimension(nobs), intent(inout) :: S
  116. character(*), intent(in) :: obstag
  117. integer, intent(in) :: nobs
  118. type(measurement), dimension(nobs) :: obs
  119. integer, intent(in) :: iens
  120. real, parameter :: ONEMETER = 9806.0
  121. ! obs stuff
  122. !
  123. integer :: p, o
  124. integer, allocatable, dimension(:) :: ipiv, jpiv
  125. real, allocatable, dimension(:) :: a1, a2, a3, a4
  126. ! grid stuff
  127. !
  128. integer :: k
  129. integer :: ni, nj, nk
  130. real :: rdummy
  131. ! vertical stuff
  132. !
  133. real, allocatable, dimension(:) :: zgrid, zcentre, zgrid_prev, zcentre_prev
  134. real, allocatable, dimension(:) :: v, v_prev
  135. ! fields & I/O stuff
  136. !
  137. real, allocatable, dimension(:, :) :: dz2d, v2d, sstbias, mld, offset, z
  138. integer :: tlevel
  139. character(8) :: fieldtag
  140. character(3) :: cmem
  141. character(80) :: fname
  142. real, dimension(2, 2) :: dz_cell, v_cell
  143. real :: dz, depth, z0, z1, z01, delta
  144. integer :: field
  145. field = NONE
  146. if (nobs == 0) then
  147. return
  148. end if
  149. if (master .and. iens == 1) then
  150. if (VERT_INTERP_GRID) then
  151. print *, trim(obstag), ': vertical interpolation in grid space'
  152. else
  153. print *, trim(obstag), ': vertical interpolation in physical space'
  154. end if
  155. end if
  156. !
  157. ! 1. Identify profiles presented in "obs"
  158. !
  159. ! note that profiles are being used in the vertical superobing by each
  160. ! ensemble member...
  161. !
  162. call insitu_setprofiles(obstag, nobs, obs)
  163. allocate(ipiv(nprof))
  164. allocate(jpiv(nprof))
  165. allocate(a1(nprof))
  166. allocate(a2(nprof))
  167. allocate(a3(nprof))
  168. allocate(a4(nprof))
  169. allocate(zgrid(nprof))
  170. allocate(zgrid_prev(nprof))
  171. allocate(zcentre(nprof))
  172. allocate(zcentre_prev(nprof))
  173. allocate(v(nprof))
  174. allocate(v_prev(nprof))
  175. ipiv = obs(pstart(1 : nprof)) % ipiv
  176. jpiv = obs(pstart(1 : nprof)) % jpiv
  177. a1 = obs(pstart(1 : nprof)) % a1
  178. a2 = obs(pstart(1 : nprof)) % a2
  179. a3 = obs(pstart(1 : nprof)) % a3
  180. a4 = obs(pstart(1 : nprof)) % a4
  181. !
  182. ! 2. Map the observations for this ensemble member proceeding by layers
  183. ! to reduce I/O:
  184. !
  185. ! -cycle through layers
  186. ! -find the middle of this layer
  187. ! -cycle through profiles
  188. ! -for each obs between the middle of the prev layer and the
  189. ! middle of this layer
  190. ! -interpolate the field value
  191. ! -write to S
  192. !
  193. ! get grid dimensions
  194. !
  195. !call parse_blkdat('idm ','integer', rdummy, ni)
  196. !call parse_blkdat('jdm ','integer', rdummy, nj)
  197. !call parse_blkdat('kdm ','integer', rdummy, nk)
  198. call get_mod_xyz(ni, nj, nk) ! [CKB,FM] Changed from using m_parse_blkdat
  199. allocate(v2d(ni, nj))
  200. allocate(dz2d(ni, nj))
  201. if (trim(obstag) == 'SAL' .or. trim(obstag) == 'GSAL') then
  202. fieldtag = 'saln '
  203. field = SALINITY
  204. elseif (trim(obstag) == 'TEM' .or. trim(obstag) == 'GTEM') then
  205. fieldtag = 'temp '
  206. field = TEMPERATURE
  207. else
  208. if (master) then
  209. print *, 'ERROR: get_S(): unknown observatioon tag "', trim(obstag), '"'
  210. end if
  211. stop
  212. end if
  213. write(cmem, '(i3.3)') iens
  214. fname = 'forecast'//cmem
  215. if (field == TEMPERATURE .and. prm_prmestexists('sstb')) then
  216. allocate(sstbias(ni, nj))
  217. allocate(mld(ni, nj))
  218. allocate(offset(ni, nj))
  219. allocate(z(ni, nj))
  220. z = 0.0d0
  221. tlevel = 1
  222. call get_mod_fld_new(trim(fname), sstbias, iens, 'sstb ', 0, tlevel, ni, nj)
  223. if (tlevel == -1) then
  224. if (master) then
  225. print *, 'ERROR: get_mod_fld_new(): failed for "sstb"'
  226. end if
  227. stop
  228. end if
  229. call get_mod_fld_new(trim(fname), mld, iens, 'dpmixl ', 0, tlevel, ni, nj)
  230. if (tlevel == -1) then
  231. if (master) then
  232. print *, 'ERROR: get_mod_fld_new(): failed for "dpmixl"'
  233. end if
  234. stop
  235. end if
  236. end if
  237. ! cycle through layers
  238. !
  239. tlevel = 1
  240. do k = 1, nk + 1
  241. if (k == 1) then
  242. zgrid_prev = 0.0
  243. zcentre_prev = 0.0
  244. end if
  245. if (k <= nk) then
  246. ! read the depth and the requested field at this layer
  247. !
  248. call get_mod_fld_new(trim(fname), dz2d, iens, 'dp ', k, tlevel, ni, nj)
  249. if (tlevel == -1) then
  250. if (master) then
  251. print *, 'ERROR: get_mod_fld_new(): failed for "dp"'
  252. end if
  253. stop
  254. end if
  255. call get_mod_fld_new(trim(fname), v2d, iens, fieldtag, k, tlevel, ni, nj)
  256. if (tlevel == -1) then
  257. if (master) then
  258. print *, 'ERROR: get_mod_fld_new(): failed for "', fieldtag, '"'
  259. end if
  260. stop
  261. end if
  262. end if
  263. ! calculate correction from SST bias at this depth
  264. !
  265. if (field == TEMPERATURE .and. prm_prmestexists('sstb')) then
  266. offset = 0.0d0
  267. z = z + dz2d / 2.0 ! at the middle of the layer
  268. where (mld > 0.0d0 .and. mld < 1.0d8) ! < 10000 m
  269. offset = sstbias * exp(-(z / mld) ** 2)
  270. end where
  271. v2d = v2d - offset
  272. z = z + dz2d / 2.0
  273. end if
  274. ! cycle through profiles
  275. !
  276. do p = 1, nprof
  277. if (k <= nk) then
  278. dz_cell(:, :) = dz2d(ipiv(p) : ipiv(p) + 1, jpiv(p) : jpiv(p) + 1)
  279. dz = dz_cell(1, 1) * a1(p) + dz_cell(2, 1) * a2(p)&
  280. + dz_cell(1, 2) * a3(p) + dz_cell(2, 2) * a4(p)
  281. dz = dz / ONEMETER
  282. zgrid(p) = zgrid_prev(p) + dz
  283. zcentre(p) = (zgrid_prev(p) + zgrid(p)) / 2.0
  284. v_cell(:, :) = v2d(ipiv(p) : ipiv(p) + 1, jpiv(p) : jpiv(p) + 1)
  285. v(p) = v_cell(1, 1) * a1(p) + v_cell(2, 1) * a2(p)&
  286. + v_cell(1, 2) * a3(p) + v_cell(2, 2) * a4(p)
  287. else
  288. ! for the lower half of the last layer -- just use the layer value
  289. ! (note that there was no reading in this case, so that
  290. ! v = v_prev)
  291. zcentre(p) = zgrid(p)
  292. end if
  293. if (k == 1) then
  294. v_prev(p) = v(p)
  295. end if
  296. ! cycle through the obs, pick the ones in between the middle of the
  297. ! previous layer and the middle of this layer, interpolate the
  298. ! ensemble field to their locations, and save the results in S
  299. !
  300. z0 = zcentre_prev(p)
  301. z1 = zcentre(p)
  302. z01 = zgrid_prev(p)
  303. if (z1 == z0) then
  304. cycle
  305. end if
  306. do while (pstart(p) <= pend(p))
  307. o = pstart(p)
  308. depth = obs(o) % depth
  309. ! check that this obs is within the current layer
  310. !
  311. if (depth > z1 .and. k <= nk) then
  312. exit ! next profile
  313. elseif (depth >= z0 .and. depth <= z1) then
  314. if (.not. VERT_INTERP_GRID) then
  315. ! interpolate linearly in physical space
  316. !
  317. S(o) = (z1 - depth) / (z1 - z0) * v_prev(p) +&
  318. (depth - z0) / (z1 - z0) * v(p)
  319. else
  320. ! interpolate linearly in the grid space
  321. !
  322. if (depth < z01) then
  323. delta = 0.5d0 * (depth - z0) / (z01 - z0)
  324. else
  325. delta = 0.5d0 + 0.5d0 * (depth - z01) / (z1 - z01)
  326. end if
  327. S(o) = (1.0d0 - delta) * v_prev(p) + delta * v(p)
  328. end if
  329. ! Here we check the range of interpolated ensemble values;
  330. ! the range of observed values is checked in insitu_QC().
  331. !
  332. if (field == SALINITY) then
  333. if ((S(o) < SAL_MIN .or. S(o) > SAL_MAX) .and. master) then
  334. print *, 'WARNING: get_S(): suspicious value (SAL): ',&
  335. 'iens =', iens, ', obs =', o, ', profile = ', p,&
  336. 'depth =', depth, ', S =', S(o)
  337. end if
  338. else if (field == TEMPERATURE) then
  339. if ((S(o) < TEM_MIN .or. S(o) > TEM_MAX) .and. master) then
  340. print *, 'WARNING: get_S(): suspicious value (TEM): ',&
  341. 'iens =', iens, ', obs =', o, ', profile = ', p,&
  342. 'depth =', depth, ', S =', S(o)
  343. end if
  344. end if
  345. else ! k == nk + 1
  346. S(o) = v(p)
  347. end if
  348. ! go to the next obs
  349. !
  350. pstart(p) = pstart(p) + 1
  351. end do ! o
  352. end do ! p
  353. zgrid_prev = zgrid
  354. zcentre_prev = zcentre
  355. v_prev = v
  356. end do ! k
  357. deallocate(dz2d)
  358. deallocate(v2d)
  359. deallocate(v_prev)
  360. deallocate(v)
  361. deallocate(zcentre_prev)
  362. deallocate(zcentre)
  363. deallocate(zgrid_prev)
  364. deallocate(zgrid)
  365. deallocate(a4)
  366. deallocate(a3)
  367. deallocate(a2)
  368. deallocate(a1)
  369. deallocate(jpiv)
  370. deallocate(ipiv)
  371. if (allocated(sstbias)) then
  372. deallocate(sstbias)
  373. deallocate(mld)
  374. deallocate(offset)
  375. deallocate(z)
  376. end if
  377. end subroutine get_S
  378. end module m_Generate_element_Si