Skip to content

Commit

Permalink
FMS2 interpolation ID replaced with derived type
Browse files Browse the repository at this point in the history
All instances of an FMS ID to the internal interpolation content is
replaced with a derived type containing additional metadata recording
the field's origin filename and fieldname.

This additional information is required in order to replicate the axis
data from the field, which is no longer provided by FMS2.

The abstraction of this type also allows us to either extend it or
redefine it in other frameworks as needed in the future.

This primarily affects the usage of the following functions:

- init_external_field
- time_interp_external
- horiz_interp_and_extrap_tracer

The following solvers are updated:

- MOM_open_boundary
- MOM_ice_shelf
- MOM_oda_driver
- MOM_MEKE
- MOM_ALE_sponge
- MOM_diabatic_aux

Of these, OBC was the most significant.  The integer handle (fid) was
previously used to determine if each segment field was constant or (if
negative) read from a file.  After being replaced by the derived type, a
new flag was added to make this determination.

All of the coupled drivers have been modified, since they support time
interpolation of T and S fields.

- FMS
- MCT
- NUOPC

The NUOPC driver also includes modifications to its CFC11 and CFC12
fields.  Changes to the MOM CFC modules replaces an `id == -1`-like
test, which is not used by the derived type.  This check has been
removed, and we now solely rely on the `present(cfc_handle)` test.

While this could change behavior, there does not seem to be any scenario
where init_external_field would return -1 but would be passed to the
function.  (But I may eat these words.)
  • Loading branch information
marshallward authored and Hallberg-NOAA committed Jun 23, 2023
1 parent 273da2f commit 6038735
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 145 deletions.
15 changes: 9 additions & 6 deletions config_src/drivers/FMS_cap/MOM_surface_forcing_gfdl.F90
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module MOM_surface_forcing_gfdl
use MOM_grid, only : ocean_grid_type
use MOM_interpolate, only : init_external_field, time_interp_external
use MOM_interpolate, only : time_interp_external_init
use MOM_interpolate, only : external_field
use MOM_io, only : slasher, write_version_number, MOM_read_data
use MOM_io, only : read_netCDF_data
use MOM_io, only : stdout_if_root
Expand Down Expand Up @@ -153,8 +154,10 @@ module MOM_surface_forcing_gfdl
!! in inputdir/temp_restore_mask.nc and the field should
!! be named 'mask'
real, pointer, dimension(:,:) :: trestore_mask => NULL() !< Mask for SST restoring [nondim]
integer :: id_srestore = -1 !< An id number for time_interp_external.
integer :: id_trestore = -1 !< An id number for time_interp_external.
type(external_field) :: srestore_handle
!< Handle for time-interpolated salt restoration field
type(external_field) :: trestore_handle
!< Handle for time-interpolated temperature restoration field

type(forcing_diags), public :: handles !< Diagnostics handles

Expand Down Expand Up @@ -345,7 +348,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! Salinity restoring logic
if (CS%restore_salt) then
call time_interp_external(CS%id_srestore, Time, data_restore, scale=US%ppt_to_S)
call time_interp_external(CS%srestore_handle, Time, data_restore, scale=US%ppt_to_S)
! open_ocn_mask indicates where to restore salinity (1 means restore, 0 does not)
open_ocn_mask(:,:) = 1.0
if (CS%mask_srestore_under_ice) then ! Do not restore under sea-ice
Expand Down Expand Up @@ -403,7 +406,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! SST restoring logic
if (CS%restore_temp) then
call time_interp_external(CS%id_trestore, Time, data_restore, scale=US%degC_to_C)
call time_interp_external(CS%trestore_handle, Time, data_restore, scale=US%degC_to_C)
if ( CS%trestore_SPEAR_ECDA ) then
do j=js,je ; do i=is,ie
if (abs(data_restore(i,j)+1.8*US%degC_to_C) < 0.0001*US%degC_to_C) then
Expand Down Expand Up @@ -1610,7 +1613,7 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, wind_stagger)

