1 !***********************************************************************
2 !* GNU Lesser General Public License
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
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.
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
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 !***********************************************************************
20 !! @brief unit tests for interpolator_mod
21 !! @author MiKyung Lee
22 !! @email gfdl.climate.model.info@noaa.gov
23 !! @description This program tests the various subroutines in interpolator_mod. The code in
24 !! in test_interpolator.F90 shows how to use interpolator_mod. This program contains the
28 program test_interpolator2
30 use fms2_io_mod, only: FmsNetcdfFile_t, UNLIMITED, &
31 register_field, register_variable_attribute, &
33 write_data, close_file, open_file
34 use mpp_mod, only: mpp_error, FATAL, WARNING
35 use time_manager_mod, only: time_type, set_calendar_type, time_manager_init, &
36 get_date_no_leap, get_date_julian, set_date, set_time, &
37 set_date_no_leap, set_date_julian, operator(/), &
38 operator(+), operator(-), time_type_to_real, increment_time, &
39 leap_year, days_in_month, print_date, print_time
40 use fms_mod, only: fms_init
41 use constants_mod, only: PI
42 use platform_mod, only: r4_kind, r8_kind
48 character(100), parameter :: ncfile='immadeup.o3.climatology.nc' !< fake climatology file
49 integer, parameter :: lkind=TEST_INTP_KIND_
50 !> the interpolation methods are not perfect.Will not get perfectly agreeing answers
51 real(r8_kind), parameter :: tol=0.1_lkind
52 integer :: calendar_type
54 !> climatology related variables and arrays (made up data)
55 integer :: nlonlat !< number of latitude and longitudinal center coordinates in file
56 integer :: nlonlatb !< number of latitude and longitudinal boundary coordinates in file
57 integer :: ntime !< number of time slices
58 integer :: npfull !< number of p levels
59 integer :: nphalf !< number of half p levels
60 real(TEST_INTP_KIND_), allocatable :: lat(:) !< climatology coordinates
61 real(TEST_INTP_KIND_), allocatable :: lon(:) !< climatology coordinates
62 real(TEST_INTP_KIND_), allocatable :: latb(:) !< climatology coordinates
63 real(TEST_INTP_KIND_), allocatable :: lonb(:) !< climatology coordinates
64 real(r8_kind), allocatable :: clim_time (:) !< climatology time
65 real(TEST_INTP_KIND_), allocatable :: pfull(:) !< climatology p level
66 real(TEST_INTP_KIND_), allocatable :: phalf(:) !< climatology p half level
67 real(TEST_INTP_KIND_), allocatable :: ozone(:,:,:,:) !< climatology ozone data
69 !> model related variables and arrays
70 integer :: nlonlat_mod, nlonlatb_mod !< number of latitude and longitude coordinates in the model
71 real(TEST_INTP_KIND_), allocatable :: lat_mod(:,:) !< model coordinates
72 real(TEST_INTP_KIND_), allocatable :: lon_mod(:,:) !< model coordinates
73 real(TEST_INTP_KIND_), allocatable :: latb_mod(:,:) !< model coordinates
74 real(TEST_INTP_KIND_), allocatable :: lonb_mod(:,:) !< model coordinates
76 !> array holding model times
77 type(time_type), allocatable :: model_time_julian(:), model_time_noleap(:)
79 type(interpolate_type) :: o3 !< recyclable interpolate_type
81 !> whether the file input is yearly, daily data
82 logical :: yearly, daily
85 logical :: test_file_daily_julian=.true., test_file_daily_noleap=.false.
86 logical :: test_file_yearly_noleap=.false., test_file_yearly_julian=.false.
87 logical :: test_file_no_time=.false.
88 integer :: nml_unit_var=99
89 character(*), parameter :: nml_file='test_interpolator.nml'
90 NAMELIST / test_interpolator_nml / test_file_daily_noleap, test_file_daily_julian, &
91 test_file_yearly_noleap, test_file_yearly_julian, test_file_no_time
93 open(unit=nml_unit_var, file=nml_file)
94 read(unit=nml_unit_var, nml=test_interpolator_nml)
98 call time_manager_init
102 call set_parameters_wrapper
103 call set_and_write_data
105 if(.not.test_file_no_time) then
106 !> test interpolator when model calendar is JULIAN
108 call set_calendar_type(calendar_type)
110 !---------------------------------------------------------
111 !> test interpolator when model calendar is NOLEAP
113 call set_calendar_type(calendar_type)
117 !> test interpolator_no_time_axis
118 if(test_file_no_time) then
119 write(*,*) 'test_intepolator_no_time_axis'
120 calendar_type=2 !< still need to set model calendar
121 call set_calendar_type(calendar_type)
122 call test_interpolator_init(o3)
123 call test_interpolator_no_time_axis(o3)
124 call test_interpolator_end(o3)
129 #include "test_interpolator_write_climatology.inc"
131 !===============================================!
132 subroutine test_interpolator_init(clim_type)
134 !> interopolator_init initializes the interpolate_type after reading in the
135 !! climatology data. The required arguments are clim_type, file_name, lonb_mod, latb_mod
136 !! where lonb_mod and latb_mod contain the model longitude and latitude values on the grid.
139 type(interpolate_type), intent(inout) :: clim_type
140 integer, dimension(1) :: data_out_of_bounds
142 data_out_of_bounds(1)=CONSTANT
143 call interpolator_init(clim_type,trim(ncfile), lonb_mod, latb_mod, data_out_of_bounds=data_out_of_bounds)
145 end subroutine test_interpolator_init
146 !===============================================!
147 subroutine test_interpolator(clim_type, model_time)
149 !> call the variants of interpolator (4D-2d) that interpolates data at a given time-point
150 !! The tests here do not test the "no_axis" interpolator routines
151 !! This subroutine also tests obtain_interpolator_time_slices for the 2D case.
155 type(interpolate_type), intent(inout) :: clim_type
156 type(time_type), dimension(ntime), intent(in) :: model_time
157 type(time_type) :: test_time
158 real(TEST_INTP_KIND_), dimension(nlonlat_mod,nlonlat_mod,npfull,1) :: interp_data !<only 1 field
159 real(TEST_INTP_KIND_), dimension(nlonlat_mod,nlonlat_mod,nphalf) :: phalf_in
160 integer :: itime, i, j, k, l
162 real(TEST_INTP_KIND_) :: answer
166 phalf_in(:,:,i)=phalf(i)
171 answer=0.5_lkind*ozone(1,1,1,itime-1)+0.5_lkind*ozone(1,1,1,itime)
172 test_time=model_time(itime-1) + (model_time(itime)-model_time(itime-1))/2
174 !> test interpolator_4D_r4/8
175 call interpolator(clim_type, test_time, phalf_in, interp_data, 'ozone')
179 call check_answers(interp_data(k,j,i,1), answer, tol, 'test interpolator_4D')
184 !> test interpolator_3_r4/8
185 call interpolator(clim_type, test_time, phalf_in, interp_data(:,:,:,1), 'ozone')
189 call check_answers(interp_data(k,j,i,1), answer, tol, 'test interpolator_3D')
194 !> test interpolator_2D_r4/8
195 call interpolator(clim_type, test_time, interp_data(:,:,1,1), 'ozone')
198 call check_answers(interp_data(k,j,1,1), answer, tol, 'test interpolator_2D')
202 !> Test obtain_interpolator_time_slices
203 call obtain_interpolator_time_slices(clim_type,test_time)
204 call interpolator(clim_type, test_time, interp_data(:,:,1,1), 'ozone')
205 call unset_interpolator_time_flag(clim_type)
208 call check_answers(interp_data(k,j,1,1), answer, tol, 'test interpolator_2D')
214 end subroutine test_interpolator
215 !===============================================!
216 subroutine test_interpolator_end(clim_type)
218 !> This subroutine tests interpolator_end
222 type(interpolate_type) :: clim_type
224 call interpolator_end(clim_type)
226 end subroutine test_interpolator_end
227 !===============================================!
228 subroutine test_interpolator_no_time_axis(clim_type)
230 !> This subroutine tests the variants (42-2D) of interpolator_no_time_axis
234 type(interpolate_type) :: clim_type
236 real(TEST_INTP_KIND_), dimension(nlonlat,nlonlat,npfull,1) :: interp_data !< last column, there is only one field
237 real(TEST_INTP_KIND_), dimension(nlonlat,nlonlat,nphalf) :: phalf_in
241 phalf_in(:,:,i)=phalf(i)
244 !> test interpolator_4D_no_time_axis_r4/8
245 call interpolator(clim_type, phalf_in, interp_data, 'ozone')
249 call check_answers(interp_data(k,j,i,1), ozone(k,j,i,1), tol, 'test interpolator_4D_no_time_axis')
254 !> test interpolator_3D_no_time_axis_r4/8
255 call interpolator(clim_type, phalf_in, interp_data(:,:,:,1), 'ozone')
259 call check_answers(interp_data(k,j,i,1), ozone(k,j,i,1), tol, 'test interpolator_3D_no_time_axis')
264 !> test interpolator_2D_no_time_axis_r4/8
265 call interpolator(clim_type, interp_data(:,:,1,1), 'ozone')
268 call check_answers(interp_data(k,j,1,1), ozone(k,j,1,1), tol, 'test interpolator_2D_no_time_axis')
272 end subroutine test_interpolator_no_time_axis
273 !===============================================!
274 subroutine test_interpolate_type_eq
276 !> This subroutine tests interpolaote_type_eq (assignment = operator)
277 !! The success of "=" is insured by checking to see if interpolation with o3_copy succeeds.
281 type(interpolate_type) :: o3_copy
284 if(calendar_type==2) call test_interpolator(o3_copy, model_time_julian)
285 if(calendar_type==4) call test_interpolator(o3_copy, model_time_noleap)
287 end subroutine test_interpolate_type_eq
288 !===============================================!
289 subroutine test_query_interpolator
291 !> This subroutne tests query_interpolator
295 character(100), parameter :: answer_field_name='ozone'
296 integer, parameter :: answer_nfields=1
298 character(100), allocatable :: field_names(:)
301 call query_interpolator(o3,nfields)
302 if( nfields .ne. answer_nfields) call mpp_error(FATAL, '')
304 allocate(field_names(nfields))
305 call query_interpolator(o3,nfields,field_names)
306 if(trim(answer_field_name).ne.trim(field_names(1))) call mpp_error(FATAL,'')
308 deallocate(field_names)
310 end subroutine test_query_interpolator
311 !===============================================!
312 subroutine run_test_set
317 if(calendar_type==2) write(*,*) "** MODEL CALENDAR JULIAN ** MODEL CALENDAR JULIAN ** MODEL CALENDAR JULIAN **"
318 if(calendar_type==4) write(*,*) "** MODEL CALENDAR NOLEAP ** MODEL CALENDAR NOLEAP ** MODEL CALENDAR NOLEAP **"
320 write(*,*) '1. interpolator_init'
321 call test_interpolator_init(o3)
323 !> test interpolator 2D-4D
324 write(*,*) '2. interpolator4D-2D'
325 if(calendar_type==2) call test_interpolator(o3, model_time_julian)
326 if(calendar_type==4) call test_interpolator(o3, model_time_noleap)
328 !> test interpolate_type_eq
329 !! This test has been commented out and will be included in the testing suite once fileobj cp is added into
330 write(*,*) '3. skipping interpolate_type_eq until bug in interpolator is fixed'
331 !call test_interpolate_type_eq()
333 !> test query_interpolator
334 write(*,*) '4. query_interpolator'
335 call test_query_interpolator()
337 !> test interpolator end
338 write(*,*) '5. interpolator_end'
339 call test_interpolator_end(o3)
341 end subroutine run_test_set
342 !===============================================!
343 subroutine check_answers(results, answers, tol, whoami)
346 real(TEST_INTP_KIND_), intent(in) :: results, answers
347 real(r8_kind), intent(in) :: tol
348 character(*) :: whoami
350 if (real(abs(results-answers),r8_kind).gt.tol) then
351 !if (results.ne.answers) then
352 write(*,*) ' EXPECTED ', answers, ' but computed ', results
353 call mpp_error(FATAL, trim(whoami))
356 end subroutine check_answers
357 !===============================================!
358 subroutine write_header
361 if(test_file_daily_noleap) &
362 write(*,"(////10x,a,i0////)") &
363 " ** DAILY FILE CAL NOLEAP ** DAILY FILE CAL NOLEAP ** DAILY FILE CAL NOLEAP ** ", lkind
364 if(test_file_daily_julian) &
365 write(*,"(////10x,a,i0/////)") &
366 ' ** DAILY FILE CAL JULIAN ** DAILY FILE CAL JULIAN ** DAILY FILE CAL JULIAN ** ', lkind
367 if(test_file_yearly_noleap) &
368 write(*,"(////10x,a,i0/////)") &
369 ' ** YEARLY FILE CAL NOLEAP ** YEARLY FILE CAL NOLEAP ** YEARLY FILE CAL NOLEAP ** ', lkind
370 if(test_file_yearly_julian) &
371 write(*,"(////10x,a,i0/////)") &
372 ' ** YEARLY FILE CAL JULIAN ** YEARLY FILE CAL JULIAN ** YEARLY FILE CAL JULIAN ** ', lkind
373 if(test_file_no_time) &
374 write(*,"(////10x,a/////)") " ** NO TIME AXIS ** NO TIME AXIS ** NO TIME AXIS **"
376 end subroutine write_header
377 !===============================================!
378 subroutine set_parameters_wrapper
380 if(test_file_daily_noleap) then
381 call set_parameters(nlonlat_in=10, nlonlat_mod_in=10, ntime_in=240, npfull_in=3, &
382 daily_in=.true., yearly_in=.false., noleap_in=.true.)
383 else if(test_file_daily_julian) then
384 call set_parameters(nlonlat_in=10, nlonlat_mod_in=10, ntime_in=240, npfull_in=3, &
385 daily_in=.true., yearly_in=.false., noleap_in=.false.)
386 else if(test_file_yearly_noleap) then
387 call set_parameters(nlonlat_in=10, nlonlat_mod_in=10, ntime_in=240, npfull_in=3, &
388 daily_in=.false., yearly_in=.true., noleap_in=.true.)
389 else if(test_file_yearly_julian) then
390 call set_parameters(nlonlat_in=10, nlonlat_mod_in=10, ntime_in=240, npfull_in=3, &
391 daily_in=.false., yearly_in=.true., noleap_in=.false.)
392 else if(test_file_no_time) then
393 call set_parameters(nlonlat_in=10, nlonlat_mod_in=10, ntime_in=0, npfull_in=3, &
394 daily_in=.true., yearly_in=.false., noleap_in=.false.)
397 end subroutine set_parameters_wrapper
398 !===============================================!
399 end program test_interpolator2