!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! GNU General Public License !!
!! !!
!! This file is part of the Flexible Modeling System (FMS). !!
!! !!
!! FMS is free software; you can redistribute it and/or modify !!
!! it and are expected to follow the terms of the GNU General Public !!
!! License as published by the Free Software Foundation. !!
!! !!
!! FMS is distributed in the hope that it will be useful, !!
!! but WITHOUT ANY WARRANTY; without even the implied warranty of !!
!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !!
!! GNU General Public License for more details. !!
!! !!
!! You should have received a copy of the GNU General Public License !!
!! along with FMS; if not, write to: !!
!! Free Software Foundation, Inc. !!
!! 59 Temple Place, Suite 330 !!
!! Boston, MA 02111-1307 USA !!
!! or see: !!
!! http://www.gnu.org/licenses/gpl.txt !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
module mom_oasis3_interface_mod
!
! Dave Bi (for OASIS3 hooks)
!
!
! Russ Fiedler (for OASIS3 hooks)
!
!
!
! Interface to OASIS3/PRISM2-5 coupling.
!
!
!
! Interface to the OASIS3
! This module serves as a hook between PRISM2-5/OASIS3 and mom4p1
! Only serial coupling has been tested at this stage (Oct 2009) although there are
! hints for the user for implementing different strategies.
!
! Standard mom surface quantities In Ocean_Ice_Boundary and Ocean_sfc may be passed
!
! The namelist ocean_oasis3_interface_nml contains the quantities to be passed to and from the ocean
!
!
!
!
!
! The number of fields to be passed to mom from external sources
!
!
! The number of fields to be passed from mom to external progams
!
!
! The fields to be passed to mom from external progams. Currently
! valid names correspond to variables in the Ice_ocean_boundary structure.
! These names must agree with names in the OASIS namcouple file
! WARNING! Note truncation of names of ssw flux components and salt_flx.
!
!
! The fields to be passed from mom to external progams. Currently
! valid names correspond to variables in the Ocean_sfc structure.
! These names must agree with the 'interpolated' names in the OASIS namcouple file
!
!
! TRUE if coupling strategy requires we send data to coupler BEFORE updating the ocean
!
!
! TRUE if coupling strategy requires we send data to coupler AFTER updating the ocean
!
!
!
!
!
#ifdef OASIS3
!prism stuff
use mod_prism_proto
use mod_prism_def_partition_proto
use mod_prism_put_proto
use mod_prism_get_proto
use mod_comprism_proto
!MOM4 modules:
use fms_mod, only: file_exist
use fms_mod, only: write_version_number, open_namelist_file, close_file, check_nml_error
use mpp_domains_mod, only: mpp_update_domains, domain2d
use mpp_mod, only: mpp_broadcast, mpp_pe, mpp_npes, mpp_root_pe
use mpp_mod, only: mpp_error, FATAL, WARNING, NOTE
use mpp_mod, only: stdlog, stdout
use mpp_domains_mod, only: mpp_get_compute_domain, &
mpp_get_data_domain, &
mpp_get_global_domain, &
mpp_global_field
use ocean_types_mod, only: ice_ocean_boundary_type, &
ocean_public_type, &
ocean_domain_type
use time_manager_mod, only: time_type
implicit none
public :: mom_prism_init, mom_prism_terminate, coupler_init, from_coupler, into_coupler, &
write_coupler_restart
private
include "mpif.h"
!
! OASIS3 variables:
!
integer :: id_component ! Component ID
integer :: id_partition ! Local partition ID
integer :: num_total_proc ! Total number of processes
integer :: num_coupling_proc ! Number of processes involved in the coupling
logical :: parallel_coupling
integer :: jf
!
! Coupling associated variables
!
integer :: ierr
integer :: limt, ljmt, i, j
integer :: step
character(len=6), parameter :: cp_modnam='mom4p1' ! Component model name same as in namcouple
integer :: imt_global, jmt_global ! 2D global layout
integer :: imt_local, jmt_local ! 2D global layout
integer iisc,iiec,jjsc,jjec
integer iisd,iied,jjsd,jjed
integer, parameter :: max_fields_in=16
integer, parameter :: max_fields_out=6
integer, dimension(max_fields_in) :: id_var_in ! ID for fields to be rcvd
integer, dimension(max_fields_out) :: id_var_out ! ID for fields to be sent
character(len=8), dimension(max_fields_in) :: mom_name_read ! Standard mom Ice_Ocean_boundary names
character(len=8), dimension(max_fields_out) :: mom_name_write ! Standard mom Ocean_Sfc names
! Namelist variables
integer :: num_fields_in ! Number of fields to be sent
integer :: num_fields_out ! Number of fields to be rcvd
character(len=8),dimension(max_fields_out) :: fields_out
character(len=8),dimension(max_fields_in) :: fields_in
logical :: send_before_ocean_update=.FALSE., &
send_after_ocean_update=.FALSE. ! Control when to pass fields to coupler.
! Work array
real, allocatable,dimension(:,:) :: vwork
contains
!-----------------------------------------------------------------------------------
subroutine mom_prism_init(mom4_local_comm)
integer :: mom4_local_comm
!--------------------!
!
! Initialize PRISM.
!
! Initialise MPI
call MPI_INIT(ierr)
call prism_init_comp_proto (id_component, cp_modnam, ierr)
if (ierr /= PRISM_Ok) then
call prism_abort_proto(id_component, 'mom4 prism_init','STOP 1')
endif
!
! PRISM attribution of local communicator.
!
call prism_get_localcomm_proto(mom4_local_comm, ierr)
if (ierr /= PRISM_Ok) then
call prism_abort_proto(id_component, 'mom4 prism_init','STOP 2')
endif
end subroutine mom_prism_init
!-----------------------------------------------------------------------------------
subroutine coupler_init(Dom, Time, Time_step_coupled, Run_len, dt_cpld)
! In this routine we set up all our arrays and determine which fields are to be passed to and fro.
! Determine the style of coupling
!
! Time, Time_step_coupled & run_len not used at the moment. They are provided for convenience if the user wishes to
! use them for initialising.
type(domain2d) :: Dom
type(time_type) :: Time, Time_step_coupled, Run_len
integer :: dt_cpld
integer, dimension(5) :: il_paral
integer, dimension(2) :: var_num_dims ! see below
integer, dimension(4) :: var_shape ! see below
integer isg,ieg,jsg,jeg
integer ioun, io_status
integer ifield
logical fmatch
namelist /mom_oasis3_interface_nml/ num_fields_in, num_fields_out,fields_in,fields_out, &
send_before_ocean_update, send_after_ocean_update
! all processors read the namelist--
ioun = open_namelist_file()
read (ioun, mom_oasis3_interface_nml,iostat=io_status)
write (stdout(),'(/)')
write (stdout(), mom_oasis3_interface_nml)
write (stdlog(), mom_oasis3_interface_nml)
ierr = check_nml_error(io_status,'mom_oasis3_interface_nml')
call close_file (ioun)
call mpp_get_compute_domain(Dom,iisc,iiec,jjsc,jjec)
call mpp_get_data_domain(Dom,iisd,iied,jjsd,jjed)
call mpp_get_global_domain(Dom,isg,ieg,jsg,jeg)
! Global domain extent
imt_global=ieg-isg+1
jmt_global=jeg-jsg+1
!B: for parallel coupling, the above global layout does NOT make sense...
! Use local domains
!
! Original did all this on the data domain using temp arrays
!limt=iied-iisd+1
!ljmt=jjed-jjsd+1
! Use computational domain directly
imt_local=iiec-iisc+1
jmt_local=jjec-jjsc+1
! Can't we get these numbers from elsewhere? Shouldn't be hardwired. RASF
! It would be better to read in num_coupling_proc and parallel_coupling and perform sanity checking.
! Can we change some of the variable names? It's really hard to figure out what they mean.
! (OASIS3 naming convention problem?).
num_total_proc = mpp_npes()
!num_coupling_proc = num_total_proc !multi-process coupling (real parallel cpl)!
num_coupling_proc = 1 !mono-process coupling
! Type of coupling
if (num_coupling_proc == num_total_proc .and. num_total_proc /= 1) then
parallel_coupling = .TRUE.
else
parallel_coupling = .FALSE.
endif
!
!! vwork is the 'coupling' array that directly communicates with oasis3.
!! -- global (if parallel_coupling=.f.) domain or
!! -- local (if parallel_coupling=.t.) domain.
!!
!
if (parallel_coupling) then
allocate(vwork(iisc:iiec,jjsc:jjec))
else
allocate(vwork(isg:ieg,jsg:jeg))
endif
vwork=0.0
! Define name (as in namcouple) and declare each field sent/received by oce:
!ice ==> ocn
mom_name_read(:)=''
mom_name_read(1)='u_flux'
mom_name_read(2)='v_flux'
mom_name_read(3)='t_flux'
mom_name_read(4)='q_flux'
mom_name_read(5)='salt_flx'
mom_name_read(6)='lw_flux'
mom_name_read(7)='sw_vdir'
mom_name_read(8)='sw_vdif'
mom_name_read(9)='sw_irdir'
mom_name_read(10)='sw_irdif'
mom_name_read(11)='lprec'
mom_name_read(12)='fprec'
mom_name_read(13)='runoff'
mom_name_read(14)='calving'
mom_name_read(15)='p'
mom_name_read(16)='sw_flux' ! For partitioning shortwave flux
!ocn ==> ice
mom_name_write(:)=''
mom_name_write(1)='t_surf'
mom_name_write(2)='s_surf'
mom_name_write(3)='u_surf'
mom_name_write(4)='v_surf'
mom_name_write(5)='sea_lev'
mom_name_write(6)='frazil'
fmatch = .false.
do jf = 1,num_fields_in
do ifield=1,max_fields_in
if ( trim(mom_name_read(ifield)) == trim(fields_in(jf)) ) then
fmatch = .true.
exit
endif
enddo
if (.not. fmatch) then
call mpp_error(FATAL,'coupler_init: Illegal input name' )
endif
fmatch = .false.
enddo
fmatch = .false.
do jf = 1,num_fields_out
do ifield=1,max_fields_in
if ( trim(mom_name_write(ifield)) == trim(fields_out(jf)) ) then
fmatch = .true.
exit
endif
enddo
if (.not. fmatch) then
call mpp_error(FATAL,'coupler_init: Illegal output name' )
endif
fmatch = .false.
enddo
il_paral (:) = 0
if( parallel_coupling ) then
! ???
!il_paral ( clim_strategy ) = clim_Box
!il_paral ( clim_offset ) = (ieg-isg+1)*(jjsc-1)+(iisc-1)
!il_paral ( clim_SizeX ) = iiec-iisc+1
!il_paral ( clim_SizeY ) = jjec-jjsc+1
!il_paral ( clim_LdX ) = ieg-isg+1
! send_before_ocean_update = .true. ! ?????
! send_after_ocean_update = .false. ! ?????
else
il_paral ( clim_strategy ) = clim_serial
il_paral ( clim_offset ) = 0
il_paral ( clim_length ) = imt_global * jmt_global
send_before_ocean_update = .false.
send_after_ocean_update = .true.
endif
!
! PRISM coupling fields declaration
!
var_num_dims(1)= 2 ! rank of coupling field
var_num_dims(2)= 1 ! number of bundles in coupling field (always 1)
var_shape(1)= lbound(vwork,1) ! min index for the coupling field local dimension
var_shape(2)= ubound(vwork,1) ! max index for the coupling field local dim
var_shape(3)= lbound(vwork,2) ! min index for the coupling field local dim
var_shape(4)= ubound(vwork,2) ! max index for the coupling field local dim
if (mpp_pe() == mpp_root_pe() .or. parallel_coupling) then
!
! The following steps need to be done:
! -> by the process if mom is monoprocess;
! -> only by the master process, if mom is parallel and only
! master process is involved in the coupling;
! -> by all processes, if mom is parallel and all processes
! are involved in the coupling.
!
! - Define parallel partitions and allocate coupling fields accordingly:
! (Refer to oasis/psmile/prism/modules/mod_prism_proto.F90 for integer value
! of clim_xxxx parameters)
! For each coupling field, association of a port to its symbolic name
! -Define the parallel decomposition associated to the port of each
! field; here no decomposition for all ports.
!
call prism_def_partition_proto (id_partition, il_paral, ierr)
!
do jf=1, num_fields_out
call prism_def_var_proto (id_var_out(jf),fields_out(jf), id_partition, &
var_num_dims, PRISM_Out, var_shape, PRISM_Real, ierr)
enddo
do jf=1, num_fields_in
call prism_def_var_proto (id_var_in(jf), fields_in(jf), id_partition, &
var_num_dims, PRISM_In, var_shape, PRISM_Real, ierr)
enddo
!
! PRISM end of declaration phase
!
call prism_enddef_proto (ierr)
if (ierr /= PRISM_Ok) then
call mpp_error(FATAL,'coupler_init: *** problem with prism_enddef_proto! ***' )
endif
endif !
end subroutine coupler_init
!=======================================================================
subroutine into_coupler(step, Ocean_sfc, Time, before_ocean_update)
!------------------------------------------!
implicit none
type (ocean_public_type) :: Ocean_sfc
type (time_type) :: Time
integer, intent(in) :: step
logical, intent(in) :: before_ocean_update ! Flag to indicate whether
! we are calling before or after updating the ocean
integer:: jf, ierr, i, j
real, pointer, dimension(:,:) :: vwork_local
! Check if we want to couple at this call.
if ( before_ocean_update .and. (.not. send_before_ocean_update) ) return
if ( (.not. before_ocean_update) .and. (.not. send_after_ocean_update) ) return
do jf = 1,num_fields_out
! Just point to array. Don't need to copy.
coupling_fields_out: select case( trim(fields_out(jf)))
case('t_surf')
vwork_local => Ocean_sfc%t_surf
case('s_surf')
vwork_local => Ocean_sfc%s_surf
case('u_surf')
vwork_local => Ocean_sfc%u_surf
case('v_surf')
vwork_local => Ocean_sfc%v_surf
case('sea_lev')
vwork_local => Ocean_sfc%sea_lev
case('frazil')
vwork_local => Ocean_sfc%frazil
case DEFAULT
call mpp_error(FATAL,&
'==>Error from into_coupler: Unknown quantity.')
end select coupling_fields_out
if (.not. parallel_coupling) then
call mpp_global_field(Ocean_sfc%domain, vwork_local(iisc:iiec,jjsc:jjec), vwork)
endif
if (mpp_pe() == mpp_root_pe() .or. parallel_coupling) then
print *, 'MOM4: into_coupler putting field at step = ', trim(fields_out(jf)), ' ', step
if (parallel_coupling) then
call prism_put_proto(id_var_out(jf),step,vwork_local,ierr)
else
call prism_put_proto(id_var_out(jf),step,vwork,ierr)
endif
if (ierr /= prism_ok.and. ierr < prism_sent) then
call prism_abort_proto(id_component, 'MOM4 into_cpl','stop 1')
endif
endif
enddo
return
end subroutine into_coupler
!-----------------------------------------------------------------------------------
subroutine from_coupler(step,Ice_ocean_boundary, Time)
! This is all highly user dependent.
use constants_mod, only: hlv ! 2.500e6 J/kg
implicit none
type (ice_ocean_boundary_type) :: Ice_ocean_boundary
type (time_type) :: Time
integer, intent(in) :: step
real :: frac_vis_dir=0.5*0.43, frac_vis_dif=0.5*0.43, &
frac_nir_dir=0.5*0.57, frac_nir_dif=0.5*0.57 ! shortwave partitioning
do jf = 1, num_fields_in
if (mpp_pe() == mpp_root_pe() .or. parallel_coupling) then
print *, 'MOM4: from_coupler getting fields at step = ', trim(fields_in(jf)), ' ', step
call prism_get_proto(id_var_in(jf),step,vwork,ierr)
if (ierr /= PRISM_Ok.and. ierr < prism_recvd) then
call prism_abort_proto(id_component, 'MOM4 _get_ ','stop 1')
endif
endif
if ( .not. parallel_coupling) then
call mpp_broadcast(vwork, imt_global*jmt_global, mpp_root_pe())
endif
! Handle conversions from raw field to what mom requires
! Sign conventions & scaling factors. Partioning of SSW etc.
! Would prefer to select on a generic name for clarity. but OASIS has 8 character limita on fields to be passed
coupling_fields_in: select case ( trim(fields_in(jf) ))
case('u_flux')
Ice_ocean_boundary%u_flux(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('v_flux')
Ice_ocean_boundary%v_flux(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('lprec')
Ice_ocean_boundary%lprec(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('salt_flx')
Ice_ocean_boundary%salt_flux(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('calving') !
Ice_ocean_boundary%calving(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('sw_vdir')
Ice_ocean_boundary%sw_flux_vis_dir(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('sw_vdif')
Ice_ocean_boundary%sw_flux_vis_dif(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('sw_irdir')
Ice_ocean_boundary%sw_flux_nir_dir(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('sw_irdif')
Ice_ocean_boundary%sw_flux_nir_dif(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('sw_flux')
Ice_ocean_boundary%sw_flux_vis_dir(iisc:iiec,jjsc:jjec) = frac_vis_dir*vwork(iisc:iiec,jjsc:jjec)
Ice_ocean_boundary%sw_flux_vis_dif(iisc:iiec,jjsc:jjec) = frac_vis_dif*vwork(iisc:iiec,jjsc:jjec)
Ice_ocean_boundary%sw_flux_nir_dir(iisc:iiec,jjsc:jjec) = frac_nir_dir*vwork(iisc:iiec,jjsc:jjec)
Ice_ocean_boundary%sw_flux_nir_dif(iisc:iiec,jjsc:jjec) = frac_nir_dif*vwork(iisc:iiec,jjsc:jjec)
case('q_flux')
Ice_ocean_boundary%q_flux(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)/hlv
case('t_flux')
Ice_ocean_boundary%t_flux(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('lw_flux')
Ice_ocean_boundary%lw_flux(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('runoff')
Ice_ocean_boundary%runoff(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('p')
Ice_ocean_boundary%p(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case('fprec')
Ice_ocean_boundary%fprec(iisc:iiec,jjsc:jjec) = vwork(iisc:iiec,jjsc:jjec)
case DEFAULT
! Probable error. Leave as warning for the moment. RASF
call mpp_error(WARNING,&
'==>Warning from from_coupler: Unknown quantity.')
end select coupling_fields_in
enddo !jf
end subroutine from_coupler
!-----------------------------------------------------------------------------------
subroutine write_coupler_restart(step,write_restart)
logical, intent(in) :: write_restart
integer, intent(in) :: step
if ( write_restart ) then
if (mpp_pe() == mpp_root_pe() .or. parallel_coupling) then
do jf = 1,num_fields_out
print *, 'MOM4: (write_coupler_restart) calling _put_restart at ', step, ' ', fields_out(jf)
call prism_put_restart_proto(id_var_out(jf),step,ierr)
if (ierr == PRISM_ToRest ) then
call mpp_error(NOTE,&
'==>Note from into_coupler: Written field to o2i file.')
else
call mpp_error(FATAL,&
'==>Error from into_coupler: writing field into o2i file failed.')
endif
enddo
endif
endif
end subroutine write_coupler_restart
!-----------------------------------------------------------------------------------
subroutine mom_prism_terminate
! PRISM termination
!
! deallocate all coupling associated arrays here ......
! Note: prism won't terminate MPI
!
call prism_terminate_proto (ierr)
if (ierr .ne. PRISM_Ok) then
call mpp_error(FATAL,&
'==>Error from coupler_termination: Failure in prism_terminate_proto.')
endif
end subroutine mom_prism_terminate
!=====
#endif
end module mom_oasis3_interface_mod