if (CS%restore_salt) then
salt_file = trim(CS%inputdir) // trim(CS%salt_restore_file)
CS%id_srestore = init_external_field(salt_file, CS%salt_restore_var_name, MOM_domain=G%Domain)
CS%srestore_handle = init_external_field(salt_file, CS%salt_restore_var_name, MOM_domain=G%Domain)
call safe_alloc_ptr(CS%srestore_mask,isd,ied,jsd,jed); CS%srestore_mask(:,:) = 1.0
if (CS%mask_srestore) then ! read a 2-d file containing a mask for restoring fluxes
flnam = trim(CS%inputdir) // 'salt_restore_mask.nc'
Expand All @@ -1620,7 +1623,7 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, wind_stagger)

if (CS%restore_temp) then
temp_file = trim(CS%inputdir) // trim(CS%temp_restore_file)
CS%id_trestore = init_external_field(temp_file, CS%temp_restore_var_name, MOM_domain=G%Domain)
CS%trestore_handle = init_external_field(temp_file, CS%temp_restore_var_name, MOM_domain=G%Domain)
call safe_alloc_ptr(CS%trestore_mask,isd,ied,jsd,jed); CS%trestore_mask(:,:) = 1.0
if (CS%mask_trestore) then ! read a 2-d file containing a mask for restoring fluxes
flnam = trim(CS%inputdir) // 'temp_restore_mask.nc'
Expand Down
15 changes: 9 additions & 6 deletions config_src/drivers/mct_cap/mom_surface_forcing_mct.F90
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module MOM_surface_forcing_mct
use MOM_grid, only : ocean_grid_type
use MOM_interpolate, only : init_external_field, time_interp_external
use MOM_interpolate, only : time_interp_external_init
use MOM_interpolate, only : external_field
use MOM_io, only : slasher, write_version_number, MOM_read_data
use MOM_io, only : stdout
use MOM_restart, only : register_restart_field, restart_init, MOM_restart_CS
Expand Down Expand Up @@ -134,8 +135,10 @@ module MOM_surface_forcing_mct
!! in inputdir/temp_restore_mask.nc and the field should
!! be named 'mask'
real, pointer, dimension(:,:) :: trestore_mask => NULL() !< mask for SST restoring
integer :: id_srestore = -1 !< id number for time_interp_external.
integer :: id_trestore = -1 !< id number for time_interp_external.
type(external_field) :: srestore_handle
!< Handle for time-interpolated salt restoration field
type(external_field) :: trestore_handle
!< Handle for time-interpolated temperature restoration field

type(forcing_diags), public :: handles !< diagnostics handles
type(MOM_restart_CS), pointer :: restart_CSp => NULL() !< restart pointer
Expand Down Expand Up @@ -348,7 +351,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! Salinity restoring logic
if (restore_salinity) then
call time_interp_external(CS%id_srestore, Time, data_restore, scale=US%ppt_to_S)
call time_interp_external(CS%srestore_handle, Time, data_restore, scale=US%ppt_to_S)
! open_ocn_mask indicates where to restore salinity (1 means restore, 0 does not)
open_ocn_mask(:,:) = 1.0
if (CS%mask_srestore_under_ice) then ! Do not restore under sea-ice
Expand Down Expand Up @@ -405,7 +408,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! SST restoring logic
if (restore_sst) then
call time_interp_external(CS%id_trestore, Time, data_restore, scale=US%degC_to_C)
call time_interp_external(CS%trestore_handle, Time, data_restore, scale=US%degC_to_C)
do j=js,je ; do i=is,ie
delta_sst = data_restore(i,j) - sfc_state%SST(i,j)
delta_sst = sign(1.0,delta_sst)*min(abs(delta_sst),CS%max_delta_trestore)
Expand Down Expand Up @@ -1292,7 +1295,7 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, restore_salt,

if (present(restore_salt)) then ; if (restore_salt) then
salt_file = trim(CS%inputdir) // trim(CS%salt_restore_file)
CS%id_srestore = init_external_field(salt_file, CS%salt_restore_var_name, domain=G%Domain%mpp_domain)
CS%srestore_handle = init_external_field(salt_file, CS%salt_restore_var_name, domain=G%Domain%mpp_domain)
call safe_alloc_ptr(CS%srestore_mask,isd,ied,jsd,jed); CS%srestore_mask(:,:) = 1.0
if (CS%mask_srestore) then ! read a 2-d file containing a mask for restoring fluxes
flnam = trim(CS%inputdir) // 'salt_restore_mask.nc'
Expand All @@ -1302,7 +1305,7 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, restore_salt,

