!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! 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 oda_driver_mod
#ifdef ENABLE_ODA
!
! Matt Harrison
!
!
!
! Top-level interface between MOM4 and ocean data assimilation modules
!
!
!
! Top level module for ocean data assimilation. Contains routines for
! initialization, termination and update of ocean data assimilation increments.
!
! 1.) Get Observations for current model time (and domain)
! based on data window (default=15 days). For PE subdomains,
! data are included if they lie within the region bounded by the
! cell centers on the first x/y halo extent.
!
! 2.)
! IF EAKF (!!not yet implemented!!)
! a.) ensemble state is distributed among NPES as follows:
! Ens01:0:(NPE1-1), Ens02:NPE1:(NPE1+NPE2-1),...,
! EnsN:sum(NPE1+...+NPE(N-1)):NPES
! b.) Redistribute state to ensemble vector using global PEset.
! (Ens01,...,EnsN):0:NPES
! c.) Calculate increments using Ensemble information.
! d.) Re-distribute increments to members and revert to
! original PEset.
! ELSE IF Var2d
! Minimize quadratic CF based on specfied prior correlation maps
! and optionally time-variant prior error variance.
! ELSE ???
!
! ENDIF
!
! 3.) Apply increments to model state based on prior analysis.
! Distribute increments uniformly over analysis period if "do_iau=T".
!
! 4.) Check for convective instability after applying increments.
!
! 5.) Write Profile (observation space) first guess, analysis and mis-fits
!
!========================================================================
!
!
!
! use the oda module
!
!
!
! Options are: Var2d, EAKF and NO_ASSIM
!
!
! Southern data mask boundary
!
!
!
! Northern data mask boundary
!
!
!
! Bottom model level for data mask
!
!
!
! Time between calls to oda (hours)
!
!
!
! Incremental analysis update (evenly distribute increments between
! calls to ODA.)
!
!
!
! Adjust for gravitational instability in model after applying increments.
!
!
!
! Size of allocation for profile data
!
!
!
! Size of allocation for surface observations
!
!
use ocean_types_mod
use oda_types_mod
use oda_core_mod
use mpp_domains_mod, only : domain2d, mpp_update_domains, &
mpp_define_domains, CYCLIC_GLOBAL_DOMAIN, BGRID_NE, &
FOLD_NORTH_EDGE, mpp_get_compute_domain, mpp_get_data_domain, &
null_domain2d, mpp_redistribute, mpp_broadcast_domain
use mpp_mod, only : mpp_npes, stdout, stdlog, mpp_error, FATAL, &
mpp_pe, mpp_declare_pelist,mpp_get_current_pelist,&
mpp_set_current_pelist, mpp_root_pe,mpp_sum, mpp_sync
use mpp_io_mod, only : MPP_MULTI, MPP_SINGLE, mpp_io_exit
use time_manager_mod, only : time_type, decrement_time, increment_time, &
get_time, get_date
use ocean_grids_mod, only : set_ocean_grid_size, set_ocean_hgrid_arrays, &
set_ocean_vgrid_arrays
use ocean_topog_mod, only : ocean_topog_init
use ocean_convect_mod, only : convection
use ocean_domains_mod, only : set_ocean_domain, get_local_indices
use constants_mod, only : radius, epsln, rho_cp
! use dr_oi_mod, only : dr_oi_init, dr_oi
use ocean_workspace_mod, only : wrk1
use write_ocean_data_mod
use ensemble_manager_mod, only : ensemble_manager_init, get_ensemble_id
use ensemble_manager_mod, only : get_ensemble_size, get_ensemble_pelist,get_ensemble_filter_pelist
IMPLICIT NONE
private
integer, parameter :: NO_ASSIM = 0, Var2d=1, EAKF=2, TEST_ENSEMBLE_ENGINE=3
integer, parameter :: max_prog_tracers = 2
integer :: max_profiles = 100000, max_sfc_obs = 100 ! for run-time allocation of observation arrays
integer :: asm_method = NO_ASSIM
character(len=8) :: assim_method = 'NO_ASSIM'
logical :: do_iau = .true. ! incremental analysis update (recommended)
logical :: do_convect_adjust=.true. ! for doing convective adjustment
! after applying increments (recommended)
logical :: save_omf_snaps = .true.
logical :: save_oma_snaps = .false.
type(grid_type), target, save :: T_grid
type(domain2d), pointer, save :: Asm_domain
integer :: isc, iec, jsc, jec, isd, ied, jsd, jed
integer :: ni, nj, nk
integer :: num_prog_tracers
real, dimension(:,:,:), allocatable, save :: corr, omf, oma, amf
integer :: nk_asm
integer :: xhalo, yhalo
integer :: index_temp, index_salt
integer :: id_omf = -1
integer :: id_oma = -1
integer :: id_amf = -1
integer :: id_put_array = -1
integer :: id_got_array = -1
integer, parameter :: max_ensemble_size = 10
integer :: pe_start(max_ensemble_size)=0, pe_end(max_ensemble_size)=0
integer :: ensemble_size = 1
logical, dimension(max_ensemble_size) :: ensemble_pe = .false.
integer, allocatable, dimension(:,:) :: ensemble_pelist
integer :: pe, npes, npes_per_member, ensemble_id
logical :: sequential_filter = .false.
integer :: isd_filt, ied_filt, jsd_filt, jed_filt
integer :: isc_filt, iec_filt, jsc_filt, jec_filt
real :: assim_start_lat = -90.0 , assim_end_lat = 90.0
integer :: assim_interval = 24
integer :: assim_tsteps=0
character(len=64) :: fname_fg, fname_anal, fname_omf, fname_oma
integer :: iunit_fg, iunit_omf, iunit_anal, iunit_oma
real, save :: dtimer
type(ocean_domain_type), pointer, save :: Dom
type(ocean_grid_type), pointer, save :: Grd
type(ocean_dist_type), target, save :: First_guess, Analysis
type(ocean_profile_type), save, allocatable :: Profiles(:)
type(ocean_surface_type), save, allocatable :: Sfc_obs(:)
type(ocean_profile_type), save, allocatable, dimension(:) :: model_obs, &
model_obsd
type(grid_type), save :: Ens_grid
type(domain2d), save :: Filter_domain, Send_domain(max_ensemble_size)
type(field_type), save :: Tracer_ens(max_prog_tracers, max_ensemble_size)
type(field_type), save :: U_ens(max_ensemble_size), V_ens(max_ensemble_size)
type(field_type), save :: SSH_ens(max_ensemble_size),T_ens(max_ensemble_size)
integer :: assim_layout(2)
integer :: filter_halo_x = 1, filter_halo_y = 1
integer :: ensemble_siz(4)
integer :: total_npes_pm,ocean_npes_pm,atmos_npes_pm
integer, allocatable :: ocean_pelist(:, :) , ocean_pelist_filter(:), original_pelist(:)
real, allocatable :: put_array(:,:,:), got_array(:,:,:)
logical :: use_this_module=.false.
public :: init_oda, oda
contains
!#######################################################################
!
!
!
! Initialize ODA core. Grid and Domain association.
!
!
!
!
!
!
!
subroutine init_oda(Domain, Grid, Ocn_Time, T_prog, Velocity, Ext_mode)
! initialize First_guess and Analysis grid and domain information
! grids are identical to ocean_model grid. Halo size may differ.
use fms_mod, only : open_namelist_file,close_file,check_nml_error
use mpp_mod, only : mpp_set_current_pelist
use diag_manager_mod, only : diag_axis_init, register_diag_field
use mpp_domains_mod, only : mpp_global_field, mpp_define_layout
use ocean_grids_mod, only : set_ocean_grid_size, set_ocean_hgrid_arrays, &
set_ocean_vgrid_arrays
use ocean_topog_mod, only : ocean_topog_init
use ocean_domains_mod, only : set_ocean_domain, get_local_indices
type(ocean_domain_type), target :: Domain
type(ocean_grid_type), target :: Grid ! domain and grid information for ocean model
type(ocean_time_type), target :: Ocn_Time
type(ocean_prog_tracer_type), intent(in) :: T_prog(:)
type(ocean_velocity_type), intent(in), optional :: Velocity
type(ocean_external_mode_type), intent(in), optional :: Ext_mode
type(time_type), pointer :: Time
real :: adum,bdum,cdum
integer :: len, xhalo, yhalo, halo(2), n, m, k, i, j, id_zt
integer :: layout(2)
integer :: ioun, io_status, ierr
integer :: secs, days
integer :: dt
integer :: stdoutunit
stdoutunit=stdout()
namelist /oda_nml / use_this_module,assim_method, assim_start_lat, &
assim_end_lat, nk_asm, assim_interval, do_iau, do_convect_adjust,&
max_profiles, max_sfc_obs, save_omf_snaps,&
save_oma_snaps, assim_layout
num_prog_tracers = size(T_prog)
index_temp = -1; index_salt = -1
do n=1, num_prog_tracers
if (T_prog(n)%name == 'temp') index_temp = n
if (T_prog(n)%name == 'salt') index_salt = n
enddo
Dom => Domain
Grd => Grid
Time => Ocn_Time%model_time
call get_time(Ocn_time%time_step,secs,days)
dt=secs+days*86400
ioun = open_namelist_file()
read(ioun,nml=oda_nml,iostat = io_status)
ierr = check_nml_error(io_status,'oda_nml')
call close_file(ioun)
if (.not.use_this_module) return
assim_interval=assim_interval*3600
write(stdoutunit,*) 'Assimilation interval= ',assim_interval,' seconds'
write(stdoutunit,*) 'Model timestep= ',dt,' seconds'
adum=assim_interval
adum=real(adum/dt)
if (adum - floor(adum) .ne. 0.0) then
call mpp_error(FATAL,'assim_interval must be integer multiple &
&of model timestep')
else
assim_tsteps=assim_interval/dt
endif
dtimer=1.0/dt
if (nk_asm > Grid%nk) nk_asm = Grid%nk
if (nk_asm < 2) call mpp_error(FATAL,&
'need to specify number of vertical levels for assimilation')
ni = Grid%ni
nj = Grid%nj
nk = Grid%nk
call mpp_get_compute_domain(Domain%domain2d, isc, iec, jsc, jec)
call mpp_get_data_domain(Domain%domain2d, isd, ied, jsd, jed)
T_grid%ni = ni
T_grid%nj = nj
T_grid%nk = nk
T_grid%Dom => Domain%domain2d
allocate(T_grid%x(0:ni+1,0:nj+1), T_grid%y(0:ni+1,0:nj+1),&
T_grid%mask(0:ni+1,0:nj+1,nk))
allocate(T_grid%z(nk))
T_grid%x=0.0
T_grid%y=0.0
T_grid%z=0.0
T_grid%mask=0.0
allocate(corr(isd:ied,jsd:jed,nk))
allocate(omf(isd:ied,jsd:jed,nk))
allocate(oma(isd:ied,jsd:jed,nk))
allocate(amf(isd:ied,jsd:jed,nk))
corr(:,:,:) = 0.0
omf(:,:,:) = 0.0
oma(:,:,:) = 0.0
amf(:,:,:) = 0.0
! get global grid information from ocean_model
call mpp_global_field(Domain%domain2d, Grid%tmask(:,:,:), &
T_grid%mask(1:ni,1:nj,:))
do j=1,nj
T_Grid%x(1:ni,j) = Grid%grid_x_t(1:ni)
enddo
do i=1,ni
T_Grid%y(i,1:nj) = Grid%grid_y_t(1:nj)
enddo
if (Grid%cyclic_x) then
T_grid%x(0,:) = T_grid%x(ni,:)-360.
T_grid%x(ni+1,:) = T_grid%x(1,:)+360.
else
T_grid%x(0,:) = T_grid%x(1,:)-1.e-10
T_grid%x(ni+1,:) = T_grid%x(ni,:)+1.e-10
endif
T_grid%y(:,0) = T_grid%y(:,1)-1.e-3
T_grid%y(:,nj+1) = T_grid%y(:,nj)+1.e-3
T_grid%z = Grid%zt
do j=0,nj+1
do i=0,ni+1
if (T_grid%y(i,j) < assim_start_lat .or. &
T_grid%y(i,j) > assim_end_lat ) then
T_grid%mask(i,j,:) = 0.0
endif
do k=nk_asm+1,nk
T_grid%mask(i,j,k) = 0.0
enddo
enddo
enddo
T_grid%cyclic = Grid%cyclic_x
if (assim_tsteps < 1) then
call mpp_error(FATAL,'invalid assim interval')
else
write(stdoutunit,*) 'Assimilating data every ', assim_tsteps, ' timesteps'
endif
select case (assim_method)
case ('NO_ASSIM')
asm_method = NO_ASSIM
case ('EAKF')
asm_method = EAKF
case ('Var2d')
asm_method = Var2d
case ('TEST_ENS')
asm_method = TEST_ENSEMBLE_ENGINE
case default
call mpp_error(FATAL,'Wrong assim_method')
end select
select case (asm_method)
case(TEST_ENSEMBLE_ENGINE)
!This is just to test the functionality of the ensemble run
!and can serve as an example of how to use the ensemble in
!real life data assimilations.
!allocate ens array(Temp(;,;,;,Nens))
!define domain for ens members(Send_domain(Nens))
!define domain for filter (filter_domain)
!broadcast_domain(Send_domain)
!do i=1,Nens
! redist(Send_domain, TEMP, filter_domain, Temp(:,:,:,i)
!enddo
!do i=1,Nens
! redist(filter_domain, temp(:,:,:,i), Send_domain, Temp2)
!enddo
!is(Temp .eq. Temp2)?
!
!Get ensemble size and id
!npes_pm (# of pe's per member) are assumed to be the same for all members
ensemble_id = get_ensemble_id()
ensemble_siz = get_ensemble_size()
ensemble_size = ensemble_siz(1)
total_npes_pm = ensemble_siz(2)
ocean_npes_pm = ensemble_siz(3)
atmos_npes_pm = ensemble_siz(4)
!First save the current pelist
allocate(original_pelist(ocean_npes_pm)) !can the argument be mpp_npes() ?
call mpp_get_current_pelist(original_pelist)
!Get ocean_pelist
allocate(ocean_pelist(1:ensemble_size,1:ocean_npes_pm))
call get_ensemble_pelist(ocean_pelist,'ocean')
!Set pelist to ocean_pelist for this ensemble member
call mpp_set_current_pelist(ocean_pelist(ensemble_id,:))
!
!Define "send" domains for individual ensemble members
!
call mpp_define_domains((/1,ni,1,nj/), Domain%layout, Send_domain(ensemble_id),&
xflags=Domain%xflags, yflags=Domain%yflags, xhalo = Domain%xhalo, &
yhalo = Domain%yhalo, name='send')
allocate(put_array(isd:ied,jsd:jed,nk))
allocate(got_array(isd:ied,jsd:jed,nk))
id_put_array = register_diag_field('oda','oda_put' ,&
Grid%tracer_axes(1:3),Time,'oda_put_array','none',&
missing_value=-1.0e+10 )
id_got_array = register_diag_field('oda','oda_got' ,&
Grid%tracer_axes(1:3),Time,'oda_got_array','none',&
missing_value=-1.0e+10 )
!
!Define the "filter" domain which is the domain for doing
!ensemble calculations (avereages, assimilations, ...)
!
!The "filter" domain should be defined on a pelist which
!is the union of Ocean PE's for all ensemble members.
!
allocate(ocean_pelist_filter(ensemble_size * ocean_npes_pm))
call get_ensemble_filter_pelist(ocean_pelist_filter,'ocean')
call mpp_set_current_pelist(ocean_pelist_filter)
call mpp_define_domains((/1,ni,1,nj/), assim_layout, Filter_domain, &
xflags=Domain%xflags, yflags=Domain%yflags,&
xhalo = filter_halo_x, yhalo = filter_halo_y, name='filter')
call mpp_get_data_domain(Filter_domain, isd_filt,ied_filt,jsd_filt,jed_filt)
call mpp_get_compute_domain(Filter_domain, isc_filt,iec_filt,jsc_filt,jec_filt)
do m = 1, ensemble_size
allocate(T_ens(m)%data(isd_filt:ied_filt,jsd_filt:jed_filt,nk))
enddo
!return to original pelist
call mpp_set_current_pelist(original_pelist)
return
case (EAKF)
call mpp_error(FATAL,'Not currently supported')
case (Var2d)
call mpp_error(FATAL,'Not currently supported')
! xhalo=1;yhalo=1
! Asm_domain => Domain%domain2d
! allocate(First_guess%temp%ex(isd:ied,jsd:jed,nk))
! First_guess%temp%ex = 0.0
! First_guess%temp%grid => T_grid
! ! assign tracer values on computational domain
! ! assume for now that the ocean decomposition is identical
! ! to assim decompositionn with the exception of halo sizes
! allocate(Analysis%temp%ex(isd:ied,jsd:jed,nk))
! Analysis%temp%grid => First_guess%temp%grid
! call oda_core_init(Domain%domain2d, T_grid,localize=.true.)
! call dr_oi_init(Domain, Grid, Time, nk_asm)
case (NO_ASSIM)
xhalo=1;yhalo=1
Asm_domain => Domain%domain2d
allocate(First_guess%temp%ex(isd:ied,jsd:jed,nk))
First_guess%temp%ex = 0.0
First_guess%temp%grid => T_grid
! assign tracer values on computational domain
! assume for now that the ocean decomposition is identical
! to assim decompositionn with the exception of halo sizes
call oda_core_init(Domain%domain2d, T_grid,localize=.true.)
end select
id_omf = register_diag_field('oda','omf_temp',&
Grid%tracer_axes(1:3),Time,'data-first guess misfit (temp)','deg_C',&
missing_value=-20.,range=(/-20.,20./))
id_oma = register_diag_field('oda','oma_temp',&
Grid%tracer_axes(1:3),Time,'data-analysis misfit (temp)','deg_C',&
missing_value=-20.,range=(/-20.,20./))
id_amf = register_diag_field('oda','amf_temp',&
Grid%tracer_axes(1:3),Time,'correction for temperature','W/m^2',&
missing_value=-2.e3,range=(/-2.e3,2.e3/))
allocate(Profiles(max_profiles))
allocate(model_obs(max_profiles), model_obsd(max_profiles))
allocate(Sfc_obs(max_sfc_obs))
call write_ocean_data_init()
return
end subroutine init_oda
! NAME="init_oda"
!#######################################################################
!
!
!
! Request ocean state increments
!
!
!
!
!
!
!
subroutine oda(Ocn_Time, T_prog, Velocity, Ext_mode, Thickness, Dens)
! use eakf_mod, only : ensemble_filter
use diag_manager_mod, only : send_data
type(ocean_time_type), target :: Ocn_Time
type(ocean_prog_tracer_type), intent(inout) :: T_prog(:)
type(ocean_velocity_type), intent(inout), optional :: Velocity
type(ocean_external_mode_type), intent(inout), optional :: Ext_mode
type(ocean_thickness_type), intent(inout), optional :: Thickness
type(ocean_density_type), intent(inout), optional :: Dens
type(time_type) :: time
! snz adds a time-step index as the analysis time-step using time_step
integer :: time_step_index = 0
integer :: ia, ja, ka, i,j, k, m, n, nprof, nsfc, itt, tau, taup1, &
nprof_g
real :: t, u, v, tfg, z1, z2
logical :: assim_time, used
integer :: stdoutunit
stdoutunit=stdout()
if (.not.use_this_module) return
time = Ocn_time%model_time
itt = Ocn_time%itt
tau = Ocn_time%tau
if (mod(itt-1,assim_tsteps) .eq. 0 .or. itt .eq. 0) then
assim_time = .true.
call get_obs(time, Profiles, Sfc_obs, nprof, nsfc)
amf = 0.0;corr=0.0
else
assim_time = .false.
endif
select case (asm_method)
case(TEST_ENSEMBLE_ENGINE)
!First save the current pelist
call mpp_get_current_pelist(original_pelist)
!Set pelist to all filter PE's
call get_ensemble_filter_pelist(ocean_pelist_filter,'ocean')
call mpp_set_current_pelist(ocean_pelist_filter)
! call mpp_set_current_pelist()
put_array = T_prog(1)%field(:,:,:,1)
got_array = -1.0
do m = 1, ensemble_size
call mpp_broadcast_domain(Send_domain(m))
enddo
!Distribute each ensemble member array to filter domain
do m = 1, ensemble_size
T_ens(m)%data(:,:,:) = 0.0
call mpp_redistribute(Send_domain(m), put_array(:,:,:), &
Filter_domain, T_ens(m)%data(:,:,:))
enddo
!Here are filter calculations
do m = 1, ensemble_size
call mpp_update_domains(T_ens(m)%data(:,:,:), Filter_domain)
enddo
!Distribute the array back to ensemble members domains
do m = 1, ensemble_size
call mpp_redistribute(Filter_domain, T_ens(m)%data(:,:,:), &
Send_domain(m), got_array(:,:,:))
enddo
!Since there was no real filter calculation the two arrays must match on each ensemble member
do k=1,nk
do j=jsc,jec; do i=isc,iec
if (got_array(i,j,k)-put_array(i,j,k) .ne. 0.0) &
call mpp_error(FATAL,'Ensemble loopback test failed!')
enddo;enddo
enddo
if (id_put_array > 0) used = send_data(id_put_array,put_array(isc:iec,jsc:jec,:),Time)
if (id_got_array > 0) used = send_data(id_got_array,got_array(isc:iec,jsc:jec,:)&
-put_array(isc:iec,jsc:jec,:),Time)
!return to original pelist
call mpp_set_current_pelist(original_pelist)
return
case (EAKF)
call mpp_error(FATAL,'Not currently supported')
case (Var2d)
! set first guess field to tau tracer
First_guess%temp%ex(:,:,:) = &
T_prog(index_temp)%field(:,:,:,tau)
call mpp_update_domains( First_guess%temp%ex, Asm_domain)
tau = Ocn_time%tau
taup1 = Ocn_time%taup1
if (assim_time) then
write(stdoutunit,*) 'itt = ',itt
write(stdoutunit,*) 'Calculating analysis using observed data'
! call dr_oi(First_guess,Profiles(1:nprof), Sfc_obs(1:nsfc), Analysis, Time)
amf(:,:,:) = (Analysis%temp%ex(:,:,:) - &
First_guess%temp%ex(:,:,:))
if (do_iau) then
! sub-divide increments equally between analysis intervals
corr = amf/assim_tsteps
else
corr = amf
endif
else
if (do_iau) then
write(stdoutunit,*) 'itt = ',itt
write(stdoutunit,*) 'Applying analysis increment to ocean state'
else
corr = 0.0
endif
endif
if (do_iau.or.assim_time) then
wrk1(:,:,:) = T_prog(index_temp)%field(:,:,:,taup1)
T_prog(index_temp)%field(:,:,:,taup1) = &
wrk1(:,:,:) + corr(:,:,:)
where (T_prog(index_temp)%field(:,:,:,taup1) .lt. -1.9)
T_prog(index_temp)%field(:,:,:,taup1) = -1.9
end where
if (present(Dens).and.do_convect_adjust) then
call convection(Ocn_Time, Thickness, T_prog, Dens)
call mpp_update_domains(T_prog(index_temp)%field(:,:,:,taup1),Dom%domain2d)
call mpp_update_domains(T_prog(index_salt)%field(:,:,:,taup1),Dom%domain2d)
else
write(stdoutunit,*) "WARNING: Not doing convection in oda"
call mpp_update_domains(T_prog(index_temp)%field(:,:,:,taup1),Dom%domain2d)
endif
if (PRESENT(Thickness)) then
amf(:,:,:) = dtimer*rho_cp*Thickness%dzt(:,:,:)*(T_prog(index_temp)%field(:,:,:,taup1) - wrk1(:,:,:))
else
! NOTE: Warning!! this diagnostic is inaccurate
do k=1,nk
amf(:,:,k) = dtimer*rho_cp*Grd%dzt(k)*(T_prog(index_temp)%field(:,:,k,taup1) - wrk1(:,:,k))
enddo
endif
else
amf = 0.0
endif
end select
if (assim_time) then
if (save_omf_snaps .or. save_oma_snaps) call open_profile_snaps(Time,nprof)
if (asm_method == NO_ASSIM) then
tau = Ocn_time%tau
taup1 = Ocn_time%taup1
First_guess%temp%ex(:,:,:) = &
T_prog(index_temp)%field(:,:,:,taup1)
call mpp_update_domains( First_guess%temp%ex, Asm_domain)
endif
if (nprof .gt. 0 .and. save_omf_snaps) then
write(stdoutunit,*) 'itt = ',itt
write(stdoutunit,*) 'Calculating first guess differences using observed data'
call copy_obs(Profiles(1:nprof),model_obs(1:nprof)) ! copy profile data to separate array for holding model guess
call copy_obs(Profiles(1:nprof),model_obsd(1:nprof))
call forward_obs( model_obs(1:nprof), First_guess%temp%ex) ! put first guess in observation data (forward operator gets defined here)
call assign_forward_model(model_obs(1:nprof),model_obsd(1:nprof))
call diff_obs(model_obs(1:nprof),Profiles(1:nprof),model_obsd(1:nprof)) ! difference obs - model
if (id_omf .gt. 0 ) then
call backward_obs(model_obsd(1:nprof),omf) ! D^T To
endif
endif
if (save_omf_snaps) then
call mpp_set_current_pelist((/mpp_pe()/))
do i=1,nprof
if (Profiles(i)%accepted) then
call write_profile(iunit_fg, model_obs(i))
call write_profile(iunit_omf, model_obsd(i))
endif
enddo
call mpp_set_current_pelist()
endif
endif
if (asm_method /= NO_ASSIM .and. assim_time) then
if (nprof .gt. 0 .and. save_oma_snaps) then
write(stdoutunit,*) 'itt = ',itt
write(stdoutunit,*) 'Calculating analysis differences using observed data'
call forward_obs( model_obs(1:nprof), Analysis%temp%ex) ! put first guess in observation data (forward operator gets defined here)
call diff_obs(model_obs(1:nprof),Profiles(1:nprof),model_obsd(1:nprof)) ! difference obs - model
endif
if (save_oma_snaps) then
call mpp_set_current_pelist((/mpp_pe()/))
do i=1,nprof
if (Profiles(i)%accepted) then
call write_profile(iunit_anal, model_obs(i))
call write_profile(iunit_oma,model_obsd(i))
endif
enddo
call mpp_set_current_pelist()
endif
if (id_oma .gt. 0 ) then
call backward_obs(model_obsd(1:nprof),oma) ! D^T To
endif
endif
if (assim_time .and. (save_omf_snaps .or. save_oma_snaps)) call close_profile_snaps(nprof)
! call mpp_sync()
99 continue
if (id_omf > 0) used = send_data(id_omf,omf(isc:iec,jsc:jec,:),&
&Time,rmask=Grd%tmask(isc-Dom%ioff:iec-Dom%ioff,&
jsc-Dom%joff:jec-Dom%joff,:))
if (id_oma > 0) used = send_data(id_oma,oma(isc:iec,jsc:jec,:),&
&Time,rmask=Grd%tmask(isc-Dom%ioff:iec-Dom%ioff,&
jsc-Dom%joff:jec-Dom%joff,:))
if (id_amf > 0) used = send_data(id_amf,amf(isc:iec,jsc:jec,:),&
&Time,rmask=Grd%tmask(isc-Dom%ioff:iec-Dom%ioff,&
jsc-Dom%joff:jec-Dom%joff,:))
return
end subroutine oda
! NAME="init_oda"
subroutine open_profile_snaps(Time,nprof)
type(time_type), intent(in) :: Time
integer, intent(in) :: nprof
character(len=2) :: cmon,cday,chr
character(len=4) :: cyr, cpe
integer :: ipe
integer :: year,month,day,hour,minute,second
ipe = mpp_pe()
! if (nprof.eq.0) return
if (ipe .lt. 10) then
write(cpe,'(a3,i1)') '000',ipe
else if (ipe .lt. 100) then
write(cpe,'(a2,i2)') '00',ipe
else if (ipe .lt. 1000) then
write(cpe,'(a1,i3)') '0',ipe
else
write(cpe,'(i4)') ipe
endif
call get_date(Time,year,month,day,hour,minute,second)
if (year .lt. 10) then
write(cyr,'(a3,i1)') '000',year
else if (year .lt. 100) then
write(cyr,'(a2,i2)') '00',year
else if (year .lt. 1000) then
write(cyr,'(a1,i3)') '0',year
else
write(cyr,'(i4)') year
endif
if (month .lt. 10) then
write(cmon,'(a1,i1)') '0',month
else
write(cmon,'(i2)') month
endif
if (day .lt. 10) then
write(cday,'(a1,i1)') '0',day
else
write(cday,'(i2)') day
endif
if (hour .lt. 10) then
write(chr,'(a1,i1)') '0',hour
else
write(chr,'(i2)') hour
endif
if (save_omf_snaps) then
write(fname_fg,'(a3,a4,a2,a2,a2,a3,a4,a3)') 'fg_',cyr,cmon,cday,chr,'.pe',cpe,'.nc'
write(fname_omf,'(a4,a4,a2,a2,a2,a3,a4,a3)') 'omf_',cyr,cmon,cday,chr,'.pe',cpe,'.nc'
endif
if (save_oma_snaps) then
write(fname_anal,'(a5,a4,a2,a2,a2,a3,a4,a3)') 'anal_',cyr,cmon,cday,chr,'.pe',cpe,'.nc'
write(fname_oma,'(a4,a4,a2,a2,a2,a3,a4,a3)') 'oma_',cyr,cmon,cday,chr,'.pe',cpe,'.nc'
endif
call mpp_set_current_pelist((/mpp_pe()/))
if (nprof.ne.0 .and. save_omf_snaps) then
iunit_fg = open_profile_file(fname_fg,1,&
grid_lon=Grd%grid_x_t,grid_lat=Grd%grid_y_t,thread=MPP_SINGLE,fset=MPP_SINGLE)
iunit_omf = open_profile_file(fname_omf,1,&
grid_lon=Grd%grid_x_t,grid_lat=Grd%grid_y_t,thread=MPP_SINGLE,fset=MPP_SINGLE)
endif
if (nprof.ne.0 .and. save_oma_snaps) then
iunit_anal = open_profile_file(fname_anal,1,&
grid_lon=Grd%grid_x_t,grid_lat=Grd%grid_y_t,thread=MPP_SINGLE,fset=MPP_SINGLE)
iunit_oma = open_profile_file(fname_oma,1,&
grid_lon=Grd%grid_x_t,grid_lat=Grd%grid_y_t,thread=MPP_SINGLE,fset=MPP_SINGLE)
endif
call mpp_set_current_pelist()
end subroutine open_profile_snaps
subroutine close_profile_snaps(nprof)
integer, intent(in) :: nprof
call mpp_set_current_pelist((/mpp_pe()/))
if (nprof.ne.0 .and. save_omf_snaps) then
call close_profile_file(iunit_fg)
call close_profile_file(iunit_omf)
endif
if (nprof.ne.0 .and. save_oma_snaps) then
call close_profile_file(iunit_anal)
call close_profile_file(iunit_oma)
endif
call mpp_set_current_pelist()
end subroutine close_profile_snaps
#endif
end module oda_driver_mod