fldread.F90 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527
  1. MODULE fldread
  2. !!======================================================================
  3. !! *** MODULE fldread ***
  4. !! Ocean forcing: read input field for surface boundary condition
  5. !!=====================================================================
  6. !! History : 2.0 ! 06-2006 (S. Masson, G. Madec) Original code
  7. !! ! 05-2008 (S. Alderson) Modified for Interpolation in memory
  8. !! ! from input grid to model grid
  9. !! ! 10-2013 (D. Delrosso, P. Oddo) implement suppression of
  10. !! ! land point prior to interpolation
  11. !!----------------------------------------------------------------------
  12. !!----------------------------------------------------------------------
  13. !! fld_read : read input fields used for the computation of the
  14. !! surface boundary condition
  15. !!----------------------------------------------------------------------
  16. USE oce ! ocean dynamics and tracers
  17. USE dom_oce ! ocean space and time domain
  18. USE phycst ! ???
  19. USE in_out_manager ! I/O manager
  20. USE iom ! I/O manager library
  21. USE geo2ocean ! for vector rotation on to model grid
  22. USE lib_mpp ! MPP library
  23. USE wrk_nemo ! work arrays
  24. USE lbclnk ! ocean lateral boundary conditions (C1D case)
  25. USE ioipsl, ONLY : ymds2ju, ju2ymds ! for calendar
  26. USE sbc_oce
  27. IMPLICIT NONE
  28. PRIVATE
  29. PUBLIC fld_map ! routine called by tides_init
  30. PUBLIC fld_read, fld_fill ! called by sbc... modules
  31. PUBLIC fld_clopn
  32. TYPE, PUBLIC :: FLD_N !: Namelist field informations
  33. CHARACTER(len = 256) :: clname ! generic name of the NetCDF flux file
  34. REAL(wp) :: nfreqh ! frequency of each flux file
  35. CHARACTER(len = 34) :: clvar ! generic name of the variable in the NetCDF flux file
  36. LOGICAL :: ln_tint ! time interpolation or not (T/F)
  37. LOGICAL :: ln_clim ! climatology or not (T/F)
  38. CHARACTER(len = 8) :: cltype ! type of data file 'daily', 'monthly' or yearly'
  39. CHARACTER(len = 256) :: wname ! generic name of a NetCDF weights file to be used, blank if not
  40. CHARACTER(len = 34) :: vcomp ! symbolic component name if a vector that needs rotation
  41. ! ! a string starting with "U" or "V" for each component
  42. ! ! chars 2 onwards identify which components go together
  43. CHARACTER(len = 34) :: lname ! generic name of a NetCDF land/sea mask file to be used, blank if not
  44. ! ! 0=sea 1=land
  45. END TYPE FLD_N
  46. TYPE, PUBLIC :: FLD !: Input field related variables
  47. CHARACTER(len = 256) :: clrootname ! generic name of the NetCDF file
  48. CHARACTER(len = 256) :: clname ! current name of the NetCDF file
  49. REAL(wp) :: nfreqh ! frequency of each flux file
  50. CHARACTER(len = 34) :: clvar ! generic name of the variable in the NetCDF flux file
  51. LOGICAL :: ln_tint ! time interpolation or not (T/F)
  52. LOGICAL :: ln_clim ! climatology or not (T/F)
  53. CHARACTER(len = 8) :: cltype ! type of data file 'daily', 'monthly' or yearly'
  54. INTEGER :: num ! iom id of the jpfld files to be read
  55. INTEGER , DIMENSION(2) :: nrec_b ! before record (1: index, 2: second since Jan. 1st 00h of nit000 year)
  56. INTEGER , DIMENSION(2) :: nrec_a ! after record (1: index, 2: second since Jan. 1st 00h of nit000 year)
  57. REAL(wp) , ALLOCATABLE, DIMENSION(:,:,: ) :: fnow ! input fields interpolated to now time step
  58. REAL(wp) , ALLOCATABLE, DIMENSION(:,:,:,:) :: fdta ! 2 consecutive record of input fields
  59. CHARACTER(len = 256) :: wgtname ! current name of the NetCDF weight file acting as a key
  60. ! ! into the WGTLIST structure
  61. CHARACTER(len = 34) :: vcomp ! symbolic name for a vector component that needs rotation
  62. LOGICAL, DIMENSION(2) :: rotn ! flag to indicate whether before/after field has been rotated
  63. INTEGER :: nreclast ! last record to be read in the current file
  64. CHARACTER(len = 256) :: lsmname ! current name of the NetCDF mask file acting as a key
  65. END TYPE FLD
  66. TYPE, PUBLIC :: MAP_POINTER !: Map from input data file to local domain
  67. INTEGER, POINTER, DIMENSION(:) :: ptr ! Array of integer pointers to 1D arrays
  68. LOGICAL :: ll_unstruc ! Unstructured (T) or structured (F) boundary data file
  69. END TYPE MAP_POINTER
  70. !$AGRIF_DO_NOT_TREAT
  71. !! keep list of all weights variables so they're only read in once
  72. !! need to add AGRIF directives not to process this structure
  73. !! also need to force wgtname to include AGRIF nest number
  74. TYPE :: WGT !: Input weights related variables
  75. CHARACTER(len = 256) :: wgtname ! current name of the NetCDF weight file
  76. INTEGER , DIMENSION(2) :: ddims ! shape of input grid
  77. INTEGER , DIMENSION(2) :: botleft ! top left corner of box in input grid containing
  78. ! ! current processor grid
  79. INTEGER , DIMENSION(2) :: topright ! top right corner of box
  80. INTEGER :: jpiwgt ! width of box on input grid
  81. INTEGER :: jpjwgt ! height of box on input grid
  82. INTEGER :: numwgt ! number of weights (4=bilinear, 16=bicubic)
  83. INTEGER :: nestid ! for agrif, keep track of nest we're in
  84. INTEGER :: overlap ! =0 when cyclic grid has no overlapping EW columns
  85. ! ! =>1 when they have one or more overlapping columns
  86. ! ! =-1 not cyclic
  87. LOGICAL :: cyclic ! east-west cyclic or not
  88. INTEGER, DIMENSION(:,:,:), POINTER :: data_jpi ! array of source integers
  89. INTEGER, DIMENSION(:,:,:), POINTER :: data_jpj ! array of source integers
  90. REAL(wp), DIMENSION(:,:,:), POINTER :: data_wgt ! array of weights on model grid
  91. REAL(wp), DIMENSION(:,:,:), POINTER :: fly_dta ! array of values on input grid
  92. REAL(wp), DIMENSION(:,:,:), POINTER :: col ! temporary array for reading in columns
  93. END TYPE WGT
  94. INTEGER, PARAMETER :: tot_wgts = 10
  95. TYPE( WGT ), DIMENSION(tot_wgts) :: ref_wgts ! array of wgts
  96. INTEGER :: nxt_wgt = 1 ! point to next available space in ref_wgts array
  97. REAL(wp), PARAMETER :: undeff_lsm = -999.00_wp
  98. !$AGRIF_END_DO_NOT_TREAT
  99. !!----------------------------------------------------------------------
  100. !! NEMO/OPA 3.3 , NEMO Consortium (2010)
  101. !! $Id: fldread.F90 4784 2014-09-24 08:44:53Z jamesharle $
  102. !! Software governed by the CeCILL licence (NEMOGCM/NEMO_CeCILL.txt)
  103. !!----------------------------------------------------------------------
  104. CONTAINS
  105. SUBROUTINE fld_read( kt, kn_fsbc, sd, map, kit, kt_offset )
  106. !!---------------------------------------------------------------------
  107. !! *** ROUTINE fld_read ***
  108. !!
  109. !! ** Purpose : provide at each time step the surface ocean fluxes
  110. !! (momentum, heat, freshwater and runoff)
  111. !!
  112. !! ** Method : READ each input fields in NetCDF files using IOM
  113. !! and intepolate it to the model time-step.
  114. !! Several assumptions are made on the input file:
  115. !! blahblahblah....
  116. !!----------------------------------------------------------------------
  117. INTEGER , INTENT(in ) :: kt ! ocean time step
  118. INTEGER , INTENT(in ) :: kn_fsbc ! sbc computation period (in time step)
  119. TYPE(FLD), INTENT(inout), DIMENSION(:) :: sd ! input field related variables
  120. TYPE(MAP_POINTER),INTENT(in), OPTIONAL, DIMENSION(:) :: map ! global-to-local mapping indices
  121. INTEGER , INTENT(in ), OPTIONAL :: kit ! subcycle timestep for timesplitting option
  122. INTEGER , INTENT(in ), OPTIONAL :: kt_offset ! provide fields at time other than "now"
  123. ! kt_offset = -1 => fields at "before" time level
  124. ! kt_offset = +1 => fields at "after" time level
  125. ! etc.
  126. !!
  127. INTEGER :: itmp ! temporary variable
  128. INTEGER :: imf ! size of the structure sd
  129. INTEGER :: jf ! dummy indices
  130. INTEGER :: isecend ! number of second since Jan. 1st 00h of nit000 year at nitend
  131. INTEGER :: isecsbc ! number of seconds between Jan. 1st 00h of nit000 year and the middle of sbc time step
  132. INTEGER :: it_offset ! local time offset variable
  133. LOGICAL :: llnxtyr ! open next year file?
  134. LOGICAL :: llnxtmth ! open next month file?
  135. LOGICAL :: llstop ! stop is the file does not exist
  136. LOGICAL :: ll_firstcall ! true if this is the first call to fld_read for this set of fields
  137. REAL(wp) :: ztinta ! ratio applied to after records when doing time interpolation
  138. REAL(wp) :: ztintb ! ratio applied to before records when doing time interpolation
  139. CHARACTER(LEN=1000) :: clfmt ! write format
  140. TYPE(MAP_POINTER) :: imap ! global-to-local mapping indices
  141. !!---------------------------------------------------------------------
  142. ll_firstcall = kt == nit000
  143. IF( PRESENT(kit) ) ll_firstcall = ll_firstcall .and. kit == 1
  144. IF ( nn_components == jp_iam_sas ) THEN ; it_offset = nn_fsbc
  145. ELSE ; it_offset = 0
  146. ENDIF
  147. IF( PRESENT(kt_offset) ) it_offset = kt_offset
  148. imap%ptr => NULL()
  149. ! Note that shifting time to be centrered in the middle of sbc time step impacts only nsec_* variables of the calendar
  150. IF( present(kit) ) THEN ! ignore kn_fsbc in this case
  151. isecsbc = nsec_year + nsec1jan000 + (kit+it_offset)*NINT( rdt/REAL(nn_baro,wp) )
  152. ELSE ! middle of sbc time step
  153. isecsbc = nsec_year + nsec1jan000 + NINT(0.5 * REAL(kn_fsbc - 1,wp) * rdttra(1)) + it_offset * NINT(rdttra(1))
  154. ENDIF
  155. imf = SIZE( sd )
  156. !
  157. IF( ll_firstcall ) THEN ! initialization
  158. DO jf = 1, imf
  159. IF( PRESENT(map) ) imap = map(jf)
  160. CALL fld_init( kn_fsbc, sd(jf), imap ) ! read each before field (put them in after as they will be swapped)
  161. END DO
  162. IF( lwp ) CALL wgt_print() ! control print
  163. ENDIF
  164. ! ! ====================================== !
  165. IF( MOD( kt-1, kn_fsbc ) == 0 ) THEN ! update field at each kn_fsbc time-step !
  166. ! ! ====================================== !
  167. !
  168. DO jf = 1, imf ! --- loop over field --- !
  169. IF( isecsbc > sd(jf)%nrec_a(2) .OR. ll_firstcall ) THEN ! read/update the after data?
  170. IF( PRESENT(map) ) imap = map(jf) ! temporary definition of map
  171. sd(jf)%nrec_b(:) = sd(jf)%nrec_a(:) ! swap before record informations
  172. sd(jf)%rotn(1) = sd(jf)%rotn(2) ! swap before rotate informations
  173. IF( sd(jf)%ln_tint ) sd(jf)%fdta(:,:,:,1) = sd(jf)%fdta(:,:,:,2) ! swap before record field
  174. CALL fld_rec( kn_fsbc, sd(jf), kt_offset = it_offset, kit = kit ) ! update after record informations
  175. ! if kn_fsbc*rdttra is larger than nfreqh (which is kind of odd),
  176. ! it is possible that the before value is no more the good one... we have to re-read it
  177. ! if before is not the last record of the file currently opened and after is the first record to be read
  178. ! in a new file which means after = 1 (the file to be opened corresponds to the current time)
  179. ! or after = nreclast + 1 (the file to be opened corresponds to a future time step)
  180. IF( .NOT. ll_firstcall .AND. sd(jf)%ln_tint .AND. sd(jf)%nrec_b(1) /= sd(jf)%nreclast &
  181. & .AND. MOD( sd(jf)%nrec_a(1), sd(jf)%nreclast ) == 1 ) THEN
  182. itmp = sd(jf)%nrec_a(1) ! temporary storage
  183. sd(jf)%nrec_a(1) = sd(jf)%nreclast ! read the last record of the file currently opened
  184. CALL fld_get( sd(jf), imap ) ! read after data
  185. sd(jf)%fdta(:,:,:,1) = sd(jf)%fdta(:,:,:,2) ! re-swap before record field
  186. sd(jf)%nrec_b(1) = sd(jf)%nrec_a(1) ! update before record informations
  187. sd(jf)%nrec_b(2) = sd(jf)%nrec_a(2) - NINT( sd(jf)%nfreqh * 3600 ) ! assume freq to be in hours in this case
  188. sd(jf)%rotn(1) = sd(jf)%rotn(2) ! update before rotate informations
  189. sd(jf)%nrec_a(1) = itmp ! move back to after record
  190. ENDIF
  191. CALL fld_clopn( sd(jf) ) ! Do we need to open a new year/month/week/day file?
  192. IF( sd(jf)%ln_tint ) THEN
  193. ! if kn_fsbc*rdttra is larger than nfreqh (which is kind of odd),
  194. ! it is possible that the before value is no more the good one... we have to re-read it
  195. ! if before record is not just just before the after record...
  196. IF( .NOT. ll_firstcall .AND. MOD( sd(jf)%nrec_a(1), sd(jf)%nreclast ) /= 1 &
  197. & .AND. sd(jf)%nrec_b(1) /= sd(jf)%nrec_a(1) - 1 ) THEN
  198. sd(jf)%nrec_a(1) = sd(jf)%nrec_a(1) - 1 ! move back to before record
  199. CALL fld_get( sd(jf), imap ) ! read after data
  200. sd(jf)%fdta(:,:,:,1) = sd(jf)%fdta(:,:,:,2) ! re-swap before record field
  201. sd(jf)%nrec_b(1) = sd(jf)%nrec_a(1) ! update before record informations
  202. sd(jf)%nrec_b(2) = sd(jf)%nrec_a(2) - NINT( sd(jf)%nfreqh * 3600 ) ! assume freq to be in hours in this case
  203. sd(jf)%rotn(1) = sd(jf)%rotn(2) ! update before rotate informations
  204. sd(jf)%nrec_a(1) = sd(jf)%nrec_a(1) + 1 ! move back to after record
  205. ENDIF
  206. ! do we have to change the year/month/week/day of the forcing field??
  207. ! if we do time interpolation we will need to open next year/month/week/day file before the end of the current
  208. ! one. If so, we are still before the end of the year/month/week/day when calling fld_rec so sd(jf)%nrec_a(1)
  209. ! will be larger than the record number that should be read for current year/month/week/day
  210. ! do we need next file data?
  211. IF( sd(jf)%nrec_a(1) > sd(jf)%nreclast ) THEN
  212. sd(jf)%nrec_a(1) = sd(jf)%nrec_a(1) - sd(jf)%nreclast !
  213. IF( .NOT. ( sd(jf)%ln_clim .AND. sd(jf)%cltype == 'yearly' ) ) THEN ! close/open the current/new file
  214. llnxtmth = sd(jf)%cltype == 'monthly' .OR. nday == nmonth_len(nmonth) ! open next month file?
  215. llnxtyr = sd(jf)%cltype == 'yearly' .OR. (nmonth == 12 .AND. llnxtmth) ! open next year file?
  216. ! if the run finishes at the end of the current year/month/week/day, we will allow next
  217. ! year/month/week/day file to be not present. If the run continue further than the current
  218. ! year/month/week/day, next year/month/week/day file must exist
  219. isecend = nsec_year + nsec1jan000 + (nitend - kt) * NINT(rdttra(1)) ! second at the end of the run
  220. llstop = isecend > sd(jf)%nrec_a(2) ! read more than 1 record of next year
  221. ! we suppose that the date of next file is next day (should be ok even for weekly files...)
  222. CALL fld_clopn( sd(jf), nyear + COUNT((/llnxtyr /)) , &
  223. & nmonth + COUNT((/llnxtmth/)) - 12 * COUNT((/llnxtyr /)), &
  224. & nday + 1 - nmonth_len(nmonth) * COUNT((/llnxtmth/)), llstop )
  225. IF( sd(jf)%num <= 0 .AND. .NOT. llstop ) THEN ! next year file does not exist
  226. CALL ctl_warn('next year/month/week/day file: '//TRIM(sd(jf)%clname)// &
  227. & ' not present -> back to current year/month/day')
  228. CALL fld_clopn( sd(jf) ) ! back to the current year/month/day
  229. sd(jf)%nrec_a(1) = sd(jf)%nreclast ! force to read the last record in the current year file
  230. ENDIF
  231. ENDIF
  232. ENDIF ! open need next file?
  233. ENDIF ! temporal interpolation?
  234. ! read after data
  235. CALL fld_get( sd(jf), imap )
  236. ENDIF ! read new data?
  237. END DO ! --- end loop over field --- !
  238. CALL fld_rot( kt, sd ) ! rotate vector before/now/after fields if needed
  239. DO jf = 1, imf ! --- loop over field --- !
  240. !
  241. IF( sd(jf)%ln_tint ) THEN ! temporal interpolation
  242. IF(lwp .AND. kt - nit000 <= 100 ) THEN
  243. clfmt = "('fld_read: var ', a, ' kt = ', i8, ' (', f9.4,' days), Y/M/D = ', i4.4,'/', i2.2,'/', i2.2," // &
  244. & "', records b/a: ', i6.4, '/', i6.4, ' (days ', f9.4,'/', f9.4, ')')"
  245. WRITE(numout, clfmt) TRIM( sd(jf)%clvar ), kt, REAL(isecsbc,wp)/rday, nyear, nmonth, nday, &
  246. & sd(jf)%nrec_b(1), sd(jf)%nrec_a(1), REAL(sd(jf)%nrec_b(2),wp)/rday, REAL(sd(jf)%nrec_a(2),wp)/rday
  247. WRITE(numout, *) 'it_offset is : ',it_offset
  248. ENDIF
  249. ! temporal interpolation weights
  250. ztinta = REAL( isecsbc - sd(jf)%nrec_b(2), wp ) / REAL( sd(jf)%nrec_a(2) - sd(jf)%nrec_b(2), wp )
  251. ztintb = 1. - ztinta
  252. !CDIR COLLAPSE
  253. sd(jf)%fnow(:,:,:) = ztintb * sd(jf)%fdta(:,:,:,1) + ztinta * sd(jf)%fdta(:,:,:,2)
  254. ELSE ! nothing to do...
  255. IF(lwp .AND. kt - nit000 <= 100 ) THEN
  256. clfmt = "('fld_read: var ', a, ' kt = ', i8,' (', f9.4,' days), Y/M/D = ', i4.4,'/', i2.2,'/', i2.2," // &
  257. & "', record: ', i6.4, ' (days ', f9.4, ' <-> ', f9.4, ')')"
  258. WRITE(numout, clfmt) TRIM(sd(jf)%clvar), kt, REAL(isecsbc,wp)/rday, nyear, nmonth, nday, &
  259. & sd(jf)%nrec_a(1), REAL(sd(jf)%nrec_b(2),wp)/rday, REAL(sd(jf)%nrec_a(2),wp)/rday
  260. ENDIF
  261. ENDIF
  262. !
  263. IF( kt == nitend - kn_fsbc + 1 ) CALL iom_close( sd(jf)%num ) ! Close the input files
  264. END DO ! --- end loop over field --- !
  265. !
  266. ! ! ====================================== !
  267. ENDIF ! update field at each kn_fsbc time-step !
  268. ! ! ====================================== !
  269. !
  270. END SUBROUTINE fld_read
  271. SUBROUTINE fld_init( kn_fsbc, sdjf, map )
  272. !!---------------------------------------------------------------------
  273. !! *** ROUTINE fld_init ***
  274. !!
  275. !! ** Purpose : - first call to fld_rec to define before values
  276. !! - if time interpolation, read before data
  277. !!----------------------------------------------------------------------
  278. INTEGER , INTENT(in ) :: kn_fsbc ! sbc computation period (in time step)
  279. TYPE(FLD), INTENT(inout) :: sdjf ! input field related variables
  280. TYPE(MAP_POINTER),INTENT(in) :: map ! global-to-local mapping indices
  281. !!
  282. LOGICAL :: llprevyr ! are we reading previous year file?
  283. LOGICAL :: llprevmth ! are we reading previous month file?
  284. LOGICAL :: llprevweek ! are we reading previous week file?
  285. LOGICAL :: llprevday ! are we reading previous day file?
  286. LOGICAL :: llprev ! llprevyr .OR. llprevmth .OR. llprevweek .OR. llprevday
  287. INTEGER :: idvar ! variable id
  288. INTEGER :: inrec ! number of record existing for this variable
  289. INTEGER :: iyear, imonth, iday ! first day of the current file in yyyy mm dd
  290. INTEGER :: isec_week ! number of seconds since start of the weekly file
  291. CHARACTER(LEN=1000) :: clfmt ! write format
  292. !!---------------------------------------------------------------------
  293. llprevyr = .FALSE.
  294. llprevmth = .FALSE.
  295. llprevweek = .FALSE.
  296. llprevday = .FALSE.
  297. isec_week = 0
  298. ! define record informations
  299. CALL fld_rec( kn_fsbc, sdjf, ldbefore = .TRUE. ) ! return before values in sdjf%nrec_a (as we will swap it later)
  300. ! Note that shifting time to be centrered in the middle of sbc time step impacts only nsec_* variables of the calendar
  301. IF( sdjf%ln_tint ) THEN ! we need to read the previous record and we will put it in the current record structure
  302. IF( sdjf%nrec_a(1) == 0 ) THEN ! we redefine record sdjf%nrec_a(1) with the last record of previous year file
  303. IF ( sdjf%nfreqh == -12 ) THEN ! yearly mean
  304. IF( sdjf%cltype == 'yearly' ) THEN ! yearly file
  305. sdjf%nrec_a(1) = 1 ! force to read the unique record
  306. llprevyr = .NOT. sdjf%ln_clim ! use previous year file?
  307. ELSE
  308. CALL ctl_stop( "fld_init: yearly mean file must be in a yearly type of file: "//TRIM(sdjf%clrootname) )
  309. ENDIF
  310. ELSEIF( sdjf%nfreqh == -1 ) THEN ! monthly mean
  311. IF( sdjf%cltype == 'monthly' ) THEN ! monthly file
  312. sdjf%nrec_a(1) = 1 ! force to read the unique record
  313. llprevmth = .TRUE. ! use previous month file?
  314. llprevyr = llprevmth .AND. nmonth == 1 ! use previous year file?
  315. ELSE ! yearly file
  316. sdjf%nrec_a(1) = 12 ! force to read december mean
  317. llprevyr = .NOT. sdjf%ln_clim ! use previous year file?
  318. ENDIF
  319. ELSE ! higher frequency mean (in hours)
  320. IF ( sdjf%cltype == 'monthly' ) THEN ! monthly file
  321. sdjf%nrec_a(1) = NINT( 24 * nmonth_len(nmonth-1) / sdjf%nfreqh ) ! last record of previous month
  322. llprevmth = .TRUE. ! use previous month file?
  323. llprevyr = llprevmth .AND. nmonth == 1 ! use previous year file?
  324. ELSEIF( sdjf%cltype(1:4) == 'week' ) THEN ! weekly file
  325. llprevweek = .TRUE. ! use previous week file?
  326. sdjf%nrec_a(1) = NINT( 24 * 7 / sdjf%nfreqh ) ! last record of previous week
  327. isec_week = NINT(rday) * 7 ! add a shift toward previous week
  328. ELSEIF( sdjf%cltype == 'daily' ) THEN ! daily file
  329. sdjf%nrec_a(1) = NINT( 24 / sdjf%nfreqh ) ! last record of previous day
  330. llprevday = .TRUE. ! use previous day file?
  331. llprevmth = llprevday .AND. nday == 1 ! use previous month file?
  332. llprevyr = llprevmth .AND. nmonth == 1 ! use previous year file?
  333. ELSE ! yearly file
  334. sdjf%nrec_a(1) = NINT( 24 * nyear_len(0) / sdjf%nfreqh ) ! last record of previous year
  335. llprevyr = .NOT. sdjf%ln_clim ! use previous year file?
  336. ENDIF
  337. ENDIF
  338. ENDIF
  339. !
  340. IF ( sdjf%cltype(1:4) == 'week' ) THEN
  341. isec_week = isec_week + ksec_week( sdjf%cltype(6:8) ) ! second since the beginning of the week
  342. llprevmth = isec_week > nsec_month ! longer time since the beginning of the week than the month
  343. llprevyr = llprevmth .AND. nmonth == 1
  344. ENDIF
  345. llprev = llprevyr .OR. llprevmth .OR. llprevweek .OR. llprevday
  346. !
  347. iyear = nyear - COUNT((/llprevyr /))
  348. imonth = nmonth - COUNT((/llprevmth/)) + 12 * COUNT((/llprevyr /))
  349. iday = nday - COUNT((/llprevday/)) + nmonth_len(nmonth-1) * COUNT((/llprevmth/)) - isec_week / NINT(rday)
  350. !
  351. CALL fld_clopn( sdjf, iyear, imonth, iday, .NOT. llprev )
  352. ! if previous year/month/day file does not exist, we switch to the current year/month/day
  353. IF( llprev .AND. sdjf%num <= 0 ) THEN
  354. CALL ctl_warn( 'previous year/month/week/day file: '//TRIM(sdjf%clrootname)// &
  355. & ' not present -> back to current year/month/week/day' )
  356. ! we force to read the first record of the current year/month/day instead of last record of previous year/month/day
  357. llprev = .FALSE.
  358. sdjf%nrec_a(1) = 1
  359. CALL fld_clopn( sdjf )
  360. ENDIF
  361. IF( llprev ) THEN ! check if the record sdjf%nrec_a(1) exists in the file
  362. idvar = iom_varid( sdjf%num, sdjf%clvar ) ! id of the variable sdjf%clvar
  363. IF( idvar <= 0 ) RETURN
  364. inrec = iom_file( sdjf%num )%dimsz( iom_file( sdjf%num )%ndims(idvar), idvar ) ! size of the last dim of idvar
  365. sdjf%nrec_a(1) = MIN( sdjf%nrec_a(1), inrec ) ! make sure we select an existing record
  366. ENDIF
  367. ! read before data in after arrays(as we will swap it later)
  368. CALL fld_get( sdjf, map )
  369. clfmt = "('fld_init : time-interpolation for ', a, ' read previous record = ', i6, ' at time = ', f7.2, ' days')"
  370. IF(lwp) WRITE(numout, clfmt) TRIM(sdjf%clvar), sdjf%nrec_a(1), REAL(sdjf%nrec_a(2),wp)/rday
  371. ENDIF
  372. !
  373. END SUBROUTINE fld_init
  374. SUBROUTINE fld_rec( kn_fsbc, sdjf, ldbefore, kit, kt_offset )
  375. !!---------------------------------------------------------------------
  376. !! *** ROUTINE fld_rec ***
  377. !!
  378. !! ** Purpose : Compute
  379. !! if sdjf%ln_tint = .TRUE.
  380. !! nrec_a: record number and its time (nrec_b is obtained from nrec_a when swapping)
  381. !! if sdjf%ln_tint = .FALSE.
  382. !! nrec_a(1): record number
  383. !! nrec_b(2) and nrec_a(2): time of the beginning and end of the record (for print only)
  384. !!----------------------------------------------------------------------
  385. INTEGER , INTENT(in ) :: kn_fsbc ! sbc computation period (in time step)
  386. TYPE(FLD), INTENT(inout) :: sdjf ! input field related variables
  387. LOGICAL , INTENT(in ), OPTIONAL :: ldbefore ! sent back before record values (default = .FALSE.)
  388. INTEGER , INTENT(in ), OPTIONAL :: kit ! index of barotropic subcycle
  389. ! used only if sdjf%ln_tint = .TRUE.
  390. INTEGER , INTENT(in ), OPTIONAL :: kt_offset ! Offset of required time level compared to "now"
  391. ! time level in units of time steps.
  392. !!
  393. LOGICAL :: llbefore ! local definition of ldbefore
  394. INTEGER :: iendrec ! end of this record (in seconds)
  395. INTEGER :: imth ! month number
  396. INTEGER :: ifreq_sec ! frequency mean (in seconds)
  397. INTEGER :: isec_week ! number of seconds since the start of the weekly file
  398. INTEGER :: it_offset ! local time offset variable
  399. REAL(wp) :: ztmp ! temporary variable
  400. !!----------------------------------------------------------------------
  401. !
  402. ! Note that shifting time to be centrered in the middle of sbc time step impacts only nsec_* variables of the calendar
  403. !
  404. IF( PRESENT(ldbefore) ) THEN ; llbefore = ldbefore .AND. sdjf%ln_tint ! needed only if sdjf%ln_tint = .TRUE.
  405. ELSE ; llbefore = .FALSE.
  406. ENDIF
  407. !
  408. IF ( nn_components == jp_iam_sas ) THEN ; it_offset = nn_fsbc
  409. ELSE ; it_offset = 0
  410. ENDIF
  411. IF( PRESENT(kt_offset) ) it_offset = kt_offset
  412. IF( PRESENT(kit) ) THEN ; it_offset = ( kit + it_offset ) * NINT( rdt/REAL(nn_baro,wp) )
  413. ELSE ; it_offset = it_offset * NINT( rdttra(1) )
  414. ENDIF
  415. !
  416. ! ! =========== !
  417. IF ( sdjf%nfreqh == -12 ) THEN ! yearly mean
  418. ! ! =========== !
  419. !
  420. IF( sdjf%ln_tint ) THEN ! time interpolation, shift by 1/2 record
  421. !
  422. ! INT( ztmp )
  423. ! /|\
  424. ! 1 | *----
  425. ! 0 |----(
  426. ! |----+----|--> time
  427. ! 0 /|\ 1 (nday/nyear_len(1))
  428. ! |
  429. ! |
  430. ! forcing record : 1
  431. !
  432. ztmp = REAL( nsec_year, wp ) / ( REAL( nyear_len(1), wp ) * rday ) + 0.5 &
  433. & + REAL( it_offset, wp ) / ( REAL( nyear_len(1), wp ) * rday )
  434. sdjf%nrec_a(1) = 1 + INT( ztmp ) - COUNT((/llbefore/))
  435. ! swap at the middle of the year
  436. IF( llbefore ) THEN ; sdjf%nrec_a(2) = nsec1jan000 - (1 - INT(ztmp)) * NINT(0.5 * rday) * nyear_len(0) + &
  437. & INT(ztmp) * NINT( 0.5 * rday) * nyear_len(1)
  438. ELSE ; sdjf%nrec_a(2) = nsec1jan000 + (1 - INT(ztmp)) * NINT(0.5 * rday) * nyear_len(1) + &
  439. & INT(ztmp) * INT(rday) * nyear_len(1) + INT(ztmp) * NINT( 0.5 * rday) * nyear_len(2)
  440. ENDIF
  441. ELSE ! no time interpolation
  442. sdjf%nrec_a(1) = 1
  443. sdjf%nrec_a(2) = NINT(rday) * nyear_len(1) + nsec1jan000 ! swap at the end of the year
  444. sdjf%nrec_b(2) = nsec1jan000 ! beginning of the year (only for print)
  445. ENDIF
  446. !
  447. ! ! ============ !
  448. ELSEIF( sdjf%nfreqh == -1 ) THEN ! monthly mean !
  449. ! ! ============ !
  450. !
  451. IF( sdjf%ln_tint ) THEN ! time interpolation, shift by 1/2 record
  452. !
  453. ! INT( ztmp )
  454. ! /|\
  455. ! 1 | *----
  456. ! 0 |----(
  457. ! |----+----|--> time
  458. ! 0 /|\ 1 (nday/nmonth_len(nmonth))
  459. ! |
  460. ! |
  461. ! forcing record : nmonth
  462. !
  463. ztmp = REAL( nsec_month, wp ) / ( REAL( nmonth_len(nmonth), wp ) * rday ) + 0.5 &
  464. & + REAL( it_offset, wp ) / ( REAL( nmonth_len(nmonth), wp ) * rday )
  465. imth = nmonth + INT( ztmp ) - COUNT((/llbefore/))
  466. IF( sdjf%cltype == 'monthly' ) THEN ; sdjf%nrec_a(1) = 1 + INT( ztmp ) - COUNT((/llbefore/))
  467. ELSE ; sdjf%nrec_a(1) = imth
  468. ENDIF
  469. sdjf%nrec_a(2) = nmonth_half( imth ) + nsec1jan000 ! swap at the middle of the month
  470. ELSE ! no time interpolation
  471. IF( sdjf%cltype == 'monthly' ) THEN ; sdjf%nrec_a(1) = 1
  472. ELSE ; sdjf%nrec_a(1) = nmonth
  473. ENDIF
  474. sdjf%nrec_a(2) = nmonth_end(nmonth ) + nsec1jan000 ! swap at the end of the month
  475. sdjf%nrec_b(2) = nmonth_end(nmonth-1) + nsec1jan000 ! beginning of the month (only for print)
  476. ENDIF
  477. !
  478. ! ! ================================ !
  479. ELSE ! higher frequency mean (in hours)
  480. ! ! ================================ !
  481. !
  482. ifreq_sec = NINT( sdjf%nfreqh * 3600 ) ! frequency mean (in seconds)
  483. IF( sdjf%cltype(1:4) == 'week' ) isec_week = ksec_week( sdjf%cltype(6:8) ) ! since the first day of the current week
  484. ! number of second since the beginning of the file
  485. IF( sdjf%cltype == 'monthly' ) THEN ; ztmp = REAL(nsec_month,wp) ! since the first day of the current month
  486. ELSEIF( sdjf%cltype(1:4) == 'week' ) THEN ; ztmp = REAL(isec_week ,wp) ! since the first day of the current week
  487. ELSEIF( sdjf%cltype == 'daily' ) THEN ; ztmp = REAL(nsec_day ,wp) ! since 00h of the current day
  488. ELSE ; ztmp = REAL(nsec_year ,wp) ! since 00h on Jan 1 of the current year
  489. ENDIF
  490. ztmp = ztmp + 0.5 * REAL(kn_fsbc - 1, wp) * rdttra(1) + REAL( it_offset, wp ) ! centrered in the middle of sbc time step
  491. ztmp = ztmp + 0.01 * rdttra(1) ! avoid truncation error
  492. IF( sdjf%ln_tint ) THEN ! time interpolation, shift by 1/2 record
  493. !
  494. ! INT( ztmp/ifreq_sec + 0.5 )
  495. ! /|\
  496. ! 2 | *-----(
  497. ! 1 | *-----(
  498. ! 0 |--(
  499. ! |--+--|--+--|--+--|--> time
  500. ! 0 /|\ 1 /|\ 2 /|\ 3 (ztmp/ifreq_sec)
  501. ! | | |
  502. ! | | |
  503. ! forcing record : 1 2 3
  504. !
  505. ztmp= ztmp / REAL(ifreq_sec, wp) + 0.5
  506. ELSE ! no time interpolation
  507. !
  508. ! INT( ztmp/ifreq_sec )
  509. ! /|\
  510. ! 2 | *-----(
  511. ! 1 | *-----(
  512. ! 0 |-----(
  513. ! |--+--|--+--|--+--|--> time
  514. ! 0 /|\ 1 /|\ 2 /|\ 3 (ztmp/ifreq_sec)
  515. ! | | |
  516. ! | | |
  517. ! forcing record : 1 2 3
  518. !
  519. ztmp= ztmp / REAL(ifreq_sec, wp)
  520. ENDIF
  521. sdjf%nrec_a(1) = 1 + INT( ztmp ) - COUNT((/llbefore/)) ! record number to be read
  522. iendrec = ifreq_sec * sdjf%nrec_a(1) + nsec1jan000 ! end of this record (in second)
  523. ! add the number of seconds between 00h Jan 1 and the end of previous month/week/day (ok if nmonth=1)
  524. IF( sdjf%cltype == 'monthly' ) iendrec = iendrec + NINT(rday) * SUM(nmonth_len(1:nmonth -1))
  525. IF( sdjf%cltype(1:4) == 'week' ) iendrec = iendrec + ( nsec_year - isec_week )
  526. IF( sdjf%cltype == 'daily' ) iendrec = iendrec + NINT(rday) * ( nday_year - 1 )
  527. IF( sdjf%ln_tint ) THEN
  528. sdjf%nrec_a(2) = iendrec - ifreq_sec / 2 ! swap at the middle of the record
  529. ELSE
  530. sdjf%nrec_a(2) = iendrec ! swap at the end of the record
  531. sdjf%nrec_b(2) = iendrec - ifreq_sec ! beginning of the record (only for print)
  532. ENDIF
  533. !
  534. ENDIF
  535. !
  536. END SUBROUTINE fld_rec
  537. SUBROUTINE fld_get( sdjf, map )
  538. !!---------------------------------------------------------------------
  539. !! *** ROUTINE fld_get ***
  540. !!
  541. !! ** Purpose : read the data
  542. !!----------------------------------------------------------------------
  543. TYPE(FLD), INTENT(inout) :: sdjf ! input field related variables
  544. TYPE(MAP_POINTER),INTENT(in) :: map ! global-to-local mapping indices
  545. !!
  546. INTEGER :: ipk ! number of vertical levels of sdjf%fdta ( 2D: ipk=1 ; 3D: ipk=jpk )
  547. INTEGER :: iw ! index into wgts array
  548. INTEGER :: ipdom ! index of the domain
  549. INTEGER :: idvar ! variable ID
  550. INTEGER :: idmspc ! number of spatial dimensions
  551. LOGICAL :: lmoor ! C1D case: point data
  552. !!---------------------------------------------------------------------
  553. !
  554. ipk = SIZE( sdjf%fnow, 3 )
  555. !
  556. IF( ASSOCIATED(map%ptr) ) THEN
  557. IF( sdjf%ln_tint ) THEN ; CALL fld_map( sdjf%num, sdjf%clvar, sdjf%fdta(:,:,:,2), sdjf%nrec_a(1), map )
  558. ELSE ; CALL fld_map( sdjf%num, sdjf%clvar, sdjf%fnow(:,:,: ), sdjf%nrec_a(1), map )
  559. ENDIF
  560. ELSE IF( LEN(TRIM(sdjf%wgtname)) > 0 ) THEN
  561. CALL wgt_list( sdjf, iw )
  562. IF( sdjf%ln_tint ) THEN ; CALL fld_interp( sdjf%num, sdjf%clvar, iw , ipk , sdjf%fdta(:,:,:,2), &
  563. & sdjf%nrec_a(1), sdjf%lsmname )
  564. ELSE ; CALL fld_interp( sdjf%num, sdjf%clvar, iw , ipk , sdjf%fnow(:,:,: ), &
  565. & sdjf%nrec_a(1), sdjf%lsmname )
  566. ENDIF
  567. ELSE
  568. IF( SIZE(sdjf%fnow, 1) == jpi ) THEN ; ipdom = jpdom_data
  569. ELSE ; ipdom = jpdom_unknown
  570. ENDIF
  571. ! C1D case: If product of spatial dimensions == ipk, then x,y are of
  572. ! size 1 (point/mooring data): this must be read onto the central grid point
  573. idvar = iom_varid( sdjf%num, sdjf%clvar )
  574. idmspc = iom_file( sdjf%num )%ndims( idvar )
  575. IF( iom_file( sdjf%num )%luld( idvar ) ) idmspc = idmspc - 1
  576. lmoor = (idmspc == 0 .OR. PRODUCT( iom_file( sdjf%num )%dimsz( 1:MAX(idmspc,1) ,idvar ) ) == ipk)
  577. !
  578. SELECT CASE( ipk )
  579. CASE(1)
  580. IF( lk_c1d .AND. lmoor ) THEN
  581. IF( sdjf%ln_tint ) THEN
  582. CALL iom_get( sdjf%num, sdjf%clvar, sdjf%fdta(2,2,1,2), sdjf%nrec_a(1) )
  583. CALL lbc_lnk( sdjf%fdta(:,:,1,2),'Z',1. )
  584. ELSE
  585. CALL iom_get( sdjf%num, sdjf%clvar, sdjf%fnow(2,2,1 ), sdjf%nrec_a(1) )
  586. CALL lbc_lnk( sdjf%fnow(:,:,1 ),'Z',1. )
  587. ENDIF
  588. ELSE
  589. IF( sdjf%ln_tint ) THEN ; CALL iom_get( sdjf%num, ipdom, sdjf%clvar, sdjf%fdta(:,:,1,2), sdjf%nrec_a(1) )
  590. ELSE ; CALL iom_get( sdjf%num, ipdom, sdjf%clvar, sdjf%fnow(:,:,1 ), sdjf%nrec_a(1) )
  591. ENDIF
  592. ENDIF
  593. CASE DEFAULT
  594. IF (lk_c1d .AND. lmoor ) THEN
  595. IF( sdjf%ln_tint ) THEN
  596. CALL iom_get( sdjf%num, jpdom_unknown, sdjf%clvar, sdjf%fdta(2,2,:,2), sdjf%nrec_a(1) )
  597. CALL lbc_lnk( sdjf%fdta(:,:,:,2),'Z',1. )
  598. ELSE
  599. CALL iom_get( sdjf%num, jpdom_unknown, sdjf%clvar, sdjf%fnow(2,2,: ), sdjf%nrec_a(1) )
  600. CALL lbc_lnk( sdjf%fnow(:,:,: ),'Z',1. )
  601. ENDIF
  602. ELSE
  603. IF( sdjf%ln_tint ) THEN ; CALL iom_get( sdjf%num, ipdom, sdjf%clvar, sdjf%fdta(:,:,:,2), sdjf%nrec_a(1) )
  604. ELSE ; CALL iom_get( sdjf%num, ipdom, sdjf%clvar, sdjf%fnow(:,:,: ), sdjf%nrec_a(1) )
  605. ENDIF
  606. ENDIF
  607. END SELECT
  608. ENDIF
  609. !
  610. sdjf%rotn(2) = .false. ! vector not yet rotated
  611. END SUBROUTINE fld_get
  612. SUBROUTINE fld_map( num, clvar, dta, nrec, map )
  613. !!---------------------------------------------------------------------
  614. !! *** ROUTINE fld_map ***
  615. !!
  616. !! ** Purpose : read global data from file and map onto local data
  617. !! using a general mapping (for open boundaries)
  618. !!----------------------------------------------------------------------
  619. #if defined key_bdy
  620. USE bdy_oce, ONLY: dta_global, dta_global2 ! workspace to read in global data arrays
  621. #endif
  622. INTEGER , INTENT(in ) :: num ! stream number
  623. CHARACTER(LEN=*) , INTENT(in ) :: clvar ! variable name
  624. REAL(wp), DIMENSION(:,:,:), INTENT(out) :: dta ! output field on model grid (2 dimensional)
  625. INTEGER , INTENT(in ) :: nrec ! record number to read (ie time slice)
  626. TYPE(MAP_POINTER) , INTENT(in ) :: map ! global-to-local mapping indices
  627. !!
  628. INTEGER :: ipi ! length of boundary data on local process
  629. INTEGER :: ipj ! length of dummy dimension ( = 1 )
  630. INTEGER :: ipk ! number of vertical levels of dta ( 2D: ipk=1 ; 3D: ipk=jpk )
  631. INTEGER :: ilendta ! length of data in file
  632. INTEGER :: idvar ! variable ID
  633. INTEGER :: ib, ik, ji, jj ! loop counters
  634. INTEGER :: ierr
  635. REAL(wp), POINTER, DIMENSION(:,:,:) :: dta_read ! work space for global data
  636. !!---------------------------------------------------------------------
  637. ipi = SIZE( dta, 1 )
  638. ipj = 1
  639. ipk = SIZE( dta, 3 )
  640. idvar = iom_varid( num, clvar )
  641. ilendta = iom_file(num)%dimsz(1,idvar)
  642. #if defined key_bdy
  643. ipj = iom_file(num)%dimsz(2,idvar)
  644. IF ( map%ll_unstruc) THEN ! unstructured open boundary data file
  645. dta_read => dta_global
  646. ELSE ! structured open boundary data file
  647. dta_read => dta_global2
  648. ENDIF
  649. #endif
  650. IF(lwp) WRITE(numout,*) 'Dim size for ',TRIM(clvar),' is ', ilendta
  651. IF(lwp) WRITE(numout,*) 'Number of levels for ',TRIM(clvar),' is ', ipk
  652. SELECT CASE( ipk )
  653. CASE(1) ; CALL iom_get ( num, jpdom_unknown, clvar, dta_read(1:ilendta,1:ipj,1 ), nrec )
  654. CASE DEFAULT ; CALL iom_get ( num, jpdom_unknown, clvar, dta_read(1:ilendta,1:ipj,1:ipk), nrec )
  655. END SELECT
  656. !
  657. IF ( map%ll_unstruc ) THEN ! unstructured open boundary data file
  658. DO ib = 1, ipi
  659. DO ik = 1, ipk
  660. dta(ib,1,ik) = dta_read(map%ptr(ib),1,ik)
  661. END DO
  662. END DO
  663. ELSE ! structured open boundary data file
  664. DO ib = 1, ipi
  665. jj=1+floor(REAL(map%ptr(ib)-1)/REAL(ilendta))
  666. ji=map%ptr(ib)-(jj-1)*ilendta
  667. DO ik = 1, ipk
  668. dta(ib,1,ik) = dta_read(ji,jj,ik)
  669. END DO
  670. END DO
  671. ENDIF
  672. END SUBROUTINE fld_map
  673. SUBROUTINE fld_rot( kt, sd )
  674. !!---------------------------------------------------------------------
  675. !! *** ROUTINE fld_rot ***
  676. !!
  677. !! ** Purpose : Vector fields may need to be rotated onto the local grid direction
  678. !!----------------------------------------------------------------------
  679. INTEGER , INTENT(in ) :: kt ! ocean time step
  680. TYPE(FLD), INTENT(inout), DIMENSION(:) :: sd ! input field related variables
  681. !!
  682. INTEGER :: ju,jv,jk,jn ! loop indices
  683. INTEGER :: imf ! size of the structure sd
  684. INTEGER :: ill ! character length
  685. INTEGER :: iv ! indice of V component
  686. REAL(wp), POINTER, DIMENSION(:,:) :: utmp, vtmp ! temporary arrays for vector rotation
  687. CHARACTER (LEN=100) :: clcomp ! dummy weight name
  688. !!---------------------------------------------------------------------
  689. CALL wrk_alloc( jpi,jpj, utmp, vtmp )
  690. !! (sga: following code should be modified so that pairs arent searched for each time
  691. !
  692. imf = SIZE( sd )
  693. DO ju = 1, imf
  694. ill = LEN_TRIM( sd(ju)%vcomp )
  695. DO jn = 2-COUNT((/sd(ju)%ln_tint/)), 2
  696. IF( ill > 0 .AND. .NOT. sd(ju)%rotn(jn) ) THEN ! find vector rotations required
  697. IF( sd(ju)%vcomp(1:1) == 'U' ) THEN ! east-west component has symbolic name starting with 'U'
  698. ! look for the north-south component which has same symbolic name but with 'U' replaced with 'V'
  699. clcomp = 'V' // sd(ju)%vcomp(2:ill) ! works even if ill == 1
  700. iv = -1
  701. DO jv = 1, imf
  702. IF( TRIM(sd(jv)%vcomp) == TRIM(clcomp) ) iv = jv
  703. END DO
  704. IF( iv > 0 ) THEN ! fields ju and iv are two components which need to be rotated together
  705. DO jk = 1, SIZE( sd(ju)%fnow, 3 )
  706. IF( sd(ju)%ln_tint )THEN
  707. CALL rot_rep( sd(ju)%fdta(:,:,jk,jn), sd(iv)%fdta(:,:,jk,jn), 'T', 'en->i', utmp(:,:) )
  708. CALL rot_rep( sd(ju)%fdta(:,:,jk,jn), sd(iv)%fdta(:,:,jk,jn), 'T', 'en->j', vtmp(:,:) )
  709. sd(ju)%fdta(:,:,jk,jn) = utmp(:,:) ; sd(iv)%fdta(:,:,jk,jn) = vtmp(:,:)
  710. ELSE
  711. CALL rot_rep( sd(ju)%fnow(:,:,jk ), sd(iv)%fnow(:,:,jk ), 'T', 'en->i', utmp(:,:) )
  712. CALL rot_rep( sd(ju)%fnow(:,:,jk ), sd(iv)%fnow(:,:,jk ), 'T', 'en->j', vtmp(:,:) )
  713. sd(ju)%fnow(:,:,jk ) = utmp(:,:) ; sd(iv)%fnow(:,:,jk ) = vtmp(:,:)
  714. ENDIF
  715. END DO
  716. sd(ju)%rotn(jn) = .TRUE. ! vector was rotated
  717. IF( lwp .AND. kt == nit000 ) WRITE(numout,*) &
  718. & 'fld_read: vector pair ('//TRIM(sd(ju)%clvar)//', '//TRIM(sd(iv)%clvar)//') rotated on to model grid'
  719. ENDIF
  720. ENDIF
  721. ENDIF
  722. END DO
  723. END DO
  724. !
  725. CALL wrk_dealloc( jpi,jpj, utmp, vtmp )
  726. !
  727. END SUBROUTINE fld_rot
  728. SUBROUTINE fld_clopn( sdjf, kyear, kmonth, kday, ldstop )
  729. !!---------------------------------------------------------------------
  730. !! *** ROUTINE fld_clopn ***
  731. !!
  732. !! ** Purpose : update the file name and open the file
  733. !!----------------------------------------------------------------------
  734. TYPE(FLD) , INTENT(inout) :: sdjf ! input field related variables
  735. INTEGER, OPTIONAL, INTENT(in ) :: kyear ! year value
  736. INTEGER, OPTIONAL, INTENT(in ) :: kmonth ! month value
  737. INTEGER, OPTIONAL, INTENT(in ) :: kday ! day value
  738. LOGICAL, OPTIONAL, INTENT(in ) :: ldstop ! stop if open to read a non-existing file (default = .TRUE.)
  739. !!
  740. LOGICAL :: llprevyr ! are we reading previous year file?
  741. LOGICAL :: llprevmth ! are we reading previous month file?
  742. INTEGER :: iyear, imonth, iday ! first day of the current file in yyyy mm dd
  743. INTEGER :: isec_week ! number of seconds since start of the weekly file
  744. INTEGER :: indexyr ! year undex (O/1/2: previous/current/next)
  745. INTEGER :: iyear_len, imonth_len ! length (days) of iyear and imonth !
  746. CHARACTER(len = 256):: clname ! temporary file name
  747. !!----------------------------------------------------------------------
  748. IF( PRESENT(kyear) ) THEN ! use given values
  749. iyear = kyear
  750. imonth = kmonth
  751. iday = kday
  752. IF ( sdjf%cltype(1:4) == 'week' ) THEN ! find the day of the beginning of the week
  753. isec_week = ksec_week( sdjf%cltype(6:8) )- (86400 * 8 )
  754. llprevmth = isec_week > nsec_month ! longer time since beginning of the week than the month
  755. llprevyr = llprevmth .AND. nmonth == 1
  756. iyear = nyear - COUNT((/llprevyr /))
  757. imonth = nmonth - COUNT((/llprevmth/)) + 12 * COUNT((/llprevyr /))
  758. iday = nday + nmonth_len(nmonth-1) * COUNT((/llprevmth/)) - isec_week / NINT(rday)
  759. ENDIF
  760. ELSE ! use current day values
  761. IF ( sdjf%cltype(1:4) == 'week' ) THEN ! find the day of the beginning of the week
  762. isec_week = ksec_week( sdjf%cltype(6:8) ) ! second since the beginning of the week
  763. llprevmth = isec_week > nsec_month ! longer time since beginning of the week than the month
  764. llprevyr = llprevmth .AND. nmonth == 1
  765. ELSE
  766. isec_week = 0
  767. llprevmth = .FALSE.
  768. llprevyr = .FALSE.
  769. ENDIF
  770. iyear = nyear - COUNT((/llprevyr /))
  771. imonth = nmonth - COUNT((/llprevmth/)) + 12 * COUNT((/llprevyr /))
  772. iday = nday + nmonth_len(nmonth-1) * COUNT((/llprevmth/)) - isec_week / NINT(rday)
  773. ENDIF
  774. ! build the new filename if not climatological data
  775. clname=TRIM(sdjf%clrootname)
  776. !
  777. ! note that sdjf%ln_clim is is only acting on the presence of the year in the file name
  778. IF( .NOT. sdjf%ln_clim ) THEN
  779. WRITE(clname, '(a,"_y",i4.4)' ) TRIM( sdjf%clrootname ), iyear ! add year
  780. IF( sdjf%cltype /= 'yearly' ) WRITE(clname, '(a,"m" ,i2.2)' ) TRIM( clname ), imonth ! add month
  781. ELSE
  782. ! build the new filename if climatological data
  783. IF( sdjf%cltype /= 'yearly' ) WRITE(clname, '(a,"_m",i2.2)' ) TRIM( sdjf%clrootname ), imonth ! add month
  784. ENDIF
  785. IF( sdjf%cltype == 'daily' .OR. sdjf%cltype(1:4) == 'week' ) &
  786. & WRITE(clname, '(a,"d" ,i2.2)' ) TRIM( clname ), iday ! add day
  787. !
  788. IF( TRIM(clname) /= TRIM(sdjf%clname) .OR. sdjf%num == 0 ) THEN ! new file to be open
  789. sdjf%clname = TRIM(clname)
  790. IF( sdjf%num /= 0 ) CALL iom_close( sdjf%num ) ! close file if already open
  791. CALL iom_open( sdjf%clname, sdjf%num, ldstop = ldstop, ldiof = LEN(TRIM(sdjf%wgtname)) > 0 )
  792. ! find the last record to be read -> update sdjf%nreclast
  793. indexyr = iyear - nyear + 1
  794. iyear_len = nyear_len( indexyr )
  795. SELECT CASE ( indexyr )
  796. CASE ( 0 ) ; imonth_len = 31 ! previous year -> imonth = 12
  797. CASE ( 1 ) ; imonth_len = nmonth_len(imonth)
  798. CASE ( 2 ) ; imonth_len = 31 ! next year -> imonth = 1
  799. END SELECT
  800. ! last record to be read in the current file
  801. IF ( sdjf%nfreqh == -12 ) THEN ; sdjf%nreclast = 1 ! yearly mean
  802. ELSEIF( sdjf%nfreqh == -1 ) THEN ! monthly mean
  803. IF( sdjf%cltype == 'monthly' ) THEN ; sdjf%nreclast = 1
  804. ELSE ; sdjf%nreclast = 12
  805. ENDIF
  806. ELSE ! higher frequency mean (in hours)
  807. IF( sdjf%cltype == 'monthly' ) THEN ; sdjf%nreclast = NINT( 24 * imonth_len / sdjf%nfreqh )
  808. ELSEIF( sdjf%cltype(1:4) == 'week' ) THEN ; sdjf%nreclast = NINT( 24 * 7 / sdjf%nfreqh )
  809. ELSEIF( sdjf%cltype == 'daily' ) THEN ; sdjf%nreclast = NINT( 24 / sdjf%nfreqh )
  810. ELSE ; sdjf%nreclast = NINT( 24 * iyear_len / sdjf%nfreqh )
  811. ENDIF
  812. ENDIF
  813. ENDIF
  814. !
  815. END SUBROUTINE fld_clopn
  816. SUBROUTINE fld_fill( sdf, sdf_n, cdir, cdcaller, cdtitle, cdnam )
  817. !!---------------------------------------------------------------------
  818. !! *** ROUTINE fld_fill ***
  819. !!
  820. !! ** Purpose : fill sdf with sdf_n and control print
  821. !!----------------------------------------------------------------------
  822. TYPE(FLD) , DIMENSION(:), INTENT(inout) :: sdf ! structure of input fields (file informations, fields read)
  823. TYPE(FLD_N), DIMENSION(:), INTENT(in ) :: sdf_n ! array of namelist information structures
  824. CHARACTER(len=*) , INTENT(in ) :: cdir ! Root directory for location of flx files
  825. CHARACTER(len=*) , INTENT(in ) :: cdcaller !
  826. CHARACTER(len=*) , INTENT(in ) :: cdtitle !
  827. CHARACTER(len=*) , INTENT(in ) :: cdnam !
  828. !
  829. INTEGER :: jf ! dummy indices
  830. !!---------------------------------------------------------------------
  831. DO jf = 1, SIZE(sdf)
  832. sdf(jf)%clrootname = TRIM( cdir )//TRIM( sdf_n(jf)%clname )
  833. sdf(jf)%clname = "not yet defined"
  834. sdf(jf)%nfreqh = sdf_n(jf)%nfreqh
  835. sdf(jf)%clvar = sdf_n(jf)%clvar
  836. sdf(jf)%ln_tint = sdf_n(jf)%ln_tint
  837. sdf(jf)%ln_clim = sdf_n(jf)%ln_clim
  838. sdf(jf)%cltype = sdf_n(jf)%cltype
  839. sdf(jf)%num = -1
  840. sdf(jf)%wgtname = " "
  841. IF( LEN( TRIM(sdf_n(jf)%wname) ) > 0 ) sdf(jf)%wgtname = TRIM( cdir )//TRIM( sdf_n(jf)%wname )
  842. sdf(jf)%lsmname = " "
  843. IF( LEN( TRIM(sdf_n(jf)%lname) ) > 0 ) sdf(jf)%lsmname = TRIM( cdir )//TRIM( sdf_n(jf)%lname )
  844. sdf(jf)%vcomp = sdf_n(jf)%vcomp
  845. sdf(jf)%rotn(:) = .TRUE. ! pretend to be rotated -> won't try to rotate data before the first call to fld_get
  846. IF( sdf(jf)%cltype(1:4) == 'week' .AND. nn_leapy == 0 ) &
  847. & CALL ctl_stop('fld_clopn: weekly file ('//TRIM(sdf(jf)%clrootname)//') needs nn_leapy = 1')
  848. IF( sdf(jf)%cltype(1:4) == 'week' .AND. sdf(jf)%ln_clim ) &
  849. & CALL ctl_stop('fld_clopn: weekly file ('//TRIM(sdf(jf)%clrootname)//') needs ln_clim = .FALSE.')
  850. sdf(jf)%nreclast = -1 ! Set to non zero default value to avoid errors, is updated to meaningful value during fld_clopn
  851. END DO
  852. IF(lwp) THEN ! control print
  853. WRITE(numout,*)
  854. WRITE(numout,*) TRIM( cdcaller )//' : '//TRIM( cdtitle )
  855. WRITE(numout,*) (/ ('~', jf = 1, LEN_TRIM( cdcaller ) ) /)
  856. WRITE(numout,*) ' '//TRIM( cdnam )//' Namelist'
  857. WRITE(numout,*) ' list of files and frequency (>0: in hours ; <0 in months)'
  858. DO jf = 1, SIZE(sdf)
  859. WRITE(numout,*) ' root filename: ' , TRIM( sdf(jf)%clrootname ), &
  860. & ' variable name: ' , TRIM( sdf(jf)%clvar )
  861. WRITE(numout,*) ' frequency: ' , sdf(jf)%nfreqh , &
  862. & ' time interp: ' , sdf(jf)%ln_tint , &
  863. & ' climatology: ' , sdf(jf)%ln_clim , &
  864. & ' weights : ' , TRIM( sdf(jf)%wgtname ), &
  865. & ' pairing : ' , TRIM( sdf(jf)%vcomp ), &
  866. & ' data type: ' , sdf(jf)%cltype , &
  867. & ' land/sea mask:' , TRIM( sdf(jf)%lsmname )
  868. call flush(numout)
  869. END DO
  870. ENDIF
  871. END SUBROUTINE fld_fill
  872. SUBROUTINE wgt_list( sd, kwgt )
  873. !!---------------------------------------------------------------------
  874. !! *** ROUTINE wgt_list ***
  875. !!
  876. !! ** Purpose : search array of WGTs and find a weights file
  877. !! entry, or return a new one adding it to the end
  878. !! if it is a new entry, the weights data is read in and
  879. !! restructured (fld_weight)
  880. !!----------------------------------------------------------------------
  881. TYPE( FLD ), INTENT(in ) :: sd ! field with name of weights file
  882. INTEGER , INTENT(inout) :: kwgt ! index of weights
  883. !!
  884. INTEGER :: kw, nestid ! local integer
  885. LOGICAL :: found ! local logical
  886. !!----------------------------------------------------------------------
  887. !
  888. !! search down linked list
  889. !! weights filename is either present or we hit the end of the list
  890. found = .FALSE.
  891. !! because agrif nest part of filenames are now added in iom_open
  892. !! to distinguish between weights files on the different grids, need to track
  893. !! nest number explicitly
  894. nestid = 0
  895. #if defined key_agrif
  896. nestid = Agrif_Fixed()
  897. #endif
  898. DO kw = 1, nxt_wgt-1
  899. IF( TRIM(ref_wgts(kw)%wgtname) == TRIM(sd%wgtname) .AND. &
  900. ref_wgts(kw)%nestid == nestid) THEN
  901. kwgt = kw
  902. found = .TRUE.
  903. EXIT
  904. ENDIF
  905. END DO
  906. IF( .NOT.found ) THEN
  907. kwgt = nxt_wgt
  908. CALL fld_weight( sd )
  909. ENDIF
  910. !
  911. END SUBROUTINE wgt_list
  912. SUBROUTINE wgt_print( )
  913. !!---------------------------------------------------------------------
  914. !! *** ROUTINE wgt_print ***
  915. !!
  916. !! ** Purpose : print the list of known weights
  917. !!----------------------------------------------------------------------
  918. INTEGER :: kw !
  919. !!----------------------------------------------------------------------
  920. !
  921. DO kw = 1, nxt_wgt-1
  922. WRITE(numout,*) 'weight file: ',TRIM(ref_wgts(kw)%wgtname)
  923. WRITE(numout,*) ' ddims: ',ref_wgts(kw)%ddims(1),ref_wgts(kw)%ddims(2)
  924. WRITE(numout,*) ' numwgt: ',ref_wgts(kw)%numwgt
  925. WRITE(numout,*) ' jpiwgt: ',ref_wgts(kw)%jpiwgt
  926. WRITE(numout,*) ' jpjwgt: ',ref_wgts(kw)%jpjwgt
  927. WRITE(numout,*) ' botleft: ',ref_wgts(kw)%botleft
  928. WRITE(numout,*) ' topright: ',ref_wgts(kw)%topright
  929. IF( ref_wgts(kw)%cyclic ) THEN
  930. WRITE(numout,*) ' cyclical'
  931. IF( ref_wgts(kw)%overlap > 0 ) WRITE(numout,*) ' with overlap of ', ref_wgts(kw)%overlap
  932. ELSE
  933. WRITE(numout,*) ' not cyclical'
  934. ENDIF
  935. IF( ASSOCIATED(ref_wgts(kw)%data_wgt) ) WRITE(numout,*) ' allocated'
  936. END DO
  937. !
  938. END SUBROUTINE wgt_print
  939. SUBROUTINE fld_weight( sd )
  940. !!---------------------------------------------------------------------
  941. !! *** ROUTINE fld_weight ***
  942. !!
  943. !! ** Purpose : create a new WGT structure and fill in data from
  944. !! file, restructuring as required
  945. !!----------------------------------------------------------------------
  946. TYPE( FLD ), INTENT(in) :: sd ! field with name of weights file
  947. !!
  948. INTEGER :: jn ! dummy loop indices
  949. INTEGER :: inum ! temporary logical unit
  950. INTEGER :: id ! temporary variable id
  951. INTEGER :: ipk ! temporary vertical dimension
  952. CHARACTER (len=5) :: aname
  953. INTEGER , DIMENSION(:), ALLOCATABLE :: ddims
  954. INTEGER , POINTER, DIMENSION(:,:) :: data_src
  955. REAL(wp), POINTER, DIMENSION(:,:) :: data_tmp
  956. LOGICAL :: cyclical
  957. INTEGER :: zwrap ! local integer
  958. !!----------------------------------------------------------------------
  959. !
  960. CALL wrk_alloc( jpi,jpj, data_src ) ! integer
  961. CALL wrk_alloc( jpi,jpj, data_tmp )
  962. !
  963. IF( nxt_wgt > tot_wgts ) THEN
  964. CALL ctl_stop("fld_weight: weights array size exceeded, increase tot_wgts")
  965. ENDIF
  966. !
  967. !! new weights file entry, add in extra information
  968. !! a weights file represents a 2D grid of a certain shape, so we assume that the current
  969. !! input data file is representative of all other files to be opened and processed with the
  970. !! current weights file
  971. !! open input data file (non-model grid)
  972. CALL iom_open( sd%clname, inum, ldiof = LEN(TRIM(sd%wgtname)) > 0 )
  973. !! get dimensions
  974. IF ( SIZE(sd%fnow, 3) > 1 ) THEN
  975. ALLOCATE( ddims(4) )
  976. ELSE
  977. ALLOCATE( ddims(3) )
  978. ENDIF
  979. id = iom_varid( inum, sd%clvar, ddims )
  980. !! close it
  981. CALL iom_close( inum )
  982. !! now open the weights file
  983. CALL iom_open ( sd%wgtname, inum ) ! interpolation weights
  984. IF ( inum > 0 ) THEN
  985. !! determine whether we have an east-west cyclic grid
  986. !! from global attribute called "ew_wrap" in the weights file
  987. !! note that if not found, iom_getatt returns -999 and cyclic with no overlap is assumed
  988. !! since this is the most common forcing configuration
  989. CALL iom_getatt(inum, 'ew_wrap', zwrap)
  990. IF( zwrap >= 0 ) THEN
  991. cyclical = .TRUE.
  992. ELSE IF( zwrap == -999 ) THEN
  993. cyclical = .TRUE.
  994. zwrap = 0
  995. ELSE
  996. cyclical = .FALSE.
  997. ENDIF
  998. ref_wgts(nxt_wgt)%ddims(1) = ddims(1)
  999. ref_wgts(nxt_wgt)%ddims(2) = ddims(2)
  1000. ref_wgts(nxt_wgt)%wgtname = sd%wgtname
  1001. ref_wgts(nxt_wgt)%overlap = zwrap
  1002. ref_wgts(nxt_wgt)%cyclic = cyclical
  1003. ref_wgts(nxt_wgt)%nestid = 0
  1004. #if defined key_agrif
  1005. ref_wgts(nxt_wgt)%nestid = Agrif_Fixed()
  1006. #endif
  1007. !! weights file is stored as a set of weights (wgt01->wgt04 or wgt01->wgt16)
  1008. !! for each weight wgtNN there is an integer array srcNN which gives the point in
  1009. !! the input data grid which is to be multiplied by the weight
  1010. !! they are both arrays on the model grid so the result of the multiplication is
  1011. !! added into an output array on the model grid as a running sum
  1012. !! two possible cases: bilinear (4 weights) or bicubic (16 weights)
  1013. id = iom_varid(inum, 'src05', ldstop=.FALSE.)
  1014. IF( id <= 0) THEN
  1015. ref_wgts(nxt_wgt)%numwgt = 4
  1016. ELSE
  1017. ref_wgts(nxt_wgt)%numwgt = 16
  1018. ENDIF
  1019. ALLOCATE( ref_wgts(nxt_wgt)%data_jpi(jpi,jpj,4) )
  1020. ALLOCATE( ref_wgts(nxt_wgt)%data_jpj(jpi,jpj,4) )
  1021. ALLOCATE( ref_wgts(nxt_wgt)%data_wgt(jpi,jpj,ref_wgts(nxt_wgt)%numwgt) )
  1022. DO jn = 1,4
  1023. aname = ' '
  1024. WRITE(aname,'(a3,i2.2)') 'src',jn
  1025. data_tmp(:,:) = 0
  1026. CALL iom_get ( inum, jpdom_data, aname, data_tmp(:,:) )
  1027. data_src(:,:) = INT(data_tmp(:,:))
  1028. ref_wgts(nxt_wgt)%data_jpj(:,:,jn) = 1 + (data_src(:,:)-1) / ref_wgts(nxt_wgt)%ddims(1)
  1029. ref_wgts(nxt_wgt)%data_jpi(:,:,jn) = data_src(:,:) - ref_wgts(nxt_wgt)%ddims(1)*(ref_wgts(nxt_wgt)%data_jpj(:,:,jn)-1)
  1030. END DO
  1031. DO jn = 1, ref_wgts(nxt_wgt)%numwgt
  1032. aname = ' '
  1033. WRITE(aname,'(a3,i2.2)') 'wgt',jn
  1034. ref_wgts(nxt_wgt)%data_wgt(:,:,jn) = 0.0
  1035. CALL iom_get ( inum, jpdom_data, aname, ref_wgts(nxt_wgt)%data_wgt(:,:,jn) )
  1036. END DO
  1037. CALL iom_close (inum)
  1038. ! find min and max indices in grid
  1039. ref_wgts(nxt_wgt)%botleft(1) = MINVAL(ref_wgts(nxt_wgt)%data_jpi(:,:,:))
  1040. ref_wgts(nxt_wgt)%botleft(2) = MINVAL(ref_wgts(nxt_wgt)%data_jpj(:,:,:))
  1041. ref_wgts(nxt_wgt)%topright(1) = MAXVAL(ref_wgts(nxt_wgt)%data_jpi(:,:,:))
  1042. ref_wgts(nxt_wgt)%topright(2) = MAXVAL(ref_wgts(nxt_wgt)%data_jpj(:,:,:))
  1043. ! and therefore dimensions of the input box
  1044. ref_wgts(nxt_wgt)%jpiwgt = ref_wgts(nxt_wgt)%topright(1) - ref_wgts(nxt_wgt)%botleft(1) + 1
  1045. ref_wgts(nxt_wgt)%jpjwgt = ref_wgts(nxt_wgt)%topright(2) - ref_wgts(nxt_wgt)%botleft(2) + 1
  1046. ! shift indexing of source grid
  1047. ref_wgts(nxt_wgt)%data_jpi(:,:,:) = ref_wgts(nxt_wgt)%data_jpi(:,:,:) - ref_wgts(nxt_wgt)%botleft(1) + 1
  1048. ref_wgts(nxt_wgt)%data_jpj(:,:,:) = ref_wgts(nxt_wgt)%data_jpj(:,:,:) - ref_wgts(nxt_wgt)%botleft(2) + 1
  1049. ! create input grid, give it a halo to allow gradient calculations
  1050. ! SA: +3 stencil is a patch to avoid out-of-bound computation in some configuration.
  1051. ! a more robust solution will be given in next release
  1052. ipk = SIZE(sd%fnow, 3)
  1053. ALLOCATE( ref_wgts(nxt_wgt)%fly_dta(ref_wgts(nxt_wgt)%jpiwgt+3, ref_wgts(nxt_wgt)%jpjwgt+3 ,ipk) )
  1054. IF( ref_wgts(nxt_wgt)%cyclic ) ALLOCATE( ref_wgts(nxt_wgt)%col(1,ref_wgts(nxt_wgt)%jpjwgt+3,ipk) )
  1055. nxt_wgt = nxt_wgt + 1
  1056. ELSE
  1057. CALL ctl_stop( ' fld_weight : unable to read the file ' )
  1058. ENDIF
  1059. DEALLOCATE (ddims )
  1060. CALL wrk_dealloc( jpi,jpj, data_src ) ! integer
  1061. CALL wrk_dealloc( jpi,jpj, data_tmp )
  1062. !
  1063. END SUBROUTINE fld_weight
  1064. SUBROUTINE apply_seaoverland(clmaskfile,zfieldo,jpi1_lsm,jpi2_lsm,jpj1_lsm, &
  1065. & jpj2_lsm,itmpi,itmpj,itmpz,rec1_lsm,recn_lsm)
  1066. !!---------------------------------------------------------------------
  1067. !! *** ROUTINE apply_seaoverland ***
  1068. !!
  1069. !! ** Purpose : avoid spurious fluxes in coastal or near-coastal areas
  1070. !! due to the wrong usage of "land" values from the coarse
  1071. !! atmospheric model when spatial interpolation is required
  1072. !! D. Delrosso INGV
  1073. !!----------------------------------------------------------------------
  1074. INTEGER :: inum,jni,jnj,jnz,jc ! temporary indices
  1075. INTEGER, INTENT(in) :: itmpi,itmpj,itmpz ! lengths
  1076. INTEGER, INTENT(in) :: jpi1_lsm,jpi2_lsm,jpj1_lsm,jpj2_lsm ! temporary indices
  1077. INTEGER, DIMENSION(3), INTENT(in) :: rec1_lsm,recn_lsm ! temporary arrays for start and length
  1078. REAL(wp),DIMENSION (:,:,:),INTENT(inout) :: zfieldo ! input/output array for seaoverland application
  1079. REAL(wp),DIMENSION (:,:,:),ALLOCATABLE :: zslmec1 ! temporary array for land point detection
  1080. REAL(wp),DIMENSION (:,:), ALLOCATABLE :: zfieldn ! array of forcing field with undeff for land points
  1081. REAL(wp),DIMENSION (:,:), ALLOCATABLE :: zfield ! array of forcing field
  1082. CHARACTER (len=100), INTENT(in) :: clmaskfile ! land/sea mask file name
  1083. !!---------------------------------------------------------------------
  1084. ALLOCATE ( zslmec1(itmpi,itmpj,itmpz) )
  1085. ALLOCATE ( zfieldn(itmpi,itmpj) )
  1086. ALLOCATE ( zfield(itmpi,itmpj) )
  1087. ! Retrieve the land sea mask data
  1088. CALL iom_open( clmaskfile, inum )
  1089. SELECT CASE( SIZE(zfieldo(jpi1_lsm:jpi2_lsm,jpj1_lsm:jpj2_lsm,:),3) )
  1090. CASE(1)
  1091. CALL iom_get( inum, jpdom_unknown, 'LSM', zslmec1(jpi1_lsm:jpi2_lsm,jpj1_lsm:jpj2_lsm,1), 1, rec1_lsm, recn_lsm)
  1092. CASE DEFAULT
  1093. CALL iom_get( inum, jpdom_unknown, 'LSM', zslmec1(jpi1_lsm:jpi2_lsm,jpj1_lsm:jpj2_lsm,:), 1, rec1_lsm, recn_lsm)
  1094. END SELECT
  1095. CALL iom_close( inum )
  1096. DO jnz=1,rec1_lsm(3) !! Loop over k dimension
  1097. DO jni=1,itmpi !! copy the original field into a tmp array
  1098. DO jnj=1,itmpj !! substituting undeff over land points
  1099. zfieldn(jni,jnj) = zfieldo(jni,jnj,jnz)
  1100. IF ( zslmec1(jni,jnj,jnz) == 1. ) THEN
  1101. zfieldn(jni,jnj) = undeff_lsm
  1102. ENDIF
  1103. END DO
  1104. END DO
  1105. CALL seaoverland(zfieldn,itmpi,itmpj,zfield)
  1106. DO jc=1,nn_lsm
  1107. CALL seaoverland(zfield,itmpi,itmpj,zfield)
  1108. END DO
  1109. ! Check for Undeff and substitute original values
  1110. IF(ANY(zfield==undeff_lsm)) THEN
  1111. DO jni=1,itmpi
  1112. DO jnj=1,itmpj
  1113. IF (zfield(jni,jnj)==undeff_lsm) THEN
  1114. zfield(jni,jnj) = zfieldo(jni,jnj,jnz)
  1115. ENDIF
  1116. ENDDO
  1117. ENDDO
  1118. ENDIF
  1119. zfieldo(:,:,jnz)=zfield(:,:)
  1120. END DO !! End Loop over k dimension
  1121. DEALLOCATE ( zslmec1 )
  1122. DEALLOCATE ( zfieldn )
  1123. DEALLOCATE ( zfield )
  1124. END SUBROUTINE apply_seaoverland
  1125. SUBROUTINE seaoverland(zfieldn,ileni,ilenj,zfield)
  1126. !!---------------------------------------------------------------------
  1127. !! *** ROUTINE seaoverland ***
  1128. !!
  1129. !! ** Purpose : create shifted matrices for seaoverland application
  1130. !! D. Delrosso INGV
  1131. !!----------------------------------------------------------------------
  1132. INTEGER,INTENT(in) :: ileni,ilenj ! lengths
  1133. REAL,DIMENSION (ileni,ilenj),INTENT(in) :: zfieldn ! array of forcing field with undeff for land points
  1134. REAL,DIMENSION (ileni,ilenj),INTENT(out) :: zfield ! array of forcing field
  1135. REAL,DIMENSION (ileni,ilenj) :: zmat1,zmat2,zmat3,zmat4 ! temporary arrays for seaoverland application
  1136. REAL,DIMENSION (ileni,ilenj) :: zmat5,zmat6,zmat7,zmat8 ! temporary arrays for seaoverland application
  1137. REAL,DIMENSION (ileni,ilenj) :: zlsm2d ! temporary arrays for seaoverland application
  1138. REAL,DIMENSION (ileni,ilenj,8) :: zlsm3d ! temporary arrays for seaoverland application
  1139. LOGICAL,DIMENSION (ileni,ilenj,8) :: ll_msknan3d ! logical mask for undeff detection
  1140. LOGICAL,DIMENSION (ileni,ilenj) :: ll_msknan2d ! logical mask for undeff detection
  1141. !!----------------------------------------------------------------------
  1142. zmat8 = eoshift(zfieldn , SHIFT=-1, BOUNDARY = (/zfieldn(:,1)/) ,DIM=2)
  1143. zmat1 = eoshift(zmat8 , SHIFT=-1, BOUNDARY = (/zmat8(1,:)/) ,DIM=1)
  1144. zmat2 = eoshift(zfieldn , SHIFT=-1, BOUNDARY = (/zfieldn(1,:)/) ,DIM=1)
  1145. zmat4 = eoshift(zfieldn , SHIFT= 1, BOUNDARY = (/zfieldn(:,ilenj)/),DIM=2)
  1146. zmat3 = eoshift(zmat4 , SHIFT=-1, BOUNDARY = (/zmat4(1,:)/) ,DIM=1)
  1147. zmat5 = eoshift(zmat4 , SHIFT= 1, BOUNDARY = (/zmat4(ileni,:)/) ,DIM=1)
  1148. zmat6 = eoshift(zfieldn , SHIFT= 1, BOUNDARY = (/zfieldn(ileni,:)/),DIM=1)
  1149. zmat7 = eoshift(zmat8 , SHIFT= 1, BOUNDARY = (/zmat8(ileni,:)/) ,DIM=1)
  1150. zlsm3d = RESHAPE( (/ zmat1, zmat2, zmat3, zmat4, zmat5, zmat6, zmat7, zmat8 /), (/ ileni, ilenj, 8 /))
  1151. ll_msknan3d = .not.(zlsm3d==undeff_lsm)
  1152. ll_msknan2d = .not.(zfieldn==undeff_lsm) ! FALSE where is Undeff (land)
  1153. zlsm2d = (SUM ( zlsm3d, 3 , ll_msknan3d ) )/(MAX(1,(COUNT( ll_msknan3d , 3 )) ))
  1154. WHERE ((COUNT( ll_msknan3d , 3 )) == 0.0_wp) zlsm2d = undeff_lsm
  1155. zfield = MERGE (zfieldn,zlsm2d,ll_msknan2d)
  1156. END SUBROUTINE seaoverland
  1157. SUBROUTINE fld_interp( num, clvar, kw, kk, dta, &
  1158. & nrec, lsmfile)
  1159. !!---------------------------------------------------------------------
  1160. !! *** ROUTINE fld_interp ***
  1161. !!
  1162. !! ** Purpose : apply weights to input gridded data to create data
  1163. !! on model grid
  1164. !!----------------------------------------------------------------------
  1165. INTEGER , INTENT(in ) :: num ! stream number
  1166. CHARACTER(LEN=*) , INTENT(in ) :: clvar ! variable name
  1167. INTEGER , INTENT(in ) :: kw ! weights number
  1168. INTEGER , INTENT(in ) :: kk ! vertical dimension of kk
  1169. REAL(wp), DIMENSION(:,:,:), INTENT(inout) :: dta ! output field on model grid
  1170. INTEGER , INTENT(in ) :: nrec ! record number to read (ie time slice)
  1171. CHARACTER(LEN=*) , INTENT(in ) :: lsmfile ! land sea mask file name
  1172. !!
  1173. REAL(wp),DIMENSION(:,:,:),ALLOCATABLE :: ztmp_fly_dta ! temporary array of values on input grid
  1174. INTEGER, DIMENSION(3) :: rec1,recn ! temporary arrays for start and length
  1175. INTEGER, DIMENSION(3) :: rec1_lsm,recn_lsm ! temporary arrays for start and length in case of seaoverland
  1176. INTEGER :: ii_lsm1,ii_lsm2,ij_lsm1,ij_lsm2 ! temporary indices
  1177. INTEGER :: jk, jn, jm, jir, jjr ! loop counters
  1178. INTEGER :: ni, nj ! lengths
  1179. INTEGER :: jpimin,jpiwid ! temporary indices
  1180. INTEGER :: jpimin_lsm,jpiwid_lsm ! temporary indices
  1181. INTEGER :: jpjmin,jpjwid ! temporary indices
  1182. INTEGER :: jpjmin_lsm,jpjwid_lsm ! temporary indices
  1183. INTEGER :: jpi1,jpi2,jpj1,jpj2 ! temporary indices
  1184. INTEGER :: jpi1_lsm,jpi2_lsm,jpj1_lsm,jpj2_lsm ! temporary indices
  1185. INTEGER :: itmpi,itmpj,itmpz ! lengths
  1186. !!----------------------------------------------------------------------
  1187. !
  1188. !! for weighted interpolation we have weights at four corners of a box surrounding
  1189. !! a model grid point, each weight is multiplied by a grid value (bilinear case)
  1190. !! or by a grid value and gradients at the corner point (bicubic case)
  1191. !! so we need to have a 4 by 4 subgrid surrounding each model point to cover both cases
  1192. !! sub grid from non-model input grid which encloses all grid points in this nemo process
  1193. jpimin = ref_wgts(kw)%botleft(1)
  1194. jpjmin = ref_wgts(kw)%botleft(2)
  1195. jpiwid = ref_wgts(kw)%jpiwgt
  1196. jpjwid = ref_wgts(kw)%jpjwgt
  1197. !! when reading in, expand this sub-grid by one halo point all the way round for calculating gradients
  1198. rec1(1) = MAX( jpimin-1, 1 )
  1199. rec1(2) = MAX( jpjmin-1, 1 )
  1200. rec1(3) = 1
  1201. recn(1) = MIN( jpiwid+2, ref_wgts(kw)%ddims(1)-rec1(1)+1 )
  1202. recn(2) = MIN( jpjwid+2, ref_wgts(kw)%ddims(2)-rec1(2)+1 )
  1203. recn(3) = kk
  1204. !! where we need to put it in the non-nemo grid fly_dta
  1205. !! note that jpi1 and jpj1 only differ from 1 when jpimin and jpjmin are 1
  1206. !! (ie at the extreme west or south of the whole input grid) and similarly for jpi2 and jpj2
  1207. jpi1 = 2 + rec1(1) - jpimin
  1208. jpj1 = 2 + rec1(2) - jpjmin
  1209. jpi2 = jpi1 + recn(1) - 1
  1210. jpj2 = jpj1 + recn(2) - 1
  1211. IF( LEN( TRIM(lsmfile) ) > 0 ) THEN
  1212. !! indeces for ztmp_fly_dta
  1213. ! --------------------------
  1214. rec1_lsm(1)=MAX(rec1(1)-nn_lsm,1) ! starting index for enlarged external data, x direction
  1215. rec1_lsm(2)=MAX(rec1(2)-nn_lsm,1) ! starting index for enlarged external data, y direction
  1216. rec1_lsm(3) = 1 ! vertical dimension
  1217. recn_lsm(1)=MIN(rec1(1)-rec1_lsm(1)+recn(1)+nn_lsm,ref_wgts(kw)%ddims(1)-rec1_lsm(1)) ! n points in x direction
  1218. recn_lsm(2)=MIN(rec1(2)-rec1_lsm(2)+recn(2)+nn_lsm,ref_wgts(kw)%ddims(2)-rec1_lsm(2)) ! n points in y direction
  1219. recn_lsm(3) = kk ! number of vertical levels in the input file
  1220. ! Avoid out of bound
  1221. jpimin_lsm = MAX( rec1_lsm(1)+1, 1 )
  1222. jpjmin_lsm = MAX( rec1_lsm(2)+1, 1 )
  1223. jpiwid_lsm = MIN( recn_lsm(1)-2,ref_wgts(kw)%ddims(1)-rec1(1)+1)
  1224. jpjwid_lsm = MIN( recn_lsm(2)-2,ref_wgts(kw)%ddims(2)-rec1(2)+1)
  1225. jpi1_lsm = 2+rec1_lsm(1)-jpimin_lsm
  1226. jpj1_lsm = 2+rec1_lsm(2)-jpjmin_lsm
  1227. jpi2_lsm = jpi1_lsm + recn_lsm(1) - 1
  1228. jpj2_lsm = jpj1_lsm + recn_lsm(2) - 1
  1229. itmpi=jpi2_lsm-jpi1_lsm+1
  1230. itmpj=jpj2_lsm-jpj1_lsm+1
  1231. itmpz=kk
  1232. ALLOCATE(ztmp_fly_dta(itmpi,itmpj,itmpz))
  1233. ztmp_fly_dta(:,:,:) = 0.0
  1234. SELECT CASE( SIZE(ztmp_fly_dta(jpi1_lsm:jpi2_lsm,jpj1_lsm:jpj2_lsm,:),3) )
  1235. CASE(1)
  1236. CALL iom_get( num, jpdom_unknown, clvar, ztmp_fly_dta(jpi1_lsm:jpi2_lsm,jpj1_lsm:jpj2_lsm,1), &
  1237. & nrec, rec1_lsm, recn_lsm)
  1238. CASE DEFAULT
  1239. CALL iom_get( num, jpdom_unknown, clvar, ztmp_fly_dta(jpi1_lsm:jpi2_lsm,jpj1_lsm:jpj2_lsm,:), &
  1240. & nrec, rec1_lsm, recn_lsm)
  1241. END SELECT
  1242. CALL apply_seaoverland(lsmfile,ztmp_fly_dta(jpi1_lsm:jpi2_lsm,jpj1_lsm:jpj2_lsm,:), &
  1243. & jpi1_lsm,jpi2_lsm,jpj1_lsm,jpj2_lsm, &
  1244. & itmpi,itmpj,itmpz,rec1_lsm,recn_lsm)
  1245. ! Relative indeces for remapping
  1246. ii_lsm1 = (rec1(1)-rec1_lsm(1))+1
  1247. ii_lsm2 = (ii_lsm1+recn(1))-1
  1248. ij_lsm1 = (rec1(2)-rec1_lsm(2))+1
  1249. ij_lsm2 = (ij_lsm1+recn(2))-1
  1250. ref_wgts(kw)%fly_dta(:,:,:) = 0.0
  1251. ref_wgts(kw)%fly_dta(jpi1:jpi2,jpj1:jpj2,:) = ztmp_fly_dta(ii_lsm1:ii_lsm2,ij_lsm1:ij_lsm2,:)
  1252. DEALLOCATE(ztmp_fly_dta)
  1253. ELSE
  1254. ref_wgts(kw)%fly_dta(:,:,:) = 0.0
  1255. SELECT CASE( SIZE(ref_wgts(kw)%fly_dta(jpi1:jpi2,jpj1:jpj2,:),3) )
  1256. CASE(1)
  1257. CALL iom_get( num, jpdom_unknown, clvar, ref_wgts(kw)%fly_dta(jpi1:jpi2,jpj1:jpj2,1), nrec, rec1, recn)
  1258. CASE DEFAULT
  1259. CALL iom_get( num, jpdom_unknown, clvar, ref_wgts(kw)%fly_dta(jpi1:jpi2,jpj1:jpj2,:), nrec, rec1, recn)
  1260. END SELECT
  1261. ENDIF
  1262. !! first four weights common to both bilinear and bicubic
  1263. !! data_jpi, data_jpj have already been shifted to (1,1) corresponding to botleft
  1264. !! note that we have to offset by 1 into fly_dta array because of halo
  1265. dta(:,:,:) = 0.0
  1266. DO jk = 1,4
  1267. DO jn = 1, jpj
  1268. DO jm = 1,jpi
  1269. ni = ref_wgts(kw)%data_jpi(jm,jn,jk)
  1270. nj = ref_wgts(kw)%data_jpj(jm,jn,jk)
  1271. dta(jm,jn,:) = dta(jm,jn,:) + ref_wgts(kw)%data_wgt(jm,jn,jk) * ref_wgts(kw)%fly_dta(ni+1,nj+1,:)
  1272. END DO
  1273. END DO
  1274. END DO
  1275. IF (ref_wgts(kw)%numwgt .EQ. 16) THEN
  1276. !! fix up halo points that we couldnt read from file
  1277. IF( jpi1 == 2 ) THEN
  1278. ref_wgts(kw)%fly_dta(jpi1-1,:,:) = ref_wgts(kw)%fly_dta(jpi1,:,:)
  1279. ENDIF
  1280. IF( jpi2 + jpimin - 1 == ref_wgts(kw)%ddims(1)+1 ) THEN
  1281. ref_wgts(kw)%fly_dta(jpi2+1,:,:) = ref_wgts(kw)%fly_dta(jpi2,:,:)
  1282. ENDIF
  1283. IF( jpj1 == 2 ) THEN
  1284. ref_wgts(kw)%fly_dta(:,jpj1-1,:) = ref_wgts(kw)%fly_dta(:,jpj1,:)
  1285. ENDIF
  1286. IF( jpj2 + jpjmin - 1 == ref_wgts(kw)%ddims(2)+1 .AND. jpj2 .lt. jpjwid+2 ) THEN
  1287. ref_wgts(kw)%fly_dta(:,jpj2+1,:) = 2.0*ref_wgts(kw)%fly_dta(:,jpj2,:) - ref_wgts(kw)%fly_dta(:,jpj2-1,:)
  1288. ENDIF
  1289. !! if data grid is cyclic we can do better on east-west edges
  1290. !! but have to allow for whether first and last columns are coincident
  1291. IF( ref_wgts(kw)%cyclic ) THEN
  1292. rec1(2) = MAX( jpjmin-1, 1 )
  1293. recn(1) = 1
  1294. recn(2) = MIN( jpjwid+2, ref_wgts(kw)%ddims(2)-rec1(2)+1 )
  1295. jpj1 = 2 + rec1(2) - jpjmin
  1296. jpj2 = jpj1 + recn(2) - 1
  1297. IF( jpi1 == 2 ) THEN
  1298. rec1(1) = ref_wgts(kw)%ddims(1) - ref_wgts(kw)%overlap
  1299. SELECT CASE( SIZE( ref_wgts(kw)%col(:,jpj1:jpj2,:),3) )
  1300. CASE(1)
  1301. CALL iom_get( num, jpdom_unknown, clvar, ref_wgts(kw)%col(:,jpj1:jpj2,1), nrec, rec1, recn)
  1302. CASE DEFAULT
  1303. CALL iom_get( num, jpdom_unknown, clvar, ref_wgts(kw)%col(:,jpj1:jpj2,:), nrec, rec1, recn)
  1304. END SELECT
  1305. ref_wgts(kw)%fly_dta(jpi1-1,jpj1:jpj2,:) = ref_wgts(kw)%col(1,jpj1:jpj2,:)
  1306. ENDIF
  1307. IF( jpi2 + jpimin - 1 == ref_wgts(kw)%ddims(1)+1 ) THEN
  1308. rec1(1) = 1 + ref_wgts(kw)%overlap
  1309. SELECT CASE( SIZE( ref_wgts(kw)%col(:,jpj1:jpj2,:),3) )
  1310. CASE(1)
  1311. CALL iom_get( num, jpdom_unknown, clvar, ref_wgts(kw)%col(:,jpj1:jpj2,1), nrec, rec1, recn)
  1312. CASE DEFAULT
  1313. CALL iom_get( num, jpdom_unknown, clvar, ref_wgts(kw)%col(:,jpj1:jpj2,:), nrec, rec1, recn)
  1314. END SELECT
  1315. ref_wgts(kw)%fly_dta(jpi2+1,jpj1:jpj2,:) = ref_wgts(kw)%col(1,jpj1:jpj2,:)
  1316. ENDIF
  1317. ENDIF
  1318. ! gradient in the i direction
  1319. DO jk = 1,4
  1320. DO jn = 1, jpj
  1321. DO jm = 1,jpi
  1322. ni = ref_wgts(kw)%data_jpi(jm,jn,jk)
  1323. nj = ref_wgts(kw)%data_jpj(jm,jn,jk)
  1324. dta(jm,jn,:) = dta(jm,jn,:) + ref_wgts(kw)%data_wgt(jm,jn,jk+4) * 0.5 * &
  1325. (ref_wgts(kw)%fly_dta(ni+2,nj+1,:) - ref_wgts(kw)%fly_dta(ni,nj+1,:))
  1326. END DO
  1327. END DO
  1328. END DO
  1329. ! gradient in the j direction
  1330. DO jk = 1,4
  1331. DO jn = 1, jpj
  1332. DO jm = 1,jpi
  1333. ni = ref_wgts(kw)%data_jpi(jm,jn,jk)
  1334. nj = ref_wgts(kw)%data_jpj(jm,jn,jk)
  1335. dta(jm,jn,:) = dta(jm,jn,:) + ref_wgts(kw)%data_wgt(jm,jn,jk+8) * 0.5 * &
  1336. (ref_wgts(kw)%fly_dta(ni+1,nj+2,:) - ref_wgts(kw)%fly_dta(ni+1,nj,:))
  1337. END DO
  1338. END DO
  1339. END DO
  1340. ! gradient in the ij direction
  1341. DO jk = 1,4
  1342. DO jn = 1, jpj
  1343. DO jm = 1,jpi
  1344. ni = ref_wgts(kw)%data_jpi(jm,jn,jk)
  1345. nj = ref_wgts(kw)%data_jpj(jm,jn,jk)
  1346. dta(jm,jn,:) = dta(jm,jn,:) + ref_wgts(kw)%data_wgt(jm,jn,jk+12) * 0.25 * ( &
  1347. (ref_wgts(kw)%fly_dta(ni+2,nj+2,:) - ref_wgts(kw)%fly_dta(ni ,nj+2,:)) - &
  1348. (ref_wgts(kw)%fly_dta(ni+2,nj ,:) - ref_wgts(kw)%fly_dta(ni ,nj ,:)))
  1349. END DO
  1350. END DO
  1351. END DO
  1352. !
  1353. END IF
  1354. !
  1355. END SUBROUTINE fld_interp
  1356. FUNCTION ksec_week( cdday )
  1357. !!---------------------------------------------------------------------
  1358. !! *** FUNCTION kshift_week ***
  1359. !!
  1360. !! ** Purpose :
  1361. !!---------------------------------------------------------------------
  1362. CHARACTER(len=*), INTENT(in) :: cdday !3 first letters of the first day of the weekly file
  1363. !!
  1364. INTEGER :: ksec_week ! output variable
  1365. INTEGER :: ijul !temp variable
  1366. INTEGER :: ishift !temp variable
  1367. CHARACTER(len=3),DIMENSION(7) :: cl_week
  1368. !!----------------------------------------------------------------------
  1369. cl_week = (/"sun","sat","fri","thu","wed","tue","mon"/)
  1370. DO ijul = 1, 7
  1371. IF( cl_week(ijul) == TRIM(cdday) ) EXIT
  1372. END DO
  1373. IF( ijul .GT. 7 ) CALL ctl_stop( 'ksec_week: wrong day for sdjf%cltype(6:8): '//TRIM(cdday) )
  1374. !
  1375. ishift = ijul * NINT(rday)
  1376. !
  1377. ksec_week = nsec_week + ishift
  1378. ksec_week = MOD( ksec_week, 7*NINT(rday) )
  1379. !
  1380. END FUNCTION ksec_week
  1381. !!======================================================================
  1382. END MODULE fldread