if (present(restore_temp)) then ; if (restore_temp) then
temp_file = trim(CS%inputdir) // trim(CS%temp_restore_file)
CS%id_trestore = init_external_field(temp_file, CS%temp_restore_var_name, domain=G%Domain%mpp_domain)
CS%trestore_handle = init_external_field(temp_file, CS%temp_restore_var_name, domain=G%Domain%mpp_domain)
call safe_alloc_ptr(CS%trestore_mask,isd,ied,jsd,jed); CS%trestore_mask(:,:) = 1.0
if (CS%mask_trestore) then ! read a 2-d file containing a mask for restoring fluxes
flnam = trim(CS%inputdir) // 'temp_restore_mask.nc'
Expand Down
28 changes: 17 additions & 11 deletions config_src/drivers/nuopc_cap/mom_surface_forcing_nuopc.F90
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module MOM_surface_forcing_nuopc
use MOM_grid, only : ocean_grid_type
use MOM_interpolate, only : init_external_field, time_interp_external
use MOM_interpolate, only : time_interp_external_init
use MOM_interpolate, only : external_field
use MOM_CFC_cap, only : CFC_cap_fluxes
use MOM_io, only : slasher, write_version_number, MOM_read_data
use MOM_io, only : stdout
Expand Down Expand Up @@ -146,10 +147,14 @@ module MOM_surface_forcing_nuopc
character(len=30) :: cfc11_var_name !< name of cfc11 in CFC_BC_file
character(len=30) :: cfc12_var_name !< name of cfc11 in CFC_BC_file
real, pointer, dimension(:,:) :: trestore_mask => NULL() !< mask for SST restoring
integer :: id_srestore = -1 !< id number for time_interp_external.
integer :: id_trestore = -1 !< id number for time_interp_external.
integer :: id_cfc11_atm = -1 !< id number for time_interp_external.
integer :: id_cfc12_atm = -1 !< id number for time_interp_external.
type(external_field) :: srestore_handle
!< Handle for time-interpolated salt restoration field
type(external_field) :: trestore_handle
!< Handle for time-interpolated temperature restoration field
type(external_field) :: cfc11_atm_handle
!< Handle for time-interpolated CFC11 restoration field
type(external_field) :: cfc12_atm_handle
!< Handle for time-interpolated CFC12 restoration field

! Diagnostics handles
type(forcing_diags), public :: handles
Expand Down Expand Up @@ -377,7 +382,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! Salinity restoring logic
if (restore_salinity) then
call time_interp_external(CS%id_srestore, Time, data_restore, scale=US%ppt_to_S)
call time_interp_external(CS%srestore_handle, Time, data_restore, scale=US%ppt_to_S)
! open_ocn_mask indicates where to restore salinity (1 means restore, 0 does not)
open_ocn_mask(:,:) = 1.0
if (CS%mask_srestore_under_ice) then ! Do not restore under sea-ice
Expand Down Expand Up @@ -434,7 +439,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! SST restoring logic
if (restore_sst) then
call time_interp_external(CS%id_trestore, Time, data_restore, scale=US%degC_to_C)
call time_interp_external(CS%trestore_handle, Time, data_restore, scale=US%degC_to_C)
do j=js,je ; do i=is,ie
delta_sst = data_restore(i,j) - sfc_state%SST(i,j)
delta_sst = sign(1.0,delta_sst)*min(abs(delta_sst),CS%max_delta_trestore)
Expand Down Expand Up @@ -596,7 +601,8 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! CFCs
if (CS%use_CFC) then
call CFC_cap_fluxes(fluxes, sfc_state, G, US, CS%Rho0, Time, CS%id_cfc11_atm, CS%id_cfc11_atm)
call CFC_cap_fluxes(fluxes, sfc_state, G, US, CS%Rho0, Time, &
CS%cfc11_atm_handle, CS%cfc11_atm_handle)
endif

if (associated(IOB%salt_flux)) then
Expand Down Expand Up @@ -1394,7 +1400,7 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, restore_salt,

