@@ -26,19 +26,45 @@ module MOM_interpolate
2626contains
2727
2828! > Read a scalar field based on model time.
29- subroutine time_interp_external_0d (field_id , time , data_in , verbose )
29+ subroutine time_interp_external_0d (field_id , time , data_in , verbose , scale )
3030 integer , intent (in ) :: field_id ! < The integer index of the external field returned
3131 ! ! from a previous call to init_external_field()
3232 type (time_type), intent (in ) :: time ! < The target time for the data
3333 real , intent (inout ) :: data_in ! < The interpolated value
3434 logical , optional , intent (in ) :: verbose ! < If true, write verbose output for debugging
35+ real , optional , intent (in ) :: scale ! < A scaling factor that new values of data_in are
36+ ! ! multiplied by before it is returned
37+ real :: data_in_pre_scale ! The input data before rescaling
38+ real :: I_scale ! The inverse of scale
39+
40+ ! Store the input value in case the scaling factor is perfectly invertable.
41+ data_in_pre_scale = data_in
42+ I_scale = 1.0
43+ if (present (scale)) then ; if ((scale /= 1.0 ) .and. (scale /= 0.0 )) then
44+ ! Because time_interp_extern has the ability to only set some values, but no clear
45+ ! mechanism to determine which values have been set, the input data has to
46+ ! be unscaled so that it will have the right values when it is returned.
47+ I_scale = 1.0 / scale
48+ data_in = data_in * I_scale
49+ endif ; endif
3550
3651 call time_interp_extern(field_id, time, data_in, verbose= verbose)
52+
53+ if (present (scale)) then ; if (scale /= 1.0 ) then
54+ ! Rescale data that has been newly set and restore the scaling of unset data.
55+ if (data_in == I_scale * data_in_pre_scale) then
56+ data_in = data_in_pre_scale
57+ else
58+ data_in = scale * data_in
59+ endif
60+ endif ; endif
61+
3762end subroutine time_interp_external_0d
3863
3964! > Read a 2d field from an external based on model time, potentially including horizontal
4065! ! interpolation and rotation of the data
41- subroutine time_interp_external_2d (field_id , time , data_in , interp , verbose , horz_interp , mask_out , turns )
66+ subroutine time_interp_external_2d (field_id , time , data_in , interp , &
67+ verbose , horz_interp , mask_out , turns , scale )
4268 integer , intent (in ) :: field_id ! < The integer index of the external field returned
4369 ! ! from a previous call to init_external_field()
4470 type (time_type), intent (in ) :: time ! < The target time for the data
@@ -50,14 +76,32 @@ subroutine time_interp_external_2d(field_id, time, data_in, interp, verbose, hor
5076 logical , dimension (:,:), &
5177 optional , intent (out ) :: mask_out ! < An array that is true where there is valid data
5278 integer , optional , intent (in ) :: turns ! < Number of quarter turns to rotate the data
79+ real , optional , intent (in ) :: scale ! < A scaling factor that new values of data_in are
80+ ! ! multiplied by before it is returned
5381
54- real , allocatable :: data_pre_rot(:,:) ! The input data before rotation
82+ real , allocatable :: data_in_pre_scale(:,:) ! The input data before rescaling
83+ real , allocatable :: data_pre_rot(:,:) ! The unscaled input data before rotation
84+ real :: I_scale ! The inverse of scale
5585 integer :: qturns ! The number of quarter turns to rotate the data
86+ integer :: i, j
5687
5788 ! TODO: Mask rotation requires logical array rotation support
5889 if (present (mask_out)) &
5990 call MOM_error(FATAL, " Rotation of masked output not yet support" )
6091
92+ if (present (scale)) then ; if ((scale /= 1.0 ) .and. (scale /= 0.0 )) then
93+ ! Because time_interp_extern has the ability to only set some values, but no clear mechanism
94+ ! to determine which values have been set, the input data has to be unscaled so that it will
95+ ! have the right values when it is returned. It may be a problem for some compiler settings
96+ ! if there are NaNs in data_in, but they will not spread.
97+ if (abs (fraction (scale)) /= 1.0 ) then
98+ ! This scaling factor may not be perfectly invertable, so store the input value
99+ allocate (data_in_pre_scale, source= data_in)
100+ endif
101+ I_scale = 1.0 / scale
102+ data_in(:,:) = I_scale * data_in(:,:)
103+ endif ; endif
104+
61105 qturns = 0 ; if (present (turns)) qturns = modulo (turns, 4 )
62106
63107 if (qturns == 0 ) then
@@ -70,12 +114,30 @@ subroutine time_interp_external_2d(field_id, time, data_in, interp, verbose, hor
70114 call rotate_array(data_pre_rot, turns, data_in)
71115 deallocate (data_pre_rot)
72116 endif
117+
118+ if (present (scale)) then ; if (scale /= 1.0 ) then
119+ ! Rescale data that has been newly set and restore the scaling of unset data.
120+ if ((abs (fraction (scale)) /= 1.0 ) .and. (scale /= 0.0 )) then
121+ do j= LBOUND (data_in,2 ),UBOUND (data_in,2 ) ; do i= LBOUND (data_in,1 ),UBOUND (data_in,1 )
122+ ! This handles the case where scale is not exactly invertable for data
123+ ! values that have not been modified by time_interp_extern.
124+ if (data_in(i,j) == I_scale * data_in_pre_scale(i,j)) then
125+ data_in(i,j) = data_in_pre_scale(i,j)
126+ else
127+ data_in(i,j) = scale * data_in(i,j)
128+ endif
129+ enddo ; enddo
130+ else
131+ data_in(:,:) = scale * data_in(:,:)
132+ endif
133+ endif ; endif
134+
73135end subroutine time_interp_external_2d
74136
75137
76138! > Read a 3d field based on model time, and rotate to the model grid
77139subroutine time_interp_external_3d (field_id , time , data_in , interp , &
78- verbose , horz_interp , mask_out , turns )
140+ verbose , horz_interp , mask_out , turns , scale )
79141 integer , intent (in ) :: field_id ! < The integer index of the external field returned
80142 ! ! from a previous call to init_external_field()
81143 type (time_type), intent (in ) :: time ! < The target time for the data
@@ -87,14 +149,32 @@ subroutine time_interp_external_3d(field_id, time, data_in, interp, &
87149 logical , dimension (:,:,:), &
88150 optional , intent (out ) :: mask_out ! < An array that is true where there is valid data
89151 integer , optional , intent (in ) :: turns ! < Number of quarter turns to rotate the data
152+ real , optional , intent (in ) :: scale ! < A scaling factor that new values of data_in are
153+ ! ! multiplied by before it is returned
90154
91- real , allocatable :: data_pre_rot(:,:,:) ! The input data before rotation
155+ real , allocatable :: data_in_pre_scale(:,:,:) ! The input data before rescaling
156+ real , allocatable :: data_pre_rot(:,:,:) ! The unscaled input data before rotation
157+ real :: I_scale ! The inverse of scale
92158 integer :: qturns ! The number of quarter turns to rotate the data
159+ integer :: i, j, k
93160
94161 ! TODO: Mask rotation requires logical array rotation support
95162 if (present (mask_out)) &
96163 call MOM_error(FATAL, " Rotation of masked output not yet support" )
97164
165+ if (present (scale)) then ; if ((scale /= 1.0 ) .and. (scale /= 0.0 )) then
166+ ! Because time_interp_extern has the ability to only set some values, but no clear mechanism
167+ ! to determine which values have been set, the input data has to be unscaled so that it will
168+ ! have the right values when it is returned. It may be a problem for some compiler settings
169+ ! if there are NaNs in data_in, but they will not spread.
170+ if (abs (fraction (scale)) /= 1.0 ) then
171+ ! This scaling factor may not be perfectly invertable, so store the input value
172+ allocate (data_in_pre_scale, source= data_in)
173+ endif
174+ I_scale = 1.0 / scale
175+ data_in(:,:,:) = I_scale * data_in(:,:,:)
176+ endif ; endif
177+
98178 qturns = 0 ; if (present (turns)) qturns = modulo (turns, 4 )
99179
100180 if (qturns == 0 ) then
@@ -107,6 +187,28 @@ subroutine time_interp_external_3d(field_id, time, data_in, interp, &
107187 call rotate_array(data_pre_rot, turns, data_in)
108188 deallocate (data_pre_rot)
109189 endif
190+
191+ if (present (scale)) then ; if (scale /= 1.0 ) then
192+ ! Rescale data that has been newly set and restore the scaling of unset data.
193+ if ((abs (fraction (scale)) /= 1.0 ) .and. (scale /= 0.0 )) then
194+ do k= LBOUND (data_in,3 ),UBOUND (data_in,3 )
195+ do j= LBOUND (data_in,2 ),UBOUND (data_in,2 )
196+ do i= LBOUND (data_in,1 ),UBOUND (data_in,1 )
197+ ! This handles the case where scale is not exactly invertable for data
198+ ! values that have not been modified by time_interp_extern.
199+ if (data_in(i,j,k) == I_scale * data_in_pre_scale(i,j,k)) then
200+ data_in(i,j,k) = data_in_pre_scale(i,j,k)
201+ else
202+ data_in(i,j,k) = scale * data_in(i,j,k)
203+ endif
204+ enddo
205+ enddo
206+ enddo
207+ else
208+ data_in(:,:,:) = scale * data_in(:,:,:)
209+ endif
210+ endif ; endif
211+
110212end subroutine time_interp_external_3d
111213
112214end module MOM_interpolate
0 commit comments