FV3 Bundle
time_interp_external.F90
Go to the documentation of this file.
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
10 !*
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 !* for more details.
15 !*
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
19 
20 
22 #include <fms_platform.h>
23 !
24 !<CONTACT EMAIL="Matthew.Harrison@noaa.gov">M.J. Harrison</CONTACT>
25 !
26 !<REVIEWER EMAIL="hsimmons@iarc.uaf.edu">Harper Simmons</REVIEWER>
27 !
28 !<OVERVIEW>
29 ! Perform I/O and time interpolation of external fields (contained in a file).
30 !</OVERVIEW>
31 
32 !<DESCRIPTION>
33 ! Perform I/O and time interpolation for external fields.
34 ! Uses udunits library to calculate calendar dates and
35 ! convert units. Allows for reading data decomposed across
36 ! model horizontal grid using optional domain2d argument
37 !
38 ! data are defined over data domain for domain2d data
39 ! (halo values are NOT updated by this module)
40 !
41 !</DESCRIPTION>
42 !
43 !<NAMELIST NAME="time_interp_external_nml">
44 ! <DATA NAME="num_io_buffers" TYPE="integer">
45 ! size of record dimension for internal buffer. This is useful for tuning i/o performance
46 ! particularly for large datasets (e.g. daily flux fields)
47 ! </DATA>
48 !</NAMELIST>
49 
50  use fms_mod, only : write_version_number
51  use mpp_mod, only : mpp_error,fatal,warning,mpp_pe, stdout, stdlog, note
52  use mpp_mod, only : input_nml_file
53  use mpp_io_mod, only : mpp_open, mpp_get_atts, mpp_get_info, mpp_netcdf, mpp_multi, mpp_single,&
54  mpp_get_times, mpp_rdonly, mpp_ascii, default_axis,axistype,fieldtype,atttype, &
55  mpp_get_axes, mpp_get_fields, mpp_read, default_field, mpp_close, &
56  mpp_get_tavg_info, validtype, mpp_is_valid, mpp_get_file_name
57  use time_manager_mod, only : time_type, get_date, set_date, operator ( >= ) , operator ( + ) , days_in_month, &
58  operator( - ), operator ( / ) , days_in_year, increment_time, &
60  use get_cal_time_mod, only : get_cal_time
65  use fms_mod, only : lowercase, open_namelist_file, check_nml_error, close_file
66  use platform_mod, only: r8_kind
67  use horiz_interp_mod, only : horiz_interp, horiz_interp_type
68 
69  implicit none
70  private
71 
72 ! Include variable "version" to be written to log file.
73 #include<file_version.h>
74 
75  integer, parameter, public :: no_region=0, inside_region=1, outside_region=2
76  integer, parameter, private :: modulo_year= 0001
77  integer, parameter, private :: linear_time_interp = 1 ! not used currently
78  integer, parameter, public :: success = 0, err_field_not_found = 1
79  integer, private :: max_fields = 100, max_files= 40
80  integer, private :: num_fields = 0, num_files=0
81  ! denotes time intervals in file (interpreted from metadata)
82  integer, private :: num_io_buffers = 2 ! set -1 to read all records from disk into memory
83  logical, private :: module_initialized = .false.
84  logical, private :: debug_this_module = .false.
85 
89 
90  private find_buf_index,&
92 
93  type, private :: ext_fieldtype
94  integer :: unit ! keep unit open when not reading all records
95  character(len=128) :: name, units
96  integer :: siz(4), ndim
97  type(domain2d) :: domain
98  type(axistype) :: axes(4)
99  type(time_type), dimension(:), pointer :: time =>null() ! midpoint of time interval
100  type(time_type), dimension(:), pointer :: start_time =>null(), end_time =>null()
101  type(fieldtype) :: field ! mpp_io type
102  type(time_type), dimension(:), pointer :: period =>null()
103  logical :: modulo_time ! denote climatological time axis
104  real, dimension(:,:,:,:), pointer :: data =>null() ! defined over data domain or global domain
105  logical, dimension(:,:,:,:), pointer :: mask =>null() ! defined over data domain or global domain
106  integer, dimension(:), pointer :: ibuf =>null() ! record numbers associated with buffers
107  real, dimension(:,:,:,:), pointer :: src_data =>null() ! input data buffer
108  type(validtype) :: valid ! data validator
109  integer :: nbuf
110  logical :: domain_present
111  real(DOUBLE_KIND) :: slope, intercept
112  integer :: isc,iec,jsc,jec
113  type(time_type) :: modulo_time_beg, modulo_time_end
114  logical :: have_modulo_times, correct_leap_year_inconsistency
115  integer :: region_type
116  integer :: is_region, ie_region, js_region, je_region
117  integer :: is_src, ie_src, js_src, je_src
118  integer :: tdim
119  integer :: numwindows
120  logical, dimension(:,:), pointer :: need_compute=>null()
121  real :: missing ! missing value
122  end type ext_fieldtype
123 
124  type, private :: filetype
125  character(len=128) :: filename = ''
126  integer :: unit = -1
127  end type filetype
128 
130  module procedure time_interp_external_0d
131  module procedure time_interp_external_2d
132  module procedure time_interp_external_3d
133  end interface
134 
135  integer :: outunit
136 
137  type(ext_fieldtype), save, private, pointer :: field(:) => null()
138  type(filetype), save, private, pointer :: opened_files(:) => null()
139 !Balaji: really should use field%missing
140  real(DOUBLE_KIND), private, parameter :: time_interp_missing=-1e99
141  contains
142 
143 ! <SUBROUTINE NAME="time_interp_external_init">
144 !
145 ! <DESCRIPTION>
146 ! Initialize the time_interp_external module
147 ! </DESCRIPTION>
148 !
149  subroutine time_interp_external_init()
151  integer :: ioun, io_status, logunit, ierr
152 
153  namelist /time_interp_external_nml/ num_io_buffers, debug_this_module, &
155 
156  ! open and read namelist
157 
158  if(module_initialized) return
159 
160  logunit = stdlog()
161  outunit = stdout()
162  call write_version_number("TIME_INTERP_EXTERNAL_MOD", version)
163 
164 #ifdef INTERNAL_FILE_NML
165  read (input_nml_file, time_interp_external_nml, iostat=io_status)
166  ierr = check_nml_error(io_status, 'time_interp_external_nml')
167 #else
168  ioun = open_namelist_file()
169  ierr=1; do while (ierr /= 0)
170  read (ioun, nml=time_interp_external_nml, iostat=io_status, end=10)
171  ierr = check_nml_error(io_status, 'time_interp_external_nml')
172  enddo
173 10 call close_file (ioun)
174 #endif
175 
176  write(logunit,time_interp_external_nml)
179 
180  module_initialized = .true.
181 
182  call time_interp_init()
183 
184  return
185 
186  end subroutine time_interp_external_init
187 ! </SUBROUTINE> NAME="time_interp_external_init"
188 
189 
190 !<FUNCTION NAME="init_external_field" TYPE="integer">
191 !
192 !<DESCRIPTION>
193 ! initialize an external field. Buffer "num_io_buffers" (default=2) in memory to reduce memory allocations.
194 ! distributed reads are supported using the optional "domain" flag.
195 ! Units conversion via the optional "desired_units" flag using udunits_mod.
196 !
197 ! Return integer id of field for future calls to time_interp_external.
198 !
199 ! </DESCRIPTION>
200 !
201 !<IN NAME="file" TYPE="character(len=*)">
202 ! filename
203 !</IN>
204 !<IN NAME="fieldname" TYPE="character(len=*)">
205 ! fieldname (in file)
206 !</IN>
207 !<IN NAME="format" TYPE="integer">
208 ! mpp_io flag for format of file (optional). Currently only "MPP_NETCDF" supported
209 !</IN>
210 !<IN NAME="threading" TYPE="integer">
211 ! mpp_io flag for threading (optional). "MPP_SINGLE" means root pe reads global field and distributes to other PEs
212 ! "MPP_MULTI" means all PEs read data
213 !</IN>
214 !<IN NAME="domain" TYPE="mpp_domains_mod:domain2d">
215 ! domain flag (optional)
216 !</IN>
217 !<IN NAME="desired_units" TYPE="character(len=*)">
218 ! Target units for data (optional), e.g. convert from deg_K to deg_C.
219 ! Failure to convert using udunits will result in failure of this module.
220 !</IN>
221 !<IN NAME="verbose" TYPE="logical">
222 ! verbose flag for debugging (optional).
223 !</IN>
224 !<INOUT NAME="axis_centers" TYPE="axistype" DIM="(4)">
225 ! MPP_IO axistype array for grid centers ordered X-Y-Z-T (optional).
226 !</INOUT>
227 !<INOUT NAME="axis_sizes" TYPE="integer" DIM="(4)">
228 ! array of axis lengths ordered X-Y-Z-T (optional).
229 !</INOUT>
230 
231 
232  function init_external_field(file,fieldname,format,threading,domain,desired_units,&
233  verbose,axis_centers,axis_sizes,override,correct_leap_year_inconsistency,&
234  permit_calendar_conversion,use_comp_domain,ierr, nwindows, ignore_axis_atts )
236  character(len=*), intent(in) :: file,fieldname
237  integer, intent(in), optional :: format, threading
238  logical, intent(in), optional :: verbose
239  character(len=*), intent(in), optional :: desired_units
240  type(domain2d), intent(in), optional :: domain
241  type(axistype), intent(inout), optional :: axis_centers(4)
242  integer, intent(inout), optional :: axis_sizes(4)
243  logical, intent(in), optional :: override, correct_leap_year_inconsistency,&
244  permit_calendar_conversion,use_comp_domain
245  integer, intent(out), optional :: ierr
246  integer, intent(in), optional :: nwindows
247  logical, optional :: ignore_axis_atts
248  real :: missing
249 
250  integer :: init_external_field
251 
252  type(fieldtype), dimension(:), allocatable :: flds
253  type(axistype), dimension(:), allocatable :: axes, fld_axes
254  type(axistype) :: time_axis
255  type(atttype), allocatable, dimension(:) :: global_atts
256 
257  real(DOUBLE_KIND) :: slope, intercept
258  integer :: form, thread, fset, unit,ndim,nvar,natt,ntime,i,j
259  integer :: iscomp,iecomp,jscomp,jecomp,isglobal,ieglobal,jsglobal,jeglobal
260  integer :: isdata,iedata,jsdata,jedata, dxsize, dysize,dxsize_max,dysize_max
261  logical :: verb, transpose_xy,use_comp_domain1
262  real, dimension(:), allocatable :: tstamp, tstart, tend, tavg
263  character(len=1) :: cart
264  character(len=1), dimension(4) :: cart_dir
265  character(len=128) :: units, fld_units
266  character(len=128) :: name, msg, calendar_type, timebeg, timeend
267  integer :: siz(4), siz_in(4), gxsize, gysize,gxsize_max, gysize_max
268  type(time_type) :: tdiff
269  integer :: yr, mon, day, hr, minu, sec
270  integer :: len, nfile, nfields_orig, nbuf, nx,ny
271  integer :: numwindows
272  logical :: ignore_axatts
273 
274 
275  if (.not. module_initialized) call mpp_error(fatal,'Must call time_interp_external_init first')
276  if(present(ierr)) ierr = success
277  ignore_axatts=.false.
278  cart_dir(1)='X';cart_dir(2)='Y';cart_dir(3)='Z';cart_dir(4)='T'
279  if(present(ignore_axis_atts)) ignore_axatts = ignore_axis_atts
280  use_comp_domain1 = .false.
281  if(PRESENT(use_comp_domain)) use_comp_domain1 = use_comp_domain
282  form=mpp_netcdf
283  if (PRESENT(format)) form = format
284  thread = mpp_multi
285  if (PRESENT(threading)) thread = threading
286  fset = mpp_single
287  verb=.false.
288  if (PRESENT(verbose)) verb=verbose
289  if (debug_this_module) verb = .true.
290  numwindows = 1
291  if(present(nwindows)) numwindows = nwindows
292 
293  units = 'same'
294  if (PRESENT(desired_units)) then
295  units = desired_units
296  call mpp_error(fatal,'==> Unit conversion via time_interp_external &
297  &has been temporarily deprecated. Previous versions of&
298  &this module used udunits_mod to perform unit conversion.&
299  & Udunits_mod is in the process of being replaced since &
300  &there were portability issues associated with this code.&
301  & Please remove the desired_units argument from calls to &
302  &this routine.')
303  endif
304  nfile = 0
305  do i=1,num_files
306  if(trim(opened_files(i)%filename) == trim(file)) then
307  nfile = i
308  exit ! file is already opened
309  endif
310  enddo
311  if(nfile == 0) then
312  call mpp_open(unit,trim(file),mpp_rdonly,form,threading=thread,&
313  fileset=fset)
314  num_files = num_files + 1
315  if(num_files > max_files) then ! not enough space in the file table, reallocate it
316  !--- z1l: For the case of multiple thread, realoc_files will cause memory leak.
317  !--- If multiple threads are working on file A. One of the thread finished first and
318  !--- begin to work on file B, the realloc_files will cause problem for
319  !--- other threads are working on the file A.
320  ! call realloc_files(2*size(opened_files))
321  call mpp_error(fatal, "time_interp_external: num_files is greater than max_files, "// &
322  "increase time_interp_external_nml max_files")
323  endif
324  opened_files(num_files)%filename = trim(file)
325  opened_files(num_files)%unit = unit
326  else
327  unit = opened_files(nfile)%unit
328  endif
329 
330  call mpp_get_info(unit,ndim,nvar,natt,ntime)
331 
332  if (ntime < 1) then
333  write(msg,'(a15,a,a58)') 'external field ',trim(fieldname),&
334  ' does not have an associated record dimension (REQUIRED) '
335  call mpp_error(fatal,trim(msg))
336  endif
337  allocate(global_atts(natt))
338  call mpp_get_atts(unit, global_atts)
339  allocate(axes(ndim))
340  call mpp_get_axes(unit, axes, time_axis)
341  allocate(flds(nvar))
342  call mpp_get_fields(unit,flds)
343  allocate(tstamp(ntime),tstart(ntime),tend(ntime),tavg(ntime))
344  call mpp_get_times(unit,tstamp)
345  transpose_xy = .false.
346  isdata=1; iedata=1; jsdata=1; jedata=1
347  gxsize=1; gysize=1
348  siz_in = 1
349 
350  if (PRESENT(domain)) then
351  call mpp_get_compute_domain(domain,iscomp,iecomp,jscomp,jecomp)
352  nx = iecomp-iscomp+1; ny = jecomp-jscomp+1
353  call mpp_get_data_domain(domain,isdata,iedata,jsdata,jedata,dxsize,dxsize_max,dysize,dysize_max)
354  call mpp_get_global_domain(domain,isglobal,ieglobal,jsglobal,jeglobal,gxsize,gxsize_max,gysize,gysize_max)
355  elseif(use_comp_domain1) then
356  call mpp_error(fatal,"init_external_field:"//&
357  " use_comp_domain=true but domain is not present")
358  endif
359 
361  nfields_orig = num_fields
362 
363  do i=1,nvar
364  call mpp_get_atts(flds(i),name=name,units=fld_units,ndim=ndim,siz=siz_in)
365  call mpp_get_tavg_info(unit,flds(i),flds,tstamp,tstart,tend,tavg)
366  call mpp_get_atts(flds(i),missing=missing)
367  ! why does it convert case of the field name?
368  if (trim(lowercase(name)) /= trim(lowercase(fieldname))) cycle
369 
370  if (verb) write(outunit,*) 'found field ',trim(fieldname), ' in file !!'
371  num_fields = num_fields + 1
372  if(num_fields > max_fields) then
373  !--- z1l: For the case of multiple thread, realoc_fields will cause memory leak.
374  !--- If multiple threads are working on field A. One of the thread finished first and
375  !--- begin to work on field B, the realloc_files will cause problem for
376  !--- other threads are working on the field A.
377  !call realloc_fields(size(field)*2)
378  call mpp_error(fatal, "time_interp_external: num_fields is greater than max_fields, "// &
379  "increase time_interp_external_nml max_fields")
380  endif
381 
382  init_external_field = num_fields
383  field(num_fields)%unit = unit
384  field(num_fields)%name = trim(name)
385  field(num_fields)%units = trim(fld_units)
386  field(num_fields)%field = flds(i)
387  field(num_fields)%isc = 1
388  field(num_fields)%iec = 1
389  field(num_fields)%jsc = 1
390  field(num_fields)%jec = 1
391  field(num_fields)%region_type = no_region
392  field(num_fields)%is_region = 0
393  field(num_fields)%ie_region = -1
394  field(num_fields)%js_region = 0
395  field(num_fields)%je_region = -1
396  if (PRESENT(domain)) then
397  field(num_fields)%domain_present = .true.
398  field(num_fields)%domain = domain
399  field(num_fields)%isc=iscomp;field(num_fields)%iec = iecomp
400  field(num_fields)%jsc=jscomp;field(num_fields)%jec = jecomp
401  else
402  field(num_fields)%domain_present = .false.
403  endif
404 
405  call mpp_get_atts(flds(i),valid=field(num_fields)%valid )
406  allocate(fld_axes(ndim))
407  call mpp_get_atts(flds(i),axes=fld_axes)
408  if (ndim > 4) call mpp_error(fatal, &
409  'invalid array rank <=4d fields supported')
410  field(num_fields)%siz = 1
411  field(num_fields)%ndim = ndim
412  field(num_fields)%tdim = 4
413  field(num_fields)%missing = missing
414  do j=1,field(num_fields)%ndim
415  cart = 'N'
416  call get_axis_cart(fld_axes(j), cart)
417  call mpp_get_atts(fld_axes(j),len=len)
418  if (cart == 'N' .and. .not. ignore_axatts) then
419  write(msg,'(a,"/",a)') trim(file),trim(fieldname)
420  call mpp_error(fatal,'file/field '//trim(msg)// &
421  ' couldnt recognize axis atts in time_interp_external')
422  else if (cart == 'N' .and. ignore_axatts) then
423  cart = cart_dir(j)
424  endif
425  select case (cart)
426  case ('X')
427  if (j.eq.2) transpose_xy = .true.
428  if (.not.PRESENT(domain) .and. .not.PRESENT(override)) then
429  isdata=1;iedata=len
430  iscomp=1;iecomp=len
431  gxsize = len
432  dxsize = len
433  field(num_fields)%isc=iscomp;field(num_fields)%iec=iecomp
434  elseif (PRESENT(override)) then
435  gxsize = len
436  if (PRESENT(axis_sizes)) axis_sizes(1) = len
437  endif
438  field(num_fields)%axes(1) = fld_axes(j)
439  if(use_comp_domain1) then
440  field(num_fields)%siz(1) = nx
441  else
442  field(num_fields)%siz(1) = dxsize
443  endif
444  if (len /= gxsize) then
445  write(msg,'(a,"/",a)') trim(file),trim(fieldname)
446  call mpp_error(fatal,'time_interp_ext, file/field '//trim(msg)//' x dim doesnt match model')
447  endif
448  case ('Y')
449  field(num_fields)%axes(2) = fld_axes(j)
450  if (.not.PRESENT(domain) .and. .not.PRESENT(override)) then
451  jsdata=1;jedata=len
452  jscomp=1;jecomp=len
453  gysize = len
454  dysize = len
455  field(num_fields)%jsc=jscomp;field(num_fields)%jec=jecomp
456  elseif (PRESENT(override)) then
457  gysize = len
458  if (PRESENT(axis_sizes)) axis_sizes(2) = len
459  endif
460  if(use_comp_domain1) then
461  field(num_fields)%siz(2) = ny
462  else
463  field(num_fields)%siz(2) = dysize
464  endif
465  if (len /= gysize) then
466  write(msg,'(a,"/",a)') trim(file),trim(fieldname)
467  call mpp_error(fatal,'time_interp_ext, file/field '//trim(msg)//' y dim doesnt match model')
468  endif
469  case ('Z')
470  field(num_fields)%axes(3) = fld_axes(j)
471  field(num_fields)%siz(3) = siz_in(3)
472  case ('T')
473  field(num_fields)%axes(4) = fld_axes(j)
474  field(num_fields)%siz(4) = ntime
475  field(num_fields)%tdim = j
476  end select
477  enddo
478  siz = field(num_fields)%siz
479 
480  if (PRESENT(axis_centers)) then
481  axis_centers = field(num_fields)%axes
482  endif
483 
484  if (PRESENT(axis_sizes) .and. .not.PRESENT(override)) then
485  axis_sizes = field(num_fields)%siz
486  endif
487 
488  deallocate(fld_axes)
489  if (verb) write(outunit,'(a,4i6)') 'field x,y,z,t local size= ',siz
490  if (verb) write(outunit,*) 'field contains data in units = ',trim(field(num_fields)%units)
491  if (transpose_xy) call mpp_error(fatal,'axis ordering not supported')
492  if (num_io_buffers .le. 1) call mpp_error(fatal,'time_interp_ext:num_io_buffers should be at least 2')
493  nbuf = min(num_io_buffers,siz(4))
494 
495  field(num_fields)%numwindows = numwindows
496  allocate(field(num_fields)%need_compute(nbuf, numwindows))
497  field(num_fields)%need_compute = .true.
498 
499  allocate(field(num_fields)%data(isdata:iedata,jsdata:jedata,siz(3),nbuf),&
500  field(num_fields)%mask(isdata:iedata,jsdata:jedata,siz(3),nbuf) )
501  field(num_fields)%mask = .false.
502  field(num_fields)%data = 0.0
503  slope=1.0;intercept=0.0
504 ! if (units /= 'same') call convert_units(trim(field(num_fields)%units),trim(units),slope,intercept)
505 ! if (verb.and.units /= 'same') then
506 ! write(outunit,*) 'attempting to convert data to units = ',trim(units)
507 ! write(outunit,'(a,f8.3,a,f8.3)') 'factor = ',slope,' offset= ',intercept
508 ! endif
509  field(num_fields)%slope = slope
510  field(num_fields)%intercept = intercept
511  allocate(field(num_fields)%ibuf(nbuf))
512  field(num_fields)%ibuf = -1
513  field(num_fields)%nbuf = 0 ! initialize buffer number so that first reading fills data(:,:,:,1)
514  if(PRESENT(override)) then
515  field(num_fields)%is_src = 1
516  field(num_fields)%ie_src = gxsize
517  field(num_fields)%js_src = 1
518  field(num_fields)%je_src = gysize
519  allocate(field(num_fields)%src_data(gxsize,gysize,siz(3),nbuf))
520  else
521  field(num_fields)%is_src = isdata
522  field(num_fields)%ie_src = iedata
523  field(num_fields)%js_src = jsdata
524  field(num_fields)%je_src = jedata
525  allocate(field(num_fields)%src_data(isdata:iedata,jsdata:jedata,siz(3),nbuf))
526  endif
527 
528  allocate(field(num_fields)%time(ntime))
529  allocate(field(num_fields)%period(ntime))
530  allocate(field(num_fields)%start_time(ntime))
531  allocate(field(num_fields)%end_time(ntime))
532 
533  call mpp_get_atts(time_axis,units=units,calendar=calendar_type)
534  do j=1,ntime
535  field(num_fields)%time(j) = get_cal_time(tstamp(j),trim(units),trim(calendar_type),permit_calendar_conversion)
536  field(num_fields)%start_time(j) = get_cal_time(tstart(j),trim(units),trim(calendar_type),permit_calendar_conversion)
537  field(num_fields)%end_time(j) = get_cal_time( tend(j),trim(units),trim(calendar_type),permit_calendar_conversion)
538  enddo
539 
540  if (field(num_fields)%modulo_time) then
541  call set_time_modulo(field(num_fields)%Time)
542  call set_time_modulo(field(num_fields)%start_time)
543  call set_time_modulo(field(num_fields)%end_time)
544  endif
545 
546  if(present(correct_leap_year_inconsistency)) then
547  field(num_fields)%correct_leap_year_inconsistency = correct_leap_year_inconsistency
548  else
549  field(num_fields)%correct_leap_year_inconsistency = .false.
550  endif
551 
552  if(get_axis_modulo_times(time_axis, timebeg, timeend)) then
553  if(get_calendar_type() == no_calendar) then
554  field(num_fields)%modulo_time_beg = set_time(timebeg)
555  field(num_fields)%modulo_time_end = set_time(timeend)
556  else
557  field(num_fields)%modulo_time_beg = set_date(timebeg)
558  field(num_fields)%modulo_time_end = set_date(timeend)
559  endif
560  field(num_fields)%have_modulo_times = .true.
561  else
562  field(num_fields)%have_modulo_times = .false.
563  endif
564  if(ntime == 1) then
565  call mpp_error(note, 'time_interp_external_mod: file '//trim(file)//' has only one time level')
566  else
567  do j= 1, ntime
568  field(num_fields)%period(j) = field(num_fields)%end_time(j)-field(num_fields)%start_time(j)
569  if (field(num_fields)%period(j) > set_time(0,0)) then
570  call get_time(field(num_fields)%period(j), sec, day)
571  sec = sec/2+mod(day,2)*43200
572  day = day/2
573  field(num_fields)%time(j) = field(num_fields)%start_time(j)+&
574  set_time(sec,day)
575  else
576  if (j > 1 .and. j < ntime) then
577  tdiff = field(num_fields)%time(j+1) - field(num_fields)%time(j-1)
578  call get_time(tdiff, sec, day)
579  sec = sec/2+mod(day,2)*43200
580  day = day/2
581  field(num_fields)%period(j) = set_time(sec,day)
582  sec = sec/2+mod(day,2)*43200
583  day = day/2
584  field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day)
585  field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day)
586  elseif ( j == 1) then
587  tdiff = field(num_fields)%time(2) - field(num_fields)%time(1)
588  call get_time(tdiff, sec, day)
589  field(num_fields)%period(j) = set_time(sec,day)
590  sec = sec/2+mod(day,2)*43200
591  day = day/2
592  field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day)
593  field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day)
594  else
595  tdiff = field(num_fields)%time(ntime) - field(num_fields)%time(ntime-1)
596  call get_time(tdiff, sec, day)
597  field(num_fields)%period(j) = set_time(sec,day)
598  sec = sec/2+mod(day,2)*43200
599  day = day/2
600  field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day)
601  field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day)
602  endif
603  endif
604  enddo
605  endif
606 
607  do j=1,ntime-1
608  if (field(num_fields)%time(j) >= field(num_fields)%time(j+1)) then
609  write(msg,'(A,i20)') "times not monotonically increasing. Filename: " &
610  //trim(file)//" field: "//trim(fieldname)//" timeslice: ", j
611  call mpp_error(fatal, trim(msg))
612  endif
613  enddo
614 
615  field(num_fields)%modulo_time = get_axis_modulo(time_axis)
616 
617  if (verb) then
618  if (field(num_fields)%modulo_time) write(outunit,*) 'data are being treated as modulo in time'
619  do j= 1, ntime
620  write(outunit,*) 'time index, ', j
621  call get_date(field(num_fields)%start_time(j),yr,mon,day,hr,minu,sec)
622  write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') &
623  'start time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec
624  call get_date(field(num_fields)%time(j),yr,mon,day,hr,minu,sec)
625  write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') &
626  'mid time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec
627  call get_date(field(num_fields)%end_time(j),yr,mon,day,hr,minu,sec)
628  write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') &
629  'end time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec
630  enddo
631  end if
632 
633  enddo
634 
635  if (num_fields == nfields_orig) then
636  if (present(ierr)) then
637  ierr = err_field_not_found
638  else
639  call mpp_error(fatal,'external field "'//trim(fieldname)//'" not found in file "'//trim(file)//'"')
640  endif
641  endif
642 
643  deallocate(global_atts)
644  deallocate(axes)
645  deallocate(flds)
646  deallocate(tstamp, tstart, tend, tavg)
647 
648  return
649 
650  end function init_external_field
651 
652 !</FUNCTION> NAME="init_external_field"
653 
654 
655  subroutine time_interp_external_2d(index, time, data_in, interp, verbose,horz_interp, mask_out, &
656  is_in, ie_in, js_in, je_in, window_id)
658  integer, intent(in) :: index
659  type(time_type), intent(in) :: time
660  real, dimension(:,:), intent(inout) :: data_in
661  integer, intent(in), optional :: interp
662  logical, intent(in), optional :: verbose
663  type(horiz_interp_type),intent(in), optional :: horz_interp
664  logical, dimension(:,:), intent(out), optional :: mask_out ! set to true where output data is valid
665  integer, intent(in), optional :: is_in, ie_in, js_in, je_in
666  integer, intent(in), optional :: window_id
667 
668  real , dimension(size(data_in,1), size(data_in,2), 1) :: data_out
669  logical, dimension(size(data_in,1), size(data_in,2), 1) :: mask3d
670 
671  data_out(:,:,1) = data_in(:,:) ! fill initial values for the portions of array that are not touched by 3d routine
672  call time_interp_external_3d(index, time, data_out, interp, verbose, horz_interp, mask3d, &
673  is_in=is_in,ie_in=ie_in,js_in=js_in,je_in=je_in,window_id=window_id)
674  data_in(:,:) = data_out(:,:,1)
675  if (PRESENT(mask_out)) mask_out(:,:) = mask3d(:,:,1)
676 
677  return
678  end subroutine time_interp_external_2d
679 
680 !<SUBROUTINE NAME="time_interp_external" >
681 !
682 !<DESCRIPTION>
683 ! Provide data from external file interpolated to current model time.
684 ! Data may be local to current processor or global, depending on
685 ! "init_external_field" flags.
686 !</DESCRIPTION>
687 !
688 !<IN NAME="index" TYPE="integer">
689 ! index of external field from previous call to init_external_field
690 !</IN>
691 !<IN NAME="time" TYPE="time_manager_mod:time_type">
692 ! target time for data
693 !</IN>
694 !<INOUT NAME="data" TYPE="real" DIM="(:,:),(:,:,:)">
695 ! global or local data array
696 !</INOUT>
697 !<IN NAME="interp" TYPE="integer">
698 ! time_interp_external defined interpolation method (optional). Currently this module only supports
699 ! LINEAR_TIME_INTERP.
700 !</IN>
701 !<IN NAME="verbose" TYPE="logical">
702 ! verbose flag for debugging (optional).
703 !</IN>
704 
705  subroutine time_interp_external_3d(index, time, data, interp,verbose,horz_interp, mask_out, is_in, ie_in, js_in, je_in, window_id)
707  integer, intent(in) :: index
708  type(time_type), intent(in) :: time
709  real, dimension(:,:,:), intent(inout) :: data
710  integer, intent(in), optional :: interp
711  logical, intent(in), optional :: verbose
712  type(horiz_interp_type), intent(in), optional :: horz_interp
713  logical, dimension(:,:,:), intent(out), optional :: mask_out ! set to true where output data is valid
714  integer, intent(in), optional :: is_in, ie_in, js_in, je_in
715  integer, intent(in), optional :: window_id
716 
717  integer :: nx, ny, nz, interp_method, t1, t2
718  integer :: i1, i2, isc, iec, jsc, jec, mod_time
719  integer :: yy, mm, dd, hh, min, ss
720  character(len=256) :: err_msg, filename
721 
722  integer :: isw, iew, jsw, jew, nxw, nyw
723  ! these are boundaries of the updated portion of the "data" argument
724  ! they are calculated using sizes of the "data" and isc,iec,jsc,jsc
725  ! fileds from respective input field, to center the updated portion
726  ! in the output array
727 
728  real :: w1,w2
729  logical :: verb
730  character(len=16) :: message1, message2
731 
732  nx = size(data,1)
733  ny = size(data,2)
734  nz = size(data,3)
735 
736  interp_method = linear_time_interp
737  if (PRESENT(interp)) interp_method = interp
738  verb=.false.
739  if (PRESENT(verbose)) verb=verbose
740  if (debug_this_module) verb = .true.
741 
742  if (index < 1.or.index > num_fields) &
743  call mpp_error(fatal,'invalid index in call to time_interp_ext -- field was not initialized or failed to initialize')
744 
745  isc=field(index)%isc;iec=field(index)%iec
746  jsc=field(index)%jsc;jec=field(index)%jec
747 
748  if( field(index)%numwindows == 1 ) then
749  nxw = iec-isc+1
750  nyw = jec-jsc+1
751  else
752  if( .not. present(is_in) .or. .not. present(ie_in) .or. .not. present(js_in) .or. .not. present(je_in) ) then
753  call mpp_error(fatal, 'time_interp_external: is_in, ie_in, js_in and je_in must be present ' // &
754  'when numwindows > 1, field='//trim(field(index)%name))
755  endif
756  nxw = ie_in - is_in + 1
757  nyw = je_in - js_in + 1
758  isc = isc + is_in - 1
759  iec = isc + ie_in - is_in
760  jsc = jsc + js_in - 1
761  jec = jsc + je_in - js_in
762  endif
763 
764  isw = (nx-nxw)/2+1; iew = isw+nxw-1
765  jsw = (ny-nyw)/2+1; jew = jsw+nyw-1
766 
767  if (nx < nxw .or. ny < nyw .or. nz < field(index)%siz(3)) then
768  write(message1,'(i6,2i5)') nx,ny,nz
769  call mpp_error(fatal,'field '//trim(field(index)%name)//' Array size mismatch in time_interp_external.'// &
770  ' Array "data" is too small. shape(data)='//message1)
771  endif
772  if(PRESENT(mask_out)) then
773  if (size(mask_out,1) /= nx .or. size(mask_out,2) /= ny .or. size(mask_out,3) /= nz) then
774  write(message1,'(i6,2i5)') nx,ny,nz
775  write(message2,'(i6,2i5)') size(mask_out,1),size(mask_out,2),size(mask_out,3)
776  call mpp_error(fatal,'field '//trim(field(index)%name)//' array size mismatch in time_interp_external.'// &
777  ' Shape of array "mask_out" does not match that of array "data".'// &
778  ' shape(data)='//message1//' shape(mask_out)='//message2)
779  endif
780  endif
781 
782  if (field(index)%siz(4) == 1) then
783  ! only one record in the file => time-independent field
784  call load_record(field(index),1,horz_interp, is_in, ie_in ,js_in, je_in,window_id)
785  i1 = find_buf_index(1,field(index)%ibuf)
786  if( field(index)%region_type == no_region ) then
787  where(field(index)%mask(isc:iec,jsc:jec,:,i1))
788  data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)
789  elsewhere
790 ! data(isw:iew,jsw:jew,:) = time_interp_missing !field(index)%missing? Balaji
791  data(isw:iew,jsw:jew,:) = field(index)%missing
792  end where
793  else
794  where(field(index)%mask(isc:iec,jsc:jec,:,i1))
795  data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)
796  end where
797  endif
798  if(PRESENT(mask_out)) &
799  mask_out(isw:iew,jsw:jew,:) = field(index)%mask(isc:iec,jsc:jec,:,i1)
800  else
801  if(field(index)%have_modulo_times) then
802  call time_interp(time,field(index)%modulo_time_beg, field(index)%modulo_time_end, field(index)%time(:), &
803  w2, t1, t2, field(index)%correct_leap_year_inconsistency, err_msg=err_msg)
804  if(err_msg .NE. '') then
805  filename = mpp_get_file_name(field(index)%unit)
806  call mpp_error(fatal,"time_interp_external 1: "//trim(err_msg)//&
807  ",file="//trim(filename)//",field="//trim(field(index)%name) )
808  endif
809  else
810  if(field(index)%modulo_time) then
811  mod_time=1
812  else
813  mod_time=0
814  endif
815  call time_interp(time,field(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg)
816  if(err_msg .NE. '') then
817  filename = mpp_get_file_name(field(index)%unit)
818  call mpp_error(fatal,"time_interp_external 2: "//trim(err_msg)//&
819  ",file="//trim(filename)//",field="//trim(field(index)%name) )
820  endif
821  endif
822  w1 = 1.0-w2
823  if (verb) then
824  call get_date(time,yy,mm,dd,hh,min,ss)
825  write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') &
826  'target time yyyy/mm/dd hh:mm:ss= ',yy,'/',mm,'/',dd,hh,':',min,':',ss
827  write(outunit,*) 't1, t2, w1, w2= ', t1, t2, w1, w2
828  endif
829 
830  call load_record(field(index),t1,horz_interp, is_in, ie_in ,js_in, je_in, window_id)
831  call load_record(field(index),t2,horz_interp, is_in, ie_in ,js_in, je_in, window_id)
832  i1 = find_buf_index(t1,field(index)%ibuf)
833  i2 = find_buf_index(t2,field(index)%ibuf)
834  if(i1<0.or.i2<0) &
835  call mpp_error(fatal,'time_interp_external : records were not loaded correctly in memory')
836 
837  if (verb) then
838  write(outunit,*) 'ibuf= ',field(index)%ibuf
839  write(outunit,*) 'i1,i2= ',i1, i2
840  endif
841 
842  if( field(index)%region_type == no_region ) then
843  where(field(index)%mask(isc:iec,jsc:jec,:,i1).and.field(index)%mask(isc:iec,jsc:jec,:,i2))
844  data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)*w1 + &
845  field(index)%data(isc:iec,jsc:jec,:,i2)*w2
846  elsewhere
847 ! data(isw:iew,jsw:jew,:) = time_interp_missing !field(index)%missing? Balaji
848  data(isw:iew,jsw:jew,:) = field(index)%missing
849  end where
850  else
851  where(field(index)%mask(isc:iec,jsc:jec,:,i1).and.field(index)%mask(isc:iec,jsc:jec,:,i2))
852  data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)*w1 + &
853  field(index)%data(isc:iec,jsc:jec,:,i2)*w2
854  end where
855  endif
856  if(PRESENT(mask_out)) &
857  mask_out(isw:iew,jsw:jew,:) = &
858  field(index)%mask(isc:iec,jsc:jec,:,i1).and.&
859  field(index)%mask(isc:iec,jsc:jec,:,i2)
860  endif
861 
862  end subroutine time_interp_external_3d
863 !</SUBROUTINE> NAME="time_interp_external"
864 
865  subroutine time_interp_external_0d(index, time, data, verbose)
867  integer, intent(in) :: index
868  type(time_type), intent(in) :: time
869  real, intent(inout) :: data
870  logical, intent(in), optional :: verbose
871 
872  integer :: t1, t2
873  integer :: i1, i2, mod_time
874  integer :: yy, mm, dd, hh, min, ss
875  character(len=256) :: err_msg, filename
876 
877  real :: w1,w2
878  logical :: verb
879 
880  verb=.false.
881  if (PRESENT(verbose)) verb=verbose
882  if (debug_this_module) verb = .true.
883 
884  if (index < 1.or.index > num_fields) &
885  call mpp_error(fatal,'invalid index in call to time_interp_ext -- field was not initialized or failed to initialize')
886 
887  if (field(index)%siz(4) == 1) then
888  ! only one record in the file => time-independent field
889  call load_record_0d(field(index),1)
890  i1 = find_buf_index(1,field(index)%ibuf)
891  data = field(index)%data(1,1,1,i1)
892  else
893  if(field(index)%have_modulo_times) then
894  call time_interp(time,field(index)%modulo_time_beg, field(index)%modulo_time_end, field(index)%time(:), &
895  w2, t1, t2, field(index)%correct_leap_year_inconsistency, err_msg=err_msg)
896  if(err_msg .NE. '') then
897  filename = mpp_get_file_name(field(index)%unit)
898  call mpp_error(fatal,"time_interp_external 3:"//trim(err_msg)//&
899  ",file="//trim(filename)//",field="//trim(field(index)%name) )
900  endif
901  else
902  if(field(index)%modulo_time) then
903  mod_time=1
904  else
905  mod_time=0
906  endif
907  call time_interp(time,field(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg)
908  if(err_msg .NE. '') then
909  filename = mpp_get_file_name(field(index)%unit)
910  call mpp_error(fatal,"time_interp_external 4:"//trim(err_msg)// &
911  ",file="//trim(filename)//",field="//trim(field(index)%name) )
912  endif
913  endif
914  w1 = 1.0-w2
915  if (verb) then
916  call get_date(time,yy,mm,dd,hh,min,ss)
917  write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') &
918  'target time yyyy/mm/dd hh:mm:ss= ',yy,'/',mm,'/',dd,hh,':',min,':',ss
919  write(outunit,*) 't1, t2, w1, w2= ', t1, t2, w1, w2
920  endif
921  call load_record_0d(field(index),t1)
922  call load_record_0d(field(index),t2)
923  i1 = find_buf_index(t1,field(index)%ibuf)
924  i2 = find_buf_index(t2,field(index)%ibuf)
925 
926  if(i1<0.or.i2<0) &
927  call mpp_error(fatal,'time_interp_external : records were not loaded correctly in memory')
928  data = field(index)%data(1,1,1,i1)*w1 + field(index)%data(1,1,1,i2)*w2
929  if (verb) then
930  write(outunit,*) 'ibuf= ',field(index)%ibuf
931  write(outunit,*) 'i1,i2= ',i1, i2
932  endif
933  endif
934 
935  end subroutine time_interp_external_0d
936 
937  subroutine set_time_modulo(Time)
939  type(time_type), intent(inout), dimension(:) :: time
940 
941  integer :: ntime, n
942  integer :: yr, mon, dy, hr, minu, sec
943 
944  ntime = size(time(:))
945 
946  do n = 1, ntime
947  call get_date(time(n), yr, mon, dy, hr, minu, sec)
948  yr = modulo_year
949  time(n) = set_date(yr, mon, dy, hr, minu, sec)
950  enddo
951 
952 
953  end subroutine set_time_modulo
954 
955 ! ============================================================================
956 ! load specified record from file
957 subroutine load_record(field, rec, interp, is_in, ie_in, js_in, je_in, window_id_in)
958  type(ext_fieldtype), intent(inout) :: field
959  integer , intent(in) :: rec ! record number
960  type(horiz_interp_type), intent(in), optional :: interp
961  integer, intent(in), optional :: is_in, ie_in, js_in, je_in
962  integer, intent(in), optional :: window_id_in
963 
964  ! ---- local vars
965  integer :: ib ! index in the array of input buffers
966  integer :: isw,iew,jsw,jew ! boundaries of the domain on each window
967  integer :: is_region, ie_region, js_region, je_region, i, j, n
968  integer :: start(4), nread(4)
969  logical :: need_compute
970  real :: mask_in(size(field%src_data,1),size(field%src_data,2),size(field%src_data,3))
971  real, allocatable :: mask_out(:,:,:)
972  integer :: window_id
973 
974  window_id = 1
975  if( PRESENT(window_id_in) ) window_id = window_id_in
976  need_compute = .true.
977 
978 !$OMP CRITICAL
979  ib = find_buf_index(rec,field%ibuf)
980 
981  if(ib>0) then
982  !--- do nothing
983  need_compute = .false.
984  else
985  ! calculate current buffer number in round-robin fasion
986  field%nbuf = field%nbuf + 1
987  if(field%nbuf > size(field%data,4).or.field%nbuf <= 0) field%nbuf = 1
988  ib = field%nbuf
989  field%ibuf(ib) = rec
990  field%need_compute(ib,:) = .true.
991 
992  if (field%domain_present .and. .not.PRESENT(interp)) then
993  if (debug_this_module) write(outunit,*) 'reading record with domain for field ',trim(field%name)
994  call mpp_read(field%unit,field%field,field%domain,field%src_data(:,:,:,ib),rec)
995  else
996  if (debug_this_module) write(outunit,*) 'reading record without domain for field ',trim(field%name)
997  start = 1; nread = 1
998  start(1) = field%is_src; nread(1) = field%ie_src - field%is_src + 1
999  start(2) = field%js_src; nread(2) = field%je_src - field%js_src + 1
1000  start(3) = 1; nread(3) = size(field%src_data,3)
1001  start(field%tdim) = rec; nread(field%tdim) = 1
1002  call mpp_read(field%unit,field%field,field%src_data(:,:,:,ib),start,nread)
1003  endif
1004  endif
1005 !$OMP END CRITICAL
1006  isw=field%isc;iew=field%iec
1007  jsw=field%jsc;jew=field%jec
1008 
1009  if( field%numwindows > 1) then
1010  if( .NOT. PRESENT(is_in) .OR. .NOT. PRESENT(ie_in) .OR. .NOT. PRESENT(js_in) .OR. .NOT. PRESENT(je_in) ) then
1011  call mpp_error(fatal, 'time_interp_external(load_record): is_in, ie_in, js_in, je_in must be present when numwindows>1')
1012  endif
1013  isw = isw + is_in - 1
1014  iew = isw + ie_in - is_in
1015  jsw = jsw + js_in - 1
1016  jew = jsw + je_in - js_in
1017  endif
1018 
1019  ! interpolate to target grid
1020 
1021  need_compute = field%need_compute(ib, window_id)
1022  if(need_compute) then
1023  if(PRESENT(interp)) then
1024  is_region = field%is_region; ie_region = field%ie_region
1025  js_region = field%js_region; je_region = field%je_region
1026  mask_in = 0.0
1027  where (mpp_is_valid(field%src_data(:,:,:,ib), field%valid)) mask_in = 1.0
1028  if ( field%region_type .NE. no_region ) then
1029  if( any(mask_in == 0.0) ) then
1030  call mpp_error(fatal, "time_interp_external: mask_in should be all 1 when region_type is not NO_REGION")
1031  endif
1032  if( field%region_type == outside_region) then
1033  do j = js_region, je_region
1034  do i = is_region, ie_region
1035  mask_in(i,j,:) = 0.0
1036  enddo
1037  enddo
1038  else ! field%region_choice == INSIDE_REGION
1039  do j = 1, size(mask_in,2)
1040  do i = 1, size(mask_in,1)
1041  if( j<js_region .OR. j>je_region .OR. i<is_region .OR. i>ie_region ) mask_in(i,j,:) = 0.0
1042  enddo
1043  enddo
1044  endif
1045  endif
1046  allocate(mask_out(isw:iew,jsw:jew, size(field%src_data,3)))
1047  call horiz_interp(interp,field%src_data(:,:,:,ib),field%data(isw:iew,jsw:jew,:,ib), &
1048  mask_in=mask_in, &
1049  mask_out=mask_out)
1050 
1051  field%mask(isw:iew,jsw:jew,:,ib) = mask_out(isw:iew,jsw:jew,:) > 0
1052  deallocate(mask_out)
1053  field%need_compute(ib, window_id) = .false.
1054  else
1055  if ( field%region_type .NE. no_region ) then
1056  call mpp_error(fatal, "time_interp_external: region_type should be NO_REGION when interp is not present")
1057  endif
1058  field%data(isw:iew,jsw:jew,:,ib) = field%src_data(isw:iew,jsw:jew,:,ib)
1059  field%mask(isw:iew,jsw:jew,:,ib) = mpp_is_valid(field%data(isw:iew,jsw:jew,:,ib),field%valid)
1060  endif
1061  ! convert units
1062  where(field%mask(isw:iew,jsw:jew,:,ib)) field%data(isw:iew,jsw:jew,:,ib) = &
1063  field%data(isw:iew,jsw:jew,:,ib)*field%slope + field%intercept
1064  endif
1065 
1066 end subroutine load_record
1067 
1068 
1069 subroutine load_record_0d(field, rec)
1070  type(ext_fieldtype), intent(inout) :: field
1071  integer , intent(in) :: rec ! record number
1072  ! ---- local vars
1073  integer :: ib ! index in the array of input buffers
1074  integer :: start(4), nread(4)
1075 
1076  ib = find_buf_index(rec,field%ibuf)
1077 
1078  if(ib>0) then
1079  return
1080  else
1081  ! calculate current buffer number in round-robin fasion
1082  field%nbuf = field%nbuf + 1
1083  if(field%nbuf > size(field%data,4).or.field%nbuf <= 0) field%nbuf = 1
1084  ib = field%nbuf
1085  field%ibuf(ib) = rec
1086 
1087  if (debug_this_module) write(outunit,*) 'reading record without domain for field ',trim(field%name)
1088  start = 1; nread = 1
1089  start(3) = 1; nread(3) = size(field%src_data,3)
1090  start(field%tdim) = rec; nread(field%tdim) = 1
1091  call mpp_read(field%unit,field%field,field%src_data(:,:,:,ib),start,nread)
1092  if ( field%region_type .NE. no_region ) then
1093  call mpp_error(fatal, "time_interp_external: region_type should be NO_REGION when field is scalar")
1094  endif
1095  field%data(1,1,:,ib) = field%src_data(1,1,:,ib)
1096  field%mask(1,1,:,ib) = mpp_is_valid(field%data(1,1,:,ib),field%valid)
1097  ! convert units
1098  where(field%mask(1,1,:,ib)) field%data(1,1,:,ib) = &
1099  field%data(1,1,:,ib)*field%slope + field%intercept
1100  endif
1101 
1102 end subroutine load_record_0d
1103 
1104 ! ============================================================================
1105 subroutine reset_src_data_region(index, is, ie, js, je)
1106  integer, intent(in) :: index
1107  integer, intent(in) :: is, ie, js, je
1108  integer :: nk, nbuf
1109 
1110  if( is == field(index)%is_src .AND. ie == field(index)%ie_src .AND. &
1111  js == field(index)%js_src .AND. ie == field(index)%je_src ) return
1112 
1113  if( .NOT. ASSOCIATED(field(index)%src_data) ) call mpp_error(fatal, &
1114  "time_interp_external: field(index)%src_data is not associated")
1115  nk = size(field(index)%src_data,3)
1116  nbuf = size(field(index)%src_data,4)
1117  deallocate(field(index)%src_data)
1118  allocate(field(index)%src_data(is:ie,js:je,nk,nbuf))
1119  field(index)%is_src = is
1120  field(index)%ie_src = ie
1121  field(index)%js_src = js
1122  field(index)%je_src = je
1123 
1124 
1125 end subroutine reset_src_data_region
1126 
1127 ! ============================================================================
1128 subroutine set_override_region(index, region_type, is_region, ie_region, js_region, je_region)
1129  integer, intent(in) :: index, region_type
1130  integer, intent(in) :: is_region, ie_region, js_region, je_region
1131 
1132  field(index)%region_type = region_type
1133  field(index)%is_region = is_region
1134  field(index)%ie_region = ie_region
1135  field(index)%js_region = js_region
1136  field(index)%je_region = je_region
1137 
1138  return
1139 
1140 end subroutine set_override_region
1141 
1142 ! ============================================================================
1143 ! reallocates array of fields, increasing its size
1144 subroutine realloc_files(n)
1145  integer, intent(in) :: n ! new size
1146 
1147  type(filetype), pointer :: ptr(:)
1148  integer :: i
1149 
1150  if (associated(opened_files)) then
1151  if (n <= size(opened_files)) return ! do nothing, if requested size no more than current
1152  endif
1153 
1154  allocate(ptr(n))
1155  do i = 1, size(ptr)
1156  ptr(i)%filename = ''
1157  ptr(i)%unit = -1
1158  enddo
1159 
1160  if (associated(opened_files))then
1161  ptr(1:size(opened_files)) = opened_files(:)
1162  deallocate(opened_files)
1163  endif
1164  opened_files => ptr
1165 
1166 end subroutine realloc_files
1167 
1168 ! ============================================================================
1169 ! reallocates array of fields,increasing its size
1170 subroutine realloc_fields(n)
1171  integer, intent(in) :: n ! new size
1172 
1173  type(ext_fieldtype), pointer :: ptr(:)
1174  integer :: i, ier
1175 
1176  if (associated(field)) then
1177  if (n <= size(field)) return ! do nothing if requested size no more then current
1178  endif
1179 
1180  allocate(ptr(n))
1181  do i=1,size(ptr)
1182  ptr(i)%unit=-1
1183  ptr(i)%name=''
1184  ptr(i)%units=''
1185  ptr(i)%siz=-1
1186  ptr(i)%ndim=-1
1187  ptr(i)%domain = null_domain2d
1188  ptr(i)%axes(:) = default_axis
1189  if (ASSOCIATED(ptr(i)%time)) DEALLOCATE(ptr(i)%time, stat=ier)
1190  if (ASSOCIATED(ptr(i)%start_time)) DEALLOCATE(ptr(i)%start_time, stat=ier)
1191  if (ASSOCIATED(ptr(i)%end_time)) DEALLOCATE(ptr(i)%end_time, stat=ier)
1192  ptr(i)%field = default_field
1193  if (ASSOCIATED(ptr(i)%period)) DEALLOCATE(ptr(i)%period, stat=ier)
1194  ptr(i)%modulo_time=.false.
1195  if (ASSOCIATED(ptr(i)%data)) DEALLOCATE(ptr(i)%data, stat=ier)
1196  if (ASSOCIATED(ptr(i)%ibuf)) DEALLOCATE(ptr(i)%ibuf, stat=ier)
1197  if (ASSOCIATED(ptr(i)%src_data)) DEALLOCATE(ptr(i)%src_data, stat=ier)
1198  ptr(i)%nbuf=-1
1199  ptr(i)%domain_present=.false.
1200  ptr(i)%slope=1.0
1201  ptr(i)%intercept=0.0
1202  ptr(i)%isc=-1;ptr(i)%iec=-1
1203  ptr(i)%jsc=-1;ptr(i)%jec=-1
1204  enddo
1205  if (associated(field)) then
1206  ptr(1:size(field)) = field(:)
1207  deallocate(field)
1208  endif
1209  field=>ptr
1210 
1211 end subroutine realloc_fields
1212 
1213 
1214  function find_buf_index(indx,buf)
1215  integer :: indx
1216  integer, dimension(:) :: buf
1217  integer :: find_buf_index
1218 
1219  integer :: nbuf, i
1220 
1221  nbuf = size(buf(:))
1222 
1223  find_buf_index = -1
1224 
1225  do i=1,nbuf
1226  if (buf(i) == indx) then
1227  find_buf_index = i
1228  exit
1229  endif
1230  enddo
1231 
1232  end function find_buf_index
1233 
1234 !<FUNCTION NAME="get_external_field_size" TYPE="integer" DIM="(4)">
1235 !
1236 !<DESCRIPTION>
1237 ! return size of field after call to init_external_field.
1238 ! Ordering is X/Y/Z/T.
1239 ! This call only makes sense for non-distributed reads.
1240 !</DESCRIPTION>
1241 !
1242 !<IN NAME="index" TYPE="integer">
1243 ! returned from previous call to init_external_field.
1244 !</IN>
1245 
1246  function get_external_field_size(index)
1248  integer :: index
1249  integer :: get_external_field_size(4)
1250 
1251  if (index .lt. 1 .or. index .gt. num_fields) &
1252  call mpp_error(fatal,'invalid index in call to get_external_field_size')
1253 
1254 
1255  get_external_field_size(1) = field(index)%siz(1)
1256  get_external_field_size(2) = field(index)%siz(2)
1257  get_external_field_size(3) = field(index)%siz(3)
1258  get_external_field_size(4) = field(index)%siz(4)
1259 
1260  end function get_external_field_size
1261 !</FUNCTION> NAME="get_external_field_size"
1262 
1263 
1264 !<FUNCTION NAME="get_external_field_missing" TYPE="real">
1265 !
1266 !<DESCRIPTION>
1267 ! return missing value
1268 !</DESCRIPTION>
1269 !
1270 !<IN NAME="index" TYPE="integer">
1271 ! returned from previous call to init_external_field.
1272 !</IN>
1273 
1274  function get_external_field_missing(index)
1276  integer :: index
1277  real :: missing
1279 
1280  if (index .lt. 1 .or. index .gt. num_fields) &
1281  call mpp_error(fatal,'invalid index in call to get_external_field_size')
1282 
1283 
1284 ! call mpp_get_atts(field(index)%field,missing=missing)
1285  get_external_field_missing = field(index)%missing
1286 
1287  end function get_external_field_missing
1288 !</FUNCTION> NAME="get_external_field_missing"
1289 
1290 !<FUNCTION NAME="get_external_field_axes" TYPE="axistype" DIM="(4)">
1291 !
1292 !<DESCRIPTION>
1293 ! return field axes after call to init_external_field.
1294 ! Ordering is X/Y/Z/T.
1295 !</DESCRIPTION>
1296 !
1297 !<IN NAME="index" TYPE="integer">
1298 ! returned from previous call to init_external_field.
1299 !</IN>
1300 
1301 
1302  function get_external_field_axes(index)
1304  integer :: index
1305  type(axistype), dimension(4) :: get_external_field_axes
1306 
1307  if (index .lt. 1 .or. index .gt. num_fields) &
1308  call mpp_error(fatal,'invalid index in call to get_external_field_size')
1309 
1310 
1311  get_external_field_axes(1) = field(index)%axes(1)
1312  get_external_field_axes(2) = field(index)%axes(2)
1313  get_external_field_axes(3) = field(index)%axes(3)
1314  get_external_field_axes(4) = field(index)%axes(4)
1315 
1316  end function get_external_field_axes
1317 !</FUNCTION> NAME="get_external_field_axes"
1318 
1319 ! ===========================================================================
1320 subroutine get_time_axis(index, time)
1321  integer , intent(in) :: index ! field id
1322  type(time_type), intent(out) :: time(:) ! array of time values to be filled
1323 
1324  integer :: n ! size of the data to be assigned
1325 
1326  if (index < 1.or.index > num_fields) &
1327  call mpp_error(fatal,'invalid index in call to get_time_axis')
1328 
1329  n = min(size(time),size(field(index)%time))
1330 
1331  time(1:n) = field(index)%time(1:n)
1332 end subroutine
1333 
1334 !<SUBROUTINE NAME="time_interp_external_exit">
1335 !
1336 !<DESCRIPTION>
1337 ! exit time_interp_external_mod. Close all open files and
1338 ! release storage
1339 !</DESCRIPTION>
1340 
1341  subroutine time_interp_external_exit()
1343  integer :: i,j
1344 !
1345 ! release storage arrays
1346 !
1347  do i=1,num_fields
1348  deallocate(field(i)%time,field(i)%start_time,field(i)%end_time,&
1349  field(i)%period,field(i)%data,field(i)%mask,field(i)%ibuf)
1350  if (ASSOCIATED(field(i)%src_data)) deallocate(field(i)%src_data)
1351  do j=1,4
1352  field(i)%axes(j) = default_axis
1353  enddo
1354  field(i)%domain = null_domain2d
1355  field(i)%field = default_field
1356  field(i)%nbuf = 0
1357  field(i)%slope = 0.
1358  field(i)%intercept = 0.
1359  enddo
1360 
1361  deallocate(field)
1362  deallocate(opened_files)
1363 
1364  num_fields = 0
1365 
1366  module_initialized = .false.
1367 
1368  end subroutine time_interp_external_exit
1369 !</SUBROUTINE> NAME="time_interp_external_exit"
1370 
1371 end module time_interp_external_mod
1372 
1373 #ifdef test_time_interp_external
1374 
1375 program test_time_interp_ext
1376 use constants_mod, only: constants_init
1377 use fms_mod, only: open_namelist_file, check_nml_error
1378 use mpp_mod, only : mpp_init, mpp_exit, mpp_npes, stdout, stdlog, fatal, mpp_error
1379 use mpp_mod, only : input_nml_file
1380 use mpp_io_mod, only : mpp_io_init, mpp_io_exit, mpp_open, mpp_rdonly, mpp_ascii, mpp_close, &
1381  axistype, mpp_get_axis_data
1382 use mpp_domains_mod, only : mpp_domains_init, domain2d, mpp_define_layout, mpp_define_domains,&
1384  mpp_domains_set_stack_size
1388  noleap
1391 implicit none
1392 
1393 
1394 
1395 integer :: id, i, io_status, unit, ierr
1396 character(len=128) :: filename, fieldname
1397 type(time_type) :: time
1398 real, allocatable, dimension(:,:,:) :: data_d, data_g
1399 logical, allocatable, dimension(:,:,:) :: mask_d
1400 type(domain2d) :: domain, domain_out
1401 integer :: layout(2), fld_size(4)
1402 integer :: isc, iec, jsc, jec, isd, ied, jsd, jed
1403 integer :: yy, mm, dd, hh, ss
1404 real :: sm,mx,mn
1405 character(len=12) :: cal_type
1406 integer :: ntime=12,year0=1991,month0=1,day0=1,days_inc=31
1407 type(horiz_interp_type) :: hinterp
1408 type(axistype) :: axis_centers(4), axis_bounds(4)
1409 real :: lon_out(180,89), lat_out(180,89)
1410 real, allocatable, dimension(:,:) :: lon_local_out, lat_local_out
1411 real, allocatable, dimension(:) :: lon_in, lat_in
1412 integer :: isc_o, iec_o, jsc_o, jec_o, outunit
1413 
1414 namelist /test_time_interp_ext_nml/ filename, fieldname,ntime,year0,month0,&
1415  day0,days_inc, cal_type
1416 
1417 call constants_init
1418 call mpp_init
1419 call mpp_io_init
1420 call mpp_domains_init
1422 call time_manager_init
1423 call horiz_interp_init
1424 
1425 #ifdef INTERNAL_FILE_NML
1426  read (input_nml_file, test_time_interp_ext_nml, iostat=io_status)
1427  ierr = check_nml_error(io_status, 'test_time_interp_ext_nml')
1428 #else
1429  unit = open_namelist_file()
1430  ierr=1; do while (ierr /= 0)
1431  read (unit, nml=test_time_interp_ext_nml, iostat=io_status, end=10)
1432  ierr = check_nml_error(io_status, 'test_time_interp_ext_nml')
1433  enddo
1434 10 call close_file (unit)
1435 #endif
1436 
1437 outunit = stdlog()
1438 write(outunit,test_time_interp_ext_nml)
1439 
1440 select case (trim(cal_type))
1441 case ('julian')
1443 case ('no_leap')
1445 case default
1446  call mpp_error(fatal,'invalid calendar type')
1447 end select
1448 
1449 outunit = stdout()
1450 write(outunit,*) 'INTERPOLATING NON DECOMPOSED FIELDS'
1451 write(outunit,*) '======================================'
1452 
1454 
1455 id = init_external_field(filename,fieldname,verbose=.true.)
1456 
1457 fld_size = get_external_field_size(id)
1458 
1459 allocate(data_g(fld_size(1),fld_size(2),fld_size(3)))
1460 data_g = 0
1461 
1462 time = set_date(year0,month0,day0,0,0,0)
1463 
1464 do i=1,ntime
1465  call time_interp_external(id,time,data_g,verbose=.true.)
1466  sm = sum(data_g)
1467  mn = minval(data_g)
1468  mx = maxval(data_g)
1469  write(outunit,*) 'sum= ', sm
1470  write(outunit,*) 'max= ', mx
1471  write(outunit,*) 'min= ', mn
1472  time = increment_time(time,0,days_inc)
1473 enddo
1474 
1475 call mpp_define_layout((/1,fld_size(1),1,fld_size(2)/),mpp_npes(),layout)
1476 call mpp_define_domains((/1,fld_size(1),1,fld_size(2)/),layout,domain)
1477 call mpp_get_compute_domain(domain,isc,iec,jsc,jec)
1478 call mpp_get_compute_domain(domain,isd,ied,jsd,jed)
1479 
1480 call mpp_domains_set_stack_size(fld_size(1)*fld_size(2)*min(fld_size(3),1)*2)
1481 allocate(data_d(isd:ied,jsd:jed,fld_size(3)))
1482 data_d = 0
1483 
1484 write(outunit,*) 'INTERPOLATING DOMAIN DECOMPOSED FIELDS'
1485 write(outunit,*) '======================================'
1486 
1487 id = init_external_field(filename,fieldname,domain=domain, verbose=.true.)
1488 
1489 time = set_date(year0,month0,day0)
1490 
1491 do i=1,ntime
1492  call time_interp_external(id,time,data_d,verbose=.true.)
1493  sm = mpp_global_sum(domain,data_d,flags=bitwise_exact_sum)
1494  mx = mpp_global_max(domain,data_d)
1495  mn = mpp_global_min(domain,data_d)
1496  write(outunit,*) 'global sum= ', sm
1497  write(outunit,*) 'global max= ', mx
1498  write(outunit,*) 'global min= ', mn
1499  time = increment_time(time,0,days_inc)
1500 enddo
1501 
1502 write(outunit,*) 'INTERPOLATING DOMAIN DECOMPOSED FIELDS USING HORIZ INTERP'
1503 write(outunit,*) '======================================'
1504 
1505 
1506 ! define a global 2 degree output grid
1507 
1508 do i=1,180
1509  lon_out(i,:) = 2.0*i*atan(1.0)/45.0
1510 enddo
1511 
1512 do i=1,89
1513  lat_out(:,i) = (i-45)*2.0*atan(1.0)/45.0
1514 enddo
1515 
1516 call mpp_define_layout((/1,180,1,89/),mpp_npes(),layout)
1517 call mpp_define_domains((/1,180,1,89/),layout,domain_out)
1518 call mpp_get_compute_domain(domain_out,isc_o,iec_o,jsc_o,jec_o)
1519 
1520 id = init_external_field(filename,fieldname,domain=domain_out,axis_centers=axis_centers,&
1521  verbose=.true., override=.true.)
1522 
1523 allocate (lon_local_out(isc_o:iec_o,jsc_o:jec_o))
1524 allocate (lat_local_out(isc_o:iec_o,jsc_o:jec_o))
1525 
1526 lon_local_out(isc_o:iec_o,jsc_o:jec_o) = lon_out(isc_o:iec_o,jsc_o:jec_o)
1527 lat_local_out(isc_o:iec_o,jsc_o:jec_o) = lat_out(isc_o:iec_o,jsc_o:jec_o)
1528 
1529 call get_axis_bounds(axis_centers(1), axis_bounds(1), axis_centers)
1530 call get_axis_bounds(axis_centers(2), axis_bounds(2), axis_centers)
1531 
1532 allocate(lon_in(fld_size(1)+1))
1533 allocate(lat_in(fld_size(2)+1))
1534 
1535 call mpp_get_axis_data(axis_bounds(1), lon_in) ; lon_in = lon_in*atan(1.0)/45
1536 call mpp_get_axis_data(axis_bounds(2), lat_in) ; lat_in = lat_in*atan(1.0)/45
1537 
1538 call horiz_interp_new(hinterp,lon_in,lat_in, lon_local_out, lat_local_out, &
1539  interp_method='bilinear')
1540 
1541 time = set_date(year0,month0,day0)
1542 
1543 deallocate(data_d)
1544 allocate(data_d(isc_o:iec_o,jsc_o:jec_o,fld_size(3)))
1545 allocate(mask_d(isc_o:iec_o,jsc_o:jec_o,fld_size(3)))
1546 do i=1,ntime
1547  data_d = 0
1548  call time_interp_external(id,time,data_d,verbose=.true.,horz_interp=hinterp, mask_out=mask_d)
1549  sm = mpp_global_sum(domain_out,data_d,flags=bitwise_exact_sum)
1550  mx = mpp_global_max(domain_out,data_d)
1551  mn = mpp_global_min(domain_out,data_d)
1552  write(outunit,*) 'global sum= ', sm
1553  write(outunit,*) 'global max= ', mx
1554  write(outunit,*) 'global min= ', mn
1555 
1556  where(mask_d)
1557  data_d = 1.0
1558  elsewhere
1559  data_d = 0.0
1560  endwhere
1561  sm = mpp_global_sum(domain_out,data_d,flags=bitwise_exact_sum)
1562  write(outunit,*) 'n valid points= ', sm
1563 
1564  time = increment_time(time,0,days_inc)
1565 enddo
1566 
1567 call horiz_interp_del(hinterp)
1568 
1569 
1571 
1572 
1573 call mpp_io_exit
1574 call mpp_exit
1575 stop
1576 
1577 end program test_time_interp_ext
1578 #endif
Definition: fms.F90:20
subroutine, public time_interp_init()
type(time_type) function, public increment_time(Time, seconds, days, ticks, err_msg, allow_neg_inc)
subroutine, public get_date(time, year, month, day, hour, minute, second, tick, err_msg)
type(ext_fieldtype), dimension(:), pointer, save, private field
subroutine, public get_time_axis(index, time)
integer, parameter, public noleap
subroutine, public time_interp_external_init()
subroutine time_interp_external_0d(index, time, data, verbose)
subroutine, public horiz_interp_del(Interp)
integer function, private find_buf_index(indx, buf)
integer, parameter, public inside_region
logical function, public get_axis_modulo(axis)
Definition: axis_utils.F90:203
logical function, public get_axis_modulo_times(axis, tbeg, tend)
Definition: axis_utils.F90:225
type(time_type) function, public get_cal_time(time_increment, units, calendar, permit_calendar_conversion)
Definition: mpp.F90:39
integer function, public check_nml_error(IOSTAT, NML_NAME)
Definition: fms.F90:658
subroutine, public time_interp_external_exit()
subroutine time_interp_external_2d(index, time, data_in, interp, verbose, horz_interp, mask_out, is_in, ie_in, js_in, je_in, window_id)
character(len=input_str_length), dimension(:), allocatable, target, public input_nml_file
Definition: mpp.F90:1378
subroutine, public set_calendar_type(type, err_msg)
type(domain2d), save, public null_domain2d
integer function, public init_external_field(file, fieldname, format, threading, domain, desired_units, verbose, axis_centers, axis_sizes, override, correct_leap_year_inconsistency, permit_calendar_conversion, use_comp_domain, ierr, nwindows, ignore_axis_atts)
integer, parameter, private modulo_year
integer, parameter, private linear_time_interp
integer function, public days_in_month(Time, err_msg)
integer, parameter, public julian
subroutine, public get_axis_cart(axis, cart)
Definition: axis_utils.F90:79
integer function, public get_calendar_type()
subroutine load_record(field, rec, interp, is_in, ie_in, js_in, je_in, window_id_in)
type(axistype), save, public default_axis
Definition: mpp_io.F90:1071
integer, parameter, public success
integer function, dimension(4), public get_external_field_size(index)
integer, parameter, public err_field_not_found
subroutine, public time_manager_init()
subroutine, public horiz_interp_init
integer, parameter, public no_region
integer, parameter, public no_calendar
subroutine, public reset_src_data_region(index, is, ie, js, je)
integer, parameter r8_kind
Definition: platform.F90:24
subroutine, public set_override_region(index, region_type, is_region, ie_region, js_region, je_region)
subroutine, public get_axis_bounds(axis, axis_bound, axes, bnd_name, err_msg)
Definition: axis_utils.F90:149
type(axistype) function, dimension(4), public get_external_field_axes(index)
real(double_kind), parameter, private time_interp_missing
integer function, public days_in_year(Time)
type(filetype), dimension(:), pointer, save, private opened_files
subroutine, private set_time_modulo(Time)
#define min(a, b)
Definition: mosaic_util.h:32
subroutine, public get_time(Time, seconds, days, ticks, err_msg)
real function, public get_external_field_missing(index)
subroutine time_interp_external_3d(index, time, data, interp, verbose, horz_interp, mask_out, is_in, ie_in, js_in, je_in, window_id)
subroutine, public constants_init
dummy routine.
Definition: constants.F90:141
type(fieldtype), save, public default_field
Definition: mpp_io.F90:1072
subroutine load_record_0d(field, rec)
integer, parameter, public outside_region