FMS  2026.01
Flexible Modeling System
metadata_transfer.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 module metadata_transfer_mod
19  use platform_mod
20 #ifdef use_libMPI
21  use mpi, only: mpi_type_create_struct, mpi_type_commit, mpi_integer, mpi_character, &
22  mpi_double, mpi_float, mpi_int, mpi_long_int, mpi_success, mpi_address_kind
23 #endif
24  use mpp_mod, only: mpp_pe, mpp_root_pe, mpp_error, fatal, mpp_get_current_pelist, mpp_npes
25  use fms_mod, only: string
26 
27  implicit none
28 
29  public
30 
31 #ifdef use_libMPI
32  external MPI_Bcast
33 #endif
34 
35  integer, parameter :: real8_type = 1 !< enumeration for real(kind=8) data type
36  integer, parameter :: real4_type = 2 !< enumeration for real(kind=4) data type
37  integer, parameter :: int8_type = 3 !< enumeration for integer(kind=8) data type
38  integer, parameter :: int4_type = 4 !< enumeration for integer(kind=4) data type
39  integer, parameter :: str_type = 5 !< enumeration for string data type
40 
41  integer, parameter :: ATTR_NAME_MAX_LENGTH = 128
42  integer, parameter :: ATTR_VALUE_MAX_LENGTH = 128
43 
44  !> Base class for broadcasting netcdf attribute data as a struct, holds the common fields
45  !! and routines for initializing the mpi datatype so that children classes can
46  !! be broadcasted.
47  type, abstract :: metadata_class
48  private
49  integer :: mpi_type_id = -1 !< MPI datatype id corresponding to this data objects data
50  !! -1 if not set
51  integer :: attribute_length = -1 !< length of the attribute value array, -1 if not set
52  character(len=ATTR_NAME_MAX_LENGTH) :: attribute_name !< name of the attribute to write
53  contains
54  procedure :: fms_metadata_broadcast
55  procedure :: fms_metadata_transfer_init
56  procedure :: get_attribute_name
57  procedure :: set_attribute_name
58  end type
59 
60 
61  !> Metadata class for real(kind=8) attribute values
62  type, extends(metadata_class) :: metadata_r8_type
63  real(r8_kind) :: attribute_value(ATTR_VALUE_MAX_LENGTH)
64  contains
65  procedure :: get_attribute_value => get_attribute_r8_value
66  procedure :: set_attribute_value => set_attribute_r8_value
67  end type metadata_r8_type
68 
69  !> Metadata class for real(kind=4) attribute values
70  type, extends(metadata_class) :: metadata_r4_type
71  real(r4_kind) :: attribute_value(ATTR_VALUE_MAX_LENGTH)
72  contains
73  procedure :: get_attribute_value => get_attribute_r4_value
74  procedure :: set_attribute_value => set_attribute_r4_value
75  end type metadata_r4_type
76 
77  !> Metadata class for integer(kind=8) attribute values
78  type, extends(metadata_class) :: metadata_i8_type
79  integer(i8_kind) :: attribute_value(ATTR_VALUE_MAX_LENGTH)
80  contains
81  procedure :: get_attribute_value => get_attribute_i8_value
82  procedure :: set_attribute_value => set_attribute_i8_value
83  end type metadata_i8_type
84 
85  !> Metadata class for integer(kind=4) attribute values
86  type, extends(metadata_class) :: metadata_i4_type
87  integer(i4_kind) :: attribute_value(ATTR_VALUE_MAX_LENGTH)
88  contains
89  procedure :: get_attribute_value => get_attribute_i4_value
90  procedure :: set_attribute_value => set_attribute_i4_value
91  end type metadata_i4_type
92 
93  !> Metadata class for string attribute values
95  character(len=ATTR_VALUE_MAX_LENGTH) :: attribute_value
96  contains
97  procedure :: get_attribute_value => get_attribute_str_value
98  procedure :: set_attribute_value => set_attribute_str_value
99  end type metadata_str_type
100 
101  contains
102 
103  !> Initialize the mpi datatype for future broadcasts
104  !! The metadata object's functions (not subroutines) are stored as fields in memory,
105  !! so they need to be included in the MPI struct declaration.
106  subroutine fms_metadata_transfer_init(this, dtype)
107  class(metadata_class), intent(inout) :: this !<metadata object to initialize for mpi communication using the struct
108  integer, intent(in) :: dtype !< data type and kind for the metadata's value
109  !! must be real8_type, real4_type, int8_type, int4_type, or str_type
110  integer, dimension(0:4) :: lengths, types
111 #ifdef use_libMPI
112  integer(KIND=MPI_ADDRESS_KIND), dimension(0:4) :: displacements
113  integer :: ierror, mpi_id
114 
115  !! since the actual data array is at the end of the struct, displacements are the same for all types
116  !displacements = (/ 0, sizeof(0), sizeof(0)*2, sizeof(0)*3, &
117  ! sizeof(0)*3 + sizeof(' ')*ATTR_NAME_MAX_LENGTH, &
118  ! sizeof(0)*4 + sizeof(' ')*ATTR_NAME_MAX_LENGTH, &
119  ! sizeof(0)*4 :+ sizeof(' ')*ATTR_NAME_MAX_LENGTH + sizeof(' ') &
120  ! /)
121  displacements(0) = 0_mpi_address_kind ! id start address
122  displacements(1) = displacements(0) + sizeof(0) ! attribute_length start address
123  displacements(2) = displacements(1) + sizeof(0) ! attribute_name start adress
124  displacements(3) = displacements(2) + sizeof(' ')*attr_name_max_length ! get_attribute_name() start address
125  displacements(4) = displacements(3) + sizeof(' ')*attr_name_max_length ! attribute_value start address
126 
127  select case(dtype)
128  case(real8_type)
129  types = (/mpi_integer, mpi_integer, mpi_character, mpi_character, mpi_double/)
130  case(real4_type)
131  types = (/mpi_integer, mpi_integer, mpi_character, mpi_character, mpi_float/)
132  case(int4_type)
133  types = (/mpi_integer, mpi_integer, mpi_character, mpi_character, mpi_int/)
134  case(int8_type)
135  types = (/mpi_integer, mpi_integer, mpi_character, mpi_character, mpi_long_int/)
136  case(str_type)
137  types = (/mpi_integer, mpi_integer, mpi_character, mpi_character, mpi_character/)
138  case default
139  call mpp_error(fatal, "fms_metadata_transfer_init:: given dtype argument contains a unsupported type")
140  end select
141 
142  !lengths = (/1, 1, 1, ATTR_NAME_MAX_LENGTH, ATTR_VALUE_MAX_LENGTH/)
143  lengths = (/1, 1, attr_name_max_length, attr_name_max_length, attr_value_max_length/)
144 
145  call mpi_type_create_struct(4, lengths, displacements, types, mpi_id, ierror)
146  if(ierror /= mpi_success) then
147  call mpp_error(fatal, "fms_metadata_transfer_init: MPI_Type_create_struct failed")
148  end if
149  call mpi_type_commit(mpi_id, ierror)
150  if(ierror /= mpi_success) then
151  call mpp_error(fatal, "fms_metadata_transfer_init: MPI_Type_commit failed")
152  end if
153  this%mpi_type_id = mpi_id
154 #else
155  call mpp_error(fatal, "fms_metadata_transfer_init: MPI library not enabled, cannot initialize metadata transfer")
156 #endif
157  end subroutine fms_metadata_transfer_init
158 
159  !> Broadcast the entire metadata object to all PEs in the current pelist
160  subroutine fms_metadata_broadcast(this)
161  class(metadata_class), intent(inout) :: this !< object that inherits metadata_class
162  integer :: ierror, curr_comm_id
163  integer, allocatable :: broadcasting_pes(:)
164  if (this%mpi_type_id .eq. -1) then
165  call mpp_error(fatal, "fms_metadata_broadcast: metadata_transfer not initialized")
166  end if
167 
168  allocate(broadcasting_pes(mpp_npes()))
169  call mpp_get_current_pelist(broadcasting_pes, commid=curr_comm_id)
170 
171 #ifdef use_libMPI
172  ! Broadcast the metadata transfer type to all processes
173  select type(this)
174  type is (metadata_r8_type)
175  call mpi_bcast(this, 1, this%mpi_type_id, mpp_root_pe(), curr_comm_id, ierror)
176  type is (metadata_r4_type)
177  call mpi_bcast(this, 1, this%mpi_type_id, mpp_root_pe(), curr_comm_id, ierror)
178  type is (metadata_i4_type)
179  call mpi_bcast(this, 1, this%mpi_type_id, mpp_root_pe(), curr_comm_id, ierror)
180  type is (metadata_i8_type)
181  call mpi_bcast(this, 1, this%mpi_type_id, mpp_root_pe(), curr_comm_id, ierror)
182  type is (metadata_str_type)
183  call mpi_bcast(this, 1, this%mpi_type_id, mpp_root_pe(), curr_comm_id, ierror)
184  end select
185  if (ierror /= mpi_success) then
186  call mpp_error(fatal, "fms_metadata_broadcast: MPI_Bcast failed")
187  end if
188 #else
189  call mpp_error(fatal, "fms_metadata_broadcast: MPI library not enabled")
190 #endif
191 
192 
193  end subroutine fms_metadata_broadcast
194 
195  !> Broadcast an array of metadata objects to all PEs in the current pelist
196  subroutine fms_metadata_broadcast_all(metadata_objs)
197  class(metadata_class), intent(inout) :: metadata_objs(:) !< list of metadata objects
198  integer :: i
199 
200  do i=1, size(metadata_objs)
201  if (metadata_objs(i)%mpi_type_id .eq. -1) then
202  call mpp_error(fatal, "fms_metadata_broadcast_all: metadata_transfer not initialized")
203  end if
204  call metadata_objs(i)%fms_metadata_broadcast()
205  enddo
206 
207  end subroutine fms_metadata_broadcast_all
208 
209  !> Getter for real 8 attribute_value
210  function get_attribute_r8_value(this) result(val)
211  class(metadata_r8_type), intent(inout) :: this
212  real(r8_kind), allocatable :: val(:)
213  val = this%attribute_value(1:this%attribute_length)
214  end function
215 
216  !> Setter for real 8 attribute_value
217  subroutine set_attribute_r8_value(this, val)
218  class(metadata_r8_type), intent(inout) :: this
219  real(r8_kind), intent(in) :: val(:) !< 8 byte real value to set attribute value to
220  if(size(val) .gt. attr_value_max_length) then
221  call mpp_error(fatal, &
222  "metadata_transfer_mod: attribute value array exceeds max length of "//string(attr_name_max_length))
223  endif
224  this%attribute_length = size(val)
225  this%attribute_value(1:size(val)) = val
226  end subroutine
227 
228  !> Getter for real 4 attribute_value
229  function get_attribute_r4_value(this) result(val)
230  class(metadata_r4_type), intent(inout) :: this
231  real(r4_kind), allocatable :: val(:)
232  val = this%attribute_value(1:this%attribute_length)
233  end function
234 
235  !> Setter for real 4 attribute_value
236  subroutine set_attribute_r4_value(this, val)
237  class(metadata_r4_type), intent(inout) :: this
238  real(r4_kind), intent(in) :: val(:) !< 4 byte real attribute to set
239  if(size(val) .gt. attr_value_max_length) then
240  call mpp_error(fatal, &
241  "metadata_transfer_mod: attribute value array exceeds max length of "//string(attr_name_max_length))
242  endif
243  this%attribute_length = size(val)
244  this%attribute_value(1:size(val)) = val
245  end subroutine
246 
247  !> Getter for integer(kind=8) attribute_value
248  function get_attribute_i8_value(this) result(val)
249  class(metadata_i8_type), intent(inout) :: this
250  integer(i8_kind), allocatable :: val(:)
251  val = this%attribute_value(1:this%attribute_length)
252  end function
253 
254  !> Setter for integer(kind=8) attribute_value
255  subroutine set_attribute_i8_value(this, val)
256  class(metadata_i8_type), intent(inout) :: this
257  integer(i8_kind), intent(in) :: val(:) !< 8 byte int attribute to set
258  if(size(val) .gt. attr_value_max_length) then
259  call mpp_error(fatal, &
260  "metadata_transfer_mod: attribute value array exceeds max length of "//string(attr_name_max_length))
261  endif
262  this%attribute_length = size(val)
263  this%attribute_value(1:size(val)) = val
264  end subroutine
265 
266  !> Getter for integer(kind=4) attribute_value
267  function get_attribute_i4_value(this) result(val)
268  class(metadata_i4_type), intent(inout) :: this
269  integer(i4_kind), allocatable :: val(:)
270  val = this%attribute_value(1:this%attribute_length)
271  end function
272 
273  !> Setter for integer(kind=4) attribute_value
274  subroutine set_attribute_i4_value(this, val)
275  class(metadata_i4_type), intent(inout) :: this
276  integer(i4_kind), intent(in) :: val(:) !< 4 byte integer to set attribute value to
277  if(size(val) .gt. attr_value_max_length) then
278  call mpp_error(fatal, &
279  "metadata_transfer_mod: attribute value array exceeds max length of "//string(attr_name_max_length))
280  endif
281  this%attribute_length = size(val)
282  this%attribute_value(1:size(val)) = val
283  end subroutine
284 
285  !> Getter for string attribute_value
286  function get_attribute_str_value(this) result(val)
287  class(metadata_str_type), intent(inout) :: this
288  character(len=:), allocatable :: val
289  val = this%attribute_value(1:this%attribute_length)
290  end function
291 
292  !> Setter for string attribute_value
293  subroutine set_attribute_str_value(this, val)
294  class(metadata_str_type), intent(inout) :: this
295  character(len=*), intent(in) :: val !< character string to set attribute value to
296  if(len(val) .gt. attr_value_max_length) then
297  call mpp_error(fatal, &
298  "metadata_transfer_mod: attribute value array exceeds max length of "//string(attr_name_max_length))
299  endif
300  this%attribute_length = len(val)
301  this%attribute_value(1:len(val)) = val
302  end subroutine
303 
304  !> Getter for attribute_name (for all metadata types)
305  function get_attribute_name(this) result(val)
306  class(metadata_class), intent(inout) :: this
307  character(len=ATTR_NAME_MAX_LENGTH) :: val
308  val = trim(this%attribute_name)
309  end function
310 
311  !> Setter for attribute_name (for all metadata types)
312  subroutine set_attribute_name(this, val)
313  class(metadata_class), intent(inout) :: this
314  character(len=*), intent(in) :: val !< character string to set attribute name to
315  if(len(val) .gt. attr_name_max_length) then
316  call mpp_error(fatal, &
317  "metadata_transfer_mod: attribute name exceeds max length of "//string(attr_value_max_length))
318  endif
319  this%attribute_name = val
320  end subroutine
321 
322 end module metadata_transfer_mod
integer function mpp_npes()
Returns processor count for current pelist.
Definition: mpp_util.inc:420
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:406
Error handler.
Definition: mpp.F90:381
Base class for broadcasting netcdf attribute data as a struct, holds the common fields and routines f...
Metadata class for integer(kind=4) attribute values.
Metadata class for integer(kind=8) attribute values.
Metadata class for real(kind=4) attribute values.
Metadata class for real(kind=8) attribute values.
Metadata class for string attribute values.