FMS  2025.04
Flexible Modeling System
fms_diag_input_buffer.F90
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 fms_diag_input_buffer_mod fms_diag_input_buffer_mod
19 !> @ingroup diag_manager
20 !! @brief
21 !> @addtogroup fms_diag_input_buffer_mod
22 !> @{
23 module fms_diag_input_buffer_mod
24 #ifdef use_yaml
25  use platform_mod, only: r8_kind, r4_kind, i4_kind, i8_kind
26  use fms_diag_axis_object_mod, only: fmsdiagaxiscontainer_type, fmsdiagfullaxis_type
27  use time_manager_mod, only: time_type
28  use mpp_mod, only: mpp_error, fatal
29  implicit NONE
30  private
31 
32  !> @brief Appends the input_data_buffer and the mask (only when the mask is set to .True.)
34  module procedure append_data_buffer_r4, append_data_buffer_r8
35  end interface
36 
37  !> @brief Sums the data in the input_data_buffer
38  interface sum_data_buffer
39  module procedure sum_data_buffer_r4, sum_data_buffer_r8
40  end interface
41 
42  !> @brief Type to hold the information needed for the input buffer
43  !! This is used when set_math_needs_to_be_done = .true. (i.e calling send_data
44  !! from an openmp region with multiple threads)
46  logical :: initialized !< .True. if the input buffer has been initialized
47  class(*), allocatable :: buffer(:,:,:,:) !< Input data passed in send_data
48  integer, allocatable :: counter(:,:,:,:)!< Number of send_data calls for each point
49  real(kind=r8_kind) :: weight !< Weight passed in send_data
50  type(time_type) :: send_data_time !< The time send data was called last
51 
52  contains
53  procedure :: get_buffer
54  procedure :: get_weight
55  procedure :: allocate_input_buffer_object
56  procedure :: init_input_buffer_object
57  procedure :: set_input_buffer_object
58  procedure :: update_input_buffer_object
59  procedure :: prepare_input_buffer_object
60  procedure :: set_send_data_time
61  procedure :: get_send_data_time
62  procedure :: is_initialized
63  end type fmsdiaginputbuffer_t
64 
65  public :: fmsdiaginputbuffer_t
66 
67  contains
68 
69  !> @brief Get the buffer from the input buffer object
70  !! @return a pointer to the buffer
71  function get_buffer(this) &
72  result(buffer)
73  class(fmsdiaginputbuffer_t), target, intent(in) :: this !< input buffer object
74  class(*), pointer :: buffer(:,:,:,:)
75 
76  buffer => this%buffer
77  end function get_buffer
78 
79 
80  !> @brief Get the weight from the input buffer object
81  !! @return a pointer to the weight
82  function get_weight(this) &
83  result(weight)
84  class(fmsdiaginputbuffer_t), target, intent(in) :: this !< input buffer object
85  real(kind=r8_kind), pointer :: weight
86 
87  weight => this%weight
88  end function get_weight
89 
90  !> @brief Initiliazes an input data buffer
91  !! @return Error message if something went wrong
92  function allocate_input_buffer_object(this, input_data, axis_ids, diag_axis) &
93  result(err_msg)
94  class(fmsdiaginputbuffer_t), intent(out) :: this !< input buffer object
95  class(*), intent(in) :: input_data(:,:,:,:) !< input data
96  integer, target, intent(in) :: axis_ids(:) !< axis ids for the field
97  class(fmsdiagaxiscontainer_type), intent(in) :: diag_axis(:) !< Array of diag_axis
98  character(len=128) :: err_msg
99 
100  integer :: naxes !< The number of axes in the field
101  integer, parameter :: ndims = 4 !< Number of dimensions
102  integer :: length(ndims) !< The length of an axis
103  integer :: a !< For looping through axes
104  integer, pointer :: axis_id !< The axis ID
105 
106  err_msg = ""
107 
108  !! Use the axis to get the size
109  !> Initialize the axis lengths to 1. Any dimension that does not have an axis will have a length
110  !! of 1.
111  length = 1
112  naxes = size(axis_ids)
113  axis_loop: do a = 1,naxes
114  axis_id => axis_ids(a)
115  select type (axis => diag_axis(axis_id)%axis)
116  type is (fmsdiagfullaxis_type)
117  length(a) = axis%axis_length()
118  end select
119  enddo axis_loop
120 
121  select type (input_data)
122  type is (real(r4_kind))
123  allocate(real(kind=r4_kind) :: this%buffer(length(1), length(2), length(3), length(4)))
124  this%buffer = 0.0_r4_kind
125  type is (real(r8_kind))
126  allocate(real(kind=r8_kind) :: this%buffer(length(1), length(2), length(3), length(4)))
127  this%buffer = 0.0_r8_kind
128  type is (integer(i4_kind))
129  allocate(integer(kind=i4_kind) :: this%buffer(length(1), length(2), length(3), length(4)))
130  this%buffer = 0_i4_kind
131  type is (integer(i8_kind))
132  allocate(integer(kind=i4_kind) :: this%buffer(length(1), length(2), length(3), length(4)))
133  this%buffer = 0_i8_kind
134  class default
135  err_msg = "The data input is not one of the supported types. &
136  &Only r4, r8, i4, and i8 types are supported."
137  end select
138 
139  this%weight = 1.0_r8_kind
140  this%initialized = .true.
141  allocate(this%counter(length(1), length(2), length(3), length(4)))
142  this%counter = 0
143  end function allocate_input_buffer_object
144 
145  !> @brief Initiliazes an input data buffer and the counter
146  subroutine init_input_buffer_object(this)
147  class(fmsdiaginputbuffer_t), intent(inout) :: this !< input buffer object
148 
149  select type(buffer=>this%buffer)
150  type is (real(kind=r8_kind))
151  buffer = 0.0_r8_kind
152  type is (real(kind=r4_kind))
153  buffer = 0.0_r4_kind
154  end select
155  this%counter = 0
156  end subroutine init_input_buffer_object
157 
158  !> @brief Sets the time send data was called last
159  subroutine set_send_data_time(this, time)
160  class(fmsdiaginputbuffer_t), intent(inout) :: this !< input buffer object
161  type(time_type), intent(in) :: time !< The time send data was called
162 
163  this%send_data_time = time
164  end subroutine set_send_data_time
165 
166  !> @brief Get the time send data was called last
167  !! @result the time send data was called last
168  function get_send_data_time(this) &
169  result(rslt)
170  class(fmsdiaginputbuffer_t), intent(in) :: this !< input buffer object
171  type(time_type) :: rslt
172 
173  rslt = this%send_data_time
174  end function get_send_data_time
175 
176  !> @brief Updates the input data buffer object for the current send_data call
177  !! @return Error message (if an error occurs)
178  function update_input_buffer_object(this, input_data, is, js, ks, ie, je, ke, mask_in, mask_out, &
179  mask_variant, var_is_masked) &
180  result(err_msg)
181 
182  class(fmsdiaginputbuffer_t), intent(inout) :: this !< input buffer object
183  class(*), intent(in) :: input_data(:,:,:,:) !< Field data
184  integer, intent(in) :: is, js, ks !< Starting index for each of the dimension
185  integer, intent(in) :: ie, je, ke !< Ending index for each of the dimensions
186  logical, intent(in) :: mask_in(:,:,:,:)
187  logical, intent(inout) :: mask_out(:,:,:,:)
188  logical, intent(in) :: mask_variant
189  logical, intent(in) :: var_is_masked
190 
191  character(len=128) :: err_msg
192 
193  if (mask_variant) then
194  err_msg = append_data_buffer_wrapper(mask_out(is:ie,js:je,ks:ke,:), mask_in, &
195  this%buffer(is:ie,js:je,ks:ke,:), input_data)
196  else
197  mask_out(is:ie,js:je,ks:ke,:) = mask_in
198  err_msg = sum_data_buffer_wrapper(mask_in, this%buffer(is:ie,js:je,ks:ke,:), input_data, &
199  this%counter(is:ie,js:je,ks:ke,:), &
200  var_is_masked)
201  endif
202 
203  end function update_input_buffer_object
204 
205  !> @brief Prepare the input data buffer to do the reduction methods (i.e divide by the number of times
206  !! send data has been called)
207  subroutine prepare_input_buffer_object(this, field_info)
208  class(fmsdiaginputbuffer_t), intent(inout) :: this !< input buffer object
209  character(len=*), intent(in) :: field_info !< Field info to append to error message
210 
211  select type (input_data => this%buffer)
212  type is (real(kind=r4_kind))
213  input_data = input_data / this%counter(1,1,1,1)
214  type is (real(kind=r8_kind))
215  input_data = input_data / this%counter(1,1,1,1)
216  class default
217  call mpp_error(fatal, "prepare_input_buffer_object::"//trim(field_info)//&
218  " has only been implemented for real variables. Contact developers.")
219  end select
220  end subroutine prepare_input_buffer_object
221 
222  !> @brief Sums the data in the input_data_buffer
223  !! @return Error message (if an error occurs)
224  function sum_data_buffer_wrapper(mask, data_out, data_in, counter, var_is_masked) &
225  result(err_msg)
226 
227  logical, intent(in) :: mask(:,:,:,:) !< Mask passed into send_data
228  class(*), intent(inout) :: data_out(:,:,:,:) !< Data currently saved in the input_data_buffer
229  class(*), intent(in) :: data_in(:,:,:,:) !< Data passed into send_data
230  integer, intent(inout) :: counter(:,:,:,:) !< Number of times data has been summed
231  logical, intent(in) :: var_is_masked !< .True. if the variable is masked
232 
233  character(len=128) :: err_msg
234 
235  err_msg = ""
236  select type(data_out)
237  type is (real(kind=r8_kind))
238  select type (data_in)
239  type is (real(kind=r8_kind))
240  call sum_data_buffer(mask, data_out, data_in, counter, var_is_masked)
241  end select
242  type is (real(kind=r4_kind))
243  select type (data_in)
244  type is (real(kind=r4_kind))
245  call sum_data_buffer(mask, data_out, data_in, counter, var_is_masked)
246  end select
247  class default
248  err_msg = "sum_data_buffer_wrapper:: has only been implemented for real. Contact developers"
249  end select
250  end function sum_data_buffer_wrapper
251 
252  !> @brief Appends the input_data_buffer and the mask (only when the mask is set to .True.)
253  !! @return Error message (if an error occurs)
254  function append_data_buffer_wrapper(mask_out, mask_in, data_out, data_in) &
255  result(err_msg)
256  logical, intent(inout) :: mask_out(:,:,:,:) !< Mask currently in the input_data_buffer
257  logical, intent(in) :: mask_in(:,:,:,:) !< Mask passed in to send_data
258  class(*), intent(inout) :: data_out(:,:,:,:) !< Data currently in the input_data_buffer
259  class(*), intent(in) :: data_in(:,:,:,:) !< Data passed in to send_data
260 
261  character(len=128) :: err_msg
262 
263  err_msg = ""
264  select type(data_out)
265  type is (real(kind=r8_kind))
266  select type (data_in)
267  type is (real(kind=r8_kind))
268  call append_data_buffer(mask_out, mask_in, data_out, data_in)
269  end select
270  type is (real(kind=r4_kind))
271  select type (data_in)
272  type is (real(kind=r4_kind))
273  call append_data_buffer(mask_out, mask_in, data_out, data_in)
274  end select
275  class default
276  err_msg = "append_data_buffer:: has only been implemented for real. Contact developers"
277  end select
278  end function append_data_buffer_wrapper
279 
280  !> @brief Sets the members of the input buffer object
281  !! @return Error message if something went wrong
282  function set_input_buffer_object(this, input_data, weight, is, js, ks, ie, je, ke) &
283  result(err_msg)
284 
285  class(fmsdiaginputbuffer_t), intent(inout) :: this !< input buffer object
286  class(*), intent(in) :: input_data(:,:,:,:) !< Field data
287  real(kind=r8_kind), intent(in) :: weight !< Weight for the field
288  integer, intent(in) :: is, js, ks !< Starting index for each of the dimension
289  integer, intent(in) :: ie, je, ke !< Ending index for each of the dimensions
290 
291  character(len=128) :: err_msg
292  err_msg = ""
293 
294  if (.not. this%initialized) then
295  err_msg = "The data buffer was never initiliazed. This shouldn't happen."
296  return
297  endif
298 
299  this%weight = weight
300 
301  select type (input_data)
302  type is (real(kind=r4_kind))
303  select type (db => this%buffer)
304  type is (real(kind=r4_kind))
305  db(is:ie, js:je, ks:ke, :) = input_data
306  class default
307  err_msg = "The data buffer was not allocated to the correct type (r4_kind). This shouldn't happen"
308  return
309  end select
310  type is (real(kind=r8_kind))
311  select type (db => this%buffer)
312  type is (real(kind=r8_kind))
313  db(is:ie, js:je, ks:ke, :) = input_data
314  class default
315  err_msg = "The data buffer was not allocated to the correct type (r8_kind). This shouldn't happen"
316  return
317  end select
318  type is (integer(kind=i4_kind))
319  select type (db => this%buffer)
320  type is (integer(kind=i4_kind))
321  db(is:ie, js:je, ks:ke, :) = input_data
322  class default
323  err_msg = "The data buffer was not allocated to the correct type (i4_kind). This shouldn't happen"
324  return
325  end select
326  type is (integer(kind=i8_kind))
327  select type (db => this%buffer)
328  type is (integer(kind=i8_kind))
329  db(is:ie, js:je, ks:ke, :) = input_data
330  class default
331  err_msg = "The data buffer was not allocated to the correct type (i8_kind). This shouldn't happen"
332  return
333  end select
334  end select
335  end function set_input_buffer_object
336 
337  !> @brief Determine if an input buffer is initialized
338  !! @return .true. if the input buffer is initialized
339  pure logical function is_initialized(this)
340  class(fmsdiaginputbuffer_t), intent(in) :: this !< input buffer object
341 
342  is_initialized = .false.
343  if (this%initialized) then
344  is_initialized = .true.
345  else
346  if (allocated(this%buffer)) is_initialized = .true.
347  endif
348  end function is_initialized
349 
350 #include "fms_diag_input_buffer_r4.fh"
351 #include "fms_diag_input_buffer_r8.fh"
352 
353 #endif
354 end module fms_diag_input_buffer_mod
355 !> @}
character(len=128) function append_data_buffer_wrapper(mask_out, mask_in, data_out, data_in)
Appends the input_data_buffer and the mask (only when the mask is set to .True.)
subroutine set_send_data_time(this, time)
Sets the time send data was called last.
pure logical function is_initialized(this)
Determine if an input buffer is initialized.
character(len=128) function allocate_input_buffer_object(this, input_data, axis_ids, diag_axis)
Initiliazes an input data buffer.
character(len=128) function update_input_buffer_object(this, input_data, is, js, ks, ie, je, ke, mask_in, mask_out, mask_variant, var_is_masked)
Updates the input data buffer object for the current send_data call.
class(*) function, dimension(:,:,:,:), pointer get_buffer(this)
Get the buffer from the input buffer object.
subroutine init_input_buffer_object(this)
Initiliazes an input data buffer and the counter.
subroutine prepare_input_buffer_object(this, field_info)
Prepare the input data buffer to do the reduction methods (i.e divide by the number of times send dat...
character(len=128) function sum_data_buffer_wrapper(mask, data_out, data_in, counter, var_is_masked)
Sums the data in the input_data_buffer.
character(len=128) function set_input_buffer_object(this, input_data, weight, is, js, ks, ie, je, ke)
Sets the members of the input buffer object.
real(kind=r8_kind) function, pointer get_weight(this)
Get the weight from the input buffer object.
type(time_type) function get_send_data_time(this)
Get the time send data was called last.
Sums the data in the input_data_buffer.
Type to hold the information needed for the input buffer This is used when set_math_needs_to_be_done ...
Error handler.
Definition: mpp.F90:381
Type to represent amounts of time. Implemented as seconds and days to allow for larger intervals.
Appends the input_data_buffer and the mask (only when the mask is set to .True.)
Type to hold the diag_axis (either subaxis or a full axis)
Type to hold the diagnostic axis description.