FMS  2025.04
Flexible Modeling System
axis_utils2.inc
1 !***********************************************************************
2 !* Apache License 2.0
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* Licensed under the Apache License, Version 2.0 (the "License");
7 !* you may not use this file except in compliance with the License.
8 !* You may obtain a copy of the License at
9 !*
10 !* http://www.apache.org/licenses/LICENSE-2.0
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
14 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 !* PARTICULAR PURPOSE. See the License for the specific language
16 !* governing permissions and limitations under the License.
17 !***********************************************************************
18 !> @defgroup axis_utils2_mod axis_utils2_mod
19 !> @ingroup axis_utils
20 !> @brief A set of utilities for manipulating axes and extracting axis attributes.
21 !! FMS2_IO equivalent version of @ref axis_utils_mod.
22 !> @author M.J. Harrison
23 
24 !> @addtogroup axis_utils2_mod
25 !> @{
26 
27  !> get axis edge data from a given file
28  subroutine axis_edges_(fileobj, name, edge_data, reproduce_null_char_bug_flag)
29 
30  class(fmsnetcdffile_t), intent(in) :: fileobj !< File object to read from
31  character(len=*), intent(in) :: name !< Name of a given axis
32  real(FMS_AU_KIND_), dimension(:), intent(out) :: edge_data !< Returned edge data from given axis name
33  logical, intent(in), optional :: reproduce_null_char_bug_flag !< Flag indicating to reproduce
34  !! the mpp_io bug where the null characters were not removed
35  !! after reading a string attribute
36 
37  integer :: ndims
38  character(len=128) :: buffer
39  integer, dimension(:), allocatable :: dim_sizes
40  real(kind=fms_au_kind_), dimension(:), allocatable :: r_var
41  real(kind=fms_au_kind_), dimension(:,:), allocatable :: r2d
42  integer :: i
43  integer :: n
44  logical :: reproduce_null_char_bug !< Local flag
45  !! indicating to reproduce the mpp_io bug where
46  !! the null characters were not removed after reading a string attribute
47  integer, parameter :: lkind = fms_au_kind_
48  integer :: edge_index(2) !< Index to use when reading the edges from the file
49  !! (/1, 2/) if the axis data is monotonically increasing
50  !! (/2, 1/) if the axis data is monotonically decreasing
51 
52  ndims = get_variable_num_dimensions(fileobj, name)
53  allocate(dim_sizes(ndims))
54 
55  call get_variable_size(fileobj, name, dim_sizes)
56 
57  n = dim_sizes(1)
58  if (size(edge_data) .ne. n+1) then
59  call mpp_error(fatal, "axis_edge: incorrect size of edge_data array.")
60  endif
61  deallocate(dim_sizes)
62 
63  reproduce_null_char_bug = .false.
64  if (present(reproduce_null_char_bug_flag)) reproduce_null_char_bug = reproduce_null_char_bug_flag
65 
66  buffer = ""
67  if (variable_att_exists(fileobj, name, "edges")) then
68  !! If the reproduce_null_char_bug flag is turned on fms2io will not remove the null character
69  call get_variable_attribute(fileobj, name, "edges", buffer(1:128), &
70  reproduce_null_char_bug_flag=reproduce_null_char_bug)
71 
72  !! Check for a null character here, if it exists *_bnds will be calculated instead of read in
73  if (reproduce_null_char_bug) then
74  i = 0
75  i = index(buffer, char(0))
76  if (i > 0) buffer = ""
77  endif
78  elseif (variable_att_exists(fileobj, name, "bounds")) then
79  !! If the reproduce_null_char_bug flag is turned on fms2io will not remove the null character
80  call get_variable_attribute(fileobj, name, "bounds", buffer(1:128), &
81  reproduce_null_char_bug_flag=reproduce_null_char_bug)
82 
83  !! Check for a null character here, if it exists *_bnds will be calculated instead of read in
84  if (reproduce_null_char_bug) then
85  i = 0
86  i = index(buffer, char(0))
87  if (i > 0) buffer = ""
88  endif
89  endif
90  if (trim(buffer) .ne. "") then
91  ndims = get_variable_num_dimensions(fileobj, buffer)
92  allocate(dim_sizes(ndims))
93 
94  call get_variable_size(fileobj, buffer, dim_sizes)
95 
96  if (size(dim_sizes) .eq. 1) then
97  if (dim_sizes(1) .ne. n+1) then
98  call mpp_error(fatal, "axis_edges: incorrect size of edge data.")
99  endif
100 
101  call read_data(fileobj, buffer, edge_data)
102 
103  elseif (size(dim_sizes) .eq. 2) then
104  if (dim_sizes(1) .ne. 2) then
105  call mpp_error(fatal, "axis_edges: first dimension of edge must be of size 2")
106  endif
107  if (dim_sizes(2) .ne. n) then
108  call mpp_error(fatal, "axis_edges: incorrect size of edge data.")
109  endif
110 
111  allocate(r2d(dim_sizes(1), dim_sizes(2)))
112  call read_data(fileobj, buffer, r2d)
113  edge_index = (/1, 2/)
114  if (r2d(1,1) .gt. r2d(1,2)) edge_index = (/2, 1 /)
115  edge_data(1:dim_sizes(2)) = r2d(edge_index(1),:)
116  edge_data(dim_sizes(2)+1) = r2d(edge_index(2),dim_sizes(2))
117  deallocate(r2d)
118  endif
119  deallocate(dim_sizes)
120  else
121  allocate(r_var(n))
122 
123  call read_data(fileobj, name, r_var)
124 
125  do i = 2, n
126  edge_data(i) = r_var(i-1) + 0.5_lkind*(r_var(i) - r_var(i-1))
127  enddo
128  edge_data(1) = r_var(1) - 0.5_lkind*(r_var(2) - r_var(1))
129  if (abs(edge_data(1)) .lt. 1.e-10_lkind) then
130  edge_data(1) = 0.0_lkind
131  endif
132  edge_data(n+1) = r_var(n) + 0.5_lkind*(r_var(n) - r_var(n-1))
133  deallocate(r_var)
134  endif
135  end subroutine axis_edges_
136 
137  !> @brief Returns lon_strt <= longitude <= lon_strt+360
138  !! @return real lon_in_range */
139 
140  function lon_in_range_(lon, l_strt)
141  real(kind=fms_au_kind_), intent(in) :: lon, l_strt
142  real(kind=fms_au_kind_) :: lon_in_range_
143  real(kind=fms_au_kind_) :: l_end
144  integer, parameter :: lkind = fms_au_kind_
145 
146  lon_in_range_ = lon
147  l_end = l_strt + 360.0_lkind
148 
149  if (abs(lon_in_range_ - l_strt) < 1.e-4_lkind) then
150  lon_in_range_ = l_strt
151  return
152  endif
153 
154  if (abs(lon_in_range_ - l_end) < 1.e-4_lkind) then
155  lon_in_range_ = l_strt
156  return
157  endif
158 
159  do
160  if (lon_in_range_ < l_strt) then
161  lon_in_range_ = real(lon_in_range_, fms_au_kind_) + real(f360, fms_au_kind_)
162  else if (lon_in_range_ > l_end) then
163  lon_in_range_ = real(lon_in_range_, fms_au_kind_) - real(f360, fms_au_kind_)
164  else
165  exit
166  end if
167  end do
168 
169  end function lon_in_range_
170 
171  !> @brief Returns monotonic array of longitudes s.t., lon_strt <= lon(:) < lon_strt+360.
172  !!
173  !! This may require that entries be moved from the beginning of the array to
174  !! the end. If no entries are moved (i.e., if lon(:) is already monotonic in
175  !! the range from lon_start to lon_start + 360), then istrt is set to 0. If
176  !! any entries are moved, then istrt is set to the original index of the entry
177  !! which becomes lon(1).
178  !!
179  !! e.g.,
180  !!
181  !! lon = 0 1 2 3 4 5 ... 358 359; lon_strt = 3
182  !! ==> lon = 3 4 5 6 7 8 ... 359 360 361 362; istrt = 4
183  !!
184  subroutine tranlon_(lon, lon_start, istrt)
185  real(kind=fms_au_kind_), intent(inout), dimension(:) :: lon
186  real(kind=fms_au_kind_), intent(in) :: lon_start
187  integer, intent(out) :: istrt
188  integer :: len, i
189  real(kind=fms_au_kind_) :: lon_strt, tmp(size(lon(:))-1)
190 
191  len = size(lon(:))
192 
193  do i = 1, len
194  lon(i) = lon_in_range(lon(i),lon_start)
195  enddo
196 
197  istrt = 0
198  do i = 1,len-1
199  if (lon(i+1) < lon(i)) then
200  istrt = i+1
201  exit
202  endif
203  enddo
204 
205  if (istrt>1) then ! grid is not monotonic
206  if (abs(lon(len)-lon(1)) < real(epsln, fms_au_kind_)) then
207  tmp = cshift(lon(1:len-1),istrt-1)
208  lon(1:len-1) = tmp
209  lon(len) = lon(1)
210  else
211  lon = cshift(lon,istrt-1)
212  endif
213 
214  lon_strt = lon(1)
215  do i=2,len
216  lon(i) = lon_in_range(lon(i),lon_strt)
217  lon_strt = lon(i)
218  enddo
219  endif
220 
221  return
222  end subroutine tranlon_
223 
224 
225  function frac_index_(rval, array)
226 
227  integer :: ia, i, ii, iunit
228  real(kind=fms_au_kind_) :: rval !< arbitrary data...same units as elements in "array"
229  real(kind=fms_au_kind_) :: frac_index_
230  real(kind=fms_au_kind_), dimension(:) :: array !< array of data points (must be monotonically increasing)
231  logical :: keep_going
232  integer, parameter :: lkind = fms_au_kind_
233  ia = size(array(:))
234 
235  do i = 2, ia
236  if (array(i) < array(i-1)) then
237  iunit = stdout()
238  write (iunit,*) '=> Error: "frac_index" array must be monotonically' &
239  & // 'increasing when searching for nearest value to ', rval
240  write (iunit,*) ' array(i) < array(i-1) for i=',i
241  write (iunit,*) ' array(i) for i=1..ia follows:'
242  do ii = 1, ia
243  write (iunit,*) 'i=',ii, ' array(i)=',array(ii)
244  enddo
245  call mpp_error(fatal,' "frac_index" array must be monotonically increasing.')
246  endif
247  enddo
248 
249  if (rval < array(1) .or. rval > array(ia)) then
250  frac_index_ = -1.0_lkind
251  else
252  i = 1
253  keep_going = .true.
254  do while (i <= ia .and. keep_going)
255  i = i+1
256  if (rval <= array(i)) then
257  frac_index_ = real((i-1), lkind) + (rval-array(i-1)) / (array(i) - array(i-1))
258  keep_going = .false.
259  endif
260  enddo
261  endif
262  end function frac_index_
263 
264  !> @brief Return index of nearest point along axis
265  !!
266  !> nearest_index = index of nearest data point within "array" corresponding to
267  !! "value".
268  !!
269  !! inputs:
270  !!
271  !! rval = arbitrary data...same units as elements in "array"
272  !! array = array of data points (must be monotonically)
273  !! ia = dimension of "array"
274  !!
275  !! output:
276  !!
277  !! nearest_index = index of nearest data point to "value"
278  !! if "value" is outside the domain of "array" then nearest_index = 1
279  !! or "ia" depending on whether array(1) or array(ia) is
280  !! closest to "value"
281  !!
282  !! note: if "array" is dimensioned array(0:ia) in the calling
283  !! program, then the returned index should be reduced
284  !! by one to account for the zero base.
285  !!
286  !! example:
287  !!
288  !! let model depths be defined by the following:
289  !! parameter (km=5)
290  !! dimension z(km)
291  !! data z /5.0, 10.0, 50.0, 100.0, 250.0/
292  !!
293  !! k1 = nearest_index (12.5, z, km)
294  !! k2 = nearest_index (0.0, z, km)
295  !!
296  !! k1 would be set to 2, and k2 would be set to 1 so that
297  !! z(k1) would be the nearest data point to 12.5 and z(k2) would
298  !! be the nearest data point to 0.0
299  !! @return integer nearest_index
300  function nearest_index_(rval, array)
301  real(kind=fms_au_kind_), intent(in) :: rval !< arbitrary data...same units as elements in "array"
302  real(kind=fms_au_kind_), intent(in), dimension(:) :: array !< array of data points (must be monotonic)
303 
304  integer :: nearest_index_
305  integer :: ia !< dimension of "array"
306  integer :: i !< For looping through "array"
307 
308  logical :: increasing !< .True. if the array is increasing
309 
310  ia = SIZE(array(:))
311 
312  ! check if array is increasing
313  increasing = .true.
314  DO i = 2, ia-1
315  IF( array(i) .lt. array(i-1)) then
316  increasing = .false.
317  exit
318  endif
319  END DO
320 
321  if (.not. increasing) then
322  ! if not increasing, check that it is decreasing
323  DO i = 2, ia-1
324  IF( array(i) .gt. array(i-1)) &
325  call mpp_error(fatal, 'axis_utils2::nearest_index array is NOT monotonously ordered')
326  END DO
327  endif
328 
329  array_is_increasing: if (increasing) then
330  !< Check if the rval is outside the range of the array
331  if (rval .le. array(1)) then
332  nearest_index_ = 1
333  return
334  elseif (rval .ge. array(ia)) then
335  nearest_index_ = ia
336  return
337  endif
338 
339  DO i = 2, ia
340  if (rval .le. array(i)) then
341  nearest_index_ = i
342  if (array(i) -rval .gt. rval - array(i-1)) nearest_index_ = i - 1
343  return
344  endif
345  END DO
346  else !array_is_decreasing
347  !< Check if the rval is outside the range of the array
348  if (rval .le. array(ia)) then
349  nearest_index_ = ia
350  return
351  elseif (rval .gt. array(1)) then
352  nearest_index_ = 1
353  return
354  endif
355 
356  DO i = 2, ia
357  if (rval .ge. array(i)) then
358  nearest_index_ = i
359  if (rval - array(i) .gt. array(i-1) -rval ) nearest_index_ = i - 1
360  return
361  endif
362  END DO
363  endif array_is_increasing
364  end function nearest_index_
365 
366  !#############################################################################
367 
368  subroutine interp_1d_linear_(grid1,grid2,data1,data2)
369 
370  real(kind=fms_au_kind_), dimension(:), intent(in) :: grid1, data1, grid2
371  real(kind=fms_au_kind_), dimension(:), intent(inout) :: data2
372 
373  integer :: n1, n2, i, n
374  real(kind=fms_au_kind_) :: w
375  integer, parameter :: lkind = fms_au_kind_
376 
377  n1 = size(grid1(:))
378  n2 = size(grid2(:))
379 
380 
381  do i = 2, n1
382  if (grid1(i) <= grid1(i-1)) call mpp_error(fatal, 'grid1 not monotonic')
383  enddo
384 
385  do i = 2, n2
386  if (grid2(i) <= grid2(i-1)) call mpp_error(fatal, 'grid2 not monotonic')
387  enddo
388 
389  if (grid1(1) > grid2(1) ) call mpp_error(fatal, 'grid2 lies outside grid1')
390  if (grid1(n1) < grid2(n2) ) call mpp_error(fatal, 'grid2 lies outside grid1')
391 
392  do i = 1, n2
393  n = nearest_index(grid2(i),grid1)
394 
395  if (grid1(n) < grid2(i)) then
396  w = (grid2(i)-grid1(n))/(grid1(n+1)-grid1(n))
397  data2(i) = (1.0_lkind-w)*data1(n) + w*data1(n+1)
398  else
399  if(n==1) then
400  data2(i) = data1(n)
401  else
402  w = (grid2(i)-grid1(n-1))/(grid1(n)-grid1(n-1))
403  data2(i) = (1.0_lkind-w)*data1(n-1) + w*data1(n)
404  endif
405  endif
406  enddo
407 
408 
409  return
410 
411  end subroutine interp_1d_linear_
412 
413  !###################################################################
414  subroutine interp_1d_cubic_spline_(grid1, grid2, data1, data2, yp1, ypn)
415 
416  real(kind=fms_au_kind_), dimension(:), intent(in) :: grid1, grid2, data1
417  real(kind=fms_au_kind_), dimension(:), intent(inout) :: data2
418  real(kind=fms_au_kind_), intent(in) :: yp1, ypn
419 
420  real(kind=fms_au_kind_), dimension(size(grid1)) :: y2, u
421  real(kind=fms_au_kind_) :: sig, p, qn, un, h, a ,b
422  integer :: n, m, i, k, klo, khi
423  integer, parameter :: lkind = fms_au_kind_
424 
425  n = size(grid1(:))
426  m = size(grid2(:))
427 
428  do i = 2, n
429  if (grid1(i) <= grid1(i-1)) call mpp_error(fatal, 'grid1 not monotonic')
430  enddo
431 
432  do i = 2, m
433  if (grid2(i) <= grid2(i-1)) call mpp_error(fatal, 'grid2 not monotonic')
434  enddo
435 
436  if (grid1(1) > grid2(1) ) call mpp_error(fatal, 'grid2 lies outside grid1')
437  if (grid1(n) < grid2(m) ) call mpp_error(fatal, 'grid2 lies outside grid1')
438 
439 if (yp1>0.99e30_lkind) then
440  y2(1) = 0.0_lkind
441  u(1) = 0.0_lkind
442  else
443  y2(1) = -0.5_lkind
444  u(1) = (3.0_lkind)/(grid1(2)-grid1(1))*((data1(2)-data1(1))/(grid1(2)-grid1(1))-yp1)
445  endif
446 
447  do i = 2, n-1
448  sig = (grid1(i)-grid1(i-1))/(grid1(i+1)-grid1(i-1))
449  p = sig*y2(i-1) + 2.0_lkind
450  y2(i) = (sig-1.0_lkind)/p
451  u(i) = (6.0_lkind*((data1(i+1)-data1(i))/(grid1(i+1)-grid1(i))-(data1(i)-data1(i-1)) &
452  /(grid1(i)-grid1(i-1)))/(grid1(i+1)-grid1(i-1))-sig*u(i-1))/p
453  enddo
454 
455  if (ypn>0.99e30_lkind) then
456  qn = 0.0_lkind
457  un = 0.0_lkind
458  else
459  qn = 0.5_lkind
460  un = (3.0_lkind)/(grid1(n)-grid1(n-1))*(ypn-(data1(n)-data1(n-1))/ &
461  (grid1(n)-grid1(n-1)))
462  endif
463 
464  y2(n) = (un-qn*u(n-1))/(qn*y2(n-1)+1.0_lkind)
465 
466  do k = n-1,1,-1
467  y2(k) = y2(k)*y2(k+1)+u(k)
468  enddo
469 
470  do k = 1, m
471  n = nearest_index(grid2(k),grid1)
472  if (grid1(n) < grid2(k)) then
473  klo = n
474  else
475  if(n==1) then
476  klo = n
477  else
478  klo = n -1
479  endif
480  endif
481 
482  khi = klo+1
483  h = grid1(khi)-grid1(klo)
484  a = (grid1(khi) - grid2(k))/h
485  b = (grid2(k) - grid1(klo))/h
486  data2(k) = a*data1(klo) + b*data1(khi)+ ((a**3-a)*y2(klo) + (b**3-b)*y2(khi))*(h**2) &
487  /6.0_lkind
488  enddo
489 
490  end subroutine interp_1d_cubic_spline_
491 
492  !###################################################################
493 
494  subroutine interp_1d_1d_(grid1,grid2,data1,data2, method, yp1, yp2)
495 
496  real(kind=fms_au_kind_), dimension(:), intent(in) :: grid1, data1, grid2
497  real(kind=fms_au_kind_), dimension(:), intent(inout) :: data2
498  character(len=*), optional, intent(in) :: method
499  real(kind=fms_au_kind_), optional, intent(in) :: yp1, yp2
500 
501  real(kind=fms_au_kind_) :: y1, y2
502  character(len=32) :: interp_method
503  integer :: k2, ks, ke
504  integer, parameter :: lkind = fms_au_kind_
505 
506  k2 = size(grid2(:))
507 
508  interp_method = "linear"
509  if(present(method)) interp_method = method
510  y1 = 1.0e30_lkind
511 
512  if(present(yp1)) y1 = yp1
513  y2 = 1.0e30_lkind
514 
515  if(present(yp2)) y2 = yp2
516  call find_index(grid1, grid2(1), grid2(k2), ks, ke)
517  select case(trim(interp_method))
518  case("linear")
519  call interp_1d_linear(grid1(ks:ke),grid2,data1(ks:ke),data2)
520  case("cubic_spline")
521  call interp_1d_cubic_spline(grid1(ks:ke),grid2,data1(ks:ke),data2, y1, y2)
522  case default
523  call mpp_error(fatal,"axis_utils: interp_method should be linear or cubic_spline")
524  end select
525 
526  return
527 
528  end subroutine interp_1d_1d_
529 
530  !###################################################################
531 
532 
533  subroutine interp_1d_2d_(grid1,grid2,data1,data2)
534 
535  real(kind=fms_au_kind_), dimension(:,:), intent(in) :: grid1, data1, grid2
536  real(kind=fms_au_kind_), dimension(:,:), intent(inout) :: data2
537 
538  integer :: n1, n2, n, k2, ks, ke
539 
540  n1 = size(grid1,1)
541  n2 = size(grid2,1)
542  k2 = size(grid2,2)
543 
544  if (n1 /= n2) call mpp_error(fatal,'grid size mismatch')
545 
546  do n = 1, n1
547  call find_index(grid1(n,:), grid2(n,1), grid2(n,k2), ks, ke)
548  call interp_1d_linear(grid1(n,ks:ke),grid2(n,:),data1(n,ks:ke),data2(n,:))
549  enddo
550 
551  return
552 
553  end subroutine interp_1d_2d_
554 
555  !###################################################################
556 
557  subroutine interp_1d_3d_(grid1,grid2,data1,data2, method, yp1, yp2)
558 
559  real(fms_au_kind_), dimension(:,:,:), intent(in) :: grid1, data1, grid2
560  real(fms_au_kind_), dimension(:,:,:), intent(inout) :: data2
561  character(len=*), optional, intent(in) :: method
562  real(kind=fms_au_kind_), optional, intent(in) :: yp1, yp2
563 
564  integer :: n1, n2, m1, m2, k2, n, m
565  real(kind=fms_au_kind_) :: y1, y2
566  character(len=32) :: interp_method
567  integer :: ks, ke
568  integer, parameter :: lkind = fms_au_kind_
569 
570  n1 = size(grid1,1)
571  n2 = size(grid2,1)
572  m1 = size(grid1,2)
573  m2 = size(grid2,2)
574  k2 = size(grid2,3)
575 
576  interp_method = "linear"
577  if(present(method)) interp_method = method
578  y1 = 1.0e30_lkind
579 
580  if(present(yp1)) y1 = yp1
581  y2 = 1.0e30_lkind
582  if(present(yp2)) y2 = yp2
583 
584  if (n1 /= n2 .or. m1 /= m2) call mpp_error(fatal,'grid size mismatch')
585 
586  select case(trim(interp_method))
587  case("linear")
588  do m = 1, m1
589  do n = 1, n1
590  call find_index(grid1(n,m,:), grid2(n,m,1), grid2(n,m,k2), ks, ke)
591  call interp_1d_linear(grid1(n,m,ks:ke),grid2(n,m,:),data1(n,m,ks:ke),data2(n,m,:))
592  enddo
593  enddo
594 
595  case("cubic_spline")
596  do m = 1, m1
597  do n = 1, n1
598  call find_index(grid1(n,m,:), grid2(n,m,1), grid2(n,m,k2), ks, ke)
599  call interp_1d_cubic_spline(grid1(n,m,ks:ke),grid2(n,m,:), data1(n,m,ks:ke),data2(n,m,:), y1, y2)
600  enddo
601  enddo
602 
603  case default
604  call mpp_error(fatal,"axis_utils: interp_method should be linear or cubic_spline")
605  end select
606 
607  return
608 
609  end subroutine interp_1d_3d_
610 
611 
612  !#####################################################################
613  subroutine find_index_(grid1, xs, xe, ks, ke)
614  real(kind=fms_au_kind_), dimension(:), intent(in) :: grid1
615  real(kind=fms_au_kind_), intent(in) :: xs, xe
616  integer, intent(out) :: ks, ke
617 
618  integer :: k, nk
619 
620  nk = size(grid1(:))
621 
622  ks = 0; ke = 0
623  do k = 1, nk-1
624  if(grid1(k) <= xs .and. grid1(k+1) > xs ) then
625  ks = k
626  exit
627  endif
628  enddo
629 
630  do k = nk, 2, -1
631  if(grid1(k) >= xe .and. grid1(k-1) < xe ) then
632  ke = k
633  exit
634  endif
635  enddo
636 
637  if(ks == 0 ) call mpp_error(fatal,' xs locate outside of grid1')
638  if(ke == 0 ) call mpp_error(fatal,' xe locate outside of grid1')
639 
640  end subroutine find_index_
641  !> @}
642  ! close documentation grouping
subroutine axis_edges_(fileobj, name, edge_data, reproduce_null_char_bug_flag)
get axis edge data from a given file
Definition: axis_utils2.inc:29
real(kind=fms_au_kind_) function frac_index_(rval, array)
integer function nearest_index_(rval, array)
Return index of nearest point along axis.
subroutine tranlon_(lon, lon_start, istrt)
Returns monotonic array of longitudes s.t., lon_strt <= lon(:) < lon_strt+360.
real(kind=fms_au_kind_) function lon_in_range_(lon, l_strt)
Returns lon_strt <= longitude <= lon_strt+360.
integer function stdout()
This function returns the current standard fortran unit numbers for output.
Definition: mpp_util.inc:42