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