FMS  2024.03
Flexible Modeling System
coupler_types.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 !> @defgroup coupler_types_mod coupler_types_mod
20 !> @ingroup coupler
21 !> @brief This module contains type declarations for the coupler.
22 !> @author Richard Slater, John Dunne
23 
24 !> @addtogroup coupler_types_mod
25 !> @{
26 module coupler_types_mod
27  use fms_mod, only: write_version_number, lowercase
28  use fms2_io_mod, only: fmsnetcdfdomainfile_t, open_file, register_restart_field
29  use fms2_io_mod, only: register_axis, unlimited, variable_exists, check_if_open
30  use fms2_io_mod, only: register_field, get_num_dimensions, variable_att_exists
31  use fms2_io_mod, only: get_variable_attribute, get_dimension_size, get_dimension_names
32  use fms2_io_mod, only: register_variable_attribute, get_variable_dimension_names
33  use fms2_io_mod, only: get_variable_num_dimensions
34 #ifdef use_deprecated_io
35  use fms_io_mod, only: restart_file_type, fms_io_register_restart_field=>register_restart_field
36  use fms_io_mod, only: query_initialized, restore_state
37 #endif
38  use time_manager_mod, only: time_type
39  use diag_manager_mod, only: register_diag_field, send_data
40  use data_override_mod, only: data_override
41  use mpp_domains_mod, only: domain2d, mpp_redistribute
42  use mpp_mod, only: mpp_error, fatal, mpp_chksum
43  use fms_string_utils_mod, only: string
44  use platform_mod, only: r4_kind, r8_kind, i8_kind, fms_file_len, fms_path_len
45 
46  implicit none
47  private
48 
49 
50  ! Include variable "version" to be written to log file.
51 #include<file_version.h>
52 
53  public coupler_types_init
61 
64 
65  character(len=*), parameter :: mod_name = 'coupler_types_mod'
66 
67 !> @}
68 
69  !! mixed precision methodology for the encapsulated types:
70  !!
71  !! bc(:) (coupler_nd_real8_field) -> field(:) (coupler_nd_real8_values)
72  !! coupler_nd_bc_type <
73  !! bc_r4(:) (coupler_nd_real4_field) -> field(:) (coupler_nd_real4_values)
74  !!
75  !! Arrays (values + field) are typically directly allocated and then 'spawn' can be used to create a new type
76  !! from a previously allocated 'template' type
77 
78  !> Coupler data for 3D values
79  !> @ingroup coupler_types_mod
81  character(len=48) :: name = ' ' !< The diagnostic name for this array
82  logical :: mean = .true. !< mean
83  logical :: override = .false. !< override
84  integer :: id_diag = 0 !< The diagnostic id for this array
85  character(len=128) :: long_name = ' ' !< The diagnostic long_name for this array
86  character(len=128) :: units = ' ' !< The units for this array
87  integer :: id_rest = 0 !< The id of this array in the restart field
88  logical :: may_init = .true. !< If true, there is an internal method
89  !! that can be used to initialize this field
90  !! if it can not be read from a restart file
91  real(r8_kind), pointer, contiguous, dimension(:,:,:) :: values => null() !< The pointer to the
92  !! array of values for this field; this
93  !! should be changed to allocatable
95 
96  !> Coupler data for 3D fields
97  !> @ingroup coupler_types_mod
99  character(len=48) :: name = ' ' !< name
100  integer :: num_fields = 0 !< num_fields
101  type(coupler_3d_real8_values_type), pointer, dimension(:) :: field => null() !< field
102  character(len=128) :: flux_type = ' ' !< flux_type
103  character(len=128) :: implementation = ' ' !< implementation
104  logical, pointer, dimension(:) :: flag => null() !< flag
105  integer :: atm_tr_index = 0 !< atm_tr_index
106  character(len=FMS_FILE_LEN) :: ice_restart_file = ' ' !< ice_restart_file
107  character(len=FMS_FILE_LEN) :: ocean_restart_file = ' ' !< ocean_restart_file
108 #ifdef use_deprecated_io
109  type(restart_file_type), pointer :: rest_type => null() !< A pointer to the restart_file_type
110  !! that is used for this field.
111 #endif
112  type(fmsnetcdfdomainfile_t), pointer :: fms2_io_rest_type => null() !< A pointer to the restart_file_type
113  !! That is used for this field
114  logical :: use_atm_pressure !< use_atm_pressure
115  logical :: use_10m_wind_speed !< use_10m_wind_speed
116  logical :: pass_through_ice !< pass_through_ice
117  real(r8_kind), pointer, dimension(:) :: param => null() !< param
118  real(r8_kind) :: mol_wt = 0.0_r8_kind !< mol_wt
120 
121  !> Coupler data for 3D values
122  !> @ingroup coupler_types_mod
124  character(len=48) :: name = ' ' !< The diagnostic name for this array
125  logical :: mean = .true. !< mean
126  logical :: override = .false. !< override
127  integer :: id_diag = 0 !< The diagnostic id for this array
128  character(len=128) :: long_name = ' ' !< The diagnostic long_name for this array
129  character(len=128) :: units = ' ' !< The units for this array
130  integer :: id_rest = 0 !< The id of this array in the restart field
131  logical :: may_init = .true. !< If true, there is an internal method
132  !! that can be used to initialize this field
133  !! if it can not be read from a restart file
134  real(r4_kind), pointer, contiguous, dimension(:,:,:) :: values => null() !< The pointer to the
135  !! array of values for this field; this
136  !! should be changed to allocatable
138 
139  !> Coupler data for 3D fields
140  !> @ingroup coupler_types_mod
142  character(len=48) :: name = ' ' !< name
143  integer :: num_fields = 0 !< num_fields
144  type(coupler_3d_real4_values_type), pointer, dimension(:) :: field => null() !< field
145  character(len=128) :: flux_type = ' ' !< flux_type
146  character(len=128) :: implementation = ' ' !< implementation
147  logical, pointer, dimension(:) :: flag => null() !< flag
148  integer :: atm_tr_index = 0 !< atm_tr_index
149  character(len=FMS_FILE_LEN) :: ice_restart_file = ' ' !< ice_restart_file
150  character(len=FMS_FILE_LEN) :: ocean_restart_file = ' ' !< ocean_restart_file
151 #ifdef use_deprecated_io
152  type(restart_file_type), pointer :: rest_type => null() !< A pointer to the restart_file_type
153  !! that is used for this field.
154 #endif
155  type(fmsnetcdfdomainfile_t), pointer :: fms2_io_rest_type => null() !< A pointer to the restart_file_type
156  !! That is used for this field
157  logical :: use_atm_pressure !< use_atm_pressure
158  logical :: use_10m_wind_speed !< use_10m_wind_speed
159  logical :: pass_through_ice !< pass_through_ice
160  !> precision needs to be r8_kind since this array is retrieved from the field_manager routine
161  !! fm_util_get_real_array which only returns a r8_kind
162  !! Might be able to change to allocatable(?) to do a conversion
163  real(r8_kind), pointer, dimension(:) :: param => null() !< param
164  real(r8_kind) :: mol_wt = 0.0_r8_kind !< mol_wt
166 
167  !> Coupler data for 3D boundary conditions
168  !> @ingroup coupler_types_mod
169  type, public :: coupler_3d_bc_type
170  integer :: num_bcs = 0 !< The number of boundary condition fields
171  type(coupler_3d_real8_field_type), dimension(:), pointer :: bc => null() !< A pointer to the array of boundary
172  !! TODO above should be renamed eventually to indicate kind=8
173  type(coupler_3d_real4_field_type), dimension(:), pointer :: bc_r4 => null() !< A pointer to the array of boundary
174  logical :: set = .false. !< If true, this type has been initialized
175  integer :: isd, isc, iec, ied !< The i-direction data and computational domain index ranges for this type
176  integer :: jsd, jsc, jec, jed !< The j-direction data and computational domain index ranges for this type
177  integer :: ks, ke !< The k-direction index ranges for this type
178  end type coupler_3d_bc_type
179 
180 
181  !> Coupler data for 2D values
182  !> @ingroup coupler_types_mod
184  character(len=48) :: name = ' ' !< The diagnostic name for this array
185  real(r8_kind), pointer, contiguous, dimension(:,:) :: values => null() !< The pointer to the
186  !! array of values for this field; this
187  !! should be changed to allocatable
188  logical :: mean = .true. !< mean
189  logical :: override = .false. !< override
190  integer :: id_diag = 0 !< The diagnostic id for this array
191  character(len=128) :: long_name = ' ' !< The diagnostic long_name for this array
192  character(len=128) :: units = ' ' !< The units for this array
193  integer :: id_rest = 0 !< The id of this array in the restart field
194  logical :: may_init = .true. !< If true, there is an internal method
195  !! that can be used to initialize this field
196  !! if it can not be read from a restart file
198 
199  !> Coupler data for 2D fields
200  !> @ingroup coupler_types_mod
202  character(len=48) :: name = ' ' !< name
203  integer :: num_fields = 0 !< num_fields
204  type(coupler_2d_real8_values_type), pointer, dimension(:) :: field => null() !< field
205  character(len=128) :: flux_type = ' ' !< flux_type
206  character(len=128) :: implementation = ' ' !< implementation
207  real(r8_kind), pointer, dimension(:) :: param => null() !< param
208  logical, pointer, dimension(:) :: flag => null() !< flag
209  integer :: atm_tr_index = 0 !< atm_tr_index
210  character(len=FMS_FILE_LEN) :: ice_restart_file = ' ' !< ice_restart_file
211  character(len=FMS_FILE_LEN) :: ocean_restart_file = ' ' !< ocean_restart_file
212 #ifdef use_deprecated_io
213  type(restart_file_type), pointer :: rest_type => null() !< A pointer to the restart_file_type
214  !! that is used for this field.
215 #endif
216  type(fmsnetcdfdomainfile_t), pointer :: fms2_io_rest_type => null() !< A pointer to the restart_file_type
217  !! That is used for this field
218  logical :: use_atm_pressure !< use_atm_pressure
219  logical :: use_10m_wind_speed !< use_10m_wind_speed
220  logical :: pass_through_ice !< pass_through_ice
221  real(r8_kind) :: mol_wt = 0.0_r8_kind !< mol_wt
223 
224  !> Coupler data for 2D values
225  !> @ingroup coupler_types_mod
227  character(len=44) :: name = ' ' !< The diagnostic name for this array
228  real(r4_kind), pointer, contiguous, dimension(:,:) :: values => null() !< The pointer to the
229  !! array of values for this field; this
230  !! should be changed to allocatable
231  logical :: mean = .true. !< mean
232  logical :: override = .false. !< override
233  integer :: id_diag = 0 !< The diagnostic id for this array
234  character(len=124) :: long_name = ' ' !< The diagnostic long_name for this array
235  character(len=124) :: units = ' ' !< The units for this array
236  integer :: id_rest = 0 !< The id of this array in the restart field
237  logical :: may_init = .true. !< If true, there is an internal method
238  !! that can be used to initialize this field
239  !! if it can not be read from a restart file
241 
242  !> Coupler data for 2D fields
243  !> @ingroup coupler_types_mod
245  character(len=44) :: name = ' ' !< name
246  integer :: num_fields = 0 !< num_fields
247  type(coupler_2d_real4_values_type), pointer, dimension(:) :: field => null() !< field
248  character(len=124) :: flux_type = ' ' !< flux_type
249  character(len=124) :: implementation = ' ' !< implementation
250  !> precision needs to be r8_kind since this array is retrieved from the field_manager routine
251  !! fm_util_get_real_array which only returns a r8_kind
252  !! Might be able to change to allocatable(?) to do a conversion
253  real(r8_kind), pointer, dimension(:) :: param => null() !< param
254  logical, pointer, dimension(:) :: flag => null() !< flag
255  integer :: atm_tr_index = 0 !< atm_tr_index
256  character(len=FMS_FILE_LEN) :: ice_restart_file = ' ' !< ice_restart_file
257  character(len=FMS_FILE_LEN) :: ocean_restart_file = ' ' !< ocean_restart_file
258 #ifdef use_deprecated_io
259  type(restart_file_type), pointer :: rest_type => null() !< A pointer to the restart_file_type
260  !! that is used for this field.
261 #endif
262  type(fmsnetcdfdomainfile_t), pointer :: fms2_io_rest_type => null() !< A pointer to the restart_file_type
263  !! That is used for this field
264  logical :: use_atm_pressure !< use_atm_pressure
265  logical :: use_10m_wind_speed !< use_10m_wind_speed
266  logical :: pass_through_ice !< pass_through_ice
267  real(r8_kind) :: mol_wt = 0.0_r8_kind !< mol_wt
269 
270  !> Coupler data for 2D boundary conditions
271  !> @ingroup coupler_types_mod
272  type, public :: coupler_2d_bc_type
273  integer :: num_bcs = 0 !< The number of boundary condition fields
274  type(coupler_2d_real8_field_type), dimension(:), pointer :: bc => null() !< A pointer to the array of boundary
275  !! condition fields
276  type(coupler_2d_real4_field_type), dimension(:), pointer :: bc_r4 => null() !< A pointer to the array of boundary
277  !! condition fields
278  logical :: set = .false. !< If true, this type has been initialized
279  integer :: isd, isc, iec, ied !< The i-direction data and computational domain index ranges for this type
280  integer :: jsd, jsc, jec, jed !< The j-direction data and computational domain index ranges for this type
281  end type coupler_2d_bc_type
282 
283  !> Coupler data for 1D values
284  !> @ingroup coupler_types_mod
286  character(len=48) :: name = ' ' !< The diagnostic name for this array
287  real(r8_kind), pointer, dimension(:) :: values => null() !< The pointer to the array of values
288  logical :: mean = .true. !< mean
289  logical :: override = .false. !< override
290  integer :: id_diag = 0 !< The diagnostic id for this array
291  character(len=128) :: long_name = ' ' !< The diagnostic long_name for this array
292  character(len=128) :: units = ' ' !< The units for this array
293  logical :: may_init = .true. !< If true, there is an internal method
294  !! that can be used to initialize this field
295  !! if it can not be read from a restart file
297 
298  !> Coupler data for 1D fields
299  !> @ingroup coupler_types_mod
301  character(len=48) :: name = ' ' !< name
302  integer :: num_fields = 0 !< num_fields
303  type(coupler_1d_real8_values_type), pointer, dimension(:) :: field => null() !< field
304  character(len=128) :: flux_type = ' ' !< flux_type
305  character(len=128) :: implementation = ' ' !< implementation
306  !> precision has been explicitly defined
307  !! to be r8_kind during mixedmode update to field_manager
308  !! this explicit definition can be removed during the coupler update and be made into FMS_CP_KIND_
309  real(r8_kind), pointer, dimension(:) :: param => null() !< param
310  logical, pointer, dimension(:) :: flag => null() !< flag
311  integer :: atm_tr_index = 0 !< atm_tr_index
312  character(len=FMS_FILE_LEN) :: ice_restart_file = ' ' !< ice_restart_file
313  character(len=FMS_FILE_LEN) :: ocean_restart_file = ' ' !< ocean_restart_file
314  logical :: use_atm_pressure !< use_atm_pressure
315  logical :: use_10m_wind_speed !< use_10m_wind_speed
316  logical :: pass_through_ice !< pass_through_ice
317  !> precision has been explicitly defined
318  !! to be r8_kind during mixedmode update to field_manager
319  !! this explicit definition can be removed during the coupler update and be made into FMS_CP_KIND_
320  real(r8_kind) :: mol_wt = 0.0_r8_kind !< mol_wt
321 
323 
324  !> Coupler data for 1D values
325  !> @ingroup coupler_types_mod
327  character(len=48) :: name = ' ' !< The diagnostic name for this array
328  real(r4_kind), pointer, dimension(:) :: values => null() !< The pointer to the array of values
329  logical :: mean = .true. !< mean
330  logical :: override = .false. !< override
331  integer :: id_diag = 0 !< The diagnostic id for this array
332  character(len=128) :: long_name = ' ' !< The diagnostic long_name for this array
333  character(len=128) :: units = ' ' !< The units for this array
334  logical :: may_init = .true. !< If true, there is an internal method
335  !! that can be used to initialize this field
336  !! if it can not be read from a restart file
338 
339  !> Coupler data for 1D fields
340  !> @ingroup coupler_types_mod
342  character(len=48) :: name = ' ' !< name
343  integer :: num_fields = 0 !< num_fields
344  type(coupler_1d_real4_values_type), pointer, dimension(:) :: field => null() !< field
345  character(len=128) :: flux_type = ' ' !< flux_type
346  character(len=128) :: implementation = ' ' !< implementation
347  !> precision needs to be r8_kind since this array is retrieved from the field_manager routine
348  !! fm_util_get_real_array which only returns a r8_kind
349  !! Might be able to change to allocatable(?) to do a conversion
350  real(r8_kind), pointer, dimension(:) :: param => null() !< param
351  logical, pointer, dimension(:) :: flag => null() !< flag
352  integer :: atm_tr_index = 0 !< atm_tr_index
353  character(len=FMS_FILE_LEN) :: ice_restart_file = ' ' !< ice_restart_file
354  character(len=FMS_FILE_LEN) :: ocean_restart_file = ' ' !< ocean_restart_file
355  logical :: use_atm_pressure !< use_atm_pressure
356  logical :: use_10m_wind_speed !< use_10m_wind_speed
357  logical :: pass_through_ice !< pass_through_ice
358  !> This is also read in r8 from the field manager, but since its not a pointer the conversion is allowed
359  real(r8_kind) :: mol_wt = 0.0_r8_kind !< mol_wt
360 
362 
363  !> Coupler data for 1D boundary conditions
364  !> @ingroup coupler_types_mod
365  type, public :: coupler_1d_bc_type
366  integer :: num_bcs = 0 !< The number of boundary condition fields
367  type(coupler_1d_real8_field_type), dimension(:), pointer :: bc => null() !< A pointer to the array of boundary
368  !! condition fields
369  type(coupler_1d_real4_field_type), dimension(:), pointer :: bc_r4 => null() !< A pointer to the array of boundary
370  !! condition fields
371  logical :: set = .false. !< If true, this type has been initialized
372  end type coupler_1d_bc_type
373 
374  !> @addtogroup coupler_types_mod
375  !> @{
376  ! The following public parameters can help in selecting the sub-elements of a
377  ! coupler type. There are duplicate values because different boundary
378  ! conditions have different sub-elements.
379  ! Note: These should be parameters, but doing so would break openMP directives.
380  integer, public :: ind_pcair = 1 !< The index of the atmospheric concentration
381  integer, public :: ind_u10 = 2 !< The index of the 10 m wind speed
382  integer, public :: ind_psurf = 3 !< The index of the surface atmospheric pressure
383  integer, public :: ind_alpha = 1 !< The index of the solubility array for a tracer
384  integer, public :: ind_csurf = 2 !< The index of the ocean surface concentration
385  integer, public :: ind_sc_no = 3 !< The index for the Schmidt number for a tracer flux
386  integer, public :: ind_flux = 1 !< The index for the tracer flux
387  integer, public :: ind_deltap= 2 !< The index for ocean-air gas partial pressure change
388  integer, public :: ind_kw = 3 !< The index for the piston velocity
389  integer, public :: ind_flux0 = 4 !< The index for the piston velocity
390  integer, public :: ind_deposition = 1 !< The index for the atmospheric deposition flux
391  integer, public :: ind_runoff = 1 !< The index for a runoff flux
392  !> @}
393 
394  ! Interface definitions for overloaded routines
395 
396  !> This is the interface to spawn one coupler_bc_type into another and then
397  !! register diagnostics associated with the new type.
398  !> @ingroup coupler_types_mod
403  end interface coupler_type_copy
404 
405  !> This is the interface to spawn one coupler_bc_type into another.
406  !> @ingroup coupler_types_mod
408  module procedure ct_spawn_1d_2d, ct_spawn_2d_2d, ct_spawn_3d_2d
409  module procedure ct_spawn_1d_3d, ct_spawn_2d_3d, ct_spawn_3d_3d
410  end interface coupler_type_spawn
411 
412  !> This is the interface to copy the field data from one coupler_bc_type
413  !! to another of the same rank, size and decomposition.
414  !> @ingroup coupler_types_mod
417  end interface coupler_type_copy_data
418 
419  !> This is the interface to redistribute the field data from one coupler_bc_type
420  !! to another of the same rank and global size, but a different decomposition.
421  !> @ingroup coupler_types_mod
424  end interface coupler_type_redistribute_data
425 
426  !> This is the interface to rescale the field data in a coupler_bc_type.
427  !> @ingroup coupler_types_mod
429  module procedure ct_rescale_data_2d_r4, ct_rescale_data_3d_r4
430  module procedure ct_rescale_data_2d_r8, ct_rescale_data_3d_r8
431  end interface coupler_type_rescale_data
432 
433  !> This is the interface to increment the field data from one coupler_bc_type
434  !! with the data from another. Both must have the same horizontal size and
435  !! decomposition, but a 2d type may be incremented by a 2d or 3d type
436  !> @ingroup coupler_types_mod
439  module procedure ct_increment_data_2d_3d_r4, ct_increment_data_2d_3d_r8
440  end interface coupler_type_increment_data
441 
442  !> This is the interface to extract a field in a coupler_bc_type into an array.
443  !> @ingroup coupler_types_mod
445  module procedure ct_extract_data_2d_r4, ct_extract_data_2d_r8
446  module procedure ct_extract_data_3d_r4, ct_extract_data_3d_r8
447  module procedure ct_extract_data_3d_2d_r4, ct_extract_data_3d_2d_r8
448  end interface coupler_type_extract_data
449 
450  !> This is the interface to set a field in a coupler_bc_type from an array.
451  !> @ingroup coupler_types_mod
453  module procedure ct_set_data_2d_r4, ct_set_data_3d_r4, ct_set_data_2d_3d_r4
454  module procedure ct_set_data_2d_r8, ct_set_data_3d_r8, ct_set_data_2d_3d_r8
455  end interface coupler_type_set_data
456 
457  !> This is the interface to set diagnostics for the arrays in a coupler_bc_type.
458  !> @ingroup coupler_types_mod
460  module procedure ct_set_diags_2d, ct_set_diags_3d
461  end interface coupler_type_set_diags
462 
463  !> This is the interface to write out checksums for the elements of a coupler_bc_type.
464  !> @ingroup coupler_types_mod
466  module procedure ct_write_chksums_2d, ct_write_chksums_3d
467  end interface coupler_type_write_chksums
468 
469  !> This is the interface to write out diagnostics of the arrays in a coupler_bc_type.
470  !> @ingroup coupler_types_mod
472  module procedure ct_send_data_2d, ct_send_data_3d
473  end interface coupler_type_send_data
474 
475  !> This is the interface to override the values of the arrays in a coupler_bc_type.
476  !> @ingroup coupler_types_mod
478  module procedure ct_data_override_2d, ct_data_override_3d
479  end interface coupler_type_data_override
480 
481  !> This is the interface to register the fields in a coupler_bc_type to be saved
482  !! in restart files.
483  !> @ingroup coupler_types_mod
485 #ifdef use_deprecated_io
486  module procedure mpp_io_ct_register_restarts_2d, mpp_io_ct_register_restarts_3d
487  module procedure mpp_io_ct_register_restarts_to_file_2d, mpp_io_ct_register_restarts_to_file_3d
488 #endif
490  end interface coupler_type_register_restarts
491 
492  !> This is the interface to read in the fields in a coupler_bc_type that have
493  !! been saved in restart files.
494  !> @ingroup coupler_types_mod
496 #ifdef use_deprecated_io
497  module procedure mpp_io_ct_restore_state_2d, mpp_io_ct_restore_state_3d
498 #endif
499  module procedure ct_restore_state_2d, ct_restore_state_3d
500  end interface coupler_type_restore_state
501 
502  !> This function interface indicates whether a coupler_bc_type has been initialized.
503  !> @ingroup coupler_types_mod
506  end interface coupler_type_initialized
507 
508  !> This is the interface to deallocate any data associated with a coupler_bc_type.
509  !> @ingroup coupler_types_mod
512  end interface coupler_type_destructor
513 
514 contains
515 
516 !> @addtogroup coupler_types_mod
517 !> @{
518 
519  !> @brief Initialize the coupler types
521 
522  logical, save :: module_is_initialized = .false.
523 
524  ! Return if already intialized
525  if (module_is_initialized) then
526  return
527  endif
528 
529  ! Write out the version of the file to the log file.
530  call write_version_number(trim(mod_name), version)
531 
532  module_is_initialized = .true.
533 
534  return
535  end subroutine coupler_types_init !}
536 
537 
538  !> @brief Copy fields from one coupler type to another. 1-D to 2-D version for generic coupler_type_copy.
539  !!
540  !! @throw FATAL, "Number of output fields exceeds zero"
541  subroutine coupler_type_copy_1d_2d(var_in, var_out, is, ie, js, je,&
542  & diag_name, axes, time, suffix)
543  type(coupler_1d_bc_type), intent(in) :: var_in !< variable to copy information from
544  type(coupler_2d_bc_type), intent(inout) :: var_out !< variable to copy information to
545  integer, intent(in) :: is !< lower bound of first dimension
546  integer, intent(in) :: ie !< upper bound of first dimension
547  integer, intent(in) :: js !< lower bound of second dimension
548  integer, intent(in) :: je !< upper bound of second dimension
549  character(len=*), intent(in) :: diag_name !< name for diagnostic file--if blank, then
550  !! don't register the fields
551  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
552  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
553  character(len=*), intent(in), optional :: suffix !< optional suffix to make the name identifier unique
554 
555  character(len=*), parameter :: error_header =&
556  & '==>Error from coupler_types_mod (coupler_type_copy_1d_2d):'
557 
558  if (var_out%num_bcs > 0) then
559  ! It is an error if the number of output fields exceeds zero, because it means this
560  ! type has already been populated.
561  call mpp_error(fatal, trim(error_header) // ' Number of output fields exceeds zero')
562  endif
563 
564  if (var_in%num_bcs >= 0)&
565  & call ct_spawn_1d_2d(var_in, var_out, (/ is, is, ie, ie /), (/ js, js, je, je /), suffix)
566 
567  if ((var_out%num_bcs > 0) .and. (diag_name .ne. ' '))&
568  & call ct_set_diags_2d(var_out, diag_name, axes, time)
569  end subroutine coupler_type_copy_1d_2d
570 
571  !> @brief Copy fields from one coupler type to another. 1-D to 3-D version for generic coupler_type_copy.
572  !!
573  !!
574  !! @throw FATAL, "Number of output fields is exceeds zero"
575  subroutine coupler_type_copy_1d_3d(var_in, var_out, is, ie, js, je, kd,&
576  & diag_name, axes, time, suffix)
577  type(coupler_1d_bc_type), intent(in) :: var_in !< variable to copy information from
578  type(coupler_3d_bc_type), intent(inout) :: var_out !< variable to copy information to
579  integer, intent(in) :: is !< lower bound of first dimension
580  integer, intent(in) :: ie !< upper bound of first dimension
581  integer, intent(in) :: js !< lower bound of second dimension
582  integer, intent(in) :: je !< upper bound of second dimension
583  integer, intent(in) :: kd !< third dimension
584  character(len=*), intent(in) :: diag_name !< name for diagnostic file--if blank, then
585  !! don't register the fields
586  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
587  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
588  character(len=*), intent(in), optional :: suffix !< optional suffix to make the name identifier unique
589 
590  character(len=*), parameter :: error_header =&
591  & '==>Error from coupler_types_mod (coupler_type_copy_1d_3d):'
592 
593  if (var_out%num_bcs > 0) then
594  ! It is an error if the number of output fields exceeds zero, because it means this
595  ! type has already been populated.
596  call mpp_error(fatal, trim(error_header) // ' Number of output fields exceeds zero')
597  endif
598 
599  if (var_in%num_bcs >= 0)&
600  & call ct_spawn_1d_3d(var_in, var_out, (/ is, is, ie, ie /), (/ js, js, je, je /), (/1, kd/), suffix)
601 
602  if ((var_out%num_bcs > 0) .and. (diag_name .ne. ' '))&
603  & call ct_set_diags_3d(var_out, diag_name, axes, time)
604  end subroutine coupler_type_copy_1d_3d
605 
606  !> @brief Copy fields from one coupler type to another. 2-D to 2-D version for generic coupler_type_copy.
607  !!
608  !! @throw FATAL, "Number of output fields is exceeds zero"
609  subroutine coupler_type_copy_2d_2d(var_in, var_out, is, ie, js, je,&
610  & diag_name, axes, time, suffix)
611  type(coupler_2d_bc_type), intent(in) :: var_in !< variable to copy information from
612  type(coupler_2d_bc_type), intent(inout) :: var_out !< variable to copy information to
613  integer, intent(in) :: is !< lower bound of first dimension
614  integer, intent(in) :: ie !< upper bound of first dimension
615  integer, intent(in) :: js !< lower bound of second dimension
616  integer, intent(in) :: je !< upper bound of second dimension
617  character(len=*), intent(in) :: diag_name !< name for diagnostic file--if blank, then
618  !! don't register the fields
619  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
620  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
621  character(len=*), intent(in), optional :: suffix !< optional suffix to make the name identifier unique
622 
623  character(len=*), parameter :: error_header =&
624  & '==>Error from coupler_types_mod (coupler_type_copy_2d_2d):'
625 
626  if (var_out%num_bcs > 0) then
627  ! It is an error if the number of output fields exceeds zero, because it means this
628  ! type has already been populated.
629  call mpp_error(fatal, trim(error_header) // ' Number of output fields exceeds zero')
630  endif
631 
632  if (var_in%num_bcs >= 0)&
633  & call ct_spawn_2d_2d(var_in, var_out, (/ is, is, ie, ie /), (/ js, js, je, je /), suffix)
634 
635  if ((var_out%num_bcs > 0) .and. (diag_name .ne. ' '))&
636  & call ct_set_diags_2d(var_out, diag_name, axes, time)
637  end subroutine coupler_type_copy_2d_2d
638 
639  !> @brief Copy fields from one coupler type to another. 2-D to 3-D version for generic coupler_type_copy.
640  !!
641  !! @throw FATAL, "Number of output fields is exceeds zero"
642  subroutine coupler_type_copy_2d_3d(var_in, var_out, is, ie, js, je, kd,&
643  & diag_name, axes, time, suffix)
644  type(coupler_2d_bc_type), intent(in) :: var_in !< variable to copy information from
645  type(coupler_3d_bc_type), intent(inout) :: var_out !< variable to copy information to
646  integer, intent(in) :: is !< lower bound of first dimension
647  integer, intent(in) :: ie !< upper bound of first dimension
648  integer, intent(in) :: js !< lower bound of second dimension
649  integer, intent(in) :: je !< upper bound of second dimension
650  integer, intent(in) :: kd !< third dimension
651  character(len=*), intent(in) :: diag_name !< name for diagnostic file--if blank, then
652  !! don't register the fields
653  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
654  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
655  character(len=*), intent(in), optional :: suffix !< optional suffix to make the name identifier unique
656 
657  character(len=*), parameter :: error_header =&
658  & '==>Error from coupler_types_mod (coupler_type_copy_2d_3d):'
659 
660  if (var_out%num_bcs > 0) then
661  ! It is an error if the number of output fields exceeds zero, because it means this
662  ! type has already been populated.
663  call mpp_error(fatal, trim(error_header) // ' Number of output fields exceeds zero')
664  endif
665 
666  if (var_in%num_bcs >= 0)&
667  & call ct_spawn_2d_3d(var_in, var_out, (/ is, is, ie, ie /), (/ js, js, je, je /), (/1, kd/), suffix)
668 
669  if ((var_out%num_bcs > 0) .and. (diag_name .ne. ' '))&
670  & call ct_set_diags_3d(var_out, diag_name, axes, time)
671  end subroutine coupler_type_copy_2d_3d
672 
673  !> @brief Copy fields from one coupler type to another. 3-D to 2-D version for generic coupler_type_copy.
674  !!
675  !! @throw FATAL, "Number of output fields is exceeds zero"
676  subroutine coupler_type_copy_3d_2d(var_in, var_out, is, ie, js, je,&
677  & diag_name, axes, time, suffix)
678  type(coupler_3d_bc_type), intent(in) :: var_in !< variable to copy information from
679  type(coupler_2d_bc_type), intent(inout) :: var_out !< variable to copy information to
680  integer, intent(in) :: is !< lower bound of first dimension
681  integer, intent(in) :: ie !< upper bound of first dimension
682  integer, intent(in) :: js !< lower bound of second dimension
683  integer, intent(in) :: je !< upper bound of second dimension
684  character(len=*), intent(in) :: diag_name !< name for diagnostic file--if blank, then
685  !! don't register the fields
686  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
687  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
688  character(len=*), intent(in), optional :: suffix !< optional suffix to make the name identifier unique
689 
690  character(len=*), parameter :: error_header =&
691  & '==>Error from coupler_types_mod (coupler_type_copy_3d_2d):'
692 
693  if (var_out%num_bcs > 0) then
694  ! It is an error if the number of output fields exceeds zero, because it means this
695  ! type has already been populated.
696  call mpp_error(fatal, trim(error_header) // ' Number of output fields exceeds zero')
697  endif
698 
699  if (var_in%num_bcs >= 0)&
700  & call ct_spawn_3d_2d(var_in, var_out, (/ is, is, ie, ie /), (/ js, js, je, je /), suffix)
701 
702  if ((var_out%num_bcs > 0) .and. (diag_name .ne. ' '))&
703  & call ct_set_diags_2d(var_out, diag_name, axes, time)
704  end subroutine coupler_type_copy_3d_2d
705 
706  !> @brief Copy fields from one coupler type to another. 3-D to 3-D version for generic coupler_type_copy.
707  !!
708  !! @throw FATAL, "Number of output fields exceeds zero"
709  subroutine coupler_type_copy_3d_3d(var_in, var_out, is, ie, js, je, kd,&
710  & diag_name, axes, time, suffix)
711  type(coupler_3d_bc_type), intent(in) :: var_in !< variable to copy information from
712  type(coupler_3d_bc_type), intent(inout) :: var_out !< variable to copy information to
713  integer, intent(in) :: is !< lower bound of first dimension
714  integer, intent(in) :: ie !< upper bound of first dimension
715  integer, intent(in) :: js !< lower bound of second dimension
716  integer, intent(in) :: je !< upper bound of second dimension
717  integer, intent(in) :: kd !< third dimension
718  character(len=*), intent(in) :: diag_name !< name for diagnostic file--if blank, then
719  !! don't register the fields
720  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
721  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
722  character(len=*), intent(in), optional :: suffix !< optional suffix to make the name identifier unique
723 
724  character(len=*), parameter :: error_header =&
725  & '==>Error from coupler_types_mod (coupler_type_copy_3d_3d):'
726 
727  if (var_out%num_bcs > 0) then
728  ! It is an error if the number of output fields exceeds zero, because it means this
729  ! type has already been populated.
730  call mpp_error(fatal, trim(error_header) // ' Number of output fields exceeds zero')
731  endif
732 
733  if (var_in%num_bcs >= 0)&
734  & call ct_spawn_3d_3d(var_in, var_out, (/ is, is, ie, ie /), (/ js, js, je, je /), (/1, kd/), suffix)
735 
736  if ((var_out%num_bcs > 0) .and. (diag_name .ne. ' '))&
737  & call ct_set_diags_3d(var_out, diag_name, axes, time)
738  end subroutine coupler_type_copy_3d_3d
739 
740 
741  !> @brief Generate one coupler type using another as a template. 1-D to 2-D version for generic coupler_type_spawn.
742  !!
743  !! @throw FATAL, "The output type has already been initialized"
744  !! @throw FATAL, "The parent type has not been initialized"
745  !! @throw FATAL, "Disordered i-dimension index bound list"
746  !! @throw FATAL, "Disordered j-dimension index bound list"
747  !! @throw FATAL, "var%bc already assocated"
748  !! @throw FATAL, "var%bc('n')%field already associated"
749  !! @throw FATAL, "var%bc('n')%field('m')%values already associated"
750  subroutine ct_spawn_1d_2d(var_in, var, idim, jdim, suffix, as_needed)
751  type(coupler_1d_bc_type), intent(in) :: var_in !< structure from which to copy information
752  type(coupler_2d_bc_type), intent(inout) :: var !< structure into which to copy information
753  integer, dimension(4), intent(in) :: idim !< The data and computational domain extents of
754  !! the first dimension in a non-decreasing list
755  integer, dimension(4), intent(in) :: jdim !< The data and computational domain extents of
756  !! the second dimension in a non-decreasing list
757  character(len=*), optional, intent(in) :: suffix !< optional suffix to make the name identifier unique
758  logical, optional, intent(in) :: as_needed !< Only do the spawn if the target type (var)
759  !! is not set and the parent type (var_in) is set.
760 
761  character(len=*), parameter :: error_header =&
762  & '==>Error from coupler_types_mod (CT_spawn_1d_2d):'
763  character(len=400) :: error_msg
764  integer :: m, n
765 
766  if (present(as_needed)) then
767  if (as_needed) then
768  if ((var%set) .or. (.not.var_in%set)) return
769  endif
770  endif
771 
772  if (var%set)&
773  & call mpp_error(fatal, trim(error_header) // ' The output type has already been initialized.')
774  if (.not.var_in%set)&
775  & call mpp_error(fatal, trim(error_header) // ' The parent type has not been initialized.')
776 
777  ! check only one kind is used
778  if(var_in%num_bcs .gt. 0) then
779  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
780  if( associated(var_in%bc) ) then
781  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both intialized,"//&
782  " only one should be associated per type.")
783  else
784  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both uninitialized,"//&
785  " one must be associated to copy field data.")
786  endif
787  endif
788  endif
789 
790  var%num_bcs = var_in%num_bcs
791  var%set = .true.
792 
793  if ((idim(1) > idim(2)) .or. (idim(3) > idim(4))) then
794  write (error_msg, *) trim(error_header), ' Disordered i-dimension index bound list ', idim
795  call mpp_error(fatal, trim(error_msg))
796  endif
797  if ((jdim(1) > jdim(2)) .or. (jdim(3) > jdim(4))) then
798  write (error_msg, *) trim(error_header), ' Disordered j-dimension index bound list ', jdim
799  call mpp_error(fatal, trim(error_msg))
800  endif
801  var%isd = idim(1) ; var%isc = idim(2) ; var%iec = idim(3) ; var%ied = idim(4)
802  var%jsd = jdim(1) ; var%jsc = jdim(2) ; var%jec = jdim(3) ; var%jed = jdim(4)
803 
804  if (var%num_bcs > 0) then
805  if (associated(var_in%bc)) then
806  if (associated(var%bc)) then
807  call mpp_error(fatal, trim(error_header) // ' var%bc already associated')
808  endif
809  allocate ( var%bc(var%num_bcs) )
810  do n = 1, var%num_bcs
811  var%bc(n)%name = var_in%bc(n)%name
812  var%bc(n)%atm_tr_index = var_in%bc(n)%atm_tr_index
813  var%bc(n)%flux_type = var_in%bc(n)%flux_type
814  var%bc(n)%implementation = var_in%bc(n)%implementation
815  var%bc(n)%ice_restart_file = var_in%bc(n)%ice_restart_file
816  var%bc(n)%ocean_restart_file = var_in%bc(n)%ocean_restart_file
817  var%bc(n)%use_atm_pressure = var_in%bc(n)%use_atm_pressure
818  var%bc(n)%use_10m_wind_speed = var_in%bc(n)%use_10m_wind_speed
819  var%bc(n)%pass_through_ice = var_in%bc(n)%pass_through_ice
820  var%bc(n)%mol_wt = var_in%bc(n)%mol_wt
821  var%bc(n)%num_fields = var_in%bc(n)%num_fields
822  if (associated(var%bc(n)%field)) then
823  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field already associated'
824  call mpp_error(fatal, trim(error_msg))
825  endif
826  allocate ( var%bc(n)%field(var%bc(n)%num_fields) )
827  do m = 1, var%bc(n)%num_fields
828  if (present(suffix)) then
829  var%bc(n)%field(m)%name = trim(var_in%bc(n)%field(m)%name) // trim(suffix)
830  else
831  var%bc(n)%field(m)%name = var_in%bc(n)%field(m)%name
832  endif
833  var%bc(n)%field(m)%long_name = var_in%bc(n)%field(m)%long_name
834  var%bc(n)%field(m)%units = var_in%bc(n)%field(m)%units
835  var%bc(n)%field(m)%may_init = var_in%bc(n)%field(m)%may_init
836  var%bc(n)%field(m)%mean = var_in%bc(n)%field(m)%mean
837  if (associated(var%bc(n)%field(m)%values)) then
838  write (error_msg, *) trim(error_header),&
839  & ' var%bc(', n, ')%field(', m, ')%values already associated'
840  call mpp_error(fatal, trim(error_msg))
841  endif
842  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
843  allocate ( var%bc(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed) )
844  var%bc(n)%field(m)%values(:,:) = 0.0_r8_kind
845  enddo
846  enddo
847  else if( associated(var_in%bc_r4)) then
848  if (associated(var%bc_r4)) then
849  call mpp_error(fatal, trim(error_header) // ' var%bc_r4 already associated')
850  endif
851  allocate ( var%bc_r4(var%num_bcs) )
852  do n = 1, var%num_bcs
853  var%bc_r4(n)%name = var_in%bc_r4(n)%name
854  var%bc_r4(n)%atm_tr_index = var_in%bc_r4(n)%atm_tr_index
855  var%bc_r4(n)%flux_type = var_in%bc_r4(n)%flux_type
856  var%bc_r4(n)%implementation = var_in%bc_r4(n)%implementation
857  var%bc_r4(n)%ice_restart_file = var_in%bc_r4(n)%ice_restart_file
858  var%bc_r4(n)%ocean_restart_file = var_in%bc_r4(n)%ocean_restart_file
859  var%bc_r4(n)%use_atm_pressure = var_in%bc_r4(n)%use_atm_pressure
860  var%bc_r4(n)%use_10m_wind_speed = var_in%bc_r4(n)%use_10m_wind_speed
861  var%bc_r4(n)%pass_through_ice = var_in%bc_r4(n)%pass_through_ice
862  var%bc_r4(n)%mol_wt = var_in%bc_r4(n)%mol_wt
863  var%bc_r4(n)%num_fields = var_in%bc_r4(n)%num_fields
864  if (associated(var%bc_r4(n)%field)) then
865  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field already associated'
866  call mpp_error(fatal, trim(error_msg))
867  endif
868  allocate ( var%bc_r4(n)%field(var%bc_r4(n)%num_fields) )
869  do m = 1, var%bc_r4(n)%num_fields
870  if (present(suffix)) then
871  var%bc_r4(n)%field(m)%name = trim(var_in%bc_r4(n)%field(m)%name) // trim(suffix)
872  else
873  var%bc_r4(n)%field(m)%name = var_in%bc_r4(n)%field(m)%name
874  endif
875  var%bc_r4(n)%field(m)%long_name = var_in%bc_r4(n)%field(m)%long_name
876  var%bc_r4(n)%field(m)%units = var_in%bc_r4(n)%field(m)%units
877  var%bc_r4(n)%field(m)%may_init = var_in%bc_r4(n)%field(m)%may_init
878  var%bc_r4(n)%field(m)%mean = var_in%bc_r4(n)%field(m)%mean
879  if (associated(var%bc_r4(n)%field(m)%values)) then
880  write (error_msg, *) trim(error_header),&
881  & ' var%bc_r4(', n, ')%field(', m, ')%values already associated'
882  call mpp_error(fatal, trim(error_msg))
883  endif
884  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
885  allocate ( var%bc_r4(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed) )
886  var%bc_r4(n)%field(m)%values(:,:) = 0.0_r4_kind
887  enddo
888  enddo
889  else
890  call mpp_error(fatal, error_header//"passed in type has unassociated coupler_field_type"// &
891  " pointers for both bc and bc_r4")
892  endif
893  endif
894  end subroutine ct_spawn_1d_2d
895 
896  !> @brief Generate one coupler type using another as a template. 1-D to 3-D version for generic CT_spawn.
897  !!
898  !! @throw FATAL, "The output type has already been initialized"
899  !! @throw FATAL, "The parent type has not been initialized"
900  !! @throw FATAL, "Disordered i-dimension index bound list"
901  !! @throw FATAL, "Disordered j-dimension index bound list"
902  !! @throw FATAL, "var%bc already assocated"
903  !! @throw FATAL, "var%bc('n')%field already associated"
904  !! @throw FATAL, "var%bc('n')%field('m')%values already associated"
905  subroutine ct_spawn_1d_3d(var_in, var, idim, jdim, kdim, suffix, as_needed)
906  type(coupler_1d_bc_type), intent(in) :: var_in !< structure from which to copy information
907  type(coupler_3d_bc_type), intent(inout) :: var !< structure into which to copy information
908  integer, dimension(4), intent(in) :: idim !< The data and computational domain extents of
909  !! the first dimension in a non-decreasing list
910  integer, dimension(4), intent(in) :: jdim !< The data and computational domain extents of
911  !! the second dimension in a non-decreasing list
912  integer, dimension(2), intent(in) :: kdim !< The array extents of the third dimension in
913  !! a non-decreasing list
914  character(len=*), optional, intent(in) :: suffix !< optional suffix to make the name identifier unique
915  logical, optional, intent(in) :: as_needed !< Only do the spawn if the target type (var)
916  !! is not set and the parent type (var_in) is set.
917 
918  character(len=*), parameter :: error_header =&
919  & '==>Error from coupler_types_mod (CT_spawn_1d_3d):'
920  character(len=400) :: error_msg
921  integer :: m, n
922 
923  if (present(as_needed)) then
924  if (as_needed) then
925  if ((var%set) .or. (.not.var_in%set)) return
926  endif
927  endif
928 
929  if (var%set)&
930  & call mpp_error(fatal, trim(error_header) // ' The output type has already been initialized.')
931  if (.not.var_in%set)&
932  & call mpp_error(fatal, trim(error_header) // ' The parent type has not been initialized.')
933 
934  ! check only one kind is used
935  if(var_in%num_bcs .gt. 0) then
936  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
937  if( associated(var_in%bc)) then
938  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both intialized,"// &
939  " only one should be associated per type")
940  else
941  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both unintialized,"// &
942  " one must be associated to copy field data")
943  endif
944  endif
945  endif
946 
947  var%num_bcs = var_in%num_bcs
948  var%set = .true.
949 
950  ! Store the array extents that are to be used with this bc_type.
951  if ((idim(1) > idim(2)) .or. (idim(3) > idim(4))) then
952  write (error_msg, *) trim(error_header), ' Disordered i-dimension index bound list ', idim
953  call mpp_error(fatal, trim(error_msg))
954  endif
955  if ((jdim(1) > jdim(2)) .or. (jdim(3) > jdim(4))) then
956  write (error_msg, *) trim(error_header), ' Disordered j-dimension index bound list ', jdim
957  call mpp_error(fatal, trim(error_msg))
958  endif
959  var%isd = idim(1) ; var%isc = idim(2) ; var%iec = idim(3) ; var%ied = idim(4)
960  var%jsd = jdim(1) ; var%jsc = jdim(2) ; var%jec = jdim(3) ; var%jed = jdim(4)
961  var%ks = kdim(1) ; var%ke = kdim(2)
962 
963  if (var%num_bcs > 0) then
964  if (kdim(1) > kdim(2)) then
965  write (error_msg, *) trim(error_header), ' Disordered k-dimension index bound list ', kdim
966  call mpp_error(fatal, trim(error_msg))
967  endif
968  if( associated(var_in%bc)) then
969  if (associated(var%bc)) then
970  call mpp_error(fatal, trim(error_header) // ' var%bc already associated')
971  endif
972  allocate ( var%bc(var%num_bcs) )
973  do n = 1, var%num_bcs
974  var%bc(n)%name = var_in%bc(n)%name
975  var%bc(n)%atm_tr_index = var_in%bc(n)%atm_tr_index
976  var%bc(n)%flux_type = var_in%bc(n)%flux_type
977  var%bc(n)%implementation = var_in%bc(n)%implementation
978  var%bc(n)%ice_restart_file = var_in%bc(n)%ice_restart_file
979  var%bc(n)%ocean_restart_file = var_in%bc(n)%ocean_restart_file
980  var%bc(n)%use_atm_pressure = var_in%bc(n)%use_atm_pressure
981  var%bc(n)%use_10m_wind_speed = var_in%bc(n)%use_10m_wind_speed
982  var%bc(n)%pass_through_ice = var_in%bc(n)%pass_through_ice
983  var%bc(n)%mol_wt = var_in%bc(n)%mol_wt
984  var%bc(n)%num_fields = var_in%bc(n)%num_fields
985  if (associated(var%bc(n)%field)) then
986  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field already associated'
987  call mpp_error(fatal, trim(error_msg))
988  endif
989  allocate ( var%bc(n)%field(var%bc(n)%num_fields) )
990  do m = 1, var%bc(n)%num_fields
991  if (present(suffix)) then
992  var%bc(n)%field(m)%name = trim(var_in%bc(n)%field(m)%name) // trim(suffix)
993  else
994  var%bc(n)%field(m)%name = var_in%bc(n)%field(m)%name
995  endif
996  var%bc(n)%field(m)%long_name = var_in%bc(n)%field(m)%long_name
997  var%bc(n)%field(m)%units = var_in%bc(n)%field(m)%units
998  var%bc(n)%field(m)%may_init = var_in%bc(n)%field(m)%may_init
999  var%bc(n)%field(m)%mean = var_in%bc(n)%field(m)%mean
1000  if (associated(var%bc(n)%field(m)%values)) then
1001  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field(', m, ')%values already associated'
1002  call mpp_error(fatal, trim(error_msg))
1003  endif
1004  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1005  allocate ( var%bc(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed,var%ks:var%ke) )
1006  var%bc(n)%field(m)%values(:,:,:) = 0.0_r8_kind
1007  enddo
1008  enddo
1009  else if(associated(var_in%bc_r4)) then
1010  if (associated(var%bc_r4)) then
1011  call mpp_error(fatal, trim(error_header) // ' var%bc_r4 already associated')
1012  endif
1013  allocate ( var%bc_r4(var%num_bcs) )
1014  do n = 1, var%num_bcs
1015  var%bc_r4(n)%name = var_in%bc_r4(n)%name
1016  var%bc_r4(n)%atm_tr_index = var_in%bc_r4(n)%atm_tr_index
1017  var%bc_r4(n)%flux_type = var_in%bc_r4(n)%flux_type
1018  var%bc_r4(n)%implementation = var_in%bc_r4(n)%implementation
1019  var%bc_r4(n)%ice_restart_file = var_in%bc_r4(n)%ice_restart_file
1020  var%bc_r4(n)%ocean_restart_file = var_in%bc_r4(n)%ocean_restart_file
1021  var%bc_r4(n)%use_atm_pressure = var_in%bc_r4(n)%use_atm_pressure
1022  var%bc_r4(n)%use_10m_wind_speed = var_in%bc_r4(n)%use_10m_wind_speed
1023  var%bc_r4(n)%pass_through_ice = var_in%bc_r4(n)%pass_through_ice
1024  var%bc_r4(n)%mol_wt = var_in%bc_r4(n)%mol_wt
1025  var%bc_r4(n)%num_fields = var_in%bc_r4(n)%num_fields
1026  if (associated(var%bc_r4(n)%field)) then
1027  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field already associated'
1028  call mpp_error(fatal, trim(error_msg))
1029  endif
1030  allocate ( var%bc_r4(n)%field(var%bc_r4(n)%num_fields) )
1031  do m = 1, var%bc_r4(n)%num_fields
1032  if (present(suffix)) then
1033  var%bc_r4(n)%field(m)%name = trim(var_in%bc_r4(n)%field(m)%name) // trim(suffix)
1034  else
1035  var%bc_r4(n)%field(m)%name = var_in%bc_r4(n)%field(m)%name
1036  endif
1037  var%bc_r4(n)%field(m)%long_name = var_in%bc_r4(n)%field(m)%long_name
1038  var%bc_r4(n)%field(m)%units = var_in%bc_r4(n)%field(m)%units
1039  var%bc_r4(n)%field(m)%may_init = var_in%bc_r4(n)%field(m)%may_init
1040  var%bc_r4(n)%field(m)%mean = var_in%bc_r4(n)%field(m)%mean
1041  if (associated(var%bc_r4(n)%field(m)%values)) then
1042  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field(', m, ')%values already associated'
1043  call mpp_error(fatal, trim(error_msg))
1044  endif
1045  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1046  allocate ( var%bc_r4(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed,var%ks:var%ke) )
1047  var%bc_r4(n)%field(m)%values(:,:,:) = 0.0_r4_kind
1048  enddo
1049  enddo
1050  else
1051  call mpp_error(fatal, error_header//"passed in type has unassociated coupler_field_type"// &
1052  " pointers for both bc and bc_r4")
1053  endif
1054  endif
1055  end subroutine ct_spawn_1d_3d
1056 
1057 
1058  !> @brief Generate one coupler type using another as a template. 2-D to 2-D version for generic CT_spawn.
1059  !!
1060  !! @throw FATAL, "The output type has already been initialized"
1061  !! @throw FATAL, "The parent type has not been initialized"
1062  !! @throw FATAL, "Disordered i-dimension index bound list"
1063  !! @throw FATAL, "Disordered j-dimension index bound list"
1064  !! @throw FATAL, "var%bc already assocated"
1065  !! @throw FATAL, "var%bc('n')%field already associated"
1066  !! @throw FATAL, "var%bc('n')%field('m')%values already associated"
1067  subroutine ct_spawn_2d_2d(var_in, var, idim, jdim, suffix, as_needed)
1068  type(coupler_2d_bc_type), intent(in) :: var_in !< structure from which to copy information
1069  type(coupler_2d_bc_type), intent(inout) :: var !< structure into which to copy information
1070  integer, dimension(4), intent(in) :: idim !< The data and computational domain extents of
1071  !! the first dimension in a non-decreasing list
1072  integer, dimension(4), intent(in) :: jdim !< The data and computational domain extents of
1073  !! the second dimension in a non-decreasing list
1074  character(len=*), optional, intent(in) :: suffix !< optional suffix to make the name identifier unique
1075  logical, optional, intent(in) :: as_needed !< Only do the spawn if the target type (var)
1076  !! is not set and the parent type (var_in) is set.
1077 
1078  character(len=*), parameter :: error_header =&
1079  & '==>Error from coupler_types_mod (CT_spawn_2d_2d):'
1080  character(len=400) :: error_msg
1081  integer :: m, n
1082 
1083  if (present(as_needed)) then
1084  if (as_needed) then
1085  if ((var%set) .or. (.not.var_in%set)) return
1086  endif
1087  endif
1088 
1089  if (var%set)&
1090  & call mpp_error(fatal, trim(error_header) // ' The output type has already been initialized.')
1091  if (.not.var_in%set)&
1092  & call mpp_error(fatal, trim(error_header) // ' The parent type has not been initialized.')
1093 
1094  ! check only one kind is used
1095  if(var_in%num_bcs .gt. 0) then
1096  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
1097  if( associated(var_in%bc) ) then
1098  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both intialized,"// &
1099  " only one should be associated per type")
1100  else
1101  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both unintialized,"// &
1102  " one must be associated to copy field data")
1103  endif
1104  endif
1105  endif
1106 
1107  var%num_bcs = var_in%num_bcs
1108  var%set = .true.
1109 
1110  if ((idim(1) > idim(2)) .or. (idim(3) > idim(4))) then
1111  write (error_msg, *) trim(error_header), ' Disordered i-dimension index bound list ', idim
1112  call mpp_error(fatal, trim(error_msg))
1113  endif
1114  if ((jdim(1) > jdim(2)) .or. (jdim(3) > jdim(4))) then
1115  write (error_msg, *) trim(error_header), ' Disordered j-dimension index bound list ', jdim
1116  call mpp_error(fatal, trim(error_msg))
1117  endif
1118  var%isd = idim(1) ; var%isc = idim(2) ; var%iec = idim(3) ; var%ied = idim(4)
1119  var%jsd = jdim(1) ; var%jsc = jdim(2) ; var%jec = jdim(3) ; var%jed = jdim(4)
1120 
1121  if (var%num_bcs > 0) then
1122  if(associated(var_in%bc)) then
1123  if (associated(var%bc)) then
1124  call mpp_error(fatal, trim(error_header) // ' var%bc already associated')
1125  endif
1126  allocate ( var%bc(var%num_bcs) )
1127  do n = 1, var%num_bcs
1128  var%bc(n)%name = var_in%bc(n)%name
1129  var%bc(n)%atm_tr_index = var_in%bc(n)%atm_tr_index
1130  var%bc(n)%flux_type = var_in%bc(n)%flux_type
1131  var%bc(n)%implementation = var_in%bc(n)%implementation
1132  var%bc(n)%ice_restart_file = var_in%bc(n)%ice_restart_file
1133  var%bc(n)%ocean_restart_file = var_in%bc(n)%ocean_restart_file
1134  var%bc(n)%use_atm_pressure = var_in%bc(n)%use_atm_pressure
1135  var%bc(n)%use_10m_wind_speed = var_in%bc(n)%use_10m_wind_speed
1136  var%bc(n)%pass_through_ice = var_in%bc(n)%pass_through_ice
1137  var%bc(n)%mol_wt = var_in%bc(n)%mol_wt
1138  var%bc(n)%num_fields = var_in%bc(n)%num_fields
1139  if (associated(var%bc(n)%field)) then
1140  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field already associated'
1141  call mpp_error(fatal, trim(error_msg))
1142  endif
1143  allocate ( var%bc(n)%field(var%bc(n)%num_fields) )
1144  do m = 1, var%bc(n)%num_fields
1145  if (present(suffix)) then
1146  var%bc(n)%field(m)%name = trim(var_in%bc(n)%field(m)%name) // trim(suffix)
1147  else
1148  var%bc(n)%field(m)%name = var_in%bc(n)%field(m)%name
1149  endif
1150  var%bc(n)%field(m)%long_name = var_in%bc(n)%field(m)%long_name
1151  var%bc(n)%field(m)%units = var_in%bc(n)%field(m)%units
1152  var%bc(n)%field(m)%may_init = var_in%bc(n)%field(m)%may_init
1153  var%bc(n)%field(m)%mean = var_in%bc(n)%field(m)%mean
1154  if (associated(var%bc(n)%field(m)%values)) then
1155  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field(', m, ')%values already associated'
1156  call mpp_error(fatal, trim(error_msg))
1157  endif
1158  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1159  allocate ( var%bc(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed) )
1160  var%bc(n)%field(m)%values(:,:) = 0.0_r8_kind
1161  enddo
1162  enddo
1163  else if (associated(var_in%bc_r4)) then
1164  if (associated(var%bc_r4)) then
1165  call mpp_error(fatal, trim(error_header) // ' var%bc_r4 already associated')
1166  endif
1167  allocate ( var%bc_r4(var%num_bcs) )
1168  do n = 1, var%num_bcs
1169  var%bc_r4(n)%name = var_in%bc_r4(n)%name
1170  var%bc_r4(n)%atm_tr_index = var_in%bc_r4(n)%atm_tr_index
1171  var%bc_r4(n)%flux_type = var_in%bc_r4(n)%flux_type
1172  var%bc_r4(n)%implementation = var_in%bc_r4(n)%implementation
1173  var%bc_r4(n)%ice_restart_file = var_in%bc_r4(n)%ice_restart_file
1174  var%bc_r4(n)%ocean_restart_file = var_in%bc_r4(n)%ocean_restart_file
1175  var%bc_r4(n)%use_atm_pressure = var_in%bc_r4(n)%use_atm_pressure
1176  var%bc_r4(n)%use_10m_wind_speed = var_in%bc_r4(n)%use_10m_wind_speed
1177  var%bc_r4(n)%pass_through_ice = var_in%bc_r4(n)%pass_through_ice
1178  var%bc_r4(n)%mol_wt = var_in%bc_r4(n)%mol_wt
1179  var%bc_r4(n)%num_fields = var_in%bc_r4(n)%num_fields
1180  if (associated(var%bc_r4(n)%field)) then
1181  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field already associated'
1182  call mpp_error(fatal, trim(error_msg))
1183  endif
1184  allocate ( var%bc_r4(n)%field(var%bc_r4(n)%num_fields) )
1185  do m = 1, var%bc_r4(n)%num_fields
1186  if (present(suffix)) then
1187  var%bc_r4(n)%field(m)%name = trim(var_in%bc_r4(n)%field(m)%name) // trim(suffix)
1188  else
1189  var%bc_r4(n)%field(m)%name = var_in%bc_r4(n)%field(m)%name
1190  endif
1191  var%bc_r4(n)%field(m)%long_name = var_in%bc_r4(n)%field(m)%long_name
1192  var%bc_r4(n)%field(m)%units = var_in%bc_r4(n)%field(m)%units
1193  var%bc_r4(n)%field(m)%may_init = var_in%bc_r4(n)%field(m)%may_init
1194  var%bc_r4(n)%field(m)%mean = var_in%bc_r4(n)%field(m)%mean
1195  if (associated(var%bc_r4(n)%field(m)%values)) then
1196  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field(', m, ')%values already associated'
1197  call mpp_error(fatal, trim(error_msg))
1198  endif
1199  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1200  allocate ( var%bc_r4(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed) )
1201  var%bc_r4(n)%field(m)%values(:,:) = 0.0_r4_kind
1202  enddo
1203  enddo
1204  else
1205  call mpp_error(fatal, error_header//"passed in type has unassociated coupler_field_type"// &
1206  " pointers for both bc and bc_r4")
1207  endif
1208  endif
1209  end subroutine ct_spawn_2d_2d
1210 
1211  !> @brief Generate one coupler type using another as a template. 2-D to 3-D version for generic CT_spawn.
1212  !!
1213  !! @throw FATAL, "The output type has already been initialized"
1214  !! @throw FATAL, "The parent type has not been initialized"
1215  !! @throw FATAL, "Disordered i-dimension index bound list"
1216  !! @throw FATAL, "Disordered j-dimension index bound list"
1217  !! @throw FATAL, "Disordered k-dimension index bound list"
1218  !! @throw FATAL, "var%bc already assocated"
1219  !! @throw FATAL, "var%bc('n')%field already associated"
1220  !! @throw FATAL, "var%bc('n')%field('m')%values already associated"
1221  subroutine ct_spawn_2d_3d(var_in, var, idim, jdim, kdim, suffix, as_needed)
1222  type(coupler_2d_bc_type), intent(in) :: var_in !< structure from which to copy information
1223  type(coupler_3d_bc_type), intent(inout) :: var !< structure into which to copy information
1224  integer, dimension(4), intent(in) :: idim !< The data and computational domain extents of
1225  !! the first dimension in a non-decreasing list
1226  integer, dimension(4), intent(in) :: jdim !< The data and computational domain extents of
1227  !! the second dimension in a non-decreasing list
1228  integer, dimension(2), intent(in) :: kdim !< The array extents of the third dimension in
1229  !! a non-decreasing list
1230  character(len=*), optional, intent(in) :: suffix !< optional suffix to make the name identifier unique
1231  logical, optional, intent(in) :: as_needed !< Only do the spawn if the target type (var)
1232  !! is not set and the parent type (var_in) is set.
1233 
1234  character(len=*), parameter :: error_header =&
1235  & '==>Error from coupler_types_mod (CT_spawn_2d_3d):'
1236  character(len=400) :: error_msg
1237  integer :: m, n
1238 
1239  if (present(as_needed)) then
1240  if (as_needed) then
1241  if ((var%set) .or. (.not.var_in%set)) return
1242  endif
1243  endif
1244 
1245  if (var%set)&
1246  & call mpp_error(fatal, trim(error_header) // ' The output type has already been initialized.')
1247  if (.not.var_in%set)&
1248  & call mpp_error(fatal, trim(error_header) // ' The parent type has not been initialized.')
1249 
1250  if(var_in%num_bcs .gt. 0) then
1251  ! check only one kind is used
1252  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
1253  if( associated(var_in%bc) ) then
1254  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both intialized,"// &
1255  " only one should be associated per type")
1256  else
1257  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both unintialized,"// &
1258  " one must be associated to copy field data")
1259  endif
1260  endif
1261  endif
1262 
1263  var%num_bcs = var_in%num_bcs
1264  var%set = .true.
1265 
1266  ! Store the array extents that are to be used with this bc_type.
1267  if ((idim(1) > idim(2)) .or. (idim(3) > idim(4))) then
1268  write (error_msg, *) trim(error_header), ' Disordered i-dimension index bound list ', idim
1269  call mpp_error(fatal, trim(error_msg))
1270  endif
1271  if ((jdim(1) > jdim(2)) .or. (jdim(3) > jdim(4))) then
1272  write (error_msg, *) trim(error_header), ' Disordered j-dimension index bound list ', jdim
1273  call mpp_error(fatal, trim(error_msg))
1274  endif
1275  if (kdim(1) > kdim(2)) then
1276  write (error_msg, *) trim(error_header), ' Disordered k-dimension index bound list ', kdim
1277  call mpp_error(fatal, trim(error_msg))
1278  endif
1279  var%isd = idim(1) ; var%isc = idim(2) ; var%iec = idim(3) ; var%ied = idim(4)
1280  var%jsd = jdim(1) ; var%jsc = jdim(2) ; var%jec = jdim(3) ; var%jed = jdim(4)
1281  var%ks = kdim(1) ; var%ke = kdim(2)
1282 
1283  if (var%num_bcs > 0) then
1284  if( associated(var_in%bc)) then
1285  if (associated(var%bc)) then
1286  call mpp_error(fatal, trim(error_header) // ' var%bc already associated')
1287  endif
1288  allocate ( var%bc(var%num_bcs) )
1289  do n = 1, var%num_bcs
1290  var%bc(n)%name = var_in%bc(n)%name
1291  var%bc(n)%atm_tr_index = var_in%bc(n)%atm_tr_index
1292  var%bc(n)%flux_type = var_in%bc(n)%flux_type
1293  var%bc(n)%implementation = var_in%bc(n)%implementation
1294  var%bc(n)%ice_restart_file = var_in%bc(n)%ice_restart_file
1295  var%bc(n)%ocean_restart_file = var_in%bc(n)%ocean_restart_file
1296  var%bc(n)%use_atm_pressure = var_in%bc(n)%use_atm_pressure
1297  var%bc(n)%use_10m_wind_speed = var_in%bc(n)%use_10m_wind_speed
1298  var%bc(n)%pass_through_ice = var_in%bc(n)%pass_through_ice
1299  var%bc(n)%mol_wt = var_in%bc(n)%mol_wt
1300  var%bc(n)%num_fields = var_in%bc(n)%num_fields
1301  if (associated(var%bc(n)%field)) then
1302  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field already associated'
1303  call mpp_error(fatal, trim(error_msg))
1304  endif
1305  allocate ( var%bc(n)%field(var%bc(n)%num_fields) )
1306  do m = 1, var%bc(n)%num_fields
1307  if (present(suffix)) then
1308  var%bc(n)%field(m)%name = trim(var_in%bc(n)%field(m)%name) // trim(suffix)
1309  else
1310  var%bc(n)%field(m)%name = var_in%bc(n)%field(m)%name
1311  endif
1312  var%bc(n)%field(m)%long_name = var_in%bc(n)%field(m)%long_name
1313  var%bc(n)%field(m)%units = var_in%bc(n)%field(m)%units
1314  var%bc(n)%field(m)%may_init = var_in%bc(n)%field(m)%may_init
1315  var%bc(n)%field(m)%mean = var_in%bc(n)%field(m)%mean
1316  if (associated(var%bc(n)%field(m)%values)) then
1317  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field(', m, ')%values already associated'
1318  call mpp_error(fatal, trim(error_msg))
1319  endif
1320  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1321  allocate ( var%bc(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed,var%ks:var%ke) )
1322  var%bc(n)%field(m)%values(:,:,:) = 0.0_r8_kind
1323  enddo
1324  enddo
1325  else if(associated(var_in%bc_r4)) then
1326  if (associated(var%bc_r4)) then
1327  call mpp_error(fatal, trim(error_header) // ' var%bc_r4 already associated')
1328  endif
1329  allocate ( var%bc_r4(var%num_bcs) )
1330  do n = 1, var%num_bcs
1331  var%bc_r4(n)%name = var_in%bc_r4(n)%name
1332  var%bc_r4(n)%atm_tr_index = var_in%bc_r4(n)%atm_tr_index
1333  var%bc_r4(n)%flux_type = var_in%bc_r4(n)%flux_type
1334  var%bc_r4(n)%implementation = var_in%bc_r4(n)%implementation
1335  var%bc_r4(n)%ice_restart_file = var_in%bc_r4(n)%ice_restart_file
1336  var%bc_r4(n)%ocean_restart_file = var_in%bc_r4(n)%ocean_restart_file
1337  var%bc_r4(n)%use_atm_pressure = var_in%bc_r4(n)%use_atm_pressure
1338  var%bc_r4(n)%use_10m_wind_speed = var_in%bc_r4(n)%use_10m_wind_speed
1339  var%bc_r4(n)%pass_through_ice = var_in%bc_r4(n)%pass_through_ice
1340  var%bc_r4(n)%mol_wt = var_in%bc_r4(n)%mol_wt
1341  var%bc_r4(n)%num_fields = var_in%bc_r4(n)%num_fields
1342  if (associated(var%bc_r4(n)%field)) then
1343  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field already associated'
1344  call mpp_error(fatal, trim(error_msg))
1345  endif
1346  allocate ( var%bc_r4(n)%field(var%bc_r4(n)%num_fields) )
1347  do m = 1, var%bc_r4(n)%num_fields
1348  if (present(suffix)) then
1349  var%bc_r4(n)%field(m)%name = trim(var_in%bc_r4(n)%field(m)%name) // trim(suffix)
1350  else
1351  var%bc_r4(n)%field(m)%name = var_in%bc_r4(n)%field(m)%name
1352  endif
1353  var%bc_r4(n)%field(m)%long_name = var_in%bc_r4(n)%field(m)%long_name
1354  var%bc_r4(n)%field(m)%units = var_in%bc_r4(n)%field(m)%units
1355  var%bc_r4(n)%field(m)%may_init = var_in%bc_r4(n)%field(m)%may_init
1356  var%bc_r4(n)%field(m)%mean = var_in%bc_r4(n)%field(m)%mean
1357  if (associated(var%bc_r4(n)%field(m)%values)) then
1358  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field(', m, ')%values already associated'
1359  call mpp_error(fatal, trim(error_msg))
1360  endif
1361  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1362  allocate ( var%bc_r4(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed,var%ks:var%ke) )
1363  var%bc_r4(n)%field(m)%values(:,:,:) = 0.0_r4_kind
1364  enddo
1365  enddo
1366  else
1367  call mpp_error(fatal, error_header//"passed in type has unassociated coupler_field_type"// &
1368  " pointers for both bc and bc_r4")
1369  endif
1370  endif
1371  end subroutine ct_spawn_2d_3d
1372 
1373  !> @brief Generate one coupler type using another as a template. 3-D to 2-D version for generic CT_spawn.
1374  !!
1375  !! @throw FATAL, "The output type has already been initialized"
1376  !! @throw FATAL, "The parent type has not been initialized"
1377  !! @throw FATAL, "Disordered i-dimension index bound list"
1378  !! @throw FATAL, "Disordered j-dimension index bound list"
1379  !! @throw FATAL, "var%bc already assocated"
1380  !! @throw FATAL, "var%bc('n')%field already associated"
1381  !! @throw FATAL, "var%bc('n')%field('m')%values already associated"
1382  subroutine ct_spawn_3d_2d(var_in, var, idim, jdim, suffix, as_needed)
1383  type(coupler_3d_bc_type), intent(in) :: var_in !< structure from which to copy information
1384  type(coupler_2d_bc_type), intent(inout) :: var !< structure into which to copy information
1385  integer, dimension(4), intent(in) :: idim !< The data and computational domain extents of
1386  !! the first dimension in a non-decreasing list
1387  integer, dimension(4), intent(in) :: jdim !< The data and computational domain extents of
1388  !! the second dimension in a non-decreasing list
1389  character(len=*), optional, intent(in) :: suffix !< optional suffix to make the name identifier unique
1390  logical, optional, intent(in) :: as_needed !< Only do the spawn if the target type (var)
1391  !! is not set and the parent type (var_in) is set.
1392 
1393  character(len=*), parameter :: error_header =&
1394  & '==>Error from coupler_types_mod (CT_spawn_3d_2d):'
1395  character(len=400) :: error_msg
1396  integer :: m, n
1397 
1398  if (present(as_needed)) then
1399  if (as_needed) then
1400  if ((var%set) .or. (.not.var_in%set)) return
1401  endif
1402  endif
1403 
1404  if (var%set)&
1405  & call mpp_error(fatal, trim(error_header) // ' The output type has already been initialized.')
1406  if (.not.var_in%set)&
1407  & call mpp_error(fatal, trim(error_header) // ' The parent type has not been initialized.')
1408 
1409  if(var_in%num_bcs .gt. 0) then
1410  ! check only one kind is used
1411  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
1412  if( associated(var_in%bc) ) then
1413  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both intialized,"// &
1414  " only one should be associated per type")
1415  else
1416  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both unintialized,"// &
1417  " one must be associated to copy field data")
1418  endif
1419  endif
1420  endif
1421 
1422  var%num_bcs = var_in%num_bcs
1423  var%set = .true.
1424 
1425  if ((idim(1) > idim(2)) .or. (idim(3) > idim(4))) then
1426  write (error_msg, *) trim(error_header), ' Disordered i-dimension index bound list ', idim
1427  call mpp_error(fatal, trim(error_msg))
1428  endif
1429  if ((jdim(1) > jdim(2)) .or. (jdim(3) > jdim(4))) then
1430  write (error_msg, *) trim(error_header), ' Disordered j-dimension index bound list ', jdim
1431  call mpp_error(fatal, trim(error_msg))
1432  endif
1433  var%isd = idim(1) ; var%isc = idim(2) ; var%iec = idim(3) ; var%ied = idim(4)
1434  var%jsd = jdim(1) ; var%jsc = jdim(2) ; var%jec = jdim(3) ; var%jed = jdim(4)
1435 
1436  if (var%num_bcs > 0) then
1437  ! if using r8_kind reals
1438  if( associated(var_in%bc)) then
1439  if (associated(var%bc)) then
1440  call mpp_error(fatal, trim(error_header) // ' var%bc already associated')
1441  endif
1442  allocate ( var%bc(var%num_bcs) )
1443  do n = 1, var%num_bcs
1444  var%bc(n)%name = var_in%bc(n)%name
1445  var%bc(n)%atm_tr_index = var_in%bc(n)%atm_tr_index
1446  var%bc(n)%flux_type = var_in%bc(n)%flux_type
1447  var%bc(n)%implementation = var_in%bc(n)%implementation
1448  var%bc(n)%ice_restart_file = var_in%bc(n)%ice_restart_file
1449  var%bc(n)%ocean_restart_file = var_in%bc(n)%ocean_restart_file
1450  var%bc(n)%use_atm_pressure = var_in%bc(n)%use_atm_pressure
1451  var%bc(n)%use_10m_wind_speed = var_in%bc(n)%use_10m_wind_speed
1452  var%bc(n)%pass_through_ice = var_in%bc(n)%pass_through_ice
1453  var%bc(n)%mol_wt = var_in%bc(n)%mol_wt
1454  var%bc(n)%num_fields = var_in%bc(n)%num_fields
1455  if (associated(var%bc(n)%field)) then
1456  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field already associated'
1457  call mpp_error(fatal, trim(error_msg))
1458  endif
1459  allocate ( var%bc(n)%field(var%bc(n)%num_fields) )
1460  do m = 1, var%bc(n)%num_fields
1461  if (present(suffix)) then
1462  var%bc(n)%field(m)%name = trim(var_in%bc(n)%field(m)%name) // trim(suffix)
1463  else
1464  var%bc(n)%field(m)%name = var_in%bc(n)%field(m)%name
1465  endif
1466  var%bc(n)%field(m)%long_name = var_in%bc(n)%field(m)%long_name
1467  var%bc(n)%field(m)%units = var_in%bc(n)%field(m)%units
1468  var%bc(n)%field(m)%may_init = var_in%bc(n)%field(m)%may_init
1469  var%bc(n)%field(m)%mean = var_in%bc(n)%field(m)%mean
1470  if (associated(var%bc(n)%field(m)%values)) then
1471  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field(', m, ')%values already associated'
1472  call mpp_error(fatal, trim(error_msg))
1473  endif
1474  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1475  allocate ( var%bc(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed) )
1476  var%bc(n)%field(m)%values(:,:) = 0.0_r8_kind
1477  enddo
1478  enddo
1479  ! if using r4_kind reals (same logic)
1480  else if (associated(var_in%bc_r4)) then
1481  if (associated(var%bc_r4)) then
1482  call mpp_error(fatal, trim(error_header) // ' var%bc_r4 already associated')
1483  endif
1484  allocate ( var%bc_r4(var%num_bcs) )
1485  do n = 1, var%num_bcs
1486  var%bc_r4(n)%name = var_in%bc_r4(n)%name
1487  var%bc_r4(n)%atm_tr_index = var_in%bc_r4(n)%atm_tr_index
1488  var%bc_r4(n)%flux_type = var_in%bc_r4(n)%flux_type
1489  var%bc_r4(n)%implementation = var_in%bc_r4(n)%implementation
1490  var%bc_r4(n)%ice_restart_file = var_in%bc_r4(n)%ice_restart_file
1491  var%bc_r4(n)%ocean_restart_file = var_in%bc_r4(n)%ocean_restart_file
1492  var%bc_r4(n)%use_atm_pressure = var_in%bc_r4(n)%use_atm_pressure
1493  var%bc_r4(n)%use_10m_wind_speed = var_in%bc_r4(n)%use_10m_wind_speed
1494  var%bc_r4(n)%pass_through_ice = var_in%bc_r4(n)%pass_through_ice
1495  var%bc_r4(n)%mol_wt = var_in%bc_r4(n)%mol_wt
1496  var%bc_r4(n)%num_fields = var_in%bc_r4(n)%num_fields
1497  if (associated(var%bc_r4(n)%field)) then
1498  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field already associated'
1499  call mpp_error(fatal, trim(error_msg))
1500  endif
1501  allocate ( var%bc_r4(n)%field(var%bc_r4(n)%num_fields) )
1502  do m = 1, var%bc_r4(n)%num_fields
1503  if (present(suffix)) then
1504  var%bc_r4(n)%field(m)%name = trim(var_in%bc_r4(n)%field(m)%name) // trim(suffix)
1505  else
1506  var%bc_r4(n)%field(m)%name = var_in%bc_r4(n)%field(m)%name
1507  endif
1508  var%bc_r4(n)%field(m)%long_name = var_in%bc_r4(n)%field(m)%long_name
1509  var%bc_r4(n)%field(m)%units = var_in%bc_r4(n)%field(m)%units
1510  var%bc_r4(n)%field(m)%may_init = var_in%bc_r4(n)%field(m)%may_init
1511  var%bc_r4(n)%field(m)%mean = var_in%bc_r4(n)%field(m)%mean
1512  if (associated(var%bc_r4(n)%field(m)%values)) then
1513  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field(', m, ')%values already associated'
1514  call mpp_error(fatal, trim(error_msg))
1515  endif
1516  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1517  allocate ( var%bc_r4(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed) )
1518  var%bc_r4(n)%field(m)%values(:,:) = 0.0_r4_kind
1519  enddo
1520  enddo
1521  else
1522  call mpp_error(fatal, error_header//"passed in type has unassociated coupler_field_type"// &
1523  " pointers for both bc and bc_r4")
1524  endif
1525  endif
1526  end subroutine ct_spawn_3d_2d
1527 
1528  !> @brief Generate one coupler type using another as a template. 3-D to 3-D version for generic CT_spawn.
1529  !!
1530  !! @throw FATAL, "The output type has already been initialized"
1531  !! @throw FATAL, "The parent type has not been initialized"
1532  !! @throw FATAL, "Disordered i-dimension index bound list"
1533  !! @throw FATAL, "Disordered j-dimension index bound list"
1534  !! @throw FATAL, "Disordered k-dimension index bound list"
1535  !! @throw FATAL, "var%bc already assocated"
1536  !! @throw FATAL, "var%bc('n')%field already associated"
1537  !! @throw FATAL, "var%bc('n')%field('m')%values already associated"
1538  subroutine ct_spawn_3d_3d(var_in, var, idim, jdim, kdim, suffix, as_needed)
1539  type(coupler_3d_bc_type), intent(in) :: var_in !< structure from which to copy information
1540  type(coupler_3d_bc_type), intent(inout) :: var !< structure into which to copy information
1541  integer, dimension(4), intent(in) :: idim !< The data and computational domain extents of
1542  !! the first dimension in a non-decreasing list
1543  integer, dimension(4), intent(in) :: jdim !< The data and computational domain extents of
1544  !! the second dimension in a non-decreasing list
1545  integer, dimension(2), intent(in) :: kdim !< The array extents of the third dimension in
1546  !! a non-decreasing list
1547  character(len=*), optional, intent(in) :: suffix !< optional suffix to make the name identifier unique
1548  logical, optional, intent(in) :: as_needed !< Only do the spawn if the target type (var)
1549  !! is not set and the parent type (var_in) is set.
1550 
1551  character(len=*), parameter :: error_header =&
1552  & '==>Error from coupler_types_mod (CT_spawn_3d_3d):'
1553  character(len=400) :: error_msg
1554  integer :: m, n
1555 
1556  if (present(as_needed)) then
1557  if (as_needed) then
1558  if ((var%set) .or. (.not.var_in%set)) return
1559  endif
1560  endif
1561 
1562  if (var%set)&
1563  & call mpp_error(fatal, trim(error_header) // ' The output type has already been initialized.')
1564  if (.not.var_in%set)&
1565  & call mpp_error(fatal, trim(error_header) // ' The parent type has not been initialized.')
1566 
1567  if(var_in%num_bcs .gt. 0) then
1568  ! check only one kind is used
1569  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
1570  if( associated(var_in%bc)) then
1571  call mpp_error(fatal, error_header//"var_in%bc and var_in%bc_r4 are both initialized,"//&
1572  "only one should be allocated per type")
1573  else
1574  call mpp_error(fatal, error_header//"var_in%bc and var%bc_r4 are both uninitialized,"//&
1575  " one must be associated to copy field data")
1576  endif
1577  endif
1578  endif
1579 
1580  var%num_bcs = var_in%num_bcs
1581  var%set = .true.
1582 
1583  if ((idim(1) > idim(2)) .or. (idim(3) > idim(4))) then
1584  write (error_msg, *) trim(error_header), ' Disordered i-dimension index bound list ', idim
1585  call mpp_error(fatal, trim(error_msg))
1586  endif
1587  if ((jdim(1) > jdim(2)) .or. (jdim(3) > jdim(4))) then
1588  write (error_msg, *) trim(error_header), ' Disordered j-dimension index bound list ', jdim
1589  call mpp_error(fatal, trim(error_msg))
1590  endif
1591  if (kdim(1) > kdim(2)) then
1592  write (error_msg, *) trim(error_header), ' Disordered k-dimension index bound list ', kdim
1593  call mpp_error(fatal, trim(error_msg))
1594  endif
1595  var%isd = idim(1) ; var%isc = idim(2) ; var%iec = idim(3) ; var%ied = idim(4)
1596  var%jsd = jdim(1) ; var%jsc = jdim(2) ; var%jec = jdim(3) ; var%jed = jdim(4)
1597  var%ks = kdim(1) ; var%ke = kdim(2)
1598 
1599  if (var%num_bcs > 0) then
1600  if(associated(var_in%bc)) then
1601  if (associated(var%bc)) then
1602  call mpp_error(fatal, trim(error_header) // ' var%bc already associated')
1603  endif
1604  allocate ( var%bc(var%num_bcs) )
1605  do n = 1, var%num_bcs
1606  var%bc(n)%name = var_in%bc(n)%name
1607  var%bc(n)%atm_tr_index = var_in%bc(n)%atm_tr_index
1608  var%bc(n)%flux_type = var_in%bc(n)%flux_type
1609  var%bc(n)%implementation = var_in%bc(n)%implementation
1610  var%bc(n)%ice_restart_file = var_in%bc(n)%ice_restart_file
1611  var%bc(n)%ocean_restart_file = var_in%bc(n)%ocean_restart_file
1612  var%bc(n)%use_atm_pressure = var_in%bc(n)%use_atm_pressure
1613  var%bc(n)%use_10m_wind_speed = var_in%bc(n)%use_10m_wind_speed
1614  var%bc(n)%pass_through_ice = var_in%bc(n)%pass_through_ice
1615  var%bc(n)%mol_wt = var_in%bc(n)%mol_wt
1616  var%bc(n)%num_fields = var_in%bc(n)%num_fields
1617  if (associated(var%bc(n)%field)) then
1618  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field already associated'
1619  call mpp_error(fatal, trim(error_msg))
1620  endif
1621  allocate ( var%bc(n)%field(var%bc(n)%num_fields) )
1622  do m = 1, var%bc(n)%num_fields
1623  if (present(suffix)) then
1624  var%bc(n)%field(m)%name = trim(var_in%bc(n)%field(m)%name) // trim(suffix)
1625  else
1626  var%bc(n)%field(m)%name = var_in%bc(n)%field(m)%name
1627  endif
1628  var%bc(n)%field(m)%long_name = var_in%bc(n)%field(m)%long_name
1629  var%bc(n)%field(m)%units = var_in%bc(n)%field(m)%units
1630  var%bc(n)%field(m)%may_init = var_in%bc(n)%field(m)%may_init
1631  var%bc(n)%field(m)%mean = var_in%bc(n)%field(m)%mean
1632  if (associated(var%bc(n)%field(m)%values)) then
1633  write (error_msg, *) trim(error_header), ' var%bc(', n, ')%field(', m, ')%values already associated'
1634  call mpp_error(fatal, trim(error_msg))
1635  endif
1636 
1637  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1638  allocate ( var%bc(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed,var%ks:var%ke) )
1639  var%bc(n)%field(m)%values(:,:,:) = 0.0_r8_kind
1640  enddo
1641  enddo
1642  else if(associated(var_in%bc_r4)) then
1643  if (associated(var%bc_r4)) then
1644  call mpp_error(fatal, trim(error_header) // ' var%bc_r4 already associated')
1645  endif
1646  allocate ( var%bc_r4(var%num_bcs) )
1647  do n = 1, var%num_bcs
1648  var%bc_r4(n)%name = var_in%bc_r4(n)%name
1649  var%bc_r4(n)%atm_tr_index = var_in%bc_r4(n)%atm_tr_index
1650  var%bc_r4(n)%flux_type = var_in%bc_r4(n)%flux_type
1651  var%bc_r4(n)%implementation = var_in%bc_r4(n)%implementation
1652  var%bc_r4(n)%ice_restart_file = var_in%bc_r4(n)%ice_restart_file
1653  var%bc_r4(n)%ocean_restart_file = var_in%bc_r4(n)%ocean_restart_file
1654  var%bc_r4(n)%use_atm_pressure = var_in%bc_r4(n)%use_atm_pressure
1655  var%bc_r4(n)%use_10m_wind_speed = var_in%bc_r4(n)%use_10m_wind_speed
1656  var%bc_r4(n)%pass_through_ice = var_in%bc_r4(n)%pass_through_ice
1657  var%bc_r4(n)%mol_wt = var_in%bc_r4(n)%mol_wt
1658  var%bc_r4(n)%num_fields = var_in%bc_r4(n)%num_fields
1659  if (associated(var%bc_r4(n)%field)) then
1660  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field already associated'
1661  call mpp_error(fatal, trim(error_msg))
1662  endif
1663  allocate ( var%bc_r4(n)%field(var%bc_r4(n)%num_fields) )
1664  do m = 1, var%bc_r4(n)%num_fields
1665  if (present(suffix)) then
1666  var%bc_r4(n)%field(m)%name = trim(var_in%bc_r4(n)%field(m)%name) // trim(suffix)
1667  else
1668  var%bc_r4(n)%field(m)%name = var_in%bc_r4(n)%field(m)%name
1669  endif
1670  var%bc_r4(n)%field(m)%long_name = var_in%bc_r4(n)%field(m)%long_name
1671  var%bc_r4(n)%field(m)%units = var_in%bc_r4(n)%field(m)%units
1672  var%bc_r4(n)%field(m)%may_init = var_in%bc_r4(n)%field(m)%may_init
1673  var%bc_r4(n)%field(m)%mean = var_in%bc_r4(n)%field(m)%mean
1674  if (associated(var%bc_r4(n)%field(m)%values)) then
1675  write (error_msg, *) trim(error_header), ' var%bc_r4(', n, ')%field(', m, ')%values already associated'
1676  call mpp_error(fatal, trim(error_msg))
1677  endif
1678 
1679  ! Note that this may be allocating a zero-sized array, which is legal in Fortran.
1680  allocate ( var%bc_r4(n)%field(m)%values(var%isd:var%ied,var%jsd:var%jed,var%ks:var%ke) )
1681  var%bc_r4(n)%field(m)%values(:,:,:) = 0.0_r4_kind
1682  enddo
1683  enddo
1684  else
1685  call mpp_error(fatal, error_header//"passed in type has unassociated coupler_field_type"// &
1686  " pointers for both bc and bc_r4")
1687  endif
1688  endif
1689  end subroutine ct_spawn_3d_3d
1690 
1691 
1692  !> @brief Copy all elements of coupler_2d_bc_type.
1693  !! Do a direct copy of the data in all elements of one
1694  !! coupler_2d_bc_type into another. Both must have the same array sizes.
1695  !!
1696  !! @throw FATAL, "bc_index is present and exceeds var_in%num_bcs."
1697  !! @throw FATAL, "field_index is present and exceeds num_fields for var_in%bc(bc_incdx)%name"
1698  !! @throw FATAL, "bc_index must be present if field_index is present."
1699  !! @throw FATAL, "There is an i-direction computational domain size mismatch."
1700  !! @throw FATAL, "There is an j-direction computational domain size mismatch."
1701  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
1702  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
1703  subroutine ct_copy_data_2d(var_in, var, halo_size, bc_index, field_index,&
1704  & exclude_flux_type, only_flux_type, pass_through_ice)
1705  type(coupler_2d_bc_type), intent(in) :: var_in !< BC_type structure with the data to copy
1706  type(coupler_2d_bc_type), intent(inout) :: var !< The recipient BC_type structure
1707  integer, optional, intent(in) :: halo_size !< The extent of the halo to copy; 0 by default
1708  integer, optional, intent(in) :: bc_index !< The index of the boundary condition
1709  !! that is being copied
1710  integer, optional, intent(in) :: field_index !< The index of the field in the
1711  !! boundary condition that is being copied
1712  character(len=*), optional, intent(in) :: exclude_flux_type !< A string describing which types
1713  !! of fluxes to exclude from this copy.
1714  character(len=*), optional, intent(in) :: only_flux_type !< A string describing which types
1715  !! of fluxes to include from this copy.
1716  logical, optional, intent(in) :: pass_through_ice !< If true, only copy BCs whose
1717  !! value of pass_through ice matches this
1718  logical :: copy_bc
1719  integer :: i, j, m, n, n1, n2, halo, i_off, j_off
1720 
1721 
1722  if (present(bc_index)) then
1723  if (bc_index > var_in%num_bcs)&
1724  & call mpp_error(fatal, "CT_copy_data_2d: bc_index is present and exceeds var_in%num_bcs.")
1725  if (present(field_index)) then
1726  if( associated(var_in%bc)) then
1727  if (field_index > var_in%bc(bc_index)%num_fields)&
1728  & call mpp_error(fatal, "CT_copy_data_2d: field_index is present and exceeds num_fields for" //&
1729  & trim(var_in%bc(bc_index)%name) )
1730  else
1731  if (field_index > var_in%bc_r4(bc_index)%num_fields)&
1732  & call mpp_error(fatal, "CT_copy_data_2d: field_index is present and exceeds num_fields for" //&
1733  & trim(var_in%bc_r4(bc_index)%name) )
1734  endif
1735  endif
1736  elseif (present(field_index)) then
1737  call mpp_error(fatal, "CT_copy_data_2d: bc_index must be present if field_index is present.")
1738  endif
1739 
1740  halo = 0
1741  if (present(halo_size)) halo = halo_size
1742 
1743  n1 = 1
1744  n2 = var_in%num_bcs
1745  if (present(bc_index)) then
1746  n1 = bc_index
1747  n2 = bc_index
1748  endif
1749 
1750  if (n2 >= n1) then
1751  ! A more consciencious implementation would include a more descriptive error messages.
1752  if ((var_in%iec-var_in%isc) /= (var%iec-var%isc))&
1753  & call mpp_error(fatal, "CT_copy_data_2d: There is an i-direction computational domain size mismatch.")
1754  if ((var_in%jec-var_in%jsc) /= (var%jec-var%jsc))&
1755  & call mpp_error(fatal, "CT_copy_data_2d: There is a j-direction computational domain size mismatch.")
1756  if ((var_in%isc-var_in%isd < halo) .or. (var_in%ied-var_in%iec < halo))&
1757  & call mpp_error(fatal, "CT_copy_data_2d: Excessive i-direction halo size for the input structure.")
1758  if ((var_in%jsc-var_in%jsd < halo) .or. (var_in%jed-var_in%jec < halo))&
1759  & call mpp_error(fatal, "CT_copy_data_2d: Excessive j-direction halo size for the input structure.")
1760  if ((var%isc-var%isd < halo) .or. (var%ied-var%iec < halo))&
1761  & call mpp_error(fatal, "CT_copy_data_2d: Excessive i-direction halo size for the output structure.")
1762  if ((var%jsc-var%jsd < halo) .or. (var%jed-var%jec < halo))&
1763  & call mpp_error(fatal, "CT_copy_data_2d: Excessive j-direction halo size for the output structure.")
1764 
1765  i_off = var_in%isc - var%isc
1766  j_off = var_in%jsc - var%jsc
1767  endif
1768 
1769  if(var_in%set .and. var_in%num_bcs .gt. 0) then
1770  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
1771  if( associated(var_in%bc) ) then
1772  call mpp_error(fatal, "CT_copy_data_2d var_in%bc and var_in%bc_r4 are both intialized,"//&
1773  " only one should be associated per type")
1774  else
1775  call mpp_error(fatal, "CT_copy_data_2d var_in%bc and var_in%bc_r4 are both unintialized,"//&
1776  " one must be associated to copy field data.")
1777  endif
1778  endif
1779  endif
1780 
1781  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
1782  if (associated(var_in%bc) .or. var_in%num_bcs .lt. 1) then
1783  do n = n1, n2
1784  copy_bc = .true.
1785  if (copy_bc .and. present(exclude_flux_type))&
1786  & copy_bc = .not.(trim(var%bc(n)%flux_type) == trim(exclude_flux_type))
1787  if (copy_bc .and. present(only_flux_type))&
1788  & copy_bc = (trim(var%bc(n)%flux_type) == trim(only_flux_type))
1789  if (copy_bc .and. present(pass_through_ice))&
1790  & copy_bc = (pass_through_ice .eqv. var%bc(n)%pass_through_ice)
1791  if (.not.copy_bc) cycle
1792 
1793  do m = 1, var%bc(n)%num_fields
1794  if (present(field_index)) then
1795  if (m /= field_index) cycle
1796  endif
1797  if ( associated(var%bc(n)%field(m)%values) ) then
1798  do j=var%jsc-halo,var%jec+halo
1799  do i=var%isc-halo,var%iec+halo
1800  var%bc(n)%field(m)%values(i,j) = var_in%bc(n)%field(m)%values(i+i_off,j+j_off)
1801  enddo
1802  enddo
1803  endif
1804  enddo
1805  enddo
1806  else if (associated(var_in%bc_r4)) then
1807  do n = n1, n2
1808  copy_bc = .true.
1809  if (copy_bc .and. present(exclude_flux_type))&
1810  & copy_bc = .not.(trim(var%bc_r4(n)%flux_type) == trim(exclude_flux_type))
1811  if (copy_bc .and. present(only_flux_type))&
1812  & copy_bc = (trim(var%bc_r4(n)%flux_type) == trim(only_flux_type))
1813  if (copy_bc .and. present(pass_through_ice))&
1814  & copy_bc = (pass_through_ice .eqv. var%bc_r4(n)%pass_through_ice)
1815  if (.not.copy_bc) cycle
1816 
1817  do m = 1, var%bc_r4(n)%num_fields
1818  if (present(field_index)) then
1819  if (m /= field_index) cycle
1820  endif
1821  if ( associated(var%bc_r4(n)%field(m)%values) ) then
1822  do j=var%jsc-halo,var%jec+halo
1823  do i=var%isc-halo,var%iec+halo
1824  var%bc_r4(n)%field(m)%values(i,j) = var_in%bc_r4(n)%field(m)%values(i+i_off,j+j_off)
1825  enddo
1826  enddo
1827  endif
1828  enddo
1829  enddo
1830  else
1831  call mpp_error(fatal, "CT_copy_data_2d: passed in type has unassociated coupler_field_type"// &
1832  " pointers for both bc and bc_r4")
1833  endif
1834  end subroutine ct_copy_data_2d
1835 
1836  !> @brief Copy all elements of coupler_3d_bc_type
1837  !!
1838  !! Do a direct copy of the data in all elements of one
1839  !! coupler_3d_bc_type into another. Both must have the same array sizes.
1840  !!
1841  !! @throw FATAL, "bc_index is present and exceeds var_in%num_bcs."
1842  !! @throw FATAL, "field_index is present and exceeds num_fields for var_in%bc(bc_incdx)%name"
1843  !! @throw FATAL, "bc_index must be present if field_index is present."
1844  !! @throw FATAL, "There is an i-direction computational domain size mismatch."
1845  !! @throw FATAL, "There is an j-direction computational domain size mismatch."
1846  !! @throw FATAL, "There is an k-direction computational domain size mismatch."
1847  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
1848  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
1849  !! @throw FATAL, "Excessive k-direction halo size for the input structure."
1850  subroutine ct_copy_data_3d(var_in, var, halo_size, bc_index, field_index,&
1851  & exclude_flux_type, only_flux_type, pass_through_ice)
1852  type(coupler_3d_bc_type), intent(in) :: var_in !< BC_type structure with the data to copy
1853  type(coupler_3d_bc_type), intent(inout) :: var !< The recipient BC_type structure
1854  integer, optional, intent(in) :: halo_size !< The extent of the halo to copy; 0 by default
1855  integer, optional, intent(in) :: bc_index !< The index of the boundary condition
1856  !! that is being copied
1857  integer, optional, intent(in) :: field_index !< The index of the field in the
1858  !! boundary condition that is being copied
1859  character(len=*), optional, intent(in) :: exclude_flux_type !< A string describing which types
1860  !! of fluxes to exclude from this copy.
1861  character(len=*), optional, intent(in) :: only_flux_type !< A string describing which types
1862  !! of fluxes to include from this copy.
1863  logical, optional, intent(in) :: pass_through_ice !< If true, only copy BCs whose
1864  !! value of pass_through ice matches this
1865  logical :: copy_bc
1866  integer :: i, j, k, m, n, n1, n2, halo, i_off, j_off, k_off
1867 
1868  if (present(bc_index)) then
1869  if (bc_index > var_in%num_bcs) &
1870  call mpp_error(fatal, "CT_copy_data_3d: bc_index is present and exceeds var_in%num_bcs.")
1871  if (present(field_index)) then
1872  if( associated(var_in%bc)) then
1873  if (field_index > var_in%bc(bc_index)%num_fields)&
1874  & call mpp_error(fatal, "CT_copy_data_3d: field_index is present and exceeds num_fields for" //&
1875  & trim(var_in%bc(bc_index)%name) )
1876  else
1877  if (field_index > var_in%bc_r4(bc_index)%num_fields)&
1878  & call mpp_error(fatal, "CT_copy_data_3d: field_index is present and exceeds num_fields for" //&
1879  & trim(var_in%bc_r4(bc_index)%name) )
1880  endif
1881  endif
1882  elseif (present(field_index)) then
1883  call mpp_error(fatal, "CT_copy_data_3d: bc_index must be present if field_index is present.")
1884  endif
1885 
1886  halo = 0
1887  if (present(halo_size)) halo = halo_size
1888 
1889  n1 = 1
1890  n2 = var_in%num_bcs
1891  if (present(bc_index)) then
1892  n1 = bc_index
1893  n2 = bc_index
1894  endif
1895 
1896  if (n2 >= n1) then
1897  ! A more consciencious implementation would include a more descriptive error messages.
1898  if ((var_in%iec-var_in%isc) /= (var%iec-var%isc))&
1899  & call mpp_error(fatal, "CT_copy_data_3d: There is an i-direction computational domain size mismatch.")
1900  if ((var_in%jec-var_in%jsc) /= (var%jec-var%jsc))&
1901  & call mpp_error(fatal, "CT_copy_data_3d: There is a j-direction computational domain size mismatch.")
1902  if ((var_in%ke-var_in%ks) /= (var%ke-var%ks))&
1903  & call mpp_error(fatal, "CT_copy_data_3d: There is a k-direction computational domain size mismatch.")
1904  if ((var_in%isc-var_in%isd < halo) .or. (var_in%ied-var_in%iec < halo))&
1905  & call mpp_error(fatal, "CT_copy_data_3d: Excessive i-direction halo size for the input structure.")
1906  if ((var_in%jsc-var_in%jsd < halo) .or. (var_in%jed-var_in%jec < halo))&
1907  & call mpp_error(fatal, "CT_copy_data_3d: Excessive j-direction halo size for the input structure.")
1908  if ((var%isc-var%isd < halo) .or. (var%ied-var%iec < halo))&
1909  & call mpp_error(fatal, "CT_copy_data_3d: Excessive i-direction halo size for the output structure.")
1910  if ((var%jsc-var%jsd < halo) .or. (var%jed-var%jec < halo))&
1911  & call mpp_error(fatal, "CT_copy_data_3d: Excessive j-direction halo size for the output structure.")
1912 
1913  i_off = var_in%isc - var%isc
1914  j_off = var_in%jsc - var%jsc
1915  k_off = var_in%ks - var%ks
1916  endif
1917 
1918  if(var_in%set .and. var_in%num_bcs .gt. 0) then
1919  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
1920  if( associated(var_in%bc) ) then
1921  call mpp_error(fatal, "CT_copy_data_3d: var_in%bc and var_in%bc_r4 are both intialized,"//&
1922  " only one should be associated per type")
1923  else
1924  call mpp_error(fatal, "CT_copy_data_3d: var_in%bc and var_in%bc_r4 are both unintialized,"//&
1925  " one must be associated to copy field data.")
1926  endif
1927  endif
1928  endif
1929 
1930  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
1931  if (associated(var_in%bc) .or. var_in%num_bcs .lt. 1) then
1932  do n = n1, n2
1933  copy_bc = .true.
1934  if (copy_bc .and. present(exclude_flux_type))&
1935  & copy_bc = .not.(trim(var%bc(n)%flux_type) == trim(exclude_flux_type))
1936  if (copy_bc .and. present(only_flux_type))&
1937  & copy_bc = (trim(var%bc(n)%flux_type) == trim(only_flux_type))
1938  if (copy_bc .and. present(pass_through_ice))&
1939  & copy_bc = (pass_through_ice .eqv. var%bc(n)%pass_through_ice)
1940  if (.not.copy_bc) cycle
1941 
1942  do m = 1, var_in%bc(n)%num_fields
1943  if (present(field_index)) then
1944  if (m /= field_index) cycle
1945  endif
1946  if ( associated(var%bc(n)%field(m)%values) ) then
1947  do k=var%ks,var%ke
1948  do j=var%jsc-halo,var%jec+halo
1949  do i=var%isc-halo,var%iec+halo
1950  var%bc(n)%field(m)%values(i,j,k) = var_in%bc(n)%field(m)%values(i+i_off,j+j_off,k+k_off)
1951  enddo
1952  enddo
1953  enddo
1954  endif
1955  enddo
1956  enddo
1957  else if (associated(var_in%bc_r4)) then
1958  do n = n1, n2
1959  copy_bc = .true.
1960  if (copy_bc .and. present(exclude_flux_type))&
1961  & copy_bc = .not.(trim(var%bc_r4(n)%flux_type) == trim(exclude_flux_type))
1962  if (copy_bc .and. present(only_flux_type))&
1963  & copy_bc = (trim(var%bc_r4(n)%flux_type) == trim(only_flux_type))
1964  if (copy_bc .and. present(pass_through_ice))&
1965  & copy_bc = (pass_through_ice .eqv. var%bc_r4(n)%pass_through_ice)
1966  if (.not.copy_bc) cycle
1967 
1968  do m = 1, var_in%bc_r4(n)%num_fields
1969  if (present(field_index)) then
1970  if (m /= field_index) cycle
1971  endif
1972  if ( associated(var%bc_r4(n)%field(m)%values) ) then
1973  do k=var%ks,var%ke
1974  do j=var%jsc-halo,var%jec+halo
1975  do i=var%isc-halo,var%iec+halo
1976  var%bc_r4(n)%field(m)%values(i,j,k) = var_in%bc_r4(n)%field(m)%values(i+i_off,j+j_off,k+k_off)
1977  enddo
1978  enddo
1979  enddo
1980  endif
1981  enddo
1982  enddo
1983  else
1984  call mpp_error(fatal, "CT_copy_data_3d: passed in type has unassociated coupler_field_type"// &
1985  " pointers for both bc and bc_r4")
1986  endif
1987  end subroutine ct_copy_data_3d
1988 
1989  !> @brief Copy all elements of coupler_2d_bc_type to coupler_3d_bc_type
1990  !!
1991  !! Do a direct copy of the data in all elements of one coupler_2d_bc_type into a
1992  !! coupler_3d_bc_type. Both must have the same array sizes for the first two dimensions, while
1993  !! the extend of the 3rd dimension that is being filled may be specified via optional arguments..
1994  !!
1995  !! @throw FATAL, "bc_index is present and exceeds var_in%num_bcs."
1996  !! @throw FATAL, "field_index is present and exceeds num_fields for var_in%bc(bc_incdx)%name"
1997  !! @throw FATAL, "bc_index must be present if field_index is present."
1998  !! @throw FATAL, "There is an i-direction computational domain size mismatch."
1999  !! @throw FATAL, "There is an j-direction computational domain size mismatch."
2000  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
2001  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
2002  subroutine ct_copy_data_2d_3d(var_in, var, halo_size, bc_index, field_index,&
2003  & exclude_flux_type, only_flux_type, pass_through_ice,&
2004  & ind3_start, ind3_end)
2005  type(coupler_2d_bc_type), intent(in) :: var_in !< BC_type structure with the data to copy
2006  type(coupler_3d_bc_type), intent(inout) :: var !< The recipient BC_type structure
2007  integer, optional, intent(in) :: halo_size !< The extent of the halo to copy; 0 by default
2008  integer, optional, intent(in) :: bc_index !< The index of the boundary condition
2009  !! that is being copied
2010  integer, optional, intent(in) :: field_index !< The index of the field in the
2011  !! boundary condition that is being copied
2012  character(len=*), optional, intent(in) :: exclude_flux_type !< A string describing which types
2013  !! of fluxes to exclude from this copy.
2014  character(len=*), optional, intent(in) :: only_flux_type !< A string describing which types
2015  !! of fluxes to include from this copy.
2016  logical, optional, intent(in) :: pass_through_ice !< If true, only copy BCs whose
2017  !! value of pass_through ice matches this
2018  integer, optional, intent(in) :: ind3_start !< The starting value of the 3rd
2019  !! index of the 3d type to fill in.
2020  integer, optional, intent(in) :: ind3_end !< The ending value of the 3rd
2021  !! index of the 3d type to fill in.
2022 
2023  logical :: copy_bc
2024  integer :: i, j, k, m, n, n1, n2, halo, i_off, j_off, ks, ke
2025 
2026  if (present(bc_index)) then
2027  if (bc_index > var_in%num_bcs)&
2028  & call mpp_error(fatal, "CT_copy_data_2d_3d: bc_index is present and exceeds var_in%num_bcs.")
2029  if (present(field_index)) then ; if (field_index > var_in%bc(bc_index)%num_fields)&
2030  & call mpp_error(fatal, "CT_copy_data_2d_3d: field_index is present and exceeds num_fields for" //&
2031  & trim(var_in%bc(bc_index)%name) )
2032  endif
2033  elseif (present(field_index)) then
2034  call mpp_error(fatal, "CT_copy_data_2d_3d: bc_index must be present if field_index is present.")
2035  endif
2036 
2037  halo = 0
2038  if (present(halo_size)) halo = halo_size
2039 
2040  n1 = 1
2041  n2 = var_in%num_bcs
2042  if (present(bc_index)) then
2043  n1 = bc_index
2044  n2 = bc_index
2045  endif
2046 
2047  if (n2 >= n1) then
2048  ! A more consciencious implementation would include a more descriptive error messages.
2049  if ((var_in%iec-var_in%isc) /= (var%iec-var%isc))&
2050  & call mpp_error(fatal, "CT_copy_data_2d_3d: There is an i-direction computational domain size mismatch.")
2051  if ((var_in%jec-var_in%jsc) /= (var%jec-var%jsc))&
2052  & call mpp_error(fatal, "CT_copy_data_2d_3d: There is a j-direction computational domain size mismatch.")
2053  if ((var_in%isc-var_in%isd < halo) .or. (var_in%ied-var_in%iec < halo))&
2054  & call mpp_error(fatal, "CT_copy_data_2d_3d: Excessive i-direction halo size for the input structure.")
2055  if ((var_in%jsc-var_in%jsd < halo) .or. (var_in%jed-var_in%jec < halo))&
2056  & call mpp_error(fatal, "CT_copy_data_2d_3d: Excessive j-direction halo size for the input structure.")
2057  if ((var%isc-var%isd < halo) .or. (var%ied-var%iec < halo))&
2058  & call mpp_error(fatal, "CT_copy_data_2d_3d: Excessive i-direction halo size for the output structure.")
2059  if ((var%jsc-var%jsd < halo) .or. (var%jed-var%jec < halo))&
2060  & call mpp_error(fatal, "CT_copy_data_2d_3d: Excessive j-direction halo size for the output structure.")
2061  endif
2062 
2063  i_off = var_in%isc - var%isc
2064  j_off = var_in%jsc - var%jsc
2065 
2066  if(var_in%set .and. var_in%num_bcs .gt. 0) then
2067  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
2068  if( associated(var_in%bc) ) then
2069  call mpp_error(fatal, "CT_copy_data_2d_3d: var_in%bc and var_in%bc_r4 are both intialized,"//&
2070  " only one should be associated per type")
2071  else
2072  call mpp_error(fatal, "CT_copy_data_2d_3d: var_in%bc and var_in%bc_r4 are both unintialized,"//&
2073  " one must be associated to copy field data.")
2074  endif
2075  endif
2076  endif
2077 
2078  ! if using r8_kind
2079  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2080  if (associated(var_in%bc) .or. var_in%num_bcs .lt. 1) then
2081  do n = n1, n2
2082  copy_bc = .true.
2083  if (copy_bc .and. present(exclude_flux_type))&
2084  & copy_bc = .not.(trim(var_in%bc(n)%flux_type) == trim(exclude_flux_type))
2085  if (copy_bc .and. present(only_flux_type))&
2086  & copy_bc = (trim(var_in%bc(n)%flux_type) == trim(only_flux_type))
2087  if (copy_bc .and. present(pass_through_ice))&
2088  & copy_bc = (pass_through_ice .eqv. var_in%bc(n)%pass_through_ice)
2089  if (.not.copy_bc) cycle
2090 
2091  do m = 1, var_in%bc(n)%num_fields
2092  if (present(field_index)) then
2093  if (m /= field_index) cycle
2094  endif
2095  if ( associated(var%bc(n)%field(m)%values) ) then
2096  ks = var%ks
2097  if (present(ind3_start)) ks = max(ks, ind3_start)
2098  ke = var%ke
2099  if (present(ind3_end)) ke = max(ke, ind3_end)
2100  do k=ks,ke
2101  do j=var%jsc-halo,var%jec+halo
2102  do i=var%isc-halo,var%iec+halo
2103  var%bc(n)%field(m)%values(i,j,k) = var_in%bc(n)%field(m)%values(i+i_off,j+j_off)
2104  enddo
2105  enddo
2106  enddo
2107  endif
2108  enddo
2109  enddo
2110  ! if using r4_kind (same logic)
2111  else if (associated(var_in%bc_r4)) then
2112  do n = n1, n2
2113  copy_bc = .true.
2114  if (copy_bc .and. present(exclude_flux_type))&
2115  & copy_bc = .not.(trim(var_in%bc_r4(n)%flux_type) == trim(exclude_flux_type))
2116  if (copy_bc .and. present(only_flux_type))&
2117  & copy_bc = (trim(var_in%bc_r4(n)%flux_type) == trim(only_flux_type))
2118  if (copy_bc .and. present(pass_through_ice))&
2119  & copy_bc = (pass_through_ice .eqv. var_in%bc_r4(n)%pass_through_ice)
2120  if (.not.copy_bc) cycle
2121 
2122  do m = 1, var_in%bc_r4(n)%num_fields
2123  if (present(field_index)) then
2124  if (m /= field_index) cycle
2125  endif
2126  if ( associated(var%bc_r4(n)%field(m)%values) ) then
2127  ks = var%ks
2128  if (present(ind3_start)) ks = max(ks, ind3_start)
2129  ke = var%ke
2130  if (present(ind3_end)) ke = max(ke, ind3_end)
2131  do k=ks,ke
2132  do j=var%jsc-halo,var%jec+halo
2133  do i=var%isc-halo,var%iec+halo
2134  var%bc_r4(n)%field(m)%values(i,j,k) = var_in%bc_r4(n)%field(m)%values(i+i_off,j+j_off)
2135  enddo
2136  enddo
2137  enddo
2138  endif
2139  enddo
2140  enddo
2141  else
2142  call mpp_error(fatal, "CT_copy_data_2d_3d: passed in type has unassociated coupler_field_type"// &
2143  " pointers for both bc and bc_r4")
2144  endif
2145  end subroutine ct_copy_data_2d_3d
2146 
2147 
2148  !> @brief Redistribute the data in all elements of a coupler_2d_bc_type
2149  !!
2150  !! Redistributes the data in all elements of one coupler_2d_bc_type
2151  !! into another, which may be on different processors with a different decomposition.
2152  !!
2153  !! @throw FATAL, "Mismatch in num_bcs in CT_copy_data_2d."
2154  !! @throw FATAL, "Mismatch in the total number of fields in CT_redistribute_data_2d."
2155  subroutine ct_redistribute_data_2d(var_in, domain_in, var_out, domain_out, complete)
2156  type(coupler_2d_bc_type), intent(in) :: var_in !< BC_type structure with the data to copy (intent in)
2157  type(domain2d), intent(in) :: domain_in !< The FMS domain for the input structure
2158  type(coupler_2d_bc_type), intent(inout) :: var_out !< The recipient BC_type structure (data intent out)
2159  type(domain2d), intent(in) :: domain_out !< The FMS domain for the output structure
2160  logical, optional, intent(in) :: complete !< If true, complete the updates
2161 
2162  real(r4_kind), pointer, dimension(:,:) :: null_ptr2D_r4 => null()
2163  real(r8_kind), pointer, dimension(:,:) :: null_ptr2D_r8 => null()
2164  logical :: do_in, do_out, do_complete
2165  integer :: m, n, fc, fc_in, fc_out
2166 
2167  do_complete = .true.
2168  if (present(complete)) do_complete = complete
2169 
2170  ! Figure out whether this PE has valid input or output fields or both.
2171  do_in = var_in%set
2172  do_out = var_out%set
2173 
2174  if(var_in%set .and. var_in%num_bcs .gt. 0) then
2175  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
2176  if( associated(var_in%bc) ) then
2177  call mpp_error(fatal, "CT_redistribute_data_2d: var_in%bc and var_in%bc_r4 are both intialized,"//&
2178  " only one should be associated per type")
2179  else
2180  call mpp_error(fatal, "CT_redistribute_data_2d: var_in%bc and var_in%bc_r4 are both intialized,"//&
2181  " only one must be associated per type to redistribute field data.")
2182  endif
2183  endif
2184  endif
2185 
2186  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2187  if(associated(var_in%bc) .or. associated(var_out%bc) .or. var_in%num_bcs .lt. 1) then
2188  fc_in = 0 ; fc_out = 0
2189  if (do_in) then
2190  do n = 1, var_in%num_bcs
2191  do m = 1, var_in%bc(n)%num_fields
2192  if (associated(var_in%bc(n)%field(m)%values)) fc_in = fc_in + 1
2193  enddo
2194  enddo
2195  endif
2196  if (fc_in == 0) do_in = .false.
2197  if (do_out) then
2198  do n = 1, var_out%num_bcs
2199  do m = 1, var_out%bc(n)%num_fields
2200  if (associated(var_out%bc(n)%field(m)%values)) fc_out = fc_out + 1
2201  enddo
2202  enddo
2203  endif
2204  if (fc_out == 0) do_out = .false.
2205 
2206  if (do_in .and. do_out) then
2207  if (var_in%num_bcs /= var_out%num_bcs) call mpp_error(fatal,&
2208  & "Mismatch in num_bcs in CT_copy_data_2d.")
2209  if (fc_in /= fc_out) call mpp_error(fatal,&
2210  & "Mismatch in the total number of fields in CT_redistribute_data_2d.")
2211  endif
2212 
2213  if (.not.(do_in .or. do_out)) return
2214 
2215  fc = 0
2216  if (do_in .and. do_out) then
2217  do n = 1, var_in%num_bcs
2218  do m = 1, var_in%bc(n)%num_fields
2219  if ( associated(var_in%bc(n)%field(m)%values) .neqv.&
2220  & associated(var_out%bc(n)%field(m)%values) ) &
2221  call mpp_error(fatal,&
2222  & "CT_redistribute_data_2d: Mismatch in which var_in and var_out fields are associated"// &
2223  & "Boundary condition:"//string(n)//" Field:"//string(m))
2224  if ( associated(var_in%bc(n)%field(m)%values) ) then
2225  fc = fc + 1
2226  call mpp_redistribute(domain_in, var_in%bc(n)%field(m)%values,&
2227  & domain_out, var_out%bc(n)%field(m)%values,&
2228  & complete=(do_complete.and.(fc==fc_in)) )
2229  endif
2230  enddo
2231  enddo
2232  elseif (do_in) then
2233  do n = 1, var_in%num_bcs
2234  do m = 1, var_in%bc(n)%num_fields
2235  if ( associated(var_in%bc(n)%field(m)%values) ) then
2236  fc = fc + 1
2237  call mpp_redistribute(domain_in, var_in%bc(n)%field(m)%values,&
2238  & domain_out, null_ptr2d_r8,&
2239  & complete=(do_complete.and.(fc==fc_in)) )
2240  endif
2241  enddo
2242  enddo
2243  elseif (do_out) then
2244  do n = 1, var_out%num_bcs
2245  do m = 1, var_out%bc(n)%num_fields
2246  if ( associated(var_out%bc(n)%field(m)%values) ) then
2247  fc = fc + 1
2248  call mpp_redistribute(domain_in, null_ptr2d_r8,&
2249  & domain_out, var_out%bc(n)%field(m)%values,&
2250  & complete=(do_complete.and.(fc==fc_out)) )
2251  endif
2252  enddo
2253  enddo
2254  endif
2255  ! same logic just uses r4_kind
2256  else if( associated(var_in%bc_r4) .or. associated(var_out%bc_r4)) then
2257  fc_in = 0 ; fc_out = 0
2258  if (do_in) then
2259  do n = 1, var_in%num_bcs
2260  do m = 1, var_in%bc_r4(n)%num_fields
2261  if (associated(var_in%bc_r4(n)%field(m)%values)) fc_in = fc_in + 1
2262  enddo
2263  enddo
2264  endif
2265  if (fc_in == 0) do_in = .false.
2266  if (do_out) then
2267  do n = 1, var_out%num_bcs
2268  do m = 1, var_out%bc_r4(n)%num_fields
2269  if (associated(var_out%bc_r4(n)%field(m)%values)) fc_out = fc_out + 1
2270  enddo
2271  enddo
2272  endif
2273  if (fc_out == 0) do_out = .false.
2274 
2275  if (do_in .and. do_out) then
2276  if (var_in%num_bcs /= var_out%num_bcs) call mpp_error(fatal,&
2277  & "Mismatch in num_bcs in CT_copy_data_2d.")
2278  if (fc_in /= fc_out) call mpp_error(fatal,&
2279  & "Mismatch in the total number of fields in CT_redistribute_data_2d.")
2280  endif
2281 
2282  if (.not.(do_in .or. do_out)) return
2283 
2284  fc = 0
2285  if (do_in .and. do_out) then
2286  do n = 1, var_in%num_bcs
2287  do m = 1, var_in%bc_r4(n)%num_fields
2288  if ( associated(var_in%bc_r4(n)%field(m)%values) .neqv.&
2289  & associated(var_out%bc_r4(n)%field(m)%values) ) &
2290  call mpp_error(fatal,&
2291  & "CT_redistribute_data_2d: Mismatch in which var_in and var_out fields are associated"// &
2292  & "Boundary condition:"//string(n)//" Field:"//string(m))
2293  if ( associated(var_in%bc_r4(n)%field(m)%values) ) then
2294  fc = fc + 1
2295  call mpp_redistribute(domain_in, var_in%bc_r4(n)%field(m)%values,&
2296  & domain_out, var_out%bc_r4(n)%field(m)%values,&
2297  & complete=(do_complete.and.(fc==fc_in)) )
2298  endif
2299  enddo
2300  enddo
2301  elseif (do_in) then
2302  do n = 1, var_in%num_bcs
2303  do m = 1, var_in%bc_r4(n)%num_fields
2304  if ( associated(var_in%bc_r4(n)%field(m)%values) ) then
2305  fc = fc + 1
2306  call mpp_redistribute(domain_in, var_in%bc_r4(n)%field(m)%values,&
2307  & domain_out, null_ptr2d_r4,&
2308  & complete=(do_complete.and.(fc==fc_in)) )
2309  endif
2310  enddo
2311  enddo
2312  elseif (do_out) then
2313  do n = 1, var_out%num_bcs
2314  do m = 1, var_out%bc_r4(n)%num_fields
2315  if ( associated(var_out%bc_r4(n)%field(m)%values) ) then
2316  fc = fc + 1
2317  call mpp_redistribute(domain_in, null_ptr2d_r4,&
2318  & domain_out, var_out%bc_r4(n)%field(m)%values,&
2319  & complete=(do_complete.and.(fc==fc_out)) )
2320  endif
2321  enddo
2322  enddo
2323  endif
2324  else
2325  call mpp_error(fatal, "CT_redistribute_data_2d: passed in type has unassociated coupler_field_type"// &
2326  " pointers for both bc and bc_r4")
2327  endif
2328  end subroutine ct_redistribute_data_2d
2329 
2330  !> @brief Redistributes the data in all elements of one coupler_2d_bc_type
2331  !!
2332  !! Redistributes the data in all elements of one coupler_2d_bc_type into another, which may be on
2333  !! different processors with a different decomposition.
2334  subroutine ct_redistribute_data_3d(var_in, domain_in, var_out, domain_out, complete)
2335  type(coupler_3d_bc_type), intent(in) :: var_in !< BC_type structure with the data to copy (intent in)
2336  type(domain2d), intent(in) :: domain_in !< The FMS domain for the input structure
2337  type(coupler_3d_bc_type), intent(inout) :: var_out !< The recipient BC_type structure (data intent out)
2338  type(domain2d), intent(in) :: domain_out !< The FMS domain for the output structure
2339  logical, optional, intent(in) :: complete !< If true, complete the updates
2340 
2341  real(r4_kind), pointer, dimension(:,:,:) :: null_ptr3D_r4 => null()
2342  real(r8_kind), pointer, dimension(:,:,:) :: null_ptr3D_r8 => null()
2343  logical :: do_in, do_out, do_complete
2344  integer :: m, n, fc, fc_in, fc_out
2345 
2346  do_complete = .true.
2347  if (present(complete)) do_complete = complete
2348 
2349  ! Figure out whether this PE has valid input or output fields or both.
2350  do_in = var_in%set
2351  do_out = var_out%set
2352 
2353  fc_in = 0
2354  fc_out = 0
2355 
2356  if(var_in%set .and. var_in%num_bcs .gt. 0) then
2357  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
2358  if( associated(var_in%bc) ) then
2359  call mpp_error(fatal, "CT_redistribute_data_3d: var_in%bc and var_in%bc_r4 are both intialized,"//&
2360  " only one should be associated per type")
2361  else
2362  call mpp_error(fatal, "CT_redistribute_data_3d: var_in%bc and var_in%bc_r4 are both intialized,"//&
2363  " only one must be associated per type to redistribute field data.")
2364  endif
2365  endif
2366  endif
2367 
2368  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2369  if( associated(var_in%bc) .or. associated(var_out%bc) .or. var_in%num_bcs .lt. 1) then
2370  if (do_in) then
2371  do n = 1, var_in%num_bcs
2372  do m = 1, var_in%bc(n)%num_fields
2373  if (associated(var_in%bc(n)%field(m)%values)) fc_in = fc_in + 1
2374  enddo
2375  enddo
2376  endif
2377  if (fc_in == 0) do_in = .false.
2378  if (do_out) then
2379  do n = 1, var_out%num_bcs
2380  do m = 1, var_out%bc(n)%num_fields
2381  if (associated(var_out%bc(n)%field(m)%values)) fc_out = fc_out + 1
2382  enddo
2383  enddo
2384  endif
2385  if (fc_out == 0) do_out = .false.
2386 
2387  if (do_in .and. do_out) then
2388  if (var_in%num_bcs /= var_out%num_bcs) call mpp_error(fatal,&
2389  & "Mismatch in num_bcs in CT_copy_data_3d.")
2390  if (fc_in /= fc_out) call mpp_error(fatal,&
2391  & "Mismatch in the total number of fields in CT_redistribute_data_3d.")
2392  endif
2393 
2394  if (.not.(do_in .or. do_out)) return
2395 
2396  fc = 0
2397  if (do_in .and. do_out) then
2398  do n = 1, var_in%num_bcs
2399  do m = 1, var_in%bc(n)%num_fields
2400  if ( associated(var_in%bc(n)%field(m)%values) .neqv.&
2401  & associated(var_out%bc(n)%field(m)%values) )&
2402  & call mpp_error(fatal,&
2403  & "CT_redistribute_data_3d: Mismatch in which var_in and var_out fields are associated"// &
2404  & "Boundary condition:"//string(n)//" Field:"//string(m))
2405  if ( associated(var_in%bc(n)%field(m)%values) ) then
2406  fc = fc + 1
2407  call mpp_redistribute(domain_in, var_in%bc(n)%field(m)%values,&
2408  & domain_out, var_out%bc(n)%field(m)%values,&
2409  & complete=(do_complete.and.(fc==fc_in)) )
2410  endif
2411  enddo
2412  enddo
2413  elseif (do_in) then
2414  do n = 1, var_in%num_bcs
2415  do m = 1, var_in%bc(n)%num_fields
2416  if ( associated(var_in%bc(n)%field(m)%values) ) then
2417  fc = fc + 1
2418  call mpp_redistribute(domain_in, var_in%bc(n)%field(m)%values,&
2419  & domain_out, null_ptr3d_r8,&
2420  & complete=(do_complete.and.(fc==fc_in)) )
2421  endif
2422  enddo
2423  enddo
2424  elseif (do_out) then
2425  do n = 1, var_out%num_bcs
2426  do m = 1, var_out%bc(n)%num_fields
2427  if ( associated(var_out%bc(n)%field(m)%values) ) then
2428  fc = fc + 1
2429  call mpp_redistribute(domain_in, null_ptr3d_r8,&
2430  & domain_out, var_out%bc(n)%field(m)%values,&
2431  & complete=(do_complete.and.(fc==fc_out)) )
2432  endif
2433  enddo
2434  enddo
2435  endif
2436  ! if using r4_kind, bc_r4 will be associated
2437  else if(associated(var_in%bc_r4) .or. associated(var_out%bc_r4)) then
2438  if (do_in) then
2439  do n = 1, var_in%num_bcs
2440  do m = 1, var_in%bc_r4(n)%num_fields
2441  if (associated(var_in%bc_r4(n)%field(m)%values)) fc_in = fc_in + 1
2442  enddo
2443  enddo
2444  endif
2445  if (fc_in == 0) do_in = .false.
2446  if (do_out) then
2447  do n = 1, var_out%num_bcs
2448  do m = 1, var_out%bc_r4(n)%num_fields
2449  if (associated(var_out%bc_r4(n)%field(m)%values)) fc_out = fc_out + 1
2450  enddo
2451  enddo
2452  endif
2453  if (fc_out == 0) do_out = .false.
2454 
2455  if (do_in .and. do_out) then
2456  if (var_in%num_bcs /= var_out%num_bcs) call mpp_error(fatal,&
2457  & "Mismatch in num_bcs in CT_copy_data_3d.")
2458  if (fc_in /= fc_out) call mpp_error(fatal,&
2459  & "Mismatch in the total number of fields in CT_redistribute_data_3d.")
2460  endif
2461 
2462  if (.not.(do_in .or. do_out)) return
2463 
2464  fc = 0
2465  if (do_in .and. do_out) then
2466  do n = 1, var_in%num_bcs
2467  do m = 1, var_in%bc_r4(n)%num_fields
2468  if ( associated(var_in%bc_r4(n)%field(m)%values) .neqv.&
2469  & associated(var_out%bc_r4(n)%field(m)%values) )&
2470  & call mpp_error(fatal,&
2471  & "CT_redistribute_data_3d: Mismatch in which var_in and var_out fields are associated"// &
2472  & "Boundary condition:"//string(n)//" Field:"//string(m))
2473  if ( associated(var_in%bc_r4(n)%field(m)%values) ) then
2474  fc = fc + 1
2475  call mpp_redistribute(domain_in, var_in%bc_r4(n)%field(m)%values,&
2476  & domain_out, var_out%bc_r4(n)%field(m)%values,&
2477  & complete=(do_complete.and.(fc==fc_in)) )
2478  endif
2479  enddo
2480  enddo
2481  elseif (do_in) then
2482  do n = 1, var_in%num_bcs
2483  do m = 1, var_in%bc_r4(n)%num_fields
2484  if ( associated(var_in%bc_r4(n)%field(m)%values) ) then
2485  fc = fc + 1
2486  call mpp_redistribute(domain_in, var_in%bc_r4(n)%field(m)%values,&
2487  & domain_out, null_ptr3d_r4,&
2488  & complete=(do_complete.and.(fc==fc_in)) )
2489  endif
2490  enddo
2491  enddo
2492  elseif (do_out) then
2493  do n = 1, var_out%num_bcs
2494  do m = 1, var_out%bc_r4(n)%num_fields
2495  if ( associated(var_out%bc_r4(n)%field(m)%values) ) then
2496  fc = fc + 1
2497  call mpp_redistribute(domain_in, null_ptr3d_r4,&
2498  & domain_out, var_out%bc_r4(n)%field(m)%values,&
2499  & complete=(do_complete.and.(fc==fc_out)) )
2500  endif
2501  enddo
2502  enddo
2503  endif
2504  else
2505  call mpp_error(fatal, "CT_redistribute_data_3d: passed in type has unassociated coupler_field_type"// &
2506  " pointers for both bc and bc_r4")
2507  endif
2508  end subroutine ct_redistribute_data_3d
2509 
2510 
2511 
2512 
2513  !> @brief Increment data in all elements of one coupler_2d_bc_type
2514  !!
2515  !! Do a direct increment of the data in all elements of one coupler_2d_bc_type into another. Both
2516  !! must have the same array sizes.
2517  !!
2518  !! @throw FATAL, "bc_index is present and exceeds var_in%num_bcs."
2519  !! @throw FATAL, "field_index is present and exceeds num_fields for var_in%bc(bc_incdx)%name"
2520  !! @throw FATAL, "bc_index must be present if field_index is present."
2521  !! @throw FATAL, "There is an i-direction computational domain size mismatch."
2522  !! @throw FATAL, "There is an j-direction computational domain size mismatch."
2523  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
2524  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
2525  subroutine ct_increment_data_2d_2d(var_in, var, halo_size, bc_index, field_index,&
2526  & scale_factor, scale_prev, exclude_flux_type, only_flux_type, pass_through_ice)
2527  type(coupler_2d_bc_type), intent(in) :: var_in !< BC_type structure with the data to add to the other type
2528  type(coupler_2d_bc_type), intent(inout) :: var !< The BC_type structure whose fields are being incremented
2529  integer, optional, intent(in) :: halo_size !< The extent of the halo to increment; 0 by default
2530  integer, optional, intent(in) :: bc_index !< The index of the boundary condition
2531  !! that is being copied
2532  integer, optional, intent(in) :: field_index !< The index of the field in the
2533  !! boundary condition that is being copied
2534  real(r8_kind), optional, intent(in) :: scale_factor !< A scaling factor for the data that is being added
2535  real(r8_kind), optional, intent(in) :: scale_prev !< A scaling factor for the data that is already here
2536  character(len=*), optional, intent(in) :: exclude_flux_type !< A string describing which types
2537  !! of fluxes to exclude from this increment.
2538  character(len=*), optional, intent(in) :: only_flux_type !< A string describing which types
2539  !! of fluxes to include from this increment.
2540  logical, optional, intent(in) :: pass_through_ice !< If true, only increment BCs whose
2541  !! value of pass_through ice matches this
2542 
2543  real(r8_kind) :: scale, sc_prev
2544  logical :: increment_bc
2545  integer :: i, j, m, n, n1, n2, halo, i_off, j_off
2546 
2547  scale = 1.0_r8_kind
2548  if (present(scale_factor)) scale = scale_factor
2549  sc_prev = 1.0_r8_kind
2550  if (present(scale_prev)) sc_prev = scale_prev
2551 
2552  if (present(bc_index)) then
2553  if (bc_index > var_in%num_bcs)&
2554  & call mpp_error(fatal, "CT_increment_data_2d_2d: bc_index is present and exceeds var_in%num_bcs.")
2555  if (present(field_index)) then
2556  if( associated(var_in%bc)) then
2557  if (field_index > var_in%bc(bc_index)%num_fields)&
2558  & call mpp_error(fatal, "CT_increment_data_2d_2d: field_index is present and exceeds num_fields for" //&
2559  & trim(var_in%bc(bc_index)%name) )
2560  else
2561  if (field_index > var_in%bc_r4(bc_index)%num_fields)&
2562  & call mpp_error(fatal, "CT_increment_data_2d_2d: field_index is present and exceeds num_fields for" //&
2563  & trim(var_in%bc_r4(bc_index)%name) )
2564  endif
2565  endif
2566  elseif (present(field_index)) then
2567  call mpp_error(fatal, "CT_increment_data_2d_2d: bc_index must be present if field_index is present.")
2568  endif
2569 
2570  halo = 0
2571  if (present(halo_size)) halo = halo_size
2572 
2573  n1 = 1
2574  n2 = var_in%num_bcs
2575  if (present(bc_index)) then
2576  n1 = bc_index
2577  n2 = bc_index
2578  endif
2579 
2580  if (n2 >= n1) then
2581  ! A more consciencious implementation would include a more descriptive error messages.
2582  if ((var_in%iec-var_in%isc) /= (var%iec-var%isc))&
2583  & call mpp_error(fatal, "CT_increment_data_2d: There is an i-direction computational domain size mismatch.")
2584  if ((var_in%jec-var_in%jsc) /= (var%jec-var%jsc))&
2585  & call mpp_error(fatal, "CT_increment_data_2d: There is a j-direction computational domain size mismatch.")
2586  if ((var_in%isc-var_in%isd < halo) .or. (var_in%ied-var_in%iec < halo))&
2587  & call mpp_error(fatal, "CT_increment_data_2d: Excessive i-direction halo size for the input structure.")
2588  if ((var_in%jsc-var_in%jsd < halo) .or. (var_in%jed-var_in%jec < halo))&
2589  & call mpp_error(fatal, "CT_increment_data_2d: Excessive j-direction halo size for the input structure.")
2590  if ((var%isc-var%isd < halo) .or. (var%ied-var%iec < halo))&
2591  & call mpp_error(fatal, "CT_increment_data_2d: Excessive i-direction halo size for the output structure.")
2592  if ((var%jsc-var%jsd < halo) .or. (var%jed-var%jec < halo))&
2593  & call mpp_error(fatal, "CT_increment_data_2d: Excessive j-direction halo size for the output structure.")
2594 
2595  i_off = var_in%isc - var%isc
2596  j_off = var_in%jsc - var%jsc
2597  endif
2598 
2599  ! check only one kind used
2600  if(var_in%set .and. var_in%num_bcs .gt. 0) then
2601  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
2602  if( associated(var_in%bc) ) then
2603  call mpp_error(fatal, "CT_increment_data_2d_2d: var_in%bc and var_in%bc_r4 are both intialized,"// &
2604  " only one should be associated per type")
2605  else
2606  call mpp_error(fatal, "CT_increment_data_2d_2d: var_in%bc and var_in%bc_r4 are both intialized,"// &
2607  " only one must be associated per type to increment field data.")
2608  endif
2609  endif
2610  endif
2611 
2612  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2613  if(associated(var_in%bc) .or. var_in%num_bcs .lt. 1) then
2614  do n = n1, n2
2615  increment_bc = .true.
2616  if (increment_bc .and. present(exclude_flux_type))&
2617  & increment_bc = .not.(trim(var%bc(n)%flux_type) == trim(exclude_flux_type))
2618  if (increment_bc .and. present(only_flux_type))&
2619  & increment_bc = (trim(var%bc(n)%flux_type) == trim(only_flux_type))
2620  if (increment_bc .and. present(pass_through_ice))&
2621  & increment_bc = (pass_through_ice .eqv. var%bc(n)%pass_through_ice)
2622  if (.not.increment_bc) cycle
2623 
2624  do m = 1, var_in%bc(n)%num_fields
2625  if (present(field_index)) then
2626  if (m /= field_index) cycle
2627  endif
2628  if ( associated(var%bc(n)%field(m)%values) ) then
2629  do j=var%jsc-halo,var%jec+halo
2630  do i=var%isc-halo,var%iec+halo
2631  var%bc(n)%field(m)%values(i,j) = sc_prev * var%bc(n)%field(m)%values(i,j) +&
2632  & scale * var_in%bc(n)%field(m)%values(i+i_off,j+j_off)
2633  enddo
2634  enddo
2635  endif
2636  enddo
2637  enddo
2638  else if(associated(var_in%bc_r4)) then
2639  do n = n1, n2
2640  increment_bc = .true.
2641  if (increment_bc .and. present(exclude_flux_type))&
2642  & increment_bc = .not.(trim(var%bc_r4(n)%flux_type) == trim(exclude_flux_type))
2643  if (increment_bc .and. present(only_flux_type))&
2644  & increment_bc = (trim(var%bc_r4(n)%flux_type) == trim(only_flux_type))
2645  if (increment_bc .and. present(pass_through_ice))&
2646  & increment_bc = (pass_through_ice .eqv. var%bc_r4(n)%pass_through_ice)
2647  if (.not.increment_bc) cycle
2648 
2649  do m = 1, var_in%bc_r4(n)%num_fields
2650  if (present(field_index)) then
2651  if (m /= field_index) cycle
2652  endif
2653  if ( associated(var%bc_r4(n)%field(m)%values) ) then
2654  do j=var%jsc-halo,var%jec+halo
2655  do i=var%isc-halo,var%iec+halo
2656  var%bc_r4(n)%field(m)%values(i,j) = real(sc_prev,r4_kind) * var%bc_r4(n)%field(m)%values(i,j) +&
2657  & real(scale,r4_kind) * var_in%bc_r4(n)%field(m)%values(i+i_off,j+j_off)
2658  enddo
2659  enddo
2660  endif
2661  enddo
2662  enddo
2663  else
2664  call mpp_error(fatal, "CT_increment_data_2d_2d: passed in type has unassociated coupler_field_type"// &
2665  " pointers for both bc and bc_r4")
2666  endif
2667  end subroutine ct_increment_data_2d_2d
2668 
2669 
2670  !> @brief Increment data in all elements of one coupler_3d_bc_type
2671  !!
2672  !! Do a direct increment of the data in all elements of one coupler_3d_bc_type into another. Both
2673  !! must have the same array sizes.
2674  !!
2675  !! @throw FATAL, "bc_index is present and exceeds var_in%num_bcs."
2676  !! @throw FATAL, "field_index is present and exceeds num_fields for var_in%bc(bc_incdx)%name"
2677  !! @throw FATAL, "bc_index must be present if field_index is present."
2678  !! @throw FATAL, "There is an i-direction computational domain size mismatch."
2679  !! @throw FATAL, "There is an j-direction computational domain size mismatch."
2680  !! @throw FATAL, "There is an k-direction computational domain size mismatch."
2681  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
2682  !! @throw FATAL, "Excessive i-direction halo size for the input structure."
2683  !! @throw FATAL, "Excessive k-direction halo size for the input structure."
2684  subroutine ct_increment_data_3d_3d(var_in, var, halo_size, bc_index, field_index,&
2685  & scale_factor, scale_prev, exclude_flux_type, only_flux_type, pass_through_ice)
2686  type(coupler_3d_bc_type), intent(in) :: var_in !< BC_type structure with the data to add to the other type
2687  type(coupler_3d_bc_type), intent(inout) :: var !< The BC_type structure whose fields are being incremented
2688  integer, optional, intent(in) :: halo_size !< The extent of the halo to copy; 0 by default
2689  integer, optional, intent(in) :: bc_index !< The index of the boundary condition
2690  !! that is being copied
2691  integer, optional, intent(in) :: field_index !< The index of the field in the
2692  !! boundary condition that is being copied
2693  real(r8_kind), optional, intent(in) :: scale_factor !< A scaling factor for the data that is being added
2694  real(r8_kind), optional, intent(in) :: scale_prev !< A scaling factor for the data that is already here
2695  character(len=*), optional, intent(in) :: exclude_flux_type !< A string describing which types
2696  !! of fluxes to exclude from this increment.
2697  character(len=*), optional, intent(in) :: only_flux_type !< A string describing which types of
2698  !! fluxes to include from this increment.
2699  logical, optional, intent(in) :: pass_through_ice !< If true, only increment BCs whose
2700  !! value of pass_through ice matches this
2701 
2702  real(r8_kind) :: scale, sc_prev
2703  logical :: increment_bc
2704  integer :: i, j, k, m, n, n1, n2, halo, i_off, j_off, k_off
2705 
2706  scale = 1.0_r8_kind
2707  if (present(scale_factor)) scale = scale_factor
2708  sc_prev = 1.0_r8_kind
2709  if (present(scale_prev)) sc_prev = scale_prev
2710 
2711  if (present(bc_index)) then
2712  if (bc_index > var_in%num_bcs)&
2713  & call mpp_error(fatal, "CT_increment_data_3d_3d: bc_index is present and exceeds var_in%num_bcs.")
2714  if(associated(var_in%bc)) then
2715  if (present(field_index)) then ; if (field_index > var_in%bc(bc_index)%num_fields)&
2716  & call mpp_error(fatal, "CT_increment_data_3d_3d: field_index is present and exceeds num_fields for" //&
2717  & trim(var_in%bc(bc_index)%name) )
2718  endif
2719  else if(associated(var_in%bc_r4)) then
2720  if (present(field_index)) then ; if (field_index > var_in%bc_r4(bc_index)%num_fields)&
2721  & call mpp_error(fatal, "CT_increment_data_3d_3d: field_index is present and exceeds num_fields for" //&
2722  & trim(var_in%bc_r4(bc_index)%name) )
2723  endif
2724  endif
2725  elseif (present(field_index)) then
2726  call mpp_error(fatal, "CT_increment_data_3d_3d: bc_index must be present if field_index is present.")
2727  endif
2728 
2729  halo = 0
2730  if (present(halo_size)) halo = halo_size
2731 
2732  n1 = 1
2733  n2 = var_in%num_bcs
2734  if (present(bc_index)) then
2735  n1 = bc_index
2736  n2 = bc_index
2737  endif
2738 
2739  if (n2 >= n1) then
2740  ! A more consciencious implementation would include a more descriptive error messages.
2741  if ((var_in%iec-var_in%isc) /= (var%iec-var%isc))&
2742  & call mpp_error(fatal, "CT_increment_data_3d: There is an i-direction computational domain size mismatch.")
2743  if ((var_in%jec-var_in%jsc) /= (var%jec-var%jsc))&
2744  & call mpp_error(fatal, "CT_increment_data_3d: There is a j-direction computational domain size mismatch.")
2745  if ((var_in%ke-var_in%ks) /= (var%ke-var%ks))&
2746  & call mpp_error(fatal, "CT_increment_data_3d: There is a k-direction computational domain size mismatch.")
2747  if ((var_in%isc-var_in%isd < halo) .or. (var_in%ied-var_in%iec < halo))&
2748  & call mpp_error(fatal, "CT_increment_data_3d: Excessive i-direction halo size for the input structure.")
2749  if ((var_in%jsc-var_in%jsd < halo) .or. (var_in%jed-var_in%jec < halo))&
2750  & call mpp_error(fatal, "CT_increment_data_3d: Excessive j-direction halo size for the input structure.")
2751  if ((var%isc-var%isd < halo) .or. (var%ied-var%iec < halo))&
2752  & call mpp_error(fatal, "CT_increment_data_3d: Excessive i-direction halo size for the output structure.")
2753  if ((var%jsc-var%jsd < halo) .or. (var%jed-var%jec < halo))&
2754  & call mpp_error(fatal, "CT_increment_data_3d: Excessive j-direction halo size for the output structure.")
2755 
2756  i_off = var_in%isc - var%isc
2757  j_off = var_in%jsc - var%jsc
2758  k_off = var_in%ks - var%ks
2759  endif
2760 
2761  ! check only one kind used
2762  if(var_in%set .and. var_in%num_bcs .gt. 0) then
2763  if(associated(var_in%bc) .eqv. associated(var_in%bc_r4)) then
2764  if( associated(var_in%bc) ) then
2765  call mpp_error(fatal, "CT_increment_data_3d_3d: var_in%bc and var_in%bc_r4 are both intialized,"//&
2766  "only one should be associated per type")
2767  else
2768  call mpp_error(fatal, "CT_increment_data_3d_3d: var_in%bc and var_in%bc_r4 are both unintialized,"//&
2769  " only one must be associated per type to increment field data.")
2770  endif
2771  endif
2772  endif
2773 
2774  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2775  if(associated(var_in%bc) .or. var_in%num_bcs .lt. 1) then
2776  do n = n1, n2
2777  increment_bc = .true.
2778  if (increment_bc .and. present(exclude_flux_type))&
2779  & increment_bc = .not.(trim(var%bc(n)%flux_type) == trim(exclude_flux_type))
2780  if (increment_bc .and. present(only_flux_type))&
2781  & increment_bc = (trim(var%bc(n)%flux_type) == trim(only_flux_type))
2782  if (increment_bc .and. present(pass_through_ice))&
2783  & increment_bc = (pass_through_ice .eqv. var%bc(n)%pass_through_ice)
2784  if (.not.increment_bc) cycle
2785 
2786  do m = 1, var_in%bc(n)%num_fields
2787  if (present(field_index)) then
2788  if (m /= field_index) cycle
2789  endif
2790  if ( associated(var%bc(n)%field(m)%values) ) then
2791  do k=var%ks,var%ke
2792  do j=var%jsc-halo,var%jec+halo
2793  do i=var%isc-halo,var%iec+halo
2794  var%bc(n)%field(m)%values(i,j,k) = sc_prev * var%bc(n)%field(m)%values(i,j,k) +&
2795  & scale * var_in%bc(n)%field(m)%values(i+i_off,j+j_off,k+k_off)
2796  enddo
2797  enddo
2798  enddo
2799  endif
2800  enddo
2801  enddo
2802  else if(associated(var_in%bc_r4)) then
2803  do n = n1, n2
2804  increment_bc = .true.
2805  if (increment_bc .and. present(exclude_flux_type))&
2806  & increment_bc = .not.(trim(var%bc_r4(n)%flux_type) == trim(exclude_flux_type))
2807  if (increment_bc .and. present(only_flux_type))&
2808  & increment_bc = (trim(var%bc_r4(n)%flux_type) == trim(only_flux_type))
2809  if (increment_bc .and. present(pass_through_ice))&
2810  & increment_bc = (pass_through_ice .eqv. var%bc_r4(n)%pass_through_ice)
2811  if (.not.increment_bc) cycle
2812 
2813  do m = 1, var_in%bc_r4(n)%num_fields
2814  if (present(field_index)) then
2815  if (m /= field_index) cycle
2816  endif
2817  if ( associated(var%bc_r4(n)%field(m)%values) ) then
2818  do k=var%ks,var%ke
2819  do j=var%jsc-halo,var%jec+halo
2820  do i=var%isc-halo,var%iec+halo
2821  var%bc_r4(n)%field(m)%values(i,j,k) = real(sc_prev,r4_kind) * var%bc_r4(n)%field(m)%values(i,j,k) +&
2822  & real(scale,r4_kind) * var_in%bc_r4(n)%field(m)%values(i+i_off,j+j_off,k+k_off)
2823  enddo
2824  enddo
2825  enddo
2826  endif
2827  enddo
2828  enddo
2829  else
2830  call mpp_error(fatal, "CT_increment_data_3d_3d: passed in type has unassociated coupler_field_type"// &
2831  " pointers for both bc and bc_r4")
2832  endif
2833  end subroutine ct_increment_data_3d_3d
2834 
2835  !! @brief Register the diagnostics of a coupler_2d_bc_type
2836  !!
2837  !! @throw FATAL, "axes has less than 2 elements"
2838  subroutine ct_set_diags_2d(var, diag_name, axes, time)
2839  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure for which to register diagnostics
2840  character(len=*), intent(in) :: diag_name !< Module name for diagnostic file--if blank, then
2841  !! don't register the fields
2842  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
2843  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
2844 
2845  integer :: m, n
2846 
2847  if (diag_name == ' ') return
2848 
2849  if (size(axes) < 2) then
2850  call mpp_error(fatal, '==>Error from coupler_types_mod' //&
2851  & '(coupler_types_set_diags_3d): axes has less than 2 elements')
2852  endif
2853 
2854  if(var%set .and. var%num_bcs .gt. 0) then
2855  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
2856  if( associated(var%bc) ) then
2857  call mpp_error(fatal, "CT_set_diags_2d: var%bc and var%bc_r4 are both intialized,"//&
2858  "only one should be associated per type")
2859  else
2860  call mpp_error(fatal, "CT_set_diags_2d: var%bc and var%bc_r4 are both intialized,"//&
2861  "one should be associated per type to register fields with diag manager")
2862  endif
2863  endif
2864  endif
2865 
2866  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2867  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
2868  do n = 1, var%num_bcs
2869  do m = 1, var%bc(n)%num_fields
2870  var%bc(n)%field(m)%id_diag = register_diag_field(diag_name,&
2871  & var%bc(n)%field(m)%name, axes(1:2), time,&
2872  & var%bc(n)%field(m)%long_name, var%bc(n)%field(m)%units)
2873  enddo
2874  enddo
2875  else if(associated(var%bc_r4)) then
2876  do n = 1, var%num_bcs
2877  do m = 1, var%bc_r4(n)%num_fields
2878  var%bc_r4(n)%field(m)%id_diag = register_diag_field(diag_name,&
2879  & var%bc_r4(n)%field(m)%name, axes(1:2), time,&
2880  & var%bc_r4(n)%field(m)%long_name, var%bc_r4(n)%field(m)%units)
2881  enddo
2882  enddo
2883  else
2884  call mpp_error(fatal, "CT_set_diags_2d: passed in type has unassociated coupler_field_type"// &
2885  " pointers for both bc and bc_r4")
2886  endif
2887 
2888 
2889  end subroutine ct_set_diags_2d
2890 
2891  !> @brief Register the diagnostics of a coupler_3d_bc_type.
2892  !!
2893  !! @throw FATAL, "axes has less than 3 elements"
2894  subroutine ct_set_diags_3d(var, diag_name, axes, time)
2895  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure for which to register diagnostics
2896  character(len=*), intent(in) :: diag_name !< Module name for diagnostic file--if blank, then
2897  !! don't register the fields
2898  integer, dimension(:), intent(in) :: axes !< array of axes identifiers for diagnostic variable registration
2899  type(time_type), intent(in) :: time !< model time variable for registering diagnostic field
2900 
2901  integer :: m, n
2902 
2903  if (diag_name == ' ') return
2904 
2905  if (size(axes) < 3) then
2906  call mpp_error(fatal, '==>Error from coupler_types_mod' //&
2907  & '(coupler_types_set_diags_3d): axes has less than 3 elements')
2908  endif
2909 
2910  if(var%set .and. var%num_bcs .gt. 0) then
2911  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
2912  if( associated(var%bc) ) then
2913  call mpp_error(fatal, "CT_set_diags_3d: var%bc and var%bc_r4 are both intialized,"//&
2914  "only one should be associated per type")
2915  else
2916  call mpp_error(fatal, "CT_set_diags_3d: var%bc and var%bc_r4 are both unintialized,"//&
2917  "one should be associated per type to register fields with diag manager")
2918  endif
2919  endif
2920  endif
2921 
2922  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2923  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
2924  do n = 1, var%num_bcs
2925  do m = 1, var%bc(n)%num_fields
2926  var%bc(n)%field(m)%id_diag = register_diag_field(diag_name,&
2927  & var%bc(n)%field(m)%name, axes(1:3), time,&
2928  & var%bc(n)%field(m)%long_name, var%bc(n)%field(m)%units )
2929  enddo
2930  enddo
2931  else if(associated(var%bc_r4)) then
2932  do n = 1, var%num_bcs
2933  do m = 1, var%bc_r4(n)%num_fields
2934  var%bc_r4(n)%field(m)%id_diag = register_diag_field(diag_name,&
2935  & var%bc_r4(n)%field(m)%name, axes(1:3), time,&
2936  & var%bc_r4(n)%field(m)%long_name, var%bc_r4(n)%field(m)%units )
2937  enddo
2938  enddo
2939  else
2940  call mpp_error(fatal, "CT_set_diags_3d: passed in type has unassociated coupler_field_type"// &
2941  " pointers for both bc and bc_r4")
2942  endif
2943  end subroutine ct_set_diags_3d
2944 
2945 
2946  !> @brief Write out all diagnostics of elements of a coupler_2d_bc_type
2947  subroutine ct_send_data_2d(var, Time, return_statuses)
2948  type(coupler_2d_bc_type), intent(in) :: var !< BC_type structure with the diagnostics to write
2949  type(time_type), intent(in) :: time !< The current model time
2950  logical, allocatable, optional, intent(out) :: return_statuses(:,:) !< Return status of send data calls
2951  !! first index is index of boundary condition
2952  !! second index is field/value within that boundary condition
2953 
2954  integer :: m, n
2955  logical :: used
2956 
2957  if(var%set .and. var%num_bcs .gt. 0) then
2958  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
2959  if( associated(var%bc) ) then
2960  call mpp_error(fatal, "CT_send_data_2d: var%bc and var%bc_r4 are both intialized,"//&
2961  "only one should be associated per type")
2962  else
2963  call mpp_error(fatal, "CT_send_data_2d: var%bc and var%bc_r4 are both unintialized,"//&
2964  "one should be associated per type to send data to diag fields")
2965  endif
2966  endif
2967  endif
2968 
2969  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
2970  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
2971 
2972  ! allocate array for returned send data statuses
2973  if( present(return_statuses) .and. var%num_bcs .gt. 0) then
2974  allocate(return_statuses(var%num_bcs, var%bc(1)%num_fields))
2975  endif
2976 
2977  do n = 1, var%num_bcs
2978  do m = 1, var%bc(n)%num_fields
2979  if (var%bc(n)%field(m)%id_diag > 0) then
2980  used = send_data(var%bc(n)%field(m)%id_diag, var%bc(n)%field(m)%values, time)
2981  if(allocated(return_statuses)) return_statuses(n,m) = used
2982  endif
2983  enddo
2984  enddo
2985 
2986  else if(associated(var%bc_r4)) then
2987 
2988  ! allocate array for returned send data statuses
2989  if( present(return_statuses) .and. var%num_bcs .gt. 0) then
2990  allocate(return_statuses(var%num_bcs, var%bc_r4(1)%num_fields))
2991  endif
2992 
2993  do n = 1, var%num_bcs
2994  do m = 1, var%bc_r4(n)%num_fields
2995  if (var%bc_r4(n)%field(m)%id_diag > 0) then
2996  used = send_data(var%bc_r4(n)%field(m)%id_diag, var%bc_r4(n)%field(m)%values, time)
2997  if(allocated(return_statuses)) return_statuses(n,m) = used
2998  endif
2999  enddo
3000  enddo
3001  else
3002  call mpp_error(fatal, "CT_send_data_2d: passed in type has unassociated coupler_field_type"// &
3003  " pointers for both bc and bc_r4")
3004  endif
3005  end subroutine ct_send_data_2d
3006 
3007  !> @brief Write out all diagnostics of elements of a coupler_3d_bc_type
3008  subroutine ct_send_data_3d(var, Time, return_statuses)
3009  type(coupler_3d_bc_type), intent(in) :: var !< BC_type structure with the diagnostics to write
3010  type(time_type), intent(in) :: time !< The current model time
3011  logical, allocatable, optional, intent(out) :: return_statuses(:,:) !< Return status of send data calls
3012  !! first index is index of boundary condition
3013  !! second index is field/value within that boundary condition
3014 
3015  integer :: m, n
3016  logical :: used
3017 
3018  if(var%set .and. var%num_bcs .gt. 0) then
3019  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3020  if( associated(var%bc) ) then
3021  call mpp_error(fatal, "CT_send_data_3d: var%bc and var%bc_r4 are both intialized,"//&
3022  "only one should be associated per type")
3023  else
3024  call mpp_error(fatal, "CT_send_data_3d: var%bc and var%bc_r4 are both unintialized,"//&
3025  "one should be associated per type to send data to diag fields")
3026  endif
3027  endif
3028  endif
3029 
3030  ! num_bcs .lt. 1 -> loop doesn't run but shouldn't error out
3031  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3032 
3033  ! allocate array for returned send data statuses
3034  if( present(return_statuses) .and. var%num_bcs .gt. 0) then
3035  allocate(return_statuses(var%num_bcs, var%bc(1)%num_fields))
3036  endif
3037 
3038  do n = 1, var%num_bcs
3039  do m = 1, var%bc(n)%num_fields
3040  if (var%bc(n)%field(m)%id_diag > 0) then
3041  used = send_data(var%bc(n)%field(m)%id_diag, var%bc(n)%field(m)%values, time)
3042  if(allocated(return_statuses)) return_statuses(n,m) = used
3043  endif
3044  enddo
3045  enddo
3046  else if(associated(var%bc_r4)) then
3047 
3048  ! allocate array for returned send data statuses
3049  if( present(return_statuses) .and. var%num_bcs .gt. 0) then
3050  allocate(return_statuses(var%num_bcs, var%bc_r4(1)%num_fields))
3051  endif
3052 
3053  do n = 1, var%num_bcs
3054  do m = 1, var%bc_r4(n)%num_fields
3055  if (var%bc_r4(n)%field(m)%id_diag > 0) then
3056  used = send_data(var%bc_r4(n)%field(m)%id_diag, var%bc_r4(n)%field(m)%values, time)
3057  if(allocated(return_statuses)) return_statuses(n,m) = used
3058  endif
3059  enddo
3060  enddo
3061  else
3062  call mpp_error(fatal, "CT_send_data_3d: passed in type has unassociated coupler_field_type"// &
3063  " pointers for both bc and bc_r4")
3064  endif
3065  end subroutine ct_send_data_3d
3066 
3067  !! @brief Register the fields in a coupler_2d_bc_type to be saved in restart files
3068  !! This subroutine registers the fields in a coupler_2d_bc_type to be saved in restart files
3069  !! specified in the field table.
3070  subroutine ct_register_restarts_2d(var, bc_rest_files, num_rest_files, mpp_domain, to_read, ocean_restart, directory)
3071  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure to be registered for restarts
3072  type(fmsnetcdfdomainfile_t), dimension(:), pointer :: bc_rest_files !< Structures describing the restart files
3073  integer, intent(out) :: num_rest_files !< The number of restart files to use
3074  type(domain2d), intent(in) :: mpp_domain !< The FMS domain to use for this registration call
3075  logical, intent(in) :: to_read !< Flag indicating if reading/writing a file
3076  logical, optional,intent(in) :: ocean_restart !< If true, use the ocean restart file name.
3077  character(len=*),optional,intent(in) :: directory !< Directory where to open the file
3078 
3079  character(len=FMS_FILE_LEN), dimension(max(1,var%num_bcs)) :: rest_file_names
3080  character(len=FMS_FILE_LEN) :: file_nm
3081  logical :: ocn_rest
3082  integer :: f, n, m
3083 
3084  character(len=20), allocatable, dimension(:) :: dim_names !< Array of dimension names
3085  character(len=20) :: io_type !< flag indicating io type: "read" "overwrite"
3086  logical, dimension(max(1,var%num_bcs)) :: file_is_open !< flag indicating if file is open
3087  character(len=FMS_PATH_LEN) :: dir !< Directory where to open the file
3088 
3089  if(var%set .and. var%num_bcs .gt. 0) then
3090  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3091  if( associated(var%bc) ) then
3092  call mpp_error(fatal, "CT_register_restarts_2d: var%bc and var%bc_r4 are both intialized,"//&
3093  "only one should be associated per type")
3094  else
3095  call mpp_error(fatal, "CT_register_restarts_2d: var%bc and var%bc_r4 are both unintialized,"//&
3096  "one should be associated per type to register restart fields")
3097  endif
3098  endif
3099  endif
3100 
3101  ocn_rest = .true.
3102  if (present(ocean_restart)) ocn_rest = ocean_restart
3103 
3104  if (present(directory)) dir = trim(directory)
3105 
3106  if (to_read) then
3107  io_type = "read"
3108  if (.not. present(directory)) dir = "INPUT/"
3109  else
3110  io_type = "overwrite"
3111  if (.not. present(directory)) dir = "RESTART/"
3112  endif
3113 
3114  num_rest_files = 0
3115 
3116  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3117  ! Determine the number and names of the restart files
3118  do n = 1, var%num_bcs
3119  if (var%bc(n)%num_fields <= 0) cycle
3120  file_nm = trim(var%bc(n)%ice_restart_file)
3121  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
3122  do f = 1, num_rest_files
3123  if (trim(file_nm) == trim(rest_file_names(f))) exit
3124  enddo
3125  if (f>num_rest_files) then
3126  num_rest_files = num_rest_files + 1
3127  rest_file_names(f) = trim(file_nm)
3128  endif
3129  enddo
3130 
3131  if (num_rest_files == 0) return
3132 
3133  allocate(bc_rest_files(num_rest_files))
3134 
3135  !< Open the files
3136  do n = 1, num_rest_files
3137  file_is_open(n) = open_file(bc_rest_files(n), trim(dir)//rest_file_names(n), io_type, mpp_domain, &
3138  & is_restart=.true.)
3139  if (file_is_open(n)) then
3140  call register_axis_wrapper(bc_rest_files(n), to_read=to_read)
3141  endif
3142  enddo
3143 
3144  ! Register the fields with the restart files
3145  do n = 1, var%num_bcs
3146  if (var%bc(n)%num_fields <= 0) cycle
3147 
3148  file_nm = trim(var%bc(n)%ice_restart_file)
3149  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
3150  do f = 1, num_rest_files
3151  if (trim(file_nm) == trim(rest_file_names(f))) exit
3152  enddo
3153 
3154  var%bc(n)%fms2_io_rest_type => bc_rest_files(f)
3155 
3156  do m = 1, var%bc(n)%num_fields
3157  if (file_is_open(f)) then
3158  if( to_read .and. variable_exists(bc_rest_files(f), var%bc(n)%field(m)%name)) then
3159  !< If reading get the dimension names from the file
3160  allocate(dim_names(get_variable_num_dimensions(bc_rest_files(f), var%bc(n)%field(m)%name)))
3161  call get_variable_dimension_names(bc_rest_files(f), &
3162  & var%bc(n)%field(m)%name, dim_names)
3163  else
3164  !< If writing use dummy dimension names
3165  allocate(dim_names(3))
3166  dim_names(1) = "xaxis_1"
3167  dim_names(2) = "yaxis_1"
3168  dim_names(3) = "Time"
3169  endif !< to_read
3170 
3171  call register_restart_field(bc_rest_files(f),&
3172  & var%bc(n)%field(m)%name, var%bc(n)%field(m)%values, dim_names, &
3173  & is_optional=var%bc(n)%field(m)%may_init )
3174 
3175  deallocate(dim_names)
3176  endif !< If file_is_open
3177  enddo !< num_fields
3178  enddo !< num_bcs
3179  else if(associated(var%bc_r4)) then
3180  ! Determine the number and names of the restart files
3181  do n = 1, var%num_bcs
3182  if (var%bc_r4(n)%num_fields <= 0) cycle
3183  file_nm = trim(var%bc_r4(n)%ice_restart_file)
3184  if (ocn_rest) file_nm = trim(var%bc_r4(n)%ocean_restart_file)
3185  do f = 1, num_rest_files
3186  if (trim(file_nm) == trim(rest_file_names(f))) exit
3187  enddo
3188  if (f>num_rest_files) then
3189  num_rest_files = num_rest_files + 1
3190  rest_file_names(f) = trim(file_nm)
3191  endif
3192  enddo
3193 
3194  if (num_rest_files == 0) return
3195 
3196  allocate(bc_rest_files(num_rest_files))
3197 
3198  !< Open the files
3199  do n = 1, num_rest_files
3200  file_is_open(n) = open_file(bc_rest_files(n), trim(dir)//rest_file_names(n), io_type, mpp_domain, &
3201  & is_restart=.true.)
3202  if (file_is_open(n)) then
3203  call register_axis_wrapper(bc_rest_files(n), to_read=to_read)
3204  endif
3205  enddo
3206 
3207  ! Register the fields with the restart files
3208  do n = 1, var%num_bcs
3209  if (var%bc_r4(n)%num_fields <= 0) cycle
3210 
3211  file_nm = trim(var%bc_r4(n)%ice_restart_file)
3212  if (ocn_rest) file_nm = trim(var%bc_r4(n)%ocean_restart_file)
3213  do f = 1, num_rest_files
3214  if (trim(file_nm) == trim(rest_file_names(f))) exit
3215  enddo
3216 
3217  var%bc_r4(n)%fms2_io_rest_type => bc_rest_files(f)
3218 
3219  do m = 1, var%bc_r4(n)%num_fields
3220  if (file_is_open(f)) then
3221  if( to_read .and. variable_exists(bc_rest_files(f), var%bc_r4(n)%field(m)%name)) then
3222  !< If reading get the dimension names from the file
3223  allocate(dim_names(get_variable_num_dimensions(bc_rest_files(f), var%bc_r4(n)%field(m)%name)))
3224  call get_variable_dimension_names(bc_rest_files(f), &
3225  & var%bc_r4(n)%field(m)%name, dim_names)
3226  else
3227  !< If writing use dummy dimension names
3228  allocate(dim_names(3))
3229  dim_names(1) = "xaxis_1"
3230  dim_names(2) = "yaxis_1"
3231  dim_names(3) = "Time"
3232  endif !< to_read
3233 
3234  call register_restart_field(bc_rest_files(f),&
3235  & var%bc_r4(n)%field(m)%name, var%bc_r4(n)%field(m)%values, dim_names, &
3236  & is_optional=var%bc_r4(n)%field(m)%may_init )
3237 
3238  deallocate(dim_names)
3239  endif !< If file_is_open
3240  enddo !< num_fields
3241  enddo !< num_bcs
3242  else
3243  call mpp_error(fatal, "CT_register_restarts_2d: passed in type has unassociated coupler_field_type"// &
3244  " pointers for both bc and bc_r4")
3245  endif ! associated(var%bc/r4)
3246 
3247  end subroutine ct_register_restarts_2d
3248 
3249  !< If reading a restart, register the dimensions that are in the file
3250  subroutine register_axis_wrapper_read(fileobj)
3251  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj !< Domain decomposed fileobj
3252 
3253  character(len=20), dimension(:), allocatable :: file_dim_names !< Array of dimension names
3254  integer :: i !< No description
3255  integer :: dim_size !< Size of the dimension
3256  integer :: ndims !< Number of dimensions in the file
3257  logical :: is_domain_decomposed !< Flag indication if domain decomposed
3258  character(len=1) :: buffer !< string buffer
3259 
3260  ndims = get_num_dimensions(fileobj)
3261  allocate(file_dim_names(ndims))
3262 
3263  call get_dimension_names(fileobj, file_dim_names)
3264 
3265  do i = 1, ndims
3266  is_domain_decomposed = .false.
3267 
3268  !< Check if the dimension is also a variable
3269  if (variable_exists(fileobj, file_dim_names(i))) then
3270 
3271  !< If the variable exists look for the "cartesian_axis" or "axis" variable attribute
3272  if (variable_att_exists(fileobj, file_dim_names(i), "axis")) then
3273  call get_variable_attribute(fileobj, file_dim_names(i), "axis", buffer)
3274 
3275  !< If the attribute exists and it is "x" or "y" register it as a domain decomposed dimension
3276  if (lowercase(buffer) .eq. "x" .or. lowercase(buffer) .eq. "y" ) then
3277  is_domain_decomposed = .true.
3278  call register_axis(fileobj, file_dim_names(i), buffer)
3279  endif
3280 
3281  else if (variable_att_exists(fileobj, file_dim_names(i), "cartesian_axis")) then
3282  call get_variable_attribute(fileobj, file_dim_names(i), "cartesian_axis", buffer)
3283 
3284  !< If the attribute exists and it "x" or "y" register it as a domain decomposed dimension
3285  if (lowercase(buffer) .eq. "x" .or. lowercase(buffer) .eq. "y" ) then
3286  is_domain_decomposed = .true.
3287  call register_axis(fileobj, file_dim_names(i), buffer)
3288  endif
3289 
3290  endif !< If variable attribute exists
3291  endif !< If variable exists
3292 
3293  if (.not. is_domain_decomposed) then
3294  call get_dimension_size(fileobj, file_dim_names(i), dim_size)
3295  call register_axis(fileobj, file_dim_names(i), dim_size)
3296  endif
3297 
3298  end do
3299 
3300  end subroutine register_axis_wrapper_read
3301 
3302  !< If writting a restart, register the variables with dummy axis names
3303  subroutine register_axis_wrapper_write(fileobj, nz)
3304  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj !< Domain decomposed fileobj
3305  integer, intent(in), optional :: nz !< length of the z dimension
3306 
3307  character(len=20) :: dim_names(4) !< Array of dimension names
3308 
3309  dim_names(1) = "xaxis_1"
3310  dim_names(2) = "yaxis_1"
3311 
3312  call register_axis(fileobj, dim_names(1), "x")
3313  call register_axis(fileobj, dim_names(2), "y")
3314 
3315  !< If nz is present register a zaxis
3316  if (.not. present(nz)) then
3317  dim_names(3) = "Time"
3318  call register_axis(fileobj, dim_names(3), unlimited)
3319  else
3320  dim_names(3) = "zaxis_1"
3321  dim_names(4) = "Time"
3322 
3323  call register_axis(fileobj, dim_names(3), nz)
3324  call register_axis(fileobj, dim_names(4), unlimited)
3325  endif !< if (.not. present(nz))
3326 
3327  !< Add the dimension names as variable so that the combiner can work correctly
3328  call register_field(fileobj, dim_names(1), "double", (/dim_names(1)/))
3329  call register_variable_attribute(fileobj, dim_names(1), "axis", "X", str_len=1)
3330 
3331  call register_field(fileobj, dim_names(2), "double", (/dim_names(2)/))
3332  call register_variable_attribute(fileobj, dim_names(2), "axis", "Y", str_len=1)
3333 
3334  end subroutine register_axis_wrapper_write
3335 
3336  subroutine register_axis_wrapper(fileobj, to_read, nz)
3337  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj !< Domain decomposed fileobj
3338  logical, intent(in) :: to_read !< Flag indicating if reading file
3339  integer, intent(in), optional :: nz !< length of the z dimension
3340 
3341  if (to_read) then
3342  call register_axis_wrapper_read(fileobj)
3343  else
3344  call register_axis_wrapper_write(fileobj, nz)
3345  endif
3346 
3347  end subroutine register_axis_wrapper
3348 
3349  !! @brief Register the fields in a coupler_3d_bc_type to be saved in restart files
3350  !! This subroutine registers the fields in a coupler_2d_bc_type to be saved in restart files
3351  !! specified in the field table.
3352  subroutine ct_register_restarts_3d(var, bc_rest_files, num_rest_files, mpp_domain, to_read, ocean_restart, directory)
3353  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure to be registered for restarts
3354  type(fmsnetcdfdomainfile_t), dimension(:), pointer :: bc_rest_files !< Structures describing the restart files
3355  integer, intent(out) :: num_rest_files !< The number of restart files to use
3356  type(domain2d), intent(in) :: mpp_domain !< The FMS domain to use for this registration call
3357  logical, intent(in) :: to_read !< Flag indicating if reading/writing a file
3358  logical, optional,intent(in) :: ocean_restart !< If true, use the ocean restart file name.
3359  character(len=*),optional,intent(in) :: directory !< Directory where to open the file
3360 
3361  character(len=FMS_FILE_LEN), dimension(max(1,var%num_bcs)) :: rest_file_names
3362  character(len=FMS_FILE_LEN) :: file_nm
3363  logical :: ocn_rest
3364  integer :: f, n, m
3365 
3366  character(len=20), allocatable, dimension(:) :: dim_names !< Array of dimension names
3367  character(len=20) :: io_type !< flag indicating io type: "read" "overwrite"
3368  logical, dimension(max(1,var%num_bcs)) :: file_is_open !< Flag indicating if file is open
3369  character(len=FMS_PATH_LEN) :: dir !< Directory where to open the file
3370  integer :: nz !< Length of the z direction of each file
3371 
3372  if(var%set .and. var%num_bcs .gt. 0) then
3373  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3374  if( associated(var%bc) ) then
3375  call mpp_error(fatal, "CT_register_restarts_3d: var%bc and var%bc_r4 are both intialized,"//&
3376  "only one should be associated per type")
3377  else
3378  call mpp_error(fatal, "CT_register_restarts_3d: var%bc and var%bc_r4 are both unintialized,"//&
3379  "one should be associated per type to register restart fields")
3380  endif
3381  endif
3382  endif
3383 
3384  ocn_rest = .true.
3385  if (present(ocean_restart)) ocn_rest = ocean_restart
3386 
3387  if (present(directory)) dir = trim(directory)
3388 
3389  if (to_read) then
3390  io_type = "read"
3391  if (.not. present(directory)) dir = "INPUT/"
3392  else
3393  io_type = "overwrite"
3394  if (.not. present(directory)) dir = "RESTART/"
3395  endif
3396 
3397  nz = var%ke - var%ks + 1 !< NOTE: This assumes that the z dimension is the same for every variable
3398  num_rest_files = 0
3399 
3400  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3401  ! Determine the number and names of the restart files
3402  do n = 1, var%num_bcs
3403  if (var%bc(n)%num_fields <= 0) cycle
3404  file_nm = trim(var%bc(n)%ice_restart_file)
3405  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
3406  do f = 1, num_rest_files
3407  if (trim(file_nm) == trim(rest_file_names(f))) exit
3408  enddo
3409  if (f>num_rest_files) then
3410  num_rest_files = num_rest_files + 1
3411  rest_file_names(f) = trim(file_nm)
3412  endif
3413  enddo
3414 
3415  if (num_rest_files == 0) return
3416 
3417  allocate(bc_rest_files(num_rest_files))
3418 
3419  !< Open the files
3420  do n = 1, num_rest_files
3421  file_is_open(n) = open_file(bc_rest_files(n), trim(dir)//rest_file_names(n), io_type, mpp_domain, &
3422  & is_restart=.true.)
3423  if (file_is_open(n)) then
3424 
3425  if (to_read) then
3426  call register_axis_wrapper(bc_rest_files(n), to_read=to_read)
3427  else
3428  call register_axis_wrapper(bc_rest_files(n), to_read=to_read, nz=nz)
3429  endif
3430  endif
3431  enddo
3432 
3433  ! Register the fields with the restart files
3434  do n = 1, var%num_bcs
3435  if (var%bc(n)%num_fields <= 0) cycle
3436 
3437  file_nm = trim(var%bc(n)%ice_restart_file)
3438  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
3439  do f = 1, num_rest_files
3440  if (trim(file_nm) == trim(rest_file_names(f))) exit
3441  enddo
3442 
3443  var%bc(n)%fms2_io_rest_type => bc_rest_files(f)
3444 
3445  do m = 1, var%bc(n)%num_fields
3446  if (file_is_open(f)) then
3447  if( to_read .and. variable_exists(bc_rest_files(f), var%bc(n)%field(m)%name)) then
3448  !< If reading get the dimension names from the file
3449  allocate(dim_names(get_variable_num_dimensions(bc_rest_files(f), var%bc(n)%field(m)%name)))
3450  call get_variable_dimension_names(bc_rest_files(f), &
3451  & var%bc(n)%field(m)%name, dim_names)
3452  else
3453  !< If writing use dummy dimension names
3454  allocate(dim_names(4))
3455  dim_names(1) = "xaxis_1"
3456  dim_names(2) = "yaxis_1"
3457  dim_names(3) = "zaxis_1"
3458  dim_names(4) = "Time"
3459  endif !< to_read
3460 
3461  call register_restart_field(bc_rest_files(f),&
3462  & var%bc(n)%field(m)%name, var%bc(n)%field(m)%values, dim_names, &
3463  & is_optional=var%bc(n)%field(m)%may_init )
3464  deallocate(dim_names)
3465  endif !< If file_is_open
3466  enddo !< num_fields
3467  enddo !< num_bcs
3468  else if(associated(var%bc_r4)) then
3469  ! Determine the number and names of the restart files
3470  do n = 1, var%num_bcs
3471  if (var%bc_r4(n)%num_fields <= 0) cycle
3472  file_nm = trim(var%bc_r4(n)%ice_restart_file)
3473  if (ocn_rest) file_nm = trim(var%bc_r4(n)%ocean_restart_file)
3474  do f = 1, num_rest_files
3475  if (trim(file_nm) == trim(rest_file_names(f))) exit
3476  enddo
3477  if (f>num_rest_files) then
3478  num_rest_files = num_rest_files + 1
3479  rest_file_names(f) = trim(file_nm)
3480  endif
3481  enddo
3482 
3483  if (num_rest_files == 0) return
3484 
3485  allocate(bc_rest_files(num_rest_files))
3486 
3487  !< Open the files
3488  do n = 1, num_rest_files
3489  file_is_open(n) = open_file(bc_rest_files(n), trim(dir)//rest_file_names(n), io_type, mpp_domain, &
3490  & is_restart=.true.)
3491  if (file_is_open(n)) then
3492 
3493  if (to_read) then
3494  call register_axis_wrapper(bc_rest_files(n), to_read=to_read)
3495  else
3496  call register_axis_wrapper(bc_rest_files(n), to_read=to_read, nz=nz)
3497  endif
3498  endif
3499  enddo
3500 
3501  ! Register the fields with the restart files
3502  do n = 1, var%num_bcs
3503  if (var%bc_r4(n)%num_fields <= 0) cycle
3504 
3505  file_nm = trim(var%bc_r4(n)%ice_restart_file)
3506  if (ocn_rest) file_nm = trim(var%bc_r4(n)%ocean_restart_file)
3507  do f = 1, num_rest_files
3508  if (trim(file_nm) == trim(rest_file_names(f))) exit
3509  enddo
3510 
3511  var%bc_r4(n)%fms2_io_rest_type => bc_rest_files(f)
3512 
3513  do m = 1, var%bc_r4(n)%num_fields
3514  if (file_is_open(f)) then
3515  if( to_read .and. variable_exists(bc_rest_files(f), var%bc_r4(n)%field(m)%name)) then
3516  !< If reading get the dimension names from the file
3517  allocate(dim_names(get_variable_num_dimensions(bc_rest_files(f), var%bc_r4(n)%field(m)%name)))
3518  call get_variable_dimension_names(bc_rest_files(f), &
3519  & var%bc_r4(n)%field(m)%name, dim_names)
3520  else
3521  !< If writing use dummy dimension names
3522  allocate(dim_names(4))
3523  dim_names(1) = "xaxis_1"
3524  dim_names(2) = "yaxis_1"
3525  dim_names(3) = "zaxis_1"
3526  dim_names(4) = "Time"
3527  endif !< to_read
3528 
3529  call register_restart_field(bc_rest_files(f),&
3530  & var%bc_r4(n)%field(m)%name, var%bc_r4(n)%field(m)%values, dim_names, &
3531  & is_optional=var%bc_r4(n)%field(m)%may_init )
3532  deallocate(dim_names)
3533  endif !< If file_is_open
3534  enddo !< num_fields
3535  enddo !< num_bcs
3536  else
3537  call mpp_error(fatal, "CT_register_restarts_2d: passed in type has unassociated coupler_field_type"// &
3538  " pointers for both bc and bc_r4")
3539  endif
3540  end subroutine ct_register_restarts_3d
3541 
3542  subroutine ct_restore_state_2d(var, use_fms2_io, directory, all_or_nothing, all_required, test_by_field)
3543  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure to restore from restart files
3544  character(len=*), optional, intent(in) :: directory !< A directory where the restart files should
3545  !! be found. The default for FMS is 'INPUT'.
3546  logical, optional, intent(in) :: all_or_nothing !< If true and there are non-mandatory
3547  !! restart fields, it is still an error if some
3548  !! fields are read successfully but others are not.
3549  logical, optional, intent(in) :: all_required !< If true, all fields must be successfully
3550  !! read from the restart file, even if they were
3551  !! registered as not mandatory.
3552  logical, optional, intent(in) :: test_by_field !< If true, all or none of the variables
3553  !! in a single field must be read successfully.
3554  logical, intent(in) :: use_fms2_io !< This is just to distinguish the interfaces
3555 
3556  integer :: n, m, num_fld
3557  character(len=80) :: unset_varname
3558  logical :: any_set, all_set, all_var_set, any_var_set, var_set
3559 
3560  if(var%set .and. var%num_bcs .gt. 0) then
3561  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3562  if( associated(var%bc) ) then
3563  call mpp_error(fatal, "CT_restore_state_2d: var%bc and var%bc_r4 are both initialized," // &
3564  "only one should be associated per type")
3565  else
3566  call mpp_error(fatal, "CT_restore_state_2d: var%bc and var%bc_r4 are both uninitialized," // &
3567  "one should be associated per type to restore state from restart")
3568  endif
3569  endif
3570  endif
3571 
3572  any_set = .false.
3573  all_set = .true.
3574  num_fld = 0
3575  unset_varname = ""
3576 
3577  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3578  do n = 1, var%num_bcs
3579  any_var_set = .false.
3580  all_var_set = .true.
3581  do m = 1, var%bc(n)%num_fields
3582  var_set = .false.
3583  if (check_if_open(var%bc(n)%fms2_io_rest_type)) then
3584  var_set = variable_exists(var%bc(n)%fms2_io_rest_type, var%bc(n)%field(m)%name)
3585  endif
3586 
3587  if (.not.var_set) unset_varname = trim(var%bc(n)%field(m)%name)
3588  if (var_set) any_set = .true.
3589  if (all_set) all_set = var_set
3590  if (var_set) any_var_set = .true.
3591  if (all_var_set) all_var_set = var_set
3592  enddo
3593 
3594  num_fld = num_fld + var%bc(n)%num_fields
3595  if ((var%bc(n)%num_fields > 0) .and. present(test_by_field)) then
3596  if (test_by_field .and. (all_var_set .neqv. any_var_set)) call mpp_error(fatal,&
3597  & "CT_restore_state_2d: test_by_field is true, and "//&
3598  & trim(unset_varname)//" was not read but some other fields in "//&
3599  & trim(trim(var%bc(n)%name))//" were.")
3600  endif
3601  enddo
3602  else if(associated(var%bc_r4)) then
3603  do n = 1, var%num_bcs
3604  any_var_set = .false.
3605  all_var_set = .true.
3606  do m = 1, var%bc_r4(n)%num_fields
3607  var_set = .false.
3608  if (check_if_open(var%bc_r4(n)%fms2_io_rest_type)) then
3609  var_set = variable_exists(var%bc_r4(n)%fms2_io_rest_type, var%bc_r4(n)%field(m)%name)
3610  endif
3611 
3612  if (.not.var_set) unset_varname = trim(var%bc_r4(n)%field(m)%name)
3613  if (var_set) any_set = .true.
3614  if (all_set) all_set = var_set
3615  if (var_set) any_var_set = .true.
3616  if (all_var_set) all_var_set = var_set
3617  enddo
3618 
3619  num_fld = num_fld + var%bc_r4(n)%num_fields
3620  if ((var%bc_r4(n)%num_fields > 0) .and. present(test_by_field)) then
3621  if (test_by_field .and. (all_var_set .neqv. any_var_set)) call mpp_error(fatal,&
3622  & "CT_restore_state_2d: test_by_field is true, and "//&
3623  & trim(unset_varname)//" was not read but some other fields in "//&
3624  & trim(trim(var%bc_r4(n)%name))//" were.")
3625  endif
3626  enddo
3627  else
3628  call mpp_error(fatal, "CT_restore_state_2d: passed in type has unassociated coupler_field_type"// &
3629  " pointers for both bc and bc_r4")
3630  endif
3631 
3632  if ((num_fld > 0) .and. present(all_or_nothing)) then
3633  if (all_or_nothing .and. (all_set .neqv. any_set)) call mpp_error(fatal,&
3634  & "CT_restore_state_2d: all_or_nothing is true, and "//&
3635  & trim(unset_varname)//" was not read but some other fields were.")
3636  endif
3637 
3638  if (present(all_required)) then
3639  if (all_required .and. .not.all_set) then
3640  call mpp_error(fatal, "CT_restore_state_2d: all_required is true, but "//&
3641  & trim(unset_varname)//" was not read from its restart file.")
3642  endif
3643  endif
3644  end subroutine ct_restore_state_2d
3645 
3646  !> @brief Read in fields from restart files into a coupler_3d_bc_type
3647  !!
3648  !! This subroutine reads in the fields in a coupler_3d_bc_type that have been saved in restart
3649  !! files.
3650  subroutine ct_restore_state_3d(var, use_fms2_io, directory, all_or_nothing, all_required, test_by_field)
3651  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure to restore from restart files
3652  character(len=*), optional, intent(in) :: directory !< A directory where the restart files should
3653  !! be found. The default for FMS is 'INPUT'.
3654  logical, intent(in) :: use_fms2_io !< This is just to distinguish the interfaces
3655  logical, optional, intent(in) :: all_or_nothing !< If true and there are non-mandatory
3656  !! restart fields, it is still an error if some
3657  !! fields are read successfully but others are not.
3658  logical, optional, intent(in) :: all_required !< If true, all fields must be successfully
3659  !! read from the restart file, even if they were
3660  !! registered as not mandatory.
3661  logical, optional, intent(in) :: test_by_field !< If true, all or none of the variables
3662  !! in a single field must be read successfully.
3663 
3664  integer :: n, m, num_fld
3665  character(len=80) :: unset_varname
3666  logical :: any_set, all_set, all_var_set, any_var_set, var_set
3667 
3668  if(var%set .and. var%num_bcs .gt. 0) then
3669  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3670  if( associated(var%bc) ) then
3671  call mpp_error(fatal, "CT_restore_state_3d: var%bc and var%bc_r4 are both initialized," // &
3672  "only one should be associated per type")
3673  else
3674  call mpp_error(fatal, "CT_restore_state_3d: var%bc and var%bc_r4 are both uninitialized," // &
3675  "one should be associated per type to restore state from restart")
3676  endif
3677  endif
3678  endif
3679 
3680  any_set = .false.
3681  all_set = .true.
3682  num_fld = 0
3683  unset_varname = ""
3684 
3685  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3686  do n = 1, var%num_bcs
3687  any_var_set = .false.
3688  all_var_set = .true.
3689  do m = 1, var%bc(n)%num_fields
3690  var_set = .false.
3691  if (check_if_open(var%bc(n)%fms2_io_rest_type)) then
3692  var_set = variable_exists(var%bc(n)%fms2_io_rest_type, var%bc(n)%field(m)%name)
3693  endif
3694 
3695  if (.not.var_set) unset_varname = trim(var%bc(n)%field(m)%name)
3696 
3697  if (var_set) any_set = .true.
3698  if (all_set) all_set = var_set
3699  if (var_set) any_var_set = .true.
3700  if (all_var_set) all_var_set = var_set
3701  enddo
3702 
3703  num_fld = num_fld + var%bc(n)%num_fields
3704  if ((var%bc(n)%num_fields > 0) .and. present(test_by_field)) then
3705  if (test_by_field .and. (all_var_set .neqv. any_var_set)) call mpp_error(fatal,&
3706  & "CT_restore_state_3d: test_by_field is true, and "//&
3707  & trim(unset_varname)//" was not read but some other fields in "//&
3708  & trim(trim(var%bc(n)%name))//" were.")
3709  endif
3710  enddo
3711  else if(associated(var%bc_r4)) then
3712  do n = 1, var%num_bcs
3713  any_var_set = .false.
3714  all_var_set = .true.
3715  do m = 1, var%bc_r4(n)%num_fields
3716  var_set = .false.
3717  if (check_if_open(var%bc_r4(n)%fms2_io_rest_type)) then
3718  var_set = variable_exists(var%bc_r4(n)%fms2_io_rest_type, var%bc_r4(n)%field(m)%name)
3719  endif
3720 
3721  if (.not.var_set) unset_varname = trim(var%bc_r4(n)%field(m)%name)
3722 
3723  if (var_set) any_set = .true.
3724  if (all_set) all_set = var_set
3725  if (var_set) any_var_set = .true.
3726  if (all_var_set) all_var_set = var_set
3727  enddo
3728 
3729  num_fld = num_fld + var%bc_r4(n)%num_fields
3730  if ((var%bc_r4(n)%num_fields > 0) .and. present(test_by_field)) then
3731  if (test_by_field .and. (all_var_set .neqv. any_var_set)) call mpp_error(fatal,&
3732  & "CT_restore_state_3d: test_by_field is true, and "//&
3733  & trim(unset_varname)//" was not read but some other fields in "//&
3734  & trim(trim(var%bc(n)%name))//" were.")
3735  endif
3736  enddo
3737  else
3738  call mpp_error(fatal, "CT_restore_state_3d: passed in type has unassociated coupler_field_type"// &
3739  " pointers for both bc and bc_r4")
3740  endif
3741 
3742 
3743  if ((num_fld > 0) .and. present(all_or_nothing)) then
3744  if (all_or_nothing .and. (all_set .neqv. any_set)) call mpp_error(fatal,&
3745  & "CT_restore_state_3d: all_or_nothing is true, and "//&
3746  & trim(unset_varname)//" was not read but some other fields were.")
3747  endif
3748 
3749  if (present(all_required)) then
3750  if (all_required .and. .not.all_set) then
3751  call mpp_error(fatal, "CT_restore_state_3d: all_required is true, but "//&
3752  & trim(unset_varname)//" was not read from its restart file.")
3753  endif
3754  endif
3755  end subroutine ct_restore_state_3d
3756 
3757 
3758  !> @brief Potentially override the values in a coupler_2d_bc_type
3759  subroutine ct_data_override_2d(gridname, var, Time)
3760  character(len=3), intent(in) :: gridname !< 3-character long model grid ID
3761  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure to override
3762  type(time_type), intent(in) :: time !< The current model time
3763  integer :: m, n
3764 
3765  if(var%set .and. var%num_bcs .gt. 0) then
3766  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3767  if( associated(var%bc) ) then
3768  call mpp_error(fatal, "CT_data_override_2d: var%bc and var%bc_r4 are both initialized," // &
3769  "only one should be associated per type")
3770  else
3771  call mpp_error(fatal, "CT_data_override_2d: var%bc and var%bc_r4 are both uninitialized," // &
3772  "one should be associated per type to perform data override")
3773  endif
3774  endif
3775  endif
3776 
3777  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3778  do n = 1, var%num_bcs
3779  do m = 1, var%bc(n)%num_fields
3780  call data_override(gridname, var%bc(n)%field(m)%name, var%bc(n)%field(m)%values, time)
3781  enddo
3782  enddo
3783  else if(associated(var%bc_r4)) then
3784  do n = 1, var%num_bcs
3785  do m = 1, var%bc_r4(n)%num_fields
3786  call data_override(gridname, var%bc_r4(n)%field(m)%name, var%bc_r4(n)%field(m)%values, time)
3787  enddo
3788  enddo
3789  else
3790  call mpp_error(fatal, "CT_data_override_2d: passed in type has unassociated coupler_field_type"// &
3791  " pointers for both bc and bc_r4")
3792  endif
3793  end subroutine ct_data_override_2d
3794 
3795  !> @brief Potentially override the values in a coupler_3d_bc_type
3796  subroutine ct_data_override_3d(gridname, var, Time)
3797  character(len=3), intent(in) :: gridname !< 3-character long model grid ID
3798  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure to override
3799  type(time_type), intent(in) :: time !< The current model time
3800  !! TODO remove this when data_override is merged in
3801  real(r8_kind), allocatable :: r8_field_values(:,:,:)
3802 
3803  integer :: m, n
3804 
3805  if(var%set .and. var%num_bcs .gt. 0) then
3806  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3807  if( associated(var%bc) ) then
3808  call mpp_error(fatal, "CT_data_override_3d: var%bc and var%bc_r4 are both initialized," // &
3809  "only one should be associated per type")
3810  else
3811  call mpp_error(fatal, "CT_data_override_3d: var%bc and var%bc_r4 are both uninitialized," // &
3812  "one should be associated per type to perform data override")
3813  endif
3814  endif
3815  endif
3816 
3817  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3818  do n = 1, var%num_bcs
3819  do m = 1, var%bc(n)%num_fields
3820  call data_override(gridname, var%bc(n)%field(m)%name, var%bc(n)%field(m)%values, time)
3821  enddo
3822  enddo
3823  else if(associated(var%bc_r4)) then
3824  do n = 1, var%num_bcs
3825  do m = 1, var%bc_r4(n)%num_fields
3826  call data_override(gridname, var%bc_r4(n)%field(m)%name, var%bc_r4(n)%field(m)%values, time)
3827  enddo
3828  enddo
3829  else
3830  call mpp_error(fatal, "CT_data_override_3d: passed in type has unassociated coupler_field_type"// &
3831  " pointers for both bc and bc_r4")
3832  endif
3833 
3834  end subroutine ct_data_override_3d
3835 
3836 
3837  !> @brief Write out checksums for the elements of a coupler_2d_bc_type
3838  subroutine ct_write_chksums_2d(var, outunit, name_lead)
3839  type(coupler_2d_bc_type), intent(in) :: var !< BC_type structure for which to register diagnostics
3840  integer, intent(in) :: outunit !< The index of a open output file
3841  character(len=*), optional, intent(in) :: name_lead !< An optional prefix for the variable names
3842 
3843  character(len=120) :: var_name
3844  integer :: m, n
3845  integer(i8_kind) :: chks ! A checksum for the field
3846 
3847  if(var%set .and. var%num_bcs .gt. 0) then
3848  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3849  if( associated(var%bc) ) then
3850  call mpp_error(fatal, "CT_write_chksums_2d: var%bc and var%bc_r4 are both initialized," // &
3851  "only one should be associated per type")
3852  else
3853  call mpp_error(fatal, "CT_write_chksums_2d: var%bc and var%bc_r4 are both uninitialized," // &
3854  "one should be associated per type to write checksums for fields")
3855  endif
3856  endif
3857  endif
3858 
3859  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3860  do n = 1, var%num_bcs
3861  do m = 1, var%bc(n)%num_fields
3862  if (present(name_lead)) then
3863  var_name = trim(name_lead)//trim(var%bc(n)%field(m)%name)
3864  else
3865  var_name = trim(var%bc(n)%field(m)%name)
3866  endif
3867  chks = mpp_chksum(var%bc(n)%field(m)%values(var%isc:var%iec,var%jsc:var%jec))
3868  if(outunit.ne.0) write(outunit, '(" CHECKSUM:: ",A40," = ",Z20)') trim(var_name), chks
3869  enddo
3870  enddo
3871  else if(associated(var%bc_r4)) then
3872  do n = 1, var%num_bcs
3873  do m = 1, var%bc_r4(n)%num_fields
3874  if (present(name_lead)) then
3875  var_name = trim(name_lead)//trim(var%bc_r4(n)%field(m)%name)
3876  else
3877  var_name = trim(var%bc_r4(n)%field(m)%name)
3878  endif
3879  chks = mpp_chksum(var%bc_r4(n)%field(m)%values(var%isc:var%iec,var%jsc:var%jec))
3880  if(outunit.ne.0) write(outunit, '(" CHECKSUM:: ",A40," = ",Z20)') trim(var_name), chks
3881  enddo
3882  enddo
3883  else
3884  call mpp_error(fatal, "CT_write_chksums_2d: passed in type has unassociated coupler_field_type"// &
3885  " pointers for both bc and bc_r4")
3886  endif
3887  end subroutine ct_write_chksums_2d
3888 
3889  !> @brief Write out checksums for the elements of a coupler_3d_bc_type
3890  subroutine ct_write_chksums_3d(var, outunit, name_lead)
3891  type(coupler_3d_bc_type), intent(in) :: var !< BC_type structure for which to register diagnostics
3892  integer, intent(in) :: outunit !< The index of a open output file
3893  character(len=*), optional, intent(in) :: name_lead !< An optional prefix for the variable names
3894 
3895  character(len=120) :: var_name
3896  integer :: m, n
3897  integer(i8_kind) :: chks ! A checksum for the field
3898 
3899  if(var%set .and. var%num_bcs .gt. 0) then
3900  if(associated(var%bc) .eqv. associated(var%bc_r4)) then
3901  if( associated(var%bc) ) then
3902  call mpp_error(fatal, "CT_write_chksums_3d: var%bc and var%bc_r4 are both initialized," // &
3903  "only one should be associated per type")
3904  else
3905  call mpp_error(fatal, "CT_write_chksums_3d: var%bc and var%bc_r4 are both uninitialized," // &
3906  "one should be associated per type to write checksums for fields")
3907  endif
3908  endif
3909  endif
3910 
3911  if(associated(var%bc) .or. var%num_bcs .lt. 1) then
3912  do n = 1, var%num_bcs
3913  do m = 1, var%bc(n)%num_fields
3914  if (present(name_lead)) then
3915  var_name = trim(name_lead)//trim(var%bc(n)%field(m)%name)
3916  else
3917  var_name = trim(var%bc(n)%field(m)%name)
3918  endif
3919  chks = mpp_chksum(var%bc(n)%field(m)%values(var%isc:var%iec,var%jsc:var%jec,:))
3920  if(outunit.ne.0) write(outunit, '(" CHECKSUM:: ",A40," = ",Z20)') trim(var_name), chks
3921  enddo
3922  enddo
3923  else if(associated(var%bc_r4)) then
3924  do n = 1, var%num_bcs
3925  do m = 1, var%bc_r4(n)%num_fields
3926  if (present(name_lead)) then
3927  var_name = trim(name_lead)//trim(var%bc_r4(n)%field(m)%name)
3928  else
3929  var_name = trim(var%bc_r4(n)%field(m)%name)
3930  endif
3931  chks = mpp_chksum(var%bc_r4(n)%field(m)%values(var%isc:var%iec,var%jsc:var%jec,:))
3932  if(outunit.ne.0) write(outunit, '(" CHECKSUM:: ",A40," = ",Z20)') trim(var_name), chks
3933  enddo
3934  enddo
3935  else
3936  call mpp_error(fatal, "CT_write_chksums_2d: passed in type has unassociated coupler_field_type"// &
3937  " pointers for both bc and bc_r4")
3938  endif
3939 
3940  end subroutine ct_write_chksums_3d
3941 
3942  !> @brief Indicate whether a coupler_1d_bc_type has been initialized.
3943  !! @return Logical
3944  logical function ct_initialized_1d(var)
3945  type(coupler_1d_bc_type), intent(in) :: var !< BC_type structure to check initialization
3946 
3947  ct_initialized_1d = var%set
3948  end function ct_initialized_1d
3949 
3950  !> @brief Indicate whether a coupler_2d_bc_type has been initialized.
3951  !! @return Logical
3952  logical function ct_initialized_2d(var)
3953  type(coupler_2d_bc_type), intent(in) :: var !< BC_type structure to check initialization
3954 
3955  ct_initialized_2d = var%set
3956  end function ct_initialized_2d
3957 
3958  !> @brief Indicate whether a coupler_3d_bc_type has been initialized.
3959  !! @return Logical
3960  logical function ct_initialized_3d(var)
3961  type(coupler_3d_bc_type), intent(in) :: var !< BC_type structure to check initialization
3962 
3963  ct_initialized_3d = var%set
3964  end function ct_initialized_3d
3965 
3966  !> @brief Deallocate all data associated with a coupler_1d_bc_type
3967  subroutine ct_destructor_1d(var)
3968  type(coupler_1d_bc_type), intent(inout) :: var !< BC_type structure to be deconstructed
3969 
3970  integer :: m, n
3971 
3972  if (var%num_bcs > 0) then
3973  if(associated(var%bc)) then
3974  do n = 1, var%num_bcs
3975  do m = 1, var%bc(n)%num_fields
3976  deallocate ( var%bc(n)%field(m)%values )
3977  enddo
3978  deallocate ( var%bc(n)%field )
3979  enddo
3980  deallocate ( var%bc )
3981  else if(associated(var%bc_r4)) then
3982  do n = 1, var%num_bcs
3983  do m = 1, var%bc_r4(n)%num_fields
3984  deallocate ( var%bc_r4(n)%field(m)%values )
3985  enddo
3986  deallocate ( var%bc_r4(n)%field )
3987  enddo
3988  deallocate ( var%bc_r4 )
3989  else
3990  call mpp_error(fatal, "CT_destructor_1d: passed in type has unassociated coupler_field_type"// &
3991  " pointers for both bc and bc_r4")
3992  endif
3993  endif
3994 
3995  var%num_bcs = 0
3996  var%set = .false.
3997  end subroutine ct_destructor_1d
3998 
3999  !> @brief Deallocate all data associated with a coupler_2d_bc_type
4000  subroutine ct_destructor_2d(var)
4001  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure to be deconstructed
4002 
4003  integer :: m, n
4004 
4005  if (var%num_bcs > 0) then
4006  if(associated(var%bc)) then
4007  do n = 1, var%num_bcs
4008  do m = 1, var%bc(n)%num_fields
4009  deallocate ( var%bc(n)%field(m)%values )
4010  enddo
4011  deallocate ( var%bc(n)%field )
4012  enddo
4013  deallocate ( var%bc )
4014  else if(associated(var%bc_r4)) then
4015  do n = 1, var%num_bcs
4016  do m = 1, var%bc_r4(n)%num_fields
4017  deallocate ( var%bc_r4(n)%field(m)%values )
4018  enddo
4019  deallocate ( var%bc_r4(n)%field )
4020  enddo
4021  deallocate ( var%bc_r4 )
4022  else
4023  call mpp_error(fatal, "CT_destructor_2d: passed in type has unassociated coupler_field_type"// &
4024  " pointers for both bc and bc_r4")
4025  endif
4026  endif
4027 
4028  var%num_bcs = 0
4029  var%set = .false.
4030  end subroutine ct_destructor_2d
4031 
4032  !> @brief Deallocate all data associated with a coupler_3d_bc_type
4033  subroutine ct_destructor_3d(var)
4034  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure to be deconstructed
4035 
4036  integer :: m, n
4037 
4038  if (var%num_bcs > 0) then
4039  if(associated(var%bc)) then
4040  do n = 1, var%num_bcs
4041  do m = 1, var%bc(n)%num_fields
4042  deallocate ( var%bc(n)%field(m)%values )
4043  enddo
4044  deallocate ( var%bc(n)%field )
4045  enddo
4046  deallocate ( var%bc )
4047  else if(associated(var%bc_r4)) then
4048  do n = 1, var%num_bcs
4049  do m = 1, var%bc_r4(n)%num_fields
4050  deallocate ( var%bc_r4(n)%field(m)%values )
4051  enddo
4052  deallocate ( var%bc_r4(n)%field )
4053  enddo
4054  deallocate ( var%bc_r4 )
4055  else
4056  call mpp_error(fatal, "CT_destructor_3d: passed in type has unassociated coupler_field_type"// &
4057  " pointers for both bc and bc_r4")
4058  endif
4059 
4060  endif
4061 
4062  var%num_bcs = 0
4063  var%set = .false.
4064  end subroutine ct_destructor_3d
4065 
4066 #include "coupler_types_r4.fh"
4067 #include "coupler_types_r8.fh"
4068 
4069  !! @brief Register the fields in a coupler_2d_bc_type to be saved in restart files
4070  !!
4071  !! This subroutine registers the fields in a coupler_2d_bc_type to be saved in restart files
4072  !! specified in the field table.
4073 #ifdef use_deprecated_io
4074  subroutine mpp_io_ct_register_restarts_2d(var, bc_rest_files, num_rest_files, mpp_domain, ocean_restart)
4075  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure to be registered for restarts
4076  type(restart_file_type), dimension(:), pointer :: bc_rest_files !< Structures describing the restart files
4077  integer, intent(out) :: num_rest_files !< The number of restart files to use
4078  type(domain2d), intent(in) :: mpp_domain !< The FMS domain to use for this registration call
4079  logical, optional, intent(in) :: ocean_restart !< If true, use the ocean restart file name.
4080 
4081  character(len=80), dimension(max(1,var%num_bcs)) :: rest_file_names
4082  character(len=80) :: file_nm
4083  logical :: ocn_rest
4084  integer :: f, n, m
4085 
4086  ocn_rest = .true.
4087  if (present(ocean_restart)) ocn_rest = ocean_restart
4088 
4089  ! Determine the number and names of the restart files
4090  num_rest_files = 0
4091  do n = 1, var%num_bcs
4092  if (var%bc(n)%num_fields <= 0) cycle
4093  file_nm = trim(var%bc(n)%ice_restart_file)
4094  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
4095  do f = 1, num_rest_files
4096  if (trim(file_nm) == trim(rest_file_names(f))) exit
4097  enddo
4098  if (f>num_rest_files) then
4099  num_rest_files = num_rest_files + 1
4100  rest_file_names(f) = trim(file_nm)
4101  endif
4102  enddo
4103 
4104  if (num_rest_files == 0) return
4105 
4106  ! Register the fields with the restart files
4107  allocate(bc_rest_files(num_rest_files))
4108  do n = 1, var%num_bcs
4109  if (var%bc(n)%num_fields <= 0) cycle
4110 
4111  file_nm = trim(var%bc(n)%ice_restart_file)
4112  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
4113  do f = 1, num_rest_files
4114  if (trim(file_nm) == trim(rest_file_names(f))) exit
4115  enddo
4116 
4117  var%bc(n)%rest_type => bc_rest_files(f)
4118  do m = 1, var%bc(n)%num_fields
4119  var%bc(n)%field(m)%id_rest = fms_io_register_restart_field(bc_rest_files(f),&
4120  & rest_file_names(f), var%bc(n)%field(m)%name, var%bc(n)%field(m)%values,&
4121  & mpp_domain, mandatory=.not.var%bc(n)%field(m)%may_init )
4122  enddo
4123  enddo
4124  end subroutine mpp_io_ct_register_restarts_2d
4125 
4126  !! @brief Register the fields in a coupler_2d_bc_type to be saved to restart files
4127  !!
4128  !! This subroutine registers the fields in a coupler_2d_bc_type to be saved in the specified
4129  !! restart file.
4130  subroutine mpp_io_ct_register_restarts_to_file_2d(var, file_name, rest_file, mpp_domain, varname_prefix)
4131  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure to be registered for restarts
4132  character(len=*), intent(in) :: file_name !< The name of the restart file
4133  type(restart_file_type), pointer :: rest_file !< A (possibly associated) structure describing
4134  !! the restart file
4135  type(domain2d), intent(in) :: mpp_domain !< The FMS domain to use for this registration call
4136  character(len=*), optional, intent(in) :: varname_prefix !< A prefix for the variable name
4137  !! in the restart file, intended to allow
4138  !! multiple BC_type variables to use the
4139  !! same restart files.
4140 
4141  character(len=128) :: var_name
4142  integer :: n, m
4143 
4144  ! Register the fields with the restart file
4145  if (.not.associated(rest_file)) allocate(rest_file)
4146  do n = 1, var%num_bcs
4147  if (var%bc(n)%num_fields <= 0) cycle
4148 
4149  var%bc(n)%rest_type => rest_file
4150  do m = 1, var%bc(n)%num_fields
4151  var_name = trim(var%bc(n)%field(m)%name)
4152  if (present(varname_prefix)) var_name = trim(varname_prefix)//trim(var_name)
4153  var%bc(n)%field(m)%id_rest = fms_io_register_restart_field(rest_file,&
4154  & file_name, var_name, var%bc(n)%field(m)%values,&
4155  & mpp_domain, mandatory=.not.var%bc(n)%field(m)%may_init )
4156  enddo
4157  enddo
4158  end subroutine mpp_io_ct_register_restarts_to_file_2d
4159 
4160  !! @brief Register the fields in a coupler_3d_bc_type to be saved to restart files
4161  !!
4162  !! This subroutine registers the fields in a coupler_3d_bc_type to be saved in restart files
4163  !! specified in the field table.
4164  subroutine mpp_io_ct_register_restarts_3d(var, bc_rest_files, num_rest_files, mpp_domain, ocean_restart)
4165  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure to be registered for restarts
4166  type(restart_file_type), dimension(:), pointer :: bc_rest_files !< Structures describing the restart files
4167  integer, intent(out) :: num_rest_files !< The number of restart files to use
4168  type(domain2d), intent(in) :: mpp_domain !< The FMS domain to use for this registration call
4169  logical, optional, intent(in) :: ocean_restart !< If true, use the ocean restart file name.
4170 
4171  character(len=80), dimension(max(1,var%num_bcs)) :: rest_file_names
4172  character(len=80) :: file_nm
4173  logical :: ocn_rest
4174  integer :: f, n, m
4175 
4176  ocn_rest = .true.
4177  if (present(ocean_restart)) ocn_rest = ocean_restart
4178 
4179  ! Determine the number and names of the restart files
4180  num_rest_files = 0
4181  do n = 1, var%num_bcs
4182  if (var%bc(n)%num_fields <= 0) cycle
4183  file_nm = trim(var%bc(n)%ice_restart_file)
4184  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
4185  do f = 1, num_rest_files
4186  if (trim(file_nm) == trim(rest_file_names(f))) exit
4187  enddo
4188  if (f>num_rest_files) then
4189  num_rest_files = num_rest_files + 1
4190  rest_file_names(f) = trim(file_nm)
4191  endif
4192  enddo
4193 
4194  if (num_rest_files == 0) return
4195 
4196  ! Register the fields with the restart files
4197  allocate(bc_rest_files(num_rest_files))
4198  do n = 1, var%num_bcs
4199  if (var%bc(n)%num_fields <= 0) cycle
4200  file_nm = trim(var%bc(n)%ice_restart_file)
4201  if (ocn_rest) file_nm = trim(var%bc(n)%ocean_restart_file)
4202  do f = 1, num_rest_files
4203  if (trim(file_nm) == trim(rest_file_names(f))) exit
4204  enddo
4205 
4206  var%bc(n)%rest_type => bc_rest_files(f)
4207  do m = 1, var%bc(n)%num_fields
4208  var%bc(n)%field(m)%id_rest = fms_io_register_restart_field(bc_rest_files(f),&
4209  & rest_file_names(f), var%bc(n)%field(m)%name, var%bc(n)%field(m)%values,&
4210  & mpp_domain, mandatory=.not.var%bc(n)%field(m)%may_init )
4211  enddo
4212  enddo
4213  end subroutine mpp_io_ct_register_restarts_3d
4214 
4215  !> @brief Register the fields in a coupler_3d_bc_type to be saved to restart files
4216  !!
4217  !! Registers the fields in a coupler_3d_bc_type to be saved in the specified restart file.
4218  subroutine mpp_io_ct_register_restarts_to_file_3d(var, file_name, rest_file, mpp_domain, varname_prefix)
4219  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure to be registered for restarts
4220  character(len=*), intent(in) :: file_name !< The name of the restart file
4221  type(restart_file_type), pointer :: rest_file !< A (possibly associated) structure describing the restart file
4222  type(domain2d), intent(in) :: mpp_domain !< The FMS domain to use for this registration call
4223  character(len=*), optional, intent(in) :: varname_prefix !< A prefix for the variable name
4224  !! in the restart file, intended to allow
4225  !! multiple BC_type variables to use the
4226  !! same restart files.
4227 
4228  character(len=128) :: var_name
4229  integer :: n, m
4230 
4231  ! Register the fields with the restart file
4232  if (.not.associated(rest_file)) allocate(rest_file)
4233  do n = 1, var%num_bcs
4234  if (var%bc(n)%num_fields <= 0) cycle
4235 
4236  var%bc(n)%rest_type => rest_file
4237  do m = 1, var%bc(n)%num_fields
4238  var_name = trim(var%bc(n)%field(m)%name)
4239  if (present(varname_prefix)) var_name = trim(varname_prefix)//trim(var_name)
4240  var%bc(n)%field(m)%id_rest = fms_io_register_restart_field(rest_file,&
4241  & file_name, var_name, var%bc(n)%field(m)%values,&
4242  & mpp_domain, mandatory=.not.var%bc(n)%field(m)%may_init )
4243  enddo
4244  enddo
4245  end subroutine mpp_io_ct_register_restarts_to_file_3d
4246 
4247  !> @brief Reads in fields from restart files into a coupler_2d_bc_type
4248  !!
4249  !! This subroutine reads in the fields in a coupler_2d_bc_type that have been saved in restart
4250  !! files.
4251  subroutine mpp_io_ct_restore_state_2d(var, directory, all_or_nothing, all_required, test_by_field)
4252  type(coupler_2d_bc_type), intent(inout) :: var !< BC_type structure to restore from restart files
4253  character(len=*), optional, intent(in) :: directory !< A directory where the restart files should
4254  !! be found. The default for FMS is 'INPUT'.
4255  logical, optional, intent(in) :: all_or_nothing !< If true and there are non-mandatory
4256  !! restart fields, it is still an error if some
4257  !! fields are read successfully but others are not.
4258  logical, optional, intent(in) :: all_required !< If true, all fields must be successfully
4259  !! read from the restart file, even if they were
4260  !! registered as not mandatory.
4261  logical, optional, intent(in) :: test_by_field !< If true, all or none of the variables
4262  !! in a single field must be read successfully.
4263 
4264  integer :: n, m, num_fld
4265  character(len=80) :: unset_varname
4266  logical :: any_set, all_set, all_var_set, any_var_set, var_set
4267 
4268  any_set = .false.
4269  all_set = .true.
4270  num_fld = 0
4271  unset_varname = ""
4272 
4273  do n = 1, var%num_bcs
4274  any_var_set = .false.
4275  all_var_set = .true.
4276  do m = 1, var%bc(n)%num_fields
4277  var_set = .false.
4278  if (var%bc(n)%field(m)%id_rest > 0) then
4279  var_set = query_initialized(var%bc(n)%rest_type, var%bc(n)%field(m)%id_rest)
4280  if (.not.var_set) then
4281  call restore_state(var%bc(n)%rest_type, var%bc(n)%field(m)%id_rest,&
4282  & directory=directory, nonfatal_missing_files=.true.)
4283  var_set = query_initialized(var%bc(n)%rest_type, var%bc(n)%field(m)%id_rest)
4284  endif
4285  endif
4286 
4287  if (.not.var_set) unset_varname = trim(var%bc(n)%field(m)%name)
4288  if (var_set) any_set = .true.
4289  if (all_set) all_set = var_set
4290  if (var_set) any_var_set = .true.
4291  if (all_var_set) all_var_set = var_set
4292  enddo
4293 
4294  num_fld = num_fld + var%bc(n)%num_fields
4295  if ((var%bc(n)%num_fields > 0) .and. present(test_by_field)) then
4296  if (test_by_field .and. (all_var_set .neqv. any_var_set)) call mpp_error(fatal,&
4297  & "mpp_io_CT_restore_state_2d: test_by_field is true, and "//&
4298  & trim(unset_varname)//" was not read but some other fields in "//&
4299  & trim(trim(var%bc(n)%name))//" were.")
4300  endif
4301  enddo
4302 
4303  if ((num_fld > 0) .and. present(all_or_nothing)) then
4304  if (all_or_nothing .and. (all_set .neqv. any_set)) call mpp_error(fatal,&
4305  & "mpp_io_CT_restore_state_2d: all_or_nothing is true, and "//&
4306  & trim(unset_varname)//" was not read but some other fields were.")
4307  endif
4308 
4309  if (present(all_required)) then
4310  if (all_required .and. .not.all_set) then
4311  call mpp_error(fatal, "mpp_io_CT_restore_state_2d: all_required is true, but "//&
4312  & trim(unset_varname)//" was not read from its restart file.")
4313  endif
4314  endif
4315  end subroutine mpp_io_ct_restore_state_2d
4316 
4317  !> @brief Read in fields from restart files into a coupler_3d_bc_type
4318  !!
4319  !! This subroutine reads in the fields in a coupler_3d_bc_type that have been saved in restart
4320  !! files.
4321  subroutine mpp_io_ct_restore_state_3d(var, directory, all_or_nothing, all_required, test_by_field)
4322  type(coupler_3d_bc_type), intent(inout) :: var !< BC_type structure to restore from restart files
4323  character(len=*), optional, intent(in) :: directory !< A directory where the restart files should
4324  !! be found. The default for FMS is 'INPUT'.
4325  logical, optional, intent(in) :: all_or_nothing !< If true and there are non-mandatory
4326  !! restart fields, it is still an error if some
4327  !! fields are read successfully but others are not.
4328  logical, optional, intent(in) :: all_required !< If true, all fields must be successfully
4329  !! read from the restart file, even if they were
4330  !! registered as not mandatory.
4331  logical, optional, intent(in) :: test_by_field !< If true, all or none of the variables
4332  !! in a single field must be read successfully.
4333 
4334  integer :: n, m, num_fld
4335  character(len=80) :: unset_varname
4336  logical :: any_set, all_set, all_var_set, any_var_set, var_set
4337 
4338  any_set = .false.
4339  all_set = .true.
4340  num_fld = 0
4341  unset_varname = ""
4342 
4343  do n = 1, var%num_bcs
4344  any_var_set = .false.
4345  all_var_set = .true.
4346  do m = 1, var%bc(n)%num_fields
4347  var_set = .false.
4348  if (var%bc(n)%field(m)%id_rest > 0) then
4349  var_set = query_initialized(var%bc(n)%rest_type, var%bc(n)%field(m)%id_rest)
4350  if (.not.var_set) then
4351  call restore_state(var%bc(n)%rest_type, var%bc(n)%field(m)%id_rest,&
4352  & directory=directory, nonfatal_missing_files=.true.)
4353  var_set = query_initialized(var%bc(n)%rest_type, var%bc(n)%field(m)%id_rest)
4354  endif
4355  endif
4356 
4357  if (.not.var_set) unset_varname = trim(var%bc(n)%field(m)%name)
4358 
4359  if (var_set) any_set = .true.
4360  if (all_set) all_set = var_set
4361  if (var_set) any_var_set = .true.
4362  if (all_var_set) all_var_set = var_set
4363  enddo
4364 
4365  num_fld = num_fld + var%bc(n)%num_fields
4366  if ((var%bc(n)%num_fields > 0) .and. present(test_by_field)) then
4367  if (test_by_field .and. (all_var_set .neqv. any_var_set)) call mpp_error(fatal,&
4368  & "mpp_io_CT_restore_state_3d: test_by_field is true, and "//&
4369  & trim(unset_varname)//" was not read but some other fields in "//&
4370  & trim(trim(var%bc(n)%name))//" were.")
4371  endif
4372  enddo
4373 
4374  if ((num_fld > 0) .and. present(all_or_nothing)) then
4375  if (all_or_nothing .and. (all_set .neqv. any_set)) call mpp_error(fatal,&
4376  & "mpp_io_CT_restore_state_3d: all_or_nothing is true, and "//&
4377  & trim(unset_varname)//" was not read but some other fields were.")
4378  endif
4379 
4380  if (present(all_required)) then
4381  if (all_required .and. .not.all_set) then
4382  call mpp_error(fatal, "mpp_io_CT_restore_state_3d: all_required is true, but "//&
4383  & trim(unset_varname)//" was not read from its restart file.")
4384  endif
4385  endif
4386  end subroutine mpp_io_ct_restore_state_3d
4387 #endif
4388 end module coupler_types_mod
4389 !> @}
4390 ! close documentation grouping
subroutine ct_redistribute_data_2d(var_in, domain_in, var_out, domain_out, complete)
Redistribute the data in all elements of a coupler_2d_bc_type.
integer, public ind_kw
The index for the piston velocity.
subroutine ct_data_override_2d(gridname, var, Time)
Potentially override the values in a coupler_2d_bc_type.
subroutine register_axis_wrapper_write(fileobj, nz)
integer, public ind_u10
The index of the 10 m wind speed.
subroutine coupler_type_copy_2d_2d(var_in, var_out, is, ie, js, je, diag_name, axes, time, suffix)
Copy fields from one coupler type to another. 2-D to 2-D version for generic coupler_type_copy.
subroutine ct_spawn_3d_2d(var_in, var, idim, jdim, suffix, as_needed)
Generate one coupler type using another as a template. 3-D to 2-D version for generic CT_spawn.
integer, public ind_runoff
The index for a runoff flux.
integer, public ind_csurf
The index of the ocean surface concentration.
subroutine, public coupler_types_init
Initialize the coupler types.
subroutine ct_spawn_3d_3d(var_in, var, idim, jdim, kdim, suffix, as_needed)
Generate one coupler type using another as a template. 3-D to 3-D version for generic CT_spawn.
logical function ct_initialized_2d(var)
Indicate whether a coupler_2d_bc_type has been initialized.
subroutine ct_register_restarts_2d(var, bc_rest_files, num_rest_files, mpp_domain, to_read, ocean_restart, directory)
subroutine ct_send_data_3d(var, Time, return_statuses)
Write out all diagnostics of elements of a coupler_3d_bc_type.
integer, public ind_sc_no
The index for the Schmidt number for a tracer flux.
integer, public ind_deposition
The index for the atmospheric deposition flux.
subroutine ct_destructor_1d(var)
Deallocate all data associated with a coupler_1d_bc_type.
subroutine ct_destructor_2d(var)
Deallocate all data associated with a coupler_2d_bc_type.
subroutine ct_spawn_2d_2d(var_in, var, idim, jdim, suffix, as_needed)
Generate one coupler type using another as a template. 2-D to 2-D version for generic CT_spawn.
subroutine ct_write_chksums_2d(var, outunit, name_lead)
Write out checksums for the elements of a coupler_2d_bc_type.
subroutine ct_increment_data_3d_3d(var_in, var, halo_size, bc_index, field_index, scale_factor, scale_prev, exclude_flux_type, only_flux_type, pass_through_ice)
Increment data in all elements of one coupler_3d_bc_type.
subroutine ct_restore_state_2d(var, use_fms2_io, directory, all_or_nothing, all_required, test_by_field)
subroutine ct_set_diags_3d(var, diag_name, axes, time)
Register the diagnostics of a coupler_3d_bc_type.
subroutine register_axis_wrapper(fileobj, to_read, nz)
subroutine, public coupler_type_copy_1d_3d(var_in, var_out, is, ie, js, je, kd, diag_name, axes, time, suffix)
Copy fields from one coupler type to another. 1-D to 3-D version for generic coupler_type_copy.
subroutine coupler_type_copy_3d_3d(var_in, var_out, is, ie, js, je, kd, diag_name, axes, time, suffix)
Copy fields from one coupler type to another. 3-D to 3-D version for generic coupler_type_copy.
subroutine ct_redistribute_data_3d(var_in, domain_in, var_out, domain_out, complete)
Redistributes the data in all elements of one coupler_2d_bc_type.
subroutine ct_spawn_1d_3d(var_in, var, idim, jdim, kdim, suffix, as_needed)
Generate one coupler type using another as a template. 1-D to 3-D version for generic CT_spawn.
subroutine ct_copy_data_3d(var_in, var, halo_size, bc_index, field_index, exclude_flux_type, only_flux_type, pass_through_ice)
Copy all elements of coupler_3d_bc_type.
integer, public ind_alpha
The index of the solubility array for a tracer.
subroutine ct_set_diags_2d(var, diag_name, axes, time)
logical function ct_initialized_1d(var)
Indicate whether a coupler_1d_bc_type has been initialized.
subroutine ct_copy_data_2d(var_in, var, halo_size, bc_index, field_index, exclude_flux_type, only_flux_type, pass_through_ice)
Copy all elements of coupler_2d_bc_type. Do a direct copy of the data in all elements of one coupler_...
subroutine ct_spawn_2d_3d(var_in, var, idim, jdim, kdim, suffix, as_needed)
Generate one coupler type using another as a template. 2-D to 3-D version for generic CT_spawn.
subroutine ct_write_chksums_3d(var, outunit, name_lead)
Write out checksums for the elements of a coupler_3d_bc_type.
subroutine ct_spawn_1d_2d(var_in, var, idim, jdim, suffix, as_needed)
Generate one coupler type using another as a template. 1-D to 2-D version for generic coupler_type_sp...
subroutine coupler_type_copy_3d_2d(var_in, var_out, is, ie, js, je, diag_name, axes, time, suffix)
Copy fields from one coupler type to another. 3-D to 2-D version for generic coupler_type_copy.
integer, public ind_flux
The index for the tracer flux.
subroutine ct_destructor_3d(var)
Deallocate all data associated with a coupler_3d_bc_type.
integer, public ind_pcair
The index of the atmospheric concentration.
subroutine coupler_type_copy_2d_3d(var_in, var_out, is, ie, js, je, kd, diag_name, axes, time, suffix)
Copy fields from one coupler type to another. 2-D to 3-D version for generic coupler_type_copy.
integer, public ind_flux0
The index for the piston velocity.
subroutine ct_copy_data_2d_3d(var_in, var, halo_size, bc_index, field_index, exclude_flux_type, only_flux_type, pass_through_ice, ind3_start, ind3_end)
Copy all elements of coupler_2d_bc_type to coupler_3d_bc_type.
integer, public ind_psurf
The index of the surface atmospheric pressure.
subroutine ct_register_restarts_3d(var, bc_rest_files, num_rest_files, mpp_domain, to_read, ocean_restart, directory)
subroutine ct_increment_data_2d_2d(var_in, var, halo_size, bc_index, field_index, scale_factor, scale_prev, exclude_flux_type, only_flux_type, pass_through_ice)
Increment data in all elements of one coupler_2d_bc_type.
subroutine ct_data_override_3d(gridname, var, Time)
Potentially override the values in a coupler_3d_bc_type.
subroutine, public coupler_type_copy_1d_2d(var_in, var_out, is, ie, js, je, diag_name, axes, time, suffix)
Copy fields from one coupler type to another. 1-D to 2-D version for generic coupler_type_copy.
subroutine register_axis_wrapper_read(fileobj)
subroutine ct_restore_state_3d(var, use_fms2_io, directory, all_or_nothing, all_required, test_by_field)
Read in fields from restart files into a coupler_3d_bc_type.
integer, public ind_deltap
The index for ocean-air gas partial pressure change.
logical function ct_initialized_3d(var)
Indicate whether a coupler_3d_bc_type has been initialized.
subroutine ct_send_data_2d(var, Time, return_statuses)
Write out all diagnostics of elements of a coupler_2d_bc_type.
This is the interface to spawn one coupler_bc_type into another and then register diagnostics associa...
This is the interface to copy the field data from one coupler_bc_type to another of the same rank,...
This is the interface to override the values of the arrays in a coupler_bc_type.
This is the interface to deallocate any data associated with a coupler_bc_type.
This is the interface to extract a field in a coupler_bc_type into an array.
This is the interface to increment the field data from one coupler_bc_type with the data from another...
This function interface indicates whether a coupler_bc_type has been initialized.
This is the interface to redistribute the field data from one coupler_bc_type to another of the same ...
This is the interface to register the fields in a coupler_bc_type to be saved in restart files.
This is the interface to rescale the field data in a coupler_bc_type.
This is the interface to read in the fields in a coupler_bc_type that have been saved in restart file...
This is the interface to write out diagnostics of the arrays in a coupler_bc_type.
This is the interface to set a field in a coupler_bc_type from an array.
This is the interface to set diagnostics for the arrays in a coupler_bc_type.
This is the interface to spawn one coupler_bc_type into another.
This is the interface to write out checksums for the elements of a coupler_bc_type.
Coupler data for 1D boundary conditions.
Coupler data for 2D boundary conditions.
Coupler data for 3D boundary conditions.
Interface for inserting and interpolating data into a file for a model's grid and time....
Register a diagnostic field for a given module.
Send data over to output fields.
Opens a given netcdf or domain file.
Definition: fms2_io.F90:122
Add a dimension to a given file.
Definition: fms2_io.F90:190
Defines a new field within the given file Example usage:
Definition: fms2_io.F90:212
Similar to register_field, but occupies the field with data for restarts Example usage:
Definition: fms2_io.F90:231
subroutine, public write_version_number(version, tag, unit)
Prints to the log file (or a specified unit) the version id string and tag name.
Definition: fms.F90:758
character(:) function, allocatable, public string(v, fmt)
Converts a number or a Boolean value to a string.
Reorganization of distributed global arrays. mpp_redistribute is used to reorganize a distributed ar...
The domain2D type contains all the necessary information to define the global, compute and data domai...
Calculate parallel checksums.
Definition: mpp.F90:1200
Error handler.
Definition: mpp.F90:382
Type to represent amounts of time. Implemented as seconds and days to allow for larger intervals.