if (present(restore_salt)) then ; if (restore_salt) then
salt_file = trim(CS%inputdir) // trim(CS%salt_restore_file)
CS%id_srestore = init_external_field(salt_file, CS%salt_restore_var_name, domain=G%Domain%mpp_domain)
CS%srestore_handle = init_external_field(salt_file, CS%salt_restore_var_name, domain=G%Domain%mpp_domain)
call safe_alloc_ptr(CS%srestore_mask,isd,ied,jsd,jed); CS%srestore_mask(:,:) = 1.0
if (CS%mask_srestore) then ! read a 2-d file containing a mask for restoring fluxes
flnam = trim(CS%inputdir) // 'salt_restore_mask.nc'
Expand All @@ -1404,7 +1410,7 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, restore_salt,

if (present(restore_temp)) then ; if (restore_temp) then
temp_file = trim(CS%inputdir) // trim(CS%temp_restore_file)
CS%id_trestore = init_external_field(temp_file, CS%temp_restore_var_name, domain=G%Domain%mpp_domain)
CS%trestore_handle = init_external_field(temp_file, CS%temp_restore_var_name, domain=G%Domain%mpp_domain)
call safe_alloc_ptr(CS%trestore_mask,isd,ied,jsd,jed); CS%trestore_mask(:,:) = 1.0
if (CS%mask_trestore) then ! read a 2-d file containing a mask for restoring fluxes
flnam = trim(CS%inputdir) // 'temp_restore_mask.nc'
Expand All @@ -1430,8 +1436,8 @@ subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, restore_salt,
"The name of the variable representing CFC-12 in "//&
"CFC_BC_FILE.", default="CFC_12", do_not_log=.true.)

CS%id_cfc11_atm = init_external_field(CS%CFC_BC_file, CS%cfc11_var_name, domain=G%Domain%mpp_domain)
CS%id_cfc12_atm = init_external_field(CS%CFC_BC_file, CS%cfc12_var_name, domain=G%Domain%mpp_domain)
CS%cfc11_atm_handle = init_external_field(CS%CFC_BC_file, CS%cfc11_var_name, domain=G%Domain%mpp_domain)
CS%cfc12_atm_handle = init_external_field(CS%CFC_BC_file, CS%cfc12_var_name, domain=G%Domain%mpp_domain)
endif
endif

Expand Down
54 changes: 32 additions & 22 deletions config_src/infra/FMS1/MOM_interp_infra.F90
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ module MOM_interp_infra
public :: time_interp_extern, init_extern_field, time_interp_extern_init
public :: get_external_field_info, axistype, get_axis_data
public :: run_horiz_interp, build_horiz_interp_weights
public :: external_field

!< Handle of an external field for interpolation
type :: external_field
private
integer :: id
!< FMS ID for the interpolated field
character(len=:), allocatable :: filename
!< Filename containing the field values
character(len=:), allocatable :: label
!< Field name in the file
end type external_field

!> Read a field based on model time, and rotate to the model domain.
interface time_interp_extern
Expand Down Expand Up @@ -167,46 +179,44 @@ end function get_extern_field_missing


!> Get information about the external fields.
subroutine get_external_field_info(field_id, size, axes, missing)
integer, intent(in) :: field_id !< The integer index of the external
subroutine get_external_field_info(field, size, axes, missing)
type(external_field), intent(in) :: field !< Handle for time interpolated external
!! field returned from a previous
!! call to init_external_field()
integer, dimension(4), optional, intent(inout) :: size !< Dimension sizes for the input data
type(axistype), dimension(4), optional, intent(inout) :: axes !< Axis types for the input data
real, optional, intent(inout) :: missing !< Missing value for the input data

if (present(size)) then
size(1:4) = get_extern_field_size(field_id)
size(1:4) = get_extern_field_size(field%id)
endif

if (present(axes)) then
axes(1:4) = get_extern_field_axes(field_id)
axes(1:4) = get_extern_field_axes(field%id)
endif

if (present(missing)) then
missing = get_extern_field_missing(field_id)
missing = get_extern_field_missing(field%id)
endif

end subroutine get_external_field_info


!> Read a scalar field based on model time.
subroutine time_interp_extern_0d(field_id, time, data_in, verbose)
integer, intent(in) :: field_id !< The integer index of the external field returned
!! from a previous call to init_external_field()
subroutine time_interp_extern_0d(field, time, data_in, verbose)
type(external_field), intent(in) :: field !< Handle for time interpolated field
type(time_type), intent(in) :: time !< The target time for the data
real, intent(inout) :: data_in !< The interpolated value
logical, optional, intent(in) :: verbose !< If true, write verbose output for debugging

