FMS  2025.02.01
Flexible Modeling System
gex.F90
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
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.
10 !*
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
14 !* for more details.
15 !*
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 !***********************************************************************
19 
20 !> @defgroup gex_mod gex_mod
21 !> @ingroup gex
22 !> @brief Simple generic exchange (gex) interface to pass (non-tracer) fields across components
23 !> @author Fabien Paulot (Fabien.Paulot@noaa.gov)
24 !!
25 !!
26 !!
27 !!# 1. Introduction
28 !!
29 !!**gex** provides a generic interface to pass diagnostic fields across components.
30 !!This interface is not meant to pass tracers across components.
31 !!
32 !!# 2. Setup
33 !!
34 !!## 2.1. Field table
35 !!
36 !!Each exchanged field needs to be specified in the `coupler_mod` field table as:
37 !!
38 !!> SENDING_COMPONENT_to_RECEIVING_COMPONENT_ex, "coupler_mod", GEX_NAME
39 !!
40 !!`SENDING_COMPONENT` and `RECEIVING_COMPONENT` can be `lnd`, `atm` and `ocn`
41 !!`GEX_NAME` is the name under which the exchanged field is stored within **gex**
42 !!
43 !!Additional information can be provided regarding the field units.
44 !!
45 !!*Example*
46 !!
47 !! "atm_to_lnd_ex","coupler_mod","dryoa"
48 !! "units","kg/m2/s"
49 !! /
50 !!
51 !!
52 !!
53 !!## 2.2 Sending routine
54 !!
55 !!Two things need to happen:
56 !!
57 !!- At initialization, obtain index of exchanged field via:
58 !!
59 !! GEX_INDEX = gex_get_index(SENDING_COMPONENT,RECEIVING_COMPONENT,GEX_NAME)
60 !!
61 !! Returns `NO_TRACER` if `gex_name` is not found
62 !!
63 !! *Example*
64 !!
65 !! gex_dryoa = gex_get_index(MODEL_ATMOS,MODEL_LAND,'dryoa')
66 !!
67 !! requests index for `dryoa` field (exchanged from atmos->land) in gex.
68 !!
69 !!- Populate exchanged field
70 !!
71 !! gex_array(:,:,GEX_INDEX) = SOME_VALUE
72 !!
73 !! `gex array` is an array that contains all exchanged fields in the sending component.
74 !! It needs to be made available in the routine where the field of interest is calculated.
75 !!
76 !! *Example*
77 !!
78 !! gex_atm2lnd(:,:,gex_dryoa) = pwt(:,:,kd)*dsinku_lnd(:,:,nomphilic)
79 !!
80 !! stores the total OA deposition
81 !!
82 !!## 2.3 Receiving routine
83 !!
84 !!Receiving is very similar to sending.
85 !!
86 !!- To get the index of requested field (at initialization)
87 !!
88 !! GEX_INDEX = gex_get_index(SENDING_COMPONENT,RECEIVING_COMPONENT,GEX_NAME)
89 !!
90 !!- To get value of exchanged field
91 !!
92 !! gex_array(:,:,GEX_INDEX)
93 !!
94 !! `gex array` is an array that contains all exchanged fields in the receiving component.
95 !! It needs to be made available in the routine where the field of interest is needed
96 !!
97 !> @file
98 !> @addtogroup gex_mod
99 !> @brief File for @ref gex_mod
100 
101 module gex_mod
102 
103 use fms_mod, only: lowercase, error_mesg, fatal, note
104 use tracer_manager_mod, only: no_tracer
105 use field_manager_mod, only: model_land, model_atmos, model_ocean, num_models
106 use field_manager_mod, only: fm_list_iter_type, fm_dump_list, fm_field_name_len, &
111 use mpp_mod, only: mpp_root_pe, mpp_pe
112 use fms_string_utils_mod, only: string
113 
114 implicit none ; private
115 
116 public :: gex_init, gex_get_index,gex_get_n_ex, gex_get_property, gex_name, gex_units
117 public :: check_gex
118 
119 character(3) :: module_name = 'gex' !< module name
120 logical :: initialized = .false. !< is module initialized
121 
122 integer, parameter :: gex_name = 1 !< internal index for gex_name
123 integer, parameter :: gex_units = 2 !< internal index for gex unit
124 
125 !> @brief This type represents the entries for a specific exchanged field
126 !> @ingroup gex_mod
128  character(fm_field_name_len):: name = '' !< gex name
129  character(fm_string_len) :: units = '' !< units (optional)
130  logical :: set = .false.
131 end type gex_type
132 
133 !> @brief This type stores information about all the exchanged fields
134 !> @ingroup gex_mod
136  type(gex_type), allocatable:: field(:)
137 end type gex_type_r
138 
139 integer, allocatable :: n_gex(:,:)
140 type(gex_type_r), allocatable :: gex_fields(:,:)
141 
142 !> @brief check that gex field was accessed by the sending component
143 !> @ingroup gex_mod
144 interface check_gex
145  module procedure check_gex_name
146  module procedure check_gex_index
147 end interface check_gex
148 
149 
150 !> @addtogroup gex_mod
151 !> @{
152 contains
153 
154 !> @brief Subroutine to initialize generic exchange between model components
155 subroutine gex_init()
156  if (initialized) return
157 
158  call field_manager_init
159 
160  allocate(n_gex(num_models,num_models))
161  allocate(gex_fields(num_models,num_models))
162 
163  n_gex(:,:) = 0
164 
165  if (mpp_pe()==mpp_root_pe()) then
166  write(*,*) ''
167  write(*,*) '####################################'
168  write(*,*) '# generic exchanged fields [gex] #'
169  write(*,*) '####################################'
170  write(*,*) ''
171  end if
172 
173  call gex_read_field_table('/coupler_mod/atm_to_lnd_ex',model_atmos,model_land)
174  call gex_read_field_table('/coupler_mod/lnd_to_atm_ex',model_land,model_atmos)
175  call gex_read_field_table('/coupler_mod/atm_to_ocn_ex',model_atmos,model_ocean)
176  call gex_read_field_table('/coupler_mod/ocn_to_atm_ex',model_ocean,model_atmos)
177 
178  if (mpp_pe()==mpp_root_pe()) then
179  write(*,*) ''
180  write(*,*) '####################################'
181  write(*,*) ''
182  end if
183 
184  initialized = .true.
185 end subroutine gex_init
186 
187 !> @brief Subroutine to fields for a given exchange
188 subroutine gex_read_field_table(listroot,MODEL_SRC,MODEL_REC)
189  character(len=*), intent(in) :: listroot !< name of the field manager list
190  integer, intent(in) :: MODEL_SRC !< index of the model where the field comes FROM
191  integer, intent(in) :: MODEL_REC !< index of the model where the field goes TO
192 
193  type(fm_list_iter_type) :: iter ! iterator over the list of tracers
194  character(fm_field_name_len) :: name = '' ! name of the tracer
195  character(fm_type_name_len) :: ftype ! type of the field table entry (not used)
196  character(fm_path_name_len) :: listname ! name of the field manager list for each tracer
197 
198  integer :: n
199 
200  if(.not.fm_dump_list(listroot, recursive=.true.)) then
201  call error_mesg('gex_read_field_table', &
202  'Cannot dump field list "'//listroot//'". No additional field will be exchanged', &
203  note)
204  return
205  endif
206 
207  n_gex(model_src,model_rec) = fm_get_length(listroot)
208  allocate(gex_fields(model_src,model_rec)%field(n_gex(model_src,model_rec)))
209 
210  call fm_init_loop(listroot,iter)
211  do while (fm_loop_over_list(iter, name, ftype, n))
212  associate(fld=>gex_fields(model_src,model_rec)%field(n)) ! define a shorthand, to avoid very long expressions
213  fld%name = trim(name)
214  ! switch to the list of tracer parameters
215  listname = trim(listroot)//'/'//trim(name)
216  if (.not.fm_change_list(listname)) then
217  call error_mesg(module_name,'Cannot change fm list to "'//trim(listname)//'"', fatal)
218  endif
219  ! read parameters
220  fld%units = fm_util_get_string('units', caller = module_name, default_value = '', scalar = .true.)
221  ! other parameters can be read here, for example:
222 
223  if (mpp_pe()==mpp_root_pe()) write(*,*) listroot,n, &
224  ' name="'//trim(fld%name)//'"', &
225  ' units="'//trim(fld%units)//'"'
226  end associate
227  end do
228 end subroutine gex_read_field_table
229 
230 !> @brief Function to return number of fields exchanged
231 function gex_get_n_ex(MODEL_SRC,MODEL_REC)
232  integer, intent(in) :: model_src !< index of the model where the field comes FROM
233  integer, intent(in) :: model_rec !< index of the model where the filed goes TO
234  integer :: gex_get_n_ex
235 
236  if (.not. initialized) call error_mesg('flux_exchange|gex_get_n_ex','gex not initialized',fatal)
237 
238  call gex_assert_valid_index(model_src, "MODEL_SRC", 1, num_models)
239  call gex_assert_valid_index(model_rec, "MODEL_REC", 1, num_models)
240 
241  gex_get_n_ex = n_gex(model_src,model_rec)
242 end function gex_get_n_ex
243 
244 !> @brief Function to return property value (string)
245 function gex_get_property(MODEL_SRC,MODEL_REC,gex_index,property)
246  integer, intent(in) :: model_src !< index of the model where the field comes FROM
247  integer, intent(in) :: model_rec !< index of the model where the filed goes TO
248  integer, intent(in) :: gex_index !< gex index
249  integer, intent(in) :: property !< requested property
250  character(len=64) :: gex_get_property
251  integer :: n
252 
253  if (.not. initialized) call error_mesg('flux_exchange|check_gex_property','gex not initialized',fatal)
254 
255  n = gex_get_n_ex(model_src, model_rec)
256  call gex_assert_valid_index(gex_index, "tracer", 1, n)
257 
258  associate(field => gex_fields(model_src,model_rec)%field(gex_index))
259  if (property.eq.gex_name) then
260  gex_get_property = trim(field%name)
261  elseif (property.eq.gex_units) then
262  gex_get_property = trim(field%units)
263  else
264  call error_mesg('flux_exchange|gex','property does not exist: ' // field%name,fatal)
265  end if
266  end associate
267 end function gex_get_property
268 
269 !> @brief Function to return index of exchanged field
270 function gex_get_index(MODEL_SRC,MODEL_REC,name,record)
271 
272  character(len=*), intent(in) :: name !< name of the tracer
273  integer, intent(in) :: model_src !< index of the model where the field comes FROM
274  integer, intent(in) :: model_rec !< index of the model where the filed goes TO
275  logical, intent(in), optional :: record !< register that the field was accessed
276  integer :: i
277  integer :: gex_get_index
278 
279  gex_get_index = no_tracer
280 
281  if (.not. initialized) call error_mesg('flux_exchange|gex_get_index','gex not initialized',fatal)
282 
283  do i = 1, n_gex(model_src,model_rec)
284  if (lowercase(trim(name)) == trim(gex_fields(model_src,model_rec)%field(i)%name)) then
285  gex_get_index = i
286  if (present(record)) then
287  if (record) gex_fields(model_src,model_rec)%field(i)%set = .true.
288  endif
289  exit
290  endif
291  enddo
292 
293  return
294 end function gex_get_index
295 
296 !> @brief Function to return the value of exchanged field and check that it was set
297 function check_gex_name(MODEL_SRC,MODEL_REC,name)
298 
299  integer, intent(in) :: model_src !< index of the model where the field comes FROM
300  integer, intent(in) :: model_rec !< index of the model where the filed goes TO
301  character(len=*), intent(in) :: name !< name of the tracer
302 
303  logical :: check_gex_name
304 
305  integer :: index
306 
307  if (.not. initialized) call error_mesg('flux_exchange|check_gex_name','gex not initialized',fatal)
308 
309  index = gex_get_index(model_src,model_rec,name)
310  check_gex_name = .false.
311  if (index.eq.no_tracer) then
312  call error_mesg('flux_exchange|gex','requested gex field does not exist '// &
313  name,fatal)
314  else
315  if (gex_fields(model_src,model_rec)%field(index)%set) then
316  check_gex_name = .true.
317  else
318  call error_mesg('flux_exchange|gex','requested gex field not set '// &
319  name,fatal)
320  end if
321  end if
322 
323  end function check_gex_name
324 
325 !> @brief Function to check if gex index is properly set
326 function check_gex_index(MODEL_SRC,MODEL_REC,index)
327 
328  integer, intent(in) :: model_src !< index of the model where the field comes FROM
329  integer, intent(in) :: model_rec !< index of the model where the filed goes TO
330  integer, intent(in) :: index !< gex index
331  logical :: check_gex_index
332 
333  check_gex_index = .false.
334 
335  if (index.eq.no_tracer) then
336  call error_mesg('flux_exchange|gex','requested gex field does not exist '// &
337  gex_fields(model_src,model_rec)%field(index)%name,fatal)
338  else
339  if (gex_fields(model_src,model_rec)%field(index)%set) then
340  check_gex_index = .true.
341  else
342  call error_mesg('flux_exchange|gex','requested gex field not set '// &
343  gex_fields(model_src,model_rec)%field(index)%name,fatal)
344  end if
345  end if
346  end function check_gex_index
347 
348 
349 !> @brief Check that an index falls within a range of valid values
350 subroutine gex_assert_valid_index(indx, name, lb, ub)
351  integer, intent(in) :: indx !< Index to check
352  integer, intent(in) :: lb !< Lower bound of valid indices
353  integer, intent(in) :: ub !< Upper bound of valid indices
354  character(*), intent(in) :: name !< Name of the index
355 
356  if (indx.lt.lb .or. indx.gt.ub) then
357  call error_mesg(module_name, "Invalid " // name // " index: " // string(indx), fatal)
358  endif
359 end subroutine gex_assert_valid_index
360 
361 end module gex_mod
362 
363 
364 !> @}
365 ! close documentation grouping
integer, parameter, public fm_string_len
The length of a character string representing character values for the field.
integer, parameter, public model_land
Land model.
integer function, public fm_get_length(name)
A function to return how many elements are contained within the named list or entry.
logical function, public fm_change_list(name)
Change the current list. Return true on success, false otherwise.
character(len=fms_path_len) function, public fm_get_current_list()
A function to return the full path of the current list.
subroutine, public field_manager_init(nfields, table_name)
Routine to initialize the field manager.
integer, parameter, public fm_field_name_len
The length of a character string representing the field name.
integer, parameter, public fm_type_name_len
The length of a character string representing the various types that the values of the field can take...
logical function, public fm_dump_list(name, recursive, unit)
A function to list properties associated with a field.
integer, parameter, public model_ocean
Ocean model.
integer, parameter, public fm_path_name_len
The length of a character string representing the field path.
subroutine, public fm_init_loop(loop_list, iter)
given a name of the list, prepares an iterator over the list content. If the name of the given list i...
integer, parameter, public model_atmos
Atmospheric model.
integer, parameter, public num_models
Number of models (ATMOS, OCEAN, LAND, ICE, COUPLER).
A function for looping over a list.
Iterator over the field manager list.
logical function, public fm_util_get_logical(name, caller, index, default_value, scalar)
Get a logical value from the Field Manager tree.
Definition: fm_util.F90:1227
real(r8_kind) function, public fm_util_get_real(name, caller, index, default_value, scalar)
Get a real value from the Field Manager tree.
Definition: fm_util.F90:1341
character(len=fm_string_len) function, public fm_util_get_string(name, caller, index, default_value, scalar)
Get a string value from the Field Manager tree.
Definition: fm_util.F90:1462
subroutine, public error_mesg(routine, message, level)
Print notes, warnings and error messages; terminates program for warning and error messages....
Definition: fms.F90:498
character(:) function, allocatable, public string(v, fmt)
Converts a number or a Boolean value to a string.
integer function, public gex_get_n_ex(MODEL_SRC, MODEL_REC)
Function to return number of fields exchanged.
Definition: gex.F90:232
subroutine, public gex_init()
Subroutine to initialize generic exchange between model components.
Definition: gex.F90:156
logical function check_gex_index(MODEL_SRC, MODEL_REC, index)
Function to check if gex index is properly set.
Definition: gex.F90:327
subroutine gex_assert_valid_index(indx, name, lb, ub)
Check that an index falls within a range of valid values.
Definition: gex.F90:351
subroutine gex_read_field_table(listroot, MODEL_SRC, MODEL_REC)
Subroutine to fields for a given exchange.
Definition: gex.F90:189
character(len=64) function, public gex_get_property(MODEL_SRC, MODEL_REC, gex_index, property)
Function to return property value (string)
Definition: gex.F90:246
logical function check_gex_name(MODEL_SRC, MODEL_REC, name)
Function to return the value of exchanged field and check that it was set.
Definition: gex.F90:298
integer function, public gex_get_index(MODEL_SRC, MODEL_REC, name, record)
Function to return index of exchanged field.
Definition: gex.F90:271
check that gex field was accessed by the sending component
Definition: gex.F90:144
This type represents the entries for a specific exchanged field.
Definition: gex.F90:127
This type stores information about all the exchanged fields.
Definition: gex.F90:135
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:407