call time_interp_external(field_id, time, data_in, verbose=verbose)
call time_interp_external(field%id, time, data_in, verbose=verbose)
end subroutine time_interp_extern_0d


!> Read a 2d field from an external based on model time, potentially including horizontal
!! interpolation and rotation of the data
subroutine time_interp_extern_2d(field_id, time, data_in, interp, verbose, horz_interp, mask_out)
integer, intent(in) :: field_id !< The integer index of the external field returned
!! from a previous call to init_external_field()
subroutine time_interp_extern_2d(field, time, data_in, interp, verbose, horz_interp, mask_out)
type(external_field), intent(in) :: field !< Handle for time interpolated field
type(time_type), intent(in) :: time !< The target time for the data
real, dimension(:,:), intent(inout) :: data_in !< The array in which to store the interpolated values
integer, optional, intent(in) :: interp !< A flag indicating the temporal interpolation method
Expand All @@ -216,15 +226,14 @@ subroutine time_interp_extern_2d(field_id, time, data_in, interp, verbose, horz_
logical, dimension(:,:), &
optional, intent(out) :: mask_out !< An array that is true where there is valid data

call time_interp_external(field_id, time, data_in, interp=interp, verbose=verbose, &
call time_interp_external(field%id, time, data_in, interp=interp, verbose=verbose, &
horz_interp=horz_interp, mask_out=mask_out)
end subroutine time_interp_extern_2d


!> Read a 3d field based on model time, and rotate to the model grid
subroutine time_interp_extern_3d(field_id, time, data_in, interp, verbose, horz_interp, mask_out)
integer, intent(in) :: field_id !< The integer index of the external field returned
!! from a previous call to init_external_field()
subroutine time_interp_extern_3d(field, time, data_in, interp, verbose, horz_interp, mask_out)
type(external_field), intent(in) :: field !< Handle for time interpolated field
type(time_type), intent(in) :: time !< The target time for the data
real, dimension(:,:,:), intent(inout) :: data_in !< The array in which to store the interpolated values
integer, optional, intent(in) :: interp !< A flag indicating the temporal interpolation method
Expand All @@ -234,14 +243,15 @@ subroutine time_interp_extern_3d(field_id, time, data_in, interp, verbose, horz_
logical, dimension(:,:,:), &
optional, intent(out) :: mask_out !< An array that is true where there is valid data

call time_interp_external(field_id, time, data_in, interp=interp, verbose=verbose, &
call time_interp_external(field%id, time, data_in, interp=interp, verbose=verbose, &
horz_interp=horz_interp, mask_out=mask_out)
end subroutine time_interp_extern_3d


!> initialize an external field
integer function init_extern_field(file, fieldname, MOM_domain, domain, verbose, &
threading, ierr, ignore_axis_atts, correct_leap_year_inconsistency )
function init_extern_field(file, fieldname, MOM_domain, domain, verbose, &
threading, ierr, ignore_axis_atts, correct_leap_year_inconsistency) &
result(field)

character(len=*), intent(in) :: file !< The name of the file to read
character(len=*), intent(in) :: fieldname !< The name of the field in the file
Expand All @@ -261,17 +271,17 @@ integer function init_extern_field(file, fieldname, MOM_domain, domain, verbose,
!! is in use, and (2) the modulo time period of the
!! data is an integer number of years, then map
!! a model date of Feb 29. onto a common year on Feb. 28.
type(external_field) :: field !< Handle to external field

if (present(MOM_Domain)) then
init_extern_field = init_external_field(file, fieldname, domain=MOM_domain%mpp_domain, &
field%id = init_external_field(file, fieldname, domain=MOM_domain%mpp_domain, &
verbose=verbose, threading=threading, ierr=ierr, ignore_axis_atts=ignore_axis_atts, &
correct_leap_year_inconsistency=correct_leap_year_inconsistency)
else
init_extern_field = init_external_field(file, fieldname, domain=domain, &
field%id = init_external_field(file, fieldname, domain=domain, &
verbose=verbose, threading=threading, ierr=ierr, ignore_axis_atts=ignore_axis_atts, &
correct_leap_year_inconsistency=correct_leap_year_inconsistency)
endif

end function init_extern_field

end module MOM_interp_infra
Loading

0 comments on commit 6038735

Please sign in to comment.