From a24974211bcdd21e932f48a5039888cb41766e66 Mon Sep 17 00:00:00 2001 From: jiandewang Date: Mon, 18 Jul 2022 15:41:24 -0400 Subject: [PATCH 01/68] add temperature limiter for SPPT in MOM_diabatic_driver --- src/parameterizations/vertical/MOM_diabatic_driver.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parameterizations/vertical/MOM_diabatic_driver.F90 b/src/parameterizations/vertical/MOM_diabatic_driver.F90 index 8e519eafdc..b75c429261 100644 --- a/src/parameterizations/vertical/MOM_diabatic_driver.F90 +++ b/src/parameterizations/vertical/MOM_diabatic_driver.F90 @@ -449,8 +449,8 @@ subroutine diabatic(u, v, h, tv, Hml, fluxes, visc, ADp, CDp, dt, Time_end, & ! These stochastic perturbations do not conserve heat, salt or mass. do k=1,nz ; do j=js,je ; do i=is,ie h(i,j,k) = max(h_in(i,j,k) + (h(i,j,k)-h_in(i,j,k)) * stoch_CS%sppt_wts(i,j), GV%Angstrom_H) - tv%T(i,j,k) = t_in(i,j,k) + (tv%T(i,j,k)-t_in(i,j,k)) * stoch_CS%sppt_wts(i,j) tv%S(i,j,k) = max(s_in(i,j,k) + (tv%S(i,j,k)-s_in(i,j,k)) * stoch_CS%sppt_wts(i,j), 0.0) + tv%T(i,j,k) = max(t_in(i,j,k) + (tv%T(i,j,k)-t_in(i,j,k)) * stoch_CS%sppt_wts(i,j), -0.054*tv%S(i,j,k)) enddo ; enddo ; enddo deallocate(h_in, t_in, s_in) endif From 5747461b62d01afd7e17628f3e822ec94c10ed11 Mon Sep 17 00:00:00 2001 From: jiandewang Date: Tue, 9 Aug 2022 22:55:33 -0400 Subject: [PATCH 02/68] NUOPC cap not to affect OpenMP threading: supporting traditional and ESMF-managed threading. authored-by: Gerhard Theurich --- config_src/drivers/nuopc_cap/mom_cap.F90 | 51 ------------------------ 1 file changed, 51 deletions(-) diff --git a/config_src/drivers/nuopc_cap/mom_cap.F90 b/config_src/drivers/nuopc_cap/mom_cap.F90 index b01e2019da..d244205b1b 100644 --- a/config_src/drivers/nuopc_cap/mom_cap.F90 +++ b/config_src/drivers/nuopc_cap/mom_cap.F90 @@ -98,8 +98,6 @@ module MOM_cap_mod use NUOPC_Model, only: model_label_Finalize => label_Finalize use NUOPC_Model, only: SetVM -!$use omp_lib , only : omp_set_num_threads - implicit none; private public SetServices @@ -149,7 +147,6 @@ module MOM_cap_mod integer :: scalar_field_count = 0 integer :: scalar_field_idx_grid_nx = 0 integer :: scalar_field_idx_grid_ny = 0 -integer :: nthrds !< number of openmp threads per task character(len=*),parameter :: u_FILE_u = & __FILE__ @@ -465,30 +462,6 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) CALL ESMF_TimeIntervalGet(TINT, S=DT_OCEAN, RC=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - !--------------------------------- - ! openmp threads - !--------------------------------- - - call ESMF_VMGet(vm, pet=localPet, peCount=localPeCount, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - if (localPeCount == 1) then - call NUOPC_CompAttributeGet(gcomp, name="nthreads", value=cvalue, & - isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) nthrds - else - nthrds = localPeCount - endif - else - nthrds = localPeCount - endif - write(logmsg,*) nthrds - call ESMF_LogWrite(trim(subname)//': nthreads = '//trim(logmsg), ESMF_LOGMSG_INFO) - -!$ call omp_set_num_threads(nthrds) - call fms_init(mpi_comm_mom) call constants_init call field_manager_init @@ -936,28 +909,6 @@ subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) call ESMF_VMGet(vm, petCount=npet, mpiCommunicator=mpicom, localPet=localPet, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - !--------------------------------- - ! openmp threads - !--------------------------------- - - call ESMF_VMGet(vm, pet=localPet, peCount=localPeCount, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - if (localPeCount == 1) then - call NUOPC_CompAttributeGet(gcomp, name="nthreads", value=cvalue, & - isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) nthrds - else - nthrds = localPeCount - endif - else - nthrds = localPeCount - endif - -!$ call omp_set_num_threads(nthrds) - !--------------------------------- ! global mom grid size !--------------------------------- @@ -1570,8 +1521,6 @@ subroutine ModelAdvance(gcomp, rc) call shr_file_setLogUnit (logunit) -!$ call omp_set_num_threads(nthrds) - ! query the Component for its clock, importState and exportState call ESMF_GridCompGet(gcomp, clock=clock, importState=importState, & exportState=exportState, rc=rc) From a22f69190527804ee03c2e67d08bbc546d2f6c39 Mon Sep 17 00:00:00 2001 From: WenhaoChen89 <96131003+WenhaoChen89@users.noreply.github.com> Date: Fri, 19 Aug 2022 14:50:16 -0400 Subject: [PATCH 03/68] Fix ALE sponge diagnostics (#188) * Change dumbbell initialization * Change in Dumbbell Layer Mode * Fix sponge diagnostics * Fix ALE Sponge Diagnostics * A minor style change removing spaces around = in optional. function arguments. * Fix ALE sponge diagnostics * character declaration fix --- .../MOM_state_initialization.F90 | 12 ++-- .../vertical/MOM_ALE_sponge.F90 | 59 +++++++++++++++---- src/tracer/RGC_tracer.F90 | 2 +- src/user/DOME2d_initialization.F90 | 10 ++-- src/user/ISOMIP_initialization.F90 | 11 ++-- src/user/RGC_initialization.F90 | 6 +- src/user/dense_water_initialization.F90 | 6 +- src/user/dumbbell_initialization.F90 | 7 ++- 8 files changed, 79 insertions(+), 34 deletions(-) diff --git a/src/initialization/MOM_state_initialization.F90 b/src/initialization/MOM_state_initialization.F90 index b8e74e3c45..58a6f9ed8d 100644 --- a/src/initialization/MOM_state_initialization.F90 +++ b/src/initialization/MOM_state_initialization.F90 @@ -2135,9 +2135,11 @@ subroutine initialize_sponges_file(G, GV, US, use_temperature, tv, u, v, depth_t if (use_temperature) then allocate(tmp_tr(isd:ied,jsd:jed,nz_data)) call MOM_read_data(filename, potemp_var, tmp_tr(:,:,:), G%Domain, scale=US%degC_to_C) - call set_up_ALE_sponge_field(tmp_tr, G, GV, tv%T, ALE_CSp) + call set_up_ALE_sponge_field(tmp_tr, G, GV, tv%T, ALE_CSp, 'temp', & + sp_long_name='temperature', sp_unit='degC s-1') call MOM_read_data(filename, salin_var, tmp_tr(:,:,:), G%Domain, scale=US%ppt_to_S) - call set_up_ALE_sponge_field(tmp_tr, G, GV, tv%S, ALE_CSp) + call set_up_ALE_sponge_field(tmp_tr, G, GV, tv%S, ALE_CSp, 'salt', & + sp_long_name='salinity', sp_unit='g kg-1 s-1') deallocate(tmp_tr) endif if (sponge_uv) then @@ -2160,8 +2162,10 @@ subroutine initialize_sponges_file(G, GV, US, use_temperature, tv, u, v, depth_t endif ! The remaining calls to set_up_sponge_field can be in any order. if ( use_temperature) then - call set_up_ALE_sponge_field(filename, potemp_var, Time, G, GV, US, tv%T, ALE_CSp, scale=US%degC_to_C) - call set_up_ALE_sponge_field(filename, salin_var, Time, G, GV, US, tv%S, ALE_CSp, scale=US%ppt_to_S) + call set_up_ALE_sponge_field(filename, potemp_var, Time, G, GV, US, tv%T, ALE_CSp, & + 'temp', sp_long_name='temperature', sp_unit='degC s-1', scale=US%degC_to_C) + call set_up_ALE_sponge_field(filename, salin_var, Time, G, GV, US, tv%S, ALE_CSp, & + 'salt', sp_long_name='salinity', sp_unit='g kg-1 s-1', scale=US%ppt_to_S) endif if (sponge_uv) then filename = trim(inputdir)//trim(state_uv_file) diff --git a/src/parameterizations/vertical/MOM_ALE_sponge.F90 b/src/parameterizations/vertical/MOM_ALE_sponge.F90 index 1631a76dd6..9f5241bb9a 100644 --- a/src/parameterizations/vertical/MOM_ALE_sponge.F90 +++ b/src/parameterizations/vertical/MOM_ALE_sponge.F90 @@ -81,6 +81,9 @@ module MOM_ALE_sponge real :: scale = 1.0 !< A multiplicative factor by which to rescale input data real, dimension(:,:), pointer :: p => NULL() !< pointer the data. real, dimension(:,:), pointer :: h => NULL() !< pointer the data grid. + character(len=:), allocatable :: name !< The name of the input field + character(len=:), allocatable :: long_name !< The long name of the input field + character(len=:), allocatable :: unit !< The unit of the input field end type p2d !> ALE sponge control structure @@ -134,7 +137,7 @@ module MOM_ALE_sponge logical :: tripolar_N !< grid is folded at its north edge !>@{ Diagnostic IDs - integer, dimension(2) :: id_sp_tendency !< Diagnostic ids for temperature and salinity + integer, dimension(MAX_FIELDS_) :: id_sp_tendency !< Diagnostic ids for tracers !! tendency due to sponges integer :: id_sp_u_tendency !< Diagnostic id for zonal momentum tendency due to !! Rayleigh damping @@ -666,15 +669,19 @@ subroutine init_ALE_sponge_diags(Time, G, diag, CS, US) !! output. type(ALE_sponge_CS), intent(inout) :: CS !< ALE sponge control structure type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + ! Local Variables + integer :: m CS%diag => diag - CS%id_sp_tendency(1) = -1 - CS%id_sp_tendency(1) = register_diag_field('ocean_model', 'sp_tendency_temp', diag%axesTL, Time, & - 'Time tendency due to temperature restoring', 'degC s-1', conversion=US%s_to_T) - CS%id_sp_tendency(2) = -1 - CS%id_sp_tendency(2) = register_diag_field('ocean_model', 'sp_tendency_salt', diag%axesTL, Time, & - 'Time tendency due to salinity restoring', 'g kg-1 s-1', conversion=US%s_to_T) + do m=1,CS%fldno + CS%id_sp_tendency(m) = -1 + CS%id_sp_tendency(m) = register_diag_field('ocean_model', & + 'sp_tendency_' // CS%Ref_val(m)%name, diag%axesTL, Time, & + 'Time tendency due to restoring ' // CS%Ref_val(m)%long_name, & + CS%Ref_val(m)%unit, conversion=US%s_to_T) + enddo + CS%id_sp_u_tendency = -1 CS%id_sp_u_tendency = register_diag_field('ocean_model', 'sp_tendency_u', diag%axesCuL, Time, & 'Zonal acceleration due to sponges', 'm s-2', conversion=US%L_T2_to_m_s2) @@ -686,7 +693,8 @@ end subroutine init_ALE_sponge_diags !> This subroutine stores the reference profile at h points for the variable !! whose address is given by f_ptr. -subroutine set_up_ALE_sponge_field_fixed(sp_val, G, GV, f_ptr, CS, scale) +subroutine set_up_ALE_sponge_field_fixed(sp_val, G, GV, f_ptr, CS, & + sp_name, sp_long_name, sp_unit, scale) type(ocean_grid_type), intent(in) :: G !< Grid structure type(verticalGrid_type), intent(in) :: GV !< ocean vertical grid structure type(ALE_sponge_CS), pointer :: CS !< ALE sponge control structure (in/out). @@ -695,16 +703,27 @@ subroutine set_up_ALE_sponge_field_fixed(sp_val, G, GV, f_ptr, CS, scale) !! arbitrary number of layers. real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & target, intent(in) :: f_ptr !< Pointer to the field to be damped + character(len=*), intent(in) :: sp_name !< The name of the tracer field + character(len=*), optional, & + intent(in) :: sp_long_name !< The long name of the tracer field + !! if not given, use the sp_name + character(len=*), optional, & + intent(in) :: sp_unit !< The unit of the tracer field + !! if not given, use the none real, optional, intent(in) :: scale !< A factor by which to rescale the input data, including any !! contributions due to dimensional rescaling. The default is 1. real :: scale_fac ! A factor by which to scale sp_val before storing it. integer :: k, col character(len=256) :: mesg ! String for error messages + character(len=256) :: long_name ! The long name of the tracer field + character(len=256) :: unit ! The unit of the tracer field if (.not.associated(CS)) return scale_fac = 1.0 ; if (present(scale)) scale_fac = scale + long_name = sp_name; if (present(sp_long_name)) long_name = sp_long_name + unit = 'none'; if (present(sp_unit)) unit = sp_unit CS%fldno = CS%fldno + 1 if (CS%fldno > MAX_FIELDS_) then @@ -716,6 +735,9 @@ subroutine set_up_ALE_sponge_field_fixed(sp_val, G, GV, f_ptr, CS, scale) ! stores the reference profile CS%Ref_val(CS%fldno)%nz_data = CS%nz_data + CS%Ref_val(CS%fldno)%name = sp_name + CS%Ref_val(CS%fldno)%long_name = long_name + CS%Ref_val(CS%fldno)%unit = unit allocate(CS%Ref_val(CS%fldno)%p(CS%nz_data,CS%num_col), source=0.0) do col=1,CS%num_col do k=1,CS%nz_data @@ -729,7 +751,8 @@ end subroutine set_up_ALE_sponge_field_fixed !> This subroutine stores the reference profile at h points for the variable !! whose address is given by filename and fieldname. -subroutine set_up_ALE_sponge_field_varying(filename, fieldname, Time, G, GV, US, f_ptr, CS, scale) +subroutine set_up_ALE_sponge_field_varying(filename, fieldname, Time, G, GV, US, f_ptr, CS, & + sp_name, sp_long_name, sp_unit, scale) character(len=*), intent(in) :: filename !< The name of the file with the !! time varying field data character(len=*), intent(in) :: fieldname !< The name of the field in the file @@ -741,6 +764,13 @@ subroutine set_up_ALE_sponge_field_varying(filename, fieldname, Time, G, GV, US, real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & target, intent(in) :: f_ptr !< Pointer to the field to be damped (in). type(ALE_sponge_CS), pointer :: CS !< Sponge control structure (in/out). + character(len=*), intent(in) :: sp_name !< The name of the tracer field + character(len=*), optional, & + intent(in) :: sp_long_name !< The long name of the tracer field + !! if not given, use the sp_name + character(len=*), optional, & + intent(in) :: sp_unit !< The unit of the tracer field + !! if not given, use 'none' real, optional, intent(in) :: scale !< A factor by which to rescale the input data, including any !! contributions due to dimensional rescaling. The default is 1. @@ -749,6 +779,11 @@ subroutine set_up_ALE_sponge_field_varying(filename, fieldname, Time, G, GV, US, integer, dimension(4) :: fld_sz integer :: nz_data !< the number of vertical levels in this input field character(len=256) :: mesg ! String for error messages + character(len=256) :: long_name ! The long name of the tracer field + character(len=256) :: unit ! The unit of the tracer field + long_name = sp_name; if (present(sp_long_name)) long_name = sp_long_name + unit = 'none'; if (present(sp_unit)) unit = sp_unit + ! Local variables for ALE remapping if (.not.associated(CS)) return @@ -768,6 +803,9 @@ subroutine set_up_ALE_sponge_field_varying(filename, fieldname, Time, G, GV, US, else CS%Ref_val(CS%fldno)%id = init_external_field(filename, fieldname) endif + CS%Ref_val(CS%fldno)%name = sp_name + CS%Ref_val(CS%fldno)%long_name = long_name + CS%Ref_val(CS%fldno)%unit = unit fld_sz(1:4) = -1 call get_external_field_info(CS%Ref_val(CS%fldno)%id, size=fld_sz) nz_data = fld_sz(3) @@ -1290,7 +1328,8 @@ subroutine rotate_ALE_sponge(sponge_in, G_in, sponge, G, GV, turns, param_file) call rotate_array(sp_val_in, turns, sp_val) ! NOTE: This points sp_val with the unrotated field. See note below. - call set_up_ALE_sponge_field(sp_val, G, GV, sp_ptr, sponge) + call set_up_ALE_sponge_field(sp_val, G, GV, sp_ptr, sponge, & + sponge_in%Ref_val(n)%name, sp_long_name=sponge_in%Ref_val(n)%long_name, sp_unit=sponge_in%Ref_val(n)%unit) deallocate(sp_val_in) else diff --git a/src/tracer/RGC_tracer.F90 b/src/tracer/RGC_tracer.F90 index 6801269245..9ceadc602d 100644 --- a/src/tracer/RGC_tracer.F90 +++ b/src/tracer/RGC_tracer.F90 @@ -231,7 +231,7 @@ subroutine initialize_RGC_tracer(restart, day, G, GV, h, diag, OBC, CS, & do m=1,1 ! This is needed to force the compiler not to do a copy in the sponge calls. tr_ptr => CS%tr(:,:,:,m) - call set_up_ALE_sponge_field(temp, G, GV, tr_ptr, sponge_CSp) + call set_up_ALE_sponge_field(temp, G, GV, tr_ptr, sponge_CSp, 'RGC_tracer') enddo deallocate(temp) endif diff --git a/src/user/DOME2d_initialization.F90 b/src/user/DOME2d_initialization.F90 index d0ed88c128..a997cde26b 100644 --- a/src/user/DOME2d_initialization.F90 +++ b/src/user/DOME2d_initialization.F90 @@ -473,12 +473,10 @@ subroutine DOME2d_initialize_sponges(G, GV, US, tv, depth_tot, param_file, use_A enddo enddo ; enddo - if ( associated(tv%T) ) then - call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp) - endif - if ( associated(tv%S) ) then - call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp) - endif + if ( associated(tv%T) ) call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp, 'temp', & + sp_long_name='temperature', sp_unit='degC s-1') + if ( associated(tv%S) ) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp, 'salt', & + sp_long_name='salinity', sp_unit='g kg-1 s-1') else diff --git a/src/user/ISOMIP_initialization.F90 b/src/user/ISOMIP_initialization.F90 index 5e91eaa86a..aaededaa8c 100644 --- a/src/user/ISOMIP_initialization.F90 +++ b/src/user/ISOMIP_initialization.F90 @@ -626,12 +626,11 @@ subroutine ISOMIP_initialize_sponges(G, GV, US, tv, depth_tot, PF, use_ALE, CSp, ! momentum is typically not damped within the sponge. ! ! The remaining calls to set_up_sponge_field can be in any order. ! - if ( associated(tv%T) ) then - call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp) - endif - if ( associated(tv%S) ) then - call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp) - endif + if ( associated(tv%T) ) call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp, 'temp', & + sp_long_name='temperature', sp_unit='degC s-1') + if ( associated(tv%S) ) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp, 'salt', & + sp_long_name='salinity', sp_unit='g kg-1 s-1') + else ! layer mode ! 1) Read eta, salt and temp from IC file diff --git a/src/user/RGC_initialization.F90 b/src/user/RGC_initialization.F90 index d9c1846a0e..b56e0b895a 100644 --- a/src/user/RGC_initialization.F90 +++ b/src/user/RGC_initialization.F90 @@ -168,8 +168,10 @@ subroutine RGC_initialize_sponges(G, GV, US, tv, u, v, depth_tot, PF, use_ALE, C call initialize_ALE_sponge(Idamp, G, GV, PF, ACSp, h, nz) ! The remaining calls to set_up_sponge_field can be in any order. - if ( associated(tv%T) ) call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp) - if ( associated(tv%S) ) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp) + if ( associated(tv%T) ) call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp, 'temp', & + sp_long_name='temperature', sp_unit='degC s-1') + if ( associated(tv%S) ) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp, 'salt', & + sp_long_name='salinity', sp_unit='g kg-1 s-1') if (sponge_uv) then U1(:,:,:) = 0.0 ; V1(:,:,:) = 0.0 diff --git a/src/user/dense_water_initialization.F90 b/src/user/dense_water_initialization.F90 index fa44a78604..a81c400256 100644 --- a/src/user/dense_water_initialization.F90 +++ b/src/user/dense_water_initialization.F90 @@ -279,8 +279,10 @@ subroutine dense_water_initialize_sponges(G, GV, US, tv, depth_tot, param_file, enddo enddo - if (associated(tv%T)) call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp) - if (associated(tv%S)) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp) + if ( associated(tv%T) ) call set_up_ALE_sponge_field(T, G, GV, tv%T, ACSp, 'temp', & + sp_long_name='temperature', sp_unit='degC s-1') + if ( associated(tv%S) ) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp, 'salt', & + sp_long_name='salinity', sp_unit='g kg-1 s-1') else call MOM_error(FATAL, "dense_water_initialize_sponges: trying to use non ALE sponge") endif diff --git a/src/user/dumbbell_initialization.F90 b/src/user/dumbbell_initialization.F90 index e4ce7e77f5..762477b6c4 100644 --- a/src/user/dumbbell_initialization.F90 +++ b/src/user/dumbbell_initialization.F90 @@ -446,12 +446,13 @@ subroutine dumbbell_initialize_sponges(G, GV, US, tv, h_in, depth_tot, param_fil enddo endif enddo ; enddo - if (associated(tv%S)) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp) + if (associated(tv%S)) call set_up_ALE_sponge_field(S, G, GV, tv%S, ACSp, 'salt', & + sp_long_name='salinity', sp_unit='g kg-1 s-1') else do j=G%jsc,G%jec ; do i=G%isc,G%iec eta(i,j,1) = 0.0 do k=2,nz - eta(i,j,k) = eta(i,j,k-1)- GV%H_to_Z * h_in(i,j,k-1) + eta(i,j,k) = eta(i,j,k-1) - GV%H_to_Z * h_in(i,j,k-1) enddo eta(i,j,nz+1) = -depth_tot(i,j) do k=1,nz @@ -469,4 +470,4 @@ subroutine dumbbell_initialize_sponges(G, GV, US, tv, h_in, depth_tot, param_fil end subroutine dumbbell_initialize_sponges -end module dumbbell_initialization +end module dumbbell_initialization \ No newline at end of file From 9c5f77e9ef72c50b23adeeae7f3c556e4f4079cb Mon Sep 17 00:00:00 2001 From: Matthew Harrison Date: Mon, 8 Aug 2022 20:02:58 -0400 Subject: [PATCH 04/68] Fix ice shelf initialization - Ice shelf needs to be initialized prior to ocean state initialization. This fixes any cases with an ice shelf using the FMScap. --- config_src/drivers/FMS_cap/ocean_model_MOM.F90 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/config_src/drivers/FMS_cap/ocean_model_MOM.F90 b/config_src/drivers/FMS_cap/ocean_model_MOM.F90 index b6bb14fc01..a12ab35240 100644 --- a/config_src/drivers/FMS_cap/ocean_model_MOM.F90 +++ b/config_src/drivers/FMS_cap/ocean_model_MOM.F90 @@ -53,6 +53,7 @@ module ocean_model_mod use MOM_variables, only : surface use MOM_verticalGrid, only : verticalGrid_type use MOM_ice_shelf, only : initialize_ice_shelf, shelf_calc_flux, ice_shelf_CS +use MOM_ice_shelf, only : initialize_ice_shelf_fluxes, initialize_ice_shelf_forces use MOM_ice_shelf, only : add_shelf_forces, ice_shelf_end, ice_shelf_save_restart use MOM_wave_interface, only: wave_parameters_CS, MOM_wave_interface_init use MOM_wave_interface, only: Update_Surface_Waves @@ -274,9 +275,13 @@ subroutine ocean_model_init(Ocean_sfc, OS, Time_init, Time_in, wind_stagger, gas if (.not.OS%is_ocean_pe) return OS%Time = Time_in ; OS%Time_dyn = Time_in + ! Call initialize MOM with an optional Ice Shelf CS which, if present triggers + ! initialization of ice shelf parameters and arrays. + call initialize_MOM(OS%Time, Time_init, param_file, OS%dirs, OS%MOM_CSp, & OS%restart_CSp, Time_in, offline_tracer_mode=OS%offline_tracer_mode, & - diag_ptr=OS%diag, count_calls=.true., waves_CSp=OS%Waves) + diag_ptr=OS%diag, count_calls=.true., ice_shelf_CSp=OS%ice_shelf_CSp, & + waves_CSp=OS%Waves) call get_MOM_state_elements(OS%MOM_CSp, G=OS%grid, GV=OS%GV, US=OS%US, C_p=OS%C_p, & C_p_scaled=OS%fluxes%C_p, use_temp=use_temperature) @@ -372,9 +377,10 @@ subroutine ocean_model_init(Ocean_sfc, OS, Time_init, Time_in, wind_stagger, gas endif if (OS%use_ice_shelf) then - call initialize_ice_shelf(param_file, OS%grid, OS%Time, OS%ice_shelf_CSp, & - OS%diag, OS%forces, OS%fluxes) + call initialize_ice_shelf_fluxes(OS%ice_shelf_CSp, OS%grid, OS%US, OS%fluxes) + call initialize_ice_shelf_forces(OS%ice_shelf_CSp, OS%grid, OS%US, OS%forces) endif + if (OS%icebergs_alter_ocean) then call marine_ice_init(OS%Time, OS%grid, param_file, OS%diag, OS%marine_ice_CSp) if (.not. OS%use_ice_shelf) & From e3c71b070dce3da60d2b43cf55728da41a0a30ae Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Sat, 6 Aug 2022 11:28:34 -0400 Subject: [PATCH 05/68] +Add runtime parameter CHANNEL_DRAG_MAX_BBL_THICK Added the new runtime parameter CHANNEL_DRAG_MAX_BBL_THICK, to allow the CHANNEL_DRAG parameterization to use the full dynamically determined depth of the bottom boundary layer, or to use a fixed maximum thickness, regardless of the settings of other parameters, although for now the default value is set based on the settings of USE_JACKSON_PARAM and DRAG_AS_BODY_FORCE in order to reproduce existing solutions by default. There are new entries in some MOM_parameter_doc files. All answers in the MOM6-examples test suite are bitwise identical, and this test suite has been tested and works with revised values of this parameter. --- .../vertical/MOM_set_viscosity.F90 | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/parameterizations/vertical/MOM_set_viscosity.F90 b/src/parameterizations/vertical/MOM_set_viscosity.F90 index 9bd995633f..448ae079f4 100644 --- a/src/parameterizations/vertical/MOM_set_viscosity.F90 +++ b/src/parameterizations/vertical/MOM_set_viscosity.F90 @@ -74,25 +74,27 @@ module MOM_set_visc !! the properties of the bottom boundary layer. logical :: linear_drag !< If true, the drag law is cdrag*`DRAG_BG_VEL`*u. !! Runtime parameter `LINEAR_DRAG`. - logical :: Channel_drag !< If true, the drag is exerted directly on each - !! layer according to what fraction of the bottom - !! they overlie. + logical :: Channel_drag !< If true, the drag is exerted directly on each layer + !! according to what fraction of the bottom they overlie. + real :: Chan_drag_max_vol !< The maximum bottom boundary layer volume within which the + !! channel drag is applied, normalized by the the full cell area, + !! or a negative value to apply no maximum [H ~> m or kg m-2]. logical :: correct_BBL_bounds !< If true, uses the correct bounds on the BBL thickness and !! viscosity so that the bottom layer feels the intended drag. logical :: RiNo_mix !< If true, use Richardson number dependent mixing. logical :: dynamic_viscous_ML !< If true, use a bulk Richardson number criterion to !! determine the mixed layer thickness for viscosity. real :: bulk_Ri_ML !< The bulk mixed layer used to determine the - !! thickness of the viscous mixed layer. Nondim. + !! thickness of the viscous mixed layer [nondim] real :: omega !< The Earth's rotation rate [T-1 ~> s-1]. real :: ustar_min !< A minimum value of ustar to avoid numerical !! problems [Z T-1 ~> m s-1]. If the value is small enough, !! this should not affect the solution. real :: TKE_decay !< The ratio of the natural Ekman depth to the TKE - !! decay scale, nondimensional. - real :: omega_frac !< When setting the decay scale for turbulence, use - !! this fraction of the absolute rotation rate blended - !! with the local value of f, as sqrt((1-of)*f^2 + of*4*omega^2). + !! decay scale [nondim] + real :: omega_frac !< When setting the decay scale for turbulence, use this + !! fraction of the absolute rotation rate blended with the local + !! value of f, as sqrt((1-of)*f^2 + of*4*omega^2) [nondim] integer :: answer_date !< The vintage of the order of arithmetic and expressions in the set !! viscosity calculations. Values below 20190101 recover the answers !! from the end of 2018, while higher values use updated and more robust @@ -233,6 +235,9 @@ subroutine set_viscous_BBL(u, v, h, tv, visc, G, GV, US, CS, pbv) real :: a2x48_apb3, Iapb, Ibma_2 ! Combinations of a and slope [H-1 ~> m-1 or m2 kg-1]. ! All of the following "volumes" have units of thickness because they are normalized ! by the full horizontal area of a velocity cell. + real :: Vol_bbl_chan ! The volume of the bottom boundary layer as used in the channel + ! drag parameterization, normalized by the full horizontal area + ! of the velocity cell [H ~> m or kg m-2]. real :: Vol_open ! The cell volume above which it is open [H ~> m or kg m-2]. real :: Vol_direct ! With less than Vol_direct [H ~> m or kg m-2], there is a direct ! solution of a cubic equation for L. @@ -733,6 +738,9 @@ subroutine set_viscous_BBL(u, v, h, tv, visc, G, GV, US, CS, pbv) if (bbl_thick < CS%BBL_thick_min) bbl_thick = CS%BBL_thick_min endif + ! Store the normalized bottom boundary layer volume. + if (CS%Channel_drag) Vol_bbl_chan = bbl_thick + ! If there is Richardson number dependent mixing, that determines ! the vertical extent of the bottom boundary layer, and there is no ! need to set that scale here. In fact, viscously reducing the @@ -746,10 +754,13 @@ subroutine set_viscous_BBL(u, v, h, tv, visc, G, GV, US, CS, pbv) if (CS%body_force_drag) bbl_thick = h_bbl_drag(i) if (CS%Channel_drag) then - ! The drag within the bottommost bbl_thick is applied as a part of + ! The drag within the bottommost Vol_bbl_chan is applied as a part of ! an enhanced bottom viscosity, while above this the drag is applied ! directly to the layers in question as a Rayleigh drag term. + ! Restrict the volume over which the channel drag is applied. + if (CS%Chan_drag_max_vol >= 0.0) Vol_bbl_chan = min(Vol_bbl_chan, CS%Chan_drag_max_vol) + !### The harmonic mean edge depths here are not invariant to offsets! if (m==1) then D_vel = D_u(I,j) @@ -931,8 +942,8 @@ subroutine set_viscous_BBL(u, v, h, tv, visc, G, GV, US, CS, pbv) ! Determine the drag contributing to the bottom boundary layer ! and the Rayleigh drag that acts on each layer. if (L(K) > L(K+1)) then - if (vol_below < bbl_thick) then - BBL_frac = (1.0-vol_below/bbl_thick)**2 + if (vol_below < Vol_bbl_chan) then + BBL_frac = (1.0-vol_below/Vol_bbl_chan)**2 BBL_visc_frac = BBL_visc_frac + BBL_frac*(L(K) - L(K+1)) else BBL_frac = 0.0 @@ -1957,6 +1968,8 @@ subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS real :: omega_frac_dflt ! The default value for the fraction of the absolute rotation rate that ! is used in place of the absolute value of the local Coriolis ! parameter in the denominator of some expressions [nondim] + real :: Chan_max_thick_dflt ! The default value for CHANNEL_DRAG_MAX_THICK [m] + real :: Z_rescale ! A rescaling factor for heights from the representation in ! a restart file to the internal representation in this run. real :: I_T_rescale ! A rescaling factor for time from the internal representation in this run @@ -2154,9 +2167,6 @@ subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS "The thickness over which near-surface velocities are "//& "averaged for the drag law under an ice shelf. By "//& "default this is the same as HBBL", units="m", default=CS%Hbbl, scale=GV%m_to_H) - ! These unit conversions are out outside the get_param calls because the are also defaults. - CS%Hbbl = CS%Hbbl * GV%m_to_H ! Rescale - CS%BBL_thick_min = CS%BBL_thick_min * GV%m_to_H ! Rescale call get_param(param_file, mdl, "KV", Kv_background, & "The background kinematic viscosity in the interior. "//& @@ -2195,9 +2205,24 @@ subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS if (CS%c_Smag < 0.0) CS%c_Smag = 0.15 endif + Chan_max_thick_dflt = -1.0 + if (CS%RiNo_mix) Chan_max_thick_dflt = 0.5*CS%Hbbl + if (CS%body_force_drag) Chan_max_thick_dflt = CS%Hbbl + call get_param(param_file, mdl, "CHANNEL_DRAG_MAX_BBL_THICK", CS%Chan_drag_max_vol, & + "The maximum bottom boundary layer thickness over which the channel drag is "//& + "exerted, or a negative value for no fixed limit, instead basing the BBL "//& + "thickness on the bottom stress, rotation and stratification. The default is "//& + "proportional to HBBL if USE_JACKSON_PARAM or DRAG_AS_BODY_FORCE is true.", & + units="m", default=Chan_max_thick_dflt, scale=GV%m_to_H, & + do_not_log=.not.CS%Channel_drag) + call get_param(param_file, mdl, "MLE_USE_PBL_MLD", MLE_use_PBL_MLD, & default=.false., do_not_log=.true.) + ! These unit conversions are out outside the get_param calls because they are also defaults. + CS%Hbbl = CS%Hbbl * GV%m_to_H ! Rescale + CS%BBL_thick_min = CS%BBL_thick_min * GV%m_to_H ! Rescale + if (CS%RiNo_mix .and. kappa_shear_at_vertex(param_file)) then ! This is necessary for reproduciblity across restarts in non-symmetric mode. call pass_var(visc%Kv_shear_Bu, G%Domain, position=CORNER, complete=.true.) From 6ec8f5a88483cf505e9da303145656d9b193b856 Mon Sep 17 00:00:00 2001 From: pjpegion Date: Thu, 25 Aug 2022 17:47:10 +0000 Subject: [PATCH 06/68] change t-freeze calcualtion for sppt --- .../vertical/MOM_diabatic_driver.F90 | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/parameterizations/vertical/MOM_diabatic_driver.F90 b/src/parameterizations/vertical/MOM_diabatic_driver.F90 index b3773fcce8..0cab88d21b 100644 --- a/src/parameterizations/vertical/MOM_diabatic_driver.F90 +++ b/src/parameterizations/vertical/MOM_diabatic_driver.F90 @@ -299,6 +299,11 @@ subroutine diabatic(u, v, h, tv, Hml, fluxes, visc, ADp, CDp, dt, Time_end, & real, dimension(SZI_(G),SZJ_(G),CS%nMode) :: & cn_IGW ! baroclinic internal gravity wave speeds [L T-1 ~> m s-1] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: temp_diag ! Previous temperature for diagnostics [C ~> degC] + real, dimension(SZI_(G)) :: T_freeze, & ! The freezing potential temperature at the current salinity [C ~> degC]. + ps ! Surface pressure [R L2 T-2 ~> Pa] + real, dimension(SZI_(G),SZK_(GV)) :: & + pressure ! The pressure at the middle of each layer [R L2 T-2 ~> Pa]. + real :: H_to_RL2_T2 ! A conversion factor from thicknesses in H to pressure [R L2 T-2 H-1 ~> Pa m-1 or Pa m2 kg-1] integer :: i, j, k, m, is, ie, js, je, nz logical :: showCallTree ! If true, show the call tree @@ -447,11 +452,34 @@ subroutine diabatic(u, v, h, tv, Hml, fluxes, visc, ADp, CDp, dt, Time_end, & if (stoch_CS%do_sppt) then ! perturb diabatic tendencies. ! These stochastic perturbations do not conserve heat, salt or mass. - do k=1,nz ; do j=js,je ; do i=is,ie - h(i,j,k) = max(h_in(i,j,k) + (h(i,j,k)-h_in(i,j,k)) * stoch_CS%sppt_wts(i,j), GV%Angstrom_H) - tv%S(i,j,k) = max(s_in(i,j,k) + (tv%S(i,j,k)-s_in(i,j,k)) * stoch_CS%sppt_wts(i,j), 0.0) - tv%T(i,j,k) = max(t_in(i,j,k) + (tv%T(i,j,k)-t_in(i,j,k)) * stoch_CS%sppt_wts(i,j), -0.054*tv%S(i,j,k)) - enddo ; enddo ; enddo + do k=1,nz; do j=js,je; do i=is,ie + h(i,j,k) = max(h_in(i,j,k) + (h(i,j,k)-h_in(i,j,k)) * stoch_CS%sppt_wts(i,j), GV%Angstrom_H) + tv%S(i,j,k) = max(s_in(i,j,k) + (tv%S(i,j,k)-s_in(i,j,k)) * stoch_CS%sppt_wts(i,j), 0.0) + enddo; enddo; enddo + ! now that we have updated thickness and salinity, calculate freeing point + H_to_RL2_T2 = GV%H_to_RZ * GV%g_Earth + do j=js,je + ps(:) = 0.0 + if (associated(fluxes%p_surf)) then ; do i=is,ie + ps(i) = fluxes%p_surf(i,j) + enddo ; endif + + do i=is,ie + pressure(i,1) = ps(i) + (0.5*H_to_RL2_T2)*h(i,j,1) + enddo + do k=2,nz ; do i=is,ie + pressure(i,k) = pressure(i,k-1) + & + (0.5*H_to_RL2_T2) * (h(i,j,k) + h(i,j,k-1)) + enddo ; enddo + do k=1,nz + call calculate_TFreeze(tv%S(is:ie,j,k), pressure(is:ie,k), T_freeze(is:ie), & + tv%eqn_of_state) + do i=is,ie + tv%T(i,j,k) = max(t_in(i,j,k) + (tv%T(i,j,k)-t_in(i,j,k)) * stoch_CS%sppt_wts(i,j), T_freeze(i)) + enddo + enddo + enddo + deallocate(h_in, t_in, s_in) endif From 3db3882c9114d6766d7bd40579dc149c9c930f53 Mon Sep 17 00:00:00 2001 From: He Wang Date: Fri, 15 Apr 2022 21:15:11 -0400 Subject: [PATCH 07/68] Change porous_barrier_ptrs to allocatable * Porous barrier variables, which are grouped in porous_barrier_ptrs, are changed from pointers to allocatables. * Copies (aliases) of these variables in MOM_CS are removed. * The name porous_barrier_ptrs is yet to be changed to minimize the number of files needed to be edited. --- src/core/MOM.F90 | 21 +++++++-------------- src/core/MOM_variables.F90 | 15 ++++++++------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 78170064ff..94ce1cb39f 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -412,12 +412,6 @@ module MOM !! increments and priors type(dbcomms_CS_type) :: dbcomms_CS !< Control structure for database client used for online ML/AI type(porous_barrier_ptrs) :: pbv !< porous barrier fractional cell metrics - real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_,NKMEM_) :: por_face_areaU !< fractional open area of U-faces [nondim] - real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_,NKMEM_) :: por_face_areaV !< fractional open area of V-faces [nondim] - real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_,NK_INTERFACE_) :: por_layer_widthU !< fractional open width - !! of U-faces [nondim] - real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_,NK_INTERFACE_) :: por_layer_widthV !< fractional open width - !! of V-faces [nondim] type(particles), pointer :: particles => NULL() ! NULL() !< a pointer to the stochastics control structure end type MOM_control_struct @@ -2478,12 +2472,11 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & CS%time_in_cycle = 0.0 ; CS%time_in_thermo_cycle = 0.0 !allocate porous topography variables - ALLOC_(CS%por_face_areaU(IsdB:IedB,jsd:jed,nz)) ; CS%por_face_areaU(:,:,:) = 1.0 - ALLOC_(CS%por_face_areaV(isd:ied,JsdB:JedB,nz)) ; CS%por_face_areaV(:,:,:) = 1.0 - ALLOC_(CS%por_layer_widthU(IsdB:IedB,jsd:jed,nz+1)) ; CS%por_layer_widthU(:,:,:) = 1.0 - ALLOC_(CS%por_layer_widthV(isd:ied,JsdB:JedB,nz+1)) ; CS%por_layer_widthV(:,:,:) = 1.0 - CS%pbv%por_face_areaU => CS%por_face_areaU; CS%pbv%por_face_areaV=> CS%por_face_areaV - CS%pbv%por_layer_widthU => CS%por_layer_widthU; CS%pbv%por_layer_widthV => CS%por_layer_widthV + ALLOC_(CS%pbv%por_face_areaU(IsdB:IedB,jsd:jed,nz)) ; CS%pbv%por_face_areaU(:,:,:) = 1.0 + ALLOC_(CS%pbv%por_face_areaV(isd:ied,JsdB:JedB,nz)) ; CS%pbv%por_face_areaV(:,:,:) = 1.0 + ALLOC_(CS%pbv%por_layer_widthU(IsdB:IedB,jsd:jed,nz+1)) ; CS%pbv%por_layer_widthU(:,:,:) = 1.0 + ALLOC_(CS%pbv%por_layer_widthV(isd:ied,JsdB:JedB,nz+1)) ; CS%pbv%por_layer_widthV(:,:,:) = 1.0 + ! Use the Wright equation of state by default, unless otherwise specified ! Note: this line and the following block ought to be in a separate ! initialization routine for tv. @@ -3775,8 +3768,8 @@ subroutine MOM_end(CS) if (CS%use_ALE_algorithm) call ALE_end(CS%ALE_CSp) !deallocate porous topography variables - DEALLOC_(CS%por_face_areaU) ; DEALLOC_(CS%por_face_areaV) - DEALLOC_(CS%por_layer_widthU) ; DEALLOC_(CS%por_layer_widthV) + DEALLOC_(CS%pbv%por_face_areaU) ; DEALLOC_(CS%pbv%por_face_areaV) + DEALLOC_(CS%pbv%por_layer_widthU) ; DEALLOC_(CS%pbv%por_layer_widthV) ! NOTE: Allocated in PressureForce_FV_Bouss if (associated(CS%tv%varT)) deallocate(CS%tv%varT) diff --git a/src/core/MOM_variables.F90 b/src/core/MOM_variables.F90 index a6f9d79fe6..ef90b0b3c5 100644 --- a/src/core/MOM_variables.F90 +++ b/src/core/MOM_variables.F90 @@ -309,16 +309,17 @@ module MOM_variables type(group_pass_type) :: pass_FA_uv !< Structure for face area group halo updates end type BT_cont_type - -!> pointers to grids modifying cell metric at porous barriers +!> Container for grids modifying cell metric at porous barriers +! TODO: rename porous_barrier_ptrs to porous_barrier_type type, public :: porous_barrier_ptrs - real, pointer, dimension(:,:,:) :: por_face_areaU => NULL() !< fractional open area of U-faces [nondim] - real, pointer, dimension(:,:,:) :: por_face_areaV => NULL() !< fractional open area of V-faces [nondim] - real, pointer, dimension(:,:,:) :: por_layer_widthU => NULL() !< fractional open width of U-faces [nondim] - real, pointer, dimension(:,:,:) :: por_layer_widthV => NULL() !< fractional open width of V-faces [nondim] + ! Each of the following fields has nz layers. + real, allocatable :: por_face_areaU(:,:,:) !< fractional open area of U-faces [nondim] + real, allocatable :: por_face_areaV(:,:,:) !< fractional open area of V-faces [nondim] + ! Each of the following fields is found at nz+1 interfaces. + real, allocatable :: por_layer_widthU(:,:,:) !< fractional open width of U-faces [nondim] + real, allocatable :: por_layer_widthV(:,:,:) !< fractional open width of V-faces [nondim] end type porous_barrier_ptrs - contains !> Allocates the fields for the surface (return) properties of From e5cfaa990c756b4a3a0710ac8afddaffd2ed8d1e Mon Sep 17 00:00:00 2001 From: He Wang Date: Sat, 16 Apr 2022 15:57:07 -0400 Subject: [PATCH 08/68] Add a control structure for porous barrier * The interface porous_widths is deleted and subroutine por_widths is renamed as porous_widths. * porous_barrier_CS is added to control input and diagnostics of the module * Diagnostics for both interface and layer averages weights are added in subroutine porous_widths. * An _init subroutine is added to facilitate reading parameters and registering diagnostics * checksum debugs are added within subroutine porous_widths. --- src/core/MOM.F90 | 15 ++++--- src/core/MOM_porous_barriers.F90 | 68 ++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 94ce1cb39f..7e50be1de6 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -103,6 +103,7 @@ module MOM use MOM_open_boundary, only : open_boundary_register_restarts use MOM_open_boundary, only : update_segment_tracer_reservoirs use MOM_open_boundary, only : rotate_OBC_config, rotate_OBC_init +use MOM_porous_barriers, only : porous_barrier_CS, porous_widths, porous_barriers_init use MOM_set_visc, only : set_viscous_BBL, set_viscous_ML use MOM_set_visc, only : set_visc_register_restarts, set_visc_CS use MOM_set_visc, only : set_visc_init, set_visc_end @@ -141,8 +142,6 @@ module MOM use MOM_wave_interface, only : wave_parameters_CS, waves_end, waves_register_restarts use MOM_wave_interface, only : Update_Stokes_Drift -use MOM_porous_barriers, only : porous_widths - ! Database client used for machine-learning interface use MOM_database_comms, only : dbcomms_CS_type, database_comms_init, dbclient_type @@ -403,6 +402,8 @@ module MOM !< Pointer to the MOM diagnostics control structure type(offline_transport_CS), pointer :: offline_CSp => NULL() !< Pointer to the offline tracer transport control structure + type(porous_barrier_CS) :: por_bar_CS + !< Control structure for porous barrier logical :: ensemble_ocean !< if true, this run is part of a !! larger ensemble for the purpose of data assimilation @@ -1089,8 +1090,10 @@ subroutine step_MOM_dynamics(forces, p_surf_begin, p_surf_end, dt, dt_thermo, & call diag_update_remap_grids(CS%diag) endif - !update porous barrier fractional cell metrics - call porous_widths(h, CS%tv, G, GV, US, eta_por, CS%pbv) + ! Update porous barrier fractional cell metrics + call enable_averages(dt, Time_local, CS%diag) + call porous_widths(h, CS%tv, G, GV, US, eta_por, CS%pbv, CS%por_bar_CS) + call disable_averaging(CS%diag) ! The bottom boundary layer properties need to be recalculated. if (bbl_time_int > 0.0) then @@ -1417,7 +1420,7 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & ! and set_viscous_BBL is called as a part of the dynamic stepping. call cpu_clock_begin(id_clock_BBL_visc) !update porous barrier fractional cell metrics - call porous_widths(h, CS%tv, G, GV, US, eta_por, CS%pbv) + call porous_widths(h, CS%tv, G, GV, US, eta_por, CS%pbv, CS%por_bar_CS) call set_viscous_BBL(u, v, h, tv, CS%visc, G, GV, US, CS%set_visc_CSp, CS%pbv) call cpu_clock_end(id_clock_BBL_visc) if (showCallTree) call callTree_wayPoint("done with set_viscous_BBL (step_MOM_thermo)") @@ -2815,6 +2818,8 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & new_sim = is_new_run(restart_CSp) call MOM_stoch_eos_init(G,Time,param_file,CS%stoch_eos_CS,restart_CSp,diag) + call porous_barriers_init(Time, US, param_file, diag, CS%por_bar_CS) + if (CS%split) then allocate(eta(SZI_(G),SZJ_(G)), source=0.0) call initialize_dyn_split_RK2(CS%u, CS%v, CS%h, CS%uh, CS%vh, eta, Time, & diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index e807f19484..139baf0320 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -10,22 +10,30 @@ module MOM_porous_barriers use MOM_variables, only : thermo_var_ptrs, porous_barrier_ptrs use MOM_verticalGrid, only : verticalGrid_type use MOM_interface_heights, only : find_eta +use MOM_time_manager, only : time_type +use MOM_diag_mediator, only : register_diag_field, diag_ctrl, post_data +use MOM_file_parser, only : param_file_type, get_param +use MOM_unit_scaling, only : unit_scale_type +use MOM_debugging, only : hchksum, uvchksum implicit none ; private -#include +public porous_widths, porous_barriers_init -public porous_widths +#include -!> Calculates curve fit from D_min, D_max, D_avg -interface porous_widths - module procedure por_widths, calc_por_layer -end interface porous_widths +type, public :: porous_barrier_CS; private + logical :: initialized = .false. !< True if this control structure has been initialized. + type(diag_ctrl), pointer :: diag => Null() !< A structure to regulate diagnostic output timing + logical :: debug !< If true, write verbose checksums for debugging purposes. + real :: mask_depth !< The depth below which porous barrier is not applied. + integer :: id_por_layer_widthU = -1, id_por_layer_widthV = -1, id_por_face_areaU = -1, id_por_face_areaV = -1 +end type porous_barrier_CS contains !> subroutine to assign cell face areas and layer widths for porous topography -subroutine por_widths(h, tv, G, GV, US, eta, pbv, eta_bt, halo_size, eta_to_m) +subroutine porous_widths(h, tv, G, GV, US, eta, pbv, CS, eta_bt, halo_size, eta_to_m) !eta_bt, halo_size, eta_to_m not currently used !variables needed to call find_eta type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. @@ -46,6 +54,7 @@ subroutine por_widths(h, tv, G, GV, US, eta, pbv, eta_bt, halo_size, eta_to_m) real, optional, intent(in) :: eta_to_m !< The conversion factor from !! the units of eta to m; by default this is US%Z_to_m. type(porous_barrier_ptrs), intent(inout) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_CS), intent(in) :: CS !local variables integer i, j, k, nk, isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB @@ -54,6 +63,10 @@ subroutine por_widths(h, tv, G, GV, US, eta, pbv, eta_bt, halo_size, eta_to_m) A_layer_prev, & ! integral of fractional open width from bottom to previous layer [Z ~> m] eta_s, & ! layer height used for fit [Z ~> m] eta_prev ! interface height of previous layer [Z ~> m] + + if (.not.CS%initialized) call MOM_error(FATAL, & + "MOM_Porous_barrier: Module must be initialized before it is used.") + isd = G%isd; ied = G%ied; jsd = G%jsd; jed = G%jed IsdB = G%IsdB; IedB = G%IedB; JsdB = G%JsdB; JedB = G%JedB @@ -118,7 +131,19 @@ subroutine por_widths(h, tv, G, GV, US, eta, pbv, eta_bt, halo_size, eta_to_m) endif enddo; enddo -end subroutine por_widths + if (CS%debug) then + call hchksum(eta, "Interface height used by porous barrier", G%HI, haloshift=0, scale=GV%H_to_m) + call uvchksum("Porous barrier weights at the layer-interface: por_layer_width[UV]", & + pbv%por_layer_widthU, pbv%por_layer_widthV, G%HI, haloshift=0) + call uvchksum("Porous barrier layer-averaged weights: por_face_area[UV]", & + pbv%por_face_areaU, pbv%por_face_areaV, G%HI, haloshift=0) + endif + + if (CS%id_por_layer_widthU > 0) call post_data(CS%id_por_layer_widthU, pbv%por_layer_widthU, CS%diag) + if (CS%id_por_layer_widthV > 0) call post_data(CS%id_por_layer_widthV, pbv%por_layer_widthV, CS%diag) + if (CS%id_por_face_areaU > 0) call post_data(CS%id_por_face_areaU, pbv%por_face_areaU, CS%diag) + if (CS%id_por_face_areaV > 0) call post_data(CS%id_por_face_areaV, pbv%por_face_areaV, CS%diag) +end subroutine porous_widths !> subroutine to calculate the profile fit for a single layer in a column subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, w_layer, A_layer) @@ -165,4 +190,31 @@ subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, w_layer, A_layer) end subroutine calc_por_layer +subroutine porous_barriers_init(Time, US, param_file, diag, CS) + type(porous_barrier_CS), intent(inout) :: CS + type(param_file_type), intent(in) :: param_file !< structure indicating parameter file to parse + type(time_type), intent(in) :: Time !< Current model time + type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure + type(unit_scale_type), intent(in) :: US + + character(len=40) :: mdl = "MOM_porous_barriers" ! This module's name. + CS%initialized = .true. + CS%diag => diag + + call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.) + call get_param(param_file, mdl, "POROUS_BARRIER_MASKING_DEPTH", CS%mask_depth, & + "The depth below which porous barrier is not applied. "//& + "This criterion is tested against TOPO_AT_VEL_VARNAME_U_AVE and TOPO_AT_VEL_VARNAME_V_AVE.", & + units="m", default=0.0, scale=US%m_to_Z) + + CS%id_por_layer_widthU = register_diag_field('ocean_model', 'por_layer_widthU', diag%axesCui, Time, & + 'Porous barrier open width fraction (at the layer interfaces) of the u-faces', 'nondim') + CS%id_por_layer_widthV = register_diag_field('ocean_model', 'por_layer_widthV', diag%axesCvi, Time, & + 'Porous barrier open width fraction (at the layer interfaces) of the v-faces', 'nondim') + CS%id_por_face_areaU = register_diag_field('ocean_model', 'por_face_areaU', diag%axesCuL, Time, & + 'Porous barrier open area fraction (layer averaged) of U-faces', 'nondim') + CS%id_por_face_areaV = register_diag_field('ocean_model', 'por_face_areaV', diag%axesCvL, Time, & + 'Porous barrier open area fraction (layer averaged) of V-faces', 'nondim') +end subroutine + end module MOM_porous_barriers From 00b48f13897bfd5a5ab7d1ae31ed5f864200afd1 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 20 Apr 2022 16:17:12 -0400 Subject: [PATCH 09/68] Fix porous_widths loop range This commit primarily fixes indexing bugs in subroutine porous_widths. * Loop range is subroutine porous_widths is changed from data domain to computation domain. * find_eta call is now with a proper halo to cover all velocity points. * Halo update for porous barrier fields is added in step_MOM_*. Other changes: * mask_depth, a component of porous_barrier_CS is now used to as the criterion for masking. This removes the limit that the cell edge depth has to be below sea surface. * Output variable eta_cor was unused and is now changed to a local. * Some unused indexing variables are removed. --- src/core/MOM.F90 | 15 +++++------ src/core/MOM_porous_barriers.F90 | 43 ++++++++++++++++---------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 7e50be1de6..ea5baf6116 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -1051,8 +1051,6 @@ subroutine step_MOM_dynamics(forces, p_surf_begin, p_surf_end, dt, dt_thermo, & integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB - real, dimension(SZI_(CS%G),SZJ_(CS%G),SZK_(CS%G)+1) :: eta_por ! layer interface heights - !! for porous topo. [Z ~> m or 1/eta_to_m] G => CS%G ; GV => CS%GV ; US => CS%US ; IDs => CS%IDs is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB @@ -1092,8 +1090,12 @@ subroutine step_MOM_dynamics(forces, p_surf_begin, p_surf_end, dt, dt_thermo, & ! Update porous barrier fractional cell metrics call enable_averages(dt, Time_local, CS%diag) - call porous_widths(h, CS%tv, G, GV, US, eta_por, CS%pbv, CS%por_bar_CS) + call porous_widths(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) call disable_averaging(CS%diag) + call pass_vector(CS%pbv%por_face_areaU, CS%pbv%por_face_areaV, & + G%Domain, direction=To_All+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) + ! call pass_vector(CS%pbv%por_layer_widthU, CS%pbv%por_layer_widthV, & + ! G%Domain, direction=To_All+SCALAR_PAIR clock=id_clock_pass, halo=CS%cont_stencil) ! The bottom boundary layer properties need to be recalculated. if (bbl_time_int > 0.0) then @@ -1381,9 +1383,6 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & integer :: halo_sz ! The size of a halo where data must be valid. integer :: is, ie, js, je, nz - real, dimension(SZI_(CS%G),SZJ_(CS%G),SZK_(CS%G)+1) :: eta_por ! layer interface heights - !! for porous topo. [Z ~> m or 1/eta_to_m] - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke showCallTree = callTree_showQuery() if (showCallTree) call callTree_enter("step_MOM_thermo(), MOM.F90") @@ -1420,7 +1419,9 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & ! and set_viscous_BBL is called as a part of the dynamic stepping. call cpu_clock_begin(id_clock_BBL_visc) !update porous barrier fractional cell metrics - call porous_widths(h, CS%tv, G, GV, US, eta_por, CS%pbv, CS%por_bar_CS) + call porous_widths(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) + call pass_vector(CS%pbv%por_layer_widthU, CS%pbv%por_layer_widthV, & + G%Domain, direction=To_ALL+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) call set_viscous_BBL(u, v, h, tv, CS%visc, G, GV, US, CS%set_visc_CSp, CS%pbv) call cpu_clock_end(id_clock_BBL_visc) if (showCallTree) call callTree_wayPoint("done with set_viscous_BBL (step_MOM_thermo)") diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index 139baf0320..3de15d6336 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -33,17 +33,15 @@ module MOM_porous_barriers contains !> subroutine to assign cell face areas and layer widths for porous topography -subroutine porous_widths(h, tv, G, GV, US, eta, pbv, CS, eta_bt, halo_size, eta_to_m) +subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) !eta_bt, halo_size, eta_to_m not currently used !variables needed to call find_eta type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2] type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various !! thermodynamic variables. - real, dimension(SZI_(G),SZJ_(G),SZK_(G)+1), intent(out) :: eta !< layer interface heights - !! [Z ~> m] or 1/eta_to_m m). real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic !! variable that gives the "correct" free surface height (Boussinesq) or total water !! column mass per unit area (non-Boussinesq). This is used to dilate the layer. @@ -57,30 +55,31 @@ subroutine porous_widths(h, tv, G, GV, US, eta, pbv, CS, eta_bt, halo_size, eta_ type(porous_barrier_CS), intent(in) :: CS !local variables - integer i, j, k, nk, isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB - real w_layer, & ! fractional open width of layer interface [nondim] - A_layer, & ! integral of fractional open width from bottom to current layer[Z ~> m] - A_layer_prev, & ! integral of fractional open width from bottom to previous layer [Z ~> m] - eta_s, & ! layer height used for fit [Z ~> m] - eta_prev ! interface height of previous layer [Z ~> m] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1):: eta !< layer interface heights [Z ~> m or 1/eta_to_m]. + integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq + real :: dmask + real :: w_layer, & ! fractional open width of layer interface [nondim] + A_layer, & ! integral of fractional open width from bottom to current layer[Z ~> m] + A_layer_prev, & ! integral of fractional open width from bottom to previous layer [Z ~> m] + eta_s, & ! layer height used for fit [Z ~> m] + eta_prev ! interface height of previous layer [Z ~> m] if (.not.CS%initialized) call MOM_error(FATAL, & "MOM_Porous_barrier: Module must be initialized before it is used.") - isd = G%isd; ied = G%ied; jsd = G%jsd; jed = G%jed - IsdB = G%IsdB; IedB = G%IedB; JsdB = G%JsdB; JedB = G%JedB + is = G%isc; ie = G%iec; js = G%jsc; je = G%jec; nk = GV%ke + Isq = G%IscB; Ieq = G%IecB; Jsq = G%JscB; Jeq = G%JecB - !eta is zero at surface and decreases downward - - nk = SZK_(G) + dmask = CS%mask_depth + !eta is zero at surface and decreases downward !currently no treatment for using optional find_eta arguments if present - call find_eta(h, tv, G, GV, US, eta) + call find_eta(h, tv, G, GV, US, eta, halo_size=1) - do j=jsd,jed; do I=IsdB,IedB - if (G%porous_DavgU(I,j) < 0.) then + do j=js,je; do I=Isq,Ieq + if (G%porous_DavgU(I,j) < dmask) then do K = nk+1,1,-1 - eta_s = max(eta(I,j,K), eta(I+1,j,K)) !take shallower layer height + eta_s = max(eta(i,j,K), eta(i+1,j,K)) !take shallower layer height if (eta_s <= G%porous_DminU(I,j)) then pbv%por_layer_widthU(I,j,K) = 0.0 A_layer_prev = 0.0 @@ -104,10 +103,10 @@ subroutine porous_widths(h, tv, G, GV, US, eta, pbv, CS, eta_bt, halo_size, eta_ endif enddo; enddo - do J=JsdB,JedB; do i=isd,ied - if (G%porous_DavgV(i,J) < 0.) then + do J=Jsq,Jeq; do i=is,ie + if (G%porous_DavgV(i,J) < dmask) then do K = nk+1,1,-1 - eta_s = max(eta(i,J,K), eta(i,J+1,K)) !take shallower layer height + eta_s = max(eta(i,j,K), eta(i,j+1,K)) !take shallower layer height if (eta_s <= G%porous_DminV(i,J)) then pbv%por_layer_widthV(i,J,K) = 0.0 A_layer_prev = 0.0 From 6e91d0278e483eb42b37f24027ca6f64cb1dc9f2 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 20 Apr 2022 21:47:54 -0400 Subject: [PATCH 10/68] Add a method to read a full set of porous barrier parameters * subroutine set_subgrid_topo_at_vel_from_file is added to read max, min and avg depth at the edges from file. * The subroutine is only called when a new runtime parameter SUBGRID_TOPO_AT_VEL is True. Default is false. --- src/core/MOM_porous_barriers.F90 | 1 + .../MOM_fixed_initialization.F90 | 9 +++ .../MOM_shared_initialization.F90 | 73 +++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index 3de15d6336..112ed682c0 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -205,6 +205,7 @@ subroutine porous_barriers_init(Time, US, param_file, diag, CS) "The depth below which porous barrier is not applied. "//& "This criterion is tested against TOPO_AT_VEL_VARNAME_U_AVE and TOPO_AT_VEL_VARNAME_V_AVE.", & units="m", default=0.0, scale=US%m_to_Z) + CS%mask_depth = -CS%mask_depth CS%id_por_layer_widthU = register_diag_field('ocean_model', 'por_layer_widthU', diag%axesCui, Time, & 'Porous barrier open width fraction (at the layer interfaces) of the u-faces', 'nondim') diff --git a/src/initialization/MOM_fixed_initialization.F90 b/src/initialization/MOM_fixed_initialization.F90 index f0fb1d23f9..88c6377abc 100644 --- a/src/initialization/MOM_fixed_initialization.F90 +++ b/src/initialization/MOM_fixed_initialization.F90 @@ -23,6 +23,7 @@ module MOM_fixed_initialization use MOM_shared_initialization, only : set_rotation_planetary, set_rotation_beta_plane, initialize_grid_rotation_angle use MOM_shared_initialization, only : reset_face_lengths_named, reset_face_lengths_file, reset_face_lengths_list use MOM_shared_initialization, only : read_face_length_list, set_velocity_depth_max, set_velocity_depth_min +use MOM_shared_initialization, only : set_subgrid_topo_at_vel_from_file use MOM_shared_initialization, only : compute_global_grid_integrals, write_ocean_geometry_file use MOM_unit_scaling, only : unit_scale_type @@ -62,6 +63,7 @@ subroutine MOM_initialize_fixed(G, US, OBC, PF, write_geom, output_dir) ! Local character(len=200) :: inputdir ! The directory where NetCDF input files are. character(len=200) :: config + logical :: read_porous_file character(len=40) :: mdl = "MOM_fixed_initialization" ! This module's name. logical :: debug ! This include declares and sets the variable "version". @@ -142,6 +144,13 @@ subroutine MOM_initialize_fixed(G, US, OBC, PF, write_geom, output_dir) end select endif + ! Read sub-grid scale topography parameters at velocity points used for porous barrier calculation + call get_param(PF, mdl, "SUBGRID_TOPO_AT_VEL", read_porous_file, & + "If true, use variables from TOPO_AT_VEL_FILE as parameters for porous barrier.", & + default=.False.) + if (read_porous_file) & + call set_subgrid_topo_at_vel_from_file(G, PF, US) + ! Calculate the value of the Coriolis parameter at the latitude ! ! of the q grid points [T-1 ~> s-1]. call MOM_initialize_rotation(G%CoriolisBu, G, PF, US=US) diff --git a/src/initialization/MOM_shared_initialization.F90 b/src/initialization/MOM_shared_initialization.F90 index 52f47f9581..04eab60569 100644 --- a/src/initialization/MOM_shared_initialization.F90 +++ b/src/initialization/MOM_shared_initialization.F90 @@ -27,6 +27,7 @@ module MOM_shared_initialization public set_rotation_planetary, set_rotation_beta_plane, initialize_grid_rotation_angle public reset_face_lengths_named, reset_face_lengths_file, reset_face_lengths_list public read_face_length_list, set_velocity_depth_max, set_velocity_depth_min +public set_subgrid_topo_at_vel_from_file public compute_global_grid_integrals, write_ocean_geometry_file ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional @@ -1165,6 +1166,78 @@ subroutine read_face_length_list(iounit, filename, num_lines, lines) end subroutine read_face_length_list ! ----------------------------------------------------------------------------- +! ----------------------------------------------------------------------------- +!> Read from a file the maximum, minimum and average bathymetry at velocity points, +!! for the use of porous barrier. +!! Note that we assume the depth values in the sub-grid bathymetry file of the same +!! convention as in-cell bathymetry file, i.e. positive below the sea surface and +!! increasing downward. This is the opposite of the convention in subroutine +!! porous_widths. Therefore, all signs of the variable are reverted here. +subroutine set_subgrid_topo_at_vel_from_file(G, param_file, US) + type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type + type(param_file_type), intent(in) :: param_file !< Parameter file structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + ! Local variables + character(len=200) :: filename, topo_file, inputdir ! Strings for file/path + character(len=200) :: varname_uhi, varname_ulo, varname_uav, & + varname_vhi, varname_vlo, varname_vav ! Variable names in file + character(len=40) :: mdl = "set_subgrid_topo_at_vel_from_file" ! This subroutine's name. + integer :: i, j + + call callTree_enter(trim(mdl)//"(), MOM_shared_initialization.F90") + + call get_param(param_file, mdl, "INPUTDIR", inputdir, default=".") + inputdir = slasher(inputdir) + call get_param(param_file, mdl, "TOPO_AT_VEL_FILE", topo_file, & + "The file from which the bathymetry parameters at the velocity points are read. "//& + "While the names of the parameters reflect their physical locations, i.e. HIGH is above LOW, "//& + "their signs follow the model's convention, which is positive below the sea surface", & + default="topog_edge.nc") + call get_param(param_file, mdl, "TOPO_AT_VEL_VARNAME_U_HIGH", varname_uhi, & + "The variable name of the highest bathymetry at the u-cells in TOPO_AT_VEL_FILE.", & + default="depthu_hi") + call get_param(param_file, mdl, "TOPO_AT_VEL_VARNAME_U_LOW", varname_ulo, & + "The variable name of the lowest bathymetry at the u-cells in TOPO_AT_VEL_FILE.", & + default="depthu_lo") + call get_param(param_file, mdl, "TOPO_AT_VEL_VARNAME_U_AVE", varname_uav, & + "The variable name of the average bathymetry at the u-cells in TOPO_AT_VEL_FILE.", & + default="depthu_av") + call get_param(param_file, mdl, "TOPO_AT_VEL_VARNAME_V_HIGH", varname_vhi, & + "The variable name of the highest bathymetry at the v-cells in TOPO_AT_VEL_FILE.", & + default="depthv_hi") + call get_param(param_file, mdl, "TOPO_AT_VEL_VARNAME_V_LOW", varname_vlo, & + "The variable name of the lowest bathymetry at the v-cells in TOPO_AT_VEL_FILE.", & + default="depthv_lo") + call get_param(param_file, mdl, "TOPO_AT_VEL_VARNAME_V_AVE", varname_vav, & + "The variable name of the average bathymetry at the v-cells in TOPO_AT_VEL_FILE.", & + default="depthv_av") + + filename = trim(inputdir)//trim(topo_file) + call log_param(param_file, mdl, "INPUTDIR/TOPO_AT_VEL_FILE", filename) + + if (.not.file_exists(filename, G%Domain)) call MOM_error(FATAL, & + " set_subgrid_topo_at_vel_from_file: Unable to open "//trim(filename)) + + call MOM_read_vector(filename, trim(varname_uhi), trim(varname_vhi), & + G%porous_DmaxU, G%porous_DmaxV, G%Domain, stagger=CGRID_NE, scale=US%m_to_Z) + call MOM_read_vector(filename, trim(varname_ulo), trim(varname_vlo), & + G%porous_DminU, G%porous_DminV, G%Domain, stagger=CGRID_NE, scale=US%m_to_Z) + call MOM_read_vector(filename, trim(varname_uav), trim(varname_vav), & + G%porous_DavgU, G%porous_DavgV, G%Domain, stagger=CGRID_NE, scale=US%m_to_Z) + + ! The signs of the depth parameters need to be reverted to comply with subroutine calc_por_layer, + ! which assumes depth is negative below the sea surface. + G%porous_DmaxU = -G%porous_DmaxU; G%porous_DminU = -G%porous_DminU; G%porous_DavgU = -G%porous_DavgU + G%porous_DmaxV = -G%porous_DmaxV; G%porous_DminV = -G%porous_DminV; G%porous_DavgV = -G%porous_DavgV + + call pass_vector(G%porous_DmaxU, G%porous_DmaxV, G%Domain, To_All+SCALAR_PAIR, CGRID_NE) + call pass_vector(G%porous_DminU, G%porous_DminV, G%Domain, To_All+SCALAR_PAIR, CGRID_NE) + call pass_vector(G%porous_DavgU, G%porous_DavgV, G%Domain, To_All+SCALAR_PAIR, CGRID_NE) + + call callTree_leave(trim(mdl)//'()') +end subroutine set_subgrid_topo_at_vel_from_file +! ----------------------------------------------------------------------------- + ! ----------------------------------------------------------------------------- !> Set the bathymetry at velocity points to be the maximum of the depths at the !! neighoring tracer points. From 0e25c5fa3aa238793c7102d043f4e1d91a85bf67 Mon Sep 17 00:00:00 2001 From: He Wang Date: Thu, 28 Apr 2022 11:09:48 -0400 Subject: [PATCH 11/68] Add timer for porous barrier * id_clock is added (as a private variable) to time porous barrier calculations. * Some small format fixes --- src/core/MOM_porous_barriers.F90 | 65 ++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index 112ed682c0..2841f18248 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -4,17 +4,18 @@ module MOM_porous_barriers ! This file is part of MOM6. See LICENSE.md for the license. -use MOM_error_handler, only : MOM_error, FATAL -use MOM_grid, only : ocean_grid_type -use MOM_unit_scaling, only : unit_scale_type -use MOM_variables, only : thermo_var_ptrs, porous_barrier_ptrs -use MOM_verticalGrid, only : verticalGrid_type +use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, CLOCK_MODULE +use MOM_error_handler, only : MOM_error, FATAL +use MOM_grid, only : ocean_grid_type +use MOM_unit_scaling, only : unit_scale_type +use MOM_variables, only : thermo_var_ptrs, porous_barrier_ptrs +use MOM_verticalGrid, only : verticalGrid_type use MOM_interface_heights, only : find_eta -use MOM_time_manager, only : time_type -use MOM_diag_mediator, only : register_diag_field, diag_ctrl, post_data -use MOM_file_parser, only : param_file_type, get_param -use MOM_unit_scaling, only : unit_scale_type -use MOM_debugging, only : hchksum, uvchksum +use MOM_time_manager, only : time_type +use MOM_diag_mediator, only : register_diag_field, diag_ctrl, post_data +use MOM_file_parser, only : param_file_type, get_param +use MOM_unit_scaling, only : unit_scale_type +use MOM_debugging, only : hchksum, uvchksum implicit none ; private @@ -30,6 +31,8 @@ module MOM_porous_barriers integer :: id_por_layer_widthU = -1, id_por_layer_widthV = -1, id_por_face_areaU = -1, id_por_face_areaV = -1 end type porous_barrier_CS +integer :: id_clock_porous_barrier !< CPU clock for porous barrier + contains !> subroutine to assign cell face areas and layer widths for porous topography @@ -67,6 +70,8 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) if (.not.CS%initialized) call MOM_error(FATAL, & "MOM_Porous_barrier: Module must be initialized before it is used.") + call cpu_clock_begin(id_clock_porous_barrier) + is = G%isc; ie = G%iec; js = G%jsc; je = G%jec; nk = GV%ke Isq = G%IscB; Ieq = G%IecB; Jsq = G%JscB; Jeq = G%JecB @@ -142,24 +147,26 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) if (CS%id_por_layer_widthV > 0) call post_data(CS%id_por_layer_widthV, pbv%por_layer_widthV, CS%diag) if (CS%id_por_face_areaU > 0) call post_data(CS%id_por_face_areaU, pbv%por_face_areaU, CS%diag) if (CS%id_por_face_areaV > 0) call post_data(CS%id_por_face_areaV, pbv%por_face_areaV, CS%diag) + + call cpu_clock_end(id_clock_porous_barrier) end subroutine porous_widths !> subroutine to calculate the profile fit for a single layer in a column subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, w_layer, A_layer) - - real, intent(in) :: D_min !< minimum topographic height [Z ~> m] - real, intent(in) :: D_max !< maximum topographic height [Z ~> m] - real, intent(in) :: D_avg !< mean topographic height [Z ~> m] - real, intent(in) :: eta_layer !< height of interface [Z ~> m] - real, intent(out) :: w_layer !< frac. open interface width of current layer [nondim] - real, intent(out) :: A_layer !< frac. open face area of current layer [Z ~> m] - !local variables - real m, a, & !convenience constant for fit [nondim] - zeta, & !normalized vertical coordinate [nondim] - psi, & !fractional width of layer between D_min and D_max [nondim] - psi_int !integral of psi from 0 to zeta - - !three parameter fit from Adcroft 2013 + real, intent(in) :: D_min !< minimum topographic height [Z ~> m] + real, intent(in) :: D_max !< maximum topographic height [Z ~> m] + real, intent(in) :: D_avg !< mean topographic height [Z ~> m] + real, intent(in) :: eta_layer !< height of interface [Z ~> m] + real, intent(out) :: w_layer !< frac. open interface width of current layer [nondim] + real, intent(out) :: A_layer !< frac. open face area of current layer [Z ~> m] + + ! local variables + real :: m, a, & ! convenience constant for fit [nondim] + zeta, & ! normalized vertical coordinate [nondim] + psi, & ! fractional width of layer between D_min and D_max [nondim] + psi_int ! integral of psi from 0 to zeta + + ! three parameter fit from Adcroft 2013 m = (D_avg - D_min)/(D_max - D_min) a = (1. - m)/m @@ -185,18 +192,18 @@ subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, w_layer, A_layer) w_layer = psi A_layer = (D_max - D_min)*psi_int endif - - end subroutine calc_por_layer subroutine porous_barriers_init(Time, US, param_file, diag, CS) - type(porous_barrier_CS), intent(inout) :: CS + type(porous_barrier_CS), intent(inout) :: CS !< Module control structure type(param_file_type), intent(in) :: param_file !< structure indicating parameter file to parse type(time_type), intent(in) :: Time !< Current model time type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure - type(unit_scale_type), intent(in) :: US + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + ! local variables character(len=40) :: mdl = "MOM_porous_barriers" ! This module's name. + CS%initialized = .true. CS%diag => diag @@ -215,6 +222,8 @@ subroutine porous_barriers_init(Time, US, param_file, diag, CS) 'Porous barrier open area fraction (layer averaged) of U-faces', 'nondim') CS%id_por_face_areaV = register_diag_field('ocean_model', 'por_face_areaV', diag%axesCvL, Time, & 'Porous barrier open area fraction (layer averaged) of V-faces', 'nondim') + + id_clock_porous_barrier = cpu_clock_id('(Ocean porous barrier)', grain=CLOCK_MODULE) end subroutine end module MOM_porous_barriers From bd4ae089f1073d53c22413b312cac4cf53673060 Mon Sep 17 00:00:00 2001 From: He Wang Date: Sun, 1 May 2022 23:33:59 -0400 Subject: [PATCH 12/68] Add options for various eta interpolation methods * A runtime parameter PORBAR_ETA_INTERP is added to control different methods for calculating interface height at the velocity points from adjacent tracer cells. * Two small thickness variables are added and scaled the unit of eta. This is to assit the harmonic mean calculation, but eventually the if-test eta_s - eta_prev>0 should be relaced by Angstrom. * Runtime parameter POROUS_BARRIER_MASKING_DEPTH is renamed to make it a liitlle bit shorter. * log_version call is added to separate out porous_barriers module in parameter file. --- src/core/MOM_porous_barriers.F90 | 82 +++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index 2841f18248..c1f8bf68ed 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -13,7 +13,7 @@ module MOM_porous_barriers use MOM_interface_heights, only : find_eta use MOM_time_manager, only : time_type use MOM_diag_mediator, only : register_diag_field, diag_ctrl, post_data -use MOM_file_parser, only : param_file_type, get_param +use MOM_file_parser, only : param_file_type, get_param, log_version use MOM_unit_scaling, only : unit_scale_type use MOM_debugging, only : hchksum, uvchksum @@ -28,11 +28,25 @@ module MOM_porous_barriers type(diag_ctrl), pointer :: diag => Null() !< A structure to regulate diagnostic output timing logical :: debug !< If true, write verbose checksums for debugging purposes. real :: mask_depth !< The depth below which porous barrier is not applied. + integer :: eta_interp !< An integer indicating how the interface heights at the velocity points + !! are calculated. Valid values are given by the parameters defined below: + !! MAX, MIN, ARITHMETIC and HARMONIC. integer :: id_por_layer_widthU = -1, id_por_layer_widthV = -1, id_por_face_areaU = -1, id_por_face_areaV = -1 end type porous_barrier_CS integer :: id_clock_porous_barrier !< CPU clock for porous barrier +!>@{ Enumeration values for eta interpolation schemes +integer, parameter :: ETA_INTERP_MAX = 1 +integer, parameter :: ETA_INTERP_MIN = 2 +integer, parameter :: ETA_INTERP_ARITH = 3 +integer, parameter :: ETA_INTERP_HARM = 4 +character(len=20), parameter :: ETA_INTERP_MAX_STRING = "MAX" +character(len=20), parameter :: ETA_INTERP_MIN_STRING = "MIN" +character(len=20), parameter :: ETA_INTERP_ARITH_STRING = "ARITHMETIC" +character(len=20), parameter :: ETA_INTERP_HARM_STRING = "HARMONIC" +!>@} + contains !> subroutine to assign cell face areas and layer widths for porous topography @@ -66,6 +80,9 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) A_layer_prev, & ! integral of fractional open width from bottom to previous layer [Z ~> m] eta_s, & ! layer height used for fit [Z ~> m] eta_prev ! interface height of previous layer [Z ~> m] + real :: Z_to_eta, H_to_eta ! Unit conversion factors for eta. + real :: h_neglect, & ! ! Negligible thicknesses, often [Z ~> m] + h_min ! ! The minimum layer thickness, often [Z ~> m] if (.not.CS%initialized) call MOM_error(FATAL, & "MOM_Porous_barrier: Module must be initialized before it is used.") @@ -81,10 +98,26 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) !currently no treatment for using optional find_eta arguments if present call find_eta(h, tv, G, GV, US, eta, halo_size=1) + Z_to_eta = 1.0 ; if (present(eta_to_m)) Z_to_eta = US%Z_to_m / eta_to_m + H_to_eta = GV%H_to_m * US%m_to_Z * Z_to_eta + h_neglect = GV%H_subroundoff * H_to_eta + h_min = GV%Angstrom_H * H_to_eta + do j=js,je; do I=Isq,Ieq if (G%porous_DavgU(I,j) < dmask) then do K = nk+1,1,-1 - eta_s = max(eta(i,j,K), eta(i+1,j,K)) !take shallower layer height + select case (CS%eta_interp) + case (ETA_INTERP_MAX) !take shallower layer height + eta_s = max(eta(i,j,K), eta(i+1,j,K)) + case (ETA_INTERP_MIN) !take deeper layer height + eta_s = min(eta(i,j,K), eta(i+1,j,K)) + case (ETA_INTERP_ARITH) !take arithmetic mean layer height + eta_s = 0.5 * (eta(i,j,K) + eta(i+1,j,K)) + case (ETA_INTERP_HARM) !take harmonic mean layer height + eta_s = 2.0 * eta(i,j,K) * eta(i+1,j,K) / (eta(i,j,K) + eta(i+1,j,K) + h_neglect) + case default + call MOM_error(FATAL, "porous_widths: invalid value for eta interpolation method.") + end select if (eta_s <= G%porous_DminU(I,j)) then pbv%por_layer_widthU(I,j,K) = 0.0 A_layer_prev = 0.0 @@ -111,7 +144,18 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) do J=Jsq,Jeq; do i=is,ie if (G%porous_DavgV(i,J) < dmask) then do K = nk+1,1,-1 - eta_s = max(eta(i,j,K), eta(i,j+1,K)) !take shallower layer height + select case (CS%eta_interp) + case (ETA_INTERP_MAX) !take shallower layer height + eta_s = max(eta(i,j,K), eta(i,j+1,K)) + case (ETA_INTERP_MIN) !take deeper layer height + eta_s = min(eta(i,j,K), eta(i,j+1,K)) + case (ETA_INTERP_ARITH) !take arithmetic mean layer height + eta_s = 0.5 * (eta(i,j,K) + eta(i,j+1,K)) + case (ETA_INTERP_HARM) !take harmonic mean layer height + eta_s = 2.0 * eta(i,j,K) * eta(i,j+1,K) / (eta(i,j,K) + eta(i,j+1,K) + h_neglect) + case default + call MOM_error(FATAL, "porous_widths: invalid value for eta interpolation method.") + end select if (eta_s <= G%porous_DminV(i,J)) then pbv%por_layer_widthV(i,J,K) = 0.0 A_layer_prev = 0.0 @@ -201,18 +245,42 @@ subroutine porous_barriers_init(Time, US, param_file, diag, CS) type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + !> This include declares and sets the variable "version". +# include "version_variable.h" ! local variables - character(len=40) :: mdl = "MOM_porous_barriers" ! This module's name. + character(len=40) :: mdl = "MOM_porous_barriers" ! This module's name. + character(len=20) :: interp_method ! String storing eta interpolation method CS%initialized = .true. CS%diag => diag + call log_version(param_file, mdl, version, "", log_to_all=.true., layout=.false., & + debugging=.false.) call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.) - call get_param(param_file, mdl, "POROUS_BARRIER_MASKING_DEPTH", CS%mask_depth, & + call get_param(param_file, mdl, "PORBAR_MASKING_DEPTH", CS%mask_depth, & "The depth below which porous barrier is not applied. "//& - "This criterion is tested against TOPO_AT_VEL_VARNAME_U_AVE and TOPO_AT_VEL_VARNAME_V_AVE.", & - units="m", default=0.0, scale=US%m_to_Z) + "The effective average depths at the velocity cells are used "//& + "to test against this criterion.", units="m", default=0.0, & + scale=US%m_to_Z) CS%mask_depth = -CS%mask_depth + call get_param(param_file, mdl, "PORBAR_ETA_INTERP", interp_method, & + "A string describing the method that decicdes how the "//& + "interface heights at the velocity points are calculated. "//& + "Valid values are:\n"//& + "\t MAX (the default) - maximum of the adjacent cells \n"//& + "\t MIN - minimum of the adjacent cells \n"//& + "\t ARITHMETIC - arithmetic mean of the adjacent cells \n"//& + "\t HARMOINIC - harmonic mean of the adjacent cells \n", & + default=ETA_INTERP_MAX_STRING) ! do_not_log=.not.CS%use_por_bar) + select case (interp_method) + case (ETA_INTERP_MAX_STRING) ; CS%eta_interp = ETA_INTERP_MAX + case (ETA_INTERP_MIN_STRING) ; CS%eta_interp = ETA_INTERP_MIN + case (ETA_INTERP_ARITH_STRING) ; CS%eta_interp = ETA_INTERP_ARITH + case (ETA_INTERP_HARM_STRING) ; CS%eta_interp = ETA_INTERP_HARM + case default + call MOM_error(FATAL, "porous_barriers_init: Unrecognized setting "// & + "#define PORBAR_ETA_INTERP "//trim(interp_method)//" found in input file.") + end select CS%id_por_layer_widthU = register_diag_field('ocean_model', 'por_layer_widthU', diag%axesCui, Time, & 'Porous barrier open width fraction (at the layer interfaces) of the u-faces', 'nondim') From 8243071e9a9e0ec3ba7dee57e0f4bf26dcd2627d Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 2 May 2022 17:22:52 -0400 Subject: [PATCH 13/68] Re-arrange looping orders in porous_widths The main loops in porous_widths are simpified and cleaned up. * calc_por_layer iss slightly modified to reduced the if-blocks in the main loops. * k-loops are moved out. * To assist this new structure, two 2-D arrays are added to the stack. * A new function eta_at_uv is added to treat different interpolations. This is currently done at every point within the loop, and it is probably adding too much an overhead. It is better pre-calculate eta_[uv] all together, which would increase the stack size. --- src/core/MOM_porous_barriers.F90 | 176 ++++++++++++++----------------- 1 file changed, 82 insertions(+), 94 deletions(-) diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index c1f8bf68ed..d1e9d65f15 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -72,17 +72,18 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) type(porous_barrier_CS), intent(in) :: CS !local variables - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1):: eta !< layer interface heights [Z ~> m or 1/eta_to_m]. - integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq - real :: dmask - real :: w_layer, & ! fractional open width of layer interface [nondim] - A_layer, & ! integral of fractional open width from bottom to current layer[Z ~> m] - A_layer_prev, & ! integral of fractional open width from bottom to previous layer [Z ~> m] - eta_s, & ! layer height used for fit [Z ~> m] - eta_prev ! interface height of previous layer [Z ~> m] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1):: eta ! Layer interface heights [Z ~> m or 1/eta_to_m]. + real, dimension(SZIB_(G),SZJB_(G)) :: A_layer_prev ! Integral of fractional open width from the bottom + ! to the previous layer at u or v points [Z ~> m] + real, dimension(SZIB_(G),SZJB_(G)) :: eta_prev ! Layter interface height of the previous layer + ! at u or v points [Z ~> m] + real :: A_layer, & ! Integral of fractional open width from bottom to current layer [Z ~> m] + eta_s ! Layer height used for fit [Z ~> m] real :: Z_to_eta, H_to_eta ! Unit conversion factors for eta. real :: h_neglect, & ! ! Negligible thicknesses, often [Z ~> m] h_min ! ! The minimum layer thickness, often [Z ~> m] + real :: dmask ! The depth below which porous barrier is not applied. + integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq if (.not.CS%initialized) call MOM_error(FATAL, & "MOM_Porous_barrier: Module must be initialized before it is used.") @@ -103,81 +104,49 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) h_neglect = GV%H_subroundoff * H_to_eta h_min = GV%Angstrom_H * H_to_eta - do j=js,je; do I=Isq,Ieq - if (G%porous_DavgU(I,j) < dmask) then - do K = nk+1,1,-1 - select case (CS%eta_interp) - case (ETA_INTERP_MAX) !take shallower layer height - eta_s = max(eta(i,j,K), eta(i+1,j,K)) - case (ETA_INTERP_MIN) !take deeper layer height - eta_s = min(eta(i,j,K), eta(i+1,j,K)) - case (ETA_INTERP_ARITH) !take arithmetic mean layer height - eta_s = 0.5 * (eta(i,j,K) + eta(i+1,j,K)) - case (ETA_INTERP_HARM) !take harmonic mean layer height - eta_s = 2.0 * eta(i,j,K) * eta(i+1,j,K) / (eta(i,j,K) + eta(i+1,j,K) + h_neglect) - case default - call MOM_error(FATAL, "porous_widths: invalid value for eta interpolation method.") - end select - if (eta_s <= G%porous_DminU(I,j)) then - pbv%por_layer_widthU(I,j,K) = 0.0 - A_layer_prev = 0.0 - if (K < nk+1) then - pbv%por_face_areaU(I,j,k) = 0.0; endif - else - call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), & - G%porous_DavgU(I,j), eta_s, w_layer, A_layer) - pbv%por_layer_widthU(I,j,K) = w_layer - if (k <= nk) then - if ((eta_s - eta_prev) > 0.0) then - pbv%por_face_areaU(I,j,k) = (A_layer - A_layer_prev)/& - (eta_s-eta_prev) - else - pbv%por_face_areaU(I,j,k) = 0.0; endif - endif - eta_prev = eta_s - A_layer_prev = A_layer - endif - enddo + ! u-points + do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + eta_prev(I,j) = eta_at_uv(eta(i,j,nk+1), eta(i+1,j,nk+1), CS%eta_interp, h_neglect) + ! eta_prev(I,j) = max(eta(i,j,nk+1), eta(i+1,j,nk+1)) + call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_prev(I,j), pbv%por_layer_widthU(I,j,nk+1), A_layer_prev(I,j)) + endif ; enddo ; enddo + + do k = nk,1,-1; do j=js,je; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + eta_s = eta_at_uv(eta(i,j,K), eta(i+1,j,K), CS%eta_interp, h_neglect) + ! eta_s = max(eta(i,j,K), eta(i+1,j,K)) + if ((eta_s - eta_prev(I,j)) > 0.0) then + call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_s, pbv%por_layer_widthU(I,j,K), A_layer) + pbv%por_face_areaU(I,j,k) = (A_layer - A_layer_prev(I,j)) / (eta_s - eta_prev(I,j)) + else + pbv%por_face_areaU(I,j,k) = 0.0 endif - enddo; enddo - - do J=Jsq,Jeq; do i=is,ie - if (G%porous_DavgV(i,J) < dmask) then - do K = nk+1,1,-1 - select case (CS%eta_interp) - case (ETA_INTERP_MAX) !take shallower layer height - eta_s = max(eta(i,j,K), eta(i,j+1,K)) - case (ETA_INTERP_MIN) !take deeper layer height - eta_s = min(eta(i,j,K), eta(i,j+1,K)) - case (ETA_INTERP_ARITH) !take arithmetic mean layer height - eta_s = 0.5 * (eta(i,j,K) + eta(i,j+1,K)) - case (ETA_INTERP_HARM) !take harmonic mean layer height - eta_s = 2.0 * eta(i,j,K) * eta(i,j+1,K) / (eta(i,j,K) + eta(i,j+1,K) + h_neglect) - case default - call MOM_error(FATAL, "porous_widths: invalid value for eta interpolation method.") - end select - if (eta_s <= G%porous_DminV(i,J)) then - pbv%por_layer_widthV(i,J,K) = 0.0 - A_layer_prev = 0.0 - if (K < nk+1) then - pbv%por_face_areaV(i,J,k) = 0.0; endif - else - call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), & - G%porous_DavgV(i,J), eta_s, w_layer, A_layer) - pbv%por_layer_widthV(i,J,K) = w_layer - if (k <= nk) then - if ((eta_s - eta_prev) > 0.0) then - pbv%por_face_areaV(i,J,k) = (A_layer - A_layer_prev)/& - (eta_s-eta_prev) - else - pbv%por_face_areaU(I,j,k) = 0.0; endif - endif - eta_prev = eta_s - A_layer_prev = A_layer - endif - enddo + eta_prev(I,j) = eta_s + A_layer_prev(I,j) = A_layer + endif ; enddo ; enddo ; enddo + + ! v-points + do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + eta_prev(i,J) = eta_at_uv(eta(i,j,nk+1), eta(i,j+1,nk+1), CS%eta_interp, h_neglect) + ! eta_prev(i,J) = max(eta(i,j,nk+1), eta(i,j+1,nk+1)) + call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_prev(i,J), pbv%por_layer_widthV(i,J,nk+1), A_layer_prev(i,J)) + endif ; enddo ; enddo + + do k = nk,1,-1; do J=Jsq,Jeq ; do i=is,ie ;if (G%porous_DavgV(i,J) < dmask) then + eta_s = eta_at_uv(eta(i,j,K), eta(i,j+1,K), CS%eta_interp, h_neglect) + ! eta_s = max(eta(i,j,K), eta(i,j+1,K)) + if ((eta_s - eta_prev(i,J)) > 0.0) then + call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_s, pbv%por_layer_widthV(i,J,K), A_layer) + pbv%por_face_areaV(i,J,k) = (A_layer - A_layer_prev(i,J)) / (eta_s - eta_prev(i,J)) + else + pbv%por_face_areaV(i,J,k) = 0.0 endif - enddo; enddo + eta_prev(i,J) = eta_s + A_layer_prev(i,J) = A_layer + endif ; enddo ; enddo ; enddo if (CS%debug) then call hchksum(eta, "Interface height used by porous barrier", G%HI, haloshift=0, scale=GV%H_to_m) @@ -195,7 +164,8 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) call cpu_clock_end(id_clock_porous_barrier) end subroutine porous_widths -!> subroutine to calculate the profile fit for a single layer in a column +!> subroutine to calculate the profile fit (the three parameter fit from Adcroft 2013) +! for a single layer in a column subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, w_layer, A_layer) real, intent(in) :: D_min !< minimum topographic height [Z ~> m] real, intent(in) :: D_max !< maximum topographic height [Z ~> m] @@ -210,34 +180,52 @@ subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, w_layer, A_layer) psi, & ! fractional width of layer between D_min and D_max [nondim] psi_int ! integral of psi from 0 to zeta - ! three parameter fit from Adcroft 2013 - m = (D_avg - D_min)/(D_max - D_min) - a = (1. - m)/m - - zeta = (eta_layer - D_min)/(D_max - D_min) - if (eta_layer <= D_min) then w_layer = 0.0 A_layer = 0.0 - elseif (eta_layer >= D_max) then + elseif (eta_layer > D_max) then w_layer = 1.0 A_layer = eta_layer - D_avg else + m = (D_avg - D_min) / (D_max - D_min) + a = (1.0 - m) / m + zeta = (eta_layer - D_min) / (D_max - D_min) if (m < 0.5) then - psi = zeta**(1./a) - psi_int = (1.-m)*zeta**(1./(1.-m)) + psi = zeta**(1.0 / a) + psi_int = (1.0 - m) * zeta**(1.0 / (1.0 - m)) elseif (m == 0.5) then psi = zeta - psi_int = 0.5*zeta*zeta + psi_int = 0.5 * zeta * zeta else - psi = 1. - (1. - zeta)**a - psi_int = zeta - m + m*((1-zeta)**(1/m)) + psi = 1.0 - (1.0 - zeta)**a + psi_int = zeta - m + m * ((1.0 - zeta)**(1 / m)) endif w_layer = psi A_layer = (D_max - D_min)*psi_int endif end subroutine calc_por_layer +function eta_at_uv(e1, e2, interp, h_neglect) result(eatuv) + real, intent(in) :: e1, e2 ! Interface heights at the adjacent tracer cells [Z ~> m] + real, intent(in) :: h_neglect ! Negligible thicknesses, often [Z ~> m] + integer, intent(in) :: interp ! Interpolation method coded by an integer + real :: eatuv + + select case (interp) + case (ETA_INTERP_MAX) ! The shallower interface height + eatuv = max(e1, e2) + case (ETA_INTERP_MIN) ! The deeper interface height + eatuv = min(e1, e2) + case (ETA_INTERP_ARITH) ! Arithmetic mean + eatuv = 0.5 * (e1 + e2) + case (ETA_INTERP_HARM) ! Harmonic mean + eatuv = 2.0 * e1 * e2 / (e1 + e2 + h_neglect) + case default + call MOM_error(FATAL, "porous_widths::eta_at_uv: "//& + "invalid value for eta interpolation method.") + end select +end function eta_at_uv + subroutine porous_barriers_init(Time, US, param_file, diag, CS) type(porous_barrier_CS), intent(inout) :: CS !< Module control structure type(param_file_type), intent(in) :: param_file !< structure indicating parameter file to parse From 43502d4d65b1e7bf71b64109cc434bed18b456db Mon Sep 17 00:00:00 2001 From: He Wang Date: Tue, 3 May 2022 10:32:41 -0400 Subject: [PATCH 14/68] Split porous barrier interface and layer widths The averaged weight over a layer (por_face_area[UV]) and the weight at the interface (por_layer_width[UV]) are now calculated separately, as the only two updates in step_mom are in fact using only one of them. This reduces some unnecessary calculations. * Two new subroutines replaced the original `porous_widths`. They could be combined with interface if we remove porous_barrier_ptrs type, and use variables (of different nz) as output. * There is a place holder switch do_next in the two calc_por subs. This is used to further reduce calculations for all layers above D_max. Will be tested and implemented in the next commit. * A new subroutine calc_eta_at_uv is added to calculate eta at uv. This simplifies the code, but also increases stack and some performance overhead. --- src/core/MOM.F90 | 7 +- src/core/MOM_porous_barriers.F90 | 321 +++++++++++++++++++++---------- 2 files changed, 224 insertions(+), 104 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index ea5baf6116..7865c7aa04 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -103,7 +103,8 @@ module MOM use MOM_open_boundary, only : open_boundary_register_restarts use MOM_open_boundary, only : update_segment_tracer_reservoirs use MOM_open_boundary, only : rotate_OBC_config, rotate_OBC_init -use MOM_porous_barriers, only : porous_barrier_CS, porous_widths, porous_barriers_init +use MOM_porous_barriers, only : porous_widths_layer, porous_widths_interface, porous_barriers_init +use MOM_porous_barriers, only : porous_barrier_CS use MOM_set_visc, only : set_viscous_BBL, set_viscous_ML use MOM_set_visc, only : set_visc_register_restarts, set_visc_CS use MOM_set_visc, only : set_visc_init, set_visc_end @@ -1090,7 +1091,7 @@ subroutine step_MOM_dynamics(forces, p_surf_begin, p_surf_end, dt, dt_thermo, & ! Update porous barrier fractional cell metrics call enable_averages(dt, Time_local, CS%diag) - call porous_widths(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) + call porous_widths_layer(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) call disable_averaging(CS%diag) call pass_vector(CS%pbv%por_face_areaU, CS%pbv%por_face_areaV, & G%Domain, direction=To_All+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) @@ -1419,7 +1420,7 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & ! and set_viscous_BBL is called as a part of the dynamic stepping. call cpu_clock_begin(id_clock_BBL_visc) !update porous barrier fractional cell metrics - call porous_widths(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) + call porous_widths_interface(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) call pass_vector(CS%pbv%por_layer_widthU, CS%pbv%por_layer_widthV, & G%Domain, direction=To_ALL+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) call set_viscous_BBL(u, v, h, tv, CS%visc, G, GV, US, CS%set_visc_CSp, CS%pbv) diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index d1e9d65f15..dff9325757 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -19,19 +19,20 @@ module MOM_porous_barriers implicit none ; private -public porous_widths, porous_barriers_init +public porous_widths_layer, porous_widths_interface, porous_barriers_init #include type, public :: porous_barrier_CS; private logical :: initialized = .false. !< True if this control structure has been initialized. type(diag_ctrl), pointer :: diag => Null() !< A structure to regulate diagnostic output timing - logical :: debug !< If true, write verbose checksums for debugging purposes. - real :: mask_depth !< The depth below which porous barrier is not applied. + logical :: debug !< If true, write verbose checksums for debugging purposes. + real :: mask_depth !< The depth below which porous barrier is not applied. integer :: eta_interp !< An integer indicating how the interface heights at the velocity points !! are calculated. Valid values are given by the parameters defined below: !! MAX, MIN, ARITHMETIC and HARMONIC. - integer :: id_por_layer_widthU = -1, id_por_layer_widthV = -1, id_por_face_areaU = -1, id_por_face_areaV = -1 + integer :: id_por_layer_widthU = -1, id_por_layer_widthV = -1, & + id_por_face_areaU = -1, id_por_face_areaV = -1 end type porous_barrier_CS integer :: id_clock_porous_barrier !< CPU clock for porous barrier @@ -49,41 +50,34 @@ module MOM_porous_barriers contains -!> subroutine to assign cell face areas and layer widths for porous topography -subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) +!> subroutine to assign porous barrier widths averaged over a layer +subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) !eta_bt, halo_size, eta_to_m not currently used !variables needed to call find_eta - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. - type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2] - type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various - !! thermodynamic variables. - real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic - !! variable that gives the "correct" free surface height (Boussinesq) or total water - !! column mass per unit area (non-Boussinesq). This is used to dilate the layer. - !! thicknesses when calculating interfaceheights [H ~> m or kg m-2]. - integer, optional, intent(in) :: halo_size !< width of halo points on - !! which to calculate eta. - - real, optional, intent(in) :: eta_to_m !< The conversion factor from - !! the units of eta to m; by default this is US%Z_to_m. - type(porous_barrier_ptrs), intent(inout) :: pbv !< porous barrier fractional cell metrics - type(porous_barrier_CS), intent(in) :: CS + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2] + type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various + !! thermodynamic variables. + real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable + !! used to dilate the layer thicknesses + !! [H ~> m or kg m-2]. + type(porous_barrier_ptrs), intent(inout) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_CS), intent(in) :: CS !< Control structure for porous barrier !local variables - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1):: eta ! Layer interface heights [Z ~> m or 1/eta_to_m]. + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: eta_u ! Layer interface heights at u points [Z ~> m] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: eta_v ! Layer interface heights at v points [Z ~> m] real, dimension(SZIB_(G),SZJB_(G)) :: A_layer_prev ! Integral of fractional open width from the bottom ! to the previous layer at u or v points [Z ~> m] - real, dimension(SZIB_(G),SZJB_(G)) :: eta_prev ! Layter interface height of the previous layer - ! at u or v points [Z ~> m] - real :: A_layer, & ! Integral of fractional open width from bottom to current layer [Z ~> m] - eta_s ! Layer height used for fit [Z ~> m] + real :: A_layer ! Integral of fractional open width from bottom to current layer [Z ~> m] real :: Z_to_eta, H_to_eta ! Unit conversion factors for eta. - real :: h_neglect, & ! ! Negligible thicknesses, often [Z ~> m] + real :: h_neglect, & ! Negligible thicknesses, often [Z ~> m] h_min ! ! The minimum layer thickness, often [Z ~> m] - real :: dmask ! The depth below which porous barrier is not applied. + real :: dmask ! The depth below which porous barrier is not applied [Z ~> m] integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq + logical :: do_next if (.not.CS%initialized) call MOM_error(FATAL, & "MOM_Porous_barrier: Module must be initialized before it is used.") @@ -95,136 +89,261 @@ subroutine porous_widths(h, tv, G, GV, US, pbv, CS, eta_bt, halo_size, eta_to_m) dmask = CS%mask_depth - !eta is zero at surface and decreases downward - !currently no treatment for using optional find_eta arguments if present - call find_eta(h, tv, G, GV, US, eta, halo_size=1) + call calc_eta_at_uv(eta_u, eta_v, CS%eta_interp, dmask, h, tv, G, GV, US) - Z_to_eta = 1.0 ; if (present(eta_to_m)) Z_to_eta = US%Z_to_m / eta_to_m + Z_to_eta = 1.0 H_to_eta = GV%H_to_m * US%m_to_Z * Z_to_eta - h_neglect = GV%H_subroundoff * H_to_eta h_min = GV%Angstrom_H * H_to_eta ! u-points do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then - eta_prev(I,j) = eta_at_uv(eta(i,j,nk+1), eta(i+1,j,nk+1), CS%eta_interp, h_neglect) - ! eta_prev(I,j) = max(eta(i,j,nk+1), eta(i+1,j,nk+1)) call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & - eta_prev(I,j), pbv%por_layer_widthU(I,j,nk+1), A_layer_prev(I,j)) + eta_u(I,j,nk+1), A_layer_prev(I,j), do_next) endif ; enddo ; enddo - do k = nk,1,-1; do j=js,je; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then - eta_s = eta_at_uv(eta(i,j,K), eta(i+1,j,K), CS%eta_interp, h_neglect) - ! eta_s = max(eta(i,j,K), eta(i+1,j,K)) - if ((eta_s - eta_prev(I,j)) > 0.0) then + do k=nk,1,-1 ; do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + if ((eta_u(I,j,K) - eta_u(I,j,K+1)) > 0.0) then call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & - eta_s, pbv%por_layer_widthU(I,j,K), A_layer) - pbv%por_face_areaU(I,j,k) = (A_layer - A_layer_prev(I,j)) / (eta_s - eta_prev(I,j)) + eta_u(I,j,K), A_layer, do_next) + pbv%por_face_areaU(I,j,k) = (A_layer - A_layer_prev(I,j)) / (eta_u(I,j,K) - eta_u(I,j,K+1)) else pbv%por_face_areaU(I,j,k) = 0.0 endif - eta_prev(I,j) = eta_s A_layer_prev(I,j) = A_layer endif ; enddo ; enddo ; enddo ! v-points do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then - eta_prev(i,J) = eta_at_uv(eta(i,j,nk+1), eta(i,j+1,nk+1), CS%eta_interp, h_neglect) - ! eta_prev(i,J) = max(eta(i,j,nk+1), eta(i,j+1,nk+1)) call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & - eta_prev(i,J), pbv%por_layer_widthV(i,J,nk+1), A_layer_prev(i,J)) + eta_v(i,J,nk+1), A_layer_prev(i,J), do_next) endif ; enddo ; enddo - do k = nk,1,-1; do J=Jsq,Jeq ; do i=is,ie ;if (G%porous_DavgV(i,J) < dmask) then - eta_s = eta_at_uv(eta(i,j,K), eta(i,j+1,K), CS%eta_interp, h_neglect) - ! eta_s = max(eta(i,j,K), eta(i,j+1,K)) - if ((eta_s - eta_prev(i,J)) > 0.0) then + do k=nk,1,-1 ; do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + if ((eta_v(i,J,K) - eta_v(i,J,K+1)) > 0.0) then call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & - eta_s, pbv%por_layer_widthV(i,J,K), A_layer) - pbv%por_face_areaV(i,J,k) = (A_layer - A_layer_prev(i,J)) / (eta_s - eta_prev(i,J)) + eta_v(i,J,K), A_layer, do_next) + pbv%por_face_areaV(i,J,k) = (A_layer - A_layer_prev(i,J)) / (eta_v(i,J,K) - eta_v(i,J,K+1)) else pbv%por_face_areaV(i,J,k) = 0.0 endif - eta_prev(i,J) = eta_s A_layer_prev(i,J) = A_layer endif ; enddo ; enddo ; enddo if (CS%debug) then - call hchksum(eta, "Interface height used by porous barrier", G%HI, haloshift=0, scale=GV%H_to_m) - call uvchksum("Porous barrier weights at the layer-interface: por_layer_width[UV]", & - pbv%por_layer_widthU, pbv%por_layer_widthV, G%HI, haloshift=0) + call uvchksum("Interface height used by porous barrier for layer weights", & + eta_u, eta_v, G%HI, haloshift=0) call uvchksum("Porous barrier layer-averaged weights: por_face_area[UV]", & - pbv%por_face_areaU, pbv%por_face_areaV, G%HI, haloshift=0) + pbv%por_face_areaU, pbv%por_face_areaV, G%HI, haloshift=0) endif - if (CS%id_por_layer_widthU > 0) call post_data(CS%id_por_layer_widthU, pbv%por_layer_widthU, CS%diag) - if (CS%id_por_layer_widthV > 0) call post_data(CS%id_por_layer_widthV, pbv%por_layer_widthV, CS%diag) if (CS%id_por_face_areaU > 0) call post_data(CS%id_por_face_areaU, pbv%por_face_areaU, CS%diag) if (CS%id_por_face_areaV > 0) call post_data(CS%id_por_face_areaV, pbv%por_face_areaV, CS%diag) call cpu_clock_end(id_clock_porous_barrier) -end subroutine porous_widths +end subroutine porous_widths_layer + +!> subroutine to assign porous barrier widths at the layer interfaces +subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) + !eta_bt, halo_size, eta_to_m not currently used + !variables needed to call find_eta + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2] + type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various + !! thermodynamic variables. + real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable + !! used to dilate the layer thicknesses + !! [H ~> m or kg m-2]. + type(porous_barrier_ptrs), intent(inout) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_CS), intent(in) :: CS !< Control structure for porous barrier + + !local variables + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: eta_u ! Layer interface height at u points [Z ~> m] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: eta_v ! Layer interface height at v points [Z ~> m] + real :: Z_to_eta, H_to_eta ! Unit conversion factors for eta. + real :: h_neglect ! Negligible thicknesses, often [Z ~> m] + real :: dmask ! The depth below which porous barrier is not applied [Z ~> m] + integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq + logical :: do_next + + if (.not.CS%initialized) call MOM_error(FATAL, & + "MOM_Porous_barrier: Module must be initialized before it is used.") + + call cpu_clock_begin(id_clock_porous_barrier) + + is = G%isc; ie = G%iec; js = G%jsc; je = G%jec; nk = GV%ke + Isq = G%IscB; Ieq = G%IecB; Jsq = G%JscB; Jeq = G%JecB + + dmask = CS%mask_depth + + call calc_eta_at_uv(eta_u, eta_v, CS%eta_interp, dmask, h, tv, G, GV, US) + + ! u-points + do K=1,nk+1 ; do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + call calc_por_interface(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_u(I,j,K), pbv%por_layer_widthU(I,j,K), do_next) + endif ; enddo ; enddo ; enddo + + ! v-points + do K=1,nk+1 ; do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + call calc_por_interface(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_v(i,J,K), pbv%por_layer_widthV(i,J,K), do_next) + endif ; enddo ; enddo ; enddo + + if (CS%debug) then + call uvchksum("Interface height used by porous barrier for interface weights", & + eta_u, eta_v, G%HI, haloshift=0) + call uvchksum("Porous barrier weights at the layer-interface: por_layer_width[UV]", & + pbv%por_layer_widthU, pbv%por_layer_widthV, G%HI, haloshift=0) + endif + + if (CS%id_por_layer_widthU > 0) call post_data(CS%id_por_layer_widthU, pbv%por_layer_widthU, CS%diag) + if (CS%id_por_layer_widthV > 0) call post_data(CS%id_por_layer_widthV, pbv%por_layer_widthV, CS%diag) + + call cpu_clock_end(id_clock_porous_barrier) +end subroutine porous_widths_interface + +subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) + !variables needed to call find_eta + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2] + type(thermo_var_ptrs), intent(in) :: tv !< A structure pointing to various + !! thermodynamic variables. + real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable + !! used to dilate the layer thicknesses + !! [H ~> m or kg m-2]. + real, intent(in) :: dmask !< The depth below which + !! porous barrier is not applied [Z ~> m] + integer, intent(in) :: interp !< eta interpolation method + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(out) :: eta_u !< Layer interface heights at u points [Z ~> m] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: eta_v !< Layer interface heights at v points [Z ~> m] + + ! local variables + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: eta ! Layer interface heights [Z ~> m or 1/eta_to_m]. + real :: Z_to_eta, H_to_eta ! Unit conversion factors for eta. + real :: h_neglect ! Negligible thicknesses, often [Z ~> m] + integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq + + is = G%isc; ie = G%iec; js = G%jsc; je = G%jec; nk = GV%ke + Isq = G%IscB; Ieq = G%IecB; Jsq = G%JscB; Jeq = G%JecB + + ! currently no treatment for using optional find_eta arguments if present + call find_eta(h, tv, G, GV, US, eta, halo_size=1) + + Z_to_eta = 1.0 + H_to_eta = GV%H_to_m * US%m_to_Z * Z_to_eta + h_neglect = GV%H_subroundoff * H_to_eta + + select case (interp) + case (ETA_INTERP_MAX) ! The shallower interface height + do K=1,nk+1 + do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + eta_u(I,j,K) = max(eta(i,j,K), eta(i+1,j,K)) + endif ; enddo ; enddo + do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + eta_v(i,J,K) = max(eta(i,j,K), eta(i,j+1,K)) + endif ; enddo ; enddo + enddo + case (ETA_INTERP_MIN) ! The deeper interface height + do K=1,nk+1 + do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + eta_u(I,j,K) = min(eta(i,j,K), eta(i+1,j,K)) + endif ; enddo ; enddo + do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + eta_v(i,J,K) = min(eta(i,j,K), eta(i,j+1,K)) + endif ; enddo ; enddo + enddo + case (ETA_INTERP_ARITH) ! Arithmetic mean + do K=1,nk+1 + do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + eta_u(I,j,K) = 0.5 * (eta(i,j,K) + eta(i+1,j,K)) + endif ; enddo ; enddo + do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + eta_v(i,J,K) = 0.5 * (eta(i,j,K) + eta(i,j+1,K)) + endif ; enddo ; enddo + enddo + case (ETA_INTERP_HARM) ! Harmonic mean + do K=1,nk+1 + do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + eta_u(I,j,K) = 2.0 * (eta(i,j,K) * eta(i+1,j,K)) / (eta(i,j,K) + eta(i+1,j,K) + h_neglect) + endif ; enddo ; enddo + do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + eta_v(i,J,K) = 2.0 * (eta(i,j,K) * eta(i,j+1,K)) / (eta(i,j,K) + eta(i,j+1,K) + h_neglect) + endif ; enddo ; enddo + enddo + case default + call MOM_error(FATAL, "porous_widths::calc_eta_at_uv: "//& + "invalid value for eta interpolation method.") + end select +end subroutine calc_eta_at_uv !> subroutine to calculate the profile fit (the three parameter fit from Adcroft 2013) ! for a single layer in a column -subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, w_layer, A_layer) - real, intent(in) :: D_min !< minimum topographic height [Z ~> m] - real, intent(in) :: D_max !< maximum topographic height [Z ~> m] +subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, A_layer, do_next) + real, intent(in) :: D_min !< minimum topographic height (deepest) [Z ~> m] + real, intent(in) :: D_max !< maximum topographic height (shallowest) [Z ~> m] real, intent(in) :: D_avg !< mean topographic height [Z ~> m] real, intent(in) :: eta_layer !< height of interface [Z ~> m] - real, intent(out) :: w_layer !< frac. open interface width of current layer [nondim] - real, intent(out) :: A_layer !< frac. open face area of current layer [Z ~> m] + real, intent(out) :: A_layer !< frac. open face area of below eta_layer [Z ~> m] + logical, intent(out) :: do_next !< False if eta_layer > D_max ! local variables - real :: m, a, & ! convenience constant for fit [nondim] - zeta, & ! normalized vertical coordinate [nondim] - psi, & ! fractional width of layer between D_min and D_max [nondim] - psi_int ! integral of psi from 0 to zeta + real :: m, & ! convenience constant for fit [nondim] + zeta ! normalized vertical coordinate [nondim] + do_next = .True. if (eta_layer <= D_min) then - w_layer = 0.0 A_layer = 0.0 elseif (eta_layer > D_max) then - w_layer = 1.0 A_layer = eta_layer - D_avg + do_next = .False. else m = (D_avg - D_min) / (D_max - D_min) - a = (1.0 - m) / m zeta = (eta_layer - D_min) / (D_max - D_min) if (m < 0.5) then - psi = zeta**(1.0 / a) - psi_int = (1.0 - m) * zeta**(1.0 / (1.0 - m)) + A_layer = (D_max - D_min) * ((1.0 - m) * zeta**(1.0 / (1.0 - m))) elseif (m == 0.5) then - psi = zeta - psi_int = 0.5 * zeta * zeta + A_layer = (D_max - D_min) * (0.5 * zeta * zeta) else - psi = 1.0 - (1.0 - zeta)**a - psi_int = zeta - m + m * ((1.0 - zeta)**(1 / m)) + A_layer = (D_max - D_min) * (zeta - m + m * ((1.0 - zeta)**(1.0 / m))) endif - w_layer = psi - A_layer = (D_max - D_min)*psi_int endif end subroutine calc_por_layer -function eta_at_uv(e1, e2, interp, h_neglect) result(eatuv) - real, intent(in) :: e1, e2 ! Interface heights at the adjacent tracer cells [Z ~> m] - real, intent(in) :: h_neglect ! Negligible thicknesses, often [Z ~> m] - integer, intent(in) :: interp ! Interpolation method coded by an integer - real :: eatuv +subroutine calc_por_interface(D_min, D_max, D_avg, eta_layer, w_layer, do_next) + real, intent(in) :: D_min !< minimum topographic height (deepest) [Z ~> m] + real, intent(in) :: D_max !< maximum topographic height (shallowest) [Z ~> m] + real, intent(in) :: D_avg !< mean topographic height [Z ~> m] + real, intent(in) :: eta_layer !< height of interface [Z ~> m] + real, intent(out) :: w_layer !< frac. open interface width at eta_layer [nondim] + logical, intent(out) :: do_next !< False if eta_layer > D_max - select case (interp) - case (ETA_INTERP_MAX) ! The shallower interface height - eatuv = max(e1, e2) - case (ETA_INTERP_MIN) ! The deeper interface height - eatuv = min(e1, e2) - case (ETA_INTERP_ARITH) ! Arithmetic mean - eatuv = 0.5 * (e1 + e2) - case (ETA_INTERP_HARM) ! Harmonic mean - eatuv = 2.0 * e1 * e2 / (e1 + e2 + h_neglect) - case default - call MOM_error(FATAL, "porous_widths::eta_at_uv: "//& - "invalid value for eta interpolation method.") - end select -end function eta_at_uv + ! local variables + real :: m, a, & ! convenience constant for fit [nondim] + zeta ! normalized vertical coordinate [nondim] + + do_next = .True. + if (eta_layer <= D_min) then + w_layer = 0.0 + elseif (eta_layer > D_max) then + w_layer = 1.0 + do_next = .False. + else + m = (D_avg - D_min) / (D_max - D_min) + a = (1.0 - m) / m + zeta = (eta_layer - D_min) / (D_max - D_min) + if (m < 0.5) then + w_layer = zeta**(1.0 / a) + elseif (m == 0.5) then + w_layer = zeta + else + w_layer = 1.0 - (1.0 - zeta)**a + endif + endif +end subroutine calc_por_interface subroutine porous_barriers_init(Time, US, param_file, diag, CS) type(porous_barrier_CS), intent(inout) :: CS !< Module control structure From 016f042603215917b00ca21252c6ec0d3dd47f71 Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 23 May 2022 16:38:37 -0400 Subject: [PATCH 15/68] Reduce unnecessary porous barrier calculations * A new runtime parameter USE_POROUS_BARRIER is added to control this feature. The default is True at the moment to assist potential test. It should be changed to false in the future. * A boolean array is added to avoid unnecessary porous barrier calculations above the shallowest points. * The layer-averaged weights are bounded by 1.0 explicitly, to avoid the cases it goes beyond due to roundoff errors. * For very thin layers (Angstrom), layer-averaged weights are set to zeros for simplicity. * Perhaps it is more reasonable to use the inferace weight for these cases. * It might be useful to make the thin layer definition a runtime parameter. * A bug related the size of eta_[uv] is fixed. This commit is potentially answer-changing when the porouss barrier module is used. --- src/core/MOM.F90 | 31 +++++++++------ src/core/MOM_porous_barriers.F90 | 68 ++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 7865c7aa04..8d2556f185 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -413,6 +413,7 @@ module MOM !! ensemble model state vectors and data assimilation !! increments and priors type(dbcomms_CS_type) :: dbcomms_CS !< Control structure for database client used for online ML/AI + logical :: use_porbar type(porous_barrier_ptrs) :: pbv !< porous barrier fractional cell metrics type(particles), pointer :: particles => NULL() ! NULL() !< a pointer to the stochastics control structure @@ -1090,13 +1091,13 @@ subroutine step_MOM_dynamics(forces, p_surf_begin, p_surf_end, dt, dt_thermo, & endif ! Update porous barrier fractional cell metrics - call enable_averages(dt, Time_local, CS%diag) - call porous_widths_layer(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) - call disable_averaging(CS%diag) - call pass_vector(CS%pbv%por_face_areaU, CS%pbv%por_face_areaV, & - G%Domain, direction=To_All+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) - ! call pass_vector(CS%pbv%por_layer_widthU, CS%pbv%por_layer_widthV, & - ! G%Domain, direction=To_All+SCALAR_PAIR clock=id_clock_pass, halo=CS%cont_stencil) + if (CS%use_porbar) then + call enable_averages(dt, Time_local, CS%diag) + call porous_widths_layer(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) + call disable_averaging(CS%diag) + call pass_vector(CS%pbv%por_face_areaU, CS%pbv%por_face_areaV, & + G%Domain, direction=To_All+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) + endif ! The bottom boundary layer properties need to be recalculated. if (bbl_time_int > 0.0) then @@ -1420,9 +1421,11 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & ! and set_viscous_BBL is called as a part of the dynamic stepping. call cpu_clock_begin(id_clock_BBL_visc) !update porous barrier fractional cell metrics - call porous_widths_interface(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) - call pass_vector(CS%pbv%por_layer_widthU, CS%pbv%por_layer_widthV, & - G%Domain, direction=To_ALL+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) + if (CS%use_porbar) then + call porous_widths_interface(h, CS%tv, G, GV, US, CS%pbv, CS%por_bar_CS) + call pass_vector(CS%pbv%por_layer_widthU, CS%pbv%por_layer_widthV, & + G%Domain, direction=To_ALL+SCALAR_PAIR, clock=id_clock_pass, halo=CS%cont_stencil) + endif call set_viscous_BBL(u, v, h, tv, CS%visc, G, GV, US, CS%set_visc_CSp, CS%pbv) call cpu_clock_end(id_clock_BBL_visc) if (showCallTree) call callTree_wayPoint("done with set_viscous_BBL (step_MOM_thermo)") @@ -1997,6 +2000,10 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & "This is only used if THICKNESSDIFFUSE is true.", & default=.false.) if (.not.CS%thickness_diffuse) CS%thickness_diffuse_first = .false. + call get_param(param_file, "MOM", "USE_POROUS_BARRIER", CS%use_porbar, & + "If true, use porous barrier to constrain the widths "//& + " and face areas at the edges of the grid cells. ", & + default=.true.) ! The default should be false after tests. call get_param(param_file, "MOM", "BATHYMETRY_AT_VEL", bathy_at_vel, & "If true, there are separate values for the basin depths "//& "at velocity points. Otherwise the effects of topography "//& @@ -2820,7 +2827,9 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & new_sim = is_new_run(restart_CSp) call MOM_stoch_eos_init(G,Time,param_file,CS%stoch_eos_CS,restart_CSp,diag) - call porous_barriers_init(Time, US, param_file, diag, CS%por_bar_CS) + + if (CS%use_porbar) & + call porous_barriers_init(Time, US, param_file, diag, CS%por_bar_CS) if (CS%split) then allocate(eta(SZI_(G),SZJ_(G)), source=0.0) diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index dff9325757..b96c33d3f3 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -67,17 +67,18 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) type(porous_barrier_CS), intent(in) :: CS !< Control structure for porous barrier !local variables - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: eta_u ! Layer interface heights at u points [Z ~> m] - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: eta_v ! Layer interface heights at v points [Z ~> m] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: eta_u ! Layer interface heights at u points [Z ~> m] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: eta_v ! Layer interface heights at v points [Z ~> m] real, dimension(SZIB_(G),SZJB_(G)) :: A_layer_prev ! Integral of fractional open width from the bottom ! to the previous layer at u or v points [Z ~> m] + logical, dimension(SZIB_(G),SZJB_(G)) :: do_I ! Booleans for calculation at u or v points + ! updated while moving up layers real :: A_layer ! Integral of fractional open width from bottom to current layer [Z ~> m] real :: Z_to_eta, H_to_eta ! Unit conversion factors for eta. real :: h_neglect, & ! Negligible thicknesses, often [Z ~> m] h_min ! ! The minimum layer thickness, often [Z ~> m] real :: dmask ! The depth below which porous barrier is not applied [Z ~> m] integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq - logical :: do_next if (.not.CS%initialized) call MOM_error(FATAL, & "MOM_Porous_barrier: Module must be initialized before it is used.") @@ -96,35 +97,39 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) h_min = GV%Angstrom_H * H_to_eta ! u-points + do j=js,je ; do I=Isq,Ieq ; do_I(I,j) = .False. ; enddo ; enddo + do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & - eta_u(I,j,nk+1), A_layer_prev(I,j), do_next) + eta_u(I,j,nk+1), A_layer_prev(I,j), do_I(I,j)) endif ; enddo ; enddo - do k=nk,1,-1 ; do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then - if ((eta_u(I,j,K) - eta_u(I,j,K+1)) > 0.0) then - call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & - eta_u(I,j,K), A_layer, do_next) - pbv%por_face_areaU(I,j,k) = (A_layer - A_layer_prev(I,j)) / (eta_u(I,j,K) - eta_u(I,j,K+1)) + do k=nk,1,-1 ; do j=js,je ; do I=Isq,Ieq ; if (do_I(I,j)) then + call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_u(I,j,K), A_layer, do_I(I,j)) + if (eta_u(I,j,K) - (eta_u(I,j,K+1)+h_min) > 0.0) then + pbv%por_face_areaU(I,j,k) = min(1.0, (A_layer - A_layer_prev(I,j)) / (eta_u(I,j,K) - eta_u(I,j,K+1))) else - pbv%por_face_areaU(I,j,k) = 0.0 + pbv%por_face_areaU(I,j,k) = 0.0 ! use calc_por_interface() might be a better choice endif A_layer_prev(I,j) = A_layer endif ; enddo ; enddo ; enddo ! v-points + do J=Jsq,Jeq ; do i=is,ie; do_I(i,J) = .False. ; enddo ; enddo + do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & - eta_v(i,J,nk+1), A_layer_prev(i,J), do_next) + eta_v(i,J,nk+1), A_layer_prev(i,J), do_I(i,J)) endif ; enddo ; enddo - do k=nk,1,-1 ; do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then - if ((eta_v(i,J,K) - eta_v(i,J,K+1)) > 0.0) then - call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & - eta_v(i,J,K), A_layer, do_next) - pbv%por_face_areaV(i,J,k) = (A_layer - A_layer_prev(i,J)) / (eta_v(i,J,K) - eta_v(i,J,K+1)) + do k=nk,1,-1 ; do J=Jsq,Jeq ; do i=is,ie ; if (do_I(i,J)) then + call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_v(i,J,K), A_layer, do_I(i,J)) + if (eta_v(i,J,K) - (eta_v(i,J,K+1)+h_min) > 0.0) then + pbv%por_face_areaV(i,J,k) = min(1.0, (A_layer - A_layer_prev(i,J)) / (eta_v(i,J,K) - eta_v(i,J,K+1))) else - pbv%por_face_areaV(i,J,k) = 0.0 + pbv%por_face_areaV(i,J,k) = 0.0 ! use calc_por_interface() might be a better choice endif A_layer_prev(i,J) = A_layer endif ; enddo ; enddo ; enddo @@ -159,13 +164,14 @@ subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) type(porous_barrier_CS), intent(in) :: CS !< Control structure for porous barrier !local variables - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: eta_u ! Layer interface height at u points [Z ~> m] - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: eta_v ! Layer interface height at v points [Z ~> m] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1) :: eta_u ! Layer interface height at u points [Z ~> m] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1) :: eta_v ! Layer interface height at v points [Z ~> m] + logical, dimension(SZIB_(G),SZJB_(G)) :: do_I ! Booleans for calculation at u or v points + ! updated while moving up layers real :: Z_to_eta, H_to_eta ! Unit conversion factors for eta. real :: h_neglect ! Negligible thicknesses, often [Z ~> m] real :: dmask ! The depth below which porous barrier is not applied [Z ~> m] integer :: i, j, k, nk, is, ie, js, je, Isq, Ieq, Jsq, Jeq - logical :: do_next if (.not.CS%initialized) call MOM_error(FATAL, & "MOM_Porous_barrier: Module must be initialized before it is used.") @@ -180,15 +186,25 @@ subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) call calc_eta_at_uv(eta_u, eta_v, CS%eta_interp, dmask, h, tv, G, GV, US) ! u-points - do K=1,nk+1 ; do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + do j=js,je ; do I=Isq,Ieq + do_I(I,j) = .False. + if (G%porous_DavgU(I,j) < dmask) do_I(I,j) = .True. + enddo ; enddo + + do K=1,nk+1 ; do j=js,je ; do I=Isq,Ieq ; if (do_I(I,j)) then call calc_por_interface(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & - eta_u(I,j,K), pbv%por_layer_widthU(I,j,K), do_next) + eta_u(I,j,K), pbv%por_layer_widthU(I,j,K), do_I(I,j)) endif ; enddo ; enddo ; enddo ! v-points - do K=1,nk+1 ; do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + do J=Jsq,Jeq ; do i=is,ie + do_I(i,J) = .False. + if (G%porous_DavgV(i,J) < dmask) do_I(i,J) = .True. + enddo ; enddo + + do K=1,nk+1 ; do J=Jsq,Jeq ; do i=is,ie ; if (do_I(i,J)) then call calc_por_interface(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & - eta_v(i,J,K), pbv%por_layer_widthV(i,J,K), do_next) + eta_v(i,J,K), pbv%por_layer_widthV(i,J,K), do_I(i,J)) endif ; enddo ; enddo ; enddo if (CS%debug) then @@ -218,8 +234,8 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) real, intent(in) :: dmask !< The depth below which !! porous barrier is not applied [Z ~> m] integer, intent(in) :: interp !< eta interpolation method - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(out) :: eta_u !< Layer interface heights at u points [Z ~> m] - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: eta_v !< Layer interface heights at v points [Z ~> m] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: eta_u !< Layer interface heights at u points [Z ~> m] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)+1), intent(out) :: eta_v !< Layer interface heights at v points [Z ~> m] ! local variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: eta ! Layer interface heights [Z ~> m or 1/eta_to_m]. From 91131053c3ccb18b17d77240d1f1346a2d119e58 Mon Sep 17 00:00:00 2001 From: He Wang Date: Sat, 6 Aug 2022 17:28:52 -0400 Subject: [PATCH 16/68] Add PORBAR_ANSWER_DATE flag * The recently introduced ANSWER_DATE functionality is used to maintain a version that should be bit-for-bit reproducible for previous porous barrier related runs. * Rename porous_barrier_ptrs to porous_barrier_type * Some small documentation edits --- src/core/MOM.F90 | 6 +- src/core/MOM_CoriolisAdv.F90 | 4 +- src/core/MOM_continuity.F90 | 4 +- src/core/MOM_continuity_PPM.F90 | 4 +- src/core/MOM_dynamics_split_RK2.F90 | 6 +- src/core/MOM_dynamics_unsplit.F90 | 4 +- src/core/MOM_dynamics_unsplit_RK2.F90 | 4 +- src/core/MOM_grid.F90 | 8 +- src/core/MOM_porous_barriers.F90 | 199 ++++++++++++------ src/core/MOM_variables.F90 | 6 +- src/framework/MOM_dyn_horgrid.F90 | 8 +- .../MOM_shared_initialization.F90 | 16 +- .../vertical/MOM_set_viscosity.F90 | 4 +- 13 files changed, 169 insertions(+), 104 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 8d2556f185..9f1fd99a26 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -134,7 +134,7 @@ module MOM use MOM_unit_scaling, only : unit_scale_type, unit_scaling_init use MOM_unit_scaling, only : unit_scaling_end, fix_restart_unit_scaling use MOM_variables, only : surface, allocate_surface_state, deallocate_surface_state -use MOM_variables, only : thermo_var_ptrs, vertvisc_type, porous_barrier_ptrs +use MOM_variables, only : thermo_var_ptrs, vertvisc_type, porous_barrier_type use MOM_variables, only : accel_diag_ptrs, cont_diag_ptrs, ocean_internal_state use MOM_variables, only : rotate_surface_state use MOM_verticalGrid, only : verticalGrid_type, verticalGridInit, verticalGridEnd @@ -414,7 +414,7 @@ module MOM !! increments and priors type(dbcomms_CS_type) :: dbcomms_CS !< Control structure for database client used for online ML/AI logical :: use_porbar - type(porous_barrier_ptrs) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type) :: pbv !< porous barrier fractional cell metrics type(particles), pointer :: particles => NULL() ! NULL() !< a pointer to the stochastics control structure end type MOM_control_struct @@ -2002,7 +2002,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & if (.not.CS%thickness_diffuse) CS%thickness_diffuse_first = .false. call get_param(param_file, "MOM", "USE_POROUS_BARRIER", CS%use_porbar, & "If true, use porous barrier to constrain the widths "//& - " and face areas at the edges of the grid cells. ", & + "and face areas at the edges of the grid cells. ", & default=.true.) ! The default should be false after tests. call get_param(param_file, "MOM", "BATHYMETRY_AT_VEL", bathy_at_vel, & "If true, there are separate values for the basin depths "//& diff --git a/src/core/MOM_CoriolisAdv.F90 b/src/core/MOM_CoriolisAdv.F90 index 6aacc479af..154db3eaa3 100644 --- a/src/core/MOM_CoriolisAdv.F90 +++ b/src/core/MOM_CoriolisAdv.F90 @@ -16,7 +16,7 @@ module MOM_CoriolisAdv use MOM_open_boundary, only : OBC_DIRECTION_N, OBC_DIRECTION_S use MOM_string_functions, only : uppercase use MOM_unit_scaling, only : unit_scale_type -use MOM_variables, only : accel_diag_ptrs, porous_barrier_ptrs +use MOM_variables, only : accel_diag_ptrs, porous_barrier_type use MOM_verticalGrid, only : verticalGrid_type use MOM_wave_interface, only : wave_parameters_CS @@ -140,7 +140,7 @@ subroutine CorAdCalc(u, v, h, uh, vh, CAu, CAv, OBC, AD, G, GV, US, CS, pbv, Wav type(accel_diag_ptrs), intent(inout) :: AD !< Storage for acceleration diagnostics type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type type(CoriolisAdv_CS), intent(in) :: CS !< Control structure for MOM_CoriolisAdv - type(porous_barrier_ptrs), intent(in) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(in) :: pbv !< porous barrier fractional cell metrics type(Wave_parameters_CS), optional, pointer :: Waves !< An optional pointer to Stokes drift CS ! Local variables diff --git a/src/core/MOM_continuity.F90 b/src/core/MOM_continuity.F90 index 0852d10cd2..541dcde66a 100644 --- a/src/core/MOM_continuity.F90 +++ b/src/core/MOM_continuity.F90 @@ -13,7 +13,7 @@ module MOM_continuity use MOM_grid, only : ocean_grid_type use MOM_open_boundary, only : ocean_OBC_type use MOM_unit_scaling, only : unit_scale_type -use MOM_variables, only : BT_cont_type, porous_barrier_ptrs +use MOM_variables, only : BT_cont_type, porous_barrier_type use MOM_verticalGrid, only : verticalGrid_type implicit none ; private @@ -61,7 +61,7 @@ subroutine continuity(u, v, hin, h, uh, vh, dt, G, GV, US, CS, OBC, pbv, uhbt, v type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type type(continuity_CS), intent(in) :: CS !< Control structure for mom_continuity. type(ocean_OBC_type), pointer :: OBC !< Open boundaries control structure. - type(porous_barrier_ptrs), intent(in) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(in) :: pbv !< porous barrier fractional cell metrics real, dimension(SZIB_(G),SZJ_(G)), & optional, intent(in) :: uhbt !< The vertically summed volume !! flux through zonal faces [H L2 T-1 ~> m3 s-1 or kg s-1]. diff --git a/src/core/MOM_continuity_PPM.F90 b/src/core/MOM_continuity_PPM.F90 index 402a6921ae..59d119f5d4 100644 --- a/src/core/MOM_continuity_PPM.F90 +++ b/src/core/MOM_continuity_PPM.F90 @@ -11,7 +11,7 @@ module MOM_continuity_PPM use MOM_open_boundary, only : ocean_OBC_type, OBC_segment_type, OBC_NONE use MOM_open_boundary, only : OBC_DIRECTION_E, OBC_DIRECTION_W, OBC_DIRECTION_N, OBC_DIRECTION_S use MOM_unit_scaling, only : unit_scale_type -use MOM_variables, only : BT_cont_type, porous_barrier_ptrs +use MOM_variables, only : BT_cont_type, porous_barrier_type use MOM_verticalGrid, only : verticalGrid_type implicit none ; private @@ -90,7 +90,7 @@ subroutine continuity_PPM(u, v, hin, h, uh, vh, dt, G, GV, US, CS, OBC, pbv, uhb type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type type(continuity_PPM_CS), intent(in) :: CS !< Module's control structure. type(ocean_OBC_type), pointer :: OBC !< Open boundaries control structure. - type(porous_barrier_ptrs), intent(in) :: pbv !< pointers to porous barrier fractional cell metrics + type(porous_barrier_type), intent(in) :: pbv !< pointers to porous barrier fractional cell metrics real, dimension(SZIB_(G),SZJ_(G)), & optional, intent(in) :: uhbt !< The summed volume flux through zonal faces !! [H L2 T-1 ~> m3 s-1 or kg s-1]. diff --git a/src/core/MOM_dynamics_split_RK2.F90 b/src/core/MOM_dynamics_split_RK2.F90 index c011d18c44..f06c692eaf 100644 --- a/src/core/MOM_dynamics_split_RK2.F90 +++ b/src/core/MOM_dynamics_split_RK2.F90 @@ -3,7 +3,7 @@ module MOM_dynamics_split_RK2 ! This file is part of MOM6. See LICENSE.md for the license. -use MOM_variables, only : vertvisc_type, thermo_var_ptrs, porous_barrier_ptrs +use MOM_variables, only : vertvisc_type, thermo_var_ptrs, porous_barrier_type use MOM_variables, only : BT_cont_type, alloc_bt_cont_type, dealloc_bt_cont_type use MOM_variables, only : accel_diag_ptrs, ocean_internal_state, cont_diag_ptrs use MOM_forcing_type, only : mech_forcing @@ -297,7 +297,7 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s type(MEKE_type), intent(inout) :: MEKE !< MEKE fields type(thickness_diffuse_CS), intent(inout) :: thickness_diffuse_CSp !< Pointer to a structure containing !! interface height diffusivities - type(porous_barrier_ptrs), intent(in) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(in) :: pbv !< porous barrier fractional cell metrics type(wave_parameters_CS), optional, pointer :: Waves !< A pointer to a structure containing !! fields related to the surface wave conditions @@ -1152,7 +1152,7 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param !! the number of times the velocity is !! truncated (this should be 0). logical, intent(out) :: calc_dtbt !< If true, recalculate the barotropic time step - type(porous_barrier_ptrs), intent(in) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(in) :: pbv !< porous barrier fractional cell metrics integer, intent(out) :: cont_stencil !< The stencil for thickness !! from the continuity solver. diff --git a/src/core/MOM_dynamics_unsplit.F90 b/src/core/MOM_dynamics_unsplit.F90 index a7517ccc4f..bc20c30a0f 100644 --- a/src/core/MOM_dynamics_unsplit.F90 +++ b/src/core/MOM_dynamics_unsplit.F90 @@ -50,7 +50,7 @@ module MOM_dynamics_unsplit !* * !********+*********+*********+*********+*********+*********+*********+** -use MOM_variables, only : vertvisc_type, thermo_var_ptrs, porous_barrier_ptrs +use MOM_variables, only : vertvisc_type, thermo_var_ptrs, porous_barrier_type use MOM_variables, only : accel_diag_ptrs, ocean_internal_state, cont_diag_ptrs use MOM_forcing_type, only : mech_forcing use MOM_checksum_packages, only : MOM_thermo_chksum, MOM_state_chksum, MOM_accel_chksum @@ -217,7 +217,7 @@ subroutine step_MOM_dyn_unsplit(u, v, h, tv, visc, Time_local, dt, forces, & !! initialize_dyn_unsplit. type(VarMix_CS), intent(inout) :: VarMix !< Variable mixing control structure type(MEKE_type), intent(inout) :: MEKE !< MEKE fields - type(porous_barrier_ptrs), intent(in) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(in) :: pbv !< porous barrier fractional cell metrics type(wave_parameters_CS), optional, pointer :: Waves !< A pointer to a structure containing !! fields related to the surface wave conditions diff --git a/src/core/MOM_dynamics_unsplit_RK2.F90 b/src/core/MOM_dynamics_unsplit_RK2.F90 index 9acb2b5c83..957306eb3d 100644 --- a/src/core/MOM_dynamics_unsplit_RK2.F90 +++ b/src/core/MOM_dynamics_unsplit_RK2.F90 @@ -48,7 +48,7 @@ module MOM_dynamics_unsplit_RK2 !* * !********+*********+*********+*********+*********+*********+*********+** -use MOM_variables, only : vertvisc_type, thermo_var_ptrs, porous_barrier_ptrs +use MOM_variables, only : vertvisc_type, thermo_var_ptrs, porous_barrier_type use MOM_variables, only : ocean_internal_state, accel_diag_ptrs, cont_diag_ptrs use MOM_forcing_type, only : mech_forcing use MOM_checksum_packages, only : MOM_thermo_chksum, MOM_state_chksum, MOM_accel_chksum @@ -230,7 +230,7 @@ subroutine step_MOM_dyn_unsplit_RK2(u_in, v_in, h_in, tv, visc, Time_local, dt, type(MEKE_type), intent(inout) :: MEKE !< MEKE fields !! fields related to the Mesoscale !! Eddy Kinetic Energy. - type(porous_barrier_ptrs), intent(in) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(in) :: pbv !< porous barrier fractional cell metrics ! Local variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_av ! Averaged layer thicknesses [H ~> m or kg m-2] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: hp ! Predicted layer thicknesses [H ~> m or kg m-2] diff --git a/src/core/MOM_grid.F90 b/src/core/MOM_grid.F90 index f73fcc33af..384e4a6d2b 100644 --- a/src/core/MOM_grid.F90 +++ b/src/core/MOM_grid.F90 @@ -113,13 +113,13 @@ module MOM_grid areaCv !< The areas of the v-grid cells [L2 ~> m2]. real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_) :: & - porous_DminU, & !< minimum topographic height of U-face [Z ~> m] - porous_DmaxU, & !< maximum topographic height of U-face [Z ~> m] + porous_DminU, & !< minimum topographic height (deepest) of U-face [Z ~> m] + porous_DmaxU, & !< maximum topographic height (shallowest) of U-face [Z ~> m] porous_DavgU !< average topographic height of U-face [Z ~> m] real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_) :: & - porous_DminV, & !< minimum topographic height of V-face [Z ~> m] - porous_DmaxV, & !< maximum topographic height of V-face [Z ~> m] + porous_DminV, & !< minimum topographic height (deepest) of V-face [Z ~> m] + porous_DmaxV, & !< maximum topographic height (shallowest) of V-face [Z ~> m] porous_DavgV !< average topographic height of V-face [Z ~> m] real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEMB_PTR_) :: & diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index b96c33d3f3..39a2de5b7f 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -8,7 +8,7 @@ module MOM_porous_barriers use MOM_error_handler, only : MOM_error, FATAL use MOM_grid, only : ocean_grid_type use MOM_unit_scaling, only : unit_scale_type -use MOM_variables, only : thermo_var_ptrs, porous_barrier_ptrs +use MOM_variables, only : thermo_var_ptrs, porous_barrier_type use MOM_verticalGrid, only : verticalGrid_type use MOM_interface_heights, only : find_eta use MOM_time_manager, only : time_type @@ -25,12 +25,16 @@ module MOM_porous_barriers type, public :: porous_barrier_CS; private logical :: initialized = .false. !< True if this control structure has been initialized. - type(diag_ctrl), pointer :: diag => Null() !< A structure to regulate diagnostic output timing - logical :: debug !< If true, write verbose checksums for debugging purposes. - real :: mask_depth !< The depth below which porous barrier is not applied. - integer :: eta_interp !< An integer indicating how the interface heights at the velocity points - !! are calculated. Valid values are given by the parameters defined below: - !! MAX, MIN, ARITHMETIC and HARMONIC. + type(diag_ctrl), pointer :: & + diag => Null() !< A structure to regulate diagnostic output timing + logical :: debug !< If true, write verbose checksums for debugging purposes. + real :: mask_depth !< The depth shallower than which porous barrier is not applied. + integer :: eta_interp !< An integer indicating how the interface heights at the velocity + !! points are calculated. Valid values are given by the parameters + !! defined below: MAX, MIN, ARITHMETIC and HARMONIC. + integer :: answer_date !< The vintage of the porous barrier weight function calculations. + !! Values below 20220806 recover the old answers in which the layer + !! averaged weights are not strictly limited by an upper-bound of 1.0 . integer :: id_por_layer_widthU = -1, id_por_layer_widthV = -1, & id_por_face_areaU = -1, id_por_face_areaV = -1 end type porous_barrier_CS @@ -52,8 +56,7 @@ module MOM_porous_barriers !> subroutine to assign porous barrier widths averaged over a layer subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) - !eta_bt, halo_size, eta_to_m not currently used - !variables needed to call find_eta + ! Note: eta_bt is not currently used type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type @@ -63,7 +66,7 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable !! used to dilate the layer thicknesses !! [H ~> m or kg m-2]. - type(porous_barrier_ptrs), intent(inout) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(inout) :: pbv !< porous barrier fractional cell metrics type(porous_barrier_CS), intent(in) :: CS !< Control structure for porous barrier !local variables @@ -88,7 +91,11 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) is = G%isc; ie = G%iec; js = G%jsc; je = G%jec; nk = GV%ke Isq = G%IscB; Ieq = G%IecB; Jsq = G%JscB; Jeq = G%JecB - dmask = CS%mask_depth + if (CS%answer_date < 20220806) then + dmask = 0.0 + else + dmask = CS%mask_depth + endif call calc_eta_at_uv(eta_u, eta_v, CS%eta_interp, dmask, h, tv, G, GV, US) @@ -104,16 +111,29 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) eta_u(I,j,nk+1), A_layer_prev(I,j), do_I(I,j)) endif ; enddo ; enddo - do k=nk,1,-1 ; do j=js,je ; do I=Isq,Ieq ; if (do_I(I,j)) then - call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & - eta_u(I,j,K), A_layer, do_I(I,j)) - if (eta_u(I,j,K) - (eta_u(I,j,K+1)+h_min) > 0.0) then - pbv%por_face_areaU(I,j,k) = min(1.0, (A_layer - A_layer_prev(I,j)) / (eta_u(I,j,K) - eta_u(I,j,K+1))) - else - pbv%por_face_areaU(I,j,k) = 0.0 ! use calc_por_interface() might be a better choice - endif - A_layer_prev(I,j) = A_layer - endif ; enddo ; enddo ; enddo + if (CS%answer_date < 20220806) then + do k=nk,1,-1 ; do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_u(I,j,K), A_layer, do_I(I,j)) + if (eta_u(I,j,K) - eta_u(I,j,K+1) > 0.0) then + pbv%por_face_areaU(I,j,k) = (A_layer - A_layer_prev(I,j)) / (eta_u(I,j,K) - eta_u(I,j,K+1)) + else + pbv%por_face_areaU(I,j,k) = 0.0 + endif + A_layer_prev(I,j) = A_layer + endif ; enddo ; enddo ; enddo + else + do k=nk,1,-1 ; do j=js,je ; do I=Isq,Ieq ; if (do_I(I,j)) then + call calc_por_layer(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_u(I,j,K), A_layer, do_I(I,j)) + if (eta_u(I,j,K) - (eta_u(I,j,K+1)+h_min) > 0.0) then + pbv%por_face_areaU(I,j,k) = min(1.0, (A_layer - A_layer_prev(I,j)) / (eta_u(I,j,K) - eta_u(I,j,K+1))) + else + pbv%por_face_areaU(I,j,k) = 0.0 ! use calc_por_interface() might be a better choice + endif + A_layer_prev(I,j) = A_layer + endif ; enddo ; enddo ; enddo + endif ! v-points do J=Jsq,Jeq ; do i=is,ie; do_I(i,J) = .False. ; enddo ; enddo @@ -123,16 +143,29 @@ subroutine porous_widths_layer(h, tv, G, GV, US, pbv, CS, eta_bt) eta_v(i,J,nk+1), A_layer_prev(i,J), do_I(i,J)) endif ; enddo ; enddo - do k=nk,1,-1 ; do J=Jsq,Jeq ; do i=is,ie ; if (do_I(i,J)) then - call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & - eta_v(i,J,K), A_layer, do_I(i,J)) - if (eta_v(i,J,K) - (eta_v(i,J,K+1)+h_min) > 0.0) then - pbv%por_face_areaV(i,J,k) = min(1.0, (A_layer - A_layer_prev(i,J)) / (eta_v(i,J,K) - eta_v(i,J,K+1))) - else - pbv%por_face_areaV(i,J,k) = 0.0 ! use calc_por_interface() might be a better choice - endif - A_layer_prev(i,J) = A_layer - endif ; enddo ; enddo ; enddo + if (CS%answer_date < 20220806) then + do k=nk,1,-1 ; do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_v(i,J,K), A_layer, do_I(i,J)) + if (eta_v(i,J,K) - eta_v(i,J,K+1) > 0.0) then + pbv%por_face_areaV(i,J,k) = (A_layer - A_layer_prev(i,J)) / (eta_v(i,J,K) - eta_v(i,J,K+1)) + else + pbv%por_face_areaV(i,J,k) = 0.0 + endif + A_layer_prev(i,J) = A_layer + endif ; enddo ; enddo ; enddo + else + do k=nk,1,-1 ; do J=Jsq,Jeq ; do i=is,ie ; if (do_I(i,J)) then + call calc_por_layer(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_v(i,J,K), A_layer, do_I(i,J)) + if (eta_v(i,J,K) - (eta_v(i,J,K+1)+h_min) > 0.0) then + pbv%por_face_areaV(i,J,k) = min(1.0, (A_layer - A_layer_prev(i,J)) / (eta_v(i,J,K) - eta_v(i,J,K+1))) + else + pbv%por_face_areaV(i,J,k) = 0.0 ! use calc_por_interface() might be a better choice + endif + A_layer_prev(i,J) = A_layer + endif ; enddo ; enddo ; enddo + endif if (CS%debug) then call uvchksum("Interface height used by porous barrier for layer weights", & @@ -149,8 +182,7 @@ end subroutine porous_widths_layer !> subroutine to assign porous barrier widths at the layer interfaces subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) - !eta_bt, halo_size, eta_to_m not currently used - !variables needed to call find_eta + ! Note: eta_bt is not currently used type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type @@ -160,7 +192,7 @@ subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable !! used to dilate the layer thicknesses !! [H ~> m or kg m-2]. - type(porous_barrier_ptrs), intent(inout) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type), intent(inout) :: pbv !< porous barrier fractional cell metrics type(porous_barrier_CS), intent(in) :: CS !< Control structure for porous barrier !local variables @@ -181,7 +213,11 @@ subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) is = G%isc; ie = G%iec; js = G%jsc; je = G%jec; nk = GV%ke Isq = G%IscB; Ieq = G%IecB; Jsq = G%JscB; Jeq = G%JecB - dmask = CS%mask_depth + if (CS%answer_date < 20220806) then + dmask = 0.0 + else + dmask = CS%mask_depth + endif call calc_eta_at_uv(eta_u, eta_v, CS%eta_interp, dmask, h, tv, G, GV, US) @@ -191,10 +227,17 @@ subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) if (G%porous_DavgU(I,j) < dmask) do_I(I,j) = .True. enddo ; enddo - do K=1,nk+1 ; do j=js,je ; do I=Isq,Ieq ; if (do_I(I,j)) then - call calc_por_interface(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & - eta_u(I,j,K), pbv%por_layer_widthU(I,j,K), do_I(I,j)) - endif ; enddo ; enddo ; enddo + if (CS%answer_date < 20220806) then + do K=1,nk+1 ; do j=js,je ; do I=Isq,Ieq ; if (G%porous_DavgU(I,j) < dmask) then + call calc_por_interface(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_u(I,j,K), pbv%por_layer_widthU(I,j,K), do_I(I,j)) + endif ; enddo ; enddo ; enddo + else + do K=1,nk+1 ; do j=js,je ; do I=Isq,Ieq ; if (do_I(I,j)) then + call calc_por_interface(G%porous_DminU(I,j), G%porous_DmaxU(I,j), G%porous_DavgU(I,j), & + eta_u(I,j,K), pbv%por_layer_widthU(I,j,K), do_I(I,j)) + endif ; enddo ; enddo ; enddo + endif ! v-points do J=Jsq,Jeq ; do i=is,ie @@ -202,10 +245,17 @@ subroutine porous_widths_interface(h, tv, G, GV, US, pbv, CS, eta_bt) if (G%porous_DavgV(i,J) < dmask) do_I(i,J) = .True. enddo ; enddo - do K=1,nk+1 ; do J=Jsq,Jeq ; do i=is,ie ; if (do_I(i,J)) then - call calc_por_interface(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & - eta_v(i,J,K), pbv%por_layer_widthV(i,J,K), do_I(i,J)) - endif ; enddo ; enddo ; enddo + if (CS%answer_date < 20220806) then + do K=1,nk+1 ; do J=Jsq,Jeq ; do i=is,ie ; if (G%porous_DavgV(i,J) < dmask) then + call calc_por_interface(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_v(i,J,K), pbv%por_layer_widthV(i,J,K), do_I(i,J)) + endif ; enddo ; enddo ; enddo + else + do K=1,nk+1 ; do J=Jsq,Jeq ; do i=is,ie ; if (do_I(i,J)) then + call calc_por_interface(G%porous_DminV(i,J), G%porous_DmaxV(i,J), G%porous_DavgV(i,J), & + eta_v(i,J,K), pbv%por_layer_widthV(i,J,K), do_I(i,J)) + endif ; enddo ; enddo ; enddo + endif if (CS%debug) then call uvchksum("Interface height used by porous barrier for interface weights", & @@ -231,7 +281,7 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable !! used to dilate the layer thicknesses !! [H ~> m or kg m-2]. - real, intent(in) :: dmask !< The depth below which + real, intent(in) :: dmask !< The depth shaller than which !! porous barrier is not applied [Z ~> m] integer, intent(in) :: interp !< eta interpolation method real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: eta_u !< Layer interface heights at u points [Z ~> m] @@ -297,14 +347,14 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) end subroutine calc_eta_at_uv !> subroutine to calculate the profile fit (the three parameter fit from Adcroft 2013) -! for a single layer in a column +! of the open face area fraction below a certain depth (eta_layer) in a column subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, A_layer, do_next) - real, intent(in) :: D_min !< minimum topographic height (deepest) [Z ~> m] - real, intent(in) :: D_max !< maximum topographic height (shallowest) [Z ~> m] - real, intent(in) :: D_avg !< mean topographic height [Z ~> m] - real, intent(in) :: eta_layer !< height of interface [Z ~> m] - real, intent(out) :: A_layer !< frac. open face area of below eta_layer [Z ~> m] - logical, intent(out) :: do_next !< False if eta_layer > D_max + real, intent(in) :: D_min !< minimum topographic height (deepest) [Z ~> m] + real, intent(in) :: D_max !< maximum topographic height (shallowest) [Z ~> m] + real, intent(in) :: D_avg !< mean topographic height [Z ~> m] + real, intent(in) :: eta_layer !< height of interface [Z ~> m] + real, intent(out) :: A_layer !< frac. open face area of below eta_layer [Z ~> m] + logical, intent(out) :: do_next !< False if eta_layer>D_max ! local variables real :: m, & ! convenience constant for fit [nondim] @@ -329,13 +379,15 @@ subroutine calc_por_layer(D_min, D_max, D_avg, eta_layer, A_layer, do_next) endif end subroutine calc_por_layer +!> subroutine to calculate the profile fit (the three parameter fit from Adcroft 2013) +! of the open interface fraction at a certain depth (eta_layer) in a column subroutine calc_por_interface(D_min, D_max, D_avg, eta_layer, w_layer, do_next) - real, intent(in) :: D_min !< minimum topographic height (deepest) [Z ~> m] - real, intent(in) :: D_max !< maximum topographic height (shallowest) [Z ~> m] - real, intent(in) :: D_avg !< mean topographic height [Z ~> m] - real, intent(in) :: eta_layer !< height of interface [Z ~> m] - real, intent(out) :: w_layer !< frac. open interface width at eta_layer [nondim] - logical, intent(out) :: do_next !< False if eta_layer > D_max + real, intent(in) :: D_min !< minimum topographic height (deepest) [Z ~> m] + real, intent(in) :: D_max !< maximum topographic height (shallowest) [Z ~> m] + real, intent(in) :: D_avg !< mean topographic height [Z ~> m] + real, intent(in) :: eta_layer !< height of interface [Z ~> m] + real, intent(out) :: w_layer !< frac. open interface width at eta_layer [nondim] + logical, intent(out) :: do_next !< False if eta_layer>D_max ! local variables real :: m, a, & ! convenience constant for fit [nondim] @@ -362,29 +414,38 @@ subroutine calc_por_interface(D_min, D_max, D_avg, eta_layer, w_layer, do_next) end subroutine calc_por_interface subroutine porous_barriers_init(Time, US, param_file, diag, CS) - type(porous_barrier_CS), intent(inout) :: CS !< Module control structure - type(param_file_type), intent(in) :: param_file !< structure indicating parameter file to parse - type(time_type), intent(in) :: Time !< Current model time - type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure - type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + type(porous_barrier_CS), intent(inout) :: CS !< Module control structure + type(param_file_type), intent(in) :: param_file !< structure indicating parameter file to parse + type(time_type), intent(in) :: Time !< Current model time + type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - !> This include declares and sets the variable "version". -# include "version_variable.h" ! local variables character(len=40) :: mdl = "MOM_porous_barriers" ! This module's name. character(len=20) :: interp_method ! String storing eta interpolation method + integer :: default_answer_date ! Global answer date + !> This include declares and sets the variable "version". +# include "version_variable.h" CS%initialized = .true. CS%diag => diag call log_version(param_file, mdl, version, "", log_to_all=.true., layout=.false., & debugging=.false.) + call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & + "This sets the default value for the various _ANSWER_DATE parameters.", & + default=99991231) + call get_param(param_file, mdl, "PORBAR_ANSWER_DATE", CS%answer_date, & + "The vintage of the porous barrier weight function calculations. Values below "//& + "20220806 recover the old answers in which the layer averaged weights are not "//& + "strictly limited by an upper-bound of 1.0 .", default=default_answer_date) call get_param(param_file, mdl, "DEBUG", CS%debug, default=.false.) call get_param(param_file, mdl, "PORBAR_MASKING_DEPTH", CS%mask_depth, & - "The depth below which porous barrier is not applied. "//& - "The effective average depths at the velocity cells are used "//& - "to test against this criterion.", units="m", default=0.0, & - scale=US%m_to_Z) + "If the effective average depth at the velocity cell is shallower than this "//& + "number, then porous barrier is not applied at that location. "//& + "PORBAR_MASKING_DEPTH is assumed to be positive below the sea surface.", & + units="m", default=0.0, scale=US%m_to_Z) + ! The sign needs to be inverted to be consistent with the sign convention of Davg_[UV] CS%mask_depth = -CS%mask_depth call get_param(param_file, mdl, "PORBAR_ETA_INTERP", interp_method, & "A string describing the method that decicdes how the "//& @@ -394,7 +455,7 @@ subroutine porous_barriers_init(Time, US, param_file, diag, CS) "\t MIN - minimum of the adjacent cells \n"//& "\t ARITHMETIC - arithmetic mean of the adjacent cells \n"//& "\t HARMOINIC - harmonic mean of the adjacent cells \n", & - default=ETA_INTERP_MAX_STRING) ! do_not_log=.not.CS%use_por_bar) + default=ETA_INTERP_MAX_STRING) select case (interp_method) case (ETA_INTERP_MAX_STRING) ; CS%eta_interp = ETA_INTERP_MAX case (ETA_INTERP_MIN_STRING) ; CS%eta_interp = ETA_INTERP_MIN diff --git a/src/core/MOM_variables.F90 b/src/core/MOM_variables.F90 index ef90b0b3c5..6fc81fa976 100644 --- a/src/core/MOM_variables.F90 +++ b/src/core/MOM_variables.F90 @@ -310,15 +310,15 @@ module MOM_variables end type BT_cont_type !> Container for grids modifying cell metric at porous barriers -! TODO: rename porous_barrier_ptrs to porous_barrier_type -type, public :: porous_barrier_ptrs +! TODO: rename porous_barrier_type to porous_barrier_type +type, public :: porous_barrier_type ! Each of the following fields has nz layers. real, allocatable :: por_face_areaU(:,:,:) !< fractional open area of U-faces [nondim] real, allocatable :: por_face_areaV(:,:,:) !< fractional open area of V-faces [nondim] ! Each of the following fields is found at nz+1 interfaces. real, allocatable :: por_layer_widthU(:,:,:) !< fractional open width of U-faces [nondim] real, allocatable :: por_layer_widthV(:,:,:) !< fractional open width of V-faces [nondim] -end type porous_barrier_ptrs +end type porous_barrier_type contains diff --git a/src/framework/MOM_dyn_horgrid.F90 b/src/framework/MOM_dyn_horgrid.F90 index bfc6f1b1a4..e97c8d981b 100644 --- a/src/framework/MOM_dyn_horgrid.F90 +++ b/src/framework/MOM_dyn_horgrid.F90 @@ -110,13 +110,13 @@ module MOM_dyn_horgrid areaCv !< The areas of the v-grid cells [L2 ~> m2]. real, allocatable, dimension(:,:) :: & - porous_DminU, & !< minimum topographic height of U-face [Z ~> m] - porous_DmaxU, & !< maximum topographic height of U-face [Z ~> m] + porous_DminU, & !< minimum topographic height (deepest) of U-face [Z ~> m] + porous_DmaxU, & !< maximum topographic height (shallowest) of U-face [Z ~> m] porous_DavgU !< average topographic height of U-face [Z ~> m] real, allocatable, dimension(:,:) :: & - porous_DminV, & !< minimum topographic height of V-face [Z ~> m] - porous_DmaxV, & !< maximum topographic height of V-face [Z ~> m] + porous_DminV, & !< minimum topographic height (deepest) of V-face [Z ~> m] + porous_DmaxV, & !< maximum topographic height (shallowest) of V-face [Z ~> m] porous_DavgV !< average topographic height of V-face [Z ~> m] real, allocatable, dimension(:,:) :: & diff --git a/src/initialization/MOM_shared_initialization.F90 b/src/initialization/MOM_shared_initialization.F90 index 04eab60569..ff272e7fce 100644 --- a/src/initialization/MOM_shared_initialization.F90 +++ b/src/initialization/MOM_shared_initialization.F90 @@ -1171,12 +1171,16 @@ end subroutine read_face_length_list !! for the use of porous barrier. !! Note that we assume the depth values in the sub-grid bathymetry file of the same !! convention as in-cell bathymetry file, i.e. positive below the sea surface and -!! increasing downward. This is the opposite of the convention in subroutine -!! porous_widths. Therefore, all signs of the variable are reverted here. +!! increasing downward; while in subroutine reset_face_lengths_list, it is implied +!! that read-in fields min_bathy, max_bathy and avg_bathy from the input file +!! CHANNEL_LIST_FILE all have negative values below the surface. Therefore, to ensure +!! backward compatibility, all signs of the variable are inverted here. +!! And porous_Dmax[UV] = shallowest point, porous_Dmin[UV] = deepest point subroutine set_subgrid_topo_at_vel_from_file(G, param_file, US) - type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type + type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type type(param_file_type), intent(in) :: param_file !< Parameter file structure - type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + ! Local variables character(len=200) :: filename, topo_file, inputdir ! Strings for file/path character(len=200) :: varname_uhi, varname_ulo, varname_uav, & @@ -1225,8 +1229,8 @@ subroutine set_subgrid_topo_at_vel_from_file(G, param_file, US) call MOM_read_vector(filename, trim(varname_uav), trim(varname_vav), & G%porous_DavgU, G%porous_DavgV, G%Domain, stagger=CGRID_NE, scale=US%m_to_Z) - ! The signs of the depth parameters need to be reverted to comply with subroutine calc_por_layer, - ! which assumes depth is negative below the sea surface. + ! The signs of the depth parameters need to be inverted to be backward compatible with input files + ! used by subroutine reset_face_lengths_list, which assumes depth is negative below the sea surface. G%porous_DmaxU = -G%porous_DmaxU; G%porous_DminU = -G%porous_DminU; G%porous_DavgU = -G%porous_DavgU G%porous_DmaxV = -G%porous_DmaxV; G%porous_DminV = -G%porous_DminV; G%porous_DavgV = -G%porous_DavgV diff --git a/src/parameterizations/vertical/MOM_set_viscosity.F90 b/src/parameterizations/vertical/MOM_set_viscosity.F90 index 448ae079f4..df6ed9063b 100644 --- a/src/parameterizations/vertical/MOM_set_viscosity.F90 +++ b/src/parameterizations/vertical/MOM_set_viscosity.F90 @@ -23,7 +23,7 @@ module MOM_set_visc use MOM_restart, only : register_restart_field_as_obsolete use MOM_safe_alloc, only : safe_alloc_ptr, safe_alloc_alloc use MOM_unit_scaling, only : unit_scale_type -use MOM_variables, only : thermo_var_ptrs, vertvisc_type, porous_barrier_ptrs +use MOM_variables, only : thermo_var_ptrs, vertvisc_type, porous_barrier_type use MOM_verticalGrid, only : verticalGrid_type use MOM_EOS, only : calculate_density, calculate_density_derivs use MOM_open_boundary, only : ocean_OBC_type, OBC_NONE, OBC_DIRECTION_E @@ -139,7 +139,7 @@ subroutine set_viscous_BBL(u, v, h, tv, visc, G, GV, US, CS, pbv) !! related fields. type(set_visc_CS), intent(inout) :: CS !< The control structure returned by a previous !! call to set_visc_init. - type(porous_barrier_ptrs),intent(in) :: pbv !< porous barrier fractional cell metrics + type(porous_barrier_type),intent(in) :: pbv !< porous barrier fractional cell metrics ! Local variables real, dimension(SZIB_(G)) :: & From b8bf337894c49d72d243751aaf8a68743bb700cf Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 8 Aug 2022 00:42:01 -0400 Subject: [PATCH 17/68] Bugfix for uninitialized eta_[uv] * Fixed a bug that eta_[uv] was not properly initialized, which caused issues with global chksums with DEBUG=True. * Documents added to comply with Doxygen tests --- src/core/MOM.F90 | 3 ++- src/core/MOM_porous_barriers.F90 | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 9f1fd99a26..1cd4ea578e 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -413,7 +413,8 @@ module MOM !! ensemble model state vectors and data assimilation !! increments and priors type(dbcomms_CS_type) :: dbcomms_CS !< Control structure for database client used for online ML/AI - logical :: use_porbar + logical :: use_porbar !< If true, use porous barrier to constrain the widths and face areas + !! at the edges of the grid cells. type(porous_barrier_type) :: pbv !< porous barrier fractional cell metrics type(particles), pointer :: particles => NULL() ! NULL() !< a pointer to the stochastics control structure diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index 39a2de5b7f..cac8a44bf0 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -23,6 +23,7 @@ module MOM_porous_barriers #include +!> The control structure for the MOM_porous_barriers module type, public :: porous_barrier_CS; private logical :: initialized = .false. !< True if this control structure has been initialized. type(diag_ctrl), pointer :: & @@ -35,8 +36,10 @@ module MOM_porous_barriers integer :: answer_date !< The vintage of the porous barrier weight function calculations. !! Values below 20220806 recover the old answers in which the layer !! averaged weights are not strictly limited by an upper-bound of 1.0 . + !>@{ Diagnostic IDs integer :: id_por_layer_widthU = -1, id_por_layer_widthV = -1, & id_por_face_areaU = -1, id_por_face_areaV = -1 + !>@} end type porous_barrier_CS integer :: id_clock_porous_barrier !< CPU clock for porous barrier @@ -303,6 +306,11 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) H_to_eta = GV%H_to_m * US%m_to_Z * Z_to_eta h_neglect = GV%H_subroundoff * H_to_eta + do K=1,nk+1 + do j=js,je ; do I=Isq,Ieq ; eta_u(I,j,K) = dmask ; enddo ; enddo + do J=Jsq,Jeq ; do i=is,ie ; eta_v(i,J,K) = dmask ; enddo ; enddo + enddo + select case (interp) case (ETA_INTERP_MAX) ! The shallower interface height do K=1,nk+1 From 34247a9b8276a6eea06b9f71abdb5b0dbb96d266 Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 22 Aug 2022 14:25:00 -0400 Subject: [PATCH 18/68] Bugfix for allocating porous barrier variables --- src/core/MOM.F90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 1cd4ea578e..7444597bbb 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -2485,10 +2485,10 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & CS%time_in_cycle = 0.0 ; CS%time_in_thermo_cycle = 0.0 !allocate porous topography variables - ALLOC_(CS%pbv%por_face_areaU(IsdB:IedB,jsd:jed,nz)) ; CS%pbv%por_face_areaU(:,:,:) = 1.0 - ALLOC_(CS%pbv%por_face_areaV(isd:ied,JsdB:JedB,nz)) ; CS%pbv%por_face_areaV(:,:,:) = 1.0 - ALLOC_(CS%pbv%por_layer_widthU(IsdB:IedB,jsd:jed,nz+1)) ; CS%pbv%por_layer_widthU(:,:,:) = 1.0 - ALLOC_(CS%pbv%por_layer_widthV(isd:ied,JsdB:JedB,nz+1)) ; CS%pbv%por_layer_widthV(:,:,:) = 1.0 + allocate(CS%pbv%por_face_areaU(IsdB:IedB,jsd:jed,nz)) ; CS%pbv%por_face_areaU(:,:,:) = 1.0 + allocate(CS%pbv%por_face_areaV(isd:ied,JsdB:JedB,nz)) ; CS%pbv%por_face_areaV(:,:,:) = 1.0 + allocate(CS%pbv%por_layer_widthU(IsdB:IedB,jsd:jed,nz+1)) ; CS%pbv%por_layer_widthU(:,:,:) = 1.0 + allocate(CS%pbv%por_layer_widthV(isd:ied,JsdB:JedB,nz+1)) ; CS%pbv%por_layer_widthV(:,:,:) = 1.0 ! Use the Wright equation of state by default, unless otherwise specified ! Note: this line and the following block ought to be in a separate @@ -3785,8 +3785,8 @@ subroutine MOM_end(CS) if (CS%use_ALE_algorithm) call ALE_end(CS%ALE_CSp) !deallocate porous topography variables - DEALLOC_(CS%pbv%por_face_areaU) ; DEALLOC_(CS%pbv%por_face_areaV) - DEALLOC_(CS%pbv%por_layer_widthU) ; DEALLOC_(CS%pbv%por_layer_widthV) + deallocate(CS%pbv%por_face_areaU) ; deallocate(CS%pbv%por_face_areaV) + deallocate(CS%pbv%por_layer_widthU) ; deallocate(CS%pbv%por_layer_widthV) ! NOTE: Allocated in PressureForce_FV_Bouss if (associated(CS%tv%varT)) deallocate(CS%tv%varT) From 1c85a00ff3a952d20535737f008edb8d17575b38 Mon Sep 17 00:00:00 2001 From: WenhaoChen89 <96131003+WenhaoChen89@users.noreply.github.com> Date: Sat, 27 Aug 2022 22:28:10 -0400 Subject: [PATCH 19/68] +Fix nudged OBCs for tracers (#194) * Fix nudged OBCs for tracers --- src/core/MOM_open_boundary.F90 | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index edaa2bc1d8..cabf9272ca 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -5070,6 +5070,7 @@ subroutine update_segment_tracer_reservoirs(G, GV, uhr, vhr, h, OBC, dt, Reg) real, intent(in) :: dt !< time increment [T ~> s] type(tracer_registry_type), pointer :: Reg !< pointer to tracer registry + ! Local variable type(OBC_segment_type), pointer :: segment=>NULL() real :: u_L_in, u_L_out ! The zonal distance moved in or out of a cell, normalized by the reservoir ! length scale [nondim] @@ -5080,6 +5081,12 @@ subroutine update_segment_tracer_reservoirs(G, GV, uhr, vhr, h, OBC, dt, Reg) ! For salinity the units would be [ppt S-1 ~> 1] integer :: i, j, k, m, n, ntr, nz integer :: ishift, idir, jshift, jdir + real :: b_in, b_out ! The 0 and 1 switch for tracer reservoirs + ! 1 if the length scale of reservoir is zero [nodim] + real :: a_in, a_out ! The 0 and 1(-1) switch for reservoir source weights + ! e.g. a_in is -1 only if b_in ==1 and uhr or vhr is inward + ! e.g. a_out is 1 only if b_out==1 and uhr or vhr is outward + ! It's clear that a_in and a_out cannot be both non-zero [nodim] nz = GV%ke ntr = Reg%ntr @@ -5087,6 +5094,8 @@ subroutine update_segment_tracer_reservoirs(G, GV, uhr, vhr, h, OBC, dt, Reg) if (associated(OBC)) then ; if (OBC%OBC_pe) then ; do n=1,OBC%number_of_segments segment=>OBC%segment(n) if (.not. associated(segment%tr_Reg)) cycle + b_in = 0.0; if (segment%Tr_InvLscale_in == 0.0) b_in = 1.0 + b_out = 0.0; if (segment%Tr_InvLscale_out == 0.0) b_out = 1.0 if (segment%is_E_or_W) then I = segment%HI%IsdB do j=segment%HI%jsd,segment%HI%jed @@ -5103,14 +5112,21 @@ subroutine update_segment_tracer_reservoirs(G, GV, uhr, vhr, h, OBC, dt, Reg) do m=1,ntr I_scale = 1.0 ; if (segment%tr_Reg%Tr(m)%scale /= 0.0) I_scale = 1.0 / segment%tr_Reg%Tr(m)%scale if (allocated(segment%tr_Reg%Tr(m)%tres)) then ; do k=1,nz + ! Calculate weights. Both a and u_L are nodim. Adding them together has no meaning. + ! However, since they cannot be both non-zero, adding them works like a switch. + ! When InvLscale_out is 0 and outflow, only interior data is applied to reservoirs + ! When InvLscale_in is 0 and inflow, only nudged data is applied to reservoirs + a_out = b_out * max(0.0, sign(1.0, idir*uhr(I,j,k))) + a_in = b_in * min(0.0, sign(1.0, idir*uhr(I,j,k))) u_L_out = max(0.0, (idir*uhr(I,j,k))*segment%Tr_InvLscale_out / & ((h(i+ishift,j,k) + GV%H_subroundoff)*G%dyCu(I,j))) u_L_in = min(0.0, (idir*uhr(I,j,k))*segment%Tr_InvLscale_in / & ((h(i+ishift,j,k) + GV%H_subroundoff)*G%dyCu(I,j))) - fac1 = 1.0 + (u_L_out-u_L_in) - segment%tr_Reg%Tr(m)%tres(I,j,k) = (1.0/fac1)*(segment%tr_Reg%Tr(m)%tres(I,j,k) + & - (u_L_out*Reg%Tr(m)%t(I+ishift,j,k) - & - u_L_in*segment%tr_Reg%Tr(m)%t(I,j,k))) + fac1 = (1.0 - (a_out - a_in)) + ((u_L_out + a_out) - (u_L_in + a_in)) + segment%tr_Reg%Tr(m)%tres(I,j,k) = (1.0/fac1) * & + ((1.0-a_out+a_in)*segment%tr_Reg%Tr(m)%tres(I,j,k)+ & + ((u_L_out+a_out)*Reg%Tr(m)%t(I+ishift,j,k) - & + (u_L_in+a_in)*segment%tr_Reg%Tr(m)%t(I,j,k))) if (allocated(OBC%tres_x)) OBC%tres_x(I,j,k,m) = I_scale * segment%tr_Reg%Tr(m)%tres(I,j,k) enddo ; endif enddo @@ -5131,14 +5147,18 @@ subroutine update_segment_tracer_reservoirs(G, GV, uhr, vhr, h, OBC, dt, Reg) do m=1,ntr I_scale = 1.0 ; if (segment%tr_Reg%Tr(m)%scale /= 0.0) I_scale = 1.0 / segment%tr_Reg%Tr(m)%scale if (allocated(segment%tr_Reg%Tr(m)%tres)) then ; do k=1,nz + a_out = b_out * max(0.0, sign(1.0, jdir*vhr(i,J,k))) + a_in = b_in * min(0.0, sign(1.0, jdir*vhr(i,J,k))) v_L_out = max(0.0, (jdir*vhr(i,J,k))*segment%Tr_InvLscale_out / & ((h(i,j+jshift,k) + GV%H_subroundoff)*G%dxCv(i,J))) v_L_in = min(0.0, (jdir*vhr(i,J,k))*segment%Tr_InvLscale_in / & ((h(i,j+jshift,k) + GV%H_subroundoff)*G%dxCv(i,J))) fac1 = 1.0 + (v_L_out-v_L_in) - segment%tr_Reg%Tr(m)%tres(i,J,k) = (1.0/fac1)*(segment%tr_Reg%Tr(m)%tres(i,J,k) + & - (v_L_out*Reg%Tr(m)%t(i,J+jshift,k) - & - v_L_in*segment%tr_Reg%Tr(m)%t(i,J,k))) + fac1 = (1.0 - (a_out - a_in)) + ((v_L_out + a_out) - (v_L_in + a_in)) + segment%tr_Reg%Tr(m)%tres(i,J,k) = (1.0/fac1) * & + ((1.0-a_out+a_in)*segment%tr_Reg%Tr(m)%tres(i,J,k) + & + ((v_L_out+a_out)*Reg%Tr(m)%t(i,J+jshift,k) - & + (v_L_in+a_in)*segment%tr_Reg%Tr(m)%t(i,J,k))) if (allocated(OBC%tres_y)) OBC%tres_y(i,J,k,m) = I_scale * segment%tr_Reg%Tr(m)%tres(i,J,k) enddo ; endif enddo From 575595a995a12960b01e8fe46b9d4c54ec3433ad Mon Sep 17 00:00:00 2001 From: Marshall Ward Date: Fri, 12 Aug 2022 14:41:24 -0400 Subject: [PATCH 20/68] Report error logs from codecov upload fail Failed codecov.io uploads now report the stderr output. This will allow us to start diagnosing the intermittent failures and, possibly, find a solution. --- .testing/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.testing/Makefile b/.testing/Makefile index 917feb311b..2bd9c6f39d 100644 --- a/.testing/Makefile +++ b/.testing/Makefile @@ -602,6 +602,7 @@ report.cov: run.cov codecov 2> build/cov/codecov.err \ && echo -e "${MAGENTA}Report uploaded to codecov.${RESET}" \ || { \ + cat build/cov/codecov.err ; \ echo -e "${RED}Failed to upload report.${RESET}" ; \ if [ "$(REQUIRE_COVERAGE_UPLOAD)" = true ] ; then false ; fi ; \ } @@ -740,6 +741,7 @@ report.cov.unit: build/unit/MOM_file_parser_tests.F90.gcov codecov 2> build/unit/codecov.err \ && echo -e "${MAGENTA}Report uploaded to codecov.${RESET}" \ || { \ + cat build/unit/codecov.err ; \ echo -e "${RED}Failed to upload report.${RESET}" ; \ if [ "$(REQUIRE_COVERAGE_UPLOAD)" = true ] ; then false ; fi ; \ } From 9a2c92bf2029b89a99679e81de0f0893673c2ba4 Mon Sep 17 00:00:00 2001 From: Kate Hedstrom Date: Mon, 29 Aug 2022 19:38:10 -0800 Subject: [PATCH 21/68] Obsolete the HENYEY_IGW_BACKGROUND_NEW option. --- src/diagnostics/MOM_obsolete_params.F90 | 1 + src/parameterizations/vertical/MOM_bkgnd_mixing.F90 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/diagnostics/MOM_obsolete_params.F90 b/src/diagnostics/MOM_obsolete_params.F90 index e0441cac2e..7fa2858583 100644 --- a/src/diagnostics/MOM_obsolete_params.F90 +++ b/src/diagnostics/MOM_obsolete_params.F90 @@ -71,6 +71,7 @@ subroutine find_obsolete_params(param_file) call obsolete_real(param_file, "VSTAR_SCALE_COEF") call obsolete_real(param_file, "ZSTAR_RIGID_SURFACE_THRESHOLD") + call obsolete_real(param_file, "HENYEY_IGW_BACKGROUND_NEW") ! Test for inconsistent parameter settings. split = .true. ; test_logic = .false. diff --git a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 index 7c427ab79a..4f94b615fb 100644 --- a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 +++ b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 @@ -255,7 +255,7 @@ subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS) call get_param(param_file, mdl, "HENYEY_IGW_BACKGROUND_NEW", CS%Henyey_IGW_background_new, & "If true, use a better latitude-dependent scaling for the "//& "background diffusivity, as described in "//& - "Harrison & Hallberg, JPO 2008.", default=.false.) + "Harrison & Hallberg, JPO 2008. This option is obsolete.", default=.false.) if (CS%Henyey_IGW_background_new) call check_bkgnd_scheme(CS, "HENYEY_IGW_BACKGROUND_NEW") if (CS%Kd>0.0 .and. (trim(CS%bkgnd_scheme_str)=="BRYAN_LEWIS_DIFFUSIVITY" .or.& From 97dfe719c7dfc47523e5202dde219c7945f75be4 Mon Sep 17 00:00:00 2001 From: Kate Hedstrom Date: Tue, 30 Aug 2022 14:50:42 -0800 Subject: [PATCH 22/68] Completing the job of obsoleting something. --- src/diagnostics/MOM_obsolete_params.F90 | 2 +- .../vertical/MOM_bkgnd_mixing.F90 | 21 +------------------ .../vertical/_V_diffusivity.dox | 20 ------------------ 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/src/diagnostics/MOM_obsolete_params.F90 b/src/diagnostics/MOM_obsolete_params.F90 index 7fa2858583..e686261fdf 100644 --- a/src/diagnostics/MOM_obsolete_params.F90 +++ b/src/diagnostics/MOM_obsolete_params.F90 @@ -71,7 +71,7 @@ subroutine find_obsolete_params(param_file) call obsolete_real(param_file, "VSTAR_SCALE_COEF") call obsolete_real(param_file, "ZSTAR_RIGID_SURFACE_THRESHOLD") - call obsolete_real(param_file, "HENYEY_IGW_BACKGROUND_NEW") + call obsolete_logical(param_file, "HENYEY_IGW_BACKGROUND_NEW") ! Test for inconsistent parameter settings. split = .true. ; test_logic = .false. diff --git a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 index 4f94b615fb..6c9a89b0d9 100644 --- a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 +++ b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 @@ -291,7 +291,7 @@ subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS) CS%Kd_via_Kdml_bug = .false. if ((CS%Kd /= CS%Kdml) .and. .not.(CS%Kd_tanh_lat_fn .or. CS%bulkmixedlayer .or. & - CS%Henyey_IGW_background .or. CS%Henyey_IGW_background_new .or. & + CS%Henyey_IGW_background .or. & CS%horiz_varying_background .or. CS%Bryan_Lewis_diffusivity)) then call get_param(param_file, mdl, "KD_BACKGROUND_VIA_KDML_BUG", CS%Kd_via_Kdml_bug, & "If true and KDML /= KD and several other conditions apply, the background "//& @@ -428,25 +428,6 @@ subroutine calculate_bkgnd_mixing(h, tv, N2_lay, Kd_lay, Kd_int, Kv_bkgnd, j, G, Kd_lay(i,k) = Kd_int(i,1) enddo ; enddo - elseif (CS%Henyey_IGW_background_new) then - I_x30 = 2.0 / invcosh(CS%N0_2Omega*2.0) ! This is evaluated at 30 deg. - I_2Omega = 0.5 / CS%omega - do k=1,nz ; do i=is,ie - abs_sinlat = max(min_sinlat, abs(sin(G%geoLatT(i,j)*deg_to_rad))) - N_2Omega = max(abs_sinlat, sqrt(N2_lay(i,k))*I_2Omega) - N02_N2 = (CS%N0_2Omega/N_2Omega)**2 - Kd_lay(i,k) = max(CS%Kd_min, CS%Kd * & - ((abs_sinlat * invcosh(N_2Omega/abs_sinlat)) * I_x30)*N02_N2) - enddo ; enddo - ! Update Kd_int and Kv_bkgnd, based on Kd_lay. These might be just used for diagnostic purposes. - do i=is,ie - Kd_int(i,1) = 0.0; Kv_bkgnd(i,1) = 0.0 - Kd_int(i,nz+1) = 0.0; Kv_bkgnd(i,nz+1) = 0.0 - enddo - do K=2,nz ; do i=is,ie - Kd_int(i,K) = 0.5*(Kd_lay(i,k-1) + Kd_lay(i,k)) - Kv_bkgnd(i,K) = Kd_int(i,K) * CS%prandtl_bkgnd - enddo ; enddo else ! Set a potentially spatially varying surface value of diffusivity. if (CS%Henyey_IGW_background) then diff --git a/src/parameterizations/vertical/_V_diffusivity.dox b/src/parameterizations/vertical/_V_diffusivity.dox index f3b7ed5962..df1ce50e27 100644 --- a/src/parameterizations/vertical/_V_diffusivity.dox +++ b/src/parameterizations/vertical/_V_diffusivity.dox @@ -253,26 +253,6 @@ in \cite harrison2008, but that isn't what is in the MOM6 code. Instead, the sur value is propagated down, with the assumption that the tidal mixing parameterization will provide the deep mixing: \ref section_Internal_Tidal_Mixing. -There is also a "new" Henyey version, taking into account the effect of stratification on -TKE dissipation, - -\todo Harrison (personal communication) recommends that this option be made obsolete and -eventually removed. - -\f[ - \epsilon = \epsilon_0 \frac{f}{f_0} \frac{\mbox{acosh} (N/f)}{\mbox{acosh} (N_0 / f_0)} -\f] - -where \f$N_0\f$ and \f$f_0\f$ are the reference buoyancy frequency and inertial frequencies, respectively -and \f$\epsilon_0\f$ is the reference dissipation at \f$(N_0, f_0)\f$. In the previous version, \f$N = -N_0\f$. Additionally, the relationship between diapycnal diffusivities and stratification is included: - -\f[ - \kappa = \frac{\epsilon}{N^2} -\f] -This approach assumes that work done against gravity is uniformly distributed throughout the water column. -The original version concentrates buoyancy work in regions of strong stratification. - \subsection subsection_danabasoglu_back Danabasoglu background mixing The shape of the \cite danabasoglu2012 background mixing has a uniform background value, with a dip From d23c9264ea9ccb4782a592fd7a84860174b379a9 Mon Sep 17 00:00:00 2001 From: jiandewang Date: Mon, 29 Aug 2022 17:58:06 -0400 Subject: [PATCH 23/68] standarize indention in MOM_diabatic_driver.F90 and remove white space --- .../vertical/MOM_diabatic_driver.F90 | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/parameterizations/vertical/MOM_diabatic_driver.F90 b/src/parameterizations/vertical/MOM_diabatic_driver.F90 index 0cab88d21b..a85dbdd787 100644 --- a/src/parameterizations/vertical/MOM_diabatic_driver.F90 +++ b/src/parameterizations/vertical/MOM_diabatic_driver.F90 @@ -453,31 +453,33 @@ subroutine diabatic(u, v, h, tv, Hml, fluxes, visc, ADp, CDp, dt, Time_end, & ! perturb diabatic tendencies. ! These stochastic perturbations do not conserve heat, salt or mass. do k=1,nz; do j=js,je; do i=is,ie - h(i,j,k) = max(h_in(i,j,k) + (h(i,j,k)-h_in(i,j,k)) * stoch_CS%sppt_wts(i,j), GV%Angstrom_H) - tv%S(i,j,k) = max(s_in(i,j,k) + (tv%S(i,j,k)-s_in(i,j,k)) * stoch_CS%sppt_wts(i,j), 0.0) + h(i,j,k) = max(h_in(i,j,k) + (h(i,j,k)-h_in(i,j,k)) * stoch_CS%sppt_wts(i,j), GV%Angstrom_H) + tv%S(i,j,k) = max(s_in(i,j,k) + (tv%S(i,j,k)-s_in(i,j,k)) * stoch_CS%sppt_wts(i,j), 0.0) enddo; enddo; enddo ! now that we have updated thickness and salinity, calculate freeing point H_to_RL2_T2 = GV%H_to_RZ * GV%g_Earth do j=js,je - ps(:) = 0.0 - if (associated(fluxes%p_surf)) then ; do i=is,ie - ps(i) = fluxes%p_surf(i,j) - enddo ; endif - - do i=is,ie - pressure(i,1) = ps(i) + (0.5*H_to_RL2_T2)*h(i,j,1) - enddo - do k=2,nz ; do i=is,ie - pressure(i,k) = pressure(i,k-1) + & - (0.5*H_to_RL2_T2) * (h(i,j,k) + h(i,j,k-1)) - enddo ; enddo - do k=1,nz - call calculate_TFreeze(tv%S(is:ie,j,k), pressure(is:ie,k), T_freeze(is:ie), & - tv%eqn_of_state) - do i=is,ie - tv%T(i,j,k) = max(t_in(i,j,k) + (tv%T(i,j,k)-t_in(i,j,k)) * stoch_CS%sppt_wts(i,j), T_freeze(i)) - enddo - enddo + ps(:) = 0.0 + if (associated(fluxes%p_surf)) then + do i=is,ie + ps(i) = fluxes%p_surf(i,j) + enddo + endif + + do i=is,ie + pressure(i,1) = ps(i) + (0.5*H_to_RL2_T2)*h(i,j,1) + enddo + do k=2,nz ; do i=is,ie + pressure(i,k) = pressure(i,k-1) + & + (0.5*H_to_RL2_T2) * (h(i,j,k) + h(i,j,k-1)) + enddo ; enddo + do k=1,nz + call calculate_TFreeze(tv%S(is:ie,j,k), pressure(is:ie,k), T_freeze(is:ie), & + tv%eqn_of_state) + do i=is,ie + tv%T(i,j,k) = max(t_in(i,j,k) + (tv%T(i,j,k)-t_in(i,j,k)) * stoch_CS%sppt_wts(i,j), T_freeze(i)) + enddo + enddo enddo deallocate(h_in, t_in, s_in) From 0e4cda9a67ad4226ed78cdb7472ae87a6563e4a2 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Sat, 13 Aug 2022 17:15:21 -0400 Subject: [PATCH 24/68] +Add an interface filtering capability Added the new module MOM_interface_filter to allow for a biharmonic or other order of filtering of the interface heights. This new capability is enabled with the new runtime parameter APPLY_INTERFACE_FILTER, and with rates of filtering determined by the new runtime parameters INTERFACE_FILTER_TIME, INTERFACE_FILTER_MAX_CFL, INTERFACE_FILTER_ORDER and INTERFACE_FILTER_ISOTROPIC. Set APPLY_INTERFACE_FILTER to True and provide a positive timescale in INTERFACE_FILTER_TIME to enable the filtering. The comments describing THICKNESSDIFFUSE and THICKNESSDIFFUSE_FIRST were also updated to clarify the distinctions with the new capabilities or to clearly identify which parameters work together. By default, all answers are bitwise identical, but there are new entries or modified comments in the MOM_parameter_doc files. --- src/core/MOM.F90 | 91 +++- src/core/MOM_variables.F90 | 2 + .../lateral/MOM_interface_filter.F90 | 468 ++++++++++++++++++ 3 files changed, 534 insertions(+), 27 deletions(-) create mode 100644 src/parameterizations/lateral/MOM_interface_filter.F90 diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 7444597bbb..f6c714b3be 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -90,6 +90,8 @@ module MOM use MOM_hor_index, only : hor_index_type, hor_index_init use MOM_hor_index, only : rotate_hor_index use MOM_interface_heights, only : find_eta +use MOM_interface_filter, only : interface_filter, interface_filter_init, interface_filter_end +use MOM_interface_filter, only : interface_filter_CS use MOM_lateral_mixing_coeffs, only : calc_slope_functions, VarMix_init, VarMix_end use MOM_lateral_mixing_coeffs, only : calc_resoln_function, calc_depth_function, VarMix_CS use MOM_MEKE, only : MEKE_alloc_register_restart, step_forward_MEKE @@ -276,6 +278,8 @@ module MOM logical :: split !< If true, use the split time stepping scheme. logical :: use_RK2 !< If true, use RK2 instead of RK3 in unsplit mode !! (i.e., no split between barotropic and baroclinic). + logical :: interface_filter !< If true, apply an interface height filter immediately + !! after any calls to thickness_diffuse. logical :: thickness_diffuse !< If true, diffuse interface height w/ a diffusivity KHTH. logical :: thickness_diffuse_first !< If true, diffuse thickness before dynamics. logical :: mixedlayer_restrat !< If true, use submesoscale mixed layer restratifying scheme. @@ -363,6 +367,8 @@ module MOM type(thickness_diffuse_CS) :: thickness_diffuse_CSp !< Pointer to the control structure used for the isopycnal height diffusive transport. !! This is also common referred to as Gent-McWilliams diffusion + type(interface_filter_CS) :: interface_filter_CSp + !< Control structure used for the interface height smoothing operator. type(mixedlayer_restrat_CS) :: mixedlayer_restrat_CSp !< Pointer to the control structure used for the mixed layer restratification type(set_visc_CS) :: set_visc_CSp @@ -435,6 +441,7 @@ module MOM integer :: id_clock_adiabatic integer :: id_clock_continuity ! also in dynamics s/r integer :: id_clock_thick_diff +integer :: id_clock_int_filter integer :: id_clock_BBL_visc integer :: id_clock_ml_restrat integer :: id_clock_diagnostics @@ -1073,19 +1080,31 @@ subroutine step_MOM_dynamics(forces, p_surf_begin, p_surf_end, dt, dt_thermo, & endif call cpu_clock_end(id_clock_varT) - if ((CS%t_dyn_rel_adv == 0.0) .and. CS%thickness_diffuse .and. CS%thickness_diffuse_first) then + if ((CS%t_dyn_rel_adv == 0.0) .and. CS%thickness_diffuse_first .and. & + (CS%thickness_diffuse .or. CS%interface_filter)) then call enable_averages(dt_thermo, Time_local+real_to_time(US%T_to_s*(dt_thermo-dt)), CS%diag) - call cpu_clock_begin(id_clock_thick_diff) - if (CS%VarMix%use_variable_mixing) & - call calc_slope_functions(h, CS%tv, dt, G, GV, US, CS%VarMix, OBC=CS%OBC) - call thickness_diffuse(h, CS%uhtr, CS%vhtr, CS%tv, dt_thermo, G, GV, US, & - CS%MEKE, CS%VarMix, CS%CDp, CS%thickness_diffuse_CSp) - call cpu_clock_end(id_clock_thick_diff) - call pass_var(h, G%Domain, clock=id_clock_pass, halo=max(2,CS%cont_stencil)) - call disable_averaging(CS%diag) - if (showCallTree) call callTree_waypoint("finished thickness_diffuse_first (step_MOM)") + if (CS%thickness_diffuse) then + call cpu_clock_begin(id_clock_thick_diff) + if (CS%VarMix%use_variable_mixing) & + call calc_slope_functions(h, CS%tv, dt, G, GV, US, CS%VarMix, OBC=CS%OBC) + call thickness_diffuse(h, CS%uhtr, CS%vhtr, CS%tv, dt_thermo, G, GV, US, & + CS%MEKE, CS%VarMix, CS%CDp, CS%thickness_diffuse_CSp) + call cpu_clock_end(id_clock_thick_diff) + call pass_var(h, G%Domain, clock=id_clock_pass, halo=max(2,CS%cont_stencil)) + if (showCallTree) call callTree_waypoint("finished thickness_diffuse_first (step_MOM)") + endif + + if (CS%interface_filter) then + call cpu_clock_begin(id_clock_int_filter) + call interface_filter(h, CS%uhtr, CS%vhtr, CS%tv, dt_thermo, G, GV, US, & + CS%CDp, CS%interface_filter_CSp) + call cpu_clock_end(id_clock_int_filter) + call pass_var(h, G%Domain, clock=id_clock_pass, halo=max(2,CS%cont_stencil)) + if (showCallTree) call callTree_waypoint("finished interface_filter_first (step_MOM)") + endif + call disable_averaging(CS%diag) ! Whenever thickness changes let the diag manager know, target grids ! for vertical remapping may need to be regenerated. call diag_update_remap_grids(CS%diag) @@ -1182,20 +1201,32 @@ subroutine step_MOM_dynamics(forces, p_surf_begin, p_surf_end, dt, dt_thermo, & endif - if (CS%thickness_diffuse .and. .not.CS%thickness_diffuse_first) then - call cpu_clock_begin(id_clock_thick_diff) + if ((CS%thickness_diffuse .or. CS%interface_filter) .and. & + .not.CS%thickness_diffuse_first) then if (CS%debug) call hchksum(h,"Pre-thickness_diffuse h", G%HI, haloshift=0, scale=GV%H_to_m) - if (CS%VarMix%use_variable_mixing) & - call calc_slope_functions(h, CS%tv, dt, G, GV, US, CS%VarMix, OBC=CS%OBC) - call thickness_diffuse(h, CS%uhtr, CS%vhtr, CS%tv, dt, G, GV, US, & - CS%MEKE, CS%VarMix, CS%CDp, CS%thickness_diffuse_CSp) + if (CS%thickness_diffuse) then + call cpu_clock_begin(id_clock_thick_diff) + if (CS%VarMix%use_variable_mixing) & + call calc_slope_functions(h, CS%tv, dt, G, GV, US, CS%VarMix, OBC=CS%OBC) + call thickness_diffuse(h, CS%uhtr, CS%vhtr, CS%tv, dt, G, GV, US, & + CS%MEKE, CS%VarMix, CS%CDp, CS%thickness_diffuse_CSp) + + if (CS%debug) call hchksum(h,"Post-thickness_diffuse h", G%HI, haloshift=1, scale=GV%H_to_m) + call cpu_clock_end(id_clock_thick_diff) + call pass_var(h, G%Domain, clock=id_clock_pass, halo=max(2,CS%cont_stencil)) + if (showCallTree) call callTree_waypoint("finished thickness_diffuse (step_MOM)") + endif - if (CS%debug) call hchksum(h,"Post-thickness_diffuse h", G%HI, haloshift=1, scale=GV%H_to_m) - call cpu_clock_end(id_clock_thick_diff) - call pass_var(h, G%Domain, clock=id_clock_pass, halo=max(2,CS%cont_stencil)) - if (showCallTree) call callTree_waypoint("finished thickness_diffuse (step_MOM)") + if (CS%interface_filter) then + call cpu_clock_begin(id_clock_int_filter) + call interface_filter(h, CS%uhtr, CS%vhtr, CS%tv, dt_thermo, G, GV, US, & + CS%CDp, CS%interface_filter_CSp) + call cpu_clock_end(id_clock_int_filter) + call pass_var(h, G%Domain, clock=id_clock_pass, halo=max(2,CS%cont_stencil)) + if (showCallTree) call callTree_waypoint("finished interface_filter (step_MOM)") + endif endif ! apply the submesoscale mixed layer restratification parameterization @@ -1993,14 +2024,15 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & "The default is influenced by ENABLE_THERMODYNAMICS.", & default=use_temperature .and. .not.CS%use_ALE_algorithm) call get_param(param_file, "MOM", "THICKNESSDIFFUSE", CS%thickness_diffuse, & - "If true, interface heights are diffused with a "//& + "If true, isopycnal surfaces are diffused with a Laplacian "//& "coefficient of KHTH.", default=.false.) - call get_param(param_file, "MOM", "THICKNESSDIFFUSE_FIRST", & - CS%thickness_diffuse_first, & - "If true, do thickness diffusion before dynamics. "//& - "This is only used if THICKNESSDIFFUSE is true.", & - default=.false.) - if (.not.CS%thickness_diffuse) CS%thickness_diffuse_first = .false. + call get_param(param_file, "MOM", "APPLY_INTERFACE_FILTER", CS%interface_filter, & + "If true, model interface heights are subjected to a grid-scale "//& + "dependent spatial smoothing, often with biharmonic filter.", default=.false.) + call get_param(param_file, "MOM", "THICKNESSDIFFUSE_FIRST", CS%thickness_diffuse_first, & + "If true, do thickness diffusion or interface height smoothing before dynamics. "//& + "This is only used if THICKNESSDIFFUSE or APPLY_INTERFACE_FILTER is true.", & + default=.false., do_not_log=.not.(CS%thickness_diffuse.or.CS%interface_filter)) call get_param(param_file, "MOM", "USE_POROUS_BARRIER", CS%use_porbar, & "If true, use porous barrier to constrain the widths "//& "and face areas at the edges of the grid cells. ", & @@ -2825,6 +2857,8 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & call VarMix_init(Time, G, GV, US, param_file, diag, CS%VarMix) call set_visc_init(Time, G, GV, US, param_file, diag, CS%visc, CS%set_visc_CSp, restart_CSp, CS%OBC) call thickness_diffuse_init(Time, G, GV, US, param_file, diag, CS%CDp, CS%thickness_diffuse_CSp) + if (CS%interface_filter) & + call interface_filter_init(Time, G, GV, US, param_file, diag, CS%CDp, CS%interface_filter_CSp) new_sim = is_new_run(restart_CSp) call MOM_stoch_eos_init(G,Time,param_file,CS%stoch_eos_CS,restart_CSp,diag) @@ -3153,6 +3187,8 @@ subroutine MOM_timing_init(CS) id_clock_pass_init = cpu_clock_id('(Ocean init message passing *)', grain=CLOCK_ROUTINE) if (CS%thickness_diffuse) & id_clock_thick_diff = cpu_clock_id('(Ocean thickness diffusion *)', grain=CLOCK_MODULE) + if (CS%interface_filter) & + id_clock_int_filter = cpu_clock_id('(Ocean interface height filter *)', grain=CLOCK_MODULE) !if (CS%mixedlayer_restrat) & id_clock_ml_restrat = cpu_clock_id('(Ocean mixed layer restrat)', grain=CLOCK_MODULE) id_clock_diagnostics = cpu_clock_id('(Ocean collective diagnostics)', grain=CLOCK_MODULE) @@ -3819,6 +3855,7 @@ subroutine MOM_end(CS) endif call thickness_diffuse_end(CS%thickness_diffuse_CSp, CS%CDp) + if (CS%interface_filter) call interface_filter_end(CS%interface_filter_CSp, CS%CDp) call VarMix_end(CS%VarMix) call set_visc_end(CS%visc, CS%set_visc_CSp) call MEKE_end(CS%MEKE) diff --git a/src/core/MOM_variables.F90 b/src/core/MOM_variables.F90 index 6fc81fa976..8279afa954 100644 --- a/src/core/MOM_variables.F90 +++ b/src/core/MOM_variables.F90 @@ -208,6 +208,8 @@ module MOM_variables real, pointer, dimension(:,:,:) :: & uh => NULL(), & !< Resolved zonal layer thickness fluxes, [H L2 T-1 ~> m3 s-1 or kg s-1] vh => NULL(), & !< Resolved meridional layer thickness fluxes, [H L2 T-1 ~> m3 s-1 or kg s-1] + uh_smooth => NULL(), & !< Interface height smoothing induced zonal volume fluxes [H L2 T-1 ~> m3 s-1 or kg s-1] + vh_smooth => NULL(), & !< Interface height smoothing induced meridional volume fluxes [H L2 T-1 ~> m3 s-1 or kg s-1] uhGM => NULL(), & !< Isopycnal height diffusion induced zonal volume fluxes [H L2 T-1 ~> m3 s-1 or kg s-1] vhGM => NULL() !< Isopycnal height diffusion induced meridional volume fluxes [H L2 T-1 ~> m3 s-1 or kg s-1] diff --git a/src/parameterizations/lateral/MOM_interface_filter.F90 b/src/parameterizations/lateral/MOM_interface_filter.F90 new file mode 100644 index 0000000000..95440c8315 --- /dev/null +++ b/src/parameterizations/lateral/MOM_interface_filter.F90 @@ -0,0 +1,468 @@ +!> Interface height filtering module +module MOM_interface_filter + +! This file is part of MOM6. See LICENSE.md for the license. + +use MOM_debugging, only : hchksum, uvchksum +use MOM_diag_mediator, only : post_data, query_averaging_enabled, diag_ctrl +use MOM_diag_mediator, only : register_diag_field, safe_alloc_ptr, time_type +use MOM_domains, only : pass_var, CORNER, pass_vector +use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe +use MOM_file_parser, only : get_param, log_version, param_file_type +use MOM_grid, only : ocean_grid_type +use MOM_interface_heights, only : find_eta +use MOM_unit_scaling, only : unit_scale_type +use MOM_variables, only : thermo_var_ptrs, cont_diag_ptrs +use MOM_verticalGrid, only : verticalGrid_type + +implicit none ; private + +#include + +public interface_filter, interface_filter_init, interface_filter_end + +! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional +! consistency testing. These are noted in comments with units like Z, H, L, and T, along with +! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units +! vary with the Boussinesq approximation, the Boussinesq variant is given first. + +!> Control structure for interface height filtering +type, public :: interface_filter_CS ; private + logical :: initialized = .false. !< True if this control structure has been initialized. + real :: max_smoothing_CFL !< Maximum value of the smoothing CFL for interface height filtering [nondim] + real :: filter_rate !< The rate at which grid-scale anomalies are damped away [T-1 ~> s-1] + integer :: filter_order !< The even power of the interface height smoothing. + !! At present valid values are 0, 2, or 4. + logical :: interface_filter !< If true, interfaces heights are diffused. + logical :: isotropic_filter !< If true, use the same filtering lengthscales in both directions, + !! otherwise use filtering lengthscales in each direction that scale + !! with the grid spacing in that direction. + logical :: debug !< write verbose checksums for debugging purposes + + type(diag_ctrl), pointer :: diag => NULL() !< structure used to regulate timing of diagnostics + + !>@{ + !! Diagnostic identifier + integer :: id_uh_sm = -1, id_vh_sm = -1 + integer :: id_L2_u = -1, id_L2_v = -1 + integer :: id_sfn_x = -1, id_sfn_y = -1 + !>@} +end type interface_filter_CS + +contains + +!> Apply a transport that leads to a smoothing of interface height, subject to limits that +!! ensure stability and positive definiteness of layer thicknesses. +!! It also updates the along-layer mass fluxes used in the tracer transport equations. +subroutine interface_filter(h, uhtr, vhtr, tv, dt, G, GV, US, CDp, CS) + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Layer thickness [H ~> m or kg m-2] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: uhtr !< Accumulated zonal mass flux + !! [L2 H ~> m3 or kg] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: vhtr !< Accumulated meridional mass flux + !! [L2 H ~> m3 or kg] + type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure + real, intent(in) :: dt !< Time increment [T ~> s] + type(cont_diag_ptrs), intent(inout) :: CDp !< Diagnostics for the continuity equation + type(interface_filter_CS), intent(inout) :: CS !< Control structure for interface height + !! filtering + ! Local variables + real :: e(SZI_(G),SZJ_(G),SZK_(GV)+1) ! Heights of interfaces, relative to mean + ! sea level [Z ~> m], positive up. + real :: de_smooth(SZI_(G),SZJ_(G),SZK_(GV)+1) ! Change in the heights of interfaces after one pass + ! of Laplacian smoothing [Z ~> m], positive downward to avoid + ! having to change other signs in the call to interface_filter. + real :: uhD(SZIB_(G),SZJ_(G),SZK_(GV)) ! Smoothing u*h fluxes within a timestep [L2 H ~> m3 or kg] + real :: vhD(SZI_(G),SZJB_(G),SZK_(GV)) ! Smoothing v*h fluxes within a timestep [L2 H ~> m3 or kg] + + real, dimension(SZIB_(G),SZJ_(G)) :: & + KH_u ! Interface height squared smoothing lengths per timestep at u-points [L2 ~> m2] + real, dimension(SZI_(G),SZJB_(G)) :: & + KH_v ! Interface height squared smoothing lengths per timestep at v-points [L2 ~> m2] + + real :: diag_sfn_x(SZIB_(G),SZJ_(G),SZK_(GV)+1) ! Diagnostic of the x-face streamfunction + ! [H L2 T-1 ~> m3 s-1 or kg s-1] + real :: diag_sfn_y(SZI_(G),SZJB_(G),SZK_(GV)+1) ! Diagnostic of the y-face streamfunction + ! [H L2 T-1 ~> m3 s-1 or kg s-1] + real :: filter_strength ! The amount of filtering within a each iteration [nondim] + real :: Idt ! The inverse of the timestep [T-1 ~> s-1] + real :: h_neglect ! A thickness that is so small it is usually lost + ! in roundoff and can be neglected [H ~> m or kg m-2]. + integer :: itt, filter_itts ! The number of iterations of the filter, set as 1/2 the power. + integer :: i, j, k, is, ie, js, je, nz, hs + + if (.not. CS%initialized) call MOM_error(FATAL, "MOM_interface_filter: "//& + "Module must be initialized before it is used.") + + if ((.not.CS%interface_filter) .or. (CS%filter_rate <= 0.0) .or. (CS%filter_order < 2)) return + + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke + h_neglect = GV%H_subroundoff + + filter_itts = CS%filter_order / 2 + Idt = 1.0 / dt + + if (filter_itts > min(G%isc-G%isd, G%jsc-G%jsd)) call MOM_error(FATAL, & + "interface_filter: The halos are not wide enough to accommodate the filter "//& + "order specified by INTERFACE_FILTER_ORDER.") + + ! Calculates interface heights, e, in [Z ~> m]. + call find_eta(h, tv, G, GV, US, e, halo_size=filter_itts) + + ! Set the smoothing length scales to apply at each iteration. + if (filter_itts == 1) then + filter_strength = min(CS%filter_rate*dt, CS%max_smoothing_CFL) + elseif (filter_itts == 2) then + filter_strength = min(sqrt(CS%filter_rate*dt), CS%max_smoothing_CFL) + else + filter_strength = min((CS%filter_rate*dt)**(1.0/filter_itts), CS%max_smoothing_CFL) + endif + hs = filter_itts-1 + if (CS%isotropic_filter) then + !$OMP parallel do default(shared) + do j=js-hs,je+hs ; do I=is-(hs+1),ie+hs + Kh_u(I,j) = (0.25*filter_strength) / (G%IdxCu(I,j)**2 + G%IdyCu(I,j)**2) + enddo ; enddo + !$OMP parallel do default(shared) + do J=js-(hs+1),je+hs ; do i=is-hs,ie+hs + KH_v(i,J) = (0.25*filter_strength) / (G%IdxCv(i,J)**2 + G%IdyCv(i,J)**2) + enddo ; enddo + else + !$OMP parallel do default(shared) + do j=js-hs,je+hs ; do I=is-(hs+1),ie+hs + Kh_u(I,j) = (0.125*filter_strength) * (min(G%areaT(i,j), G%areaT(i+1,j)) * G%IdyCu(I,j))**2 + enddo ; enddo + !$OMP parallel do default(shared) + do J=js-(hs+1),je+hs ; do i=is-hs,ie+hs + Kh_v(i,J) = (0.125*filter_strength) * (min(G%areaT(i,j), G%areaT(i,j+1)) * G%IdxCv(i,J))**2 + enddo ; enddo + endif + + if (CS%debug) then + call uvchksum("Kh_[uv]", Kh_u, Kh_v, G%HI, haloshift=hs, & + scale=US%L_to_m**2, scalar_pair=.true.) + call hchksum(h, "interface_filter_1 h", G%HI, haloshift=hs+1, scale=GV%H_to_m) + call hchksum(e, "interface_filter_1 e", G%HI, haloshift=hs+1, scale=US%Z_to_m) + endif + + ! Calculate uhD, vhD from h, e, KH_u, KH_v + call filter_interface(h, e, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size=filter_itts-1) + + + do itt=2,filter_itts + hs = (filter_itts - itt) + 1 ! Set the halo size to work on. + !$OMP parallel do default(shared) + do j=js-hs,je+hs + do i=is-hs,ie+hs ; de_smooth(i,j,nz+1) = 0.0 ; enddo + do k=nz,1,-1 ; do i=is-hs,ie+hs + de_smooth(i,j,k) = de_smooth(i,j,k+1) + GV%H_to_Z * G%IareaT(i,j) * & + ((uhD(I,j,k) - uhD(I-1,j,k)) + (vhD(i,J,k) - vhD(i,J-1,k))) + enddo ; enddo + enddo + + ! Calculate uhD, vhD from h, de_smooth, KH_u, KH_v + call filter_interface(h, de_smooth, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size=filter_itts-itt) + enddo + + ! Offer diagnostic fields for averaging. This must occur before updating the layer thicknesses + ! so that the diagnostics can be remapped properly to other diagnostic vertical coordinates. + if (query_averaging_enabled(CS%diag)) then + if (CS%id_sfn_x > 0) then + diag_sfn_x(:,:,1) = 0.0 ; diag_sfn_x(:,:,nz+1) = 0.0 + do K=nz,2,-1 ; do j=js,je ; do I=is-1,ie + if (CS%id_sfn_x>0) diag_sfn_x(I,j,K) = diag_sfn_x(I,j,K+1) + uhD(I,j,k) + enddo ; enddo ; enddo + call post_data(CS%id_sfn_x, diag_sfn_x, CS%diag) + endif + if (CS%id_sfn_y > 0) then + diag_sfn_y(:,:,1) = 0.0 ; diag_sfn_y(:,:,nz+1) = 0.0 + do K=nz,2,-1 ; do J=js-1,je ; do i=is,ie + diag_sfn_y(i,J,K) = diag_sfn_y(i,J,K+1) + vhD(i,J,k) + enddo ; enddo ; enddo + call post_data(CS%id_sfn_y, diag_sfn_y, CS%diag) + endif + if (CS%id_uh_sm > 0) call post_data(CS%id_uh_sm, Idt*uhD(:,:,:), CS%diag) + if (CS%id_vh_sm > 0) call post_data(CS%id_vh_sm, Idt*vhD(:,:,:), CS%diag) + if (CS%id_L2_u > 0) call post_data(CS%id_L2_u, KH_u, CS%diag) + if (CS%id_L2_v > 0) call post_data(CS%id_L2_v, KH_v, CS%diag) + endif + + ! Update the layer thicknesses, and store the transports that will be needed for the tracers. + !$OMP parallel do default(shared) + do k=1,nz + do j=js,je ; do I=is-1,ie + uhtr(I,j,k) = uhtr(I,j,k) + uhD(I,j,k) + enddo ; enddo + do J=js-1,je ; do i=is,ie + vhtr(i,J,k) = vhtr(i,J,k) + vhD(i,J,k) + enddo ; enddo + do j=js,je ; do i=is,ie + h(i,j,k) = h(i,j,k) - G%IareaT(i,j) * & + ((uhD(I,j,k) - uhD(I-1,j,k)) + (vhD(i,J,k) - vhD(i,J-1,k))) + if (h(i,j,k) < GV%Angstrom_H) h(i,j,k) = GV%Angstrom_H + enddo ; enddo + + ! Store the transports associated with the smoothing if they are needed for diagnostics. + if (associated(CDp%uh_smooth)) then ; do j=js,je ; do I=is-1,ie + CDp%uh_smooth(I,j,k) = uhD(I,j,k)*Idt + enddo ; enddo ; endif + if (associated(CDp%vh_smooth)) then ; do J=js-1,je ; do i=is,ie + CDp%vh_smooth(i,J,k) = vhD(i,J,k)*Idt + enddo ; enddo ; endif + + enddo + + if (CS%debug) then + call uvchksum("interface_filter [uv]hD", uhD, vhD, & + G%HI, haloshift=0, scale=GV%H_to_m*US%L_to_m**2) + call uvchksum("interface_filter [uv]htr", uhtr, vhtr, & + G%HI, haloshift=0, scale=US%L_to_m**2*GV%H_to_m) + call hchksum(h, "interface_filter h", G%HI, haloshift=0, scale=GV%H_to_m) + endif + +end subroutine interface_filter + +!> Calculates parameterized layer transports for use in the continuity equation. +!! Fluxes are limited to give positive definite thicknesses. +!! Called by interface_filter(). +subroutine filter_interface(h, e, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size) + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: e !< Interface positions [Z ~> m] + real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: Kh_u !< Interface smoothing lengths squared + !! at u points [L2 ~> m2] + real, dimension(SZI_(G),SZJB_(G)), intent(in) :: Kh_v !< Interface smoothing lengths squared + !! at v points [L2 ~> m2] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(out) :: uhD !< Zonal mass fluxes + !! [H L2 ~> m3 or kg] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(out) :: vhD !< Meridional mass fluxes + !! [H L2 ~> m3 or kg] + integer, optional, intent(in) :: halo_size !< The size of the halo to work on, + !! 0 by default. + + ! Local variables + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & + h_avail ! The mass available for diffusion out of each face [H L2 ~> m3 or kg]. + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: & + h_avail_rsum ! The running sum of h_avail above an interface [H L2 ~> m3 or kg]. + real :: uhtot(SZIB_(G),SZJ_(G)) ! The vertical sum of uhD [H L2 ~> m3 or kg]. + real :: vhtot(SZI_(G),SZJB_(G)) ! The vertical sum of vhD [H L2 ~> m3 or kg]. + real :: Slope ! The slope of density surfaces, calculated in a way that is always + ! between -1 and 1 after undoing dimensional scaling, [Z L-1 ~> nondim] + real :: Sfn_est ! A preliminary estimate (before limiting) of the overturning + ! streamfunction [H L2 ~> m3 or kg]. + real :: Sfn ! The overturning streamfunction [H L2 ~> m3 or kg]. + real :: h_neglect ! A thickness that is so small it is usually lost + ! in roundoff and can be neglected [H ~> m or kg m-2]. + integer :: i, j, k, is, ie, js, je, nz, hs + + hs = 0 ; if (present(halo_size)) hs = halo_size + is = G%isc-hs ; ie = G%iec+hs ; js = G%jsc-hs ; je = G%jec+hs ; nz = GV%ke + + h_neglect = GV%H_subroundoff + + ! Find the maximum and minimum permitted streamfunction. + !$OMP parallel do default(shared) + do j=js-1,je+1 + do i=is-1,ie+1 + h_avail_rsum(i,j,1) = 0.0 + h_avail(i,j,1) = max(0.25*G%areaT(i,j)*(h(i,j,1)-GV%Angstrom_H),0.0) + h_avail_rsum(i,j,2) = h_avail(i,j,1) + enddo + do k=2,nz ; do i=is-1,ie+1 + h_avail(i,j,k) = max(0.25*G%areaT(i,j)*(h(i,j,k)-GV%Angstrom_H),0.0) + h_avail_rsum(i,j,k+1) = h_avail_rsum(i,j,k) + h_avail(i,j,k) + enddo ; enddo + enddo + + !$OMP parallel do default(shared) private(Slope,Sfn_est,Sfn) + do j=js,je + do I=is-1,ie ; uhtot(I,j) = 0.0 ; enddo + do K=nz,2,-1 + do I=is-1,ie + Slope = ((e(i,j,K)-e(i+1,j,K))*G%IdxCu(I,j)) * G%mask2dCu(I,j) + + Sfn_est = (KH_u(I,j)*G%dy_Cu(I,j)) * (GV%Z_to_H * Slope) + + ! Make sure that there is enough mass above to allow the streamfunction + ! to satisfy the boundary condition of 0 at the surface. + Sfn = min(max(Sfn_est, -h_avail_rsum(i,j,K)), h_avail_rsum(i+1,j,K)) + + ! The actual transport is limited by the mass available in the two + ! neighboring grid cells. + uhD(I,j,k) = max(min((Sfn - uhtot(I,j)), h_avail(i,j,k)), & + -h_avail(i+1,j,k)) + + ! sfn_x(I,j,K) = max(min(Sfn, uhtot(I,j)+h_avail(i,j,k)), & + ! uhtot(I,j)-h_avail(i+1,j,K)) + + uhtot(I,j) = uhtot(I,j) + uhD(I,j,k) + + enddo + enddo ! end of k-loop + + ! In layer 1, enforce the boundary conditions that Sfn(z=0) = 0.0 + do I=is-1,ie ; uhD(I,j,1) = -uhtot(I,j) ; enddo + enddo ! end of j-loop + + ! Calculate the meridional fluxes and gradients. + + !$OMP parallel do default(shared) private(Slope,Sfn_est,Sfn) + do J=js-1,je + do i=is,ie ; vhtot(i,J) = 0.0 ; enddo + do K=nz,2,-1 + do i=is,ie + Slope = ((e(i,j,K)-e(i,j+1,K))*G%IdyCv(i,J)) * G%mask2dCv(i,J) + + Sfn_est = (KH_v(i,J)*G%dx_Cv(i,J)) * (GV%Z_to_H * Slope) + + ! Make sure that there is enough mass above to allow the streamfunction + ! to satisfy the boundary condition of 0 at the surface. + Sfn = min(max(Sfn_est, -h_avail_rsum(i,j,K)), h_avail_rsum(i,j+1,K)) + + ! The actual transport is limited by the mass available in the two neighboring grid cells. + vhD(i,J,k) = max(min((Sfn - vhtot(i,J)), h_avail(i,j,k)), -h_avail(i,j+1,k)) + + ! sfn_y(i,J,K) = max(min(Sfn, vhtot(i,J)+h_avail(i,j,k)), & + ! vhtot(i,J)-h_avail(i,j+1,k)) + + vhtot(i,J) = vhtot(i,J) + vhD(i,J,k) + + enddo + enddo ! end of k-loop + ! In layer 1, enforce the boundary conditions that Sfn(z=0) = 0.0 + do i=is,ie ; vhD(i,J,1) = -vhtot(i,J) ; enddo + enddo ! end of j-loop + +end subroutine filter_interface + +!> Initialize the interface height filtering module/structure +subroutine interface_filter_init(Time, G, GV, US, param_file, diag, CDp, CS) + type(time_type), intent(in) :: Time !< Current model time + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + type(param_file_type), intent(in) :: param_file !< Parameter file handles + type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure + type(cont_diag_ptrs), intent(inout) :: CDp !< Continuity equation diagnostics + type(interface_filter_CS), intent(inout) :: CS !< Control structure for interface height filtering + + ! Local variables + character(len=40) :: mdl = "MOM_interface_filter" ! This module's name. + ! This include declares and sets the variable "version". +# include "version_variable.h" + real :: grid_sp ! The local grid spacing [L ~> m] + real :: interface_filter_time ! The grid-scale interface height filtering timescale [T ~> s] + integer :: i, j + + CS%initialized = .true. + CS%diag => diag + + ! Read all relevant parameters and write them to the model log. + call log_version(param_file, mdl, version, "") + call get_param(param_file, mdl, "INTERFACE_FILTER_TIME", interface_filter_time, & + "If positive, interface heights are subjected to a grid-scale "//& + "dependent biharmonic filter, using a rate based on this timescale.", & + default=0.0, units="s", scale=US%s_to_T) + CS%filter_rate = 0.0 + if (interface_filter_time > 0.0) CS%filter_rate = 1.0 / interface_filter_time + CS%interface_filter = (interface_filter_time > 0.0) + call get_param(param_file, mdl, "INTERFACE_FILTER_MAX_CFL", CS%max_smoothing_CFL, & + "The maximum value of the local CFL ratio that "//& + "is permitted for the interface height smoothing. 1.0 is the "//& + "marginally unstable value.", units="nondimensional", default=0.8) + if (CS%max_smoothing_CFL < 0.0) CS%max_smoothing_CFL = 0.0 + + call get_param(param_file, mdl, "INTERFACE_FILTER_ORDER", CS%filter_order, & + "The even power of the interface height smoothing. "//& + "At present valid values are 0, 2, 4 or 6.", default=4) + if (CS%filter_order == 0) then + CS%filter_rate = 0.0 + elseif ((CS%filter_order /= 2) .and. (CS%filter_order /= 4) .and. (CS%filter_order /= 6)) then + call MOM_error(FATAL, "Unsupported value of INTERFACE_FILTER_ORDER specified. "//& + "Only 0, 2, 4 or 6 are supported.") + endif + call get_param(param_file, mdl, "INTERFACE_FILTER_ISOTROPIC", CS%isotropic_filter, & + "If true, use the same filtering lengthscales in both directions; "//& + "otherwise use filtering lengthscales in each direction that scale "//& + "with the grid spacing in that direction.", default=.true.) + + call get_param(param_file, mdl, "DEBUG", CS%debug, & + "If true, write out verbose debugging data.", & + default=.false., debuggingParam=.true.) + + if (CS%filter_order > 0) then + CS%id_uh_sm = register_diag_field('ocean_model', 'uh_smooth', diag%axesCuL, Time, & + 'Interface Smoothing Zonal Thickness Flux', & + 'kg s-1', conversion=GV%H_to_kg_m2*US%L_to_m**2*US%s_to_T, & + y_cell_method='sum', v_extensive=.true.) + CS%id_vh_sm = register_diag_field('ocean_model', 'vh_smooth', diag%axesCvL, Time, & + 'Interface Smoothing Meridional Thickness Flux', & + 'kg s-1', conversion=GV%H_to_kg_m2*US%L_to_m**2*US%s_to_T, & + x_cell_method='sum', v_extensive=.true.) + + CS%id_L2_u = register_diag_field('ocean_model', 'Lsmooth2_u', diag%axesCu1, Time, & + 'Interface height smoothing length-scale squared at U-points', & + 'm2', conversion=US%L_to_m**2) + CS%id_L2_v = register_diag_field('ocean_model', 'Lsmooth2_u', diag%axesCv1, Time, & + 'Interface height smoothing length-scale squared at V-points', & + 'm2', conversion=US%L_to_m**2) + + CS%id_sfn_x = register_diag_field('ocean_model', 'Smooth_sfn_x', diag%axesCui, Time, & + 'Interface smoothing Zonal Overturning Streamfunction', & + 'm3 s-1', conversion=GV%H_to_m*US%L_to_m**2*US%s_to_T) + CS%id_sfn_y = register_diag_field('ocean_model', 'Smooth_sfn_y', diag%axesCvi, Time, & + 'Interface smoothing Meridional Overturning Streamfunction', & + 'm3 s-1', conversion=GV%H_to_m*US%L_to_m**2*US%s_to_T) + endif + +end subroutine interface_filter_init + +!> Deallocate the interface height filtering control structure +subroutine interface_filter_end(CS, CDp) + type(interface_filter_CS), intent(inout) :: CS !< Control structure for interface height filtering + type(cont_diag_ptrs), intent(inout) :: CDp !< Continuity diagnostic control structure + + ! NOTE: [uv]h_smooth are not yet used in diagnostics, but they are here for now for completeness. + if (associated(CDp%uh_smooth)) deallocate(CDp%uh_smooth) + if (associated(CDp%vh_smooth)) deallocate(CDp%vh_smooth) + +end subroutine interface_filter_end + +!> \namespace mom_interface_filter +!! +!! \section section_gm Interface height filtering +!! +!! Interface height filtering is implemented via along-layer mass fluxes +!! \f[ +!! h^\dagger \leftarrow h^n - \Delta t \nabla \cdot ( \vec{uh}^* ) +!! \f] +!! where the mass fluxes are cast as the difference in vector streamfunction +!! +!! \f[ +!! \vec{uh}^* = \delta_k \vec{\psi} . +!! \f] +!! +!! The streamfunction is proportional to the interface slope in the difference between +!! unsmoothed interface heights and those smoothed with one pass of a Laplacian filter. +!! \f[ +!! \vec{\psi} = - \kappa_h \frac{\nabla_z \rho}{\partial_z \rho} +!! = \frac{g\kappa_h}{\rho_o} \frac{\nabla \rho}{N^2} = \kappa_h \frac{M^2}{N^2} +!! \f] +!! +!! The result of the above expression is subsequently bounded by minimum and maximum values, including an upper +!! diffusivity consistent with numerical stability (\f$ \kappa_{cfl} \f$ is calculated internally). +!! +!! \subsection section_filter_module_parameters Module mom_interface_filter parameters +!! +!! | Symbol | Module parameter | +!! | ------ | --------------- | +!! | - | Interface_filter | +!! | - | Smoothing_MAX_CFL | +!! + +end module MOM_interface_filter From a490004b184a5eb59f1dcc123370321af6ea1094 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 15 Aug 2022 17:44:45 -0400 Subject: [PATCH 25/68] Corrected doxygen comments for interface filter Corrected the doxygen comments for the interface filtering module, to avoid a section-name conflict with the GM module, and renamed some internal variables for greater clarity when reading the code. All answers are bitwise identical. --- .../lateral/MOM_interface_filter.F90 | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/parameterizations/lateral/MOM_interface_filter.F90 b/src/parameterizations/lateral/MOM_interface_filter.F90 index 95440c8315..feed4bd930 100644 --- a/src/parameterizations/lateral/MOM_interface_filter.F90 +++ b/src/parameterizations/lateral/MOM_interface_filter.F90 @@ -78,9 +78,9 @@ subroutine interface_filter(h, uhtr, vhtr, tv, dt, G, GV, US, CDp, CS) real :: vhD(SZI_(G),SZJB_(G),SZK_(GV)) ! Smoothing v*h fluxes within a timestep [L2 H ~> m3 or kg] real, dimension(SZIB_(G),SZJ_(G)) :: & - KH_u ! Interface height squared smoothing lengths per timestep at u-points [L2 ~> m2] + Lsm2_u ! Interface height squared smoothing lengths per timestep at u-points [L2 ~> m2] real, dimension(SZI_(G),SZJB_(G)) :: & - KH_v ! Interface height squared smoothing lengths per timestep at v-points [L2 ~> m2] + Lsm2_v ! Interface height squared smoothing lengths per timestep at v-points [L2 ~> m2] real :: diag_sfn_x(SZIB_(G),SZJ_(G),SZK_(GV)+1) ! Diagnostic of the x-face streamfunction ! [H L2 T-1 ~> m3 s-1 or kg s-1] @@ -123,32 +123,32 @@ subroutine interface_filter(h, uhtr, vhtr, tv, dt, G, GV, US, CDp, CS) if (CS%isotropic_filter) then !$OMP parallel do default(shared) do j=js-hs,je+hs ; do I=is-(hs+1),ie+hs - Kh_u(I,j) = (0.25*filter_strength) / (G%IdxCu(I,j)**2 + G%IdyCu(I,j)**2) + Lsm2_u(I,j) = (0.25*filter_strength) / (G%IdxCu(I,j)**2 + G%IdyCu(I,j)**2) enddo ; enddo !$OMP parallel do default(shared) do J=js-(hs+1),je+hs ; do i=is-hs,ie+hs - KH_v(i,J) = (0.25*filter_strength) / (G%IdxCv(i,J)**2 + G%IdyCv(i,J)**2) + Lsm2_v(i,J) = (0.25*filter_strength) / (G%IdxCv(i,J)**2 + G%IdyCv(i,J)**2) enddo ; enddo else !$OMP parallel do default(shared) do j=js-hs,je+hs ; do I=is-(hs+1),ie+hs - Kh_u(I,j) = (0.125*filter_strength) * (min(G%areaT(i,j), G%areaT(i+1,j)) * G%IdyCu(I,j))**2 + Lsm2_u(I,j) = (0.125*filter_strength) * (min(G%areaT(i,j), G%areaT(i+1,j)) * G%IdyCu(I,j))**2 enddo ; enddo !$OMP parallel do default(shared) do J=js-(hs+1),je+hs ; do i=is-hs,ie+hs - Kh_v(i,J) = (0.125*filter_strength) * (min(G%areaT(i,j), G%areaT(i,j+1)) * G%IdxCv(i,J))**2 + Lsm2_v(i,J) = (0.125*filter_strength) * (min(G%areaT(i,j), G%areaT(i,j+1)) * G%IdxCv(i,J))**2 enddo ; enddo endif if (CS%debug) then - call uvchksum("Kh_[uv]", Kh_u, Kh_v, G%HI, haloshift=hs, & + call uvchksum("Kh_[uv]", Lsm2_u, Lsm2_v, G%HI, haloshift=hs, & scale=US%L_to_m**2, scalar_pair=.true.) call hchksum(h, "interface_filter_1 h", G%HI, haloshift=hs+1, scale=GV%H_to_m) call hchksum(e, "interface_filter_1 e", G%HI, haloshift=hs+1, scale=US%Z_to_m) endif - ! Calculate uhD, vhD from h, e, KH_u, KH_v - call filter_interface(h, e, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size=filter_itts-1) + ! Calculate uhD, vhD from h, e, Lsm2_u, Lsm2_v + call filter_interface(h, e, Lsm2_u, Lsm2_v, uhD, vhD, G, GV, US, halo_size=filter_itts-1) do itt=2,filter_itts @@ -162,8 +162,8 @@ subroutine interface_filter(h, uhtr, vhtr, tv, dt, G, GV, US, CDp, CS) enddo ; enddo enddo - ! Calculate uhD, vhD from h, de_smooth, KH_u, KH_v - call filter_interface(h, de_smooth, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size=filter_itts-itt) + ! Calculate uhD, vhD from h, de_smooth, Lsm2_u, Lsm2_v + call filter_interface(h, de_smooth, Lsm2_u, Lsm2_v, uhD, vhD, G, GV, US, halo_size=filter_itts-itt) enddo ! Offer diagnostic fields for averaging. This must occur before updating the layer thicknesses @@ -185,8 +185,8 @@ subroutine interface_filter(h, uhtr, vhtr, tv, dt, G, GV, US, CDp, CS) endif if (CS%id_uh_sm > 0) call post_data(CS%id_uh_sm, Idt*uhD(:,:,:), CS%diag) if (CS%id_vh_sm > 0) call post_data(CS%id_vh_sm, Idt*vhD(:,:,:), CS%diag) - if (CS%id_L2_u > 0) call post_data(CS%id_L2_u, KH_u, CS%diag) - if (CS%id_L2_v > 0) call post_data(CS%id_L2_v, KH_v, CS%diag) + if (CS%id_L2_u > 0) call post_data(CS%id_L2_u, Lsm2_u, CS%diag) + if (CS%id_L2_v > 0) call post_data(CS%id_L2_v, Lsm2_v, CS%diag) endif ! Update the layer thicknesses, and store the transports that will be needed for the tracers. @@ -227,22 +227,22 @@ end subroutine interface_filter !> Calculates parameterized layer transports for use in the continuity equation. !! Fluxes are limited to give positive definite thicknesses. !! Called by interface_filter(). -subroutine filter_interface(h, e, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size) +subroutine filter_interface(h, e, Lsm2_u, Lsm2_v, uhD, vhD, G, GV, US, halo_size) type(ocean_grid_type), intent(in) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: e !< Interface positions [Z ~> m] - real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: Kh_u !< Interface smoothing lengths squared + real, dimension(SZIB_(G),SZJ_(G)), intent(in) :: Lsm2_u !< Interface smoothing lengths squared !! at u points [L2 ~> m2] - real, dimension(SZI_(G),SZJB_(G)), intent(in) :: Kh_v !< Interface smoothing lengths squared + real, dimension(SZI_(G),SZJB_(G)), intent(in) :: Lsm2_v !< Interface smoothing lengths squared !! at v points [L2 ~> m2] real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(out) :: uhD !< Zonal mass fluxes !! [H L2 ~> m3 or kg] real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(out) :: vhD !< Meridional mass fluxes !! [H L2 ~> m3 or kg] integer, optional, intent(in) :: halo_size !< The size of the halo to work on, - !! 0 by default. + !! 0 by default. ! Local variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & @@ -286,7 +286,7 @@ subroutine filter_interface(h, e, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size) do I=is-1,ie Slope = ((e(i,j,K)-e(i+1,j,K))*G%IdxCu(I,j)) * G%mask2dCu(I,j) - Sfn_est = (KH_u(I,j)*G%dy_Cu(I,j)) * (GV%Z_to_H * Slope) + Sfn_est = (Lsm2_u(I,j)*G%dy_Cu(I,j)) * (GV%Z_to_H * Slope) ! Make sure that there is enough mass above to allow the streamfunction ! to satisfy the boundary condition of 0 at the surface. @@ -318,7 +318,7 @@ subroutine filter_interface(h, e, Kh_u, Kh_v, uhD, vhD, G, GV, US, halo_size) do i=is,ie Slope = ((e(i,j,K)-e(i,j+1,K))*G%IdyCv(i,J)) * G%mask2dCv(i,J) - Sfn_est = (KH_v(i,J)*G%dx_Cv(i,J)) * (GV%Z_to_H * Slope) + Sfn_est = (Lsm2_v(i,J)*G%dx_Cv(i,J)) * (GV%Z_to_H * Slope) ! Make sure that there is enough mass above to allow the streamfunction ! to satisfy the boundary condition of 0 at the surface. @@ -435,7 +435,7 @@ end subroutine interface_filter_end !> \namespace mom_interface_filter !! -!! \section section_gm Interface height filtering +!! \section section_interface_filter Interface height filtering !! !! Interface height filtering is implemented via along-layer mass fluxes !! \f[ @@ -447,22 +447,25 @@ end subroutine interface_filter_end !! \vec{uh}^* = \delta_k \vec{\psi} . !! \f] !! -!! The streamfunction is proportional to the interface slope in the difference between -!! unsmoothed interface heights and those smoothed with one pass of a Laplacian filter. +!! The streamfunction is proportional to the slope in the difference between +!! unsmoothed interface heights and those smoothed with one (or more) passes of a Laplacian +!! filter, depending on the order of the filter, or to the slope for a Laplacian +!! filter !! \f[ -!! \vec{\psi} = - \kappa_h \frac{\nabla_z \rho}{\partial_z \rho} -!! = \frac{g\kappa_h}{\rho_o} \frac{\nabla \rho}{N^2} = \kappa_h \frac{M^2}{N^2} +!! \vec{\psi} = - \kappa_h {\nabla \eta - \eta_smooth} !! \f] !! -!! The result of the above expression is subsequently bounded by minimum and maximum values, including an upper -!! diffusivity consistent with numerical stability (\f$ \kappa_{cfl} \f$ is calculated internally). +!! The result of the above expression is subsequently bounded by minimum and maximum values, including a +!! maximum smoothing rate for numerical stability (\f$ \kappa_{h} \f$ is calculated internally). !! !! \subsection section_filter_module_parameters Module mom_interface_filter parameters !! !! | Symbol | Module parameter | !! | ------ | --------------- | -!! | - | Interface_filter | -!! | - | Smoothing_MAX_CFL | +!! | - | APPLY_INTERFACE_FILTER | +!! | - | INTERFACE_FILTER_TIME | +!! | - | INTERFACE_FILTER_MAX_CFL | +!! | - | INTERFACE_FILTER_ORDER | !! end module MOM_interface_filter From ba9e7f17c35ac61e4441d860c5cb801fff9d7652 Mon Sep 17 00:00:00 2001 From: Marshall Ward Date: Wed, 17 Aug 2022 11:18:12 -0400 Subject: [PATCH 26/68] Disable sigsetjmp for default compilation The sigsetjmp function is part of the POSIX, but is not required to be defined as a symbol, and may be implemented as a macro. Since Fortran C bindings require a symbol, we cannot bind to macro implementations. The prior implementation assumed a Linux glibc binding of __sigsetjmp (accessed by a `sigsetjmp` macro), but this did not work on BSD and MacOS builds, which have a dedicated `sigsetjmp` symbol. Although the autoconf build included a macro to test and assign the symbol to `SIGSETJMP_NAME`, this did not resolve builds based on mkmf or similar build systems, and would fail to compile. To resolve this, the SIGSETJMP_NAME points to a placeholder function, `sigsetjmp_missing` which permits compilation but raises an error if called. Since this function is only used in our unit testing, and even then only for tests which would otherwise raise FATAL, this change will not disrupt any simulations. However, it does mean that only "power" users who build with either autoconf or `-DSIGSETJMP_NAME=\"...\"` will be able to run the unit tests. In practice, it should be sufficient to direct users to the autoconf builds, and no actual disruptions are expected. --- ac/configure.ac | 31 +++++++++++++++++++------------ src/framework/posix.F90 | 18 ++++++++++++++++++ src/framework/posix.h | 2 +- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/ac/configure.ac b/ac/configure.ac index dc4962307e..8d74d71fbd 100644 --- a/ac/configure.ac +++ b/ac/configure.ac @@ -234,21 +234,28 @@ AC_SUBST([SRC_DIRS], AC_CONFIG_COMMANDS(Makefile.dep, [make depend]) -# setjmp verification +# POSIX verification tests AC_LANG_PUSH([C]) -# Verify that either sigsetjmp (POSIX) or __sigsetjmp (glibc) are available. -AC_CHECK_FUNC([sigsetjmp]) -AS_IF([test "$ac_cv_func_sigsetjmp" == "yes"], [ - SIGSETJMP_NAME="sigsetjmp" -], [ - AC_CHECK_FUNC([__sigsetjmp], [ - SIGSETJMP_NAME="__sigsetjmp" - ], [ - AC_MSG_ERROR([Could not find a symbol for sigsetjmp.]) +# These symbols may be defined as macros, making them inaccessible by Fortran. +# The following exist in BSD and Linux, so we just test for them. +AC_CHECK_FUNC([setjmp], [], [AC_MSG_ERROR([Could not find setjmp.])]) +AC_CHECK_FUNC([longjmp], [], [AC_MSG_ERROR([Could not find longjmp.])]) +AC_CHECK_FUNC([siglongjmp], [], [AC_MSG_ERROR([Could not find siglongjmp.])]) + +# Determine the sigsetjmp symbol. If missing, then point to sigsetjmp_missing. +# +# Supported symbols: +# sigsetjmp POSIX, BSD libc (MacOS) +# __sigsetjmp glibc (Linux) +SIGSETJMP="sigsetjmp_missing" +for sigsetjmp_fn in sigsetjmp __sigsetjmp; do + AC_CHECK_FUNC([${sigsetjmp_fn}], [ + SIGSETJMP=${sigsetjmp_fn} + break ]) -]) -AC_DEFINE_UNQUOTED([SIGSETJMP_NAME], ["$SIGSETJMP_NAME"]) +done +AC_DEFINE_UNQUOTED([SIGSETJMP_NAME], ["${SIGSETJMP}"]) # Determine the size of jmp_buf and sigjmp_buf AC_CHECK_SIZEOF([jmp_buf], [], [#include ]) diff --git a/src/framework/posix.F90 b/src/framework/posix.F90 index 522024071e..142d7634e2 100644 --- a/src/framework/posix.F90 +++ b/src/framework/posix.F90 @@ -344,4 +344,22 @@ subroutine siglongjmp(env, val) call siglongjmp_posix(env, val_c) end subroutine siglongjmp +!> Placeholder function for a missing or unconfigured sigsetjmp +!! +!! The symbol for sigsetjmp can be platform-dependent and may not exist if +!! defined as a macro. This function allows compilation, and reports a runtime +!! error if used in the program. +function sigsetjmp_missing(env, savesigs) result(rc) bind(c) + type(sigjmp_buf), intent(in) :: env + !< Current process state (unused) + integer(kind=c_int), value, intent(in) :: savesigs + !< Enable signal state flag (unused) + integer(kind=c_int) :: rc + !< Function return code (unused) + + print '(a)', 'ERROR: sigsetjmp() is not implemented in this build.' + print '(a)', 'Recompile with autoconf or -DSIGSETJMP_NAME=\"\".' + error stop +end function sigsetjmp_missing + end module posix diff --git a/src/framework/posix.h b/src/framework/posix.h index d60a868a91..96dec57814 100644 --- a/src/framework/posix.h +++ b/src/framework/posix.h @@ -14,7 +14,7 @@ ! glibc defines sigsetjmp as __sigsetjmp via macro readable from . #ifndef SIGSETJMP_NAME -#define SIGSETJMP_NAME "__sigsetjmp" +#define SIGSETJMP_NAME "sigsetjmp_missing" #endif ! This should be defined by /usr/include/signal.h From 00044b2f328a9a0c037ed64303c13fd1aa61d40e Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Thu, 1 Sep 2022 10:57:55 -0400 Subject: [PATCH 27/68] (*)+Rename KDML to KD_ML_TOT and restrict its use Added a new argument to set_diffusivity_init and bkgnd_mixing_init to specify when a physically based ocean surface boundary layer mixing scheme, like KPP, ePBL or a bulk mixed layer is in use, and use this to restrict when a constant diffusivity is used as a crude parameterization of the surface boundary layer. This constant mixed layer diffusivity was formerly set with KDML, but is now set with KD_ML_TOT, but KDML will still work for input but is no longer logged and triggers a warning message if it is used. Also removed KDML from the description of the ADIABATIC option. This leads to changes in the entries of several MOM_parameter_doc files, and it could lead to answer changes that set a non-default value of KDML and use ePBL or KPP, but it seems unlikely that any such cases would exist. --- src/core/MOM.F90 | 7 +- .../vertical/MOM_bkgnd_mixing.F90 | 77 ++++++++++++------- .../vertical/MOM_diabatic_driver.F90 | 5 +- .../vertical/MOM_set_diffusivity.F90 | 11 ++- .../vertical/MOM_set_viscosity.F90 | 7 +- 5 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index f6c714b3be..267a162b00 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -1982,10 +1982,9 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & default=.false.) CS%tv%T_is_conT = use_conT_absS ; CS%tv%S_is_absS = use_conT_absS call get_param(param_file, "MOM", "ADIABATIC", CS%adiabatic, & - "There are no diapycnal mass fluxes if ADIABATIC is "//& - "true. This assumes that KD = KDML = 0.0 and that "//& - "there is no buoyancy forcing, but makes the model "//& - "faster by eliminating subroutine calls.", default=.false.) + "There are no diapycnal mass fluxes if ADIABATIC is true. "//& + "This assumes that KD = 0.0 and that there is no buoyancy forcing, "//& + "but makes the model faster by eliminating subroutine calls.", default=.false.) call get_param(param_file, "MOM", "DO_DYNAMICS", CS%do_dynamics, & "If False, skips the dynamics calls that update u & v, as well as "//& "the gravity wave adjustment to h. This may be a fragile feature, "//& diff --git a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 index 7c427ab79a..9eabd8dc4d 100644 --- a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 +++ b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 @@ -9,7 +9,7 @@ module MOM_bkgnd_mixing use MOM_diag_mediator, only : diag_ctrl, time_type, register_diag_field use MOM_diag_mediator, only : post_data use MOM_error_handler, only : MOM_error, FATAL, WARNING, NOTE -use MOM_file_parser, only : get_param, log_version, param_file_type +use MOM_file_parser, only : get_param, log_param, log_version, param_file_type use MOM_file_parser, only : openParameterBlock, closeParameterBlock use MOM_forcing_type, only : forcing use MOM_grid, only : ocean_grid_type @@ -63,9 +63,11 @@ module MOM_bkgnd_mixing real :: Kd_tanh_lat_scale !< A nondimensional scaling for the range of !! diffusivities with Kd_tanh_lat_fn. Valid values !! are in the range of -2 to 2; 0.4 reproduces CM2M. - real :: Kdml !< mixed layer diapycnal diffusivity [Z2 T-1 ~> m2 s-1] - !! when bulkmixedlayer==.false. - real :: Hmix !< mixed layer thickness [Z ~> m] when bulkmixedlayer==.false. + real :: Kd_tot_ml !< The mixed layer diapycnal diffusivity [Z2 T-1 ~> m2 s-1] + !! when no other physically based mixed layer turbulence + !! parameterization is being used. + real :: Hmix !< mixed layer thickness [Z ~> m] when no physically based + !! ocean surface boundary layer parameterization is used. logical :: Kd_tanh_lat_fn !< If true, use the tanh dependence of Kd_sfc on !! latitude, like GFDL CM2.1/CM2M. There is no !! physical justification for this form, and it can @@ -92,7 +94,8 @@ module MOM_bkgnd_mixing !! where kd is the diapycnal diffusivity. This approach assumes that work done !! against gravity is uniformly distributed throughout the column. Whereas, kd=kd_0*e, !! as in the original version, concentrates buoyancy work in regions of strong stratification. - logical :: bulkmixedlayer !< If true, a refined bulk mixed layer scheme is used + logical :: physical_OBL_scheme !< If true, a physically-based scheme is used to determine mixing in the + !! ocean's surface boundary layer, such as ePBL, KPP, or a refined bulk mixed layer scheme. logical :: Kd_via_Kdml_bug !< If true and KDML /= KD and a number of other higher precedence !! options are not used, the background diffusivity is set incorrectly using a !! bug that was introduced in March, 2018. @@ -109,7 +112,7 @@ module MOM_bkgnd_mixing contains !> Initialize the background mixing routine. -subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS) +subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS, physical_OBL_scheme) type(time_type), intent(in) :: Time !< The current time. type(ocean_grid_type), intent(in) :: G !< Grid structure. @@ -117,15 +120,20 @@ subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS) type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type type(param_file_type), intent(in) :: param_file !< Run-time parameter file handle type(diag_ctrl), target, intent(inout) :: diag !< Diagnostics control structure. - type(bkgnd_mixing_cs), pointer :: CS !< This module's control structure. + type(bkgnd_mixing_cs), pointer :: CS !< This module's control structure. + logical, intent(in) :: physical_OBL_scheme !< If true, a physically based + !! parameterization (like KPP or ePBL or a bulk mixed + !! layer) is used outside of set_diffusivity to + !! specify the mixing that occurs in the ocean's + !! surface boundary layer. ! Local variables real :: Kv ! The interior vertical viscosity [Z2 T-1 ~> m2 s-1] - read to set Prandtl ! number unless it is provided as a parameter real :: prandtl_bkgnd_comp ! Kv/CS%Kd. Gets compared with user-specified prandtl_bkgnd. -! This include declares and sets the variable "version". -#include "version_variable.h" + ! This include declares and sets the variable "version". +# include "version_variable.h" if (associated(CS)) then call MOM_error(WARNING, "bkgnd_mixing_init called with an associated "// & @@ -154,21 +162,38 @@ subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS) ! The following is needed to set one of the choices of vertical background mixing - ! BULKMIXEDLAYER is not always defined (e.g., CM2G63L), so the following line by passes - ! the need to include BULKMIXEDLAYER in MOM_input - CS%bulkmixedlayer = (GV%nkml > 0) - if (CS%bulkmixedlayer) then + CS%physical_OBL_scheme = physical_OBL_scheme + if (CS%physical_OBL_scheme) then ! Check that Kdml is not set when using bulk mixed layer - call get_param(param_file, mdl, "KDML", CS%Kdml, default=-1.) - if (CS%Kdml>0.) call MOM_error(FATAL, & - "bkgnd_mixing_init: KDML cannot be set when using bulk mixed layer.") - CS%Kdml = CS%Kd ! This is not used with a bulk mixed layer, but also cannot be a NaN. + call get_param(param_file, mdl, "KDML", CS%Kd_tot_ml, default=-1., do_not_log=.true.) + if (CS%Kd_tot_ml>0.) call MOM_error(FATAL, & + "bkgnd_mixing_init: KDML is a depricated parameter that should not be used.") + call get_param(param_file, mdl, "KD_ML_TOT", CS%Kd_tot_ml, default=-1., do_not_log=.true.) + if (CS%Kd_tot_ml>0.) call MOM_error(FATAL, & + "bkgnd_mixing_init: KD_ML_TOT cannot be set when using a physically based ocean "//& + "boundary layer mixing parameterization.") + CS%Kd_tot_ml = CS%Kd ! This is not used with a bulk mixed layer, but also cannot be a NaN. else - call get_param(param_file, mdl, "KDML", CS%Kdml, & + call get_param(param_file, mdl, "KD_ML_TOT", CS%Kd_tot_ml, & + "The total diapcynal diffusivity in the surface mixed layer when there is "//& + "not a physically based parameterization of mixing in the mixed layer, such "//& + "as bulk mixed layer or KPP or ePBL.", & + units="m2 s-1", default=CS%Kd*US%Z2_T_to_m2_s, scale=US%m2_s_to_Z2_T, do_not_log=.true.) + if (abs(CS%Kd_tot_ml - CS%Kd) <= 1.0e-15*abs(CS%Kd)) then + call get_param(param_file, mdl, "KDML", CS%Kd_tot_ml, & "If BULKMIXEDLAYER is false, KDML is the elevated "//& "diapycnal diffusivity in the topmost HMIX of fluid. "//& "KDML is only used if BULKMIXEDLAYER is false.", & - units="m2 s-1", default=CS%Kd*US%Z2_T_to_m2_s, scale=US%m2_s_to_Z2_T) + units="m2 s-1", default=CS%Kd*US%Z2_T_to_m2_s, scale=US%m2_s_to_Z2_T, do_not_log=.true.) + if (abs(CS%Kd_tot_ml - CS%Kd) > 1.0e-15*abs(CS%Kd)) & + call MOM_error(WARNING, "KDML is a depricated parameter. Use KD_ML_TOT instead.") + endif + call log_param(param_file, mdl, "KD_ML_TOT", CS%Kd_tot_ml*US%Z2_T_to_m2_s, & + "The total diapcynal diffusivity in the surface mixed layer when there is "//& + "not a physically based parameterization of mixing in the mixed layer, such "//& + "as bulk mixed layer or KPP or ePBL.", & + units="m2 s-1", default=CS%Kd*US%Z2_T_to_m2_s) + call get_param(param_file, mdl, "HMIX_FIXED", CS%Hmix, & "The prescribed depth over which the near-surface "//& "viscosity and diffusivity are elevated when the bulk "//& @@ -290,13 +315,13 @@ subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS) "MOM_bkgnd_mixing: KD_TANH_LAT_FN can not be used with HENYEY_IGW_BACKGROUND.") CS%Kd_via_Kdml_bug = .false. - if ((CS%Kd /= CS%Kdml) .and. .not.(CS%Kd_tanh_lat_fn .or. CS%bulkmixedlayer .or. & + if ((CS%Kd /= CS%Kd_tot_ml) .and. .not.(CS%Kd_tanh_lat_fn .or. CS%physical_OBL_scheme .or. & CS%Henyey_IGW_background .or. CS%Henyey_IGW_background_new .or. & CS%horiz_varying_background .or. CS%Bryan_Lewis_diffusivity)) then call get_param(param_file, mdl, "KD_BACKGROUND_VIA_KDML_BUG", CS%Kd_via_Kdml_bug, & "If true and KDML /= KD and several other conditions apply, the background "//& "diffusivity is set incorrectly using a bug that was introduced in March, 2018.", & - default=.true.) ! The default should be changed to false and this parameter obsoleted. + default=.false.) ! The default should be changed to false and this parameter obsoleted. endif ! call closeParameterBlock(param_file) @@ -471,7 +496,7 @@ subroutine calculate_bkgnd_mixing(h, tv, N2_lay, Kd_lay, Kd_int, Kv_bkgnd, j, G, endif ! Now set background diffusivies based on these surface values, possibly with vertical structure. - if ((.not.CS%bulkmixedlayer) .and. (CS%Kd /= CS%Kdml)) then + if ((.not.CS%physical_OBL_scheme) .and. (CS%Kd /= CS%Kd_tot_ml)) then ! This is a crude way to put in a diffusive boundary layer without an explicit boundary ! layer turbulence scheme. It should not be used for any realistic ocean models. I_Hmix = 1.0 / (CS%Hmix + GV%H_subroundoff*GV%H_to_Z) @@ -481,16 +506,16 @@ subroutine calculate_bkgnd_mixing(h, tv, N2_lay, Kd_lay, Kd_int, Kv_bkgnd, j, G, if (CS%Kd_via_Kdml_bug) then ! These two lines should update Kd_lay, not Kd_int. They were correctly working on the ! same variables until MOM6 commit 7a818716 (PR#750), which was added on March 26, 2018. - if (depth_c <= CS%Hmix) then ; Kd_int(i,K) = CS%Kdml + if (depth_c <= CS%Hmix) then ; Kd_int(i,K) = CS%Kd_tot_ml elseif (depth_c >= 2.0*CS%Hmix) then ; Kd_int(i,K) = Kd_sfc(i) else - Kd_lay(i,k) = ((Kd_sfc(i) - CS%Kdml) * I_Hmix) * depth_c + (2.0*CS%Kdml - Kd_sfc(i)) + Kd_lay(i,k) = ((Kd_sfc(i) - CS%Kd_tot_ml) * I_Hmix) * depth_c + (2.0*CS%Kd_tot_ml - Kd_sfc(i)) endif else - if (depth_c <= CS%Hmix) then ; Kd_lay(i,k) = CS%Kdml + if (depth_c <= CS%Hmix) then ; Kd_lay(i,k) = CS%Kd_tot_ml elseif (depth_c >= 2.0*CS%Hmix) then ; Kd_lay(i,k) = Kd_sfc(i) else - Kd_lay(i,k) = ((Kd_sfc(i) - CS%Kdml) * I_Hmix) * depth_c + (2.0*CS%Kdml - Kd_sfc(i)) + Kd_lay(i,k) = ((Kd_sfc(i) - CS%Kd_tot_ml) * I_Hmix) * depth_c + (2.0*CS%Kd_tot_ml - Kd_sfc(i)) endif endif diff --git a/src/parameterizations/vertical/MOM_diabatic_driver.F90 b/src/parameterizations/vertical/MOM_diabatic_driver.F90 index 278fb1ddda..71e47fe792 100644 --- a/src/parameterizations/vertical/MOM_diabatic_driver.F90 +++ b/src/parameterizations/vertical/MOM_diabatic_driver.F90 @@ -2925,6 +2925,7 @@ subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, di character(len=48) :: thickness_units character(len=40) :: var_name character(len=160) :: var_descript + logical :: physical_OBL_scheme integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, nz, nbands, m isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed ; nz = GV%ke IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB @@ -3434,9 +3435,11 @@ subroutine diabatic_driver_init(Time, G, GV, US, param_file, useALEalgorithm, di call internal_tides_init(Time, G, GV, US, param_file, diag, CS%int_tide) endif + physical_OBL_scheme = (CS%use_bulkmixedlayer .or. CS%use_KPP .or. CS%use_energetic_PBL) ! initialize module for setting diffusivities call set_diffusivity_init(Time, G, GV, US, param_file, diag, CS%set_diff_CSp, CS%int_tide, & - halo_TS=CS%halo_TS_diff, double_diffuse=CS%double_diffuse) + halo_TS=CS%halo_TS_diff, double_diffuse=CS%double_diffuse, & + physical_OBL_scheme=physical_OBL_scheme) if (CS%useKPP .and. (CS%double_diffuse .and. .not.CS%use_CVMix_ddiff)) & call MOM_error(FATAL, 'diabatic_driver_init: DOUBLE_DIFFUSION (old method) does not work '//& diff --git a/src/parameterizations/vertical/MOM_set_diffusivity.F90 b/src/parameterizations/vertical/MOM_set_diffusivity.F90 index 2e27877350..d7ac808217 100644 --- a/src/parameterizations/vertical/MOM_set_diffusivity.F90 +++ b/src/parameterizations/vertical/MOM_set_diffusivity.F90 @@ -1963,7 +1963,7 @@ subroutine set_density_ratios(h, tv, kb, G, GV, US, CS, j, ds_dsp1, rho_0) end subroutine set_density_ratios subroutine set_diffusivity_init(Time, G, GV, US, param_file, diag, CS, int_tide_CSp, halo_TS, & - double_diffuse) + double_diffuse, physical_OBL_scheme) type(time_type), intent(in) :: Time !< The current model time type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure. type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. @@ -1974,10 +1974,15 @@ subroutine set_diffusivity_init(Time, G, GV, US, param_file, diag, CS, int_tide_ type(set_diffusivity_CS), pointer :: CS !< pointer set to point to the module control !! structure. type(int_tide_CS), intent(in), target :: int_tide_CSp !< Internal tide control struct - integer, optional, intent(out) :: halo_TS !< The halo size of tracer points that must be + integer, intent(out) :: halo_TS !< The halo size of tracer points that must be !! valid for the calculations in set_diffusivity. logical, intent(out) :: double_diffuse !< This indicates whether some version !! of double diffusion is being used. + logical, intent(in) :: physical_OBL_scheme !< If true, a physically based + !! parameterization (like KPP or ePBL or a bulk mixed + !! layer) is used outside of set_diffusivity to + !! specify the mixing that occurs in the ocean's + !! surface boundary layer. ! Local variables real :: decay_length @@ -2164,7 +2169,7 @@ subroutine set_diffusivity_init(Time, G, GV, US, param_file, diag, CS, int_tide_ default=.false., do_not_log=.not.TKE_to_Kd_used) ! set params related to the background mixing - call bkgnd_mixing_init(Time, G, GV, US, param_file, CS%diag, CS%bkgnd_mixing_csp) + call bkgnd_mixing_init(Time, G, GV, US, param_file, CS%diag, CS%bkgnd_mixing_csp, physical_OBL_scheme) call get_param(param_file, mdl, "KV", CS%Kv, & "The background kinematic viscosity in the interior. "//& diff --git a/src/parameterizations/vertical/MOM_set_viscosity.F90 b/src/parameterizations/vertical/MOM_set_viscosity.F90 index df6ed9063b..2c90dac3f6 100644 --- a/src/parameterizations/vertical/MOM_set_viscosity.F90 +++ b/src/parameterizations/vertical/MOM_set_viscosity.F90 @@ -2050,10 +2050,9 @@ subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS do_not_log=.true.) if (adiabatic) then call log_param(param_file, mdl, "ADIABATIC",adiabatic, & - "There are no diapycnal mass fluxes if ADIABATIC is "//& - "true. This assumes that KD = KDML = 0.0 and that "//& - "there is no buoyancy forcing, but makes the model "//& - "faster by eliminating subroutine calls.", default=.false.) + "There are no diapycnal mass fluxes if ADIABATIC is true. "//& + "This assumes that KD = 0.0 and that there is no buoyancy forcing, "//& + "but makes the model faster by eliminating subroutine calls.", default=.false.) endif if (.not.adiabatic) then From f2481128670e39d0c2696c5532e445d74957686c Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Thu, 1 Sep 2022 15:18:21 -0400 Subject: [PATCH 28/68] +(*)Replace KVBBL with KV_EXTRA_BBL Replaced the runtime parameter KVBBL, which sets the total viscosity in the bottom boundary layer when BOTTOMDRAGLAW is false, with KV_EXTRA_BBL, which sets the viscosity anomaly relative to KV or other turbulent viscosities, and has a default value of 0 m2/s, instead of the previous nonzero default for KVBBL. Specifying KVBBL will still give equivalent results, but is no longer logged and triggers a warning message if it is used. This leads to changes in the entries of several MOM_parameter_doc files, and while all existing solutions are bitwise identical, by changing the definition of KVBBL there could be some changes at the level of roundoff for cases that do not use BOTTOMDRAGLAW = True. --- .../vertical/MOM_vert_friction.F90 | 98 ++++++++++++------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index 21ae10fef2..20ae902adf 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -9,7 +9,7 @@ module MOM_vert_friction use MOM_diag_mediator, only : diag_ctrl use MOM_debugging, only : uvchksum, hchksum use MOM_error_handler, only : MOM_error, FATAL, WARNING, NOTE -use MOM_file_parser, only : get_param, log_version, param_file_type +use MOM_file_parser, only : get_param, log_param, log_version, param_file_type use MOM_forcing_type, only : mech_forcing use MOM_get_input, only : directories use MOM_grid, only : ocean_grid_type @@ -43,11 +43,14 @@ module MOM_vert_friction real :: Hmix !< The mixed layer thickness in thickness units [H ~> m or kg m-2]. real :: Hmix_stress !< The mixed layer thickness over which the wind !! stress is applied with direct_stress [H ~> m or kg m-2]. - real :: Kvml !< The mixed layer vertical viscosity [Z2 T-1 ~> m2 s-1]. + real :: Kvml_inv2 !< The extra vertical viscosity scale in [Z2 T-1 ~> m2 s-1] in a + !! surface mixed layer with a characteristic thickness given by Hmix, + !! and scaling proportional to (Hmix/z)^2, where z is the distance + !! from the surface; this can get very large with thin layers. real :: Kv !< The interior vertical viscosity [Z2 T-1 ~> m2 s-1]. real :: Hbbl !< The static bottom boundary layer thickness [H ~> m or kg m-2]. - real :: Kvbbl !< The vertical viscosity in the bottom boundary - !! layer [Z2 T-1 ~> m2 s-1]. + real :: Kv_extra_bbl !< An extra vertical viscosity in the bottom boundary layer of thickness + !! Hbbl when there is not a bottom drag law in use [Z2 T-1 ~> m2 s-1]. real :: maxvel !< Velocity components greater than maxvel are truncated [L T-1 ~> m s-1]. real :: vel_underflow !< Velocity components smaller than vel_underflow @@ -94,8 +97,9 @@ module MOM_vert_friction !! layers based solely on harmonic mean thicknesses !! for the purpose of determining the extent to which !! the thicknesses used in the viscosities are upwinded. - logical :: direct_stress !< If true, the wind stress is distributed over the - !! topmost Hmix_stress of fluid and KVML may be very small. + logical :: direct_stress !< If true, the wind stress is distributed over the topmost Hmix_stress + !! of fluid, and the added mixed layer viscosity from KVML or a more + !! realistic viscosity parameterization is not needed for stability. logical :: dynamic_viscous_ML !< If true, use the results from a dynamic !! calculation, perhaps based on a bulk Richardson !! number criterion, to determine the mixed layer @@ -1140,7 +1144,7 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, real, dimension(SZIB_(G)), intent(in) :: kv_bbl !< Bottom boundary layer viscosity [Z2 T-1 ~> m2 s-1]. real, dimension(SZIB_(G),SZK_(GV)+1), & intent(in) :: z_i !< Estimate of interface heights above the bottom, - !! normalized by the bottom boundary layer thickness + !! normalized by the bottom boundary layer thickness [nondim] real, dimension(SZIB_(G)), intent(out) :: h_ml !< Mixed layer depth [H ~> m or kg m-2] integer, intent(in) :: j !< j-index to find coupling coefficient for real, intent(in) :: dt !< Time increment [T ~> s] @@ -1210,14 +1214,14 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, ! The following loop calculates the vertical average velocity and ! surface mixed layer contributions to the vertical viscosity. do i=is,ie ; Kv_tot(i,1) = 0.0 ; enddo - if ((GV%nkml>0) .or. do_shelf) then ; do k=2,nz ; do i=is,ie + if ((GV%nkml>0) .or. do_shelf .or. (CS%Kvml_inv2 <= 0.0) ) then ; do k=2,nz ; do i=is,ie if (do_i(i)) Kv_tot(i,K) = CS%Kv enddo ; enddo ; else I_Hmix = 1.0 / (CS%Hmix + h_neglect) do i=is,ie ; z_t(i) = h_neglect*I_Hmix ; enddo do K=2,nz ; do i=is,ie ; if (do_i(i)) then z_t(i) = z_t(i) + h_harm(i,k-1)*I_Hmix - Kv_tot(i,K) = CS%Kv + CS%Kvml / ((z_t(i)*z_t(i)) * & + Kv_tot(i,K) = CS%Kv + CS%Kvml_inv2 / ((z_t(i)*z_t(i)) * & (1.0 + 0.09*z_t(i)*z_t(i)*z_t(i)*z_t(i)*z_t(i)*z_t(i))) endif ; enddo ; enddo endif @@ -1231,7 +1235,8 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, a_cpl(i,nz+1) = kv_bbl(i) / (I_amax*kv_bbl(i) + (bbl_thick(i)+h_neglect)*GV%H_to_Z) endif else - a_cpl(i,nz+1) = CS%Kvbbl / ((0.5*hvel(i,nz)+h_neglect)*GV%H_to_Z + I_amax*CS%Kvbbl) + a_cpl(i,nz+1) = (CS%Kv + CS%Kv_extra_bbl) / & + ((0.5*hvel(i,nz)+h_neglect)*GV%H_to_Z + I_amax*(CS%Kv+CS%Kv_extra_bbl)) endif endif ; enddo @@ -1299,7 +1304,7 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, h_shear = r + h_neglect endif else - Kv_tot(i,K) = Kv_tot(i,K) + (CS%Kvbbl-CS%Kv)*botfn + Kv_tot(i,K) = Kv_tot(i,K) + CS%Kv_extra_bbl*botfn h_shear = 0.5*(hvel(i,k) + hvel(i,k-1) + h_neglect) endif @@ -1626,6 +1631,7 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & ! Local variables real :: Kv_dflt ! A default viscosity [m2 s-1]. + real :: Kv_BBL ! A viscosity in the bottom boundary layer with a simple scheme [Z2 T-1 ~> m2 s-1]. real :: Hmix_m ! A boundary layer thickness [m]. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. @@ -1687,9 +1693,9 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "actual velocity in the bottommost HBBL, depending on "//& "LINEAR_DRAG.", default=.true.) call get_param(param_file, mdl, "DIRECT_STRESS", CS%direct_stress, & - "If true, the wind stress is distributed over the "//& - "topmost HMIX_STRESS of fluid (like in HYCOM), and KVML "//& - "may be set to a very small value.", default=.false.) + "If true, the wind stress is distributed over the topmost HMIX_STRESS of fluid "//& + "(like in HYCOM), and the added mixed layer viscosity from KVML or another "//& + "more realistic parameterization is not needed for stability.", default=.false.) call get_param(param_file, mdl, "DYNAMIC_VISCOUS_ML", CS%dynamic_viscous_ML, & "If true, use a bulk Richardson number criterion to "//& "determine the mixed layer thickness for viscosity.", & @@ -1724,12 +1730,12 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & if (CS%direct_stress) then if (GV%nkml < 1) then call get_param(param_file, mdl, "HMIX_STRESS", CS%Hmix_stress, & - "The depth over which the wind stress is applied if "//& - "DIRECT_STRESS is true.", units="m", default=Hmix_m, scale=GV%m_to_H) + "The depth over which the wind stress is applied if DIRECT_STRESS is true.", & + units="m", default=Hmix_m, scale=GV%m_to_H) else call get_param(param_file, mdl, "HMIX_STRESS", CS%Hmix_stress, & - "The depth over which the wind stress is applied if "//& - "DIRECT_STRESS is true.", units="m", fail_if_missing=.true., scale=GV%m_to_H) + "The depth over which the wind stress is applied if DIRECT_STRESS is true.", & + units="m", fail_if_missing=.true., scale=GV%m_to_H) endif if (CS%Hmix_stress <= 0.0) call MOM_error(FATAL, "vertvisc_init: " // & "HMIX_STRESS must be set to a positive value if DIRECT_STRESS is true.") @@ -1739,28 +1745,46 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "The molecular value, ~1e-6 m2 s-1, may be used.", & units="m2 s-1", fail_if_missing=.true., scale=US%m2_s_to_Z2_T, unscaled=Kv_dflt) - if (GV%nkml < 1) call get_param(param_file, mdl, "KVML", CS%Kvml, & - "The kinematic viscosity in the mixed layer. A typical "//& - "value is ~1e-2 m2 s-1. KVML is not used if "//& - "BULKMIXEDLAYER is true. The default is set by KV.", & - units="m2 s-1", default=Kv_dflt, scale=US%m2_s_to_Z2_T) - if (.not.CS%bottomdraglaw) call get_param(param_file, mdl, "KVBBL", CS%Kvbbl, & - "The kinematic viscosity in the benthic boundary layer. "//& - "A typical value is ~1e-2 m2 s-1. KVBBL is not used if "//& - "BOTTOMDRAGLAW is true. The default is set by KV.", & - units="m2 s-1", default=Kv_dflt, scale=US%m2_s_to_Z2_T) + if (GV%nkml < 1) call get_param(param_file, mdl, "KVML", CS%Kvml_inv2, & + "The extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//& + "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2 to allow for finite "//& + "wind stresses to be transmitted through infinitessimally thin surface layers. "//& + "This is an older option for numerical convenience without a strong physical "//& + "basis, and its use is now discouraged. KVML is not used if BULKMIXEDLAYER "//& + "is true.", & + units="m2 s-1", default=Kv_dflt, scale=US%m2_s_to_Z2_T, do_not_log=(GV%nkml>0)) + if (.not.CS%bottomdraglaw) then + call get_param(param_file, mdl, "KV_EXTRA_BBL", CS%Kv_extra_bbl, & + "An extra kinematic viscosity in the benthic boundary layer. "//& + "KV_EXTRA_BBL is not used if BOTTOMDRAGLAW is true.", & + units="m2 s-1", default=0.0, scale=US%m2_s_to_Z2_T, do_not_log=.true.) + if (CS%Kv_extra_bbl == 0.0) then + call get_param(param_file, mdl, "KVBBL", Kv_BBL, & + "An extra kinematic viscosity in the benthic boundary layer. "//& + "KV_EXTRA_BBL is not used if BOTTOMDRAGLAW is true.", & + units="m2 s-1", default=US%Z2_T_to_m2_s*CS%Kv, scale=US%m2_s_to_Z2_T, do_not_log=.true.) + if (abs(Kv_BBL - CS%Kv) > 1.0e-15*abs(CS%Kv)) then + call MOM_error(WARNING, "KVBBL is a depricated parameter. Use KV_EXTRA_BBL instead.") + CS%Kv_extra_bbl = Kv_BBL - CS%Kv + endif + endif + call log_param(param_file, mdl, "KV_EXTRA_BBL", US%Z2_T_to_m2_s*CS%Kv_extra_bbl, & + "An extra kinematic viscosity in the benthic boundary layer. "//& + "KV_EXTRA_BBL is not used if BOTTOMDRAGLAW is true.", & + units="m2 s-1", default=0.0) + endif call get_param(param_file, mdl, "HBBL", CS%Hbbl, & - "The thickness of a bottom boundary layer with a "//& - "viscosity of KVBBL if BOTTOMDRAGLAW is not defined, or "//& - "the thickness over which near-bottom velocities are "//& - "averaged for the drag law if BOTTOMDRAGLAW is defined "//& - "but LINEAR_DRAG is not.", units="m", fail_if_missing=.true., scale=GV%m_to_H) + "The thickness of a bottom boundary layer with a viscosity increased by "//& + "KV_EXTRA_BBL if BOTTOMDRAGLAW is not defined, or the thickness over which "//& + "near-bottom velocities are averaged for the drag law if BOTTOMDRAGLAW is "//& + "defined but LINEAR_DRAG is not.", & + units="m", fail_if_missing=.true., scale=GV%m_to_H) call get_param(param_file, mdl, "MAXVEL", CS%maxvel, & - "The maximum velocity allowed before the velocity "//& - "components are truncated.", units="m s-1", default=3.0e8, scale=US%m_s_to_L_T) + "The maximum velocity allowed before the velocity components are truncated.", & + units="m s-1", default=3.0e8, scale=US%m_s_to_L_T) call get_param(param_file, mdl, "CFL_BASED_TRUNCATIONS", CS%CFL_based_trunc, & - "If true, base truncations on the CFL number, and not an "//& - "absolute speed.", default=.true.) + "If true, base truncations on the CFL number, and not an absolute speed.", & + default=.true.) call get_param(param_file, mdl, "CFL_TRUNCATE", CS%CFL_trunc, & "The value of the CFL number that will cause velocity "//& "components to be truncated; instability can occur past 0.5.", & From a20e5f61639ad9c914fa4d97ea8a87ca8a139211 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Thu, 1 Sep 2022 15:32:03 -0400 Subject: [PATCH 29/68] +Spell HARMONIC right in PORBAR_ETA_INTERP docs Corrected a spelling error, from HARMOINIC to HARMONIC, in the get_param description of one of the valid options for PORBAR_ETA_INTERP, and a few other spelling errors in comments in MOM_porous_barriers.F90. All answers are bitwise identical, but an entry in some MOM_parameter_doc files will change. --- src/core/MOM_porous_barriers.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/MOM_porous_barriers.F90 b/src/core/MOM_porous_barriers.F90 index cac8a44bf0..0e48cf07fd 100644 --- a/src/core/MOM_porous_barriers.F90 +++ b/src/core/MOM_porous_barriers.F90 @@ -284,7 +284,7 @@ subroutine calc_eta_at_uv(eta_u, eta_v, interp, dmask, h, tv, G, GV, US, eta_bt) real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: eta_bt !< optional barotropic variable !! used to dilate the layer thicknesses !! [H ~> m or kg m-2]. - real, intent(in) :: dmask !< The depth shaller than which + real, intent(in) :: dmask !< The depth shallower than which !! porous barrier is not applied [Z ~> m] integer, intent(in) :: interp !< eta interpolation method real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: eta_u !< Layer interface heights at u points [Z ~> m] @@ -456,13 +456,13 @@ subroutine porous_barriers_init(Time, US, param_file, diag, CS) ! The sign needs to be inverted to be consistent with the sign convention of Davg_[UV] CS%mask_depth = -CS%mask_depth call get_param(param_file, mdl, "PORBAR_ETA_INTERP", interp_method, & - "A string describing the method that decicdes how the "//& + "A string describing the method that decides how the "//& "interface heights at the velocity points are calculated. "//& "Valid values are:\n"//& "\t MAX (the default) - maximum of the adjacent cells \n"//& "\t MIN - minimum of the adjacent cells \n"//& "\t ARITHMETIC - arithmetic mean of the adjacent cells \n"//& - "\t HARMOINIC - harmonic mean of the adjacent cells \n", & + "\t HARMONIC - harmonic mean of the adjacent cells \n", & default=ETA_INTERP_MAX_STRING) select case (interp_method) case (ETA_INTERP_MAX_STRING) ; CS%eta_interp = ETA_INTERP_MAX From ba48d9d9949f28be0c266d5fae5ab8566a5094c6 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Fri, 2 Sep 2022 09:37:07 -0400 Subject: [PATCH 30/68] +Replace KVML with KV_ML_INVZ2 Renamed the runtime parameter KVML with KV_ML_INVZ2 and modified the comments describing it to more clearly indicate what it does and to explicitly discourage its use. Specifying KVML will still give identical results, but is no longer logged and triggers a warning message if it is used. The two descriptions of the HBBL runtime parameter in MOM_vert_friction and MOM_set_viscosity were also made consistent. Also added units to several comments describing real variables and corrected some spelling errors in comments in MOM_vert_friction.F90. This commit leads to changes in the entries of several MOM_parameter_doc files, but all existing solutions are bitwise identical. --- .../vertical/MOM_set_viscosity.F90 | 10 +-- .../vertical/MOM_vert_friction.F90 | 62 ++++++++++++------- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/parameterizations/vertical/MOM_set_viscosity.F90 b/src/parameterizations/vertical/MOM_set_viscosity.F90 index 2c90dac3f6..80be1ed12f 100644 --- a/src/parameterizations/vertical/MOM_set_viscosity.F90 +++ b/src/parameterizations/vertical/MOM_set_viscosity.F90 @@ -2110,11 +2110,11 @@ subroutine set_visc_init(Time, G, GV, US, param_file, diag, visc, CS, restart_CS endif call get_param(param_file, mdl, "HBBL", CS%Hbbl, & - "The thickness of a bottom boundary layer with a "//& - "viscosity of KVBBL if BOTTOMDRAGLAW is not defined, or "//& - "the thickness over which near-bottom velocities are "//& - "averaged for the drag law if BOTTOMDRAGLAW is defined "//& - "but LINEAR_DRAG is not.", units="m", fail_if_missing=.true.) ! Rescaled later + "The thickness of a bottom boundary layer with a viscosity increased by "//& + "KV_EXTRA_BBL if BOTTOMDRAGLAW is not defined, or the thickness over which "//& + "near-bottom velocities are averaged for the drag law if BOTTOMDRAGLAW is "//& + "defined but LINEAR_DRAG is not.", & + units="m", fail_if_missing=.true.) ! Rescaled later if (CS%bottomdraglaw) then call get_param(param_file, mdl, "CDRAG", CS%cdrag, & "CDRAG is the drag coefficient relating the magnitude of "//& diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index 20ae902adf..cd3ad22a28 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -96,10 +96,10 @@ module MOM_vert_friction real :: harm_BL_val !< A scale to determine when water is in the boundary !! layers based solely on harmonic mean thicknesses !! for the purpose of determining the extent to which - !! the thicknesses used in the viscosities are upwinded. + !! the thicknesses used in the viscosities are upwinded [nondim]. logical :: direct_stress !< If true, the wind stress is distributed over the topmost Hmix_stress - !! of fluid, and the added mixed layer viscosity from KVML or a more - !! realistic viscosity parameterization is not needed for stability. + !! of fluid, and an added mixed layer viscosity or a physically based + !! boundary layer turbulence parameterization is not needed for stability. logical :: dynamic_viscous_ML !< If true, use the results from a dynamic !! calculation, perhaps based on a bulk Richardson !! number criterion, to determine the mixed layer @@ -154,7 +154,7 @@ module MOM_vert_friction !! is the interfacial coupling thickness per time step, !! encompassing background viscosity as well as contributions from !! enhanced mixed and bottom layer viscosities. -!! $r_k$ is a Rayleight drag term due to channel drag. +!! $r_k$ is a Rayleigh drag term due to channel drag. !! There is an additional stress term on the right-hand side !! if DIRECT_STRESS is true, applied to the surface layer. @@ -302,7 +302,7 @@ subroutine vertvisc(u, v, h, forces, visc, dt, OBC, ADp, CDp, G, GV, US, CS, & ! denote the diagonal of the system as b_k, the subdiagonal as a_k ! and the superdiagonal as c_k. The right-hand side terms are d_k. ! - ! ignoring the rayleigh drag contribution, + ! ignoring the Rayleigh drag contribution, ! we have a_k = -dt_Z_to_H * a_u(k) ! b_k = h_u(k) + dt_Z_to_H * (a_u(k) + a_u(k+1)) ! c_k = -dt_Z_to_H * a_u(k+1) @@ -717,11 +717,11 @@ subroutine vertvisc_coef(u, v, h, forces, visc, dt, G, GV, US, CS, OBC) real, allocatable, dimension(:,:,:) :: Kv_v !< Total vertical viscosity at v-points [Z2 T-1 ~> m2 s-1]. real :: zcol(SZI_(G)) ! The height of an interface at h-points [H ~> m or kg m-2]. real :: botfn ! A function which goes from 1 at the bottom to 0 much more - ! than Hbbl into the interior. + ! than Hbbl into the interior [nondim]. real :: topfn ! A function which goes from 1 at the top to 0 much more - ! than Htbl into the interior. + ! than Htbl into the interior [nondim]. real :: z2 ! The distance from the bottom, normalized by Hbbl [nondim] - real :: z2_wt ! A nondimensional (0-1) weight used when calculating z2. + real :: z2_wt ! A nondimensional (0-1) weight used when calculating z2 [nondim]. real :: z_clear ! The clearance of an interface above the surrounding topography [H ~> m or kg m-2]. real :: a_cpl_max ! The maximum drag coefficient across interfaces, set so that it will be ! representable as a 32-bit float in MKS units [Z T-1 ~> m s-1] @@ -730,7 +730,7 @@ subroutine vertvisc_coef(u, v, h, forces, visc, dt, G, GV, US, CS, OBC) real :: I_valBL ! The inverse of a scaling factor determining when water is ! still within the boundary layer, as determined by the sum - ! of the harmonic mean thicknesses. + ! of the harmonic mean thicknesses [nondim]. logical, dimension(SZIB_(G)) :: do_i, do_i_shelf logical :: do_any_shelf integer, dimension(SZIB_(G)) :: & @@ -1163,7 +1163,7 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, u_star, & ! ustar at a velocity point [Z T-1 ~> m s-1]. absf, & ! The average of the neighboring absolute values of f [T-1 ~> s-1]. ! h_ml, & ! The mixed layer depth [H ~> m or kg m-2]. - nk_visc, & ! The (real) interface index of the base of mixed layer. + nk_visc, & ! The (real) interface index of the base of mixed layer [nondim]. z_t, & ! The distance from the top, sometimes normalized ! by Hmix, [H ~> m or kg m-2] or [nondim]. kv_TBL, & ! The viscosity in a top boundary layer under ice [Z2 T-1 ~> m2 s-1]. @@ -1694,8 +1694,9 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "LINEAR_DRAG.", default=.true.) call get_param(param_file, mdl, "DIRECT_STRESS", CS%direct_stress, & "If true, the wind stress is distributed over the topmost HMIX_STRESS of fluid "//& - "(like in HYCOM), and the added mixed layer viscosity from KVML or another "//& - "more realistic parameterization is not needed for stability.", default=.false.) + "(like in HYCOM), and an added mixed layer viscosity or a physically based "//& + "boundary layer turbulence parameterization is not needed for stability.", & + default=.false.) call get_param(param_file, mdl, "DYNAMIC_VISCOUS_ML", CS%dynamic_viscous_ML, & "If true, use a bulk Richardson number criterion to "//& "determine the mixed layer thickness for viscosity.", & @@ -1745,14 +1746,33 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "The molecular value, ~1e-6 m2 s-1, may be used.", & units="m2 s-1", fail_if_missing=.true., scale=US%m2_s_to_Z2_T, unscaled=Kv_dflt) - if (GV%nkml < 1) call get_param(param_file, mdl, "KVML", CS%Kvml_inv2, & - "The extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//& - "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2 to allow for finite "//& - "wind stresses to be transmitted through infinitessimally thin surface layers. "//& - "This is an older option for numerical convenience without a strong physical "//& - "basis, and its use is now discouraged. KVML is not used if BULKMIXEDLAYER "//& - "is true.", & - units="m2 s-1", default=Kv_dflt, scale=US%m2_s_to_Z2_T, do_not_log=(GV%nkml>0)) + if (GV%nkml < 1) then + call get_param(param_file, mdl, "KV_ML_INVZ2", CS%Kvml_inv2, & + "An extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//& + "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2, where z is the "//& + "distance from the surface, to allow for finite wind stresses to be "//& + "transmitted through infinitesimally thin surface layers. This is an "//& + "older option for numerical convenience without a strong physical basis, "//& + "and its use is now discouraged.", & + units="m2 s-1", default=-1.0, scale=US%m2_s_to_Z2_T, do_not_log=.true.) + if (CS%Kvml_inv2 < 0.0) then + call get_param(param_file, mdl, "KVML", CS%Kvml_inv2, & + "The scale for an extra kinematic viscosity in the mixed layer", & + units="m2 s-1", default=Kv_dflt, scale=US%m2_s_to_Z2_T, do_not_log=.true.) + if (CS%Kvml_inv2 >= 0.0) & + call MOM_error(WARNING, "KVML is a deprecated parameter. Use KV_ML_INVZ2 instead.") + endif + if (CS%Kvml_inv2 < 0.0) CS%Kvml_inv2 = CS%Kv ! Change this default later to 0.0. + call log_param(param_file, mdl, "KV_ML_INVZ2", US%Z2_T_to_m2_s*CS%Kvml_inv2, & + "An extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//& + "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2, where z is the "//& + "distance from the surface, to allow for finite wind stresses to be "//& + "transmitted through infinitesimally thin surface layers. This is an "//& + "older option for numerical convenience without a strong physical basis, "//& + "and its use is now discouraged.", & + units="m2 s-1", default=Kv_dflt) + endif + if (.not.CS%bottomdraglaw) then call get_param(param_file, mdl, "KV_EXTRA_BBL", CS%Kv_extra_bbl, & "An extra kinematic viscosity in the benthic boundary layer. "//& @@ -1764,7 +1784,7 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "KV_EXTRA_BBL is not used if BOTTOMDRAGLAW is true.", & units="m2 s-1", default=US%Z2_T_to_m2_s*CS%Kv, scale=US%m2_s_to_Z2_T, do_not_log=.true.) if (abs(Kv_BBL - CS%Kv) > 1.0e-15*abs(CS%Kv)) then - call MOM_error(WARNING, "KVBBL is a depricated parameter. Use KV_EXTRA_BBL instead.") + call MOM_error(WARNING, "KVBBL is a deprecated parameter. Use KV_EXTRA_BBL instead.") CS%Kv_extra_bbl = Kv_BBL - CS%Kv endif endif From 6ecf4e1f8733b9990295ea6f4e9990544d10bf3a Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Fri, 2 Sep 2022 11:53:27 -0400 Subject: [PATCH 31/68] Refactor boundary layer code in find_coupling_coef Refactored the way that find_coupling_coef sets the viscous coupling in the surface boundary layer, in preparation for adding new options. All answers are bitwise identical. --- .../vertical/MOM_vert_friction.F90 | 265 ++++++++++++------ 1 file changed, 177 insertions(+), 88 deletions(-) diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index cd3ad22a28..9ee937bc4b 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -43,7 +43,7 @@ module MOM_vert_friction real :: Hmix !< The mixed layer thickness in thickness units [H ~> m or kg m-2]. real :: Hmix_stress !< The mixed layer thickness over which the wind !! stress is applied with direct_stress [H ~> m or kg m-2]. - real :: Kvml_inv2 !< The extra vertical viscosity scale in [Z2 T-1 ~> m2 s-1] in a + real :: Kvml_invZ2 !< The extra vertical viscosity scale in [Z2 T-1 ~> m2 s-1] in a !! surface mixed layer with a characteristic thickness given by Hmix, !! and scaling proportional to (Hmix/z)^2, where z is the distance !! from the surface; this can get very large with thin layers. @@ -1141,7 +1141,9 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, intent(in) :: h_harm !< Harmonic mean of thicknesses around a velocity !! grid point [H ~> m or kg m-2] real, dimension(SZIB_(G)), intent(in) :: bbl_thick !< Bottom boundary layer thickness [H ~> m or kg m-2] - real, dimension(SZIB_(G)), intent(in) :: kv_bbl !< Bottom boundary layer viscosity [Z2 T-1 ~> m2 s-1]. + real, dimension(SZIB_(G)), intent(in) :: kv_bbl !< Bottom boundary layer viscosity, exclusive of + !! any depth-dependent contributions from + !! visc%Kv_shear [Z2 T-1 ~> m2 s-1]. real, dimension(SZIB_(G),SZK_(GV)+1), & intent(in) :: z_i !< Estimate of interface heights above the bottom, !! normalized by the bottom boundary layer thickness [nondim] @@ -1163,7 +1165,6 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, u_star, & ! ustar at a velocity point [Z T-1 ~> m s-1]. absf, & ! The average of the neighboring absolute values of f [T-1 ~> s-1]. ! h_ml, & ! The mixed layer depth [H ~> m or kg m-2]. - nk_visc, & ! The (real) interface index of the base of mixed layer [nondim]. z_t, & ! The distance from the top, sometimes normalized ! by Hmix, [H ~> m or kg m-2] or [nondim]. kv_TBL, & ! The viscosity in a top boundary layer under ice [Z2 T-1 ~> m2 s-1]. @@ -1171,21 +1172,25 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, real, dimension(SZIB_(G),SZK_(GV)+1) :: & Kv_tot, & ! The total viscosity at an interface [Z2 T-1 ~> m2 s-1]. Kv_add ! A viscosity to add [Z2 T-1 ~> m2 s-1]. + integer, dimension(SZIB_(G)) :: & + nk_in_ml ! The index of the deepest interface in the mixed layer. real :: h_shear ! The distance over which shears occur [H ~> m or kg m-2]. - real :: r ! A thickness to compare with Hbbl [H ~> m or kg m-2]. + real :: dhc ! The distance between the center of adjacent layers [H ~> m or kg m-2]. real :: visc_ml ! The mixed layer viscosity [Z2 T-1 ~> m2 s-1]. real :: I_Hmix ! The inverse of the mixed layer thickness [H-1 ~> m-1 or m2 kg-1]. real :: a_ml ! The layer coupling coefficient across an interface in ! the mixed layer [Z T-1 ~> m s-1]. + real :: a_floor ! A lower bound on the layer coupling coefficient across an interface in + ! the mixed layer [Z T-1 ~> m s-1]. real :: I_amax ! The inverse of the maximum coupling coefficient [T Z-1 ~> s m-1]. real :: temp1 ! A temporary variable [H Z ~> m2 or kg m-1] - real :: h_neglect ! A thickness that is so small it is usually lost - ! in roundoff and can be neglected [H ~> m or kg m-2]. + real :: h_neglect ! A thickness that is so small it is usually lost + ! in roundoff and can be neglected [H ~> m or kg m-2]. real :: z2 ! A copy of z_i [nondim] real :: botfn ! A function that is 1 at the bottom and small far from it [nondim] real :: topfn ! A function that is 1 at the top and small far from it [nondim] real :: kv_top ! A viscosity associated with the top boundary layer [Z2 T-1 ~> m2 s-1] - logical :: do_shelf, do_OBCs + logical :: do_shelf, do_OBCs, can_exit integer :: i, k, is, ie, max_nk integer :: nz @@ -1211,37 +1216,33 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, if (associated(OBC)) then ; do_OBCS = (OBC%number_of_segments > 0) ; endif h_ml(:) = 0.0 -! The following loop calculates the vertical average velocity and -! surface mixed layer contributions to the vertical viscosity. + ! This top boundary condition is appropriate when the wind stress is determined + ! externally and does not change within a timestep due to the surface velocity. do i=is,ie ; Kv_tot(i,1) = 0.0 ; enddo - if ((GV%nkml>0) .or. do_shelf .or. (CS%Kvml_inv2 <= 0.0) ) then ; do k=2,nz ; do i=is,ie - if (do_i(i)) Kv_tot(i,K) = CS%Kv - enddo ; enddo ; else + do K=2,nz+1 ; do i=is,ie + Kv_tot(i,K) = CS%Kv + enddo ; enddo + + if ((CS%Kvml_invZ2 > 0.0) .and. .not.do_shelf) then + ! This is an older (vintage ~1997) way to prevent wind stresses from driving very + ! large flows in nearly massless near-surface layers when there is not a physically- + ! based surface boundary layer parameterization. It does not have a plausible + ! physical basis, and probably should not be used. I_Hmix = 1.0 / (CS%Hmix + h_neglect) do i=is,ie ; z_t(i) = h_neglect*I_Hmix ; enddo do K=2,nz ; do i=is,ie ; if (do_i(i)) then z_t(i) = z_t(i) + h_harm(i,k-1)*I_Hmix - Kv_tot(i,K) = CS%Kv + CS%Kvml_inv2 / ((z_t(i)*z_t(i)) * & + Kv_tot(i,K) = CS%Kv + CS%Kvml_invZ2 / ((z_t(i)*z_t(i)) * & (1.0 + 0.09*z_t(i)*z_t(i)*z_t(i)*z_t(i)*z_t(i)*z_t(i))) endif ; enddo ; enddo endif - do i=is,ie ; if (do_i(i)) then - if (CS%bottomdraglaw) then - r = hvel(i,nz)*0.5 - if (r < bbl_thick(i)) then - a_cpl(i,nz+1) = kv_bbl(i) / (I_amax*kv_bbl(i) + (r+h_neglect)*GV%H_to_Z) - else - a_cpl(i,nz+1) = kv_bbl(i) / (I_amax*kv_bbl(i) + (bbl_thick(i)+h_neglect)*GV%H_to_Z) - endif - else - a_cpl(i,nz+1) = (CS%Kv + CS%Kv_extra_bbl) / & - ((0.5*hvel(i,nz)+h_neglect)*GV%H_to_Z + I_amax*(CS%Kv+CS%Kv_extra_bbl)) - endif - endif ; enddo - if (associated(visc%Kv_shear)) then - ! The factor of 2 that used to be required in the viscosities is no longer needed. + ! Add in viscosities that are determined by physical processes that are handled in + ! other modules, and which do not respond immediately to the changing layer thicknesses. + ! These processes may include shear-driven mixing or contributions from some boundary + ! layer turbulence schemes. Other viscosity contributions that respond to the evolving + ! layer thicknesses or the surface wind stresses are added later. if (work_on_u) then do K=2,nz ; do i=is,ie ; if (do_i(i)) then Kv_add(i,K) = 0.5*(visc%Kv_shear(i,j,k) + visc%Kv_shear(i+1,j,k)) @@ -1278,6 +1279,9 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, endif if (associated(visc%Kv_shear_Bu)) then + ! This is similar to what was done above, but for contributions coming from the corner + ! (vorticity) points. Because OBCs run through the faces and corners there is no need + ! to further modify these viscosities here to take OBCs into account. if (work_on_u) then do K=2,nz ; do I=Is,Ie ; If (do_i(I)) then Kv_tot(I,K) = Kv_tot(I,K) + (0.5)*(visc%Kv_shear_Bu(I,J-1,k) + visc%Kv_shear_Bu(I,J,k)) @@ -1289,29 +1293,71 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, endif endif - do K=nz,2,-1 ; do i=is,ie ; if (do_i(i)) then - ! botfn determines when a point is within the influence of the bottom - ! boundary layer, going from 1 at the bottom to 0 in the interior. - z2 = z_i(i,k) - botfn = 1.0 / (1.0 + 0.09*z2*z2*z2*z2*z2*z2) + ! Set the viscous coupling coefficients, excluding surface mixed layer contributions + ! for now, but including viscous bottom drag, working up from the bottom. + if (CS%bottomdraglaw) then + do i=is,ie ; if (do_i(i)) then + dhc = hvel(i,nz)*0.5 + ! These expressions assume that Kv_tot(i,nz+1) = CS%Kv, consistent with + ! the suppression of turbulent mixing by the presence of a solid boundary. + if (dhc < bbl_thick(i)) then + a_cpl(i,nz+1) = kv_bbl(i) / (I_amax*kv_bbl(i) + (dhc+h_neglect)*GV%H_to_Z) + else + a_cpl(i,nz+1) = kv_bbl(i) / (I_amax*kv_bbl(i) + (bbl_thick(i)+h_neglect)*GV%H_to_Z) + endif + endif ; enddo + do K=nz,2,-1 ; do i=is,ie ; if (do_i(i)) then + ! botfn determines when a point is within the influence of the bottom + ! boundary layer, going from 1 at the bottom to 0 in the interior. + z2 = z_i(i,k) + botfn = 1.0 / (1.0 + 0.09*z2*z2*z2*z2*z2*z2) - if (CS%bottomdraglaw) then Kv_tot(i,K) = Kv_tot(i,K) + (kv_bbl(i) - CS%Kv)*botfn - r = 0.5*(hvel(i,k) + hvel(i,k-1)) - if (r > bbl_thick(i)) then - h_shear = ((1.0 - botfn) * r + botfn*bbl_thick(i)) + h_neglect + dhc = 0.5*(hvel(i,k) + hvel(i,k-1)) + if (dhc > bbl_thick(i)) then + h_shear = ((1.0 - botfn) * dhc + botfn*bbl_thick(i)) + h_neglect else - h_shear = r + h_neglect + h_shear = dhc + h_neglect endif - else + + ! Calculate the coupling coefficients from the viscosities. + a_cpl(i,K) = Kv_tot(i,K) / (h_shear*GV%H_to_Z + I_amax*Kv_tot(i,K)) + endif ; enddo ; enddo ! i & k loops + elseif (abs(CS%Kv_extra_bbl) > 0.0) then + ! There is a simple enhancement of the near-bottom viscosities, but no adjustment + ! of the viscous coupling length scales to give a particular bottom stress. + do i=is,ie ; if (do_i(i)) then + a_cpl(i,nz+1) = (Kv_tot(i,nz+1) + CS%Kv_extra_bbl) / & + ((0.5*hvel(i,nz)+h_neglect)*GV%H_to_Z + I_amax*(Kv_tot(i,nz+1)+CS%Kv_extra_bbl)) + endif ; enddo + do K=nz,2,-1 ; do i=is,ie ; if (do_i(i)) then + ! botfn determines when a point is within the influence of the bottom + ! boundary layer, going from 1 at the bottom to 0 in the interior. + z2 = z_i(i,k) + botfn = 1.0 / (1.0 + 0.09*z2*z2*z2*z2*z2*z2) + Kv_tot(i,K) = Kv_tot(i,K) + CS%Kv_extra_bbl*botfn h_shear = 0.5*(hvel(i,k) + hvel(i,k-1) + h_neglect) - endif - ! Calculate the coupling coefficients from the viscosities. - a_cpl(i,K) = Kv_tot(i,K) / (h_shear*GV%H_to_Z + I_amax*Kv_tot(i,K)) - endif ; enddo ; enddo ! i & k loops + ! Calculate the coupling coefficients from the viscosities. + a_cpl(i,K) = Kv_tot(i,K) / (h_shear*GV%H_to_Z + I_amax*Kv_tot(i,K)) + endif ; enddo ; enddo ! i & k loops + else + ! Any near-bottom viscous enhancements were already incorporated into Kv_tot, and there is + ! no adjustment of the viscous coupling length scales to give a particular bottom stress. + do i=is,ie ; if (do_i(i)) then + a_cpl(i,nz+1) = Kv_tot(i,nz+1) / ((0.5*hvel(i,nz)+h_neglect)*GV%H_to_Z + I_amax*Kv_tot(i,nz+1)) + endif ; enddo + do K=nz,2,-1 ; do i=is,ie ; if (do_i(i)) then + h_shear = 0.5*(hvel(i,k) + hvel(i,k-1) + h_neglect) + ! Calculate the coupling coefficients from the viscosities. + a_cpl(i,K) = Kv_tot(i,K) / (h_shear*GV%H_to_Z + I_amax*Kv_tot(i,K)) + endif ; enddo ; enddo ! i & k loops + endif + ! Add surface intensified viscous coupling, either as a no-slip boundary condition under a + ! rigid ice-shelf, or due to wind-stress driven surface boundary layer mixing that has not + ! already been added via visc%Kv_shear. if (do_shelf) then ! Set the coefficients to include the no-slip surface stress. do i=is,ie ; if (do_i(i)) then @@ -1336,68 +1382,110 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, z_t(i) = z_t(i) + hvel(i,k-1) / tbl_thick(i) topfn = 1.0 / (1.0 + 0.09 * z_t(i)**6) - r = 0.5*(hvel(i,k)+hvel(i,k-1)) - if (r > tbl_thick(i)) then - h_shear = ((1.0 - topfn) * r + topfn*tbl_thick(i)) + h_neglect + dhc = 0.5*(hvel(i,k)+hvel(i,k-1)) + if (dhc > tbl_thick(i)) then + h_shear = ((1.0 - topfn) * dhc + topfn*tbl_thick(i)) + h_neglect else - h_shear = r + h_neglect + h_shear = dhc + h_neglect endif kv_top = topfn * kv_TBL(i) a_cpl(i,K) = a_cpl(i,K) + kv_top / (h_shear*GV%H_to_Z + I_amax*kv_top) endif ; enddo ; enddo + elseif (CS%dynamic_viscous_ML .or. (GV%nkml>0)) then - max_nk = 0 - do i=is,ie ; if (do_i(i)) then - if (GV%nkml>0) nk_visc(i) = real(GV%nkml+1) - if (work_on_u) then + + ! Find the friction velocity and the absolute value of the Coriolis parameter at this point. + u_star(:) = 0.0 ! Zero out the friction velocity on land points. + if (work_on_u) then + do I=is,ie ; if (do_i(I)) then u_star(I) = 0.5*(forces%ustar(i,j) + forces%ustar(i+1,j)) absf(I) = 0.5*(abs(G%CoriolisBu(I,J-1)) + abs(G%CoriolisBu(I,J))) - if (CS%dynamic_viscous_ML) nk_visc(I) = visc%nkml_visc_u(I,j) + 1 - else - u_star(i) = 0.5*(forces%ustar(i,j) + forces%ustar(i,j+1)) - absf(i) = 0.5*(abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J))) - if (CS%dynamic_viscous_ML) nk_visc(i) = visc%nkml_visc_v(i,J) + 1 - endif - h_ml(i) = h_neglect ; z_t(i) = 0.0 - max_nk = max(max_nk,ceiling(nk_visc(i) - 1.0)) - endif ; enddo - - if (do_OBCS) then ; if (work_on_u) then - do I=is,ie ; if (do_i(I) .and. (OBC%segnum_u(I,j) /= OBC_NONE)) then + endif ; enddo + if (do_OBCs) then ; do I=is,ie ; if (do_i(I) .and. (OBC%segnum_u(I,j) /= OBC_NONE)) then if (OBC%segment(OBC%segnum_u(I,j))%direction == OBC_DIRECTION_E) & u_star(I) = forces%ustar(i,j) if (OBC%segment(OBC%segnum_u(I,j))%direction == OBC_DIRECTION_W) & u_star(I) = forces%ustar(i+1,j) - endif ; enddo + endif ; enddo ; endif else - do i=is,ie ; if (do_i(i) .and. (OBC%segnum_v(i,J) /= OBC_NONE)) then + do i=is,ie ; if (do_i(i)) then + u_star(i) = 0.5*(forces%ustar(i,j) + forces%ustar(i,j+1)) + absf(i) = 0.5*(abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J))) + endif ; enddo + if (do_OBCs) then ; do i=is,ie ; if (do_i(i) .and. (OBC%segnum_v(i,J) /= OBC_NONE)) then if (OBC%segment(OBC%segnum_v(i,J))%direction == OBC_DIRECTION_N) & u_star(i) = forces%ustar(i,j) if (OBC%segment(OBC%segnum_v(i,J))%direction == OBC_DIRECTION_S) & u_star(i) = forces%ustar(i,j+1) + endif ; enddo ; endif + endif + + ! Determine the thickness of the surface ocean boundary layer and its extent in index space. + nk_in_ml(:) = 0 + if (CS%dynamic_viscous_ML) then + ! The fractional number of layers that are within the viscous boundary layer were + ! previously stored in visc%nkml_visc_[uv]. + h_ml(:) = h_neglect + max_nk = 0 + if (work_on_u) then + do i=is,ie ; if (do_i(i)) then + nk_in_ml(I) = ceiling(visc%nkml_visc_u(I,j)) + max_nk = max(max_nk, nk_in_ml(I)) + endif ; enddo + do k=1,max_nk ; do i=is,ie ; if (do_i(i)) then + if (k <= visc%nkml_visc_u(I,j)) then ! This layer is all in the ML. + h_ml(i) = h_ml(i) + hvel(i,k) + elseif (k < visc%nkml_visc_u(I,j) + 1.0) then ! Part of this layer is in the ML. + h_ml(i) = h_ml(i) + ((visc%nkml_visc_u(I,j) + 1.0) - k) * hvel(i,k) + endif + endif ; enddo ; enddo + else + do i=is,ie ; if (do_i(i)) then + nk_in_ml(i) = ceiling(visc%nkml_visc_v(i,J)) + max_nk = max(max_nk, nk_in_ml(i)) + endif ; enddo + do k=1,max_nk ; do i=is,ie ; if (do_i(i)) then + if (k <= visc%nkml_visc_v(i,J)) then ! This layer is all in the ML. + h_ml(i) = h_ml(i) + hvel(i,k) + elseif (k < visc%nkml_visc_v(i,J) + 1.0) then ! Part of this layer is in the ML. + h_ml(i) = h_ml(i) + ((visc%nkml_visc_v(i,J) + 1.0) - k) * hvel(i,k) + endif + endif ; enddo ; enddo + endif + + elseif (GV%nkml>0) then + ! This is a simple application of a refined-bulk mixed layer with GV%nkml sublayers. + max_nk = GV%nkml + do i=is,ie ; if (do_i(i)) then + nk_in_ml(i) = GV%nkml endif ; enddo - endif ; endif - do k=1,max_nk ; do i=is,ie ; if (do_i(i)) then - if (k+1 <= nk_visc(i)) then ! This layer is all in the ML. + h_ml(:) = h_neglect + do k=1,GV%nkml ; do i=is,ie ; if (do_i(i)) then h_ml(i) = h_ml(i) + hvel(i,k) - elseif (k < nk_visc(i)) then ! Part of this layer is in the ML. - h_ml(i) = h_ml(i) + (nk_visc(i) - k) * hvel(i,k) - endif - endif ; enddo ; enddo + endif ; enddo ; enddo + endif + + ! Avoid working on land or on columns where the viscous coupling could not be increased. + do i=is,ie ; if ((u_star(i)<=0.0) .or. (.not.do_i(i))) nk_in_ml(i) = 0 ; enddo - do K=2,max_nk ; do i=is,ie ; if (do_i(i)) then ; if (k < nk_visc(i)) then - ! Set the viscosity at the interfaces. + ! Set the viscous coupling at the interfaces as the larger of what was previously + ! set and the contributions from the surface boundary layer. + z_t(:) = 0.0 + do K=2,max_nk ; do i=is,ie ; if (k <= nk_in_ml(i)) then z_t(i) = z_t(i) + hvel(i,k-1) + temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z ! This viscosity is set to go to 0 at the mixed layer top and bottom (in a log-layer) ! and be further limited by rotation to give the natural Ekman length. visc_ml = u_star(i) * 0.41 * (temp1*u_star(i)) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) a_ml = visc_ml / (0.25*(hvel(i,k)+hvel(i,k-1) + h_neglect) * GV%H_to_Z + 0.5*I_amax*visc_ml) - ! Choose the largest estimate of a. - if (a_ml > a_cpl(i,K)) a_cpl(i,K) = a_ml - endif ; endif ; enddo ; enddo + + ! Choose the largest estimate of a_cpl, but these could be changed to be additive. + a_cpl(i,K) = max(a_cpl(i,K), a_ml) + ! An option could be added to change this to: a_cpl(i,K) = a_cpl(i,K) + a_ml + endif ; enddo ; enddo endif end subroutine find_coupling_coef @@ -1704,13 +1792,13 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & call get_param(param_file, mdl, "U_TRUNC_FILE", CS%u_trunc_file, & "The absolute path to a file into which the accelerations "//& "leading to zonal velocity truncations are written. "//& - "Undefine this for efficiency if this diagnostic is not "//& - "needed.", default=" ", debuggingParam=.true.) + "Undefine this for efficiency if this diagnostic is not needed.", & + default=" ", debuggingParam=.true.) call get_param(param_file, mdl, "V_TRUNC_FILE", CS%v_trunc_file, & "The absolute path to a file into which the accelerations "//& "leading to meridional velocity truncations are written. "//& - "Undefine this for efficiency if this diagnostic is not "//& - "needed.", default=" ", debuggingParam=.true.) + "Undefine this for efficiency if this diagnostic is not needed.", & + default=" ", debuggingParam=.true.) call get_param(param_file, mdl, "HARMONIC_VISC", CS%harmonic_visc, & "If true, use the harmonic mean thicknesses for "//& "calculating the vertical viscosity.", default=.false.) @@ -1746,8 +1834,9 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "The molecular value, ~1e-6 m2 s-1, may be used.", & units="m2 s-1", fail_if_missing=.true., scale=US%m2_s_to_Z2_T, unscaled=Kv_dflt) + CS%Kvml_invZ2 = 0.0 if (GV%nkml < 1) then - call get_param(param_file, mdl, "KV_ML_INVZ2", CS%Kvml_inv2, & + call get_param(param_file, mdl, "KV_ML_INVZ2", CS%Kvml_invZ2, & "An extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//& "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2, where z is the "//& "distance from the surface, to allow for finite wind stresses to be "//& @@ -1755,15 +1844,15 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "older option for numerical convenience without a strong physical basis, "//& "and its use is now discouraged.", & units="m2 s-1", default=-1.0, scale=US%m2_s_to_Z2_T, do_not_log=.true.) - if (CS%Kvml_inv2 < 0.0) then - call get_param(param_file, mdl, "KVML", CS%Kvml_inv2, & + if (CS%Kvml_invZ2 < 0.0) then + call get_param(param_file, mdl, "KVML", CS%Kvml_invZ2, & "The scale for an extra kinematic viscosity in the mixed layer", & units="m2 s-1", default=Kv_dflt, scale=US%m2_s_to_Z2_T, do_not_log=.true.) - if (CS%Kvml_inv2 >= 0.0) & + if (CS%Kvml_invZ2 >= 0.0) & call MOM_error(WARNING, "KVML is a deprecated parameter. Use KV_ML_INVZ2 instead.") endif - if (CS%Kvml_inv2 < 0.0) CS%Kvml_inv2 = CS%Kv ! Change this default later to 0.0. - call log_param(param_file, mdl, "KV_ML_INVZ2", US%Z2_T_to_m2_s*CS%Kvml_inv2, & + if (CS%Kvml_invZ2 < 0.0) CS%Kvml_invZ2 = CS%Kv ! Change this default later to 0.0. + call log_param(param_file, mdl, "KV_ML_INVZ2", US%Z2_T_to_m2_s*CS%Kvml_invZ2, & "An extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//& "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2, where z is the "//& "distance from the surface, to allow for finite wind stresses to be "//& @@ -1826,7 +1915,7 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "Flag to use Stokes drift Mixing via the Lagrangian "//& " current (Eulerian plus Stokes drift). "//& " Still needs work and testing, so not recommended for use.",& - Default=.false.) + default=.false.) !BGR 04/04/2018{ ! StokesMixing is required for MOM6 for some Langmuir mixing parameterization. ! The code used here has not been developed for vanishing layers or in From 9c58e0f4ff4cc8aa940359d2a18148fab181f6a7 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Sat, 3 Sep 2022 17:17:52 -0400 Subject: [PATCH 32/68] +Add FIXED_DEPTH_LOTW_ML and LOTW_VISCOUS_ML_FLOOR Added the new runtime parameters FIXED_DEPTH_LOTW_ML and LOTW_VISCOUS_ML_FLOOR to turn on a law-of-the-wall based scheme to specify the viscosity in a boundary layer of thickness HMIX_FIXED, and to apply a law-of-the-wall based lower bound on the viscous coupling strengths across interfaces that are within the surface boundary layer thickness, as determined in one of several different ways. Because these are false by default, all answers are bitwise identical, but there are new entries in some MOM_parameter_doc files. --- .../vertical/MOM_vert_friction.F90 | 105 +++++++++++++++--- 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index 9ee937bc4b..67b629f0bb 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -104,6 +104,15 @@ module MOM_vert_friction !! calculation, perhaps based on a bulk Richardson !! number criterion, to determine the mixed layer !! thickness for viscosity. + logical :: fixed_LOTW_ML !< If true, use a Law-of-the-wall prescription for the mixed layer + !! viscosity within a boundary layer that is the lesser of Hmix and the + !! total depth of the ocean in a column. + logical :: apply_LOTW_floor !< If true, use a Law-of-the-wall prescription to set a lower bound + !! on the viscous coupling between layers within the surface boundary + !! layer, based the distance of interfaces from the surface. This only + !! acts when there are large changes in the thicknesses of successive + !! layers or when the viscosity is set externally and the wind stress + !! has subsequently increased. integer :: answer_date !< The vintage of the order of arithmetic and expressions in the viscous !! calculations. Values below 20190101 recover the answers from the end !! of 2018, while higher values use expressions that do not use an @@ -1184,6 +1193,8 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, ! the mixed layer [Z T-1 ~> m s-1]. real :: I_amax ! The inverse of the maximum coupling coefficient [T Z-1 ~> s m-1]. real :: temp1 ! A temporary variable [H Z ~> m2 or kg m-1] + real :: ustar2_denom ! A temporary variable in the surface boundary layer turbulence + ! calculations [Z H-1 T-1 ~> s-1 or m3 kg-1 s-1] real :: h_neglect ! A thickness that is so small it is usually lost ! in roundoff and can be neglected [H ~> m or kg m-2]. real :: z2 ! A copy of z_i [nondim] @@ -1393,7 +1404,7 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, a_cpl(i,K) = a_cpl(i,K) + kv_top / (h_shear*GV%H_to_Z + I_amax*kv_top) endif ; enddo ; enddo - elseif (CS%dynamic_viscous_ML .or. (GV%nkml>0)) then + elseif (CS%dynamic_viscous_ML .or. (GV%nkml>0) .or. CS%fixed_LOTW_ML .or. CS%apply_LOTW_floor) then ! Find the friction velocity and the absolute value of the Coriolis parameter at this point. u_star(:) = 0.0 ! Zero out the friction velocity on land points. @@ -1465,6 +1476,25 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, do k=1,GV%nkml ; do i=is,ie ; if (do_i(i)) then h_ml(i) = h_ml(i) + hvel(i,k) endif ; enddo ; enddo + elseif (CS%fixed_LOTW_ML .or. CS%apply_LOTW_floor) then + ! Determine which interfaces are within CS%Hmix of the surface, and set the viscous + ! boundary layer thickness to the the smaller of CS%Hmix and the depth of the ocean. + h_ml(:) = 0.0 + do k=1,nz + can_exit = .true. + do i=is,ie ; if (do_i(i) .and. (h_ml(i) < CS%Hmix)) then + nk_in_ml(i) = k + if (h_ml(i) + hvel(i,k) < CS%Hmix) then + h_ml(i) = h_ml(i) + hvel(i,k) + can_exit = .false. ! Part of the next deeper layer is also in the mixed layer. + else + h_ml(i) = CS%Hmix + endif + endif ; enddo + if (can_exit) exit ! All remaining layers in this row are below the mixed layer depth. + enddo + max_nk = 0 + do i=is,ie ; max_nk = max(max_nk, nk_in_ml(i)) ; enddo endif ! Avoid working on land or on columns where the viscous coupling could not be increased. @@ -1473,19 +1503,55 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, ! Set the viscous coupling at the interfaces as the larger of what was previously ! set and the contributions from the surface boundary layer. z_t(:) = 0.0 - do K=2,max_nk ; do i=is,ie ; if (k <= nk_in_ml(i)) then - z_t(i) = z_t(i) + hvel(i,k-1) - - temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z - ! This viscosity is set to go to 0 at the mixed layer top and bottom (in a log-layer) - ! and be further limited by rotation to give the natural Ekman length. - visc_ml = u_star(i) * 0.41 * (temp1*u_star(i)) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) - a_ml = visc_ml / (0.25*(hvel(i,k)+hvel(i,k-1) + h_neglect) * GV%H_to_Z + 0.5*I_amax*visc_ml) - - ! Choose the largest estimate of a_cpl, but these could be changed to be additive. - a_cpl(i,K) = max(a_cpl(i,K), a_ml) - ! An option could be added to change this to: a_cpl(i,K) = a_cpl(i,K) + a_ml - endif ; enddo ; enddo + if (CS%apply_LOTW_floor .and. & + (CS%dynamic_viscous_ML .or. (GV%nkml>0) .or. CS%fixed_LOTW_ML)) then + do K=2,max_nk ; do i=is,ie ; if (k <= nk_in_ml(i)) then + z_t(i) = z_t(i) + hvel(i,k-1) + + ! The viscosity in visc_ml is set to go to 0 at the mixed layer top and bottom + ! (in a log-layer) and be further limited by rotation to give the natural Ekman length. + temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z + ustar2_denom = (0.41 * u_star(i)**2) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) + visc_ml = temp1 * ustar2_denom + ! Set the viscous coupling based on the model's vertical resolution. The omission of + ! the I_amax factor here is consistent with answer dates above 20190101. + a_ml = visc_ml / (0.25*(hvel(i,k)+hvel(i,k-1) + h_neglect) * GV%H_to_Z) + + ! As a floor on the viscous coupling, assume that the length scale in the denominator can + ! not be larger than the distance from the surface, consistent with a logarithmic velocity + ! profile. This is consistent with visc_ml, but cancels out common factors of z_t. + a_floor = (h_ml(i) - z_t(i)) * ustar2_denom + + ! Choose the largest estimate of a_cpl. + a_cpl(i,K) = max(a_cpl(i,K), a_ml, a_floor) + ! An option could be added to change this to: a_cpl(i,K) = max(a_cpl(i,K) + a_ml, a_floor) + endif ; enddo ; enddo + elseif (CS%apply_LOTW_floor) then + do K=2,max_nk ; do i=is,ie ; if (k <= nk_in_ml(i)) then + z_t(i) = z_t(i) + hvel(i,k-1) + + temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z + ustar2_denom = (0.41 * u_star(i)**2) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) + + ! As a floor on the viscous coupling, assume that the length scale in the denominator can not + ! be larger than the distance from the surface, consistent with a logarithmic velocity profile. + a_cpl(i,K) = max(a_cpl(i,K), (h_ml(i) - z_t(i)) * ustar2_denom) + endif ; enddo ; enddo + else + do K=2,max_nk ; do i=is,ie ; if (k <= nk_in_ml(i)) then + z_t(i) = z_t(i) + hvel(i,k-1) + + temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z + ! This viscosity is set to go to 0 at the mixed layer top and bottom (in a log-layer) + ! and be further limited by rotation to give the natural Ekman length. + visc_ml = u_star(i) * 0.41 * (temp1*u_star(i)) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) + a_ml = visc_ml / (0.25*(hvel(i,k)+hvel(i,k-1) + h_neglect) * GV%H_to_Z + 0.5*I_amax*visc_ml) + + ! Choose the largest estimate of a_cpl, but these could be changed to be additive. + a_cpl(i,K) = max(a_cpl(i,K), a_ml) + ! An option could be added to change this to: a_cpl(i,K) = a_cpl(i,K) + a_ml + endif ; enddo ; enddo + endif endif end subroutine find_coupling_coef @@ -1789,6 +1855,17 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "If true, use a bulk Richardson number criterion to "//& "determine the mixed layer thickness for viscosity.", & default=.false.) + call get_param(param_file, mdl, "FIXED_DEPTH_LOTW_ML", CS%fixed_LOTW_ML, & + "If true, use a Law-of-the-wall prescription for the mixed layer viscosity "//& + "within a boundary layer that is the lesser of HMIX_FIXED and the total "//& + "depth of the ocean in a column.", default=.false.) + call get_param(param_file, mdl, "LOTW_VISCOUS_ML_FLOOR", CS%apply_LOTW_floor, & + "If true, use a Law-of-the-wall prescription to set a lower bound on the "//& + "viscous coupling between layers within the surface boundary layer, based "//& + "the distance of interfaces from the surface. This only acts when there "//& + "are large changes in the thicknesses of successive layers or when the "//& + "viscosity is set externally and the wind stress has subsequently increased.", & + default=.false.) call get_param(param_file, mdl, "U_TRUNC_FILE", CS%u_trunc_file, & "The absolute path to a file into which the accelerations "//& "leading to zonal velocity truncations are written. "//& From f02b1d674cbb1507d952c0084b9336cae0ed4cca Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 5 Sep 2022 08:17:05 -0400 Subject: [PATCH 33/68] +(*)Set ustar with wind_forcing_2gyre Set the friction velocity, ustar, when the wind stresses are specified via wind_forcing_2gyre and wind_forcing_2gyre. This was done by adding the new subroutine stresses_to_ustar to start to standardize the calculation of the friction velocities from the wind stresses. Neverworld_wind_forcing and wind_forcing_gyres were already using the same expressions for ustar as are in stresses_to_ustar, so the new routine is now used in these cases. In other routines that set ustar, the answers differ at roundoff due to the difference between division by a reference density and multiplication by its reciprocal, so using the new subroutine in those cases should be wrapped in an answer_date flag, which is being left for a later commit. This is being added now as a result of a new commit that uses ustar as a part of the specification of viscosities even in idealized cases. All answers in the MOM6-examples test suite are bitwise identical. --- .../solo_driver/MOM_surface_forcing.F90 | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/config_src/drivers/solo_driver/MOM_surface_forcing.F90 b/config_src/drivers/solo_driver/MOM_surface_forcing.F90 index 10b5f377fa..c1e125be83 100644 --- a/config_src/drivers/solo_driver/MOM_surface_forcing.F90 +++ b/config_src/drivers/solo_driver/MOM_surface_forcing.F90 @@ -106,7 +106,7 @@ module MOM_surface_forcing real :: gyres_taux_cos_amp !< The amplitude of cosine wind stress gyres [R L Z T-1 ~> Pa], if WIND_CONFIG=='gyres' real :: gyres_taux_n_pis !< The number of sine lobes in the basin if WIND_CONFIG=='gyres' integer :: answer_date !< This 8-digit integer gives the approximate date with which the order - !! of arithmetic and and expressions were added to the code. + !! of arithmetic and expressions were added to the code. !! Dates before 20190101 use original answers. !! Dates after 20190101 use a form of the gyre wind stresses that are !! rotationally invariant and more likely to be the same between compilers. @@ -161,8 +161,8 @@ module MOM_surface_forcing character(len=200) :: salinityrestore_file = '' !< The file from which to read the sea surface !! salinity to restore toward - character(len=80) :: stress_x_var = '' !< X-windstress variable name in the input file - character(len=80) :: stress_y_var = '' !< Y-windstress variable name in the input file + character(len=80) :: stress_x_var = '' !< X-wind stress variable name in the input file + character(len=80) :: stress_y_var = '' !< Y-wind stress variable name in the input file character(len=80) :: ustar_var = '' !< ustar variable name in the input file character(len=80) :: LW_var = '' !< longwave heat flux variable name in the input file character(len=80) :: SW_var = '' !< shortwave heat flux variable name in the input file @@ -447,6 +447,8 @@ subroutine wind_forcing_2gyre(sfc_state, forces, day, G, US, CS) forces%tauy(i,J) = 0.0 enddo ; enddo + if (associated(forces%ustar)) call stresses_to_ustar(forces, G, US, CS) + call callTree_leave("wind_forcing_2gyre") end subroutine wind_forcing_2gyre @@ -484,6 +486,8 @@ subroutine wind_forcing_1gyre(sfc_state, forces, day, G, US, CS) forces%tauy(i,J) = 0.0 enddo ; enddo + if (associated(forces%ustar)) call stresses_to_ustar(forces, G, US, CS) + call callTree_leave("wind_forcing_1gyre") end subroutine wind_forcing_1gyre @@ -499,8 +503,6 @@ subroutine wind_forcing_gyres(sfc_state, forces, day, G, US, CS) !! a previous surface_forcing_init call ! Local variables real :: PI ! A common irrational number, 3.1415926535... [nondim] - real :: I_rho ! The inverse of the reference density times a ratio of scaling - ! factors [Z L-1 R-1 ~> m3 kg-1] real :: y ! The latitude relative to the south normalized by the domain extent [nondim] integer :: i, j, is, ie, js, je, Isq, Ieq, Jsq, Jeq @@ -530,12 +532,7 @@ subroutine wind_forcing_gyres(sfc_state, forces, day, G, US, CS) forces%taux(i-1,j)*forces%taux(i-1,j) + forces%taux(i,j)*forces%taux(i,j)))/CS%Rho0) ) enddo ; enddo else - I_rho = US%L_to_Z / CS%Rho0 - do j=js,je ; do i=is,ie - forces%ustar(i,j) = sqrt( (CS%gust_const + & - sqrt(0.5*((forces%tauy(i,J-1)**2 + forces%tauy(i,J)**2) + & - (forces%taux(I-1,j)**2 + forces%taux(I,j)**2))) ) * I_rho ) - enddo ; enddo + call stresses_to_ustar(forces, G, US, CS) endif call callTree_leave("wind_forcing_gyres") @@ -558,8 +555,6 @@ subroutine Neverworld_wind_forcing(sfc_state, forces, day, G, US, CS) real :: Pa_to_RLZ_T2 ! A unit conversion factor from Pa to the internal units ! for wind stresses [R Z L T-2 Pa-1 ~> 1] real :: PI ! A common irrational number, 3.1415926535... [nondim] - real :: I_rho ! The inverse of the reference density times a ratio of scaling - ! factors [Z L-1 R-1 ~> m3 kg-1] real :: y ! The latitude relative to the south normalized by the domain extent [nondim] real :: tau_max ! The magnitude of the wind stress [R Z L T-2 ~> Pa] real :: off ! An offset in the relative latitude [nondim] @@ -602,14 +597,7 @@ subroutine Neverworld_wind_forcing(sfc_state, forces, day, G, US, CS) enddo ; enddo ! Set the surface friction velocity, in units of [Z T-1 ~> m s-1]. ustar is always positive. - if (associated(forces%ustar)) then - I_rho = US%L_to_Z / CS%Rho0 - do j=js,je ; do i=is,ie - forces%ustar(i,j) = sqrt( (CS%gust_const + & - sqrt(0.5*((forces%tauy(i,J-1)**2 + forces%tauy(i,J)**2) + & - (forces%taux(I-1,j)**2 + forces%taux(I,j)**2))) ) * I_rho ) - enddo ; enddo - endif + if (associated(forces%ustar)) call stresses_to_ustar(forces, G, US, CS) end subroutine Neverworld_wind_forcing @@ -625,8 +613,6 @@ subroutine scurve_wind_forcing(sfc_state, forces, day, G, US, CS) !! a previous surface_forcing_init call ! Local variables integer :: i, j, kseg - real :: I_rho ! The inverse of the reference density times a ratio of scaling - ! factors [Z L-1 R-1 ~> m3 kg-1] real :: y_curve ! The latitude relative to the southern end of a curve segment [degreesN] real :: L_curve ! The latitudinal extent of a curve segment [degreesN] ! real :: ydata(7) = (/ -70., -45., -15., 0., 15., 45., 70. /) @@ -657,14 +643,7 @@ subroutine scurve_wind_forcing(sfc_state, forces, day, G, US, CS) enddo ; enddo ! Set the surface friction velocity, in units of [Z T-1 ~> m s-1]. ustar is always positive. - if (associated(forces%ustar)) then - I_rho = US%L_to_Z / CS%Rho0 - do j=G%jsc,G%jec ; do i=G%isc,G%iec - forces%ustar(i,j) = sqrt( (CS%gust_const + & - sqrt(0.5*((forces%tauy(i,J-1)**2 + forces%tauy(i,J)**2) + & - (forces%taux(I-1,j)**2 + forces%taux(I,j)**2))) ) * I_rho ) - enddo ; enddo - endif + if (associated(forces%ustar)) call stresses_to_ustar(forces, G, US, CS) end subroutine scurve_wind_forcing @@ -892,6 +871,37 @@ subroutine wind_forcing_by_data_override(sfc_state, forces, day, G, US, CS) call callTree_leave("wind_forcing_by_data_override") end subroutine wind_forcing_by_data_override +!> Translate the wind stresses into the friction velocity, including effects of background gustiness. +subroutine stresses_to_ustar(forces, G, US, CS) + type(mech_forcing), intent(inout) :: forces !< A structure with the driving mechanical forces + type(ocean_grid_type), intent(in) :: G !< Grid structure. + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + type(surface_forcing_CS), pointer :: CS !< pointer to control structure returned by + !! a previous surface_forcing_init call + ! Local variables + real :: I_rho ! The inverse of the reference density times a ratio of scaling + ! factors [Z L-1 R-1 ~> m3 kg-1] + integer :: i, j, is, ie, js, je + + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + I_rho = US%L_to_Z / CS%Rho0 + + if (CS%read_gust_2d) then + do j=js,je ; do i=is,ie + forces%ustar(i,j) = sqrt( (CS%gust(i,j) + & + sqrt(0.5*((forces%tauy(i,j-1)**2 + forces%tauy(i,j)**2) + & + (forces%taux(i-1,j)**2 + forces%taux(i,j)**2))) ) * I_rho ) + enddo ; enddo + else + do j=js,je ; do i=is,ie + forces%ustar(i,j) = sqrt( (CS%gust_const + & + sqrt(0.5*((forces%tauy(i,J-1)**2 + forces%tauy(i,J)**2) + & + (forces%taux(I-1,j)**2 + forces%taux(I,j)**2))) ) * I_rho ) + enddo ; enddo + endif + +end subroutine stresses_to_ustar !> Specifies zero surface buoyancy fluxes from input files. subroutine buoyancy_forcing_from_files(sfc_state, fluxes, day, dt, G, US, CS) From de3f260ef67f51bb2757cd42c23dedb1545e3a8a Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 5 Sep 2022 10:08:58 -0400 Subject: [PATCH 34/68] +Made the von Karman constant a runtime parameter Made the von Karman constant into a runtime parameter, specified with VON_KARMAN_CONST or VON_KARMAN_BBL, replacing a hard-coded value in multiple places. By default, all answers are bitwise identical, but there are one or two new entries in the MOM_parameter_doc files. --- .../lateral/MOM_mixed_layer_restrat.F90 | 35 +++++++++++-------- .../vertical/MOM_bulk_mixed_layer.F90 | 20 ++++++----- .../vertical/MOM_diabatic_aux.F90 | 3 +- .../vertical/MOM_diapyc_energy_req.F90 | 13 ++++--- .../vertical/MOM_energetic_PBL.F90 | 6 ++-- .../vertical/MOM_set_diffusivity.F90 | 12 +++++-- .../vertical/MOM_vert_friction.F90 | 10 ++++-- 7 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 b/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 index 864669a217..a8efa4cf12 100644 --- a/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 +++ b/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 @@ -48,6 +48,7 @@ module MOM_mixed_layer_restrat logical :: MLE_use_PBL_MLD !< If true, use the MLD provided by the PBL parameterization. !! if false, MLE will calculate a MLD based on a density difference !! based on the parameter MLE_DENSITY_DIFF. + real :: vonKar !< The von Karman constant as used for mixed layer viscosity [nomdim] real :: MLE_MLD_decay_time !< Time-scale to use in a running-mean when MLD is retreating [T ~> s]. real :: MLE_MLD_decay_time2 !< Time-scale to use in a running-mean when filtered MLD is retreating [T ~> s]. real :: MLE_density_diff !< Density difference used in detecting mixed-layer depth [R ~> kg m-3]. @@ -189,6 +190,8 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var real :: dh ! Portion of the layer thickness that is in the mixed layer [H ~> m or kg m-2] real :: res_scaling_fac ! The resolution-dependent scaling factor [nondim] real :: I_LFront ! The inverse of the frontal length scale [L-1 ~> m-1] + real :: vonKar_x_pi2 ! A scaling constant that is approximately the von Karman constant times + ! pi squared [nondim] logical :: line_is_empty, keep_going, res_upscale integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz @@ -200,6 +203,8 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var covTS(:)=0.0 !!Functionality not implemented yet; in future, should be passed in tv varS(:)=0.0 + vonKar_x_pi2 = CS%vonKar * 9.8696 + if (.not.associated(tv%eqn_of_state)) call MOM_error(FATAL, "MOM_mixedlayer_restrat: "// & "An equation of state must be used with this module.") if (.not. allocated(VarMix%Rd_dx_h) .and. CS%front_length > 0.) & @@ -380,11 +385,10 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var ( sqrt( 0.5 * ( G%dxCu(I,j)**2 + G%dyCu(I,j)**2 ) ) * I_LFront ) & * min( 1., 0.5*( VarMix%Rd_dx_h(i,j) + VarMix%Rd_dx_h(i+1,j) ) ) - ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) + ! peak ML visc: u_star * von_Karman * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) ! momentum mixing rate: pi^2*visc/h_ml^2 - ! 0.41 is the von Karmen constant, 9.8696 = pi^2. h_vel = 0.5*((htot_fast(i,j) + htot_fast(i+1,j)) + h_neglect) * GV%H_to_Z - mom_mixrate = (0.41*9.8696)*u_star**2 / & + mom_mixrate = vonKar_x_pi2*u_star**2 / & (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star) timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef @@ -393,7 +397,7 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var (Rml_av_fast(i+1,j)-Rml_av_fast(i,j)) * (h_vel**2 * GV%Z_to_H) ! As above but using the slow filtered MLD h_vel = 0.5*((htot_slow(i,j) + htot_slow(i+1,j)) + h_neglect) * GV%H_to_Z - mom_mixrate = (0.41*9.8696)*u_star**2 / & + mom_mixrate = vonKar_x_pi2*u_star**2 / & (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star) timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef2 @@ -456,11 +460,10 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var ( sqrt( 0.5 * ( (G%dxCv(i,J))**2 + (G%dyCv(i,J))**2 ) ) * I_LFront ) & * min( 1., 0.5*( VarMix%Rd_dx_h(i,j) + VarMix%Rd_dx_h(i,j+1) ) ) - ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) + ! peak ML visc: u_star * von_Karman * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) ! momentum mixing rate: pi^2*visc/h_ml^2 - ! 0.41 is the von Karmen constant, 9.8696 = pi^2. h_vel = 0.5*((htot_fast(i,j) + htot_fast(i,j+1)) + h_neglect) * GV%H_to_Z - mom_mixrate = (0.41*9.8696)*u_star**2 / & + mom_mixrate = vonKar_x_pi2*u_star**2 / & (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star) timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef @@ -469,7 +472,7 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var (Rml_av_fast(i,j+1)-Rml_av_fast(i,j)) * (h_vel**2 * GV%Z_to_H) ! As above but using the slow filtered MLD h_vel = 0.5*((htot_slow(i,j) + htot_slow(i,j+1)) + h_neglect) * GV%H_to_Z - mom_mixrate = (0.41*9.8696)*u_star**2 / & + mom_mixrate = vonKar_x_pi2*u_star**2 / & (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star) timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef2 @@ -617,6 +620,8 @@ subroutine mixedlayer_restrat_BML(h, uhtr, vhtr, tv, forces, dt, G, GV, US, CS) real :: h_vel ! htot interpolated onto velocity points [Z ~> m]. (The units are not H.) real :: absf ! absolute value of f, interpolated to velocity points [T-1 ~> s-1] real :: u_star ! surface friction velocity, interpolated to velocity points [Z T-1 ~> m s-1]. + real :: vonKar_x_pi2 ! A scaling constant that is approximately the von Karman constant times + ! pi squared [nondim] real :: mom_mixrate ! rate at which momentum is homogenized within mixed layer [T-1 ~> s-1] real :: timescale ! mixing growth timescale [T ~> s] real :: h_min ! The minimum layer thickness [H ~> m or kg m-2]. h_min could be 0. @@ -653,6 +658,7 @@ subroutine mixedlayer_restrat_BML(h, uhtr, vhtr, tv, forces, dt, G, GV, US, CS) uDml(:) = 0.0 ; vDml(:) = 0.0 I4dt = 0.25 / dt g_Rho0 = GV%g_Earth / GV%Rho0 + vonKar_x_pi2 = CS%vonKar * 9.8696 use_EOS = associated(tv%eqn_of_state) h_neglect = GV%H_subroundoff dz_neglect = GV%H_subroundoff*GV%H_to_Z @@ -701,10 +707,9 @@ subroutine mixedlayer_restrat_BML(h, uhtr, vhtr, tv, forces, dt, G, GV, US, CS) u_star = 0.5*(forces%ustar(i,j) + forces%ustar(i+1,j)) absf = 0.5*(abs(G%CoriolisBu(I,J-1)) + abs(G%CoriolisBu(I,J))) - ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) + ! peak ML visc: u_star * von_Karman * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) ! momentum mixing rate: pi^2*visc/h_ml^2 - ! 0.41 is the von Karmen constant, 9.8696 = pi^2. - mom_mixrate = (0.41*9.8696)*u_star**2 / & + mom_mixrate = vonKar_x_pi2*u_star**2 / & (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star) timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) @@ -748,10 +753,9 @@ subroutine mixedlayer_restrat_BML(h, uhtr, vhtr, tv, forces, dt, G, GV, US, CS) u_star = 0.5*(forces%ustar(i,j) + forces%ustar(i,j+1)) absf = 0.5*(abs(G%CoriolisBu(I-1,J)) + abs(G%CoriolisBu(I,J))) - ! peak ML visc: u_star * 0.41 * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) + ! peak ML visc: u_star * von_Karman * (h_ml*u_star)/(absf*h_ml + 4.0*u_star) ! momentum mixing rate: pi^2*visc/h_ml^2 - ! 0.41 is the von Karmen constant, 9.8696 = pi^2. - mom_mixrate = (0.41*9.8696)*u_star**2 / & + mom_mixrate = vonKar_x_pi2*u_star**2 / & (absf*h_vel**2 + 4.0*(h_vel+dz_neglect)*u_star) timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) @@ -877,6 +881,9 @@ logical function mixedlayer_restrat_init(Time, G, GV, US, param_file, diag, CS, call get_param(param_file, mdl, "USE_STANLEY_ML", CS%use_stanley_ml, & "If true, turn on Stanley SGS T variance parameterization "// & "in ML restrat code.", default=.false.) + call get_param(param_file, mdl, 'VON_KARMAN_CONST', CS%vonKar, & + 'The value the von Karman constant as used for mixed layer viscosity.', & + units='nondim', default=0.41) ! We use GV%nkml to distinguish between the old and new implementation of MLE. ! The old implementation only works for the layer model with nkml>0. if (GV%nkml==0) then diff --git a/src/parameterizations/vertical/MOM_bulk_mixed_layer.F90 b/src/parameterizations/vertical/MOM_bulk_mixed_layer.F90 index aa0d05ce79..49d62bbde4 100644 --- a/src/parameterizations/vertical/MOM_bulk_mixed_layer.F90 +++ b/src/parameterizations/vertical/MOM_bulk_mixed_layer.F90 @@ -49,6 +49,7 @@ module MOM_bulk_mixed_layer !! the mixed layer is converted to TKE [nondim]. real :: bulk_Ri_convective !< The efficiency with which convectively !! released mean kinetic energy becomes TKE [nondim]. + real :: vonKar !< The von Karman constant as used for mixed layer viscosity [nomdim] real :: Hmix_min !< The minimum mixed layer thickness [H ~> m or kg m-2]. real :: H_limit_fluxes !< When the total ocean depth is less than this !! value [H ~> m or kg m-2], scale away all surface forcing to @@ -316,7 +317,7 @@ subroutine bulkmixedlayer(h_3d, u_3d, v_3d, tv, fluxes, dt, ea, eb, G, GV, US, C real :: H_nbr ! A minimum thickness based on neighboring thicknesses [H ~> m or kg m-2]. real :: absf_x_H ! The absolute value of f times the mixed layer thickness [Z T-1 ~> m s-1]. - real :: kU_star ! Ustar times the Von Karmen constant [Z T-1 ~> m s-1]. + real :: kU_star ! Ustar times the Von Karman constant [Z T-1 ~> m s-1]. real :: dt__diag ! A recaled copy of dt_diag (if present) or dt [T ~> s]. logical :: write_diags ! If true, write out diagnostics with this step. logical :: reset_diags ! If true, zero out the accumulated diagnostics. @@ -618,12 +619,12 @@ subroutine bulkmixedlayer(h_3d, u_3d, v_3d, tv, fluxes, dt, ea, eb, G, GV, US, C ! as the third piece will then optimally describe mixed layer ! restratification. For nkml>=4 the whole strategy should be revisited. do i=is,ie - kU_star = 0.41*fluxes%ustar(i,j) ! Maybe could be replaced with u*+w*? + kU_star = CS%vonKar*fluxes%ustar(i,j) ! Maybe could be replaced with u*+w*? if (associated(fluxes%ustar_shelf) .and. & associated(fluxes%frac_shelf_h)) then if (fluxes%frac_shelf_h(i,j) > 0.0) & kU_star = (1.0 - fluxes%frac_shelf_h(i,j)) * kU_star + & - fluxes%frac_shelf_h(i,j) * (0.41*fluxes%ustar_shelf(i,j)) + fluxes%frac_shelf_h(i,j) * (CS%vonKar*fluxes%ustar_shelf(i,j)) endif absf_x_H = 0.25 * GV%H_to_Z * h(i,0) * & ((abs(G%CoriolisBu(I,J)) + abs(G%CoriolisBu(I-1,J-1))) + & @@ -1344,11 +1345,11 @@ subroutine find_starting_TKE(htot, h_CA, fluxes, Conv_En, cTKE, dKE_FC, dKE_CA, ! the equatorial areas. Although it is not cast as a parameter, it should ! be considered an empirical parameter, and it might depend strongly on the ! number of sublayers in the mixed layer and their locations. -! The 0.41 is VonKarman's constant. This equation assumes that small & large -! scales contribute to mixed layer deepening at similar rates, even though -! small scales are dissipated more rapidly (implying they are less efficient). -! Ih = 1.0/(16.0*0.41*U_star*dt) - Ih = GV%H_to_Z/(3.0*0.41*U_star*dt) +! This equation assumes that small & large scales contribute to mixed layer +! deepening at similar rates, even though small scales are dissipated more +! rapidly (implying they are less efficient). +! Ih = 1.0/(16.0*CS%vonKar*U_star*dt) + Ih = GV%H_to_Z/(3.0*CS%vonKar*U_star*dt) cMKE(1,i) = 4.0 * Ih ; cMKE(2,i) = (absf_Ustar*GV%H_to_Z) * Ih if (Idecay_len_TKE(i) > 0.0) then @@ -3387,6 +3388,9 @@ subroutine bulkmixedlayer_init(Time, G, GV, US, param_file, diag, CS) "kinetic energy is converted to turbulent kinetic "//& "energy. By default BULK_RI_CONVECTIVE=BULK_RI_ML.", & units="nondim", default=CS%bulk_Ri_ML) + call get_param(param_file, mdl, 'VON_KARMAN_CONST', CS%vonKar, & + 'The value the von Karman constant as used for mixed layer viscosity.', & + units='nondim', default=0.41) call get_param(param_file, mdl, "HMIX_MIN", CS%Hmix_min, & "The minimum mixed layer depth if the mixed layer depth "//& "is determined dynamically.", units="m", default=0.0, scale=GV%m_to_H, & diff --git a/src/parameterizations/vertical/MOM_diabatic_aux.F90 b/src/parameterizations/vertical/MOM_diabatic_aux.F90 index b4cb46830d..a3450bd6e4 100644 --- a/src/parameterizations/vertical/MOM_diabatic_aux.F90 +++ b/src/parameterizations/vertical/MOM_diabatic_aux.F90 @@ -153,8 +153,7 @@ subroutine make_frazil(h, tv, G, GV, US, CS, p_surf, halo) pressure(i,1) = ps(i) + (0.5*H_to_RL2_T2)*h(i,j,1) enddo do k=2,nz ; do i=is,ie - pressure(i,k) = pressure(i,k-1) + & - (0.5*H_to_RL2_T2) * (h(i,j,k) + h(i,j,k-1)) + pressure(i,k) = pressure(i,k-1) + (0.5*H_to_RL2_T2) * (h(i,j,k) + h(i,j,k-1)) enddo ; enddo endif diff --git a/src/parameterizations/vertical/MOM_diapyc_energy_req.F90 b/src/parameterizations/vertical/MOM_diapyc_energy_req.F90 index 975a11d909..2ddf8b8c7a 100644 --- a/src/parameterizations/vertical/MOM_diapyc_energy_req.F90 +++ b/src/parameterizations/vertical/MOM_diapyc_energy_req.F90 @@ -27,8 +27,9 @@ module MOM_diapyc_energy_req type, public :: diapyc_energy_req_CS ; private logical :: initialized = .false. !< A variable that is here because empty !! structures are not permitted by some compilers. - real :: test_Kh_scaling !< A scaling factor for the diapycnal diffusivity. - real :: ColHt_scaling !< A scaling factor for the column height change correction term. + real :: test_Kh_scaling !< A scaling factor for the diapycnal diffusivity [nondim] + real :: ColHt_scaling !< A scaling factor for the column height change correction term [nondim] + real :: VonKar !< The von Karman coefficient as used in this module [nondim] logical :: use_test_Kh_profile !< If true, use the internal test diffusivity profile in place of !! any that might be passed in as an argument. type(diag_ctrl), pointer :: diag => NULL() !< A structure that is used to @@ -104,7 +105,7 @@ subroutine diapyc_energy_req_test(h_3d, dt, tv, G, GV, US, CS, Kd_int) do K=2,nz tmp1 = h_top(K) * h_bot(K) * GV%H_to_Z Kd(K) = CS%test_Kh_scaling * & - ustar * 0.41 * (tmp1*ustar) / (absf*tmp1 + htot*ustar) + ustar * CS%VonKar * (tmp1*ustar) / (absf*tmp1 + htot*ustar) enddo endif may_print = is_root_PE() .and. (i==ie) .and. (j==je) @@ -1292,10 +1293,12 @@ subroutine diapyc_energy_req_init(Time, G, GV, US, param_file, diag, CS) call get_param(param_file, mdl, "ENERGY_REQ_COL_HT_SCALING", CS%ColHt_scaling, & "A scaling factor for the column height change correction "//& "used in testing the energy requirements.", default=1.0, units="nondim") - call get_param(param_file, mdl, "ENERGY_REQ_USE_TEST_PROFILE", & - CS%use_test_Kh_profile, & + call get_param(param_file, mdl, "ENERGY_REQ_USE_TEST_PROFILE", CS%use_test_Kh_profile, & "If true, use the internal test diffusivity profile in "//& "place of any that might be passed in as an argument.", default=.false.) + call get_param(param_file, mdl, 'VON_KARMAN_CONST', CS%vonKar, & + 'The value the von Karman constant as used for mixed layer viscosity.', & + units='nondim', default=0.41) CS%id_ERt = register_diag_field('ocean_model', 'EnReqTest_ERt', diag%axesZi, Time, & "Diffusivity Energy Requirements, top-down", & diff --git a/src/parameterizations/vertical/MOM_energetic_PBL.F90 b/src/parameterizations/vertical/MOM_energetic_PBL.F90 index 0e090b12e3..862f775225 100644 --- a/src/parameterizations/vertical/MOM_energetic_PBL.F90 +++ b/src/parameterizations/vertical/MOM_energetic_PBL.F90 @@ -36,8 +36,7 @@ module MOM_energetic_PBL logical :: initialized = .false. !< True if this control structure has been initialized. !/ Constants - real :: VonKar = 0.41 !< The von Karman coefficient. This should be a runtime parameter, - !! but because it is set to 0.4 at runtime in KPP it might change answers. + real :: VonKar !< The von Karman coefficient as used in the ePBL module [nondim] real :: omega !< The Earth's rotation rate [T-1 ~> s-1]. real :: omega_frac !< When setting the decay scale for turbulence, use this fraction of !! the absolute rotation rate blended with the local value of f, as @@ -1982,6 +1981,9 @@ subroutine energetic_PBL_init(Time, G, GV, US, param_file, diag, CS) "A nondimensional scaling factor controlling the inhibition "//& "of the diffusive length scale by rotation. Making this larger "//& "decreases the PBL diffusivity.", units="nondim", default=1.0) + call get_param(param_file, mdl, 'VON_KARMAN_CONST', CS%vonKar, & + 'The value the von Karman constant as used for mixed layer viscosity.', & + units='nondim', default=0.41) call get_param(param_file, mdl, "DEFAULT_ANSWER_DATE", default_answer_date, & "This sets the default value for the various _ANSWER_DATE parameters.", & default=99991231) diff --git a/src/parameterizations/vertical/MOM_set_diffusivity.F90 b/src/parameterizations/vertical/MOM_set_diffusivity.F90 index d7ac808217..6d35616b3a 100644 --- a/src/parameterizations/vertical/MOM_set_diffusivity.F90 +++ b/src/parameterizations/vertical/MOM_set_diffusivity.F90 @@ -73,6 +73,8 @@ module MOM_set_diffusivity !! added. logical :: use_LOTW_BBL_diffusivity !< If true, use simpler/less precise, BBL diffusivity. logical :: LOTW_BBL_use_omega !< If true, use simpler/less precise, BBL diffusivity. + real :: Von_Karm !< The von Karman constant as used in the BBL diffusivity calculation + !! [nondim]. See (http://en.wikipedia.org/wiki/Von_Karman_constant) real :: BBL_effic !< efficiency with which the energy extracted !! by bottom drag drives BBL diffusion [nondim] real :: cdrag !< quadratic drag coefficient [nondim] @@ -1406,7 +1408,6 @@ subroutine add_LOTW_BBL_diffusivity(h, u, v, tv, fluxes, visc, j, N2_int, Kd_int logical :: Rayleigh_drag ! Set to true if there are Rayleigh drag velocities defined in visc, on ! the assumption that this extracted energy also drives diapycnal mixing. integer :: i, k, km1 - real, parameter :: von_karm = 0.41 ! Von Karman constant (http://en.wikipedia.org/wiki/Von_Karman_constant) logical :: do_diag_Kd_BBL if (.not.(CS%bottomdraglaw .and. (CS%BBL_effic > 0.0))) return @@ -1483,7 +1484,7 @@ subroutine add_LOTW_BBL_diffusivity(h, u, v, tv, fluxes, visc, j, N2_int, Kd_int if ( ustar_D + absf * ( z_bot * D_minus_z ) == 0.) then Kd_wall = 0. else - Kd_wall = ((von_karm * ustar2) * (z_bot * D_minus_z)) & + Kd_wall = ((CS%von_karm * ustar2) * (z_bot * D_minus_z)) & / (ustar_D + absf * (z_bot * D_minus_z)) endif @@ -1995,6 +1996,7 @@ subroutine set_diffusivity_init(Time, G, GV, US, param_file, diag, CS, int_tide_ ! This include declares and sets the variable "version". # include "version_variable.h" character(len=40) :: mdl = "MOM_set_diffusivity" ! This module's name. + real :: vonKar ! The von Karman constant as used for mixed layer viscosity [nomdim] real :: omega_frac_dflt ! The default value for the fraction of the absolute rotation rate ! that is used in place of the absolute value of the local Coriolis ! parameter in the denominator of some expressions [nondim] @@ -2152,6 +2154,12 @@ subroutine set_diffusivity_init(Time, G, GV, US, param_file, diag, CS, int_tide_ call get_param(param_file, mdl, "LOTW_BBL_USE_OMEGA", CS%LOTW_BBL_use_omega, & "If true, use the maximum of Omega and N for the TKE to diffusion "//& "calculation. Otherwise, N is N.", default=.true.) + call get_param(param_file, mdl, 'VON_KARMAN_CONST', vonKar, & + 'The value the von Karman constant as used for mixed layer viscosity.', & + units='nondim', default=0.41) + call get_param(param_file, mdl, 'VON_KARMAN_BBL', CS%von_Karm, & + 'The value the von Karman constant as used in calculating the BBL diffusivity.', & + units='nondim', default=vonKar) endif else CS%use_LOTW_BBL_diffusivity = .false. ! This parameterization depends on a u* from viscous BBL diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index 67b629f0bb..f928327254 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -51,6 +51,7 @@ module MOM_vert_friction real :: Hbbl !< The static bottom boundary layer thickness [H ~> m or kg m-2]. real :: Kv_extra_bbl !< An extra vertical viscosity in the bottom boundary layer of thickness !! Hbbl when there is not a bottom drag law in use [Z2 T-1 ~> m2 s-1]. + real :: vonKar !< The von Karman constant as used for mixed layer viscosity [nomdim] real :: maxvel !< Velocity components greater than maxvel are truncated [L T-1 ~> m s-1]. real :: vel_underflow !< Velocity components smaller than vel_underflow @@ -1511,7 +1512,7 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, ! The viscosity in visc_ml is set to go to 0 at the mixed layer top and bottom ! (in a log-layer) and be further limited by rotation to give the natural Ekman length. temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z - ustar2_denom = (0.41 * u_star(i)**2) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) + ustar2_denom = (CS%vonKar * u_star(i)**2) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) visc_ml = temp1 * ustar2_denom ! Set the viscous coupling based on the model's vertical resolution. The omission of ! the I_amax factor here is consistent with answer dates above 20190101. @@ -1531,7 +1532,7 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, z_t(i) = z_t(i) + hvel(i,k-1) temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z - ustar2_denom = (0.41 * u_star(i)**2) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) + ustar2_denom = (CS%vonKar * u_star(i)**2) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) ! As a floor on the viscous coupling, assume that the length scale in the denominator can not ! be larger than the distance from the surface, consistent with a logarithmic velocity profile. @@ -1544,7 +1545,7 @@ subroutine find_coupling_coef(a_cpl, hvel, do_i, h_harm, bbl_thick, kv_bbl, z_i, temp1 = (z_t(i)*h_ml(i) - z_t(i)*z_t(i))*GV%H_to_Z ! This viscosity is set to go to 0 at the mixed layer top and bottom (in a log-layer) ! and be further limited by rotation to give the natural Ekman length. - visc_ml = u_star(i) * 0.41 * (temp1*u_star(i)) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) + visc_ml = u_star(i) * CS%vonKar * (temp1*u_star(i)) / (absf(i)*temp1 + (h_ml(i)+h_neglect)*u_star(i)) a_ml = visc_ml / (0.25*(hvel(i,k)+hvel(i,k-1) + h_neglect) * GV%H_to_Z + 0.5*I_amax*visc_ml) ! Choose the largest estimate of a_cpl, but these could be changed to be additive. @@ -1866,6 +1867,9 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "are large changes in the thicknesses of successive layers or when the "//& "viscosity is set externally and the wind stress has subsequently increased.", & default=.false.) + call get_param(param_file, mdl, 'VON_KARMAN_CONST', CS%vonKar, & + 'The value the von Karman constant as used for mixed layer viscosity.', & + units='nondim', default=0.41) call get_param(param_file, mdl, "U_TRUNC_FILE", CS%u_trunc_file, & "The absolute path to a file into which the accelerations "//& "leading to zonal velocity truncations are written. "//& From 974e1eacda3b8f3f7a87f8dae2805ecfd1d9823d Mon Sep 17 00:00:00 2001 From: Marshall Ward Date: Mon, 5 Sep 2022 14:03:16 -0400 Subject: [PATCH 35/68] Barotropic weight vectorization This patch modifies the calculation of barotropic weight functions wt_[uv] to a more vectorizable form. The if-blocks used to evaluate the piecewise function was replaced with a sequence of min/max evaluations, which are vectorizable. A nondimensional subroundoff term is also introducted to address the division by zero in the evaluation of 1 - 0.5 * Instep / visc_rem. When visc_rem is zero, this term will never be selected by the max() operator, since it is negatively asymptotic. When visc_rem is sufficiently large to accept this term, the value of subroundoff is too small to modify the significand of the floating point value. In our runs, the overall time of btstep was reduced by 6%. Experiment: benchmark Platform: AMD Ryzen 5 2600 Compiler: gfortran 12.2 Flags: -O3 -mavx Timer: (Ocean BT pre-calcs only) Before: - 1.781952 - 1.787916 - 1.790447 After: - 1.544491 - 1.553111 - 1.544491 Timer: (Ocean barotropic mode stepping) Before: - 4.042281 - 4.050225 - 4.051413 After: - 3.785838 - 3.822393 - 3.835511 Speedup: ~13% of "(Ocean BT pre-calc step)" ~6% speedup of btstep --- src/core/MOM_barotropic.F90 | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index 5a02f64240..83cc0b7d09 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -398,6 +398,10 @@ module MOM_barotropic character*(20), parameter :: BT_CONT_STRING = "FROM_BT_CONT" !>@} +!> A negligible parameter which avoids division by zero, but is too small to +!! modify physical values. +real, parameter :: subroundoff = 1e-30 + contains !> This subroutine time steps the barotropic equations explicitly. @@ -1001,23 +1005,23 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, !$OMP parallel do default(shared) private(visc_rem) do k=1,nz ; do j=js,je ; do I=is-1,ie - ! rem needs greater than visc_rem_u and 1-Instep/visc_rem_u. + ! rem needs to be greater than visc_rem_u and 1-Instep/visc_rem_u. ! The 0.5 below is just for safety. - if (visc_rem_u(I,j,k) <= 0.0) then ; visc_rem = 0.0 - elseif (visc_rem_u(I,j,k) >= 1.0) then ; visc_rem = 1.0 - elseif (visc_rem_u(I,j,k)**2 > visc_rem_u(I,j,k) - 0.5*Instep) then - visc_rem = visc_rem_u(I,j,k) - else ; visc_rem = 1.0 - 0.5*Instep/visc_rem_u(I,j,k) ; endif + ! NOTE: subroundoff is a neglible value used to prevent division by zero. + ! When 1-0.5*Instep/visc_rem exceeds visc_rem, the subroundoff is too small + ! to modify the significand. When visc_rem is small, the max() operators + ! select visc_rem or 0. So subroundoff cannot impact the final value. + visc_rem = min(visc_rem_u(I,j,k), 1.) + visc_rem = max(visc_rem, 1. - 0.5 * Instep / (visc_rem + subroundoff)) + visc_rem = max(visc_rem, 0.) wt_u(I,j,k) = CS%frhatu(I,j,k) * visc_rem enddo ; enddo ; enddo !$OMP parallel do default(shared) private(visc_rem) do k=1,nz ; do J=js-1,je ; do i=is,ie - ! rem needs greater than visc_rem_v and 1-Instep/visc_rem_v. - if (visc_rem_v(i,J,k) <= 0.0) then ; visc_rem = 0.0 - elseif (visc_rem_v(i,J,k) >= 1.0) then ; visc_rem = 1.0 - elseif (visc_rem_v(i,J,k)**2 > visc_rem_v(i,J,k) - 0.5*Instep) then - visc_rem = visc_rem_v(i,J,k) - else ; visc_rem = 1.0 - 0.5*Instep/visc_rem_v(i,J,k) ; endif + ! As above, rem must be greater than visc_rem_v and 1-Instep/visc_rem_v. + visc_rem = min(visc_rem_v(I,j,k), 1.) + visc_rem = max(visc_rem, 1. - 0.5 * Instep / (visc_rem + subroundoff)) + visc_rem = max(visc_rem, 0.) wt_v(i,J,k) = CS%frhatv(i,J,k) * visc_rem enddo ; enddo ; enddo From f0d668041a3288254c3f2bcd425581113513ddbd Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 12 Sep 2022 21:07:26 -0400 Subject: [PATCH 36/68] Avoid extra post_data calls from vertvisc Add calls to query_averaging_enabled to avoid unnecessary extra calls to post_data for diagnostics that will never be used in vertvisc and vertvisc_coef. All answers are bitwise identical, but the C.I. testing appears to fail because there are fewer calls to post_data. --- .../vertical/MOM_vert_friction.F90 | 108 +++++++++--------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index f928327254..668cee2c2f 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -6,7 +6,7 @@ module MOM_vert_friction use MOM_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr use MOM_diag_mediator, only : post_product_u, post_product_sum_u use MOM_diag_mediator, only : post_product_v, post_product_sum_v -use MOM_diag_mediator, only : diag_ctrl +use MOM_diag_mediator, only : diag_ctrl, query_averaging_enabled use MOM_debugging, only : uvchksum, hchksum use MOM_error_handler, only : MOM_error, FATAL, WARNING, NOTE use MOM_file_parser, only : get_param, log_param, log_version, param_file_type @@ -514,49 +514,51 @@ subroutine vertvisc(u, v, h, forces, visc, dt, OBC, ADp, CDp, G, GV, US, CS, & endif ! Offer diagnostic fields for averaging. - if (CS%id_du_dt_visc > 0) & - call post_data(CS%id_du_dt_visc, ADp%du_dt_visc, CS%diag) - if (CS%id_dv_dt_visc > 0) & - call post_data(CS%id_dv_dt_visc, ADp%dv_dt_visc, CS%diag) - if (present(taux_bot) .and. (CS%id_taux_bot > 0)) & - call post_data(CS%id_taux_bot, taux_bot, CS%diag) - if (present(tauy_bot) .and. (CS%id_tauy_bot > 0)) & - call post_data(CS%id_tauy_bot, tauy_bot, CS%diag) - if (CS%id_du_dt_str > 0) & - call post_data(CS%id_du_dt_str, ADp%du_dt_str, CS%diag) - if (CS%id_dv_dt_str > 0) & - call post_data(CS%id_dv_dt_str, ADp%dv_dt_str, CS%diag) - - if (associated(ADp%du_dt_visc) .and. associated(ADp%du_dt_visc)) then - ! Diagnostics of the fractional thicknesses times momentum budget terms - ! 3D diagnostics of hf_du(dv)_dt_visc are commented because there is no clarity on proper remapping grid option. - ! The code is retained for debugging purposes in the future. - !if (CS%id_hf_du_dt_visc > 0) & - ! call post_product_u(CS%id_hf_du_dt_visc, ADp%du_dt_visc, ADp%diag_hfrac_u, G, nz, CS%diag) - !if (CS%id_hf_dv_dt_visc > 0) & - ! call post_product_v(CS%id_hf_dv_dt_visc, ADp%dv_dt_visc, ADp%diag_hfrac_v, G, nz, CS%diag) - - ! Diagnostics for thickness-weighted vertically averaged viscous accelerations - if (CS%id_hf_du_dt_visc_2d > 0) & - call post_product_sum_u(CS%id_hf_du_dt_visc_2d, ADp%du_dt_visc, ADp%diag_hfrac_u, G, nz, CS%diag) - if (CS%id_hf_dv_dt_visc_2d > 0) & - call post_product_sum_v(CS%id_hf_dv_dt_visc_2d, ADp%dv_dt_visc, ADp%diag_hfrac_v, G, nz, CS%diag) - - ! Diagnostics for thickness x viscous accelerations - if (CS%id_h_du_dt_visc > 0) call post_product_u(CS%id_h_du_dt_visc, ADp%du_dt_visc, ADp%diag_hu, G, nz, CS%diag) - if (CS%id_h_dv_dt_visc > 0) call post_product_v(CS%id_h_dv_dt_visc, ADp%dv_dt_visc, ADp%diag_hv, G, nz, CS%diag) - endif + if (query_averaging_enabled(CS%diag)) then + if (CS%id_du_dt_visc > 0) & + call post_data(CS%id_du_dt_visc, ADp%du_dt_visc, CS%diag) + if (CS%id_dv_dt_visc > 0) & + call post_data(CS%id_dv_dt_visc, ADp%dv_dt_visc, CS%diag) + if (present(taux_bot) .and. (CS%id_taux_bot > 0)) & + call post_data(CS%id_taux_bot, taux_bot, CS%diag) + if (present(tauy_bot) .and. (CS%id_tauy_bot > 0)) & + call post_data(CS%id_tauy_bot, tauy_bot, CS%diag) + if (CS%id_du_dt_str > 0) & + call post_data(CS%id_du_dt_str, ADp%du_dt_str, CS%diag) + if (CS%id_dv_dt_str > 0) & + call post_data(CS%id_dv_dt_str, ADp%dv_dt_str, CS%diag) + + if (associated(ADp%du_dt_visc) .and. associated(ADp%du_dt_visc)) then + ! Diagnostics of the fractional thicknesses times momentum budget terms + ! 3D diagnostics of hf_du(dv)_dt_visc are commented because there is no clarity on proper remapping grid option. + ! The code is retained for debugging purposes in the future. + !if (CS%id_hf_du_dt_visc > 0) & + ! call post_product_u(CS%id_hf_du_dt_visc, ADp%du_dt_visc, ADp%diag_hfrac_u, G, nz, CS%diag) + !if (CS%id_hf_dv_dt_visc > 0) & + ! call post_product_v(CS%id_hf_dv_dt_visc, ADp%dv_dt_visc, ADp%diag_hfrac_v, G, nz, CS%diag) + + ! Diagnostics for thickness-weighted vertically averaged viscous accelerations + if (CS%id_hf_du_dt_visc_2d > 0) & + call post_product_sum_u(CS%id_hf_du_dt_visc_2d, ADp%du_dt_visc, ADp%diag_hfrac_u, G, nz, CS%diag) + if (CS%id_hf_dv_dt_visc_2d > 0) & + call post_product_sum_v(CS%id_hf_dv_dt_visc_2d, ADp%dv_dt_visc, ADp%diag_hfrac_v, G, nz, CS%diag) + + ! Diagnostics for thickness x viscous accelerations + if (CS%id_h_du_dt_visc > 0) call post_product_u(CS%id_h_du_dt_visc, ADp%du_dt_visc, ADp%diag_hu, G, nz, CS%diag) + if (CS%id_h_dv_dt_visc > 0) call post_product_v(CS%id_h_dv_dt_visc, ADp%dv_dt_visc, ADp%diag_hv, G, nz, CS%diag) + endif - if (associated(ADp%du_dt_str) .and. associated(ADp%dv_dt_str)) then - ! Diagnostics for thickness x wind stress accelerations - if (CS%id_h_du_dt_str > 0) call post_product_u(CS%id_h_du_dt_str, ADp%du_dt_str, ADp%diag_hu, G, nz, CS%diag) - if (CS%id_h_dv_dt_str > 0) call post_product_v(CS%id_h_dv_dt_str, ADp%dv_dt_str, ADp%diag_hv, G, nz, CS%diag) + if (associated(ADp%du_dt_str) .and. associated(ADp%dv_dt_str)) then + ! Diagnostics for thickness x wind stress accelerations + if (CS%id_h_du_dt_str > 0) call post_product_u(CS%id_h_du_dt_str, ADp%du_dt_str, ADp%diag_hu, G, nz, CS%diag) + if (CS%id_h_dv_dt_str > 0) call post_product_v(CS%id_h_dv_dt_str, ADp%dv_dt_str, ADp%diag_hv, G, nz, CS%diag) - ! Diagnostics for wind stress accelerations multiplied by visc_rem_[uv], - if (CS%id_du_dt_str_visc_rem > 0) & - call post_product_u(CS%id_du_dt_str_visc_rem, ADp%du_dt_str, ADp%visc_rem_u, G, nz, CS%diag) - if (CS%id_dv_dt_str_visc_rem > 0) & - call post_product_v(CS%id_dv_dt_str_visc_rem, ADp%dv_dt_str, ADp%visc_rem_v, G, nz, CS%diag) + ! Diagnostics for wind stress accelerations multiplied by visc_rem_[uv], + if (CS%id_du_dt_str_visc_rem > 0) & + call post_product_u(CS%id_du_dt_str_visc_rem, ADp%du_dt_str, ADp%visc_rem_u, G, nz, CS%diag) + if (CS%id_dv_dt_str_visc_rem > 0) & + call post_product_v(CS%id_dv_dt_str_visc_rem, ADp%dv_dt_str, ADp%visc_rem_v, G, nz, CS%diag) + endif endif end subroutine vertvisc @@ -1117,16 +1119,18 @@ subroutine vertvisc_coef(u, v, h, forces, visc, dt, G, GV, US, CS, OBC) endif ! Offer diagnostic fields for averaging. - if (associated(visc%Kv_slow) .and. (CS%id_Kv_slow > 0)) & - call post_data(CS%id_Kv_slow, visc%Kv_slow, CS%diag) - if (CS%id_Kv_u > 0) call post_data(CS%id_Kv_u, Kv_u, CS%diag) - if (CS%id_Kv_v > 0) call post_data(CS%id_Kv_v, Kv_v, CS%diag) - if (CS%id_au_vv > 0) call post_data(CS%id_au_vv, CS%a_u, CS%diag) - if (CS%id_av_vv > 0) call post_data(CS%id_av_vv, CS%a_v, CS%diag) - if (CS%id_h_u > 0) call post_data(CS%id_h_u, CS%h_u, CS%diag) - if (CS%id_h_v > 0) call post_data(CS%id_h_v, CS%h_v, CS%diag) - if (CS%id_hML_u > 0) call post_data(CS%id_hML_u, hML_u, CS%diag) - if (CS%id_hML_v > 0) call post_data(CS%id_hML_v, hML_v, CS%diag) + if (query_averaging_enabled(CS%diag)) then + if (associated(visc%Kv_slow) .and. (CS%id_Kv_slow > 0)) & + call post_data(CS%id_Kv_slow, visc%Kv_slow, CS%diag) + if (CS%id_Kv_u > 0) call post_data(CS%id_Kv_u, Kv_u, CS%diag) + if (CS%id_Kv_v > 0) call post_data(CS%id_Kv_v, Kv_v, CS%diag) + if (CS%id_au_vv > 0) call post_data(CS%id_au_vv, CS%a_u, CS%diag) + if (CS%id_av_vv > 0) call post_data(CS%id_av_vv, CS%a_v, CS%diag) + if (CS%id_h_u > 0) call post_data(CS%id_h_u, CS%h_u, CS%diag) + if (CS%id_h_v > 0) call post_data(CS%id_h_v, CS%h_v, CS%diag) + if (CS%id_hML_u > 0) call post_data(CS%id_hML_u, hML_u, CS%diag) + if (CS%id_hML_v > 0) call post_data(CS%id_hML_v, hML_v, CS%diag) + endif if (allocated(hML_u)) deallocate(hML_u) if (allocated(hML_v)) deallocate(hML_v) From fae71c4c7eb3c3c340ca2aa0daad81f27007b849 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Wed, 14 Sep 2022 13:45:32 -0400 Subject: [PATCH 37/68] *Change defaults for KV_ML_INVZ2 and 3 bug flags Updated the default values for KV_ML_INVZ2, BAROTROPIC_TIDAL_SAL_BUG, LAYER_Z_INIT_IC_EXTRAP_BUG, and KAPPA_SHEAR_VERTEX_PSURF_BUG. Also completed obsoleting of HENYEY_IGW_BACKGROUND_NEW. This PR will change answers in some cases, unless the MOM_input files already included lines with the following settings, or otherwise explicitly set these parameters: KV_ML_INVZ2 = 0.0 BAROTROPIC_TIDAL_SAL_BUG = False LAYER_Z_INIT_IC_EXTRAP_BUG = False KAPPA_SHEAR_VERTEX_PSURF_BUG = False For each of these parameters, there is another parameter (such as BULKMIXEDLAYER or TIDES) whose value determines whether they might be used in a particular case. To determine whether or why answers for a particular configuration might change with this PR, compare the MOM_parameter_doc.all files from equivalent runs before and after these code changes to determine whether any of these parameters are taking the default value. This commit could change answers in some cases that use default values for these parameters, and the entries in some MOM_parameter_doc files will change. The answers in the MOM6-examples test suite are bitwise identical, due to some entries that were recently added to the MOM_input files for these cases. --- src/core/MOM_barotropic.F90 | 2 +- .../MOM_state_initialization.F90 | 2 +- .../vertical/MOM_bkgnd_mixing.F90 | 23 +------------------ .../vertical/MOM_kappa_shear.F90 | 2 +- .../vertical/MOM_vert_friction.F90 | 9 ++++---- 5 files changed, 8 insertions(+), 30 deletions(-) diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index 83cc0b7d09..5aceee34ef 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -4476,7 +4476,7 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, "If true, the tidal self-attraction and loading anomaly in the barotropic "//& "solver has the wrong sign, replicating a long-standing bug with a scalar "//& "self-attraction and loading term or the SAL term from a previous simulation.", & - default=.true., do_not_log=(det_de==0.0)) + default=.false., do_not_log=(det_de==0.0)) call get_param(param_file, mdl, "SADOURNY", CS%Sadourny, & "If true, the Coriolis terms are discretized with the "//& "Sadourny (1975) energy conserving scheme, otherwise "//& diff --git a/src/initialization/MOM_state_initialization.F90 b/src/initialization/MOM_state_initialization.F90 index 58a6f9ed8d..0eac15f6d7 100644 --- a/src/initialization/MOM_state_initialization.F90 +++ b/src/initialization/MOM_state_initialization.F90 @@ -2651,7 +2651,7 @@ subroutine MOM_temp_salt_initialize_from_Z(h, tv, depth_tot, G, GV, US, PF, just "If true use an expression with a vertical indexing bug for extrapolating the "//& "densities at the bottom of unstable profiles from data when finding the "//& "initial interface locations in layered mode from a dataset of T and S.", & - default=.true., do_not_log=just_read) + default=.false., do_not_log=just_read) ! Reusing MINIMUM_DEPTH for the default mixed layer depth may be a strange choice, but ! it reproduces previous answers. endif diff --git a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 index 179dfa470c..ba47f281e8 100644 --- a/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 +++ b/src/parameterizations/vertical/MOM_bkgnd_mixing.F90 @@ -80,20 +80,6 @@ module MOM_bkgnd_mixing !! Henyey et al, JGR (1986) latitudinal scaling for the background diapycnal diffusivity, !! which gives a marked decrease in the diffusivity near the equator. The simplification !! here is to assume that the in-situ stratification is the same as the reference stratificaiton. - logical :: Henyey_IGW_background_new !< same as Henyey_IGW_background - !! but incorporate the effect of stratification on TKE dissipation, - !! e = f/f_0 * acosh(N/f) / acosh(N_0/f_0) * e_0 - !! where e is the TKE dissipation, and N_0 and f_0 - !! are the reference buoyancy frequency and inertial frequencies respectively. - !! e_0 is the reference dissipation at (N_0,f_0). In the previous version, N=N_0. - !! Additionally, the squared inverse relationship between diapycnal diffusivities - !! and stratification is included: - !! - !! kd = e/N^2 - !! - !! where kd is the diapycnal diffusivity. This approach assumes that work done - !! against gravity is uniformly distributed throughout the column. Whereas, kd=kd_0*e, - !! as in the original version, concentrates buoyancy work in regions of strong stratification. logical :: physical_OBL_scheme !< If true, a physically-based scheme is used to determine mixing in the !! ocean's surface boundary layer, such as ePBL, KPP, or a refined bulk mixed layer scheme. logical :: Kd_via_Kdml_bug !< If true and KDML /= KD and a number of other higher precedence @@ -276,13 +262,6 @@ subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS, physical_OBL "Harrison & Hallberg, JPO 2008.", default=.false.) if (CS%Henyey_IGW_background) call check_bkgnd_scheme(CS, "HENYEY_IGW_BACKGROUND") - - call get_param(param_file, mdl, "HENYEY_IGW_BACKGROUND_NEW", CS%Henyey_IGW_background_new, & - "If true, use a better latitude-dependent scaling for the "//& - "background diffusivity, as described in "//& - "Harrison & Hallberg, JPO 2008. This option is obsolete.", default=.false.) - if (CS%Henyey_IGW_background_new) call check_bkgnd_scheme(CS, "HENYEY_IGW_BACKGROUND_NEW") - if (CS%Kd>0.0 .and. (trim(CS%bkgnd_scheme_str)=="BRYAN_LEWIS_DIFFUSIVITY" .or.& trim(CS%bkgnd_scheme_str)=="HORIZ_VARYING_BACKGROUND" )) then call MOM_error(WARNING, "bkgnd_mixing_init: a nonzero constant background "//& @@ -321,7 +300,7 @@ subroutine bkgnd_mixing_init(Time, G, GV, US, param_file, diag, CS, physical_OBL call get_param(param_file, mdl, "KD_BACKGROUND_VIA_KDML_BUG", CS%Kd_via_Kdml_bug, & "If true and KDML /= KD and several other conditions apply, the background "//& "diffusivity is set incorrectly using a bug that was introduced in March, 2018.", & - default=.false.) ! The default should be changed to false and this parameter obsoleted. + default=.false.) ! This parameter should be obsoleted. endif ! call closeParameterBlock(param_file) diff --git a/src/parameterizations/vertical/MOM_kappa_shear.F90 b/src/parameterizations/vertical/MOM_kappa_shear.F90 index f7673b347d..118ec9a1a1 100644 --- a/src/parameterizations/vertical/MOM_kappa_shear.F90 +++ b/src/parameterizations/vertical/MOM_kappa_shear.F90 @@ -1858,7 +1858,7 @@ function kappa_shear_init(Time, G, GV, US, param_file, diag, CS) call get_param(param_file, mdl, "KAPPA_SHEAR_VERTEX_PSURF_BUG", CS%psurf_bug, & "If true, do a simple average of the cell surface pressures to get a pressure "//& "at the corner if VERTEX_SHEAR=True. Otherwise mask out any land points in "//& - "the average.", default=.true., do_not_log=(just_read .or. (.not.CS%KS_at_vertex))) + "the average.", default=.false., do_not_log=(just_read .or. (.not.CS%KS_at_vertex))) call get_param(param_file, mdl, "KAPPA_SHEAR_ITER_BUG", CS%dKdQ_iteration_bug, & "If true, use an older, dimensionally inconsistent estimate of the "//& diff --git a/src/parameterizations/vertical/MOM_vert_friction.F90 b/src/parameterizations/vertical/MOM_vert_friction.F90 index 668cee2c2f..dff879d83e 100644 --- a/src/parameterizations/vertical/MOM_vert_friction.F90 +++ b/src/parameterizations/vertical/MOM_vert_friction.F90 @@ -1789,7 +1789,6 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & ! Local variables - real :: Kv_dflt ! A default viscosity [m2 s-1]. real :: Kv_BBL ! A viscosity in the bottom boundary layer with a simple scheme [Z2 T-1 ~> m2 s-1]. real :: Hmix_m ! A boundary layer thickness [m]. integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. @@ -1917,7 +1916,7 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & call get_param(param_file, mdl, "KV", CS%Kv, & "The background kinematic viscosity in the interior. "//& "The molecular value, ~1e-6 m2 s-1, may be used.", & - units="m2 s-1", fail_if_missing=.true., scale=US%m2_s_to_Z2_T, unscaled=Kv_dflt) + units="m2 s-1", fail_if_missing=.true., scale=US%m2_s_to_Z2_T) CS%Kvml_invZ2 = 0.0 if (GV%nkml < 1) then @@ -1932,11 +1931,11 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & if (CS%Kvml_invZ2 < 0.0) then call get_param(param_file, mdl, "KVML", CS%Kvml_invZ2, & "The scale for an extra kinematic viscosity in the mixed layer", & - units="m2 s-1", default=Kv_dflt, scale=US%m2_s_to_Z2_T, do_not_log=.true.) + units="m2 s-1", default=0.0, scale=US%m2_s_to_Z2_T, do_not_log=.true.) if (CS%Kvml_invZ2 >= 0.0) & call MOM_error(WARNING, "KVML is a deprecated parameter. Use KV_ML_INVZ2 instead.") endif - if (CS%Kvml_invZ2 < 0.0) CS%Kvml_invZ2 = CS%Kv ! Change this default later to 0.0. + if (CS%Kvml_invZ2 < 0.0) CS%Kvml_invZ2 = 0.0 call log_param(param_file, mdl, "KV_ML_INVZ2", US%Z2_T_to_m2_s*CS%Kvml_invZ2, & "An extra kinematic viscosity in a mixed layer of thickness HMIX_FIXED, "//& "with the actual viscosity scaling as 1/(z*HMIX_FIXED)^2, where z is the "//& @@ -1944,7 +1943,7 @@ subroutine vertvisc_init(MIS, Time, G, GV, US, param_file, diag, ADp, dirs, & "transmitted through infinitesimally thin surface layers. This is an "//& "older option for numerical convenience without a strong physical basis, "//& "and its use is now discouraged.", & - units="m2 s-1", default=Kv_dflt) + units="m2 s-1", default=0.0) endif if (.not.CS%bottomdraglaw) then From be04ce631d6a026d1549208d89d22c3c9ea19224 Mon Sep 17 00:00:00 2001 From: He Wang Date: Sun, 18 Sep 2022 13:18:25 -0400 Subject: [PATCH 38/68] Correct a couple of typos in BT solver * Description of wt_eta related variables * Longname of PFu_BT --- src/core/MOM_barotropic.F90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index 5aceee34ef..d098036d98 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -680,18 +680,18 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, real, allocatable :: wt_vel(:) ! The raw or relative weights of each of the barotropic timesteps ! in determining the average velocities [nondim] real, allocatable :: wt_eta(:) ! The raw or relative weights of each of the barotropic timesteps - ! in determining the average the average of eta [nondim] + ! in determining the average eta [nondim] real, allocatable :: wt_accel(:) ! The raw or relative weights of each of the barotropic timesteps ! in determining the average accelerations [nondim] real, allocatable :: wt_trans(:) ! The raw or relative weights of each of the barotropic timesteps ! in determining the average transports [nondim] real, allocatable :: wt_accel2(:) ! A potentially un-normalized copy of wt_accel [nondim] real :: sum_wt_vel ! The sum of the raw weights used to find average velocities [nondim] - real :: sum_wt_eta ! The sum of the raw weights used to find average the average of eta [nondim] + real :: sum_wt_eta ! The sum of the raw weights used to find average eta [nondim] real :: sum_wt_accel ! The sum of the raw weights used to find average accelerations [nondim] real :: sum_wt_trans ! The sum of the raw weights used to find average transports [nondim] real :: I_sum_wt_vel ! The inverse of the sum of the raw weights used to find average velocities [nondim] - real :: I_sum_wt_eta ! The inverse of the sum of the raw weights used to find the average of eta [nondim] + real :: I_sum_wt_eta ! The inverse of the sum of the raw weights used to find eta [nondim] real :: I_sum_wt_accel ! The inverse of the sum of the raw weights used to find average accelerations [nondim] real :: I_sum_wt_trans ! The inverse of the sum of the raw weights used to find average transports [nondim] real :: dt_filt ! The half-width of the barotropic filter [T ~> s]. @@ -4788,7 +4788,7 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, endif CS%id_PFu_bt = register_diag_field('ocean_model', 'PFuBT', diag%axesCu1, Time, & - 'Zonal Anomalous Barotropic Pressure Force Force Acceleration', 'm s-2', conversion=US%L_T2_to_m_s2) + 'Zonal Anomalous Barotropic Pressure Force Acceleration', 'm s-2', conversion=US%L_T2_to_m_s2) CS%id_PFv_bt = register_diag_field('ocean_model', 'PFvBT', diag%axesCv1, Time, & 'Meridional Anomalous Barotropic Pressure Force Acceleration', 'm s-2', conversion=US%L_T2_to_m_s2) CS%id_Coru_bt = register_diag_field('ocean_model', 'CoruBT', diag%axesCu1, Time, & From 4ef14d6e3ae88206dd32f9fdac5d2563997ca94d Mon Sep 17 00:00:00 2001 From: He Wang Date: Sun, 18 Sep 2022 16:42:57 -0400 Subject: [PATCH 39/68] Log actual BT halo size values Logging BT halo size parameters should happen after the modification in clone_MOM_domain, where they are lower-bounded by the domain halo. This commit fixes this and therefore may result a change of parameters in MOM_parameter_doc.layout in past runs. --- src/core/MOM_barotropic.F90 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index d098036d98..b576a91ea4 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -4396,13 +4396,6 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, #else wd_halos(1) = bt_halo_sz; wd_halos(2) = bt_halo_sz #endif - call log_param(param_file, mdl, "!BT x-halo", wd_halos(1), & - "The barotropic x-halo size that is actually used.", & - layoutParam=.true.) - call log_param(param_file, mdl, "!BT y-halo", wd_halos(2), & - "The barotropic y-halo size that is actually used.", & - layoutParam=.true.) - call get_param(param_file, mdl, "NONLINEAR_BT_CONTINUITY", CS%Nonlinear_continuity, & "If true, use nonlinear transports in the barotropic "//& "continuity equation. This does not apply if "//& @@ -4623,6 +4616,12 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, call MOM_mesg("barotropic_init: barotropic y-halo size increased.", 3) endif #endif + call log_param(param_file, mdl, "!BT x-halo", wd_halos(1), & + "The barotropic x-halo size that is actually used.", & + layoutParam=.true.) + call log_param(param_file, mdl, "!BT y-halo", wd_halos(2), & + "The barotropic y-halo size that is actually used.", & + layoutParam=.true.) CS%isdw = G%isc-wd_halos(1) ; CS%iedw = G%iec+wd_halos(1) CS%jsdw = G%jsc-wd_halos(2) ; CS%jedw = G%jec+wd_halos(2) From d88c58a300cd703f829c8396086f445408a41e8a Mon Sep 17 00:00:00 2001 From: He Wang Date: Sun, 18 Sep 2022 22:11:43 -0400 Subject: [PATCH 40/68] Fix hifreq BT steps diagnostics This commit fixes the conflicting timestamps for diagnostics at the BT step level (do_hifreq_output=True). Instead of the actual dtbt, a nominal time interval is used to "squeeze" output at all nstep+nfilter steps into a time interval of a single baroclinic timestep. --- src/core/MOM_barotropic.F90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index b576a91ea4..67f07db3f3 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -630,6 +630,8 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, real :: visc_rem ! A work variable that may equal visc_rem_[uv] [nondim] real :: vel_prev ! The previous velocity [L T-1 ~> m s-1]. real :: dtbt ! The barotropic time step [T ~> s]. + real :: dtbt_diag ! The nominal barotropic time step used in hifreq diagnostics [T ~> s]. + ! dtbt_diag = dt/(nstep+nfilter) real :: bebt ! A copy of CS%bebt [nondim]. real :: be_proj ! The fractional amount by which velocities are projected ! when project_velocity is true [nondim]. For now be_proj is set @@ -1694,6 +1696,8 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, if (nstep+nfilter==0 ) call MOM_error(FATAL, & "btstep: number of barotropic step (nstep+nfilter) is 0") + dtbt_diag = dt/(nstep+nfilter) + ! Set up the normalized weights for the filtered velocity. sum_wt_vel = 0.0 ; sum_wt_eta = 0.0 ; sum_wt_accel = 0.0 ; sum_wt_trans = 0.0 allocate(wt_vel(nstep+nfilter)) ; allocate(wt_eta(nstep+nfilter)) @@ -2354,7 +2358,7 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, !$OMP end parallel if (do_hifreq_output) then - time_step_end = time_bt_start + real_to_time(n*US%T_to_s*dtbt) + time_step_end = time_bt_start + real_to_time(n*US%T_to_s*dtbt_diag) call enable_averages(dtbt, time_step_end, CS%diag) if (CS%id_ubt_hifreq > 0) call post_data(CS%id_ubt_hifreq, ubt(IsdB:IedB,jsd:jed), CS%diag) if (CS%id_vbt_hifreq > 0) call post_data(CS%id_vbt_hifreq, vbt(isd:ied,JsdB:JedB), CS%diag) From e8ec93910248ae9eb0299a69ffdbd14b57b7f1df Mon Sep 17 00:00:00 2001 From: Marshall Ward Date: Thu, 7 Jul 2022 17:25:49 -0400 Subject: [PATCH 41/68] FMS2: open_ASCII_file and open_namelist_file This patch re-implements the FMS2 implementations of `open_ASCII_file` and `open_namelist_file` to remove their dependency on FMS1 functions which have been staged for deletion. Note that if a file is opened with `mpp_open` but closed with `close_file_unit`, then it will raise an error in `fms_io_exit`. This will no longer be an issue after all references to `mpp_open` have been removed. But in the meantime, we will need to ensure that all unit-based `close_file` calls were not opened with `mpp_open`. There is also a minor patch to `.testing/Makefile` which selects the framework ("infra") source dependency, rather than hard-set to FMS1. --- .testing/Makefile | 19 +++- config_src/infra/FMS2/MOM_io_infra.F90 | 132 ++++++++++++++++++++++--- 2 files changed, 136 insertions(+), 15 deletions(-) diff --git a/.testing/Makefile b/.testing/Makefile index 2bd9c6f39d..530a552181 100644 --- a/.testing/Makefile +++ b/.testing/Makefile @@ -182,14 +182,25 @@ endif SOURCE = \ $(foreach ext,F90 inc c h,$(wildcard $(1)/*/*.$(ext) $(1)/*/*/*.$(ext))) -MOM_SOURCE = $(call SOURCE,../src) \ - $(wildcard ../config_src/infra/FMS1/*.F90) \ +MOM_SOURCE = \ + $(call SOURCE,../src) \ $(wildcard ../config_src/drivers/solo_driver/*.F90) \ $(wildcard ../config_src/ext*/*/*.F90) -TARGET_SOURCE = $(call SOURCE,build/target_codebase/src) \ - $(wildcard build/target_codebase/config_src/infra/FMS1/*.F90) \ + +TARGET_SOURCE = \ + $(call SOURCE,build/target_codebase/src) \ $(wildcard build/target_codebase/config_src/drivers/solo_driver/*.F90) \ $(wildcard build/target_codebase/config_src/ext*/*.F90) + +# NOTE: Current default framework is FMS1, but this could change. +ifeq ($(FRAMEWORK), fms2) + MOM_SOURCE +=$(wildcard ../config_src/infra/FMS2/*.F90) + TARGET_SOURCE += $(wildcard build/target_codebase/config_src/infra/FMS2/*.F90) +else + MOM_SOURCE += $(wildcard ../config_src/infra/FMS1/*.F90) + TARGET_SOURCE += $(wildcard build/target_codebase/config_src/infra/FMS1/*.F90) +endif + FMS_SOURCE = $(call SOURCE,deps/fms/src) diff --git a/config_src/infra/FMS2/MOM_io_infra.F90 b/config_src/infra/FMS2/MOM_io_infra.F90 index c8c55524f8..dc8a9af3d5 100644 --- a/config_src/infra/FMS2/MOM_io_infra.F90 +++ b/config_src/infra/FMS2/MOM_io_infra.F90 @@ -19,7 +19,7 @@ module MOM_io_infra use fms2_io_mod, only : get_global_io_domain_indices use fms_io_utils_mod, only : fms2_file_exist => file_exists -use fms_mod, only : write_version_number, open_namelist_file, check_nml_error +use fms_mod, only : write_version_number, check_nml_error use fms_io_mod, only : file_exist, field_exist, field_size, read_data use fms_io_mod, only : fms_io_exit, get_filename_appendix use mpp_domains_mod, only : mpp_get_compute_domain, mpp_get_global_domain @@ -31,6 +31,8 @@ module MOM_io_infra use mpp_io_mod, only : mpp_get_info, mpp_get_times use mpp_io_mod, only : mpp_io_init use mpp_mod, only : stdout_if_root=>stdout +use mpp_mod, only : mpp_pe, mpp_root_pe, mpp_npes +use mpp_mod, only : mpp_get_current_pelist_name ! These are encoding constants. use mpp_io_mod, only : APPEND_FILE=>MPP_APPEND, WRITEONLY_FILE=>MPP_WRONLY use mpp_io_mod, only : OVERWRITE_FILE=>MPP_OVERWR, READONLY_FILE=>MPP_RDONLY @@ -205,10 +207,20 @@ end subroutine close_file_type !> closes a file. If the unit does not point to an open file, !! close_file_unit simply returns without doing anything. -subroutine close_file_unit(unit) - integer, intent(inout) :: unit !< The I/O unit for the file to be closed - - call mpp_close(unit) +subroutine close_file_unit(iounit) + integer, intent(inout) :: iounit !< The I/O unit for the file to be closed + + logical :: unit_is_open + + ! NOTE: Files opened by `mpp_open` must be closed by `mpp_close`. Otherwise, + ! an error will occur during `fms_io_exit`. + ! + ! Since there is no way to check if `fms_io_init` was called, we are forced + ! to visually confirm that the input unit was not created by `mpp_open`. + ! + ! After `mpp_open` has been removed, this message can be deleted. + inquire(iounit, opened=unit_is_open) + if (unit_is_open) close(iounit) end subroutine close_file_unit !> Ensure that the output stream associated with a file handle is fully sent to disk. @@ -242,10 +254,30 @@ subroutine io_infra_end() end subroutine io_infra_end !> Open a single namelist file that is potentially readable by all PEs. -function MOM_namelist_file(file) result(unit) - character(len=*), optional, intent(in) :: file !< The file to open, by default "input.nml". - integer :: unit !< The opened unit number of the namelist file - unit = open_namelist_file(file) +function MOM_namelist_file(filepath) result(iounit) + character(len=*), optional, intent(in) :: filepath + !< The file to open, by default "input.nml". + integer :: iounit + !< The opened unit number of the namelist file + + character(len=:), allocatable :: nmlpath + ! Namelist path + character(len=:), allocatable :: nmlpath_pe + ! Hypothetical namelist path exclusive to the current PE list + + if (present(filepath)) then + nmlpath = trim(filepath) + else + ! FMS1 first checks for a namelist unique to the PE list, `input_{}.nml`. + ! If not found, it defaults to `input.nml`. + nmlpath_pe = 'input_' // trim(mpp_get_current_pelist_name()) // '.nml' + if (file_exists(nmlpath_pe)) then + nmlpath = nmlpath_pe + else + nmlpath = 'input.nml' + endif + endif + call open_ASCII_file(iounit, nmlpath, action=READONLY_FILE) end function MOM_namelist_file !> Checks the iostat argument that is returned after reading a namelist variable and writes a @@ -403,9 +435,87 @@ subroutine open_ASCII_file(unit, file, action, threading, fileset) !! to threading=MULTIPLE write to the same file (SINGLE_FILE) !! or to one file per PE (MULTIPLE, the default). - call mpp_open(unit, file, action=action, form=ASCII_FILE, threading=threading, fileset=fileset, & - nohdrs=.true.) + integer :: action_flag + integer :: threading_flag + integer :: fileset_flag + logical :: exists + logical :: is_open + character(len=6) :: action_arg, position_arg + character(len=:), allocatable :: filename + + ! NOTE: This function is written to emulate the original behavior of mpp_open + ! from the FMS1 library, on which the MOM API is still based. Much of this + ! can be removed if we choose to drop this compatibility, but for now we + ! try to retain as much as possible. + + ! NOTE: Default FMS1 I/O settings are summarized below. + ! + ! access: Fortran and mpp_open default to SEQUENTIAL. + ! form: The Fortran and mpp_open default (for MPP_ASCII) is FORMATTED. + ! recl: mpp_open uses Fortran defaults when unset, so can be ignored. + ! ios: FMS1 allowed this to be caught, but we do not support it. + ! action/position: In mpp_open, these are inferred from `action`. + ! + ! MOM flag FMS1 flag action position + ! -------- -------- ------ -------- + ! READONLY_FILE MPP_RDONLY READ REWIND + ! WRITEONLY_FILE MPP_WRONLY WRITE REWIND + ! OVERWRITE_FILE MPP_OVERWR WRITE REWIND + ! APPEND_FILE MPP_APPEND WRITE APPEND + ! + ! From this, we can omit `access`, `form`, and `recl`, and can construct + ! `action` and `position` from the input arguments. + + ! I/O configuration + + action_flag = WRITEONLY_FILE + if (present(action)) action_flag = action + + action_arg = 'write' + if (action_flag == READONLY_FILE) action_arg = 'read' + + position_arg = 'rewind' + if (action_flag == APPEND_FILE) position_arg = 'append' + + ! Threading configuration + + threading_flag = SINGLE_FILE + if (present(threading)) threading_flag = threading + + fileset_flag = MULTIPLE + if (present(fileset)) fileset_flag = fileset + + ! Force fileset to be consistent with threading (as in FMS1) + if (threading_flag == SINGLE_FILE) fileset_flag = SINGLE_FILE + + ! Construct the distributed filename, if needed + filename = file + if (fileset_flag == MULTIPLE) then + if (mpp_npes() > 10000) then + write(filename, '(a,".",i6.6)') trim(filename), mpp_pe() - mpp_root_pe() + else + write(filename, '(a,".",i4.4)') trim(filename), mpp_pe() - mpp_root_pe() + endif + endif + + inquire(file=filename, exist=exists) + if (exists .and. action_flag == WRITEONLY_FILE) & + call MOM_error(WARNING, 'open_ASCII_file: File ' // trim(filename) // & + ' opened WRITEONLY already exists!') + + open(newunit=unit, file=filename, action=trim(action_arg), & + position=trim(position_arg)) + + ! This checks if open() failed but did not raise a runtime error. + inquire(unit, opened=is_open) + if (.not. is_open) & + call MOM_error(FATAL, 'open_ASCII_file: File ' // trim(filename) // & + ' failed to open.') + ! NOTE: There are two possible mpp_write_meta functions in FMS1: + ! - call mpp_write_meta( unit, 'filename', cval=mpp_file(unit)%name) + ! - call mpp_write_meta( unit, 'NumFilesInSet', ival=nfiles) + ! I'm not convinced we actually want these, but note them here in case. end subroutine open_ASCII_file From 2e9c21e528371c997767df0ecff8c25a6f512531 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Tue, 13 Sep 2022 05:18:57 -0400 Subject: [PATCH 42/68] +Add only_read_from_restarts Added the new overloaded interface only_read_from_restarts to allow for variables that are not registered to be stored in restart files to be read from the restart files. Versions of this routine that work on 2d, 3d or 4d arrays and pairs of 3d arrays have been added for now, although the pattern should be clear enough if other sizes of arrays are needed. They use the new private routine find_var_in_restart_files to determine whether a named variable is in the restart files and the full path to the appropriate file. These routines allow for the contents of the restart files to be gracefully altered, while preserving the ability to use restart files with the previous format. All answers are bitwise identical, but there is a new public interface. --- src/framework/MOM_restart.F90 | 203 +++++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 3 deletions(-) diff --git a/src/framework/MOM_restart.F90 b/src/framework/MOM_restart.F90 index 6eba9be727..a76e96499f 100644 --- a/src/framework/MOM_restart.F90 +++ b/src/framework/MOM_restart.F90 @@ -4,12 +4,12 @@ module MOM_restart ! This file is part of MOM6. See LICENSE.md for the license. use MOM_checksums, only : chksum => rotated_field_chksum -use MOM_domains, only : PE_here, num_PEs +use MOM_domains, only : PE_here, num_PEs, AGRID, BGRID_NE, CGRID_NE use MOM_error_handler, only : MOM_error, FATAL, WARNING, NOTE, is_root_pe use MOM_file_parser, only : get_param, log_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type use MOM_io, only : create_file, file_type, fieldtype, file_exists, open_file, close_file -use MOM_io, only : MOM_read_data, read_data, MOM_write_field, read_field_chksum +use MOM_io, only : MOM_read_data, read_data, MOM_write_field, read_field_chksum, field_exists use MOM_io, only : get_file_info, get_file_fields, get_field_atts, get_file_times use MOM_io, only : vardesc, var_desc, query_vardesc, modify_vardesc, get_filename_appendix use MOM_io, only : MULTIPLE, READONLY_FILE, SINGLE_FILE @@ -22,7 +22,7 @@ module MOM_restart implicit none ; private public restart_init, restart_end, restore_state, register_restart_field -public save_restart, query_initialized, set_initialized +public save_restart, query_initialized, set_initialized, only_read_from_restarts public restart_registry_lock, restart_init_end, vardesc public restart_files_exist, determine_is_new_run, is_new_run public register_restart_field_as_obsolete, register_restart_pair @@ -144,6 +144,16 @@ module MOM_restart module procedure set_initialized_3d_name, set_initialized_4d_name end interface +!> Read optional variables from restart files. +interface only_read_from_restarts + module procedure only_read_restart_field_4d + module procedure only_read_restart_field_3d + module procedure only_read_restart_field_2d +! module procedure only_read_restart_field_1d +! module procedure only_read_restart_field_0d + module procedure only_read_restart_pair_3d +end interface + contains !> Register a restart field as obsolete @@ -1042,6 +1052,193 @@ subroutine set_initialized_4d_name(f_ptr, name, CS) end subroutine set_initialized_4d_name +!====================== only_read_from_restarts variants ======================= + +!> Try to read a named 4-d field from the restart files +subroutine only_read_restart_field_4d(varname, f_ptr, G, CS, position, filename, directory, success, scale) + character(len=*), intent(in) :: varname !< The variable name to be used in the restart file + real, dimension(:,:,:,:), intent(inout) :: f_ptr !< The array for the field to be read + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure + type(MOM_restart_CS), intent(in) :: CS !< MOM restart control struct + integer, optional, intent(in) :: position !< A coded integer indicating the horizontal + !! position of this variable + character(len=*), optional, intent(in) :: filename !< The list of restart file names or a single + !! character 'r' to read automatically named files + character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files. + logical, optional, intent(out) :: success !< True if the field was read successfully + real, optional, intent(in) :: scale !< A factor by which the field will be scaled + + ! Local variables + character(len=:), allocatable :: file_path ! The full path to the file with the variable + logical :: found ! True if the variable was found. + logical :: is_global ! True if the variable is in a global file. + + found = find_var_in_restart_files(varname, G, CS, file_path, filename, directory, is_global) + + if (found) then + call MOM_read_data(file_path, varname, f_ptr, G%domain, timelevel=1, position=position, & + scale=scale, global_file=is_global) + endif + if (present(success)) success = found + +end subroutine only_read_restart_field_4d + +!> Try to read a named 3-d field from the restart files +subroutine only_read_restart_field_3d(varname, f_ptr, G, CS, position, filename, directory, success, scale) + character(len=*), intent(in) :: varname !< The variable name to be used in the restart file + real, dimension(:,:,:), intent(inout) :: f_ptr !< The array for the field to be read + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure + type(MOM_restart_CS), intent(in) :: CS !< MOM restart control struct + integer, optional, intent(in) :: position !< A coded integer indicating the horizontal + !! position of this variable + character(len=*), optional, intent(in) :: filename !< The list of restart file names or a single + !! character 'r' to read automatically named files + character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files. + logical, optional, intent(out) :: success !< True if the field was read successfully + real, optional, intent(in) :: scale !< A factor by which the field will be scaled + + ! Local variables + character(len=:), allocatable :: file_path ! The full path to the file with the variable + logical :: found ! True if the variable was found. + logical :: is_global ! True if the variable is in a global file. + + found = find_var_in_restart_files(varname, G, CS, file_path, filename, directory, is_global) + + if (found) then + call MOM_read_data(file_path, varname, f_ptr, G%domain, timelevel=1, position=position, & + scale=scale, global_file=is_global) + endif + if (present(success)) success = found + +end subroutine only_read_restart_field_3d + +!> Try to read a named 2-d field from the restart files +subroutine only_read_restart_field_2d(varname, f_ptr, G, CS, position, filename, directory, success, scale) + character(len=*), intent(in) :: varname !< The variable name to be used in the restart file + real, dimension(:,:), intent(inout) :: f_ptr !< The array for the field to be read + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure + type(MOM_restart_CS), intent(in) :: CS !< MOM restart control struct + integer, optional, intent(in) :: position !< A coded integer indicating the horizontal + !! position of this variable + character(len=*), optional, intent(in) :: filename !< The list of restart file names or a single + !! character 'r' to read automatically named files + character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files. + logical, optional, intent(out) :: success !< True if the field was read successfully + real, optional, intent(in) :: scale !< A factor by which the field will be scaled + + ! Local variables + character(len=:), allocatable :: file_path ! The full path to the file with the variable + logical :: found ! True if the variable was found. + logical :: is_global ! True if the variable is in a global file. + + found = find_var_in_restart_files(varname, G, CS, file_path, filename, directory, is_global) + + if (found) then + call MOM_read_data(file_path, varname, f_ptr, G%domain, timelevel=1, position=position, & + scale=scale, global_file=is_global) + endif + if (present(success)) success = found + +end subroutine only_read_restart_field_2d + + +!> Try to read a named 3-d field from the restart files +subroutine only_read_restart_pair_3d(a_ptr, b_ptr, a_name, b_name, G, CS, & + stagger, filename, directory, success, scale) + real, dimension(:,:,:), intent(inout) :: a_ptr !< The array for the first field to be read + real, dimension(:,:,:), intent(inout) :: b_ptr !< The array for the second field to be read + character(len=*), intent(in) :: a_name !< The first variable name to be used in the restart file + character(len=*), intent(in) :: b_name !< The second variable name to be used in the restart file + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure + type(MOM_restart_CS), intent(in) :: CS !< MOM restart control struct + integer, optional, intent(in) :: stagger !< A coded integer indicating the horizontal + !! position of this pair of variables + character(len=*), optional, intent(in) :: filename !< The list of restart file names or a single + !! character 'r' to read automatically named files + character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files. + logical, optional, intent(out) :: success !< True if the field was read successfully + real, optional, intent(in) :: scale !< A factor by which the field will be scaled + + ! Local variables + character(len=:), allocatable :: file_path_a ! The full path to the file with the first variable + character(len=:), allocatable :: file_path_b ! The full path to the file with the second variable + integer :: a_pos, b_pos ! A coded position for the two variables. + logical :: a_found, b_found ! True if the variables were found. + logical :: global_a, global_b ! True if the variables are in global files. + + a_found = find_var_in_restart_files(a_name, G, CS, file_path_a, filename, directory, global_a) + b_found = find_var_in_restart_files(b_name, G, CS, file_path_b, filename, directory, global_b) + + a_pos = EAST_FACE ; b_pos = NORTH_FACE + if (present(stagger)) then ; select case (stagger) + case (AGRID) ; a_pos = CENTER ; b_pos = CENTER + case (BGRID_NE) ; a_pos = CORNER ; b_pos = CORNER + case (CGRID_NE) ; a_pos = EAST_FACE ; b_pos = NORTH_FACE + case default ; a_pos = EAST_FACE ; b_pos = NORTH_FACE + end select ; endif + + if (a_found .and. b_found) then + call MOM_read_data(file_path_a, a_name, a_ptr, G%domain, timelevel=1, position=a_pos, & + scale=scale, global_file=global_b, file_may_be_4d=.true.) + call MOM_read_data(file_path_b, b_name, b_ptr, G%domain, timelevel=1, position=b_pos, & + scale=scale, global_file=global_b, file_may_be_4d=.true.) + endif + if (present(success)) success = (a_found .and. b_found) + +end subroutine only_read_restart_pair_3d + +!> Return an indicationof whether the named variable is the restart files, and provie the full path +!! to the restart file in which a variable is found. +function find_var_in_restart_files(varname, G, CS, file_path, filename, directory, is_global) result (found) + character(len=*), intent(in) :: varname !< The variable name to be used in the restart file + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure + type(MOM_restart_CS), intent(in) :: CS !< MOM restart control struct + character(len=:), allocatable, intent(out) :: file_path !< The full path to the file in which the + !! variable is found + character(len=*), optional, intent(in) :: filename !< The list of restart file names or a single + !! character 'r' to read automatically named files + character(len=*), optional, intent(in) :: directory !< The directory in which to seek restart files. + logical, optional, intent(out) :: is_global !< True if the file is global. + logical :: found !< True if the named variable was found in the restart files. + + ! Local variables + character(len=240), allocatable, dimension(:) :: file_paths ! The possible file names. + character(len=:), allocatable :: dir ! The directory to read from. + character(len=:), allocatable :: fname ! The list of file names. + logical, allocatable, dimension(:) :: global_file ! True if the file is global + integer :: n, num_files + + dir = "./INPUT/" ; if (present(directory)) dir = trim(directory) + + ! Set the default return values. + found = .false. + file_path = "" + if (present(is_global)) is_global = .false. + + fname = 'r' + if (present(filename)) then + if (.not.((LEN_TRIM(filename) == 1) .and. (filename(1:1) == 'F'))) fname = filename + endif + + num_files = get_num_restart_files(fname, dir, G, CS) + if (num_files == 0) return + allocate(file_paths(num_files), global_file(num_files)) + num_files = open_restart_units(fname, dir, G, CS, file_paths=file_paths, global_files=global_file) + + do n=1,num_files ; if (field_exists(file_paths(n), varname, MOM_Domain=G%domain)) then + found = .true. + file_path = file_paths(n) + if (present(is_global)) is_global = global_file(n) + exit + endif ; enddo + + deallocate(file_paths, global_file) + +end function find_var_in_restart_files + +!====================== end of the only_read_from_restarts variants ======================= + + !> save_restart saves all registered variables to restart files. subroutine save_restart(directory, time, G, CS, time_stamped, filename, GV, num_rest_files, write_IC) character(len=*), intent(in) :: directory !< The directory where the restart files From 5201c34a3151a46dc9e18bb4489af32a6cb79052 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Tue, 13 Sep 2022 05:19:45 -0400 Subject: [PATCH 43/68] +Add new runtime parameter STORE_CORIOLIS_ACCEL Added the option to store the Coriolis and advective accelerations from the end of one split RK2 dynamics time step for use in the predictor phase of the next time step, in order to allow for a future commit that will properly remap these accelerations in ALE mode. This change is controlled by the new runtime parameter STORE_CORIOLIS_ACCEL, and it leads to changes in the fields that are saved in the restart files. This commit also includes the necessary changes to allow the restart files written in one mode to be read in the other and give bitwise identical restarts. Because the next step's predictor CAu and CAv accelerations now have to coexist with the previous step's CAu and CAv accelerations, which are used for derived diagnostics (like energy budget terms), the predictor CAu and CAv terms have to be stored in new variables, CS%CAu_pred and CS%CAv_pred, and there is a new accel_diag_ptrs type in the dyn_split_RK2 control structure to diagnose truncation errors arising in the predictor step. Several comments have also been modified to document the dimensions of variables. All answers and diagnostics are bitwise identical. --- src/core/MOM_dynamics_split_RK2.F90 | 258 ++++++++++++++++++++-------- 1 file changed, 183 insertions(+), 75 deletions(-) diff --git a/src/core/MOM_dynamics_split_RK2.F90 b/src/core/MOM_dynamics_split_RK2.F90 index f06c692eaf..d95c6feea3 100644 --- a/src/core/MOM_dynamics_split_RK2.F90 +++ b/src/core/MOM_dynamics_split_RK2.F90 @@ -21,16 +21,17 @@ module MOM_dynamics_split_RK2 use MOM_domains, only : To_South, To_West, To_All, CGRID_NE, SCALAR_PAIR use MOM_domains, only : To_North, To_East, Omit_Corners use MOM_domains, only : create_group_pass, do_group_pass, group_pass_type -use MOM_domains, only : start_group_pass, complete_group_pass, pass_var +use MOM_domains, only : start_group_pass, complete_group_pass, pass_var, pass_vector use MOM_debugging, only : hchksum, uvchksum use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, WARNING, is_root_pe use MOM_error_handler, only : MOM_set_verbosity, callTree_showQuery use MOM_error_handler, only : callTree_enter, callTree_leave, callTree_waypoint use MOM_file_parser, only : get_param, log_version, param_file_type use MOM_get_input, only : directories -use MOM_io, only : vardesc, var_desc +use MOM_io, only : vardesc, var_desc, EAST_FACE, NORTH_FACE use MOM_restart, only : register_restart_field, register_restart_pair use MOM_restart, only : query_initialized, set_initialized, save_restart +use MOM_restart, only : only_read_from_restarts use MOM_restart, only : restart_init, is_new_run, MOM_restart_CS use MOM_time_manager, only : time_type, time_type_to_real, operator(+) use MOM_time_manager, only : operator(-), operator(>), operator(*), operator(/) @@ -77,12 +78,14 @@ module MOM_dynamics_split_RK2 type, public :: MOM_dyn_split_RK2_CS ; private real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_,NKMEM_) :: & CAu, & !< CAu = f*v - u.grad(u) [L T-2 ~> m s-2] + CAu_pred, & !< The predictor step value of CAu = f*v - u.grad(u) [L T-2 ~> m s-2] PFu, & !< PFu = -dM/dx [L T-2 ~> m s-2] PFu_Stokes, & !< PFu_Stokes = -d/dx int_r (u_L*duS/dr) [L T-2 ~> m s-2] diffu !< Zonal acceleration due to convergence of the along-isopycnal stress tensor [L T-2 ~> m s-2] real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_,NKMEM_) :: & CAv, & !< CAv = -f*u - u.grad(v) [L T-2 ~> m s-2] + CAv_pred, & !< The predictor step value of CAv = -f*u - u.grad(v) [L T-2 ~> m s-2] PFv, & !< PFv = -dM/dy [L T-2 ~> m s-2] PFv_Stokes, & !< PFv_Stokes = -d/dy int_r (v_L*dvS/dr) [L T-2 ~> m s-2] diffv !< Meridional acceleration due to convergence of the along-isopycnal stress tensor [L T-2 ~> m s-2] @@ -91,7 +94,7 @@ module MOM_dynamics_split_RK2 !< Both the fraction of the zonal momentum originally in a !! layer that remains after a time-step of viscosity, and the !! fraction of a time-step worth of a barotropic acceleration - !! that a layer experiences after viscosity is applied. + !! that a layer experiences after viscosity is applied [nondim]. !! Nondimensional between 0 (at the bottom) and 1 (far above). real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_,NKMEM_) :: u_accel_bt !< The zonal layer accelerations due to the difference between @@ -101,7 +104,7 @@ module MOM_dynamics_split_RK2 !< Both the fraction of the meridional momentum originally in !! a layer that remains after a time-step of viscosity, and the !! fraction of a time-step worth of a barotropic acceleration - !! that a layer experiences after viscosity is applied. + !! that a layer experiences after viscosity is applied [nondim]. !! Nondimensional between 0 (at the bottom) and 1 (far above). real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_,NKMEM_) :: v_accel_bt !< The meridional layer accelerations due to the difference between @@ -150,6 +153,12 @@ module MOM_dynamics_split_RK2 !! barotropic solver. logical :: calc_dtbt !< If true, calculate the barotropic time-step !! dynamically. + logical :: store_CAu !< If true, store the Coriolis and advective accelerations at the + !! end of the timestep for use in the next predictor step. + logical :: CAu_pred_stored !< If true, the Coriolis and advective accelerations at the + !! end of the timestep have been stored for use in the next + !! predictor step. This is used to accomodate various generations + !! of restart files. real :: be !< A nondimensional number from 0.5 to 1 that controls !! the backward weighting of the time stepping scheme [nondim] @@ -191,13 +200,16 @@ module MOM_dynamics_split_RK2 integer :: id_u_BT_accel_visc_rem = -1, id_v_BT_accel_visc_rem = -1 !>@} - type(diag_ctrl), pointer :: diag !< A structure that is used to regulate the + type(diag_ctrl), pointer :: diag => NULL() !< A structure that is used to regulate the !! timing of diagnostic output. - type(accel_diag_ptrs), pointer :: ADp !< A structure pointing to the various + type(accel_diag_ptrs), pointer :: ADp => NULL() !< A structure pointing to the various !! accelerations in the momentum equations, !! which can later be used to calculate !! derived diagnostics like energy budgets. - type(cont_diag_ptrs), pointer :: CDp !< A structure with pointers to various + type(accel_diag_ptrs), pointer :: AD_pred => NULL() !< A structure pointing to the various + !! predictor step accelerations in the momentum equations, + !! which can be used to debug truncations. + type(cont_diag_ptrs), pointer :: CDp => NULL() !< A structure with pointers to various !! terms in the continuity equations, !! which can later be used to calculate !! derived diagnostics like energy budgets. @@ -304,28 +316,30 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s ! local variables real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: up ! Predicted zonal velocity [L T-1 ~> m s-1]. real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: vp ! Predicted meridional velocity [L T-1 ~> m s-1]. - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: hp ! Predicted thickness [H ~> m or kg m-2]. + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: hp ! Predicted thickness [H ~> m or kg m-2]. real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: ueffA ! Effective Area of U-Faces [H L ~> m2] real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: veffA ! Effective Area of V-Faces [H L ~> m2] - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: u_bc_accel - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: v_bc_accel - ! u_bc_accel and v_bc_accel are the summed baroclinic accelerations of each - ! layer calculated by the non-barotropic part of the model [L T-2 ~> m s-2]. - - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), target :: uh_in - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), target :: vh_in - ! uh_in and vh_in are the zonal or meridional mass transports that would be - ! obtained using the initial velocities [H L2 T-1 ~> m3 s-1 or kg s-1]. - - real, dimension(SZI_(G),SZJ_(G)) :: eta_pred - real, dimension(SZI_(G),SZJ_(G)) :: deta_dt - ! eta_pred is the predictor value of the free surface height or column mass, - ! [H ~> m or kg m-2]. - - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: u_old_rad_OBC - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: v_old_rad_OBC - ! u_old_rad_OBC and v_old_rad_OBC are the starting velocities, which are - ! saved for use in the Flather open boundary condition code [L T-1 ~> m s-1]. + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: u_bc_accel ! The summed zonal baroclinic accelerations + ! of each layer calculated by the non-barotropic + ! part of the model [L T-2 ~> m s-2] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: v_bc_accel ! The summed meridional baroclinic accelerations + ! of each layer calculated by the non-barotropic + ! part of the model [L T-2 ~> m s-2] + + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), target :: uh_in ! The zonal mass transports that would be + ! obtained using the initial velocities [H L2 T-1 ~> m3 s-1 or kg s-1] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), target :: vh_in ! The meridional mass transports that would be + ! obtained using the initial velocities [H L2 T-1 ~> m3 s-1 or kg s-1] + + real, dimension(SZI_(G),SZJ_(G)) :: eta_pred ! The predictor value of the free surface height + ! or column mass [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G)) :: deta_dt ! A diagnostic of the time derivative of the free surface + ! height or column mass [H T-1 ~> m s-1 or kg m-2 s-1] + + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)) :: u_old_rad_OBC ! The starting zonal velocities, which are + ! saved for use in the Flather open boundary condition code [L T-1 ~> m s-1] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)) :: v_old_rad_OBC ! The starting meridional velocities, which are + ! saved for use in the Flather open boundary condition code [L T-1 ~> m s-1] real :: pres_to_eta ! A factor that converts pressures to the units of eta ! [H T2 R-1 L-2 ~> m Pa-1 or kg m-2 Pa-1] @@ -351,7 +365,7 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s h_av ! The layer thickness time-averaged over a time step [H ~> m or kg m-2]. real :: dt_pred ! The time step for the predictor part of the baroclinic time stepping [T ~> s]. - real :: Idt_bc ! Inverse of the baroclinic timestep + real :: Idt_bc ! Inverse of the baroclinic timestep [T-1 ~> s-1] logical :: dyn_p_surf logical :: BT_cont_BT_thick ! If true, use the BT_cont_type to estimate the @@ -368,9 +382,9 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB u_av => CS%u_av ; v_av => CS%v_av ; h_av => CS%h_av ; eta => CS%eta - Idt_bc = 1./dt + Idt_bc = 1.0 / dt - sym=.false.;if (G%Domain%symmetric) sym=.true. ! switch to include symmetric domain in checksums + sym = G%Domain%symmetric ! switch to include symmetric domain in checksums showCallTree = callTree_showQuery() if (showCallTree) call callTree_enter("step_MOM_dyn_split_RK2(), MOM_dynamics_split_RK2.F90") @@ -494,21 +508,25 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s call start_group_pass(CS%pass_eta, G%Domain, clock=id_clock_pass) ! CAu = -(f+zeta_av)/h_av vh + d/dx KE_av - call cpu_clock_begin(id_clock_Cor) - call CorAdCalc(u_av, v_av, h_av, uh, vh, CS%CAu, CS%CAv, CS%OBC, CS%ADp, & - G, Gv, US, CS%CoriolisAdv, pbv, Waves=Waves) - call cpu_clock_end(id_clock_Cor) - if (showCallTree) call callTree_wayPoint("done with CorAdCalc (step_MOM_dyn_split_RK2)") + if (.not.CS%CAu_pred_stored) then + ! Calculate a predictor-step estimate of the Coriolis and momentum advection terms, + ! if it was not already stored from the end of the previous time step. + call cpu_clock_begin(id_clock_Cor) + call CorAdCalc(u_av, v_av, h_av, uh, vh, CS%CAu_pred, CS%CAv_pred, CS%OBC, CS%AD_pred, & + G, GV, US, CS%CoriolisAdv, pbv, Waves=Waves) + call cpu_clock_end(id_clock_Cor) + if (showCallTree) call callTree_wayPoint("done with CorAdCalc (step_MOM_dyn_split_RK2)") + endif ! u_bc_accel = CAu + PFu + diffu(u[n-1]) call cpu_clock_begin(id_clock_btforce) !$OMP parallel do default(shared) do k=1,nz do j=js,je ; do I=Isq,Ieq - u_bc_accel(I,j,k) = (CS%CAu(I,j,k) + CS%PFu(I,j,k)) + CS%diffu(I,j,k) + u_bc_accel(I,j,k) = (CS%CAu_pred(I,j,k) + CS%PFu(I,j,k)) + CS%diffu(I,j,k) enddo ; enddo do J=Jsq,Jeq ; do i=is,ie - v_bc_accel(i,J,k) = (CS%CAv(i,J,k) + CS%PFv(i,J,k)) + CS%diffv(i,J,k) + v_bc_accel(i,J,k) = (CS%CAv_pred(i,J,k) + CS%PFv(i,J,k)) + CS%diffv(i,J,k) enddo ; enddo enddo if (associated(CS%OBC)) then @@ -517,10 +535,10 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s call cpu_clock_end(id_clock_btforce) if (CS%debug) then - call MOM_accel_chksum("pre-btstep accel", CS%CAu, CS%CAv, CS%PFu, CS%PFv, & + call MOM_accel_chksum("pre-btstep accel", CS%CAu_pred, CS%CAv_pred, CS%PFu, CS%PFv, & CS%diffu, CS%diffv, G, GV, US, CS%pbce, u_bc_accel, v_bc_accel, & symmetric=sym) - call check_redundant("pre-btstep CS%Ca ", CS%Cau, CS%Cav, G) + call check_redundant("pre-btstep CS%CA ", CS%CAu_pred, CS%CAv_pred, G) call check_redundant("pre-btstep CS%PF ", CS%PFu, CS%PFv, G) call check_redundant("pre-btstep CS%diff ", CS%diffu, CS%diffv, G) call check_redundant("pre-btstep u_bc_accel ", u_bc_accel, v_bc_accel, G) @@ -591,6 +609,7 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s if (calc_dtbt) call set_dtbt(G, GV, US, CS%barotropic_CSp, eta, CS%pbce) if (showCallTree) call callTree_enter("btstep(), MOM_barotropic.F90") ! This is the predictor step call to btstep. + ! The CS%ADp argument here stores the weights for certain integrated diagnostics. call btstep(u, v, eta, dt, u_bc_accel, v_bc_accel, forces, CS%pbce, CS%eta_PF, u_av, v_av, & CS%u_accel_bt, CS%v_accel_bt, eta_pred, CS%uhbt, CS%vhbt, G, GV, US, & CS%barotropic_CSp, CS%visc_rem_u, CS%visc_rem_v, CS%ADp, CS%OBC, CS%BT_cont, & @@ -621,7 +640,7 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s call uvchksum("Predictor 1 [uv]h", uh, vh, G%HI,haloshift=2, & symmetric=sym, scale=GV%H_to_m*US%L_to_m**2*US%s_to_T) ! call MOM_state_chksum("Predictor 1", up, vp, h, uh, vh, G, GV, US, haloshift=1) - call MOM_accel_chksum("Predictor accel", CS%CAu, CS%CAv, CS%PFu, CS%PFv, & + call MOM_accel_chksum("Predictor accel", CS%CAu_pred, CS%CAv_pred, CS%PFu, CS%PFv, & CS%diffu, CS%diffv, G, GV, US, CS%pbce, CS%u_accel_bt, CS%v_accel_bt, symmetric=sym) call MOM_state_chksum("Predictor 1 init", u, v, h, uh, vh, G, GV, US, haloshift=2, & symmetric=sym) @@ -637,7 +656,7 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s endif call vertvisc_coef(up, vp, h, forces, visc, dt_pred, G, GV, US, CS%vertvisc_CSp, & CS%OBC) - call vertvisc(up, vp, h, forces, visc, dt_pred, CS%OBC, CS%ADp, CS%CDp, G, & + call vertvisc(up, vp, h, forces, visc, dt_pred, CS%OBC, CS%AD_pred, CS%CDp, G, & GV, US, CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot, waves=waves) if (showCallTree) call callTree_wayPoint("done with vertvisc (step_MOM_dyn_split_RK2)") if (G%nonblocking_updates) then @@ -796,7 +815,7 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s call MOM_accel_chksum("corr pre-btstep accel", CS%CAu, CS%CAv, CS%PFu, CS%PFv, & CS%diffu, CS%diffv, G, GV, US, CS%pbce, u_bc_accel, v_bc_accel, & symmetric=sym) - call check_redundant("corr pre-btstep CS%Ca ", CS%Cau, CS%Cav, G) + call check_redundant("corr pre-btstep CS%CA ", CS%CAu, CS%CAv, G) call check_redundant("corr pre-btstep CS%PF ", CS%PFu, CS%PFv, G) call check_redundant("corr pre-btstep CS%diff ", CS%diffu, CS%diffv, G) call check_redundant("corr pre-btstep u_bc_accel ", u_bc_accel, v_bc_accel, G) @@ -923,6 +942,23 @@ subroutine step_MOM_dyn_split_RK2(u, v, h, tv, visc, Time_local, dt, forces, p_s enddo ; enddo enddo + if (CS%store_CAu) then + ! Calculate a predictor-step estimate of the Coriolis and momentum advection terms + ! for use in the next time step, possibly after it has been vertically remapped. + call cpu_clock_begin(id_clock_Cor) + call disable_averaging(CS%diag) ! These calculations should not be used for diagnostics. + ! CAu = -(f+zeta_av)/h_av vh + d/dx KE_av + call CorAdCalc(u_av, v_av, h_av, uh, vh, CS%CAu_pred, CS%CAv_pred, CS%OBC, CS%AD_pred, & + G, GV, US, CS%CoriolisAdv, pbv, Waves=Waves) + CS%CAu_pred_stored = .true. + call enable_averages(dt, Time_local, CS%diag) ! Reenable the averaging + call cpu_clock_end(id_clock_Cor) + if (showCallTree) call callTree_wayPoint("done with CorAdCalc (step_MOM_dyn_split_RK2)") + else + CS%CAu_pred_stored = .false. + endif + + ! The time-averaged free surface height has already been set by the last call to btstep. ! Deallocate this memory to avoid a memory leak. ### We should revisit how this array is declared. -RWH @@ -1045,8 +1081,9 @@ subroutine register_restarts_dyn_split_RK2(HI, GV, US, param_file, CS, restart_C real, dimension(SZI_(HI),SZJB_(HI),SZK_(GV)), & target, intent(inout) :: vh !< merid volume or mass transport [H L2 T-1 ~> m3 s-1 or kg s-1] - type(vardesc) :: vd(2) - character(len=48) :: thickness_units, flux_units + character(len=40) :: mdl = "MOM_dynamics_split_RK2" ! This module's name. + type(vardesc) :: vd(2) + character(len=48) :: thickness_units, flux_units integer :: isd, ied, jsd, jed, nz, IsdB, IedB, JsdB, JedB @@ -1065,6 +1102,8 @@ subroutine register_restarts_dyn_split_RK2(HI, GV, US, param_file, CS, restart_C ALLOC_(CS%diffv(isd:ied,JsdB:JedB,nz)) ; CS%diffv(:,:,:) = 0.0 ALLOC_(CS%CAu(IsdB:IedB,jsd:jed,nz)) ; CS%CAu(:,:,:) = 0.0 ALLOC_(CS%CAv(isd:ied,JsdB:JedB,nz)) ; CS%CAv(:,:,:) = 0.0 + ALLOC_(CS%CAu_pred(IsdB:IedB,jsd:jed,nz)) ; CS%CAu_pred(:,:,:) = 0.0 + ALLOC_(CS%CAv_pred(isd:ied,JsdB:JedB,nz)) ; CS%CAv_pred(:,:,:) = 0.0 ALLOC_(CS%PFu(IsdB:IedB,jsd:jed,nz)) ; CS%PFu(:,:,:) = 0.0 ALLOC_(CS%PFv(isd:ied,JsdB:JedB,nz)) ; CS%PFv(:,:,:) = 0.0 @@ -1076,6 +1115,11 @@ subroutine register_restarts_dyn_split_RK2(HI, GV, US, param_file, CS, restart_C thickness_units = get_thickness_units(GV) flux_units = get_flux_units(GV) + call get_param(param_file, mdl, "STORE_CORIOLIS_ACCEL", CS%store_CAu, & + "If true, calculate the Coriolis accelerations at the end of each "//& + "timestep for use in the predictor step of the next split RK2 timestep.", & + default=.true., do_not_log=.true.) + if (GV%Boussinesq) then call register_restart_field(CS%eta, "sfc", .false., restart_CS, & longname="Free surface Height", units=thickness_units, conversion=GV%H_to_mks) @@ -1084,18 +1128,27 @@ subroutine register_restarts_dyn_split_RK2(HI, GV, US, param_file, CS, restart_C longname="Bottom Pressure", units=thickness_units, conversion=GV%H_to_mks) endif + ! These are needed, either to calculate CAu and CAv or to calculate the velocity anomalies in + ! the barotropic solver's Coriolis terms. vd(1) = var_desc("u2", "m s-1", "Auxiliary Zonal velocity", 'u', 'L') vd(2) = var_desc("v2", "m s-1", "Auxiliary Meridional velocity", 'v', 'L') call register_restart_pair(CS%u_av, CS%v_av, vd(1), vd(2), .false., restart_CS, & conversion=US%L_T_to_m_s) - call register_restart_field(CS%h_av, "h2", .false., restart_CS, & + if (CS%store_CAu) then + vd(1) = var_desc("CAu", "m s-2", "Zonal Coriolis and advactive acceleration", 'u', 'L') + vd(2) = var_desc("CAv", "m s-2", "Meridional Coriolis and advactive acceleration", 'v', 'L') + call register_restart_pair(CS%CAu_pred, CS%CAv_pred, vd(1), vd(2), .false., restart_CS, & + conversion=US%L_T2_to_m_s2) + else + call register_restart_field(CS%h_av, "h2", .false., restart_CS, & longname="Auxiliary Layer Thickness", units=thickness_units, conversion=GV%H_to_mks) - vd(1) = var_desc("uh", flux_units, "Zonal thickness flux", 'u', 'L') - vd(2) = var_desc("vh", flux_units, "Meridional thickness flux", 'v', 'L') - call register_restart_pair(uh, vh, vd(1), vd(2), .false., restart_CS, & - conversion=GV%H_to_MKS*US%L_to_m**2*US%s_to_T) + vd(1) = var_desc("uh", flux_units, "Zonal thickness flux", 'u', 'L') + vd(2) = var_desc("vh", flux_units, "Meridional thickness flux", 'v', 'L') + call register_restart_pair(uh, vh, vd(1), vd(2), .false., restart_CS, & + conversion=GV%H_to_MKS*US%L_to_m**2*US%s_to_T) + endif vd(1) = var_desc("diffu", "m s-2", "Zonal horizontal viscous acceleration", 'u', 'L') vd(2) = var_desc("diffv", "m s-2", "Meridional horizontal viscous acceleration", 'v', 'L') @@ -1172,6 +1225,7 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param ! restart file to the internal representation in this run [various units ~> 1] type(group_pass_type) :: pass_av_h_uvh logical :: use_tides, debug_truncations + logical :: read_uv, read_h2 integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed, nz integer :: IsdB, IedB, JsdB, JedB @@ -1217,6 +1271,10 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param "If true, use the summed layered fluxes plus an "//& "adjustment due to the change in the barotropic velocity "//& "in the barotropic continuity equation.", default=.true.) + call get_param(param_file, mdl, "STORE_CORIOLIS_ACCEL", CS%store_CAu, & + "If true, calculate the Coriolis accelerations at the end of each "//& + "timestep for use in the predictor step of the next split RK2 timestep.", & + default=.true.) call get_param(param_file, mdl, "DEBUG", CS%debug, & "If true, write out verbose debugging data.", & default=.false., debuggingParam=.true.) @@ -1262,6 +1320,15 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param Accel_diag%u_accel_bt => CS%u_accel_bt Accel_diag%v_accel_bt => CS%v_accel_bt + allocate(CS%AD_pred) + CS%AD_pred%diffu => CS%diffu + CS%AD_pred%diffv => CS%diffv + CS%AD_pred%PFu => CS%PFu + CS%AD_pred%PFv => CS%PFv + CS%AD_pred%CAu => CS%CAu_pred + CS%AD_pred%CAv => CS%CAv_pred + CS%AD_pred%u_accel_bt => CS%u_accel_bt + CS%AD_pred%v_accel_bt => CS%v_accel_bt ! Accel_diag%pbce => CS%pbce ! Accel_diag%u_accel_bt => CS%u_accel_bt ; Accel_diag%v_accel_bt => CS%v_accel_bt @@ -1348,38 +1415,77 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param do k=1,nz ; do J=JsdB,JedB ; do i=isd,ied ; CS%v_av(i,J,k) = vel_rescale * CS%v_av(i,J,k) ; enddo ; enddo ; enddo endif - ! This call is just here to initialize uh and vh. - if (.not. query_initialized(uh, "uh", restart_CS) .or. & - .not. query_initialized(vh, "vh", restart_CS)) then - do k=1,nz ; do j=jsd,jed ; do i=isd,ied ; h_tmp(i,j,k) = h(i,j,k) ; enddo ; enddo ; enddo - call continuity(u, v, h, h_tmp, uh, vh, dt, G, GV, US, CS%continuity_CSp, CS%OBC, pbv) - call pass_var(h_tmp, G%Domain, clock=id_clock_pass_init) - do k=1,nz ; do j=jsd,jed ; do i=isd,ied - CS%h_av(i,j,k) = 0.5*(h(i,j,k) + h_tmp(i,j,k)) - enddo ; enddo ; enddo - call set_initialized(uh, "uh", restart_CS) - call set_initialized(vh, "vh", restart_CS) - call set_initialized(CS%h_av, "h2", restart_CS) + if (CS%store_CAu) then + if (query_initialized(CS%CAu_pred, "CAu", restart_CS) .and. & + query_initialized(CS%CAv_pred, "CAv", restart_CS)) then + CS%CAu_pred_stored = .true. + else + call only_read_from_restarts(uh, vh, 'uh', 'vh', G, restart_CS, stagger=CGRID_NE, & + filename=dirs%input_filename, directory=dirs%restart_input_dir, & + success=read_uv, scale=US%m_to_L**2*US%T_to_s/GV%H_to_mks) + call only_read_from_restarts('h2', CS%h_av, G, restart_CS, & + filename=dirs%input_filename, directory=dirs%restart_input_dir, & + success=read_h2, scale=1.0/GV%H_to_mks) + if (read_uv .and. read_h2) then + call pass_var(CS%h_av, G%Domain, clock=id_clock_pass_init) + else + do k=1,nz ; do j=jsd,jed ; do i=isd,ied ; h_tmp(i,j,k) = h(i,j,k) ; enddo ; enddo ; enddo + call continuity(CS%u_av, CS%v_av, h, h_tmp, uh, vh, dt, G, GV, US, CS%continuity_CSp, CS%OBC, pbv) + call pass_var(h_tmp, G%Domain, clock=id_clock_pass_init) + do k=1,nz ; do j=jsd,jed ; do i=isd,ied + CS%h_av(i,j,k) = 0.5*(h(i,j,k) + h_tmp(i,j,k)) + enddo ; enddo ; enddo + endif + call pass_vector(CS%u_av, CS%v_av, G%Domain, halo=2, clock=id_clock_pass_init, complete=.false.) + call pass_vector(uh, vh, G%Domain, halo=2, clock=id_clock_pass_init, complete=.true.) + call CorAdCalc(CS%u_av, CS%v_av, CS%h_av, uh, vh, CS%CAu_pred, CS%CAv_pred, CS%OBC, CS%ADp, & + G, GV, US, CS%CoriolisAdv, pbv) !, Waves=Waves) + CS%CAu_pred_stored = .true. + endif else - if (.not. query_initialized(CS%h_av, "h2", restart_CS)) then - CS%h_av(:,:,:) = h(:,:,:) + CS%CAu_pred_stored = .false. + ! This call is just here to initialize uh and vh. + if (.not. query_initialized(uh, "uh", restart_CS) .or. & + .not. query_initialized(vh, "vh", restart_CS)) then + do k=1,nz ; do j=jsd,jed ; do i=isd,ied ; h_tmp(i,j,k) = h(i,j,k) ; enddo ; enddo ; enddo + call continuity(u, v, h, h_tmp, uh, vh, dt, G, GV, US, CS%continuity_CSp, CS%OBC, pbv) + call pass_var(h_tmp, G%Domain, clock=id_clock_pass_init) + do k=1,nz ; do j=jsd,jed ; do i=isd,ied + CS%h_av(i,j,k) = 0.5*(h(i,j,k) + h_tmp(i,j,k)) + enddo ; enddo ; enddo + call set_initialized(uh, "uh", restart_CS) + call set_initialized(vh, "vh", restart_CS) call set_initialized(CS%h_av, "h2", restart_CS) - elseif ((GV%m_to_H_restart /= 0.0) .and. (GV%m_to_H_restart /= 1.0)) then - H_rescale = 1.0 / GV%m_to_H_restart - do k=1,nz ; do j=js,je ; do i=is,ie ; CS%h_av(i,j,k) = H_rescale * CS%h_av(i,j,k) ; enddo ; enddo ; enddo - endif - if ( (GV%m_to_H_restart * US%s_to_T_restart * US%m_to_L_restart /= 0.0) .and. & - (US%s_to_T_restart /= (GV%m_to_H_restart * US%m_to_L_restart**2)) ) then - uH_rescale = US%s_to_T_restart / (GV%m_to_H_restart * US%m_to_L_restart**2) - do k=1,nz ; do j=js,je ; do I=G%IscB,G%IecB ; uh(I,j,k) = uH_rescale * uh(I,j,k) ; enddo ; enddo ; enddo - do k=1,nz ; do J=G%JscB,G%JecB ; do i=is,ie ; vh(i,J,k) = uH_rescale * vh(i,J,k) ; enddo ; enddo ; enddo + ! Try reading the CAu and CAv fields from the restart file, in case this restart file is + ! using a newer format. + call only_read_from_restarts(CS%CAu_pred, CS%CAv_pred, "CAu", "CAv", G, restart_CS, & + stagger=CGRID_NE, filename=dirs%input_filename, directory=dirs%restart_input_dir, & + success=read_uv, scale=US%m_s_to_L_T*US%T_to_s) + CS%CAu_pred_stored = read_uv + else + if (.not. query_initialized(CS%h_av, "h2", restart_CS)) then + CS%h_av(:,:,:) = h(:,:,:) + call set_initialized(CS%h_av, "h2", restart_CS) + elseif ((GV%m_to_H_restart /= 0.0) .and. (GV%m_to_H_restart /= 1.0)) then + H_rescale = 1.0 / GV%m_to_H_restart + do k=1,nz ; do j=js,je ; do i=is,ie ; CS%h_av(i,j,k) = H_rescale * CS%h_av(i,j,k) ; enddo ; enddo ; enddo + endif + if ( (GV%m_to_H_restart * US%s_to_T_restart * US%m_to_L_restart /= 0.0) .and. & + (US%s_to_T_restart /= (GV%m_to_H_restart * US%m_to_L_restart**2)) ) then + uH_rescale = US%s_to_T_restart / (GV%m_to_H_restart * US%m_to_L_restart**2) + do k=1,nz ; do j=js,je ; do I=G%IscB,G%IecB ; uh(I,j,k) = uH_rescale * uh(I,j,k) ; enddo ; enddo ; enddo + do k=1,nz ; do J=G%JscB,G%JecB ; do i=is,ie ; vh(i,J,k) = uH_rescale * vh(i,J,k) ; enddo ; enddo ; enddo + endif endif endif - call cpu_clock_begin(id_clock_pass_init) call create_group_pass(pass_av_h_uvh, CS%u_av, CS%v_av, G%Domain, halo=2) - call create_group_pass(pass_av_h_uvh, CS%h_av, G%Domain, halo=2) - call create_group_pass(pass_av_h_uvh, uh, vh, G%Domain, halo=2) + if (CS%CAu_pred_stored) then + call create_group_pass(pass_av_h_uvh, CS%CAu_pred, CS%CAv_pred, G%Domain, halo=2) + else + call create_group_pass(pass_av_h_uvh, CS%h_av, G%Domain, halo=2) + call create_group_pass(pass_av_h_uvh, uh, vh, G%Domain, halo=2) + endif call do_group_pass(pass_av_h_uvh, G%Domain) call cpu_clock_end(id_clock_pass_init) @@ -1601,6 +1707,7 @@ subroutine end_dyn_split_RK2(CS) DEALLOC_(CS%diffu) ; DEALLOC_(CS%diffv) DEALLOC_(CS%CAu) ; DEALLOC_(CS%CAv) + DEALLOC_(CS%CAu_pred) ; DEALLOC_(CS%CAv_pred) DEALLOC_(CS%PFu) ; DEALLOC_(CS%PFv) if (associated(CS%taux_bot)) deallocate(CS%taux_bot) @@ -1613,6 +1720,7 @@ subroutine end_dyn_split_RK2(CS) DEALLOC_(CS%h_av) ; DEALLOC_(CS%u_av) ; DEALLOC_(CS%v_av) call dealloc_BT_cont_type(CS%BT_cont) + deallocate(CS%AD_pred) deallocate(CS) end subroutine end_dyn_split_RK2 From 9fcdce9cffd37860462538774521d1145b0ba904 Mon Sep 17 00:00:00 2001 From: Stephen Griffies Date: Sun, 9 Oct 2022 12:20:35 -0400 Subject: [PATCH 44/68] updates to MOM6 documentation Changes made to the main equations documentation files for MOM6. Changes were made on top of Kate's original documentation. --- docs/images/ALE_general_schematic.png | Bin 0 -> 162179 bytes docs/ocean.bib | 60 +++++ src/ALE/_ALE.dox | 232 ++++++++++++------ src/ALE/_ALE_timestep.dox | 109 ++++---- src/core/_General_coordinate.dox | 200 ++++++++++----- src/core/_Governing.dox | 223 ++++++++++++----- src/core/_Notation.dox | 62 +++-- src/parameterizations/vertical/_CVMix_KPP.dox | 2 +- 8 files changed, 634 insertions(+), 254 deletions(-) create mode 100644 docs/images/ALE_general_schematic.png diff --git a/docs/images/ALE_general_schematic.png b/docs/images/ALE_general_schematic.png new file mode 100644 index 0000000000000000000000000000000000000000..3f492ed56d22ec450954ed03f23596650fe0ffb0 GIT binary patch literal 162179 zcmaHTbySpV+r118k`f9M(nu*G4TE&U(A}NV9Twf?&;mnuN+Sjg%}~-IJ%qIM@8Nvk zIiBxb>-YWvYZkg@p67nyE9HRkJ#%stDlAJEl$x<8I}c3x z2v4c1)8lDvXfILY2h-53c|vzq6vL{~!H&i4w6vQa5_6JE?;Wl~my*!*LD`wtOQxKHE}aty$4AVaDKU9R=8@Sqk=dI#=}f2Ya>l7@wU(96ySJ!x zo@5FhiEIpHZiV^oLAPHu$CdR}dNuboI>Kk9oyN^~lUZI7=_J4{6PsOrgf^R~$v;mb ziU4!o1N}Ck?30qBU0E;*=>-Rz1x@KZlV-u94Y>#U|Nk1V0SjfN!r}7B=c1w{^5NE5$d$ULMG%W0zo%8IOtiv(Xl0QmQl<2U*7cYBNxaK@`y2eZmZB!$UUHmjyh)jDbV@3#>d?o)#Efz>CP!kcOn$Q~ zO!#l_1-`~wJ@P$YiQ-xv`6xMQVQ*i$6650FkTl=wZ4h^QdKx)Z1R*IT-ANeC;ICR;%MQZ(EU%g)_B8Ilz0-J8 z*YR$GYJ7_z>pFD#BB{fO*CdB!;)n0? zC|7HGs2^PGA>S=oR^*t?g#FhG4hO%;NQo1>kX!IWay+7;F)=DrBIF)lO?ofW`IZ8e zIP!w=A%3{{DtV6HM&qhG9n?~rJ0jC^-JP31D{W_|?=@`7*uO&LVly8xH@9>8D|I+&!*GR8$R+AY~PmzHa&iIXPhWYCg5P9=AYK((&zFoGZY= zfPMTZk)EDj9=5=~y}g~duwZ<*!<0RJIR4qQA-JLE=f1uhS>CJj-ixHyfzsd59@BF$ zJ)C|VZ|knsjrZi>T&KP$kE;)Ml$Z&ZGv{V=&p(f%=ZxyuUcp#){eh#l;MUK=93p{>T91W&%7_qoTX$i&>~$%FX&5sL-39NzE@lIo)*J7`CI z!g2B?QHnv6bBaqK(f~0tqrdmG3VDE~Qpv_8Ec>s8`)eIT-EaqnhWbYHW!Jt&Jeahw zwk|B!3h((@Yte(>d~<=cI$9rOV$?l%2}x=A{P_OgYp;um(q8EQGlsuhkdZINLH?Uz z5MO$TWt${w;<6_tl^!IkV@`lCEoIA{723MwoToq~eg6ArTz{oetG3&x(g3{A@KO;D zO`N~q@1F;%Ks0jx9-mr-B@CY`k;i5z`4HhbuRYeex6p3j+;MJg=ip#4Wl`h)_qyhi zp(Vl#_#lV7O^c}zSp|%rVs^XFw|>vktq^E?W7a5fGiyl7Lp>iAv>gozuPuksG!{Po zb>Yh-uyw*CcTM!q2mjBWD%L>Ba@gz&MLj&|q3ItPQPX1E+?3zl?XVf9-rC=<+HQde z{`1kqAR(c{xZtb&o!KT;Uhr1ShliFLO^+C-NmJ!SRxhk3YR&9l(C3>JFz^_O%p;eU za<7&!#R&(;m)BmpBKmFOTIl}+h1G08?_Ns8(uh05&V<}n!&i^?5N28rk_!C-H$R41 zetC~Kp~;N-hWi0HrpVxHV8hYN@EUy`FC`025FThPqXO$CcZ?QM@-)+#g zubiBmoY)Q5`R5*~Nd*Eg>P61x+!?Jt|H71>dGWrWhTgt%US!5K%-%-B_mQX|7d&E3 zR$V4v{d}1qm?h(dliLpsPc9|_z4?u>nJ53bT2mm3@Bm~*(`tU4*i+x3kJ7G8lMkH_ z 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 Q@Cz(Ir5kst^&kzYW~JKvr=% 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 J=$c93{{foHG`au) literal 0 HcmV?d00001 diff --git a/docs/ocean.bib b/docs/ocean.bib index 2297f25354..ec35116efc 100644 --- a/docs/ocean.bib +++ b/docs/ocean.bib @@ -70,3 +70,63 @@ @article{Kasahara1974 title = {Various Vertical Coordinate Systems Used for Numerical Weather Prediction}, journal = {Monthly Weather Rev.} } + +@Article{Griffies_Adcroft_Hallberg2020, +author = "S.M. Griffies and A. Adcroft and R.W. Hallberg", +title = "A primer on the vertical Lagrangian-remap method in + ocean models based on finite volume generalized vertical coordinates", +journal = "Journal of Advances in Modeling Earth Systems", +year = "2020", +volume = "12", +doi = "10.1029/2019MS001954", +} + +@Article{Shao_etal_2020, +author = "A. Shao and A.J. Adcroft and R.W. Hallberg and S.M. Griffies", +title = "A general-coordinate, nonlocal neutral diffusion operator", +journal = "Journal of Advances in Modeling Earth Systems", +year = "2020", +volume = "12", +doi = "10.1029/2019MS001992", +} + +@Article{GM95, +author = "P. R. Gent and J. Willebrand and T. J. McDougall and J. C. McWilliams", +title = "Parameterizing eddy-induced tracer transports in ocean circulation models", +journal = "Journal of Physical Oceanography", +year = "1995", +volume = "25", +pages = "463--474", +doi = "10.1175/1520-0485(1995)025<0463:PEITTI>2.0.CO;2", +} + +@Article{foxkemper_etal2008, +author = "Baylor Fox-Kemper and Raffaele Ferrari and Robert Hallberg", +title = "Parameterization of mixed layer eddies. {I}: {T}heory and diagnosis", +journal = "Journal of Physical Oceanography", +year = "2008", +volume = "38", +pages = "1145--1165", +doi = "10.1175/2007JPO3792.1", +} + +@Article{McDougall_etal_2021, +author = "T. J. McDougall and P.M.\ Barker and R.M.\ Holmes and R.\ Pawlowicz and S.M.\ Grif\/f\/ies and P.J.\ Durack", +title = "The interpretation of temperature and salinity variables in numerical ocean model output, + and the calculation of heat fluxes and heat content", +journal = "Geoscientific Model Development", +year = "2021", +volume = "14", +pages = "6445--6466", +doi = "10.5194/gmd-14-6445-2021", +} + +@article{Young2010, +author = "W. R. Young", +year = "2010", +title = "Dynamic Enthalpy, {Conservative Temperature}, and the Seawater {Boussinesq} Approximation", +journal = "Journal of Physical Oceanography", +volume = "40", +pages = "394--400", +doi = "10.1175/2009JPO4294.1", +} diff --git a/src/ALE/_ALE.dox b/src/ALE/_ALE.dox index 9313ed2aa1..70bf1a8553 100644 --- a/src/ALE/_ALE.dox +++ b/src/ALE/_ALE.dox @@ -1,90 +1,184 @@ -/*! \page ALE ALE +/*! \page ALE Vertical Lagrangian method: conceptual -\section section_ALE Basics of the Vertical Lagrangian-Remap Method in MOM6 +\section section_ALE Lagrangian and ALE -As discussed by \cite adcroft2006, there are two general classes +As discussed by Adcroft and Hallberg (2008) \cite adcroft2006 and +Griffies, Adcroft and Hallberg (2020) \cite Griffies_Adcroft_Hallberg2020, +we can conceive of two general classes of algorithms that frame how hydrostatic ocean models are formulated. The two classes differ in how they treat the vertical direction. Quasi-Eulerian methods follow the approach traditionally -used in geopotential coordinate models, whereby vertical motion -is diagnosed via the continuity equation. Quasi-Lagrangian -methods are traditionally used by layered isopycnal models, with -the Lagrangian approach specifying motion that crosses coordinate +used in geopotential coordinate models, whereby vertical motion is +diagnosed via the continuity equation. Quasi-Lagrangian methods are +traditionally used by layered isopycnal models, with the vertical +Lagrangian approach specifying motion that crosses coordinate surfaces. Indeed, such dia-surface flow can be set to zero using -Lagrangian methods for studies of adiabatic dynamics. MOM6 makes -use of the vertical Lagrangian remap method, as pioneered for -ocean modeling by \cite bleck2002, which is a limit case of the -Arbitrary-Lagrangian-Eulerian method (\cite hirt1997). Dia-surface +Lagrangian methods for studies of adiabatic dynamics. MOM6 makes use +of the vertical Lagrangian remap method, as pioneered for ocean +modeling by Bleck (2002) \cite bleck2002 and further documented by +\cite Griffies_Adcroft_Hallberg2020, with this method a limit case of +the Arbitrary-Lagrangian-Eulerian method (\cite hirt1997). Dia-surface transport is implemented via a remapping so that the method can be -summarized as the Lagrangian plus remap approach and is essentially -a one-dimensional version of the incremental remapping of +summarized as the Lagrangian plus remap approach and so it is a +one-dimensional version of the incremental remapping of Dukowicz (2000) \cite dukowicz2000. -The MOM6 implementation of the vertical Lagrangian-remap method makes use -of two general steps. The first evolves the ocean state forward in -time according to a vertical Lagrangian limit with \f$\dot{r}=0\f$. Hence, -the horizontal momentum, thickness, and tracers are time stepped -with the red terms removed in equations \eqref{eq:h-horz-momentum,h-equations,momentum}, -\eqref{eq:h-thickness-equation,h-equations,thickness}, \eqref{eq:h-temperature-equation,h-equations,potential temperature}, -and \eqref{eq:h-salinity-equation,h-equations,salinity}. All advective transport thus -occurs within a layer as defined by constant \f$r\f$-surfaces so that -the volume within each layer is fixed. All other terms are retained in -their full form, including subgrid scale terms that contribute to -the transfer of tracer and momentum into distinct \f$r\f$ layers (e.g., -dia-surface diffusion of tracer and velocity). Maintaining constant -volume within a layer yet allowing for tracers to move between layers -engenders no inconsistency between tracer and thickness evolution. The -reason is that tracer diffusion, even dia-surface diffusion, does -not transfer volume. +\image html ALE_general_schematic.png "Schematic of the 3d Lagrangian regrid/remap method" width=70% +\image latex ALE_general_schematic.png "Schematic of the 3d Lagrangian regrid/remap method" width=0.7\textwidth -The second step in the algorithm comprises the generation of a new +Refer to the above figure taken from Griffies, Adcroft, and Hallberg +(2020) \cite Griffies_Adcroft_Hallberg2020. It shows a schematic of +the Lagrangian-remap method as well as the Arbitrary +Lagrangian-Eulerian (ALE) method. The first panel shows a square fluid +region and square grid used to represent the fluid, along with +rectangular subregions partitioned by grid lines. The second panel +shows the result of evolving the fluid region and evolving the +grid. The grid can evolve according to the fluid flow, as per a +Lagrangian method, or it can evolve according to some specified grid +evolution, as per an ALE method. The right panel depicts the grid +reinitialization onto a target grid (the regrid step). A regrid step +necessitates a corresponding remap step to estimate the ocean state on +the target grid, with conservative remapping required to preserve +integrated scalar contents (e.g., potential enthalpy, salt mass, and +seawater mass). The regrid/remap steps are needed for Lagrangian +methods in order for the grid to retain an accurate representation of +the ocean state. Ideally, the remap step does not affect any changes +to the fluid state; rather, it only modifies where in space the fluid +state is represented. However, any numerical realization incurs +interpolation inaccuracies that lead to unphysical (spurious) state +changes. + +\section section_ALE_MOM Vertical Lagrangian regrid/remap method + +We now get a bit more specific to the vertical Lagrangian method. +For this purpose, recall recall the basic dynamical equations (those +equations with a time derivative) of MOM6 discussed in +\ref General_Coordinate +\f{align} +\rho_0 +\left[ \frac{\partial \mathbf{u}}{\partial t} + \frac{( f + \zeta )}{h} \, +\hat{\mathbf{z}} \times h \, \mathbf{u} + \underbrace{ \dot{r} \, +\frac{\partial \mathbf{u}}{\partial r} } +\right] +&= -\nabla_r \, (p + \rho_{0} \, K) - +\rho \nabla_r \, \Phi + \mathbf{\mathcal{F}} +&\mbox{horizontal momentum} +\label{eq:h-horz-momentum-vlm} +\\ +\frac{\partial h}{\partial t} + \nabla_r \cdot \left( h \, \mathbf{u} \right) + +\underbrace{ \delta_r ( z_r \dot{r} ) } + &= 0 +&\mbox{thickness} +\label{eq:h-thickness-equation-vlm} +\\ +\frac{\partial ( \theta \, h )}{\partial t} + \nabla_r \cdot \left( \theta h \, +\mathbf{u} \right) + \underbrace{ \delta_r ( \theta \, z_r \dot{r} ) } +&= +h \mathbf{\mathcal{N}}_\theta^\gamma - \delta_r J_\theta^{(z)} +&\mbox{potential/Conservative temp} +\label{eq:h-temperature-equation-vlm} + \\ +\frac{\partial ( S \, h )}{\partial t} + \nabla_r \cdot \left( S \, h \, +\mathbf{u} \right) + \underbrace{ \delta_r ( S \, z_r \dot{r} ) } + &= +h \mathbf{\mathcal{N}}_S^\gamma - \delta_r J_S^{(z)} +&\mbox{salinity} +\label{eq:h-salinity-equation-vlm} +\f} +The MOM6 implementation of the vertical Lagrangian method makes +use of two general steps. The first evolves the ocean state forward in +time according to a vertical Lagrangian approach with with +\f$\dot{r}=0\f$. Hence, the horizontal momentum, thickness, and +tracers are time stepped with the underbraced terms removed in the +above equations. All advective transport occurs within a layer as +defined by constant \f$r\f$-surfaces so that the volume within each +layer is fixed. All other terms are retained in their full form, +including subgrid scale terms that contribute to the transfer of +tracer and momentum into distinct \f$r\f$ layers (e.g., dia-surface +diffusion of tracer and velocity). Maintaining constant volume within +a layer yet allowing for tracers to move between layers engenders no +inconsistency between tracer and thickness evolution. The reason is +that tracer diffusion, even dia-surface diffusion, does not transfer +volume. + +The second step in the method comprises the generation of a new vertical grid following a prescription, such as whether the grid -should align with isopcynals or constant \f$z^{*}\f$ or a combination. The -ocean state is then vertically remapped to the newly generated vertical -grid. The remapping step incorporates dia-surface transfer of properties, -with such transfer depending on the prescription given for the vertical +should align with isopcynals or constant \f$z^{*}\f$ or a combination. +This second step is known as the regrid step. The ocean state is then +vertically remapped to the newly generated vertical grid. This +remapping step incorporates dia-surface transfer of properties, with +such transfer depending on the prescription given for the vertical grid generation. To minimize discretization errors and the associated -spurious mixing, the remapping step makes use of the high order accurate -methods developed by \cite white2008 and \cite white2009. +spurious mixing, the remapping step makes use of the high order +accurate methods developed by \cite white2008 and \cite white2009. -The underlying algorithm for treatment of the vertical can -be related to operator-splitting of the red terms in equations -\eqref{eq:h-thickness-equation,h-equations,thickness}--\eqref{eq:h-temperature-equation,h-equations,potential temperature}. If we -consider, for simplicity, an Euler-forward update for a time-step \f$\Delta -t\f$, the time-stepping for the continuity and temperature equation can -be summarized as -\f{eqnarray} -\label{html:ale-equations}\notag \\ -h^\dagger &= h^{(n)} - \Delta t \left[ \nabla_r \cdot \left( h \, \mathbf{u} \right) \right] -&\mbox{thickness} \label{eq:ale-thickness-equation} \\ -\theta^\dagger \, h^\dagger &= \theta^{(n)} \, h^{(n)} - \Delta t \left[ \nabla_r \cdot \left( \theta h \, \mathbf{u} \right) - h \boldsymbol{\mathcal{N}}_\theta^\gamma + \delta_r J_\theta^{(z)} \right] -&\;\;\;\;\mbox{potential temp} \label{eq:ale-temperature-equation} \\ -h^{(n+1)} &= h^\dagger - \Delta t \, \delta_r \left( z_r \dot{r} \right) -&\mbox{move grid} \label{eq:ale-new-grid} \\ -\theta^{(n+1)} h^{(n+1)} &= \theta^\dagger h^\dagger - \Delta t \, \delta_r \left( z_r \dot{r} \, \theta^\dagger \right) -&\mbox{remap temperature.} \label{eq:ale-remap-temperature} -\f} +\section section_ALE_MOM_numerics Outlining the numerical algorithm -Substituting \eqref{eq:ale-thickness-equation,ale-equations,thickness} into \eqref{eq:ale-new-grid,ale-equations,move grid} -recovers a time-discrete form of \eqref{eq:h-thickness-equation,h-equations,thickness}. The -intermediate quantities indicated by \f$^\dagger\f$-symbols are the result of -the vertical Lagrangian step of the algorithm. What were the red terms in -the continuous-in-time equations are used to evolve the the intermediate -quantities to the final updated quantities each step. In MOM6, equation -\eqref{eq:ale-new-grid,ale-equations,move grid} is essentially used to define the dia-surface -transport \f$z_r \dot{r}\f$ by prescribing \f$h^{(n+1)}\f$. For example, to -recover a z-coordinate model, \f$h^{(n+1)}=\Delta z\f$, and \f$z_r \dot{r}\f$ -becomes the Eulerian vertical velocity, \f$w\f$. +The underlying algorithm for treatment of the vertical can be related +to operator-splitting of the underbraced terms in the above equations. +If we consider, for simplicity, an Euler-forward update for a +time-step \f$\Delta t\f$, the time-stepping for the thickness and +tracer equation (\f$C\f$ is an arbitrary tracer) can be summarized as +(from Table 1 in Griffies, Adcroft and Hallberg (2020) +\cite Griffies_Adcroft_Hallberg2020) +\f{align} +\label{html:ale-equations}\notag +\\ + \delta_{r} w^{\scriptstyle{\mathrm{grid}}} + &= -\nabla_{r} \cdot [h \, \mathbf{u}]^{(n)} + &\mbox{layer motion via convergence of horz advection} +\\ + h^{\dagger} &= h^{(n)} + \Delta t \, \delta_{r} w^{\scriptstyle{\mathrm{grid}}} += h^{(n)} - \Delta t \, \nabla_{r} \cdot [h \, \mathbf{u}]^{(n)} + &\mbox{horz advection thickness update} +\\ + [h \, C]^{\dagger} &= [h \, C]^{(n)} -\Delta t \, \nabla_{r} \cdot [ h \, C \, \mathbf{u} ]^{(n)} + &\mbox{horz advection tracer update} +\\ + h^{(n+1)} &= h^{\scriptstyle{\mathrm{target}}} + &\mbox{regrid to the target grid} +\\ + \delta_{r} w^{(\dot{r})} &= -(h^{\scriptstyle{\mathrm{target}}} - h^{\dagger})/\Delta t + &\mbox{diagnose dia-surface transport} +\\ + [h \, C]^{(n+1)} &= [h \, C]^{\dagger} - \Delta t \, \delta_{r} ( w^{(\dot{r})} \, C^{\dagger}) + &\mbox{remap tracer using dia-surface transport} +\f} +The first three equations constitute the Lagrangian portion of the +algorithm. In particular, the second equation provides an +intermediate or ``predictor'' value for the updated thickness, +\f$h^{\dagger}\f$, resulting from the vertical Lagrangian update. +Similarly, the third equation performs a Lagrangian update of the +thickness-weighted tracer to intermediate values, again operationally +realized by dropping the \f$w^{(\dot{r})}\f$ contribution. +The fourth equation is the regrid step, which is the key step in the +algorithm with the new grid defined by the new thickness +\f$h^{(n+1)}\f$. The new thickness is prescribed by the target values +for the vertical grid, +\f{align} + h^{(n+1)} = h^{\scriptstyle{\mathrm{target}}}. +\f} +The prescribed target grid thicknesses are then used to diagnose the +dia-surface velocity according to +\f{align} + \delta_{r} w^{(\dot{r})} = -(h^{\scriptstyle{\mathrm{target}}} - h^{\dagger})/\Delta t. +\f} +This step, and the remaining step for tracers, constitute the +remapping portion of the algorithm. For example, if the prescribed +coordinate surfaces are geopotentials, then \f$w^{(\dot{r})} = w\f$ +and \f$h^{\scriptstyle{\mathrm{target}}} = h^{(n)}\f$, in which case the +remap step reduces to Cartesian vertical advection. Within the above framework for evolving the ocean state, we make use of a standard split-explicit time stepping method by decomposing the horizontal momentum equation into its fast (depth integrated) and slow -(deviation from depth integrated) components. Furthermore, we follow the -methods of \cite hallberg2009 to ensure that the free surface resulting -from time stepping the depth integrated thickness equation (i.e., the -free surface equation) is consistent with the sum of the thicknesses -that result from time stepping the layer thickness equations for each -of the discretized layers; i.e., \f$\sum_{k} h = H + \eta\f$. +(deviation from depth integrated) components. Furthermore, we follow +the methods of Hallberg and Adcroft (2009) \cite hallberg2009 to +ensure that the free surface resulting from time stepping the depth +integrated thickness equation (i.e., the free surface equation) is +consistent with the sum of the thicknesses that result from time +stepping the layer thickness equations for each of the discretized +layers; i.e., \f$\sum_{k} h = H + \eta\f$. */ diff --git a/src/ALE/_ALE_timestep.dox b/src/ALE/_ALE_timestep.dox index e6da55fda9..42a88480be 100644 --- a/src/ALE/_ALE_timestep.dox +++ b/src/ALE/_ALE_timestep.dox @@ -1,50 +1,63 @@ -/*! \page ALE_Timestep ALE Timestep - -\section section_ALE_remap Explanation of ALE remapping - -The Arbitrary Lagrangian-Eulerian (ALE) remapping is not a timestep in the traditional -sense, but rather an operation performed to bring the vertical coordinate back to the target -specification. This remapping can be less frequent than the momentum or -thermodynamic timesteps, but must be done before the layer interfaces become entangled -with each other. - -Assuming the target vertical grid is level \f$z\f$-surfaces, the initial state is -shown on the left in the following figure: - -\image html remapping1.png "The initial state with level surface (left) and the perturbed state after a wave has come through (right)." -\image latex remapping1.png "The initial state with level surface (left) and the perturbed state after a wave has come through (right)." - -Some time later, a wave has perturbed the surfaces which move with the -fluid and it has been determined that a remapping operation is needed. The -target vertical grid is still level \f$z\f$-surfaces, so this new target -grid is shown overlaid on the left as regrid: - -\image html remapping2.png "The regrid operation (left) and the remap operation (right)." -\image latex remapping2.png "The regrid operation (left) and the remap operation (right)." - -The complex part of the operation is remapping the wavy field onto the new grid as -shown on the right and again in the final frame after the old deformed coordinate -system has been deleted: - -\image html remapping3.png "The final state after remapping." -\image latex remapping3.png "The final state after remapping." - -Mathematically, the new layer thicknesses, \f$h_k\f$, are computed and then populated -with the new velocities and tracers: - -\f[ - h_k^{\mbox{new}} = \nabla_k z_{\mbox{coord}} -\f] -\f[ - \sum h_k^{\mbox{new}} = \sum h_k^{\mbox{old}} -\f] -\f[ - \vec{u}_k^{\mbox{new}} = \frac{1}{h_k} \int_{z_{k + \frac{1}{2}}}^{z_{k + - \frac{1}{2}} + h_k} \vec{u}^{\mbox{old}}(z')dz' -\f] -\f[ - \theta^{\mbox{new}} = \frac{1}{h_k} \int_{z_{k + \frac{1}{2}}}^{z_{k + - \frac{1}{2}} + h_k} \theta^{\mbox{old}}(z')dz' -\f] +/*! \page ALE_Timestep Vertical Lagrangian method in pictures + +\section section_ALE_remap Graphical explanation of vertical Lagrangian method + +Vertical Lagrangian regridding/remapping is not a timestep method in +the traditional sense. Rather, it is a sequence of operations +performed to bring the vertical grid back to a target specification +(the regrid step), and then to remap the ocean state onto this new +grid (the remap step). This regrid/remap process can be chosen to be +less frequent than the momentum or thermodynamic timesteps. We are +motivated to choose less frequent regrid/remap steps to save +computational time and to reduce spurious mixing that occurs due to +truncation errors in the remap step. However, there is a downside to +delaying the regrid/remap. Namely, if delayed too long then the layer +interfaces can become entangled (i.e., no longer monotonic in the +vertical), which is a common problem with purely Lagrangian methods. +On this page we illustrate the regrid/remap steps by making use of +Figure 3 from Griffies, Adcroft, and Hallberg (2020) +\cite Griffies_Adcroft_Hallberg2020. + +For purposes of this example, assume that the target vertical grid is +comprised of geopotential \f$z\f$-surfaces, with the initial ocean +state (e.g., the temperature field) shown on the left in the following +figure. + +\image html remapping1.png "Initial state with level surface (left) and perturbed state after a wave has come through (right)" width=60% +\image latex remapping1.png "Initial state with level surface (left) and perturbed state after a wave has come through (right)" width=0.6\textwidth + +Some time later, assume a wave has perturbed the ocean state. During +the Lagrangian portion of the algorithm, the coordinate surfaces move +vertically with the ocean fluid according to \f$\dot{r}=0\f$. Assume +now that the algorithm has determined that a regrid step is needed, +with the target vertical grid still geopotential \f$z\f$-surfaces, so +this new target grid is shown overlaid on the left as a regrid. + +\image html remapping2.png "The regrid operation (left) and the remap operation (right)" width=60% +\image latex remapping2.png "The regrid operation (left) and the remap operation (right)" width=0.6\textwidth + +The most complex part of the method involves remapping the wavy ocean +field onto the new grid. This step also incurs truncation errors that +are a function of the vertical grid spacing and the numerical method +used to perform the remapping. We illustrate this remap step in the +figure above, as well as in the frame below shown after the old +deformed coordinate grid has been deleted: + +\image html remapping3.png "The final state after regriddinig and remapping" width=30% +\image latex remapping3.png "The final state after regridding and remapping" width=0.3\textwidth + +The new layer thicknesses, \f$h_k\f$, are computed and then the layers +are populated with the new velocities and tracers +\f{align} + % h_k^{\mbox{new}} %= \nabla_k z_{\mbox{coord}} \\ + \sum h_k^{\mbox{new}} &= \sum h_k^{\mbox{old}} +\\ + \mathbf{u}_k^{\mbox{new}} + &= \frac{1}{h_k} + \int_{z_{k + \frac{1}{2}}}^{z_{k + \frac{1}{2}} + h_k} \mathbf{u}^{\mbox{old}}(z') \, \mathrm{d}z' +\\ + \theta^{\mbox{new}} &= \frac{1}{h_k} + \int_{z_{k + \frac{1}{2}}}^{z_{k + \frac{1}{2}} + h_k} \theta^{\mbox{old}}(z') \, \mathrm{d}z' +\f} */ diff --git a/src/core/_General_coordinate.dox b/src/core/_General_coordinate.dox index cdaf8a34ea..aa7ab24b88 100644 --- a/src/core/_General_coordinate.dox +++ b/src/core/_General_coordinate.dox @@ -1,76 +1,158 @@ -/*! \page General_Coordinate General coordinate equations +/*! \page General_Coordinate Generalized vertical coordinate equations -Transforming to a vertical coordinate \f$r(z,x,y,t)\f$, with \f$\dot{r} = \frac{\partial r}{\partial t}\f$ ... +The ocean equations discretized by MOM6 are formulated using +generalized vertical coordinates. Motivation for using generalized +vertical coordinates, and a full accounting of the ocean equations +written using these coordinates, can be found in Griffies, Adcroft and +Hallberg (2020) \cite Griffies_Adcroft_Hallberg2020. Here we provide +a brief summary. -The Boussinesq hydrostatic equations of motion in general-coordinate -\f$r\f$ are: +Consider a smooth function of space and time, \f$r(x,y,z,t)\f$, that +has a single-signed and non-zero vertical derivative known as the +specific thickness +\f{align} + \partial z/\partial r = (\partial r/\partial z)^{-1} = \mbox{specific thickness.} +\f} +The specific thickness measures the inverse vertical stratification of +the vertical coordinate surfaces. As so constrained, \f$r\f$ can +uniquely prescribe a positiion in the vertical. Consequently, the +ocean equations can be mapped one-to-one from geopotential vertical +coordinates to generalized vertical coordinate. Upon transforming to +\f$r\f$-coordinates, the material time derivative of \f$r\f$ appears +throughout the equations, playing the role of a pseudo-vertical +velocity, and we make use of the following shorthand for this +derivative +\f{align} +\dot{r} = D_{t} r. +\f} -\f{eqnarray} +The Boussinesq hydrostatic ocean equations take the following form using +generalized vertical coordinates (\f$r\f$-coordinates) +\f{align} \label{html:r-equations}\notag \\ -\rho_0 \left( \frac{\partial \mathbf{u}}{\partial t} + ( f + \zeta ) \, \hat{\mathbf{z}} \times \mathbf{u} + \dot{r} \, \frac{\partial \mathbf{u}}{\partial r} + \nabla_r \, K \right) &= -\nabla_r \, p - \rho \nabla_r \, \Phi + \boldsymbol{\mathcal{F}} -&\mbox{momentum} \label{eq:r-horz-momentum} \\ -\rho \, \frac{\partial \Phi}{\partial r} + \frac{\partial p}{\partial r} &= 0 -&\mbox{hydrostatic} \label{eq:r-hydrostatic-equation} \\ -\frac{\partial z_r }{\partial t} + \nabla_r \cdot \, \left( z_r \, \mathbf{u} \right) + \frac{\partial ( z_r \, \dot{r} ) }{\partial r} &= 0 -&\mbox{thickness} \label{eq:r-non-divergence} \\ -\frac{\partial ( \theta \, z_r ) }{\partial t} + \nabla_r \cdot \left( \theta z_r \, \mathbf{u} \right) + \frac{\partial ( \theta \, z_r \, \dot{r} )}{\partial r} &= z_r \boldsymbol{\mathcal{N}}_\theta^\gamma - \frac{\partial J_\theta^{(z)}}{\partial r} -&\mbox{potential temp} \label{eq:r-temperature-equation} \\ -\frac{\partial ( S \, z_r) }{\partial t} + \nabla_r \cdot \left( S \, z_r \, \mathbf{u} \right) + \frac{\partial ( S \, z_r \, \dot{r} )}{\partial r} &= z_r \boldsymbol{\mathcal{N}}_S^\gamma - \frac{\partial J_S^{(z)}}{\partial r} -&\mbox{salinity} \label{eq:r-salinity-equation} \\ -\rho &= \rho\left( S, \theta, -g \rho_0 z(r) \right) +\rho_o \left[ + \partial_{t} \mathbf{u} + (f + \zeta) \, \hat{\mathbf{z}} \times \mathbf{u} + + \dot{r} \, \partial_{r} \mathbf{u} \right] + &= -\nabla_r \, (p + \rho_{o} \, K) -\rho \nabla_r \Phi + \rho_{o} \, \mathbf{\mathcal{F}} + &\mbox{horizontal momentum} +\label{eq:r-horz-momentum} +\\ +\rho \, \partial_{r} \Phi + \partial_{r}p + &= 0 +&\mbox{hydrostatic} +\label{eq:r-hydrostatic-equation} +\\ + \partial_{t}( z_r) ++ \nabla_r \cdot ( z_r \, \mathbf{u} ) ++ \partial_{r} ( z_r \, \dot{r} ) +&= 0 +&\mbox{specific thickness} +\label{eq:r-non-divergence} +\\ + \partial_{t} ( \theta \, z_r ) ++ \nabla_r \cdot ( \theta z_r \, \mathbf{u} ) ++ \partial_{r} ( \theta \, z_r \, \dot{r} ) +&= z_r \mathbf{\mathcal{N}}_\theta^\gamma +- \partial_{r} J_\theta^{(z)} +&\mbox{potential/Conservative temp} +\label{eq:r-temperature-equation} +\\ +\partial_{t} ( S \, z_r) ++ \nabla_r \cdot ( S \, z_r \, \mathbf{u} ) ++ \partial_{r} ( S \, z_r \, \dot{r} ) +&= z_r \mathbf{\mathcal{N}}_S^\gamma +- \partial_{r} J_S^{(z)} +&\mbox{salinity} +\label{eq:r-salinity-equation} +\\ +\rho &= \rho( S, \theta, -g \rho_0 z ) &\mbox{equation of state.} \f} +The time derivatives appearing in these equations are computed with +the generalized vertical coordinate fixed rather than the +geopotential. It is a common misconception that the horizontal +velocity, \f$\mathbf{u}\f$, is rotated to align with constant \f$r\f$ +surfaces. Such is not the case. Rather, the horizontal velocity, +\f$\mathbf{u}\f$, is precisely the same horizontal velocity used with +geopotential coordinates. However, its evolution has here been +formulated using generalized vertical coordinates. -The time derivatives are now computed with the generalized vertical -coordinate fixed rather than the geopotential. We introduced the -specific thickness, \f$z_r = \partial z/\partial r\f$, which measures the -inverse vertical stratification of the vertical coordinate surfaces. - - Similar to \cite bleck2002, MOM6 is discretized in the vertical by - integrating between surfaces of \f$r\f$ to yield layer equations where the - layer thickness is \f$h = \int z_r dr\f$ and variables are treated as finite - volume averages over each layer: - -\f{eqnarray} -\label{html:h-equations}\notag \\ -\rho_0 \left( \frac{\partial \mathbf{u}}{\partial t} + \frac{( f + \zeta )}{h} \, +As a finite volume model, MOM6 is discretized in the vertical by +integrating between surfaces of constant \f$r\f$. The layer thickness +is a basic term appearing in these equations, which results from +integrating the specific thickness over a layer +\f{align} +h = \int z_r \, \mathrm{d}r. +\f} +Correspondingly, the model variables are treated as finite volume +averages over each layer, with full accounting of this finite volume +approach presented in Griffies, Adcroft and Hallberg (2020) +\cite Griffies_Adcroft_Hallberg2020, and with the semi-discrete model +ocean model equations written as follows. +\f{align} +\rho_0 +\left[ \frac{\partial \mathbf{u}}{\partial t} + \frac{( f + \zeta )}{h} \, \hat{\mathbf{z}} \times h \, \mathbf{u} + \underbrace{ \dot{r} \, -\frac{\partial \mathbf{u}}{\partial r} } + \nabla_r K \right) &= -\nabla_r \, p - -\rho \nabla_r \, \Phi + \boldsymbol{\mathcal{F}} -&\mbox{momentum} \label{eq:h-horz-momentum} \\ -\rho \, \delta_r \Phi + \delta_r p &= 0 -&\mbox{hydrostatic} \label{eq:h-hydrostatic-equation} \\ +\frac{\partial \mathbf{u}}{\partial r} } +\right] +&= -\nabla_r \, (p + \rho_{0} \, K) - +\rho \nabla_r \, \Phi + \mathbf{\mathcal{F}} +&\mbox{horizontal momentum} +\label{eq:h-horz-momentum} +\\ +\rho \, \delta_r \Phi + \delta_r p +&= 0 +&\mbox{hydrostatic} +\label{eq:h-hydrostatic-equation} +\\ \frac{\partial h}{\partial t} + \nabla_r \cdot \left( h \, \mathbf{u} \right) + -\underbrace{ \delta_r ( z_r \dot{r} ) } &= 0 -&\mbox{thickness} \label{eq:h-thickness-equation} \\ +\underbrace{ \delta_r ( z_r \dot{r} ) } + &= 0 +&\mbox{thickness} +\label{eq:h-thickness-equation} +\\ \frac{\partial ( \theta \, h )}{\partial t} + \nabla_r \cdot \left( \theta h \, -\mathbf{u} \right) + \underbrace{ \delta_r ( \theta \, z_r \dot{r} ) } &= -h \boldsymbol{\mathcal{N}}_\theta^\gamma - \delta_r J_\theta^{(z)} -&\mbox{potential temp} \label{eq:h-temperature-equation} \\ +\mathbf{u} \right) + \underbrace{ \delta_r ( \theta \, z_r \dot{r} ) } +&= +h \mathbf{\mathcal{N}}_\theta^\gamma - \delta_r J_\theta^{(z)} +&\mbox{potential/Conservative temp} +\label{eq:h-temperature-equation} +\\ \frac{\partial ( S \, h )}{\partial t} + \nabla_r \cdot \left( S \, h \, -\mathbf{u} \right) + \underbrace{ \delta_r ( S \, z_r \dot{r} ) } &= -h \boldsymbol{\mathcal{N}}_S^\gamma - \delta_r J_S^{(z)} -&\mbox{salinity} \label{eq:h-salinity-equation} \\ +\mathbf{u} \right) + \underbrace{ \delta_r ( S \, z_r \dot{r} ) } +&= +h \mathbf{\mathcal{N}}_S^\gamma - \delta_r J_S^{(z)} +&\mbox{salinity} +\label{eq:h-salinity-equation} +\\ \rho &= \rho\left( S, \theta, -g \rho_0 z(r) \right) &\mbox{equation of state,} \label{eq:h-equation-of-state} \f} - -where \f$\delta_{r} = \mathrm{d}r \, (\partial/\partial r)\f$ is the discrete -vertical difference operator. The pressure gradient accelerations -in the momentum equation \eqref{eq:h-horz-momentum,h-equations,momentum} are written in +where +\f{align} +\delta_{r} = \mathrm{d}r \, (\partial/\partial r) +\f} +is the discrete vertical difference operator. The pressure gradient +accelerations in the momentum equation are written in continuous-in-the-vertical form for brevity; the exact discretization -is detailed in \cite adcroft2008. The MOM6 time-stepping algorithm -integrates the above layer-averaged equations forward allowing the -vertical grid to follow the motion, i.e. \f$\dot{r}=0\f$, so that the underbraced -terms are dropped. This approach is generally known as the Lagrangian -method but here the Lagrangian method is used only in the vertical -direction. After each Lagrangian step, a remap step is applied that -generates a new vertical grid of the user's choosing. The ocean state is -then mapped from the old to the new grid. The physical state is not meant -to change during the remap step, yet truncation errors make remapping -imperfect. We employ high-order accurate reconstructions to minimize -errors introduced during the remap step (\cite white2008, \cite white2009). The -connection between time-stepping and remapping is described in -section \ref ALE_Timestep. +is detailed in \cite adcroft2008 and +\cite Griffies_Adcroft_Hallberg2020. The \f$1/h\f$ and \f$h\f$ appearing in +the horizontal momentum equation are carefully handled in the code to +ensure proper cancellation even when the layer thickness goes to zero; +i.e., l'Hospital's rule is respected. + +The MOM6 time-stepping algorithm integrates the above layer-averaged +equations forward in time allowing the vertical grid to follow the +motion, i.e. \f$\dot{r}=0\f$, so that the underbraced terms are +dropped. This approach is generally known as a Lagrangian method, with +the Lagrangian approach in MOM6 limited to the vertical +direction. After each Lagrangian step, a regrid step is applied that +generates a new vertical grid of the user's choosing. The ocean state +is then remapped from the old to the new grid. The physical state is +not meant to change during the remap step, yet truncation errors make +remapping imperfect. We employ high-order accurate reconstructions to +minimize errors introduced during the remap step (\cite white2008, +\cite white2009). The connection between time-stepping and remapping +is described in section \ref ALE_Timestep. */ diff --git a/src/core/_Governing.dox b/src/core/_Governing.dox index 646ba52c09..e6140a0ba4 100644 --- a/src/core/_Governing.dox +++ b/src/core/_Governing.dox @@ -1,71 +1,176 @@ /*! \page Governing_Equations Governing Equations -The Boussinesq hydrostatic equations of motion in height coordinates are - -\f{eqnarray} D_t \boldsymbol{u} + f \widehat{\boldsymbol{k}} \times \boldsymbol{u} + \frac{\rho}{\rho_o} \boldsymbol{\nabla}_z \Phi + \frac{1}{\rho_o} \boldsymbol{\nabla}_z p &= \boldsymbol{\mathcal{F}} &\mbox{ momentum} \\ - \rho \, \frac{\partial \Phi}{\partial z} + \frac{\partial p}{\partial z} &= 0 &\mbox{ hydrostatic} \\ - \boldsymbol{\nabla}_z \cdotp \boldsymbol{u} + \frac{\partial w}{\partial z} &= 0 &\mbox{ thickness} \\ - D_t \theta &= \boldsymbol{\mathcal{N}}_\theta^\gamma - \frac{\partial J_\theta^{(z)}}{\partial z} &\mbox{ potential temp} \\ - D_t S &= \boldsymbol{\mathcal{N}}_S^\gamma - \frac{\partial J_S^{(z)}}{\partial z} &\mbox{ salinity} \\ - \rho &= \rho(S, \theta, z) &\mbox{ equation of state.} +MOM6 is a hydrostatic ocean circulation model that time steps either +the non-Boussinesq ocean equations (where the flow velocity is +divergent: \f$\nabla \cdot \mathbf{v} \ne 0\f$), or the Boussinesq +ocean equations (where velocity is non-divergent: \f$\nabla \cdot +\mathbf{v} = 0\f$). We here display the Boussinesq version since +it is most commonly used (as of 2022). We start by casting the +equations in geopotentiial coordinates prior to transforming to the +generalized vertical coordinates used by MOM6. A more thorough +discussion of these equations, and their finite volume realization +appropriate for MOM6, can be found in Griffies, Adcroft and Hallberg (2020) +\cite Griffies_Adcroft_Hallberg2020. + +The hydrostatic Boussinesq ocean equations, written using geopotential +vertical coordinates, are given by +\f{align} + \rho_o \left[ + D_t \mathbf{u} + f \hat{\mathbf{z}} \times \mathbf{u} + \right] + &= -\rho \, \nabla_z \Phi - \nabla_z p + + \rho_o \, \mathbf{\mathcal{F}} + &\mbox{horizontal momentum} +\\ + \rho \, \partial_{z} \Phi + \partial_{z} p &= 0 &\mbox{hydrostatic} +\\ + \nabla_z \cdotp \mathbf{u} + \partial_{z} w + &= 0 + &\mbox{continuity} +\\ + D_t \theta &= \mathbf{\mathcal{N}}_\theta^\gamma + - \partial_{z} J_\theta^{(z)} + &\mbox{potential or Conservative temp} + \\ + D_t S &= \mathbf{\mathcal{N}}_S^\gamma +- \partial_{z} J_S^{(z)} + &\mbox{salinity} +\\ + \rho &= \rho(S, \theta, z) &\mbox{ equation of state} +\\ + \mathbf{v} &= \mathbf{u} + \hat{\mathbf{z}} \, w &\mbox{velocity field.} \f} -where notation is described in \ref Notation, \f$\boldsymbol{\mathcal{F}}\f$ represents the accelerations due to -the divergence of stresses including those provided through boundary interactions. - -The prognostic thermodynamic variables are potential temperature, -\f$\theta\f$, and salinity \f$S\f$, which are related to in situ density -\f$\rho\f$ through the \cite wright1997 equation of state. In the potential -temperature and salinity equations, fluxes due to diabatic, vertically -oriented processes are indicated by \f$J^{(z)}\f$. The tendency due to the -convergence of fluxes oriented along neutral directions is indicated by -\f$\boldsymbol{\mathcal{N}}^\gamma\f$. Our implementation of this neutral -diffusion parameterization is detailed in Shao et al. (personal comm.) - -The total derivative is - -\f{eqnarray} D_t & \equiv \frac{\partial}{\partial t} + \boldsymbol{v} \cdotp \boldsymbol{\nabla} \\ - &= \frac{\partial}{\partial t} + \boldsymbol{u} \cdotp \boldsymbol{\nabla}_z + w \frac{\partial}{\partial z}. +The acceleration term, \f$\mathbf{\mathcal{F}}\f$, in the +horizontal momentum equation includes the acceleration due to the +divergence of internal frictional stresses as well as from bottom and +surface boundary stresses. Other notation is described in \ref +Notation. + +The prognostic temperature, \f$\theta\f$, is either potential +temperature or Conservative Temperature, depending on the chosen +equation of state, and \f$S\f$ is the salinity. We generally follow +the discussion of \cite McDougall_etal_2021 for how to interpret the +prognostic temperature and salinity in ocean models. MOM6 has +historically used the Wright (1997) \cite wright1997 equation of state +to compute the in situ density, \f$\rho\f$. However, there +are other options as documented in \ref Equation_of_State. In the +potential temperature and salinity equations, fluxes due to diabatic +processes are indicated by \f$J^{(z)}\f$. Tendencies due to the +convergence of fluxes oriented along neutral directions are indicated +by \f$\mathbf{\mathcal{N}}^\gamma\f$, with our implementation of +neutral diffusion detailed in Shao et al (2020) +\cite Shao_etal_2020. + +The total or material time derivative operator is given by +\f{align} + D_t &\equiv \partial_{t} + \mathbf{v} \cdotp \nabla + \\ + &= \partial_{t} + \mathbf{u} \cdotp \nabla_z + w \, \partial_{z}, \f} - -The non-divergence of flow allows a total derivative to be re-written in flux form: - -\f{eqnarray} D_t \theta &= \frac{\partial}{\partial t} + \boldsymbol{\nabla} \cdotp ( \boldsymbol{v} \theta ) \\ - &= \frac{\partial}{\partial t} + \boldsymbol{\nabla}_z \cdotp ( \boldsymbol{u} \theta ) + \frac{\partial ( w \theta )}{\partial z}. +where the second equality explosed the horizontal and vertical terms. Using the non-divergence condition +on the three-dimensional velocity allows us to write the material time derivative of an arbitrary scalar field, +\f$\psi\f$, into a flux-form equation +\f{align} D_t \psi &= ( \partial_{t} + \mathbf{u} \cdotp \nabla) \, \psi + \\ + &= \partial_{t} \psi + \nabla \cdotp (\mathbf{v} \, \psi) +\\ + &= \partial_{t} \psi + \nabla_z \cdotp ( \mathbf{u} \, \psi) + \partial_{z} ( w \, \psi). \f} - -The above equations of motion can thus be written as: - -\f{eqnarray} D_t \boldsymbol{u} + f \widehat{\boldsymbol{k}} \times \boldsymbol{u} + \frac{\rho}{\rho_o}\boldsymbol{\nabla}_z \Phi + \frac{1}{\rho_o} \boldsymbol{\nabla}_z p &= \boldsymbol{\mathcal{F}} &\mbox{ momentum}\\ - \rho \, \frac{\partial \Phi}{\partial z} + \frac{\partial p}{\partial z} &= 0 &\mbox{ hydrostatic} \\ - \boldsymbol{\nabla}_z \cdotp \boldsymbol{u} + \frac{\partial w}{\partial z} &= 0 &\mbox{ thickness} \\ - \frac{\partial \theta}{\partial t} + \boldsymbol{\nabla}_z \cdotp ( \boldsymbol{u} \theta ) + \frac{\partial ( w \theta )}{\partial z} &= \boldsymbol{\mathcal{N}}_\theta^\gamma - \frac{\partial J_\theta^{(z)}}{\partial z} &\mbox{ potential temp} \\ - \frac{\partial S}{\partial t} + \boldsymbol{\nabla}_z \cdotp ( \boldsymbol{u} S ) + \frac{\partial ( w S )}{\partial z} &= \boldsymbol{\mathcal{N}}_S^\gamma - \frac{\partial J_S^{(z)}}{\partial z} &\mbox{ salinity} \\ - \rho &= \rho(S, \theta, z) &\mbox{ equation of state.} +Discretizing the flux-form scalar equations means that fluxes +transferring scalars between grid cells act in a conservative manner. +Consequently, the domain integrated scalar (e.g., total seawater volume, total +salt content, total potential enthalpy) is affected only via surface and bottom +boundary transport. Such global conservation properties are +maintained by MOM6 to within computational roundoff, with this level +of precision found to be essential for using MOM6 to study +climate. Making use of the flux-form scalar conservation equations +brings the model equations to the form +\f{align} + \rho_o \left[ + D_t \mathbf{u} + f \hat{\mathbf{z}} \times \mathbf{u} + \right] + &= -\rho \, \nabla_z \Phi - \nabla_z p + + \rho_o \, \mathbf{\mathcal{F}} + &\mbox{horizontal momentum} +\\ + \rho \, \partial_{z} \Phi + \partial_{z} p &= 0 &\mbox{hydrostatic} +\\ + \nabla_z \cdotp \mathbf{u} + \partial_{z} w + &= 0 + &\mbox{continuity} +\\ +\partial_{t} \theta + \nabla_z \cdotp (\mathbf{u} \, \theta) + \partial_{z} (w \, \theta) +&= \mathbf{\mathcal{N}}_\theta^\gamma - \partial_{z} J_\theta^{(z)} +&\mbox{potential or Conservative temp} +\\ +\partial_{t} S + \nabla_z \cdotp (\mathbf{u} \, S) + \partial_{z}(w \, S) +&= \mathbf{\mathcal{N}}_S^\gamma -\partial_{z} J_S^{(z)} + &\mbox{salinity} +\\ +\rho &= \rho(S, \theta, z) &\mbox{equation of state.} \f} -\section vector_invariant_eqns Vector Invariant Equations - -MOM6 solves the momentum equations written in vector-invariant form. - -A vector identity allows the total derivative of velocity to be written in the vector-invariant form: - -\f{eqnarray} D_t \boldsymbol{u} &= \partial_t \boldsymbol{u} + \boldsymbol{v} \cdotp \boldsymbol{\nabla} \boldsymbol{u} \\ - &= \partial_t \boldsymbol{u} + \boldsymbol{u} \cdotp \boldsymbol{\nabla}_z \boldsymbol{u} + w \partial_z \boldsymbol{u} \\ - &= \partial_t \boldsymbol{u} + \left( \boldsymbol{\nabla} - \times \boldsymbol{u} \right) \times \boldsymbol{v} + \boldsymbol{\nabla} \underbrace{\frac{1}{2} \left|\boldsymbol{u}\right|^2}_{\equiv K} . +\section vector_invariant_eqns Vector invariant velocity equation + +MOM6 time steps the horizontal velocity equation in its +vector-invariant form. To derive this equation we make use of the +following vector identity +\f{align} + D_t \mathbf{u} + &= + \partial_t \mathbf{u} + \mathbf{v} \cdotp \nabla \mathbf{u} + \\ + &= + \partial_t \mathbf{u} + \mathbf{u} \cdotp \nabla_z \mathbf{u} + w \partial_z \mathbf{u} + \\ + &= + \partial_t \mathbf{u} + \left( \nabla \times \mathbf{u} \right) \times \mathbf{v} + + \nabla \left|\mathbf{u}\right|^2/2 + \\ + &= + \partial_t \mathbf{u} + w \, \partial_{z} \mathbf{u} + + \zeta \, \hat{\mathbf{z}} \times \mathbf{u} + \nabla_{z} K, \f} - -The flux-form equations of motion in height coordinates can thus be written succinctly as: - -\f{eqnarray} \partial_t \boldsymbol{u} + \left( f \widehat{\boldsymbol{k}} + - \boldsymbol{\nabla} \times \boldsymbol{u} \right) \times \boldsymbol{v} + \boldsymbol{\nabla} K - + \frac{\rho}{\rho_o} \boldsymbol{\nabla} \Phi + \frac{1}{\rho_o} \boldsymbol{\nabla} p &= \boldsymbol{\mathcal{F}} &\mbox{ momentum} \\ - \boldsymbol{\nabla}_z \cdotp \boldsymbol{u} + \partial_z w &= 0 &\mbox{ thickness} \\ - \partial_t \theta + \boldsymbol{\nabla}_z \cdotp ( \boldsymbol{u} \theta ) + \partial_z ( w \theta ) &= \boldsymbol{\mathcal{N}}_\theta^\gamma - \frac{\partial J_\theta^{(z)}}{\partial z} &\mbox{ potential temp} \\ - \partial_t S + \boldsymbol{\nabla}_z \cdotp ( \boldsymbol{u} S ) + \partial_z ( w S ) &= \boldsymbol{\mathcal{N}}_S^\gamma - \frac{\partial J_S^{(z)}}{\partial z} &\mbox{ salinity} \\ - \rho &= \rho(S, \theta, z) &\mbox{ equation of state} +where we introduced the vertical component to the relative vorticity +\f{align} + \zeta = \hat{\mathbf{z}} \cdot (\nabla \times \mathbf{u}) + = \partial_{x}v - \partial_{y} u, +\label{eq:relative-vorticity-z} +\f} +as well as the kinetic energy per mass contained in the horizontal flow +\f{align} + K = (u^{2} + v^{2})/2. +\label{eq:kinetic-energy-per-mass} +\f} +It is just the horizontal kinetic energy per mass that appears when +making the hydrostatic approximation, whereas a non-hydrostatic fluid +(such as the MITgcm) includes the contribution from vertical motion. With +these identities we are led to the MOM6 flux-form equations of motion in +geopotential coordinates +\f{align} + \rho_{o} \left[ + \partial_t \mathbf{u} + w \, \partial_{z} \mathbf{u} + + (f + \zeta) \hat{\mathbf{z}} \times \mathbf{u} + \right] + &= -\nabla_{z} (p + K) - \rho \, \nabla_{z} \Phi + \rho_{o} \, \mathbf{\mathcal{F}} + &\mbox{vector-invariant horz velocity} +\\ + \rho \, \partial_{z} \Phi + \partial_{z} p &= 0 &\mbox{hydrostatic} +\\ + \nabla_z \cdotp \mathbf{u} + \partial_{z} w + &= 0 + &\mbox{continuity} + \\ + \partial_t \theta + \nabla_z \cdotp ( \mathbf{u} \, \theta ) + \partial_z ( w \, \theta ) + &= \mathbf{\mathcal{N}}_\theta^\gamma - \partial_{z} J_\theta^{(z)} + &\mbox{potential/Conservative temp} + \\ + \partial_t S + \nabla_z \cdotp ( \mathbf{u} \, S ) + \partial_z (w \, S) + &= \mathbf{\mathcal{N}}_S^\gamma - \partial_{z} J_S^{(z)} + &\mbox{salinity} + \\ + \rho &= \rho(S, \theta, z) &\mbox{equation of state.} \f} -where the horizontal momentum equations and vertical hydrostatic balance equation have been written as a single three-dimensional equation. */ diff --git a/src/core/_Notation.dox b/src/core/_Notation.dox index faecb3b258..1360ab17db 100644 --- a/src/core/_Notation.dox +++ b/src/core/_Notation.dox @@ -2,34 +2,60 @@ \section Symbols Symbols for variables -\f$z\f$ refers to elevation (or height), increasing upward so that for much of the ocean \f$z\f$ is negative. +\f$z\f$ refers to geopotential elevation (or height), increasing +upward and with \f$z=0\f$ defining the resting ocean surface. Much of +the ocean has \f$z < 0\f$. -\f$x\f$ and \f$y\f$ are the Cartesian horizontal coordinates. +\f$x\f$ and \f$y\f$ are the Cartesian horizontal coordinates. MOM6 + uses generalized orthogonal curvilinear horizontal + coordinates. However, the equations are simpler to write using + Cartesian coordinates, and it is very straightforward to generalize + the horizontal coordinates using the methods in Chapters 20 and 21 of + \cite SMGbook. -\f$\lambda\f$ and \f$\phi\f$ are the geographic coordinates on a sphere (longitude and latitude respectively). +\f$\lambda\f$ and \f$\phi\f$ are the geographic coordinates on a +sphere (longitude and latitude, respectively). -Horizontal components of velocity are indicated by \f$u\f$ and \f$v\f$ and vertical component by \f$w\f$. +Horizontal components of velocity are indicated by \f$u\f$ and \f$v\f$ +and vertical component by \f$w\f$. -\f$p\f$ is pressure and \f$\Phi\f$ is geo-potential: +\f$p\f$ is the hydrostatic pressure. - \f[ \Phi = g z .\f] +\f$\Phi\f$ is the geopotential. In the absence of tides, the +geopotential is given by \f$\Phi = g z,\f$ whereas more general +expressions hold when including astronomical tide forcing. -The thermodynamic state variables are usually salinity, \f$S\f$, and potential temperature, \f$\theta\f$ or the absolute salinity and conservative temperature, depending on the equation of state. \f$\rho\f$ is in-situ density. +The thermodynamic state variables can be salinity, \f$S\f$, and +potential temperature, \f$\theta\f$. Alternatively, one can choose +the Conservative Temperature if using the TEOS10 equation of state +from \cite TEOS2010. -\section vector_notation Vector notation - -The three-dimensional velocity vector is denoted \f$\boldsymbol{v}\f$ +\f$\rho\f$ is the in-situ density computed as a function +\f$\rho(S,\theta,p)\f$ for non-Boussinesq ocean or +\f$\rho(S,\theta,p=-g \, \rho_o \, z)\f$ for Boussinesq ocean. See +Young (2010) \cite Young2010 or Section 2.4 of Vallis (2017) +\cite GVbook for reasoning behind the simplified pressure +used in the Boussinesq equation of state. - \f[\boldsymbol{v} = \boldsymbol{u} + \widehat{\boldsymbol{k}} w ,\f] -where \f$\widehat{\boldsymbol{k}}\f$ is the unit vector pointed in the upward vertical direction and \f$\boldsymbol{u} = (u, v, 0)\f$ is the horizontal -component of velocity normal to the vertical. -The gradient operator without a suffix is three dimensional: - - \f[\boldsymbol{\nabla} = ( \boldsymbol{\nabla}_z, \partial_z ) .\f] +\section vector_notation Vector notation -but a suffix indicates a lateral gradient along a surface of constant property indicated by the suffix: +The three-dimensional velocity vector is denoted \f$\boldsymbol{v}\f$ +and it is decomposed into its horizontal and vertical components according to + \f[\boldsymbol{v} + = \boldsymbol{u} + \hat{\boldsymbol{z}} w + = \hat{\boldsymbol{x}} u + \hat{\boldsymbol{y}} v + \hat{\boldsymbol{z}} w, + \f] +where \f$\hat{\boldsymbol{z}}\f$ is the unit vector pointed in the +upward vertical direction and \f$\boldsymbol{u} = (u, v, 0)\f$ is the +horizontal component of velocity normal to the vertical. + +The three-dimensional gradient operator is denoted \f$\nabla\f$, and it is decomposed into +its horizontal and vertical components according to + \f[\nabla + = \nabla_z + \hat{\boldsymbol{z}} \partial_z + = \hat{\boldsymbol{x}} \partial_x + \hat{\boldsymbol{y}} \partial_y + \hat{\boldsymbol{z}} \partial_z. + \f] - \f[\boldsymbol{\nabla}_z = \left( \left. \partial_x \right|_z, \left. \partial_y \right|_z, 0 \right) .\f] */ diff --git a/src/parameterizations/vertical/_CVMix_KPP.dox b/src/parameterizations/vertical/_CVMix_KPP.dox index 7a65b6a6a3..72c166c284 100644 --- a/src/parameterizations/vertical/_CVMix_KPP.dox +++ b/src/parameterizations/vertical/_CVMix_KPP.dox @@ -7,7 +7,7 @@ The formulation and implementation of KPP is described in great detail in the [CVMix manual](https://github.com/CVMix/CVMix-description/raw/master/cvmix.pdf) (written by our own Steve Griffies). - \section section_KPP_nutshell KPP in a nutshell + \section section_KPP_nutshell KPP in a nutshell Large et al., \cite large1994, decompose the parameterized boundary layer turbulent flux of a scalar, \f$ s \f$, as \f[ \overline{w^\prime s^\prime} = -K \partial_z s + K \gamma_s(\sigma), \f] From d214ad8f3090ef6217bccd51fd7fcbdeffa0c111 Mon Sep 17 00:00:00 2001 From: Marshall Ward Date: Tue, 4 Oct 2022 13:32:06 -0400 Subject: [PATCH 45/68] Add netcdf-fortran to MacOS homebrew build The latest homebrew `netcdf` package appears to no longer include netCDF Fortran headers and tools. This patch installs the new `netcdf-fortran` package which adds them back. --- .github/actions/macos-setup/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/macos-setup/action.yml b/.github/actions/macos-setup/action.yml index 645a51b619..197a2d83c8 100644 --- a/.github/actions/macos-setup/action.yml +++ b/.github/actions/macos-setup/action.yml @@ -13,5 +13,6 @@ runs: brew update brew install automake brew install netcdf + brew install netcdf-fortran brew install mpich echo "::endgroup::" From 539ba09e2ff4d01877d4377812b8aecdd297cc7c Mon Sep 17 00:00:00 2001 From: Stephen Griffies Date: Fri, 7 Oct 2022 10:16:50 -0400 Subject: [PATCH 46/68] updates to MOM6 documentation --- src/ALE/_ALE.dox | 6 +++--- src/ALE/_ALE_timestep.dox | 2 +- src/core/_Governing.dox | 6 +++--- src/core/_Notation.dox | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ALE/_ALE.dox b/src/ALE/_ALE.dox index 70bf1a8553..82b4a7a04d 100644 --- a/src/ALE/_ALE.dox +++ b/src/ALE/_ALE.dox @@ -1,6 +1,6 @@ /*! \page ALE Vertical Lagrangian method: conceptual -\section section_ALE Lagrangian and ALE +\subsection section_ALE Lagrangian and ALE As discussed by Adcroft and Hallberg (2008) \cite adcroft2006 and Griffies, Adcroft and Hallberg (2020) \cite Griffies_Adcroft_Hallberg2020, @@ -48,7 +48,7 @@ state is represented. However, any numerical realization incurs interpolation inaccuracies that lead to unphysical (spurious) state changes. -\section section_ALE_MOM Vertical Lagrangian regrid/remap method +\subsection section_ALE_MOM Vertical Lagrangian regrid/remap method We now get a bit more specific to the vertical Lagrangian method. For this purpose, recall recall the basic dynamical equations (those @@ -113,7 +113,7 @@ spurious mixing, the remapping step makes use of the high order accurate methods developed by \cite white2008 and \cite white2009. -\section section_ALE_MOM_numerics Outlining the numerical algorithm +\subsection section_ALE_MOM_numerics Outlining the numerical algorithm The underlying algorithm for treatment of the vertical can be related to operator-splitting of the underbraced terms in the above equations. diff --git a/src/ALE/_ALE_timestep.dox b/src/ALE/_ALE_timestep.dox index 42a88480be..d4f53ec6dc 100644 --- a/src/ALE/_ALE_timestep.dox +++ b/src/ALE/_ALE_timestep.dox @@ -1,6 +1,6 @@ /*! \page ALE_Timestep Vertical Lagrangian method in pictures -\section section_ALE_remap Graphical explanation of vertical Lagrangian method +\subsection section_ALE_remap Graphical explanation of vertical Lagrangian method Vertical Lagrangian regridding/remapping is not a timestep method in the traditional sense. Rather, it is a sequence of operations diff --git a/src/core/_Governing.dox b/src/core/_Governing.dox index e6140a0ba4..0350d500b5 100644 --- a/src/core/_Governing.dox +++ b/src/core/_Governing.dox @@ -59,8 +59,8 @@ potential temperature and salinity equations, fluxes due to diabatic processes are indicated by \f$J^{(z)}\f$. Tendencies due to the convergence of fluxes oriented along neutral directions are indicated by \f$\mathbf{\mathcal{N}}^\gamma\f$, with our implementation of -neutral diffusion detailed in Shao et al (2020) -\cite Shao_etal_2020. +neutral diffusion detailed in Shao et al (2020) \cite +Shao_etal_2020. The total or material time derivative operator is given by \f{align} @@ -111,7 +111,7 @@ brings the model equations to the form \rho &= \rho(S, \theta, z) &\mbox{equation of state.} \f} -\section vector_invariant_eqns Vector invariant velocity equation +\subsection vector_invariant_eqns Vector invariant velocity equation MOM6 time steps the horizontal velocity equation in its vector-invariant form. To derive this equation we make use of the diff --git a/src/core/_Notation.dox b/src/core/_Notation.dox index 1360ab17db..3e5ab12667 100644 --- a/src/core/_Notation.dox +++ b/src/core/_Notation.dox @@ -1,6 +1,6 @@ /*! \page Notation Notation for equations -\section Symbols Symbols for variables +\subsubsection Symbols Symbols for variables \f$z\f$ refers to geopotential elevation (or height), increasing upward and with \f$z=0\f$ defining the resting ocean surface. Much of @@ -39,7 +39,7 @@ used in the Boussinesq equation of state. -\section vector_notation Vector notation +\subsubsection vector_notation Vector notation The three-dimensional velocity vector is denoted \f$\boldsymbol{v}\f$ and it is decomposed into its horizontal and vertical components according to From 253262ccd5e3475f0600ab2ee632e25f80f02c71 Mon Sep 17 00:00:00 2001 From: Stephen Griffies Date: Fri, 7 Oct 2022 11:00:30 -0400 Subject: [PATCH 47/68] correct some doxygen errors related to subsections, cites --- src/ALE/_ALE.dox | 6 +++--- src/ALE/_ALE_timestep.dox | 2 +- src/core/_General_coordinate.dox | 8 ++++---- src/core/_Governing.dox | 6 +++--- src/core/_Notation.dox | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ALE/_ALE.dox b/src/ALE/_ALE.dox index 82b4a7a04d..4943ff5c05 100644 --- a/src/ALE/_ALE.dox +++ b/src/ALE/_ALE.dox @@ -1,6 +1,6 @@ /*! \page ALE Vertical Lagrangian method: conceptual -\subsection section_ALE Lagrangian and ALE +\section section_ALE Lagrangian and ALE As discussed by Adcroft and Hallberg (2008) \cite adcroft2006 and Griffies, Adcroft and Hallberg (2020) \cite Griffies_Adcroft_Hallberg2020, @@ -48,7 +48,7 @@ state is represented. However, any numerical realization incurs interpolation inaccuracies that lead to unphysical (spurious) state changes. -\subsection section_ALE_MOM Vertical Lagrangian regrid/remap method +\section section_ALE_MOM Vertical Lagrangian regrid/remap method We now get a bit more specific to the vertical Lagrangian method. For this purpose, recall recall the basic dynamical equations (those @@ -113,7 +113,7 @@ spurious mixing, the remapping step makes use of the high order accurate methods developed by \cite white2008 and \cite white2009. -\subsection section_ALE_MOM_numerics Outlining the numerical algorithm +\section section_ALE_MOM_numerics Outlining the numerical algorithm The underlying algorithm for treatment of the vertical can be related to operator-splitting of the underbraced terms in the above equations. diff --git a/src/ALE/_ALE_timestep.dox b/src/ALE/_ALE_timestep.dox index d4f53ec6dc..13e95f4866 100644 --- a/src/ALE/_ALE_timestep.dox +++ b/src/ALE/_ALE_timestep.dox @@ -1,6 +1,6 @@ /*! \page ALE_Timestep Vertical Lagrangian method in pictures -\subsection section_ALE_remap Graphical explanation of vertical Lagrangian method +\section section_ALE_remap Graphical explanation of vertical Lagrangian method Vertical Lagrangian regridding/remapping is not a timestep method in the traditional sense. Rather, it is a sequence of operations diff --git a/src/core/_General_coordinate.dox b/src/core/_General_coordinate.dox index aa7ab24b88..a658d8db29 100644 --- a/src/core/_General_coordinate.dox +++ b/src/core/_General_coordinate.dox @@ -86,8 +86,8 @@ h = \int z_r \, \mathrm{d}r. \f} Correspondingly, the model variables are treated as finite volume averages over each layer, with full accounting of this finite volume -approach presented in Griffies, Adcroft and Hallberg (2020) -\cite Griffies_Adcroft_Hallberg2020, and with the semi-discrete model +approach presented in Griffies, Adcroft and Hallberg (2020) \cite +Griffies_Adcroft_Hallberg2020, and with the semi-discrete model ocean model equations written as follows. \f{align} \rho_0 @@ -135,8 +135,8 @@ where is the discrete vertical difference operator. The pressure gradient accelerations in the momentum equation are written in continuous-in-the-vertical form for brevity; the exact discretization -is detailed in \cite adcroft2008 and -\cite Griffies_Adcroft_Hallberg2020. The \f$1/h\f$ and \f$h\f$ appearing in +is detailed in \cite adcroft2008 and \cite +Griffies_Adcroft_Hallberg2020. The \f$1/h\f$ and \f$h\f$ appearing in the horizontal momentum equation are carefully handled in the code to ensure proper cancellation even when the layer thickness goes to zero; i.e., l'Hospital's rule is respected. diff --git a/src/core/_Governing.dox b/src/core/_Governing.dox index 0350d500b5..9fb7f0f50e 100644 --- a/src/core/_Governing.dox +++ b/src/core/_Governing.dox @@ -59,8 +59,8 @@ potential temperature and salinity equations, fluxes due to diabatic processes are indicated by \f$J^{(z)}\f$. Tendencies due to the convergence of fluxes oriented along neutral directions are indicated by \f$\mathbf{\mathcal{N}}^\gamma\f$, with our implementation of -neutral diffusion detailed in Shao et al (2020) \cite -Shao_etal_2020. +neutral diffusion detailed in Shao et al (2020) +\cite Shao_etal_2020. The total or material time derivative operator is given by \f{align} @@ -111,7 +111,7 @@ brings the model equations to the form \rho &= \rho(S, \theta, z) &\mbox{equation of state.} \f} -\subsection vector_invariant_eqns Vector invariant velocity equation +\section vector_invariant_eqns Vector invariant velocity equation MOM6 time steps the horizontal velocity equation in its vector-invariant form. To derive this equation we make use of the diff --git a/src/core/_Notation.dox b/src/core/_Notation.dox index 3e5ab12667..1360ab17db 100644 --- a/src/core/_Notation.dox +++ b/src/core/_Notation.dox @@ -1,6 +1,6 @@ /*! \page Notation Notation for equations -\subsubsection Symbols Symbols for variables +\section Symbols Symbols for variables \f$z\f$ refers to geopotential elevation (or height), increasing upward and with \f$z=0\f$ defining the resting ocean surface. Much of @@ -39,7 +39,7 @@ used in the Boussinesq equation of state. -\subsubsection vector_notation Vector notation +\section vector_notation Vector notation The three-dimensional velocity vector is denoted \f$\boldsymbol{v}\f$ and it is decomposed into its horizontal and vertical components according to From 06afa1c4c9fcb175a3a1a0ea026b03f4893e4b92 Mon Sep 17 00:00:00 2001 From: Stephen Griffies Date: Fri, 7 Oct 2022 12:51:19 -0400 Subject: [PATCH 48/68] correct some \cite errors --- src/core/_General_coordinate.dox | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/_General_coordinate.dox b/src/core/_General_coordinate.dox index a658d8db29..ffb0d5e757 100644 --- a/src/core/_General_coordinate.dox +++ b/src/core/_General_coordinate.dox @@ -86,8 +86,8 @@ h = \int z_r \, \mathrm{d}r. \f} Correspondingly, the model variables are treated as finite volume averages over each layer, with full accounting of this finite volume -approach presented in Griffies, Adcroft and Hallberg (2020) \cite -Griffies_Adcroft_Hallberg2020, and with the semi-discrete model +approach presented in Griffies, Adcroft and Hallberg (2020) +\cite Griffies_Adcroft_Hallberg2020, and with the semi-discrete model ocean model equations written as follows. \f{align} \rho_0 @@ -135,8 +135,8 @@ where is the discrete vertical difference operator. The pressure gradient accelerations in the momentum equation are written in continuous-in-the-vertical form for brevity; the exact discretization -is detailed in \cite adcroft2008 and \cite -Griffies_Adcroft_Hallberg2020. The \f$1/h\f$ and \f$h\f$ appearing in +is detailed in \cite adcroft2008 and +\cite Griffies_Adcroft_Hallberg2020. The \f$1/h\f$ and \f$h\f$ appearing in the horizontal momentum equation are carefully handled in the code to ensure proper cancellation even when the layer thickness goes to zero; i.e., l'Hospital's rule is respected. From b7018c6bf9a3a058819e6a153366d5816534a4c8 Mon Sep 17 00:00:00 2001 From: Stephen Griffies Date: Mon, 10 Oct 2022 20:13:37 -0400 Subject: [PATCH 49/68] edits for resolving rtd compile errors --- docs/equations.rst | 5 +++-- src/ALE/_ALE.dox | 16 ++++++++-------- src/ALE/_ALE_timestep.dox | 13 ++++++------- src/core/_General_coordinate.dox | 6 +++--- src/core/_Governing.dox | 6 +++--- src/core/_Notation.dox | 24 +++++++++++++----------- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/docs/equations.rst b/docs/equations.rst index 9d15050927..f90a8f4181 100644 --- a/docs/equations.rst +++ b/docs/equations.rst @@ -6,8 +6,9 @@ hydrostatic primitive equations (either Boussinesq or non-Boussinesq). We present the equations starting from the hydrostatic Boussinesq equation in height coordinates and progress through vector-invariant and -general-coordinate equations to the final equations used in the A.L.E. -algorithm, taken from :cite:`Adcroft2019`. +general-coordinate equations to the final equations used in the +vertical Lagrangian algorithm, taken from +:cite:`Adcroft2019` and :cite:`Griffies_Adcroft_Hallberg2020`. .. toctree:: :maxdepth: 2 diff --git a/src/ALE/_ALE.dox b/src/ALE/_ALE.dox index 4943ff5c05..b3b4f54213 100644 --- a/src/ALE/_ALE.dox +++ b/src/ALE/_ALE.dox @@ -1,6 +1,6 @@ /*! \page ALE Vertical Lagrangian method: conceptual -\section section_ALE Lagrangian and ALE +\section section_ALE Lagrangian and ALE As discussed by Adcroft and Hallberg (2008) \cite adcroft2006 and Griffies, Adcroft and Hallberg (2020) \cite Griffies_Adcroft_Hallberg2020, @@ -127,14 +127,14 @@ tracer equation (\f$C\f$ is an arbitrary tracer) can be summarized as \\ \delta_{r} w^{\scriptstyle{\mathrm{grid}}} &= -\nabla_{r} \cdot [h \, \mathbf{u}]^{(n)} - &\mbox{layer motion via convergence of horz advection} + &\mbox{layer motion via horz conv} \\ h^{\dagger} &= h^{(n)} + \Delta t \, \delta_{r} w^{\scriptstyle{\mathrm{grid}}} = h^{(n)} - \Delta t \, \nabla_{r} \cdot [h \, \mathbf{u}]^{(n)} - &\mbox{horz advection thickness update} + &\mbox{update thickness via horz advect} \\ [h \, C]^{\dagger} &= [h \, C]^{(n)} -\Delta t \, \nabla_{r} \cdot [ h \, C \, \mathbf{u} ]^{(n)} - &\mbox{horz advection tracer update} + &\mbox{update tracer via horz advect} \\ h^{(n+1)} &= h^{\scriptstyle{\mathrm{target}}} &\mbox{regrid to the target grid} @@ -143,11 +143,11 @@ tracer equation (\f$C\f$ is an arbitrary tracer) can be summarized as &\mbox{diagnose dia-surface transport} \\ [h \, C]^{(n+1)} &= [h \, C]^{\dagger} - \Delta t \, \delta_{r} ( w^{(\dot{r})} \, C^{\dagger}) - &\mbox{remap tracer using dia-surface transport} + &\mbox{remap tracer via dia-surface transport} \f} The first three equations constitute the Lagrangian portion of the algorithm. In particular, the second equation provides an -intermediate or ``predictor'' value for the updated thickness, +intermediate or predictor value for the updated thickness, \f$h^{\dagger}\f$, resulting from the vertical Lagrangian update. Similarly, the third equation performs a Lagrangian update of the thickness-weighted tracer to intermediate values, again operationally @@ -166,8 +166,8 @@ dia-surface velocity according to \f} This step, and the remaining step for tracers, constitute the remapping portion of the algorithm. For example, if the prescribed -coordinate surfaces are geopotentials, then \f$w^{(\dot{r})} = w\f$ -and \f$h^{\scriptstyle{\mathrm{target}}} = h^{(n)}\f$, in which case the +coordinate surfaces are geopotentials, then \f$w^{(\dot{r})}\f$ and +\f$h^{\scriptstyle{\mathrm{target}}} = h^{(n)}\f$, in which case the remap step reduces to Cartesian vertical advection. Within the above framework for evolving the ocean state, we make use diff --git a/src/ALE/_ALE_timestep.dox b/src/ALE/_ALE_timestep.dox index 13e95f4866..04ed495e77 100644 --- a/src/ALE/_ALE_timestep.dox +++ b/src/ALE/_ALE_timestep.dox @@ -1,6 +1,6 @@ /*! \page ALE_Timestep Vertical Lagrangian method in pictures -\section section_ALE_remap Graphical explanation of vertical Lagrangian method +\section section_ALE_remap Graphical explanation of vertical Lagrangian method Vertical Lagrangian regridding/remapping is not a timestep method in the traditional sense. Rather, it is a sequence of operations @@ -49,15 +49,14 @@ deformed coordinate grid has been deleted: The new layer thicknesses, \f$h_k\f$, are computed and then the layers are populated with the new velocities and tracers \f{align} - % h_k^{\mbox{new}} %= \nabla_k z_{\mbox{coord}} \\ - \sum h_k^{\mbox{new}} &= \sum h_k^{\mbox{old}} + \sum h_k^{\scriptstyle{\mathrm{new}}} &= \sum h_k^{\scriptstyle{\mathrm{old}}} \\ - \mathbf{u}_k^{\mbox{new}} + \mathbf{u}_k^{\scriptstyle{\mathrm{new}}} &= \frac{1}{h_k} - \int_{z_{k + \frac{1}{2}}}^{z_{k + \frac{1}{2}} + h_k} \mathbf{u}^{\mbox{old}}(z') \, \mathrm{d}z' + \int_{z_{k + 1/2}}^{z_{k + 1/2} + h_k} \mathbf{u}^{\scriptstyle{\mathrm{old}}}(z') \, \mathrm{d}z' \\ - \theta^{\mbox{new}} &= \frac{1}{h_k} - \int_{z_{k + \frac{1}{2}}}^{z_{k + \frac{1}{2}} + h_k} \theta^{\mbox{old}}(z') \, \mathrm{d}z' + \theta_k^{\scriptstyle{\mathrm{new}}} &= \frac{1}{h_k} + \int_{z_{k + 1/2}}^{z_{k + 1/2} + h_k} \theta^{\scriptstyle{\mathrm{old}}}(z') \, \mathrm{d}z' \f} */ diff --git a/src/core/_General_coordinate.dox b/src/core/_General_coordinate.dox index ffb0d5e757..6effc4717b 100644 --- a/src/core/_General_coordinate.dox +++ b/src/core/_General_coordinate.dox @@ -86,7 +86,7 @@ h = \int z_r \, \mathrm{d}r. \f} Correspondingly, the model variables are treated as finite volume averages over each layer, with full accounting of this finite volume -approach presented in Griffies, Adcroft and Hallberg (2020) +approach presented in Griffies, Adcroft and Hallberg (2020) \cite Griffies_Adcroft_Hallberg2020, and with the semi-discrete model ocean model equations written as follows. \f{align} @@ -135,10 +135,10 @@ where is the discrete vertical difference operator. The pressure gradient accelerations in the momentum equation are written in continuous-in-the-vertical form for brevity; the exact discretization -is detailed in \cite adcroft2008 and +is detailed in \cite adcroft2008 and \cite Griffies_Adcroft_Hallberg2020. The \f$1/h\f$ and \f$h\f$ appearing in the horizontal momentum equation are carefully handled in the code to -ensure proper cancellation even when the layer thickness goes to zero; +ensure proper cancellation even when the layer thickness goes to zero i.e., l'Hospital's rule is respected. The MOM6 time-stepping algorithm integrates the above layer-averaged diff --git a/src/core/_Governing.dox b/src/core/_Governing.dox index 9fb7f0f50e..466e9d957e 100644 --- a/src/core/_Governing.dox +++ b/src/core/_Governing.dox @@ -59,7 +59,7 @@ potential temperature and salinity equations, fluxes due to diabatic processes are indicated by \f$J^{(z)}\f$. Tendencies due to the convergence of fluxes oriented along neutral directions are indicated by \f$\mathbf{\mathcal{N}}^\gamma\f$, with our implementation of -neutral diffusion detailed in Shao et al (2020) +neutral diffusion detailed in Shao et al (2020) \cite Shao_etal_2020. The total or material time derivative operator is given by @@ -154,7 +154,7 @@ geopotential coordinates + (f + \zeta) \hat{\mathbf{z}} \times \mathbf{u} \right] &= -\nabla_{z} (p + K) - \rho \, \nabla_{z} \Phi + \rho_{o} \, \mathbf{\mathcal{F}} - &\mbox{vector-invariant horz velocity} + &\mbox{vector-inv horz velocity} \\ \rho \, \partial_{z} \Phi + \partial_{z} p &= 0 &\mbox{hydrostatic} \\ @@ -164,7 +164,7 @@ geopotential coordinates \\ \partial_t \theta + \nabla_z \cdotp ( \mathbf{u} \, \theta ) + \partial_z ( w \, \theta ) &= \mathbf{\mathcal{N}}_\theta^\gamma - \partial_{z} J_\theta^{(z)} - &\mbox{potential/Conservative temp} + &\mbox{potential/Cons temp} \\ \partial_t S + \nabla_z \cdotp ( \mathbf{u} \, S ) + \partial_z (w \, S) &= \mathbf{\mathcal{N}}_S^\gamma - \partial_{z} J_S^{(z)} diff --git a/src/core/_Notation.dox b/src/core/_Notation.dox index 1360ab17db..b91baac5fe 100644 --- a/src/core/_Notation.dox +++ b/src/core/_Notation.dox @@ -41,21 +41,23 @@ used in the Boussinesq equation of state. \section vector_notation Vector notation -The three-dimensional velocity vector is denoted \f$\boldsymbol{v}\f$ +The three-dimensional velocity vector is denoted \f$\mathbf{v}\f$ and it is decomposed into its horizontal and vertical components according to - \f[\boldsymbol{v} - = \boldsymbol{u} + \hat{\boldsymbol{z}} w - = \hat{\boldsymbol{x}} u + \hat{\boldsymbol{y}} v + \hat{\boldsymbol{z}} w, - \f] -where \f$\hat{\boldsymbol{z}}\f$ is the unit vector pointed in the -upward vertical direction and \f$\boldsymbol{u} = (u, v, 0)\f$ is the +\f{align} +\mathbf{v} + = \mathbf{u} + \hat{\mathbf{z}} \, w + = \hat{\mathbf{x}} \, u + \hat{\mathbf{y}} \, v + \hat{\mathbf{z}} \, w, + \f} +where \f$\hat{\mathbf{z}}\f$ is the unit vector pointed in the +upward vertical direction and \f$\mathbf{u} = (u, v, 0)\f$ is the horizontal component of velocity normal to the vertical. The three-dimensional gradient operator is denoted \f$\nabla\f$, and it is decomposed into its horizontal and vertical components according to - \f[\nabla - = \nabla_z + \hat{\boldsymbol{z}} \partial_z - = \hat{\boldsymbol{x}} \partial_x + \hat{\boldsymbol{y}} \partial_y + \hat{\boldsymbol{z}} \partial_z. - \f] +\f{align} +\nabla + = \nabla_z + \hat{\mathbf{z}} \, \partial_z + = \hat{\mathbf{x}} \, \partial_x + \hat{\mathbf{y}} \, \partial_y + \hat{\mathbf{z}} \, \partial_z. + \f} */ From 7fccd192fcb59317cc5f5aa6709bd04c10c48ed6 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 20 Jul 2022 23:47:22 -0400 Subject: [PATCH 50/68] Implement spherical harmonics SAL from MPAS-Ocean This is an initial step to add the functionality of calculating self-attraction and loading (SAL) with the spherical harmonic transform (SHT) method. The code in this commit is a direction translation and adoption of the "parallel SAL" from MPAS-Ocean model developed at the Department of Energy's Los Alamos National Laboratory (Barton et al. (2022) and Brus et al. (2022)). This commit serves as an "MVP" so that the MPAS-Ocean algorithm is functional in MOM6. Further refactor, optimization and documentation should be put in place. * A new module MOM_spherical_harmonics is introduced to house precomputed coefficients used for SHT. * New subroutines are added in module MOM_tidal_forcing for calculating SAL with the SHT method. --- .../lateral/MOM_spherical_harmonics.f90 | 153 ++ .../lateral/MOM_tidal_forcing.F90 | 1752 ++++++++++++++++- 2 files changed, 1904 insertions(+), 1 deletion(-) create mode 100644 src/parameterizations/lateral/MOM_spherical_harmonics.f90 diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 new file mode 100644 index 0000000000..c933213b55 --- /dev/null +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 @@ -0,0 +1,153 @@ +module MOM_spherical_harmonics +use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, & + CLOCK_MODULE +use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, WARNING +use MOM_file_parser, only : get_param, log_version, param_file_type +use MOM_grid, only : ocean_grid_type +use MOM_unit_scaling, only : unit_scale_type + +implicit none ; private + +public spherical_harmonics_init, spherical_harmonics_end, associatedLegendrePolynomials, SHOrderDegreeToIndex + +#include + +type, public :: sht_CS + integer :: nOrder, lmax + real, allocatable :: sinLatCell(:,:), cosLatCell(:,:) + real, allocatable :: complexFactorRe(:,:,:), complexFactorIm(:,:,:), complexExpRe(:,:,:), complexExpIm(:,:,:) + real, allocatable :: pmnm2(:,:), pmnm1(:,:), pmn(:,:) + real, allocatable :: aRecurrenceCoeff(:,:), bRecurrenceCoeff(:,:) + logical :: bfb +end type sht_CS + +contains + +subroutine spherical_harmonics_init(G, param_file, CS) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(sht_CS), intent(inout) :: CS + type(param_file_type), intent(in) :: param_file !< A structure indicating + real :: PI ! 3.1415926... calculated as 4*atan(1) + real :: RADIAN + integer :: is, ie, js, je + integer :: i, j + integer :: m, n + ! This include declares and sets the variable "version". +# include "version_variable.h" + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + PI = 4.0*atan(1.0) + RADIAN = PI / 180.0 + + call get_param(param_file, '', "SHT_ORDER", CS%nOrder, & + "Order of spherical harmonics transformation. ", default=1) + CS%lmax = (CS%nOrder + 1) * (CS%nOrder + 2) / 2 + call get_param(param_file, '', "SHT_BFB", CS%bfb, & + "If true, use bfb sum. Default is False.", default=.False.) + + allocate(CS%pmn(is:ie,js:je)) ; CS%pmn(:,:) = 0.0 + allocate(CS%pmnm1(is:ie,js:je)); CS%pmnm1(:,:) = 0.0 + allocate(CS%pmnm2(is:ie,js:je)); CS%pmnm2(:,:) = 0.0 + + ! Compute recurrence relationship coefficients + allocate(CS%aRecurrenceCoeff(CS%nOrder+1,CS%nOrder+1)); CS%aRecurrenceCoeff(:,:) = 0.0 + allocate(CS%bRecurrenceCoeff(CS%nOrder+1,CS%nOrder+1)); CS%bRecurrenceCoeff(:,:) = 0.0 + + do m = 0, CS%nOrder + do n = m, CS%nOrder + if (m /= n) then + CS%aRecurrenceCoeff(n+1,m+1) = sqrt(real((2*n-1)*(2*n+1)) / real((n-m)*(n+m))) + CS%bRecurrenceCoeff(n+1,m+1) = sqrt(real((2*n+1)*(n+m-1)*(n-m-1)) / real((n-m)*(n+m)*(2*n-3))) + endif + enddo + enddo + + ! Precompute complex exponential factors + allocate(CS%complexFactorRe(is:ie, js:je, CS%nOrder+1)); CS%complexFactorRe(:,:,:) = 0.0 + allocate(CS%complexFactorIm(is:ie, js:je, CS%nOrder+1)); CS%complexFactorIm(:,:,:) = 0.0 + allocate(CS%complexExpRe(is:ie, js:je, CS%nOrder+1)); CS%complexExpRe(:,:,:) = 0.0 + allocate(CS%complexExpIm(is:ie, js:je, CS%nOrder+1)); CS%complexExpIm(:,:,:) = 0.0 + + do m = 0, CS%nOrder + do j = js,je ; do i = is,ie + CS%complexExpRe(i, j, m+1) = cos(real(m) * (G%geolonT(i,j)*RADIAN)) + CS%complexExpIm(i, j, m+1) = sin(real(m) * (G%geolonT(i,j)*RADIAN)) + CS%complexFactorRe(i, j, m+1) = CS%complexExpRe(i, j, m+1) * G%areaT(i,j) / G%Rad_Earth**2 + CS%complexFactorIm(i, j, m+1) = CS%complexExpIm(i, j, m+1) * G%areaT(i,j) / G%Rad_Earth**2 + enddo ; enddo + enddo + + ! allocate(Snm_local(2*lmax),Snm(2*lmax)) + ! allocate(SnmRe_local(lmax),SnmRe(lmax)) + ! allocate(SnmIm_local(lmax),SnmIm(lmax)) + ! allocate(SnmIm_local_reproSum(nCellsOwned,lmax)) + ! allocate(SnmRe_local_reproSum(nCellsOwned,lmax)) + ! allocate(Snm_local_reproSum(nCellsOwned,2*lmax)) + + + ! Pre-compute sin and cos of latCell (co-latitude) values + allocate(CS%sinLatCell(is:ie,js:je)); CS%sinLatCell(:,:) = 0.0 + allocate(CS%cosLatCell(is:ie,js:je)); CS%cosLatCell(:,:) = 0.0 + do j = js,je ; do i = is,ie + CS%sinLatCell(i,j) = sin(0.5*PI - G%geolatT(i,j)*RADIAN) + CS%cosLatCell(i,j) = cos(0.5*PI - G%geolatT(i,j)*RADIAN) + enddo ; enddo +endsubroutine spherical_harmonics_init + +subroutine spherical_harmonics_end(CS) + type(sht_CS), intent(inout) :: CS + + deallocate(CS%sinLatCell, CS%cosLatCell) + deallocate(CS%complexFactorRe, CS%complexFactorIm, CS%complexExpRe, CS%complexExpIm) + deallocate(CS%pmnm2, CS%pmnm1, CS%pmn) + deallocate(CS%aRecurrenceCoeff, CS%bRecurrenceCoeff) +end subroutine spherical_harmonics_end + +subroutine associatedLegendrePolynomials(n, m, l, CS, G) + type(ocean_grid_type), intent(in) :: G + integer, intent(in) :: n + integer, intent(in) :: m + integer, intent(out) :: l + type(sht_CS), intent(inout) :: CS + ! real, dimension(:,:), intent(inout) :: pmnm2 + ! real, dimension(:,:), intent(inout) :: pmnm1 + ! real, dimension(:,:), intent(inout) :: pmn + + integer :: i, j, k + real, parameter :: PI = 4.0*atan(1.0) ! 3.1415926... calculated as 4*atan(1) + integer :: is, ie, js, je + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + l = SHOrderDegreeToIndex(n,m, CS%nOrder) + + if (n == m) then + do j = js, je ; do i = is, ie + CS%pmnm2(i,j) = sqrt(1.0/(4.0*PI)) * CS%sinLatCell(i,j)**m + do k = 1, m + CS%pmnm2(i,j) = CS%pmnm2(i,j) * sqrt(real(2*k+1)/real(2*k)) + enddo + enddo ; enddo + else if (n == m+1) then + do j = js, je ; do i = is, ie + CS%pmnm1(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosLatCell(i,j) * CS%pmnm2(i,j) + enddo ; enddo + else + do j = js, je ; do i = is, ie + CS%pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosLatCell(i,j) * CS%pmnm1(i,j) & + - CS%bRecurrenceCoeff(n+1,m+1) * CS%pmnm2(i,j) + enddo ; enddo + endif +end subroutine associatedLegendrePolynomials + +function SHOrderDegreeToIndex(n,m, nOrder) result(l)!{{{ + + integer :: l + integer :: n + integer :: m + integer :: nOrder + + l = (nOrder+1)*m - m*(m+1)/2 + n+1 + +end function SHOrderDegreeToIndex + +end module MOM_spherical_harmonics \ No newline at end of file diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index b8a9b3134c..679e8a0fc8 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -12,6 +12,10 @@ module MOM_tidal_forcing use MOM_io, only : field_exists, file_exists, MOM_read_data use MOM_time_manager, only : set_date, time_type, time_type_to_real, operator(-) use MOM_unit_scaling, only : unit_scale_type +use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end +use MOM_spherical_harmonics, only : associatedLegendrePolynomials, SHOrderDegreeToIndex +use MOM_spherical_harmonics, only : sht_CS +use MOM_coms_infra, only : sum_across_PEs implicit none ; private @@ -46,6 +50,7 @@ module MOM_tidal_forcing !! equilibrium tide. Set to false if providing tidal phases !! that have already been shifted by the !! astronomical/equilibrium argument. + logical :: tidal_sal_sht real :: sal_scalar !< The constant of proportionality between sea surface !! height (really it should be bottom pressure) anomalies !! and bottom geopotential anomalies [nondim]. @@ -70,9 +75,11 @@ module MOM_tidal_forcing cosphase_prev(:,:,:), & !< The cosine and sine of the phase of the sinphase_prev(:,:,:), & !< amphidromes in the previous tidal solutions. amp_prev(:,:,:) !< The amplitude of the previous tidal solution [Z ~> m]. + type(sht_CS) :: sht end type tidal_forcing_CS integer :: id_clock_tides !< CPU clock for tides +integer :: id_clock_SAL !< CPU clock for inline self-attration and loading contains @@ -360,6 +367,9 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) "TIDES and TIDE_USE_SAL_SCALAR are true.", units="m m-1", & fail_if_missing=.true.) + call get_param(param_file, mdl, "TIDAL_SAL_SHT", CS%tidal_sal_sht, & + "If true, use inline SAL.", default=.false.) + if (nc > MAX_CONSTITUENTS) then write(mesg,'("Increase MAX_CONSTITUENTS in MOM_tidal_forcing.F90 to at least",I3, & &"to accommodate all the registered tidal constituents.")') nc @@ -519,10 +529,1506 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) enddo endif + if (CS%tidal_sal_sht) then + call spherical_harmonics_init(G, param_file, CS%sht) + id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_MODULE) + endif + id_clock_tides = cpu_clock_id('(Ocean tides)', grain=CLOCK_MODULE) end subroutine tidal_forcing_init +subroutine getloadLoveNums(nlm, LoveScaling) !{{{ + + integer, intent(in) :: nlm + real, dimension(:), intent(out) :: LoveScaling + + real, dimension(:), allocatable :: H, L, K + real, dimension(:,:), allocatable :: LoveDat + real :: H1, L1, K1 + integer :: i, j, n, m + real, parameter :: rhoE=5517.0 ! Average density of Earth (kg/m^3) + real, parameter :: rhoW=1035.0 ! Density of water (kg/m^3) + integer, parameter :: lmax=1440 + + allocate(LoveDat(4,lmax+1)) + + LoveDat(1:4,1) = (/ 0.0, 0.0000000000, 0.0000000000, -1.0000000000 /) !{{{ + LoveDat(1:4,2) = (/ 1.0, -1.2858777580, -8.9608179370e-1, -1.0000000000 /) + LoveDat(1:4,3) = (/ 2.0, -9.9079949000e-1, 2.3286695000e-2, -3.0516104000e-1 /) + LoveDat(1:4,4) = (/ 3.0, -1.0499631000, 6.9892136000e-2, -1.9585733000e-1 /) + LoveDat(1:4,5) = (/ 4.0, -1.0526477000, 5.8670467000e-2, -1.3352284000e-1 /) + LoveDat(1:4,6) = (/ 5.0, -1.0855918000, 4.6165153000e-2, -1.0456531000e-1 /) + LoveDat(1:4,7) = (/ 6.0, -1.1431163000, 3.8586926000e-2, -9.0184841000e-2 /) + LoveDat(1:4,8) = (/ 7.0, -1.2116273000, 3.4198827000e-2, -8.1906787000e-2 /) + LoveDat(1:4,9) = (/ 8.0, -1.2831157000, 3.1474998000e-2, -7.6379141000e-2 /) + LoveDat(1:4,10) = (/ 9.0, -1.3538554000, 2.9624407000e-2, -7.2250183000e-2 /) + LoveDat(1:4,11) = (/ 10.0, -1.4223516000, 2.8273961000e-2, -6.8934145000e-2 /) + LoveDat(1:4,12) = (/ 11.0, -1.4881117000, 2.7242278000e-2, -6.6147992000e-2 /) + LoveDat(1:4,13) = (/ 12.0, -1.5510428000, 2.6431124000e-2, -6.3736253000e-2 /) + LoveDat(1:4,14) = (/ 13.0, -1.6111895000, 2.5779507000e-2, -6.1602870000e-2 /) + LoveDat(1:4,15) = (/ 14.0, -1.6686329000, 2.5245139000e-2, -5.9683159000e-2 /) + LoveDat(1:4,16) = (/ 15.0, -1.7234569000, 2.4796803000e-2, -5.7931180000e-2 /) + LoveDat(1:4,17) = (/ 16.0, -1.7757418000, 2.4410861000e-2, -5.6313294000e-2 /) + LoveDat(1:4,18) = (/ 17.0, -1.8255646000, 2.4069336000e-2, -5.4804452000e-2 /) + LoveDat(1:4,19) = (/ 18.0, -1.8730019000, 2.3758645000e-2, -5.3385807000e-2 /) + LoveDat(1:4,20) = (/ 19.0, -1.9181321000, 2.3468646000e-2, -5.2043088000e-2 /) + LoveDat(1:4,21) = (/ 20.0, -1.9610366000, 2.3191893000e-2, -5.0765423000e-2 /) + LoveDat(1:4,22) = (/ 21.0, -2.0018000000, 2.2923032000e-2, -4.9544487000e-2 /) + LoveDat(1:4,23) = (/ 22.0, -2.0405101000, 2.2658321000e-2, -4.8373866000e-2 /) + LoveDat(1:4,24) = (/ 23.0, -2.0772571000, 2.2395242000e-2, -4.7248575000e-2 /) + LoveDat(1:4,25) = (/ 24.0, -2.1121328000, 2.2132200000e-2, -4.6164708000e-2 /) + LoveDat(1:4,26) = (/ 25.0, -2.1452296000, 2.1868280000e-2, -4.5119160000e-2 /) + LoveDat(1:4,27) = (/ 26.0, -2.1766398000, 2.1603063000e-2, -4.4109431000e-2 /) + LoveDat(1:4,28) = (/ 27.0, -2.2064546000, 2.1336479000e-2, -4.3133464000e-2 /) + LoveDat(1:4,29) = (/ 28.0, -2.2347634000, 2.1068700000e-2, -4.2189540000e-2 /) + LoveDat(1:4,30) = (/ 29.0, -2.2616531000, 2.0800053000e-2, -4.1276184000e-2 /) + LoveDat(1:4,31) = (/ 30.0, -2.2872080000, 2.0530962000e-2, -4.0392105000e-2 /) + LoveDat(1:4,32) = (/ 31.0, -2.3115088000, 2.0261897000e-2, -3.9536148000e-2 /) + LoveDat(1:4,33) = (/ 32.0, -2.3346328000, 1.9993346000e-2, -3.8707260000e-2 /) + LoveDat(1:4,34) = (/ 33.0, -2.3566536000, 1.9725790000e-2, -3.7904463000e-2 /) + LoveDat(1:4,35) = (/ 34.0, -2.3776409000, 1.9459686000e-2, -3.7126837000e-2 /) + LoveDat(1:4,36) = (/ 35.0, -2.3976605000, 1.9195459000e-2, -3.6373510000e-2 /) + LoveDat(1:4,37) = (/ 36.0, -2.4167746000, 1.8933494000e-2, -3.5643644000e-2 /) + LoveDat(1:4,38) = (/ 37.0, -2.4350414000, 1.8674136000e-2, -3.4936432000e-2 /) + LoveDat(1:4,39) = (/ 38.0, -2.4525156000, 1.8417687000e-2, -3.4251094000e-2 /) + LoveDat(1:4,40) = (/ 39.0, -2.4692484000, 1.8164407000e-2, -3.3586873000e-2 /) + LoveDat(1:4,41) = (/ 40.0, -2.4852876000, 1.7914518000e-2, -3.2943035000e-2 /) + LoveDat(1:4,42) = (/ 41.0, -2.5006779000, 1.7668203000e-2, -3.2318866000e-2 /) + LoveDat(1:4,43) = (/ 42.0, -2.5154609000, 1.7425613000e-2, -3.1713675000e-2 /) + LoveDat(1:4,44) = (/ 43.0, -2.5296755000, 1.7186866000e-2, -3.1126789000e-2 /) + LoveDat(1:4,45) = (/ 44.0, -2.5433577000, 1.6952053000e-2, -3.0557557000e-2 /) + LoveDat(1:4,46) = (/ 45.0, -2.5565412000, 1.6721240000e-2, -3.0005347000e-2 /) + LoveDat(1:4,47) = (/ 46.0, -2.5692574000, 1.6494470000e-2, -2.9469547000e-2 /) + LoveDat(1:4,48) = (/ 47.0, -2.5815353000, 1.6271769000e-2, -2.8949568000e-2 /) + LoveDat(1:4,49) = (/ 48.0, -2.5934022000, 1.6053144000e-2, -2.8444838000e-2 /) + LoveDat(1:4,50) = (/ 49.0, -2.6048833000, 1.5838586000e-2, -2.7954806000e-2 /) + LoveDat(1:4,51) = (/ 50.0, -2.6160021000, 1.5628077000e-2, -2.7478940000e-2 /) + LoveDat(1:4,52) = (/ 51.0, -2.6267805000, 1.5421585000e-2, -2.7016729000e-2 /) + LoveDat(1:4,53) = (/ 52.0, -2.6372389000, 1.5219071000e-2, -2.6567679000e-2 /) + LoveDat(1:4,54) = (/ 53.0, -2.6473964000, 1.5020486000e-2, -2.6131317000e-2 /) + LoveDat(1:4,55) = (/ 54.0, -2.6572706000, 1.4825779000e-2, -2.5707185000e-2 /) + LoveDat(1:4,56) = (/ 55.0, -2.6668781000, 1.4634888000e-2, -2.5294846000e-2 /) + LoveDat(1:4,57) = (/ 56.0, -2.6762345000, 1.4447752000e-2, -2.4893877000e-2 /) + LoveDat(1:4,58) = (/ 57.0, -2.6853540000, 1.4264303000e-2, -2.4503874000e-2 /) + LoveDat(1:4,59) = (/ 58.0, -2.6942503000, 1.4084474000e-2, -2.4124449000e-2 /) + LoveDat(1:4,60) = (/ 59.0, -2.7029358000, 1.3908192000e-2, -2.3755228000e-2 /) + LoveDat(1:4,61) = (/ 60.0, -2.7114225000, 1.3735386000e-2, -2.3395852000e-2 /) + LoveDat(1:4,62) = (/ 61.0, -2.7197214000, 1.3565983000e-2, -2.3045980000e-2 /) + LoveDat(1:4,63) = (/ 62.0, -2.7278428000, 1.3399909000e-2, -2.2705280000e-2 /) + LoveDat(1:4,64) = (/ 63.0, -2.7357965000, 1.3237092000e-2, -2.2373437000e-2 /) + LoveDat(1:4,65) = (/ 64.0, -2.7435916000, 1.3077458000e-2, -2.2050147000e-2 /) + LoveDat(1:4,66) = (/ 65.0, -2.7512366000, 1.2920935000e-2, -2.1735119000e-2 /) + LoveDat(1:4,67) = (/ 66.0, -2.7587397000, 1.2767451000e-2, -2.1428073000e-2 /) + LoveDat(1:4,68) = (/ 67.0, -2.7661083000, 1.2616936000e-2, -2.1128742000e-2 /) + LoveDat(1:4,69) = (/ 68.0, -2.7733496000, 1.2469319000e-2, -2.0836869000e-2 /) + LoveDat(1:4,70) = (/ 69.0, -2.7804703000, 1.2324532000e-2, -2.0552206000e-2 /) + LoveDat(1:4,71) = (/ 70.0, -2.7874767000, 1.2182508000e-2, -2.0274516000e-2 /) + LoveDat(1:4,72) = (/ 71.0, -2.7943748000, 1.2043181000e-2, -2.0003572000e-2 /) + LoveDat(1:4,73) = (/ 72.0, -2.8011702000, 1.1906487000e-2, -1.9739156000e-2 /) + LoveDat(1:4,74) = (/ 73.0, -2.8078682000, 1.1772362000e-2, -1.9481058000e-2 /) + LoveDat(1:4,75) = (/ 74.0, -2.8144738000, 1.1640746000e-2, -1.9229076000e-2 /) + LoveDat(1:4,76) = (/ 75.0, -2.8209918000, 1.1511578000e-2, -1.8983017000e-2 /) + LoveDat(1:4,77) = (/ 76.0, -2.8274266000, 1.1384799000e-2, -1.8742695000e-2 /) + LoveDat(1:4,78) = (/ 77.0, -2.8337824000, 1.1260352000e-2, -1.8507931000e-2 /) + LoveDat(1:4,79) = (/ 78.0, -2.8400633000, 1.1138183000e-2, -1.8278553000e-2 /) + LoveDat(1:4,80) = (/ 79.0, -2.8462730000, 1.1018236000e-2, -1.8054395000e-2 /) + LoveDat(1:4,81) = (/ 80.0, -2.8524152000, 1.0900460000e-2, -1.7835300000e-2 /) + LoveDat(1:4,82) = (/ 81.0, -2.8584932000, 1.0784802000e-2, -1.7621113000e-2 /) + LoveDat(1:4,83) = (/ 82.0, -2.8645103000, 1.0671213000e-2, -1.7411688000e-2 /) + LoveDat(1:4,84) = (/ 83.0, -2.8704696000, 1.0559645000e-2, -1.7206882000e-2 /) + LoveDat(1:4,85) = (/ 84.0, -2.8763739000, 1.0450051000e-2, -1.7006560000e-2 /) + LoveDat(1:4,86) = (/ 85.0, -2.8822260000, 1.0342384000e-2, -1.6810590000e-2 /) + LoveDat(1:4,87) = (/ 86.0, -2.8880285000, 1.0236599000e-2, -1.6618845000e-2 /) + LoveDat(1:4,88) = (/ 87.0, -2.8937839000, 1.0132655000e-2, -1.6431203000e-2 /) + LoveDat(1:4,89) = (/ 88.0, -2.8994945000, 1.0030508000e-2, -1.6247547000e-2 /) + LoveDat(1:4,90) = (/ 89.0, -2.9051627000, 9.9301169000e-3, -1.6067762000e-2 /) + LoveDat(1:4,91) = (/ 90.0, -2.9107905000, 9.8314429000e-3, -1.5891741000e-2 /) + LoveDat(1:4,92) = (/ 91.0, -2.9163799000, 9.7344467000e-3, -1.5719376000e-2 /) + LoveDat(1:4,93) = (/ 92.0, -2.9219330000, 9.6390907000e-3, -1.5550567000e-2 /) + LoveDat(1:4,94) = (/ 93.0, -2.9274514000, 9.5453383000e-3, -1.5385215000e-2 /) + LoveDat(1:4,95) = (/ 94.0, -2.9329370000, 9.4531538000e-3, -1.5223225000e-2 /) + LoveDat(1:4,96) = (/ 95.0, -2.9383913000, 9.3625026000e-3, -1.5064506000e-2 /) + LoveDat(1:4,97) = (/ 96.0, -2.9438161000, 9.2733509000e-3, -1.4908968000e-2 /) + LoveDat(1:4,98) = (/ 97.0, -2.9492127000, 9.1856660000e-3, -1.4756526000e-2 /) + LoveDat(1:4,99) = (/ 98.0, -2.9545826000, 9.0994159000e-3, -1.4607099000e-2 /) + LoveDat(1:4,100) = (/ 99.0, -2.9599272000, 9.0145695000e-3, -1.4460604000e-2 /) + LoveDat(1:4,101) = (/ 100.0, -2.9652476000, 8.9310967000e-3, -1.4316967000e-2 /) + LoveDat(1:4,102) = (/ 101.0, -2.9705453000, 8.8489681000e-3, -1.4176111000e-2 /) + LoveDat(1:4,103) = (/ 102.0, -2.9758213000, 8.7681548000e-3, -1.4037965000e-2 /) + LoveDat(1:4,104) = (/ 103.0, -2.9810767000, 8.6886292000e-3, -1.3902458000e-2 /) + LoveDat(1:4,105) = (/ 104.0, -2.9863125000, 8.6103640000e-3, -1.3769523000e-2 /) + LoveDat(1:4,106) = (/ 105.0, -2.9915299000, 8.5333328000e-3, -1.3639094000e-2 /) + LoveDat(1:4,107) = (/ 106.0, -2.9967298000, 8.4575097000e-3, -1.3511108000e-2 /) + LoveDat(1:4,108) = (/ 107.0, -3.0019129000, 8.3828699000e-3, -1.3385503000e-2 /) + LoveDat(1:4,109) = (/ 108.0, -3.0070803000, 8.3093886000e-3, -1.3262220000e-2 /) + LoveDat(1:4,110) = (/ 109.0, -3.0122328000, 8.2370423000e-3, -1.3141201000e-2 /) + LoveDat(1:4,111) = (/ 110.0, -3.0173710000, 8.1658076000e-3, -1.3022390000e-2 /) + LoveDat(1:4,112) = (/ 111.0, -3.0224958000, 8.0956619000e-3, -1.2905734000e-2 /) + LoveDat(1:4,113) = (/ 112.0, -3.0276079000, 8.0265832000e-3, -1.2791179000e-2 /) + LoveDat(1:4,114) = (/ 113.0, -3.0327080000, 7.9585500000e-3, -1.2678675000e-2 /) + LoveDat(1:4,115) = (/ 114.0, -3.0377966000, 7.8915413000e-3, -1.2568172000e-2 /) + LoveDat(1:4,116) = (/ 115.0, -3.0428744000, 7.8255367000e-3, -1.2459622000e-2 /) + LoveDat(1:4,117) = (/ 116.0, -3.0479420000, 7.7605163000e-3, -1.2352979000e-2 /) + LoveDat(1:4,118) = (/ 117.0, -3.0529999000, 7.6964606000e-3, -1.2248198000e-2 /) + LoveDat(1:4,119) = (/ 118.0, -3.0580486000, 7.6333507000e-3, -1.2145235000e-2 /) + LoveDat(1:4,120) = (/ 119.0, -3.0630887000, 7.5711680000e-3, -1.2044048000e-2 /) + LoveDat(1:4,121) = (/ 120.0, -3.0681205000, 7.5098946000e-3, -1.1944594000e-2 /) + LoveDat(1:4,122) = (/ 121.0, -3.0731446000, 7.4495128000e-3, -1.1846835000e-2 /) + LoveDat(1:4,123) = (/ 122.0, -3.0781614000, 7.3900054000e-3, -1.1750732000e-2 /) + LoveDat(1:4,124) = (/ 123.0, -3.0831713000, 7.3313557000e-3, -1.1656245000e-2 /) + LoveDat(1:4,125) = (/ 124.0, -3.0881747000, 7.2735474000e-3, -1.1563340000e-2 /) + LoveDat(1:4,126) = (/ 125.0, -3.0931718000, 7.2165644000e-3, -1.1471980000e-2 /) + LoveDat(1:4,127) = (/ 126.0, -3.0981632000, 7.1603911000e-3, -1.1382130000e-2 /) + LoveDat(1:4,128) = (/ 127.0, -3.1031490000, 7.1050124000e-3, -1.1293757000e-2 /) + LoveDat(1:4,129) = (/ 128.0, -3.1081296000, 7.0504134000e-3, -1.1206828000e-2 /) + LoveDat(1:4,130) = (/ 129.0, -3.1131054000, 6.9965795000e-3, -1.1121311000e-2 /) + LoveDat(1:4,131) = (/ 130.0, -3.1180765000, 6.9434967000e-3, -1.1037175000e-2 /) + LoveDat(1:4,132) = (/ 131.0, -3.1230433000, 6.8911509000e-3, -1.0954391000e-2 /) + LoveDat(1:4,133) = (/ 132.0, -3.1280059000, 6.8395288000e-3, -1.0872928000e-2 /) + LoveDat(1:4,134) = (/ 133.0, -3.1329647000, 6.7886171000e-3, -1.0792758000e-2 /) + LoveDat(1:4,135) = (/ 134.0, -3.1379199000, 6.7384029000e-3, -1.0713853000e-2 /) + LoveDat(1:4,136) = (/ 135.0, -3.1428716000, 6.6888735000e-3, -1.0636187000e-2 /) + LoveDat(1:4,137) = (/ 136.0, -3.1478201000, 6.6400168000e-3, -1.0559733000e-2 /) + LoveDat(1:4,138) = (/ 137.0, -3.1527656000, 6.5918206000e-3, -1.0484466000e-2 /) + LoveDat(1:4,139) = (/ 138.0, -3.1577082000, 6.5442732000e-3, -1.0410360000e-2 /) + LoveDat(1:4,140) = (/ 139.0, -3.1626481000, 6.4973631000e-3, -1.0337392000e-2 /) + LoveDat(1:4,141) = (/ 140.0, -3.1675855000, 6.4510790000e-3, -1.0265537000e-2 /) + LoveDat(1:4,142) = (/ 141.0, -3.1725205000, 6.4054099000e-3, -1.0194773000e-2 /) + LoveDat(1:4,143) = (/ 142.0, -3.1774533000, 6.3603452000e-3, -1.0125078000e-2 /) + LoveDat(1:4,144) = (/ 143.0, -3.1823840000, 6.3158742000e-3, -1.0056429000e-2 /) + LoveDat(1:4,145) = (/ 144.0, -3.1873127000, 6.2719868000e-3, -9.9888045000e-3 /) + LoveDat(1:4,146) = (/ 145.0, -3.1922396000, 6.2286729000e-3, -9.9221850000e-3 /) + LoveDat(1:4,147) = (/ 146.0, -3.1971648000, 6.1859227000e-3, -9.8565496000e-3 /) + LoveDat(1:4,148) = (/ 147.0, -3.2020883000, 6.1437265000e-3, -9.7918788000e-3 /) + LoveDat(1:4,149) = (/ 148.0, -3.2070102000, 6.1020749000e-3, -9.7281532000e-3 /) + LoveDat(1:4,150) = (/ 149.0, -3.2119308000, 6.0609589000e-3, -9.6653542000e-3 /) + LoveDat(1:4,151) = (/ 150.0, -3.2168500000, 6.0203693000e-3, -9.6034635000e-3 /) + LoveDat(1:4,152) = (/ 151.0, -3.2217679000, 5.9802974000e-3, -9.5424633000e-3 /) + LoveDat(1:4,153) = (/ 152.0, -3.2266847000, 5.9407346000e-3, -9.4823362000e-3 /) + LoveDat(1:4,154) = (/ 153.0, -3.2316003000, 5.9016724000e-3, -9.4230652000e-3 /) + LoveDat(1:4,155) = (/ 154.0, -3.2365149000, 5.8631026000e-3, -9.3646338000e-3 /) + LoveDat(1:4,156) = (/ 155.0, -3.2414284000, 5.8250172000e-3, -9.3070259000e-3 /) + LoveDat(1:4,157) = (/ 156.0, -3.2463411000, 5.7874081000e-3, -9.2502257000e-3 /) + LoveDat(1:4,158) = (/ 157.0, -3.2512529000, 5.7502678000e-3, -9.1942178000e-3 /) + LoveDat(1:4,159) = (/ 158.0, -3.2561639000, 5.7135886000e-3, -9.1389873000e-3 /) + LoveDat(1:4,160) = (/ 159.0, -3.2610741000, 5.6773630000e-3, -9.0845194000e-3 /) + LoveDat(1:4,161) = (/ 160.0, -3.2659835000, 5.6415839000e-3, -9.0308000000e-3 /) + LoveDat(1:4,162) = (/ 161.0, -3.2708923000, 5.6062442000e-3, -8.9778149000e-3 /) + LoveDat(1:4,163) = (/ 162.0, -3.2758004000, 5.5713368000e-3, -8.9255506000e-3 /) + LoveDat(1:4,164) = (/ 163.0, -3.2807079000, 5.5368550000e-3, -8.8739938000e-3 /) + LoveDat(1:4,165) = (/ 164.0, -3.2856148000, 5.5027920000e-3, -8.8231314000e-3 /) + LoveDat(1:4,166) = (/ 165.0, -3.2905211000, 5.4691413000e-3, -8.7729507000e-3 /) + LoveDat(1:4,167) = (/ 166.0, -3.2954269000, 5.4358966000e-3, -8.7234394000e-3 /) + LoveDat(1:4,168) = (/ 167.0, -3.3003322000, 5.4030515000e-3, -8.6745852000e-3 /) + LoveDat(1:4,169) = (/ 168.0, -3.3052370000, 5.3705998000e-3, -8.6263763000e-3 /) + LoveDat(1:4,170) = (/ 169.0, -3.3101414000, 5.3385356000e-3, -8.5788012000e-3 /) + LoveDat(1:4,171) = (/ 170.0, -3.3150452000, 5.3068529000e-3, -8.5318484000e-3 /) + LoveDat(1:4,172) = (/ 171.0, -3.3199486000, 5.2755459000e-3, -8.4855070000e-3 /) + LoveDat(1:4,173) = (/ 172.0, -3.3248516000, 5.2446089000e-3, -8.4397661000e-3 /) + LoveDat(1:4,174) = (/ 173.0, -3.3297541000, 5.2140364000e-3, -8.3946150000e-3 /) + LoveDat(1:4,175) = (/ 174.0, -3.3346563000, 5.1838229000e-3, -8.3500435000e-3 /) + LoveDat(1:4,176) = (/ 175.0, -3.3395580000, 5.1539630000e-3, -8.3060415000e-3 /) + LoveDat(1:4,177) = (/ 176.0, -3.3444593000, 5.1244515000e-3, -8.2625990000e-3 /) + LoveDat(1:4,178) = (/ 177.0, -3.3493602000, 5.0952833000e-3, -8.2197063000e-3 /) + LoveDat(1:4,179) = (/ 178.0, -3.3542607000, 5.0664532000e-3, -8.1773539000e-3 /) + LoveDat(1:4,180) = (/ 179.0, -3.3591609000, 5.0379563000e-3, -8.1355327000e-3 /) + LoveDat(1:4,181) = (/ 180.0, -3.3640606000, 5.0097879000e-3, -8.0942335000e-3 /) + LoveDat(1:4,182) = (/ 181.0, -3.3689599000, 4.9819430000e-3, -8.0534474000e-3 /) + LoveDat(1:4,183) = (/ 182.0, -3.3738588000, 4.9544170000e-3, -8.0131658000e-3 /) + LoveDat(1:4,184) = (/ 183.0, -3.3787572000, 4.9272053000e-3, -7.9733801000e-3 /) + LoveDat(1:4,185) = (/ 184.0, -3.3836553000, 4.9003034000e-3, -7.9340821000e-3 /) + LoveDat(1:4,186) = (/ 185.0, -3.3885529000, 4.8737069000e-3, -7.8952635000e-3 /) + LoveDat(1:4,187) = (/ 186.0, -3.3934501000, 4.8474114000e-3, -7.8569164000e-3 /) + LoveDat(1:4,188) = (/ 187.0, -3.3983469000, 4.8214127000e-3, -7.8190330000e-3 /) + LoveDat(1:4,189) = (/ 188.0, -3.4032432000, 4.7957066000e-3, -7.7816057000e-3 /) + LoveDat(1:4,190) = (/ 189.0, -3.4081390000, 4.7702889000e-3, -7.7446269000e-3 /) + LoveDat(1:4,191) = (/ 190.0, -3.4130344000, 4.7451557000e-3, -7.7080893000e-3 /) + LoveDat(1:4,192) = (/ 191.0, -3.4179292000, 4.7203030000e-3, -7.6719857000e-3 /) + LoveDat(1:4,193) = (/ 192.0, -3.4228236000, 4.6957268000e-3, -7.6363091000e-3 /) + LoveDat(1:4,194) = (/ 193.0, -3.4277174000, 4.6714235000e-3, -7.6010526000e-3 /) + LoveDat(1:4,195) = (/ 194.0, -3.4326107000, 4.6473891000e-3, -7.5662095000e-3 /) + LoveDat(1:4,196) = (/ 195.0, -3.4375035000, 4.6236200000e-3, -7.5317730000e-3 /) + LoveDat(1:4,197) = (/ 196.0, -3.4423957000, 4.6001126000e-3, -7.4977367000e-3 /) + LoveDat(1:4,198) = (/ 197.0, -3.4472873000, 4.5768634000e-3, -7.4640943000e-3 /) + LoveDat(1:4,199) = (/ 198.0, -3.4521783000, 4.5538688000e-3, -7.4308395000e-3 /) + LoveDat(1:4,200) = (/ 199.0, -3.4570687000, 4.5311254000e-3, -7.3979662000e-3 /) + LoveDat(1:4,201) = (/ 200.0, -3.4619585000, 4.5086298000e-3, -7.3654685000e-3 /) + LoveDat(1:4,202) = (/ 201.0, -3.4668476000, 4.4863788000e-3, -7.3333403000e-3 /) + LoveDat(1:4,203) = (/ 202.0, -3.4717360000, 4.4643689000e-3, -7.3015761000e-3 /) + LoveDat(1:4,204) = (/ 203.0, -3.4766237000, 4.4425971000e-3, -7.2701701000e-3 /) + LoveDat(1:4,205) = (/ 204.0, -3.4815107000, 4.4210601000e-3, -7.2391168000e-3 /) + LoveDat(1:4,206) = (/ 205.0, -3.4863970000, 4.3997550000e-3, -7.2084108000e-3 /) + LoveDat(1:4,207) = (/ 206.0, -3.4912825000, 4.3786785000e-3, -7.1780467000e-3 /) + LoveDat(1:4,208) = (/ 207.0, -3.4961672000, 4.3578278000e-3, -7.1480193000e-3 /) + LoveDat(1:4,209) = (/ 208.0, -3.5010512000, 4.3371999000e-3, -7.1183236000e-3 /) + LoveDat(1:4,210) = (/ 209.0, -3.5059343000, 4.3167918000e-3, -7.0889544000e-3 /) + LoveDat(1:4,211) = (/ 210.0, -3.5108165000, 4.2966008000e-3, -7.0599068000e-3 /) + LoveDat(1:4,212) = (/ 211.0, -3.5156979000, 4.2766239000e-3, -7.0311760000e-3 /) + LoveDat(1:4,213) = (/ 212.0, -3.5205784000, 4.2568586000e-3, -7.0027573000e-3 /) + LoveDat(1:4,214) = (/ 213.0, -3.5254580000, 4.2373019000e-3, -6.9746460000e-3 /) + LoveDat(1:4,215) = (/ 214.0, -3.5303366000, 4.2179514000e-3, -6.9468375000e-3 /) + LoveDat(1:4,216) = (/ 215.0, -3.5352143000, 4.1988043000e-3, -6.9193272000e-3 /) + LoveDat(1:4,217) = (/ 216.0, -3.5400909000, 4.1798580000e-3, -6.8921109000e-3 /) + LoveDat(1:4,218) = (/ 217.0, -3.5449666000, 4.1611101000e-3, -6.8651842000e-3 /) + LoveDat(1:4,219) = (/ 218.0, -3.5498412000, 4.1425580000e-3, -6.8385428000e-3 /) + LoveDat(1:4,220) = (/ 219.0, -3.5547147000, 4.1241992000e-3, -6.8121826000e-3 /) + LoveDat(1:4,221) = (/ 220.0, -3.5595871000, 4.1060313000e-3, -6.7860995000e-3 /) + LoveDat(1:4,222) = (/ 221.0, -3.5644584000, 4.0880520000e-3, -6.7602894000e-3 /) + LoveDat(1:4,223) = (/ 222.0, -3.5693286000, 4.0702588000e-3, -6.7347484000e-3 /) + LoveDat(1:4,224) = (/ 223.0, -3.5741976000, 4.0526495000e-3, -6.7094726000e-3 /) + LoveDat(1:4,225) = (/ 224.0, -3.5790654000, 4.0352217000e-3, -6.6844583000e-3 /) + LoveDat(1:4,226) = (/ 225.0, -3.5839320000, 4.0179733000e-3, -6.6597016000e-3 /) + LoveDat(1:4,227) = (/ 226.0, -3.5887973000, 4.0009020000e-3, -6.6351989000e-3 /) + LoveDat(1:4,228) = (/ 227.0, -3.5936613000, 3.9840057000e-3, -6.6109466000e-3 /) + LoveDat(1:4,229) = (/ 228.0, -3.5985240000, 3.9672821000e-3, -6.5869411000e-3 /) + LoveDat(1:4,230) = (/ 229.0, -3.6033854000, 3.9507293000e-3, -6.5631791000e-3 /) + LoveDat(1:4,231) = (/ 230.0, -3.6082455000, 3.9343450000e-3, -6.5396569000e-3 /) + LoveDat(1:4,232) = (/ 231.0, -3.6131041000, 3.9181273000e-3, -6.5163713000e-3 /) + LoveDat(1:4,233) = (/ 232.0, -3.6179613000, 3.9020742000e-3, -6.4933190000e-3 /) + LoveDat(1:4,234) = (/ 233.0, -3.6228171000, 3.8861836000e-3, -6.4704966000e-3 /) + LoveDat(1:4,235) = (/ 234.0, -3.6276714000, 3.8704536000e-3, -6.4479012000e-3 /) + LoveDat(1:4,236) = (/ 235.0, -3.6325242000, 3.8548822000e-3, -6.4255293000e-3 /) + LoveDat(1:4,237) = (/ 236.0, -3.6373754000, 3.8394677000e-3, -6.4033781000e-3 /) + LoveDat(1:4,238) = (/ 237.0, -3.6422252000, 3.8242080000e-3, -6.3814445000e-3 /) + LoveDat(1:4,239) = (/ 238.0, -3.6470733000, 3.8091013000e-3, -6.3597254000e-3 /) + LoveDat(1:4,240) = (/ 239.0, -3.6519198000, 3.7941458000e-3, -6.3382179000e-3 /) + LoveDat(1:4,241) = (/ 240.0, -3.6567647000, 3.7793398000e-3, -6.3169193000e-3 /) + LoveDat(1:4,242) = (/ 241.0, -3.6616079000, 3.7646814000e-3, -6.2958265000e-3 /) + LoveDat(1:4,243) = (/ 242.0, -3.6664494000, 3.7501690000e-3, -6.2749370000e-3 /) + LoveDat(1:4,244) = (/ 243.0, -3.6712891000, 3.7358007000e-3, -6.2542478000e-3 /) + LoveDat(1:4,245) = (/ 244.0, -3.6761271000, 3.7215749000e-3, -6.2337563000e-3 /) + LoveDat(1:4,246) = (/ 245.0, -3.6809634000, 3.7074899000e-3, -6.2134599000e-3 /) + LoveDat(1:4,247) = (/ 246.0, -3.6857978000, 3.6935441000e-3, -6.1933559000e-3 /) + LoveDat(1:4,248) = (/ 247.0, -3.6906303000, 3.6797359000e-3, -6.1734419000e-3 /) + LoveDat(1:4,249) = (/ 248.0, -3.6954610000, 3.6660636000e-3, -6.1537152000e-3 /) + LoveDat(1:4,250) = (/ 249.0, -3.7002898000, 3.6525257000e-3, -6.1341734000e-3 /) + LoveDat(1:4,251) = (/ 250.0, -3.7051167000, 3.6391206000e-3, -6.1148140000e-3 /) + LoveDat(1:4,252) = (/ 251.0, -3.7099416000, 3.6258468000e-3, -6.0956346000e-3 /) + LoveDat(1:4,253) = (/ 252.0, -3.7147645000, 3.6127027000e-3, -6.0766330000e-3 /) + LoveDat(1:4,254) = (/ 253.0, -3.7195854000, 3.5996869000e-3, -6.0578067000e-3 /) + LoveDat(1:4,255) = (/ 254.0, -3.7244043000, 3.5867979000e-3, -6.0391534000e-3 /) + LoveDat(1:4,256) = (/ 255.0, -3.7292211000, 3.5740342000e-3, -6.0206710000e-3 /) + LoveDat(1:4,257) = (/ 256.0, -3.7340357000, 3.5613944000e-3, -6.0023572000e-3 /) + LoveDat(1:4,258) = (/ 257.0, -3.7388483000, 3.5488772000e-3, -5.9842098000e-3 /) + LoveDat(1:4,259) = (/ 258.0, -3.7436587000, 3.5364810000e-3, -5.9662266000e-3 /) + LoveDat(1:4,260) = (/ 259.0, -3.7484669000, 3.5242045000e-3, -5.9484056000e-3 /) + LoveDat(1:4,261) = (/ 260.0, -3.7532729000, 3.5120464000e-3, -5.9307447000e-3 /) + LoveDat(1:4,262) = (/ 261.0, -3.7580766000, 3.5000053000e-3, -5.9132419000e-3 /) + LoveDat(1:4,263) = (/ 262.0, -3.7628780000, 3.4880799000e-3, -5.8958950000e-3 /) + LoveDat(1:4,264) = (/ 263.0, -3.7676772000, 3.4762689000e-3, -5.8787022000e-3 /) + LoveDat(1:4,265) = (/ 264.0, -3.7724740000, 3.4645710000e-3, -5.8616614000e-3 /) + LoveDat(1:4,266) = (/ 265.0, -3.7772685000, 3.4529849000e-3, -5.8447709000e-3 /) + LoveDat(1:4,267) = (/ 266.0, -3.7820605000, 3.4415093000e-3, -5.8280285000e-3 /) + LoveDat(1:4,268) = (/ 267.0, -3.7868501000, 3.4301431000e-3, -5.8114326000e-3 /) + LoveDat(1:4,269) = (/ 268.0, -3.7916373000, 3.4188851000e-3, -5.7949812000e-3 /) + LoveDat(1:4,270) = (/ 269.0, -3.7964220000, 3.4077339000e-3, -5.7786726000e-3 /) + LoveDat(1:4,271) = (/ 270.0, -3.8012042000, 3.3966884000e-3, -5.7625050000e-3 /) + LoveDat(1:4,272) = (/ 271.0, -3.8059839000, 3.3857475000e-3, -5.7464766000e-3 /) + LoveDat(1:4,273) = (/ 272.0, -3.8107610000, 3.3749099000e-3, -5.7305857000e-3 /) + LoveDat(1:4,274) = (/ 273.0, -3.8155355000, 3.3641746000e-3, -5.7148305000e-3 /) + LoveDat(1:4,275) = (/ 274.0, -3.8203074000, 3.3535404000e-3, -5.6992095000e-3 /) + LoveDat(1:4,276) = (/ 275.0, -3.8250766000, 3.3430061000e-3, -5.6837210000e-3 /) + LoveDat(1:4,277) = (/ 276.0, -3.8298432000, 3.3325707000e-3, -5.6683633000e-3 /) + LoveDat(1:4,278) = (/ 277.0, -3.8346070000, 3.3222331000e-3, -5.6531348000e-3 /) + LoveDat(1:4,279) = (/ 278.0, -3.8393682000, 3.3119922000e-3, -5.6380340000e-3 /) + LoveDat(1:4,280) = (/ 279.0, -3.8441265000, 3.3018470000e-3, -5.6230593000e-3 /) + LoveDat(1:4,281) = (/ 280.0, -3.8488821000, 3.2917964000e-3, -5.6082092000e-3 /) + LoveDat(1:4,282) = (/ 281.0, -3.8536348000, 3.2818393000e-3, -5.5934822000e-3 /) + LoveDat(1:4,283) = (/ 282.0, -3.8583847000, 3.2719748000e-3, -5.5788767000e-3 /) + LoveDat(1:4,284) = (/ 283.0, -3.8631317000, 3.2622018000e-3, -5.5643913000e-3 /) + LoveDat(1:4,285) = (/ 284.0, -3.8678759000, 3.2525193000e-3, -5.5500246000e-3 /) + LoveDat(1:4,286) = (/ 285.0, -3.8726170000, 3.2429264000e-3, -5.5357752000e-3 /) + LoveDat(1:4,287) = (/ 286.0, -3.8773553000, 3.2334221000e-3, -5.5216416000e-3 /) + LoveDat(1:4,288) = (/ 287.0, -3.8820905000, 3.2240054000e-3, -5.5076224000e-3 /) + LoveDat(1:4,289) = (/ 288.0, -3.8868227000, 3.2146753000e-3, -5.4937164000e-3 /) + LoveDat(1:4,290) = (/ 289.0, -3.8915519000, 3.2054310000e-3, -5.4799221000e-3 /) + LoveDat(1:4,291) = (/ 290.0, -3.8962780000, 3.1962715000e-3, -5.4662383000e-3 /) + LoveDat(1:4,292) = (/ 291.0, -3.9010010000, 3.1871958000e-3, -5.4526635000e-3 /) + LoveDat(1:4,293) = (/ 292.0, -3.9057209000, 3.1782032000e-3, -5.4391967000e-3 /) + LoveDat(1:4,294) = (/ 293.0, -3.9104377000, 3.1692926000e-3, -5.4258363000e-3 /) + LoveDat(1:4,295) = (/ 294.0, -3.9151512000, 3.1604632000e-3, -5.4125813000e-3 /) + LoveDat(1:4,296) = (/ 295.0, -3.9198616000, 3.1517142000e-3, -5.3994305000e-3 /) + LoveDat(1:4,297) = (/ 296.0, -3.9245687000, 3.1430446000e-3, -5.3863824000e-3 /) + LoveDat(1:4,298) = (/ 297.0, -3.9292725000, 3.1344537000e-3, -5.3734361000e-3 /) + LoveDat(1:4,299) = (/ 298.0, -3.9339731000, 3.1259405000e-3, -5.3605902000e-3 /) + LoveDat(1:4,300) = (/ 299.0, -3.9386704000, 3.1175043000e-3, -5.3478437000e-3 /) + LoveDat(1:4,301) = (/ 300.0, -3.9433643000, 3.1091442000e-3, -5.3351954000e-3 /) + LoveDat(1:4,302) = (/ 301.0, -3.9480548000, 3.1008594000e-3, -5.3226441000e-3 /) + LoveDat(1:4,303) = (/ 302.0, -3.9527420000, 3.0926491000e-3, -5.3101888000e-3 /) + LoveDat(1:4,304) = (/ 303.0, -3.9574257000, 3.0845126000e-3, -5.2978283000e-3 /) + LoveDat(1:4,305) = (/ 304.0, -3.9621060000, 3.0764490000e-3, -5.2855615000e-3 /) + LoveDat(1:4,306) = (/ 305.0, -3.9667828000, 3.0684575000e-3, -5.2733874000e-3 /) + LoveDat(1:4,307) = (/ 306.0, -3.9714561000, 3.0605375000e-3, -5.2613050000e-3 /) + LoveDat(1:4,308) = (/ 307.0, -3.9761259000, 3.0526881000e-3, -5.2493131000e-3 /) + LoveDat(1:4,309) = (/ 308.0, -3.9807921000, 3.0449085000e-3, -5.2374107000e-3 /) + LoveDat(1:4,310) = (/ 309.0, -3.9854548000, 3.0371982000e-3, -5.2255969000e-3 /) + LoveDat(1:4,311) = (/ 310.0, -3.9901138000, 3.0295562000e-3, -5.2138707000e-3 /) + LoveDat(1:4,312) = (/ 311.0, -3.9947693000, 3.0219820000e-3, -5.2022310000e-3 /) + LoveDat(1:4,313) = (/ 312.0, -3.9994210000, 3.0144747000e-3, -5.1906768000e-3 /) + LoveDat(1:4,314) = (/ 313.0, -4.0040691000, 3.0070337000e-3, -5.1792073000e-3 /) + LoveDat(1:4,315) = (/ 314.0, -4.0087135000, 2.9996584000e-3, -5.1678215000e-3 /) + LoveDat(1:4,316) = (/ 315.0, -4.0133542000, 2.9923479000e-3, -5.1565183000e-3 /) + LoveDat(1:4,317) = (/ 316.0, -4.0179911000, 2.9851016000e-3, -5.1452970000e-3 /) + LoveDat(1:4,318) = (/ 317.0, -4.0226242000, 2.9779189000e-3, -5.1341566000e-3 /) + LoveDat(1:4,319) = (/ 318.0, -4.0272535000, 2.9707990000e-3, -5.1230962000e-3 /) + LoveDat(1:4,320) = (/ 319.0, -4.0318790000, 2.9637414000e-3, -5.1121150000e-3 /) + LoveDat(1:4,321) = (/ 320.0, -4.0365006000, 2.9567453000e-3, -5.1012119000e-3 /) + LoveDat(1:4,322) = (/ 321.0, -4.0411184000, 2.9498101000e-3, -5.0903863000e-3 /) + LoveDat(1:4,323) = (/ 322.0, -4.0457322000, 2.9429353000e-3, -5.0796372000e-3 /) + LoveDat(1:4,324) = (/ 323.0, -4.0503421000, 2.9361201000e-3, -5.0689638000e-3 /) + LoveDat(1:4,325) = (/ 324.0, -4.0549481000, 2.9293639000e-3, -5.0583652000e-3 /) + LoveDat(1:4,326) = (/ 325.0, -4.0595501000, 2.9226662000e-3, -5.0478407000e-3 /) + LoveDat(1:4,327) = (/ 326.0, -4.0641480000, 2.9160263000e-3, -5.0373894000e-3 /) + LoveDat(1:4,328) = (/ 327.0, -4.0687420000, 2.9094435000e-3, -5.0270106000e-3 /) + LoveDat(1:4,329) = (/ 328.0, -4.0733319000, 2.9029174000e-3, -5.0167034000e-3 /) + LoveDat(1:4,330) = (/ 329.0, -4.0779177000, 2.8964474000e-3, -5.0064671000e-3 /) + LoveDat(1:4,331) = (/ 330.0, -4.0824995000, 2.8900327000e-3, -4.9963009000e-3 /) + LoveDat(1:4,332) = (/ 331.0, -4.0870771000, 2.8836730000e-3, -4.9862041000e-3 /) + LoveDat(1:4,333) = (/ 332.0, -4.0916505000, 2.8773676000e-3, -4.9761758000e-3 /) + LoveDat(1:4,334) = (/ 333.0, -4.0962198000, 2.8711159000e-3, -4.9662155000e-3 /) + LoveDat(1:4,335) = (/ 334.0, -4.1007850000, 2.8649173000e-3, -4.9563223000e-3 /) + LoveDat(1:4,336) = (/ 335.0, -4.1053459000, 2.8587715000e-3, -4.9464955000e-3 /) + LoveDat(1:4,337) = (/ 336.0, -4.1099025000, 2.8526777000e-3, -4.9367344000e-3 /) + LoveDat(1:4,338) = (/ 337.0, -4.1144549000, 2.8466354000e-3, -4.9270384000e-3 /) + LoveDat(1:4,339) = (/ 338.0, -4.1190030000, 2.8406442000e-3, -4.9174066000e-3 /) + LoveDat(1:4,340) = (/ 339.0, -4.1235469000, 2.8347035000e-3, -4.9078386000e-3 /) + LoveDat(1:4,341) = (/ 340.0, -4.1280863000, 2.8288128000e-3, -4.8983335000e-3 /) + LoveDat(1:4,342) = (/ 341.0, -4.1326215000, 2.8229715000e-3, -4.8888907000e-3 /) + LoveDat(1:4,343) = (/ 342.0, -4.1371523000, 2.8171792000e-3, -4.8795095000e-3 /) + LoveDat(1:4,344) = (/ 343.0, -4.1416786000, 2.8114353000e-3, -4.8701893000e-3 /) + LoveDat(1:4,345) = (/ 344.0, -4.1462006000, 2.8057394000e-3, -4.8609295000e-3 /) + LoveDat(1:4,346) = (/ 345.0, -4.1507181000, 2.8000909000e-3, -4.8517295000e-3 /) + LoveDat(1:4,347) = (/ 346.0, -4.1552312000, 2.7944894000e-3, -4.8425885000e-3 /) + LoveDat(1:4,348) = (/ 347.0, -4.1597397000, 2.7889344000e-3, -4.8335060000e-3 /) + LoveDat(1:4,349) = (/ 348.0, -4.1642438000, 2.7834254000e-3, -4.8244814000e-3 /) + LoveDat(1:4,350) = (/ 349.0, -4.1687434000, 2.7779620000e-3, -4.8155141000e-3 /) + LoveDat(1:4,351) = (/ 350.0, -4.1732384000, 2.7725436000e-3, -4.8066034000e-3 /) + LoveDat(1:4,352) = (/ 351.0, -4.1777288000, 2.7671698000e-3, -4.7977488000e-3 /) + LoveDat(1:4,353) = (/ 352.0, -4.1822147000, 2.7618402000e-3, -4.7889498000e-3 /) + LoveDat(1:4,354) = (/ 353.0, -4.1866959000, 2.7565543000e-3, -4.7802057000e-3 /) + LoveDat(1:4,355) = (/ 354.0, -4.1911725000, 2.7513117000e-3, -4.7715160000e-3 /) + LoveDat(1:4,356) = (/ 355.0, -4.1956445000, 2.7461118000e-3, -4.7628800000e-3 /) + LoveDat(1:4,357) = (/ 356.0, -4.2001118000, 2.7409544000e-3, -4.7542974000e-3 /) + LoveDat(1:4,358) = (/ 357.0, -4.2045744000, 2.7358388000e-3, -4.7457675000e-3 /) + LoveDat(1:4,359) = (/ 358.0, -4.2090323000, 2.7307648000e-3, -4.7372897000e-3 /) + LoveDat(1:4,360) = (/ 359.0, -4.2134854000, 2.7257319000e-3, -4.7288636000e-3 /) + LoveDat(1:4,361) = (/ 360.0, -4.2179338000, 2.7207397000e-3, -4.7204886000e-3 /) + LoveDat(1:4,362) = (/ 361.0, -4.2223775000, 2.7157877000e-3, -4.7121643000e-3 /) + LoveDat(1:4,363) = (/ 362.0, -4.2268163000, 2.7108756000e-3, -4.7038900000e-3 /) + LoveDat(1:4,364) = (/ 363.0, -4.2312503000, 2.7060029000e-3, -4.6956653000e-3 /) + LoveDat(1:4,365) = (/ 364.0, -4.2356795000, 2.7011692000e-3, -4.6874897000e-3 /) + LoveDat(1:4,366) = (/ 365.0, -4.2401039000, 2.6963742000e-3, -4.6793627000e-3 /) + LoveDat(1:4,367) = (/ 366.0, -4.2445234000, 2.6916175000e-3, -4.6712838000e-3 /) + LoveDat(1:4,368) = (/ 367.0, -4.2489380000, 2.6868986000e-3, -4.6632526000e-3 /) + LoveDat(1:4,369) = (/ 368.0, -4.2533476000, 2.6822172000e-3, -4.6552684000e-3 /) + LoveDat(1:4,370) = (/ 369.0, -4.2577524000, 2.6775728000e-3, -4.6473310000e-3 /) + LoveDat(1:4,371) = (/ 370.0, -4.2621522000, 2.6729652000e-3, -4.6394397000e-3 /) + LoveDat(1:4,372) = (/ 371.0, -4.2665470000, 2.6683940000e-3, -4.6315942000e-3 /) + LoveDat(1:4,373) = (/ 372.0, -4.2709369000, 2.6638587000e-3, -4.6237940000e-3 /) + LoveDat(1:4,374) = (/ 373.0, -4.2753218000, 2.6593590000e-3, -4.6160387000e-3 /) + LoveDat(1:4,375) = (/ 374.0, -4.2797016000, 2.6548946000e-3, -4.6083277000e-3 /) + LoveDat(1:4,376) = (/ 375.0, -4.2840764000, 2.6504651000e-3, -4.6006607000e-3 /) + LoveDat(1:4,377) = (/ 376.0, -4.2884462000, 2.6460701000e-3, -4.5930373000e-3 /) + LoveDat(1:4,378) = (/ 377.0, -4.2928108000, 2.6417093000e-3, -4.5854569000e-3 /) + LoveDat(1:4,379) = (/ 378.0, -4.2971704000, 2.6373823000e-3, -4.5779192000e-3 /) + LoveDat(1:4,380) = (/ 379.0, -4.3015249000, 2.6330888000e-3, -4.5704238000e-3 /) + LoveDat(1:4,381) = (/ 380.0, -4.3058742000, 2.6288285000e-3, -4.5629702000e-3 /) + LoveDat(1:4,382) = (/ 381.0, -4.3102184000, 2.6246011000e-3, -4.5555581000e-3 /) + LoveDat(1:4,383) = (/ 382.0, -4.3145575000, 2.6204061000e-3, -4.5481870000e-3 /) + LoveDat(1:4,384) = (/ 383.0, -4.3188914000, 2.6162432000e-3, -4.5408565000e-3 /) + LoveDat(1:4,385) = (/ 384.0, -4.3232200000, 2.6121122000e-3, -4.5335663000e-3 /) + LoveDat(1:4,386) = (/ 385.0, -4.3275435000, 2.6080128000e-3, -4.5263159000e-3 /) + LoveDat(1:4,387) = (/ 386.0, -4.3318617000, 2.6039445000e-3, -4.5191050000e-3 /) + LoveDat(1:4,388) = (/ 387.0, -4.3361747000, 2.5999071000e-3, -4.5119331000e-3 /) + LoveDat(1:4,389) = (/ 388.0, -4.3404824000, 2.5959002000e-3, -4.5048000000e-3 /) + LoveDat(1:4,390) = (/ 389.0, -4.3447848000, 2.5919236000e-3, -4.4977052000e-3 /) + LoveDat(1:4,391) = (/ 390.0, -4.3490820000, 2.5879770000e-3, -4.4906484000e-3 /) + LoveDat(1:4,392) = (/ 391.0, -4.3533738000, 2.5840600000e-3, -4.4836292000e-3 /) + LoveDat(1:4,393) = (/ 392.0, -4.3576603000, 2.5801724000e-3, -4.4766472000e-3 /) + LoveDat(1:4,394) = (/ 393.0, -4.3619414000, 2.5763138000e-3, -4.4697021000e-3 /) + LoveDat(1:4,395) = (/ 394.0, -4.3662172000, 2.5724840000e-3, -4.4627935000e-3 /) + LoveDat(1:4,396) = (/ 395.0, -4.3704876000, 2.5686827000e-3, -4.4559212000e-3 /) + LoveDat(1:4,397) = (/ 396.0, -4.3747527000, 2.5649095000e-3, -4.4490846000e-3 /) + LoveDat(1:4,398) = (/ 397.0, -4.3790123000, 2.5611642000e-3, -4.4422836000e-3 /) + LoveDat(1:4,399) = (/ 398.0, -4.3832665000, 2.5574466000e-3, -4.4355178000e-3 /) + LoveDat(1:4,400) = (/ 399.0, -4.3875152000, 2.5537563000e-3, -4.4287868000e-3 /) + LoveDat(1:4,401) = (/ 400.0, -4.3917586000, 2.5500930000e-3, -4.4220903000e-3 /) + LoveDat(1:4,402) = (/ 401.0, -4.3959964000, 2.5464565000e-3, -4.4154280000e-3 /) + LoveDat(1:4,403) = (/ 402.0, -4.4002288000, 2.5428466000e-3, -4.4087995000e-3 /) + LoveDat(1:4,404) = (/ 403.0, -4.4044556000, 2.5392629000e-3, -4.4022046000e-3 /) + LoveDat(1:4,405) = (/ 404.0, -4.4086770000, 2.5357051000e-3, -4.3956430000e-3 /) + LoveDat(1:4,406) = (/ 405.0, -4.4128928000, 2.5321731000e-3, -4.3891142000e-3 /) + LoveDat(1:4,407) = (/ 406.0, -4.4171031000, 2.5286666000e-3, -4.3826181000e-3 /) + LoveDat(1:4,408) = (/ 407.0, -4.4213078000, 2.5251852000e-3, -4.3761543000e-3 /) + LoveDat(1:4,409) = (/ 408.0, -4.4255070000, 2.5217289000e-3, -4.3697225000e-3 /) + LoveDat(1:4,410) = (/ 409.0, -4.4297006000, 2.5182972000e-3, -4.3633224000e-3 /) + LoveDat(1:4,411) = (/ 410.0, -4.4338886000, 2.5148899000e-3, -4.3569537000e-3 /) + LoveDat(1:4,412) = (/ 411.0, -4.4380709000, 2.5115069000e-3, -4.3506162000e-3 /) + LoveDat(1:4,413) = (/ 412.0, -4.4422477000, 2.5081478000e-3, -4.3443095000e-3 /) + LoveDat(1:4,414) = (/ 413.0, -4.4464188000, 2.5048125000e-3, -4.3380334000e-3 /) + LoveDat(1:4,415) = (/ 414.0, -4.4505843000, 2.5015006000e-3, -4.3317876000e-3 /) + LoveDat(1:4,416) = (/ 415.0, -4.4547441000, 2.4982119000e-3, -4.3255718000e-3 /) + LoveDat(1:4,417) = (/ 416.0, -4.4588982000, 2.4949463000e-3, -4.3193857000e-3 /) + LoveDat(1:4,418) = (/ 417.0, -4.4630466000, 2.4917034000e-3, -4.3132290000e-3 /) + LoveDat(1:4,419) = (/ 418.0, -4.4671894000, 2.4884831000e-3, -4.3071016000e-3 /) + LoveDat(1:4,420) = (/ 419.0, -4.4713264000, 2.4852851000e-3, -4.3010031000e-3 /) + LoveDat(1:4,421) = (/ 420.0, -4.4754577000, 2.4821092000e-3, -4.2949332000e-3 /) + LoveDat(1:4,422) = (/ 421.0, -4.4795832000, 2.4789551000e-3, -4.2888918000e-3 /) + LoveDat(1:4,423) = (/ 422.0, -4.4837030000, 2.4758227000e-3, -4.2828785000e-3 /) + LoveDat(1:4,424) = (/ 423.0, -4.4878171000, 2.4727118000e-3, -4.2768931000e-3 /) + LoveDat(1:4,425) = (/ 424.0, -4.4919253000, 2.4696220000e-3, -4.2709353000e-3 /) + LoveDat(1:4,426) = (/ 425.0, -4.4960278000, 2.4665532000e-3, -4.2650050000e-3 /) + LoveDat(1:4,427) = (/ 426.0, -4.5001245000, 2.4635053000e-3, -4.2591017000e-3 /) + LoveDat(1:4,428) = (/ 427.0, -4.5042153000, 2.4604778000e-3, -4.2532254000e-3 /) + LoveDat(1:4,429) = (/ 428.0, -4.5083003000, 2.4574708000e-3, -4.2473758000e-3 /) + LoveDat(1:4,430) = (/ 429.0, -4.5123795000, 2.4544839000e-3, -4.2415526000e-3 /) + LoveDat(1:4,431) = (/ 430.0, -4.5164529000, 2.4515170000e-3, -4.2357555000e-3 /) + LoveDat(1:4,432) = (/ 431.0, -4.5205204000, 2.4485699000e-3, -4.2299844000e-3 /) + LoveDat(1:4,433) = (/ 432.0, -4.5245820000, 2.4456423000e-3, -4.2242391000e-3 /) + LoveDat(1:4,434) = (/ 433.0, -4.5286377000, 2.4427340000e-3, -4.2185193000e-3 /) + LoveDat(1:4,435) = (/ 434.0, -4.5326876000, 2.4398450000e-3, -4.2128247000e-3 /) + LoveDat(1:4,436) = (/ 435.0, -4.5367315000, 2.4369749000e-3, -4.2071552000e-3 /) + LoveDat(1:4,437) = (/ 436.0, -4.5407695000, 2.4341235000e-3, -4.2015105000e-3 /) + LoveDat(1:4,438) = (/ 437.0, -4.5448016000, 2.4312908000e-3, -4.1958904000e-3 /) + LoveDat(1:4,439) = (/ 438.0, -4.5488278000, 2.4284765000e-3, -4.1902947000e-3 /) + LoveDat(1:4,440) = (/ 439.0, -4.5528480000, 2.4256804000e-3, -4.1847233000e-3 /) + LoveDat(1:4,441) = (/ 440.0, -4.5568623000, 2.4229023000e-3, -4.1791757000e-3 /) + LoveDat(1:4,442) = (/ 441.0, -4.5608706000, 2.4201420000e-3, -4.1736520000e-3 /) + LoveDat(1:4,443) = (/ 442.0, -4.5648729000, 2.4173995000e-3, -4.1681518000e-3 /) + LoveDat(1:4,444) = (/ 443.0, -4.5688693000, 2.4146744000e-3, -4.1626750000e-3 /) + LoveDat(1:4,445) = (/ 444.0, -4.5728596000, 2.4119666000e-3, -4.1572213000e-3 /) + LoveDat(1:4,446) = (/ 445.0, -4.5768440000, 2.4092760000e-3, -4.1517905000e-3 /) + LoveDat(1:4,447) = (/ 446.0, -4.5808223000, 2.4066023000e-3, -4.1463825000e-3 /) + LoveDat(1:4,448) = (/ 447.0, -4.5847946000, 2.4039454000e-3, -4.1409971000e-3 /) + LoveDat(1:4,449) = (/ 448.0, -4.5887608000, 2.4013051000e-3, -4.1356340000e-3 /) + LoveDat(1:4,450) = (/ 449.0, -4.5927211000, 2.3986813000e-3, -4.1302931000e-3 /) + LoveDat(1:4,451) = (/ 450.0, -4.5966752000, 2.3960738000e-3, -4.1249742000e-3 /) + LoveDat(1:4,452) = (/ 451.0, -4.6006234000, 2.3934824000e-3, -4.1196771000e-3 /) + LoveDat(1:4,453) = (/ 452.0, -4.6045654000, 2.3909070000e-3, -4.1144015000e-3 /) + LoveDat(1:4,454) = (/ 453.0, -4.6085014000, 2.3883473000e-3, -4.1091474000e-3 /) + LoveDat(1:4,455) = (/ 454.0, -4.6124313000, 2.3858033000e-3, -4.1039146000e-3 /) + LoveDat(1:4,456) = (/ 455.0, -4.6163550000, 2.3832748000e-3, -4.0987028000e-3 /) + LoveDat(1:4,457) = (/ 456.0, -4.6202727000, 2.3807615000e-3, -4.0935118000e-3 /) + LoveDat(1:4,458) = (/ 457.0, -4.6241843000, 2.3782635000e-3, -4.0883416000e-3 /) + LoveDat(1:4,459) = (/ 458.0, -4.6280897000, 2.3757804000e-3, -4.0831919000e-3 /) + LoveDat(1:4,460) = (/ 459.0, -4.6319890000, 2.3733122000e-3, -4.0780626000e-3 /) + LoveDat(1:4,461) = (/ 460.0, -4.6358822000, 2.3708588000e-3, -4.0729534000e-3 /) + LoveDat(1:4,462) = (/ 461.0, -4.6397692000, 2.3684198000e-3, -4.0678643000e-3 /) + LoveDat(1:4,463) = (/ 462.0, -4.6436501000, 2.3659953000e-3, -4.0627950000e-3 /) + LoveDat(1:4,464) = (/ 463.0, -4.6475249000, 2.3635851000e-3, -4.0577454000e-3 /) + LoveDat(1:4,465) = (/ 464.0, -4.6513934000, 2.3611889000e-3, -4.0527153000e-3 /) + LoveDat(1:4,466) = (/ 465.0, -4.6552558000, 2.3588068000e-3, -4.0477046000e-3 /) + LoveDat(1:4,467) = (/ 466.0, -4.6591120000, 2.3564384000e-3, -4.0427131000e-3 /) + LoveDat(1:4,468) = (/ 467.0, -4.6629620000, 2.3540838000e-3, -4.0377406000e-3 /) + LoveDat(1:4,469) = (/ 468.0, -4.6668058000, 2.3517427000e-3, -4.0327870000e-3 /) + LoveDat(1:4,470) = (/ 469.0, -4.6706434000, 2.3494150000e-3, -4.0278521000e-3 /) + LoveDat(1:4,471) = (/ 470.0, -4.6744748000, 2.3471006000e-3, -4.0229358000e-3 /) + LoveDat(1:4,472) = (/ 471.0, -4.6783000000, 2.3447994000e-3, -4.0180379000e-3 /) + LoveDat(1:4,473) = (/ 472.0, -4.6821189000, 2.3425111000e-3, -4.0131582000e-3 /) + LoveDat(1:4,474) = (/ 473.0, -4.6859316000, 2.3402357000e-3, -4.0082967000e-3 /) + LoveDat(1:4,475) = (/ 474.0, -4.6897381000, 2.3379731000e-3, -4.0034532000e-3 /) + LoveDat(1:4,476) = (/ 475.0, -4.6935383000, 2.3357231000e-3, -3.9986274000e-3 /) + LoveDat(1:4,477) = (/ 476.0, -4.6973323000, 2.3334855000e-3, -3.9938194000e-3 /) + LoveDat(1:4,478) = (/ 477.0, -4.7011201000, 2.3312604000e-3, -3.9890289000e-3 /) + LoveDat(1:4,479) = (/ 478.0, -4.7049015000, 2.3290474000e-3, -3.9842557000e-3 /) + LoveDat(1:4,480) = (/ 479.0, -4.7086767000, 2.3268466000e-3, -3.9794999000e-3 /) + LoveDat(1:4,481) = (/ 480.0, -4.7124456000, 2.3246577000e-3, -3.9747611000e-3 /) + LoveDat(1:4,482) = (/ 481.0, -4.7162083000, 2.3224807000e-3, -3.9700393000e-3 /) + LoveDat(1:4,483) = (/ 482.0, -4.7199646000, 2.3203154000e-3, -3.9653344000e-3 /) + LoveDat(1:4,484) = (/ 483.0, -4.7237147000, 2.3181618000e-3, -3.9606461000e-3 /) + LoveDat(1:4,485) = (/ 484.0, -4.7274585000, 2.3160196000e-3, -3.9559744000e-3 /) + LoveDat(1:4,486) = (/ 485.0, -4.7311959000, 2.3138889000e-3, -3.9513192000e-3 /) + LoveDat(1:4,487) = (/ 486.0, -4.7349271000, 2.3117694000e-3, -3.9466802000e-3 /) + LoveDat(1:4,488) = (/ 487.0, -4.7386519000, 2.3096610000e-3, -3.9420575000e-3 /) + LoveDat(1:4,489) = (/ 488.0, -4.7423704000, 2.3075637000e-3, -3.9374508000e-3 /) + LoveDat(1:4,490) = (/ 489.0, -4.7460826000, 2.3054773000e-3, -3.9328600000e-3 /) + LoveDat(1:4,491) = (/ 490.0, -4.7497885000, 2.3034017000e-3, -3.9282850000e-3 /) + LoveDat(1:4,492) = (/ 491.0, -4.7534880000, 2.3013368000e-3, -3.9237256000e-3 /) + LoveDat(1:4,493) = (/ 492.0, -4.7571812000, 2.2992825000e-3, -3.9191818000e-3 /) + LoveDat(1:4,494) = (/ 493.0, -4.7608681000, 2.2972386000e-3, -3.9146535000e-3 /) + LoveDat(1:4,495) = (/ 494.0, -4.7645486000, 2.2952052000e-3, -3.9101404000e-3 /) + LoveDat(1:4,496) = (/ 495.0, -4.7682227000, 2.2931820000e-3, -3.9056425000e-3 /) + LoveDat(1:4,497) = (/ 496.0, -4.7718905000, 2.2911690000e-3, -3.9011597000e-3 /) + LoveDat(1:4,498) = (/ 497.0, -4.7755520000, 2.2891660000e-3, -3.8966919000e-3 /) + LoveDat(1:4,499) = (/ 498.0, -4.7792071000, 2.2871729000e-3, -3.8922389000e-3 /) + LoveDat(1:4,500) = (/ 499.0, -4.7828558000, 2.2851898000e-3, -3.8878005000e-3 /) + LoveDat(1:4,501) = (/ 500.0, -4.7864981000, 2.2832163000e-3, -3.8833768000e-3 /) + LoveDat(1:4,502) = (/ 501.0, -4.7901341000, 2.2812525000e-3, -3.8789676000e-3 /) + LoveDat(1:4,503) = (/ 502.0, -4.7937636000, 2.2792983000e-3, -3.8745728000e-3 /) + LoveDat(1:4,504) = (/ 503.0, -4.7973868000, 2.2773535000e-3, -3.8701922000e-3 /) + LoveDat(1:4,505) = (/ 504.0, -4.8010036000, 2.2754180000e-3, -3.8658258000e-3 /) + LoveDat(1:4,506) = (/ 505.0, -4.8046141000, 2.2734918000e-3, -3.8614735000e-3 /) + LoveDat(1:4,507) = (/ 506.0, -4.8082181000, 2.2715748000e-3, -3.8571351000e-3 /) + LoveDat(1:4,508) = (/ 507.0, -4.8118157000, 2.2696668000e-3, -3.8528105000e-3 /) + LoveDat(1:4,509) = (/ 508.0, -4.8154069000, 2.2677678000e-3, -3.8484997000e-3 /) + LoveDat(1:4,510) = (/ 509.0, -4.8189918000, 2.2658777000e-3, -3.8442025000e-3 /) + LoveDat(1:4,511) = (/ 510.0, -4.8225702000, 2.2639964000e-3, -3.8399188000e-3 /) + LoveDat(1:4,512) = (/ 511.0, -4.8261422000, 2.2621237000e-3, -3.8356485000e-3 /) + LoveDat(1:4,513) = (/ 512.0, -4.8297078000, 2.2602597000e-3, -3.8313916000e-3 /) + LoveDat(1:4,514) = (/ 513.0, -4.8332670000, 2.2584041000e-3, -3.8271479000e-3 /) + LoveDat(1:4,515) = (/ 514.0, -4.8368197000, 2.2565570000e-3, -3.8229173000e-3 /) + LoveDat(1:4,516) = (/ 515.0, -4.8403661000, 2.2547183000e-3, -3.8186997000e-3 /) + LoveDat(1:4,517) = (/ 516.0, -4.8439060000, 2.2528877000e-3, -3.8144951000e-3 /) + LoveDat(1:4,518) = (/ 517.0, -4.8474395000, 2.2510654000e-3, -3.8103033000e-3 /) + LoveDat(1:4,519) = (/ 518.0, -4.8509666000, 2.2492511000e-3, -3.8061243000e-3 /) + LoveDat(1:4,520) = (/ 519.0, -4.8544872000, 2.2474448000e-3, -3.8019578000e-3 /) + LoveDat(1:4,521) = (/ 520.0, -4.8580014000, 2.2456465000e-3, -3.7978040000e-3 /) + LoveDat(1:4,522) = (/ 521.0, -4.8615092000, 2.2438560000e-3, -3.7936626000e-3 /) + LoveDat(1:4,523) = (/ 522.0, -4.8650105000, 2.2420732000e-3, -3.7895335000e-3 /) + LoveDat(1:4,524) = (/ 523.0, -4.8685054000, 2.2402981000e-3, -3.7854168000e-3 /) + LoveDat(1:4,525) = (/ 524.0, -4.8719939000, 2.2385305000e-3, -3.7813122000e-3 /) + LoveDat(1:4,526) = (/ 525.0, -4.8754759000, 2.2367705000e-3, -3.7772197000e-3 /) + LoveDat(1:4,527) = (/ 526.0, -4.8789515000, 2.2350179000e-3, -3.7731392000e-3 /) + LoveDat(1:4,528) = (/ 527.0, -4.8824206000, 2.2332727000e-3, -3.7690706000e-3 /) + LoveDat(1:4,529) = (/ 528.0, -4.8858833000, 2.2315347000e-3, -3.7650139000e-3 /) + LoveDat(1:4,530) = (/ 529.0, -4.8893395000, 2.2298040000e-3, -3.7609689000e-3 /) + LoveDat(1:4,531) = (/ 530.0, -4.8927893000, 2.2280804000e-3, -3.7569356000e-3 /) + LoveDat(1:4,532) = (/ 531.0, -4.8962327000, 2.2263638000e-3, -3.7529139000e-3 /) + LoveDat(1:4,533) = (/ 532.0, -4.8996696000, 2.2246542000e-3, -3.7489037000e-3 /) + LoveDat(1:4,534) = (/ 533.0, -4.9031000000, 2.2229515000e-3, -3.7449049000e-3 /) + LoveDat(1:4,535) = (/ 534.0, -4.9065240000, 2.2212556000e-3, -3.7409174000e-3 /) + LoveDat(1:4,536) = (/ 535.0, -4.9099415000, 2.2195665000e-3, -3.7369411000e-3 /) + LoveDat(1:4,537) = (/ 536.0, -4.9133526000, 2.2178841000e-3, -3.7329761000e-3 /) + LoveDat(1:4,538) = (/ 537.0, -4.9167573000, 2.2162082000e-3, -3.7290221000e-3 /) + LoveDat(1:4,539) = (/ 538.0, -4.9201554000, 2.2145390000e-3, -3.7250792000e-3 /) + LoveDat(1:4,540) = (/ 539.0, -4.9235472000, 2.2128762000e-3, -3.7211471000e-3 /) + LoveDat(1:4,541) = (/ 540.0, -4.9269324000, 2.2112198000e-3, -3.7172260000e-3 /) + LoveDat(1:4,542) = (/ 541.0, -4.9303112000, 2.2095698000e-3, -3.7133156000e-3 /) + LoveDat(1:4,543) = (/ 542.0, -4.9336836000, 2.2079261000e-3, -3.7094160000e-3 /) + LoveDat(1:4,544) = (/ 543.0, -4.9370495000, 2.2062885000e-3, -3.7055269000e-3 /) + LoveDat(1:4,545) = (/ 544.0, -4.9404089000, 2.2046571000e-3, -3.7016485000e-3 /) + LoveDat(1:4,546) = (/ 545.0, -4.9437619000, 2.2030318000e-3, -3.6977805000e-3 /) + LoveDat(1:4,547) = (/ 546.0, -4.9471084000, 2.2014125000e-3, -3.6939229000e-3 /) + LoveDat(1:4,548) = (/ 547.0, -4.9504485000, 2.1997991000e-3, -3.6900757000e-3 /) + LoveDat(1:4,549) = (/ 548.0, -4.9537821000, 2.1981917000e-3, -3.6862387000e-3 /) + LoveDat(1:4,550) = (/ 549.0, -4.9571092000, 2.1965901000e-3, -3.6824120000e-3 /) + LoveDat(1:4,551) = (/ 550.0, -4.9604299000, 2.1949942000e-3, -3.6785954000e-3 /) + LoveDat(1:4,552) = (/ 551.0, -4.9637442000, 2.1934040000e-3, -3.6747888000e-3 /) + LoveDat(1:4,553) = (/ 552.0, -4.9670519000, 2.1918195000e-3, -3.6709922000e-3 /) + LoveDat(1:4,554) = (/ 553.0, -4.9703533000, 2.1902406000e-3, -3.6672056000e-3 /) + LoveDat(1:4,555) = (/ 554.0, -4.9736481000, 2.1886671000e-3, -3.6634288000e-3 /) + LoveDat(1:4,556) = (/ 555.0, -4.9769366000, 2.1870992000e-3, -3.6596618000e-3 /) + LoveDat(1:4,557) = (/ 556.0, -4.9802185000, 2.1855366000e-3, -3.6559045000e-3 /) + LoveDat(1:4,558) = (/ 557.0, -4.9834940000, 2.1839795000e-3, -3.6521569000e-3 /) + LoveDat(1:4,559) = (/ 558.0, -4.9867631000, 2.1824276000e-3, -3.6484189000e-3 /) + LoveDat(1:4,560) = (/ 559.0, -4.9900257000, 2.1808809000e-3, -3.6446904000e-3 /) + LoveDat(1:4,561) = (/ 560.0, -4.9932819000, 2.1793394000e-3, -3.6409714000e-3 /) + LoveDat(1:4,562) = (/ 561.0, -4.9965316000, 2.1778031000e-3, -3.6372617000e-3 /) + LoveDat(1:4,563) = (/ 562.0, -4.9997749000, 2.1762718000e-3, -3.6335615000e-3 /) + LoveDat(1:4,564) = (/ 563.0, -5.0030117000, 2.1747455000e-3, -3.6298704000e-3 /) + LoveDat(1:4,565) = (/ 564.0, -5.0062421000, 2.1732242000e-3, -3.6261887000e-3 /) + LoveDat(1:4,566) = (/ 565.0, -5.0094660000, 2.1717078000e-3, -3.6225160000e-3 /) + LoveDat(1:4,567) = (/ 566.0, -5.0126835000, 2.1701963000e-3, -3.6188525000e-3 /) + LoveDat(1:4,568) = (/ 567.0, -5.0158946000, 2.1686895000e-3, -3.6151980000e-3 /) + LoveDat(1:4,569) = (/ 568.0, -5.0190992000, 2.1671876000e-3, -3.6115525000e-3 /) + LoveDat(1:4,570) = (/ 569.0, -5.0222974000, 2.1656903000e-3, -3.6079159000e-3 /) + LoveDat(1:4,571) = (/ 570.0, -5.0254891000, 2.1641977000e-3, -3.6042882000e-3 /) + LoveDat(1:4,572) = (/ 571.0, -5.0286744000, 2.1627096000e-3, -3.6006692000e-3 /) + LoveDat(1:4,573) = (/ 572.0, -5.0318533000, 2.1612262000e-3, -3.5970590000e-3 /) + LoveDat(1:4,574) = (/ 573.0, -5.0350258000, 2.1597472000e-3, -3.5934575000e-3 /) + LoveDat(1:4,575) = (/ 574.0, -5.0381918000, 2.1582727000e-3, -3.5898647000e-3 /) + LoveDat(1:4,576) = (/ 575.0, -5.0413514000, 2.1568026000e-3, -3.5862804000e-3 /) + LoveDat(1:4,577) = (/ 576.0, -5.0445046000, 2.1553369000e-3, -3.5827047000e-3 /) + LoveDat(1:4,578) = (/ 577.0, -5.0476514000, 2.1538755000e-3, -3.5791374000e-3 /) + LoveDat(1:4,579) = (/ 578.0, -5.0507917000, 2.1524183000e-3, -3.5755785000e-3 /) + LoveDat(1:4,580) = (/ 579.0, -5.0539256000, 2.1509654000e-3, -3.5720280000e-3 /) + LoveDat(1:4,581) = (/ 580.0, -5.0570532000, 2.1495166000e-3, -3.5684858000e-3 /) + LoveDat(1:4,582) = (/ 581.0, -5.0601743000, 2.1480720000e-3, -3.5649519000e-3 /) + LoveDat(1:4,583) = (/ 582.0, -5.0632890000, 2.1466315000e-3, -3.5614262000e-3 /) + LoveDat(1:4,584) = (/ 583.0, -5.0663973000, 2.1451950000e-3, -3.5579086000e-3 /) + LoveDat(1:4,585) = (/ 584.0, -5.0694991000, 2.1437625000e-3, -3.5543992000e-3 /) + LoveDat(1:4,586) = (/ 585.0, -5.0725946000, 2.1423339000e-3, -3.5508978000e-3 /) + LoveDat(1:4,587) = (/ 586.0, -5.0756837000, 2.1409093000e-3, -3.5474044000e-3 /) + LoveDat(1:4,588) = (/ 587.0, -5.0787664000, 2.1394885000e-3, -3.5439189000e-3 /) + LoveDat(1:4,589) = (/ 588.0, -5.0818427000, 2.1380716000e-3, -3.5404414000e-3 /) + LoveDat(1:4,590) = (/ 589.0, -5.0849126000, 2.1366585000e-3, -3.5369717000e-3 /) + LoveDat(1:4,591) = (/ 590.0, -5.0879762000, 2.1352491000e-3, -3.5335099000e-3 /) + LoveDat(1:4,592) = (/ 591.0, -5.0910333000, 2.1338434000e-3, -3.5300557000e-3 /) + LoveDat(1:4,593) = (/ 592.0, -5.0940841000, 2.1324413000e-3, -3.5266094000e-3 /) + LoveDat(1:4,594) = (/ 593.0, -5.0971285000, 2.1310429000e-3, -3.5231706000e-3 /) + LoveDat(1:4,595) = (/ 594.0, -5.1001665000, 2.1296481000e-3, -3.5197395000e-3 /) + LoveDat(1:4,596) = (/ 595.0, -5.1031982000, 2.1282569000e-3, -3.5163160000e-3 /) + LoveDat(1:4,597) = (/ 596.0, -5.1062234000, 2.1268691000e-3, -3.5129000000e-3 /) + LoveDat(1:4,598) = (/ 597.0, -5.1092424000, 2.1254848000e-3, -3.5094915000e-3 /) + LoveDat(1:4,599) = (/ 598.0, -5.1122549000, 2.1241039000e-3, -3.5060904000e-3 /) + LoveDat(1:4,600) = (/ 599.0, -5.1152611000, 2.1227265000e-3, -3.5026968000e-3 /) + LoveDat(1:4,601) = (/ 600.0, -5.1182610000, 2.1213524000e-3, -3.4993105000e-3 /) + LoveDat(1:4,602) = (/ 601.0, -5.1212545000, 2.1199816000e-3, -3.4959315000e-3 /) + LoveDat(1:4,603) = (/ 602.0, -5.1242417000, 2.1186141000e-3, -3.4925597000e-3 /) + LoveDat(1:4,604) = (/ 603.0, -5.1272225000, 2.1172498000e-3, -3.4891952000e-3 /) + LoveDat(1:4,605) = (/ 604.0, -5.1301970000, 2.1158888000e-3, -3.4858379000e-3 /) + LoveDat(1:4,606) = (/ 605.0, -5.1331651000, 2.1145309000e-3, -3.4824877000e-3 /) + LoveDat(1:4,607) = (/ 606.0, -5.1361270000, 2.1131762000e-3, -3.4791445000e-3 /) + LoveDat(1:4,608) = (/ 607.0, -5.1390825000, 2.1118246000e-3, -3.4758085000e-3 /) + LoveDat(1:4,609) = (/ 608.0, -5.1420316000, 2.1104761000e-3, -3.4724795000e-3 /) + LoveDat(1:4,610) = (/ 609.0, -5.1449745000, 2.1091306000e-3, -3.4691574000e-3 /) + LoveDat(1:4,611) = (/ 610.0, -5.1479111000, 2.1077881000e-3, -3.4658423000e-3 /) + LoveDat(1:4,612) = (/ 611.0, -5.1508413000, 2.1064486000e-3, -3.4625341000e-3 /) + LoveDat(1:4,613) = (/ 612.0, -5.1537652000, 2.1051120000e-3, -3.4592327000e-3 /) + LoveDat(1:4,614) = (/ 613.0, -5.1566829000, 2.1037784000e-3, -3.4559381000e-3 /) + LoveDat(1:4,615) = (/ 614.0, -5.1595942000, 2.1024476000e-3, -3.4526504000e-3 /) + LoveDat(1:4,616) = (/ 615.0, -5.1624993000, 2.1011196000e-3, -3.4493693000e-3 /) + LoveDat(1:4,617) = (/ 616.0, -5.1653981000, 2.0997945000e-3, -3.4460950000e-3 /) + LoveDat(1:4,618) = (/ 617.0, -5.1682905000, 2.0984722000e-3, -3.4428273000e-3 /) + LoveDat(1:4,619) = (/ 618.0, -5.1711768000, 2.0971526000e-3, -3.4395663000e-3 /) + LoveDat(1:4,620) = (/ 619.0, -5.1740567000, 2.0958358000e-3, -3.4363118000e-3 /) + LoveDat(1:4,621) = (/ 620.0, -5.1769304000, 2.0945216000e-3, -3.4330639000e-3 /) + LoveDat(1:4,622) = (/ 621.0, -5.1797978000, 2.0932101000e-3, -3.4298226000e-3 /) + LoveDat(1:4,623) = (/ 622.0, -5.1826589000, 2.0919012000e-3, -3.4265877000e-3 /) + LoveDat(1:4,624) = (/ 623.0, -5.1855138000, 2.0905950000e-3, -3.4233592000e-3 /) + LoveDat(1:4,625) = (/ 624.0, -5.1883625000, 2.0892913000e-3, -3.4201372000e-3 /) + LoveDat(1:4,626) = (/ 625.0, -5.1912049000, 2.0879902000e-3, -3.4169215000e-3 /) + LoveDat(1:4,627) = (/ 626.0, -5.1940410000, 2.0866915000e-3, -3.4137122000e-3 /) + LoveDat(1:4,628) = (/ 627.0, -5.1968710000, 2.0853954000e-3, -3.4105092000e-3 /) + LoveDat(1:4,629) = (/ 628.0, -5.1996947000, 2.0841018000e-3, -3.4073124000e-3 /) + LoveDat(1:4,630) = (/ 629.0, -5.2025121000, 2.0828105000e-3, -3.4041219000e-3 /) + LoveDat(1:4,631) = (/ 630.0, -5.2053234000, 2.0815217000e-3, -3.4009376000e-3 /) + LoveDat(1:4,632) = (/ 631.0, -5.2081285000, 2.0802353000e-3, -3.3977595000e-3 /) + LoveDat(1:4,633) = (/ 632.0, -5.2109273000, 2.0789512000e-3, -3.3945875000e-3 /) + LoveDat(1:4,634) = (/ 633.0, -5.2137199000, 2.0776695000e-3, -3.3914216000e-3 /) + LoveDat(1:4,635) = (/ 634.0, -5.2165064000, 2.0763900000e-3, -3.3882618000e-3 /) + LoveDat(1:4,636) = (/ 635.0, -5.2192866000, 2.0751129000e-3, -3.3851080000e-3 /) + LoveDat(1:4,637) = (/ 636.0, -5.2220607000, 2.0738380000e-3, -3.3819602000e-3 /) + LoveDat(1:4,638) = (/ 637.0, -5.2248286000, 2.0725653000e-3, -3.3788184000e-3 /) + LoveDat(1:4,639) = (/ 638.0, -5.2275903000, 2.0712949000e-3, -3.3756826000e-3 /) + LoveDat(1:4,640) = (/ 639.0, -5.2303458000, 2.0700266000e-3, -3.3725527000e-3 /) + LoveDat(1:4,641) = (/ 640.0, -5.2330952000, 2.0687604000e-3, -3.3694286000e-3 /) + LoveDat(1:4,642) = (/ 641.0, -5.2358384000, 2.0674964000e-3, -3.3663104000e-3 /) + LoveDat(1:4,643) = (/ 642.0, -5.2385755000, 2.0662346000e-3, -3.3631981000e-3 /) + LoveDat(1:4,644) = (/ 643.0, -5.2413064000, 2.0649747000e-3, -3.3600915000e-3 /) + LoveDat(1:4,645) = (/ 644.0, -5.2440312000, 2.0637170000e-3, -3.3569907000e-3 /) + LoveDat(1:4,646) = (/ 645.0, -5.2467498000, 2.0624613000e-3, -3.3538957000e-3 /) + LoveDat(1:4,647) = (/ 646.0, -5.2494624000, 2.0612076000e-3, -3.3508063000e-3 /) + LoveDat(1:4,648) = (/ 647.0, -5.2521688000, 2.0599559000e-3, -3.3477227000e-3 /) + LoveDat(1:4,649) = (/ 648.0, -5.2548690000, 2.0587062000e-3, -3.3446446000e-3 /) + LoveDat(1:4,650) = (/ 649.0, -5.2575632000, 2.0574585000e-3, -3.3415722000e-3 /) + LoveDat(1:4,651) = (/ 650.0, -5.2602513000, 2.0562126000e-3, -3.3385054000e-3 /) + LoveDat(1:4,652) = (/ 651.0, -5.2629332000, 2.0549687000e-3, -3.3354442000e-3 /) + LoveDat(1:4,653) = (/ 652.0, -5.2656091000, 2.0537266000e-3, -3.3323885000e-3 /) + LoveDat(1:4,654) = (/ 653.0, -5.2682789000, 2.0524865000e-3, -3.3293383000e-3 /) + LoveDat(1:4,655) = (/ 654.0, -5.2709426000, 2.0512481000e-3, -3.3262936000e-3 /) + LoveDat(1:4,656) = (/ 655.0, -5.2736002000, 2.0500116000e-3, -3.3232543000e-3 /) + LoveDat(1:4,657) = (/ 656.0, -5.2762518000, 2.0487769000e-3, -3.3202205000e-3 /) + LoveDat(1:4,658) = (/ 657.0, -5.2788973000, 2.0475440000e-3, -3.3171921000e-3 /) + LoveDat(1:4,659) = (/ 658.0, -5.2815367000, 2.0463128000e-3, -3.3141691000e-3 /) + LoveDat(1:4,660) = (/ 659.0, -5.2841701000, 2.0450834000e-3, -3.3111514000e-3 /) + LoveDat(1:4,661) = (/ 660.0, -5.2867975000, 2.0438557000e-3, -3.3081390000e-3 /) + LoveDat(1:4,662) = (/ 661.0, -5.2894188000, 2.0426297000e-3, -3.3051319000e-3 /) + LoveDat(1:4,663) = (/ 662.0, -5.2920341000, 2.0414054000e-3, -3.3021302000e-3 /) + LoveDat(1:4,664) = (/ 663.0, -5.2946433000, 2.0401828000e-3, -3.2991336000e-3 /) + LoveDat(1:4,665) = (/ 664.0, -5.2972466000, 2.0389618000e-3, -3.2961423000e-3 /) + LoveDat(1:4,666) = (/ 665.0, -5.2998438000, 2.0377425000e-3, -3.2931562000e-3 /) + LoveDat(1:4,667) = (/ 666.0, -5.3024350000, 2.0365247000e-3, -3.2901753000e-3 /) + LoveDat(1:4,668) = (/ 667.0, -5.3050203000, 2.0353086000e-3, -3.2871995000e-3 /) + LoveDat(1:4,669) = (/ 668.0, -5.3075995000, 2.0340940000e-3, -3.2842288000e-3 /) + LoveDat(1:4,670) = (/ 669.0, -5.3101728000, 2.0328810000e-3, -3.2812633000e-3 /) + LoveDat(1:4,671) = (/ 670.0, -5.3127401000, 2.0316695000e-3, -3.2783028000e-3 /) + LoveDat(1:4,672) = (/ 671.0, -5.3153014000, 2.0304596000e-3, -3.2753474000e-3 /) + LoveDat(1:4,673) = (/ 672.0, -5.3178568000, 2.0292512000e-3, -3.2723970000e-3 /) + LoveDat(1:4,674) = (/ 673.0, -5.3204062000, 2.0280443000e-3, -3.2694517000e-3 /) + LoveDat(1:4,675) = (/ 674.0, -5.3229496000, 2.0268388000e-3, -3.2665113000e-3 /) + LoveDat(1:4,676) = (/ 675.0, -5.3254871000, 2.0256348000e-3, -3.2635759000e-3 /) + LoveDat(1:4,677) = (/ 676.0, -5.3280187000, 2.0244322000e-3, -3.2606454000e-3 /) + LoveDat(1:4,678) = (/ 677.0, -5.3305444000, 2.0232311000e-3, -3.2577199000e-3 /) + LoveDat(1:4,679) = (/ 678.0, -5.3330641000, 2.0220314000e-3, -3.2547992000e-3 /) + LoveDat(1:4,680) = (/ 679.0, -5.3355779000, 2.0208331000e-3, -3.2518834000e-3 /) + LoveDat(1:4,681) = (/ 680.0, -5.3380858000, 2.0196361000e-3, -3.2489725000e-3 /) + LoveDat(1:4,682) = (/ 681.0, -5.3405878000, 2.0184406000e-3, -3.2460664000e-3 /) + LoveDat(1:4,683) = (/ 682.0, -5.3430840000, 2.0172463000e-3, -3.2431652000e-3 /) + LoveDat(1:4,684) = (/ 683.0, -5.3455742000, 2.0160534000e-3, -3.2402687000e-3 /) + LoveDat(1:4,685) = (/ 684.0, -5.3480585000, 2.0148619000e-3, -3.2373770000e-3 /) + LoveDat(1:4,686) = (/ 685.0, -5.3505370000, 2.0136716000e-3, -3.2344900000e-3 /) + LoveDat(1:4,687) = (/ 686.0, -5.3530097000, 2.0124826000e-3, -3.2316078000e-3 /) + LoveDat(1:4,688) = (/ 687.0, -5.3554764000, 2.0112949000e-3, -3.2287303000e-3 /) + LoveDat(1:4,689) = (/ 688.0, -5.3579373000, 2.0101085000e-3, -3.2258574000e-3 /) + LoveDat(1:4,690) = (/ 689.0, -5.3603924000, 2.0089233000e-3, -3.2229893000e-3 /) + LoveDat(1:4,691) = (/ 690.0, -5.3628417000, 2.0077394000e-3, -3.2201258000e-3 /) + LoveDat(1:4,692) = (/ 691.0, -5.3652851000, 2.0065567000e-3, -3.2172669000e-3 /) + LoveDat(1:4,693) = (/ 692.0, -5.3677227000, 2.0053752000e-3, -3.2144126000e-3 /) + LoveDat(1:4,694) = (/ 693.0, -5.3701545000, 2.0041948000e-3, -3.2115629000e-3 /) + LoveDat(1:4,695) = (/ 694.0, -5.3725805000, 2.0030157000e-3, -3.2087178000e-3 /) + LoveDat(1:4,696) = (/ 695.0, -5.3750006000, 2.0018377000e-3, -3.2058772000e-3 /) + LoveDat(1:4,697) = (/ 696.0, -5.3774150000, 2.0006609000e-3, -3.2030412000e-3 /) + LoveDat(1:4,698) = (/ 697.0, -5.3798237000, 1.9994853000e-3, -3.2002097000e-3 /) + LoveDat(1:4,699) = (/ 698.0, -5.3822265000, 1.9983108000e-3, -3.1973826000e-3 /) + LoveDat(1:4,700) = (/ 699.0, -5.3846236000, 1.9971373000e-3, -3.1945601000e-3 /) + LoveDat(1:4,701) = (/ 700.0, -5.3870149000, 1.9959650000e-3, -3.1917420000e-3 /) + LoveDat(1:4,702) = (/ 701.0, -5.3894005000, 1.9947938000e-3, -3.1889283000e-3 /) + LoveDat(1:4,703) = (/ 702.0, -5.3917803000, 1.9936237000e-3, -3.1861191000e-3 /) + LoveDat(1:4,704) = (/ 703.0, -5.3941544000, 1.9924547000e-3, -3.1833143000e-3 /) + LoveDat(1:4,705) = (/ 704.0, -5.3965228000, 1.9912867000e-3, -3.1805139000e-3 /) + LoveDat(1:4,706) = (/ 705.0, -5.3988854000, 1.9901198000e-3, -3.1777178000e-3 /) + LoveDat(1:4,707) = (/ 706.0, -5.4012423000, 1.9889539000e-3, -3.1749261000e-3 /) + LoveDat(1:4,708) = (/ 707.0, -5.4035936000, 1.9877890000e-3, -3.1721387000e-3 /) + LoveDat(1:4,709) = (/ 708.0, -5.4059391000, 1.9866252000e-3, -3.1693556000e-3 /) + LoveDat(1:4,710) = (/ 709.0, -5.4082790000, 1.9854623000e-3, -3.1665769000e-3 /) + LoveDat(1:4,711) = (/ 710.0, -5.4106131000, 1.9843005000e-3, -3.1638024000e-3 /) + LoveDat(1:4,712) = (/ 711.0, -5.4129416000, 1.9831396000e-3, -3.1610322000e-3 /) + LoveDat(1:4,713) = (/ 712.0, -5.4152645000, 1.9819797000e-3, -3.1582662000e-3 /) + LoveDat(1:4,714) = (/ 713.0, -5.4175816000, 1.9808208000e-3, -3.1555045000e-3 /) + LoveDat(1:4,715) = (/ 714.0, -5.4198932000, 1.9796628000e-3, -3.1527469000e-3 /) + LoveDat(1:4,716) = (/ 715.0, -5.4221991000, 1.9785058000e-3, -3.1499936000e-3 /) + LoveDat(1:4,717) = (/ 716.0, -5.4244993000, 1.9773497000e-3, -3.1472445000e-3 /) + LoveDat(1:4,718) = (/ 717.0, -5.4267939000, 1.9761945000e-3, -3.1444995000e-3 /) + LoveDat(1:4,719) = (/ 718.0, -5.4290830000, 1.9750402000e-3, -3.1417587000e-3 /) + LoveDat(1:4,720) = (/ 719.0, -5.4313664000, 1.9738869000e-3, -3.1390221000e-3 /) + LoveDat(1:4,721) = (/ 720.0, -5.4336442000, 1.9727344000e-3, -3.1362895000e-3 /) + LoveDat(1:4,722) = (/ 721.0, -5.4359164000, 1.9715828000e-3, -3.1335611000e-3 /) + LoveDat(1:4,723) = (/ 722.0, -5.4381830000, 1.9704321000e-3, -3.1308367000e-3 /) + LoveDat(1:4,724) = (/ 723.0, -5.4404441000, 1.9692823000e-3, -3.1281164000e-3 /) + LoveDat(1:4,725) = (/ 724.0, -5.4426996000, 1.9681333000e-3, -3.1254002000e-3 /) + LoveDat(1:4,726) = (/ 725.0, -5.4449495000, 1.9669852000e-3, -3.1226881000e-3 /) + LoveDat(1:4,727) = (/ 726.0, -5.4471939000, 1.9658379000e-3, -3.1199799000e-3 /) + LoveDat(1:4,728) = (/ 727.0, -5.4494328000, 1.9646915000e-3, -3.1172758000e-3 /) + LoveDat(1:4,729) = (/ 728.0, -5.4516661000, 1.9635458000e-3, -3.1145757000e-3 /) + LoveDat(1:4,730) = (/ 729.0, -5.4538938000, 1.9624010000e-3, -3.1118796000e-3 /) + LoveDat(1:4,731) = (/ 730.0, -5.4561161000, 1.9612570000e-3, -3.1091874000e-3 /) + LoveDat(1:4,732) = (/ 731.0, -5.4583329000, 1.9601138000e-3, -3.1064992000e-3 /) + LoveDat(1:4,733) = (/ 732.0, -5.4605441000, 1.9589714000e-3, -3.1038149000e-3 /) + LoveDat(1:4,734) = (/ 733.0, -5.4627499000, 1.9578298000e-3, -3.1011346000e-3 /) + LoveDat(1:4,735) = (/ 734.0, -5.4649502000, 1.9566889000e-3, -3.0984582000e-3 /) + LoveDat(1:4,736) = (/ 735.0, -5.4671450000, 1.9555488000e-3, -3.0957857000e-3 /) + LoveDat(1:4,737) = (/ 736.0, -5.4693343000, 1.9544095000e-3, -3.0931171000e-3 /) + LoveDat(1:4,738) = (/ 737.0, -5.4715182000, 1.9532709000e-3, -3.0904524000e-3 /) + LoveDat(1:4,739) = (/ 738.0, -5.4736966000, 1.9521331000e-3, -3.0877915000e-3 /) + LoveDat(1:4,740) = (/ 739.0, -5.4758696000, 1.9509960000e-3, -3.0851345000e-3 /) + LoveDat(1:4,741) = (/ 740.0, -5.4780372000, 1.9498596000e-3, -3.0824813000e-3 /) + LoveDat(1:4,742) = (/ 741.0, -5.4801993000, 1.9487240000e-3, -3.0798319000e-3 /) + LoveDat(1:4,743) = (/ 742.0, -5.4823560000, 1.9475891000e-3, -3.0771864000e-3 /) + LoveDat(1:4,744) = (/ 743.0, -5.4845073000, 1.9464549000e-3, -3.0745446000e-3 /) + LoveDat(1:4,745) = (/ 744.0, -5.4866533000, 1.9453214000e-3, -3.0719066000e-3 /) + LoveDat(1:4,746) = (/ 745.0, -5.4887938000, 1.9441885000e-3, -3.0692724000e-3 /) + LoveDat(1:4,747) = (/ 746.0, -5.4909289000, 1.9430564000e-3, -3.0666420000e-3 /) + LoveDat(1:4,748) = (/ 747.0, -5.4930587000, 1.9419250000e-3, -3.0640153000e-3 /) + LoveDat(1:4,749) = (/ 748.0, -5.4951831000, 1.9407942000e-3, -3.0613923000e-3 /) + LoveDat(1:4,750) = (/ 749.0, -5.4973021000, 1.9396641000e-3, -3.0587731000e-3 /) + LoveDat(1:4,751) = (/ 750.0, -5.4994158000, 1.9385347000e-3, -3.0561575000e-3 /) + LoveDat(1:4,752) = (/ 751.0, -5.5015242000, 1.9374059000e-3, -3.0535457000e-3 /) + LoveDat(1:4,753) = (/ 752.0, -5.5036272000, 1.9362778000e-3, -3.0509375000e-3 /) + LoveDat(1:4,754) = (/ 753.0, -5.5057250000, 1.9351503000e-3, -3.0483331000e-3 /) + LoveDat(1:4,755) = (/ 754.0, -5.5078174000, 1.9340235000e-3, -3.0457322000e-3 /) + LoveDat(1:4,756) = (/ 755.0, -5.5099044000, 1.9328973000e-3, -3.0431351000e-3 /) + LoveDat(1:4,757) = (/ 756.0, -5.5119863000, 1.9317717000e-3, -3.0405415000e-3 /) + LoveDat(1:4,758) = (/ 757.0, -5.5140628000, 1.9306468000e-3, -3.0379516000e-3 /) + LoveDat(1:4,759) = (/ 758.0, -5.5161340000, 1.9295224000e-3, -3.0353653000e-3 /) + LoveDat(1:4,760) = (/ 759.0, -5.5182000000, 1.9283987000e-3, -3.0327826000e-3 /) + LoveDat(1:4,761) = (/ 760.0, -5.5202607000, 1.9272756000e-3, -3.0302035000e-3 /) + LoveDat(1:4,762) = (/ 761.0, -5.5223161000, 1.9261531000e-3, -3.0276280000e-3 /) + LoveDat(1:4,763) = (/ 762.0, -5.5243664000, 1.9250311000e-3, -3.0250561000e-3 /) + LoveDat(1:4,764) = (/ 763.0, -5.5264113000, 1.9239098000e-3, -3.0224877000e-3 /) + LoveDat(1:4,765) = (/ 764.0, -5.5284511000, 1.9227890000e-3, -3.0199228000e-3 /) + LoveDat(1:4,766) = (/ 765.0, -5.5304856000, 1.9216689000e-3, -3.0173615000e-3 /) + LoveDat(1:4,767) = (/ 766.0, -5.5325150000, 1.9205493000e-3, -3.0148038000e-3 /) + LoveDat(1:4,768) = (/ 767.0, -5.5345391000, 1.9194303000e-3, -3.0122495000e-3 /) + LoveDat(1:4,769) = (/ 768.0, -5.5365581000, 1.9183118000e-3, -3.0096987000e-3 /) + LoveDat(1:4,770) = (/ 769.0, -5.5385718000, 1.9171939000e-3, -3.0071515000e-3 /) + LoveDat(1:4,771) = (/ 770.0, -5.5405804000, 1.9160766000e-3, -3.0046077000e-3 /) + LoveDat(1:4,772) = (/ 771.0, -5.5425839000, 1.9149598000e-3, -3.0020674000e-3 /) + LoveDat(1:4,773) = (/ 772.0, -5.5445822000, 1.9138435000e-3, -2.9995305000e-3 /) + LoveDat(1:4,774) = (/ 773.0, -5.5465753000, 1.9127278000e-3, -2.9969971000e-3 /) + LoveDat(1:4,775) = (/ 774.0, -5.5485633000, 1.9116127000e-3, -2.9944671000e-3 /) + LoveDat(1:4,776) = (/ 775.0, -5.5505462000, 1.9104981000e-3, -2.9919406000e-3 /) + LoveDat(1:4,777) = (/ 776.0, -5.5525239000, 1.9093840000e-3, -2.9894175000e-3 /) + LoveDat(1:4,778) = (/ 777.0, -5.5544966000, 1.9082704000e-3, -2.9868978000e-3 /) + LoveDat(1:4,779) = (/ 778.0, -5.5564641000, 1.9071573000e-3, -2.9843815000e-3 /) + LoveDat(1:4,780) = (/ 779.0, -5.5584266000, 1.9060448000e-3, -2.9818686000e-3 /) + LoveDat(1:4,781) = (/ 780.0, -5.5603840000, 1.9049328000e-3, -2.9793591000e-3 /) + LoveDat(1:4,782) = (/ 781.0, -5.5623363000, 1.9038213000e-3, -2.9768530000e-3 /) + LoveDat(1:4,783) = (/ 782.0, -5.5642835000, 1.9027103000e-3, -2.9743502000e-3 /) + LoveDat(1:4,784) = (/ 783.0, -5.5662257000, 1.9015998000e-3, -2.9718507000e-3 /) + LoveDat(1:4,785) = (/ 784.0, -5.5681628000, 1.9004898000e-3, -2.9693547000e-3 /) + LoveDat(1:4,786) = (/ 785.0, -5.5700949000, 1.8993803000e-3, -2.9668619000e-3 /) + LoveDat(1:4,787) = (/ 786.0, -5.5720220000, 1.8982713000e-3, -2.9643725000e-3 /) + LoveDat(1:4,788) = (/ 787.0, -5.5739440000, 1.8971627000e-3, -2.9618864000e-3 /) + LoveDat(1:4,789) = (/ 788.0, -5.5758611000, 1.8960547000e-3, -2.9594036000e-3 /) + LoveDat(1:4,790) = (/ 789.0, -5.5777731000, 1.8949471000e-3, -2.9569240000e-3 /) + LoveDat(1:4,791) = (/ 790.0, -5.5796802000, 1.8938401000e-3, -2.9544478000e-3 /) + LoveDat(1:4,792) = (/ 791.0, -5.5815822000, 1.8927334000e-3, -2.9519749000e-3 /) + LoveDat(1:4,793) = (/ 792.0, -5.5834793000, 1.8916273000e-3, -2.9495052000e-3 /) + LoveDat(1:4,794) = (/ 793.0, -5.5853715000, 1.8905216000e-3, -2.9470388000e-3 /) + LoveDat(1:4,795) = (/ 794.0, -5.5872586000, 1.8894164000e-3, -2.9445756000e-3 /) + LoveDat(1:4,796) = (/ 795.0, -5.5891409000, 1.8883117000e-3, -2.9421157000e-3 /) + LoveDat(1:4,797) = (/ 796.0, -5.5910182000, 1.8872074000e-3, -2.9396591000e-3 /) + LoveDat(1:4,798) = (/ 797.0, -5.5928905000, 1.8861036000e-3, -2.9372056000e-3 /) + LoveDat(1:4,799) = (/ 798.0, -5.5947580000, 1.8850002000e-3, -2.9347554000e-3 /) + LoveDat(1:4,800) = (/ 799.0, -5.5966205000, 1.8838973000e-3, -2.9323083000e-3 /) + LoveDat(1:4,801) = (/ 800.0, -5.5984781000, 1.8827948000e-3, -2.9298645000e-3 /) + LoveDat(1:4,802) = (/ 801.0, -5.6003309000, 1.8816928000e-3, -2.9274239000e-3 /) + LoveDat(1:4,803) = (/ 802.0, -5.6021787000, 1.8805912000e-3, -2.9249864000e-3 /) + LoveDat(1:4,804) = (/ 803.0, -5.6040217000, 1.8794901000e-3, -2.9225522000e-3 /) + LoveDat(1:4,805) = (/ 804.0, -5.6058598000, 1.8783894000e-3, -2.9201211000e-3 /) + LoveDat(1:4,806) = (/ 805.0, -5.6076931000, 1.8772891000e-3, -2.9176931000e-3 /) + LoveDat(1:4,807) = (/ 806.0, -5.6095215000, 1.8761892000e-3, -2.9152683000e-3 /) + LoveDat(1:4,808) = (/ 807.0, -5.6113451000, 1.8750898000e-3, -2.9128467000e-3 /) + LoveDat(1:4,809) = (/ 808.0, -5.6131638000, 1.8739909000e-3, -2.9104282000e-3 /) + LoveDat(1:4,810) = (/ 809.0, -5.6149777000, 1.8728923000e-3, -2.9080128000e-3 /) + LoveDat(1:4,811) = (/ 810.0, -5.6167869000, 1.8717942000e-3, -2.9056005000e-3 /) + LoveDat(1:4,812) = (/ 811.0, -5.6185912000, 1.8706965000e-3, -2.9031914000e-3 /) + LoveDat(1:4,813) = (/ 812.0, -5.6203907000, 1.8695992000e-3, -2.9007853000e-3 /) + LoveDat(1:4,814) = (/ 813.0, -5.6221855000, 1.8685023000e-3, -2.8983824000e-3 /) + LoveDat(1:4,815) = (/ 814.0, -5.6239754000, 1.8674058000e-3, -2.8959825000e-3 /) + LoveDat(1:4,816) = (/ 815.0, -5.6257606000, 1.8663098000e-3, -2.8935857000e-3 /) + LoveDat(1:4,817) = (/ 816.0, -5.6275411000, 1.8652141000e-3, -2.8911920000e-3 /) + LoveDat(1:4,818) = (/ 817.0, -5.6293168000, 1.8641189000e-3, -2.8888014000e-3 /) + LoveDat(1:4,819) = (/ 818.0, -5.6310878000, 1.8630241000e-3, -2.8864138000e-3 /) + LoveDat(1:4,820) = (/ 819.0, -5.6328540000, 1.8619297000e-3, -2.8840292000e-3 /) + LoveDat(1:4,821) = (/ 820.0, -5.6346155000, 1.8608357000e-3, -2.8816477000e-3 /) + LoveDat(1:4,822) = (/ 821.0, -5.6363723000, 1.8597420000e-3, -2.8792693000e-3 /) + LoveDat(1:4,823) = (/ 822.0, -5.6381245000, 1.8586488000e-3, -2.8768939000e-3 /) + LoveDat(1:4,824) = (/ 823.0, -5.6398719000, 1.8575560000e-3, -2.8745215000e-3 /) + LoveDat(1:4,825) = (/ 824.0, -5.6416146000, 1.8564636000e-3, -2.8721521000e-3 /) + LoveDat(1:4,826) = (/ 825.0, -5.6433527000, 1.8553716000e-3, -2.8697857000e-3 /) + LoveDat(1:4,827) = (/ 826.0, -5.6450861000, 1.8542799000e-3, -2.8674223000e-3 /) + LoveDat(1:4,828) = (/ 827.0, -5.6468149000, 1.8531887000e-3, -2.8650619000e-3 /) + LoveDat(1:4,829) = (/ 828.0, -5.6485390000, 1.8520979000e-3, -2.8627045000e-3 /) + LoveDat(1:4,830) = (/ 829.0, -5.6502584000, 1.8510074000e-3, -2.8603501000e-3 /) + LoveDat(1:4,831) = (/ 830.0, -5.6519733000, 1.8499173000e-3, -2.8579986000e-3 /) + LoveDat(1:4,832) = (/ 831.0, -5.6536835000, 1.8488276000e-3, -2.8556502000e-3 /) + LoveDat(1:4,833) = (/ 832.0, -5.6553891000, 1.8477384000e-3, -2.8533046000e-3 /) + LoveDat(1:4,834) = (/ 833.0, -5.6570901000, 1.8466494000e-3, -2.8509621000e-3 /) + LoveDat(1:4,835) = (/ 834.0, -5.6587866000, 1.8455609000e-3, -2.8486224000e-3 /) + LoveDat(1:4,836) = (/ 835.0, -5.6604784000, 1.8444728000e-3, -2.8462858000e-3 /) + LoveDat(1:4,837) = (/ 836.0, -5.6621657000, 1.8433850000e-3, -2.8439520000e-3 /) + LoveDat(1:4,838) = (/ 837.0, -5.6638484000, 1.8422976000e-3, -2.8416212000e-3 /) + LoveDat(1:4,839) = (/ 838.0, -5.6655266000, 1.8412106000e-3, -2.8392933000e-3 /) + LoveDat(1:4,840) = (/ 839.0, -5.6672002000, 1.8401239000e-3, -2.8369683000e-3 /) + LoveDat(1:4,841) = (/ 840.0, -5.6688693000, 1.8390377000e-3, -2.8346462000e-3 /) + LoveDat(1:4,842) = (/ 841.0, -5.6705338000, 1.8379518000e-3, -2.8323270000e-3 /) + LoveDat(1:4,843) = (/ 842.0, -5.6721939000, 1.8368663000e-3, -2.8300107000e-3 /) + LoveDat(1:4,844) = (/ 843.0, -5.6738494000, 1.8357811000e-3, -2.8276973000e-3 /) + LoveDat(1:4,845) = (/ 844.0, -5.6755004000, 1.8346964000e-3, -2.8253867000e-3 /) + LoveDat(1:4,846) = (/ 845.0, -5.6771470000, 1.8336120000e-3, -2.8230791000e-3 /) + LoveDat(1:4,847) = (/ 846.0, -5.6787890000, 1.8325279000e-3, -2.8207743000e-3 /) + LoveDat(1:4,848) = (/ 847.0, -5.6804266000, 1.8314443000e-3, -2.8184724000e-3 /) + LoveDat(1:4,849) = (/ 848.0, -5.6820597000, 1.8303610000e-3, -2.8161733000e-3 /) + LoveDat(1:4,850) = (/ 849.0, -5.6836884000, 1.8292781000e-3, -2.8138771000e-3 /) + LoveDat(1:4,851) = (/ 850.0, -5.6853127000, 1.8281955000e-3, -2.8115837000e-3 /) + LoveDat(1:4,852) = (/ 851.0, -5.6869325000, 1.8271133000e-3, -2.8092932000e-3 /) + LoveDat(1:4,853) = (/ 852.0, -5.6885478000, 1.8260315000e-3, -2.8070055000e-3 /) + LoveDat(1:4,854) = (/ 853.0, -5.6901588000, 1.8249501000e-3, -2.8047206000e-3 /) + LoveDat(1:4,855) = (/ 854.0, -5.6917653000, 1.8238690000e-3, -2.8024385000e-3 /) + LoveDat(1:4,856) = (/ 855.0, -5.6933675000, 1.8227882000e-3, -2.8001593000e-3 /) + LoveDat(1:4,857) = (/ 856.0, -5.6949653000, 1.8217079000e-3, -2.7978829000e-3 /) + LoveDat(1:4,858) = (/ 857.0, -5.6965586000, 1.8206279000e-3, -2.7956092000e-3 /) + LoveDat(1:4,859) = (/ 858.0, -5.6981477000, 1.8195482000e-3, -2.7933384000e-3 /) + LoveDat(1:4,860) = (/ 859.0, -5.6997323000, 1.8184690000e-3, -2.7910703000e-3 /) + LoveDat(1:4,861) = (/ 860.0, -5.7013126000, 1.8173900000e-3, -2.7888051000e-3 /) + LoveDat(1:4,862) = (/ 861.0, -5.7028886000, 1.8163115000e-3, -2.7865426000e-3 /) + LoveDat(1:4,863) = (/ 862.0, -5.7044602000, 1.8152333000e-3, -2.7842829000e-3 /) + LoveDat(1:4,864) = (/ 863.0, -5.7060275000, 1.8141555000e-3, -2.7820260000e-3 /) + LoveDat(1:4,865) = (/ 864.0, -5.7075905000, 1.8130780000e-3, -2.7797718000e-3 /) + LoveDat(1:4,866) = (/ 865.0, -5.7091492000, 1.8120009000e-3, -2.7775204000e-3 /) + LoveDat(1:4,867) = (/ 866.0, -5.7107035000, 1.8109241000e-3, -2.7752717000e-3 /) + LoveDat(1:4,868) = (/ 867.0, -5.7122536000, 1.8098477000e-3, -2.7730258000e-3 /) + LoveDat(1:4,869) = (/ 868.0, -5.7137995000, 1.8087717000e-3, -2.7707826000e-3 /) + LoveDat(1:4,870) = (/ 869.0, -5.7153410000, 1.8076960000e-3, -2.7685421000e-3 /) + LoveDat(1:4,871) = (/ 870.0, -5.7168783000, 1.8066207000e-3, -2.7663044000e-3 /) + LoveDat(1:4,872) = (/ 871.0, -5.7184113000, 1.8055458000e-3, -2.7640694000e-3 /) + LoveDat(1:4,873) = (/ 872.0, -5.7199401000, 1.8044712000e-3, -2.7618372000e-3 /) + LoveDat(1:4,874) = (/ 873.0, -5.7214646000, 1.8033969000e-3, -2.7596076000e-3 /) + LoveDat(1:4,875) = (/ 874.0, -5.7229850000, 1.8023230000e-3, -2.7573808000e-3 /) + LoveDat(1:4,876) = (/ 875.0, -5.7245011000, 1.8012495000e-3, -2.7551566000e-3 /) + LoveDat(1:4,877) = (/ 876.0, -5.7260130000, 1.8001763000e-3, -2.7529352000e-3 /) + LoveDat(1:4,878) = (/ 877.0, -5.7275207000, 1.7991035000e-3, -2.7507164000e-3 /) + LoveDat(1:4,879) = (/ 878.0, -5.7290242000, 1.7980311000e-3, -2.7485003000e-3 /) + LoveDat(1:4,880) = (/ 879.0, -5.7305236000, 1.7969590000e-3, -2.7462870000e-3 /) + LoveDat(1:4,881) = (/ 880.0, -5.7320187000, 1.7958873000e-3, -2.7440763000e-3 /) + LoveDat(1:4,882) = (/ 881.0, -5.7335097000, 1.7948159000e-3, -2.7418682000e-3 /) + LoveDat(1:4,883) = (/ 882.0, -5.7349966000, 1.7937449000e-3, -2.7396629000e-3 /) + LoveDat(1:4,884) = (/ 883.0, -5.7364793000, 1.7926742000e-3, -2.7374601000e-3 /) + LoveDat(1:4,885) = (/ 884.0, -5.7379579000, 1.7916039000e-3, -2.7352601000e-3 /) + LoveDat(1:4,886) = (/ 885.0, -5.7394323000, 1.7905340000e-3, -2.7330627000e-3 /) + LoveDat(1:4,887) = (/ 886.0, -5.7409027000, 1.7894644000e-3, -2.7308680000e-3 /) + LoveDat(1:4,888) = (/ 887.0, -5.7423689000, 1.7883951000e-3, -2.7286759000e-3 /) + LoveDat(1:4,889) = (/ 888.0, -5.7438310000, 1.7873263000e-3, -2.7264864000e-3 /) + LoveDat(1:4,890) = (/ 889.0, -5.7452891000, 1.7862578000e-3, -2.7242996000e-3 /) + LoveDat(1:4,891) = (/ 890.0, -5.7467430000, 1.7851896000e-3, -2.7221154000e-3 /) + LoveDat(1:4,892) = (/ 891.0, -5.7481929000, 1.7841218000e-3, -2.7199338000e-3 /) + LoveDat(1:4,893) = (/ 892.0, -5.7496387000, 1.7830544000e-3, -2.7177548000e-3 /) + LoveDat(1:4,894) = (/ 893.0, -5.7510805000, 1.7819873000e-3, -2.7155785000e-3 /) + LoveDat(1:4,895) = (/ 894.0, -5.7525182000, 1.7809206000e-3, -2.7134047000e-3 /) + LoveDat(1:4,896) = (/ 895.0, -5.7539519000, 1.7798543000e-3, -2.7112336000e-3 /) + LoveDat(1:4,897) = (/ 896.0, -5.7553816000, 1.7787883000e-3, -2.7090651000e-3 /) + LoveDat(1:4,898) = (/ 897.0, -5.7568072000, 1.7777227000e-3, -2.7068991000e-3 /) + LoveDat(1:4,899) = (/ 898.0, -5.7582289000, 1.7766574000e-3, -2.7047358000e-3 /) + LoveDat(1:4,900) = (/ 899.0, -5.7596465000, 1.7755925000e-3, -2.7025750000e-3 /) + LoveDat(1:4,901) = (/ 900.0, -5.7610602000, 1.7745280000e-3, -2.7004169000e-3 /) + LoveDat(1:4,902) = (/ 901.0, -5.7624698000, 1.7734638000e-3, -2.6982613000e-3 /) + LoveDat(1:4,903) = (/ 902.0, -5.7638755000, 1.7724000000e-3, -2.6961082000e-3 /) + LoveDat(1:4,904) = (/ 903.0, -5.7652772000, 1.7713365000e-3, -2.6939578000e-3 /) + LoveDat(1:4,905) = (/ 904.0, -5.7666750000, 1.7702734000e-3, -2.6918099000e-3 /) + LoveDat(1:4,906) = (/ 905.0, -5.7680688000, 1.7692107000e-3, -2.6896645000e-3 /) + LoveDat(1:4,907) = (/ 906.0, -5.7694587000, 1.7681484000e-3, -2.6875218000e-3 /) + LoveDat(1:4,908) = (/ 907.0, -5.7708447000, 1.7670864000e-3, -2.6853815000e-3 /) + LoveDat(1:4,909) = (/ 908.0, -5.7722267000, 1.7660247000e-3, -2.6832438000e-3 /) + LoveDat(1:4,910) = (/ 909.0, -5.7736048000, 1.7649635000e-3, -2.6811087000e-3 /) + LoveDat(1:4,911) = (/ 910.0, -5.7749791000, 1.7639026000e-3, -2.6789761000e-3 /) + LoveDat(1:4,912) = (/ 911.0, -5.7763494000, 1.7628421000e-3, -2.6768460000e-3 /) + LoveDat(1:4,913) = (/ 912.0, -5.7777158000, 1.7617819000e-3, -2.6747185000e-3 /) + LoveDat(1:4,914) = (/ 913.0, -5.7790784000, 1.7607221000e-3, -2.6725934000e-3 /) + LoveDat(1:4,915) = (/ 914.0, -5.7804371000, 1.7596627000e-3, -2.6704709000e-3 /) + LoveDat(1:4,916) = (/ 915.0, -5.7817919000, 1.7586037000e-3, -2.6683510000e-3 /) + LoveDat(1:4,917) = (/ 916.0, -5.7831429000, 1.7575450000e-3, -2.6662335000e-3 /) + LoveDat(1:4,918) = (/ 917.0, -5.7844901000, 1.7564867000e-3, -2.6641185000e-3 /) + LoveDat(1:4,919) = (/ 918.0, -5.7858334000, 1.7554288000e-3, -2.6620060000e-3 /) + LoveDat(1:4,920) = (/ 919.0, -5.7871729000, 1.7543712000e-3, -2.6598961000e-3 /) + LoveDat(1:4,921) = (/ 920.0, -5.7885086000, 1.7533140000e-3, -2.6577886000e-3 /) + LoveDat(1:4,922) = (/ 921.0, -5.7898405000, 1.7522572000e-3, -2.6556836000e-3 /) + LoveDat(1:4,923) = (/ 922.0, -5.7911686000, 1.7512008000e-3, -2.6535811000e-3 /) + LoveDat(1:4,924) = (/ 923.0, -5.7924928000, 1.7501447000e-3, -2.6514811000e-3 /) + LoveDat(1:4,925) = (/ 924.0, -5.7938134000, 1.7490890000e-3, -2.6493836000e-3 /) + LoveDat(1:4,926) = (/ 925.0, -5.7951301000, 1.7480337000e-3, -2.6472885000e-3 /) + LoveDat(1:4,927) = (/ 926.0, -5.7964431000, 1.7469788000e-3, -2.6451960000e-3 /) + LoveDat(1:4,928) = (/ 927.0, -5.7977523000, 1.7459242000e-3, -2.6431058000e-3 /) + LoveDat(1:4,929) = (/ 928.0, -5.7990578000, 1.7448700000e-3, -2.6410182000e-3 /) + LoveDat(1:4,930) = (/ 929.0, -5.8003595000, 1.7438162000e-3, -2.6389330000e-3 /) + LoveDat(1:4,931) = (/ 930.0, -5.8016575000, 1.7427628000e-3, -2.6368502000e-3 /) + LoveDat(1:4,932) = (/ 931.0, -5.8029518000, 1.7417098000e-3, -2.6347699000e-3 /) + LoveDat(1:4,933) = (/ 932.0, -5.8042424000, 1.7406571000e-3, -2.6326921000e-3 /) + LoveDat(1:4,934) = (/ 933.0, -5.8055293000, 1.7396049000e-3, -2.6306167000e-3 /) + LoveDat(1:4,935) = (/ 934.0, -5.8068125000, 1.7385530000e-3, -2.6285437000e-3 /) + LoveDat(1:4,936) = (/ 935.0, -5.8080920000, 1.7375015000e-3, -2.6264732000e-3 /) + LoveDat(1:4,937) = (/ 936.0, -5.8093679000, 1.7364504000e-3, -2.6244051000e-3 /) + LoveDat(1:4,938) = (/ 937.0, -5.8106400000, 1.7353996000e-3, -2.6223394000e-3 /) + LoveDat(1:4,939) = (/ 938.0, -5.8119085000, 1.7343493000e-3, -2.6202762000e-3 /) + LoveDat(1:4,940) = (/ 939.0, -5.8131734000, 1.7332993000e-3, -2.6182153000e-3 /) + LoveDat(1:4,941) = (/ 940.0, -5.8144346000, 1.7322497000e-3, -2.6161569000e-3 /) + LoveDat(1:4,942) = (/ 941.0, -5.8156922000, 1.7312006000e-3, -2.6141009000e-3 /) + LoveDat(1:4,943) = (/ 942.0, -5.8169461000, 1.7301518000e-3, -2.6120473000e-3 /) + LoveDat(1:4,944) = (/ 943.0, -5.8181965000, 1.7291034000e-3, -2.6099961000e-3 /) + LoveDat(1:4,945) = (/ 944.0, -5.8194432000, 1.7280553000e-3, -2.6079473000e-3 /) + LoveDat(1:4,946) = (/ 945.0, -5.8206864000, 1.7270077000e-3, -2.6059010000e-3 /) + LoveDat(1:4,947) = (/ 946.0, -5.8219259000, 1.7259605000e-3, -2.6038570000e-3 /) + LoveDat(1:4,948) = (/ 947.0, -5.8231619000, 1.7249137000e-3, -2.6018154000e-3 /) + LoveDat(1:4,949) = (/ 948.0, -5.8243943000, 1.7238672000e-3, -2.5997761000e-3 /) + LoveDat(1:4,950) = (/ 949.0, -5.8256231000, 1.7228212000e-3, -2.5977393000e-3 /) + LoveDat(1:4,951) = (/ 950.0, -5.8268484000, 1.7217755000e-3, -2.5957048000e-3 /) + LoveDat(1:4,952) = (/ 951.0, -5.8280701000, 1.7207303000e-3, -2.5936728000e-3 /) + LoveDat(1:4,953) = (/ 952.0, -5.8292883000, 1.7196854000e-3, -2.5916430000e-3 /) + LoveDat(1:4,954) = (/ 953.0, -5.8305029000, 1.7186410000e-3, -2.5896157000e-3 /) + LoveDat(1:4,955) = (/ 954.0, -5.8317141000, 1.7175970000e-3, -2.5875907000e-3 /) + LoveDat(1:4,956) = (/ 955.0, -5.8329217000, 1.7165533000e-3, -2.5855681000e-3 /) + LoveDat(1:4,957) = (/ 956.0, -5.8341258000, 1.7155101000e-3, -2.5835478000e-3 /) + LoveDat(1:4,958) = (/ 957.0, -5.8353264000, 1.7144672000e-3, -2.5815299000e-3 /) + LoveDat(1:4,959) = (/ 958.0, -5.8365235000, 1.7134248000e-3, -2.5795144000e-3 /) + LoveDat(1:4,960) = (/ 959.0, -5.8377172000, 1.7123828000e-3, -2.5775012000e-3 /) + LoveDat(1:4,961) = (/ 960.0, -5.8389074000, 1.7113411000e-3, -2.5754903000e-3 /) + LoveDat(1:4,962) = (/ 961.0, -5.8400941000, 1.7102999000e-3, -2.5734818000e-3 /) + LoveDat(1:4,963) = (/ 962.0, -5.8412773000, 1.7092591000e-3, -2.5714756000e-3 /) + LoveDat(1:4,964) = (/ 963.0, -5.8424571000, 1.7082187000e-3, -2.5694717000e-3 /) + LoveDat(1:4,965) = (/ 964.0, -5.8436335000, 1.7071787000e-3, -2.5674702000e-3 /) + LoveDat(1:4,966) = (/ 965.0, -5.8448065000, 1.7061391000e-3, -2.5654710000e-3 /) + LoveDat(1:4,967) = (/ 966.0, -5.8459760000, 1.7051000000e-3, -2.5634741000e-3 /) + LoveDat(1:4,968) = (/ 967.0, -5.8471421000, 1.7040612000e-3, -2.5614796000e-3 /) + LoveDat(1:4,969) = (/ 968.0, -5.8483048000, 1.7030229000e-3, -2.5594873000e-3 /) + LoveDat(1:4,970) = (/ 969.0, -5.8494641000, 1.7019850000e-3, -2.5574974000e-3 /) + LoveDat(1:4,971) = (/ 970.0, -5.8506200000, 1.7009475000e-3, -2.5555098000e-3 /) + LoveDat(1:4,972) = (/ 971.0, -5.8517725000, 1.6999104000e-3, -2.5535245000e-3 /) + LoveDat(1:4,973) = (/ 972.0, -5.8529217000, 1.6988737000e-3, -2.5515415000e-3 /) + LoveDat(1:4,974) = (/ 973.0, -5.8540675000, 1.6978374000e-3, -2.5495608000e-3 /) + LoveDat(1:4,975) = (/ 974.0, -5.8552099000, 1.6968016000e-3, -2.5475824000e-3 /) + LoveDat(1:4,976) = (/ 975.0, -5.8563490000, 1.6957662000e-3, -2.5456062000e-3 /) + LoveDat(1:4,977) = (/ 976.0, -5.8574847000, 1.6947312000e-3, -2.5436324000e-3 /) + LoveDat(1:4,978) = (/ 977.0, -5.8586172000, 1.6936966000e-3, -2.5416609000e-3 /) + LoveDat(1:4,979) = (/ 978.0, -5.8597463000, 1.6926625000e-3, -2.5396916000e-3 /) + LoveDat(1:4,980) = (/ 979.0, -5.8608720000, 1.6916288000e-3, -2.5377246000e-3 /) + LoveDat(1:4,981) = (/ 980.0, -5.8619945000, 1.6905955000e-3, -2.5357599000e-3 /) + LoveDat(1:4,982) = (/ 981.0, -5.8631137000, 1.6895626000e-3, -2.5337975000e-3 /) + LoveDat(1:4,983) = (/ 982.0, -5.8642296000, 1.6885302000e-3, -2.5318373000e-3 /) + LoveDat(1:4,984) = (/ 983.0, -5.8653422000, 1.6874982000e-3, -2.5298794000e-3 /) + LoveDat(1:4,985) = (/ 984.0, -5.8664515000, 1.6864666000e-3, -2.5279238000e-3 /) + LoveDat(1:4,986) = (/ 985.0, -5.8675576000, 1.6854355000e-3, -2.5259704000e-3 /) + LoveDat(1:4,987) = (/ 986.0, -5.8686604000, 1.6844048000e-3, -2.5240193000e-3 /) + LoveDat(1:4,988) = (/ 987.0, -5.8697599000, 1.6833745000e-3, -2.5220704000e-3 /) + LoveDat(1:4,989) = (/ 988.0, -5.8708562000, 1.6823447000e-3, -2.5201238000e-3 /) + LoveDat(1:4,990) = (/ 989.0, -5.8719493000, 1.6813153000e-3, -2.5181795000e-3 /) + LoveDat(1:4,991) = (/ 990.0, -5.8730391000, 1.6802863000e-3, -2.5162374000e-3 /) + LoveDat(1:4,992) = (/ 991.0, -5.8741258000, 1.6792578000e-3, -2.5142975000e-3 /) + LoveDat(1:4,993) = (/ 992.0, -5.8752092000, 1.6782297000e-3, -2.5123598000e-3 /) + LoveDat(1:4,994) = (/ 993.0, -5.8762894000, 1.6772020000e-3, -2.5104244000e-3 /) + LoveDat(1:4,995) = (/ 994.0, -5.8773664000, 1.6761748000e-3, -2.5084913000e-3 /) + LoveDat(1:4,996) = (/ 995.0, -5.8784403000, 1.6751480000e-3, -2.5065603000e-3 /) + LoveDat(1:4,997) = (/ 996.0, -5.8795109000, 1.6741217000e-3, -2.5046316000e-3 /) + LoveDat(1:4,998) = (/ 997.0, -5.8805784000, 1.6730958000e-3, -2.5027051000e-3 /) + LoveDat(1:4,999) = (/ 998.0, -5.8816427000, 1.6720704000e-3, -2.5007809000e-3 /) + LoveDat(1:4,1000) = (/ 999.0, -5.8827039000, 1.6710454000e-3, -2.4988588000e-3 /) + LoveDat(1:4,1001) = (/ 1000.0, -5.8837619000, 1.6700209000e-3, -2.4969390000e-3 /) + LoveDat(1:4,1002) = (/ 1001.0, -5.8848168000, 1.6689968000e-3, -2.4950213000e-3 /) + LoveDat(1:4,1003) = (/ 1002.0, -5.8858686000, 1.6679732000e-3, -2.4931059000e-3 /) + LoveDat(1:4,1004) = (/ 1003.0, -5.8869172000, 1.6669500000e-3, -2.4911927000e-3 /) + LoveDat(1:4,1005) = (/ 1004.0, -5.8879627000, 1.6659272000e-3, -2.4892817000e-3 /) + LoveDat(1:4,1006) = (/ 1005.0, -5.8890051000, 1.6649049000e-3, -2.4873729000e-3 /) + LoveDat(1:4,1007) = (/ 1006.0, -5.8900444000, 1.6638831000e-3, -2.4854663000e-3 /) + LoveDat(1:4,1008) = (/ 1007.0, -5.8910807000, 1.6628617000e-3, -2.4835619000e-3 /) + LoveDat(1:4,1009) = (/ 1008.0, -5.8921138000, 1.6618408000e-3, -2.4816596000e-3 /) + LoveDat(1:4,1010) = (/ 1009.0, -5.8931439000, 1.6608204000e-3, -2.4797596000e-3 /) + LoveDat(1:4,1011) = (/ 1010.0, -5.8941708000, 1.6598004000e-3, -2.4778617000e-3 /) + LoveDat(1:4,1012) = (/ 1011.0, -5.8951948000, 1.6587808000e-3, -2.4759660000e-3 /) + LoveDat(1:4,1013) = (/ 1012.0, -5.8962156000, 1.6577617000e-3, -2.4740725000e-3 /) + LoveDat(1:4,1014) = (/ 1013.0, -5.8972335000, 1.6567431000e-3, -2.4721812000e-3 /) + LoveDat(1:4,1015) = (/ 1014.0, -5.8982483000, 1.6557250000e-3, -2.4702920000e-3 /) + LoveDat(1:4,1016) = (/ 1015.0, -5.8992600000, 1.6547073000e-3, -2.4684051000e-3 /) + LoveDat(1:4,1017) = (/ 1016.0, -5.9002688000, 1.6536901000e-3, -2.4665202000e-3 /) + LoveDat(1:4,1018) = (/ 1017.0, -5.9012745000, 1.6526733000e-3, -2.4646376000e-3 /) + LoveDat(1:4,1019) = (/ 1018.0, -5.9022772000, 1.6516570000e-3, -2.4627571000e-3 /) + LoveDat(1:4,1020) = (/ 1019.0, -5.9032769000, 1.6506412000e-3, -2.4608788000e-3 /) + LoveDat(1:4,1021) = (/ 1020.0, -5.9042737000, 1.6496258000e-3, -2.4590026000e-3 /) + LoveDat(1:4,1022) = (/ 1021.0, -5.9052674000, 1.6486109000e-3, -2.4571286000e-3 /) + LoveDat(1:4,1023) = (/ 1022.0, -5.9062582000, 1.6475965000e-3, -2.4552567000e-3 /) + LoveDat(1:4,1024) = (/ 1023.0, -5.9072460000, 1.6465826000e-3, -2.4533870000e-3 /) + LoveDat(1:4,1025) = (/ 1024.0, -5.9082308000, 1.6455691000e-3, -2.4515194000e-3 /) + LoveDat(1:4,1026) = (/ 1025.0, -5.9092127000, 1.6445561000e-3, -2.4496539000e-3 /) + LoveDat(1:4,1027) = (/ 1026.0, -5.9101917000, 1.6435436000e-3, -2.4477906000e-3 /) + LoveDat(1:4,1028) = (/ 1027.0, -5.9111677000, 1.6425316000e-3, -2.4459295000e-3 /) + LoveDat(1:4,1029) = (/ 1028.0, -5.9121408000, 1.6415200000e-3, -2.4440704000e-3 /) + LoveDat(1:4,1030) = (/ 1029.0, -5.9131109000, 1.6405089000e-3, -2.4422135000e-3 /) + LoveDat(1:4,1031) = (/ 1030.0, -5.9140782000, 1.6394983000e-3, -2.4403587000e-3 /) + LoveDat(1:4,1032) = (/ 1031.0, -5.9150425000, 1.6384882000e-3, -2.4385061000e-3 /) + LoveDat(1:4,1033) = (/ 1032.0, -5.9160039000, 1.6374786000e-3, -2.4366555000e-3 /) + LoveDat(1:4,1034) = (/ 1033.0, -5.9169625000, 1.6364694000e-3, -2.4348071000e-3 /) + LoveDat(1:4,1035) = (/ 1034.0, -5.9179181000, 1.6354607000e-3, -2.4329608000e-3 /) + LoveDat(1:4,1036) = (/ 1035.0, -5.9188709000, 1.6344526000e-3, -2.4311166000e-3 /) + LoveDat(1:4,1037) = (/ 1036.0, -5.9198208000, 1.6334449000e-3, -2.4292746000e-3 /) + LoveDat(1:4,1038) = (/ 1037.0, -5.9207678000, 1.6324377000e-3, -2.4274346000e-3 /) + LoveDat(1:4,1039) = (/ 1038.0, -5.9217120000, 1.6314309000e-3, -2.4255967000e-3 /) + LoveDat(1:4,1040) = (/ 1039.0, -5.9226533000, 1.6304247000e-3, -2.4237610000e-3 /) + LoveDat(1:4,1041) = (/ 1040.0, -5.9235918000, 1.6294190000e-3, -2.4219273000e-3 /) + LoveDat(1:4,1042) = (/ 1041.0, -5.9245275000, 1.6284137000e-3, -2.4200958000e-3 /) + LoveDat(1:4,1043) = (/ 1042.0, -5.9254603000, 1.6274090000e-3, -2.4182663000e-3 /) + LoveDat(1:4,1044) = (/ 1043.0, -5.9263904000, 1.6264047000e-3, -2.4164389000e-3 /) + LoveDat(1:4,1045) = (/ 1044.0, -5.9273176000, 1.6254009000e-3, -2.4146136000e-3 /) + LoveDat(1:4,1046) = (/ 1045.0, -5.9282420000, 1.6243977000e-3, -2.4127904000e-3 /) + LoveDat(1:4,1047) = (/ 1046.0, -5.9291636000, 1.6233949000e-3, -2.4109693000e-3 /) + LoveDat(1:4,1048) = (/ 1047.0, -5.9300824000, 1.6223926000e-3, -2.4091503000e-3 /) + LoveDat(1:4,1049) = (/ 1048.0, -5.9309984000, 1.6213909000e-3, -2.4073333000e-3 /) + LoveDat(1:4,1050) = (/ 1049.0, -5.9319117000, 1.6203896000e-3, -2.4055185000e-3 /) + LoveDat(1:4,1051) = (/ 1050.0, -5.9328222000, 1.6193888000e-3, -2.4037057000e-3 /) + LoveDat(1:4,1052) = (/ 1051.0, -5.9337299000, 1.6183886000e-3, -2.4018949000e-3 /) + LoveDat(1:4,1053) = (/ 1052.0, -5.9346349000, 1.6173888000e-3, -2.4000862000e-3 /) + LoveDat(1:4,1054) = (/ 1053.0, -5.9355371000, 1.6163895000e-3, -2.3982796000e-3 /) + LoveDat(1:4,1055) = (/ 1054.0, -5.9364366000, 1.6153908000e-3, -2.3964751000e-3 /) + LoveDat(1:4,1056) = (/ 1055.0, -5.9373334000, 1.6143925000e-3, -2.3946726000e-3 /) + LoveDat(1:4,1057) = (/ 1056.0, -5.9382274000, 1.6133948000e-3, -2.3928722000e-3 /) + LoveDat(1:4,1058) = (/ 1057.0, -5.9391187000, 1.6123976000e-3, -2.3910738000e-3 /) + LoveDat(1:4,1059) = (/ 1058.0, -5.9400074000, 1.6114009000e-3, -2.3892775000e-3 /) + LoveDat(1:4,1060) = (/ 1059.0, -5.9408933000, 1.6104046000e-3, -2.3874833000e-3 /) + LoveDat(1:4,1061) = (/ 1060.0, -5.9417765000, 1.6094089000e-3, -2.3856910000e-3 /) + LoveDat(1:4,1062) = (/ 1061.0, -5.9426570000, 1.6084138000e-3, -2.3839009000e-3 /) + LoveDat(1:4,1063) = (/ 1062.0, -5.9435349000, 1.6074191000e-3, -2.3821127000e-3 /) + LoveDat(1:4,1064) = (/ 1063.0, -5.9444100000, 1.6064249000e-3, -2.3803266000e-3 /) + LoveDat(1:4,1065) = (/ 1064.0, -5.9452825000, 1.6054313000e-3, -2.3785426000e-3 /) + LoveDat(1:4,1066) = (/ 1065.0, -5.9461524000, 1.6044382000e-3, -2.3767606000e-3 /) + LoveDat(1:4,1067) = (/ 1066.0, -5.9470196000, 1.6034456000e-3, -2.3749806000e-3 /) + LoveDat(1:4,1068) = (/ 1067.0, -5.9478841000, 1.6024535000e-3, -2.3732026000e-3 /) + LoveDat(1:4,1069) = (/ 1068.0, -5.9487460000, 1.6014619000e-3, -2.3714267000e-3 /) + LoveDat(1:4,1070) = (/ 1069.0, -5.9496053000, 1.6004709000e-3, -2.3696528000e-3 /) + LoveDat(1:4,1071) = (/ 1070.0, -5.9504619000, 1.5994804000e-3, -2.3678809000e-3 /) + LoveDat(1:4,1072) = (/ 1071.0, -5.9513160000, 1.5984904000e-3, -2.3661110000e-3 /) + LoveDat(1:4,1073) = (/ 1072.0, -5.9521674000, 1.5975009000e-3, -2.3643432000e-3 /) + LoveDat(1:4,1074) = (/ 1073.0, -5.9530162000, 1.5965120000e-3, -2.3625773000e-3 /) + LoveDat(1:4,1075) = (/ 1074.0, -5.9538624000, 1.5955236000e-3, -2.3608135000e-3 /) + LoveDat(1:4,1076) = (/ 1075.0, -5.9547061000, 1.5945357000e-3, -2.3590517000e-3 /) + LoveDat(1:4,1077) = (/ 1076.0, -5.9555471000, 1.5935483000e-3, -2.3572919000e-3 /) + LoveDat(1:4,1078) = (/ 1077.0, -5.9563856000, 1.5925615000e-3, -2.3555341000e-3 /) + LoveDat(1:4,1079) = (/ 1078.0, -5.9572215000, 1.5915752000e-3, -2.3537782000e-3 /) + LoveDat(1:4,1080) = (/ 1079.0, -5.9580548000, 1.5905894000e-3, -2.3520244000e-3 /) + LoveDat(1:4,1081) = (/ 1080.0, -5.9588856000, 1.5896041000e-3, -2.3502726000e-3 /) + LoveDat(1:4,1082) = (/ 1081.0, -5.9597138000, 1.5886194000e-3, -2.3485228000e-3 /) + LoveDat(1:4,1083) = (/ 1082.0, -5.9605395000, 1.5876353000e-3, -2.3467750000e-3 /) + LoveDat(1:4,1084) = (/ 1083.0, -5.9613627000, 1.5866516000e-3, -2.3450292000e-3 /) + LoveDat(1:4,1085) = (/ 1084.0, -5.9621833000, 1.5856685000e-3, -2.3432853000e-3 /) + LoveDat(1:4,1086) = (/ 1085.0, -5.9630014000, 1.5846860000e-3, -2.3415434000e-3 /) + LoveDat(1:4,1087) = (/ 1086.0, -5.9638170000, 1.5837039000e-3, -2.3398036000e-3 /) + LoveDat(1:4,1088) = (/ 1087.0, -5.9646301000, 1.5827224000e-3, -2.3380657000e-3 /) + LoveDat(1:4,1089) = (/ 1088.0, -5.9654407000, 1.5817415000e-3, -2.3363297000e-3 /) + LoveDat(1:4,1090) = (/ 1089.0, -5.9662488000, 1.5807611000e-3, -2.3345958000e-3 /) + LoveDat(1:4,1091) = (/ 1090.0, -5.9670544000, 1.5797812000e-3, -2.3328638000e-3 /) + LoveDat(1:4,1092) = (/ 1091.0, -5.9678575000, 1.5788019000e-3, -2.3311338000e-3 /) + LoveDat(1:4,1093) = (/ 1092.0, -5.9686582000, 1.5778231000e-3, -2.3294057000e-3 /) + LoveDat(1:4,1094) = (/ 1093.0, -5.9694563000, 1.5768449000e-3, -2.3276797000e-3 /) + LoveDat(1:4,1095) = (/ 1094.0, -5.9702521000, 1.5758672000e-3, -2.3259555000e-3 /) + LoveDat(1:4,1096) = (/ 1095.0, -5.9710453000, 1.5748901000e-3, -2.3242334000e-3 /) + LoveDat(1:4,1097) = (/ 1096.0, -5.9718361000, 1.5739135000e-3, -2.3225132000e-3 /) + LoveDat(1:4,1098) = (/ 1097.0, -5.9726245000, 1.5729374000e-3, -2.3207950000e-3 /) + LoveDat(1:4,1099) = (/ 1098.0, -5.9734105000, 1.5719619000e-3, -2.3190787000e-3 /) + LoveDat(1:4,1100) = (/ 1099.0, -5.9741940000, 1.5709870000e-3, -2.3173643000e-3 /) + LoveDat(1:4,1101) = (/ 1100.0, -5.9749751000, 1.5700126000e-3, -2.3156519000e-3 /) + LoveDat(1:4,1102) = (/ 1101.0, -5.9757538000, 1.5690387000e-3, -2.3139415000e-3 /) + LoveDat(1:4,1103) = (/ 1102.0, -5.9765300000, 1.5680654000e-3, -2.3122330000e-3 /) + LoveDat(1:4,1104) = (/ 1103.0, -5.9773039000, 1.5670927000e-3, -2.3105264000e-3 /) + LoveDat(1:4,1105) = (/ 1104.0, -5.9780754000, 1.5661205000e-3, -2.3088218000e-3 /) + LoveDat(1:4,1106) = (/ 1105.0, -5.9788445000, 1.5651489000e-3, -2.3071191000e-3 /) + LoveDat(1:4,1107) = (/ 1106.0, -5.9796112000, 1.5641778000e-3, -2.3054184000e-3 /) + LoveDat(1:4,1108) = (/ 1107.0, -5.9803755000, 1.5632073000e-3, -2.3037195000e-3 /) + LoveDat(1:4,1109) = (/ 1108.0, -5.9811375000, 1.5622374000e-3, -2.3020226000e-3 /) + LoveDat(1:4,1110) = (/ 1109.0, -5.9818971000, 1.5612680000e-3, -2.3003277000e-3 /) + LoveDat(1:4,1111) = (/ 1110.0, -5.9826543000, 1.5602991000e-3, -2.2986346000e-3 /) + LoveDat(1:4,1112) = (/ 1111.0, -5.9834092000, 1.5593309000e-3, -2.2969435000e-3 /) + LoveDat(1:4,1113) = (/ 1112.0, -5.9841618000, 1.5583632000e-3, -2.2952543000e-3 /) + LoveDat(1:4,1114) = (/ 1113.0, -5.9849120000, 1.5573960000e-3, -2.2935670000e-3 /) + LoveDat(1:4,1115) = (/ 1114.0, -5.9856599000, 1.5564294000e-3, -2.2918817000e-3 /) + LoveDat(1:4,1116) = (/ 1115.0, -5.9864054000, 1.5554634000e-3, -2.2901982000e-3 /) + LoveDat(1:4,1117) = (/ 1116.0, -5.9871487000, 1.5544979000e-3, -2.2885167000e-3 /) + LoveDat(1:4,1118) = (/ 1117.0, -5.9878896000, 1.5535331000e-3, -2.2868370000e-3 /) + LoveDat(1:4,1119) = (/ 1118.0, -5.9886282000, 1.5525687000e-3, -2.2851593000e-3 /) + LoveDat(1:4,1120) = (/ 1119.0, -5.9893646000, 1.5516050000e-3, -2.2834835000e-3 /) + LoveDat(1:4,1121) = (/ 1120.0, -5.9900986000, 1.5506418000e-3, -2.2818095000e-3 /) + LoveDat(1:4,1122) = (/ 1121.0, -5.9908304000, 1.5496792000e-3, -2.2801375000e-3 /) + LoveDat(1:4,1123) = (/ 1122.0, -5.9915599000, 1.5487171000e-3, -2.2784674000e-3 /) + LoveDat(1:4,1124) = (/ 1123.0, -5.9922871000, 1.5477557000e-3, -2.2767991000e-3 /) + LoveDat(1:4,1125) = (/ 1124.0, -5.9930120000, 1.5467948000e-3, -2.2751328000e-3 /) + LoveDat(1:4,1126) = (/ 1125.0, -5.9937347000, 1.5458344000e-3, -2.2734683000e-3 /) + LoveDat(1:4,1127) = (/ 1126.0, -5.9944551000, 1.5448747000e-3, -2.2718058000e-3 /) + LoveDat(1:4,1128) = (/ 1127.0, -5.9951733000, 1.5439155000e-3, -2.2701451000e-3 /) + LoveDat(1:4,1129) = (/ 1128.0, -5.9958892000, 1.5429569000e-3, -2.2684863000e-3 /) + LoveDat(1:4,1130) = (/ 1129.0, -5.9966029000, 1.5419989000e-3, -2.2668294000e-3 /) + LoveDat(1:4,1131) = (/ 1130.0, -5.9973144000, 1.5410414000e-3, -2.2651743000e-3 /) + LoveDat(1:4,1132) = (/ 1131.0, -5.9980236000, 1.5400845000e-3, -2.2635212000e-3 /) + LoveDat(1:4,1133) = (/ 1132.0, -5.9987307000, 1.5391282000e-3, -2.2618699000e-3 /) + LoveDat(1:4,1134) = (/ 1133.0, -5.9994355000, 1.5381725000e-3, -2.2602205000e-3 /) + LoveDat(1:4,1135) = (/ 1134.0, -6.0001381000, 1.5372174000e-3, -2.2585729000e-3 /) + LoveDat(1:4,1136) = (/ 1135.0, -6.0008385000, 1.5362628000e-3, -2.2569272000e-3 /) + LoveDat(1:4,1137) = (/ 1136.0, -6.0015367000, 1.5353089000e-3, -2.2552834000e-3 /) + LoveDat(1:4,1138) = (/ 1137.0, -6.0022328000, 1.5343555000e-3, -2.2536414000e-3 /) + LoveDat(1:4,1139) = (/ 1138.0, -6.0029266000, 1.5334027000e-3, -2.2520013000e-3 /) + LoveDat(1:4,1140) = (/ 1139.0, -6.0036183000, 1.5324504000e-3, -2.2503631000e-3 /) + LoveDat(1:4,1141) = (/ 1140.0, -6.0043078000, 1.5314988000e-3, -2.2487267000e-3 /) + LoveDat(1:4,1142) = (/ 1141.0, -6.0049952000, 1.5305477000e-3, -2.2470922000e-3 /) + LoveDat(1:4,1143) = (/ 1142.0, -6.0056804000, 1.5295973000e-3, -2.2454595000e-3 /) + LoveDat(1:4,1144) = (/ 1143.0, -6.0063635000, 1.5286474000e-3, -2.2438287000e-3 /) + LoveDat(1:4,1145) = (/ 1144.0, -6.0070444000, 1.5276981000e-3, -2.2421997000e-3 /) + LoveDat(1:4,1146) = (/ 1145.0, -6.0077231000, 1.5267494000e-3, -2.2405726000e-3 /) + LoveDat(1:4,1147) = (/ 1146.0, -6.0083998000, 1.5258013000e-3, -2.2389473000e-3 /) + LoveDat(1:4,1148) = (/ 1147.0, -6.0090743000, 1.5248538000e-3, -2.2373238000e-3 /) + LoveDat(1:4,1149) = (/ 1148.0, -6.0097467000, 1.5239068000e-3, -2.2357022000e-3 /) + LoveDat(1:4,1150) = (/ 1149.0, -6.0104170000, 1.5229605000e-3, -2.2340824000e-3 /) + LoveDat(1:4,1151) = (/ 1150.0, -6.0110851000, 1.5220147000e-3, -2.2324645000e-3 /) + LoveDat(1:4,1152) = (/ 1151.0, -6.0117512000, 1.5210696000e-3, -2.2308484000e-3 /) + LoveDat(1:4,1153) = (/ 1152.0, -6.0124152000, 1.5201250000e-3, -2.2292341000e-3 /) + LoveDat(1:4,1154) = (/ 1153.0, -6.0130771000, 1.5191811000e-3, -2.2276216000e-3 /) + LoveDat(1:4,1155) = (/ 1154.0, -6.0137369000, 1.5182377000e-3, -2.2260110000e-3 /) + LoveDat(1:4,1156) = (/ 1155.0, -6.0143946000, 1.5172949000e-3, -2.2244022000e-3 /) + LoveDat(1:4,1157) = (/ 1156.0, -6.0150502000, 1.5163527000e-3, -2.2227952000e-3 /) + LoveDat(1:4,1158) = (/ 1157.0, -6.0157038000, 1.5154112000e-3, -2.2211900000e-3 /) + LoveDat(1:4,1159) = (/ 1158.0, -6.0163553000, 1.5144702000e-3, -2.2195866000e-3 /) + LoveDat(1:4,1160) = (/ 1159.0, -6.0170048000, 1.5135298000e-3, -2.2179851000e-3 /) + LoveDat(1:4,1161) = (/ 1160.0, -6.0176522000, 1.5125900000e-3, -2.2163853000e-3 /) + LoveDat(1:4,1162) = (/ 1161.0, -6.0182976000, 1.5116508000e-3, -2.2147874000e-3 /) + LoveDat(1:4,1163) = (/ 1162.0, -6.0189409000, 1.5107122000e-3, -2.2131913000e-3 /) + LoveDat(1:4,1164) = (/ 1163.0, -6.0195822000, 1.5097742000e-3, -2.2115970000e-3 /) + LoveDat(1:4,1165) = (/ 1164.0, -6.0202215000, 1.5088369000e-3, -2.2100045000e-3 /) + LoveDat(1:4,1166) = (/ 1165.0, -6.0208588000, 1.5079001000e-3, -2.2084138000e-3 /) + LoveDat(1:4,1167) = (/ 1166.0, -6.0214940000, 1.5069639000e-3, -2.2068249000e-3 /) + LoveDat(1:4,1168) = (/ 1167.0, -6.0221273000, 1.5060283000e-3, -2.2052377000e-3 /) + LoveDat(1:4,1169) = (/ 1168.0, -6.0227585000, 1.5050934000e-3, -2.2036524000e-3 /) + LoveDat(1:4,1170) = (/ 1169.0, -6.0233877000, 1.5041590000e-3, -2.2020689000e-3 /) + LoveDat(1:4,1171) = (/ 1170.0, -6.0240150000, 1.5032253000e-3, -2.2004872000e-3 /) + LoveDat(1:4,1172) = (/ 1171.0, -6.0246402000, 1.5022921000e-3, -2.1989072000e-3 /) + LoveDat(1:4,1173) = (/ 1172.0, -6.0252635000, 1.5013596000e-3, -2.1973290000e-3 /) + LoveDat(1:4,1174) = (/ 1173.0, -6.0258848000, 1.5004276000e-3, -2.1957527000e-3 /) + LoveDat(1:4,1175) = (/ 1174.0, -6.0265042000, 1.4994963000e-3, -2.1941781000e-3 /) + LoveDat(1:4,1176) = (/ 1175.0, -6.0271215000, 1.4985656000e-3, -2.1926052000e-3 /) + LoveDat(1:4,1177) = (/ 1176.0, -6.0277369000, 1.4976355000e-3, -2.1910342000e-3 /) + LoveDat(1:4,1178) = (/ 1177.0, -6.0283504000, 1.4967060000e-3, -2.1894649000e-3 /) + LoveDat(1:4,1179) = (/ 1178.0, -6.0289619000, 1.4957771000e-3, -2.1878974000e-3 /) + LoveDat(1:4,1180) = (/ 1179.0, -6.0295715000, 1.4948489000e-3, -2.1863317000e-3 /) + LoveDat(1:4,1181) = (/ 1180.0, -6.0301791000, 1.4939212000e-3, -2.1847678000e-3 /) + LoveDat(1:4,1182) = (/ 1181.0, -6.0307848000, 1.4929942000e-3, -2.1832056000e-3 /) + LoveDat(1:4,1183) = (/ 1182.0, -6.0313886000, 1.4920677000e-3, -2.1816451000e-3 /) + LoveDat(1:4,1184) = (/ 1183.0, -6.0319905000, 1.4911419000e-3, -2.1800865000e-3 /) + LoveDat(1:4,1185) = (/ 1184.0, -6.0325904000, 1.4902167000e-3, -2.1785296000e-3 /) + LoveDat(1:4,1186) = (/ 1185.0, -6.0331885000, 1.4892921000e-3, -2.1769744000e-3 /) + LoveDat(1:4,1187) = (/ 1186.0, -6.0337846000, 1.4883682000e-3, -2.1754210000e-3 /) + LoveDat(1:4,1188) = (/ 1187.0, -6.0343789000, 1.4874448000e-3, -2.1738694000e-3 /) + LoveDat(1:4,1189) = (/ 1188.0, -6.0349712000, 1.4865221000e-3, -2.1723195000e-3 /) + LoveDat(1:4,1190) = (/ 1189.0, -6.0355617000, 1.4856000000e-3, -2.1707714000e-3 /) + LoveDat(1:4,1191) = (/ 1190.0, -6.0361503000, 1.4846785000e-3, -2.1692250000e-3 /) + LoveDat(1:4,1192) = (/ 1191.0, -6.0367370000, 1.4837576000e-3, -2.1676804000e-3 /) + LoveDat(1:4,1193) = (/ 1192.0, -6.0373218000, 1.4828373000e-3, -2.1661375000e-3 /) + LoveDat(1:4,1194) = (/ 1193.0, -6.0379048000, 1.4819177000e-3, -2.1645963000e-3 /) + LoveDat(1:4,1195) = (/ 1194.0, -6.0384859000, 1.4809986000e-3, -2.1630569000e-3 /) + LoveDat(1:4,1196) = (/ 1195.0, -6.0390651000, 1.4800802000e-3, -2.1615192000e-3 /) + LoveDat(1:4,1197) = (/ 1196.0, -6.0396426000, 1.4791625000e-3, -2.1599833000e-3 /) + LoveDat(1:4,1198) = (/ 1197.0, -6.0402181000, 1.4782453000e-3, -2.1584490000e-3 /) + LoveDat(1:4,1199) = (/ 1198.0, -6.0407919000, 1.4773288000e-3, -2.1569166000e-3 /) + LoveDat(1:4,1200) = (/ 1199.0, -6.0413638000, 1.4764129000e-3, -2.1553858000e-3 /) + LoveDat(1:4,1201) = (/ 1200.0, -6.0419338000, 1.4754976000e-3, -2.1538568000e-3 /) + LoveDat(1:4,1202) = (/ 1201.0, -6.0425021000, 1.4745829000e-3, -2.1523295000e-3 /) + LoveDat(1:4,1203) = (/ 1202.0, -6.0430685000, 1.4736689000e-3, -2.1508039000e-3 /) + LoveDat(1:4,1204) = (/ 1203.0, -6.0436331000, 1.4727555000e-3, -2.1492800000e-3 /) + LoveDat(1:4,1205) = (/ 1204.0, -6.0441959000, 1.4718427000e-3, -2.1477579000e-3 /) + LoveDat(1:4,1206) = (/ 1205.0, -6.0447570000, 1.4709305000e-3, -2.1462375000e-3 /) + LoveDat(1:4,1207) = (/ 1206.0, -6.0453162000, 1.4700190000e-3, -2.1447188000e-3 /) + LoveDat(1:4,1208) = (/ 1207.0, -6.0458736000, 1.4691081000e-3, -2.1432018000e-3 /) + LoveDat(1:4,1209) = (/ 1208.0, -6.0464292000, 1.4681978000e-3, -2.1416865000e-3 /) + LoveDat(1:4,1210) = (/ 1209.0, -6.0469831000, 1.4672882000e-3, -2.1401729000e-3 /) + LoveDat(1:4,1211) = (/ 1210.0, -6.0475352000, 1.4663791000e-3, -2.1386610000e-3 /) + LoveDat(1:4,1212) = (/ 1211.0, -6.0480855000, 1.4654707000e-3, -2.1371509000e-3 /) + LoveDat(1:4,1213) = (/ 1212.0, -6.0486341000, 1.4645630000e-3, -2.1356424000e-3 /) + LoveDat(1:4,1214) = (/ 1213.0, -6.0491809000, 1.4636558000e-3, -2.1341356000e-3 /) + LoveDat(1:4,1215) = (/ 1214.0, -6.0497259000, 1.4627493000e-3, -2.1326306000e-3 /) + LoveDat(1:4,1216) = (/ 1215.0, -6.0502692000, 1.4618435000e-3, -2.1311272000e-3 /) + LoveDat(1:4,1217) = (/ 1216.0, -6.0508107000, 1.4609382000e-3, -2.1296255000e-3 /) + LoveDat(1:4,1218) = (/ 1217.0, -6.0513505000, 1.4600336000e-3, -2.1281255000e-3 /) + LoveDat(1:4,1219) = (/ 1218.0, -6.0518886000, 1.4591297000e-3, -2.1266272000e-3 /) + LoveDat(1:4,1220) = (/ 1219.0, -6.0524250000, 1.4582263000e-3, -2.1251306000e-3 /) + LoveDat(1:4,1221) = (/ 1220.0, -6.0529596000, 1.4573236000e-3, -2.1236357000e-3 /) + LoveDat(1:4,1222) = (/ 1221.0, -6.0534925000, 1.4564215000e-3, -2.1221424000e-3 /) + LoveDat(1:4,1223) = (/ 1222.0, -6.0540237000, 1.4555201000e-3, -2.1206509000e-3 /) + LoveDat(1:4,1224) = (/ 1223.0, -6.0545531000, 1.4546193000e-3, -2.1191610000e-3 /) + LoveDat(1:4,1225) = (/ 1224.0, -6.0550809000, 1.4537191000e-3, -2.1176728000e-3 /) + LoveDat(1:4,1226) = (/ 1225.0, -6.0556070000, 1.4528196000e-3, -2.1161863000e-3 /) + LoveDat(1:4,1227) = (/ 1226.0, -6.0561314000, 1.4519207000e-3, -2.1147014000e-3 /) + LoveDat(1:4,1228) = (/ 1227.0, -6.0566541000, 1.4510224000e-3, -2.1132182000e-3 /) + LoveDat(1:4,1229) = (/ 1228.0, -6.0571751000, 1.4501248000e-3, -2.1117367000e-3 /) + LoveDat(1:4,1230) = (/ 1229.0, -6.0576944000, 1.4492278000e-3, -2.1102569000e-3 /) + LoveDat(1:4,1231) = (/ 1230.0, -6.0582120000, 1.4483315000e-3, -2.1087787000e-3 /) + LoveDat(1:4,1232) = (/ 1231.0, -6.0587280000, 1.4474358000e-3, -2.1073022000e-3 /) + LoveDat(1:4,1233) = (/ 1232.0, -6.0592424000, 1.4465407000e-3, -2.1058273000e-3 /) + LoveDat(1:4,1234) = (/ 1233.0, -6.0597550000, 1.4456463000e-3, -2.1043541000e-3 /) + LoveDat(1:4,1235) = (/ 1234.0, -6.0602660000, 1.4447525000e-3, -2.1028826000e-3 /) + LoveDat(1:4,1236) = (/ 1235.0, -6.0607754000, 1.4438593000e-3, -2.1014127000e-3 /) + LoveDat(1:4,1237) = (/ 1236.0, -6.0612831000, 1.4429668000e-3, -2.0999445000e-3 /) + LoveDat(1:4,1238) = (/ 1237.0, -6.0617892000, 1.4420750000e-3, -2.0984779000e-3 /) + LoveDat(1:4,1239) = (/ 1238.0, -6.0622936000, 1.4411837000e-3, -2.0970130000e-3 /) + LoveDat(1:4,1240) = (/ 1239.0, -6.0627964000, 1.4402931000e-3, -2.0955497000e-3 /) + LoveDat(1:4,1241) = (/ 1240.0, -6.0632976000, 1.4394032000e-3, -2.0940880000e-3 /) + LoveDat(1:4,1242) = (/ 1241.0, -6.0637972000, 1.4385139000e-3, -2.0926281000e-3 /) + LoveDat(1:4,1243) = (/ 1242.0, -6.0642951000, 1.4376252000e-3, -2.0911697000e-3 /) + LoveDat(1:4,1244) = (/ 1243.0, -6.0647914000, 1.4367372000e-3, -2.0897130000e-3 /) + LoveDat(1:4,1245) = (/ 1244.0, -6.0652862000, 1.4358498000e-3, -2.0882579000e-3 /) + LoveDat(1:4,1246) = (/ 1245.0, -6.0657793000, 1.4349631000e-3, -2.0868045000e-3 /) + LoveDat(1:4,1247) = (/ 1246.0, -6.0662708000, 1.4340770000e-3, -2.0853527000e-3 /) + LoveDat(1:4,1248) = (/ 1247.0, -6.0667608000, 1.4331916000e-3, -2.0839025000e-3 /) + LoveDat(1:4,1249) = (/ 1248.0, -6.0672491000, 1.4323068000e-3, -2.0824540000e-3 /) + LoveDat(1:4,1250) = (/ 1249.0, -6.0677359000, 1.4314226000e-3, -2.0810070000e-3 /) + LoveDat(1:4,1251) = (/ 1250.0, -6.0682211000, 1.4305391000e-3, -2.0795618000e-3 /) + LoveDat(1:4,1252) = (/ 1251.0, -6.0687047000, 1.4296562000e-3, -2.0781181000e-3 /) + LoveDat(1:4,1253) = (/ 1252.0, -6.0691867000, 1.4287740000e-3, -2.0766760000e-3 /) + LoveDat(1:4,1254) = (/ 1253.0, -6.0696672000, 1.4278925000e-3, -2.0752356000e-3 /) + LoveDat(1:4,1255) = (/ 1254.0, -6.0701462000, 1.4270115000e-3, -2.0737968000e-3 /) + LoveDat(1:4,1256) = (/ 1255.0, -6.0706235000, 1.4261312000e-3, -2.0723596000e-3 /) + LoveDat(1:4,1257) = (/ 1256.0, -6.0710993000, 1.4252516000e-3, -2.0709241000e-3 /) + LoveDat(1:4,1258) = (/ 1257.0, -6.0715736000, 1.4243726000e-3, -2.0694901000e-3 /) + LoveDat(1:4,1259) = (/ 1258.0, -6.0720464000, 1.4234943000e-3, -2.0680577000e-3 /) + LoveDat(1:4,1260) = (/ 1259.0, -6.0725175000, 1.4226166000e-3, -2.0666270000e-3 /) + LoveDat(1:4,1261) = (/ 1260.0, -6.0729872000, 1.4217396000e-3, -2.0651979000e-3 /) + LoveDat(1:4,1262) = (/ 1261.0, -6.0734554000, 1.4208632000e-3, -2.0637703000e-3 /) + LoveDat(1:4,1263) = (/ 1262.0, -6.0739220000, 1.4199874000e-3, -2.0623444000e-3 /) + LoveDat(1:4,1264) = (/ 1263.0, -6.0743871000, 1.4191123000e-3, -2.0609201000e-3 /) + LoveDat(1:4,1265) = (/ 1264.0, -6.0748507000, 1.4182379000e-3, -2.0594973000e-3 /) + LoveDat(1:4,1266) = (/ 1265.0, -6.0753127000, 1.4173641000e-3, -2.0580762000e-3 /) + LoveDat(1:4,1267) = (/ 1266.0, -6.0757733000, 1.4164910000e-3, -2.0566566000e-3 /) + LoveDat(1:4,1268) = (/ 1267.0, -6.0762324000, 1.4156185000e-3, -2.0552387000e-3 /) + LoveDat(1:4,1269) = (/ 1268.0, -6.0766899000, 1.4147466000e-3, -2.0538223000e-3 /) + LoveDat(1:4,1270) = (/ 1269.0, -6.0771460000, 1.4138754000e-3, -2.0524076000e-3 /) + LoveDat(1:4,1271) = (/ 1270.0, -6.0776006000, 1.4130049000e-3, -2.0509944000e-3 /) + LoveDat(1:4,1272) = (/ 1271.0, -6.0780537000, 1.4121350000e-3, -2.0495828000e-3 /) + LoveDat(1:4,1273) = (/ 1272.0, -6.0785054000, 1.4112658000e-3, -2.0481728000e-3 /) + LoveDat(1:4,1274) = (/ 1273.0, -6.0789555000, 1.4103972000e-3, -2.0467643000e-3 /) + LoveDat(1:4,1275) = (/ 1274.0, -6.0794042000, 1.4095293000e-3, -2.0453575000e-3 /) + LoveDat(1:4,1276) = (/ 1275.0, -6.0798515000, 1.4086620000e-3, -2.0439522000e-3 /) + LoveDat(1:4,1277) = (/ 1276.0, -6.0802972000, 1.4077954000e-3, -2.0425485000e-3 /) + LoveDat(1:4,1278) = (/ 1277.0, -6.0807415000, 1.4069294000e-3, -2.0411464000e-3 /) + LoveDat(1:4,1279) = (/ 1278.0, -6.0811844000, 1.4060641000e-3, -2.0397458000e-3 /) + LoveDat(1:4,1280) = (/ 1279.0, -6.0816258000, 1.4051994000e-3, -2.0383468000e-3 /) + LoveDat(1:4,1281) = (/ 1280.0, -6.0820658000, 1.4043354000e-3, -2.0369494000e-3 /) + LoveDat(1:4,1282) = (/ 1281.0, -6.0825043000, 1.4034720000e-3, -2.0355536000e-3 /) + LoveDat(1:4,1283) = (/ 1282.0, -6.0829414000, 1.4026093000e-3, -2.0341593000e-3 /) + LoveDat(1:4,1284) = (/ 1283.0, -6.0833771000, 1.4017473000e-3, -2.0327665000e-3 /) + LoveDat(1:4,1285) = (/ 1284.0, -6.0838114000, 1.4008859000e-3, -2.0313754000e-3 /) + LoveDat(1:4,1286) = (/ 1285.0, -6.0842442000, 1.4000251000e-3, -2.0299858000e-3 /) + LoveDat(1:4,1287) = (/ 1286.0, -6.0846756000, 1.3991650000e-3, -2.0285977000e-3 /) + LoveDat(1:4,1288) = (/ 1287.0, -6.0851056000, 1.3983056000e-3, -2.0272112000e-3 /) + LoveDat(1:4,1289) = (/ 1288.0, -6.0855342000, 1.3974468000e-3, -2.0258263000e-3 /) + LoveDat(1:4,1290) = (/ 1289.0, -6.0859614000, 1.3965887000e-3, -2.0244429000e-3 /) + LoveDat(1:4,1291) = (/ 1290.0, -6.0863871000, 1.3957312000e-3, -2.0230610000e-3 /) + LoveDat(1:4,1292) = (/ 1291.0, -6.0868115000, 1.3948744000e-3, -2.0216807000e-3 /) + LoveDat(1:4,1293) = (/ 1292.0, -6.0872345000, 1.3940183000e-3, -2.0203020000e-3 /) + LoveDat(1:4,1294) = (/ 1293.0, -6.0876561000, 1.3931628000e-3, -2.0189247000e-3 /) + LoveDat(1:4,1295) = (/ 1294.0, -6.0880764000, 1.3923079000e-3, -2.0175491000e-3 /) + LoveDat(1:4,1296) = (/ 1295.0, -6.0884952000, 1.3914537000e-3, -2.0161749000e-3 /) + LoveDat(1:4,1297) = (/ 1296.0, -6.0889127000, 1.3906002000e-3, -2.0148024000e-3 /) + LoveDat(1:4,1298) = (/ 1297.0, -6.0893288000, 1.3897473000e-3, -2.0134313000e-3 /) + LoveDat(1:4,1299) = (/ 1298.0, -6.0897435000, 1.3888951000e-3, -2.0120618000e-3 /) + LoveDat(1:4,1300) = (/ 1299.0, -6.0901569000, 1.3880436000e-3, -2.0106938000e-3 /) + LoveDat(1:4,1301) = (/ 1300.0, -6.0905689000, 1.3871927000e-3, -2.0093273000e-3 /) + LoveDat(1:4,1302) = (/ 1301.0, -6.0909796000, 1.3863424000e-3, -2.0079624000e-3 /) + LoveDat(1:4,1303) = (/ 1302.0, -6.0913889000, 1.3854928000e-3, -2.0065990000e-3 /) + LoveDat(1:4,1304) = (/ 1303.0, -6.0917969000, 1.3846439000e-3, -2.0052371000e-3 /) + LoveDat(1:4,1305) = (/ 1304.0, -6.0922035000, 1.3837956000e-3, -2.0038767000e-3 /) + LoveDat(1:4,1306) = (/ 1305.0, -6.0926088000, 1.3829480000e-3, -2.0025179000e-3 /) + LoveDat(1:4,1307) = (/ 1306.0, -6.0930127000, 1.3821011000e-3, -2.0011606000e-3 /) + LoveDat(1:4,1308) = (/ 1307.0, -6.0934154000, 1.3812548000e-3, -1.9998048000e-3 /) + LoveDat(1:4,1309) = (/ 1308.0, -6.0938167000, 1.3804091000e-3, -1.9984505000e-3 /) + LoveDat(1:4,1310) = (/ 1309.0, -6.0942166000, 1.3795642000e-3, -1.9970977000e-3 /) + LoveDat(1:4,1311) = (/ 1310.0, -6.0946153000, 1.3787199000e-3, -1.9957464000e-3 /) + LoveDat(1:4,1312) = (/ 1311.0, -6.0950127000, 1.3778762000e-3, -1.9943967000e-3 /) + LoveDat(1:4,1313) = (/ 1312.0, -6.0954087000, 1.3770332000e-3, -1.9930484000e-3 /) + LoveDat(1:4,1314) = (/ 1313.0, -6.0958034000, 1.3761909000e-3, -1.9917017000e-3 /) + LoveDat(1:4,1315) = (/ 1314.0, -6.0961969000, 1.3753492000e-3, -1.9903564000e-3 /) + LoveDat(1:4,1316) = (/ 1315.0, -6.0965890000, 1.3745082000e-3, -1.9890127000e-3 /) + LoveDat(1:4,1317) = (/ 1316.0, -6.0969798000, 1.3736678000e-3, -1.9876705000e-3 /) + LoveDat(1:4,1318) = (/ 1317.0, -6.0973694000, 1.3728281000e-3, -1.9863297000e-3 /) + LoveDat(1:4,1319) = (/ 1318.0, -6.0977577000, 1.3719890000e-3, -1.9849905000e-3 /) + LoveDat(1:4,1320) = (/ 1319.0, -6.0981446000, 1.3711507000e-3, -1.9836527000e-3 /) + LoveDat(1:4,1321) = (/ 1320.0, -6.0985303000, 1.3703129000e-3, -1.9823165000e-3 /) + LoveDat(1:4,1322) = (/ 1321.0, -6.0989148000, 1.3694759000e-3, -1.9809817000e-3 /) + LoveDat(1:4,1323) = (/ 1322.0, -6.0992979000, 1.3686395000e-3, -1.9796485000e-3 /) + LoveDat(1:4,1324) = (/ 1323.0, -6.0996798000, 1.3678037000e-3, -1.9783167000e-3 /) + LoveDat(1:4,1325) = (/ 1324.0, -6.1000605000, 1.3669686000e-3, -1.9769864000e-3 /) + LoveDat(1:4,1326) = (/ 1325.0, -6.1004399000, 1.3661342000e-3, -1.9756576000e-3 /) + LoveDat(1:4,1327) = (/ 1326.0, -6.1008180000, 1.3653005000e-3, -1.9743302000e-3 /) + LoveDat(1:4,1328) = (/ 1327.0, -6.1011948000, 1.3644674000e-3, -1.9730044000e-3 /) + LoveDat(1:4,1329) = (/ 1328.0, -6.1015705000, 1.3636349000e-3, -1.9716800000e-3 /) + LoveDat(1:4,1330) = (/ 1329.0, -6.1019449000, 1.3628031000e-3, -1.9703571000e-3 /) + LoveDat(1:4,1331) = (/ 1330.0, -6.1023180000, 1.3619720000e-3, -1.9690357000e-3 /) + LoveDat(1:4,1332) = (/ 1331.0, -6.1026899000, 1.3611416000e-3, -1.9677157000e-3 /) + LoveDat(1:4,1333) = (/ 1332.0, -6.1030606000, 1.3603118000e-3, -1.9663972000e-3 /) + LoveDat(1:4,1334) = (/ 1333.0, -6.1034300000, 1.3594826000e-3, -1.9650802000e-3 /) + LoveDat(1:4,1335) = (/ 1334.0, -6.1037983000, 1.3586541000e-3, -1.9637647000e-3 /) + LoveDat(1:4,1336) = (/ 1335.0, -6.1041653000, 1.3578263000e-3, -1.9624506000e-3 /) + LoveDat(1:4,1337) = (/ 1336.0, -6.1045311000, 1.3569992000e-3, -1.9611380000e-3 /) + LoveDat(1:4,1338) = (/ 1337.0, -6.1048956000, 1.3561727000e-3, -1.9598268000e-3 /) + LoveDat(1:4,1339) = (/ 1338.0, -6.1052590000, 1.3553468000e-3, -1.9585171000e-3 /) + LoveDat(1:4,1340) = (/ 1339.0, -6.1056212000, 1.3545217000e-3, -1.9572089000e-3 /) + LoveDat(1:4,1341) = (/ 1340.0, -6.1059821000, 1.3536972000e-3, -1.9559021000e-3 /) + LoveDat(1:4,1342) = (/ 1341.0, -6.1063419000, 1.3528733000e-3, -1.9545968000e-3 /) + LoveDat(1:4,1343) = (/ 1342.0, -6.1067005000, 1.3520501000e-3, -1.9532929000e-3 /) + LoveDat(1:4,1344) = (/ 1343.0, -6.1070578000, 1.3512276000e-3, -1.9519905000e-3 /) + LoveDat(1:4,1345) = (/ 1344.0, -6.1074140000, 1.3504057000e-3, -1.9506896000e-3 /) + LoveDat(1:4,1346) = (/ 1345.0, -6.1077690000, 1.3495845000e-3, -1.9493900000e-3 /) + LoveDat(1:4,1347) = (/ 1346.0, -6.1081229000, 1.3487640000e-3, -1.9480920000e-3 /) + LoveDat(1:4,1348) = (/ 1347.0, -6.1084755000, 1.3479441000e-3, -1.9467953000e-3 /) + LoveDat(1:4,1349) = (/ 1348.0, -6.1088270000, 1.3471249000e-3, -1.9455001000e-3 /) + LoveDat(1:4,1350) = (/ 1349.0, -6.1091773000, 1.3463063000e-3, -1.9442064000e-3 /) + LoveDat(1:4,1351) = (/ 1350.0, -6.1095265000, 1.3454884000e-3, -1.9429141000e-3 /) + LoveDat(1:4,1352) = (/ 1351.0, -6.1098744000, 1.3446712000e-3, -1.9416232000e-3 /) + LoveDat(1:4,1353) = (/ 1352.0, -6.1102213000, 1.3438546000e-3, -1.9403338000e-3 /) + LoveDat(1:4,1354) = (/ 1353.0, -6.1105669000, 1.3430387000e-3, -1.9390458000e-3 /) + LoveDat(1:4,1355) = (/ 1354.0, -6.1109115000, 1.3422234000e-3, -1.9377592000e-3 /) + LoveDat(1:4,1356) = (/ 1355.0, -6.1112548000, 1.3414088000e-3, -1.9364741000e-3 /) + LoveDat(1:4,1357) = (/ 1356.0, -6.1115971000, 1.3405949000e-3, -1.9351903000e-3 /) + LoveDat(1:4,1358) = (/ 1357.0, -6.1119382000, 1.3397816000e-3, -1.9339081000e-3 /) + LoveDat(1:4,1359) = (/ 1358.0, -6.1122781000, 1.3389690000e-3, -1.9326272000e-3 /) + LoveDat(1:4,1360) = (/ 1359.0, -6.1126170000, 1.3381571000e-3, -1.9313478000e-3 /) + LoveDat(1:4,1361) = (/ 1360.0, -6.1129546000, 1.3373458000e-3, -1.9300697000e-3 /) + LoveDat(1:4,1362) = (/ 1361.0, -6.1132912000, 1.3365352000e-3, -1.9287931000e-3 /) + LoveDat(1:4,1363) = (/ 1362.0, -6.1136267000, 1.3357252000e-3, -1.9275180000e-3 /) + LoveDat(1:4,1364) = (/ 1363.0, -6.1139610000, 1.3349159000e-3, -1.9262442000e-3 /) + LoveDat(1:4,1365) = (/ 1364.0, -6.1142942000, 1.3341073000e-3, -1.9249718000e-3 /) + LoveDat(1:4,1366) = (/ 1365.0, -6.1146263000, 1.3332993000e-3, -1.9237009000e-3 /) + LoveDat(1:4,1367) = (/ 1366.0, -6.1149573000, 1.3324920000e-3, -1.9224314000e-3 /) + LoveDat(1:4,1368) = (/ 1367.0, -6.1152872000, 1.3316853000e-3, -1.9211632000e-3 /) + LoveDat(1:4,1369) = (/ 1368.0, -6.1156160000, 1.3308793000e-3, -1.9198965000e-3 /) + LoveDat(1:4,1370) = (/ 1369.0, -6.1159437000, 1.3300740000e-3, -1.9186312000e-3 /) + LoveDat(1:4,1371) = (/ 1370.0, -6.1162702000, 1.3292693000e-3, -1.9173673000e-3 /) + LoveDat(1:4,1372) = (/ 1371.0, -6.1165957000, 1.3284653000e-3, -1.9161048000e-3 /) + LoveDat(1:4,1373) = (/ 1372.0, -6.1169202000, 1.3276619000e-3, -1.9148437000e-3 /) + LoveDat(1:4,1374) = (/ 1373.0, -6.1172435000, 1.3268592000e-3, -1.9135840000e-3 /) + LoveDat(1:4,1375) = (/ 1374.0, -6.1175657000, 1.3260572000e-3, -1.9123257000e-3 /) + LoveDat(1:4,1376) = (/ 1375.0, -6.1178869000, 1.3252558000e-3, -1.9110688000e-3 /) + LoveDat(1:4,1377) = (/ 1376.0, -6.1182070000, 1.3244551000e-3, -1.9098133000e-3 /) + LoveDat(1:4,1378) = (/ 1377.0, -6.1185260000, 1.3236551000e-3, -1.9085591000e-3 /) + LoveDat(1:4,1379) = (/ 1378.0, -6.1188440000, 1.3228557000e-3, -1.9073064000e-3 /) + LoveDat(1:4,1380) = (/ 1379.0, -6.1191609000, 1.3220569000e-3, -1.9060550000e-3 /) + LoveDat(1:4,1381) = (/ 1380.0, -6.1194767000, 1.3212589000e-3, -1.9048051000e-3 /) + LoveDat(1:4,1382) = (/ 1381.0, -6.1197915000, 1.3204614000e-3, -1.9035565000e-3 /) + LoveDat(1:4,1383) = (/ 1382.0, -6.1201052000, 1.3196647000e-3, -1.9023093000e-3 /) + LoveDat(1:4,1384) = (/ 1383.0, -6.1204179000, 1.3188686000e-3, -1.9010635000e-3 /) + LoveDat(1:4,1385) = (/ 1384.0, -6.1207295000, 1.3180732000e-3, -1.8998190000e-3 /) + LoveDat(1:4,1386) = (/ 1385.0, -6.1210401000, 1.3172784000e-3, -1.8985760000e-3 /) + LoveDat(1:4,1387) = (/ 1386.0, -6.1213496000, 1.3164843000e-3, -1.8973343000e-3 /) + LoveDat(1:4,1388) = (/ 1387.0, -6.1216581000, 1.3156908000e-3, -1.8960940000e-3 /) + LoveDat(1:4,1389) = (/ 1388.0, -6.1219656000, 1.3148980000e-3, -1.8948550000e-3 /) + LoveDat(1:4,1390) = (/ 1389.0, -6.1222720000, 1.3141059000e-3, -1.8936175000e-3 /) + LoveDat(1:4,1391) = (/ 1390.0, -6.1225774000, 1.3133144000e-3, -1.8923813000e-3 /) + LoveDat(1:4,1392) = (/ 1391.0, -6.1228818000, 1.3125236000e-3, -1.8911464000e-3 /) + LoveDat(1:4,1393) = (/ 1392.0, -6.1231851000, 1.3117334000e-3, -1.8899130000e-3 /) + LoveDat(1:4,1394) = (/ 1393.0, -6.1234875000, 1.3109439000e-3, -1.8886809000e-3 /) + LoveDat(1:4,1395) = (/ 1394.0, -6.1237888000, 1.3101551000e-3, -1.8874501000e-3 /) + LoveDat(1:4,1396) = (/ 1395.0, -6.1240891000, 1.3093669000e-3, -1.8862207000e-3 /) + LoveDat(1:4,1397) = (/ 1396.0, -6.1243884000, 1.3085794000e-3, -1.8849927000e-3 /) + LoveDat(1:4,1398) = (/ 1397.0, -6.1246867000, 1.3077925000e-3, -1.8837660000e-3 /) + LoveDat(1:4,1399) = (/ 1398.0, -6.1249840000, 1.3070063000e-3, -1.8825407000e-3 /) + LoveDat(1:4,1400) = (/ 1399.0, -6.1252803000, 1.3062208000e-3, -1.8813168000e-3 /) + LoveDat(1:4,1401) = (/ 1400.0, -6.1255756000, 1.3054359000e-3, -1.8800942000e-3 /) + LoveDat(1:4,1402) = (/ 1401.0, -6.1258699000, 1.3046517000e-3, -1.8788729000e-3 /) + LoveDat(1:4,1403) = (/ 1402.0, -6.1261632000, 1.3038681000e-3, -1.8776530000e-3 /) + LoveDat(1:4,1404) = (/ 1403.0, -6.1264555000, 1.3030852000e-3, -1.8764345000e-3 /) + LoveDat(1:4,1405) = (/ 1404.0, -6.1267469000, 1.3023029000e-3, -1.8752172000e-3 /) + LoveDat(1:4,1406) = (/ 1405.0, -6.1270372000, 1.3015213000e-3, -1.8740014000e-3 /) + LoveDat(1:4,1407) = (/ 1406.0, -6.1273266000, 1.3007404000e-3, -1.8727868000e-3 /) + LoveDat(1:4,1408) = (/ 1407.0, -6.1276150000, 1.2999601000e-3, -1.8715736000e-3 /) + LoveDat(1:4,1409) = (/ 1408.0, -6.1279024000, 1.2991804000e-3, -1.8703618000e-3 /) + LoveDat(1:4,1410) = (/ 1409.0, -6.1281889000, 1.2984015000e-3, -1.8691513000e-3 /) + LoveDat(1:4,1411) = (/ 1410.0, -6.1284744000, 1.2976231000e-3, -1.8679421000e-3 /) + LoveDat(1:4,1412) = (/ 1411.0, -6.1287590000, 1.2968455000e-3, -1.8667343000e-3 /) + LoveDat(1:4,1413) = (/ 1412.0, -6.1290425000, 1.2960685000e-3, -1.8655277000e-3 /) + LoveDat(1:4,1414) = (/ 1413.0, -6.1293252000, 1.2952921000e-3, -1.8643226000e-3 /) + LoveDat(1:4,1415) = (/ 1414.0, -6.1296068000, 1.2945164000e-3, -1.8631187000e-3 /) + LoveDat(1:4,1416) = (/ 1415.0, -6.1298876000, 1.2937414000e-3, -1.8619162000e-3 /) + LoveDat(1:4,1417) = (/ 1416.0, -6.1301673000, 1.2929670000e-3, -1.8607150000e-3 /) + LoveDat(1:4,1418) = (/ 1417.0, -6.1304462000, 1.2921933000e-3, -1.8595151000e-3 /) + LoveDat(1:4,1419) = (/ 1418.0, -6.1307241000, 1.2914202000e-3, -1.8583165000e-3 /) + LoveDat(1:4,1420) = (/ 1419.0, -6.1310010000, 1.2906478000e-3, -1.8571193000e-3 /) + LoveDat(1:4,1421) = (/ 1420.0, -6.1312770000, 1.2898761000e-3, -1.8559234000e-3 /) + LoveDat(1:4,1422) = (/ 1421.0, -6.1315521000, 1.2891050000e-3, -1.8547288000e-3 /) + LoveDat(1:4,1423) = (/ 1422.0, -6.1318263000, 1.2883345000e-3, -1.8535355000e-3 /) + LoveDat(1:4,1424) = (/ 1423.0, -6.1320995000, 1.2875647000e-3, -1.8523435000e-3 /) + LoveDat(1:4,1425) = (/ 1424.0, -6.1323718000, 1.2867956000e-3, -1.8511528000e-3 /) + LoveDat(1:4,1426) = (/ 1425.0, -6.1326432000, 1.2860271000e-3, -1.8499635000e-3 /) + LoveDat(1:4,1427) = (/ 1426.0, -6.1329136000, 1.2852592000e-3, -1.8487754000e-3 /) + LoveDat(1:4,1428) = (/ 1427.0, -6.1331832000, 1.2844921000e-3, -1.8475887000e-3 /) + LoveDat(1:4,1429) = (/ 1428.0, -6.1334518000, 1.2837255000e-3, -1.8464033000e-3 /) + LoveDat(1:4,1430) = (/ 1429.0, -6.1337196000, 1.2829597000e-3, -1.8452191000e-3 /) + LoveDat(1:4,1431) = (/ 1430.0, -6.1339864000, 1.2821945000e-3, -1.8440363000e-3 /) + LoveDat(1:4,1432) = (/ 1431.0, -6.1342523000, 1.2814299000e-3, -1.8428548000e-3 /) + LoveDat(1:4,1433) = (/ 1432.0, -6.1345173000, 1.2806660000e-3, -1.8416745000e-3 /) + LoveDat(1:4,1434) = (/ 1433.0, -6.1347815000, 1.2799027000e-3, -1.8404956000e-3 /) + LoveDat(1:4,1435) = (/ 1434.0, -6.1350447000, 1.2791401000e-3, -1.8393180000e-3 /) + LoveDat(1:4,1436) = (/ 1435.0, -6.1353070000, 1.2783782000e-3, -1.8381416000e-3 /) + LoveDat(1:4,1437) = (/ 1436.0, -6.1355685000, 1.2776168000e-3, -1.8369666000e-3 /) + LoveDat(1:4,1438) = (/ 1437.0, -6.1358290000, 1.2768562000e-3, -1.8357928000e-3 /) + LoveDat(1:4,1439) = (/ 1438.0, -6.1360887000, 1.2760962000e-3, -1.8346203000e-3 /) + LoveDat(1:4,1440) = (/ 1439.0, -6.1363475000, 1.2753368000e-3, -1.8334492000e-3 /) !}}} + LoveDat(1:4,1441) = (/ 1440.0, -6.1366054000, 1.2745781000e-3, -1.8322792000e-3 /) + + + ! if (trim(config_ocean_run_mode) .eq. 'init') then + if (.False.) then + + LoveScaling(:) = 1.0 + + else + + allocate(H(lmax+1),L(lmax+1),K(lmax+1)) + + H(1:lmax+1) = LoveDat(2,1:lmax+1) + L(1:lmax+1) = LoveDat(3,1:lmax+1) + K(1:lmax+1) = LoveDat(4,1:lmax+1) + + ! Convert from CM to CF + H1 = H(2) + L1 = L(2) + K1 = K(2) + H(2) = 2.0/3.0*(H1 - L1) + L(2) = -1.0/3.0*(H1 - L1) + K(2) = -1.0/3.0*H1 - 2.0/3.0*L1 - 1.0 + + do m = 0,nlm + do n = m,nlm + i = SHOrderDegreeToIndex(n,m,nlm) + LoveScaling(i) = (1.0 + K(n+1) - H(n+1))/real(2*n+1) + enddo + enddo + LoveScaling = LoveScaling * 3.0 * rhoW / rhoE + + endif + +end subroutine!}}} + !> This subroutine finds a named variable in a list of files and reads its !! values into a domain-decomposed 2-d array subroutine find_in_files(filenames, varname, array, G, scale) @@ -587,10 +2093,11 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_tidal !< The tidal forcing geopotential height !! anomalies [Z ~> m]. type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - type(tidal_forcing_CS), intent(in) :: CS !< The control structure returned by a + type(tidal_forcing_CS), intent(inout) :: CS !< The control structure returned by a !! previous call to tidal_forcing_init. ! Local variables + real, dimension(SZI_(G),SZJ_(G)) :: eta_sal !< SAL real :: now ! The relative time compared with the tidal reference [T ~> s] real :: amp_cosomegat, amp_sinomegat ! The tidal amplitudes times the components of phase [Z ~> m] real :: cosomegat, sinomegat ! The components of the phase [nondim] @@ -648,10 +2155,250 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) enddo ; enddo enddo ; endif + if (CS%TIDAL_SAL_SHT) then + eta_sal = 0.0 + call calc_tidal_SAL(eta, eta_sal, G, CS%sht) + call pass_var(eta_sal, G%domain) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 + eta_tidal(i,j) = eta_tidal(i,j) + eta_sal(i,j) + enddo ; enddo + endif call cpu_clock_end(id_clock_tides) end subroutine calc_tidal_forcing +subroutine calc_tidal_SAL(eta, eta_sal, G, sht) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + real, dimension(SZI_(G),SZJ_(G)), intent(in) :: eta !< The sea surface height anomaly from + !! a time-mean geoid [Z ~> m]. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_sal !< The sea surface height anomaly from + !! a time-mean geoid [Z ~> m]. + type(sht_CS), intent(inout) :: sht + real, allocatable :: SnmRe_local(:), SnmIm_local(:) + real, allocatable :: SnmRe_local_reproSum(:), SnmIm_local_reproSum(:) + real, allocatable :: SnmRe(:), SnmIm(:) + real, allocatable :: Snm_local(:), Snm_local_reproSum(:), Snm(:) + real, allocatable :: LoveScaling(:) + + integer :: i, j + integer :: is, ie, js, je + integer :: n, m, l + real :: mFac + + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + call cpu_clock_begin(id_clock_SAL) + + allocate(Snm(2*sht%lmax)); Snm = 0.0 + allocate(SnmRe(sht%lmax)); SnmRe = 0.0 + allocate(SnmIm(sht%lmax)); SnmIm = 0.0 + + if (sht%bfb) then + allocate(Snm_local_reproSum(2*sht%lmax)); Snm_local_reproSum = 0.0 + allocate(SnmRe_local_reproSum(sht%lmax)); SnmRe_local_reproSum = 0.0 + allocate(SnmIm_local_reproSum(sht%lmax)); SnmIm_local_reproSum = 0.0 + else + allocate(Snm_local(2*sht%lmax)); Snm_local = 0.0 + allocate(SnmRe_local(sht%lmax)); SnmRe_local = 0.0 + allocate(SnmIm_local(sht%lmax)); SnmIm_local = 0.0 + endif + + ! Get SAL scaling factors + ALLOC_(LoveScaling(sht%lmax)) + call getloadLoveNums(sht%nOrder, LoveScaling) + + !!!!!!!!!!!!!!!!!!!!! + ! Forward Transform + !!!!!!!!!!!!!!!!!!!!! + + do m = 0, sht%nOrder + !------------ + ! n = m + !------------ + n = m + ! Calculate associated Legendre polynomial for n=m (output pmnm2) + ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) + call associatedLegendrePolynomials(n, m, l, sht, G) + + ! Compute local integral contribution + if (sht%bfb) then + ! do iCell = endIdx, startIdx, -1 + ! SnmRe_local_reproSum(iCell,l) = SnmRe_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm2(iCell)*complexFactorRe(iCell,m+1) + ! SnmIm_local_reproSum(iCell,l) = SnmIm_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm2(iCell)*complexFactorIm(iCell,m+1) + ! enddo + else + do j = js,je ; do i = is,ie + SnmRe_local(l) = SnmRe_local(l) + eta(i,j) * sht%pmnm2(i,j) * sht%complexFactorRe(i,j,m+1) + SnmIm_local(l) = SnmIm_local(l) + eta(i,j) * sht%pmnm2(i,j) * sht%complexFactorIm(i,j,m+1) + enddo ; enddo + endif + + !------------ + ! n = m+1 + !------------ + n = m+1 + if (n <= sht%nOrder) then + ! Calculate associated Legendre polynomial for n = m+1 using recurrence relationship + ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) + call associatedLegendrePolynomials(n, m, l, sht, G) + + ! Compute local integral contribution + if (sht%bfb) then + ! do iCell = endIdx, startIdx, -1 + ! SnmRe_local_reproSum(iCell,l) = SnmRe_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm1(iCell)*complexFactorRe(iCell,m+1) + ! SnmIm_local_reproSum(iCell,l) = SnmIm_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm1(iCell)*complexFactorIm(iCell,m+1) + ! enddo + else + do j = js,je ; do i = is,ie + SnmRe_local(l) = SnmRe_local(l) + eta(i,j) * sht%pmnm1(i,j) * sht%complexFactorRe(i,j,m+1) + SnmIm_local(l) = SnmIm_local(l) + eta(i,j) * sht%pmnm1(i,j) * sht%complexFactorIm(i,j,m+1) + enddo ; enddo + endif + endif + + !------------ + ! n > m+1 + !------------ + do n = m+2,sht%nOrder + ! Calculate associated Legendre polynomial using recurrence relationship + ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) + call associatedLegendrePolynomials(n, m, l, sht, G) + + ! Update associated Ledgendre polynomial values for next recurrence + do j = js,je ; do i = is,ie + sht%pmnm2(i,j) = sht%pmnm1(i,j) + sht%pmnm1(i,j) = sht%pmn(i,j) + enddo ; enddo + + ! Compute local integral contribution + if (sht%bfb) then + ! do iCell = endIdx, startIdx, -1 + ! SnmRe_local_reproSum(iCell,l) = SnmRe_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmn(iCell)*complexFactorRe(iCell,m+1) + ! SnmIm_local_reproSum(iCell,l) = SnmIm_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmn(iCell)*complexFactorIm(iCell,m+1) + ! enddo + else + do j = js,je ; do i = is,ie + SnmRe_local(l) = SnmRe_local(l) + eta(i,j) * sht%pmn(i,j) * sht%complexFactorRe(i,j,m+1) + SnmIm_local(l) = SnmIm_local(l) + eta(i,j) * sht%pmn(i,j) * sht%complexFactorIm(i,j,m+1) + enddo ; enddo + endif + enddo ! n loop + enddo ! m loop + + ! call mpas_timer_stop('Parallel SAL: Forward Transform') + + ! call mpas_timer_start('Parallel SAL: Communication') + if (sht%bfb) then + ! do m = 1,lmax + ! do iCell = 1,nCellsOwned + ! Snm_local_reproSum(iCell,m) = SnmRe_local_reproSum(iCell,m) + ! Snm_local_reproSum(iCell,lmax+m) = SnmIm_local_reproSum(iCell,m) + ! enddo + ! enddo + else + do m = 1,sht%lmax + Snm_local(m) = SnmRe_local(m) + Snm_local(sht%lmax+m) = SnmIm_local(m) + enddo + endif + + ! Compute global integral by summing local contributions + if (sht%bfb) then + !threadNum = mpas_threading_get_thread_num() + !if ( threadNum == 0 ) then + ! Snm = mpas_global_sum_nfld(Snm_local_reproSum,dminfo%comm) + !endif + else + call sum_across_PEs(Snm_local, 2*sht%lmax) + endif + + do m = 1,sht%lmax + SnmRe(m) = Snm_local(m) + SnmIm(m) = Snm_local(sht%lmax+m) + enddo + + ! call mpas_timer_stop('Parallel SAL: Communication') + + ! call mpas_timer_start('Parallel SAL: Inverse Transform') + + !!!!!!!!!!!!!!!!!!!!! + ! Apply SAL scaling + !!!!!!!!!!!!!!!!!!!!! + + do m = 0,sht%nOrder + do n = m,sht%nOrder + l = SHOrderDegreeToIndex(n,m,sht%norder) + SnmRe(l) = SnmRe(l)*LoveScaling(l) + SnmIm(l) = SnmIm(l)*LoveScaling(l) + enddo + enddo + + !!!!!!!!!!!!!!!!!!!! + ! Inverse transform + !!!!!!!!!!!!!!!!!!!! + + do m = 0,sht%nOrder + if (m>0) then + mFac = 2.0 + else + mFac = 1.0 + endif + + !------------ + ! n = m + !------------ + n = m + ! Calculate associated Legendre polynomial using recurrence relationship + ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) + call associatedLegendrePolynomials(n, m, l, sht, G) + + ! Sum together product of spherical harmonic functions and coefficients + do j = js,je ; do i = is,ie + eta_sal(i,j) = eta_sal(i,j) & + + mFac * sht%pmnm2(i,j) * (SnmRe(l) * sht%complexExpRe(i,j,m+1) + SnmIm(l) * sht%complexExpIm(i,j,m+1)) + enddo ; enddo + + !------------ + ! n = m+1 + !------------ + n = m+1 + if (n <= sht%nOrder) then + ! Calculate associated Legendre polynomial using recurrence relationship + ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) + call associatedLegendrePolynomials(n, m, l, sht, G) + + ! Sum together product of spherical harmonic functions and coefficients + do j = js,je ; do i = is,ie + eta_sal(i,j) = eta_sal(i,j) & + + mFac * sht%pmnm1(i,j) * (SnmRe(l) * sht%complexExpRe(i,j,m+1) + SnmIm(l) * sht%complexExpIm(i,j,m+1)) + enddo ; enddo + endif + + !------------ + ! n > m+1 + !------------ + do n = m+2,sht%nOrder + ! Calculate associated Legendre polynomial using recurrence relationship + ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) + call associatedLegendrePolynomials(n, m, l, sht, G) + + ! Update associated Ledgendre polynomial values for next recurrence + do j = js,je ; do i = is,ie + sht%pmnm2(i,j) = sht%pmnm1(i,j) + sht%pmnm1(i,j) = sht%pmn(i,j) + enddo ; enddo + + ! Sum together product of spherical harmonic functions and coefficients + do j = js,je ; do i = is,ie + eta_sal(i,j) = eta_sal(i,j) & + + mFac * sht%pmn(i,j) * (SnmRe(l) * sht%complexExpRe(i,j,m+1) + SnmIm(l) * sht%complexExpIm(i,j,m+1)) + enddo ; enddo + enddo ! n loop + enddo ! m loop + + call cpu_clock_end(id_clock_SAL) +end subroutine calc_tidal_SAL + !> This subroutine deallocates memory associated with the tidal forcing module. subroutine tidal_forcing_end(CS) type(tidal_forcing_CS), intent(inout) :: CS !< The control structure returned by a previous call @@ -667,6 +2414,9 @@ subroutine tidal_forcing_end(CS) if (allocated(CS%cosphase_prev)) deallocate(CS%cosphase_prev) if (allocated(CS%sinphase_prev)) deallocate(CS%sinphase_prev) if (allocated(CS%amp_prev)) deallocate(CS%amp_prev) + + if (CS%tidal_sal_sht) & + call spherical_harmonics_end(CS%sht) end subroutine tidal_forcing_end !> \namespace tidal_forcing From f9d5b0528aea3d044a70c5d17cc520491189f6ea Mon Sep 17 00:00:00 2001 From: He Wang Date: Thu, 28 Jul 2022 15:44:28 -0400 Subject: [PATCH 51/68] Self-contained spherical harmonics module The actual calculation of SHT is stripped out of the tidal SAL subroutine to make module MOM_spherical_harmonics self-contained. * Forward and inverse spherical harmonic transform calculations are imported from MOM_tidal_forcing module, as two new subroutines are in module MOM_spherical_harmonics. This generalization allows the spherical harmonic transforms to applications other than SSH based SAL. * The main loops in the two new subroutine are also simplified. * Child variables in the control structure for SHT are now private. * The associated Legendre polynomials in the diagonal (n=m) is now precomputed and stored, rather than recalculating at every step. This does not have the memory cost as fully precomputing all the polynomials, but still reduces some repeat calculations. The performance appears to be improved with this change. * SAL calculation is simplified as forward transform, scaling and inverse transform. * A few variables names inherited from MPAS-O are changed. --- .../lateral/MOM_spherical_harmonics.f90 | 284 +++++++++++++----- .../lateral/MOM_tidal_forcing.F90 | 235 ++------------- 2 files changed, 225 insertions(+), 294 deletions(-) diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 index c933213b55..d798098832 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 @@ -1,3 +1,4 @@ +!> Laplace's spherical harmonics transforms (SHT) module MOM_spherical_harmonics use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, & CLOCK_MODULE @@ -5,54 +6,195 @@ module MOM_spherical_harmonics use MOM_file_parser, only : get_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type use MOM_unit_scaling, only : unit_scale_type +use MOM_coms_infra, only : sum_across_PEs implicit none ; private -public spherical_harmonics_init, spherical_harmonics_end, associatedLegendrePolynomials, SHOrderDegreeToIndex +public spherical_harmonics_init, spherical_harmonics_end, SHOrderDegreeToIndex, calc_lmax +public spherical_harmonics_forward, spherical_harmonics_inverse #include -type, public :: sht_CS - integer :: nOrder, lmax - real, allocatable :: sinLatCell(:,:), cosLatCell(:,:) - real, allocatable :: complexFactorRe(:,:,:), complexFactorIm(:,:,:), complexExpRe(:,:,:), complexExpIm(:,:,:) - real, allocatable :: pmnm2(:,:), pmnm1(:,:), pmn(:,:) - real, allocatable :: aRecurrenceCoeff(:,:), bRecurrenceCoeff(:,:) - logical :: bfb +type, public :: sht_CS ; private + logical :: initialized = .False. !< True if this control structure has been initialized. + integer :: nOrder !< Maximum degree of the spherical harmonics [nodim]. + integer :: lmax !< Number of associated Legendre polynomials of nonnegative m [=(nOrder+1)*(nOrder+2)/2] [nodim]. + real, allocatable :: cosCoLatT(:,:) !< Precomputed cosine of colatitude at the t-cells [nondim]. + real, allocatable :: Pmm(:,:,:) !< Precomputed associated Legendre polynomials of which m=n at the t-cells [nondim]. + real, allocatable :: complexFactorRe(:,:,:), complexFactorIm(:,:,:), & !< Precomputed exponential factors + complexExpRe(:,:,:), complexExpIm(:,:,:) !! at the t-cells [nondim]. + real, allocatable :: aRecurrenceCoeff(:,:), bRecurrenceCoeff(:,:) !< Precomputed recurrennce coefficients [nondim]. + logical :: bfb !< True if use reproducable global sums end type sht_CS contains +subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(sht_CS), intent(in) :: CS !< Control structure for spherical harmonics trasnforms + real, intent(in) :: var(:,:) !< Input 2-D variable + real, intent(out) :: SnmRe(:), SnmIm(:) !< Real and imaginary SHT coefficients + integer, intent(in), optional :: Nd !< Maximum degree of the spherical harmonics, overriding nOrder + !! in the control structure. + ! local variables + integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics + integer :: i, j, k + integer :: is, ie, js, je + integer :: m, n, l + real, allocatable :: Snm_local(:), SnmRe_local(:), SnmIm_local(:) + real :: pmn, & ! Current associated Legendre polynomials of degree n and order m + pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m + pmnm2 ! Associated Legendre polynomials of degree n-2 and order m + + if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & + "spherical_harmonics_forward: Module must be initialized before it is used.") + + Nmax = CS%nOrder; if (present(Nd)) Nmax = Nd + + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + if (CS%bfb) then + ! allocate(Snm_local_reproSum(2*CS%lmax)); Snm_local_reproSum = 0.0 + ! allocate(SnmRe_local_reproSum(CS%lmax)); SnmRe_local_reproSum = 0.0 + ! allocate(SnmIm_local_reproSum(CS%lmax)); SnmIm_local_reproSum = 0.0 + else + allocate(Snm_local(2*CS%lmax)); Snm_local = 0.0 + allocate(SnmRe_local(CS%lmax)); SnmRe_local = 0.0 + allocate(SnmIm_local(CS%lmax)); SnmIm_local = 0.0 + endif + + do m=0,Nmax + l = SHOrderDegreeToIndex(m, m, Nmax) + + do j=js,je ; do i=is,ie + pmn = CS%Pmm(i,j,m+1) + SnmRe_local(l) = SnmRe_local(l) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) + SnmIm_local(l) = SnmIm_local(l) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) + + pmnm2 = 0.0; pmnm1 = pmn + do n = m+1, Nmax + pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 + SnmRe_local(l+n-m) = SnmRe_local(l+n-m) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) + SnmIm_local(l+n-m) = SnmIm_local(l+n-m) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) + pmnm2 = pmnm1; pmnm1 = pmn + enddo + enddo ; enddo + enddo + + ! call mpas_timer_stop('Parallel SAL: Forward Transform') + + ! call mpas_timer_start('Parallel SAL: Communication') + if (CS%bfb) then + ! do m = 1,lmax + ! do iCell = 1,nCellsOwned + ! Snm_local_reproSum(iCell,m) = SnmRe_local_reproSum(iCell,m) + ! Snm_local_reproSum(iCell,lmax+m) = SnmIm_local_reproSum(iCell,m) + ! enddo + ! enddo + else + do m = 1,CS%lmax + Snm_local(m) = SnmRe_local(m) + Snm_local(CS%lmax+m) = SnmIm_local(m) + enddo + endif + + ! Compute global integral by summing local contributions + if (CS%bfb) then + !threadNum = mpas_threading_get_thread_num() + !if ( threadNum == 0 ) then + ! Snm = mpas_global_sum_nfld(Snm_local_reproSum,dminfo%comm) + !endif + else + call sum_across_PEs(Snm_local, 2*CS%lmax) + endif + + do m=1,CS%lmax + SnmRe(m) = Snm_local(m) + SnmIm(m) = Snm_local(CS%lmax+m) + enddo +end subroutine spherical_harmonics_forward + +subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(sht_CS), intent(in) :: CS !< Control structure for spherical harmonics trasnforms + real, intent(out) :: var(:,:) !< Output 2-D variable + real, intent(in) :: SnmRe(:), SnmIm(:) !< Real and imaginary SHT coefficients including + !! any additional scaling factors such as Love numbers + integer, intent(in), optional :: Nd !< Maximum degree of the spherical harmonics, overriding nOrder + !! in the control structure. + ! local variables + integer :: i, j, k + integer :: is, ie, js, je + integer :: m, n, l + integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics + real :: mFac ! A constant multiplier. mFac = 1 (if m==0) or 2 (if m>0) + real :: pmn, & ! Current associated Legendre polynomials of degree n and order m + pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m + pmnm2 ! Associated Legendre polynomials of degree n-2 and order m + + if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & + "spherical_harmonics_inverse: Module must be initialized before it is used.") + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + Nmax = CS%nOrder; if (present(Nd)) Nmax = Nd + + var = 0.0 + do m=0,Nmax + mFac = sign(1.0, m-0.5)*0.5 + 1.5 + l = SHOrderDegreeToIndex(m, m, Nmax) + + do j=js,je ; do i=is,ie + pmn = CS%Pmm(i,j,m+1) + var(i,j) = var(i,j) & + + mFac * pmn * (SnmRe(l) * CS%complexExpRe(i,j,m+1) + SnmIm(l) * CS%complexExpIm(i,j,m+1)) + + pmnm2 = 0.0; pmnm1 = pmn + do n=m+1,Nmax + pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 + var(i,j) = var(i,j) & + + mFac * pmn * (SnmRe(l+n-m) * CS%complexExpRe(i,j,m+1) + SnmIm(l+n-m) * CS%complexExpIm(i,j,m+1)) + pmnm2 = pmnm1; pmnm1 = pmn + enddo + enddo ; enddo + enddo +end subroutine spherical_harmonics_inverse subroutine spherical_harmonics_init(G, param_file, CS) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(sht_CS), intent(inout) :: CS - type(param_file_type), intent(in) :: param_file !< A structure indicating - real :: PI ! 3.1415926... calculated as 4*atan(1) - real :: RADIAN + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(param_file_type), intent(in) :: param_file !< A structure indicating + type(sht_CS), intent(inout) :: CS !< Control structure for spherical harmonics trasnforms + + ! local variables + real, parameter :: PI = 4.0*atan(1.0) ! 3.1415926... calculated as 4*atan(1) [nodim] + real, parameter :: RADIAN = PI / 180.0 ! Degree to Radian constant [rad/degree] + real, dimension(SZI_(G),SZJ_(G)) :: sinCoLatT ! sine of colatitude at the t-cells [nondim]. + real :: Pmm_coef ! = sqrt{ 1.0/(4.0*PI) * prod[(2k+1)/2k)] } [nondim]. integer :: is, ie, js, je - integer :: i, j + integer :: i, j, k integer :: m, n + integer :: Nd_tidal_SAL ! This include declares and sets the variable "version". # include "version_variable.h" - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + character(len=40) :: mdl = "MOM_spherical_harmonics" ! This module's name. + + CS%initialized = .True. - PI = 4.0*atan(1.0) - RADIAN = PI / 180.0 + call log_version(param_file, mdl, version, "") - call get_param(param_file, '', "SHT_ORDER", CS%nOrder, & - "Order of spherical harmonics transformation. ", default=1) - CS%lmax = (CS%nOrder + 1) * (CS%nOrder + 2) / 2 - call get_param(param_file, '', "SHT_BFB", CS%bfb, & + call get_param(param_file, mdl, "TIDAL_SAL_SHT_DEGREE", Nd_tidal_SAL, & + "The maximum degree of the spherical harmonics transformation used for "// & + "calculating the self-attraction and loading term for tides.", & + default=0, do_not_log=.true.) + CS%nOrder = Nd_tidal_SAL + CS%lmax = calc_lmax(CS%nOrder) + call get_param(param_file, mdl, "SHT_BFB", CS%bfb, & "If true, use bfb sum. Default is False.", default=.False.) - allocate(CS%pmn(is:ie,js:je)) ; CS%pmn(:,:) = 0.0 - allocate(CS%pmnm1(is:ie,js:je)); CS%pmnm1(:,:) = 0.0 - allocate(CS%pmnm2(is:ie,js:je)); CS%pmnm2(:,:) = 0.0 + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec - ! Compute recurrence relationship coefficients - allocate(CS%aRecurrenceCoeff(CS%nOrder+1,CS%nOrder+1)); CS%aRecurrenceCoeff(:,:) = 0.0 + ! Recurrence relationship coefficients + ! a has an additional row of zero to accommodate the nonexistent element P(m+2,m+1) when m=n=CS%nOrder + allocate(CS%aRecurrenceCoeff(CS%nOrder+2,CS%nOrder+1)); CS%aRecurrenceCoeff(:,:) = 0.0 allocate(CS%bRecurrenceCoeff(CS%nOrder+1,CS%nOrder+1)); CS%bRecurrenceCoeff(:,:) = 0.0 - do m = 0, CS%nOrder do n = m, CS%nOrder if (m /= n) then @@ -62,14 +204,13 @@ subroutine spherical_harmonics_init(G, param_file, CS) enddo enddo - ! Precompute complex exponential factors + ! Complex exponential factors allocate(CS%complexFactorRe(is:ie, js:je, CS%nOrder+1)); CS%complexFactorRe(:,:,:) = 0.0 allocate(CS%complexFactorIm(is:ie, js:je, CS%nOrder+1)); CS%complexFactorIm(:,:,:) = 0.0 allocate(CS%complexExpRe(is:ie, js:je, CS%nOrder+1)); CS%complexExpRe(:,:,:) = 0.0 allocate(CS%complexExpIm(is:ie, js:je, CS%nOrder+1)); CS%complexExpIm(:,:,:) = 0.0 - - do m = 0, CS%nOrder - do j = js,je ; do i = is,ie + do m=0,CS%nOrder + do j=js,je ; do i=is,ie CS%complexExpRe(i, j, m+1) = cos(real(m) * (G%geolonT(i,j)*RADIAN)) CS%complexExpIm(i, j, m+1) = sin(real(m) * (G%geolonT(i,j)*RADIAN)) CS%complexFactorRe(i, j, m+1) = CS%complexExpRe(i, j, m+1) * G%areaT(i,j) / G%Rad_Earth**2 @@ -77,67 +218,46 @@ subroutine spherical_harmonics_init(G, param_file, CS) enddo ; enddo enddo - ! allocate(Snm_local(2*lmax),Snm(2*lmax)) - ! allocate(SnmRe_local(lmax),SnmRe(lmax)) - ! allocate(SnmIm_local(lmax),SnmIm(lmax)) - ! allocate(SnmIm_local_reproSum(nCellsOwned,lmax)) - ! allocate(SnmRe_local_reproSum(nCellsOwned,lmax)) - ! allocate(Snm_local_reproSum(nCellsOwned,2*lmax)) - - - ! Pre-compute sin and cos of latCell (co-latitude) values - allocate(CS%sinLatCell(is:ie,js:je)); CS%sinLatCell(:,:) = 0.0 - allocate(CS%cosLatCell(is:ie,js:je)); CS%cosLatCell(:,:) = 0.0 - do j = js,je ; do i = is,ie - CS%sinLatCell(i,j) = sin(0.5*PI - G%geolatT(i,j)*RADIAN) - CS%cosLatCell(i,j) = cos(0.5*PI - G%geolatT(i,j)*RADIAN) + ! sine and cosine of colatitude + allocate(CS%cosCoLatT(is:ie,js:je)); CS%cosCoLatT(:,:) = 0.0 + do j=js,je ; do i=is,ie + CS%cosCoLatT(i,j) = cos(0.5*PI - G%geolatT(i,j)*RADIAN) + sinCoLatT(i,j) = sin(0.5*PI - G%geolatT(i,j)*RADIAN) enddo ; enddo -endsubroutine spherical_harmonics_init + + ! The diagonal elements of the associated Legendre polynomials (n=m) + allocate(CS%Pmm(is:ie,js:je,m+1)); CS%Pmm(:,:,:) = 0.0 + do m=0,CS%nOrder + ! Pmm_coef = 1.0/(4.0*PI) + ! do k=1,m ; Pmm_coef = Pmm_coef * real(2*k+1)/real(2*k); enddo + ! Pmm_coef = sqrt(Pmm_coef) + ! do j=js,je ; do i=is,ie + ! CS%Pmm(i,j,m+1) = Pmm_coef * sinCoLatT(i,j)**m + ! enddo ; enddo + do j=js,je ; do i=is,ie + CS%Pmm(i,j,m+1) = sqrt(1.0/(4.0*PI)) * sinCoLatT(i,j)**m + do k = 1, m + CS%Pmm(i,j,m+1) = CS%Pmm(i,j,m+1) * sqrt(real(2*k+1)/real(2*k)) + enddo + enddo ; enddo + enddo +end subroutine spherical_harmonics_init subroutine spherical_harmonics_end(CS) type(sht_CS), intent(inout) :: CS - deallocate(CS%sinLatCell, CS%cosLatCell) + deallocate(CS%cosCoLatT) + deallocate(CS%Pmm) deallocate(CS%complexFactorRe, CS%complexFactorIm, CS%complexExpRe, CS%complexExpIm) - deallocate(CS%pmnm2, CS%pmnm1, CS%pmn) deallocate(CS%aRecurrenceCoeff, CS%bRecurrenceCoeff) end subroutine spherical_harmonics_end -subroutine associatedLegendrePolynomials(n, m, l, CS, G) - type(ocean_grid_type), intent(in) :: G - integer, intent(in) :: n - integer, intent(in) :: m - integer, intent(out) :: l - type(sht_CS), intent(inout) :: CS - ! real, dimension(:,:), intent(inout) :: pmnm2 - ! real, dimension(:,:), intent(inout) :: pmnm1 - ! real, dimension(:,:), intent(inout) :: pmn - - integer :: i, j, k - real, parameter :: PI = 4.0*atan(1.0) ! 3.1415926... calculated as 4*atan(1) - integer :: is, ie, js, je - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec - - l = SHOrderDegreeToIndex(n,m, CS%nOrder) +function calc_lmax(Nd) result(lmax) + integer :: Nd + integer :: lmax - if (n == m) then - do j = js, je ; do i = is, ie - CS%pmnm2(i,j) = sqrt(1.0/(4.0*PI)) * CS%sinLatCell(i,j)**m - do k = 1, m - CS%pmnm2(i,j) = CS%pmnm2(i,j) * sqrt(real(2*k+1)/real(2*k)) - enddo - enddo ; enddo - else if (n == m+1) then - do j = js, je ; do i = is, ie - CS%pmnm1(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosLatCell(i,j) * CS%pmnm2(i,j) - enddo ; enddo - else - do j = js, je ; do i = is, ie - CS%pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosLatCell(i,j) * CS%pmnm1(i,j) & - - CS%bRecurrenceCoeff(n+1,m+1) * CS%pmnm2(i,j) - enddo ; enddo - endif -end subroutine associatedLegendrePolynomials + lmax = (Nd+2) * (Nd+1) / 2 +end function calc_lmax function SHOrderDegreeToIndex(n,m, nOrder) result(l)!{{{ diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 679e8a0fc8..04bfe23aac 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -12,10 +12,9 @@ module MOM_tidal_forcing use MOM_io, only : field_exists, file_exists, MOM_read_data use MOM_time_manager, only : set_date, time_type, time_type_to_real, operator(-) use MOM_unit_scaling, only : unit_scale_type -use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end -use MOM_spherical_harmonics, only : associatedLegendrePolynomials, SHOrderDegreeToIndex +use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end, calc_lmax +use MOM_spherical_harmonics, only : spherical_harmonics_forward, spherical_harmonics_inverse, SHOrderDegreeToIndex use MOM_spherical_harmonics, only : sht_CS -use MOM_coms_infra, only : sum_across_PEs implicit none ; private @@ -76,6 +75,7 @@ module MOM_tidal_forcing sinphase_prev(:,:,:), & !< amphidromes in the previous tidal solutions. amp_prev(:,:,:) !< The amplitude of the previous tidal solution [Z ~> m]. type(sht_CS) :: sht + integer :: sal_sht_Nd end type tidal_forcing_CS integer :: id_clock_tides !< CPU clock for tides @@ -530,6 +530,10 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) endif if (CS%tidal_sal_sht) then + call get_param(param_file, mdl, "TIDAL_SAL_SHT_DEGREE", CS%sal_sht_Nd, & + "The maximum degree of the spherical harmonics transformation used for "// & + "calculating the self-attraction and loading term for tides.", & + default=0, do_not_log=.not. CS%tidal_sal_sht) call spherical_harmonics_init(G, param_file, CS%sht) id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_MODULE) endif @@ -2157,7 +2161,7 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) if (CS%TIDAL_SAL_SHT) then eta_sal = 0.0 - call calc_tidal_SAL(eta, eta_sal, G, CS%sht) + call calc_SAL_sht(eta, eta_sal, G, CS) call pass_var(eta_sal, G%domain) do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 eta_tidal(i,j) = eta_tidal(i,j) + eta_sal(i,j) @@ -2167,237 +2171,44 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) end subroutine calc_tidal_forcing -subroutine calc_tidal_SAL(eta, eta_sal, G, sht) +subroutine calc_SAL_sht(eta, eta_sal, G, CS) type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. real, dimension(SZI_(G),SZJ_(G)), intent(in) :: eta !< The sea surface height anomaly from !! a time-mean geoid [Z ~> m]. real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_sal !< The sea surface height anomaly from !! a time-mean geoid [Z ~> m]. - type(sht_CS), intent(inout) :: sht - real, allocatable :: SnmRe_local(:), SnmIm_local(:) - real, allocatable :: SnmRe_local_reproSum(:), SnmIm_local_reproSum(:) + ! type(sht_CS), intent(in) :: sht + type(tidal_forcing_CS), intent(in) :: CS !< Tidal forcing control struct real, allocatable :: SnmRe(:), SnmIm(:) - real, allocatable :: Snm_local(:), Snm_local_reproSum(:), Snm(:) real, allocatable :: LoveScaling(:) - integer :: i, j - integer :: is, ie, js, je integer :: n, m, l - real :: mFac - - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + integer :: lmax + lmax = calc_lmax(CS%sal_sht_Nd) call cpu_clock_begin(id_clock_SAL) - allocate(Snm(2*sht%lmax)); Snm = 0.0 - allocate(SnmRe(sht%lmax)); SnmRe = 0.0 - allocate(SnmIm(sht%lmax)); SnmIm = 0.0 - - if (sht%bfb) then - allocate(Snm_local_reproSum(2*sht%lmax)); Snm_local_reproSum = 0.0 - allocate(SnmRe_local_reproSum(sht%lmax)); SnmRe_local_reproSum = 0.0 - allocate(SnmIm_local_reproSum(sht%lmax)); SnmIm_local_reproSum = 0.0 - else - allocate(Snm_local(2*sht%lmax)); Snm_local = 0.0 - allocate(SnmRe_local(sht%lmax)); SnmRe_local = 0.0 - allocate(SnmIm_local(sht%lmax)); SnmIm_local = 0.0 - endif + allocate(SnmRe(lmax)); SnmRe = 0.0 + allocate(SnmIm(lmax)); SnmIm = 0.0 ! Get SAL scaling factors - ALLOC_(LoveScaling(sht%lmax)) - call getloadLoveNums(sht%nOrder, LoveScaling) - - !!!!!!!!!!!!!!!!!!!!! - ! Forward Transform - !!!!!!!!!!!!!!!!!!!!! - - do m = 0, sht%nOrder - !------------ - ! n = m - !------------ - n = m - ! Calculate associated Legendre polynomial for n=m (output pmnm2) - ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) - call associatedLegendrePolynomials(n, m, l, sht, G) - - ! Compute local integral contribution - if (sht%bfb) then - ! do iCell = endIdx, startIdx, -1 - ! SnmRe_local_reproSum(iCell,l) = SnmRe_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm2(iCell)*complexFactorRe(iCell,m+1) - ! SnmIm_local_reproSum(iCell,l) = SnmIm_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm2(iCell)*complexFactorIm(iCell,m+1) - ! enddo - else - do j = js,je ; do i = is,ie - SnmRe_local(l) = SnmRe_local(l) + eta(i,j) * sht%pmnm2(i,j) * sht%complexFactorRe(i,j,m+1) - SnmIm_local(l) = SnmIm_local(l) + eta(i,j) * sht%pmnm2(i,j) * sht%complexFactorIm(i,j,m+1) - enddo ; enddo - endif - - !------------ - ! n = m+1 - !------------ - n = m+1 - if (n <= sht%nOrder) then - ! Calculate associated Legendre polynomial for n = m+1 using recurrence relationship - ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) - call associatedLegendrePolynomials(n, m, l, sht, G) - - ! Compute local integral contribution - if (sht%bfb) then - ! do iCell = endIdx, startIdx, -1 - ! SnmRe_local_reproSum(iCell,l) = SnmRe_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm1(iCell)*complexFactorRe(iCell,m+1) - ! SnmIm_local_reproSum(iCell,l) = SnmIm_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmnm1(iCell)*complexFactorIm(iCell,m+1) - ! enddo - else - do j = js,je ; do i = is,ie - SnmRe_local(l) = SnmRe_local(l) + eta(i,j) * sht%pmnm1(i,j) * sht%complexFactorRe(i,j,m+1) - SnmIm_local(l) = SnmIm_local(l) + eta(i,j) * sht%pmnm1(i,j) * sht%complexFactorIm(i,j,m+1) - enddo ; enddo - endif - endif - - !------------ - ! n > m+1 - !------------ - do n = m+2,sht%nOrder - ! Calculate associated Legendre polynomial using recurrence relationship - ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) - call associatedLegendrePolynomials(n, m, l, sht, G) - - ! Update associated Ledgendre polynomial values for next recurrence - do j = js,je ; do i = is,ie - sht%pmnm2(i,j) = sht%pmnm1(i,j) - sht%pmnm1(i,j) = sht%pmn(i,j) - enddo ; enddo + ALLOC_(LoveScaling(lmax)) + call getloadLoveNums(CS%sal_sht_Nd, LoveScaling) - ! Compute local integral contribution - if (sht%bfb) then - ! do iCell = endIdx, startIdx, -1 - ! SnmRe_local_reproSum(iCell,l) = SnmRe_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmn(iCell)*complexFactorRe(iCell,m+1) - ! SnmIm_local_reproSum(iCell,l) = SnmIm_local_reproSum(iCell,l) + sshSmoothed(iCell)*pmn(iCell)*complexFactorIm(iCell,m+1) - ! enddo - else - do j = js,je ; do i = is,ie - SnmRe_local(l) = SnmRe_local(l) + eta(i,j) * sht%pmn(i,j) * sht%complexFactorRe(i,j,m+1) - SnmIm_local(l) = SnmIm_local(l) + eta(i,j) * sht%pmn(i,j) * sht%complexFactorIm(i,j,m+1) - enddo ; enddo - endif - enddo ! n loop - enddo ! m loop - - ! call mpas_timer_stop('Parallel SAL: Forward Transform') - - ! call mpas_timer_start('Parallel SAL: Communication') - if (sht%bfb) then - ! do m = 1,lmax - ! do iCell = 1,nCellsOwned - ! Snm_local_reproSum(iCell,m) = SnmRe_local_reproSum(iCell,m) - ! Snm_local_reproSum(iCell,lmax+m) = SnmIm_local_reproSum(iCell,m) - ! enddo - ! enddo - else - do m = 1,sht%lmax - Snm_local(m) = SnmRe_local(m) - Snm_local(sht%lmax+m) = SnmIm_local(m) - enddo - endif - - ! Compute global integral by summing local contributions - if (sht%bfb) then - !threadNum = mpas_threading_get_thread_num() - !if ( threadNum == 0 ) then - ! Snm = mpas_global_sum_nfld(Snm_local_reproSum,dminfo%comm) - !endif - else - call sum_across_PEs(Snm_local, 2*sht%lmax) - endif + call spherical_harmonics_forward(G, CS%sht, eta, SnmRe, SnmIm, CS%sal_sht_Nd) - do m = 1,sht%lmax - SnmRe(m) = Snm_local(m) - SnmIm(m) = Snm_local(sht%lmax+m) - enddo - - ! call mpas_timer_stop('Parallel SAL: Communication') - - ! call mpas_timer_start('Parallel SAL: Inverse Transform') - - !!!!!!!!!!!!!!!!!!!!! - ! Apply SAL scaling - !!!!!!!!!!!!!!!!!!!!! - - do m = 0,sht%nOrder - do n = m,sht%nOrder - l = SHOrderDegreeToIndex(n,m,sht%norder) + do m = 0,CS%sal_sht_Nd + do n = m,CS%sal_sht_Nd + l = SHOrderDegreeToIndex(n,m,CS%sal_sht_Nd) SnmRe(l) = SnmRe(l)*LoveScaling(l) SnmIm(l) = SnmIm(l)*LoveScaling(l) enddo enddo - !!!!!!!!!!!!!!!!!!!! - ! Inverse transform - !!!!!!!!!!!!!!!!!!!! - - do m = 0,sht%nOrder - if (m>0) then - mFac = 2.0 - else - mFac = 1.0 - endif - - !------------ - ! n = m - !------------ - n = m - ! Calculate associated Legendre polynomial using recurrence relationship - ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) - call associatedLegendrePolynomials(n, m, l, sht, G) - - ! Sum together product of spherical harmonic functions and coefficients - do j = js,je ; do i = is,ie - eta_sal(i,j) = eta_sal(i,j) & - + mFac * sht%pmnm2(i,j) * (SnmRe(l) * sht%complexExpRe(i,j,m+1) + SnmIm(l) * sht%complexExpIm(i,j,m+1)) - enddo ; enddo - - !------------ - ! n = m+1 - !------------ - n = m+1 - if (n <= sht%nOrder) then - ! Calculate associated Legendre polynomial using recurrence relationship - ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) - call associatedLegendrePolynomials(n, m, l, sht, G) - - ! Sum together product of spherical harmonic functions and coefficients - do j = js,je ; do i = is,ie - eta_sal(i,j) = eta_sal(i,j) & - + mFac * sht%pmnm1(i,j) * (SnmRe(l) * sht%complexExpRe(i,j,m+1) + SnmIm(l) * sht%complexExpIm(i,j,m+1)) - enddo ; enddo - endif - - !------------ - ! n > m+1 - !------------ - do n = m+2,sht%nOrder - ! Calculate associated Legendre polynomial using recurrence relationship - ! call associatedLegendrePolynomials(n, m, l, sht%pmnm2, sht%pmnm1, sht%pmn) - call associatedLegendrePolynomials(n, m, l, sht, G) - - ! Update associated Ledgendre polynomial values for next recurrence - do j = js,je ; do i = is,ie - sht%pmnm2(i,j) = sht%pmnm1(i,j) - sht%pmnm1(i,j) = sht%pmn(i,j) - enddo ; enddo - - ! Sum together product of spherical harmonic functions and coefficients - do j = js,je ; do i = is,ie - eta_sal(i,j) = eta_sal(i,j) & - + mFac * sht%pmn(i,j) * (SnmRe(l) * sht%complexExpRe(i,j,m+1) + SnmIm(l) * sht%complexExpIm(i,j,m+1)) - enddo ; enddo - enddo ! n loop - enddo ! m loop + call spherical_harmonics_inverse(G, CS%sht, SnmRe, SnmIm, eta_sal, CS%sal_sht_Nd) call cpu_clock_end(id_clock_SAL) -end subroutine calc_tidal_SAL +end subroutine calc_SAL_sht !> This subroutine deallocates memory associated with the tidal forcing module. subroutine tidal_forcing_end(CS) From aff923ce485e48e911144bd3d1121ab23ea7f885 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 3 Aug 2022 23:01:38 -0400 Subject: [PATCH 52/68] New module to store Love Numbers * A new module is introduced with the sole purpose of storing Love numbers. This makes module MOM_tidal_forcing less chunkier and easier to read. * A new function in module MOM_spherical_harmonics is introduced to calculate the starting index of the first element for each order m, replacing a previous function of a similar purpose but a bit more complicated. --- .../lateral/MOM_load_love_numbers.F90 | 1453 ++++++++++++++++ .../lateral/MOM_spherical_harmonics.f90 | 25 +- .../lateral/MOM_tidal_forcing.F90 | 1534 +---------------- 3 files changed, 1508 insertions(+), 1504 deletions(-) create mode 100644 src/parameterizations/lateral/MOM_load_love_numbers.F90 diff --git a/src/parameterizations/lateral/MOM_load_love_numbers.F90 b/src/parameterizations/lateral/MOM_load_love_numbers.F90 new file mode 100644 index 0000000000..6f46d99c5b --- /dev/null +++ b/src/parameterizations/lateral/MOM_load_love_numbers.F90 @@ -0,0 +1,1453 @@ +!> Load Love Numbers for degree range [0, 1440] +module MOM_load_love_numbers + +implicit none ; private + +public LoveDat + +integer, parameter :: lmax = 1440 +real, dimension(4, lmax+1), parameter :: & + LoveDat = & + reshape((/ 0.0, 0.0000000000, 0.0000000000 , -1.0000000000 , & + 1.0, -1.2858777580,-8.9608179370e-1, -1.0000000000 , & + 2.0, -0.9907994900, 2.3286695000e-2, -3.0516104000e-1, & + 3.0, -1.0499631000, 6.9892136000e-2, -1.9585733000e-1, & + 4.0, -1.0526477000, 5.8670467000e-2, -1.3352284000e-1, & + 5.0, -1.0855918000, 4.6165153000e-2, -1.0456531000e-1, & + 6.0, -1.1431163000, 3.8586926000e-2, -9.0184841000e-2, & + 7.0, -1.2116273000, 3.4198827000e-2, -8.1906787000e-2, & + 8.0, -1.2831157000, 3.1474998000e-2, -7.6379141000e-2, & + 9.0, -1.3538554000, 2.9624407000e-2, -7.2250183000e-2, & + 10.0, -1.4223516000, 2.8273961000e-2, -6.8934145000e-2, & + 11.0, -1.4881117000, 2.7242278000e-2, -6.6147992000e-2, & + 12.0, -1.5510428000, 2.6431124000e-2, -6.3736253000e-2, & + 13.0, -1.6111895000, 2.5779507000e-2, -6.1602870000e-2, & + 14.0, -1.6686329000, 2.5245139000e-2, -5.9683159000e-2, & + 15.0, -1.7234569000, 2.4796803000e-2, -5.7931180000e-2, & + 16.0, -1.7757418000, 2.4410861000e-2, -5.6313294000e-2, & + 17.0, -1.8255646000, 2.4069336000e-2, -5.4804452000e-2, & + 18.0, -1.8730019000, 2.3758645000e-2, -5.3385807000e-2, & + 19.0, -1.9181321000, 2.3468646000e-2, -5.2043088000e-2, & + 20.0, -1.9610366000, 2.3191893000e-2, -5.0765423000e-2, & + 21.0, -2.0018000000, 2.2923032000e-2, -4.9544487000e-2, & + 22.0, -2.0405101000, 2.2658321000e-2, -4.8373866000e-2, & + 23.0, -2.0772571000, 2.2395242000e-2, -4.7248575000e-2, & + 24.0, -2.1121328000, 2.2132200000e-2, -4.6164708000e-2, & + 25.0, -2.1452296000, 2.1868280000e-2, -4.5119160000e-2, & + 26.0, -2.1766398000, 2.1603063000e-2, -4.4109431000e-2, & + 27.0, -2.2064546000, 2.1336479000e-2, -4.3133464000e-2, & + 28.0, -2.2347634000, 2.1068700000e-2, -4.2189540000e-2, & + 29.0, -2.2616531000, 2.0800053000e-2, -4.1276184000e-2, & + 30.0, -2.2872080000, 2.0530962000e-2, -4.0392105000e-2, & + 31.0, -2.3115088000, 2.0261897000e-2, -3.9536148000e-2, & + 32.0, -2.3346328000, 1.9993346000e-2, -3.8707260000e-2, & + 33.0, -2.3566536000, 1.9725790000e-2, -3.7904463000e-2, & + 34.0, -2.3776409000, 1.9459686000e-2, -3.7126837000e-2, & + 35.0, -2.3976605000, 1.9195459000e-2, -3.6373510000e-2, & + 36.0, -2.4167746000, 1.8933494000e-2, -3.5643644000e-2, & + 37.0, -2.4350414000, 1.8674136000e-2, -3.4936432000e-2, & + 38.0, -2.4525156000, 1.8417687000e-2, -3.4251094000e-2, & + 39.0, -2.4692484000, 1.8164407000e-2, -3.3586873000e-2, & + 40.0, -2.4852876000, 1.7914518000e-2, -3.2943035000e-2, & + 41.0, -2.5006779000, 1.7668203000e-2, -3.2318866000e-2, & + 42.0, -2.5154609000, 1.7425613000e-2, -3.1713675000e-2, & + 43.0, -2.5296755000, 1.7186866000e-2, -3.1126789000e-2, & + 44.0, -2.5433577000, 1.6952053000e-2, -3.0557557000e-2, & + 45.0, -2.5565412000, 1.6721240000e-2, -3.0005347000e-2, & + 46.0, -2.5692574000, 1.6494470000e-2, -2.9469547000e-2, & + 47.0, -2.5815353000, 1.6271769000e-2, -2.8949568000e-2, & + 48.0, -2.5934022000, 1.6053144000e-2, -2.8444838000e-2, & + 49.0, -2.6048833000, 1.5838586000e-2, -2.7954806000e-2, & + 50.0, -2.6160021000, 1.5628077000e-2, -2.7478940000e-2, & + 51.0, -2.6267805000, 1.5421585000e-2, -2.7016729000e-2, & + 52.0, -2.6372389000, 1.5219071000e-2, -2.6567679000e-2, & + 53.0, -2.6473964000, 1.5020486000e-2, -2.6131317000e-2, & + 54.0, -2.6572706000, 1.4825779000e-2, -2.5707185000e-2, & + 55.0, -2.6668781000, 1.4634888000e-2, -2.5294846000e-2, & + 56.0, -2.6762345000, 1.4447752000e-2, -2.4893877000e-2, & + 57.0, -2.6853540000, 1.4264303000e-2, -2.4503874000e-2, & + 58.0, -2.6942503000, 1.4084474000e-2, -2.4124449000e-2, & + 59.0, -2.7029358000, 1.3908192000e-2, -2.3755228000e-2, & + 60.0, -2.7114225000, 1.3735386000e-2, -2.3395852000e-2, & + 61.0, -2.7197214000, 1.3565983000e-2, -2.3045980000e-2, & + 62.0, -2.7278428000, 1.3399909000e-2, -2.2705280000e-2, & + 63.0, -2.7357965000, 1.3237092000e-2, -2.2373437000e-2, & + 64.0, -2.7435916000, 1.3077458000e-2, -2.2050147000e-2, & + 65.0, -2.7512366000, 1.2920935000e-2, -2.1735119000e-2, & + 66.0, -2.7587397000, 1.2767451000e-2, -2.1428073000e-2, & + 67.0, -2.7661083000, 1.2616936000e-2, -2.1128742000e-2, & + 68.0, -2.7733496000, 1.2469319000e-2, -2.0836869000e-2, & + 69.0, -2.7804703000, 1.2324532000e-2, -2.0552206000e-2, & + 70.0, -2.7874767000, 1.2182508000e-2, -2.0274516000e-2, & + 71.0, -2.7943748000, 1.2043181000e-2, -2.0003572000e-2, & + 72.0, -2.8011702000, 1.1906487000e-2, -1.9739156000e-2, & + 73.0, -2.8078682000, 1.1772362000e-2, -1.9481058000e-2, & + 74.0, -2.8144738000, 1.1640746000e-2, -1.9229076000e-2, & + 75.0, -2.8209918000, 1.1511578000e-2, -1.8983017000e-2, & + 76.0, -2.8274266000, 1.1384799000e-2, -1.8742695000e-2, & + 77.0, -2.8337824000, 1.1260352000e-2, -1.8507931000e-2, & + 78.0, -2.8400633000, 1.1138183000e-2, -1.8278553000e-2, & + 79.0, -2.8462730000, 1.1018236000e-2, -1.8054395000e-2, & + 80.0, -2.8524152000, 1.0900460000e-2, -1.7835300000e-2, & + 81.0, -2.8584932000, 1.0784802000e-2, -1.7621113000e-2, & + 82.0, -2.8645103000, 1.0671213000e-2, -1.7411688000e-2, & + 83.0, -2.8704696000, 1.0559645000e-2, -1.7206882000e-2, & + 84.0, -2.8763739000, 1.0450051000e-2, -1.7006560000e-2, & + 85.0, -2.8822260000, 1.0342384000e-2, -1.6810590000e-2, & + 86.0, -2.8880285000, 1.0236599000e-2, -1.6618845000e-2, & + 87.0, -2.8937839000, 1.0132655000e-2, -1.6431203000e-2, & + 88.0, -2.8994945000, 1.0030508000e-2, -1.6247547000e-2, & + 89.0, -2.9051627000, 9.9301169000e-3, -1.6067762000e-2, & + 90.0, -2.9107905000, 9.8314429000e-3, -1.5891741000e-2, & + 91.0, -2.9163799000, 9.7344467000e-3, -1.5719376000e-2, & + 92.0, -2.9219330000, 9.6390907000e-3, -1.5550567000e-2, & + 93.0, -2.9274514000, 9.5453383000e-3, -1.5385215000e-2, & + 94.0, -2.9329370000, 9.4531538000e-3, -1.5223225000e-2, & + 95.0, -2.9383913000, 9.3625026000e-3, -1.5064506000e-2, & + 96.0, -2.9438161000, 9.2733509000e-3, -1.4908968000e-2, & + 97.0, -2.9492127000, 9.1856660000e-3, -1.4756526000e-2, & + 98.0, -2.9545826000, 9.0994159000e-3, -1.4607099000e-2, & + 99.0, -2.9599272000, 9.0145695000e-3, -1.4460604000e-2, & + 100.0, -2.9652476000, 8.9310967000e-3, -1.4316967000e-2, & + 101.0, -2.9705453000, 8.8489681000e-3, -1.4176111000e-2, & + 102.0, -2.9758213000, 8.7681548000e-3, -1.4037965000e-2, & + 103.0, -2.9810767000, 8.6886292000e-3, -1.3902458000e-2, & + 104.0, -2.9863125000, 8.6103640000e-3, -1.3769523000e-2, & + 105.0, -2.9915299000, 8.5333328000e-3, -1.3639094000e-2, & + 106.0, -2.9967298000, 8.4575097000e-3, -1.3511108000e-2, & + 107.0, -3.0019129000, 8.3828699000e-3, -1.3385503000e-2, & + 108.0, -3.0070803000, 8.3093886000e-3, -1.3262220000e-2, & + 109.0, -3.0122328000, 8.2370423000e-3, -1.3141201000e-2, & + 110.0, -3.0173710000, 8.1658076000e-3, -1.3022390000e-2, & + 111.0, -3.0224958000, 8.0956619000e-3, -1.2905734000e-2, & + 112.0, -3.0276079000, 8.0265832000e-3, -1.2791179000e-2, & + 113.0, -3.0327080000, 7.9585500000e-3, -1.2678675000e-2, & + 114.0, -3.0377966000, 7.8915413000e-3, -1.2568172000e-2, & + 115.0, -3.0428744000, 7.8255367000e-3, -1.2459622000e-2, & + 116.0, -3.0479420000, 7.7605163000e-3, -1.2352979000e-2, & + 117.0, -3.0529999000, 7.6964606000e-3, -1.2248198000e-2, & + 118.0, -3.0580486000, 7.6333507000e-3, -1.2145235000e-2, & + 119.0, -3.0630887000, 7.5711680000e-3, -1.2044048000e-2, & + 120.0, -3.0681205000, 7.5098946000e-3, -1.1944594000e-2, & + 121.0, -3.0731446000, 7.4495128000e-3, -1.1846835000e-2, & + 122.0, -3.0781614000, 7.3900054000e-3, -1.1750732000e-2, & + 123.0, -3.0831713000, 7.3313557000e-3, -1.1656245000e-2, & + 124.0, -3.0881747000, 7.2735474000e-3, -1.1563340000e-2, & + 125.0, -3.0931718000, 7.2165644000e-3, -1.1471980000e-2, & + 126.0, -3.0981632000, 7.1603911000e-3, -1.1382130000e-2, & + 127.0, -3.1031490000, 7.1050124000e-3, -1.1293757000e-2, & + 128.0, -3.1081296000, 7.0504134000e-3, -1.1206828000e-2, & + 129.0, -3.1131054000, 6.9965795000e-3, -1.1121311000e-2, & + 130.0, -3.1180765000, 6.9434967000e-3, -1.1037175000e-2, & + 131.0, -3.1230433000, 6.8911509000e-3, -1.0954391000e-2, & + 132.0, -3.1280059000, 6.8395288000e-3, -1.0872928000e-2, & + 133.0, -3.1329647000, 6.7886171000e-3, -1.0792758000e-2, & + 134.0, -3.1379199000, 6.7384029000e-3, -1.0713853000e-2, & + 135.0, -3.1428716000, 6.6888735000e-3, -1.0636187000e-2, & + 136.0, -3.1478201000, 6.6400168000e-3, -1.0559733000e-2, & + 137.0, -3.1527656000, 6.5918206000e-3, -1.0484466000e-2, & + 138.0, -3.1577082000, 6.5442732000e-3, -1.0410360000e-2, & + 139.0, -3.1626481000, 6.4973631000e-3, -1.0337392000e-2, & + 140.0, -3.1675855000, 6.4510790000e-3, -1.0265537000e-2, & + 141.0, -3.1725205000, 6.4054099000e-3, -1.0194773000e-2, & + 142.0, -3.1774533000, 6.3603452000e-3, -1.0125078000e-2, & + 143.0, -3.1823840000, 6.3158742000e-3, -1.0056429000e-2, & + 144.0, -3.1873127000, 6.2719868000e-3, -9.9888045000e-3, & + 145.0, -3.1922396000, 6.2286729000e-3, -9.9221850000e-3, & + 146.0, -3.1971648000, 6.1859227000e-3, -9.8565496000e-3, & + 147.0, -3.2020883000, 6.1437265000e-3, -9.7918788000e-3, & + 148.0, -3.2070102000, 6.1020749000e-3, -9.7281532000e-3, & + 149.0, -3.2119308000, 6.0609589000e-3, -9.6653542000e-3, & + 150.0, -3.2168500000, 6.0203693000e-3, -9.6034635000e-3, & + 151.0, -3.2217679000, 5.9802974000e-3, -9.5424633000e-3, & + 152.0, -3.2266847000, 5.9407346000e-3, -9.4823362000e-3, & + 153.0, -3.2316003000, 5.9016724000e-3, -9.4230652000e-3, & + 154.0, -3.2365149000, 5.8631026000e-3, -9.3646338000e-3, & + 155.0, -3.2414284000, 5.8250172000e-3, -9.3070259000e-3, & + 156.0, -3.2463411000, 5.7874081000e-3, -9.2502257000e-3, & + 157.0, -3.2512529000, 5.7502678000e-3, -9.1942178000e-3, & + 158.0, -3.2561639000, 5.7135886000e-3, -9.1389873000e-3, & + 159.0, -3.2610741000, 5.6773630000e-3, -9.0845194000e-3, & + 160.0, -3.2659835000, 5.6415839000e-3, -9.0308000000e-3, & + 161.0, -3.2708923000, 5.6062442000e-3, -8.9778149000e-3, & + 162.0, -3.2758004000, 5.5713368000e-3, -8.9255506000e-3, & + 163.0, -3.2807079000, 5.5368550000e-3, -8.8739938000e-3, & + 164.0, -3.2856148000, 5.5027920000e-3, -8.8231314000e-3, & + 165.0, -3.2905211000, 5.4691413000e-3, -8.7729507000e-3, & + 166.0, -3.2954269000, 5.4358966000e-3, -8.7234394000e-3, & + 167.0, -3.3003322000, 5.4030515000e-3, -8.6745852000e-3, & + 168.0, -3.3052370000, 5.3705998000e-3, -8.6263763000e-3, & + 169.0, -3.3101414000, 5.3385356000e-3, -8.5788012000e-3, & + 170.0, -3.3150452000, 5.3068529000e-3, -8.5318484000e-3, & + 171.0, -3.3199486000, 5.2755459000e-3, -8.4855070000e-3, & + 172.0, -3.3248516000, 5.2446089000e-3, -8.4397661000e-3, & + 173.0, -3.3297541000, 5.2140364000e-3, -8.3946150000e-3, & + 174.0, -3.3346563000, 5.1838229000e-3, -8.3500435000e-3, & + 175.0, -3.3395580000, 5.1539630000e-3, -8.3060415000e-3, & + 176.0, -3.3444593000, 5.1244515000e-3, -8.2625990000e-3, & + 177.0, -3.3493602000, 5.0952833000e-3, -8.2197063000e-3, & + 178.0, -3.3542607000, 5.0664532000e-3, -8.1773539000e-3, & + 179.0, -3.3591609000, 5.0379563000e-3, -8.1355327000e-3, & + 180.0, -3.3640606000, 5.0097879000e-3, -8.0942335000e-3, & + 181.0, -3.3689599000, 4.9819430000e-3, -8.0534474000e-3, & + 182.0, -3.3738588000, 4.9544170000e-3, -8.0131658000e-3, & + 183.0, -3.3787572000, 4.9272053000e-3, -7.9733801000e-3, & + 184.0, -3.3836553000, 4.9003034000e-3, -7.9340821000e-3, & + 185.0, -3.3885529000, 4.8737069000e-3, -7.8952635000e-3, & + 186.0, -3.3934501000, 4.8474114000e-3, -7.8569164000e-3, & + 187.0, -3.3983469000, 4.8214127000e-3, -7.8190330000e-3, & + 188.0, -3.4032432000, 4.7957066000e-3, -7.7816057000e-3, & + 189.0, -3.4081390000, 4.7702889000e-3, -7.7446269000e-3, & + 190.0, -3.4130344000, 4.7451557000e-3, -7.7080893000e-3, & + 191.0, -3.4179292000, 4.7203030000e-3, -7.6719857000e-3, & + 192.0, -3.4228236000, 4.6957268000e-3, -7.6363091000e-3, & + 193.0, -3.4277174000, 4.6714235000e-3, -7.6010526000e-3, & + 194.0, -3.4326107000, 4.6473891000e-3, -7.5662095000e-3, & + 195.0, -3.4375035000, 4.6236200000e-3, -7.5317730000e-3, & + 196.0, -3.4423957000, 4.6001126000e-3, -7.4977367000e-3, & + 197.0, -3.4472873000, 4.5768634000e-3, -7.4640943000e-3, & + 198.0, -3.4521783000, 4.5538688000e-3, -7.4308395000e-3, & + 199.0, -3.4570687000, 4.5311254000e-3, -7.3979662000e-3, & + 200.0, -3.4619585000, 4.5086298000e-3, -7.3654685000e-3, & + 201.0, -3.4668476000, 4.4863788000e-3, -7.3333403000e-3, & + 202.0, -3.4717360000, 4.4643689000e-3, -7.3015761000e-3, & + 203.0, -3.4766237000, 4.4425971000e-3, -7.2701701000e-3, & + 204.0, -3.4815107000, 4.4210601000e-3, -7.2391168000e-3, & + 205.0, -3.4863970000, 4.3997550000e-3, -7.2084108000e-3, & + 206.0, -3.4912825000, 4.3786785000e-3, -7.1780467000e-3, & + 207.0, -3.4961672000, 4.3578278000e-3, -7.1480193000e-3, & + 208.0, -3.5010512000, 4.3371999000e-3, -7.1183236000e-3, & + 209.0, -3.5059343000, 4.3167918000e-3, -7.0889544000e-3, & + 210.0, -3.5108165000, 4.2966008000e-3, -7.0599068000e-3, & + 211.0, -3.5156979000, 4.2766239000e-3, -7.0311760000e-3, & + 212.0, -3.5205784000, 4.2568586000e-3, -7.0027573000e-3, & + 213.0, -3.5254580000, 4.2373019000e-3, -6.9746460000e-3, & + 214.0, -3.5303366000, 4.2179514000e-3, -6.9468375000e-3, & + 215.0, -3.5352143000, 4.1988043000e-3, -6.9193272000e-3, & + 216.0, -3.5400909000, 4.1798580000e-3, -6.8921109000e-3, & + 217.0, -3.5449666000, 4.1611101000e-3, -6.8651842000e-3, & + 218.0, -3.5498412000, 4.1425580000e-3, -6.8385428000e-3, & + 219.0, -3.5547147000, 4.1241992000e-3, -6.8121826000e-3, & + 220.0, -3.5595871000, 4.1060313000e-3, -6.7860995000e-3, & + 221.0, -3.5644584000, 4.0880520000e-3, -6.7602894000e-3, & + 222.0, -3.5693286000, 4.0702588000e-3, -6.7347484000e-3, & + 223.0, -3.5741976000, 4.0526495000e-3, -6.7094726000e-3, & + 224.0, -3.5790654000, 4.0352217000e-3, -6.6844583000e-3, & + 225.0, -3.5839320000, 4.0179733000e-3, -6.6597016000e-3, & + 226.0, -3.5887973000, 4.0009020000e-3, -6.6351989000e-3, & + 227.0, -3.5936613000, 3.9840057000e-3, -6.6109466000e-3, & + 228.0, -3.5985240000, 3.9672821000e-3, -6.5869411000e-3, & + 229.0, -3.6033854000, 3.9507293000e-3, -6.5631791000e-3, & + 230.0, -3.6082455000, 3.9343450000e-3, -6.5396569000e-3, & + 231.0, -3.6131041000, 3.9181273000e-3, -6.5163713000e-3, & + 232.0, -3.6179613000, 3.9020742000e-3, -6.4933190000e-3, & + 233.0, -3.6228171000, 3.8861836000e-3, -6.4704966000e-3, & + 234.0, -3.6276714000, 3.8704536000e-3, -6.4479012000e-3, & + 235.0, -3.6325242000, 3.8548822000e-3, -6.4255293000e-3, & + 236.0, -3.6373754000, 3.8394677000e-3, -6.4033781000e-3, & + 237.0, -3.6422252000, 3.8242080000e-3, -6.3814445000e-3, & + 238.0, -3.6470733000, 3.8091013000e-3, -6.3597254000e-3, & + 239.0, -3.6519198000, 3.7941458000e-3, -6.3382179000e-3, & + 240.0, -3.6567647000, 3.7793398000e-3, -6.3169193000e-3, & + 241.0, -3.6616079000, 3.7646814000e-3, -6.2958265000e-3, & + 242.0, -3.6664494000, 3.7501690000e-3, -6.2749370000e-3, & + 243.0, -3.6712891000, 3.7358007000e-3, -6.2542478000e-3, & + 244.0, -3.6761271000, 3.7215749000e-3, -6.2337563000e-3, & + 245.0, -3.6809634000, 3.7074899000e-3, -6.2134599000e-3, & + 246.0, -3.6857978000, 3.6935441000e-3, -6.1933559000e-3, & + 247.0, -3.6906303000, 3.6797359000e-3, -6.1734419000e-3, & + 248.0, -3.6954610000, 3.6660636000e-3, -6.1537152000e-3, & + 249.0, -3.7002898000, 3.6525257000e-3, -6.1341734000e-3, & + 250.0, -3.7051167000, 3.6391206000e-3, -6.1148140000e-3, & + 251.0, -3.7099416000, 3.6258468000e-3, -6.0956346000e-3, & + 252.0, -3.7147645000, 3.6127027000e-3, -6.0766330000e-3, & + 253.0, -3.7195854000, 3.5996869000e-3, -6.0578067000e-3, & + 254.0, -3.7244043000, 3.5867979000e-3, -6.0391534000e-3, & + 255.0, -3.7292211000, 3.5740342000e-3, -6.0206710000e-3, & + 256.0, -3.7340357000, 3.5613944000e-3, -6.0023572000e-3, & + 257.0, -3.7388483000, 3.5488772000e-3, -5.9842098000e-3, & + 258.0, -3.7436587000, 3.5364810000e-3, -5.9662266000e-3, & + 259.0, -3.7484669000, 3.5242045000e-3, -5.9484056000e-3, & + 260.0, -3.7532729000, 3.5120464000e-3, -5.9307447000e-3, & + 261.0, -3.7580766000, 3.5000053000e-3, -5.9132419000e-3, & + 262.0, -3.7628780000, 3.4880799000e-3, -5.8958950000e-3, & + 263.0, -3.7676772000, 3.4762689000e-3, -5.8787022000e-3, & + 264.0, -3.7724740000, 3.4645710000e-3, -5.8616614000e-3, & + 265.0, -3.7772685000, 3.4529849000e-3, -5.8447709000e-3, & + 266.0, -3.7820605000, 3.4415093000e-3, -5.8280285000e-3, & + 267.0, -3.7868501000, 3.4301431000e-3, -5.8114326000e-3, & + 268.0, -3.7916373000, 3.4188851000e-3, -5.7949812000e-3, & + 269.0, -3.7964220000, 3.4077339000e-3, -5.7786726000e-3, & + 270.0, -3.8012042000, 3.3966884000e-3, -5.7625050000e-3, & + 271.0, -3.8059839000, 3.3857475000e-3, -5.7464766000e-3, & + 272.0, -3.8107610000, 3.3749099000e-3, -5.7305857000e-3, & + 273.0, -3.8155355000, 3.3641746000e-3, -5.7148305000e-3, & + 274.0, -3.8203074000, 3.3535404000e-3, -5.6992095000e-3, & + 275.0, -3.8250766000, 3.3430061000e-3, -5.6837210000e-3, & + 276.0, -3.8298432000, 3.3325707000e-3, -5.6683633000e-3, & + 277.0, -3.8346070000, 3.3222331000e-3, -5.6531348000e-3, & + 278.0, -3.8393682000, 3.3119922000e-3, -5.6380340000e-3, & + 279.0, -3.8441265000, 3.3018470000e-3, -5.6230593000e-3, & + 280.0, -3.8488821000, 3.2917964000e-3, -5.6082092000e-3, & + 281.0, -3.8536348000, 3.2818393000e-3, -5.5934822000e-3, & + 282.0, -3.8583847000, 3.2719748000e-3, -5.5788767000e-3, & + 283.0, -3.8631317000, 3.2622018000e-3, -5.5643913000e-3, & + 284.0, -3.8678759000, 3.2525193000e-3, -5.5500246000e-3, & + 285.0, -3.8726170000, 3.2429264000e-3, -5.5357752000e-3, & + 286.0, -3.8773553000, 3.2334221000e-3, -5.5216416000e-3, & + 287.0, -3.8820905000, 3.2240054000e-3, -5.5076224000e-3, & + 288.0, -3.8868227000, 3.2146753000e-3, -5.4937164000e-3, & + 289.0, -3.8915519000, 3.2054310000e-3, -5.4799221000e-3, & + 290.0, -3.8962780000, 3.1962715000e-3, -5.4662383000e-3, & + 291.0, -3.9010010000, 3.1871958000e-3, -5.4526635000e-3, & + 292.0, -3.9057209000, 3.1782032000e-3, -5.4391967000e-3, & + 293.0, -3.9104377000, 3.1692926000e-3, -5.4258363000e-3, & + 294.0, -3.9151512000, 3.1604632000e-3, -5.4125813000e-3, & + 295.0, -3.9198616000, 3.1517142000e-3, -5.3994305000e-3, & + 296.0, -3.9245687000, 3.1430446000e-3, -5.3863824000e-3, & + 297.0, -3.9292725000, 3.1344537000e-3, -5.3734361000e-3, & + 298.0, -3.9339731000, 3.1259405000e-3, -5.3605902000e-3, & + 299.0, -3.9386704000, 3.1175043000e-3, -5.3478437000e-3, & + 300.0, -3.9433643000, 3.1091442000e-3, -5.3351954000e-3, & + 301.0, -3.9480548000, 3.1008594000e-3, -5.3226441000e-3, & + 302.0, -3.9527420000, 3.0926491000e-3, -5.3101888000e-3, & + 303.0, -3.9574257000, 3.0845126000e-3, -5.2978283000e-3, & + 304.0, -3.9621060000, 3.0764490000e-3, -5.2855615000e-3, & + 305.0, -3.9667828000, 3.0684575000e-3, -5.2733874000e-3, & + 306.0, -3.9714561000, 3.0605375000e-3, -5.2613050000e-3, & + 307.0, -3.9761259000, 3.0526881000e-3, -5.2493131000e-3, & + 308.0, -3.9807921000, 3.0449085000e-3, -5.2374107000e-3, & + 309.0, -3.9854548000, 3.0371982000e-3, -5.2255969000e-3, & + 310.0, -3.9901138000, 3.0295562000e-3, -5.2138707000e-3, & + 311.0, -3.9947693000, 3.0219820000e-3, -5.2022310000e-3, & + 312.0, -3.9994210000, 3.0144747000e-3, -5.1906768000e-3, & + 313.0, -4.0040691000, 3.0070337000e-3, -5.1792073000e-3, & + 314.0, -4.0087135000, 2.9996584000e-3, -5.1678215000e-3, & + 315.0, -4.0133542000, 2.9923479000e-3, -5.1565183000e-3, & + 316.0, -4.0179911000, 2.9851016000e-3, -5.1452970000e-3, & + 317.0, -4.0226242000, 2.9779189000e-3, -5.1341566000e-3, & + 318.0, -4.0272535000, 2.9707990000e-3, -5.1230962000e-3, & + 319.0, -4.0318790000, 2.9637414000e-3, -5.1121150000e-3, & + 320.0, -4.0365006000, 2.9567453000e-3, -5.1012119000e-3, & + 321.0, -4.0411184000, 2.9498101000e-3, -5.0903863000e-3, & + 322.0, -4.0457322000, 2.9429353000e-3, -5.0796372000e-3, & + 323.0, -4.0503421000, 2.9361201000e-3, -5.0689638000e-3, & + 324.0, -4.0549481000, 2.9293639000e-3, -5.0583652000e-3, & + 325.0, -4.0595501000, 2.9226662000e-3, -5.0478407000e-3, & + 326.0, -4.0641480000, 2.9160263000e-3, -5.0373894000e-3, & + 327.0, -4.0687420000, 2.9094435000e-3, -5.0270106000e-3, & + 328.0, -4.0733319000, 2.9029174000e-3, -5.0167034000e-3, & + 329.0, -4.0779177000, 2.8964474000e-3, -5.0064671000e-3, & + 330.0, -4.0824995000, 2.8900327000e-3, -4.9963009000e-3, & + 331.0, -4.0870771000, 2.8836730000e-3, -4.9862041000e-3, & + 332.0, -4.0916505000, 2.8773676000e-3, -4.9761758000e-3, & + 333.0, -4.0962198000, 2.8711159000e-3, -4.9662155000e-3, & + 334.0, -4.1007850000, 2.8649173000e-3, -4.9563223000e-3, & + 335.0, -4.1053459000, 2.8587715000e-3, -4.9464955000e-3, & + 336.0, -4.1099025000, 2.8526777000e-3, -4.9367344000e-3, & + 337.0, -4.1144549000, 2.8466354000e-3, -4.9270384000e-3, & + 338.0, -4.1190030000, 2.8406442000e-3, -4.9174066000e-3, & + 339.0, -4.1235469000, 2.8347035000e-3, -4.9078386000e-3, & + 340.0, -4.1280863000, 2.8288128000e-3, -4.8983335000e-3, & + 341.0, -4.1326215000, 2.8229715000e-3, -4.8888907000e-3, & + 342.0, -4.1371523000, 2.8171792000e-3, -4.8795095000e-3, & + 343.0, -4.1416786000, 2.8114353000e-3, -4.8701893000e-3, & + 344.0, -4.1462006000, 2.8057394000e-3, -4.8609295000e-3, & + 345.0, -4.1507181000, 2.8000909000e-3, -4.8517295000e-3, & + 346.0, -4.1552312000, 2.7944894000e-3, -4.8425885000e-3, & + 347.0, -4.1597397000, 2.7889344000e-3, -4.8335060000e-3, & + 348.0, -4.1642438000, 2.7834254000e-3, -4.8244814000e-3, & + 349.0, -4.1687434000, 2.7779620000e-3, -4.8155141000e-3, & + 350.0, -4.1732384000, 2.7725436000e-3, -4.8066034000e-3, & + 351.0, -4.1777288000, 2.7671698000e-3, -4.7977488000e-3, & + 352.0, -4.1822147000, 2.7618402000e-3, -4.7889498000e-3, & + 353.0, -4.1866959000, 2.7565543000e-3, -4.7802057000e-3, & + 354.0, -4.1911725000, 2.7513117000e-3, -4.7715160000e-3, & + 355.0, -4.1956445000, 2.7461118000e-3, -4.7628800000e-3, & + 356.0, -4.2001118000, 2.7409544000e-3, -4.7542974000e-3, & + 357.0, -4.2045744000, 2.7358388000e-3, -4.7457675000e-3, & + 358.0, -4.2090323000, 2.7307648000e-3, -4.7372897000e-3, & + 359.0, -4.2134854000, 2.7257319000e-3, -4.7288636000e-3, & + 360.0, -4.2179338000, 2.7207397000e-3, -4.7204886000e-3, & + 361.0, -4.2223775000, 2.7157877000e-3, -4.7121643000e-3, & + 362.0, -4.2268163000, 2.7108756000e-3, -4.7038900000e-3, & + 363.0, -4.2312503000, 2.7060029000e-3, -4.6956653000e-3, & + 364.0, -4.2356795000, 2.7011692000e-3, -4.6874897000e-3, & + 365.0, -4.2401039000, 2.6963742000e-3, -4.6793627000e-3, & + 366.0, -4.2445234000, 2.6916175000e-3, -4.6712838000e-3, & + 367.0, -4.2489380000, 2.6868986000e-3, -4.6632526000e-3, & + 368.0, -4.2533476000, 2.6822172000e-3, -4.6552684000e-3, & + 369.0, -4.2577524000, 2.6775728000e-3, -4.6473310000e-3, & + 370.0, -4.2621522000, 2.6729652000e-3, -4.6394397000e-3, & + 371.0, -4.2665470000, 2.6683940000e-3, -4.6315942000e-3, & + 372.0, -4.2709369000, 2.6638587000e-3, -4.6237940000e-3, & + 373.0, -4.2753218000, 2.6593590000e-3, -4.6160387000e-3, & + 374.0, -4.2797016000, 2.6548946000e-3, -4.6083277000e-3, & + 375.0, -4.2840764000, 2.6504651000e-3, -4.6006607000e-3, & + 376.0, -4.2884462000, 2.6460701000e-3, -4.5930373000e-3, & + 377.0, -4.2928108000, 2.6417093000e-3, -4.5854569000e-3, & + 378.0, -4.2971704000, 2.6373823000e-3, -4.5779192000e-3, & + 379.0, -4.3015249000, 2.6330888000e-3, -4.5704238000e-3, & + 380.0, -4.3058742000, 2.6288285000e-3, -4.5629702000e-3, & + 381.0, -4.3102184000, 2.6246011000e-3, -4.5555581000e-3, & + 382.0, -4.3145575000, 2.6204061000e-3, -4.5481870000e-3, & + 383.0, -4.3188914000, 2.6162432000e-3, -4.5408565000e-3, & + 384.0, -4.3232200000, 2.6121122000e-3, -4.5335663000e-3, & + 385.0, -4.3275435000, 2.6080128000e-3, -4.5263159000e-3, & + 386.0, -4.3318617000, 2.6039445000e-3, -4.5191050000e-3, & + 387.0, -4.3361747000, 2.5999071000e-3, -4.5119331000e-3, & + 388.0, -4.3404824000, 2.5959002000e-3, -4.5048000000e-3, & + 389.0, -4.3447848000, 2.5919236000e-3, -4.4977052000e-3, & + 390.0, -4.3490820000, 2.5879770000e-3, -4.4906484000e-3, & + 391.0, -4.3533738000, 2.5840600000e-3, -4.4836292000e-3, & + 392.0, -4.3576603000, 2.5801724000e-3, -4.4766472000e-3, & + 393.0, -4.3619414000, 2.5763138000e-3, -4.4697021000e-3, & + 394.0, -4.3662172000, 2.5724840000e-3, -4.4627935000e-3, & + 395.0, -4.3704876000, 2.5686827000e-3, -4.4559212000e-3, & + 396.0, -4.3747527000, 2.5649095000e-3, -4.4490846000e-3, & + 397.0, -4.3790123000, 2.5611642000e-3, -4.4422836000e-3, & + 398.0, -4.3832665000, 2.5574466000e-3, -4.4355178000e-3, & + 399.0, -4.3875152000, 2.5537563000e-3, -4.4287868000e-3, & + 400.0, -4.3917586000, 2.5500930000e-3, -4.4220903000e-3, & + 401.0, -4.3959964000, 2.5464565000e-3, -4.4154280000e-3, & + 402.0, -4.4002288000, 2.5428466000e-3, -4.4087995000e-3, & + 403.0, -4.4044556000, 2.5392629000e-3, -4.4022046000e-3, & + 404.0, -4.4086770000, 2.5357051000e-3, -4.3956430000e-3, & + 405.0, -4.4128928000, 2.5321731000e-3, -4.3891142000e-3, & + 406.0, -4.4171031000, 2.5286666000e-3, -4.3826181000e-3, & + 407.0, -4.4213078000, 2.5251852000e-3, -4.3761543000e-3, & + 408.0, -4.4255070000, 2.5217289000e-3, -4.3697225000e-3, & + 409.0, -4.4297006000, 2.5182972000e-3, -4.3633224000e-3, & + 410.0, -4.4338886000, 2.5148899000e-3, -4.3569537000e-3, & + 411.0, -4.4380709000, 2.5115069000e-3, -4.3506162000e-3, & + 412.0, -4.4422477000, 2.5081478000e-3, -4.3443095000e-3, & + 413.0, -4.4464188000, 2.5048125000e-3, -4.3380334000e-3, & + 414.0, -4.4505843000, 2.5015006000e-3, -4.3317876000e-3, & + 415.0, -4.4547441000, 2.4982119000e-3, -4.3255718000e-3, & + 416.0, -4.4588982000, 2.4949463000e-3, -4.3193857000e-3, & + 417.0, -4.4630466000, 2.4917034000e-3, -4.3132290000e-3, & + 418.0, -4.4671894000, 2.4884831000e-3, -4.3071016000e-3, & + 419.0, -4.4713264000, 2.4852851000e-3, -4.3010031000e-3, & + 420.0, -4.4754577000, 2.4821092000e-3, -4.2949332000e-3, & + 421.0, -4.4795832000, 2.4789551000e-3, -4.2888918000e-3, & + 422.0, -4.4837030000, 2.4758227000e-3, -4.2828785000e-3, & + 423.0, -4.4878171000, 2.4727118000e-3, -4.2768931000e-3, & + 424.0, -4.4919253000, 2.4696220000e-3, -4.2709353000e-3, & + 425.0, -4.4960278000, 2.4665532000e-3, -4.2650050000e-3, & + 426.0, -4.5001245000, 2.4635053000e-3, -4.2591017000e-3, & + 427.0, -4.5042153000, 2.4604778000e-3, -4.2532254000e-3, & + 428.0, -4.5083003000, 2.4574708000e-3, -4.2473758000e-3, & + 429.0, -4.5123795000, 2.4544839000e-3, -4.2415526000e-3, & + 430.0, -4.5164529000, 2.4515170000e-3, -4.2357555000e-3, & + 431.0, -4.5205204000, 2.4485699000e-3, -4.2299844000e-3, & + 432.0, -4.5245820000, 2.4456423000e-3, -4.2242391000e-3, & + 433.0, -4.5286377000, 2.4427340000e-3, -4.2185193000e-3, & + 434.0, -4.5326876000, 2.4398450000e-3, -4.2128247000e-3, & + 435.0, -4.5367315000, 2.4369749000e-3, -4.2071552000e-3, & + 436.0, -4.5407695000, 2.4341235000e-3, -4.2015105000e-3, & + 437.0, -4.5448016000, 2.4312908000e-3, -4.1958904000e-3, & + 438.0, -4.5488278000, 2.4284765000e-3, -4.1902947000e-3, & + 439.0, -4.5528480000, 2.4256804000e-3, -4.1847233000e-3, & + 440.0, -4.5568623000, 2.4229023000e-3, -4.1791757000e-3, & + 441.0, -4.5608706000, 2.4201420000e-3, -4.1736520000e-3, & + 442.0, -4.5648729000, 2.4173995000e-3, -4.1681518000e-3, & + 443.0, -4.5688693000, 2.4146744000e-3, -4.1626750000e-3, & + 444.0, -4.5728596000, 2.4119666000e-3, -4.1572213000e-3, & + 445.0, -4.5768440000, 2.4092760000e-3, -4.1517905000e-3, & + 446.0, -4.5808223000, 2.4066023000e-3, -4.1463825000e-3, & + 447.0, -4.5847946000, 2.4039454000e-3, -4.1409971000e-3, & + 448.0, -4.5887608000, 2.4013051000e-3, -4.1356340000e-3, & + 449.0, -4.5927211000, 2.3986813000e-3, -4.1302931000e-3, & + 450.0, -4.5966752000, 2.3960738000e-3, -4.1249742000e-3, & + 451.0, -4.6006234000, 2.3934824000e-3, -4.1196771000e-3, & + 452.0, -4.6045654000, 2.3909070000e-3, -4.1144015000e-3, & + 453.0, -4.6085014000, 2.3883473000e-3, -4.1091474000e-3, & + 454.0, -4.6124313000, 2.3858033000e-3, -4.1039146000e-3, & + 455.0, -4.6163550000, 2.3832748000e-3, -4.0987028000e-3, & + 456.0, -4.6202727000, 2.3807615000e-3, -4.0935118000e-3, & + 457.0, -4.6241843000, 2.3782635000e-3, -4.0883416000e-3, & + 458.0, -4.6280897000, 2.3757804000e-3, -4.0831919000e-3, & + 459.0, -4.6319890000, 2.3733122000e-3, -4.0780626000e-3, & + 460.0, -4.6358822000, 2.3708588000e-3, -4.0729534000e-3, & + 461.0, -4.6397692000, 2.3684198000e-3, -4.0678643000e-3, & + 462.0, -4.6436501000, 2.3659953000e-3, -4.0627950000e-3, & + 463.0, -4.6475249000, 2.3635851000e-3, -4.0577454000e-3, & + 464.0, -4.6513934000, 2.3611889000e-3, -4.0527153000e-3, & + 465.0, -4.6552558000, 2.3588068000e-3, -4.0477046000e-3, & + 466.0, -4.6591120000, 2.3564384000e-3, -4.0427131000e-3, & + 467.0, -4.6629620000, 2.3540838000e-3, -4.0377406000e-3, & + 468.0, -4.6668058000, 2.3517427000e-3, -4.0327870000e-3, & + 469.0, -4.6706434000, 2.3494150000e-3, -4.0278521000e-3, & + 470.0, -4.6744748000, 2.3471006000e-3, -4.0229358000e-3, & + 471.0, -4.6783000000, 2.3447994000e-3, -4.0180379000e-3, & + 472.0, -4.6821189000, 2.3425111000e-3, -4.0131582000e-3, & + 473.0, -4.6859316000, 2.3402357000e-3, -4.0082967000e-3, & + 474.0, -4.6897381000, 2.3379731000e-3, -4.0034532000e-3, & + 475.0, -4.6935383000, 2.3357231000e-3, -3.9986274000e-3, & + 476.0, -4.6973323000, 2.3334855000e-3, -3.9938194000e-3, & + 477.0, -4.7011201000, 2.3312604000e-3, -3.9890289000e-3, & + 478.0, -4.7049015000, 2.3290474000e-3, -3.9842557000e-3, & + 479.0, -4.7086767000, 2.3268466000e-3, -3.9794999000e-3, & + 480.0, -4.7124456000, 2.3246577000e-3, -3.9747611000e-3, & + 481.0, -4.7162083000, 2.3224807000e-3, -3.9700393000e-3, & + 482.0, -4.7199646000, 2.3203154000e-3, -3.9653344000e-3, & + 483.0, -4.7237147000, 2.3181618000e-3, -3.9606461000e-3, & + 484.0, -4.7274585000, 2.3160196000e-3, -3.9559744000e-3, & + 485.0, -4.7311959000, 2.3138889000e-3, -3.9513192000e-3, & + 486.0, -4.7349271000, 2.3117694000e-3, -3.9466802000e-3, & + 487.0, -4.7386519000, 2.3096610000e-3, -3.9420575000e-3, & + 488.0, -4.7423704000, 2.3075637000e-3, -3.9374508000e-3, & + 489.0, -4.7460826000, 2.3054773000e-3, -3.9328600000e-3, & + 490.0, -4.7497885000, 2.3034017000e-3, -3.9282850000e-3, & + 491.0, -4.7534880000, 2.3013368000e-3, -3.9237256000e-3, & + 492.0, -4.7571812000, 2.2992825000e-3, -3.9191818000e-3, & + 493.0, -4.7608681000, 2.2972386000e-3, -3.9146535000e-3, & + 494.0, -4.7645486000, 2.2952052000e-3, -3.9101404000e-3, & + 495.0, -4.7682227000, 2.2931820000e-3, -3.9056425000e-3, & + 496.0, -4.7718905000, 2.2911690000e-3, -3.9011597000e-3, & + 497.0, -4.7755520000, 2.2891660000e-3, -3.8966919000e-3, & + 498.0, -4.7792071000, 2.2871729000e-3, -3.8922389000e-3, & + 499.0, -4.7828558000, 2.2851898000e-3, -3.8878005000e-3, & + 500.0, -4.7864981000, 2.2832163000e-3, -3.8833768000e-3, & + 501.0, -4.7901341000, 2.2812525000e-3, -3.8789676000e-3, & + 502.0, -4.7937636000, 2.2792983000e-3, -3.8745728000e-3, & + 503.0, -4.7973868000, 2.2773535000e-3, -3.8701922000e-3, & + 504.0, -4.8010036000, 2.2754180000e-3, -3.8658258000e-3, & + 505.0, -4.8046141000, 2.2734918000e-3, -3.8614735000e-3, & + 506.0, -4.8082181000, 2.2715748000e-3, -3.8571351000e-3, & + 507.0, -4.8118157000, 2.2696668000e-3, -3.8528105000e-3, & + 508.0, -4.8154069000, 2.2677678000e-3, -3.8484997000e-3, & + 509.0, -4.8189918000, 2.2658777000e-3, -3.8442025000e-3, & + 510.0, -4.8225702000, 2.2639964000e-3, -3.8399188000e-3, & + 511.0, -4.8261422000, 2.2621237000e-3, -3.8356485000e-3, & + 512.0, -4.8297078000, 2.2602597000e-3, -3.8313916000e-3, & + 513.0, -4.8332670000, 2.2584041000e-3, -3.8271479000e-3, & + 514.0, -4.8368197000, 2.2565570000e-3, -3.8229173000e-3, & + 515.0, -4.8403661000, 2.2547183000e-3, -3.8186997000e-3, & + 516.0, -4.8439060000, 2.2528877000e-3, -3.8144951000e-3, & + 517.0, -4.8474395000, 2.2510654000e-3, -3.8103033000e-3, & + 518.0, -4.8509666000, 2.2492511000e-3, -3.8061243000e-3, & + 519.0, -4.8544872000, 2.2474448000e-3, -3.8019578000e-3, & + 520.0, -4.8580014000, 2.2456465000e-3, -3.7978040000e-3, & + 521.0, -4.8615092000, 2.2438560000e-3, -3.7936626000e-3, & + 522.0, -4.8650105000, 2.2420732000e-3, -3.7895335000e-3, & + 523.0, -4.8685054000, 2.2402981000e-3, -3.7854168000e-3, & + 524.0, -4.8719939000, 2.2385305000e-3, -3.7813122000e-3, & + 525.0, -4.8754759000, 2.2367705000e-3, -3.7772197000e-3, & + 526.0, -4.8789515000, 2.2350179000e-3, -3.7731392000e-3, & + 527.0, -4.8824206000, 2.2332727000e-3, -3.7690706000e-3, & + 528.0, -4.8858833000, 2.2315347000e-3, -3.7650139000e-3, & + 529.0, -4.8893395000, 2.2298040000e-3, -3.7609689000e-3, & + 530.0, -4.8927893000, 2.2280804000e-3, -3.7569356000e-3, & + 531.0, -4.8962327000, 2.2263638000e-3, -3.7529139000e-3, & + 532.0, -4.8996696000, 2.2246542000e-3, -3.7489037000e-3, & + 533.0, -4.9031000000, 2.2229515000e-3, -3.7449049000e-3, & + 534.0, -4.9065240000, 2.2212556000e-3, -3.7409174000e-3, & + 535.0, -4.9099415000, 2.2195665000e-3, -3.7369411000e-3, & + 536.0, -4.9133526000, 2.2178841000e-3, -3.7329761000e-3, & + 537.0, -4.9167573000, 2.2162082000e-3, -3.7290221000e-3, & + 538.0, -4.9201554000, 2.2145390000e-3, -3.7250792000e-3, & + 539.0, -4.9235472000, 2.2128762000e-3, -3.7211471000e-3, & + 540.0, -4.9269324000, 2.2112198000e-3, -3.7172260000e-3, & + 541.0, -4.9303112000, 2.2095698000e-3, -3.7133156000e-3, & + 542.0, -4.9336836000, 2.2079261000e-3, -3.7094160000e-3, & + 543.0, -4.9370495000, 2.2062885000e-3, -3.7055269000e-3, & + 544.0, -4.9404089000, 2.2046571000e-3, -3.7016485000e-3, & + 545.0, -4.9437619000, 2.2030318000e-3, -3.6977805000e-3, & + 546.0, -4.9471084000, 2.2014125000e-3, -3.6939229000e-3, & + 547.0, -4.9504485000, 2.1997991000e-3, -3.6900757000e-3, & + 548.0, -4.9537821000, 2.1981917000e-3, -3.6862387000e-3, & + 549.0, -4.9571092000, 2.1965901000e-3, -3.6824120000e-3, & + 550.0, -4.9604299000, 2.1949942000e-3, -3.6785954000e-3, & + 551.0, -4.9637442000, 2.1934040000e-3, -3.6747888000e-3, & + 552.0, -4.9670519000, 2.1918195000e-3, -3.6709922000e-3, & + 553.0, -4.9703533000, 2.1902406000e-3, -3.6672056000e-3, & + 554.0, -4.9736481000, 2.1886671000e-3, -3.6634288000e-3, & + 555.0, -4.9769366000, 2.1870992000e-3, -3.6596618000e-3, & + 556.0, -4.9802185000, 2.1855366000e-3, -3.6559045000e-3, & + 557.0, -4.9834940000, 2.1839795000e-3, -3.6521569000e-3, & + 558.0, -4.9867631000, 2.1824276000e-3, -3.6484189000e-3, & + 559.0, -4.9900257000, 2.1808809000e-3, -3.6446904000e-3, & + 560.0, -4.9932819000, 2.1793394000e-3, -3.6409714000e-3, & + 561.0, -4.9965316000, 2.1778031000e-3, -3.6372617000e-3, & + 562.0, -4.9997749000, 2.1762718000e-3, -3.6335615000e-3, & + 563.0, -5.0030117000, 2.1747455000e-3, -3.6298704000e-3, & + 564.0, -5.0062421000, 2.1732242000e-3, -3.6261887000e-3, & + 565.0, -5.0094660000, 2.1717078000e-3, -3.6225160000e-3, & + 566.0, -5.0126835000, 2.1701963000e-3, -3.6188525000e-3, & + 567.0, -5.0158946000, 2.1686895000e-3, -3.6151980000e-3, & + 568.0, -5.0190992000, 2.1671876000e-3, -3.6115525000e-3, & + 569.0, -5.0222974000, 2.1656903000e-3, -3.6079159000e-3, & + 570.0, -5.0254891000, 2.1641977000e-3, -3.6042882000e-3, & + 571.0, -5.0286744000, 2.1627096000e-3, -3.6006692000e-3, & + 572.0, -5.0318533000, 2.1612262000e-3, -3.5970590000e-3, & + 573.0, -5.0350258000, 2.1597472000e-3, -3.5934575000e-3, & + 574.0, -5.0381918000, 2.1582727000e-3, -3.5898647000e-3, & + 575.0, -5.0413514000, 2.1568026000e-3, -3.5862804000e-3, & + 576.0, -5.0445046000, 2.1553369000e-3, -3.5827047000e-3, & + 577.0, -5.0476514000, 2.1538755000e-3, -3.5791374000e-3, & + 578.0, -5.0507917000, 2.1524183000e-3, -3.5755785000e-3, & + 579.0, -5.0539256000, 2.1509654000e-3, -3.5720280000e-3, & + 580.0, -5.0570532000, 2.1495166000e-3, -3.5684858000e-3, & + 581.0, -5.0601743000, 2.1480720000e-3, -3.5649519000e-3, & + 582.0, -5.0632890000, 2.1466315000e-3, -3.5614262000e-3, & + 583.0, -5.0663973000, 2.1451950000e-3, -3.5579086000e-3, & + 584.0, -5.0694991000, 2.1437625000e-3, -3.5543992000e-3, & + 585.0, -5.0725946000, 2.1423339000e-3, -3.5508978000e-3, & + 586.0, -5.0756837000, 2.1409093000e-3, -3.5474044000e-3, & + 587.0, -5.0787664000, 2.1394885000e-3, -3.5439189000e-3, & + 588.0, -5.0818427000, 2.1380716000e-3, -3.5404414000e-3, & + 589.0, -5.0849126000, 2.1366585000e-3, -3.5369717000e-3, & + 590.0, -5.0879762000, 2.1352491000e-3, -3.5335099000e-3, & + 591.0, -5.0910333000, 2.1338434000e-3, -3.5300557000e-3, & + 592.0, -5.0940841000, 2.1324413000e-3, -3.5266094000e-3, & + 593.0, -5.0971285000, 2.1310429000e-3, -3.5231706000e-3, & + 594.0, -5.1001665000, 2.1296481000e-3, -3.5197395000e-3, & + 595.0, -5.1031982000, 2.1282569000e-3, -3.5163160000e-3, & + 596.0, -5.1062234000, 2.1268691000e-3, -3.5129000000e-3, & + 597.0, -5.1092424000, 2.1254848000e-3, -3.5094915000e-3, & + 598.0, -5.1122549000, 2.1241039000e-3, -3.5060904000e-3, & + 599.0, -5.1152611000, 2.1227265000e-3, -3.5026968000e-3, & + 600.0, -5.1182610000, 2.1213524000e-3, -3.4993105000e-3, & + 601.0, -5.1212545000, 2.1199816000e-3, -3.4959315000e-3, & + 602.0, -5.1242417000, 2.1186141000e-3, -3.4925597000e-3, & + 603.0, -5.1272225000, 2.1172498000e-3, -3.4891952000e-3, & + 604.0, -5.1301970000, 2.1158888000e-3, -3.4858379000e-3, & + 605.0, -5.1331651000, 2.1145309000e-3, -3.4824877000e-3, & + 606.0, -5.1361270000, 2.1131762000e-3, -3.4791445000e-3, & + 607.0, -5.1390825000, 2.1118246000e-3, -3.4758085000e-3, & + 608.0, -5.1420316000, 2.1104761000e-3, -3.4724795000e-3, & + 609.0, -5.1449745000, 2.1091306000e-3, -3.4691574000e-3, & + 610.0, -5.1479111000, 2.1077881000e-3, -3.4658423000e-3, & + 611.0, -5.1508413000, 2.1064486000e-3, -3.4625341000e-3, & + 612.0, -5.1537652000, 2.1051120000e-3, -3.4592327000e-3, & + 613.0, -5.1566829000, 2.1037784000e-3, -3.4559381000e-3, & + 614.0, -5.1595942000, 2.1024476000e-3, -3.4526504000e-3, & + 615.0, -5.1624993000, 2.1011196000e-3, -3.4493693000e-3, & + 616.0, -5.1653981000, 2.0997945000e-3, -3.4460950000e-3, & + 617.0, -5.1682905000, 2.0984722000e-3, -3.4428273000e-3, & + 618.0, -5.1711768000, 2.0971526000e-3, -3.4395663000e-3, & + 619.0, -5.1740567000, 2.0958358000e-3, -3.4363118000e-3, & + 620.0, -5.1769304000, 2.0945216000e-3, -3.4330639000e-3, & + 621.0, -5.1797978000, 2.0932101000e-3, -3.4298226000e-3, & + 622.0, -5.1826589000, 2.0919012000e-3, -3.4265877000e-3, & + 623.0, -5.1855138000, 2.0905950000e-3, -3.4233592000e-3, & + 624.0, -5.1883625000, 2.0892913000e-3, -3.4201372000e-3, & + 625.0, -5.1912049000, 2.0879902000e-3, -3.4169215000e-3, & + 626.0, -5.1940410000, 2.0866915000e-3, -3.4137122000e-3, & + 627.0, -5.1968710000, 2.0853954000e-3, -3.4105092000e-3, & + 628.0, -5.1996947000, 2.0841018000e-3, -3.4073124000e-3, & + 629.0, -5.2025121000, 2.0828105000e-3, -3.4041219000e-3, & + 630.0, -5.2053234000, 2.0815217000e-3, -3.4009376000e-3, & + 631.0, -5.2081285000, 2.0802353000e-3, -3.3977595000e-3, & + 632.0, -5.2109273000, 2.0789512000e-3, -3.3945875000e-3, & + 633.0, -5.2137199000, 2.0776695000e-3, -3.3914216000e-3, & + 634.0, -5.2165064000, 2.0763900000e-3, -3.3882618000e-3, & + 635.0, -5.2192866000, 2.0751129000e-3, -3.3851080000e-3, & + 636.0, -5.2220607000, 2.0738380000e-3, -3.3819602000e-3, & + 637.0, -5.2248286000, 2.0725653000e-3, -3.3788184000e-3, & + 638.0, -5.2275903000, 2.0712949000e-3, -3.3756826000e-3, & + 639.0, -5.2303458000, 2.0700266000e-3, -3.3725527000e-3, & + 640.0, -5.2330952000, 2.0687604000e-3, -3.3694286000e-3, & + 641.0, -5.2358384000, 2.0674964000e-3, -3.3663104000e-3, & + 642.0, -5.2385755000, 2.0662346000e-3, -3.3631981000e-3, & + 643.0, -5.2413064000, 2.0649747000e-3, -3.3600915000e-3, & + 644.0, -5.2440312000, 2.0637170000e-3, -3.3569907000e-3, & + 645.0, -5.2467498000, 2.0624613000e-3, -3.3538957000e-3, & + 646.0, -5.2494624000, 2.0612076000e-3, -3.3508063000e-3, & + 647.0, -5.2521688000, 2.0599559000e-3, -3.3477227000e-3, & + 648.0, -5.2548690000, 2.0587062000e-3, -3.3446446000e-3, & + 649.0, -5.2575632000, 2.0574585000e-3, -3.3415722000e-3, & + 650.0, -5.2602513000, 2.0562126000e-3, -3.3385054000e-3, & + 651.0, -5.2629332000, 2.0549687000e-3, -3.3354442000e-3, & + 652.0, -5.2656091000, 2.0537266000e-3, -3.3323885000e-3, & + 653.0, -5.2682789000, 2.0524865000e-3, -3.3293383000e-3, & + 654.0, -5.2709426000, 2.0512481000e-3, -3.3262936000e-3, & + 655.0, -5.2736002000, 2.0500116000e-3, -3.3232543000e-3, & + 656.0, -5.2762518000, 2.0487769000e-3, -3.3202205000e-3, & + 657.0, -5.2788973000, 2.0475440000e-3, -3.3171921000e-3, & + 658.0, -5.2815367000, 2.0463128000e-3, -3.3141691000e-3, & + 659.0, -5.2841701000, 2.0450834000e-3, -3.3111514000e-3, & + 660.0, -5.2867975000, 2.0438557000e-3, -3.3081390000e-3, & + 661.0, -5.2894188000, 2.0426297000e-3, -3.3051319000e-3, & + 662.0, -5.2920341000, 2.0414054000e-3, -3.3021302000e-3, & + 663.0, -5.2946433000, 2.0401828000e-3, -3.2991336000e-3, & + 664.0, -5.2972466000, 2.0389618000e-3, -3.2961423000e-3, & + 665.0, -5.2998438000, 2.0377425000e-3, -3.2931562000e-3, & + 666.0, -5.3024350000, 2.0365247000e-3, -3.2901753000e-3, & + 667.0, -5.3050203000, 2.0353086000e-3, -3.2871995000e-3, & + 668.0, -5.3075995000, 2.0340940000e-3, -3.2842288000e-3, & + 669.0, -5.3101728000, 2.0328810000e-3, -3.2812633000e-3, & + 670.0, -5.3127401000, 2.0316695000e-3, -3.2783028000e-3, & + 671.0, -5.3153014000, 2.0304596000e-3, -3.2753474000e-3, & + 672.0, -5.3178568000, 2.0292512000e-3, -3.2723970000e-3, & + 673.0, -5.3204062000, 2.0280443000e-3, -3.2694517000e-3, & + 674.0, -5.3229496000, 2.0268388000e-3, -3.2665113000e-3, & + 675.0, -5.3254871000, 2.0256348000e-3, -3.2635759000e-3, & + 676.0, -5.3280187000, 2.0244322000e-3, -3.2606454000e-3, & + 677.0, -5.3305444000, 2.0232311000e-3, -3.2577199000e-3, & + 678.0, -5.3330641000, 2.0220314000e-3, -3.2547992000e-3, & + 679.0, -5.3355779000, 2.0208331000e-3, -3.2518834000e-3, & + 680.0, -5.3380858000, 2.0196361000e-3, -3.2489725000e-3, & + 681.0, -5.3405878000, 2.0184406000e-3, -3.2460664000e-3, & + 682.0, -5.3430840000, 2.0172463000e-3, -3.2431652000e-3, & + 683.0, -5.3455742000, 2.0160534000e-3, -3.2402687000e-3, & + 684.0, -5.3480585000, 2.0148619000e-3, -3.2373770000e-3, & + 685.0, -5.3505370000, 2.0136716000e-3, -3.2344900000e-3, & + 686.0, -5.3530097000, 2.0124826000e-3, -3.2316078000e-3, & + 687.0, -5.3554764000, 2.0112949000e-3, -3.2287303000e-3, & + 688.0, -5.3579373000, 2.0101085000e-3, -3.2258574000e-3, & + 689.0, -5.3603924000, 2.0089233000e-3, -3.2229893000e-3, & + 690.0, -5.3628417000, 2.0077394000e-3, -3.2201258000e-3, & + 691.0, -5.3652851000, 2.0065567000e-3, -3.2172669000e-3, & + 692.0, -5.3677227000, 2.0053752000e-3, -3.2144126000e-3, & + 693.0, -5.3701545000, 2.0041948000e-3, -3.2115629000e-3, & + 694.0, -5.3725805000, 2.0030157000e-3, -3.2087178000e-3, & + 695.0, -5.3750006000, 2.0018377000e-3, -3.2058772000e-3, & + 696.0, -5.3774150000, 2.0006609000e-3, -3.2030412000e-3, & + 697.0, -5.3798237000, 1.9994853000e-3, -3.2002097000e-3, & + 698.0, -5.3822265000, 1.9983108000e-3, -3.1973826000e-3, & + 699.0, -5.3846236000, 1.9971373000e-3, -3.1945601000e-3, & + 700.0, -5.3870149000, 1.9959650000e-3, -3.1917420000e-3, & + 701.0, -5.3894005000, 1.9947938000e-3, -3.1889283000e-3, & + 702.0, -5.3917803000, 1.9936237000e-3, -3.1861191000e-3, & + 703.0, -5.3941544000, 1.9924547000e-3, -3.1833143000e-3, & + 704.0, -5.3965228000, 1.9912867000e-3, -3.1805139000e-3, & + 705.0, -5.3988854000, 1.9901198000e-3, -3.1777178000e-3, & + 706.0, -5.4012423000, 1.9889539000e-3, -3.1749261000e-3, & + 707.0, -5.4035936000, 1.9877890000e-3, -3.1721387000e-3, & + 708.0, -5.4059391000, 1.9866252000e-3, -3.1693556000e-3, & + 709.0, -5.4082790000, 1.9854623000e-3, -3.1665769000e-3, & + 710.0, -5.4106131000, 1.9843005000e-3, -3.1638024000e-3, & + 711.0, -5.4129416000, 1.9831396000e-3, -3.1610322000e-3, & + 712.0, -5.4152645000, 1.9819797000e-3, -3.1582662000e-3, & + 713.0, -5.4175816000, 1.9808208000e-3, -3.1555045000e-3, & + 714.0, -5.4198932000, 1.9796628000e-3, -3.1527469000e-3, & + 715.0, -5.4221991000, 1.9785058000e-3, -3.1499936000e-3, & + 716.0, -5.4244993000, 1.9773497000e-3, -3.1472445000e-3, & + 717.0, -5.4267939000, 1.9761945000e-3, -3.1444995000e-3, & + 718.0, -5.4290830000, 1.9750402000e-3, -3.1417587000e-3, & + 719.0, -5.4313664000, 1.9738869000e-3, -3.1390221000e-3, & + 720.0, -5.4336442000, 1.9727344000e-3, -3.1362895000e-3, & + 721.0, -5.4359164000, 1.9715828000e-3, -3.1335611000e-3, & + 722.0, -5.4381830000, 1.9704321000e-3, -3.1308367000e-3, & + 723.0, -5.4404441000, 1.9692823000e-3, -3.1281164000e-3, & + 724.0, -5.4426996000, 1.9681333000e-3, -3.1254002000e-3, & + 725.0, -5.4449495000, 1.9669852000e-3, -3.1226881000e-3, & + 726.0, -5.4471939000, 1.9658379000e-3, -3.1199799000e-3, & + 727.0, -5.4494328000, 1.9646915000e-3, -3.1172758000e-3, & + 728.0, -5.4516661000, 1.9635458000e-3, -3.1145757000e-3, & + 729.0, -5.4538938000, 1.9624010000e-3, -3.1118796000e-3, & + 730.0, -5.4561161000, 1.9612570000e-3, -3.1091874000e-3, & + 731.0, -5.4583329000, 1.9601138000e-3, -3.1064992000e-3, & + 732.0, -5.4605441000, 1.9589714000e-3, -3.1038149000e-3, & + 733.0, -5.4627499000, 1.9578298000e-3, -3.1011346000e-3, & + 734.0, -5.4649502000, 1.9566889000e-3, -3.0984582000e-3, & + 735.0, -5.4671450000, 1.9555488000e-3, -3.0957857000e-3, & + 736.0, -5.4693343000, 1.9544095000e-3, -3.0931171000e-3, & + 737.0, -5.4715182000, 1.9532709000e-3, -3.0904524000e-3, & + 738.0, -5.4736966000, 1.9521331000e-3, -3.0877915000e-3, & + 739.0, -5.4758696000, 1.9509960000e-3, -3.0851345000e-3, & + 740.0, -5.4780372000, 1.9498596000e-3, -3.0824813000e-3, & + 741.0, -5.4801993000, 1.9487240000e-3, -3.0798319000e-3, & + 742.0, -5.4823560000, 1.9475891000e-3, -3.0771864000e-3, & + 743.0, -5.4845073000, 1.9464549000e-3, -3.0745446000e-3, & + 744.0, -5.4866533000, 1.9453214000e-3, -3.0719066000e-3, & + 745.0, -5.4887938000, 1.9441885000e-3, -3.0692724000e-3, & + 746.0, -5.4909289000, 1.9430564000e-3, -3.0666420000e-3, & + 747.0, -5.4930587000, 1.9419250000e-3, -3.0640153000e-3, & + 748.0, -5.4951831000, 1.9407942000e-3, -3.0613923000e-3, & + 749.0, -5.4973021000, 1.9396641000e-3, -3.0587731000e-3, & + 750.0, -5.4994158000, 1.9385347000e-3, -3.0561575000e-3, & + 751.0, -5.5015242000, 1.9374059000e-3, -3.0535457000e-3, & + 752.0, -5.5036272000, 1.9362778000e-3, -3.0509375000e-3, & + 753.0, -5.5057250000, 1.9351503000e-3, -3.0483331000e-3, & + 754.0, -5.5078174000, 1.9340235000e-3, -3.0457322000e-3, & + 755.0, -5.5099044000, 1.9328973000e-3, -3.0431351000e-3, & + 756.0, -5.5119863000, 1.9317717000e-3, -3.0405415000e-3, & + 757.0, -5.5140628000, 1.9306468000e-3, -3.0379516000e-3, & + 758.0, -5.5161340000, 1.9295224000e-3, -3.0353653000e-3, & + 759.0, -5.5182000000, 1.9283987000e-3, -3.0327826000e-3, & + 760.0, -5.5202607000, 1.9272756000e-3, -3.0302035000e-3, & + 761.0, -5.5223161000, 1.9261531000e-3, -3.0276280000e-3, & + 762.0, -5.5243664000, 1.9250311000e-3, -3.0250561000e-3, & + 763.0, -5.5264113000, 1.9239098000e-3, -3.0224877000e-3, & + 764.0, -5.5284511000, 1.9227890000e-3, -3.0199228000e-3, & + 765.0, -5.5304856000, 1.9216689000e-3, -3.0173615000e-3, & + 766.0, -5.5325150000, 1.9205493000e-3, -3.0148038000e-3, & + 767.0, -5.5345391000, 1.9194303000e-3, -3.0122495000e-3, & + 768.0, -5.5365581000, 1.9183118000e-3, -3.0096987000e-3, & + 769.0, -5.5385718000, 1.9171939000e-3, -3.0071515000e-3, & + 770.0, -5.5405804000, 1.9160766000e-3, -3.0046077000e-3, & + 771.0, -5.5425839000, 1.9149598000e-3, -3.0020674000e-3, & + 772.0, -5.5445822000, 1.9138435000e-3, -2.9995305000e-3, & + 773.0, -5.5465753000, 1.9127278000e-3, -2.9969971000e-3, & + 774.0, -5.5485633000, 1.9116127000e-3, -2.9944671000e-3, & + 775.0, -5.5505462000, 1.9104981000e-3, -2.9919406000e-3, & + 776.0, -5.5525239000, 1.9093840000e-3, -2.9894175000e-3, & + 777.0, -5.5544966000, 1.9082704000e-3, -2.9868978000e-3, & + 778.0, -5.5564641000, 1.9071573000e-3, -2.9843815000e-3, & + 779.0, -5.5584266000, 1.9060448000e-3, -2.9818686000e-3, & + 780.0, -5.5603840000, 1.9049328000e-3, -2.9793591000e-3, & + 781.0, -5.5623363000, 1.9038213000e-3, -2.9768530000e-3, & + 782.0, -5.5642835000, 1.9027103000e-3, -2.9743502000e-3, & + 783.0, -5.5662257000, 1.9015998000e-3, -2.9718507000e-3, & + 784.0, -5.5681628000, 1.9004898000e-3, -2.9693547000e-3, & + 785.0, -5.5700949000, 1.8993803000e-3, -2.9668619000e-3, & + 786.0, -5.5720220000, 1.8982713000e-3, -2.9643725000e-3, & + 787.0, -5.5739440000, 1.8971627000e-3, -2.9618864000e-3, & + 788.0, -5.5758611000, 1.8960547000e-3, -2.9594036000e-3, & + 789.0, -5.5777731000, 1.8949471000e-3, -2.9569240000e-3, & + 790.0, -5.5796802000, 1.8938401000e-3, -2.9544478000e-3, & + 791.0, -5.5815822000, 1.8927334000e-3, -2.9519749000e-3, & + 792.0, -5.5834793000, 1.8916273000e-3, -2.9495052000e-3, & + 793.0, -5.5853715000, 1.8905216000e-3, -2.9470388000e-3, & + 794.0, -5.5872586000, 1.8894164000e-3, -2.9445756000e-3, & + 795.0, -5.5891409000, 1.8883117000e-3, -2.9421157000e-3, & + 796.0, -5.5910182000, 1.8872074000e-3, -2.9396591000e-3, & + 797.0, -5.5928905000, 1.8861036000e-3, -2.9372056000e-3, & + 798.0, -5.5947580000, 1.8850002000e-3, -2.9347554000e-3, & + 799.0, -5.5966205000, 1.8838973000e-3, -2.9323083000e-3, & + 800.0, -5.5984781000, 1.8827948000e-3, -2.9298645000e-3, & + 801.0, -5.6003309000, 1.8816928000e-3, -2.9274239000e-3, & + 802.0, -5.6021787000, 1.8805912000e-3, -2.9249864000e-3, & + 803.0, -5.6040217000, 1.8794901000e-3, -2.9225522000e-3, & + 804.0, -5.6058598000, 1.8783894000e-3, -2.9201211000e-3, & + 805.0, -5.6076931000, 1.8772891000e-3, -2.9176931000e-3, & + 806.0, -5.6095215000, 1.8761892000e-3, -2.9152683000e-3, & + 807.0, -5.6113451000, 1.8750898000e-3, -2.9128467000e-3, & + 808.0, -5.6131638000, 1.8739909000e-3, -2.9104282000e-3, & + 809.0, -5.6149777000, 1.8728923000e-3, -2.9080128000e-3, & + 810.0, -5.6167869000, 1.8717942000e-3, -2.9056005000e-3, & + 811.0, -5.6185912000, 1.8706965000e-3, -2.9031914000e-3, & + 812.0, -5.6203907000, 1.8695992000e-3, -2.9007853000e-3, & + 813.0, -5.6221855000, 1.8685023000e-3, -2.8983824000e-3, & + 814.0, -5.6239754000, 1.8674058000e-3, -2.8959825000e-3, & + 815.0, -5.6257606000, 1.8663098000e-3, -2.8935857000e-3, & + 816.0, -5.6275411000, 1.8652141000e-3, -2.8911920000e-3, & + 817.0, -5.6293168000, 1.8641189000e-3, -2.8888014000e-3, & + 818.0, -5.6310878000, 1.8630241000e-3, -2.8864138000e-3, & + 819.0, -5.6328540000, 1.8619297000e-3, -2.8840292000e-3, & + 820.0, -5.6346155000, 1.8608357000e-3, -2.8816477000e-3, & + 821.0, -5.6363723000, 1.8597420000e-3, -2.8792693000e-3, & + 822.0, -5.6381245000, 1.8586488000e-3, -2.8768939000e-3, & + 823.0, -5.6398719000, 1.8575560000e-3, -2.8745215000e-3, & + 824.0, -5.6416146000, 1.8564636000e-3, -2.8721521000e-3, & + 825.0, -5.6433527000, 1.8553716000e-3, -2.8697857000e-3, & + 826.0, -5.6450861000, 1.8542799000e-3, -2.8674223000e-3, & + 827.0, -5.6468149000, 1.8531887000e-3, -2.8650619000e-3, & + 828.0, -5.6485390000, 1.8520979000e-3, -2.8627045000e-3, & + 829.0, -5.6502584000, 1.8510074000e-3, -2.8603501000e-3, & + 830.0, -5.6519733000, 1.8499173000e-3, -2.8579986000e-3, & + 831.0, -5.6536835000, 1.8488276000e-3, -2.8556502000e-3, & + 832.0, -5.6553891000, 1.8477384000e-3, -2.8533046000e-3, & + 833.0, -5.6570901000, 1.8466494000e-3, -2.8509621000e-3, & + 834.0, -5.6587866000, 1.8455609000e-3, -2.8486224000e-3, & + 835.0, -5.6604784000, 1.8444728000e-3, -2.8462858000e-3, & + 836.0, -5.6621657000, 1.8433850000e-3, -2.8439520000e-3, & + 837.0, -5.6638484000, 1.8422976000e-3, -2.8416212000e-3, & + 838.0, -5.6655266000, 1.8412106000e-3, -2.8392933000e-3, & + 839.0, -5.6672002000, 1.8401239000e-3, -2.8369683000e-3, & + 840.0, -5.6688693000, 1.8390377000e-3, -2.8346462000e-3, & + 841.0, -5.6705338000, 1.8379518000e-3, -2.8323270000e-3, & + 842.0, -5.6721939000, 1.8368663000e-3, -2.8300107000e-3, & + 843.0, -5.6738494000, 1.8357811000e-3, -2.8276973000e-3, & + 844.0, -5.6755004000, 1.8346964000e-3, -2.8253867000e-3, & + 845.0, -5.6771470000, 1.8336120000e-3, -2.8230791000e-3, & + 846.0, -5.6787890000, 1.8325279000e-3, -2.8207743000e-3, & + 847.0, -5.6804266000, 1.8314443000e-3, -2.8184724000e-3, & + 848.0, -5.6820597000, 1.8303610000e-3, -2.8161733000e-3, & + 849.0, -5.6836884000, 1.8292781000e-3, -2.8138771000e-3, & + 850.0, -5.6853127000, 1.8281955000e-3, -2.8115837000e-3, & + 851.0, -5.6869325000, 1.8271133000e-3, -2.8092932000e-3, & + 852.0, -5.6885478000, 1.8260315000e-3, -2.8070055000e-3, & + 853.0, -5.6901588000, 1.8249501000e-3, -2.8047206000e-3, & + 854.0, -5.6917653000, 1.8238690000e-3, -2.8024385000e-3, & + 855.0, -5.6933675000, 1.8227882000e-3, -2.8001593000e-3, & + 856.0, -5.6949653000, 1.8217079000e-3, -2.7978829000e-3, & + 857.0, -5.6965586000, 1.8206279000e-3, -2.7956092000e-3, & + 858.0, -5.6981477000, 1.8195482000e-3, -2.7933384000e-3, & + 859.0, -5.6997323000, 1.8184690000e-3, -2.7910703000e-3, & + 860.0, -5.7013126000, 1.8173900000e-3, -2.7888051000e-3, & + 861.0, -5.7028886000, 1.8163115000e-3, -2.7865426000e-3, & + 862.0, -5.7044602000, 1.8152333000e-3, -2.7842829000e-3, & + 863.0, -5.7060275000, 1.8141555000e-3, -2.7820260000e-3, & + 864.0, -5.7075905000, 1.8130780000e-3, -2.7797718000e-3, & + 865.0, -5.7091492000, 1.8120009000e-3, -2.7775204000e-3, & + 866.0, -5.7107035000, 1.8109241000e-3, -2.7752717000e-3, & + 867.0, -5.7122536000, 1.8098477000e-3, -2.7730258000e-3, & + 868.0, -5.7137995000, 1.8087717000e-3, -2.7707826000e-3, & + 869.0, -5.7153410000, 1.8076960000e-3, -2.7685421000e-3, & + 870.0, -5.7168783000, 1.8066207000e-3, -2.7663044000e-3, & + 871.0, -5.7184113000, 1.8055458000e-3, -2.7640694000e-3, & + 872.0, -5.7199401000, 1.8044712000e-3, -2.7618372000e-3, & + 873.0, -5.7214646000, 1.8033969000e-3, -2.7596076000e-3, & + 874.0, -5.7229850000, 1.8023230000e-3, -2.7573808000e-3, & + 875.0, -5.7245011000, 1.8012495000e-3, -2.7551566000e-3, & + 876.0, -5.7260130000, 1.8001763000e-3, -2.7529352000e-3, & + 877.0, -5.7275207000, 1.7991035000e-3, -2.7507164000e-3, & + 878.0, -5.7290242000, 1.7980311000e-3, -2.7485003000e-3, & + 879.0, -5.7305236000, 1.7969590000e-3, -2.7462870000e-3, & + 880.0, -5.7320187000, 1.7958873000e-3, -2.7440763000e-3, & + 881.0, -5.7335097000, 1.7948159000e-3, -2.7418682000e-3, & + 882.0, -5.7349966000, 1.7937449000e-3, -2.7396629000e-3, & + 883.0, -5.7364793000, 1.7926742000e-3, -2.7374601000e-3, & + 884.0, -5.7379579000, 1.7916039000e-3, -2.7352601000e-3, & + 885.0, -5.7394323000, 1.7905340000e-3, -2.7330627000e-3, & + 886.0, -5.7409027000, 1.7894644000e-3, -2.7308680000e-3, & + 887.0, -5.7423689000, 1.7883951000e-3, -2.7286759000e-3, & + 888.0, -5.7438310000, 1.7873263000e-3, -2.7264864000e-3, & + 889.0, -5.7452891000, 1.7862578000e-3, -2.7242996000e-3, & + 890.0, -5.7467430000, 1.7851896000e-3, -2.7221154000e-3, & + 891.0, -5.7481929000, 1.7841218000e-3, -2.7199338000e-3, & + 892.0, -5.7496387000, 1.7830544000e-3, -2.7177548000e-3, & + 893.0, -5.7510805000, 1.7819873000e-3, -2.7155785000e-3, & + 894.0, -5.7525182000, 1.7809206000e-3, -2.7134047000e-3, & + 895.0, -5.7539519000, 1.7798543000e-3, -2.7112336000e-3, & + 896.0, -5.7553816000, 1.7787883000e-3, -2.7090651000e-3, & + 897.0, -5.7568072000, 1.7777227000e-3, -2.7068991000e-3, & + 898.0, -5.7582289000, 1.7766574000e-3, -2.7047358000e-3, & + 899.0, -5.7596465000, 1.7755925000e-3, -2.7025750000e-3, & + 900.0, -5.7610602000, 1.7745280000e-3, -2.7004169000e-3, & + 901.0, -5.7624698000, 1.7734638000e-3, -2.6982613000e-3, & + 902.0, -5.7638755000, 1.7724000000e-3, -2.6961082000e-3, & + 903.0, -5.7652772000, 1.7713365000e-3, -2.6939578000e-3, & + 904.0, -5.7666750000, 1.7702734000e-3, -2.6918099000e-3, & + 905.0, -5.7680688000, 1.7692107000e-3, -2.6896645000e-3, & + 906.0, -5.7694587000, 1.7681484000e-3, -2.6875218000e-3, & + 907.0, -5.7708447000, 1.7670864000e-3, -2.6853815000e-3, & + 908.0, -5.7722267000, 1.7660247000e-3, -2.6832438000e-3, & + 909.0, -5.7736048000, 1.7649635000e-3, -2.6811087000e-3, & + 910.0, -5.7749791000, 1.7639026000e-3, -2.6789761000e-3, & + 911.0, -5.7763494000, 1.7628421000e-3, -2.6768460000e-3, & + 912.0, -5.7777158000, 1.7617819000e-3, -2.6747185000e-3, & + 913.0, -5.7790784000, 1.7607221000e-3, -2.6725934000e-3, & + 914.0, -5.7804371000, 1.7596627000e-3, -2.6704709000e-3, & + 915.0, -5.7817919000, 1.7586037000e-3, -2.6683510000e-3, & + 916.0, -5.7831429000, 1.7575450000e-3, -2.6662335000e-3, & + 917.0, -5.7844901000, 1.7564867000e-3, -2.6641185000e-3, & + 918.0, -5.7858334000, 1.7554288000e-3, -2.6620060000e-3, & + 919.0, -5.7871729000, 1.7543712000e-3, -2.6598961000e-3, & + 920.0, -5.7885086000, 1.7533140000e-3, -2.6577886000e-3, & + 921.0, -5.7898405000, 1.7522572000e-3, -2.6556836000e-3, & + 922.0, -5.7911686000, 1.7512008000e-3, -2.6535811000e-3, & + 923.0, -5.7924928000, 1.7501447000e-3, -2.6514811000e-3, & + 924.0, -5.7938134000, 1.7490890000e-3, -2.6493836000e-3, & + 925.0, -5.7951301000, 1.7480337000e-3, -2.6472885000e-3, & + 926.0, -5.7964431000, 1.7469788000e-3, -2.6451960000e-3, & + 927.0, -5.7977523000, 1.7459242000e-3, -2.6431058000e-3, & + 928.0, -5.7990578000, 1.7448700000e-3, -2.6410182000e-3, & + 929.0, -5.8003595000, 1.7438162000e-3, -2.6389330000e-3, & + 930.0, -5.8016575000, 1.7427628000e-3, -2.6368502000e-3, & + 931.0, -5.8029518000, 1.7417098000e-3, -2.6347699000e-3, & + 932.0, -5.8042424000, 1.7406571000e-3, -2.6326921000e-3, & + 933.0, -5.8055293000, 1.7396049000e-3, -2.6306167000e-3, & + 934.0, -5.8068125000, 1.7385530000e-3, -2.6285437000e-3, & + 935.0, -5.8080920000, 1.7375015000e-3, -2.6264732000e-3, & + 936.0, -5.8093679000, 1.7364504000e-3, -2.6244051000e-3, & + 937.0, -5.8106400000, 1.7353996000e-3, -2.6223394000e-3, & + 938.0, -5.8119085000, 1.7343493000e-3, -2.6202762000e-3, & + 939.0, -5.8131734000, 1.7332993000e-3, -2.6182153000e-3, & + 940.0, -5.8144346000, 1.7322497000e-3, -2.6161569000e-3, & + 941.0, -5.8156922000, 1.7312006000e-3, -2.6141009000e-3, & + 942.0, -5.8169461000, 1.7301518000e-3, -2.6120473000e-3, & + 943.0, -5.8181965000, 1.7291034000e-3, -2.6099961000e-3, & + 944.0, -5.8194432000, 1.7280553000e-3, -2.6079473000e-3, & + 945.0, -5.8206864000, 1.7270077000e-3, -2.6059010000e-3, & + 946.0, -5.8219259000, 1.7259605000e-3, -2.6038570000e-3, & + 947.0, -5.8231619000, 1.7249137000e-3, -2.6018154000e-3, & + 948.0, -5.8243943000, 1.7238672000e-3, -2.5997761000e-3, & + 949.0, -5.8256231000, 1.7228212000e-3, -2.5977393000e-3, & + 950.0, -5.8268484000, 1.7217755000e-3, -2.5957048000e-3, & + 951.0, -5.8280701000, 1.7207303000e-3, -2.5936728000e-3, & + 952.0, -5.8292883000, 1.7196854000e-3, -2.5916430000e-3, & + 953.0, -5.8305029000, 1.7186410000e-3, -2.5896157000e-3, & + 954.0, -5.8317141000, 1.7175970000e-3, -2.5875907000e-3, & + 955.0, -5.8329217000, 1.7165533000e-3, -2.5855681000e-3, & + 956.0, -5.8341258000, 1.7155101000e-3, -2.5835478000e-3, & + 957.0, -5.8353264000, 1.7144672000e-3, -2.5815299000e-3, & + 958.0, -5.8365235000, 1.7134248000e-3, -2.5795144000e-3, & + 959.0, -5.8377172000, 1.7123828000e-3, -2.5775012000e-3, & + 960.0, -5.8389074000, 1.7113411000e-3, -2.5754903000e-3, & + 961.0, -5.8400941000, 1.7102999000e-3, -2.5734818000e-3, & + 962.0, -5.8412773000, 1.7092591000e-3, -2.5714756000e-3, & + 963.0, -5.8424571000, 1.7082187000e-3, -2.5694717000e-3, & + 964.0, -5.8436335000, 1.7071787000e-3, -2.5674702000e-3, & + 965.0, -5.8448065000, 1.7061391000e-3, -2.5654710000e-3, & + 966.0, -5.8459760000, 1.7051000000e-3, -2.5634741000e-3, & + 967.0, -5.8471421000, 1.7040612000e-3, -2.5614796000e-3, & + 968.0, -5.8483048000, 1.7030229000e-3, -2.5594873000e-3, & + 969.0, -5.8494641000, 1.7019850000e-3, -2.5574974000e-3, & + 970.0, -5.8506200000, 1.7009475000e-3, -2.5555098000e-3, & + 971.0, -5.8517725000, 1.6999104000e-3, -2.5535245000e-3, & + 972.0, -5.8529217000, 1.6988737000e-3, -2.5515415000e-3, & + 973.0, -5.8540675000, 1.6978374000e-3, -2.5495608000e-3, & + 974.0, -5.8552099000, 1.6968016000e-3, -2.5475824000e-3, & + 975.0, -5.8563490000, 1.6957662000e-3, -2.5456062000e-3, & + 976.0, -5.8574847000, 1.6947312000e-3, -2.5436324000e-3, & + 977.0, -5.8586172000, 1.6936966000e-3, -2.5416609000e-3, & + 978.0, -5.8597463000, 1.6926625000e-3, -2.5396916000e-3, & + 979.0, -5.8608720000, 1.6916288000e-3, -2.5377246000e-3, & + 980.0, -5.8619945000, 1.6905955000e-3, -2.5357599000e-3, & + 981.0, -5.8631137000, 1.6895626000e-3, -2.5337975000e-3, & + 982.0, -5.8642296000, 1.6885302000e-3, -2.5318373000e-3, & + 983.0, -5.8653422000, 1.6874982000e-3, -2.5298794000e-3, & + 984.0, -5.8664515000, 1.6864666000e-3, -2.5279238000e-3, & + 985.0, -5.8675576000, 1.6854355000e-3, -2.5259704000e-3, & + 986.0, -5.8686604000, 1.6844048000e-3, -2.5240193000e-3, & + 987.0, -5.8697599000, 1.6833745000e-3, -2.5220704000e-3, & + 988.0, -5.8708562000, 1.6823447000e-3, -2.5201238000e-3, & + 989.0, -5.8719493000, 1.6813153000e-3, -2.5181795000e-3, & + 990.0, -5.8730391000, 1.6802863000e-3, -2.5162374000e-3, & + 991.0, -5.8741258000, 1.6792578000e-3, -2.5142975000e-3, & + 992.0, -5.8752092000, 1.6782297000e-3, -2.5123598000e-3, & + 993.0, -5.8762894000, 1.6772020000e-3, -2.5104244000e-3, & + 994.0, -5.8773664000, 1.6761748000e-3, -2.5084913000e-3, & + 995.0, -5.8784403000, 1.6751480000e-3, -2.5065603000e-3, & + 996.0, -5.8795109000, 1.6741217000e-3, -2.5046316000e-3, & + 997.0, -5.8805784000, 1.6730958000e-3, -2.5027051000e-3, & + 998.0, -5.8816427000, 1.6720704000e-3, -2.5007809000e-3, & + 999.0, -5.8827039000, 1.6710454000e-3, -2.4988588000e-3, & + 1000.0, -5.8837619000, 1.6700209000e-3, -2.4969390000e-3, & + 1001.0, -5.8848168000, 1.6689968000e-3, -2.4950213000e-3, & + 1002.0, -5.8858686000, 1.6679732000e-3, -2.4931059000e-3, & + 1003.0, -5.8869172000, 1.6669500000e-3, -2.4911927000e-3, & + 1004.0, -5.8879627000, 1.6659272000e-3, -2.4892817000e-3, & + 1005.0, -5.8890051000, 1.6649049000e-3, -2.4873729000e-3, & + 1006.0, -5.8900444000, 1.6638831000e-3, -2.4854663000e-3, & + 1007.0, -5.8910807000, 1.6628617000e-3, -2.4835619000e-3, & + 1008.0, -5.8921138000, 1.6618408000e-3, -2.4816596000e-3, & + 1009.0, -5.8931439000, 1.6608204000e-3, -2.4797596000e-3, & + 1010.0, -5.8941708000, 1.6598004000e-3, -2.4778617000e-3, & + 1011.0, -5.8951948000, 1.6587808000e-3, -2.4759660000e-3, & + 1012.0, -5.8962156000, 1.6577617000e-3, -2.4740725000e-3, & + 1013.0, -5.8972335000, 1.6567431000e-3, -2.4721812000e-3, & + 1014.0, -5.8982483000, 1.6557250000e-3, -2.4702920000e-3, & + 1015.0, -5.8992600000, 1.6547073000e-3, -2.4684051000e-3, & + 1016.0, -5.9002688000, 1.6536901000e-3, -2.4665202000e-3, & + 1017.0, -5.9012745000, 1.6526733000e-3, -2.4646376000e-3, & + 1018.0, -5.9022772000, 1.6516570000e-3, -2.4627571000e-3, & + 1019.0, -5.9032769000, 1.6506412000e-3, -2.4608788000e-3, & + 1020.0, -5.9042737000, 1.6496258000e-3, -2.4590026000e-3, & + 1021.0, -5.9052674000, 1.6486109000e-3, -2.4571286000e-3, & + 1022.0, -5.9062582000, 1.6475965000e-3, -2.4552567000e-3, & + 1023.0, -5.9072460000, 1.6465826000e-3, -2.4533870000e-3, & + 1024.0, -5.9082308000, 1.6455691000e-3, -2.4515194000e-3, & + 1025.0, -5.9092127000, 1.6445561000e-3, -2.4496539000e-3, & + 1026.0, -5.9101917000, 1.6435436000e-3, -2.4477906000e-3, & + 1027.0, -5.9111677000, 1.6425316000e-3, -2.4459295000e-3, & + 1028.0, -5.9121408000, 1.6415200000e-3, -2.4440704000e-3, & + 1029.0, -5.9131109000, 1.6405089000e-3, -2.4422135000e-3, & + 1030.0, -5.9140782000, 1.6394983000e-3, -2.4403587000e-3, & + 1031.0, -5.9150425000, 1.6384882000e-3, -2.4385061000e-3, & + 1032.0, -5.9160039000, 1.6374786000e-3, -2.4366555000e-3, & + 1033.0, -5.9169625000, 1.6364694000e-3, -2.4348071000e-3, & + 1034.0, -5.9179181000, 1.6354607000e-3, -2.4329608000e-3, & + 1035.0, -5.9188709000, 1.6344526000e-3, -2.4311166000e-3, & + 1036.0, -5.9198208000, 1.6334449000e-3, -2.4292746000e-3, & + 1037.0, -5.9207678000, 1.6324377000e-3, -2.4274346000e-3, & + 1038.0, -5.9217120000, 1.6314309000e-3, -2.4255967000e-3, & + 1039.0, -5.9226533000, 1.6304247000e-3, -2.4237610000e-3, & + 1040.0, -5.9235918000, 1.6294190000e-3, -2.4219273000e-3, & + 1041.0, -5.9245275000, 1.6284137000e-3, -2.4200958000e-3, & + 1042.0, -5.9254603000, 1.6274090000e-3, -2.4182663000e-3, & + 1043.0, -5.9263904000, 1.6264047000e-3, -2.4164389000e-3, & + 1044.0, -5.9273176000, 1.6254009000e-3, -2.4146136000e-3, & + 1045.0, -5.9282420000, 1.6243977000e-3, -2.4127904000e-3, & + 1046.0, -5.9291636000, 1.6233949000e-3, -2.4109693000e-3, & + 1047.0, -5.9300824000, 1.6223926000e-3, -2.4091503000e-3, & + 1048.0, -5.9309984000, 1.6213909000e-3, -2.4073333000e-3, & + 1049.0, -5.9319117000, 1.6203896000e-3, -2.4055185000e-3, & + 1050.0, -5.9328222000, 1.6193888000e-3, -2.4037057000e-3, & + 1051.0, -5.9337299000, 1.6183886000e-3, -2.4018949000e-3, & + 1052.0, -5.9346349000, 1.6173888000e-3, -2.4000862000e-3, & + 1053.0, -5.9355371000, 1.6163895000e-3, -2.3982796000e-3, & + 1054.0, -5.9364366000, 1.6153908000e-3, -2.3964751000e-3, & + 1055.0, -5.9373334000, 1.6143925000e-3, -2.3946726000e-3, & + 1056.0, -5.9382274000, 1.6133948000e-3, -2.3928722000e-3, & + 1057.0, -5.9391187000, 1.6123976000e-3, -2.3910738000e-3, & + 1058.0, -5.9400074000, 1.6114009000e-3, -2.3892775000e-3, & + 1059.0, -5.9408933000, 1.6104046000e-3, -2.3874833000e-3, & + 1060.0, -5.9417765000, 1.6094089000e-3, -2.3856910000e-3, & + 1061.0, -5.9426570000, 1.6084138000e-3, -2.3839009000e-3, & + 1062.0, -5.9435349000, 1.6074191000e-3, -2.3821127000e-3, & + 1063.0, -5.9444100000, 1.6064249000e-3, -2.3803266000e-3, & + 1064.0, -5.9452825000, 1.6054313000e-3, -2.3785426000e-3, & + 1065.0, -5.9461524000, 1.6044382000e-3, -2.3767606000e-3, & + 1066.0, -5.9470196000, 1.6034456000e-3, -2.3749806000e-3, & + 1067.0, -5.9478841000, 1.6024535000e-3, -2.3732026000e-3, & + 1068.0, -5.9487460000, 1.6014619000e-3, -2.3714267000e-3, & + 1069.0, -5.9496053000, 1.6004709000e-3, -2.3696528000e-3, & + 1070.0, -5.9504619000, 1.5994804000e-3, -2.3678809000e-3, & + 1071.0, -5.9513160000, 1.5984904000e-3, -2.3661110000e-3, & + 1072.0, -5.9521674000, 1.5975009000e-3, -2.3643432000e-3, & + 1073.0, -5.9530162000, 1.5965120000e-3, -2.3625773000e-3, & + 1074.0, -5.9538624000, 1.5955236000e-3, -2.3608135000e-3, & + 1075.0, -5.9547061000, 1.5945357000e-3, -2.3590517000e-3, & + 1076.0, -5.9555471000, 1.5935483000e-3, -2.3572919000e-3, & + 1077.0, -5.9563856000, 1.5925615000e-3, -2.3555341000e-3, & + 1078.0, -5.9572215000, 1.5915752000e-3, -2.3537782000e-3, & + 1079.0, -5.9580548000, 1.5905894000e-3, -2.3520244000e-3, & + 1080.0, -5.9588856000, 1.5896041000e-3, -2.3502726000e-3, & + 1081.0, -5.9597138000, 1.5886194000e-3, -2.3485228000e-3, & + 1082.0, -5.9605395000, 1.5876353000e-3, -2.3467750000e-3, & + 1083.0, -5.9613627000, 1.5866516000e-3, -2.3450292000e-3, & + 1084.0, -5.9621833000, 1.5856685000e-3, -2.3432853000e-3, & + 1085.0, -5.9630014000, 1.5846860000e-3, -2.3415434000e-3, & + 1086.0, -5.9638170000, 1.5837039000e-3, -2.3398036000e-3, & + 1087.0, -5.9646301000, 1.5827224000e-3, -2.3380657000e-3, & + 1088.0, -5.9654407000, 1.5817415000e-3, -2.3363297000e-3, & + 1089.0, -5.9662488000, 1.5807611000e-3, -2.3345958000e-3, & + 1090.0, -5.9670544000, 1.5797812000e-3, -2.3328638000e-3, & + 1091.0, -5.9678575000, 1.5788019000e-3, -2.3311338000e-3, & + 1092.0, -5.9686582000, 1.5778231000e-3, -2.3294057000e-3, & + 1093.0, -5.9694563000, 1.5768449000e-3, -2.3276797000e-3, & + 1094.0, -5.9702521000, 1.5758672000e-3, -2.3259555000e-3, & + 1095.0, -5.9710453000, 1.5748901000e-3, -2.3242334000e-3, & + 1096.0, -5.9718361000, 1.5739135000e-3, -2.3225132000e-3, & + 1097.0, -5.9726245000, 1.5729374000e-3, -2.3207950000e-3, & + 1098.0, -5.9734105000, 1.5719619000e-3, -2.3190787000e-3, & + 1099.0, -5.9741940000, 1.5709870000e-3, -2.3173643000e-3, & + 1100.0, -5.9749751000, 1.5700126000e-3, -2.3156519000e-3, & + 1101.0, -5.9757538000, 1.5690387000e-3, -2.3139415000e-3, & + 1102.0, -5.9765300000, 1.5680654000e-3, -2.3122330000e-3, & + 1103.0, -5.9773039000, 1.5670927000e-3, -2.3105264000e-3, & + 1104.0, -5.9780754000, 1.5661205000e-3, -2.3088218000e-3, & + 1105.0, -5.9788445000, 1.5651489000e-3, -2.3071191000e-3, & + 1106.0, -5.9796112000, 1.5641778000e-3, -2.3054184000e-3, & + 1107.0, -5.9803755000, 1.5632073000e-3, -2.3037195000e-3, & + 1108.0, -5.9811375000, 1.5622374000e-3, -2.3020226000e-3, & + 1109.0, -5.9818971000, 1.5612680000e-3, -2.3003277000e-3, & + 1110.0, -5.9826543000, 1.5602991000e-3, -2.2986346000e-3, & + 1111.0, -5.9834092000, 1.5593309000e-3, -2.2969435000e-3, & + 1112.0, -5.9841618000, 1.5583632000e-3, -2.2952543000e-3, & + 1113.0, -5.9849120000, 1.5573960000e-3, -2.2935670000e-3, & + 1114.0, -5.9856599000, 1.5564294000e-3, -2.2918817000e-3, & + 1115.0, -5.9864054000, 1.5554634000e-3, -2.2901982000e-3, & + 1116.0, -5.9871487000, 1.5544979000e-3, -2.2885167000e-3, & + 1117.0, -5.9878896000, 1.5535331000e-3, -2.2868370000e-3, & + 1118.0, -5.9886282000, 1.5525687000e-3, -2.2851593000e-3, & + 1119.0, -5.9893646000, 1.5516050000e-3, -2.2834835000e-3, & + 1120.0, -5.9900986000, 1.5506418000e-3, -2.2818095000e-3, & + 1121.0, -5.9908304000, 1.5496792000e-3, -2.2801375000e-3, & + 1122.0, -5.9915599000, 1.5487171000e-3, -2.2784674000e-3, & + 1123.0, -5.9922871000, 1.5477557000e-3, -2.2767991000e-3, & + 1124.0, -5.9930120000, 1.5467948000e-3, -2.2751328000e-3, & + 1125.0, -5.9937347000, 1.5458344000e-3, -2.2734683000e-3, & + 1126.0, -5.9944551000, 1.5448747000e-3, -2.2718058000e-3, & + 1127.0, -5.9951733000, 1.5439155000e-3, -2.2701451000e-3, & + 1128.0, -5.9958892000, 1.5429569000e-3, -2.2684863000e-3, & + 1129.0, -5.9966029000, 1.5419989000e-3, -2.2668294000e-3, & + 1130.0, -5.9973144000, 1.5410414000e-3, -2.2651743000e-3, & + 1131.0, -5.9980236000, 1.5400845000e-3, -2.2635212000e-3, & + 1132.0, -5.9987307000, 1.5391282000e-3, -2.2618699000e-3, & + 1133.0, -5.9994355000, 1.5381725000e-3, -2.2602205000e-3, & + 1134.0, -6.0001381000, 1.5372174000e-3, -2.2585729000e-3, & + 1135.0, -6.0008385000, 1.5362628000e-3, -2.2569272000e-3, & + 1136.0, -6.0015367000, 1.5353089000e-3, -2.2552834000e-3, & + 1137.0, -6.0022328000, 1.5343555000e-3, -2.2536414000e-3, & + 1138.0, -6.0029266000, 1.5334027000e-3, -2.2520013000e-3, & + 1139.0, -6.0036183000, 1.5324504000e-3, -2.2503631000e-3, & + 1140.0, -6.0043078000, 1.5314988000e-3, -2.2487267000e-3, & + 1141.0, -6.0049952000, 1.5305477000e-3, -2.2470922000e-3, & + 1142.0, -6.0056804000, 1.5295973000e-3, -2.2454595000e-3, & + 1143.0, -6.0063635000, 1.5286474000e-3, -2.2438287000e-3, & + 1144.0, -6.0070444000, 1.5276981000e-3, -2.2421997000e-3, & + 1145.0, -6.0077231000, 1.5267494000e-3, -2.2405726000e-3, & + 1146.0, -6.0083998000, 1.5258013000e-3, -2.2389473000e-3, & + 1147.0, -6.0090743000, 1.5248538000e-3, -2.2373238000e-3, & + 1148.0, -6.0097467000, 1.5239068000e-3, -2.2357022000e-3, & + 1149.0, -6.0104170000, 1.5229605000e-3, -2.2340824000e-3, & + 1150.0, -6.0110851000, 1.5220147000e-3, -2.2324645000e-3, & + 1151.0, -6.0117512000, 1.5210696000e-3, -2.2308484000e-3, & + 1152.0, -6.0124152000, 1.5201250000e-3, -2.2292341000e-3, & + 1153.0, -6.0130771000, 1.5191811000e-3, -2.2276216000e-3, & + 1154.0, -6.0137369000, 1.5182377000e-3, -2.2260110000e-3, & + 1155.0, -6.0143946000, 1.5172949000e-3, -2.2244022000e-3, & + 1156.0, -6.0150502000, 1.5163527000e-3, -2.2227952000e-3, & + 1157.0, -6.0157038000, 1.5154112000e-3, -2.2211900000e-3, & + 1158.0, -6.0163553000, 1.5144702000e-3, -2.2195866000e-3, & + 1159.0, -6.0170048000, 1.5135298000e-3, -2.2179851000e-3, & + 1160.0, -6.0176522000, 1.5125900000e-3, -2.2163853000e-3, & + 1161.0, -6.0182976000, 1.5116508000e-3, -2.2147874000e-3, & + 1162.0, -6.0189409000, 1.5107122000e-3, -2.2131913000e-3, & + 1163.0, -6.0195822000, 1.5097742000e-3, -2.2115970000e-3, & + 1164.0, -6.0202215000, 1.5088369000e-3, -2.2100045000e-3, & + 1165.0, -6.0208588000, 1.5079001000e-3, -2.2084138000e-3, & + 1166.0, -6.0214940000, 1.5069639000e-3, -2.2068249000e-3, & + 1167.0, -6.0221273000, 1.5060283000e-3, -2.2052377000e-3, & + 1168.0, -6.0227585000, 1.5050934000e-3, -2.2036524000e-3, & + 1169.0, -6.0233877000, 1.5041590000e-3, -2.2020689000e-3, & + 1170.0, -6.0240150000, 1.5032253000e-3, -2.2004872000e-3, & + 1171.0, -6.0246402000, 1.5022921000e-3, -2.1989072000e-3, & + 1172.0, -6.0252635000, 1.5013596000e-3, -2.1973290000e-3, & + 1173.0, -6.0258848000, 1.5004276000e-3, -2.1957527000e-3, & + 1174.0, -6.0265042000, 1.4994963000e-3, -2.1941781000e-3, & + 1175.0, -6.0271215000, 1.4985656000e-3, -2.1926052000e-3, & + 1176.0, -6.0277369000, 1.4976355000e-3, -2.1910342000e-3, & + 1177.0, -6.0283504000, 1.4967060000e-3, -2.1894649000e-3, & + 1178.0, -6.0289619000, 1.4957771000e-3, -2.1878974000e-3, & + 1179.0, -6.0295715000, 1.4948489000e-3, -2.1863317000e-3, & + 1180.0, -6.0301791000, 1.4939212000e-3, -2.1847678000e-3, & + 1181.0, -6.0307848000, 1.4929942000e-3, -2.1832056000e-3, & + 1182.0, -6.0313886000, 1.4920677000e-3, -2.1816451000e-3, & + 1183.0, -6.0319905000, 1.4911419000e-3, -2.1800865000e-3, & + 1184.0, -6.0325904000, 1.4902167000e-3, -2.1785296000e-3, & + 1185.0, -6.0331885000, 1.4892921000e-3, -2.1769744000e-3, & + 1186.0, -6.0337846000, 1.4883682000e-3, -2.1754210000e-3, & + 1187.0, -6.0343789000, 1.4874448000e-3, -2.1738694000e-3, & + 1188.0, -6.0349712000, 1.4865221000e-3, -2.1723195000e-3, & + 1189.0, -6.0355617000, 1.4856000000e-3, -2.1707714000e-3, & + 1190.0, -6.0361503000, 1.4846785000e-3, -2.1692250000e-3, & + 1191.0, -6.0367370000, 1.4837576000e-3, -2.1676804000e-3, & + 1192.0, -6.0373218000, 1.4828373000e-3, -2.1661375000e-3, & + 1193.0, -6.0379048000, 1.4819177000e-3, -2.1645963000e-3, & + 1194.0, -6.0384859000, 1.4809986000e-3, -2.1630569000e-3, & + 1195.0, -6.0390651000, 1.4800802000e-3, -2.1615192000e-3, & + 1196.0, -6.0396426000, 1.4791625000e-3, -2.1599833000e-3, & + 1197.0, -6.0402181000, 1.4782453000e-3, -2.1584490000e-3, & + 1198.0, -6.0407919000, 1.4773288000e-3, -2.1569166000e-3, & + 1199.0, -6.0413638000, 1.4764129000e-3, -2.1553858000e-3, & + 1200.0, -6.0419338000, 1.4754976000e-3, -2.1538568000e-3, & + 1201.0, -6.0425021000, 1.4745829000e-3, -2.1523295000e-3, & + 1202.0, -6.0430685000, 1.4736689000e-3, -2.1508039000e-3, & + 1203.0, -6.0436331000, 1.4727555000e-3, -2.1492800000e-3, & + 1204.0, -6.0441959000, 1.4718427000e-3, -2.1477579000e-3, & + 1205.0, -6.0447570000, 1.4709305000e-3, -2.1462375000e-3, & + 1206.0, -6.0453162000, 1.4700190000e-3, -2.1447188000e-3, & + 1207.0, -6.0458736000, 1.4691081000e-3, -2.1432018000e-3, & + 1208.0, -6.0464292000, 1.4681978000e-3, -2.1416865000e-3, & + 1209.0, -6.0469831000, 1.4672882000e-3, -2.1401729000e-3, & + 1210.0, -6.0475352000, 1.4663791000e-3, -2.1386610000e-3, & + 1211.0, -6.0480855000, 1.4654707000e-3, -2.1371509000e-3, & + 1212.0, -6.0486341000, 1.4645630000e-3, -2.1356424000e-3, & + 1213.0, -6.0491809000, 1.4636558000e-3, -2.1341356000e-3, & + 1214.0, -6.0497259000, 1.4627493000e-3, -2.1326306000e-3, & + 1215.0, -6.0502692000, 1.4618435000e-3, -2.1311272000e-3, & + 1216.0, -6.0508107000, 1.4609382000e-3, -2.1296255000e-3, & + 1217.0, -6.0513505000, 1.4600336000e-3, -2.1281255000e-3, & + 1218.0, -6.0518886000, 1.4591297000e-3, -2.1266272000e-3, & + 1219.0, -6.0524250000, 1.4582263000e-3, -2.1251306000e-3, & + 1220.0, -6.0529596000, 1.4573236000e-3, -2.1236357000e-3, & + 1221.0, -6.0534925000, 1.4564215000e-3, -2.1221424000e-3, & + 1222.0, -6.0540237000, 1.4555201000e-3, -2.1206509000e-3, & + 1223.0, -6.0545531000, 1.4546193000e-3, -2.1191610000e-3, & + 1224.0, -6.0550809000, 1.4537191000e-3, -2.1176728000e-3, & + 1225.0, -6.0556070000, 1.4528196000e-3, -2.1161863000e-3, & + 1226.0, -6.0561314000, 1.4519207000e-3, -2.1147014000e-3, & + 1227.0, -6.0566541000, 1.4510224000e-3, -2.1132182000e-3, & + 1228.0, -6.0571751000, 1.4501248000e-3, -2.1117367000e-3, & + 1229.0, -6.0576944000, 1.4492278000e-3, -2.1102569000e-3, & + 1230.0, -6.0582120000, 1.4483315000e-3, -2.1087787000e-3, & + 1231.0, -6.0587280000, 1.4474358000e-3, -2.1073022000e-3, & + 1232.0, -6.0592424000, 1.4465407000e-3, -2.1058273000e-3, & + 1233.0, -6.0597550000, 1.4456463000e-3, -2.1043541000e-3, & + 1234.0, -6.0602660000, 1.4447525000e-3, -2.1028826000e-3, & + 1235.0, -6.0607754000, 1.4438593000e-3, -2.1014127000e-3, & + 1236.0, -6.0612831000, 1.4429668000e-3, -2.0999445000e-3, & + 1237.0, -6.0617892000, 1.4420750000e-3, -2.0984779000e-3, & + 1238.0, -6.0622936000, 1.4411837000e-3, -2.0970130000e-3, & + 1239.0, -6.0627964000, 1.4402931000e-3, -2.0955497000e-3, & + 1240.0, -6.0632976000, 1.4394032000e-3, -2.0940880000e-3, & + 1241.0, -6.0637972000, 1.4385139000e-3, -2.0926281000e-3, & + 1242.0, -6.0642951000, 1.4376252000e-3, -2.0911697000e-3, & + 1243.0, -6.0647914000, 1.4367372000e-3, -2.0897130000e-3, & + 1244.0, -6.0652862000, 1.4358498000e-3, -2.0882579000e-3, & + 1245.0, -6.0657793000, 1.4349631000e-3, -2.0868045000e-3, & + 1246.0, -6.0662708000, 1.4340770000e-3, -2.0853527000e-3, & + 1247.0, -6.0667608000, 1.4331916000e-3, -2.0839025000e-3, & + 1248.0, -6.0672491000, 1.4323068000e-3, -2.0824540000e-3, & + 1249.0, -6.0677359000, 1.4314226000e-3, -2.0810070000e-3, & + 1250.0, -6.0682211000, 1.4305391000e-3, -2.0795618000e-3, & + 1251.0, -6.0687047000, 1.4296562000e-3, -2.0781181000e-3, & + 1252.0, -6.0691867000, 1.4287740000e-3, -2.0766760000e-3, & + 1253.0, -6.0696672000, 1.4278925000e-3, -2.0752356000e-3, & + 1254.0, -6.0701462000, 1.4270115000e-3, -2.0737968000e-3, & + 1255.0, -6.0706235000, 1.4261312000e-3, -2.0723596000e-3, & + 1256.0, -6.0710993000, 1.4252516000e-3, -2.0709241000e-3, & + 1257.0, -6.0715736000, 1.4243726000e-3, -2.0694901000e-3, & + 1258.0, -6.0720464000, 1.4234943000e-3, -2.0680577000e-3, & + 1259.0, -6.0725175000, 1.4226166000e-3, -2.0666270000e-3, & + 1260.0, -6.0729872000, 1.4217396000e-3, -2.0651979000e-3, & + 1261.0, -6.0734554000, 1.4208632000e-3, -2.0637703000e-3, & + 1262.0, -6.0739220000, 1.4199874000e-3, -2.0623444000e-3, & + 1263.0, -6.0743871000, 1.4191123000e-3, -2.0609201000e-3, & + 1264.0, -6.0748507000, 1.4182379000e-3, -2.0594973000e-3, & + 1265.0, -6.0753127000, 1.4173641000e-3, -2.0580762000e-3, & + 1266.0, -6.0757733000, 1.4164910000e-3, -2.0566566000e-3, & + 1267.0, -6.0762324000, 1.4156185000e-3, -2.0552387000e-3, & + 1268.0, -6.0766899000, 1.4147466000e-3, -2.0538223000e-3, & + 1269.0, -6.0771460000, 1.4138754000e-3, -2.0524076000e-3, & + 1270.0, -6.0776006000, 1.4130049000e-3, -2.0509944000e-3, & + 1271.0, -6.0780537000, 1.4121350000e-3, -2.0495828000e-3, & + 1272.0, -6.0785054000, 1.4112658000e-3, -2.0481728000e-3, & + 1273.0, -6.0789555000, 1.4103972000e-3, -2.0467643000e-3, & + 1274.0, -6.0794042000, 1.4095293000e-3, -2.0453575000e-3, & + 1275.0, -6.0798515000, 1.4086620000e-3, -2.0439522000e-3, & + 1276.0, -6.0802972000, 1.4077954000e-3, -2.0425485000e-3, & + 1277.0, -6.0807415000, 1.4069294000e-3, -2.0411464000e-3, & + 1278.0, -6.0811844000, 1.4060641000e-3, -2.0397458000e-3, & + 1279.0, -6.0816258000, 1.4051994000e-3, -2.0383468000e-3, & + 1280.0, -6.0820658000, 1.4043354000e-3, -2.0369494000e-3, & + 1281.0, -6.0825043000, 1.4034720000e-3, -2.0355536000e-3, & + 1282.0, -6.0829414000, 1.4026093000e-3, -2.0341593000e-3, & + 1283.0, -6.0833771000, 1.4017473000e-3, -2.0327665000e-3, & + 1284.0, -6.0838114000, 1.4008859000e-3, -2.0313754000e-3, & + 1285.0, -6.0842442000, 1.4000251000e-3, -2.0299858000e-3, & + 1286.0, -6.0846756000, 1.3991650000e-3, -2.0285977000e-3, & + 1287.0, -6.0851056000, 1.3983056000e-3, -2.0272112000e-3, & + 1288.0, -6.0855342000, 1.3974468000e-3, -2.0258263000e-3, & + 1289.0, -6.0859614000, 1.3965887000e-3, -2.0244429000e-3, & + 1290.0, -6.0863871000, 1.3957312000e-3, -2.0230610000e-3, & + 1291.0, -6.0868115000, 1.3948744000e-3, -2.0216807000e-3, & + 1292.0, -6.0872345000, 1.3940183000e-3, -2.0203020000e-3, & + 1293.0, -6.0876561000, 1.3931628000e-3, -2.0189247000e-3, & + 1294.0, -6.0880764000, 1.3923079000e-3, -2.0175491000e-3, & + 1295.0, -6.0884952000, 1.3914537000e-3, -2.0161749000e-3, & + 1296.0, -6.0889127000, 1.3906002000e-3, -2.0148024000e-3, & + 1297.0, -6.0893288000, 1.3897473000e-3, -2.0134313000e-3, & + 1298.0, -6.0897435000, 1.3888951000e-3, -2.0120618000e-3, & + 1299.0, -6.0901569000, 1.3880436000e-3, -2.0106938000e-3, & + 1300.0, -6.0905689000, 1.3871927000e-3, -2.0093273000e-3, & + 1301.0, -6.0909796000, 1.3863424000e-3, -2.0079624000e-3, & + 1302.0, -6.0913889000, 1.3854928000e-3, -2.0065990000e-3, & + 1303.0, -6.0917969000, 1.3846439000e-3, -2.0052371000e-3, & + 1304.0, -6.0922035000, 1.3837956000e-3, -2.0038767000e-3, & + 1305.0, -6.0926088000, 1.3829480000e-3, -2.0025179000e-3, & + 1306.0, -6.0930127000, 1.3821011000e-3, -2.0011606000e-3, & + 1307.0, -6.0934154000, 1.3812548000e-3, -1.9998048000e-3, & + 1308.0, -6.0938167000, 1.3804091000e-3, -1.9984505000e-3, & + 1309.0, -6.0942166000, 1.3795642000e-3, -1.9970977000e-3, & + 1310.0, -6.0946153000, 1.3787199000e-3, -1.9957464000e-3, & + 1311.0, -6.0950127000, 1.3778762000e-3, -1.9943967000e-3, & + 1312.0, -6.0954087000, 1.3770332000e-3, -1.9930484000e-3, & + 1313.0, -6.0958034000, 1.3761909000e-3, -1.9917017000e-3, & + 1314.0, -6.0961969000, 1.3753492000e-3, -1.9903564000e-3, & + 1315.0, -6.0965890000, 1.3745082000e-3, -1.9890127000e-3, & + 1316.0, -6.0969798000, 1.3736678000e-3, -1.9876705000e-3, & + 1317.0, -6.0973694000, 1.3728281000e-3, -1.9863297000e-3, & + 1318.0, -6.0977577000, 1.3719890000e-3, -1.9849905000e-3, & + 1319.0, -6.0981446000, 1.3711507000e-3, -1.9836527000e-3, & + 1320.0, -6.0985303000, 1.3703129000e-3, -1.9823165000e-3, & + 1321.0, -6.0989148000, 1.3694759000e-3, -1.9809817000e-3, & + 1322.0, -6.0992979000, 1.3686395000e-3, -1.9796485000e-3, & + 1323.0, -6.0996798000, 1.3678037000e-3, -1.9783167000e-3, & + 1324.0, -6.1000605000, 1.3669686000e-3, -1.9769864000e-3, & + 1325.0, -6.1004399000, 1.3661342000e-3, -1.9756576000e-3, & + 1326.0, -6.1008180000, 1.3653005000e-3, -1.9743302000e-3, & + 1327.0, -6.1011948000, 1.3644674000e-3, -1.9730044000e-3, & + 1328.0, -6.1015705000, 1.3636349000e-3, -1.9716800000e-3, & + 1329.0, -6.1019449000, 1.3628031000e-3, -1.9703571000e-3, & + 1330.0, -6.1023180000, 1.3619720000e-3, -1.9690357000e-3, & + 1331.0, -6.1026899000, 1.3611416000e-3, -1.9677157000e-3, & + 1332.0, -6.1030606000, 1.3603118000e-3, -1.9663972000e-3, & + 1333.0, -6.1034300000, 1.3594826000e-3, -1.9650802000e-3, & + 1334.0, -6.1037983000, 1.3586541000e-3, -1.9637647000e-3, & + 1335.0, -6.1041653000, 1.3578263000e-3, -1.9624506000e-3, & + 1336.0, -6.1045311000, 1.3569992000e-3, -1.9611380000e-3, & + 1337.0, -6.1048956000, 1.3561727000e-3, -1.9598268000e-3, & + 1338.0, -6.1052590000, 1.3553468000e-3, -1.9585171000e-3, & + 1339.0, -6.1056212000, 1.3545217000e-3, -1.9572089000e-3, & + 1340.0, -6.1059821000, 1.3536972000e-3, -1.9559021000e-3, & + 1341.0, -6.1063419000, 1.3528733000e-3, -1.9545968000e-3, & + 1342.0, -6.1067005000, 1.3520501000e-3, -1.9532929000e-3, & + 1343.0, -6.1070578000, 1.3512276000e-3, -1.9519905000e-3, & + 1344.0, -6.1074140000, 1.3504057000e-3, -1.9506896000e-3, & + 1345.0, -6.1077690000, 1.3495845000e-3, -1.9493900000e-3, & + 1346.0, -6.1081229000, 1.3487640000e-3, -1.9480920000e-3, & + 1347.0, -6.1084755000, 1.3479441000e-3, -1.9467953000e-3, & + 1348.0, -6.1088270000, 1.3471249000e-3, -1.9455001000e-3, & + 1349.0, -6.1091773000, 1.3463063000e-3, -1.9442064000e-3, & + 1350.0, -6.1095265000, 1.3454884000e-3, -1.9429141000e-3, & + 1351.0, -6.1098744000, 1.3446712000e-3, -1.9416232000e-3, & + 1352.0, -6.1102213000, 1.3438546000e-3, -1.9403338000e-3, & + 1353.0, -6.1105669000, 1.3430387000e-3, -1.9390458000e-3, & + 1354.0, -6.1109115000, 1.3422234000e-3, -1.9377592000e-3, & + 1355.0, -6.1112548000, 1.3414088000e-3, -1.9364741000e-3, & + 1356.0, -6.1115971000, 1.3405949000e-3, -1.9351903000e-3, & + 1357.0, -6.1119382000, 1.3397816000e-3, -1.9339081000e-3, & + 1358.0, -6.1122781000, 1.3389690000e-3, -1.9326272000e-3, & + 1359.0, -6.1126170000, 1.3381571000e-3, -1.9313478000e-3, & + 1360.0, -6.1129546000, 1.3373458000e-3, -1.9300697000e-3, & + 1361.0, -6.1132912000, 1.3365352000e-3, -1.9287931000e-3, & + 1362.0, -6.1136267000, 1.3357252000e-3, -1.9275180000e-3, & + 1363.0, -6.1139610000, 1.3349159000e-3, -1.9262442000e-3, & + 1364.0, -6.1142942000, 1.3341073000e-3, -1.9249718000e-3, & + 1365.0, -6.1146263000, 1.3332993000e-3, -1.9237009000e-3, & + 1366.0, -6.1149573000, 1.3324920000e-3, -1.9224314000e-3, & + 1367.0, -6.1152872000, 1.3316853000e-3, -1.9211632000e-3, & + 1368.0, -6.1156160000, 1.3308793000e-3, -1.9198965000e-3, & + 1369.0, -6.1159437000, 1.3300740000e-3, -1.9186312000e-3, & + 1370.0, -6.1162702000, 1.3292693000e-3, -1.9173673000e-3, & + 1371.0, -6.1165957000, 1.3284653000e-3, -1.9161048000e-3, & + 1372.0, -6.1169202000, 1.3276619000e-3, -1.9148437000e-3, & + 1373.0, -6.1172435000, 1.3268592000e-3, -1.9135840000e-3, & + 1374.0, -6.1175657000, 1.3260572000e-3, -1.9123257000e-3, & + 1375.0, -6.1178869000, 1.3252558000e-3, -1.9110688000e-3, & + 1376.0, -6.1182070000, 1.3244551000e-3, -1.9098133000e-3, & + 1377.0, -6.1185260000, 1.3236551000e-3, -1.9085591000e-3, & + 1378.0, -6.1188440000, 1.3228557000e-3, -1.9073064000e-3, & + 1379.0, -6.1191609000, 1.3220569000e-3, -1.9060550000e-3, & + 1380.0, -6.1194767000, 1.3212589000e-3, -1.9048051000e-3, & + 1381.0, -6.1197915000, 1.3204614000e-3, -1.9035565000e-3, & + 1382.0, -6.1201052000, 1.3196647000e-3, -1.9023093000e-3, & + 1383.0, -6.1204179000, 1.3188686000e-3, -1.9010635000e-3, & + 1384.0, -6.1207295000, 1.3180732000e-3, -1.8998190000e-3, & + 1385.0, -6.1210401000, 1.3172784000e-3, -1.8985760000e-3, & + 1386.0, -6.1213496000, 1.3164843000e-3, -1.8973343000e-3, & + 1387.0, -6.1216581000, 1.3156908000e-3, -1.8960940000e-3, & + 1388.0, -6.1219656000, 1.3148980000e-3, -1.8948550000e-3, & + 1389.0, -6.1222720000, 1.3141059000e-3, -1.8936175000e-3, & + 1390.0, -6.1225774000, 1.3133144000e-3, -1.8923813000e-3, & + 1391.0, -6.1228818000, 1.3125236000e-3, -1.8911464000e-3, & + 1392.0, -6.1231851000, 1.3117334000e-3, -1.8899130000e-3, & + 1393.0, -6.1234875000, 1.3109439000e-3, -1.8886809000e-3, & + 1394.0, -6.1237888000, 1.3101551000e-3, -1.8874501000e-3, & + 1395.0, -6.1240891000, 1.3093669000e-3, -1.8862207000e-3, & + 1396.0, -6.1243884000, 1.3085794000e-3, -1.8849927000e-3, & + 1397.0, -6.1246867000, 1.3077925000e-3, -1.8837660000e-3, & + 1398.0, -6.1249840000, 1.3070063000e-3, -1.8825407000e-3, & + 1399.0, -6.1252803000, 1.3062208000e-3, -1.8813168000e-3, & + 1400.0, -6.1255756000, 1.3054359000e-3, -1.8800942000e-3, & + 1401.0, -6.1258699000, 1.3046517000e-3, -1.8788729000e-3, & + 1402.0, -6.1261632000, 1.3038681000e-3, -1.8776530000e-3, & + 1403.0, -6.1264555000, 1.3030852000e-3, -1.8764345000e-3, & + 1404.0, -6.1267469000, 1.3023029000e-3, -1.8752172000e-3, & + 1405.0, -6.1270372000, 1.3015213000e-3, -1.8740014000e-3, & + 1406.0, -6.1273266000, 1.3007404000e-3, -1.8727868000e-3, & + 1407.0, -6.1276150000, 1.2999601000e-3, -1.8715736000e-3, & + 1408.0, -6.1279024000, 1.2991804000e-3, -1.8703618000e-3, & + 1409.0, -6.1281889000, 1.2984015000e-3, -1.8691513000e-3, & + 1410.0, -6.1284744000, 1.2976231000e-3, -1.8679421000e-3, & + 1411.0, -6.1287590000, 1.2968455000e-3, -1.8667343000e-3, & + 1412.0, -6.1290425000, 1.2960685000e-3, -1.8655277000e-3, & + 1413.0, -6.1293252000, 1.2952921000e-3, -1.8643226000e-3, & + 1414.0, -6.1296068000, 1.2945164000e-3, -1.8631187000e-3, & + 1415.0, -6.1298876000, 1.2937414000e-3, -1.8619162000e-3, & + 1416.0, -6.1301673000, 1.2929670000e-3, -1.8607150000e-3, & + 1417.0, -6.1304462000, 1.2921933000e-3, -1.8595151000e-3, & + 1418.0, -6.1307241000, 1.2914202000e-3, -1.8583165000e-3, & + 1419.0, -6.1310010000, 1.2906478000e-3, -1.8571193000e-3, & + 1420.0, -6.1312770000, 1.2898761000e-3, -1.8559234000e-3, & + 1421.0, -6.1315521000, 1.2891050000e-3, -1.8547288000e-3, & + 1422.0, -6.1318263000, 1.2883345000e-3, -1.8535355000e-3, & + 1423.0, -6.1320995000, 1.2875647000e-3, -1.8523435000e-3, & + 1424.0, -6.1323718000, 1.2867956000e-3, -1.8511528000e-3, & + 1425.0, -6.1326432000, 1.2860271000e-3, -1.8499635000e-3, & + 1426.0, -6.1329136000, 1.2852592000e-3, -1.8487754000e-3, & + 1427.0, -6.1331832000, 1.2844921000e-3, -1.8475887000e-3, & + 1428.0, -6.1334518000, 1.2837255000e-3, -1.8464033000e-3, & + 1429.0, -6.1337196000, 1.2829597000e-3, -1.8452191000e-3, & + 1430.0, -6.1339864000, 1.2821945000e-3, -1.8440363000e-3, & + 1431.0, -6.1342523000, 1.2814299000e-3, -1.8428548000e-3, & + 1432.0, -6.1345173000, 1.2806660000e-3, -1.8416745000e-3, & + 1433.0, -6.1347815000, 1.2799027000e-3, -1.8404956000e-3, & + 1434.0, -6.1350447000, 1.2791401000e-3, -1.8393180000e-3, & + 1435.0, -6.1353070000, 1.2783782000e-3, -1.8381416000e-3, & + 1436.0, -6.1355685000, 1.2776168000e-3, -1.8369666000e-3, & + 1437.0, -6.1358290000, 1.2768562000e-3, -1.8357928000e-3, & + 1438.0, -6.1360887000, 1.2760962000e-3, -1.8346203000e-3, & + 1439.0, -6.1363475000, 1.2753368000e-3, -1.8334492000e-3, & + 1440.0, -6.1366054000, 1.2745781000e-3, -1.8322792000e-3 & + /), (/4, lmax+1/)) +end module MOM_load_love_numbers \ No newline at end of file diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 index d798098832..cf9108de61 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 @@ -10,7 +10,7 @@ module MOM_spherical_harmonics implicit none ; private -public spherical_harmonics_init, spherical_harmonics_end, SHOrderDegreeToIndex, calc_lmax +public spherical_harmonics_init, spherical_harmonics_end, order2index, calc_lmax public spherical_harmonics_forward, spherical_harmonics_inverse #include @@ -63,7 +63,7 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) endif do m=0,Nmax - l = SHOrderDegreeToIndex(m, m, Nmax) + l = order2index(m, Nmax) do j=js,je ; do i=is,ie pmn = CS%Pmm(i,j,m+1) @@ -140,7 +140,7 @@ subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) var = 0.0 do m=0,Nmax mFac = sign(1.0, m-0.5)*0.5 + 1.5 - l = SHOrderDegreeToIndex(m, m, Nmax) + l = order2index(m, Nmax) do j=js,je ; do i=is,ie pmn = CS%Pmm(i,j,m+1) @@ -252,22 +252,23 @@ subroutine spherical_harmonics_end(CS) deallocate(CS%aRecurrenceCoeff, CS%bRecurrenceCoeff) end subroutine spherical_harmonics_end +!> The function calc_lmax returns the number of real elements (cosine) of the spherical harmonics, +!! given the maximum degree, function calc_lmax(Nd) result(lmax) - integer :: Nd integer :: lmax + integer, intent(in) :: Nd lmax = (Nd+2) * (Nd+1) / 2 end function calc_lmax -function SHOrderDegreeToIndex(n,m, nOrder) result(l)!{{{ - +!> The function returns the one-dimension index number at (n=0, m=m), given order (m) and maximum degree (Nd) +!! The one-dimensional array is organized following degree being the faster moving dimension. +function order2index(m, Nd) result(l) integer :: l - integer :: n - integer :: m - integer :: nOrder - - l = (nOrder+1)*m - m*(m+1)/2 + n+1 + integer, intent(in) :: m + integer, intent(in) :: Nd -end function SHOrderDegreeToIndex + l = ((Nd+1) + (Nd+1-(m-1)))*m/2 + 1 +end function order2index end module MOM_spherical_harmonics \ No newline at end of file diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 04bfe23aac..384f6b19d2 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -12,9 +12,10 @@ module MOM_tidal_forcing use MOM_io, only : field_exists, file_exists, MOM_read_data use MOM_time_manager, only : set_date, time_type, time_type_to_real, operator(-) use MOM_unit_scaling, only : unit_scale_type -use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end, calc_lmax -use MOM_spherical_harmonics, only : spherical_harmonics_forward, spherical_harmonics_inverse, SHOrderDegreeToIndex +use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end, order2index, calc_lmax +use MOM_spherical_harmonics, only : spherical_harmonics_forward, spherical_harmonics_inverse use MOM_spherical_harmonics, only : sht_CS +use MOM_load_love_numbers, only : LoveDat implicit none ; private @@ -76,6 +77,7 @@ module MOM_tidal_forcing amp_prev(:,:,:) !< The amplitude of the previous tidal solution [Z ~> m]. type(sht_CS) :: sht integer :: sal_sht_Nd + real, allocatable :: LoveScaling(:) end type tidal_forcing_CS integer :: id_clock_tides !< CPU clock for tides @@ -262,6 +264,7 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) character(len=128) :: mesg character(len=200) :: tidal_input_files(4*MAX_CONSTITUENTS) integer :: i, j, c, is, ie, js, je, isd, ied, jsd, jed, nc + integer :: lmax is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec isd = G%isd ; ied = G%ied ; jsd = G%jsd; jed = G%jed @@ -534,6 +537,9 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) "The maximum degree of the spherical harmonics transformation used for "// & "calculating the self-attraction and loading term for tides.", & default=0, do_not_log=.not. CS%tidal_sal_sht) + lmax = calc_lmax(CS%sal_sht_Nd) + allocate(CS%LoveScaling(lmax)) + call calc_love_scaling(CS%sal_sht_Nd, CS%LoveScaling) call spherical_harmonics_init(G, param_file, CS%sht) id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_MODULE) endif @@ -542,1496 +548,42 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) end subroutine tidal_forcing_init -subroutine getloadLoveNums(nlm, LoveScaling) !{{{ +subroutine calc_love_scaling(nlm, LoveScaling) + integer, intent(in) :: nlm !< Maximum spherical harmonics degree + real, dimension(:), intent(out) :: LoveScaling ! Scaling factors for inverse SHT - integer, intent(in) :: nlm - real, dimension(:), intent(out) :: LoveScaling - - real, dimension(:), allocatable :: H, L, K - real, dimension(:,:), allocatable :: LoveDat + ! Local variables + real, parameter :: rhoE = 5517.0 ! Average density of Earth (kg/m^3) + real, parameter :: rhoW = 1035.0 ! Density of water (kg/m^3) + real, dimension(:), allocatable :: HDat, LDat, KDat real :: H1, L1, K1 - integer :: i, j, n, m - real, parameter :: rhoE=5517.0 ! Average density of Earth (kg/m^3) - real, parameter :: rhoW=1035.0 ! Density of water (kg/m^3) - integer, parameter :: lmax=1440 - - allocate(LoveDat(4,lmax+1)) - - LoveDat(1:4,1) = (/ 0.0, 0.0000000000, 0.0000000000, -1.0000000000 /) !{{{ - LoveDat(1:4,2) = (/ 1.0, -1.2858777580, -8.9608179370e-1, -1.0000000000 /) - LoveDat(1:4,3) = (/ 2.0, -9.9079949000e-1, 2.3286695000e-2, -3.0516104000e-1 /) - LoveDat(1:4,4) = (/ 3.0, -1.0499631000, 6.9892136000e-2, -1.9585733000e-1 /) - LoveDat(1:4,5) = (/ 4.0, -1.0526477000, 5.8670467000e-2, -1.3352284000e-1 /) - LoveDat(1:4,6) = (/ 5.0, -1.0855918000, 4.6165153000e-2, -1.0456531000e-1 /) - LoveDat(1:4,7) = (/ 6.0, -1.1431163000, 3.8586926000e-2, -9.0184841000e-2 /) - LoveDat(1:4,8) = (/ 7.0, -1.2116273000, 3.4198827000e-2, -8.1906787000e-2 /) - LoveDat(1:4,9) = (/ 8.0, -1.2831157000, 3.1474998000e-2, -7.6379141000e-2 /) - LoveDat(1:4,10) = (/ 9.0, -1.3538554000, 2.9624407000e-2, -7.2250183000e-2 /) - LoveDat(1:4,11) = (/ 10.0, -1.4223516000, 2.8273961000e-2, -6.8934145000e-2 /) - LoveDat(1:4,12) = (/ 11.0, -1.4881117000, 2.7242278000e-2, -6.6147992000e-2 /) - LoveDat(1:4,13) = (/ 12.0, -1.5510428000, 2.6431124000e-2, -6.3736253000e-2 /) - LoveDat(1:4,14) = (/ 13.0, -1.6111895000, 2.5779507000e-2, -6.1602870000e-2 /) - LoveDat(1:4,15) = (/ 14.0, -1.6686329000, 2.5245139000e-2, -5.9683159000e-2 /) - LoveDat(1:4,16) = (/ 15.0, -1.7234569000, 2.4796803000e-2, -5.7931180000e-2 /) - LoveDat(1:4,17) = (/ 16.0, -1.7757418000, 2.4410861000e-2, -5.6313294000e-2 /) - LoveDat(1:4,18) = (/ 17.0, -1.8255646000, 2.4069336000e-2, -5.4804452000e-2 /) - LoveDat(1:4,19) = (/ 18.0, -1.8730019000, 2.3758645000e-2, -5.3385807000e-2 /) - LoveDat(1:4,20) = (/ 19.0, -1.9181321000, 2.3468646000e-2, -5.2043088000e-2 /) - LoveDat(1:4,21) = (/ 20.0, -1.9610366000, 2.3191893000e-2, -5.0765423000e-2 /) - LoveDat(1:4,22) = (/ 21.0, -2.0018000000, 2.2923032000e-2, -4.9544487000e-2 /) - LoveDat(1:4,23) = (/ 22.0, -2.0405101000, 2.2658321000e-2, -4.8373866000e-2 /) - LoveDat(1:4,24) = (/ 23.0, -2.0772571000, 2.2395242000e-2, -4.7248575000e-2 /) - LoveDat(1:4,25) = (/ 24.0, -2.1121328000, 2.2132200000e-2, -4.6164708000e-2 /) - LoveDat(1:4,26) = (/ 25.0, -2.1452296000, 2.1868280000e-2, -4.5119160000e-2 /) - LoveDat(1:4,27) = (/ 26.0, -2.1766398000, 2.1603063000e-2, -4.4109431000e-2 /) - LoveDat(1:4,28) = (/ 27.0, -2.2064546000, 2.1336479000e-2, -4.3133464000e-2 /) - LoveDat(1:4,29) = (/ 28.0, -2.2347634000, 2.1068700000e-2, -4.2189540000e-2 /) - LoveDat(1:4,30) = (/ 29.0, -2.2616531000, 2.0800053000e-2, -4.1276184000e-2 /) - LoveDat(1:4,31) = (/ 30.0, -2.2872080000, 2.0530962000e-2, -4.0392105000e-2 /) - LoveDat(1:4,32) = (/ 31.0, -2.3115088000, 2.0261897000e-2, -3.9536148000e-2 /) - LoveDat(1:4,33) = (/ 32.0, -2.3346328000, 1.9993346000e-2, -3.8707260000e-2 /) - LoveDat(1:4,34) = (/ 33.0, -2.3566536000, 1.9725790000e-2, -3.7904463000e-2 /) - LoveDat(1:4,35) = (/ 34.0, -2.3776409000, 1.9459686000e-2, -3.7126837000e-2 /) - LoveDat(1:4,36) = (/ 35.0, -2.3976605000, 1.9195459000e-2, -3.6373510000e-2 /) - LoveDat(1:4,37) = (/ 36.0, -2.4167746000, 1.8933494000e-2, -3.5643644000e-2 /) - LoveDat(1:4,38) = (/ 37.0, -2.4350414000, 1.8674136000e-2, -3.4936432000e-2 /) - LoveDat(1:4,39) = (/ 38.0, -2.4525156000, 1.8417687000e-2, -3.4251094000e-2 /) - LoveDat(1:4,40) = (/ 39.0, -2.4692484000, 1.8164407000e-2, -3.3586873000e-2 /) - LoveDat(1:4,41) = (/ 40.0, -2.4852876000, 1.7914518000e-2, -3.2943035000e-2 /) - LoveDat(1:4,42) = (/ 41.0, -2.5006779000, 1.7668203000e-2, -3.2318866000e-2 /) - LoveDat(1:4,43) = (/ 42.0, -2.5154609000, 1.7425613000e-2, -3.1713675000e-2 /) - LoveDat(1:4,44) = (/ 43.0, -2.5296755000, 1.7186866000e-2, -3.1126789000e-2 /) - LoveDat(1:4,45) = (/ 44.0, -2.5433577000, 1.6952053000e-2, -3.0557557000e-2 /) - LoveDat(1:4,46) = (/ 45.0, -2.5565412000, 1.6721240000e-2, -3.0005347000e-2 /) - LoveDat(1:4,47) = (/ 46.0, -2.5692574000, 1.6494470000e-2, -2.9469547000e-2 /) - LoveDat(1:4,48) = (/ 47.0, -2.5815353000, 1.6271769000e-2, -2.8949568000e-2 /) - LoveDat(1:4,49) = (/ 48.0, -2.5934022000, 1.6053144000e-2, -2.8444838000e-2 /) - LoveDat(1:4,50) = (/ 49.0, -2.6048833000, 1.5838586000e-2, -2.7954806000e-2 /) - LoveDat(1:4,51) = (/ 50.0, -2.6160021000, 1.5628077000e-2, -2.7478940000e-2 /) - LoveDat(1:4,52) = (/ 51.0, -2.6267805000, 1.5421585000e-2, -2.7016729000e-2 /) - LoveDat(1:4,53) = (/ 52.0, -2.6372389000, 1.5219071000e-2, -2.6567679000e-2 /) - LoveDat(1:4,54) = (/ 53.0, -2.6473964000, 1.5020486000e-2, -2.6131317000e-2 /) - LoveDat(1:4,55) = (/ 54.0, -2.6572706000, 1.4825779000e-2, -2.5707185000e-2 /) - LoveDat(1:4,56) = (/ 55.0, -2.6668781000, 1.4634888000e-2, -2.5294846000e-2 /) - LoveDat(1:4,57) = (/ 56.0, -2.6762345000, 1.4447752000e-2, -2.4893877000e-2 /) - LoveDat(1:4,58) = (/ 57.0, -2.6853540000, 1.4264303000e-2, -2.4503874000e-2 /) - LoveDat(1:4,59) = (/ 58.0, -2.6942503000, 1.4084474000e-2, -2.4124449000e-2 /) - LoveDat(1:4,60) = (/ 59.0, -2.7029358000, 1.3908192000e-2, -2.3755228000e-2 /) - LoveDat(1:4,61) = (/ 60.0, -2.7114225000, 1.3735386000e-2, -2.3395852000e-2 /) - LoveDat(1:4,62) = (/ 61.0, -2.7197214000, 1.3565983000e-2, -2.3045980000e-2 /) - LoveDat(1:4,63) = (/ 62.0, -2.7278428000, 1.3399909000e-2, -2.2705280000e-2 /) - LoveDat(1:4,64) = (/ 63.0, -2.7357965000, 1.3237092000e-2, -2.2373437000e-2 /) - LoveDat(1:4,65) = (/ 64.0, -2.7435916000, 1.3077458000e-2, -2.2050147000e-2 /) - LoveDat(1:4,66) = (/ 65.0, -2.7512366000, 1.2920935000e-2, -2.1735119000e-2 /) - LoveDat(1:4,67) = (/ 66.0, -2.7587397000, 1.2767451000e-2, -2.1428073000e-2 /) - LoveDat(1:4,68) = (/ 67.0, -2.7661083000, 1.2616936000e-2, -2.1128742000e-2 /) - LoveDat(1:4,69) = (/ 68.0, -2.7733496000, 1.2469319000e-2, -2.0836869000e-2 /) - LoveDat(1:4,70) = (/ 69.0, -2.7804703000, 1.2324532000e-2, -2.0552206000e-2 /) - LoveDat(1:4,71) = (/ 70.0, -2.7874767000, 1.2182508000e-2, -2.0274516000e-2 /) - LoveDat(1:4,72) = (/ 71.0, -2.7943748000, 1.2043181000e-2, -2.0003572000e-2 /) - LoveDat(1:4,73) = (/ 72.0, -2.8011702000, 1.1906487000e-2, -1.9739156000e-2 /) - LoveDat(1:4,74) = (/ 73.0, -2.8078682000, 1.1772362000e-2, -1.9481058000e-2 /) - LoveDat(1:4,75) = (/ 74.0, -2.8144738000, 1.1640746000e-2, -1.9229076000e-2 /) - LoveDat(1:4,76) = (/ 75.0, -2.8209918000, 1.1511578000e-2, -1.8983017000e-2 /) - LoveDat(1:4,77) = (/ 76.0, -2.8274266000, 1.1384799000e-2, -1.8742695000e-2 /) - LoveDat(1:4,78) = (/ 77.0, -2.8337824000, 1.1260352000e-2, -1.8507931000e-2 /) - LoveDat(1:4,79) = (/ 78.0, -2.8400633000, 1.1138183000e-2, -1.8278553000e-2 /) - LoveDat(1:4,80) = (/ 79.0, -2.8462730000, 1.1018236000e-2, -1.8054395000e-2 /) - LoveDat(1:4,81) = (/ 80.0, -2.8524152000, 1.0900460000e-2, -1.7835300000e-2 /) - LoveDat(1:4,82) = (/ 81.0, -2.8584932000, 1.0784802000e-2, -1.7621113000e-2 /) - LoveDat(1:4,83) = (/ 82.0, -2.8645103000, 1.0671213000e-2, -1.7411688000e-2 /) - LoveDat(1:4,84) = (/ 83.0, -2.8704696000, 1.0559645000e-2, -1.7206882000e-2 /) - LoveDat(1:4,85) = (/ 84.0, -2.8763739000, 1.0450051000e-2, -1.7006560000e-2 /) - LoveDat(1:4,86) = (/ 85.0, -2.8822260000, 1.0342384000e-2, -1.6810590000e-2 /) - LoveDat(1:4,87) = (/ 86.0, -2.8880285000, 1.0236599000e-2, -1.6618845000e-2 /) - LoveDat(1:4,88) = (/ 87.0, -2.8937839000, 1.0132655000e-2, -1.6431203000e-2 /) - LoveDat(1:4,89) = (/ 88.0, -2.8994945000, 1.0030508000e-2, -1.6247547000e-2 /) - LoveDat(1:4,90) = (/ 89.0, -2.9051627000, 9.9301169000e-3, -1.6067762000e-2 /) - LoveDat(1:4,91) = (/ 90.0, -2.9107905000, 9.8314429000e-3, -1.5891741000e-2 /) - LoveDat(1:4,92) = (/ 91.0, -2.9163799000, 9.7344467000e-3, -1.5719376000e-2 /) - LoveDat(1:4,93) = (/ 92.0, -2.9219330000, 9.6390907000e-3, -1.5550567000e-2 /) - LoveDat(1:4,94) = (/ 93.0, -2.9274514000, 9.5453383000e-3, -1.5385215000e-2 /) - LoveDat(1:4,95) = (/ 94.0, -2.9329370000, 9.4531538000e-3, -1.5223225000e-2 /) - LoveDat(1:4,96) = (/ 95.0, -2.9383913000, 9.3625026000e-3, -1.5064506000e-2 /) - LoveDat(1:4,97) = (/ 96.0, -2.9438161000, 9.2733509000e-3, -1.4908968000e-2 /) - LoveDat(1:4,98) = (/ 97.0, -2.9492127000, 9.1856660000e-3, -1.4756526000e-2 /) - LoveDat(1:4,99) = (/ 98.0, -2.9545826000, 9.0994159000e-3, -1.4607099000e-2 /) - LoveDat(1:4,100) = (/ 99.0, -2.9599272000, 9.0145695000e-3, -1.4460604000e-2 /) - LoveDat(1:4,101) = (/ 100.0, -2.9652476000, 8.9310967000e-3, -1.4316967000e-2 /) - LoveDat(1:4,102) = (/ 101.0, -2.9705453000, 8.8489681000e-3, -1.4176111000e-2 /) - LoveDat(1:4,103) = (/ 102.0, -2.9758213000, 8.7681548000e-3, -1.4037965000e-2 /) - LoveDat(1:4,104) = (/ 103.0, -2.9810767000, 8.6886292000e-3, -1.3902458000e-2 /) - LoveDat(1:4,105) = (/ 104.0, -2.9863125000, 8.6103640000e-3, -1.3769523000e-2 /) - LoveDat(1:4,106) = (/ 105.0, -2.9915299000, 8.5333328000e-3, -1.3639094000e-2 /) - LoveDat(1:4,107) = (/ 106.0, -2.9967298000, 8.4575097000e-3, -1.3511108000e-2 /) - LoveDat(1:4,108) = (/ 107.0, -3.0019129000, 8.3828699000e-3, -1.3385503000e-2 /) - LoveDat(1:4,109) = (/ 108.0, -3.0070803000, 8.3093886000e-3, -1.3262220000e-2 /) - LoveDat(1:4,110) = (/ 109.0, -3.0122328000, 8.2370423000e-3, -1.3141201000e-2 /) - LoveDat(1:4,111) = (/ 110.0, -3.0173710000, 8.1658076000e-3, -1.3022390000e-2 /) - LoveDat(1:4,112) = (/ 111.0, -3.0224958000, 8.0956619000e-3, -1.2905734000e-2 /) - LoveDat(1:4,113) = (/ 112.0, -3.0276079000, 8.0265832000e-3, -1.2791179000e-2 /) - LoveDat(1:4,114) = (/ 113.0, -3.0327080000, 7.9585500000e-3, -1.2678675000e-2 /) - LoveDat(1:4,115) = (/ 114.0, -3.0377966000, 7.8915413000e-3, -1.2568172000e-2 /) - LoveDat(1:4,116) = (/ 115.0, -3.0428744000, 7.8255367000e-3, -1.2459622000e-2 /) - LoveDat(1:4,117) = (/ 116.0, -3.0479420000, 7.7605163000e-3, -1.2352979000e-2 /) - LoveDat(1:4,118) = (/ 117.0, -3.0529999000, 7.6964606000e-3, -1.2248198000e-2 /) - LoveDat(1:4,119) = (/ 118.0, -3.0580486000, 7.6333507000e-3, -1.2145235000e-2 /) - LoveDat(1:4,120) = (/ 119.0, -3.0630887000, 7.5711680000e-3, -1.2044048000e-2 /) - LoveDat(1:4,121) = (/ 120.0, -3.0681205000, 7.5098946000e-3, -1.1944594000e-2 /) - LoveDat(1:4,122) = (/ 121.0, -3.0731446000, 7.4495128000e-3, -1.1846835000e-2 /) - LoveDat(1:4,123) = (/ 122.0, -3.0781614000, 7.3900054000e-3, -1.1750732000e-2 /) - LoveDat(1:4,124) = (/ 123.0, -3.0831713000, 7.3313557000e-3, -1.1656245000e-2 /) - LoveDat(1:4,125) = (/ 124.0, -3.0881747000, 7.2735474000e-3, -1.1563340000e-2 /) - LoveDat(1:4,126) = (/ 125.0, -3.0931718000, 7.2165644000e-3, -1.1471980000e-2 /) - LoveDat(1:4,127) = (/ 126.0, -3.0981632000, 7.1603911000e-3, -1.1382130000e-2 /) - LoveDat(1:4,128) = (/ 127.0, -3.1031490000, 7.1050124000e-3, -1.1293757000e-2 /) - LoveDat(1:4,129) = (/ 128.0, -3.1081296000, 7.0504134000e-3, -1.1206828000e-2 /) - LoveDat(1:4,130) = (/ 129.0, -3.1131054000, 6.9965795000e-3, -1.1121311000e-2 /) - LoveDat(1:4,131) = (/ 130.0, -3.1180765000, 6.9434967000e-3, -1.1037175000e-2 /) - LoveDat(1:4,132) = (/ 131.0, -3.1230433000, 6.8911509000e-3, -1.0954391000e-2 /) - LoveDat(1:4,133) = (/ 132.0, -3.1280059000, 6.8395288000e-3, -1.0872928000e-2 /) - LoveDat(1:4,134) = (/ 133.0, -3.1329647000, 6.7886171000e-3, -1.0792758000e-2 /) - LoveDat(1:4,135) = (/ 134.0, -3.1379199000, 6.7384029000e-3, -1.0713853000e-2 /) - LoveDat(1:4,136) = (/ 135.0, -3.1428716000, 6.6888735000e-3, -1.0636187000e-2 /) - LoveDat(1:4,137) = (/ 136.0, -3.1478201000, 6.6400168000e-3, -1.0559733000e-2 /) - LoveDat(1:4,138) = (/ 137.0, -3.1527656000, 6.5918206000e-3, -1.0484466000e-2 /) - LoveDat(1:4,139) = (/ 138.0, -3.1577082000, 6.5442732000e-3, -1.0410360000e-2 /) - LoveDat(1:4,140) = (/ 139.0, -3.1626481000, 6.4973631000e-3, -1.0337392000e-2 /) - LoveDat(1:4,141) = (/ 140.0, -3.1675855000, 6.4510790000e-3, -1.0265537000e-2 /) - LoveDat(1:4,142) = (/ 141.0, -3.1725205000, 6.4054099000e-3, -1.0194773000e-2 /) - LoveDat(1:4,143) = (/ 142.0, -3.1774533000, 6.3603452000e-3, -1.0125078000e-2 /) - LoveDat(1:4,144) = (/ 143.0, -3.1823840000, 6.3158742000e-3, -1.0056429000e-2 /) - LoveDat(1:4,145) = (/ 144.0, -3.1873127000, 6.2719868000e-3, -9.9888045000e-3 /) - LoveDat(1:4,146) = (/ 145.0, -3.1922396000, 6.2286729000e-3, -9.9221850000e-3 /) - LoveDat(1:4,147) = (/ 146.0, -3.1971648000, 6.1859227000e-3, -9.8565496000e-3 /) - LoveDat(1:4,148) = (/ 147.0, -3.2020883000, 6.1437265000e-3, -9.7918788000e-3 /) - LoveDat(1:4,149) = (/ 148.0, -3.2070102000, 6.1020749000e-3, -9.7281532000e-3 /) - LoveDat(1:4,150) = (/ 149.0, -3.2119308000, 6.0609589000e-3, -9.6653542000e-3 /) - LoveDat(1:4,151) = (/ 150.0, -3.2168500000, 6.0203693000e-3, -9.6034635000e-3 /) - LoveDat(1:4,152) = (/ 151.0, -3.2217679000, 5.9802974000e-3, -9.5424633000e-3 /) - LoveDat(1:4,153) = (/ 152.0, -3.2266847000, 5.9407346000e-3, -9.4823362000e-3 /) - LoveDat(1:4,154) = (/ 153.0, -3.2316003000, 5.9016724000e-3, -9.4230652000e-3 /) - LoveDat(1:4,155) = (/ 154.0, -3.2365149000, 5.8631026000e-3, -9.3646338000e-3 /) - LoveDat(1:4,156) = (/ 155.0, -3.2414284000, 5.8250172000e-3, -9.3070259000e-3 /) - LoveDat(1:4,157) = (/ 156.0, -3.2463411000, 5.7874081000e-3, -9.2502257000e-3 /) - LoveDat(1:4,158) = (/ 157.0, -3.2512529000, 5.7502678000e-3, -9.1942178000e-3 /) - LoveDat(1:4,159) = (/ 158.0, -3.2561639000, 5.7135886000e-3, -9.1389873000e-3 /) - LoveDat(1:4,160) = (/ 159.0, -3.2610741000, 5.6773630000e-3, -9.0845194000e-3 /) - LoveDat(1:4,161) = (/ 160.0, -3.2659835000, 5.6415839000e-3, -9.0308000000e-3 /) - LoveDat(1:4,162) = (/ 161.0, -3.2708923000, 5.6062442000e-3, -8.9778149000e-3 /) - LoveDat(1:4,163) = (/ 162.0, -3.2758004000, 5.5713368000e-3, -8.9255506000e-3 /) - LoveDat(1:4,164) = (/ 163.0, -3.2807079000, 5.5368550000e-3, -8.8739938000e-3 /) - LoveDat(1:4,165) = (/ 164.0, -3.2856148000, 5.5027920000e-3, -8.8231314000e-3 /) - LoveDat(1:4,166) = (/ 165.0, -3.2905211000, 5.4691413000e-3, -8.7729507000e-3 /) - LoveDat(1:4,167) = (/ 166.0, -3.2954269000, 5.4358966000e-3, -8.7234394000e-3 /) - LoveDat(1:4,168) = (/ 167.0, -3.3003322000, 5.4030515000e-3, -8.6745852000e-3 /) - LoveDat(1:4,169) = (/ 168.0, -3.3052370000, 5.3705998000e-3, -8.6263763000e-3 /) - LoveDat(1:4,170) = (/ 169.0, -3.3101414000, 5.3385356000e-3, -8.5788012000e-3 /) - LoveDat(1:4,171) = (/ 170.0, -3.3150452000, 5.3068529000e-3, -8.5318484000e-3 /) - LoveDat(1:4,172) = (/ 171.0, -3.3199486000, 5.2755459000e-3, -8.4855070000e-3 /) - LoveDat(1:4,173) = (/ 172.0, -3.3248516000, 5.2446089000e-3, -8.4397661000e-3 /) - LoveDat(1:4,174) = (/ 173.0, -3.3297541000, 5.2140364000e-3, -8.3946150000e-3 /) - LoveDat(1:4,175) = (/ 174.0, -3.3346563000, 5.1838229000e-3, -8.3500435000e-3 /) - LoveDat(1:4,176) = (/ 175.0, -3.3395580000, 5.1539630000e-3, -8.3060415000e-3 /) - LoveDat(1:4,177) = (/ 176.0, -3.3444593000, 5.1244515000e-3, -8.2625990000e-3 /) - LoveDat(1:4,178) = (/ 177.0, -3.3493602000, 5.0952833000e-3, -8.2197063000e-3 /) - LoveDat(1:4,179) = (/ 178.0, -3.3542607000, 5.0664532000e-3, -8.1773539000e-3 /) - LoveDat(1:4,180) = (/ 179.0, -3.3591609000, 5.0379563000e-3, -8.1355327000e-3 /) - LoveDat(1:4,181) = (/ 180.0, -3.3640606000, 5.0097879000e-3, -8.0942335000e-3 /) - LoveDat(1:4,182) = (/ 181.0, -3.3689599000, 4.9819430000e-3, -8.0534474000e-3 /) - LoveDat(1:4,183) = (/ 182.0, -3.3738588000, 4.9544170000e-3, -8.0131658000e-3 /) - LoveDat(1:4,184) = (/ 183.0, -3.3787572000, 4.9272053000e-3, -7.9733801000e-3 /) - LoveDat(1:4,185) = (/ 184.0, -3.3836553000, 4.9003034000e-3, -7.9340821000e-3 /) - LoveDat(1:4,186) = (/ 185.0, -3.3885529000, 4.8737069000e-3, -7.8952635000e-3 /) - LoveDat(1:4,187) = (/ 186.0, -3.3934501000, 4.8474114000e-3, -7.8569164000e-3 /) - LoveDat(1:4,188) = (/ 187.0, -3.3983469000, 4.8214127000e-3, -7.8190330000e-3 /) - LoveDat(1:4,189) = (/ 188.0, -3.4032432000, 4.7957066000e-3, -7.7816057000e-3 /) - LoveDat(1:4,190) = (/ 189.0, -3.4081390000, 4.7702889000e-3, -7.7446269000e-3 /) - LoveDat(1:4,191) = (/ 190.0, -3.4130344000, 4.7451557000e-3, -7.7080893000e-3 /) - LoveDat(1:4,192) = (/ 191.0, -3.4179292000, 4.7203030000e-3, -7.6719857000e-3 /) - LoveDat(1:4,193) = (/ 192.0, -3.4228236000, 4.6957268000e-3, -7.6363091000e-3 /) - LoveDat(1:4,194) = (/ 193.0, -3.4277174000, 4.6714235000e-3, -7.6010526000e-3 /) - LoveDat(1:4,195) = (/ 194.0, -3.4326107000, 4.6473891000e-3, -7.5662095000e-3 /) - LoveDat(1:4,196) = (/ 195.0, -3.4375035000, 4.6236200000e-3, -7.5317730000e-3 /) - LoveDat(1:4,197) = (/ 196.0, -3.4423957000, 4.6001126000e-3, -7.4977367000e-3 /) - LoveDat(1:4,198) = (/ 197.0, -3.4472873000, 4.5768634000e-3, -7.4640943000e-3 /) - LoveDat(1:4,199) = (/ 198.0, -3.4521783000, 4.5538688000e-3, -7.4308395000e-3 /) - LoveDat(1:4,200) = (/ 199.0, -3.4570687000, 4.5311254000e-3, -7.3979662000e-3 /) - LoveDat(1:4,201) = (/ 200.0, -3.4619585000, 4.5086298000e-3, -7.3654685000e-3 /) - LoveDat(1:4,202) = (/ 201.0, -3.4668476000, 4.4863788000e-3, -7.3333403000e-3 /) - LoveDat(1:4,203) = (/ 202.0, -3.4717360000, 4.4643689000e-3, -7.3015761000e-3 /) - LoveDat(1:4,204) = (/ 203.0, -3.4766237000, 4.4425971000e-3, -7.2701701000e-3 /) - LoveDat(1:4,205) = (/ 204.0, -3.4815107000, 4.4210601000e-3, -7.2391168000e-3 /) - LoveDat(1:4,206) = (/ 205.0, -3.4863970000, 4.3997550000e-3, -7.2084108000e-3 /) - LoveDat(1:4,207) = (/ 206.0, -3.4912825000, 4.3786785000e-3, -7.1780467000e-3 /) - LoveDat(1:4,208) = (/ 207.0, -3.4961672000, 4.3578278000e-3, -7.1480193000e-3 /) - LoveDat(1:4,209) = (/ 208.0, -3.5010512000, 4.3371999000e-3, -7.1183236000e-3 /) - LoveDat(1:4,210) = (/ 209.0, -3.5059343000, 4.3167918000e-3, -7.0889544000e-3 /) - LoveDat(1:4,211) = (/ 210.0, -3.5108165000, 4.2966008000e-3, -7.0599068000e-3 /) - LoveDat(1:4,212) = (/ 211.0, -3.5156979000, 4.2766239000e-3, -7.0311760000e-3 /) - LoveDat(1:4,213) = (/ 212.0, -3.5205784000, 4.2568586000e-3, -7.0027573000e-3 /) - LoveDat(1:4,214) = (/ 213.0, -3.5254580000, 4.2373019000e-3, -6.9746460000e-3 /) - LoveDat(1:4,215) = (/ 214.0, -3.5303366000, 4.2179514000e-3, -6.9468375000e-3 /) - LoveDat(1:4,216) = (/ 215.0, -3.5352143000, 4.1988043000e-3, -6.9193272000e-3 /) - LoveDat(1:4,217) = (/ 216.0, -3.5400909000, 4.1798580000e-3, -6.8921109000e-3 /) - LoveDat(1:4,218) = (/ 217.0, -3.5449666000, 4.1611101000e-3, -6.8651842000e-3 /) - LoveDat(1:4,219) = (/ 218.0, -3.5498412000, 4.1425580000e-3, -6.8385428000e-3 /) - LoveDat(1:4,220) = (/ 219.0, -3.5547147000, 4.1241992000e-3, -6.8121826000e-3 /) - LoveDat(1:4,221) = (/ 220.0, -3.5595871000, 4.1060313000e-3, -6.7860995000e-3 /) - LoveDat(1:4,222) = (/ 221.0, -3.5644584000, 4.0880520000e-3, -6.7602894000e-3 /) - LoveDat(1:4,223) = (/ 222.0, -3.5693286000, 4.0702588000e-3, -6.7347484000e-3 /) - LoveDat(1:4,224) = (/ 223.0, -3.5741976000, 4.0526495000e-3, -6.7094726000e-3 /) - LoveDat(1:4,225) = (/ 224.0, -3.5790654000, 4.0352217000e-3, -6.6844583000e-3 /) - LoveDat(1:4,226) = (/ 225.0, -3.5839320000, 4.0179733000e-3, -6.6597016000e-3 /) - LoveDat(1:4,227) = (/ 226.0, -3.5887973000, 4.0009020000e-3, -6.6351989000e-3 /) - LoveDat(1:4,228) = (/ 227.0, -3.5936613000, 3.9840057000e-3, -6.6109466000e-3 /) - LoveDat(1:4,229) = (/ 228.0, -3.5985240000, 3.9672821000e-3, -6.5869411000e-3 /) - LoveDat(1:4,230) = (/ 229.0, -3.6033854000, 3.9507293000e-3, -6.5631791000e-3 /) - LoveDat(1:4,231) = (/ 230.0, -3.6082455000, 3.9343450000e-3, -6.5396569000e-3 /) - LoveDat(1:4,232) = (/ 231.0, -3.6131041000, 3.9181273000e-3, -6.5163713000e-3 /) - LoveDat(1:4,233) = (/ 232.0, -3.6179613000, 3.9020742000e-3, -6.4933190000e-3 /) - LoveDat(1:4,234) = (/ 233.0, -3.6228171000, 3.8861836000e-3, -6.4704966000e-3 /) - LoveDat(1:4,235) = (/ 234.0, -3.6276714000, 3.8704536000e-3, -6.4479012000e-3 /) - LoveDat(1:4,236) = (/ 235.0, -3.6325242000, 3.8548822000e-3, -6.4255293000e-3 /) - LoveDat(1:4,237) = (/ 236.0, -3.6373754000, 3.8394677000e-3, -6.4033781000e-3 /) - LoveDat(1:4,238) = (/ 237.0, -3.6422252000, 3.8242080000e-3, -6.3814445000e-3 /) - LoveDat(1:4,239) = (/ 238.0, -3.6470733000, 3.8091013000e-3, -6.3597254000e-3 /) - LoveDat(1:4,240) = (/ 239.0, -3.6519198000, 3.7941458000e-3, -6.3382179000e-3 /) - LoveDat(1:4,241) = (/ 240.0, -3.6567647000, 3.7793398000e-3, -6.3169193000e-3 /) - LoveDat(1:4,242) = (/ 241.0, -3.6616079000, 3.7646814000e-3, -6.2958265000e-3 /) - LoveDat(1:4,243) = (/ 242.0, -3.6664494000, 3.7501690000e-3, -6.2749370000e-3 /) - LoveDat(1:4,244) = (/ 243.0, -3.6712891000, 3.7358007000e-3, -6.2542478000e-3 /) - LoveDat(1:4,245) = (/ 244.0, -3.6761271000, 3.7215749000e-3, -6.2337563000e-3 /) - LoveDat(1:4,246) = (/ 245.0, -3.6809634000, 3.7074899000e-3, -6.2134599000e-3 /) - LoveDat(1:4,247) = (/ 246.0, -3.6857978000, 3.6935441000e-3, -6.1933559000e-3 /) - LoveDat(1:4,248) = (/ 247.0, -3.6906303000, 3.6797359000e-3, -6.1734419000e-3 /) - LoveDat(1:4,249) = (/ 248.0, -3.6954610000, 3.6660636000e-3, -6.1537152000e-3 /) - LoveDat(1:4,250) = (/ 249.0, -3.7002898000, 3.6525257000e-3, -6.1341734000e-3 /) - LoveDat(1:4,251) = (/ 250.0, -3.7051167000, 3.6391206000e-3, -6.1148140000e-3 /) - LoveDat(1:4,252) = (/ 251.0, -3.7099416000, 3.6258468000e-3, -6.0956346000e-3 /) - LoveDat(1:4,253) = (/ 252.0, -3.7147645000, 3.6127027000e-3, -6.0766330000e-3 /) - LoveDat(1:4,254) = (/ 253.0, -3.7195854000, 3.5996869000e-3, -6.0578067000e-3 /) - LoveDat(1:4,255) = (/ 254.0, -3.7244043000, 3.5867979000e-3, -6.0391534000e-3 /) - LoveDat(1:4,256) = (/ 255.0, -3.7292211000, 3.5740342000e-3, -6.0206710000e-3 /) - LoveDat(1:4,257) = (/ 256.0, -3.7340357000, 3.5613944000e-3, -6.0023572000e-3 /) - LoveDat(1:4,258) = (/ 257.0, -3.7388483000, 3.5488772000e-3, -5.9842098000e-3 /) - LoveDat(1:4,259) = (/ 258.0, -3.7436587000, 3.5364810000e-3, -5.9662266000e-3 /) - LoveDat(1:4,260) = (/ 259.0, -3.7484669000, 3.5242045000e-3, -5.9484056000e-3 /) - LoveDat(1:4,261) = (/ 260.0, -3.7532729000, 3.5120464000e-3, -5.9307447000e-3 /) - LoveDat(1:4,262) = (/ 261.0, -3.7580766000, 3.5000053000e-3, -5.9132419000e-3 /) - LoveDat(1:4,263) = (/ 262.0, -3.7628780000, 3.4880799000e-3, -5.8958950000e-3 /) - LoveDat(1:4,264) = (/ 263.0, -3.7676772000, 3.4762689000e-3, -5.8787022000e-3 /) - LoveDat(1:4,265) = (/ 264.0, -3.7724740000, 3.4645710000e-3, -5.8616614000e-3 /) - LoveDat(1:4,266) = (/ 265.0, -3.7772685000, 3.4529849000e-3, -5.8447709000e-3 /) - LoveDat(1:4,267) = (/ 266.0, -3.7820605000, 3.4415093000e-3, -5.8280285000e-3 /) - LoveDat(1:4,268) = (/ 267.0, -3.7868501000, 3.4301431000e-3, -5.8114326000e-3 /) - LoveDat(1:4,269) = (/ 268.0, -3.7916373000, 3.4188851000e-3, -5.7949812000e-3 /) - LoveDat(1:4,270) = (/ 269.0, -3.7964220000, 3.4077339000e-3, -5.7786726000e-3 /) - LoveDat(1:4,271) = (/ 270.0, -3.8012042000, 3.3966884000e-3, -5.7625050000e-3 /) - LoveDat(1:4,272) = (/ 271.0, -3.8059839000, 3.3857475000e-3, -5.7464766000e-3 /) - LoveDat(1:4,273) = (/ 272.0, -3.8107610000, 3.3749099000e-3, -5.7305857000e-3 /) - LoveDat(1:4,274) = (/ 273.0, -3.8155355000, 3.3641746000e-3, -5.7148305000e-3 /) - LoveDat(1:4,275) = (/ 274.0, -3.8203074000, 3.3535404000e-3, -5.6992095000e-3 /) - LoveDat(1:4,276) = (/ 275.0, -3.8250766000, 3.3430061000e-3, -5.6837210000e-3 /) - LoveDat(1:4,277) = (/ 276.0, -3.8298432000, 3.3325707000e-3, -5.6683633000e-3 /) - LoveDat(1:4,278) = (/ 277.0, -3.8346070000, 3.3222331000e-3, -5.6531348000e-3 /) - LoveDat(1:4,279) = (/ 278.0, -3.8393682000, 3.3119922000e-3, -5.6380340000e-3 /) - LoveDat(1:4,280) = (/ 279.0, -3.8441265000, 3.3018470000e-3, -5.6230593000e-3 /) - LoveDat(1:4,281) = (/ 280.0, -3.8488821000, 3.2917964000e-3, -5.6082092000e-3 /) - LoveDat(1:4,282) = (/ 281.0, -3.8536348000, 3.2818393000e-3, -5.5934822000e-3 /) - LoveDat(1:4,283) = (/ 282.0, -3.8583847000, 3.2719748000e-3, -5.5788767000e-3 /) - LoveDat(1:4,284) = (/ 283.0, -3.8631317000, 3.2622018000e-3, -5.5643913000e-3 /) - LoveDat(1:4,285) = (/ 284.0, -3.8678759000, 3.2525193000e-3, -5.5500246000e-3 /) - LoveDat(1:4,286) = (/ 285.0, -3.8726170000, 3.2429264000e-3, -5.5357752000e-3 /) - LoveDat(1:4,287) = (/ 286.0, -3.8773553000, 3.2334221000e-3, -5.5216416000e-3 /) - LoveDat(1:4,288) = (/ 287.0, -3.8820905000, 3.2240054000e-3, -5.5076224000e-3 /) - LoveDat(1:4,289) = (/ 288.0, -3.8868227000, 3.2146753000e-3, -5.4937164000e-3 /) - LoveDat(1:4,290) = (/ 289.0, -3.8915519000, 3.2054310000e-3, -5.4799221000e-3 /) - LoveDat(1:4,291) = (/ 290.0, -3.8962780000, 3.1962715000e-3, -5.4662383000e-3 /) - LoveDat(1:4,292) = (/ 291.0, -3.9010010000, 3.1871958000e-3, -5.4526635000e-3 /) - LoveDat(1:4,293) = (/ 292.0, -3.9057209000, 3.1782032000e-3, -5.4391967000e-3 /) - LoveDat(1:4,294) = (/ 293.0, -3.9104377000, 3.1692926000e-3, -5.4258363000e-3 /) - LoveDat(1:4,295) = (/ 294.0, -3.9151512000, 3.1604632000e-3, -5.4125813000e-3 /) - LoveDat(1:4,296) = (/ 295.0, -3.9198616000, 3.1517142000e-3, -5.3994305000e-3 /) - LoveDat(1:4,297) = (/ 296.0, -3.9245687000, 3.1430446000e-3, -5.3863824000e-3 /) - LoveDat(1:4,298) = (/ 297.0, -3.9292725000, 3.1344537000e-3, -5.3734361000e-3 /) - LoveDat(1:4,299) = (/ 298.0, -3.9339731000, 3.1259405000e-3, -5.3605902000e-3 /) - LoveDat(1:4,300) = (/ 299.0, -3.9386704000, 3.1175043000e-3, -5.3478437000e-3 /) - LoveDat(1:4,301) = (/ 300.0, -3.9433643000, 3.1091442000e-3, -5.3351954000e-3 /) - LoveDat(1:4,302) = (/ 301.0, -3.9480548000, 3.1008594000e-3, -5.3226441000e-3 /) - LoveDat(1:4,303) = (/ 302.0, -3.9527420000, 3.0926491000e-3, -5.3101888000e-3 /) - LoveDat(1:4,304) = (/ 303.0, -3.9574257000, 3.0845126000e-3, -5.2978283000e-3 /) - LoveDat(1:4,305) = (/ 304.0, -3.9621060000, 3.0764490000e-3, -5.2855615000e-3 /) - LoveDat(1:4,306) = (/ 305.0, -3.9667828000, 3.0684575000e-3, -5.2733874000e-3 /) - LoveDat(1:4,307) = (/ 306.0, -3.9714561000, 3.0605375000e-3, -5.2613050000e-3 /) - LoveDat(1:4,308) = (/ 307.0, -3.9761259000, 3.0526881000e-3, -5.2493131000e-3 /) - LoveDat(1:4,309) = (/ 308.0, -3.9807921000, 3.0449085000e-3, -5.2374107000e-3 /) - LoveDat(1:4,310) = (/ 309.0, -3.9854548000, 3.0371982000e-3, -5.2255969000e-3 /) - LoveDat(1:4,311) = (/ 310.0, -3.9901138000, 3.0295562000e-3, -5.2138707000e-3 /) - LoveDat(1:4,312) = (/ 311.0, -3.9947693000, 3.0219820000e-3, -5.2022310000e-3 /) - LoveDat(1:4,313) = (/ 312.0, -3.9994210000, 3.0144747000e-3, -5.1906768000e-3 /) - LoveDat(1:4,314) = (/ 313.0, -4.0040691000, 3.0070337000e-3, -5.1792073000e-3 /) - LoveDat(1:4,315) = (/ 314.0, -4.0087135000, 2.9996584000e-3, -5.1678215000e-3 /) - LoveDat(1:4,316) = (/ 315.0, -4.0133542000, 2.9923479000e-3, -5.1565183000e-3 /) - LoveDat(1:4,317) = (/ 316.0, -4.0179911000, 2.9851016000e-3, -5.1452970000e-3 /) - LoveDat(1:4,318) = (/ 317.0, -4.0226242000, 2.9779189000e-3, -5.1341566000e-3 /) - LoveDat(1:4,319) = (/ 318.0, -4.0272535000, 2.9707990000e-3, -5.1230962000e-3 /) - LoveDat(1:4,320) = (/ 319.0, -4.0318790000, 2.9637414000e-3, -5.1121150000e-3 /) - LoveDat(1:4,321) = (/ 320.0, -4.0365006000, 2.9567453000e-3, -5.1012119000e-3 /) - LoveDat(1:4,322) = (/ 321.0, -4.0411184000, 2.9498101000e-3, -5.0903863000e-3 /) - LoveDat(1:4,323) = (/ 322.0, -4.0457322000, 2.9429353000e-3, -5.0796372000e-3 /) - LoveDat(1:4,324) = (/ 323.0, -4.0503421000, 2.9361201000e-3, -5.0689638000e-3 /) - LoveDat(1:4,325) = (/ 324.0, -4.0549481000, 2.9293639000e-3, -5.0583652000e-3 /) - LoveDat(1:4,326) = (/ 325.0, -4.0595501000, 2.9226662000e-3, -5.0478407000e-3 /) - LoveDat(1:4,327) = (/ 326.0, -4.0641480000, 2.9160263000e-3, -5.0373894000e-3 /) - LoveDat(1:4,328) = (/ 327.0, -4.0687420000, 2.9094435000e-3, -5.0270106000e-3 /) - LoveDat(1:4,329) = (/ 328.0, -4.0733319000, 2.9029174000e-3, -5.0167034000e-3 /) - LoveDat(1:4,330) = (/ 329.0, -4.0779177000, 2.8964474000e-3, -5.0064671000e-3 /) - LoveDat(1:4,331) = (/ 330.0, -4.0824995000, 2.8900327000e-3, -4.9963009000e-3 /) - LoveDat(1:4,332) = (/ 331.0, -4.0870771000, 2.8836730000e-3, -4.9862041000e-3 /) - LoveDat(1:4,333) = (/ 332.0, -4.0916505000, 2.8773676000e-3, -4.9761758000e-3 /) - LoveDat(1:4,334) = (/ 333.0, -4.0962198000, 2.8711159000e-3, -4.9662155000e-3 /) - LoveDat(1:4,335) = (/ 334.0, -4.1007850000, 2.8649173000e-3, -4.9563223000e-3 /) - LoveDat(1:4,336) = (/ 335.0, -4.1053459000, 2.8587715000e-3, -4.9464955000e-3 /) - LoveDat(1:4,337) = (/ 336.0, -4.1099025000, 2.8526777000e-3, -4.9367344000e-3 /) - LoveDat(1:4,338) = (/ 337.0, -4.1144549000, 2.8466354000e-3, -4.9270384000e-3 /) - LoveDat(1:4,339) = (/ 338.0, -4.1190030000, 2.8406442000e-3, -4.9174066000e-3 /) - LoveDat(1:4,340) = (/ 339.0, -4.1235469000, 2.8347035000e-3, -4.9078386000e-3 /) - LoveDat(1:4,341) = (/ 340.0, -4.1280863000, 2.8288128000e-3, -4.8983335000e-3 /) - LoveDat(1:4,342) = (/ 341.0, -4.1326215000, 2.8229715000e-3, -4.8888907000e-3 /) - LoveDat(1:4,343) = (/ 342.0, -4.1371523000, 2.8171792000e-3, -4.8795095000e-3 /) - LoveDat(1:4,344) = (/ 343.0, -4.1416786000, 2.8114353000e-3, -4.8701893000e-3 /) - LoveDat(1:4,345) = (/ 344.0, -4.1462006000, 2.8057394000e-3, -4.8609295000e-3 /) - LoveDat(1:4,346) = (/ 345.0, -4.1507181000, 2.8000909000e-3, -4.8517295000e-3 /) - LoveDat(1:4,347) = (/ 346.0, -4.1552312000, 2.7944894000e-3, -4.8425885000e-3 /) - LoveDat(1:4,348) = (/ 347.0, -4.1597397000, 2.7889344000e-3, -4.8335060000e-3 /) - LoveDat(1:4,349) = (/ 348.0, -4.1642438000, 2.7834254000e-3, -4.8244814000e-3 /) - LoveDat(1:4,350) = (/ 349.0, -4.1687434000, 2.7779620000e-3, -4.8155141000e-3 /) - LoveDat(1:4,351) = (/ 350.0, -4.1732384000, 2.7725436000e-3, -4.8066034000e-3 /) - LoveDat(1:4,352) = (/ 351.0, -4.1777288000, 2.7671698000e-3, -4.7977488000e-3 /) - LoveDat(1:4,353) = (/ 352.0, -4.1822147000, 2.7618402000e-3, -4.7889498000e-3 /) - LoveDat(1:4,354) = (/ 353.0, -4.1866959000, 2.7565543000e-3, -4.7802057000e-3 /) - LoveDat(1:4,355) = (/ 354.0, -4.1911725000, 2.7513117000e-3, -4.7715160000e-3 /) - LoveDat(1:4,356) = (/ 355.0, -4.1956445000, 2.7461118000e-3, -4.7628800000e-3 /) - LoveDat(1:4,357) = (/ 356.0, -4.2001118000, 2.7409544000e-3, -4.7542974000e-3 /) - LoveDat(1:4,358) = (/ 357.0, -4.2045744000, 2.7358388000e-3, -4.7457675000e-3 /) - LoveDat(1:4,359) = (/ 358.0, -4.2090323000, 2.7307648000e-3, -4.7372897000e-3 /) - LoveDat(1:4,360) = (/ 359.0, -4.2134854000, 2.7257319000e-3, -4.7288636000e-3 /) - LoveDat(1:4,361) = (/ 360.0, -4.2179338000, 2.7207397000e-3, -4.7204886000e-3 /) - LoveDat(1:4,362) = (/ 361.0, -4.2223775000, 2.7157877000e-3, -4.7121643000e-3 /) - LoveDat(1:4,363) = (/ 362.0, -4.2268163000, 2.7108756000e-3, -4.7038900000e-3 /) - LoveDat(1:4,364) = (/ 363.0, -4.2312503000, 2.7060029000e-3, -4.6956653000e-3 /) - LoveDat(1:4,365) = (/ 364.0, -4.2356795000, 2.7011692000e-3, -4.6874897000e-3 /) - LoveDat(1:4,366) = (/ 365.0, -4.2401039000, 2.6963742000e-3, -4.6793627000e-3 /) - LoveDat(1:4,367) = (/ 366.0, -4.2445234000, 2.6916175000e-3, -4.6712838000e-3 /) - LoveDat(1:4,368) = (/ 367.0, -4.2489380000, 2.6868986000e-3, -4.6632526000e-3 /) - LoveDat(1:4,369) = (/ 368.0, -4.2533476000, 2.6822172000e-3, -4.6552684000e-3 /) - LoveDat(1:4,370) = (/ 369.0, -4.2577524000, 2.6775728000e-3, -4.6473310000e-3 /) - LoveDat(1:4,371) = (/ 370.0, -4.2621522000, 2.6729652000e-3, -4.6394397000e-3 /) - LoveDat(1:4,372) = (/ 371.0, -4.2665470000, 2.6683940000e-3, -4.6315942000e-3 /) - LoveDat(1:4,373) = (/ 372.0, -4.2709369000, 2.6638587000e-3, -4.6237940000e-3 /) - LoveDat(1:4,374) = (/ 373.0, -4.2753218000, 2.6593590000e-3, -4.6160387000e-3 /) - LoveDat(1:4,375) = (/ 374.0, -4.2797016000, 2.6548946000e-3, -4.6083277000e-3 /) - LoveDat(1:4,376) = (/ 375.0, -4.2840764000, 2.6504651000e-3, -4.6006607000e-3 /) - LoveDat(1:4,377) = (/ 376.0, -4.2884462000, 2.6460701000e-3, -4.5930373000e-3 /) - LoveDat(1:4,378) = (/ 377.0, -4.2928108000, 2.6417093000e-3, -4.5854569000e-3 /) - LoveDat(1:4,379) = (/ 378.0, -4.2971704000, 2.6373823000e-3, -4.5779192000e-3 /) - LoveDat(1:4,380) = (/ 379.0, -4.3015249000, 2.6330888000e-3, -4.5704238000e-3 /) - LoveDat(1:4,381) = (/ 380.0, -4.3058742000, 2.6288285000e-3, -4.5629702000e-3 /) - LoveDat(1:4,382) = (/ 381.0, -4.3102184000, 2.6246011000e-3, -4.5555581000e-3 /) - LoveDat(1:4,383) = (/ 382.0, -4.3145575000, 2.6204061000e-3, -4.5481870000e-3 /) - LoveDat(1:4,384) = (/ 383.0, -4.3188914000, 2.6162432000e-3, -4.5408565000e-3 /) - LoveDat(1:4,385) = (/ 384.0, -4.3232200000, 2.6121122000e-3, -4.5335663000e-3 /) - LoveDat(1:4,386) = (/ 385.0, -4.3275435000, 2.6080128000e-3, -4.5263159000e-3 /) - LoveDat(1:4,387) = (/ 386.0, -4.3318617000, 2.6039445000e-3, -4.5191050000e-3 /) - LoveDat(1:4,388) = (/ 387.0, -4.3361747000, 2.5999071000e-3, -4.5119331000e-3 /) - LoveDat(1:4,389) = (/ 388.0, -4.3404824000, 2.5959002000e-3, -4.5048000000e-3 /) - LoveDat(1:4,390) = (/ 389.0, -4.3447848000, 2.5919236000e-3, -4.4977052000e-3 /) - LoveDat(1:4,391) = (/ 390.0, -4.3490820000, 2.5879770000e-3, -4.4906484000e-3 /) - LoveDat(1:4,392) = (/ 391.0, -4.3533738000, 2.5840600000e-3, -4.4836292000e-3 /) - LoveDat(1:4,393) = (/ 392.0, -4.3576603000, 2.5801724000e-3, -4.4766472000e-3 /) - LoveDat(1:4,394) = (/ 393.0, -4.3619414000, 2.5763138000e-3, -4.4697021000e-3 /) - LoveDat(1:4,395) = (/ 394.0, -4.3662172000, 2.5724840000e-3, -4.4627935000e-3 /) - LoveDat(1:4,396) = (/ 395.0, -4.3704876000, 2.5686827000e-3, -4.4559212000e-3 /) - LoveDat(1:4,397) = (/ 396.0, -4.3747527000, 2.5649095000e-3, -4.4490846000e-3 /) - LoveDat(1:4,398) = (/ 397.0, -4.3790123000, 2.5611642000e-3, -4.4422836000e-3 /) - LoveDat(1:4,399) = (/ 398.0, -4.3832665000, 2.5574466000e-3, -4.4355178000e-3 /) - LoveDat(1:4,400) = (/ 399.0, -4.3875152000, 2.5537563000e-3, -4.4287868000e-3 /) - LoveDat(1:4,401) = (/ 400.0, -4.3917586000, 2.5500930000e-3, -4.4220903000e-3 /) - LoveDat(1:4,402) = (/ 401.0, -4.3959964000, 2.5464565000e-3, -4.4154280000e-3 /) - LoveDat(1:4,403) = (/ 402.0, -4.4002288000, 2.5428466000e-3, -4.4087995000e-3 /) - LoveDat(1:4,404) = (/ 403.0, -4.4044556000, 2.5392629000e-3, -4.4022046000e-3 /) - LoveDat(1:4,405) = (/ 404.0, -4.4086770000, 2.5357051000e-3, -4.3956430000e-3 /) - LoveDat(1:4,406) = (/ 405.0, -4.4128928000, 2.5321731000e-3, -4.3891142000e-3 /) - LoveDat(1:4,407) = (/ 406.0, -4.4171031000, 2.5286666000e-3, -4.3826181000e-3 /) - LoveDat(1:4,408) = (/ 407.0, -4.4213078000, 2.5251852000e-3, -4.3761543000e-3 /) - LoveDat(1:4,409) = (/ 408.0, -4.4255070000, 2.5217289000e-3, -4.3697225000e-3 /) - LoveDat(1:4,410) = (/ 409.0, -4.4297006000, 2.5182972000e-3, -4.3633224000e-3 /) - LoveDat(1:4,411) = (/ 410.0, -4.4338886000, 2.5148899000e-3, -4.3569537000e-3 /) - LoveDat(1:4,412) = (/ 411.0, -4.4380709000, 2.5115069000e-3, -4.3506162000e-3 /) - LoveDat(1:4,413) = (/ 412.0, -4.4422477000, 2.5081478000e-3, -4.3443095000e-3 /) - LoveDat(1:4,414) = (/ 413.0, -4.4464188000, 2.5048125000e-3, -4.3380334000e-3 /) - LoveDat(1:4,415) = (/ 414.0, -4.4505843000, 2.5015006000e-3, -4.3317876000e-3 /) - LoveDat(1:4,416) = (/ 415.0, -4.4547441000, 2.4982119000e-3, -4.3255718000e-3 /) - LoveDat(1:4,417) = (/ 416.0, -4.4588982000, 2.4949463000e-3, -4.3193857000e-3 /) - LoveDat(1:4,418) = (/ 417.0, -4.4630466000, 2.4917034000e-3, -4.3132290000e-3 /) - LoveDat(1:4,419) = (/ 418.0, -4.4671894000, 2.4884831000e-3, -4.3071016000e-3 /) - LoveDat(1:4,420) = (/ 419.0, -4.4713264000, 2.4852851000e-3, -4.3010031000e-3 /) - LoveDat(1:4,421) = (/ 420.0, -4.4754577000, 2.4821092000e-3, -4.2949332000e-3 /) - LoveDat(1:4,422) = (/ 421.0, -4.4795832000, 2.4789551000e-3, -4.2888918000e-3 /) - LoveDat(1:4,423) = (/ 422.0, -4.4837030000, 2.4758227000e-3, -4.2828785000e-3 /) - LoveDat(1:4,424) = (/ 423.0, -4.4878171000, 2.4727118000e-3, -4.2768931000e-3 /) - LoveDat(1:4,425) = (/ 424.0, -4.4919253000, 2.4696220000e-3, -4.2709353000e-3 /) - LoveDat(1:4,426) = (/ 425.0, -4.4960278000, 2.4665532000e-3, -4.2650050000e-3 /) - LoveDat(1:4,427) = (/ 426.0, -4.5001245000, 2.4635053000e-3, -4.2591017000e-3 /) - LoveDat(1:4,428) = (/ 427.0, -4.5042153000, 2.4604778000e-3, -4.2532254000e-3 /) - LoveDat(1:4,429) = (/ 428.0, -4.5083003000, 2.4574708000e-3, -4.2473758000e-3 /) - LoveDat(1:4,430) = (/ 429.0, -4.5123795000, 2.4544839000e-3, -4.2415526000e-3 /) - LoveDat(1:4,431) = (/ 430.0, -4.5164529000, 2.4515170000e-3, -4.2357555000e-3 /) - LoveDat(1:4,432) = (/ 431.0, -4.5205204000, 2.4485699000e-3, -4.2299844000e-3 /) - LoveDat(1:4,433) = (/ 432.0, -4.5245820000, 2.4456423000e-3, -4.2242391000e-3 /) - LoveDat(1:4,434) = (/ 433.0, -4.5286377000, 2.4427340000e-3, -4.2185193000e-3 /) - LoveDat(1:4,435) = (/ 434.0, -4.5326876000, 2.4398450000e-3, -4.2128247000e-3 /) - LoveDat(1:4,436) = (/ 435.0, -4.5367315000, 2.4369749000e-3, -4.2071552000e-3 /) - LoveDat(1:4,437) = (/ 436.0, -4.5407695000, 2.4341235000e-3, -4.2015105000e-3 /) - LoveDat(1:4,438) = (/ 437.0, -4.5448016000, 2.4312908000e-3, -4.1958904000e-3 /) - LoveDat(1:4,439) = (/ 438.0, -4.5488278000, 2.4284765000e-3, -4.1902947000e-3 /) - LoveDat(1:4,440) = (/ 439.0, -4.5528480000, 2.4256804000e-3, -4.1847233000e-3 /) - LoveDat(1:4,441) = (/ 440.0, -4.5568623000, 2.4229023000e-3, -4.1791757000e-3 /) - LoveDat(1:4,442) = (/ 441.0, -4.5608706000, 2.4201420000e-3, -4.1736520000e-3 /) - LoveDat(1:4,443) = (/ 442.0, -4.5648729000, 2.4173995000e-3, -4.1681518000e-3 /) - LoveDat(1:4,444) = (/ 443.0, -4.5688693000, 2.4146744000e-3, -4.1626750000e-3 /) - LoveDat(1:4,445) = (/ 444.0, -4.5728596000, 2.4119666000e-3, -4.1572213000e-3 /) - LoveDat(1:4,446) = (/ 445.0, -4.5768440000, 2.4092760000e-3, -4.1517905000e-3 /) - LoveDat(1:4,447) = (/ 446.0, -4.5808223000, 2.4066023000e-3, -4.1463825000e-3 /) - LoveDat(1:4,448) = (/ 447.0, -4.5847946000, 2.4039454000e-3, -4.1409971000e-3 /) - LoveDat(1:4,449) = (/ 448.0, -4.5887608000, 2.4013051000e-3, -4.1356340000e-3 /) - LoveDat(1:4,450) = (/ 449.0, -4.5927211000, 2.3986813000e-3, -4.1302931000e-3 /) - LoveDat(1:4,451) = (/ 450.0, -4.5966752000, 2.3960738000e-3, -4.1249742000e-3 /) - LoveDat(1:4,452) = (/ 451.0, -4.6006234000, 2.3934824000e-3, -4.1196771000e-3 /) - LoveDat(1:4,453) = (/ 452.0, -4.6045654000, 2.3909070000e-3, -4.1144015000e-3 /) - LoveDat(1:4,454) = (/ 453.0, -4.6085014000, 2.3883473000e-3, -4.1091474000e-3 /) - LoveDat(1:4,455) = (/ 454.0, -4.6124313000, 2.3858033000e-3, -4.1039146000e-3 /) - LoveDat(1:4,456) = (/ 455.0, -4.6163550000, 2.3832748000e-3, -4.0987028000e-3 /) - LoveDat(1:4,457) = (/ 456.0, -4.6202727000, 2.3807615000e-3, -4.0935118000e-3 /) - LoveDat(1:4,458) = (/ 457.0, -4.6241843000, 2.3782635000e-3, -4.0883416000e-3 /) - LoveDat(1:4,459) = (/ 458.0, -4.6280897000, 2.3757804000e-3, -4.0831919000e-3 /) - LoveDat(1:4,460) = (/ 459.0, -4.6319890000, 2.3733122000e-3, -4.0780626000e-3 /) - LoveDat(1:4,461) = (/ 460.0, -4.6358822000, 2.3708588000e-3, -4.0729534000e-3 /) - LoveDat(1:4,462) = (/ 461.0, -4.6397692000, 2.3684198000e-3, -4.0678643000e-3 /) - LoveDat(1:4,463) = (/ 462.0, -4.6436501000, 2.3659953000e-3, -4.0627950000e-3 /) - LoveDat(1:4,464) = (/ 463.0, -4.6475249000, 2.3635851000e-3, -4.0577454000e-3 /) - LoveDat(1:4,465) = (/ 464.0, -4.6513934000, 2.3611889000e-3, -4.0527153000e-3 /) - LoveDat(1:4,466) = (/ 465.0, -4.6552558000, 2.3588068000e-3, -4.0477046000e-3 /) - LoveDat(1:4,467) = (/ 466.0, -4.6591120000, 2.3564384000e-3, -4.0427131000e-3 /) - LoveDat(1:4,468) = (/ 467.0, -4.6629620000, 2.3540838000e-3, -4.0377406000e-3 /) - LoveDat(1:4,469) = (/ 468.0, -4.6668058000, 2.3517427000e-3, -4.0327870000e-3 /) - LoveDat(1:4,470) = (/ 469.0, -4.6706434000, 2.3494150000e-3, -4.0278521000e-3 /) - LoveDat(1:4,471) = (/ 470.0, -4.6744748000, 2.3471006000e-3, -4.0229358000e-3 /) - LoveDat(1:4,472) = (/ 471.0, -4.6783000000, 2.3447994000e-3, -4.0180379000e-3 /) - LoveDat(1:4,473) = (/ 472.0, -4.6821189000, 2.3425111000e-3, -4.0131582000e-3 /) - LoveDat(1:4,474) = (/ 473.0, -4.6859316000, 2.3402357000e-3, -4.0082967000e-3 /) - LoveDat(1:4,475) = (/ 474.0, -4.6897381000, 2.3379731000e-3, -4.0034532000e-3 /) - LoveDat(1:4,476) = (/ 475.0, -4.6935383000, 2.3357231000e-3, -3.9986274000e-3 /) - LoveDat(1:4,477) = (/ 476.0, -4.6973323000, 2.3334855000e-3, -3.9938194000e-3 /) - LoveDat(1:4,478) = (/ 477.0, -4.7011201000, 2.3312604000e-3, -3.9890289000e-3 /) - LoveDat(1:4,479) = (/ 478.0, -4.7049015000, 2.3290474000e-3, -3.9842557000e-3 /) - LoveDat(1:4,480) = (/ 479.0, -4.7086767000, 2.3268466000e-3, -3.9794999000e-3 /) - LoveDat(1:4,481) = (/ 480.0, -4.7124456000, 2.3246577000e-3, -3.9747611000e-3 /) - LoveDat(1:4,482) = (/ 481.0, -4.7162083000, 2.3224807000e-3, -3.9700393000e-3 /) - LoveDat(1:4,483) = (/ 482.0, -4.7199646000, 2.3203154000e-3, -3.9653344000e-3 /) - LoveDat(1:4,484) = (/ 483.0, -4.7237147000, 2.3181618000e-3, -3.9606461000e-3 /) - LoveDat(1:4,485) = (/ 484.0, -4.7274585000, 2.3160196000e-3, -3.9559744000e-3 /) - LoveDat(1:4,486) = (/ 485.0, -4.7311959000, 2.3138889000e-3, -3.9513192000e-3 /) - LoveDat(1:4,487) = (/ 486.0, -4.7349271000, 2.3117694000e-3, -3.9466802000e-3 /) - LoveDat(1:4,488) = (/ 487.0, -4.7386519000, 2.3096610000e-3, -3.9420575000e-3 /) - LoveDat(1:4,489) = (/ 488.0, -4.7423704000, 2.3075637000e-3, -3.9374508000e-3 /) - LoveDat(1:4,490) = (/ 489.0, -4.7460826000, 2.3054773000e-3, -3.9328600000e-3 /) - LoveDat(1:4,491) = (/ 490.0, -4.7497885000, 2.3034017000e-3, -3.9282850000e-3 /) - LoveDat(1:4,492) = (/ 491.0, -4.7534880000, 2.3013368000e-3, -3.9237256000e-3 /) - LoveDat(1:4,493) = (/ 492.0, -4.7571812000, 2.2992825000e-3, -3.9191818000e-3 /) - LoveDat(1:4,494) = (/ 493.0, -4.7608681000, 2.2972386000e-3, -3.9146535000e-3 /) - LoveDat(1:4,495) = (/ 494.0, -4.7645486000, 2.2952052000e-3, -3.9101404000e-3 /) - LoveDat(1:4,496) = (/ 495.0, -4.7682227000, 2.2931820000e-3, -3.9056425000e-3 /) - LoveDat(1:4,497) = (/ 496.0, -4.7718905000, 2.2911690000e-3, -3.9011597000e-3 /) - LoveDat(1:4,498) = (/ 497.0, -4.7755520000, 2.2891660000e-3, -3.8966919000e-3 /) - LoveDat(1:4,499) = (/ 498.0, -4.7792071000, 2.2871729000e-3, -3.8922389000e-3 /) - LoveDat(1:4,500) = (/ 499.0, -4.7828558000, 2.2851898000e-3, -3.8878005000e-3 /) - LoveDat(1:4,501) = (/ 500.0, -4.7864981000, 2.2832163000e-3, -3.8833768000e-3 /) - LoveDat(1:4,502) = (/ 501.0, -4.7901341000, 2.2812525000e-3, -3.8789676000e-3 /) - LoveDat(1:4,503) = (/ 502.0, -4.7937636000, 2.2792983000e-3, -3.8745728000e-3 /) - LoveDat(1:4,504) = (/ 503.0, -4.7973868000, 2.2773535000e-3, -3.8701922000e-3 /) - LoveDat(1:4,505) = (/ 504.0, -4.8010036000, 2.2754180000e-3, -3.8658258000e-3 /) - LoveDat(1:4,506) = (/ 505.0, -4.8046141000, 2.2734918000e-3, -3.8614735000e-3 /) - LoveDat(1:4,507) = (/ 506.0, -4.8082181000, 2.2715748000e-3, -3.8571351000e-3 /) - LoveDat(1:4,508) = (/ 507.0, -4.8118157000, 2.2696668000e-3, -3.8528105000e-3 /) - LoveDat(1:4,509) = (/ 508.0, -4.8154069000, 2.2677678000e-3, -3.8484997000e-3 /) - LoveDat(1:4,510) = (/ 509.0, -4.8189918000, 2.2658777000e-3, -3.8442025000e-3 /) - LoveDat(1:4,511) = (/ 510.0, -4.8225702000, 2.2639964000e-3, -3.8399188000e-3 /) - LoveDat(1:4,512) = (/ 511.0, -4.8261422000, 2.2621237000e-3, -3.8356485000e-3 /) - LoveDat(1:4,513) = (/ 512.0, -4.8297078000, 2.2602597000e-3, -3.8313916000e-3 /) - LoveDat(1:4,514) = (/ 513.0, -4.8332670000, 2.2584041000e-3, -3.8271479000e-3 /) - LoveDat(1:4,515) = (/ 514.0, -4.8368197000, 2.2565570000e-3, -3.8229173000e-3 /) - LoveDat(1:4,516) = (/ 515.0, -4.8403661000, 2.2547183000e-3, -3.8186997000e-3 /) - LoveDat(1:4,517) = (/ 516.0, -4.8439060000, 2.2528877000e-3, -3.8144951000e-3 /) - LoveDat(1:4,518) = (/ 517.0, -4.8474395000, 2.2510654000e-3, -3.8103033000e-3 /) - LoveDat(1:4,519) = (/ 518.0, -4.8509666000, 2.2492511000e-3, -3.8061243000e-3 /) - LoveDat(1:4,520) = (/ 519.0, -4.8544872000, 2.2474448000e-3, -3.8019578000e-3 /) - LoveDat(1:4,521) = (/ 520.0, -4.8580014000, 2.2456465000e-3, -3.7978040000e-3 /) - LoveDat(1:4,522) = (/ 521.0, -4.8615092000, 2.2438560000e-3, -3.7936626000e-3 /) - LoveDat(1:4,523) = (/ 522.0, -4.8650105000, 2.2420732000e-3, -3.7895335000e-3 /) - LoveDat(1:4,524) = (/ 523.0, -4.8685054000, 2.2402981000e-3, -3.7854168000e-3 /) - LoveDat(1:4,525) = (/ 524.0, -4.8719939000, 2.2385305000e-3, -3.7813122000e-3 /) - LoveDat(1:4,526) = (/ 525.0, -4.8754759000, 2.2367705000e-3, -3.7772197000e-3 /) - LoveDat(1:4,527) = (/ 526.0, -4.8789515000, 2.2350179000e-3, -3.7731392000e-3 /) - LoveDat(1:4,528) = (/ 527.0, -4.8824206000, 2.2332727000e-3, -3.7690706000e-3 /) - LoveDat(1:4,529) = (/ 528.0, -4.8858833000, 2.2315347000e-3, -3.7650139000e-3 /) - LoveDat(1:4,530) = (/ 529.0, -4.8893395000, 2.2298040000e-3, -3.7609689000e-3 /) - LoveDat(1:4,531) = (/ 530.0, -4.8927893000, 2.2280804000e-3, -3.7569356000e-3 /) - LoveDat(1:4,532) = (/ 531.0, -4.8962327000, 2.2263638000e-3, -3.7529139000e-3 /) - LoveDat(1:4,533) = (/ 532.0, -4.8996696000, 2.2246542000e-3, -3.7489037000e-3 /) - LoveDat(1:4,534) = (/ 533.0, -4.9031000000, 2.2229515000e-3, -3.7449049000e-3 /) - LoveDat(1:4,535) = (/ 534.0, -4.9065240000, 2.2212556000e-3, -3.7409174000e-3 /) - LoveDat(1:4,536) = (/ 535.0, -4.9099415000, 2.2195665000e-3, -3.7369411000e-3 /) - LoveDat(1:4,537) = (/ 536.0, -4.9133526000, 2.2178841000e-3, -3.7329761000e-3 /) - LoveDat(1:4,538) = (/ 537.0, -4.9167573000, 2.2162082000e-3, -3.7290221000e-3 /) - LoveDat(1:4,539) = (/ 538.0, -4.9201554000, 2.2145390000e-3, -3.7250792000e-3 /) - LoveDat(1:4,540) = (/ 539.0, -4.9235472000, 2.2128762000e-3, -3.7211471000e-3 /) - LoveDat(1:4,541) = (/ 540.0, -4.9269324000, 2.2112198000e-3, -3.7172260000e-3 /) - LoveDat(1:4,542) = (/ 541.0, -4.9303112000, 2.2095698000e-3, -3.7133156000e-3 /) - LoveDat(1:4,543) = (/ 542.0, -4.9336836000, 2.2079261000e-3, -3.7094160000e-3 /) - LoveDat(1:4,544) = (/ 543.0, -4.9370495000, 2.2062885000e-3, -3.7055269000e-3 /) - LoveDat(1:4,545) = (/ 544.0, -4.9404089000, 2.2046571000e-3, -3.7016485000e-3 /) - LoveDat(1:4,546) = (/ 545.0, -4.9437619000, 2.2030318000e-3, -3.6977805000e-3 /) - LoveDat(1:4,547) = (/ 546.0, -4.9471084000, 2.2014125000e-3, -3.6939229000e-3 /) - LoveDat(1:4,548) = (/ 547.0, -4.9504485000, 2.1997991000e-3, -3.6900757000e-3 /) - LoveDat(1:4,549) = (/ 548.0, -4.9537821000, 2.1981917000e-3, -3.6862387000e-3 /) - LoveDat(1:4,550) = (/ 549.0, -4.9571092000, 2.1965901000e-3, -3.6824120000e-3 /) - LoveDat(1:4,551) = (/ 550.0, -4.9604299000, 2.1949942000e-3, -3.6785954000e-3 /) - LoveDat(1:4,552) = (/ 551.0, -4.9637442000, 2.1934040000e-3, -3.6747888000e-3 /) - LoveDat(1:4,553) = (/ 552.0, -4.9670519000, 2.1918195000e-3, -3.6709922000e-3 /) - LoveDat(1:4,554) = (/ 553.0, -4.9703533000, 2.1902406000e-3, -3.6672056000e-3 /) - LoveDat(1:4,555) = (/ 554.0, -4.9736481000, 2.1886671000e-3, -3.6634288000e-3 /) - LoveDat(1:4,556) = (/ 555.0, -4.9769366000, 2.1870992000e-3, -3.6596618000e-3 /) - LoveDat(1:4,557) = (/ 556.0, -4.9802185000, 2.1855366000e-3, -3.6559045000e-3 /) - LoveDat(1:4,558) = (/ 557.0, -4.9834940000, 2.1839795000e-3, -3.6521569000e-3 /) - LoveDat(1:4,559) = (/ 558.0, -4.9867631000, 2.1824276000e-3, -3.6484189000e-3 /) - LoveDat(1:4,560) = (/ 559.0, -4.9900257000, 2.1808809000e-3, -3.6446904000e-3 /) - LoveDat(1:4,561) = (/ 560.0, -4.9932819000, 2.1793394000e-3, -3.6409714000e-3 /) - LoveDat(1:4,562) = (/ 561.0, -4.9965316000, 2.1778031000e-3, -3.6372617000e-3 /) - LoveDat(1:4,563) = (/ 562.0, -4.9997749000, 2.1762718000e-3, -3.6335615000e-3 /) - LoveDat(1:4,564) = (/ 563.0, -5.0030117000, 2.1747455000e-3, -3.6298704000e-3 /) - LoveDat(1:4,565) = (/ 564.0, -5.0062421000, 2.1732242000e-3, -3.6261887000e-3 /) - LoveDat(1:4,566) = (/ 565.0, -5.0094660000, 2.1717078000e-3, -3.6225160000e-3 /) - LoveDat(1:4,567) = (/ 566.0, -5.0126835000, 2.1701963000e-3, -3.6188525000e-3 /) - LoveDat(1:4,568) = (/ 567.0, -5.0158946000, 2.1686895000e-3, -3.6151980000e-3 /) - LoveDat(1:4,569) = (/ 568.0, -5.0190992000, 2.1671876000e-3, -3.6115525000e-3 /) - LoveDat(1:4,570) = (/ 569.0, -5.0222974000, 2.1656903000e-3, -3.6079159000e-3 /) - LoveDat(1:4,571) = (/ 570.0, -5.0254891000, 2.1641977000e-3, -3.6042882000e-3 /) - LoveDat(1:4,572) = (/ 571.0, -5.0286744000, 2.1627096000e-3, -3.6006692000e-3 /) - LoveDat(1:4,573) = (/ 572.0, -5.0318533000, 2.1612262000e-3, -3.5970590000e-3 /) - LoveDat(1:4,574) = (/ 573.0, -5.0350258000, 2.1597472000e-3, -3.5934575000e-3 /) - LoveDat(1:4,575) = (/ 574.0, -5.0381918000, 2.1582727000e-3, -3.5898647000e-3 /) - LoveDat(1:4,576) = (/ 575.0, -5.0413514000, 2.1568026000e-3, -3.5862804000e-3 /) - LoveDat(1:4,577) = (/ 576.0, -5.0445046000, 2.1553369000e-3, -3.5827047000e-3 /) - LoveDat(1:4,578) = (/ 577.0, -5.0476514000, 2.1538755000e-3, -3.5791374000e-3 /) - LoveDat(1:4,579) = (/ 578.0, -5.0507917000, 2.1524183000e-3, -3.5755785000e-3 /) - LoveDat(1:4,580) = (/ 579.0, -5.0539256000, 2.1509654000e-3, -3.5720280000e-3 /) - LoveDat(1:4,581) = (/ 580.0, -5.0570532000, 2.1495166000e-3, -3.5684858000e-3 /) - LoveDat(1:4,582) = (/ 581.0, -5.0601743000, 2.1480720000e-3, -3.5649519000e-3 /) - LoveDat(1:4,583) = (/ 582.0, -5.0632890000, 2.1466315000e-3, -3.5614262000e-3 /) - LoveDat(1:4,584) = (/ 583.0, -5.0663973000, 2.1451950000e-3, -3.5579086000e-3 /) - LoveDat(1:4,585) = (/ 584.0, -5.0694991000, 2.1437625000e-3, -3.5543992000e-3 /) - LoveDat(1:4,586) = (/ 585.0, -5.0725946000, 2.1423339000e-3, -3.5508978000e-3 /) - LoveDat(1:4,587) = (/ 586.0, -5.0756837000, 2.1409093000e-3, -3.5474044000e-3 /) - LoveDat(1:4,588) = (/ 587.0, -5.0787664000, 2.1394885000e-3, -3.5439189000e-3 /) - LoveDat(1:4,589) = (/ 588.0, -5.0818427000, 2.1380716000e-3, -3.5404414000e-3 /) - LoveDat(1:4,590) = (/ 589.0, -5.0849126000, 2.1366585000e-3, -3.5369717000e-3 /) - LoveDat(1:4,591) = (/ 590.0, -5.0879762000, 2.1352491000e-3, -3.5335099000e-3 /) - LoveDat(1:4,592) = (/ 591.0, -5.0910333000, 2.1338434000e-3, -3.5300557000e-3 /) - LoveDat(1:4,593) = (/ 592.0, -5.0940841000, 2.1324413000e-3, -3.5266094000e-3 /) - LoveDat(1:4,594) = (/ 593.0, -5.0971285000, 2.1310429000e-3, -3.5231706000e-3 /) - LoveDat(1:4,595) = (/ 594.0, -5.1001665000, 2.1296481000e-3, -3.5197395000e-3 /) - LoveDat(1:4,596) = (/ 595.0, -5.1031982000, 2.1282569000e-3, -3.5163160000e-3 /) - LoveDat(1:4,597) = (/ 596.0, -5.1062234000, 2.1268691000e-3, -3.5129000000e-3 /) - LoveDat(1:4,598) = (/ 597.0, -5.1092424000, 2.1254848000e-3, -3.5094915000e-3 /) - LoveDat(1:4,599) = (/ 598.0, -5.1122549000, 2.1241039000e-3, -3.5060904000e-3 /) - LoveDat(1:4,600) = (/ 599.0, -5.1152611000, 2.1227265000e-3, -3.5026968000e-3 /) - LoveDat(1:4,601) = (/ 600.0, -5.1182610000, 2.1213524000e-3, -3.4993105000e-3 /) - LoveDat(1:4,602) = (/ 601.0, -5.1212545000, 2.1199816000e-3, -3.4959315000e-3 /) - LoveDat(1:4,603) = (/ 602.0, -5.1242417000, 2.1186141000e-3, -3.4925597000e-3 /) - LoveDat(1:4,604) = (/ 603.0, -5.1272225000, 2.1172498000e-3, -3.4891952000e-3 /) - LoveDat(1:4,605) = (/ 604.0, -5.1301970000, 2.1158888000e-3, -3.4858379000e-3 /) - LoveDat(1:4,606) = (/ 605.0, -5.1331651000, 2.1145309000e-3, -3.4824877000e-3 /) - LoveDat(1:4,607) = (/ 606.0, -5.1361270000, 2.1131762000e-3, -3.4791445000e-3 /) - LoveDat(1:4,608) = (/ 607.0, -5.1390825000, 2.1118246000e-3, -3.4758085000e-3 /) - LoveDat(1:4,609) = (/ 608.0, -5.1420316000, 2.1104761000e-3, -3.4724795000e-3 /) - LoveDat(1:4,610) = (/ 609.0, -5.1449745000, 2.1091306000e-3, -3.4691574000e-3 /) - LoveDat(1:4,611) = (/ 610.0, -5.1479111000, 2.1077881000e-3, -3.4658423000e-3 /) - LoveDat(1:4,612) = (/ 611.0, -5.1508413000, 2.1064486000e-3, -3.4625341000e-3 /) - LoveDat(1:4,613) = (/ 612.0, -5.1537652000, 2.1051120000e-3, -3.4592327000e-3 /) - LoveDat(1:4,614) = (/ 613.0, -5.1566829000, 2.1037784000e-3, -3.4559381000e-3 /) - LoveDat(1:4,615) = (/ 614.0, -5.1595942000, 2.1024476000e-3, -3.4526504000e-3 /) - LoveDat(1:4,616) = (/ 615.0, -5.1624993000, 2.1011196000e-3, -3.4493693000e-3 /) - LoveDat(1:4,617) = (/ 616.0, -5.1653981000, 2.0997945000e-3, -3.4460950000e-3 /) - LoveDat(1:4,618) = (/ 617.0, -5.1682905000, 2.0984722000e-3, -3.4428273000e-3 /) - LoveDat(1:4,619) = (/ 618.0, -5.1711768000, 2.0971526000e-3, -3.4395663000e-3 /) - LoveDat(1:4,620) = (/ 619.0, -5.1740567000, 2.0958358000e-3, -3.4363118000e-3 /) - LoveDat(1:4,621) = (/ 620.0, -5.1769304000, 2.0945216000e-3, -3.4330639000e-3 /) - LoveDat(1:4,622) = (/ 621.0, -5.1797978000, 2.0932101000e-3, -3.4298226000e-3 /) - LoveDat(1:4,623) = (/ 622.0, -5.1826589000, 2.0919012000e-3, -3.4265877000e-3 /) - LoveDat(1:4,624) = (/ 623.0, -5.1855138000, 2.0905950000e-3, -3.4233592000e-3 /) - LoveDat(1:4,625) = (/ 624.0, -5.1883625000, 2.0892913000e-3, -3.4201372000e-3 /) - LoveDat(1:4,626) = (/ 625.0, -5.1912049000, 2.0879902000e-3, -3.4169215000e-3 /) - LoveDat(1:4,627) = (/ 626.0, -5.1940410000, 2.0866915000e-3, -3.4137122000e-3 /) - LoveDat(1:4,628) = (/ 627.0, -5.1968710000, 2.0853954000e-3, -3.4105092000e-3 /) - LoveDat(1:4,629) = (/ 628.0, -5.1996947000, 2.0841018000e-3, -3.4073124000e-3 /) - LoveDat(1:4,630) = (/ 629.0, -5.2025121000, 2.0828105000e-3, -3.4041219000e-3 /) - LoveDat(1:4,631) = (/ 630.0, -5.2053234000, 2.0815217000e-3, -3.4009376000e-3 /) - LoveDat(1:4,632) = (/ 631.0, -5.2081285000, 2.0802353000e-3, -3.3977595000e-3 /) - LoveDat(1:4,633) = (/ 632.0, -5.2109273000, 2.0789512000e-3, -3.3945875000e-3 /) - LoveDat(1:4,634) = (/ 633.0, -5.2137199000, 2.0776695000e-3, -3.3914216000e-3 /) - LoveDat(1:4,635) = (/ 634.0, -5.2165064000, 2.0763900000e-3, -3.3882618000e-3 /) - LoveDat(1:4,636) = (/ 635.0, -5.2192866000, 2.0751129000e-3, -3.3851080000e-3 /) - LoveDat(1:4,637) = (/ 636.0, -5.2220607000, 2.0738380000e-3, -3.3819602000e-3 /) - LoveDat(1:4,638) = (/ 637.0, -5.2248286000, 2.0725653000e-3, -3.3788184000e-3 /) - LoveDat(1:4,639) = (/ 638.0, -5.2275903000, 2.0712949000e-3, -3.3756826000e-3 /) - LoveDat(1:4,640) = (/ 639.0, -5.2303458000, 2.0700266000e-3, -3.3725527000e-3 /) - LoveDat(1:4,641) = (/ 640.0, -5.2330952000, 2.0687604000e-3, -3.3694286000e-3 /) - LoveDat(1:4,642) = (/ 641.0, -5.2358384000, 2.0674964000e-3, -3.3663104000e-3 /) - LoveDat(1:4,643) = (/ 642.0, -5.2385755000, 2.0662346000e-3, -3.3631981000e-3 /) - LoveDat(1:4,644) = (/ 643.0, -5.2413064000, 2.0649747000e-3, -3.3600915000e-3 /) - LoveDat(1:4,645) = (/ 644.0, -5.2440312000, 2.0637170000e-3, -3.3569907000e-3 /) - LoveDat(1:4,646) = (/ 645.0, -5.2467498000, 2.0624613000e-3, -3.3538957000e-3 /) - LoveDat(1:4,647) = (/ 646.0, -5.2494624000, 2.0612076000e-3, -3.3508063000e-3 /) - LoveDat(1:4,648) = (/ 647.0, -5.2521688000, 2.0599559000e-3, -3.3477227000e-3 /) - LoveDat(1:4,649) = (/ 648.0, -5.2548690000, 2.0587062000e-3, -3.3446446000e-3 /) - LoveDat(1:4,650) = (/ 649.0, -5.2575632000, 2.0574585000e-3, -3.3415722000e-3 /) - LoveDat(1:4,651) = (/ 650.0, -5.2602513000, 2.0562126000e-3, -3.3385054000e-3 /) - LoveDat(1:4,652) = (/ 651.0, -5.2629332000, 2.0549687000e-3, -3.3354442000e-3 /) - LoveDat(1:4,653) = (/ 652.0, -5.2656091000, 2.0537266000e-3, -3.3323885000e-3 /) - LoveDat(1:4,654) = (/ 653.0, -5.2682789000, 2.0524865000e-3, -3.3293383000e-3 /) - LoveDat(1:4,655) = (/ 654.0, -5.2709426000, 2.0512481000e-3, -3.3262936000e-3 /) - LoveDat(1:4,656) = (/ 655.0, -5.2736002000, 2.0500116000e-3, -3.3232543000e-3 /) - LoveDat(1:4,657) = (/ 656.0, -5.2762518000, 2.0487769000e-3, -3.3202205000e-3 /) - LoveDat(1:4,658) = (/ 657.0, -5.2788973000, 2.0475440000e-3, -3.3171921000e-3 /) - LoveDat(1:4,659) = (/ 658.0, -5.2815367000, 2.0463128000e-3, -3.3141691000e-3 /) - LoveDat(1:4,660) = (/ 659.0, -5.2841701000, 2.0450834000e-3, -3.3111514000e-3 /) - LoveDat(1:4,661) = (/ 660.0, -5.2867975000, 2.0438557000e-3, -3.3081390000e-3 /) - LoveDat(1:4,662) = (/ 661.0, -5.2894188000, 2.0426297000e-3, -3.3051319000e-3 /) - LoveDat(1:4,663) = (/ 662.0, -5.2920341000, 2.0414054000e-3, -3.3021302000e-3 /) - LoveDat(1:4,664) = (/ 663.0, -5.2946433000, 2.0401828000e-3, -3.2991336000e-3 /) - LoveDat(1:4,665) = (/ 664.0, -5.2972466000, 2.0389618000e-3, -3.2961423000e-3 /) - LoveDat(1:4,666) = (/ 665.0, -5.2998438000, 2.0377425000e-3, -3.2931562000e-3 /) - LoveDat(1:4,667) = (/ 666.0, -5.3024350000, 2.0365247000e-3, -3.2901753000e-3 /) - LoveDat(1:4,668) = (/ 667.0, -5.3050203000, 2.0353086000e-3, -3.2871995000e-3 /) - LoveDat(1:4,669) = (/ 668.0, -5.3075995000, 2.0340940000e-3, -3.2842288000e-3 /) - LoveDat(1:4,670) = (/ 669.0, -5.3101728000, 2.0328810000e-3, -3.2812633000e-3 /) - LoveDat(1:4,671) = (/ 670.0, -5.3127401000, 2.0316695000e-3, -3.2783028000e-3 /) - LoveDat(1:4,672) = (/ 671.0, -5.3153014000, 2.0304596000e-3, -3.2753474000e-3 /) - LoveDat(1:4,673) = (/ 672.0, -5.3178568000, 2.0292512000e-3, -3.2723970000e-3 /) - LoveDat(1:4,674) = (/ 673.0, -5.3204062000, 2.0280443000e-3, -3.2694517000e-3 /) - LoveDat(1:4,675) = (/ 674.0, -5.3229496000, 2.0268388000e-3, -3.2665113000e-3 /) - LoveDat(1:4,676) = (/ 675.0, -5.3254871000, 2.0256348000e-3, -3.2635759000e-3 /) - LoveDat(1:4,677) = (/ 676.0, -5.3280187000, 2.0244322000e-3, -3.2606454000e-3 /) - LoveDat(1:4,678) = (/ 677.0, -5.3305444000, 2.0232311000e-3, -3.2577199000e-3 /) - LoveDat(1:4,679) = (/ 678.0, -5.3330641000, 2.0220314000e-3, -3.2547992000e-3 /) - LoveDat(1:4,680) = (/ 679.0, -5.3355779000, 2.0208331000e-3, -3.2518834000e-3 /) - LoveDat(1:4,681) = (/ 680.0, -5.3380858000, 2.0196361000e-3, -3.2489725000e-3 /) - LoveDat(1:4,682) = (/ 681.0, -5.3405878000, 2.0184406000e-3, -3.2460664000e-3 /) - LoveDat(1:4,683) = (/ 682.0, -5.3430840000, 2.0172463000e-3, -3.2431652000e-3 /) - LoveDat(1:4,684) = (/ 683.0, -5.3455742000, 2.0160534000e-3, -3.2402687000e-3 /) - LoveDat(1:4,685) = (/ 684.0, -5.3480585000, 2.0148619000e-3, -3.2373770000e-3 /) - LoveDat(1:4,686) = (/ 685.0, -5.3505370000, 2.0136716000e-3, -3.2344900000e-3 /) - LoveDat(1:4,687) = (/ 686.0, -5.3530097000, 2.0124826000e-3, -3.2316078000e-3 /) - LoveDat(1:4,688) = (/ 687.0, -5.3554764000, 2.0112949000e-3, -3.2287303000e-3 /) - LoveDat(1:4,689) = (/ 688.0, -5.3579373000, 2.0101085000e-3, -3.2258574000e-3 /) - LoveDat(1:4,690) = (/ 689.0, -5.3603924000, 2.0089233000e-3, -3.2229893000e-3 /) - LoveDat(1:4,691) = (/ 690.0, -5.3628417000, 2.0077394000e-3, -3.2201258000e-3 /) - LoveDat(1:4,692) = (/ 691.0, -5.3652851000, 2.0065567000e-3, -3.2172669000e-3 /) - LoveDat(1:4,693) = (/ 692.0, -5.3677227000, 2.0053752000e-3, -3.2144126000e-3 /) - LoveDat(1:4,694) = (/ 693.0, -5.3701545000, 2.0041948000e-3, -3.2115629000e-3 /) - LoveDat(1:4,695) = (/ 694.0, -5.3725805000, 2.0030157000e-3, -3.2087178000e-3 /) - LoveDat(1:4,696) = (/ 695.0, -5.3750006000, 2.0018377000e-3, -3.2058772000e-3 /) - LoveDat(1:4,697) = (/ 696.0, -5.3774150000, 2.0006609000e-3, -3.2030412000e-3 /) - LoveDat(1:4,698) = (/ 697.0, -5.3798237000, 1.9994853000e-3, -3.2002097000e-3 /) - LoveDat(1:4,699) = (/ 698.0, -5.3822265000, 1.9983108000e-3, -3.1973826000e-3 /) - LoveDat(1:4,700) = (/ 699.0, -5.3846236000, 1.9971373000e-3, -3.1945601000e-3 /) - LoveDat(1:4,701) = (/ 700.0, -5.3870149000, 1.9959650000e-3, -3.1917420000e-3 /) - LoveDat(1:4,702) = (/ 701.0, -5.3894005000, 1.9947938000e-3, -3.1889283000e-3 /) - LoveDat(1:4,703) = (/ 702.0, -5.3917803000, 1.9936237000e-3, -3.1861191000e-3 /) - LoveDat(1:4,704) = (/ 703.0, -5.3941544000, 1.9924547000e-3, -3.1833143000e-3 /) - LoveDat(1:4,705) = (/ 704.0, -5.3965228000, 1.9912867000e-3, -3.1805139000e-3 /) - LoveDat(1:4,706) = (/ 705.0, -5.3988854000, 1.9901198000e-3, -3.1777178000e-3 /) - LoveDat(1:4,707) = (/ 706.0, -5.4012423000, 1.9889539000e-3, -3.1749261000e-3 /) - LoveDat(1:4,708) = (/ 707.0, -5.4035936000, 1.9877890000e-3, -3.1721387000e-3 /) - LoveDat(1:4,709) = (/ 708.0, -5.4059391000, 1.9866252000e-3, -3.1693556000e-3 /) - LoveDat(1:4,710) = (/ 709.0, -5.4082790000, 1.9854623000e-3, -3.1665769000e-3 /) - LoveDat(1:4,711) = (/ 710.0, -5.4106131000, 1.9843005000e-3, -3.1638024000e-3 /) - LoveDat(1:4,712) = (/ 711.0, -5.4129416000, 1.9831396000e-3, -3.1610322000e-3 /) - LoveDat(1:4,713) = (/ 712.0, -5.4152645000, 1.9819797000e-3, -3.1582662000e-3 /) - LoveDat(1:4,714) = (/ 713.0, -5.4175816000, 1.9808208000e-3, -3.1555045000e-3 /) - LoveDat(1:4,715) = (/ 714.0, -5.4198932000, 1.9796628000e-3, -3.1527469000e-3 /) - LoveDat(1:4,716) = (/ 715.0, -5.4221991000, 1.9785058000e-3, -3.1499936000e-3 /) - LoveDat(1:4,717) = (/ 716.0, -5.4244993000, 1.9773497000e-3, -3.1472445000e-3 /) - LoveDat(1:4,718) = (/ 717.0, -5.4267939000, 1.9761945000e-3, -3.1444995000e-3 /) - LoveDat(1:4,719) = (/ 718.0, -5.4290830000, 1.9750402000e-3, -3.1417587000e-3 /) - LoveDat(1:4,720) = (/ 719.0, -5.4313664000, 1.9738869000e-3, -3.1390221000e-3 /) - LoveDat(1:4,721) = (/ 720.0, -5.4336442000, 1.9727344000e-3, -3.1362895000e-3 /) - LoveDat(1:4,722) = (/ 721.0, -5.4359164000, 1.9715828000e-3, -3.1335611000e-3 /) - LoveDat(1:4,723) = (/ 722.0, -5.4381830000, 1.9704321000e-3, -3.1308367000e-3 /) - LoveDat(1:4,724) = (/ 723.0, -5.4404441000, 1.9692823000e-3, -3.1281164000e-3 /) - LoveDat(1:4,725) = (/ 724.0, -5.4426996000, 1.9681333000e-3, -3.1254002000e-3 /) - LoveDat(1:4,726) = (/ 725.0, -5.4449495000, 1.9669852000e-3, -3.1226881000e-3 /) - LoveDat(1:4,727) = (/ 726.0, -5.4471939000, 1.9658379000e-3, -3.1199799000e-3 /) - LoveDat(1:4,728) = (/ 727.0, -5.4494328000, 1.9646915000e-3, -3.1172758000e-3 /) - LoveDat(1:4,729) = (/ 728.0, -5.4516661000, 1.9635458000e-3, -3.1145757000e-3 /) - LoveDat(1:4,730) = (/ 729.0, -5.4538938000, 1.9624010000e-3, -3.1118796000e-3 /) - LoveDat(1:4,731) = (/ 730.0, -5.4561161000, 1.9612570000e-3, -3.1091874000e-3 /) - LoveDat(1:4,732) = (/ 731.0, -5.4583329000, 1.9601138000e-3, -3.1064992000e-3 /) - LoveDat(1:4,733) = (/ 732.0, -5.4605441000, 1.9589714000e-3, -3.1038149000e-3 /) - LoveDat(1:4,734) = (/ 733.0, -5.4627499000, 1.9578298000e-3, -3.1011346000e-3 /) - LoveDat(1:4,735) = (/ 734.0, -5.4649502000, 1.9566889000e-3, -3.0984582000e-3 /) - LoveDat(1:4,736) = (/ 735.0, -5.4671450000, 1.9555488000e-3, -3.0957857000e-3 /) - LoveDat(1:4,737) = (/ 736.0, -5.4693343000, 1.9544095000e-3, -3.0931171000e-3 /) - LoveDat(1:4,738) = (/ 737.0, -5.4715182000, 1.9532709000e-3, -3.0904524000e-3 /) - LoveDat(1:4,739) = (/ 738.0, -5.4736966000, 1.9521331000e-3, -3.0877915000e-3 /) - LoveDat(1:4,740) = (/ 739.0, -5.4758696000, 1.9509960000e-3, -3.0851345000e-3 /) - LoveDat(1:4,741) = (/ 740.0, -5.4780372000, 1.9498596000e-3, -3.0824813000e-3 /) - LoveDat(1:4,742) = (/ 741.0, -5.4801993000, 1.9487240000e-3, -3.0798319000e-3 /) - LoveDat(1:4,743) = (/ 742.0, -5.4823560000, 1.9475891000e-3, -3.0771864000e-3 /) - LoveDat(1:4,744) = (/ 743.0, -5.4845073000, 1.9464549000e-3, -3.0745446000e-3 /) - LoveDat(1:4,745) = (/ 744.0, -5.4866533000, 1.9453214000e-3, -3.0719066000e-3 /) - LoveDat(1:4,746) = (/ 745.0, -5.4887938000, 1.9441885000e-3, -3.0692724000e-3 /) - LoveDat(1:4,747) = (/ 746.0, -5.4909289000, 1.9430564000e-3, -3.0666420000e-3 /) - LoveDat(1:4,748) = (/ 747.0, -5.4930587000, 1.9419250000e-3, -3.0640153000e-3 /) - LoveDat(1:4,749) = (/ 748.0, -5.4951831000, 1.9407942000e-3, -3.0613923000e-3 /) - LoveDat(1:4,750) = (/ 749.0, -5.4973021000, 1.9396641000e-3, -3.0587731000e-3 /) - LoveDat(1:4,751) = (/ 750.0, -5.4994158000, 1.9385347000e-3, -3.0561575000e-3 /) - LoveDat(1:4,752) = (/ 751.0, -5.5015242000, 1.9374059000e-3, -3.0535457000e-3 /) - LoveDat(1:4,753) = (/ 752.0, -5.5036272000, 1.9362778000e-3, -3.0509375000e-3 /) - LoveDat(1:4,754) = (/ 753.0, -5.5057250000, 1.9351503000e-3, -3.0483331000e-3 /) - LoveDat(1:4,755) = (/ 754.0, -5.5078174000, 1.9340235000e-3, -3.0457322000e-3 /) - LoveDat(1:4,756) = (/ 755.0, -5.5099044000, 1.9328973000e-3, -3.0431351000e-3 /) - LoveDat(1:4,757) = (/ 756.0, -5.5119863000, 1.9317717000e-3, -3.0405415000e-3 /) - LoveDat(1:4,758) = (/ 757.0, -5.5140628000, 1.9306468000e-3, -3.0379516000e-3 /) - LoveDat(1:4,759) = (/ 758.0, -5.5161340000, 1.9295224000e-3, -3.0353653000e-3 /) - LoveDat(1:4,760) = (/ 759.0, -5.5182000000, 1.9283987000e-3, -3.0327826000e-3 /) - LoveDat(1:4,761) = (/ 760.0, -5.5202607000, 1.9272756000e-3, -3.0302035000e-3 /) - LoveDat(1:4,762) = (/ 761.0, -5.5223161000, 1.9261531000e-3, -3.0276280000e-3 /) - LoveDat(1:4,763) = (/ 762.0, -5.5243664000, 1.9250311000e-3, -3.0250561000e-3 /) - LoveDat(1:4,764) = (/ 763.0, -5.5264113000, 1.9239098000e-3, -3.0224877000e-3 /) - LoveDat(1:4,765) = (/ 764.0, -5.5284511000, 1.9227890000e-3, -3.0199228000e-3 /) - LoveDat(1:4,766) = (/ 765.0, -5.5304856000, 1.9216689000e-3, -3.0173615000e-3 /) - LoveDat(1:4,767) = (/ 766.0, -5.5325150000, 1.9205493000e-3, -3.0148038000e-3 /) - LoveDat(1:4,768) = (/ 767.0, -5.5345391000, 1.9194303000e-3, -3.0122495000e-3 /) - LoveDat(1:4,769) = (/ 768.0, -5.5365581000, 1.9183118000e-3, -3.0096987000e-3 /) - LoveDat(1:4,770) = (/ 769.0, -5.5385718000, 1.9171939000e-3, -3.0071515000e-3 /) - LoveDat(1:4,771) = (/ 770.0, -5.5405804000, 1.9160766000e-3, -3.0046077000e-3 /) - LoveDat(1:4,772) = (/ 771.0, -5.5425839000, 1.9149598000e-3, -3.0020674000e-3 /) - LoveDat(1:4,773) = (/ 772.0, -5.5445822000, 1.9138435000e-3, -2.9995305000e-3 /) - LoveDat(1:4,774) = (/ 773.0, -5.5465753000, 1.9127278000e-3, -2.9969971000e-3 /) - LoveDat(1:4,775) = (/ 774.0, -5.5485633000, 1.9116127000e-3, -2.9944671000e-3 /) - LoveDat(1:4,776) = (/ 775.0, -5.5505462000, 1.9104981000e-3, -2.9919406000e-3 /) - LoveDat(1:4,777) = (/ 776.0, -5.5525239000, 1.9093840000e-3, -2.9894175000e-3 /) - LoveDat(1:4,778) = (/ 777.0, -5.5544966000, 1.9082704000e-3, -2.9868978000e-3 /) - LoveDat(1:4,779) = (/ 778.0, -5.5564641000, 1.9071573000e-3, -2.9843815000e-3 /) - LoveDat(1:4,780) = (/ 779.0, -5.5584266000, 1.9060448000e-3, -2.9818686000e-3 /) - LoveDat(1:4,781) = (/ 780.0, -5.5603840000, 1.9049328000e-3, -2.9793591000e-3 /) - LoveDat(1:4,782) = (/ 781.0, -5.5623363000, 1.9038213000e-3, -2.9768530000e-3 /) - LoveDat(1:4,783) = (/ 782.0, -5.5642835000, 1.9027103000e-3, -2.9743502000e-3 /) - LoveDat(1:4,784) = (/ 783.0, -5.5662257000, 1.9015998000e-3, -2.9718507000e-3 /) - LoveDat(1:4,785) = (/ 784.0, -5.5681628000, 1.9004898000e-3, -2.9693547000e-3 /) - LoveDat(1:4,786) = (/ 785.0, -5.5700949000, 1.8993803000e-3, -2.9668619000e-3 /) - LoveDat(1:4,787) = (/ 786.0, -5.5720220000, 1.8982713000e-3, -2.9643725000e-3 /) - LoveDat(1:4,788) = (/ 787.0, -5.5739440000, 1.8971627000e-3, -2.9618864000e-3 /) - LoveDat(1:4,789) = (/ 788.0, -5.5758611000, 1.8960547000e-3, -2.9594036000e-3 /) - LoveDat(1:4,790) = (/ 789.0, -5.5777731000, 1.8949471000e-3, -2.9569240000e-3 /) - LoveDat(1:4,791) = (/ 790.0, -5.5796802000, 1.8938401000e-3, -2.9544478000e-3 /) - LoveDat(1:4,792) = (/ 791.0, -5.5815822000, 1.8927334000e-3, -2.9519749000e-3 /) - LoveDat(1:4,793) = (/ 792.0, -5.5834793000, 1.8916273000e-3, -2.9495052000e-3 /) - LoveDat(1:4,794) = (/ 793.0, -5.5853715000, 1.8905216000e-3, -2.9470388000e-3 /) - LoveDat(1:4,795) = (/ 794.0, -5.5872586000, 1.8894164000e-3, -2.9445756000e-3 /) - LoveDat(1:4,796) = (/ 795.0, -5.5891409000, 1.8883117000e-3, -2.9421157000e-3 /) - LoveDat(1:4,797) = (/ 796.0, -5.5910182000, 1.8872074000e-3, -2.9396591000e-3 /) - LoveDat(1:4,798) = (/ 797.0, -5.5928905000, 1.8861036000e-3, -2.9372056000e-3 /) - LoveDat(1:4,799) = (/ 798.0, -5.5947580000, 1.8850002000e-3, -2.9347554000e-3 /) - LoveDat(1:4,800) = (/ 799.0, -5.5966205000, 1.8838973000e-3, -2.9323083000e-3 /) - LoveDat(1:4,801) = (/ 800.0, -5.5984781000, 1.8827948000e-3, -2.9298645000e-3 /) - LoveDat(1:4,802) = (/ 801.0, -5.6003309000, 1.8816928000e-3, -2.9274239000e-3 /) - LoveDat(1:4,803) = (/ 802.0, -5.6021787000, 1.8805912000e-3, -2.9249864000e-3 /) - LoveDat(1:4,804) = (/ 803.0, -5.6040217000, 1.8794901000e-3, -2.9225522000e-3 /) - LoveDat(1:4,805) = (/ 804.0, -5.6058598000, 1.8783894000e-3, -2.9201211000e-3 /) - LoveDat(1:4,806) = (/ 805.0, -5.6076931000, 1.8772891000e-3, -2.9176931000e-3 /) - LoveDat(1:4,807) = (/ 806.0, -5.6095215000, 1.8761892000e-3, -2.9152683000e-3 /) - LoveDat(1:4,808) = (/ 807.0, -5.6113451000, 1.8750898000e-3, -2.9128467000e-3 /) - LoveDat(1:4,809) = (/ 808.0, -5.6131638000, 1.8739909000e-3, -2.9104282000e-3 /) - LoveDat(1:4,810) = (/ 809.0, -5.6149777000, 1.8728923000e-3, -2.9080128000e-3 /) - LoveDat(1:4,811) = (/ 810.0, -5.6167869000, 1.8717942000e-3, -2.9056005000e-3 /) - LoveDat(1:4,812) = (/ 811.0, -5.6185912000, 1.8706965000e-3, -2.9031914000e-3 /) - LoveDat(1:4,813) = (/ 812.0, -5.6203907000, 1.8695992000e-3, -2.9007853000e-3 /) - LoveDat(1:4,814) = (/ 813.0, -5.6221855000, 1.8685023000e-3, -2.8983824000e-3 /) - LoveDat(1:4,815) = (/ 814.0, -5.6239754000, 1.8674058000e-3, -2.8959825000e-3 /) - LoveDat(1:4,816) = (/ 815.0, -5.6257606000, 1.8663098000e-3, -2.8935857000e-3 /) - LoveDat(1:4,817) = (/ 816.0, -5.6275411000, 1.8652141000e-3, -2.8911920000e-3 /) - LoveDat(1:4,818) = (/ 817.0, -5.6293168000, 1.8641189000e-3, -2.8888014000e-3 /) - LoveDat(1:4,819) = (/ 818.0, -5.6310878000, 1.8630241000e-3, -2.8864138000e-3 /) - LoveDat(1:4,820) = (/ 819.0, -5.6328540000, 1.8619297000e-3, -2.8840292000e-3 /) - LoveDat(1:4,821) = (/ 820.0, -5.6346155000, 1.8608357000e-3, -2.8816477000e-3 /) - LoveDat(1:4,822) = (/ 821.0, -5.6363723000, 1.8597420000e-3, -2.8792693000e-3 /) - LoveDat(1:4,823) = (/ 822.0, -5.6381245000, 1.8586488000e-3, -2.8768939000e-3 /) - LoveDat(1:4,824) = (/ 823.0, -5.6398719000, 1.8575560000e-3, -2.8745215000e-3 /) - LoveDat(1:4,825) = (/ 824.0, -5.6416146000, 1.8564636000e-3, -2.8721521000e-3 /) - LoveDat(1:4,826) = (/ 825.0, -5.6433527000, 1.8553716000e-3, -2.8697857000e-3 /) - LoveDat(1:4,827) = (/ 826.0, -5.6450861000, 1.8542799000e-3, -2.8674223000e-3 /) - LoveDat(1:4,828) = (/ 827.0, -5.6468149000, 1.8531887000e-3, -2.8650619000e-3 /) - LoveDat(1:4,829) = (/ 828.0, -5.6485390000, 1.8520979000e-3, -2.8627045000e-3 /) - LoveDat(1:4,830) = (/ 829.0, -5.6502584000, 1.8510074000e-3, -2.8603501000e-3 /) - LoveDat(1:4,831) = (/ 830.0, -5.6519733000, 1.8499173000e-3, -2.8579986000e-3 /) - LoveDat(1:4,832) = (/ 831.0, -5.6536835000, 1.8488276000e-3, -2.8556502000e-3 /) - LoveDat(1:4,833) = (/ 832.0, -5.6553891000, 1.8477384000e-3, -2.8533046000e-3 /) - LoveDat(1:4,834) = (/ 833.0, -5.6570901000, 1.8466494000e-3, -2.8509621000e-3 /) - LoveDat(1:4,835) = (/ 834.0, -5.6587866000, 1.8455609000e-3, -2.8486224000e-3 /) - LoveDat(1:4,836) = (/ 835.0, -5.6604784000, 1.8444728000e-3, -2.8462858000e-3 /) - LoveDat(1:4,837) = (/ 836.0, -5.6621657000, 1.8433850000e-3, -2.8439520000e-3 /) - LoveDat(1:4,838) = (/ 837.0, -5.6638484000, 1.8422976000e-3, -2.8416212000e-3 /) - LoveDat(1:4,839) = (/ 838.0, -5.6655266000, 1.8412106000e-3, -2.8392933000e-3 /) - LoveDat(1:4,840) = (/ 839.0, -5.6672002000, 1.8401239000e-3, -2.8369683000e-3 /) - LoveDat(1:4,841) = (/ 840.0, -5.6688693000, 1.8390377000e-3, -2.8346462000e-3 /) - LoveDat(1:4,842) = (/ 841.0, -5.6705338000, 1.8379518000e-3, -2.8323270000e-3 /) - LoveDat(1:4,843) = (/ 842.0, -5.6721939000, 1.8368663000e-3, -2.8300107000e-3 /) - LoveDat(1:4,844) = (/ 843.0, -5.6738494000, 1.8357811000e-3, -2.8276973000e-3 /) - LoveDat(1:4,845) = (/ 844.0, -5.6755004000, 1.8346964000e-3, -2.8253867000e-3 /) - LoveDat(1:4,846) = (/ 845.0, -5.6771470000, 1.8336120000e-3, -2.8230791000e-3 /) - LoveDat(1:4,847) = (/ 846.0, -5.6787890000, 1.8325279000e-3, -2.8207743000e-3 /) - LoveDat(1:4,848) = (/ 847.0, -5.6804266000, 1.8314443000e-3, -2.8184724000e-3 /) - LoveDat(1:4,849) = (/ 848.0, -5.6820597000, 1.8303610000e-3, -2.8161733000e-3 /) - LoveDat(1:4,850) = (/ 849.0, -5.6836884000, 1.8292781000e-3, -2.8138771000e-3 /) - LoveDat(1:4,851) = (/ 850.0, -5.6853127000, 1.8281955000e-3, -2.8115837000e-3 /) - LoveDat(1:4,852) = (/ 851.0, -5.6869325000, 1.8271133000e-3, -2.8092932000e-3 /) - LoveDat(1:4,853) = (/ 852.0, -5.6885478000, 1.8260315000e-3, -2.8070055000e-3 /) - LoveDat(1:4,854) = (/ 853.0, -5.6901588000, 1.8249501000e-3, -2.8047206000e-3 /) - LoveDat(1:4,855) = (/ 854.0, -5.6917653000, 1.8238690000e-3, -2.8024385000e-3 /) - LoveDat(1:4,856) = (/ 855.0, -5.6933675000, 1.8227882000e-3, -2.8001593000e-3 /) - LoveDat(1:4,857) = (/ 856.0, -5.6949653000, 1.8217079000e-3, -2.7978829000e-3 /) - LoveDat(1:4,858) = (/ 857.0, -5.6965586000, 1.8206279000e-3, -2.7956092000e-3 /) - LoveDat(1:4,859) = (/ 858.0, -5.6981477000, 1.8195482000e-3, -2.7933384000e-3 /) - LoveDat(1:4,860) = (/ 859.0, -5.6997323000, 1.8184690000e-3, -2.7910703000e-3 /) - LoveDat(1:4,861) = (/ 860.0, -5.7013126000, 1.8173900000e-3, -2.7888051000e-3 /) - LoveDat(1:4,862) = (/ 861.0, -5.7028886000, 1.8163115000e-3, -2.7865426000e-3 /) - LoveDat(1:4,863) = (/ 862.0, -5.7044602000, 1.8152333000e-3, -2.7842829000e-3 /) - LoveDat(1:4,864) = (/ 863.0, -5.7060275000, 1.8141555000e-3, -2.7820260000e-3 /) - LoveDat(1:4,865) = (/ 864.0, -5.7075905000, 1.8130780000e-3, -2.7797718000e-3 /) - LoveDat(1:4,866) = (/ 865.0, -5.7091492000, 1.8120009000e-3, -2.7775204000e-3 /) - LoveDat(1:4,867) = (/ 866.0, -5.7107035000, 1.8109241000e-3, -2.7752717000e-3 /) - LoveDat(1:4,868) = (/ 867.0, -5.7122536000, 1.8098477000e-3, -2.7730258000e-3 /) - LoveDat(1:4,869) = (/ 868.0, -5.7137995000, 1.8087717000e-3, -2.7707826000e-3 /) - LoveDat(1:4,870) = (/ 869.0, -5.7153410000, 1.8076960000e-3, -2.7685421000e-3 /) - LoveDat(1:4,871) = (/ 870.0, -5.7168783000, 1.8066207000e-3, -2.7663044000e-3 /) - LoveDat(1:4,872) = (/ 871.0, -5.7184113000, 1.8055458000e-3, -2.7640694000e-3 /) - LoveDat(1:4,873) = (/ 872.0, -5.7199401000, 1.8044712000e-3, -2.7618372000e-3 /) - LoveDat(1:4,874) = (/ 873.0, -5.7214646000, 1.8033969000e-3, -2.7596076000e-3 /) - LoveDat(1:4,875) = (/ 874.0, -5.7229850000, 1.8023230000e-3, -2.7573808000e-3 /) - LoveDat(1:4,876) = (/ 875.0, -5.7245011000, 1.8012495000e-3, -2.7551566000e-3 /) - LoveDat(1:4,877) = (/ 876.0, -5.7260130000, 1.8001763000e-3, -2.7529352000e-3 /) - LoveDat(1:4,878) = (/ 877.0, -5.7275207000, 1.7991035000e-3, -2.7507164000e-3 /) - LoveDat(1:4,879) = (/ 878.0, -5.7290242000, 1.7980311000e-3, -2.7485003000e-3 /) - LoveDat(1:4,880) = (/ 879.0, -5.7305236000, 1.7969590000e-3, -2.7462870000e-3 /) - LoveDat(1:4,881) = (/ 880.0, -5.7320187000, 1.7958873000e-3, -2.7440763000e-3 /) - LoveDat(1:4,882) = (/ 881.0, -5.7335097000, 1.7948159000e-3, -2.7418682000e-3 /) - LoveDat(1:4,883) = (/ 882.0, -5.7349966000, 1.7937449000e-3, -2.7396629000e-3 /) - LoveDat(1:4,884) = (/ 883.0, -5.7364793000, 1.7926742000e-3, -2.7374601000e-3 /) - LoveDat(1:4,885) = (/ 884.0, -5.7379579000, 1.7916039000e-3, -2.7352601000e-3 /) - LoveDat(1:4,886) = (/ 885.0, -5.7394323000, 1.7905340000e-3, -2.7330627000e-3 /) - LoveDat(1:4,887) = (/ 886.0, -5.7409027000, 1.7894644000e-3, -2.7308680000e-3 /) - LoveDat(1:4,888) = (/ 887.0, -5.7423689000, 1.7883951000e-3, -2.7286759000e-3 /) - LoveDat(1:4,889) = (/ 888.0, -5.7438310000, 1.7873263000e-3, -2.7264864000e-3 /) - LoveDat(1:4,890) = (/ 889.0, -5.7452891000, 1.7862578000e-3, -2.7242996000e-3 /) - LoveDat(1:4,891) = (/ 890.0, -5.7467430000, 1.7851896000e-3, -2.7221154000e-3 /) - LoveDat(1:4,892) = (/ 891.0, -5.7481929000, 1.7841218000e-3, -2.7199338000e-3 /) - LoveDat(1:4,893) = (/ 892.0, -5.7496387000, 1.7830544000e-3, -2.7177548000e-3 /) - LoveDat(1:4,894) = (/ 893.0, -5.7510805000, 1.7819873000e-3, -2.7155785000e-3 /) - LoveDat(1:4,895) = (/ 894.0, -5.7525182000, 1.7809206000e-3, -2.7134047000e-3 /) - LoveDat(1:4,896) = (/ 895.0, -5.7539519000, 1.7798543000e-3, -2.7112336000e-3 /) - LoveDat(1:4,897) = (/ 896.0, -5.7553816000, 1.7787883000e-3, -2.7090651000e-3 /) - LoveDat(1:4,898) = (/ 897.0, -5.7568072000, 1.7777227000e-3, -2.7068991000e-3 /) - LoveDat(1:4,899) = (/ 898.0, -5.7582289000, 1.7766574000e-3, -2.7047358000e-3 /) - LoveDat(1:4,900) = (/ 899.0, -5.7596465000, 1.7755925000e-3, -2.7025750000e-3 /) - LoveDat(1:4,901) = (/ 900.0, -5.7610602000, 1.7745280000e-3, -2.7004169000e-3 /) - LoveDat(1:4,902) = (/ 901.0, -5.7624698000, 1.7734638000e-3, -2.6982613000e-3 /) - LoveDat(1:4,903) = (/ 902.0, -5.7638755000, 1.7724000000e-3, -2.6961082000e-3 /) - LoveDat(1:4,904) = (/ 903.0, -5.7652772000, 1.7713365000e-3, -2.6939578000e-3 /) - LoveDat(1:4,905) = (/ 904.0, -5.7666750000, 1.7702734000e-3, -2.6918099000e-3 /) - LoveDat(1:4,906) = (/ 905.0, -5.7680688000, 1.7692107000e-3, -2.6896645000e-3 /) - LoveDat(1:4,907) = (/ 906.0, -5.7694587000, 1.7681484000e-3, -2.6875218000e-3 /) - LoveDat(1:4,908) = (/ 907.0, -5.7708447000, 1.7670864000e-3, -2.6853815000e-3 /) - LoveDat(1:4,909) = (/ 908.0, -5.7722267000, 1.7660247000e-3, -2.6832438000e-3 /) - LoveDat(1:4,910) = (/ 909.0, -5.7736048000, 1.7649635000e-3, -2.6811087000e-3 /) - LoveDat(1:4,911) = (/ 910.0, -5.7749791000, 1.7639026000e-3, -2.6789761000e-3 /) - LoveDat(1:4,912) = (/ 911.0, -5.7763494000, 1.7628421000e-3, -2.6768460000e-3 /) - LoveDat(1:4,913) = (/ 912.0, -5.7777158000, 1.7617819000e-3, -2.6747185000e-3 /) - LoveDat(1:4,914) = (/ 913.0, -5.7790784000, 1.7607221000e-3, -2.6725934000e-3 /) - LoveDat(1:4,915) = (/ 914.0, -5.7804371000, 1.7596627000e-3, -2.6704709000e-3 /) - LoveDat(1:4,916) = (/ 915.0, -5.7817919000, 1.7586037000e-3, -2.6683510000e-3 /) - LoveDat(1:4,917) = (/ 916.0, -5.7831429000, 1.7575450000e-3, -2.6662335000e-3 /) - LoveDat(1:4,918) = (/ 917.0, -5.7844901000, 1.7564867000e-3, -2.6641185000e-3 /) - LoveDat(1:4,919) = (/ 918.0, -5.7858334000, 1.7554288000e-3, -2.6620060000e-3 /) - LoveDat(1:4,920) = (/ 919.0, -5.7871729000, 1.7543712000e-3, -2.6598961000e-3 /) - LoveDat(1:4,921) = (/ 920.0, -5.7885086000, 1.7533140000e-3, -2.6577886000e-3 /) - LoveDat(1:4,922) = (/ 921.0, -5.7898405000, 1.7522572000e-3, -2.6556836000e-3 /) - LoveDat(1:4,923) = (/ 922.0, -5.7911686000, 1.7512008000e-3, -2.6535811000e-3 /) - LoveDat(1:4,924) = (/ 923.0, -5.7924928000, 1.7501447000e-3, -2.6514811000e-3 /) - LoveDat(1:4,925) = (/ 924.0, -5.7938134000, 1.7490890000e-3, -2.6493836000e-3 /) - LoveDat(1:4,926) = (/ 925.0, -5.7951301000, 1.7480337000e-3, -2.6472885000e-3 /) - LoveDat(1:4,927) = (/ 926.0, -5.7964431000, 1.7469788000e-3, -2.6451960000e-3 /) - LoveDat(1:4,928) = (/ 927.0, -5.7977523000, 1.7459242000e-3, -2.6431058000e-3 /) - LoveDat(1:4,929) = (/ 928.0, -5.7990578000, 1.7448700000e-3, -2.6410182000e-3 /) - LoveDat(1:4,930) = (/ 929.0, -5.8003595000, 1.7438162000e-3, -2.6389330000e-3 /) - LoveDat(1:4,931) = (/ 930.0, -5.8016575000, 1.7427628000e-3, -2.6368502000e-3 /) - LoveDat(1:4,932) = (/ 931.0, -5.8029518000, 1.7417098000e-3, -2.6347699000e-3 /) - LoveDat(1:4,933) = (/ 932.0, -5.8042424000, 1.7406571000e-3, -2.6326921000e-3 /) - LoveDat(1:4,934) = (/ 933.0, -5.8055293000, 1.7396049000e-3, -2.6306167000e-3 /) - LoveDat(1:4,935) = (/ 934.0, -5.8068125000, 1.7385530000e-3, -2.6285437000e-3 /) - LoveDat(1:4,936) = (/ 935.0, -5.8080920000, 1.7375015000e-3, -2.6264732000e-3 /) - LoveDat(1:4,937) = (/ 936.0, -5.8093679000, 1.7364504000e-3, -2.6244051000e-3 /) - LoveDat(1:4,938) = (/ 937.0, -5.8106400000, 1.7353996000e-3, -2.6223394000e-3 /) - LoveDat(1:4,939) = (/ 938.0, -5.8119085000, 1.7343493000e-3, -2.6202762000e-3 /) - LoveDat(1:4,940) = (/ 939.0, -5.8131734000, 1.7332993000e-3, -2.6182153000e-3 /) - LoveDat(1:4,941) = (/ 940.0, -5.8144346000, 1.7322497000e-3, -2.6161569000e-3 /) - LoveDat(1:4,942) = (/ 941.0, -5.8156922000, 1.7312006000e-3, -2.6141009000e-3 /) - LoveDat(1:4,943) = (/ 942.0, -5.8169461000, 1.7301518000e-3, -2.6120473000e-3 /) - LoveDat(1:4,944) = (/ 943.0, -5.8181965000, 1.7291034000e-3, -2.6099961000e-3 /) - LoveDat(1:4,945) = (/ 944.0, -5.8194432000, 1.7280553000e-3, -2.6079473000e-3 /) - LoveDat(1:4,946) = (/ 945.0, -5.8206864000, 1.7270077000e-3, -2.6059010000e-3 /) - LoveDat(1:4,947) = (/ 946.0, -5.8219259000, 1.7259605000e-3, -2.6038570000e-3 /) - LoveDat(1:4,948) = (/ 947.0, -5.8231619000, 1.7249137000e-3, -2.6018154000e-3 /) - LoveDat(1:4,949) = (/ 948.0, -5.8243943000, 1.7238672000e-3, -2.5997761000e-3 /) - LoveDat(1:4,950) = (/ 949.0, -5.8256231000, 1.7228212000e-3, -2.5977393000e-3 /) - LoveDat(1:4,951) = (/ 950.0, -5.8268484000, 1.7217755000e-3, -2.5957048000e-3 /) - LoveDat(1:4,952) = (/ 951.0, -5.8280701000, 1.7207303000e-3, -2.5936728000e-3 /) - LoveDat(1:4,953) = (/ 952.0, -5.8292883000, 1.7196854000e-3, -2.5916430000e-3 /) - LoveDat(1:4,954) = (/ 953.0, -5.8305029000, 1.7186410000e-3, -2.5896157000e-3 /) - LoveDat(1:4,955) = (/ 954.0, -5.8317141000, 1.7175970000e-3, -2.5875907000e-3 /) - LoveDat(1:4,956) = (/ 955.0, -5.8329217000, 1.7165533000e-3, -2.5855681000e-3 /) - LoveDat(1:4,957) = (/ 956.0, -5.8341258000, 1.7155101000e-3, -2.5835478000e-3 /) - LoveDat(1:4,958) = (/ 957.0, -5.8353264000, 1.7144672000e-3, -2.5815299000e-3 /) - LoveDat(1:4,959) = (/ 958.0, -5.8365235000, 1.7134248000e-3, -2.5795144000e-3 /) - LoveDat(1:4,960) = (/ 959.0, -5.8377172000, 1.7123828000e-3, -2.5775012000e-3 /) - LoveDat(1:4,961) = (/ 960.0, -5.8389074000, 1.7113411000e-3, -2.5754903000e-3 /) - LoveDat(1:4,962) = (/ 961.0, -5.8400941000, 1.7102999000e-3, -2.5734818000e-3 /) - LoveDat(1:4,963) = (/ 962.0, -5.8412773000, 1.7092591000e-3, -2.5714756000e-3 /) - LoveDat(1:4,964) = (/ 963.0, -5.8424571000, 1.7082187000e-3, -2.5694717000e-3 /) - LoveDat(1:4,965) = (/ 964.0, -5.8436335000, 1.7071787000e-3, -2.5674702000e-3 /) - LoveDat(1:4,966) = (/ 965.0, -5.8448065000, 1.7061391000e-3, -2.5654710000e-3 /) - LoveDat(1:4,967) = (/ 966.0, -5.8459760000, 1.7051000000e-3, -2.5634741000e-3 /) - LoveDat(1:4,968) = (/ 967.0, -5.8471421000, 1.7040612000e-3, -2.5614796000e-3 /) - LoveDat(1:4,969) = (/ 968.0, -5.8483048000, 1.7030229000e-3, -2.5594873000e-3 /) - LoveDat(1:4,970) = (/ 969.0, -5.8494641000, 1.7019850000e-3, -2.5574974000e-3 /) - LoveDat(1:4,971) = (/ 970.0, -5.8506200000, 1.7009475000e-3, -2.5555098000e-3 /) - LoveDat(1:4,972) = (/ 971.0, -5.8517725000, 1.6999104000e-3, -2.5535245000e-3 /) - LoveDat(1:4,973) = (/ 972.0, -5.8529217000, 1.6988737000e-3, -2.5515415000e-3 /) - LoveDat(1:4,974) = (/ 973.0, -5.8540675000, 1.6978374000e-3, -2.5495608000e-3 /) - LoveDat(1:4,975) = (/ 974.0, -5.8552099000, 1.6968016000e-3, -2.5475824000e-3 /) - LoveDat(1:4,976) = (/ 975.0, -5.8563490000, 1.6957662000e-3, -2.5456062000e-3 /) - LoveDat(1:4,977) = (/ 976.0, -5.8574847000, 1.6947312000e-3, -2.5436324000e-3 /) - LoveDat(1:4,978) = (/ 977.0, -5.8586172000, 1.6936966000e-3, -2.5416609000e-3 /) - LoveDat(1:4,979) = (/ 978.0, -5.8597463000, 1.6926625000e-3, -2.5396916000e-3 /) - LoveDat(1:4,980) = (/ 979.0, -5.8608720000, 1.6916288000e-3, -2.5377246000e-3 /) - LoveDat(1:4,981) = (/ 980.0, -5.8619945000, 1.6905955000e-3, -2.5357599000e-3 /) - LoveDat(1:4,982) = (/ 981.0, -5.8631137000, 1.6895626000e-3, -2.5337975000e-3 /) - LoveDat(1:4,983) = (/ 982.0, -5.8642296000, 1.6885302000e-3, -2.5318373000e-3 /) - LoveDat(1:4,984) = (/ 983.0, -5.8653422000, 1.6874982000e-3, -2.5298794000e-3 /) - LoveDat(1:4,985) = (/ 984.0, -5.8664515000, 1.6864666000e-3, -2.5279238000e-3 /) - LoveDat(1:4,986) = (/ 985.0, -5.8675576000, 1.6854355000e-3, -2.5259704000e-3 /) - LoveDat(1:4,987) = (/ 986.0, -5.8686604000, 1.6844048000e-3, -2.5240193000e-3 /) - LoveDat(1:4,988) = (/ 987.0, -5.8697599000, 1.6833745000e-3, -2.5220704000e-3 /) - LoveDat(1:4,989) = (/ 988.0, -5.8708562000, 1.6823447000e-3, -2.5201238000e-3 /) - LoveDat(1:4,990) = (/ 989.0, -5.8719493000, 1.6813153000e-3, -2.5181795000e-3 /) - LoveDat(1:4,991) = (/ 990.0, -5.8730391000, 1.6802863000e-3, -2.5162374000e-3 /) - LoveDat(1:4,992) = (/ 991.0, -5.8741258000, 1.6792578000e-3, -2.5142975000e-3 /) - LoveDat(1:4,993) = (/ 992.0, -5.8752092000, 1.6782297000e-3, -2.5123598000e-3 /) - LoveDat(1:4,994) = (/ 993.0, -5.8762894000, 1.6772020000e-3, -2.5104244000e-3 /) - LoveDat(1:4,995) = (/ 994.0, -5.8773664000, 1.6761748000e-3, -2.5084913000e-3 /) - LoveDat(1:4,996) = (/ 995.0, -5.8784403000, 1.6751480000e-3, -2.5065603000e-3 /) - LoveDat(1:4,997) = (/ 996.0, -5.8795109000, 1.6741217000e-3, -2.5046316000e-3 /) - LoveDat(1:4,998) = (/ 997.0, -5.8805784000, 1.6730958000e-3, -2.5027051000e-3 /) - LoveDat(1:4,999) = (/ 998.0, -5.8816427000, 1.6720704000e-3, -2.5007809000e-3 /) - LoveDat(1:4,1000) = (/ 999.0, -5.8827039000, 1.6710454000e-3, -2.4988588000e-3 /) - LoveDat(1:4,1001) = (/ 1000.0, -5.8837619000, 1.6700209000e-3, -2.4969390000e-3 /) - LoveDat(1:4,1002) = (/ 1001.0, -5.8848168000, 1.6689968000e-3, -2.4950213000e-3 /) - LoveDat(1:4,1003) = (/ 1002.0, -5.8858686000, 1.6679732000e-3, -2.4931059000e-3 /) - LoveDat(1:4,1004) = (/ 1003.0, -5.8869172000, 1.6669500000e-3, -2.4911927000e-3 /) - LoveDat(1:4,1005) = (/ 1004.0, -5.8879627000, 1.6659272000e-3, -2.4892817000e-3 /) - LoveDat(1:4,1006) = (/ 1005.0, -5.8890051000, 1.6649049000e-3, -2.4873729000e-3 /) - LoveDat(1:4,1007) = (/ 1006.0, -5.8900444000, 1.6638831000e-3, -2.4854663000e-3 /) - LoveDat(1:4,1008) = (/ 1007.0, -5.8910807000, 1.6628617000e-3, -2.4835619000e-3 /) - LoveDat(1:4,1009) = (/ 1008.0, -5.8921138000, 1.6618408000e-3, -2.4816596000e-3 /) - LoveDat(1:4,1010) = (/ 1009.0, -5.8931439000, 1.6608204000e-3, -2.4797596000e-3 /) - LoveDat(1:4,1011) = (/ 1010.0, -5.8941708000, 1.6598004000e-3, -2.4778617000e-3 /) - LoveDat(1:4,1012) = (/ 1011.0, -5.8951948000, 1.6587808000e-3, -2.4759660000e-3 /) - LoveDat(1:4,1013) = (/ 1012.0, -5.8962156000, 1.6577617000e-3, -2.4740725000e-3 /) - LoveDat(1:4,1014) = (/ 1013.0, -5.8972335000, 1.6567431000e-3, -2.4721812000e-3 /) - LoveDat(1:4,1015) = (/ 1014.0, -5.8982483000, 1.6557250000e-3, -2.4702920000e-3 /) - LoveDat(1:4,1016) = (/ 1015.0, -5.8992600000, 1.6547073000e-3, -2.4684051000e-3 /) - LoveDat(1:4,1017) = (/ 1016.0, -5.9002688000, 1.6536901000e-3, -2.4665202000e-3 /) - LoveDat(1:4,1018) = (/ 1017.0, -5.9012745000, 1.6526733000e-3, -2.4646376000e-3 /) - LoveDat(1:4,1019) = (/ 1018.0, -5.9022772000, 1.6516570000e-3, -2.4627571000e-3 /) - LoveDat(1:4,1020) = (/ 1019.0, -5.9032769000, 1.6506412000e-3, -2.4608788000e-3 /) - LoveDat(1:4,1021) = (/ 1020.0, -5.9042737000, 1.6496258000e-3, -2.4590026000e-3 /) - LoveDat(1:4,1022) = (/ 1021.0, -5.9052674000, 1.6486109000e-3, -2.4571286000e-3 /) - LoveDat(1:4,1023) = (/ 1022.0, -5.9062582000, 1.6475965000e-3, -2.4552567000e-3 /) - LoveDat(1:4,1024) = (/ 1023.0, -5.9072460000, 1.6465826000e-3, -2.4533870000e-3 /) - LoveDat(1:4,1025) = (/ 1024.0, -5.9082308000, 1.6455691000e-3, -2.4515194000e-3 /) - LoveDat(1:4,1026) = (/ 1025.0, -5.9092127000, 1.6445561000e-3, -2.4496539000e-3 /) - LoveDat(1:4,1027) = (/ 1026.0, -5.9101917000, 1.6435436000e-3, -2.4477906000e-3 /) - LoveDat(1:4,1028) = (/ 1027.0, -5.9111677000, 1.6425316000e-3, -2.4459295000e-3 /) - LoveDat(1:4,1029) = (/ 1028.0, -5.9121408000, 1.6415200000e-3, -2.4440704000e-3 /) - LoveDat(1:4,1030) = (/ 1029.0, -5.9131109000, 1.6405089000e-3, -2.4422135000e-3 /) - LoveDat(1:4,1031) = (/ 1030.0, -5.9140782000, 1.6394983000e-3, -2.4403587000e-3 /) - LoveDat(1:4,1032) = (/ 1031.0, -5.9150425000, 1.6384882000e-3, -2.4385061000e-3 /) - LoveDat(1:4,1033) = (/ 1032.0, -5.9160039000, 1.6374786000e-3, -2.4366555000e-3 /) - LoveDat(1:4,1034) = (/ 1033.0, -5.9169625000, 1.6364694000e-3, -2.4348071000e-3 /) - LoveDat(1:4,1035) = (/ 1034.0, -5.9179181000, 1.6354607000e-3, -2.4329608000e-3 /) - LoveDat(1:4,1036) = (/ 1035.0, -5.9188709000, 1.6344526000e-3, -2.4311166000e-3 /) - LoveDat(1:4,1037) = (/ 1036.0, -5.9198208000, 1.6334449000e-3, -2.4292746000e-3 /) - LoveDat(1:4,1038) = (/ 1037.0, -5.9207678000, 1.6324377000e-3, -2.4274346000e-3 /) - LoveDat(1:4,1039) = (/ 1038.0, -5.9217120000, 1.6314309000e-3, -2.4255967000e-3 /) - LoveDat(1:4,1040) = (/ 1039.0, -5.9226533000, 1.6304247000e-3, -2.4237610000e-3 /) - LoveDat(1:4,1041) = (/ 1040.0, -5.9235918000, 1.6294190000e-3, -2.4219273000e-3 /) - LoveDat(1:4,1042) = (/ 1041.0, -5.9245275000, 1.6284137000e-3, -2.4200958000e-3 /) - LoveDat(1:4,1043) = (/ 1042.0, -5.9254603000, 1.6274090000e-3, -2.4182663000e-3 /) - LoveDat(1:4,1044) = (/ 1043.0, -5.9263904000, 1.6264047000e-3, -2.4164389000e-3 /) - LoveDat(1:4,1045) = (/ 1044.0, -5.9273176000, 1.6254009000e-3, -2.4146136000e-3 /) - LoveDat(1:4,1046) = (/ 1045.0, -5.9282420000, 1.6243977000e-3, -2.4127904000e-3 /) - LoveDat(1:4,1047) = (/ 1046.0, -5.9291636000, 1.6233949000e-3, -2.4109693000e-3 /) - LoveDat(1:4,1048) = (/ 1047.0, -5.9300824000, 1.6223926000e-3, -2.4091503000e-3 /) - LoveDat(1:4,1049) = (/ 1048.0, -5.9309984000, 1.6213909000e-3, -2.4073333000e-3 /) - LoveDat(1:4,1050) = (/ 1049.0, -5.9319117000, 1.6203896000e-3, -2.4055185000e-3 /) - LoveDat(1:4,1051) = (/ 1050.0, -5.9328222000, 1.6193888000e-3, -2.4037057000e-3 /) - LoveDat(1:4,1052) = (/ 1051.0, -5.9337299000, 1.6183886000e-3, -2.4018949000e-3 /) - LoveDat(1:4,1053) = (/ 1052.0, -5.9346349000, 1.6173888000e-3, -2.4000862000e-3 /) - LoveDat(1:4,1054) = (/ 1053.0, -5.9355371000, 1.6163895000e-3, -2.3982796000e-3 /) - LoveDat(1:4,1055) = (/ 1054.0, -5.9364366000, 1.6153908000e-3, -2.3964751000e-3 /) - LoveDat(1:4,1056) = (/ 1055.0, -5.9373334000, 1.6143925000e-3, -2.3946726000e-3 /) - LoveDat(1:4,1057) = (/ 1056.0, -5.9382274000, 1.6133948000e-3, -2.3928722000e-3 /) - LoveDat(1:4,1058) = (/ 1057.0, -5.9391187000, 1.6123976000e-3, -2.3910738000e-3 /) - LoveDat(1:4,1059) = (/ 1058.0, -5.9400074000, 1.6114009000e-3, -2.3892775000e-3 /) - LoveDat(1:4,1060) = (/ 1059.0, -5.9408933000, 1.6104046000e-3, -2.3874833000e-3 /) - LoveDat(1:4,1061) = (/ 1060.0, -5.9417765000, 1.6094089000e-3, -2.3856910000e-3 /) - LoveDat(1:4,1062) = (/ 1061.0, -5.9426570000, 1.6084138000e-3, -2.3839009000e-3 /) - LoveDat(1:4,1063) = (/ 1062.0, -5.9435349000, 1.6074191000e-3, -2.3821127000e-3 /) - LoveDat(1:4,1064) = (/ 1063.0, -5.9444100000, 1.6064249000e-3, -2.3803266000e-3 /) - LoveDat(1:4,1065) = (/ 1064.0, -5.9452825000, 1.6054313000e-3, -2.3785426000e-3 /) - LoveDat(1:4,1066) = (/ 1065.0, -5.9461524000, 1.6044382000e-3, -2.3767606000e-3 /) - LoveDat(1:4,1067) = (/ 1066.0, -5.9470196000, 1.6034456000e-3, -2.3749806000e-3 /) - LoveDat(1:4,1068) = (/ 1067.0, -5.9478841000, 1.6024535000e-3, -2.3732026000e-3 /) - LoveDat(1:4,1069) = (/ 1068.0, -5.9487460000, 1.6014619000e-3, -2.3714267000e-3 /) - LoveDat(1:4,1070) = (/ 1069.0, -5.9496053000, 1.6004709000e-3, -2.3696528000e-3 /) - LoveDat(1:4,1071) = (/ 1070.0, -5.9504619000, 1.5994804000e-3, -2.3678809000e-3 /) - LoveDat(1:4,1072) = (/ 1071.0, -5.9513160000, 1.5984904000e-3, -2.3661110000e-3 /) - LoveDat(1:4,1073) = (/ 1072.0, -5.9521674000, 1.5975009000e-3, -2.3643432000e-3 /) - LoveDat(1:4,1074) = (/ 1073.0, -5.9530162000, 1.5965120000e-3, -2.3625773000e-3 /) - LoveDat(1:4,1075) = (/ 1074.0, -5.9538624000, 1.5955236000e-3, -2.3608135000e-3 /) - LoveDat(1:4,1076) = (/ 1075.0, -5.9547061000, 1.5945357000e-3, -2.3590517000e-3 /) - LoveDat(1:4,1077) = (/ 1076.0, -5.9555471000, 1.5935483000e-3, -2.3572919000e-3 /) - LoveDat(1:4,1078) = (/ 1077.0, -5.9563856000, 1.5925615000e-3, -2.3555341000e-3 /) - LoveDat(1:4,1079) = (/ 1078.0, -5.9572215000, 1.5915752000e-3, -2.3537782000e-3 /) - LoveDat(1:4,1080) = (/ 1079.0, -5.9580548000, 1.5905894000e-3, -2.3520244000e-3 /) - LoveDat(1:4,1081) = (/ 1080.0, -5.9588856000, 1.5896041000e-3, -2.3502726000e-3 /) - LoveDat(1:4,1082) = (/ 1081.0, -5.9597138000, 1.5886194000e-3, -2.3485228000e-3 /) - LoveDat(1:4,1083) = (/ 1082.0, -5.9605395000, 1.5876353000e-3, -2.3467750000e-3 /) - LoveDat(1:4,1084) = (/ 1083.0, -5.9613627000, 1.5866516000e-3, -2.3450292000e-3 /) - LoveDat(1:4,1085) = (/ 1084.0, -5.9621833000, 1.5856685000e-3, -2.3432853000e-3 /) - LoveDat(1:4,1086) = (/ 1085.0, -5.9630014000, 1.5846860000e-3, -2.3415434000e-3 /) - LoveDat(1:4,1087) = (/ 1086.0, -5.9638170000, 1.5837039000e-3, -2.3398036000e-3 /) - LoveDat(1:4,1088) = (/ 1087.0, -5.9646301000, 1.5827224000e-3, -2.3380657000e-3 /) - LoveDat(1:4,1089) = (/ 1088.0, -5.9654407000, 1.5817415000e-3, -2.3363297000e-3 /) - LoveDat(1:4,1090) = (/ 1089.0, -5.9662488000, 1.5807611000e-3, -2.3345958000e-3 /) - LoveDat(1:4,1091) = (/ 1090.0, -5.9670544000, 1.5797812000e-3, -2.3328638000e-3 /) - LoveDat(1:4,1092) = (/ 1091.0, -5.9678575000, 1.5788019000e-3, -2.3311338000e-3 /) - LoveDat(1:4,1093) = (/ 1092.0, -5.9686582000, 1.5778231000e-3, -2.3294057000e-3 /) - LoveDat(1:4,1094) = (/ 1093.0, -5.9694563000, 1.5768449000e-3, -2.3276797000e-3 /) - LoveDat(1:4,1095) = (/ 1094.0, -5.9702521000, 1.5758672000e-3, -2.3259555000e-3 /) - LoveDat(1:4,1096) = (/ 1095.0, -5.9710453000, 1.5748901000e-3, -2.3242334000e-3 /) - LoveDat(1:4,1097) = (/ 1096.0, -5.9718361000, 1.5739135000e-3, -2.3225132000e-3 /) - LoveDat(1:4,1098) = (/ 1097.0, -5.9726245000, 1.5729374000e-3, -2.3207950000e-3 /) - LoveDat(1:4,1099) = (/ 1098.0, -5.9734105000, 1.5719619000e-3, -2.3190787000e-3 /) - LoveDat(1:4,1100) = (/ 1099.0, -5.9741940000, 1.5709870000e-3, -2.3173643000e-3 /) - LoveDat(1:4,1101) = (/ 1100.0, -5.9749751000, 1.5700126000e-3, -2.3156519000e-3 /) - LoveDat(1:4,1102) = (/ 1101.0, -5.9757538000, 1.5690387000e-3, -2.3139415000e-3 /) - LoveDat(1:4,1103) = (/ 1102.0, -5.9765300000, 1.5680654000e-3, -2.3122330000e-3 /) - LoveDat(1:4,1104) = (/ 1103.0, -5.9773039000, 1.5670927000e-3, -2.3105264000e-3 /) - LoveDat(1:4,1105) = (/ 1104.0, -5.9780754000, 1.5661205000e-3, -2.3088218000e-3 /) - LoveDat(1:4,1106) = (/ 1105.0, -5.9788445000, 1.5651489000e-3, -2.3071191000e-3 /) - LoveDat(1:4,1107) = (/ 1106.0, -5.9796112000, 1.5641778000e-3, -2.3054184000e-3 /) - LoveDat(1:4,1108) = (/ 1107.0, -5.9803755000, 1.5632073000e-3, -2.3037195000e-3 /) - LoveDat(1:4,1109) = (/ 1108.0, -5.9811375000, 1.5622374000e-3, -2.3020226000e-3 /) - LoveDat(1:4,1110) = (/ 1109.0, -5.9818971000, 1.5612680000e-3, -2.3003277000e-3 /) - LoveDat(1:4,1111) = (/ 1110.0, -5.9826543000, 1.5602991000e-3, -2.2986346000e-3 /) - LoveDat(1:4,1112) = (/ 1111.0, -5.9834092000, 1.5593309000e-3, -2.2969435000e-3 /) - LoveDat(1:4,1113) = (/ 1112.0, -5.9841618000, 1.5583632000e-3, -2.2952543000e-3 /) - LoveDat(1:4,1114) = (/ 1113.0, -5.9849120000, 1.5573960000e-3, -2.2935670000e-3 /) - LoveDat(1:4,1115) = (/ 1114.0, -5.9856599000, 1.5564294000e-3, -2.2918817000e-3 /) - LoveDat(1:4,1116) = (/ 1115.0, -5.9864054000, 1.5554634000e-3, -2.2901982000e-3 /) - LoveDat(1:4,1117) = (/ 1116.0, -5.9871487000, 1.5544979000e-3, -2.2885167000e-3 /) - LoveDat(1:4,1118) = (/ 1117.0, -5.9878896000, 1.5535331000e-3, -2.2868370000e-3 /) - LoveDat(1:4,1119) = (/ 1118.0, -5.9886282000, 1.5525687000e-3, -2.2851593000e-3 /) - LoveDat(1:4,1120) = (/ 1119.0, -5.9893646000, 1.5516050000e-3, -2.2834835000e-3 /) - LoveDat(1:4,1121) = (/ 1120.0, -5.9900986000, 1.5506418000e-3, -2.2818095000e-3 /) - LoveDat(1:4,1122) = (/ 1121.0, -5.9908304000, 1.5496792000e-3, -2.2801375000e-3 /) - LoveDat(1:4,1123) = (/ 1122.0, -5.9915599000, 1.5487171000e-3, -2.2784674000e-3 /) - LoveDat(1:4,1124) = (/ 1123.0, -5.9922871000, 1.5477557000e-3, -2.2767991000e-3 /) - LoveDat(1:4,1125) = (/ 1124.0, -5.9930120000, 1.5467948000e-3, -2.2751328000e-3 /) - LoveDat(1:4,1126) = (/ 1125.0, -5.9937347000, 1.5458344000e-3, -2.2734683000e-3 /) - LoveDat(1:4,1127) = (/ 1126.0, -5.9944551000, 1.5448747000e-3, -2.2718058000e-3 /) - LoveDat(1:4,1128) = (/ 1127.0, -5.9951733000, 1.5439155000e-3, -2.2701451000e-3 /) - LoveDat(1:4,1129) = (/ 1128.0, -5.9958892000, 1.5429569000e-3, -2.2684863000e-3 /) - LoveDat(1:4,1130) = (/ 1129.0, -5.9966029000, 1.5419989000e-3, -2.2668294000e-3 /) - LoveDat(1:4,1131) = (/ 1130.0, -5.9973144000, 1.5410414000e-3, -2.2651743000e-3 /) - LoveDat(1:4,1132) = (/ 1131.0, -5.9980236000, 1.5400845000e-3, -2.2635212000e-3 /) - LoveDat(1:4,1133) = (/ 1132.0, -5.9987307000, 1.5391282000e-3, -2.2618699000e-3 /) - LoveDat(1:4,1134) = (/ 1133.0, -5.9994355000, 1.5381725000e-3, -2.2602205000e-3 /) - LoveDat(1:4,1135) = (/ 1134.0, -6.0001381000, 1.5372174000e-3, -2.2585729000e-3 /) - LoveDat(1:4,1136) = (/ 1135.0, -6.0008385000, 1.5362628000e-3, -2.2569272000e-3 /) - LoveDat(1:4,1137) = (/ 1136.0, -6.0015367000, 1.5353089000e-3, -2.2552834000e-3 /) - LoveDat(1:4,1138) = (/ 1137.0, -6.0022328000, 1.5343555000e-3, -2.2536414000e-3 /) - LoveDat(1:4,1139) = (/ 1138.0, -6.0029266000, 1.5334027000e-3, -2.2520013000e-3 /) - LoveDat(1:4,1140) = (/ 1139.0, -6.0036183000, 1.5324504000e-3, -2.2503631000e-3 /) - LoveDat(1:4,1141) = (/ 1140.0, -6.0043078000, 1.5314988000e-3, -2.2487267000e-3 /) - LoveDat(1:4,1142) = (/ 1141.0, -6.0049952000, 1.5305477000e-3, -2.2470922000e-3 /) - LoveDat(1:4,1143) = (/ 1142.0, -6.0056804000, 1.5295973000e-3, -2.2454595000e-3 /) - LoveDat(1:4,1144) = (/ 1143.0, -6.0063635000, 1.5286474000e-3, -2.2438287000e-3 /) - LoveDat(1:4,1145) = (/ 1144.0, -6.0070444000, 1.5276981000e-3, -2.2421997000e-3 /) - LoveDat(1:4,1146) = (/ 1145.0, -6.0077231000, 1.5267494000e-3, -2.2405726000e-3 /) - LoveDat(1:4,1147) = (/ 1146.0, -6.0083998000, 1.5258013000e-3, -2.2389473000e-3 /) - LoveDat(1:4,1148) = (/ 1147.0, -6.0090743000, 1.5248538000e-3, -2.2373238000e-3 /) - LoveDat(1:4,1149) = (/ 1148.0, -6.0097467000, 1.5239068000e-3, -2.2357022000e-3 /) - LoveDat(1:4,1150) = (/ 1149.0, -6.0104170000, 1.5229605000e-3, -2.2340824000e-3 /) - LoveDat(1:4,1151) = (/ 1150.0, -6.0110851000, 1.5220147000e-3, -2.2324645000e-3 /) - LoveDat(1:4,1152) = (/ 1151.0, -6.0117512000, 1.5210696000e-3, -2.2308484000e-3 /) - LoveDat(1:4,1153) = (/ 1152.0, -6.0124152000, 1.5201250000e-3, -2.2292341000e-3 /) - LoveDat(1:4,1154) = (/ 1153.0, -6.0130771000, 1.5191811000e-3, -2.2276216000e-3 /) - LoveDat(1:4,1155) = (/ 1154.0, -6.0137369000, 1.5182377000e-3, -2.2260110000e-3 /) - LoveDat(1:4,1156) = (/ 1155.0, -6.0143946000, 1.5172949000e-3, -2.2244022000e-3 /) - LoveDat(1:4,1157) = (/ 1156.0, -6.0150502000, 1.5163527000e-3, -2.2227952000e-3 /) - LoveDat(1:4,1158) = (/ 1157.0, -6.0157038000, 1.5154112000e-3, -2.2211900000e-3 /) - LoveDat(1:4,1159) = (/ 1158.0, -6.0163553000, 1.5144702000e-3, -2.2195866000e-3 /) - LoveDat(1:4,1160) = (/ 1159.0, -6.0170048000, 1.5135298000e-3, -2.2179851000e-3 /) - LoveDat(1:4,1161) = (/ 1160.0, -6.0176522000, 1.5125900000e-3, -2.2163853000e-3 /) - LoveDat(1:4,1162) = (/ 1161.0, -6.0182976000, 1.5116508000e-3, -2.2147874000e-3 /) - LoveDat(1:4,1163) = (/ 1162.0, -6.0189409000, 1.5107122000e-3, -2.2131913000e-3 /) - LoveDat(1:4,1164) = (/ 1163.0, -6.0195822000, 1.5097742000e-3, -2.2115970000e-3 /) - LoveDat(1:4,1165) = (/ 1164.0, -6.0202215000, 1.5088369000e-3, -2.2100045000e-3 /) - LoveDat(1:4,1166) = (/ 1165.0, -6.0208588000, 1.5079001000e-3, -2.2084138000e-3 /) - LoveDat(1:4,1167) = (/ 1166.0, -6.0214940000, 1.5069639000e-3, -2.2068249000e-3 /) - LoveDat(1:4,1168) = (/ 1167.0, -6.0221273000, 1.5060283000e-3, -2.2052377000e-3 /) - LoveDat(1:4,1169) = (/ 1168.0, -6.0227585000, 1.5050934000e-3, -2.2036524000e-3 /) - LoveDat(1:4,1170) = (/ 1169.0, -6.0233877000, 1.5041590000e-3, -2.2020689000e-3 /) - LoveDat(1:4,1171) = (/ 1170.0, -6.0240150000, 1.5032253000e-3, -2.2004872000e-3 /) - LoveDat(1:4,1172) = (/ 1171.0, -6.0246402000, 1.5022921000e-3, -2.1989072000e-3 /) - LoveDat(1:4,1173) = (/ 1172.0, -6.0252635000, 1.5013596000e-3, -2.1973290000e-3 /) - LoveDat(1:4,1174) = (/ 1173.0, -6.0258848000, 1.5004276000e-3, -2.1957527000e-3 /) - LoveDat(1:4,1175) = (/ 1174.0, -6.0265042000, 1.4994963000e-3, -2.1941781000e-3 /) - LoveDat(1:4,1176) = (/ 1175.0, -6.0271215000, 1.4985656000e-3, -2.1926052000e-3 /) - LoveDat(1:4,1177) = (/ 1176.0, -6.0277369000, 1.4976355000e-3, -2.1910342000e-3 /) - LoveDat(1:4,1178) = (/ 1177.0, -6.0283504000, 1.4967060000e-3, -2.1894649000e-3 /) - LoveDat(1:4,1179) = (/ 1178.0, -6.0289619000, 1.4957771000e-3, -2.1878974000e-3 /) - LoveDat(1:4,1180) = (/ 1179.0, -6.0295715000, 1.4948489000e-3, -2.1863317000e-3 /) - LoveDat(1:4,1181) = (/ 1180.0, -6.0301791000, 1.4939212000e-3, -2.1847678000e-3 /) - LoveDat(1:4,1182) = (/ 1181.0, -6.0307848000, 1.4929942000e-3, -2.1832056000e-3 /) - LoveDat(1:4,1183) = (/ 1182.0, -6.0313886000, 1.4920677000e-3, -2.1816451000e-3 /) - LoveDat(1:4,1184) = (/ 1183.0, -6.0319905000, 1.4911419000e-3, -2.1800865000e-3 /) - LoveDat(1:4,1185) = (/ 1184.0, -6.0325904000, 1.4902167000e-3, -2.1785296000e-3 /) - LoveDat(1:4,1186) = (/ 1185.0, -6.0331885000, 1.4892921000e-3, -2.1769744000e-3 /) - LoveDat(1:4,1187) = (/ 1186.0, -6.0337846000, 1.4883682000e-3, -2.1754210000e-3 /) - LoveDat(1:4,1188) = (/ 1187.0, -6.0343789000, 1.4874448000e-3, -2.1738694000e-3 /) - LoveDat(1:4,1189) = (/ 1188.0, -6.0349712000, 1.4865221000e-3, -2.1723195000e-3 /) - LoveDat(1:4,1190) = (/ 1189.0, -6.0355617000, 1.4856000000e-3, -2.1707714000e-3 /) - LoveDat(1:4,1191) = (/ 1190.0, -6.0361503000, 1.4846785000e-3, -2.1692250000e-3 /) - LoveDat(1:4,1192) = (/ 1191.0, -6.0367370000, 1.4837576000e-3, -2.1676804000e-3 /) - LoveDat(1:4,1193) = (/ 1192.0, -6.0373218000, 1.4828373000e-3, -2.1661375000e-3 /) - LoveDat(1:4,1194) = (/ 1193.0, -6.0379048000, 1.4819177000e-3, -2.1645963000e-3 /) - LoveDat(1:4,1195) = (/ 1194.0, -6.0384859000, 1.4809986000e-3, -2.1630569000e-3 /) - LoveDat(1:4,1196) = (/ 1195.0, -6.0390651000, 1.4800802000e-3, -2.1615192000e-3 /) - LoveDat(1:4,1197) = (/ 1196.0, -6.0396426000, 1.4791625000e-3, -2.1599833000e-3 /) - LoveDat(1:4,1198) = (/ 1197.0, -6.0402181000, 1.4782453000e-3, -2.1584490000e-3 /) - LoveDat(1:4,1199) = (/ 1198.0, -6.0407919000, 1.4773288000e-3, -2.1569166000e-3 /) - LoveDat(1:4,1200) = (/ 1199.0, -6.0413638000, 1.4764129000e-3, -2.1553858000e-3 /) - LoveDat(1:4,1201) = (/ 1200.0, -6.0419338000, 1.4754976000e-3, -2.1538568000e-3 /) - LoveDat(1:4,1202) = (/ 1201.0, -6.0425021000, 1.4745829000e-3, -2.1523295000e-3 /) - LoveDat(1:4,1203) = (/ 1202.0, -6.0430685000, 1.4736689000e-3, -2.1508039000e-3 /) - LoveDat(1:4,1204) = (/ 1203.0, -6.0436331000, 1.4727555000e-3, -2.1492800000e-3 /) - LoveDat(1:4,1205) = (/ 1204.0, -6.0441959000, 1.4718427000e-3, -2.1477579000e-3 /) - LoveDat(1:4,1206) = (/ 1205.0, -6.0447570000, 1.4709305000e-3, -2.1462375000e-3 /) - LoveDat(1:4,1207) = (/ 1206.0, -6.0453162000, 1.4700190000e-3, -2.1447188000e-3 /) - LoveDat(1:4,1208) = (/ 1207.0, -6.0458736000, 1.4691081000e-3, -2.1432018000e-3 /) - LoveDat(1:4,1209) = (/ 1208.0, -6.0464292000, 1.4681978000e-3, -2.1416865000e-3 /) - LoveDat(1:4,1210) = (/ 1209.0, -6.0469831000, 1.4672882000e-3, -2.1401729000e-3 /) - LoveDat(1:4,1211) = (/ 1210.0, -6.0475352000, 1.4663791000e-3, -2.1386610000e-3 /) - LoveDat(1:4,1212) = (/ 1211.0, -6.0480855000, 1.4654707000e-3, -2.1371509000e-3 /) - LoveDat(1:4,1213) = (/ 1212.0, -6.0486341000, 1.4645630000e-3, -2.1356424000e-3 /) - LoveDat(1:4,1214) = (/ 1213.0, -6.0491809000, 1.4636558000e-3, -2.1341356000e-3 /) - LoveDat(1:4,1215) = (/ 1214.0, -6.0497259000, 1.4627493000e-3, -2.1326306000e-3 /) - LoveDat(1:4,1216) = (/ 1215.0, -6.0502692000, 1.4618435000e-3, -2.1311272000e-3 /) - LoveDat(1:4,1217) = (/ 1216.0, -6.0508107000, 1.4609382000e-3, -2.1296255000e-3 /) - LoveDat(1:4,1218) = (/ 1217.0, -6.0513505000, 1.4600336000e-3, -2.1281255000e-3 /) - LoveDat(1:4,1219) = (/ 1218.0, -6.0518886000, 1.4591297000e-3, -2.1266272000e-3 /) - LoveDat(1:4,1220) = (/ 1219.0, -6.0524250000, 1.4582263000e-3, -2.1251306000e-3 /) - LoveDat(1:4,1221) = (/ 1220.0, -6.0529596000, 1.4573236000e-3, -2.1236357000e-3 /) - LoveDat(1:4,1222) = (/ 1221.0, -6.0534925000, 1.4564215000e-3, -2.1221424000e-3 /) - LoveDat(1:4,1223) = (/ 1222.0, -6.0540237000, 1.4555201000e-3, -2.1206509000e-3 /) - LoveDat(1:4,1224) = (/ 1223.0, -6.0545531000, 1.4546193000e-3, -2.1191610000e-3 /) - LoveDat(1:4,1225) = (/ 1224.0, -6.0550809000, 1.4537191000e-3, -2.1176728000e-3 /) - LoveDat(1:4,1226) = (/ 1225.0, -6.0556070000, 1.4528196000e-3, -2.1161863000e-3 /) - LoveDat(1:4,1227) = (/ 1226.0, -6.0561314000, 1.4519207000e-3, -2.1147014000e-3 /) - LoveDat(1:4,1228) = (/ 1227.0, -6.0566541000, 1.4510224000e-3, -2.1132182000e-3 /) - LoveDat(1:4,1229) = (/ 1228.0, -6.0571751000, 1.4501248000e-3, -2.1117367000e-3 /) - LoveDat(1:4,1230) = (/ 1229.0, -6.0576944000, 1.4492278000e-3, -2.1102569000e-3 /) - LoveDat(1:4,1231) = (/ 1230.0, -6.0582120000, 1.4483315000e-3, -2.1087787000e-3 /) - LoveDat(1:4,1232) = (/ 1231.0, -6.0587280000, 1.4474358000e-3, -2.1073022000e-3 /) - LoveDat(1:4,1233) = (/ 1232.0, -6.0592424000, 1.4465407000e-3, -2.1058273000e-3 /) - LoveDat(1:4,1234) = (/ 1233.0, -6.0597550000, 1.4456463000e-3, -2.1043541000e-3 /) - LoveDat(1:4,1235) = (/ 1234.0, -6.0602660000, 1.4447525000e-3, -2.1028826000e-3 /) - LoveDat(1:4,1236) = (/ 1235.0, -6.0607754000, 1.4438593000e-3, -2.1014127000e-3 /) - LoveDat(1:4,1237) = (/ 1236.0, -6.0612831000, 1.4429668000e-3, -2.0999445000e-3 /) - LoveDat(1:4,1238) = (/ 1237.0, -6.0617892000, 1.4420750000e-3, -2.0984779000e-3 /) - LoveDat(1:4,1239) = (/ 1238.0, -6.0622936000, 1.4411837000e-3, -2.0970130000e-3 /) - LoveDat(1:4,1240) = (/ 1239.0, -6.0627964000, 1.4402931000e-3, -2.0955497000e-3 /) - LoveDat(1:4,1241) = (/ 1240.0, -6.0632976000, 1.4394032000e-3, -2.0940880000e-3 /) - LoveDat(1:4,1242) = (/ 1241.0, -6.0637972000, 1.4385139000e-3, -2.0926281000e-3 /) - LoveDat(1:4,1243) = (/ 1242.0, -6.0642951000, 1.4376252000e-3, -2.0911697000e-3 /) - LoveDat(1:4,1244) = (/ 1243.0, -6.0647914000, 1.4367372000e-3, -2.0897130000e-3 /) - LoveDat(1:4,1245) = (/ 1244.0, -6.0652862000, 1.4358498000e-3, -2.0882579000e-3 /) - LoveDat(1:4,1246) = (/ 1245.0, -6.0657793000, 1.4349631000e-3, -2.0868045000e-3 /) - LoveDat(1:4,1247) = (/ 1246.0, -6.0662708000, 1.4340770000e-3, -2.0853527000e-3 /) - LoveDat(1:4,1248) = (/ 1247.0, -6.0667608000, 1.4331916000e-3, -2.0839025000e-3 /) - LoveDat(1:4,1249) = (/ 1248.0, -6.0672491000, 1.4323068000e-3, -2.0824540000e-3 /) - LoveDat(1:4,1250) = (/ 1249.0, -6.0677359000, 1.4314226000e-3, -2.0810070000e-3 /) - LoveDat(1:4,1251) = (/ 1250.0, -6.0682211000, 1.4305391000e-3, -2.0795618000e-3 /) - LoveDat(1:4,1252) = (/ 1251.0, -6.0687047000, 1.4296562000e-3, -2.0781181000e-3 /) - LoveDat(1:4,1253) = (/ 1252.0, -6.0691867000, 1.4287740000e-3, -2.0766760000e-3 /) - LoveDat(1:4,1254) = (/ 1253.0, -6.0696672000, 1.4278925000e-3, -2.0752356000e-3 /) - LoveDat(1:4,1255) = (/ 1254.0, -6.0701462000, 1.4270115000e-3, -2.0737968000e-3 /) - LoveDat(1:4,1256) = (/ 1255.0, -6.0706235000, 1.4261312000e-3, -2.0723596000e-3 /) - LoveDat(1:4,1257) = (/ 1256.0, -6.0710993000, 1.4252516000e-3, -2.0709241000e-3 /) - LoveDat(1:4,1258) = (/ 1257.0, -6.0715736000, 1.4243726000e-3, -2.0694901000e-3 /) - LoveDat(1:4,1259) = (/ 1258.0, -6.0720464000, 1.4234943000e-3, -2.0680577000e-3 /) - LoveDat(1:4,1260) = (/ 1259.0, -6.0725175000, 1.4226166000e-3, -2.0666270000e-3 /) - LoveDat(1:4,1261) = (/ 1260.0, -6.0729872000, 1.4217396000e-3, -2.0651979000e-3 /) - LoveDat(1:4,1262) = (/ 1261.0, -6.0734554000, 1.4208632000e-3, -2.0637703000e-3 /) - LoveDat(1:4,1263) = (/ 1262.0, -6.0739220000, 1.4199874000e-3, -2.0623444000e-3 /) - LoveDat(1:4,1264) = (/ 1263.0, -6.0743871000, 1.4191123000e-3, -2.0609201000e-3 /) - LoveDat(1:4,1265) = (/ 1264.0, -6.0748507000, 1.4182379000e-3, -2.0594973000e-3 /) - LoveDat(1:4,1266) = (/ 1265.0, -6.0753127000, 1.4173641000e-3, -2.0580762000e-3 /) - LoveDat(1:4,1267) = (/ 1266.0, -6.0757733000, 1.4164910000e-3, -2.0566566000e-3 /) - LoveDat(1:4,1268) = (/ 1267.0, -6.0762324000, 1.4156185000e-3, -2.0552387000e-3 /) - LoveDat(1:4,1269) = (/ 1268.0, -6.0766899000, 1.4147466000e-3, -2.0538223000e-3 /) - LoveDat(1:4,1270) = (/ 1269.0, -6.0771460000, 1.4138754000e-3, -2.0524076000e-3 /) - LoveDat(1:4,1271) = (/ 1270.0, -6.0776006000, 1.4130049000e-3, -2.0509944000e-3 /) - LoveDat(1:4,1272) = (/ 1271.0, -6.0780537000, 1.4121350000e-3, -2.0495828000e-3 /) - LoveDat(1:4,1273) = (/ 1272.0, -6.0785054000, 1.4112658000e-3, -2.0481728000e-3 /) - LoveDat(1:4,1274) = (/ 1273.0, -6.0789555000, 1.4103972000e-3, -2.0467643000e-3 /) - LoveDat(1:4,1275) = (/ 1274.0, -6.0794042000, 1.4095293000e-3, -2.0453575000e-3 /) - LoveDat(1:4,1276) = (/ 1275.0, -6.0798515000, 1.4086620000e-3, -2.0439522000e-3 /) - LoveDat(1:4,1277) = (/ 1276.0, -6.0802972000, 1.4077954000e-3, -2.0425485000e-3 /) - LoveDat(1:4,1278) = (/ 1277.0, -6.0807415000, 1.4069294000e-3, -2.0411464000e-3 /) - LoveDat(1:4,1279) = (/ 1278.0, -6.0811844000, 1.4060641000e-3, -2.0397458000e-3 /) - LoveDat(1:4,1280) = (/ 1279.0, -6.0816258000, 1.4051994000e-3, -2.0383468000e-3 /) - LoveDat(1:4,1281) = (/ 1280.0, -6.0820658000, 1.4043354000e-3, -2.0369494000e-3 /) - LoveDat(1:4,1282) = (/ 1281.0, -6.0825043000, 1.4034720000e-3, -2.0355536000e-3 /) - LoveDat(1:4,1283) = (/ 1282.0, -6.0829414000, 1.4026093000e-3, -2.0341593000e-3 /) - LoveDat(1:4,1284) = (/ 1283.0, -6.0833771000, 1.4017473000e-3, -2.0327665000e-3 /) - LoveDat(1:4,1285) = (/ 1284.0, -6.0838114000, 1.4008859000e-3, -2.0313754000e-3 /) - LoveDat(1:4,1286) = (/ 1285.0, -6.0842442000, 1.4000251000e-3, -2.0299858000e-3 /) - LoveDat(1:4,1287) = (/ 1286.0, -6.0846756000, 1.3991650000e-3, -2.0285977000e-3 /) - LoveDat(1:4,1288) = (/ 1287.0, -6.0851056000, 1.3983056000e-3, -2.0272112000e-3 /) - LoveDat(1:4,1289) = (/ 1288.0, -6.0855342000, 1.3974468000e-3, -2.0258263000e-3 /) - LoveDat(1:4,1290) = (/ 1289.0, -6.0859614000, 1.3965887000e-3, -2.0244429000e-3 /) - LoveDat(1:4,1291) = (/ 1290.0, -6.0863871000, 1.3957312000e-3, -2.0230610000e-3 /) - LoveDat(1:4,1292) = (/ 1291.0, -6.0868115000, 1.3948744000e-3, -2.0216807000e-3 /) - LoveDat(1:4,1293) = (/ 1292.0, -6.0872345000, 1.3940183000e-3, -2.0203020000e-3 /) - LoveDat(1:4,1294) = (/ 1293.0, -6.0876561000, 1.3931628000e-3, -2.0189247000e-3 /) - LoveDat(1:4,1295) = (/ 1294.0, -6.0880764000, 1.3923079000e-3, -2.0175491000e-3 /) - LoveDat(1:4,1296) = (/ 1295.0, -6.0884952000, 1.3914537000e-3, -2.0161749000e-3 /) - LoveDat(1:4,1297) = (/ 1296.0, -6.0889127000, 1.3906002000e-3, -2.0148024000e-3 /) - LoveDat(1:4,1298) = (/ 1297.0, -6.0893288000, 1.3897473000e-3, -2.0134313000e-3 /) - LoveDat(1:4,1299) = (/ 1298.0, -6.0897435000, 1.3888951000e-3, -2.0120618000e-3 /) - LoveDat(1:4,1300) = (/ 1299.0, -6.0901569000, 1.3880436000e-3, -2.0106938000e-3 /) - LoveDat(1:4,1301) = (/ 1300.0, -6.0905689000, 1.3871927000e-3, -2.0093273000e-3 /) - LoveDat(1:4,1302) = (/ 1301.0, -6.0909796000, 1.3863424000e-3, -2.0079624000e-3 /) - LoveDat(1:4,1303) = (/ 1302.0, -6.0913889000, 1.3854928000e-3, -2.0065990000e-3 /) - LoveDat(1:4,1304) = (/ 1303.0, -6.0917969000, 1.3846439000e-3, -2.0052371000e-3 /) - LoveDat(1:4,1305) = (/ 1304.0, -6.0922035000, 1.3837956000e-3, -2.0038767000e-3 /) - LoveDat(1:4,1306) = (/ 1305.0, -6.0926088000, 1.3829480000e-3, -2.0025179000e-3 /) - LoveDat(1:4,1307) = (/ 1306.0, -6.0930127000, 1.3821011000e-3, -2.0011606000e-3 /) - LoveDat(1:4,1308) = (/ 1307.0, -6.0934154000, 1.3812548000e-3, -1.9998048000e-3 /) - LoveDat(1:4,1309) = (/ 1308.0, -6.0938167000, 1.3804091000e-3, -1.9984505000e-3 /) - LoveDat(1:4,1310) = (/ 1309.0, -6.0942166000, 1.3795642000e-3, -1.9970977000e-3 /) - LoveDat(1:4,1311) = (/ 1310.0, -6.0946153000, 1.3787199000e-3, -1.9957464000e-3 /) - LoveDat(1:4,1312) = (/ 1311.0, -6.0950127000, 1.3778762000e-3, -1.9943967000e-3 /) - LoveDat(1:4,1313) = (/ 1312.0, -6.0954087000, 1.3770332000e-3, -1.9930484000e-3 /) - LoveDat(1:4,1314) = (/ 1313.0, -6.0958034000, 1.3761909000e-3, -1.9917017000e-3 /) - LoveDat(1:4,1315) = (/ 1314.0, -6.0961969000, 1.3753492000e-3, -1.9903564000e-3 /) - LoveDat(1:4,1316) = (/ 1315.0, -6.0965890000, 1.3745082000e-3, -1.9890127000e-3 /) - LoveDat(1:4,1317) = (/ 1316.0, -6.0969798000, 1.3736678000e-3, -1.9876705000e-3 /) - LoveDat(1:4,1318) = (/ 1317.0, -6.0973694000, 1.3728281000e-3, -1.9863297000e-3 /) - LoveDat(1:4,1319) = (/ 1318.0, -6.0977577000, 1.3719890000e-3, -1.9849905000e-3 /) - LoveDat(1:4,1320) = (/ 1319.0, -6.0981446000, 1.3711507000e-3, -1.9836527000e-3 /) - LoveDat(1:4,1321) = (/ 1320.0, -6.0985303000, 1.3703129000e-3, -1.9823165000e-3 /) - LoveDat(1:4,1322) = (/ 1321.0, -6.0989148000, 1.3694759000e-3, -1.9809817000e-3 /) - LoveDat(1:4,1323) = (/ 1322.0, -6.0992979000, 1.3686395000e-3, -1.9796485000e-3 /) - LoveDat(1:4,1324) = (/ 1323.0, -6.0996798000, 1.3678037000e-3, -1.9783167000e-3 /) - LoveDat(1:4,1325) = (/ 1324.0, -6.1000605000, 1.3669686000e-3, -1.9769864000e-3 /) - LoveDat(1:4,1326) = (/ 1325.0, -6.1004399000, 1.3661342000e-3, -1.9756576000e-3 /) - LoveDat(1:4,1327) = (/ 1326.0, -6.1008180000, 1.3653005000e-3, -1.9743302000e-3 /) - LoveDat(1:4,1328) = (/ 1327.0, -6.1011948000, 1.3644674000e-3, -1.9730044000e-3 /) - LoveDat(1:4,1329) = (/ 1328.0, -6.1015705000, 1.3636349000e-3, -1.9716800000e-3 /) - LoveDat(1:4,1330) = (/ 1329.0, -6.1019449000, 1.3628031000e-3, -1.9703571000e-3 /) - LoveDat(1:4,1331) = (/ 1330.0, -6.1023180000, 1.3619720000e-3, -1.9690357000e-3 /) - LoveDat(1:4,1332) = (/ 1331.0, -6.1026899000, 1.3611416000e-3, -1.9677157000e-3 /) - LoveDat(1:4,1333) = (/ 1332.0, -6.1030606000, 1.3603118000e-3, -1.9663972000e-3 /) - LoveDat(1:4,1334) = (/ 1333.0, -6.1034300000, 1.3594826000e-3, -1.9650802000e-3 /) - LoveDat(1:4,1335) = (/ 1334.0, -6.1037983000, 1.3586541000e-3, -1.9637647000e-3 /) - LoveDat(1:4,1336) = (/ 1335.0, -6.1041653000, 1.3578263000e-3, -1.9624506000e-3 /) - LoveDat(1:4,1337) = (/ 1336.0, -6.1045311000, 1.3569992000e-3, -1.9611380000e-3 /) - LoveDat(1:4,1338) = (/ 1337.0, -6.1048956000, 1.3561727000e-3, -1.9598268000e-3 /) - LoveDat(1:4,1339) = (/ 1338.0, -6.1052590000, 1.3553468000e-3, -1.9585171000e-3 /) - LoveDat(1:4,1340) = (/ 1339.0, -6.1056212000, 1.3545217000e-3, -1.9572089000e-3 /) - LoveDat(1:4,1341) = (/ 1340.0, -6.1059821000, 1.3536972000e-3, -1.9559021000e-3 /) - LoveDat(1:4,1342) = (/ 1341.0, -6.1063419000, 1.3528733000e-3, -1.9545968000e-3 /) - LoveDat(1:4,1343) = (/ 1342.0, -6.1067005000, 1.3520501000e-3, -1.9532929000e-3 /) - LoveDat(1:4,1344) = (/ 1343.0, -6.1070578000, 1.3512276000e-3, -1.9519905000e-3 /) - LoveDat(1:4,1345) = (/ 1344.0, -6.1074140000, 1.3504057000e-3, -1.9506896000e-3 /) - LoveDat(1:4,1346) = (/ 1345.0, -6.1077690000, 1.3495845000e-3, -1.9493900000e-3 /) - LoveDat(1:4,1347) = (/ 1346.0, -6.1081229000, 1.3487640000e-3, -1.9480920000e-3 /) - LoveDat(1:4,1348) = (/ 1347.0, -6.1084755000, 1.3479441000e-3, -1.9467953000e-3 /) - LoveDat(1:4,1349) = (/ 1348.0, -6.1088270000, 1.3471249000e-3, -1.9455001000e-3 /) - LoveDat(1:4,1350) = (/ 1349.0, -6.1091773000, 1.3463063000e-3, -1.9442064000e-3 /) - LoveDat(1:4,1351) = (/ 1350.0, -6.1095265000, 1.3454884000e-3, -1.9429141000e-3 /) - LoveDat(1:4,1352) = (/ 1351.0, -6.1098744000, 1.3446712000e-3, -1.9416232000e-3 /) - LoveDat(1:4,1353) = (/ 1352.0, -6.1102213000, 1.3438546000e-3, -1.9403338000e-3 /) - LoveDat(1:4,1354) = (/ 1353.0, -6.1105669000, 1.3430387000e-3, -1.9390458000e-3 /) - LoveDat(1:4,1355) = (/ 1354.0, -6.1109115000, 1.3422234000e-3, -1.9377592000e-3 /) - LoveDat(1:4,1356) = (/ 1355.0, -6.1112548000, 1.3414088000e-3, -1.9364741000e-3 /) - LoveDat(1:4,1357) = (/ 1356.0, -6.1115971000, 1.3405949000e-3, -1.9351903000e-3 /) - LoveDat(1:4,1358) = (/ 1357.0, -6.1119382000, 1.3397816000e-3, -1.9339081000e-3 /) - LoveDat(1:4,1359) = (/ 1358.0, -6.1122781000, 1.3389690000e-3, -1.9326272000e-3 /) - LoveDat(1:4,1360) = (/ 1359.0, -6.1126170000, 1.3381571000e-3, -1.9313478000e-3 /) - LoveDat(1:4,1361) = (/ 1360.0, -6.1129546000, 1.3373458000e-3, -1.9300697000e-3 /) - LoveDat(1:4,1362) = (/ 1361.0, -6.1132912000, 1.3365352000e-3, -1.9287931000e-3 /) - LoveDat(1:4,1363) = (/ 1362.0, -6.1136267000, 1.3357252000e-3, -1.9275180000e-3 /) - LoveDat(1:4,1364) = (/ 1363.0, -6.1139610000, 1.3349159000e-3, -1.9262442000e-3 /) - LoveDat(1:4,1365) = (/ 1364.0, -6.1142942000, 1.3341073000e-3, -1.9249718000e-3 /) - LoveDat(1:4,1366) = (/ 1365.0, -6.1146263000, 1.3332993000e-3, -1.9237009000e-3 /) - LoveDat(1:4,1367) = (/ 1366.0, -6.1149573000, 1.3324920000e-3, -1.9224314000e-3 /) - LoveDat(1:4,1368) = (/ 1367.0, -6.1152872000, 1.3316853000e-3, -1.9211632000e-3 /) - LoveDat(1:4,1369) = (/ 1368.0, -6.1156160000, 1.3308793000e-3, -1.9198965000e-3 /) - LoveDat(1:4,1370) = (/ 1369.0, -6.1159437000, 1.3300740000e-3, -1.9186312000e-3 /) - LoveDat(1:4,1371) = (/ 1370.0, -6.1162702000, 1.3292693000e-3, -1.9173673000e-3 /) - LoveDat(1:4,1372) = (/ 1371.0, -6.1165957000, 1.3284653000e-3, -1.9161048000e-3 /) - LoveDat(1:4,1373) = (/ 1372.0, -6.1169202000, 1.3276619000e-3, -1.9148437000e-3 /) - LoveDat(1:4,1374) = (/ 1373.0, -6.1172435000, 1.3268592000e-3, -1.9135840000e-3 /) - LoveDat(1:4,1375) = (/ 1374.0, -6.1175657000, 1.3260572000e-3, -1.9123257000e-3 /) - LoveDat(1:4,1376) = (/ 1375.0, -6.1178869000, 1.3252558000e-3, -1.9110688000e-3 /) - LoveDat(1:4,1377) = (/ 1376.0, -6.1182070000, 1.3244551000e-3, -1.9098133000e-3 /) - LoveDat(1:4,1378) = (/ 1377.0, -6.1185260000, 1.3236551000e-3, -1.9085591000e-3 /) - LoveDat(1:4,1379) = (/ 1378.0, -6.1188440000, 1.3228557000e-3, -1.9073064000e-3 /) - LoveDat(1:4,1380) = (/ 1379.0, -6.1191609000, 1.3220569000e-3, -1.9060550000e-3 /) - LoveDat(1:4,1381) = (/ 1380.0, -6.1194767000, 1.3212589000e-3, -1.9048051000e-3 /) - LoveDat(1:4,1382) = (/ 1381.0, -6.1197915000, 1.3204614000e-3, -1.9035565000e-3 /) - LoveDat(1:4,1383) = (/ 1382.0, -6.1201052000, 1.3196647000e-3, -1.9023093000e-3 /) - LoveDat(1:4,1384) = (/ 1383.0, -6.1204179000, 1.3188686000e-3, -1.9010635000e-3 /) - LoveDat(1:4,1385) = (/ 1384.0, -6.1207295000, 1.3180732000e-3, -1.8998190000e-3 /) - LoveDat(1:4,1386) = (/ 1385.0, -6.1210401000, 1.3172784000e-3, -1.8985760000e-3 /) - LoveDat(1:4,1387) = (/ 1386.0, -6.1213496000, 1.3164843000e-3, -1.8973343000e-3 /) - LoveDat(1:4,1388) = (/ 1387.0, -6.1216581000, 1.3156908000e-3, -1.8960940000e-3 /) - LoveDat(1:4,1389) = (/ 1388.0, -6.1219656000, 1.3148980000e-3, -1.8948550000e-3 /) - LoveDat(1:4,1390) = (/ 1389.0, -6.1222720000, 1.3141059000e-3, -1.8936175000e-3 /) - LoveDat(1:4,1391) = (/ 1390.0, -6.1225774000, 1.3133144000e-3, -1.8923813000e-3 /) - LoveDat(1:4,1392) = (/ 1391.0, -6.1228818000, 1.3125236000e-3, -1.8911464000e-3 /) - LoveDat(1:4,1393) = (/ 1392.0, -6.1231851000, 1.3117334000e-3, -1.8899130000e-3 /) - LoveDat(1:4,1394) = (/ 1393.0, -6.1234875000, 1.3109439000e-3, -1.8886809000e-3 /) - LoveDat(1:4,1395) = (/ 1394.0, -6.1237888000, 1.3101551000e-3, -1.8874501000e-3 /) - LoveDat(1:4,1396) = (/ 1395.0, -6.1240891000, 1.3093669000e-3, -1.8862207000e-3 /) - LoveDat(1:4,1397) = (/ 1396.0, -6.1243884000, 1.3085794000e-3, -1.8849927000e-3 /) - LoveDat(1:4,1398) = (/ 1397.0, -6.1246867000, 1.3077925000e-3, -1.8837660000e-3 /) - LoveDat(1:4,1399) = (/ 1398.0, -6.1249840000, 1.3070063000e-3, -1.8825407000e-3 /) - LoveDat(1:4,1400) = (/ 1399.0, -6.1252803000, 1.3062208000e-3, -1.8813168000e-3 /) - LoveDat(1:4,1401) = (/ 1400.0, -6.1255756000, 1.3054359000e-3, -1.8800942000e-3 /) - LoveDat(1:4,1402) = (/ 1401.0, -6.1258699000, 1.3046517000e-3, -1.8788729000e-3 /) - LoveDat(1:4,1403) = (/ 1402.0, -6.1261632000, 1.3038681000e-3, -1.8776530000e-3 /) - LoveDat(1:4,1404) = (/ 1403.0, -6.1264555000, 1.3030852000e-3, -1.8764345000e-3 /) - LoveDat(1:4,1405) = (/ 1404.0, -6.1267469000, 1.3023029000e-3, -1.8752172000e-3 /) - LoveDat(1:4,1406) = (/ 1405.0, -6.1270372000, 1.3015213000e-3, -1.8740014000e-3 /) - LoveDat(1:4,1407) = (/ 1406.0, -6.1273266000, 1.3007404000e-3, -1.8727868000e-3 /) - LoveDat(1:4,1408) = (/ 1407.0, -6.1276150000, 1.2999601000e-3, -1.8715736000e-3 /) - LoveDat(1:4,1409) = (/ 1408.0, -6.1279024000, 1.2991804000e-3, -1.8703618000e-3 /) - LoveDat(1:4,1410) = (/ 1409.0, -6.1281889000, 1.2984015000e-3, -1.8691513000e-3 /) - LoveDat(1:4,1411) = (/ 1410.0, -6.1284744000, 1.2976231000e-3, -1.8679421000e-3 /) - LoveDat(1:4,1412) = (/ 1411.0, -6.1287590000, 1.2968455000e-3, -1.8667343000e-3 /) - LoveDat(1:4,1413) = (/ 1412.0, -6.1290425000, 1.2960685000e-3, -1.8655277000e-3 /) - LoveDat(1:4,1414) = (/ 1413.0, -6.1293252000, 1.2952921000e-3, -1.8643226000e-3 /) - LoveDat(1:4,1415) = (/ 1414.0, -6.1296068000, 1.2945164000e-3, -1.8631187000e-3 /) - LoveDat(1:4,1416) = (/ 1415.0, -6.1298876000, 1.2937414000e-3, -1.8619162000e-3 /) - LoveDat(1:4,1417) = (/ 1416.0, -6.1301673000, 1.2929670000e-3, -1.8607150000e-3 /) - LoveDat(1:4,1418) = (/ 1417.0, -6.1304462000, 1.2921933000e-3, -1.8595151000e-3 /) - LoveDat(1:4,1419) = (/ 1418.0, -6.1307241000, 1.2914202000e-3, -1.8583165000e-3 /) - LoveDat(1:4,1420) = (/ 1419.0, -6.1310010000, 1.2906478000e-3, -1.8571193000e-3 /) - LoveDat(1:4,1421) = (/ 1420.0, -6.1312770000, 1.2898761000e-3, -1.8559234000e-3 /) - LoveDat(1:4,1422) = (/ 1421.0, -6.1315521000, 1.2891050000e-3, -1.8547288000e-3 /) - LoveDat(1:4,1423) = (/ 1422.0, -6.1318263000, 1.2883345000e-3, -1.8535355000e-3 /) - LoveDat(1:4,1424) = (/ 1423.0, -6.1320995000, 1.2875647000e-3, -1.8523435000e-3 /) - LoveDat(1:4,1425) = (/ 1424.0, -6.1323718000, 1.2867956000e-3, -1.8511528000e-3 /) - LoveDat(1:4,1426) = (/ 1425.0, -6.1326432000, 1.2860271000e-3, -1.8499635000e-3 /) - LoveDat(1:4,1427) = (/ 1426.0, -6.1329136000, 1.2852592000e-3, -1.8487754000e-3 /) - LoveDat(1:4,1428) = (/ 1427.0, -6.1331832000, 1.2844921000e-3, -1.8475887000e-3 /) - LoveDat(1:4,1429) = (/ 1428.0, -6.1334518000, 1.2837255000e-3, -1.8464033000e-3 /) - LoveDat(1:4,1430) = (/ 1429.0, -6.1337196000, 1.2829597000e-3, -1.8452191000e-3 /) - LoveDat(1:4,1431) = (/ 1430.0, -6.1339864000, 1.2821945000e-3, -1.8440363000e-3 /) - LoveDat(1:4,1432) = (/ 1431.0, -6.1342523000, 1.2814299000e-3, -1.8428548000e-3 /) - LoveDat(1:4,1433) = (/ 1432.0, -6.1345173000, 1.2806660000e-3, -1.8416745000e-3 /) - LoveDat(1:4,1434) = (/ 1433.0, -6.1347815000, 1.2799027000e-3, -1.8404956000e-3 /) - LoveDat(1:4,1435) = (/ 1434.0, -6.1350447000, 1.2791401000e-3, -1.8393180000e-3 /) - LoveDat(1:4,1436) = (/ 1435.0, -6.1353070000, 1.2783782000e-3, -1.8381416000e-3 /) - LoveDat(1:4,1437) = (/ 1436.0, -6.1355685000, 1.2776168000e-3, -1.8369666000e-3 /) - LoveDat(1:4,1438) = (/ 1437.0, -6.1358290000, 1.2768562000e-3, -1.8357928000e-3 /) - LoveDat(1:4,1439) = (/ 1438.0, -6.1360887000, 1.2760962000e-3, -1.8346203000e-3 /) - LoveDat(1:4,1440) = (/ 1439.0, -6.1363475000, 1.2753368000e-3, -1.8334492000e-3 /) !}}} - LoveDat(1:4,1441) = (/ 1440.0, -6.1366054000, 1.2745781000e-3, -1.8322792000e-3 /) - - - ! if (trim(config_ocean_run_mode) .eq. 'init') then - if (.False.) then - - LoveScaling(:) = 1.0 - - else - - allocate(H(lmax+1),L(lmax+1),K(lmax+1)) + integer :: n, m, l + integer :: n_tot - H(1:lmax+1) = LoveDat(2,1:lmax+1) - L(1:lmax+1) = LoveDat(3,1:lmax+1) - K(1:lmax+1) = LoveDat(4,1:lmax+1) + n_tot = size(LoveDat, dim=2) - ! Convert from CM to CF - H1 = H(2) - L1 = L(2) - K1 = K(2) - H(2) = 2.0/3.0*(H1 - L1) - L(2) = -1.0/3.0*(H1 - L1) - K(2) = -1.0/3.0*H1 - 2.0/3.0*L1 - 1.0 + if (nlm+1 > n_tot) call MOM_error(FATAL, "MOM_tidal_forcing " // & + "calc_love_scaling: maximum spherical harmonics degree is larger than " // & + "the size of the stored Love Number in MOM_load_love_number.") - do m = 0,nlm - do n = m,nlm - i = SHOrderDegreeToIndex(n,m,nlm) - LoveScaling(i) = (1.0 + K(n+1) - H(n+1))/real(2*n+1) - enddo - enddo - LoveScaling = LoveScaling * 3.0 * rhoW / rhoE + allocate(HDat(nlm+1), LDat(nlm+1), KDat(nlm+1)) + HDat(:) = LoveDat(2,1:nlm+1) ; LDat(:) = LoveDat(3,1:nlm+1) ; KDat(:) = LoveDat(4,1:nlm+1) + if (nlm > 0) then + ! Convert from CM to CF + H1 = HDat(2) ; L1 = LDat(2) ; K1 = KDat(2) + HDat(2) = 2.0/3.0*(H1 - L1) + LDat(2) = -1.0/3.0*(H1 - L1) + KDat(2) = -1.0/3.0*H1 - 2.0/3.0*L1 - 1.0 endif -end subroutine!}}} + do m=0,nlm + do n=m,nlm + l = order2index(m,nlm) + LoveScaling(l+n-m) = (1.0 + KDat(n+1) - HDat(n+1)) / real(2*n+1) * 3.0 * rhoW / rhoE + enddo + enddo +end subroutine calc_love_scaling !> This subroutine finds a named variable in a list of files and reads its !! values into a domain-decomposed 2-d array @@ -2191,17 +743,13 @@ subroutine calc_SAL_sht(eta, eta_sal, G, CS) allocate(SnmRe(lmax)); SnmRe = 0.0 allocate(SnmIm(lmax)); SnmIm = 0.0 - ! Get SAL scaling factors - ALLOC_(LoveScaling(lmax)) - call getloadLoveNums(CS%sal_sht_Nd, LoveScaling) - call spherical_harmonics_forward(G, CS%sht, eta, SnmRe, SnmIm, CS%sal_sht_Nd) do m = 0,CS%sal_sht_Nd + l = order2index(m,CS%sal_sht_Nd) do n = m,CS%sal_sht_Nd - l = SHOrderDegreeToIndex(n,m,CS%sal_sht_Nd) - SnmRe(l) = SnmRe(l)*LoveScaling(l) - SnmIm(l) = SnmIm(l)*LoveScaling(l) + SnmRe(l+n-m) = SnmRe(l+n-m)*CS%LoveScaling(l+n-m) + SnmIm(l+n-m) = SnmIm(l+n-m)*CS%LoveScaling(l+n-m) enddo enddo @@ -2226,8 +774,10 @@ subroutine tidal_forcing_end(CS) if (allocated(CS%sinphase_prev)) deallocate(CS%sinphase_prev) if (allocated(CS%amp_prev)) deallocate(CS%amp_prev) - if (CS%tidal_sal_sht) & + if (CS%tidal_sal_sht) then + if (allocated(CS%LoveScaling)) deallocate(CS%LoveScaling) call spherical_harmonics_end(CS%sht) + endif end subroutine tidal_forcing_end !> \namespace tidal_forcing From 2b9bd38efd849ca1eca31213049433ad72f2933e Mon Sep 17 00:00:00 2001 From: He Wang Date: Thu, 4 Aug 2022 11:54:03 -0400 Subject: [PATCH 53/68] Add reproducing sum option to SHT * Option to do reproducing sums for forward spherical harmonic transform is added, which is controlled by a runtime parameter. * New timers are added to monitor the performance of the more expensive global sum. --- .../lateral/MOM_spherical_harmonics.f90 | 141 +++++++++++------- .../lateral/MOM_tidal_forcing.F90 | 4 +- 2 files changed, 88 insertions(+), 57 deletions(-) diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 index cf9108de61..e992e80731 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.f90 @@ -1,12 +1,13 @@ -!> Laplace's spherical harmonics transforms (SHT) +!> Laplace's spherical harmonic transforms (SHT) module MOM_spherical_harmonics use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, & - CLOCK_MODULE + CLOCK_MODULE, CLOCK_ROUTINE, CLOCK_LOOP use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, WARNING use MOM_file_parser, only : get_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type use MOM_unit_scaling, only : unit_scale_type -use MOM_coms_infra, only : sum_across_PEs +use MOM_coms_infra, only : sum_across_PEs +use MOM_coms, only : reproducing_sum implicit none ; private @@ -24,9 +25,14 @@ module MOM_spherical_harmonics real, allocatable :: complexFactorRe(:,:,:), complexFactorIm(:,:,:), & !< Precomputed exponential factors complexExpRe(:,:,:), complexExpIm(:,:,:) !! at the t-cells [nondim]. real, allocatable :: aRecurrenceCoeff(:,:), bRecurrenceCoeff(:,:) !< Precomputed recurrennce coefficients [nondim]. - logical :: bfb !< True if use reproducable global sums + logical :: reprod_sum !< True if use reproducable global sums end type sht_CS +integer :: id_clock_sht=-1 !< CPU clock for SHT [MODULE] +integer :: id_clock_sht_forward=-1 !< CPU clock for forward transforms [ROUTINE] +integer :: id_clock_sht_inverse=-1 !< CPU clock for inverse transforms [ROUTINE] +integer :: id_clock_sht_global_sum=-1 !< CPU clock for global summation in forward transforms [LOOP] + contains subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. @@ -41,6 +47,7 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) integer :: is, ie, js, je integer :: m, n, l real, allocatable :: Snm_local(:), SnmRe_local(:), SnmIm_local(:) + real, allocatable :: SnmRe_local_reproSum(:,:,:), SnmIm_local_reproSum(:,:,:) real :: pmn, & ! Current associated Legendre polynomials of degree n and order m pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m pmnm2 ! Associated Legendre polynomials of degree n-2 and order m @@ -48,69 +55,79 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & "spherical_harmonics_forward: Module must be initialized before it is used.") + if (id_clock_sht>0) call cpu_clock_begin(id_clock_sht) + if (id_clock_sht_forward>0) call cpu_clock_begin(id_clock_sht_forward) + Nmax = CS%nOrder; if (present(Nd)) Nmax = Nd is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec - if (CS%bfb) then - ! allocate(Snm_local_reproSum(2*CS%lmax)); Snm_local_reproSum = 0.0 - ! allocate(SnmRe_local_reproSum(CS%lmax)); SnmRe_local_reproSum = 0.0 - ! allocate(SnmIm_local_reproSum(CS%lmax)); SnmIm_local_reproSum = 0.0 + if (CS%reprod_sum) then + allocate(SnmRe_local_reproSum(is:ie, js:je, CS%lmax)); SnmRe_local_reproSum = 0.0 + allocate(SnmIm_local_reproSum(is:ie, js:je, CS%lmax)); SnmIm_local_reproSum = 0.0 + + do m=0,Nmax + l = order2index(m, Nmax) + do j=js,je ; do i=is,ie + pmn = CS%Pmm(i,j,m+1) + SnmRe_local_reproSum(i,j,l) = var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) + SnmIm_local_reproSum(i,j,l) = var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) + + pmnm2 = 0.0; pmnm1 = pmn + do n = m+1, Nmax + pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 + SnmRe_local_reproSum(i,j,l+n-m) = var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) + SnmIm_local_reproSum(i,j,l+n-m) = var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) + pmnm2 = pmnm1; pmnm1 = pmn + enddo + enddo ; enddo + enddo else allocate(Snm_local(2*CS%lmax)); Snm_local = 0.0 allocate(SnmRe_local(CS%lmax)); SnmRe_local = 0.0 allocate(SnmIm_local(CS%lmax)); SnmIm_local = 0.0 - endif - - do m=0,Nmax - l = order2index(m, Nmax) - - do j=js,je ; do i=is,ie - pmn = CS%Pmm(i,j,m+1) - SnmRe_local(l) = SnmRe_local(l) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) - SnmIm_local(l) = SnmIm_local(l) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) - pmnm2 = 0.0; pmnm1 = pmn - do n = m+1, Nmax - pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 - SnmRe_local(l+n-m) = SnmRe_local(l+n-m) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) - SnmIm_local(l+n-m) = SnmIm_local(l+n-m) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) - pmnm2 = pmnm1; pmnm1 = pmn - enddo - enddo ; enddo - enddo + do m=0,Nmax + l = order2index(m, Nmax) + do j=js,je ; do i=is,ie + pmn = CS%Pmm(i,j,m+1) + SnmRe_local(l) = SnmRe_local(l) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) + SnmIm_local(l) = SnmIm_local(l) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) + + pmnm2 = 0.0; pmnm1 = pmn + do n = m+1, Nmax + pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 + SnmRe_local(l+n-m) = SnmRe_local(l+n-m) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) + SnmIm_local(l+n-m) = SnmIm_local(l+n-m) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) + pmnm2 = pmnm1; pmnm1 = pmn + enddo + enddo ; enddo + enddo + endif - ! call mpas_timer_stop('Parallel SAL: Forward Transform') + if (id_clock_sht_global_sum>0) call cpu_clock_begin(id_clock_sht_global_sum) - ! call mpas_timer_start('Parallel SAL: Communication') - if (CS%bfb) then - ! do m = 1,lmax - ! do iCell = 1,nCellsOwned - ! Snm_local_reproSum(iCell,m) = SnmRe_local_reproSum(iCell,m) - ! Snm_local_reproSum(iCell,lmax+m) = SnmIm_local_reproSum(iCell,m) - ! enddo - ! enddo + if (CS%reprod_sum) then + do m=1,CS%lmax + SnmRe(m) = reproducing_sum(SnmRe_local_reproSum(:,:,m)) + SnmIm(m) = reproducing_sum(SnmIm_local_reproSum(:,:,m)) + enddo else - do m = 1,CS%lmax + do m=1,CS%lmax Snm_local(m) = SnmRe_local(m) Snm_local(CS%lmax+m) = SnmIm_local(m) enddo - endif - - ! Compute global integral by summing local contributions - if (CS%bfb) then - !threadNum = mpas_threading_get_thread_num() - !if ( threadNum == 0 ) then - ! Snm = mpas_global_sum_nfld(Snm_local_reproSum,dminfo%comm) - !endif - else call sum_across_PEs(Snm_local, 2*CS%lmax) + + do m=1,CS%lmax + SnmRe(m) = Snm_local(m) + SnmIm(m) = Snm_local(CS%lmax+m) + enddo endif - do m=1,CS%lmax - SnmRe(m) = Snm_local(m) - SnmIm(m) = Snm_local(CS%lmax+m) - enddo + if (id_clock_sht_global_sum>0) call cpu_clock_end(id_clock_sht_global_sum) + if (id_clock_sht_forward>0) call cpu_clock_end(id_clock_sht_forward) + if (id_clock_sht>0) call cpu_clock_end(id_clock_sht) end subroutine spherical_harmonics_forward subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) @@ -133,10 +150,14 @@ subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & "spherical_harmonics_inverse: Module must be initialized before it is used.") - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + + if (id_clock_sht>0) call cpu_clock_begin(id_clock_sht) + if (id_clock_sht_inverse>0) call cpu_clock_begin(id_clock_sht_inverse) Nmax = CS%nOrder; if (present(Nd)) Nmax = Nd + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + var = 0.0 do m=0,Nmax mFac = sign(1.0, m-0.5)*0.5 + 1.5 @@ -156,6 +177,9 @@ subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) enddo enddo ; enddo enddo + + if (id_clock_sht_inverse>0) call cpu_clock_end(id_clock_sht_inverse) + if (id_clock_sht>0) call cpu_clock_end(id_clock_sht) end subroutine spherical_harmonics_inverse subroutine spherical_harmonics_init(G, param_file, CS) @@ -176,20 +200,21 @@ subroutine spherical_harmonics_init(G, param_file, CS) # include "version_variable.h" character(len=40) :: mdl = "MOM_spherical_harmonics" ! This module's name. + if (CS%initialized) return CS%initialized = .True. - call log_version(param_file, mdl, version, "") + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + call log_version(param_file, mdl, version, "") call get_param(param_file, mdl, "TIDAL_SAL_SHT_DEGREE", Nd_tidal_SAL, & "The maximum degree of the spherical harmonics transformation used for "// & "calculating the self-attraction and loading term for tides.", & default=0, do_not_log=.true.) CS%nOrder = Nd_tidal_SAL CS%lmax = calc_lmax(CS%nOrder) - call get_param(param_file, mdl, "SHT_BFB", CS%bfb, & - "If true, use bfb sum. Default is False.", default=.False.) - - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + call get_param(param_file, mdl, "SHT_REPRODUCING_SUM", CS%reprod_sum, & + "If true, use reproducing sums (invariant to PE layout) in inverse transform "// & + "of spherical harmonics. Otherwise use a simple sum of floationg point numbers. ", default=.False.) ! Recurrence relationship coefficients ! a has an additional row of zero to accommodate the nonexistent element P(m+2,m+1) when m=n=CS%nOrder @@ -241,6 +266,12 @@ subroutine spherical_harmonics_init(G, param_file, CS) enddo enddo ; enddo enddo + + id_clock_sht = cpu_clock_id('(Ocean spherical harmonics)', grain=CLOCK_MODULE) + id_clock_sht_forward = cpu_clock_id('(Ocean SHT forward)', grain=CLOCK_ROUTINE) + id_clock_sht_inverse = cpu_clock_id('(Ocean SHT inverse)', grain=CLOCK_ROUTINE) + id_clock_sht_global_sum = cpu_clock_id('(Ocean SHT global sum)', grain=CLOCK_LOOP) + end subroutine spherical_harmonics_init subroutine spherical_harmonics_end(CS) diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 384f6b19d2..e646cc1e13 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -4,7 +4,7 @@ module MOM_tidal_forcing ! This file is part of MOM6. See LICENSE.md for the license. use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, & - CLOCK_MODULE + CLOCK_MODULE, CLOCK_ROUTINE use MOM_domains, only : pass_var use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, WARNING use MOM_file_parser, only : get_param, log_version, param_file_type @@ -541,7 +541,7 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) allocate(CS%LoveScaling(lmax)) call calc_love_scaling(CS%sal_sht_Nd, CS%LoveScaling) call spherical_harmonics_init(G, param_file, CS%sht) - id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_MODULE) + id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_ROUTINE) endif id_clock_tides = cpu_clock_id('(Ocean tides)', grain=CLOCK_MODULE) From 1c83b2d2d029bfbbcc87427df4e54ee97f8726cb Mon Sep 17 00:00:00 2001 From: He Wang Date: Thu, 4 Aug 2022 19:00:33 -0400 Subject: [PATCH 54/68] Optimize spherical harmonics module * Associated Legendre polynomials calculation is vectorized. * Some redundant variables are removed. * Fix filename suffix for module MOM_spherical_harmonics --- ...monics.f90 => MOM_spherical_harmonics.F90} | 149 +++++++++--------- 1 file changed, 75 insertions(+), 74 deletions(-) rename src/parameterizations/lateral/{MOM_spherical_harmonics.f90 => MOM_spherical_harmonics.F90} (69%) diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 similarity index 69% rename from src/parameterizations/lateral/MOM_spherical_harmonics.f90 rename to src/parameterizations/lateral/MOM_spherical_harmonics.F90 index e992e80731..736e57bfed 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.f90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 @@ -35,22 +35,25 @@ module MOM_spherical_harmonics contains subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(sht_CS), intent(in) :: CS !< Control structure for spherical harmonics trasnforms - real, intent(in) :: var(:,:) !< Input 2-D variable - real, intent(out) :: SnmRe(:), SnmIm(:) !< Real and imaginary SHT coefficients - integer, intent(in), optional :: Nd !< Maximum degree of the spherical harmonics, overriding nOrder - !! in the control structure. + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(sht_CS), intent(in) :: CS !< Control structure for SHT + real, dimension(SZI_(G),SZJ_(G)), & + intent(in) :: var(:,:) !< Input 2-D variable + real, intent(out) :: SnmRe(:), & !< Output real and imaginary SHT coefficients + SnmIm(:) !! [nondim] + integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics + !! overriding nOrder in the CS [nondim] ! local variables integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics + integer :: Ltot ! Local copy of the number of spherical harmonics + real, dimension(SZI_(G),SZJ_(G)) :: & + pmn, & ! Current associated Legendre polynomials of degree n and order m + pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m + pmnm2 ! Associated Legendre polynomials of degree n-2 and order m + real, allocatable :: SnmRe_reproSum(:,:,:), SnmIm_reproSum(:,:,:) integer :: i, j, k integer :: is, ie, js, je integer :: m, n, l - real, allocatable :: Snm_local(:), SnmRe_local(:), SnmIm_local(:) - real, allocatable :: SnmRe_local_reproSum(:,:,:), SnmIm_local_reproSum(:,:,:) - real :: pmn, & ! Current associated Legendre polynomials of degree n and order m - pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m - pmnm2 ! Associated Legendre polynomials of degree n-2 and order m if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & "spherical_harmonics_forward: Module must be initialized before it is used.") @@ -59,70 +62,65 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) if (id_clock_sht_forward>0) call cpu_clock_begin(id_clock_sht_forward) Nmax = CS%nOrder; if (present(Nd)) Nmax = Nd + Ltot = calc_lmax(Nmax) is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + do l=1,Ltot ; SnmRe(l) = 0.0; SnmIm(l) = 0.0 ; enddo + if (CS%reprod_sum) then - allocate(SnmRe_local_reproSum(is:ie, js:je, CS%lmax)); SnmRe_local_reproSum = 0.0 - allocate(SnmIm_local_reproSum(is:ie, js:je, CS%lmax)); SnmIm_local_reproSum = 0.0 + allocate(SnmRe_reproSum(is:ie, js:je, CS%lmax)); SnmRe_reproSum = 0.0 + allocate(SnmIm_reproSum(is:ie, js:je, CS%lmax)); SnmIm_reproSum = 0.0 do m=0,Nmax l = order2index(m, Nmax) + do j=js,je ; do i=is,ie - pmn = CS%Pmm(i,j,m+1) - SnmRe_local_reproSum(i,j,l) = var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) - SnmIm_local_reproSum(i,j,l) = var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) - - pmnm2 = 0.0; pmnm1 = pmn - do n = m+1, Nmax - pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 - SnmRe_local_reproSum(i,j,l+n-m) = var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) - SnmIm_local_reproSum(i,j,l+n-m) = var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) - pmnm2 = pmnm1; pmnm1 = pmn - enddo + SnmRe_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorRe(i,j,m+1) + SnmIm_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorIm(i,j,m+1) + pmnm2(i,j) = 0.0 + pmnm1(i,j) = CS%Pmm(i,j,m+1) enddo ; enddo + + do n = m+1, Nmax ; do j=js,je ; do i=is,ie + pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1(i,j) - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2(i,j) + SnmRe_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorRe(i,j,m+1) + SnmIm_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorIm(i,j,m+1) + pmnm2(i,j) = pmnm1(i,j) + pmnm1(i,j) = pmn(i,j) + enddo ; enddo ; enddo enddo else - allocate(Snm_local(2*CS%lmax)); Snm_local = 0.0 - allocate(SnmRe_local(CS%lmax)); SnmRe_local = 0.0 - allocate(SnmIm_local(CS%lmax)); SnmIm_local = 0.0 - do m=0,Nmax l = order2index(m, Nmax) + do j=js,je ; do i=is,ie - pmn = CS%Pmm(i,j,m+1) - SnmRe_local(l) = SnmRe_local(l) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) - SnmIm_local(l) = SnmIm_local(l) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) - - pmnm2 = 0.0; pmnm1 = pmn - do n = m+1, Nmax - pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 - SnmRe_local(l+n-m) = SnmRe_local(l+n-m) + var(i,j) * pmn * CS%complexFactorRe(i,j,m+1) - SnmIm_local(l+n-m) = SnmIm_local(l+n-m) + var(i,j) * pmn * CS%complexFactorIm(i,j,m+1) - pmnm2 = pmnm1; pmnm1 = pmn - enddo + SnmRe(l) = SnmRe(l) + var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorRe(i,j,m+1) + SnmIm(l) = SnmIm(l) + var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorIm(i,j,m+1) + pmnm2(i,j) = 0.0 + pmnm1(i,j) = CS%Pmm(i,j,m+1) enddo ; enddo + + do n=m+1, Nmax ; do j=js,je ; do i=is,ie + pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1(i,j) - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2(i,j) + SnmRe(l+n-m) = SnmRe(l+n-m) + var(i,j) * pmn(i,j) * CS%complexFactorRe(i,j,m+1) + SnmIm(l+n-m) = SnmIm(l+n-m) + var(i,j) * pmn(i,j) * CS%complexFactorIm(i,j,m+1) + pmnm2(i,j) = pmnm1(i,j) + pmnm1(i,j) = pmn(i,j) + enddo ; enddo ; enddo enddo endif if (id_clock_sht_global_sum>0) call cpu_clock_begin(id_clock_sht_global_sum) if (CS%reprod_sum) then - do m=1,CS%lmax - SnmRe(m) = reproducing_sum(SnmRe_local_reproSum(:,:,m)) - SnmIm(m) = reproducing_sum(SnmIm_local_reproSum(:,:,m)) + do l=1,Ltot + SnmRe(l) = reproducing_sum(SnmRe_reproSum(:,:,l)) + SnmIm(l) = reproducing_sum(SnmIm_reproSum(:,:,l)) enddo else - do m=1,CS%lmax - Snm_local(m) = SnmRe_local(m) - Snm_local(CS%lmax+m) = SnmIm_local(m) - enddo - call sum_across_PEs(Snm_local, 2*CS%lmax) - - do m=1,CS%lmax - SnmRe(m) = Snm_local(m) - SnmIm(m) = Snm_local(CS%lmax+m) - enddo + call sum_across_PEs(SnmRe, Ltot) + call sum_across_PEs(SnmIm, Ltot) endif if (id_clock_sht_global_sum>0) call cpu_clock_end(id_clock_sht_global_sum) @@ -131,22 +129,24 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) end subroutine spherical_harmonics_forward subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(sht_CS), intent(in) :: CS !< Control structure for spherical harmonics trasnforms - real, intent(out) :: var(:,:) !< Output 2-D variable - real, intent(in) :: SnmRe(:), SnmIm(:) !< Real and imaginary SHT coefficients including - !! any additional scaling factors such as Love numbers - integer, intent(in), optional :: Nd !< Maximum degree of the spherical harmonics, overriding nOrder - !! in the control structure. + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(sht_CS), intent(in) :: CS !< Control structure for SHT + real, intent(in) :: SnmRe(:), & !< Real and imaginary SHT coefficients with + SnmIm(:) !! any scaling factors such as Love numbers [nondim] + real, dimension(SZI_(G),SZJ_(G)), & + intent(out) :: var(:,:) !< Output 2-D variable + integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics + !! overriding nOrder in the CS [nondim] ! local variables + integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics + real :: mFac ! A constant multiplier. mFac = 1 (if m==0) or 2 (if m>0) + real, dimension(SZI_(G),SZJ_(G)) :: & + pmn, & ! Current associated Legendre polynomials of degree n and order m + pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m + pmnm2 ! Associated Legendre polynomials of degree n-2 and order m integer :: i, j, k integer :: is, ie, js, je integer :: m, n, l - integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics - real :: mFac ! A constant multiplier. mFac = 1 (if m==0) or 2 (if m>0) - real :: pmn, & ! Current associated Legendre polynomials of degree n and order m - pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m - pmnm2 ! Associated Legendre polynomials of degree n-2 and order m if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & "spherical_harmonics_inverse: Module must be initialized before it is used.") @@ -164,18 +164,19 @@ subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) l = order2index(m, Nmax) do j=js,je ; do i=is,ie - pmn = CS%Pmm(i,j,m+1) var(i,j) = var(i,j) & - + mFac * pmn * (SnmRe(l) * CS%complexExpRe(i,j,m+1) + SnmIm(l) * CS%complexExpIm(i,j,m+1)) - - pmnm2 = 0.0; pmnm1 = pmn - do n=m+1,Nmax - pmn = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1 - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2 - var(i,j) = var(i,j) & - + mFac * pmn * (SnmRe(l+n-m) * CS%complexExpRe(i,j,m+1) + SnmIm(l+n-m) * CS%complexExpIm(i,j,m+1)) - pmnm2 = pmnm1; pmnm1 = pmn - enddo + + mFac * CS%Pmm(i,j,m+1) * (SnmRe(l) * CS%complexExpRe(i,j,m+1) + SnmIm(l) * CS%complexExpIm(i,j,m+1)) + pmnm2(i,j) = 0.0 + pmnm1(i,j) = CS%Pmm(i,j,m+1) enddo ; enddo + + do n=m+1,Nmax ; do j=js,je ; do i=is,ie + pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1(i,j) - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2(i,j) + var(i,j) = var(i,j) & + + mFac * pmn(i,j) * (SnmRe(l+n-m) * CS%complexExpRe(i,j,m+1) + SnmIm(l+n-m) * CS%complexExpIm(i,j,m+1)) + pmnm2(i,j) = pmnm1(i,j) + pmnm1(i,j) = pmn(i,j) + enddo ; enddo ; enddo enddo if (id_clock_sht_inverse>0) call cpu_clock_end(id_clock_sht_inverse) From a2e883a95ea9a1bc2b1906b764f9bc4dbeff2e99 Mon Sep 17 00:00:00 2001 From: He Wang Date: Tue, 6 Sep 2022 22:41:52 -0400 Subject: [PATCH 55/68] Move some SHT SAL related allocations to init * In MOM_tidal_forcing module, spherical harmonic coefficients (for SAL) are now parts of tidal_forcing_CS to avoid repeated allocations. The same applies to the Love number scaling factors. * Allocations for arrays used for reproducing sums are moved to subroutine spherical_harmonics_init in the MOM_spherical_harmonics module. --- .../lateral/MOM_spherical_harmonics.F90 | 31 +++++++++++-------- .../lateral/MOM_tidal_forcing.F90 | 23 +++++++------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 index 736e57bfed..d982780a5d 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 @@ -25,6 +25,7 @@ module MOM_spherical_harmonics real, allocatable :: complexFactorRe(:,:,:), complexFactorIm(:,:,:), & !< Precomputed exponential factors complexExpRe(:,:,:), complexExpIm(:,:,:) !! at the t-cells [nondim]. real, allocatable :: aRecurrenceCoeff(:,:), bRecurrenceCoeff(:,:) !< Precomputed recurrennce coefficients [nondim]. + real, allocatable :: SnmRe_reproSum(:,:,:), SnmIm_reproSum(:,:,:) logical :: reprod_sum !< True if use reproducable global sums end type sht_CS @@ -36,9 +37,9 @@ module MOM_spherical_harmonics contains subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(sht_CS), intent(in) :: CS !< Control structure for SHT + type(sht_CS), intent(inout) :: CS !< Control structure for SHT real, dimension(SZI_(G),SZJ_(G)), & - intent(in) :: var(:,:) !< Input 2-D variable + intent(in) :: var !< Input 2-D variable real, intent(out) :: SnmRe(:), & !< Output real and imaginary SHT coefficients SnmIm(:) !! [nondim] integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics @@ -50,10 +51,10 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) pmn, & ! Current associated Legendre polynomials of degree n and order m pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m pmnm2 ! Associated Legendre polynomials of degree n-2 and order m - real, allocatable :: SnmRe_reproSum(:,:,:), SnmIm_reproSum(:,:,:) integer :: i, j, k integer :: is, ie, js, je integer :: m, n, l + ! real, dimension(SZI_(G),SZJ_(G), CS%lmax) :: SnmRe_reproSum, SnmIm_reproSum if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & "spherical_harmonics_forward: Module must be initialized before it is used.") @@ -69,23 +70,20 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) do l=1,Ltot ; SnmRe(l) = 0.0; SnmIm(l) = 0.0 ; enddo if (CS%reprod_sum) then - allocate(SnmRe_reproSum(is:ie, js:je, CS%lmax)); SnmRe_reproSum = 0.0 - allocate(SnmIm_reproSum(is:ie, js:je, CS%lmax)); SnmIm_reproSum = 0.0 - do m=0,Nmax l = order2index(m, Nmax) do j=js,je ; do i=is,ie - SnmRe_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorRe(i,j,m+1) - SnmIm_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorIm(i,j,m+1) + CS%SnmRe_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorRe(i,j,m+1) + CS%SnmIm_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorIm(i,j,m+1) pmnm2(i,j) = 0.0 pmnm1(i,j) = CS%Pmm(i,j,m+1) enddo ; enddo do n = m+1, Nmax ; do j=js,je ; do i=is,ie pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1(i,j) - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2(i,j) - SnmRe_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorRe(i,j,m+1) - SnmIm_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorIm(i,j,m+1) + CS%SnmRe_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorRe(i,j,m+1) + CS%SnmIm_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorIm(i,j,m+1) pmnm2(i,j) = pmnm1(i,j) pmnm1(i,j) = pmn(i,j) enddo ; enddo ; enddo @@ -115,8 +113,8 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) if (CS%reprod_sum) then do l=1,Ltot - SnmRe(l) = reproducing_sum(SnmRe_reproSum(:,:,l)) - SnmIm(l) = reproducing_sum(SnmIm_reproSum(:,:,l)) + SnmRe(l) = reproducing_sum(CS%SnmRe_reproSum(:,:,l)) + SnmIm(l) = reproducing_sum(CS%SnmIm_reproSum(:,:,l)) enddo else call sum_across_PEs(SnmRe, Ltot) @@ -134,7 +132,7 @@ subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) real, intent(in) :: SnmRe(:), & !< Real and imaginary SHT coefficients with SnmIm(:) !! any scaling factors such as Love numbers [nondim] real, dimension(SZI_(G),SZJ_(G)), & - intent(out) :: var(:,:) !< Output 2-D variable + intent(out) :: var !< Output 2-D variable integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics !! overriding nOrder in the CS [nondim] ! local variables @@ -268,6 +266,11 @@ subroutine spherical_harmonics_init(G, param_file, CS) enddo ; enddo enddo + if (CS%reprod_sum) then + allocate(CS%SnmRe_reproSum(is:ie, js:je, CS%lmax)); CS%SnmRe_reproSum = 0.0 + allocate(CS%SnmIm_reproSum(is:ie, js:je, CS%lmax)); CS%SnmIm_reproSum = 0.0 + endif + id_clock_sht = cpu_clock_id('(Ocean spherical harmonics)', grain=CLOCK_MODULE) id_clock_sht_forward = cpu_clock_id('(Ocean SHT forward)', grain=CLOCK_ROUTINE) id_clock_sht_inverse = cpu_clock_id('(Ocean SHT inverse)', grain=CLOCK_ROUTINE) @@ -282,6 +285,8 @@ subroutine spherical_harmonics_end(CS) deallocate(CS%Pmm) deallocate(CS%complexFactorRe, CS%complexFactorIm, CS%complexExpRe, CS%complexExpIm) deallocate(CS%aRecurrenceCoeff, CS%bRecurrenceCoeff) + if (CS%reprod_sum) & + deallocate(CS%SnmRe_reproSum, CS%SnmIm_reproSum) end subroutine spherical_harmonics_end !> The function calc_lmax returns the number of real elements (cosine) of the spherical harmonics, diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index e646cc1e13..a41fbaa0f4 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -78,6 +78,7 @@ module MOM_tidal_forcing type(sht_CS) :: sht integer :: sal_sht_Nd real, allocatable :: LoveScaling(:) + real, allocatable :: SnmRe(:), SnmIm(:) end type tidal_forcing_CS integer :: id_clock_tides !< CPU clock for tides @@ -538,6 +539,9 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) "calculating the self-attraction and loading term for tides.", & default=0, do_not_log=.not. CS%tidal_sal_sht) lmax = calc_lmax(CS%sal_sht_Nd) + allocate(CS%SnmRe(lmax)); CS%SnmRe = 0.0 + allocate(CS%SnmIm(lmax)); CS%SnmIm = 0.0 + allocate(CS%LoveScaling(lmax)) call calc_love_scaling(CS%sal_sht_Nd, CS%LoveScaling) call spherical_harmonics_init(G, param_file, CS%sht) @@ -730,30 +734,25 @@ subroutine calc_SAL_sht(eta, eta_sal, G, CS) real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_sal !< The sea surface height anomaly from !! a time-mean geoid [Z ~> m]. ! type(sht_CS), intent(in) :: sht - type(tidal_forcing_CS), intent(in) :: CS !< Tidal forcing control struct - real, allocatable :: SnmRe(:), SnmIm(:) + type(tidal_forcing_CS), intent(inout) :: CS !< Tidal forcing control struct real, allocatable :: LoveScaling(:) integer :: n, m, l - integer :: lmax - lmax = calc_lmax(CS%sal_sht_Nd) call cpu_clock_begin(id_clock_SAL) - allocate(SnmRe(lmax)); SnmRe = 0.0 - allocate(SnmIm(lmax)); SnmIm = 0.0 - - call spherical_harmonics_forward(G, CS%sht, eta, SnmRe, SnmIm, CS%sal_sht_Nd) + call spherical_harmonics_forward(G, CS%sht, eta, CS%SnmRe, CS%SnmIm, CS%sal_sht_Nd) + ! Multiply scaling to each mode do m = 0,CS%sal_sht_Nd l = order2index(m,CS%sal_sht_Nd) do n = m,CS%sal_sht_Nd - SnmRe(l+n-m) = SnmRe(l+n-m)*CS%LoveScaling(l+n-m) - SnmIm(l+n-m) = SnmIm(l+n-m)*CS%LoveScaling(l+n-m) + CS%SnmRe(l+n-m) = CS%SnmRe(l+n-m)*CS%LoveScaling(l+n-m) + CS%SnmIm(l+n-m) = CS%SnmIm(l+n-m)*CS%LoveScaling(l+n-m) enddo enddo - call spherical_harmonics_inverse(G, CS%sht, SnmRe, SnmIm, eta_sal, CS%sal_sht_Nd) + call spherical_harmonics_inverse(G, CS%sht, CS%SnmRe, CS%SnmIm, eta_sal, CS%sal_sht_Nd) call cpu_clock_end(id_clock_SAL) end subroutine calc_SAL_sht @@ -776,6 +775,8 @@ subroutine tidal_forcing_end(CS) if (CS%tidal_sal_sht) then if (allocated(CS%LoveScaling)) deallocate(CS%LoveScaling) + if (allocated(CS%SnmRe)) deallocate(CS%SnmRe) + if (allocated(CS%SnmIm)) deallocate(CS%SnmIm) call spherical_harmonics_end(CS%sht) endif end subroutine tidal_forcing_end From d5d1ae230ff5276168d810261fcd62a2957cd029 Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 26 Sep 2022 13:38:02 -0400 Subject: [PATCH 56/68] Add documentations in spherical harmonics module * Documentations are added for all modules related to SHT SAL. References from the MPAS-O group will need to be updated once they are published. * Variables names are shortened and changed to follow MOM6 style. (from camel to snake) * Change RhoE and RhoW in Love number scaling to runtime parameters * Correct a bug in a_recur size * Local arrays in SHT subroutines are properly initialized. --- .../lateral/MOM_load_love_numbers.F90 | 37 ++- .../lateral/MOM_spherical_harmonics.F90 | 307 +++++++++++------- .../lateral/MOM_tidal_forcing.F90 | 144 ++++---- 3 files changed, 313 insertions(+), 175 deletions(-) diff --git a/src/parameterizations/lateral/MOM_load_love_numbers.F90 b/src/parameterizations/lateral/MOM_load_love_numbers.F90 index 6f46d99c5b..865c65edb7 100644 --- a/src/parameterizations/lateral/MOM_load_love_numbers.F90 +++ b/src/parameterizations/lateral/MOM_load_love_numbers.F90 @@ -3,11 +3,11 @@ module MOM_load_love_numbers implicit none ; private -public LoveDat +public Love_Data integer, parameter :: lmax = 1440 real, dimension(4, lmax+1), parameter :: & - LoveDat = & + Love_Data = & reshape((/ 0.0, 0.0000000000, 0.0000000000 , -1.0000000000 , & 1.0, -1.2858777580,-8.9608179370e-1, -1.0000000000 , & 2.0, -0.9907994900, 2.3286695000e-2, -3.0516104000e-1, & @@ -1449,5 +1449,36 @@ module MOM_load_love_numbers 1438.0, -6.1360887000, 1.2760962000e-3, -1.8346203000e-3, & 1439.0, -6.1363475000, 1.2753368000e-3, -1.8334492000e-3, & 1440.0, -6.1366054000, 1.2745781000e-3, -1.8322792000e-3 & - /), (/4, lmax+1/)) + /), (/4, lmax+1/)) !< Load Love numbers + +!> \namespace mom_load_love_numbers +!! This module serves the sole purpose of storing load Love number. The Love numbers are used for the self-attraction +!! and loading (SAL) calculation, which is currently embedded in MOM_tidal_forcing module. This separate module ensures +!! the readability of the tidal module. +!! +!! Variable Love_Data stores the Love numbers up to degree 1440. From left to right: degree, h, l, and k. Data in this +!! module is imported from SAL calculation in Model for Prediction Across Scales (MPAS)-Ocean developed by Los Alamos +!! National Laboratory and University of Michigan (Barton et al. (2022) and Brus et al. (2022)). The load Love numbers +!! are from Wang et al. (2012), which are in the center of mass of total Earth system reference frame (CM). When used, +!! Love numbers with degree<2 should be converted to center of mass solid Earth reference frame (CF) (Blewitt (2003)), +!! as in subroutine calc_love_scaling in MOM_tidal_forcing module. +!! +!! References: +!! +!! Barton, K.N., Nairita, P., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J., +!! Wirasaet, D., and Schindelegger, M., 2022: Performance of Model for Prediction Across Scales (MPAS) Ocean as a +!! Global Barotropic Tide Model. Journal of Advances in Modeling Earth Systems, in review. +!! +!! Blewitt, G., 2003. Self‐consistency in reference frames, geocenter definition, and surface loading of the solid +!! Earth. Journal of geophysical research: solid earth, 108(B2). +!! https://doi.org/10.1029/2002JB002082 +!! +!! Brus, S.R., Barton, K.N., Nairita, P., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., +!! Westerink, J., and Schindelegger, M., 2022: Scalable self attraction and loading calculations for unstructured ocean +!! models. Ocean Modelling, in review. +!! +!! Wang, H., Xiang, L., Jia, L., Jiang, L., Wang, Z., Hu, B. and Gao, P., 2012. Load Love numbers and Green's functions +!! for elastic Earth models PREM, iasp91, ak135, and modified models with refined crustal structure from Crust 2.0. +!! Computers & Geosciences, 49, pp.190-199. +!! https://doi.org/10.1016/j.cageo.2012.06.022 end module MOM_load_love_numbers \ No newline at end of file diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 index d982780a5d..e2ef5b37b3 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 @@ -2,10 +2,9 @@ module MOM_spherical_harmonics use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end, & CLOCK_MODULE, CLOCK_ROUTINE, CLOCK_LOOP -use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, WARNING +use MOM_error_handler, only : MOM_error, FATAL use MOM_file_parser, only : get_param, log_version, param_file_type use MOM_grid, only : ocean_grid_type -use MOM_unit_scaling, only : unit_scale_type use MOM_coms_infra, only : sum_across_PEs use MOM_coms, only : reproducing_sum @@ -16,17 +15,21 @@ module MOM_spherical_harmonics #include +!> Control structure for spherical harmonic transforms type, public :: sht_CS ; private logical :: initialized = .False. !< True if this control structure has been initialized. - integer :: nOrder !< Maximum degree of the spherical harmonics [nodim]. - integer :: lmax !< Number of associated Legendre polynomials of nonnegative m [=(nOrder+1)*(nOrder+2)/2] [nodim]. - real, allocatable :: cosCoLatT(:,:) !< Precomputed cosine of colatitude at the t-cells [nondim]. - real, allocatable :: Pmm(:,:,:) !< Precomputed associated Legendre polynomials of which m=n at the t-cells [nondim]. - real, allocatable :: complexFactorRe(:,:,:), complexFactorIm(:,:,:), & !< Precomputed exponential factors - complexExpRe(:,:,:), complexExpIm(:,:,:) !! at the t-cells [nondim]. - real, allocatable :: aRecurrenceCoeff(:,:), bRecurrenceCoeff(:,:) !< Precomputed recurrennce coefficients [nondim]. - real, allocatable :: SnmRe_reproSum(:,:,:), SnmIm_reproSum(:,:,:) - logical :: reprod_sum !< True if use reproducable global sums + integer :: ndegree !< Maximum degree of the spherical harmonics [nodim]. + integer :: lmax !< Number of associated Legendre polynomials of nonnegative m + !! [lmax=(ndegree+1)*(ndegree+2)/2] [nodim]. + real, allocatable :: cos_clatT(:,:) !< Precomputed cosine of colatitude at the t-cells [nondim]. + real, allocatable :: Pmm(:,:,:) !< Precomputed associated Legendre polynomials (m=n) at the t-cells [nondim]. + real, allocatable :: cos_lonT(:,:,:), sin_lonT(:,:,:) !< Precomputed exponential factors at the t-cells [nondim]. + real, allocatable :: cos_lonT_wtd(:,:,:), & !< Precomputed exponential factors at the t-cells weighted by a + sin_lonT_wtd(:,:,:) !! nondimensionalized cell area [nondim] + real, allocatable :: a_recur(:,:), b_recur(:,:) !< Precomputed recurrence coefficients [nondim]. + real, allocatable :: Snm_Re_raw(:,:,:), & !< 3D array to store un-summed SHT coefficients + Snm_Im_raw(:,:,:) !! at the t-cells for reproducing sums [same as input variable] + logical :: reprod_sum !< True if use reproducible global sums end type sht_CS integer :: id_clock_sht=-1 !< CPU clock for SHT [MODULE] @@ -35,26 +38,27 @@ module MOM_spherical_harmonics integer :: id_clock_sht_global_sum=-1 !< CPU clock for global summation in forward transforms [LOOP] contains -subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(sht_CS), intent(inout) :: CS !< Control structure for SHT + +!> Calculates forward spherical harmonics transforms +subroutine spherical_harmonics_forward(G, CS, var, Snm_Re, Snm_Im, Nd) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(sht_CS), intent(inout) :: CS !< Control structure for SHT real, dimension(SZI_(G),SZJ_(G)), & - intent(in) :: var !< Input 2-D variable - real, intent(out) :: SnmRe(:), & !< Output real and imaginary SHT coefficients - SnmIm(:) !! [nondim] - integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics - !! overriding nOrder in the CS [nondim] + intent(in) :: var !< Input 2-D variable [] + real, intent(out) :: Snm_Re(:), & !< Output real and imaginary SHT coefficients + Snm_Im(:) !! [same as input variable] + integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics + !! overriding ndegree in the CS [nondim] ! local variables - integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics - integer :: Ltot ! Local copy of the number of spherical harmonics + integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics [nodim] + integer :: Ltot ! Local copy of the number of spherical harmonics [nodim] real, dimension(SZI_(G),SZJ_(G)) :: & - pmn, & ! Current associated Legendre polynomials of degree n and order m - pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m - pmnm2 ! Associated Legendre polynomials of degree n-2 and order m + pmn, & ! Current associated Legendre polynomials of degree n and order m [nodim] + pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m [nodim] + pmnm2 ! Associated Legendre polynomials of degree n-2 and order m [nodim] integer :: i, j, k - integer :: is, ie, js, je + integer :: is, ie, js, je, isd, ied, jsd, jed integer :: m, n, l - ! real, dimension(SZI_(G),SZJ_(G), CS%lmax) :: SnmRe_reproSum, SnmIm_reproSum if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & "spherical_harmonics_forward: Module must be initialized before it is used.") @@ -62,28 +66,34 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) if (id_clock_sht>0) call cpu_clock_begin(id_clock_sht) if (id_clock_sht_forward>0) call cpu_clock_begin(id_clock_sht_forward) - Nmax = CS%nOrder; if (present(Nd)) Nmax = Nd + Nmax = CS%ndegree; if (present(Nd)) Nmax = Nd Ltot = calc_lmax(Nmax) - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed - do l=1,Ltot ; SnmRe(l) = 0.0; SnmIm(l) = 0.0 ; enddo + do j=jsd,jed ; do i=isd,ied + pmn(i,j) = 0.0; pmnm1(i,j) = 0.0; pmnm2(i,j) = 0.0 + enddo ; enddo + + do l=1,Ltot ; Snm_Re(l) = 0.0; Snm_Im(l) = 0.0 ; enddo if (CS%reprod_sum) then do m=0,Nmax l = order2index(m, Nmax) do j=js,je ; do i=is,ie - CS%SnmRe_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorRe(i,j,m+1) - CS%SnmIm_reproSum(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorIm(i,j,m+1) + CS%Snm_Re_raw(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%cos_lonT_wtd(i,j,m+1) + CS%Snm_Im_raw(i,j,l) = var(i,j) * CS%Pmm(i,j,m+1) * CS%sin_lonT_wtd(i,j,m+1) pmnm2(i,j) = 0.0 pmnm1(i,j) = CS%Pmm(i,j,m+1) enddo ; enddo do n = m+1, Nmax ; do j=js,je ; do i=is,ie - pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1(i,j) - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2(i,j) - CS%SnmRe_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorRe(i,j,m+1) - CS%SnmIm_reproSum(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%complexFactorIm(i,j,m+1) + pmn(i,j) = & + CS%a_recur(n+1,m+1) * CS%cos_clatT(i,j) * pmnm1(i,j) - CS%b_recur(n+1,m+1) * pmnm2(i,j) + CS%Snm_Re_raw(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%cos_lonT_wtd(i,j,m+1) + CS%Snm_Im_raw(i,j,l+n-m) = var(i,j) * pmn(i,j) * CS%sin_lonT_wtd(i,j,m+1) pmnm2(i,j) = pmnm1(i,j) pmnm1(i,j) = pmn(i,j) enddo ; enddo ; enddo @@ -93,16 +103,17 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) l = order2index(m, Nmax) do j=js,je ; do i=is,ie - SnmRe(l) = SnmRe(l) + var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorRe(i,j,m+1) - SnmIm(l) = SnmIm(l) + var(i,j) * CS%Pmm(i,j,m+1) * CS%complexFactorIm(i,j,m+1) + Snm_Re(l) = Snm_Re(l) + var(i,j) * CS%Pmm(i,j,m+1) * CS%cos_lonT_wtd(i,j,m+1) + Snm_Im(l) = Snm_Im(l) + var(i,j) * CS%Pmm(i,j,m+1) * CS%sin_lonT_wtd(i,j,m+1) pmnm2(i,j) = 0.0 pmnm1(i,j) = CS%Pmm(i,j,m+1) enddo ; enddo do n=m+1, Nmax ; do j=js,je ; do i=is,ie - pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1(i,j) - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2(i,j) - SnmRe(l+n-m) = SnmRe(l+n-m) + var(i,j) * pmn(i,j) * CS%complexFactorRe(i,j,m+1) - SnmIm(l+n-m) = SnmIm(l+n-m) + var(i,j) * pmn(i,j) * CS%complexFactorIm(i,j,m+1) + pmn(i,j) = & + CS%a_recur(n+1,m+1) * CS%cos_clatT(i,j) * pmnm1(i,j) - CS%b_recur(n+1,m+1) * pmnm2(i,j) + Snm_Re(l+n-m) = Snm_Re(l+n-m) + var(i,j) * pmn(i,j) * CS%cos_lonT_wtd(i,j,m+1) + Snm_Im(l+n-m) = Snm_Im(l+n-m) + var(i,j) * pmn(i,j) * CS%sin_lonT_wtd(i,j,m+1) pmnm2(i,j) = pmnm1(i,j) pmnm1(i,j) = pmn(i,j) enddo ; enddo ; enddo @@ -113,12 +124,12 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) if (CS%reprod_sum) then do l=1,Ltot - SnmRe(l) = reproducing_sum(CS%SnmRe_reproSum(:,:,l)) - SnmIm(l) = reproducing_sum(CS%SnmIm_reproSum(:,:,l)) + Snm_Re(l) = reproducing_sum(CS%Snm_Re_raw(:,:,l)) + Snm_Im(l) = reproducing_sum(CS%Snm_Im_raw(:,:,l)) enddo else - call sum_across_PEs(SnmRe, Ltot) - call sum_across_PEs(SnmIm, Ltot) + call sum_across_PEs(Snm_Re, Ltot) + call sum_across_PEs(Snm_Im, Ltot) endif if (id_clock_sht_global_sum>0) call cpu_clock_end(id_clock_sht_global_sum) @@ -126,24 +137,25 @@ subroutine spherical_harmonics_forward(G, CS, var, SnmRe, SnmIm, Nd) if (id_clock_sht>0) call cpu_clock_end(id_clock_sht) end subroutine spherical_harmonics_forward -subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - type(sht_CS), intent(in) :: CS !< Control structure for SHT - real, intent(in) :: SnmRe(:), & !< Real and imaginary SHT coefficients with - SnmIm(:) !! any scaling factors such as Love numbers [nondim] +!> Calculates inverse spherical harmonics transforms +subroutine spherical_harmonics_inverse(G, CS, Snm_Re, Snm_Im, var, Nd) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + type(sht_CS), intent(in) :: CS !< Control structure for SHT + real, intent(in) :: Snm_Re(:), & !< Real and imaginary SHT coefficients with + Snm_Im(:) !! any scaling factors such as Love numbers [] real, dimension(SZI_(G),SZJ_(G)), & - intent(out) :: var !< Output 2-D variable - integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics - !! overriding nOrder in the CS [nondim] + intent(out) :: var !< Output 2-D variable [] + integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics + !! overriding ndegree in the CS [nondim] ! local variables - integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics - real :: mFac ! A constant multiplier. mFac = 1 (if m==0) or 2 (if m>0) + integer :: Nmax ! Local copy of the maximum degree of the spherical harmonics [nodim] + real :: mFac ! A constant multiplier. mFac = 1 (if m==0) or 2 (if m>0) [nodim] real, dimension(SZI_(G),SZJ_(G)) :: & - pmn, & ! Current associated Legendre polynomials of degree n and order m - pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m - pmnm2 ! Associated Legendre polynomials of degree n-2 and order m + pmn, & ! Current associated Legendre polynomials of degree n and order m [nodim] + pmnm1, & ! Associated Legendre polynomials of degree n-1 and order m [nodim] + pmnm2 ! Associated Legendre polynomials of degree n-2 and order m [nodim] integer :: i, j, k - integer :: is, ie, js, je + integer :: is, ie, js, je, isd, ied, jsd, jed integer :: m, n, l if (.not.CS%initialized) call MOM_error(FATAL, "MOM_spherical_harmonics " // & @@ -152,26 +164,34 @@ subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) if (id_clock_sht>0) call cpu_clock_begin(id_clock_sht) if (id_clock_sht_inverse>0) call cpu_clock_begin(id_clock_sht_inverse) - Nmax = CS%nOrder; if (present(Nd)) Nmax = Nd + Nmax = CS%ndegree; if (present(Nd)) Nmax = Nd - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed + + do j=jsd,jed ; do i=isd,ied + pmn(i,j) = 0.0; pmnm1(i,j) = 0.0; pmnm2(i,j) = 0.0 + var(i,j) = 0.0 + enddo ; enddo - var = 0.0 do m=0,Nmax mFac = sign(1.0, m-0.5)*0.5 + 1.5 l = order2index(m, Nmax) do j=js,je ; do i=is,ie var(i,j) = var(i,j) & - + mFac * CS%Pmm(i,j,m+1) * (SnmRe(l) * CS%complexExpRe(i,j,m+1) + SnmIm(l) * CS%complexExpIm(i,j,m+1)) + + mFac * CS%Pmm(i,j,m+1) * ( Snm_Re(l) * CS%cos_lonT(i,j,m+1) & + + Snm_Im(l) * CS%sin_lonT(i,j,m+1)) pmnm2(i,j) = 0.0 pmnm1(i,j) = CS%Pmm(i,j,m+1) enddo ; enddo do n=m+1,Nmax ; do j=js,je ; do i=is,ie - pmn(i,j) = CS%aRecurrenceCoeff(n+1,m+1) * CS%cosCoLatT(i,j) * pmnm1(i,j) - CS%bRecurrenceCoeff(n+1,m+1) * pmnm2(i,j) + pmn(i,j) = & + CS%a_recur(n+1,m+1) * CS%cos_clatT(i,j) * pmnm1(i,j) - CS%b_recur(n+1,m+1) * pmnm2(i,j) var(i,j) = var(i,j) & - + mFac * pmn(i,j) * (SnmRe(l+n-m) * CS%complexExpRe(i,j,m+1) + SnmIm(l+n-m) * CS%complexExpIm(i,j,m+1)) + + mFac * pmn(i,j) * ( Snm_Re(l+n-m) * CS%cos_lonT(i,j,m+1) & + + Snm_Im(l+n-m) * CS%sin_lonT(i,j,m+1)) pmnm2(i,j) = pmnm1(i,j) pmnm1(i,j) = pmn(i,j) enddo ; enddo ; enddo @@ -181,20 +201,21 @@ subroutine spherical_harmonics_inverse(G, CS, SnmRe, SnmIm, var, Nd) if (id_clock_sht>0) call cpu_clock_end(id_clock_sht) end subroutine spherical_harmonics_inverse +!> Calculate precomputed coefficients subroutine spherical_harmonics_init(G, param_file, CS) type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. type(param_file_type), intent(in) :: param_file !< A structure indicating - type(sht_CS), intent(inout) :: CS !< Control structure for spherical harmonics trasnforms + type(sht_CS), intent(inout) :: CS !< Control structure for spherical harmonic transforms ! local variables real, parameter :: PI = 4.0*atan(1.0) ! 3.1415926... calculated as 4*atan(1) [nodim] real, parameter :: RADIAN = PI / 180.0 ! Degree to Radian constant [rad/degree] - real, dimension(SZI_(G),SZJ_(G)) :: sinCoLatT ! sine of colatitude at the t-cells [nondim]. + real, dimension(SZI_(G),SZJ_(G)) :: sin_clatT ! sine of colatitude at the t-cells [nondim]. real :: Pmm_coef ! = sqrt{ 1.0/(4.0*PI) * prod[(2k+1)/2k)] } [nondim]. integer :: is, ie, js, je integer :: i, j, k integer :: m, n - integer :: Nd_tidal_SAL + integer :: Nd_tidal_SAL ! Maximum degree for tidal SAL ! This include declares and sets the variable "version". # include "version_variable.h" character(len=40) :: mdl = "MOM_spherical_harmonics" ! This module's name. @@ -209,57 +230,53 @@ subroutine spherical_harmonics_init(G, param_file, CS) "The maximum degree of the spherical harmonics transformation used for "// & "calculating the self-attraction and loading term for tides.", & default=0, do_not_log=.true.) - CS%nOrder = Nd_tidal_SAL - CS%lmax = calc_lmax(CS%nOrder) + CS%ndegree = Nd_tidal_SAL + CS%lmax = calc_lmax(CS%ndegree) call get_param(param_file, mdl, "SHT_REPRODUCING_SUM", CS%reprod_sum, & "If true, use reproducing sums (invariant to PE layout) in inverse transform "// & - "of spherical harmonics. Otherwise use a simple sum of floationg point numbers. ", default=.False.) - - ! Recurrence relationship coefficients - ! a has an additional row of zero to accommodate the nonexistent element P(m+2,m+1) when m=n=CS%nOrder - allocate(CS%aRecurrenceCoeff(CS%nOrder+2,CS%nOrder+1)); CS%aRecurrenceCoeff(:,:) = 0.0 - allocate(CS%bRecurrenceCoeff(CS%nOrder+1,CS%nOrder+1)); CS%bRecurrenceCoeff(:,:) = 0.0 - do m = 0, CS%nOrder - do n = m, CS%nOrder - if (m /= n) then - CS%aRecurrenceCoeff(n+1,m+1) = sqrt(real((2*n-1)*(2*n+1)) / real((n-m)*(n+m))) - CS%bRecurrenceCoeff(n+1,m+1) = sqrt(real((2*n+1)*(n+m-1)*(n-m-1)) / real((n-m)*(n+m)*(2*n-3))) - endif - enddo - enddo + "of spherical harmonics. Otherwise use a simple sum of floating point numbers. ", & + default=.False.) + + ! Calculate recurrence relationship coefficients + allocate(CS%a_recur(CS%ndegree+1, CS%ndegree+1)); CS%a_recur(:,:) = 0.0 + allocate(CS%b_recur(CS%ndegree+1, CS%ndegree+1)); CS%b_recur(:,:) = 0.0 + do m=0,CS%ndegree ; do n=m+1,CS%ndegree + CS%a_recur(n+1,m+1) = sqrt(real((2*n-1) * (2*n+1)) / real((n-m) * (n+m))) + CS%b_recur(n+1,m+1) = sqrt(real((2*n+1) * (n+m-1) * (n-m-1)) / real((n-m) * (n+m) * (2*n-3))) + enddo ; enddo - ! Complex exponential factors - allocate(CS%complexFactorRe(is:ie, js:je, CS%nOrder+1)); CS%complexFactorRe(:,:,:) = 0.0 - allocate(CS%complexFactorIm(is:ie, js:je, CS%nOrder+1)); CS%complexFactorIm(:,:,:) = 0.0 - allocate(CS%complexExpRe(is:ie, js:je, CS%nOrder+1)); CS%complexExpRe(:,:,:) = 0.0 - allocate(CS%complexExpIm(is:ie, js:je, CS%nOrder+1)); CS%complexExpIm(:,:,:) = 0.0 - do m=0,CS%nOrder + ! Calculate complex exponential factors + allocate(CS%cos_lonT_wtd(is:ie, js:je, CS%ndegree+1)); CS%cos_lonT_wtd(:,:,:) = 0.0 + allocate(CS%sin_lonT_wtd(is:ie, js:je, CS%ndegree+1)); CS%sin_lonT_wtd(:,:,:) = 0.0 + allocate(CS%cos_lonT(is:ie, js:je, CS%ndegree+1)); CS%cos_lonT(:,:,:) = 0.0 + allocate(CS%sin_lonT(is:ie, js:je, CS%ndegree+1)); CS%sin_lonT(:,:,:) = 0.0 + do m=0,CS%ndegree do j=js,je ; do i=is,ie - CS%complexExpRe(i, j, m+1) = cos(real(m) * (G%geolonT(i,j)*RADIAN)) - CS%complexExpIm(i, j, m+1) = sin(real(m) * (G%geolonT(i,j)*RADIAN)) - CS%complexFactorRe(i, j, m+1) = CS%complexExpRe(i, j, m+1) * G%areaT(i,j) / G%Rad_Earth**2 - CS%complexFactorIm(i, j, m+1) = CS%complexExpIm(i, j, m+1) * G%areaT(i,j) / G%Rad_Earth**2 + CS%cos_lonT(i,j,m+1) = cos(real(m) * (G%geolonT(i,j)*RADIAN)) + CS%sin_lonT(i,j,m+1) = sin(real(m) * (G%geolonT(i,j)*RADIAN)) + CS%cos_lonT_wtd(i,j,m+1) = CS%cos_lonT(i,j,m+1) * G%areaT(i,j) / G%Rad_Earth**2 + CS%sin_lonT_wtd(i,j,m+1) = CS%sin_lonT(i,j,m+1) * G%areaT(i,j) / G%Rad_Earth**2 enddo ; enddo enddo - ! sine and cosine of colatitude - allocate(CS%cosCoLatT(is:ie,js:je)); CS%cosCoLatT(:,:) = 0.0 + ! Calculate sine and cosine of colatitude + allocate(CS%cos_clatT(is:ie, js:je)); CS%cos_clatT(:,:) = 0.0 do j=js,je ; do i=is,ie - CS%cosCoLatT(i,j) = cos(0.5*PI - G%geolatT(i,j)*RADIAN) - sinCoLatT(i,j) = sin(0.5*PI - G%geolatT(i,j)*RADIAN) + CS%cos_clatT(i,j) = cos(0.5*PI - G%geolatT(i,j)*RADIAN) + sin_clatT(i,j) = sin(0.5*PI - G%geolatT(i,j)*RADIAN) enddo ; enddo - ! The diagonal elements of the associated Legendre polynomials (n=m) + ! Calculate the diagonal elements of the associated Legendre polynomials (n=m) allocate(CS%Pmm(is:ie,js:je,m+1)); CS%Pmm(:,:,:) = 0.0 - do m=0,CS%nOrder + do m=0,CS%ndegree ! Pmm_coef = 1.0/(4.0*PI) ! do k=1,m ; Pmm_coef = Pmm_coef * real(2*k+1)/real(2*k); enddo ! Pmm_coef = sqrt(Pmm_coef) ! do j=js,je ; do i=is,ie - ! CS%Pmm(i,j,m+1) = Pmm_coef * sinCoLatT(i,j)**m + ! CS%Pmm(i,j,m+1) = Pmm_coef * sin_clatT(i,j)**m ! enddo ; enddo do j=js,je ; do i=is,ie - CS%Pmm(i,j,m+1) = sqrt(1.0/(4.0*PI)) * sinCoLatT(i,j)**m + CS%Pmm(i,j,m+1) = sqrt(1.0/(4.0*PI)) * sin_clatT(i,j)**m do k = 1, m CS%Pmm(i,j,m+1) = CS%Pmm(i,j,m+1) * sqrt(real(2*k+1)/real(2*k)) enddo @@ -267,8 +284,8 @@ subroutine spherical_harmonics_init(G, param_file, CS) enddo if (CS%reprod_sum) then - allocate(CS%SnmRe_reproSum(is:ie, js:je, CS%lmax)); CS%SnmRe_reproSum = 0.0 - allocate(CS%SnmIm_reproSum(is:ie, js:je, CS%lmax)); CS%SnmIm_reproSum = 0.0 + allocate(CS%Snm_Re_raw(is:ie, js:je, CS%lmax)); CS%Snm_Re_raw = 0.0 + allocate(CS%Snm_Im_raw(is:ie, js:je, CS%lmax)); CS%Snm_Im_raw = 0.0 endif id_clock_sht = cpu_clock_id('(Ocean spherical harmonics)', grain=CLOCK_MODULE) @@ -278,34 +295,92 @@ subroutine spherical_harmonics_init(G, param_file, CS) end subroutine spherical_harmonics_init +!> Deallocate any variables allocated in spherical_harmonics_init subroutine spherical_harmonics_end(CS) - type(sht_CS), intent(inout) :: CS + type(sht_CS), intent(inout) :: CS !< Control structure for spherical harmonic transforms - deallocate(CS%cosCoLatT) + deallocate(CS%cos_clatT) deallocate(CS%Pmm) - deallocate(CS%complexFactorRe, CS%complexFactorIm, CS%complexExpRe, CS%complexExpIm) - deallocate(CS%aRecurrenceCoeff, CS%bRecurrenceCoeff) + deallocate(CS%cos_lonT_wtd, CS%sin_lonT_wtd, CS%cos_lonT, CS%sin_lonT) + deallocate(CS%a_recur, CS%b_recur) if (CS%reprod_sum) & - deallocate(CS%SnmRe_reproSum, CS%SnmIm_reproSum) + deallocate(CS%Snm_Re_raw, CS%Snm_Im_raw) end subroutine spherical_harmonics_end -!> The function calc_lmax returns the number of real elements (cosine) of the spherical harmonics, -!! given the maximum degree, +!> Calculates the number of real elements (cosine) of spherical harmonics given maximum degree Nd. function calc_lmax(Nd) result(lmax) - integer :: lmax - integer, intent(in) :: Nd + integer :: lmax !< Number of real spherical harmonic modes [nodim] + integer, intent(in) :: Nd !< Maximum degree [nodim] lmax = (Nd+2) * (Nd+1) / 2 end function calc_lmax -!> The function returns the one-dimension index number at (n=0, m=m), given order (m) and maximum degree (Nd) -!! The one-dimensional array is organized following degree being the faster moving dimension. +!> Calculates the one-dimensional index number at (n=0, m=m), given order m and maximum degree Nd. +!! It is sequenced with degree (n) changing first and order (m) changing second. function order2index(m, Nd) result(l) - integer :: l - integer, intent(in) :: m - integer, intent(in) :: Nd + integer :: l !< One-dimensional index number [nodim] + integer, intent(in) :: m !< Current order number [nodim] + integer, intent(in) :: Nd !< Maximum degree [nodim] l = ((Nd+1) + (Nd+1-(m-1)))*m/2 + 1 end function order2index +!> \namespace mom_spherical_harmonics +!! +!! This module contains the subroutines to calculate spherical harmonic transforms (SHT), namely, forward transform +!! of a two-dimensional field into a given number of spherical harmonic modes and its inverse transform. This module +!! is primarily used to but not limited to calculate self-attraction and loading (SAL) term, which is mostly relevant to +!! high frequency motions such as tides. Should other needs arise in the future, this API can be easily modified. +!! Currently, the transforms are for t-cell fields only. +!! +!! This module is stemmed from SAL calculation in Model for Prediction Across Scales (MPAS)-Ocean developed by Los +!! Alamos National Laboratory and University of Michigan (Barton et al. (2022) and Brus et al. (2022)). The algorithm +!! for forward and inverse transforms loosely follows Schaeffer (2013). +!! +!! In forward transform, a two-dimensional physical field can be projected into a series of spherical harmonics. The +!! spherical harmonic coefficient of degree n and order m for a field $f(\theta, \phi)$ is calculated as follows: +!! \f[ +!! f^m_n = \int^{2\pi}_{0}\int^{\pi}_{0}f(\theta,\phi)Y^m_n(\theta,\phi)\sin\theta d\theta d\phi +!! \f] +!! and +!! \f[ +!! Y^m_n(\theta,\phi) = P^m_n(\cos\theta)\exp(im\phi) +!! \f] +!! where $P^m_n(\cos \theta)$ is the normalized associated Legendre polynomial of degree n and order m. $\phi$ is the +!! longitude and $\theta$ is the colatitude. +!! Or, written in the discretized form: +!! \f[ +!! f^m_n = \sum^{Nj}_{0}\sum^{Ni}_{0}f(i,j)Y^m_n(i,j)A(i,j)/r_e^2 +!! \f] +!! where $A$ is the area of the cell and $r_e$ is the radius of the Earth. +!! +!! In inverse transform, the first N degree spherical harmonic coefficients are used to reconstruct a two-dimensional +!! physical field: +!! \f[ +!! f(\theta,\phi) = \sum^N_{n=0}\sum^{n}_{m=-n}f^m_nY^m_n(\theta,\phi) +!! \f] +!! +!! The exponential coefficients are pre-computed and stored in the memory. The associated Legendre polynomials are +!! computed "on-the-fly", using the recurrence relationships to avoid large memory usage and take the advantage of +!! array vectorization. +!! +!! The maximum degree of the spherical harmonics is a runtime parameter and the maximum used by all SHT applications. +!! At the moment, it is only decided by TIDAL_SAL_SHT_DEGREE. +!! +!! The forward transforms involve a global summation. Runtime flag SHT_REPRODUCING_SUM controls whether this is done +!! in a bit-wise reproducing way or not. +!! +!! References: +!! +!! Barton, K.N., Nairita, P., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J., +!! Wirasaet, D., and Schindelegger, M., 2022: Performance of Model for Prediction Across Scales (MPAS) Ocean as a +!! Global Barotropic Tide Model. Journal of Advances in Modeling Earth Systems, in review. +!! +!! Brus, S.R., Barton, K.N., Nairita, P., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., +!! Westerink, J., and Schindelegger, M., 2022: Scalable self attraction and loading calculations for unstructured ocean +!! models. Ocean Modelling, in review. +!! +!! Schaeffer, N., 2013. Efficient spherical harmonic transforms aimed at pseudospectral numerical simulations. +!! Geochemistry, Geophysics, Geosystems, 14(3), pp.751-758. +!! https://doi.org/10.1002/ggge.20071 end module MOM_spherical_harmonics \ No newline at end of file diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index a41fbaa0f4..16ad30d640 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -15,7 +15,7 @@ module MOM_tidal_forcing use MOM_spherical_harmonics, only : spherical_harmonics_init, spherical_harmonics_end, order2index, calc_lmax use MOM_spherical_harmonics, only : spherical_harmonics_forward, spherical_harmonics_inverse use MOM_spherical_harmonics, only : sht_CS -use MOM_load_love_numbers, only : LoveDat +use MOM_load_love_numbers, only : Love_Data implicit none ; private @@ -50,7 +50,7 @@ module MOM_tidal_forcing !! equilibrium tide. Set to false if providing tidal phases !! that have already been shifted by the !! astronomical/equilibrium argument. - logical :: tidal_sal_sht + logical :: tidal_sal_sht !< If true, use online spherical harmonics to calculate SAL real :: sal_scalar !< The constant of proportionality between sea surface !! height (really it should be bottom pressure) anomalies !! and bottom geopotential anomalies [nondim]. @@ -75,14 +75,15 @@ module MOM_tidal_forcing cosphase_prev(:,:,:), & !< The cosine and sine of the phase of the sinphase_prev(:,:,:), & !< amphidromes in the previous tidal solutions. amp_prev(:,:,:) !< The amplitude of the previous tidal solution [Z ~> m]. - type(sht_CS) :: sht - integer :: sal_sht_Nd - real, allocatable :: LoveScaling(:) - real, allocatable :: SnmRe(:), SnmIm(:) + type(sht_CS) :: sht !< Spherical harmonic transforms (SHT) for SAL + integer :: sal_sht_Nd !< Maximum degree for SHT [nodim] + real, allocatable :: Love_Scaling(:) !< Love number for each SHT mode [nodim] + real, allocatable :: Snm_Re(:), & !< Real and imaginary SHT coefficient for SHT SAL + Snm_Im(:) !! [Z ~> m] end type tidal_forcing_CS integer :: id_clock_tides !< CPU clock for tides -integer :: id_clock_SAL !< CPU clock for inline self-attration and loading +integer :: id_clock_SAL !< CPU clock for self-attraction and loading contains @@ -265,7 +266,10 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) character(len=128) :: mesg character(len=200) :: tidal_input_files(4*MAX_CONSTITUENTS) integer :: i, j, c, is, ie, js, je, isd, ied, jsd, jed, nc - integer :: lmax + integer :: lmax ! Total modes of the real spherical harmonics [nondim] + real :: rhoW ! The average density of sea water [R ~> kg m-3]. + real :: rhoE ! The average density of Earth [R ~> kg m-3]. + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec isd = G%isd ; ied = G%ied ; jsd = G%jsd; jed = G%jed @@ -372,7 +376,8 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) fail_if_missing=.true.) call get_param(param_file, mdl, "TIDAL_SAL_SHT", CS%tidal_sal_sht, & - "If true, use inline SAL.", default=.false.) + "If true, use the online spherical harmonics method to calculate "//& + "self-attraction and loading term in tides.", default=.false.) if (nc > MAX_CONSTITUENTS) then write(mesg,'("Increase MAX_CONSTITUENTS in MOM_tidal_forcing.F90 to at least",I3, & @@ -538,12 +543,22 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) "The maximum degree of the spherical harmonics transformation used for "// & "calculating the self-attraction and loading term for tides.", & default=0, do_not_log=.not. CS%tidal_sal_sht) + call get_param(param_file, mdl, "RHO_0", rhoW, & + "The mean ocean density used with BOUSSINESQ true to "//& + "calculate accelerations and the mass for conservation "//& + "properties, or with BOUSSINSEQ false to convert some "//& + "parameters from vertical units of m to kg m-2.", & + units="kg m-3", default=1035.0, scale=US%kg_m3_to_R, do_not_log=.True.) + call get_param(param_file, mdl, "RHO_E", rhoE, & + "The mean solid earth density. This is used for calculating the "// & + "self-attraction and loading term.", units="kg m-3", & + default=5517.0, scale=US%kg_m3_to_R, do_not_log=.not. CS%tidal_sal_sht) lmax = calc_lmax(CS%sal_sht_Nd) - allocate(CS%SnmRe(lmax)); CS%SnmRe = 0.0 - allocate(CS%SnmIm(lmax)); CS%SnmIm = 0.0 + allocate(CS%Snm_Re(lmax)); CS%Snm_Re(:) = 0.0 + allocate(CS%Snm_Im(lmax)); CS%Snm_Im(:) = 0.0 - allocate(CS%LoveScaling(lmax)) - call calc_love_scaling(CS%sal_sht_Nd, CS%LoveScaling) + allocate(CS%Love_Scaling(lmax)); CS%Love_Scaling(:) = 0.0 + call calc_love_scaling(CS%sal_sht_Nd, rhoW, rhoE, CS%Love_Scaling) call spherical_harmonics_init(G, param_file, CS%sht) id_clock_SAL = cpu_clock_id('(Ocean SAL)', grain=CLOCK_ROUTINE) endif @@ -552,41 +567,43 @@ subroutine tidal_forcing_init(Time, G, US, param_file, CS) end subroutine tidal_forcing_init -subroutine calc_love_scaling(nlm, LoveScaling) - integer, intent(in) :: nlm !< Maximum spherical harmonics degree - real, dimension(:), intent(out) :: LoveScaling ! Scaling factors for inverse SHT +!> This subroutine calculates coefficients of the spherical harmonic modes for self-attraction and loading. +!! The algorithm is based on the SAL implementation in MPAS-ocean, which was modified by Kristin Barton from +!! routine written by K. Quinn (March 2010) and modified by M. Schindelegger (May 2017). +subroutine calc_love_scaling(nlm, rhoW, rhoE, Love_Scaling) + integer, intent(in) :: nlm !< Maximum spherical harmonics degree [nondim] + real, intent(in) :: rhoW !< The average density of sea water [R ~> kg m-3] + real, intent(in) :: rhoE !< The average density of Earth [R ~> kg m-3] + real, dimension(:), intent(out) :: Love_Scaling !< Scaling factors for inverse SHT [nondim] ! Local variables - real, parameter :: rhoE = 5517.0 ! Average density of Earth (kg/m^3) - real, parameter :: rhoW = 1035.0 ! Density of water (kg/m^3) - real, dimension(:), allocatable :: HDat, LDat, KDat - real :: H1, L1, K1 + real, dimension(:), allocatable :: HDat, LDat, KDat ! Love numbers converted in CF reference frames + real :: H1, L1, K1 ! Temporary variables to store degree 1 Love numbers + integer :: n_tot ! Size of the stored Love numbers integer :: n, m, l - integer :: n_tot - n_tot = size(LoveDat, dim=2) + n_tot = size(Love_Data, dim=2) if (nlm+1 > n_tot) call MOM_error(FATAL, "MOM_tidal_forcing " // & "calc_love_scaling: maximum spherical harmonics degree is larger than " // & - "the size of the stored Love Number in MOM_load_love_number.") + "the size of the stored Love numbers in MOM_load_love_number.") allocate(HDat(nlm+1), LDat(nlm+1), KDat(nlm+1)) - HDat(:) = LoveDat(2,1:nlm+1) ; LDat(:) = LoveDat(3,1:nlm+1) ; KDat(:) = LoveDat(4,1:nlm+1) + HDat(:) = Love_Data(2,1:nlm+1) ; LDat(:) = Love_Data(3,1:nlm+1) ; KDat(:) = Love_Data(4,1:nlm+1) + ! Convert reference frames from CM to CF if (nlm > 0) then - ! Convert from CM to CF H1 = HDat(2) ; L1 = LDat(2) ; K1 = KDat(2) - HDat(2) = 2.0/3.0*(H1 - L1) - LDat(2) = -1.0/3.0*(H1 - L1) - KDat(2) = -1.0/3.0*H1 - 2.0/3.0*L1 - 1.0 + HDat(2) = ( 2.0 / 3.0) * (H1 - L1) + LDat(2) = (-1.0 / 3.0) * (H1 - L1) + KDat(2) = (-1.0 / 3.0) * H1 - (2.0 / 3.0) * L1 - 1.0 endif - do m=0,nlm - do n=m,nlm - l = order2index(m,nlm) - LoveScaling(l+n-m) = (1.0 + KDat(n+1) - HDat(n+1)) / real(2*n+1) * 3.0 * rhoW / rhoE - enddo - enddo + do m=0,nlm ; do n=m,nlm + l = order2index(m,nlm) + ! Love_Scaling(l+n-m) = (3.0 / real(2*n+1)) * (rhoW / rhoE) * (1.0 + KDat(n+1) - HDat(n+1)) + Love_Scaling(l+n-m) = (1.0 + KDat(n+1) - HDat(n+1)) / real(2*n+1) * 3.0 * rhoW / rhoE + enddo ; enddo end subroutine calc_love_scaling !> This subroutine finds a named variable in a list of files and reads its @@ -657,7 +674,7 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) !! previous call to tidal_forcing_init. ! Local variables - real, dimension(SZI_(G),SZJ_(G)) :: eta_sal !< SAL + real, dimension(SZI_(G),SZJ_(G)) :: eta_sal !< SAL calculated by spherical harmonics real :: now ! The relative time compared with the tidal reference [T ~> s] real :: amp_cosomegat, amp_sinomegat ! The tidal amplitudes times the components of phase [Z ~> m] real :: cosomegat, sinomegat ! The components of the phase [nondim] @@ -715,10 +732,10 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) enddo ; enddo enddo ; endif - if (CS%TIDAL_SAL_SHT) then - eta_sal = 0.0 + if (CS%tidal_sal_sht) then + eta_sal(:,:) = 0.0 call calc_SAL_sht(eta, eta_sal, G, CS) - call pass_var(eta_sal, G%domain) + do j=Jsq,Jeq+1 ; do i=Isq,Ieq+1 eta_tidal(i,j) = eta_tidal(i,j) + eta_sal(i,j) enddo ; enddo @@ -727,32 +744,34 @@ subroutine calc_tidal_forcing(Time, eta, eta_tidal, G, US, CS) end subroutine calc_tidal_forcing +!> This subroutine calculates self-attraction and loading using the spherical harmonics method. subroutine calc_SAL_sht(eta, eta_sal, G, CS) - type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. - real, dimension(SZI_(G),SZJ_(G)), intent(in) :: eta !< The sea surface height anomaly from - !! a time-mean geoid [Z ~> m]. - real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_sal !< The sea surface height anomaly from - !! a time-mean geoid [Z ~> m]. - ! type(sht_CS), intent(in) :: sht - type(tidal_forcing_CS), intent(inout) :: CS !< Tidal forcing control struct - real, allocatable :: LoveScaling(:) + type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. + real, dimension(SZI_(G),SZJ_(G)), intent(in) :: eta !< The sea surface height anomaly from + !! a time-mean geoid [Z ~> m]. + real, dimension(SZI_(G),SZJ_(G)), intent(out) :: eta_sal !< The sea surface height anomaly from + !! self-attraction and loading [Z ~> m]. + type(tidal_forcing_CS), intent(inout) :: CS !< Tidal forcing control struct + ! Local variables integer :: n, m, l call cpu_clock_begin(id_clock_SAL) - call spherical_harmonics_forward(G, CS%sht, eta, CS%SnmRe, CS%SnmIm, CS%sal_sht_Nd) + call spherical_harmonics_forward(G, CS%sht, eta, CS%Snm_Re, CS%Snm_Im, CS%sal_sht_Nd) - ! Multiply scaling to each mode + ! Multiply scaling factors to each mode do m = 0,CS%sal_sht_Nd - l = order2index(m,CS%sal_sht_Nd) + l = order2index(m, CS%sal_sht_Nd) do n = m,CS%sal_sht_Nd - CS%SnmRe(l+n-m) = CS%SnmRe(l+n-m)*CS%LoveScaling(l+n-m) - CS%SnmIm(l+n-m) = CS%SnmIm(l+n-m)*CS%LoveScaling(l+n-m) + CS%Snm_Re(l+n-m) = CS%Snm_Re(l+n-m) * CS%Love_Scaling(l+n-m) + CS%Snm_Im(l+n-m) = CS%Snm_Im(l+n-m) * CS%Love_Scaling(l+n-m) enddo enddo - call spherical_harmonics_inverse(G, CS%sht, CS%SnmRe, CS%SnmIm, eta_sal, CS%sal_sht_Nd) + call spherical_harmonics_inverse(G, CS%sht, CS%Snm_Re, CS%Snm_Im, eta_sal, CS%sal_sht_Nd) + + call pass_var(eta_sal, G%domain) call cpu_clock_end(id_clock_SAL) end subroutine calc_SAL_sht @@ -774,9 +793,9 @@ subroutine tidal_forcing_end(CS) if (allocated(CS%amp_prev)) deallocate(CS%amp_prev) if (CS%tidal_sal_sht) then - if (allocated(CS%LoveScaling)) deallocate(CS%LoveScaling) - if (allocated(CS%SnmRe)) deallocate(CS%SnmRe) - if (allocated(CS%SnmIm)) deallocate(CS%SnmIm) + if (allocated(CS%Love_Scaling)) deallocate(CS%Love_Scaling) + if (allocated(CS%Snm_Re)) deallocate(CS%Snm_Re) + if (allocated(CS%Snm_Im)) deallocate(CS%Snm_Im) call spherical_harmonics_end(CS%sht) endif end subroutine tidal_forcing_end @@ -809,5 +828,18 @@ end subroutine tidal_forcing_end !! details, see Arbic et al., 2004, DSR II). With TIDAL_SAL_FROM_FILE !! or USE_PREVIOUS_TIDES,a list of input files must be provided to !! describe each constituent's properties from a previous solution. - +!! +!! This module also contains a method to calculate self-attraction +!! and loading using spherical harmonic transforms. The algorithm is +!! based on SAL calculation in Model for Prediction Across Scales +!! (MPAS)-Ocean developed by Los Alamos National Laboratory and +!! University of Michigan (Barton et al. (2022) and Brus et al. (2022)). +!! +!! Barton, K.N., Nairita, P., Brus, S.R., Petersen, M.R., Arbic, B.K., Engwirda, D., Roberts, A.F., Westerink, J., +!! Wirasaet, D., and Schindelegger, M., 2022: Performance of Model for Prediction Across Scales (MPAS) Ocean as a +!! Global Barotropic Tide Model. Journal of Advances in Modeling Earth Systems, in review. +!! +!! Brus, S.R., Barton, K.N., Nairita, P., Roberts, A.F., Engwirda, D., Petersen, M.R., Arbic, B.K., Wirasaet, D., +!! Westerink, J., and Schindelegger, M., 2022: Scalable self attraction and loading calculations for unstructured ocean +!! models. Ocean Modelling, in review. end module MOM_tidal_forcing From a907bfd108981364fcf000d05e8fc22bd6c48553 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 28 Sep 2022 15:45:41 -0400 Subject: [PATCH 57/68] Small modification of spherical harmonic SAL * Love number scaling coefficients multiplication is re-ordered to avoid ambiguity and to follow MOM6 style. * Coefficients for Pmm is simplified. Both changes introduce bit-wise level answer change to previous SHT SAL related unpublished commits. * Patches for Doxygen --- .../lateral/MOM_load_love_numbers.F90 | 2 +- .../lateral/MOM_spherical_harmonics.F90 | 42 +++++++++---------- .../lateral/MOM_tidal_forcing.F90 | 5 +-- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/parameterizations/lateral/MOM_load_love_numbers.F90 b/src/parameterizations/lateral/MOM_load_love_numbers.F90 index 865c65edb7..84819b5915 100644 --- a/src/parameterizations/lateral/MOM_load_love_numbers.F90 +++ b/src/parameterizations/lateral/MOM_load_love_numbers.F90 @@ -5,7 +5,7 @@ module MOM_load_love_numbers public Love_Data -integer, parameter :: lmax = 1440 +integer, parameter :: lmax = 1440 !< Maximum degree of the stored Love numbers real, dimension(4, lmax+1), parameter :: & Love_Data = & reshape((/ 0.0, 0.0000000000, 0.0000000000 , -1.0000000000 , & diff --git a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 index e2ef5b37b3..54b441fa8b 100644 --- a/src/parameterizations/lateral/MOM_spherical_harmonics.F90 +++ b/src/parameterizations/lateral/MOM_spherical_harmonics.F90 @@ -23,12 +23,14 @@ module MOM_spherical_harmonics !! [lmax=(ndegree+1)*(ndegree+2)/2] [nodim]. real, allocatable :: cos_clatT(:,:) !< Precomputed cosine of colatitude at the t-cells [nondim]. real, allocatable :: Pmm(:,:,:) !< Precomputed associated Legendre polynomials (m=n) at the t-cells [nondim]. - real, allocatable :: cos_lonT(:,:,:), sin_lonT(:,:,:) !< Precomputed exponential factors at the t-cells [nondim]. - real, allocatable :: cos_lonT_wtd(:,:,:), & !< Precomputed exponential factors at the t-cells weighted by a - sin_lonT_wtd(:,:,:) !! nondimensionalized cell area [nondim] - real, allocatable :: a_recur(:,:), b_recur(:,:) !< Precomputed recurrence coefficients [nondim]. - real, allocatable :: Snm_Re_raw(:,:,:), & !< 3D array to store un-summed SHT coefficients - Snm_Im_raw(:,:,:) !! at the t-cells for reproducing sums [same as input variable] + real, allocatable :: cos_lonT(:,:,:), & !< Precomputed cosine factors at the t-cells [nondim]. + sin_lonT(:,:,:) !< Precomputed sine factors at the t-cells [nondim]. + real, allocatable :: cos_lonT_wtd(:,:,:), & !< Precomputed area-weighted cosine factors at the t-cells [nondim] + sin_lonT_wtd(:,:,:) !< Precomputed area-weighted sine factors at the t-cells [nondim] + real, allocatable :: a_recur(:,:), & !< Precomputed recurrence coefficients a [nondim]. + b_recur(:,:) !< Precomputed recurrence coefficients b [nondim]. + real, allocatable :: Snm_Re_raw(:,:,:), & !< Array to store un-summed SHT coefficients + Snm_Im_raw(:,:,:) !< at the t-cells for reproducing sums [same as input variable] logical :: reprod_sum !< True if use reproducible global sums end type sht_CS @@ -45,8 +47,8 @@ subroutine spherical_harmonics_forward(G, CS, var, Snm_Re, Snm_Im, Nd) type(sht_CS), intent(inout) :: CS !< Control structure for SHT real, dimension(SZI_(G),SZJ_(G)), & intent(in) :: var !< Input 2-D variable [] - real, intent(out) :: Snm_Re(:), & !< Output real and imaginary SHT coefficients - Snm_Im(:) !! [same as input variable] + real, intent(out) :: Snm_Re(:) !< SHT coefficients for the real modes (cosine) + real, intent(out) :: Snm_Im(:) !< SHT coefficients for the imaginary modes (sine) integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics !! overriding ndegree in the CS [nondim] ! local variables @@ -141,8 +143,8 @@ end subroutine spherical_harmonics_forward subroutine spherical_harmonics_inverse(G, CS, Snm_Re, Snm_Im, var, Nd) type(ocean_grid_type), intent(in) :: G !< The ocean's grid structure. type(sht_CS), intent(in) :: CS !< Control structure for SHT - real, intent(in) :: Snm_Re(:), & !< Real and imaginary SHT coefficients with - Snm_Im(:) !! any scaling factors such as Love numbers [] + real, intent(in) :: Snm_Re(:) !< SHT coefficients for the real modes (cosine) + real, intent(in) :: Snm_Im(:) !< SHT coefficients for the imaginary modes (sine) real, dimension(SZI_(G),SZJ_(G)), & intent(out) :: var !< Output 2-D variable [] integer, optional, intent(in) :: Nd !< Maximum degree of the spherical harmonics @@ -269,17 +271,11 @@ subroutine spherical_harmonics_init(G, param_file, CS) ! Calculate the diagonal elements of the associated Legendre polynomials (n=m) allocate(CS%Pmm(is:ie,js:je,m+1)); CS%Pmm(:,:,:) = 0.0 do m=0,CS%ndegree - ! Pmm_coef = 1.0/(4.0*PI) - ! do k=1,m ; Pmm_coef = Pmm_coef * real(2*k+1)/real(2*k); enddo - ! Pmm_coef = sqrt(Pmm_coef) - ! do j=js,je ; do i=is,ie - ! CS%Pmm(i,j,m+1) = Pmm_coef * sin_clatT(i,j)**m - ! enddo ; enddo + Pmm_coef = 1.0/(4.0*PI) + do k=1,m ; Pmm_coef = Pmm_coef * (real(2*k+1) / real(2*k)); enddo + Pmm_coef = sqrt(Pmm_coef) do j=js,je ; do i=is,ie - CS%Pmm(i,j,m+1) = sqrt(1.0/(4.0*PI)) * sin_clatT(i,j)**m - do k = 1, m - CS%Pmm(i,j,m+1) = CS%Pmm(i,j,m+1) * sqrt(real(2*k+1)/real(2*k)) - enddo + CS%Pmm(i,j,m+1) = Pmm_coef * (sin_clatT(i,j)**m) enddo ; enddo enddo @@ -338,7 +334,7 @@ end function order2index !! for forward and inverse transforms loosely follows Schaeffer (2013). !! !! In forward transform, a two-dimensional physical field can be projected into a series of spherical harmonics. The -!! spherical harmonic coefficient of degree n and order m for a field $f(\theta, \phi)$ is calculated as follows: +!! spherical harmonic coefficient of degree n and order m for a field \f$f(\theta, \phi)\f$ is calculated as follows: !! \f[ !! f^m_n = \int^{2\pi}_{0}\int^{\pi}_{0}f(\theta,\phi)Y^m_n(\theta,\phi)\sin\theta d\theta d\phi !! \f] @@ -346,8 +342,8 @@ end function order2index !! \f[ !! Y^m_n(\theta,\phi) = P^m_n(\cos\theta)\exp(im\phi) !! \f] -!! where $P^m_n(\cos \theta)$ is the normalized associated Legendre polynomial of degree n and order m. $\phi$ is the -!! longitude and $\theta$ is the colatitude. +!! where \f$P^m_n(\cos \theta)\f$ is the normalized associated Legendre polynomial of degree n and order m. \f$\phi\f$ +!! is the longitude and \f$\theta\f$ is the colatitude. !! Or, written in the discretized form: !! \f[ !! f^m_n = \sum^{Nj}_{0}\sum^{Ni}_{0}f(i,j)Y^m_n(i,j)A(i,j)/r_e^2 diff --git a/src/parameterizations/lateral/MOM_tidal_forcing.F90 b/src/parameterizations/lateral/MOM_tidal_forcing.F90 index 16ad30d640..dcf12f915f 100644 --- a/src/parameterizations/lateral/MOM_tidal_forcing.F90 +++ b/src/parameterizations/lateral/MOM_tidal_forcing.F90 @@ -79,7 +79,7 @@ module MOM_tidal_forcing integer :: sal_sht_Nd !< Maximum degree for SHT [nodim] real, allocatable :: Love_Scaling(:) !< Love number for each SHT mode [nodim] real, allocatable :: Snm_Re(:), & !< Real and imaginary SHT coefficient for SHT SAL - Snm_Im(:) !! [Z ~> m] + Snm_Im(:) !< [Z ~> m] end type tidal_forcing_CS integer :: id_clock_tides !< CPU clock for tides @@ -601,8 +601,7 @@ subroutine calc_love_scaling(nlm, rhoW, rhoE, Love_Scaling) do m=0,nlm ; do n=m,nlm l = order2index(m,nlm) - ! Love_Scaling(l+n-m) = (3.0 / real(2*n+1)) * (rhoW / rhoE) * (1.0 + KDat(n+1) - HDat(n+1)) - Love_Scaling(l+n-m) = (1.0 + KDat(n+1) - HDat(n+1)) / real(2*n+1) * 3.0 * rhoW / rhoE + Love_Scaling(l+n-m) = (3.0 / real(2*n+1)) * (rhoW / rhoE) * (1.0 + KDat(n+1) - HDat(n+1)) enddo ; enddo end subroutine calc_love_scaling From 32c2af3aa7ce5af046ce63e9851c7ef6f46f9109 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Tue, 4 Oct 2022 09:40:37 -0400 Subject: [PATCH 58/68] +Split remap_all_state_vars into 2 routines Started the refactoring of code related to ALE_main, including: - Split remap_all_state_vars into remap_tracers and remap_velocities - Created the new public subroutine pre_ALE_diagnostics, and call it from step_MOM - Eliminate the unused argument conv_adjust to regridding_main - Eliminated check_grid, and moved these tests into regridding_main - Eliminate check_remapping_grid, and replace it with calls directly to check_grid_column inside of remapping_main All answers are bitwise identical, but there are new public interfaces and changes to the arguments of other public interfaces. --- src/ALE/MOM_ALE.F90 | 258 ++++++++++-------- src/ALE/MOM_regridding.F90 | 45 +-- src/core/MOM.F90 | 11 +- .../MOM_state_initialization.F90 | 2 +- 4 files changed, 162 insertions(+), 154 deletions(-) diff --git a/src/ALE/MOM_ALE.F90 b/src/ALE/MOM_ALE.F90 index ca3b9d54de..ffe9d55d81 100644 --- a/src/ALE/MOM_ALE.F90 +++ b/src/ALE/MOM_ALE.F90 @@ -10,7 +10,7 @@ module MOM_ALE ! This file is part of MOM6. See LICENSE.md for the license. -use MOM_debugging, only : check_column_integrals, hchksum, uvchksum +use MOM_debugging, only : check_column_integrals use MOM_diag_mediator, only : register_diag_field, post_data, diag_ctrl use MOM_diag_mediator, only : time_type, diag_update_remap_grids use MOM_diag_vkernels, only : interpolate_column, reintegrate_column @@ -140,6 +140,7 @@ module MOM_ALE public ALE_updateVerticalGridType public ALE_initThicknessToCoord public ALE_update_regrid_weights +public pre_ALE_diagnostics public ALE_remap_init_conds public ALE_register_diags @@ -392,6 +393,34 @@ subroutine ALE_end(CS) end subroutine ALE_end +!> Save any diagnostics of the state before ALE remapping. These diagnostics are +!! mostly used for debugging. +subroutine pre_ALE_diagnostics(G, GV, US, h, u, v, tv, CS) + type(ocean_grid_type), intent(in) :: G !< Ocean grid informations + type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Current 3D grid obtained after the + !! last time step [H ~> m or kg m-2] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: u !< Zonal velocity field [L T-1 ~> m s-1] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: v !< Meridional velocity field [L T-1 ~> m s-1] + type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure + type(ALE_CS), pointer :: CS !< Regridding parameters and options + + ! Local variables + real :: eta_preale(SZI_(G),SZJ_(G),SZK_(GV)+1) ! Interface heights before remapping [Z ~> m] + + if (CS%id_u_preale > 0) call post_data(CS%id_u_preale, u, CS%diag) + if (CS%id_v_preale > 0) call post_data(CS%id_v_preale, v, CS%diag) + if (CS%id_h_preale > 0) call post_data(CS%id_h_preale, h, CS%diag) + if (CS%id_T_preale > 0) call post_data(CS%id_T_preale, tv%T, CS%diag) + if (CS%id_S_preale > 0) call post_data(CS%id_S_preale, tv%S, CS%diag) + if (CS%id_e_preale > 0) then + call find_eta(h, tv, G, GV, US, eta_preale, dZref=G%Z_ref) + call post_data(CS%id_e_preale, eta_preale, CS%diag) + endif + +end subroutine pre_ALE_diagnostics + !> Takes care of (1) building a new grid and (2) remapping all variables between !! the old grid and the new grid. The creation of the new grid can be based !! on z coordinates, target interface densities, sigma coordinates or any @@ -409,10 +438,9 @@ subroutine ALE_main( G, GV, US, h, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h) type(ALE_CS), pointer :: CS !< Regridding parameters and options type(ocean_OBC_type), pointer :: OBC !< Open boundary structure real, optional, intent(in) :: dt !< Time step between calls to ALE_main [T ~> s] - real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: frac_shelf_h !< Fractional ice shelf coverage [nondim] + real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: frac_shelf_h !< Fractional ice shelf coverage [nondim] ! Local variables real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: dzRegrid ! The change in grid interface positions - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: eta_preale real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_new ! New 3D grid obtained after last time step [H ~> m or kg m-2] logical :: PCM_cell(SZI_(G),SZJ_(G),SZK_(GV)) !< If true, PCM remapping should be used in a cell. integer :: ntr, i, j, k, isc, iec, jsc, jec, nk @@ -421,22 +449,6 @@ subroutine ALE_main( G, GV, US, h, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h) if (CS%show_call_tree) call callTree_enter("ALE_main(), MOM_ALE.F90") - ! These diagnostics of the state before ALE is applied are mostly used for debugging. - if (CS%id_u_preale > 0) call post_data(CS%id_u_preale, u, CS%diag) - if (CS%id_v_preale > 0) call post_data(CS%id_v_preale, v, CS%diag) - if (CS%id_h_preale > 0) call post_data(CS%id_h_preale, h, CS%diag) - if (CS%id_T_preale > 0) call post_data(CS%id_T_preale, tv%T, CS%diag) - if (CS%id_S_preale > 0) call post_data(CS%id_S_preale, tv%S, CS%diag) - if (CS%id_e_preale > 0) then - call find_eta(h, tv, G, GV, US, eta_preale, dZref=G%Z_ref) - call post_data(CS%id_e_preale, eta_preale, CS%diag) - endif - - if (present(dt)) then - call ALE_update_regrid_weights( dt, CS ) - endif - dzRegrid(:,:,:) = 0.0 - ! If necessary, do some preparatory work to clean up the model state before regridding. ! This adjusts the input thicknesses prior to remapping, based on the verical coordinate. @@ -448,22 +460,21 @@ subroutine ALE_main( G, GV, US, h, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h) ! Build new grid. The new grid is stored in h_new. The old grid is h. ! Both are needed for the subsequent remapping of variables. - call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid, conv_adjust=.false., & + dzRegrid(:,:,:) = 0.0 + call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid, & frac_shelf_h=frac_shelf_h, PCM_cell=PCM_cell) - call check_grid( G, GV, h, 0. ) - if (CS%show_call_tree) call callTree_waypoint("new grid generated (ALE_main)") ! The presence of dt is used for expediency to distinguish whether ALE_main is being called during init - ! or in the main loop. Tendency diagnostics in remap_all_state_vars also rely on this logic. + ! or in the main loop. Tendency diagnostics in remap_tracers also rely on this logic. if (present(dt)) then call diag_update_remap_grids(CS%diag) endif ! Remap all variables from old grid h onto new grid h_new - call remap_all_state_vars( CS, G, GV, h, h_new, Reg, OBC, dzRegrid, u, v, & - CS%show_call_tree, dt, PCM_cell=PCM_cell ) + call remap_tracers(CS, G, GV, h, h_new, Reg, CS%show_call_tree, dt, PCM_cell=PCM_cell) + call remap_velocities(CS, G, GV, h, h_new, u, v, OBC, dzRegrid, debug=CS%show_call_tree, dt=dt) if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_main)") @@ -474,13 +485,6 @@ subroutine ALE_main( G, GV, US, h, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h) h(i,j,k) = h_new(i,j,k) enddo ; enddo ; enddo - if (CS%debug) then - call hchksum(h, "Post-ALE_main h", G%HI, haloshift=0, scale=GV%H_to_m) - call hchksum(tv%T, "Post-ALE_main T", G%HI, haloshift=0, scale=US%C_to_degC) - call hchksum(tv%S, "Post-ALE_main S", G%HI, haloshift=0, scale=US%S_to_ppt) - call uvchksum("Post-ALE_main [uv]", u, v, G%HI, haloshift=0, scale=US%L_T_to_m_s) - endif - if (CS%id_dzRegrid>0 .and. present(dt)) call post_data(CS%id_dzRegrid, dzRegrid, CS%diag) if (CS%show_call_tree) call callTree_leave("ALE_main()") @@ -524,15 +528,13 @@ subroutine ALE_main_offline( G, GV, h, tv, Reg, CS, OBC, dt) ! Build new grid. The new grid is stored in h_new. The old grid is h. ! Both are needed for the subsequent remapping of variables. - call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid, conv_adjust=.false. ) - - call check_grid( G, GV, h, 0. ) + call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid) if (CS%show_call_tree) call callTree_waypoint("new grid generated (ALE_main)") ! Remap all variables from old grid h onto new grid h_new - call remap_all_state_vars( CS, G, GV, h, h_new, Reg, OBC, debug=CS%show_call_tree, dt=dt ) + call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree, dt=dt) if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_main)") @@ -580,12 +582,11 @@ subroutine ALE_offline_inputs(CS, G, GV, h, tv, Reg, uhtr, vhtr, Kd, debug, OBC) ! Build new grid from the Zstar state onto the requested vertical coordinate. The new grid is stored ! in h_new. The old grid is h. Both are needed for the subsequent remapping of variables. Convective ! adjustment right now is not used because it is unclear what to do with vanished layers - call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid, conv_adjust=.false. ) - call check_grid( G, GV, h_new, 0. ) + call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid) if (CS%show_call_tree) call callTree_waypoint("new grid generated (ALE_offline_inputs)") ! Remap all variables from old grid h onto new grid h_new - call remap_all_state_vars( CS, G, GV, h, h_new, Reg, OBC, debug=CS%show_call_tree ) + call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree) if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_inputs)") ! Reintegrate mass transports from Zstar to the offline vertical coordinate @@ -658,15 +659,13 @@ subroutine ALE_offline_tracer_final( G, GV, h, tv, h_target, Reg, CS, OBC) ntr = 0 ; if (associated(Reg)) ntr = Reg%ntr call hybgen_unmix(G, GV, G%US, CS%hybgen_unmixCS, tv, Reg, ntr, h) endif - call regridding_main( CS%remapCS, CS%regridCS, G, GV, h_target, tv, h_new, dzRegrid, conv_adjust=.false. ) - call check_grid( G, GV, h_target, 0. ) - + call regridding_main( CS%remapCS, CS%regridCS, G, GV, h_target, tv, h_new, dzRegrid) if (CS%show_call_tree) call callTree_waypoint("Source and target grids checked (ALE_offline_tracer_final)") ! Remap all variables from old grid h onto new grid h_new - call remap_all_state_vars( CS, G, GV, h, h_new, Reg, OBC, debug=CS%show_call_tree ) + call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree) if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_offline_tracer_final)") @@ -681,33 +680,6 @@ subroutine ALE_offline_tracer_final( G, GV, h, tv, h_target, Reg, CS, OBC) if (CS%show_call_tree) call callTree_leave("ALE_offline_tracer_final()") end subroutine ALE_offline_tracer_final -!> Check grid for negative thicknesses -subroutine check_grid( G, GV, h, threshold ) - type(ocean_grid_type), intent(in) :: G !< Ocean grid structure - type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Current 3D grid obtained after the - !! last time step [H ~> m or kg m-2] - real, intent(in) :: threshold !< Value below which to flag issues, - !! [H ~> m or kg m-2] - ! Local variables - integer :: i, j - - do j = G%jsc,G%jec ; do i = G%isc,G%iec - if (G%mask2dT(i,j)>0.) then - if (minval(h(i,j,:)) < threshold) then - write(0,*) 'check_grid: i,j=',i,j,'h(i,j,:)=',h(i,j,:) - if (threshold <= 0.) then - call MOM_error(FATAL,"MOM_ALE, check_grid: negative thickness encountered.") - else - call MOM_error(FATAL,"MOM_ALE, check_grid: too tiny thickness encountered.") - endif - endif - endif - enddo ; enddo - - -end subroutine check_grid - !> For a state-based coordinate, accelerate the process of regridding by !! repeatedly applying the grid calculation algorithm @@ -782,7 +754,7 @@ subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, d ! generate new grid if (CS%do_conv_adj) call convective_adjustment(G, GV, h_loc, tv_local) - call regridding_main(CS%remapCS, CS%regridCS, G, GV, h_loc, tv_local, h, dzInterface, conv_adjust=.false.) + call regridding_main(CS%remapCS, CS%regridCS, G, GV, h_loc, tv_local, h, dzInterface) dzIntTotal(:,:,:) = dzIntTotal(:,:,:) + dzInterface(:,:,:) ! remap from original grid onto new grid @@ -798,20 +770,18 @@ subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, d enddo ! remap all state variables (including those that weren't needed for regridding) - call remap_all_state_vars(CS, G, GV, h_orig, h, Reg, OBC, dzIntTotal, u, v) + call remap_tracers(CS, G, GV, h_orig, h, Reg) + call remap_velocities(CS, G, GV, h_orig, h, u, v, OBC, dzIntTotal) ! save total dzregrid for diags if needed? if (present(dzRegrid)) dzRegrid(:,:,:) = dzIntTotal(:,:,:) end subroutine ALE_regrid_accelerated -!> This routine takes care of remapping all variable between the old and the -!! new grids. When velocity components need to be remapped, thicknesses at -!! velocity points are taken to be arithmetic averages of tracer thicknesses. -!! This routine is called during initialization of the model at time=0, to +!> This routine takes care of remapping all tracer variables between the old and the +!! new grids. This routine is called during initialization of the model at time=0, to !! remap initial conditions to the model grid. It is also called during a !! time step to update the state. -subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & - dzInterface, u, v, debug, dt, PCM_cell) +subroutine remap_tracers(CS, G, GV, h_old, h_new, Reg, debug, dt, PCM_cell) type(ALE_CS), intent(in) :: CS !< ALE control structure type(ocean_grid_type), intent(in) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure @@ -820,25 +790,13 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h_new !< Thickness of destination grid !! [H ~> m or kg m-2] type(tracer_registry_type), pointer :: Reg !< Tracer registry structure - type(ocean_OBC_type), pointer :: OBC !< Open boundary structure - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), & - optional, intent(in) :: dzInterface !< Change in interface position - !! [H ~> m or kg m-2] - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & - optional, intent(inout) :: u !< Zonal velocity [L T-1 ~> m s-1] - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), & - optional, intent(inout) :: v !< Meridional velocity [L T-1 ~> m s-1] logical, optional, intent(in) :: debug !< If true, show the call tree real, optional, intent(in) :: dt !< time step for diagnostics [T ~> s] logical, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & optional, intent(in) :: PCM_cell !< Use PCM remapping in cells where true ! Local variables - real, dimension(SZI_(G),SZJ_(G)) :: h_tot ! The vertically summed thicknesses [H ~> m or kg m-2] - real :: h_mask_vel ! A depth below which the thicknesses at a velocity point are masked out [H ~> m or kg m-2] - real, dimension(GV%ke+1) :: dz ! The change in interface heights interpolated to - ! a velocity point [H ~> m or kg m-2] - real :: tr_column(GV%ke) ! A column of updated tracer concentrations + real :: tr_column(GV%ke) ! A column of updated tracer concentrations [CU ~> Conc] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: work_conc ! The rate of change of concentrations [Conc T-1 ~> Conc s-1] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: work_cont ! The rate of change of cell-integrated tracer ! content [Conc H T-1 ~> Conc m s-1 or Conc kg m-2 s-1] or @@ -847,10 +805,6 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & ! content [Conc H T-1 ~> Conc m s-1 or Conc kg m-2 s-1] logical :: PCM(GV%ke) ! If true, do PCM remapping from a cell. real :: Idt ! The inverse of the timestep [T-1 ~> s-1] - real :: u_src(GV%ke) ! A column of u-velocities on the source grid [L T-1 ~> m s-1] - real :: u_tgt(GV%ke) ! A column of u-velocities on the target grid [L T-1 ~> m s-1] - real :: v_src(GV%ke) ! A column of v-velocities on the source grid [L T-1 ~> m s-1] - real :: v_tgt(GV%ke) ! A column of v-velocities on the target grid [L T-1 ~> m s-1] real :: h1(GV%ke) ! A column of source grid layer thicknesses [H ~> m or kg m-2] real :: h2(GV%ke) ! A column of target grid layer thicknesses [H ~> m or kg m-2] real :: h_neglect, h_neglect_edge ! Tiny thicknesses used in remapping [H ~> m or kg m-2] @@ -861,13 +815,6 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & show_call_tree = .false. if (present(debug)) show_call_tree = debug - ! If remap_uv_using_old_alg is .true. and u or v is requested, then we must have dzInterface. Otherwise, - ! u and v can be remapped without dzInterface - if ( .not. present(dzInterface) .and. (CS%remap_uv_using_old_alg .and. (present(u) .or. present(v))) ) then - call MOM_error(FATAL, "remap_all_state_vars: dzInterface must be present if using old algorithm "// & - "and u/v are to be remapped") - endif - if (CS%answer_date >= 20190101) then h_neglect = GV%H_subroundoff ; h_neglect_edge = GV%H_subroundoff elseif (GV%Boussinesq) then @@ -876,7 +823,7 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & h_neglect = GV%kg_m2_to_H*1.0e-30 ; h_neglect_edge = GV%kg_m2_to_H*1.0e-10 endif - if (show_call_tree) call callTree_enter("remap_all_state_vars(), MOM_ALE.F90") + if (show_call_tree) call callTree_enter("remap_tracers(), MOM_ALE.F90") nz = GV%ke @@ -890,7 +837,7 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & ! Remap all registered tracers, including temperature and salinity. if (ntr>0) then - if (show_call_tree) call callTree_waypoint("remapping tracers (remap_all_state_vars)") + if (show_call_tree) call callTree_waypoint("remapping tracers (remap_tracers)") !$OMP parallel do default(shared) private(h1,h2,tr_column,Tr,PCM,work_conc,work_cont,work_2d) do m=1,ntr ! For each tracer Tr => Reg%Tr(m) @@ -938,6 +885,8 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & if (Tr%id_remap_cont > 0) then call post_data(Tr%id_remap_cont, work_cont, CS%diag) endif + nz = GV%ke + if (Tr%id_remap_cont_2d > 0) then do j = G%jsc,G%jec ; do i = G%isc,G%iec work_2d(i,j) = 0.0 @@ -952,9 +901,79 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & endif ! endif for ntr > 0 - if (show_call_tree) call callTree_waypoint("tracers remapped (remap_all_state_vars)") - if (CS%partial_cell_vel_remap .and. (present(u) .or. present(v)) ) then + if (CS%id_vert_remap_h > 0) call post_data(CS%id_vert_remap_h, h_old, CS%diag) + if ((CS%id_vert_remap_h_tendency > 0) .and. present(dt)) then + do k = 1, nz ; do j = G%jsc,G%jec ; do i = G%isc,G%iec + work_cont(i,j,k) = (h_new(i,j,k) - h_old(i,j,k))*Idt + enddo ; enddo ; enddo + call post_data(CS%id_vert_remap_h_tendency, work_cont, CS%diag) + endif + + if (show_call_tree) call callTree_leave("remap_tracers(), MOM_ALE.F90") + +end subroutine remap_tracers + +!> This routine remaps velocity components between the old and the new grids, +!! with thicknesses at velocity points taken to be arithmetic averages of tracer thicknesses. +!! This routine may be called during initialization of the model at time=0, to +!! remap initial conditions to the model grid. It is also called during a +!! time step to update the state. +subroutine remap_velocities(CS, G, GV, h_old, h_new, u, v, OBC, dzInterface, debug, dt) + type(ALE_CS), intent(in) :: CS !< ALE control structure + type(ocean_grid_type), intent(in) :: G !< Ocean grid structure + type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h_old !< Thickness of source grid + !! [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h_new !< Thickness of destination grid + !! [H ~> m or kg m-2] + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & + intent(inout) :: u !< Zonal velocity [L T-1 ~> m s-1] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), & + intent(inout) :: v !< Meridional velocity [L T-1 ~> m s-1] + type(ocean_OBC_type), pointer :: OBC !< Open boundary structure + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), & + optional, intent(in) :: dzInterface !< Change in interface position + !! [H ~> m or kg m-2] + logical, optional, intent(in) :: debug !< If true, show the call tree + real, optional, intent(in) :: dt !< time step for diagnostics [T ~> s] + + ! Local variables + real, dimension(SZI_(G),SZJ_(G)) :: h_tot ! The vertically summed thicknesses [H ~> m or kg m-2] + real :: h_mask_vel ! A depth below which the thicknesses at a velocity point are masked out [H ~> m or kg m-2] + real, dimension(GV%ke+1) :: dz ! The change in interface heights interpolated to + ! a velocity point [H ~> m or kg m-2] + logical :: PCM(GV%ke) ! If true, do PCM remapping from a cell. + real :: u_src(GV%ke) ! A column of u-velocities on the source grid [L T-1 ~> m s-1] + real :: u_tgt(GV%ke) ! A column of u-velocities on the target grid [L T-1 ~> m s-1] + real :: v_src(GV%ke) ! A column of v-velocities on the source grid [L T-1 ~> m s-1] + real :: v_tgt(GV%ke) ! A column of v-velocities on the target grid [L T-1 ~> m s-1] + real :: h1(GV%ke) ! A column of source grid layer thicknesses [H ~> m or kg m-2] + real :: h2(GV%ke) ! A column of target grid layer thicknesses [H ~> m or kg m-2] + real :: h_neglect, h_neglect_edge ! Tiny thicknesses used in remapping [H ~> m or kg m-2] + logical :: show_call_tree + integer :: i, j, k, nz + + show_call_tree = .false. + if (present(debug)) show_call_tree = debug + if (show_call_tree) call callTree_enter("remap_velocities()") + + ! If remap_uv_using_old_alg is .true. and u or v is requested, then we must have dzInterface. Otherwise, + ! u and v can be remapped without dzInterface + if (CS%remap_uv_using_old_alg .and. .not.present(dzInterface) ) call MOM_error(FATAL, & + "remap_velocities: dzInterface must be present if using old algorithm.") + + if (CS%answer_date >= 20190101) then + h_neglect = GV%H_subroundoff ; h_neglect_edge = GV%H_subroundoff + elseif (GV%Boussinesq) then + h_neglect = GV%m_to_H*1.0e-30 ; h_neglect_edge = GV%m_to_H*1.0e-10 + else + h_neglect = GV%kg_m2_to_H*1.0e-30 ; h_neglect_edge = GV%kg_m2_to_H*1.0e-10 + endif + + nz = GV%ke + + if (CS%partial_cell_vel_remap) then h_tot(:,:) = 0.0 do k=1,GV%ke ; do j=G%jsc-1,G%jec+1 ; do i=G%isc-1,G%iec+1 h_tot(i,j) = h_tot(i,j) + h_old(i,j,k) @@ -962,13 +981,12 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & endif ! Remap u velocity component - if ( present(u) ) then + if ( .true. ) then !$OMP parallel do default(shared) private(h1,h2,dz,u_src,h_mask_vel,u_tgt) do j=G%jsc,G%jec ; do I=G%IscB,G%IecB ; if (G%mask2dCu(I,j)>0.) then ! Build the start and final grids do k=1,nz - u_src(k) = u(I,j,k) h1(k) = 0.5*(h_old(i,j,k) + h_old(i+1,j,k)) h2(k) = 0.5*(h_new(i,j,k) + h_new(i+1,j,k)) enddo @@ -994,6 +1012,9 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & endif ; endif ! --- Remap u profiles from the source vertical grid onto the new target grid. + do k=1,nz + u_src(k) = u(I,j,k) + enddo call remapping_core_h(CS%vel_remapCS, nz, h1, u_src, nz, h2, u_tgt, & h_neglect, h_neglect_edge) @@ -1007,15 +1028,14 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & endif ; enddo ; enddo endif - if (show_call_tree) call callTree_waypoint("u remapped (remap_all_state_vars)") + if (show_call_tree) call callTree_waypoint("u remapped (remap_velocities)") ! Remap v velocity component - if ( present(v) ) then + if ( .true. ) then !$OMP parallel do default(shared) private(h1,h2,v_src,dz,h_mask_vel,v_tgt) do J=G%JscB,G%JecB ; do i=G%isc,G%iec ; if (G%mask2dCv(i,J)>0.) then ! Build the start and final grids do k=1,nz - v_src(k) = v(i,J,k) h1(k) = 0.5*(h_old(i,j,k) + h_old(i,j+1,k)) h2(k) = 0.5*(h_new(i,j,k) + h_new(i,j+1,k)) enddo @@ -1039,6 +1059,9 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & endif ; endif ! --- Remap v profiles from the source vertical grid onto the new target grid. + do k=1,nz + v_src(k) = v(i,J,k) + enddo call remapping_core_h(CS%vel_remapCS, nz, h1, v_src, nz, h2, v_tgt, & h_neglect, h_neglect_edge) @@ -1052,17 +1075,10 @@ subroutine remap_all_state_vars(CS, G, GV, h_old, h_new, Reg, OBC, & endif ; enddo ; enddo endif - if (CS%id_vert_remap_h > 0) call post_data(CS%id_vert_remap_h, h_old, CS%diag) - if ((CS%id_vert_remap_h_tendency > 0) .and. present(dt)) then - do k = 1, nz ; do j = G%jsc,G%jec ; do i = G%isc,G%iec - work_cont(i,j,k) = (h_new(i,j,k) - h_old(i,j,k))*Idt - enddo ; enddo ; enddo - call post_data(CS%id_vert_remap_h_tendency, work_cont, CS%diag) - endif - if (show_call_tree) call callTree_waypoint("v remapped (remap_all_state_vars)") - if (show_call_tree) call callTree_leave("remap_all_state_vars()") + if (show_call_tree) call callTree_waypoint("v remapped (remap_velocities)") + if (show_call_tree) call callTree_leave("remap_velocities()") -end subroutine remap_all_state_vars +end subroutine remap_velocities !> Mask out thicknesses to 0 when their runing sum exceeds a specified value. diff --git a/src/ALE/MOM_regridding.F90 b/src/ALE/MOM_regridding.F90 index e5ce4019ba..de287af98a 100644 --- a/src/ALE/MOM_regridding.F90 +++ b/src/ALE/MOM_regridding.F90 @@ -137,7 +137,7 @@ module MOM_regridding ! The following routines are visible to the outside world public initialize_regridding, end_regridding, regridding_main public regridding_preadjust_reqs, convective_adjustment -public inflate_vanished_layers_old, check_remapping_grid, check_grid_column +public inflate_vanished_layers_old, check_grid_column public set_regrid_params, get_regrid_size, write_regrid_file public uniformResolution, setCoordinateResolution public set_target_densities_from_GV, set_target_densities @@ -794,7 +794,7 @@ end subroutine end_regridding !------------------------------------------------------------------------------ !> Dispatching regridding routine for orchestrating regridding & remapping -subroutine regridding_main( remapCS, CS, G, GV, h, tv, h_new, dzInterface, conv_adjust, & +subroutine regridding_main( remapCS, CS, G, GV, h, tv, h_new, dzInterface, & frac_shelf_h, PCM_cell) !------------------------------------------------------------------------------ ! This routine takes care of (1) building a new grid and (2) remapping between @@ -823,24 +823,14 @@ subroutine regridding_main( remapCS, CS, G, GV, h, tv, h_new, dzInterface, conv_ type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamical variables (T, S, ...) real, dimension(SZI_(G),SZJ_(G),CS%nk), intent(inout) :: h_new !< New 3D grid consistent with target coordinate real, dimension(SZI_(G),SZJ_(G),CS%nk+1), intent(inout) :: dzInterface !< The change in position of each interface - logical, intent(in ) :: conv_adjust !< If true, regridding_main should do - !! convective adjustment, but because it no - !! longer does convective adjustment this must - !! be false. This argument has been retained to - !! trap inconsistent code, but will eventually - !! be eliminated. real, dimension(SZI_(G),SZJ_(G)), optional, intent(in ) :: frac_shelf_h !< Fractional ice shelf coverage logical, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & optional, intent(out ) :: PCM_cell !< Use PCM remapping in cells where true ! Local variables real :: trickGnuCompiler + integer :: i, j - if (conv_adjust) call MOM_error(FATAL, & - "regridding_main: convective adjustment no longer is done inside of regridding_main. "//& - "The code needs to be modified to call regridding_main() with conv_adjust=.false, "//& - "and a call to convective_adjustment added before calling regridding_main() "//& - "if regridding_preadjust_reqs() indicates that this is necessary.") if (present(PCM_cell)) PCM_cell(:,:,:) = .false. select case ( CS%regridding_scheme ) @@ -879,8 +869,18 @@ subroutine regridding_main( remapCS, CS, G, GV, h, tv, h_new, dzInterface, conv_ end select ! type of grid #ifdef __DO_SAFETY_CHECKS__ - if (CS%nk == GV%ke) call check_remapping_grid(G, GV, h, dzInterface,'in regridding_main') + if (CS%nk == GV%ke) then + do j = G%jsc-1,G%jec+1 ; do i = G%isc-1,G%iec+1 ; if (G%mask2dT(i,j)>0.) then + call check_grid_column( GV%ke, h(i,j,:), dzInterface(i,j,:), 'in regridding_main') + endif ; enddo ; enddo + endif #endif + do j=G%jsc,G%jec ; do i=G%isc,G%iec ; if (G%mask2dT(i,j) > 0.) then + if (minval(h(i,j,:)) < 0.0) then + write(0,*) 'regridding_main check_grid: i,j=', i, j, 'h_new(i,j,:)=', h_new(i,j,:) + call MOM_error(FATAL, "regridding_main: negative thickness encountered.") + endif + endif ; enddo ; enddo end subroutine regridding_main @@ -952,23 +952,6 @@ subroutine calc_h_new_by_dz(CS, G, GV, h, dzInterface, h_new) end subroutine calc_h_new_by_dz -!> Check that the total thickness of two grids match -subroutine check_remapping_grid( G, GV, h, dzInterface, msg ) - type(ocean_grid_type), intent(in) :: G !< Grid structure - type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2] - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: dzInterface !< Change in interface positions - !! [H ~> m or kg m-2] - character(len=*), intent(in) :: msg !< Message to append to errors - ! Local variables - integer :: i, j - - !$OMP parallel do default(shared) - do j = G%jsc-1,G%jec+1 ; do i = G%isc-1,G%iec+1 - if (G%mask2dT(i,j)>0.) call check_grid_column( GV%ke, h(i,j,:), dzInterface(i,j,:), msg ) - enddo ; enddo - -end subroutine check_remapping_grid !> Check that the total thickness of new and old grids are consistent subroutine check_grid_column( nk, h, dzInterface, msg ) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 267a162b00..7f61aba024 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -52,7 +52,8 @@ module MOM ! MOM core modules use MOM_ALE, only : ALE_init, ALE_end, ALE_main, ALE_CS, adjustGridForIntegrity use MOM_ALE, only : ALE_getCoordinate, ALE_getCoordinateUnits, ALE_writeCoordinateFile -use MOM_ALE, only : ALE_updateVerticalGridType, ALE_remap_init_conds, ALE_register_diags +use MOM_ALE, only : ALE_updateVerticalGridType, ALE_remap_init_conds +use MOM_ALE, only : ALE_update_regrid_weights, pre_ALE_diagnostics, ALE_register_diags use MOM_ALE_sponge, only : rotate_ALE_sponge, update_ALE_sponge_field use MOM_barotropic, only : Barotropic_CS use MOM_boundary_update, only : call_OBC_register, OBC_register_end, update_OBC_CS @@ -1508,6 +1509,10 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & call check_redundant("Pre-ALE ", u, v, G) endif call cpu_clock_begin(id_clock_ALE) + + call pre_ALE_diagnostics( G, GV, US, h, u, v, tv, CS%ALE_CSp) + call ALE_update_regrid_weights(dtdia, CS%ALE_CSp) + if (use_ice_shelf) then call ALE_main(G, GV, US, h, u, v, tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, & dtdia, CS%frac_shelf_h) @@ -2796,6 +2801,10 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & if (CS%debug) then call uvchksum("Post ALE adjust init cond [uv]", CS%u, CS%v, G%HI, haloshift=1) call hchksum(CS%h, "Post ALE adjust init cond h", G%HI, haloshift=1, scale=GV%H_to_m) + if (use_temperature) then + call hchksum(CS%tv%T, "Post ALE adjust init cond T", G%HI, haloshift=1, scale=US%C_to_degC) + call hchksum(CS%tv%S, "Post ALE adjust init cond S", G%HI, haloshift=1, scale=US%S_to_ppt) + endif endif endif if ( CS%use_ALE_algorithm ) call ALE_updateVerticalGridType( CS%ALE_CSp, GV ) diff --git a/src/initialization/MOM_state_initialization.F90 b/src/initialization/MOM_state_initialization.F90 index 0eac15f6d7..31dbb41dcc 100644 --- a/src/initialization/MOM_state_initialization.F90 +++ b/src/initialization/MOM_state_initialization.F90 @@ -2779,7 +2779,7 @@ subroutine MOM_temp_salt_initialize_from_Z(h, tv, depth_tot, G, GV, US, PF, just call regridding_preadjust_reqs(regridCS, do_conv_adj, ignore) if (do_conv_adj) call convective_adjustment(G, GV_loc, h1, tv_loc) - call regridding_main( remapCS, regridCS, G, GV_loc, h1, tv_loc, h, dz_interface, conv_adjust=.false., & + call regridding_main( remapCS, regridCS, G, GV_loc, h1, tv_loc, h, dz_interface, & frac_shelf_h=frac_shelf_h ) deallocate( dz_interface ) From 992f1c7f211e162936a634900d0f49b1cf6f0b71 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Thu, 6 Oct 2022 07:01:09 -0400 Subject: [PATCH 59/68] +Add new arguments to ALE_main Continued the refactoring of code related to ALE_main, including: - Added the new public subroutine pre_ALE_adjustments - Added the new arguments h_new, dzRegrid, and PCM_cell to ALE_main - Added the new arguments h_new and dzRegrid to ALE_offline_main - Moved the code copying the new thickness to the primary thickness out of ALE_main and into step_MOM_thermo All answers are bitwise identical, but there are new public interfaces and changes to the arguments of other public interfaces. --- src/ALE/MOM_ALE.F90 | 152 ++++++++++++++++---------------- src/core/MOM.F90 | 53 ++++++++--- src/tracer/MOM_offline_main.F90 | 16 +++- 3 files changed, 131 insertions(+), 90 deletions(-) diff --git a/src/ALE/MOM_ALE.F90 b/src/ALE/MOM_ALE.F90 index ffe9d55d81..0c61ccb25f 100644 --- a/src/ALE/MOM_ALE.F90 +++ b/src/ALE/MOM_ALE.F90 @@ -141,6 +141,7 @@ module MOM_ALE public ALE_initThicknessToCoord public ALE_update_regrid_weights public pre_ALE_diagnostics +public pre_ALE_adjustments public ALE_remap_init_conds public ALE_register_diags @@ -421,16 +422,51 @@ subroutine pre_ALE_diagnostics(G, GV, US, h, u, v, tv, CS) end subroutine pre_ALE_diagnostics + +!> Potentially do some preparatory work, such as convective adjustment, to clean up the model +!! state before regridding. +subroutine pre_ALE_adjustments(G, GV, US, h, tv, Reg, CS, u, v) + type(ocean_grid_type), intent(in) :: G !< Ocean grid informations + type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure + type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Current 3D grid obtained after the + !! last time step [H ~> m or kg m-2] + type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure + type(tracer_registry_type), pointer :: Reg !< Tracer registry structure + type(ALE_CS), pointer :: CS !< Regridding parameters and options + real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & + optional, intent(inout) :: u !< Zonal velocity field [L T-1 ~> m s-1] + real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), & + optional, intent(inout) :: v !< Meridional velocity field [L T-1 ~> m s-1] + + integer :: ntr + + ! Do column-wise convective adjustment. + ! Tracers and velocities should probably also undergo consistent adjustments. + if (CS%do_conv_adj) call convective_adjustment(G, GV, h, tv) + + if (CS%use_hybgen_unmix) then + ntr = 0 ; if (associated(Reg)) ntr = Reg%ntr + call hybgen_unmix(G, GV, US, CS%hybgen_unmixCS, tv, Reg, ntr, h) + endif + +end subroutine pre_ALE_adjustments + !> Takes care of (1) building a new grid and (2) remapping all variables between !! the old grid and the new grid. The creation of the new grid can be based !! on z coordinates, target interface densities, sigma coordinates or any !! arbitrary coordinate system. -subroutine ALE_main( G, GV, US, h, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h) +subroutine ALE_main( G, GV, US, h, h_new, dzRegrid, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h, PCM_cell) type(ocean_grid_type), intent(in) :: G !< Ocean grid informations type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Current 3D grid obtained after the - !! last time step [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses in 3D grid before + !! regridding [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(out) :: h_new !< Layer thicknesses in 3D grid after + !! regridding [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: dzRegrid !< The change in grid interface positions + !! due to regridding, in the same units as + !! thicknesses [H ~> m or kg m-2] real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: u !< Zonal velocity field [L T-1 ~> m s-1] real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: v !< Meridional velocity field [L T-1 ~> m s-1] type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure @@ -439,55 +475,31 @@ subroutine ALE_main( G, GV, US, h, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h) type(ocean_OBC_type), pointer :: OBC !< Open boundary structure real, optional, intent(in) :: dt !< Time step between calls to ALE_main [T ~> s] real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: frac_shelf_h !< Fractional ice shelf coverage [nondim] - ! Local variables - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: dzRegrid ! The change in grid interface positions - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_new ! New 3D grid obtained after last time step [H ~> m or kg m-2] - logical :: PCM_cell(SZI_(G),SZJ_(G),SZK_(GV)) !< If true, PCM remapping should be used in a cell. - integer :: ntr, i, j, k, isc, iec, jsc, jec, nk - - isc = G%isc ; iec = G%iec ; jsc = G%jsc ; jec = G%jec ; nk = GV%ke + logical, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & + optional, intent(out) :: PCM_cell !< If true, use PCM remapping in a cell. - if (CS%show_call_tree) call callTree_enter("ALE_main(), MOM_ALE.F90") + ! Local variables + logical :: showCallTree - ! If necessary, do some preparatory work to clean up the model state before regridding. + showCallTree = callTree_showQuery() - ! This adjusts the input thicknesses prior to remapping, based on the verical coordinate. - if (CS%do_conv_adj) call convective_adjustment(G, GV, h, tv) - if (CS%use_hybgen_unmix) then - ntr = 0 ; if (associated(Reg)) ntr = Reg%ntr - call hybgen_unmix(G, GV, G%US, CS%hybgen_unmixCS, tv, Reg, ntr, h) - endif + if (showCallTree) call callTree_enter("ALE_main(), MOM_ALE.F90") - ! Build new grid. The new grid is stored in h_new. The old grid is h. + ! Build the new grid and store it in h_new. The old grid is retained as h. ! Both are needed for the subsequent remapping of variables. dzRegrid(:,:,:) = 0.0 call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid, & frac_shelf_h=frac_shelf_h, PCM_cell=PCM_cell) - if (CS%show_call_tree) call callTree_waypoint("new grid generated (ALE_main)") - - ! The presence of dt is used for expediency to distinguish whether ALE_main is being called during init - ! or in the main loop. Tendency diagnostics in remap_tracers also rely on this logic. - if (present(dt)) then - call diag_update_remap_grids(CS%diag) - endif - - ! Remap all variables from old grid h onto new grid h_new - call remap_tracers(CS, G, GV, h, h_new, Reg, CS%show_call_tree, dt, PCM_cell=PCM_cell) - call remap_velocities(CS, G, GV, h, h_new, u, v, OBC, dzRegrid, debug=CS%show_call_tree, dt=dt) - - if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_main)") + if (showCallTree) call callTree_waypoint("new grid generated (ALE_main)") - ! Override old grid with new one. The new grid 'h_new' is built in - ! one of the 'build_...' routines above. - !$OMP parallel do default(shared) - do k=1,nk ; do j=jsc-1,jec+1 ; do i=isc-1,iec+1 - h(i,j,k) = h_new(i,j,k) - enddo ; enddo ; enddo + ! Remap all variables from the old grid h onto the new grid h_new + call remap_tracers(CS, G, GV, h, h_new, Reg, showCallTree, dt, PCM_cell=PCM_cell) + call remap_velocities(CS, G, GV, h, h_new, u, v, OBC, dzRegrid, debug=showCallTree, dt=dt) - if (CS%id_dzRegrid>0 .and. present(dt)) call post_data(CS%id_dzRegrid, dzRegrid, CS%diag) + if (CS%id_dzRegrid>0 .and. present(dt)) call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) - if (CS%show_call_tree) call callTree_leave("ALE_main()") + if (showCallTree) call callTree_leave("ALE_main()") end subroutine ALE_main @@ -495,58 +507,43 @@ end subroutine ALE_main !! the old grid and the new grid. The creation of the new grid can be based !! on z coordinates, target interface densities, sigma coordinates or any !! arbitrary coordinate system. -subroutine ALE_main_offline( G, GV, h, tv, Reg, CS, OBC, dt) +subroutine ALE_main_offline( G, GV, h, h_new, dzRegrid, tv, Reg, CS, OBC, dt) type(ocean_grid_type), intent(in) :: G !< Ocean grid informations type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Current 3D grid obtained after the !! last time step [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(out) :: h_new !< Layer thicknesses in 3D grid after + !! regridding [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: dzRegrid !< The change in grid interface positions + !! due to regridding, in the same units as + !! thicknesses [H ~> m or kg m-2] type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure type(tracer_registry_type), pointer :: Reg !< Tracer registry structure type(ALE_CS), pointer :: CS !< Regridding parameters and options type(ocean_OBC_type), pointer :: OBC !< Open boundary structure real, optional, intent(in) :: dt !< Time step between calls to ALE_main [T ~> s] - ! Local variables - real, dimension(SZI_(G), SZJ_(G), SZK_(GV)+1) :: dzRegrid ! The change in grid interface positions - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_new ! New 3D grid obtained after last time step [H ~> m or kg m-2] - integer :: ntr, i, j, k, isc, iec, jsc, jec, nk - isc = G%isc ; iec = G%iec ; jsc = G%jsc ; jec = G%jec ; nk = GV%ke + ! Local variables + logical :: showCallTree - if (CS%show_call_tree) call callTree_enter("ALE_main_offline(), MOM_ALE.F90") + showCallTree = callTree_showQuery() - if (present(dt)) then - call ALE_update_regrid_weights( dt, CS ) - endif - dzRegrid(:,:,:) = 0.0 - - ! This adjusts the input state prior to remapping, depending on the verical coordinate. - if (CS%do_conv_adj) call convective_adjustment(G, GV, h, tv) - if (CS%use_hybgen_unmix) then - ntr = 0 ; if (associated(Reg)) ntr = Reg%ntr - call hybgen_unmix(G, GV, G%US, CS%hybgen_unmixCS, tv, Reg, ntr, h) - endif + if (showCallTree) call callTree_enter("ALE_main_offline(), MOM_ALE.F90") ! Build new grid. The new grid is stored in h_new. The old grid is h. ! Both are needed for the subsequent remapping of variables. + dzRegrid(:,:,:) = 0.0 call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid) - if (CS%show_call_tree) call callTree_waypoint("new grid generated (ALE_main)") + if (showCallTree) call callTree_waypoint("new grid generated (ALE_main)") - ! Remap all variables from old grid h onto new grid h_new + ! Remap all tracers from old grid h onto new grid h_new call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree, dt=dt) - if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_main)") - - ! Override old grid with new one. The new grid 'h_new' is built in - ! one of the 'build_...' routines above. - !$OMP parallel do default(shared) - do k = 1,nk ; do j = jsc-1,jec+1 ; do i = isc-1,iec+1 - h(i,j,k) = h_new(i,j,k) - enddo ; enddo ; enddo + if (CS%id_dzRegrid>0 .and. present(dt)) call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) - if (CS%show_call_tree) call callTree_leave("ALE_main()") - if (CS%id_dzRegrid>0 .and. present(dt)) call post_data(CS%id_dzRegrid, dzRegrid, CS%diag) + if (showCallTree) call callTree_leave("ALE_main_offline()") end subroutine ALE_main_offline @@ -568,7 +565,7 @@ subroutine ALE_offline_inputs(CS, G, GV, h, tv, Reg, uhtr, vhtr, Kd, debug, OBC) ! Local variables integer :: nk, i, j, k, isc, iec, jsc, jec real, dimension(SZI_(G), SZJ_(G), SZK_(GV)) :: h_new ! Layer thicknesses after regridding [H ~> m or kg m-2] - real, dimension(SZI_(G), SZJ_(G), SZK_(GV)+1) :: dzRegrid ! The change in grid interface positions + real, dimension(SZI_(G), SZJ_(G), SZK_(GV)+1) :: dzRegrid ! The change in grid interface positions [H ~> m or kg m-2] real, dimension(SZK_(GV)) :: h_src ! Source grid thicknesses at velocity points [H ~> m or kg m-2] real, dimension(SZK_(GV)) :: h_dest ! Destination grid thicknesses at velocity points [H ~> m or kg m-2] real, dimension(SZK_(GV)) :: temp_vec ! Transports on the destination grid [H L2 ~> m3 or kg] @@ -648,17 +645,15 @@ subroutine ALE_offline_tracer_final( G, GV, h, tv, h_target, Reg, CS, OBC) real, dimension(SZI_(G), SZJ_(G), SZK_(GV)+1) :: dzRegrid !< The change in grid interface positions real, dimension(SZI_(G), SZJ_(G), SZK_(GV)) :: h_new !< Regridded target thicknesses - integer :: ntr, i, j, k, isc, iec, jsc, jec, nk + integer :: i, j, k, isc, iec, jsc, jec, nk isc = G%isc ; iec = G%iec ; jsc = G%jsc ; jec = G%jec ; nk = GV%ke if (CS%show_call_tree) call callTree_enter("ALE_offline_tracer_final(), MOM_ALE.F90") + ! Need to make sure that h_target is consistent with the current offline ALE confiuration - if (CS%do_conv_adj) call convective_adjustment(G, GV, h_target, tv) - if (CS%use_hybgen_unmix) then - ntr = 0 ; if (associated(Reg)) ntr = Reg%ntr - call hybgen_unmix(G, GV, G%US, CS%hybgen_unmixCS, tv, Reg, ntr, h) - endif + call pre_ALE_adjustments(G, GV, G%US, h_target, tv, Reg, CS) + call regridding_main( CS%remapCS, CS%regridCS, G, GV, h_target, tv, h_new, dzRegrid) if (CS%show_call_tree) call callTree_waypoint("Source and target grids checked (ALE_offline_tracer_final)") @@ -700,7 +695,7 @@ subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, d optional, pointer :: Reg !< Tracer registry to remap onto new grid real, optional, intent(in) :: dt !< Model timestep to provide a timescale for regridding [T ~> s] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), & - optional, intent(inout) :: dzRegrid !< Final change in interface positions + optional, intent(inout) :: dzRegrid !< Final change in interface positions [H ~> m or kg m-2] logical, optional, intent(in) :: initial !< Whether we're being called from an initialization !! routine (and expect diagnostics to work) @@ -754,6 +749,7 @@ subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, d ! generate new grid if (CS%do_conv_adj) call convective_adjustment(G, GV, h_loc, tv_local) + call regridding_main(CS%remapCS, CS%regridCS, G, GV, h_loc, tv_local, h, dzInterface) dzIntTotal(:,:,:) = dzIntTotal(:,:,:) + dzInterface(:,:,:) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 7f61aba024..7cdf765638 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -52,7 +52,7 @@ module MOM ! MOM core modules use MOM_ALE, only : ALE_init, ALE_end, ALE_main, ALE_CS, adjustGridForIntegrity use MOM_ALE, only : ALE_getCoordinate, ALE_getCoordinateUnits, ALE_writeCoordinateFile -use MOM_ALE, only : ALE_updateVerticalGridType, ALE_remap_init_conds +use MOM_ALE, only : ALE_updateVerticalGridType, ALE_remap_init_conds, pre_ALE_adjustments use MOM_ALE, only : ALE_update_regrid_weights, pre_ALE_diagnostics, ALE_register_diags use MOM_ALE_sponge, only : rotate_ALE_sponge, update_ALE_sponge_field use MOM_barotropic, only : Barotropic_CS @@ -161,7 +161,7 @@ module MOM use MOM_offline_main, only : offline_redistribute_residual, offline_diabatic_ale use MOM_offline_main, only : offline_fw_fluxes_into_ocean, offline_fw_fluxes_out_ocean use MOM_offline_main, only : offline_advection_layer, offline_transport_end -use MOM_ALE, only : ale_offline_tracer_final, ALE_main_offline +use MOM_ALE, only : ale_offline_tracer_final use MOM_ice_shelf, only : ice_shelf_CS, ice_shelf_query, initialize_ice_shelf use MOM_particles_mod, only : particles, particles_init, particles_run, particles_save_restart, particles_end @@ -1410,13 +1410,17 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & optional, pointer :: Waves !< Container for wave related parameters !! the fields in Waves are intent in here. + real :: h_new(SZI_(G),SZJ_(G),SZK_(GV)) ! Layer thicknesses after regridding [H ~> m or kg m-2] + real :: dzRegrid(SZI_(G),SZJ_(G),SZK_(GV)+1) ! The change in grid interface positions due to regridding, + ! in the same units as thicknesses [H ~> m or kg m-2] + logical :: PCM_cell(SZI_(G),SZJ_(G),SZK_(GV)) ! If true, PCM remapping should be used in a cell. logical :: use_ice_shelf ! Needed for selecting the right ALE interface. logical :: showCallTree type(group_pass_type) :: pass_T_S, pass_T_S_h, pass_uv_T_S_h integer :: dynamics_stencil ! The computational stencil for the calculations ! in the dynamic core. integer :: halo_sz ! The size of a halo where data must be valid. - integer :: is, ie, js, je, nz + integer :: i, j, k, is, ie, js, je, nz is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke showCallTree = callTree_showQuery() @@ -1510,16 +1514,26 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & endif call cpu_clock_begin(id_clock_ALE) - call pre_ALE_diagnostics( G, GV, US, h, u, v, tv, CS%ALE_CSp) + call pre_ALE_diagnostics(G, GV, US, h, u, v, tv, CS%ALE_CSp) call ALE_update_regrid_weights(dtdia, CS%ALE_CSp) + call pre_ALE_adjustments(G, GV, US, h, tv, CS%tracer_Reg, CS%ALE_CSp, u, v) + ! Adjust the target grids for diagnostics, in case there have been thickness adjustments. + call diag_update_remap_grids(CS%diag) if (use_ice_shelf) then - call ALE_main(G, GV, US, h, u, v, tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, & - dtdia, CS%frac_shelf_h) + call ALE_main(G, GV, US, h, h_new, dzRegrid, u, v, tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, & + dtdia, CS%frac_shelf_h, PCM_cell=PCM_cell) else - call ALE_main(G, GV, US, h, u, v, tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, dtdia) + call ALE_main(G, GV, US, h, h_new, dzRegrid, u, v, tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, & + dtdia, PCM_cell=PCM_cell) endif + ! Replace the old grid with new one. All remapping must be done by this point in the code. + !$OMP parallel do default(shared) + do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1 + h(i,j,k) = h_new(i,j,k) + enddo ; enddo ; enddo + if (showCallTree) call callTree_waypoint("finished ALE_main (step_MOM_thermo)") call cpu_clock_end(id_clock_ALE) endif ! endif for the block "if ( CS%use_ALE_algorithm )" @@ -1852,12 +1866,16 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & ! This include declares and sets the variable "version". # include "version_variable.h" - integer :: i, j, is, ie, js, je, isd, ied, jsd, jed, nz + integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed, nz integer :: IsdB, IedB, JsdB, JedB real :: dtbt ! If negative, this specifies the barotropic timestep as a fraction ! of the maximum stable value [nondim]. real, allocatable, dimension(:,:) :: eta ! free surface height or column mass [H ~> m or kg m-2] + real, allocatable, dimension(:,:,:) :: h_new ! Layer thicknesses after regridding [H ~> m or kg m-2] + real, allocatable, dimension(:,:,:) :: dzRegrid ! The change in grid interface positions due to regridding, + ! in the same units as thicknesses [H ~> m or kg m-2] + logical, allocatable, dimension(:,:,:) :: PCM_cell ! If true, PCM remapping should be used in a cell. type(group_pass_type) :: tmp_pass_uv_T_S_h, pass_uv_T_S_h real :: default_val ! default value for a parameter @@ -2780,14 +2798,27 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & endif call callTree_waypoint("Calling adjustGridForIntegrity() to remap initial conditions (initialize_MOM)") call adjustGridForIntegrity(CS%ALE_CSp, G, GV, CS%h ) + call pre_ALE_adjustments(G, GV, US, CS%h, CS%tv, CS%tracer_Reg, CS%ALE_CSp, CS%u, CS%v) + call callTree_waypoint("Calling ALE_main() to remap initial conditions (initialize_MOM)") + allocate(h_new(isd:ied, jsd:jed, nz), source=0.0) + allocate(dzRegrid(isd:ied, jsd:jed, nz+1), source=0.0) + allocate(PCM_cell(isd:ied, jsd:jed, nz), source=.false.) if (use_ice_shelf) then - call ALE_main(G, GV, US, CS%h, CS%u, CS%v, CS%tv, CS%tracer_Reg, CS%ALE_CSp, & - CS%OBC, frac_shelf_h=CS%frac_shelf_h) + call ALE_main(G, GV, US, CS%h, h_new, dzRegrid, CS%u, CS%v, CS%tv, CS%tracer_Reg, CS%ALE_CSp, & + CS%OBC, frac_shelf_h=CS%frac_shelf_h, PCM_cell=PCM_cell) else - call ALE_main( G, GV, US, CS%h, CS%u, CS%v, CS%tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC) + call ALE_main(G, GV, US, CS%h, h_new, dzRegrid, CS%u, CS%v, CS%tv, CS%tracer_Reg, CS%ALE_CSp, & + CS%OBC, PCM_cell=PCM_cell) endif + ! Replace the old grid with new one. All remapping must be done at this point. + !$OMP parallel do default(shared) + do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1 + CS%h(i,j,k) = h_new(i,j,k) + enddo ; enddo ; enddo + deallocate(h_new, dzRegrid, PCM_cell) + call cpu_clock_begin(id_clock_pass_init) call create_group_pass(tmp_pass_uv_T_S_h, CS%u, CS%v, G%Domain) if (use_temperature) then diff --git a/src/tracer/MOM_offline_main.F90 b/src/tracer/MOM_offline_main.F90 index c1582dca4a..279a9a70e2 100644 --- a/src/tracer/MOM_offline_main.F90 +++ b/src/tracer/MOM_offline_main.F90 @@ -5,6 +5,7 @@ module MOM_offline_main ! This file is part of MOM6. See LICENSE.md for the license. use MOM_ALE, only : ALE_CS, ALE_main_offline, ALE_offline_inputs +use MOM_ALE, only : pre_ALE_adjustments, ALE_update_regrid_weights use MOM_checksums, only : hchksum, uvchksum use MOM_coms, only : reproducing_sum use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end @@ -229,7 +230,10 @@ subroutine offline_advection_ale(fluxes, Time_start, time_interval, G, GV, US, C ! Variables used to keep track of layer thicknesses at various points in the code real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & h_new, & ! Updated layer thicknesses [H ~> m or kg m-2] + h_post_remap, & ! Layer thicknesses after remapping [H ~> m or kg m-2] h_vol ! Layer volumes [H L2 ~> m3 or kg] + real :: dzRegrid(SZI_(G),SZJ_(G),SZK_(GV)+1) ! The change in grid interface positions due to regridding, + ! in the same units as thicknesses [H ~> m or kg m-2] integer :: niter, iter real :: Inum_iter ! The inverse of the number of iterations [nondim] character(len=256) :: mesg ! The text of an error message @@ -347,7 +351,17 @@ subroutine offline_advection_ale(fluxes, Time_start, time_interval, G, GV, US, C call MOM_tracer_chkinv(debug_msg, G, GV, h_new, CS%tracer_reg) endif call cpu_clock_begin(id_clock_ALE) - call ALE_main_offline(G, GV, h_new, CS%tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, CS%dt_offline) + + call ALE_update_regrid_weights(CS%dt_offline, CS%ALE_CSp) + call pre_ALE_adjustments(G, GV, US, h_new, CS%tv, CS%tracer_Reg, CS%ALE_CSp) + ! Adjust the target grids for diagnostics, in case there have been thickness adjustments. + ! call diag_update_remap_grids(CS%diag, alt_h=h_new) + + call ALE_main_offline(G, GV, h_new, h_post_remap, dzRegrid, CS%tv, CS%tracer_Reg, & + CS%ALE_CSp, CS%OBC, CS%dt_offline) + do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1 + h_new(i,j,k) = h_post_remap(i,j,k) + enddo ; enddo ; enddo call cpu_clock_end(id_clock_ALE) if (CS%debug) then From 37acb38867c59bb590f137a2981e052c23dd74f7 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Thu, 6 Oct 2022 08:56:24 -0400 Subject: [PATCH 60/68] Reorder dzRegrid diagnostic Move the post_data call for dzRegrid to immediately follow its calculation in regridding main. All answers and diagnostics are bitwise identical, but because this commit changes the order in which some diagnostics are posted, the TC testing falsely reports a failure due to changed diagnostics. --- src/ALE/MOM_ALE.F90 | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ALE/MOM_ALE.F90 b/src/ALE/MOM_ALE.F90 index 0c61ccb25f..3beeca8638 100644 --- a/src/ALE/MOM_ALE.F90 +++ b/src/ALE/MOM_ALE.F90 @@ -12,7 +12,7 @@ module MOM_ALE use MOM_debugging, only : check_column_integrals use MOM_diag_mediator, only : register_diag_field, post_data, diag_ctrl -use MOM_diag_mediator, only : time_type, diag_update_remap_grids +use MOM_diag_mediator, only : time_type, diag_update_remap_grids, query_averaging_enabled use MOM_diag_vkernels, only : interpolate_column, reintegrate_column use MOM_domains, only : create_group_pass, do_group_pass, group_pass_type use MOM_error_handler, only : MOM_error, FATAL, WARNING @@ -491,14 +491,16 @@ subroutine ALE_main( G, GV, US, h, h_new, dzRegrid, u, v, tv, Reg, CS, OBC, dt, call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid, & frac_shelf_h=frac_shelf_h, PCM_cell=PCM_cell) + if (CS%id_dzRegrid>0) then ; if (query_averaging_enabled(CS%diag)) then + call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) + endif ; endif + if (showCallTree) call callTree_waypoint("new grid generated (ALE_main)") ! Remap all variables from the old grid h onto the new grid h_new call remap_tracers(CS, G, GV, h, h_new, Reg, showCallTree, dt, PCM_cell=PCM_cell) call remap_velocities(CS, G, GV, h, h_new, u, v, OBC, dzRegrid, debug=showCallTree, dt=dt) - if (CS%id_dzRegrid>0 .and. present(dt)) call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) - if (showCallTree) call callTree_leave("ALE_main()") end subroutine ALE_main @@ -535,14 +537,16 @@ subroutine ALE_main_offline( G, GV, h, h_new, dzRegrid, tv, Reg, CS, OBC, dt) dzRegrid(:,:,:) = 0.0 call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid) + if (CS%id_dzRegrid>0) then ; if (query_averaging_enabled(CS%diag)) then + call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) + endif ; endif + if (showCallTree) call callTree_waypoint("new grid generated (ALE_main)") ! Remap all tracers from old grid h onto new grid h_new call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree, dt=dt) - if (CS%id_dzRegrid>0 .and. present(dt)) call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) - if (showCallTree) call callTree_leave("ALE_main_offline()") end subroutine ALE_main_offline From d66f03ca22a9608a0cf4abf4e8e3b45761b122e4 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Thu, 6 Oct 2022 12:28:32 -0400 Subject: [PATCH 61/68] +Split ALE_main into ALE_regrid & ALE_remap calls Replaced ALE_main with ALE_regrid and calls to ALE_remap_tracers and ALE_remap_velocities from step_MOM_thermo. Also eliminated ALE_main_offline and ALE_offline_tracer_final, as they can now be replaced with a calls to ALE_regrid and a call to ALE_remap_tracers. Also added unit descriptions to a number of comments describing variables in MOM_ALE.F90. All answers are bitwise identical, but there are 3 new publicly visible subroutines (ALE_regrid, ALE_remap_tracers, and ALE_remap_velocities) and three previous interfaces (ALE_main, ALE_main_offline and ALE_offline_tracer_final) have been eliminated. --- src/ALE/MOM_ALE.F90 | 215 +++++++++----------------------- src/core/MOM.F90 | 83 +++++++----- src/tracer/MOM_offline_main.F90 | 25 ++-- 3 files changed, 123 insertions(+), 200 deletions(-) diff --git a/src/ALE/MOM_ALE.F90 b/src/ALE/MOM_ALE.F90 index 3beeca8638..8116ba3e17 100644 --- a/src/ALE/MOM_ALE.F90 +++ b/src/ALE/MOM_ALE.F90 @@ -123,12 +123,12 @@ module MOM_ALE ! Publicly available functions public ALE_init public ALE_end -public ALE_main -public ALE_main_offline +public ALE_regrid public ALE_offline_inputs -public ALE_offline_tracer_final public ALE_regrid_accelerated public ALE_remap_scalar +public ALE_remap_tracers +public ALE_remap_velocities public ALE_PLM_edge_values public TS_PLM_edge_values public TS_PPM_edge_values @@ -166,7 +166,7 @@ subroutine ALE_init( param_file, GV, US, max_depth, CS) ! Local variables character(len=40) :: mdl = "MOM_ALE" ! This module's name. character(len=80) :: string, vel_string ! Temporary strings - real :: filter_shallow_depth, filter_deep_depth + real :: filter_shallow_depth, filter_deep_depth ! Depth ranges of filtering [H ~> m or kg m-2] integer :: default_answer_date ! The default setting for the various ANSWER_DATE flags. logical :: default_2018_answers ! The default setting for the various 2018_ANSWERS flags. logical :: answers_2018 ! If true, use the order of arithmetic and expressions for remapping @@ -452,11 +452,9 @@ subroutine pre_ALE_adjustments(G, GV, US, h, tv, Reg, CS, u, v) end subroutine pre_ALE_adjustments -!> Takes care of (1) building a new grid and (2) remapping all variables between -!! the old grid and the new grid. The creation of the new grid can be based -!! on z coordinates, target interface densities, sigma coordinates or any -!! arbitrary coordinate system. -subroutine ALE_main( G, GV, US, h, h_new, dzRegrid, u, v, tv, Reg, CS, OBC, dt, frac_shelf_h, PCM_cell) +!> Takes care of building a new grid. The creation of the new grid can be based on z coordinates, +!! target interface densities, sigma coordinates or any arbitrary coordinate system. +subroutine ALE_regrid( G, GV, US, h, h_new, dzRegrid, tv, CS, frac_shelf_h, PCM_cell) type(ocean_grid_type), intent(in) :: G !< Ocean grid informations type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type @@ -467,13 +465,8 @@ subroutine ALE_main( G, GV, US, h, h_new, dzRegrid, u, v, tv, Reg, CS, OBC, dt, real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: dzRegrid !< The change in grid interface positions !! due to regridding, in the same units as !! thicknesses [H ~> m or kg m-2] - real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: u !< Zonal velocity field [L T-1 ~> m s-1] - real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: v !< Meridional velocity field [L T-1 ~> m s-1] type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure - type(tracer_registry_type), pointer :: Reg !< Tracer registry structure type(ALE_CS), pointer :: CS !< Regridding parameters and options - type(ocean_OBC_type), pointer :: OBC !< Open boundary structure - real, optional, intent(in) :: dt !< Time step between calls to ALE_main [T ~> s] real, dimension(SZI_(G),SZJ_(G)), optional, intent(in) :: frac_shelf_h !< Fractional ice shelf coverage [nondim] logical, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & optional, intent(out) :: PCM_cell !< If true, use PCM remapping in a cell. @@ -483,7 +476,7 @@ subroutine ALE_main( G, GV, US, h, h_new, dzRegrid, u, v, tv, Reg, CS, OBC, dt, showCallTree = callTree_showQuery() - if (showCallTree) call callTree_enter("ALE_main(), MOM_ALE.F90") + if (showCallTree) call callTree_enter("ALE_regrid(), MOM_ALE.F90") ! Build the new grid and store it in h_new. The old grid is retained as h. ! Both are needed for the subsequent remapping of variables. @@ -495,61 +488,9 @@ subroutine ALE_main( G, GV, US, h, h_new, dzRegrid, u, v, tv, Reg, CS, OBC, dt, call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) endif ; endif - if (showCallTree) call callTree_waypoint("new grid generated (ALE_main)") - - ! Remap all variables from the old grid h onto the new grid h_new - call remap_tracers(CS, G, GV, h, h_new, Reg, showCallTree, dt, PCM_cell=PCM_cell) - call remap_velocities(CS, G, GV, h, h_new, u, v, OBC, dzRegrid, debug=showCallTree, dt=dt) - - if (showCallTree) call callTree_leave("ALE_main()") - -end subroutine ALE_main - -!> Takes care of (1) building a new grid and (2) remapping all variables between -!! the old grid and the new grid. The creation of the new grid can be based -!! on z coordinates, target interface densities, sigma coordinates or any -!! arbitrary coordinate system. -subroutine ALE_main_offline( G, GV, h, h_new, dzRegrid, tv, Reg, CS, OBC, dt) - type(ocean_grid_type), intent(in) :: G !< Ocean grid informations - type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Current 3D grid obtained after the - !! last time step [H ~> m or kg m-2] - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(out) :: h_new !< Layer thicknesses in 3D grid after - !! regridding [H ~> m or kg m-2] - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(out) :: dzRegrid !< The change in grid interface positions - !! due to regridding, in the same units as - !! thicknesses [H ~> m or kg m-2] - type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure - type(tracer_registry_type), pointer :: Reg !< Tracer registry structure - type(ALE_CS), pointer :: CS !< Regridding parameters and options - type(ocean_OBC_type), pointer :: OBC !< Open boundary structure - real, optional, intent(in) :: dt !< Time step between calls to ALE_main [T ~> s] - - ! Local variables - logical :: showCallTree - - showCallTree = callTree_showQuery() - - if (showCallTree) call callTree_enter("ALE_main_offline(), MOM_ALE.F90") - - ! Build new grid. The new grid is stored in h_new. The old grid is h. - ! Both are needed for the subsequent remapping of variables. - dzRegrid(:,:,:) = 0.0 - call regridding_main( CS%remapCS, CS%regridCS, G, GV, h, tv, h_new, dzRegrid) - - if (CS%id_dzRegrid>0) then ; if (query_averaging_enabled(CS%diag)) then - call post_data(CS%id_dzRegrid, dzRegrid, CS%diag, alt_h=h_new) - endif ; endif - - if (showCallTree) call callTree_waypoint("new grid generated (ALE_main)") + if (showCallTree) call callTree_leave("ALE_regrid()") - ! Remap all tracers from old grid h onto new grid h_new - - call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree, dt=dt) - - if (showCallTree) call callTree_leave("ALE_main_offline()") - -end subroutine ALE_main_offline +end subroutine ALE_regrid !> Regrid/remap stored fields used for offline tracer integrations. These input fields are assumed to have !! the same layer thicknesses at the end of the last offline interval (which should be a Zstar grid). This @@ -563,7 +504,7 @@ subroutine ALE_offline_inputs(CS, G, GV, h, tv, Reg, uhtr, vhtr, Kd, debug, OBC) type(tracer_registry_type), pointer :: Reg !< Tracer registry structure real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), intent(inout) :: uhtr !< Zonal mass fluxes [H L2 ~> m3 or kg] real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), intent(inout) :: vhtr !< Meridional mass fluxes [H L2 ~> m3 or kg] - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kd !< Input diffusivites [Z2 T-1 ~> m2 s-1] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(inout) :: Kd !< Input diffusivities [Z2 T-1 ~> m2 s-1] logical, intent(in ) :: debug !< If true, then turn checksums type(ocean_OBC_type), pointer :: OBC !< Open boundary structure ! Local variables @@ -571,7 +512,7 @@ subroutine ALE_offline_inputs(CS, G, GV, h, tv, Reg, uhtr, vhtr, Kd, debug, OBC) real, dimension(SZI_(G), SZJ_(G), SZK_(GV)) :: h_new ! Layer thicknesses after regridding [H ~> m or kg m-2] real, dimension(SZI_(G), SZJ_(G), SZK_(GV)+1) :: dzRegrid ! The change in grid interface positions [H ~> m or kg m-2] real, dimension(SZK_(GV)) :: h_src ! Source grid thicknesses at velocity points [H ~> m or kg m-2] - real, dimension(SZK_(GV)) :: h_dest ! Destination grid thicknesses at velocity points [H ~> m or kg m-2] + real, dimension(SZK_(GV)) :: h_dest ! Destination grid thicknesses at velocity points [H ~> m or kg m-2] real, dimension(SZK_(GV)) :: temp_vec ! Transports on the destination grid [H L2 ~> m3 or kg] isc = G%isc ; iec = G%iec ; jsc = G%jsc ; jec = G%jec ; nk = GV%ke @@ -587,7 +528,7 @@ subroutine ALE_offline_inputs(CS, G, GV, h, tv, Reg, uhtr, vhtr, Kd, debug, OBC) if (CS%show_call_tree) call callTree_waypoint("new grid generated (ALE_offline_inputs)") ! Remap all variables from old grid h onto new grid h_new - call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree) + call ALE_remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree) if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_inputs)") ! Reintegrate mass transports from Zstar to the offline vertical coordinate @@ -624,62 +565,13 @@ subroutine ALE_offline_inputs(CS, G, GV, h, tv, Reg, uhtr, vhtr, Kd, debug, OBC) ! Copy over the new layer thicknesses do k = 1,nk ; do j = jsc-1,jec+1 ; do i = isc-1,iec+1 - h(i,j,k) = h_new(i,j,k) + h(i,j,k) = h_new(i,j,k) enddo ; enddo ; enddo if (CS%show_call_tree) call callTree_leave("ALE_offline_inputs()") end subroutine ALE_offline_inputs -!> Remaps all tracers from h onto h_target. This is intended to be called when tracers -!! are done offline. In the case where transports don't quite conserve, we still want to -!! make sure that layer thicknesses offline do not drift too far away from the online model -subroutine ALE_offline_tracer_final( G, GV, h, tv, h_target, Reg, CS, OBC) - type(ocean_grid_type), intent(in) :: G !< Ocean grid informations - type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Current 3D grid obtained after the - !! last time step [H ~> m or kg m-2] - type(thermo_var_ptrs), intent(inout) :: tv !< Thermodynamic variable structure - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h_target !< Current 3D grid obtained after - !! last time step [H ~> m or kg m-2] - type(tracer_registry_type), pointer :: Reg !< Tracer registry structure - type(ALE_CS), pointer :: CS !< Regridding parameters and options - type(ocean_OBC_type), pointer :: OBC !< Open boundary structure - ! Local variables - - real, dimension(SZI_(G), SZJ_(G), SZK_(GV)+1) :: dzRegrid !< The change in grid interface positions - real, dimension(SZI_(G), SZJ_(G), SZK_(GV)) :: h_new !< Regridded target thicknesses - integer :: i, j, k, isc, iec, jsc, jec, nk - - isc = G%isc ; iec = G%iec ; jsc = G%jsc ; jec = G%jec ; nk = GV%ke - - if (CS%show_call_tree) call callTree_enter("ALE_offline_tracer_final(), MOM_ALE.F90") - - ! Need to make sure that h_target is consistent with the current offline ALE confiuration - call pre_ALE_adjustments(G, GV, G%US, h_target, tv, Reg, CS) - - call regridding_main( CS%remapCS, CS%regridCS, G, GV, h_target, tv, h_new, dzRegrid) - - if (CS%show_call_tree) call callTree_waypoint("Source and target grids checked (ALE_offline_tracer_final)") - - ! Remap all variables from old grid h onto new grid h_new - - call remap_tracers(CS, G, GV, h, h_new, Reg, debug=CS%show_call_tree) - - if (CS%show_call_tree) call callTree_waypoint("state remapped (ALE_offline_tracer_final)") - - ! Override old grid with new one. The new grid 'h_new' is built in - ! one of the 'build_...' routines above. - !$OMP parallel do default(shared) - do k = 1,nk - do j = jsc-1,jec+1 ; do i = isc-1,iec+1 - h(i,j,k) = h_new(i,j,k) - enddo ; enddo - enddo - if (CS%show_call_tree) call callTree_leave("ALE_offline_tracer_final()") -end subroutine ALE_offline_tracer_final - - !> For a state-based coordinate, accelerate the process of regridding by !! repeatedly applying the grid calculation algorithm subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, dzRegrid, initial) @@ -707,11 +599,15 @@ subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, d integer :: i, j, itt, nz type(thermo_var_ptrs) :: tv_local ! local/intermediate temp/salt type(group_pass_type) :: pass_T_S_h ! group pass if the coordinate has a stencil - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_loc, h_orig ! A working copy of layer thicknesses - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), target :: T, S ! local temporary state + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_loc ! A working copy of layer thicknesses [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_orig ! The original layer thicknesses [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), target :: T ! local temporary temperatures [C ~> degC] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), target :: S ! local temporary salinities [S ~> ppt] ! we have to keep track of the total dzInterface if for some reason ! we're using the old remapping algorithm for u/v - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: dzInterface, dzIntTotal + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: dzInterface ! Interface height changes within + ! an iteration [H ~> m or kg m-2] + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: dzIntTotal ! Cumulative interface position changes [H ~> m or kg m-2] real :: h_neglect, h_neglect_edge ! small thicknesses [H ~> m or kg m-2] nz = GV%ke @@ -746,7 +642,6 @@ subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, d h_neglect = GV%kg_m2_to_H * 1.0e-30 ; h_neglect_edge = GV%kg_m2_to_H * 1.0e-10 endif - do itt = 1, n_itt call do_group_pass(pass_T_S_h, G%domain) @@ -770,8 +665,8 @@ subroutine ALE_regrid_accelerated(CS, G, GV, h, tv, n_itt, u, v, OBC, Reg, dt, d enddo ! remap all state variables (including those that weren't needed for regridding) - call remap_tracers(CS, G, GV, h_orig, h, Reg) - call remap_velocities(CS, G, GV, h_orig, h, u, v, OBC, dzIntTotal) + call ALE_remap_tracers(CS, G, GV, h_orig, h, Reg) + call ALE_remap_velocities(CS, G, GV, h_orig, h, u, v, OBC, dzIntTotal) ! save total dzregrid for diags if needed? if (present(dzRegrid)) dzRegrid(:,:,:) = dzIntTotal(:,:,:) @@ -781,7 +676,7 @@ end subroutine ALE_regrid_accelerated !! new grids. This routine is called during initialization of the model at time=0, to !! remap initial conditions to the model grid. It is also called during a !! time step to update the state. -subroutine remap_tracers(CS, G, GV, h_old, h_new, Reg, debug, dt, PCM_cell) +subroutine ALE_remap_tracers(CS, G, GV, h_old, h_new, Reg, debug, dt, PCM_cell) type(ALE_CS), intent(in) :: CS !< ALE control structure type(ocean_grid_type), intent(in) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure @@ -823,7 +718,7 @@ subroutine remap_tracers(CS, G, GV, h_old, h_new, Reg, debug, dt, PCM_cell) h_neglect = GV%kg_m2_to_H*1.0e-30 ; h_neglect_edge = GV%kg_m2_to_H*1.0e-10 endif - if (show_call_tree) call callTree_enter("remap_tracers(), MOM_ALE.F90") + if (show_call_tree) call callTree_enter("ALE_remap_tracers(), MOM_ALE.F90") nz = GV%ke @@ -837,7 +732,7 @@ subroutine remap_tracers(CS, G, GV, h_old, h_new, Reg, debug, dt, PCM_cell) ! Remap all registered tracers, including temperature and salinity. if (ntr>0) then - if (show_call_tree) call callTree_waypoint("remapping tracers (remap_tracers)") + if (show_call_tree) call callTree_waypoint("remapping tracers (ALE_remap_tracers)") !$OMP parallel do default(shared) private(h1,h2,tr_column,Tr,PCM,work_conc,work_cont,work_2d) do m=1,ntr ! For each tracer Tr => Reg%Tr(m) @@ -885,7 +780,6 @@ subroutine remap_tracers(CS, G, GV, h_old, h_new, Reg, debug, dt, PCM_cell) if (Tr%id_remap_cont > 0) then call post_data(Tr%id_remap_cont, work_cont, CS%diag) endif - nz = GV%ke if (Tr%id_remap_cont_2d > 0) then do j = G%jsc,G%jec ; do i = G%isc,G%iec @@ -910,16 +804,16 @@ subroutine remap_tracers(CS, G, GV, h_old, h_new, Reg, debug, dt, PCM_cell) call post_data(CS%id_vert_remap_h_tendency, work_cont, CS%diag) endif - if (show_call_tree) call callTree_leave("remap_tracers(), MOM_ALE.F90") + if (show_call_tree) call callTree_leave("ALE_remap_tracers(), MOM_ALE.F90") -end subroutine remap_tracers +end subroutine ALE_remap_tracers !> This routine remaps velocity components between the old and the new grids, !! with thicknesses at velocity points taken to be arithmetic averages of tracer thicknesses. !! This routine may be called during initialization of the model at time=0, to !! remap initial conditions to the model grid. It is also called during a !! time step to update the state. -subroutine remap_velocities(CS, G, GV, h_old, h_new, u, v, OBC, dzInterface, debug, dt) +subroutine ALE_remap_velocities(CS, G, GV, h_old, h_new, u, v, OBC, dzInterface, debug, dt) type(ALE_CS), intent(in) :: CS !< ALE control structure type(ocean_grid_type), intent(in) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure @@ -956,12 +850,12 @@ subroutine remap_velocities(CS, G, GV, h_old, h_new, u, v, OBC, dzInterface, deb show_call_tree = .false. if (present(debug)) show_call_tree = debug - if (show_call_tree) call callTree_enter("remap_velocities()") + if (show_call_tree) call callTree_enter("ALE_remap_velocities()") ! If remap_uv_using_old_alg is .true. and u or v is requested, then we must have dzInterface. Otherwise, ! u and v can be remapped without dzInterface if (CS%remap_uv_using_old_alg .and. .not.present(dzInterface) ) call MOM_error(FATAL, & - "remap_velocities: dzInterface must be present if using old algorithm.") + "ALE_remap_velocities: dzInterface must be present if using old algorithm.") if (CS%answer_date >= 20190101) then h_neglect = GV%H_subroundoff ; h_neglect_edge = GV%H_subroundoff @@ -1028,7 +922,7 @@ subroutine remap_velocities(CS, G, GV, h_old, h_new, u, v, OBC, dzInterface, deb endif ; enddo ; enddo endif - if (show_call_tree) call callTree_waypoint("u remapped (remap_velocities)") + if (show_call_tree) call callTree_waypoint("u remapped (ALE_remap_velocities)") ! Remap v velocity component if ( .true. ) then @@ -1075,13 +969,13 @@ subroutine remap_velocities(CS, G, GV, h_old, h_new, u, v, OBC, dzInterface, deb endif ; enddo ; enddo endif - if (show_call_tree) call callTree_waypoint("v remapped (remap_velocities)") - if (show_call_tree) call callTree_leave("remap_velocities()") + if (show_call_tree) call callTree_waypoint("v remapped (ALE_remap_velocities)") + if (show_call_tree) call callTree_leave("ALE_remap_velocities()") -end subroutine remap_velocities +end subroutine ALE_remap_velocities -!> Mask out thicknesses to 0 when their runing sum exceeds a specified value. +!> Mask out thicknesses to 0 when their running sum exceeds a specified value. subroutine apply_partial_cell_mask(h1, h_mask) real, dimension(:), intent(inout) :: h1 !< A column of thicknesses to be masked out after their !! running vertical sum exceeds h_mask [H ~> m or kg m-2] @@ -1141,10 +1035,11 @@ subroutine ALE_remap_scalar(CS, G, GV, nk_src, h_src, s_src, h_dst, s_dst, all_c integer, intent(in) :: nk_src !< Number of levels on source grid real, dimension(SZI_(G),SZJ_(G),nk_src), intent(in) :: h_src !< Level thickness of source grid !! [H ~> m or kg m-2] - real, dimension(SZI_(G),SZJ_(G),nk_src), intent(in) :: s_src !< Scalar on source grid + real, dimension(SZI_(G),SZJ_(G),nk_src), intent(in) :: s_src !< Scalar on source grid, in arbitrary units [A] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)),intent(in) :: h_dst !< Level thickness of destination grid !! [H ~> m or kg m-2] - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)),intent(inout) :: s_dst !< Scalar on destination grid + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)),intent(inout) :: s_dst !< Scalar on destination grid, in the same + !! arbitrary units as s_src [A] logical, optional, intent(in) :: all_cells !< If false, only reconstruct for !! non-vanished cells. Use all vanished !! layers otherwise (default). @@ -1158,8 +1053,8 @@ subroutine ALE_remap_scalar(CS, G, GV, nk_src, h_src, s_src, h_dst, s_dst, all_c !! for remapping ! Local variables integer :: i, j, k, n_points - real :: dx(GV%ke+1) - real :: h_neglect, h_neglect_edge + real :: dx(GV%ke+1) ! Change in interface position [H ~> m or kg m-2] + real :: h_neglect, h_neglect_edge ! Tiny thicknesses used in remapping [H ~> m or kg m-2] logical :: ignore_vanished_layers, use_remapping_core_w, use_2018_remap ignore_vanished_layers = .false. @@ -1238,18 +1133,18 @@ subroutine ALE_PLM_edge_values( CS, G, GV, h, Q, bdry_extrap, Q_t, Q_b ) real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & intent(in) :: h !< layer thickness [H ~> m or kg m-2] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(in) :: Q !< 3d scalar array + intent(in) :: Q !< 3d scalar array, in arbitrary units [A] logical, intent(in) :: bdry_extrap !< If true, use high-order boundary !! extrapolation within boundary cells real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(inout) :: Q_t !< Scalar at the top edge of each layer + intent(inout) :: Q_t !< Scalar at the top edge of each layer [A] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(inout) :: Q_b !< Scalar at the bottom edge of each layer + intent(inout) :: Q_b !< Scalar at the bottom edge of each layer [A] ! Local variables integer :: i, j, k - real :: slp(GV%ke) - real :: mslp - real :: h_neglect + real :: slp(GV%ke) ! Tracer slope times the cell width [A] + real :: mslp ! Monotonized tracer slope times the cell width [A] + real :: h_neglect ! Tiny thicknesses used in remapping [H ~> m or kg m-2] if (CS%answer_date >= 20190101) then h_neglect = GV%H_subroundoff @@ -1297,13 +1192,13 @@ subroutine TS_PPM_edge_values( CS, S_t, S_b, T_t, T_b, G, GV, tv, h, bdry_extrap type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure type(ALE_CS), intent(inout) :: CS !< module control structure real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(inout) :: S_t !< Salinity at the top edge of each layer + intent(inout) :: S_t !< Salinity at the top edge of each layer [S ~> ppt] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(inout) :: S_b !< Salinity at the bottom edge of each layer + intent(inout) :: S_b !< Salinity at the bottom edge of each layer [S ~> ppt] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(inout) :: T_t !< Temperature at the top edge of each layer + intent(inout) :: T_t !< Temperature at the top edge of each layer [C ~> degC] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(inout) :: T_b !< Temperature at the bottom edge of each layer + intent(inout) :: T_b !< Temperature at the bottom edge of each layer [C ~> degC] type(thermo_var_ptrs), intent(in) :: tv !< thermodynamics structure real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & intent(in) :: h !< layer thicknesses [H ~> m or kg m-2] @@ -1402,7 +1297,9 @@ end subroutine ALE_initRegridding function ALE_getCoordinate( CS ) type(ALE_CS), pointer :: CS !< module control structure - real, dimension(CS%nk+1) :: ALE_getCoordinate + real, dimension(CS%nk+1) :: ALE_getCoordinate !< The coordinate positions, in the appropriate units + !! of the target coordinate, e.g. [Z ~> m] for z*, + !! non-dimensional for sigma, etc. ALE_getCoordinate(:) = getCoordinateInterfaces( CS%regridCS, undo_scaling=.true. ) end function ALE_getCoordinate @@ -1432,7 +1329,7 @@ subroutine ALE_update_regrid_weights( dt, CS ) real, intent(in) :: dt !< Time-step used between ALE calls [T ~> s] type(ALE_CS), pointer :: CS !< ALE control structure ! Local variables - real :: w ! An implicit weighting estimate. + real :: w ! An implicit weighting estimate [nondim] if (associated(CS)) then w = 0.0 @@ -1459,7 +1356,7 @@ subroutine ALE_updateVerticalGridType(CS, GV) GV%zAxisUnits = getCoordinateUnits( CS%regridCS ) GV%zAxisLongName = getCoordinateShortName( CS%regridCS ) GV%direction = -1 ! Because of ferret in z* mode. Need method to set - ! as function of coordinae mode. + ! as function of coordinate mode. end subroutine ALE_updateVerticalGridType diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 7cdf765638..c61f130ef7 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -50,9 +50,10 @@ module MOM use MOM_unit_tests, only : unit_tests ! MOM core modules -use MOM_ALE, only : ALE_init, ALE_end, ALE_main, ALE_CS, adjustGridForIntegrity +use MOM_ALE, only : ALE_init, ALE_end, ALE_regrid, ALE_CS, adjustGridForIntegrity use MOM_ALE, only : ALE_getCoordinate, ALE_getCoordinateUnits, ALE_writeCoordinateFile use MOM_ALE, only : ALE_updateVerticalGridType, ALE_remap_init_conds, pre_ALE_adjustments +use MOM_ALE, only : ALE_remap_tracers, ALE_remap_velocities use MOM_ALE, only : ALE_update_regrid_weights, pre_ALE_diagnostics, ALE_register_diags use MOM_ALE_sponge, only : rotate_ALE_sponge, update_ALE_sponge_field use MOM_barotropic, only : Barotropic_CS @@ -161,7 +162,6 @@ module MOM use MOM_offline_main, only : offline_redistribute_residual, offline_diabatic_ale use MOM_offline_main, only : offline_fw_fluxes_into_ocean, offline_fw_fluxes_out_ocean use MOM_offline_main, only : offline_advection_layer, offline_transport_end -use MOM_ALE, only : ale_offline_tracer_final use MOM_ice_shelf, only : ice_shelf_CS, ice_shelf_query, initialize_ice_shelf use MOM_particles_mod, only : particles, particles_init, particles_run, particles_save_restart, particles_end @@ -1388,7 +1388,7 @@ subroutine step_MOM_tracer_dyn(CS, G, GV, US, h, Time_local) end subroutine step_MOM_tracer_dyn !> MOM_step_thermo orchestrates the thermodynamic time stepping and vertical -!! remapping, via calls to diabatic (or adiabatic) and ALE_main. +!! remapping, via calls to diabatic (or adiabatic) and ALE_regrid. subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & Time_end_thermo, update_BBL, Waves) type(MOM_control_struct), intent(inout) :: CS !< Master MOM control structure @@ -1491,7 +1491,7 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & ! Regridding/remapping is done here, at end of thermodynamics time step ! (that may comprise several dynamical time steps) - ! The routine 'ALE_main' can be found in 'MOM_ALE.F90'. + ! The routine 'ALE_regrid' can be found in 'MOM_ALE.F90'. if ( CS%use_ALE_algorithm ) then call enable_averages(dtdia, Time_end_thermo, CS%diag) ! call pass_vector(u, v, G%Domain) @@ -1516,25 +1516,29 @@ subroutine step_MOM_thermo(CS, G, GV, US, u, v, h, tv, fluxes, dtdia, & call pre_ALE_diagnostics(G, GV, US, h, u, v, tv, CS%ALE_CSp) call ALE_update_regrid_weights(dtdia, CS%ALE_CSp) + ! Do any necessary adjustments ot the state prior to remapping. call pre_ALE_adjustments(G, GV, US, h, tv, CS%tracer_Reg, CS%ALE_CSp, u, v) ! Adjust the target grids for diagnostics, in case there have been thickness adjustments. call diag_update_remap_grids(CS%diag) if (use_ice_shelf) then - call ALE_main(G, GV, US, h, h_new, dzRegrid, u, v, tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, & - dtdia, CS%frac_shelf_h, PCM_cell=PCM_cell) + call ALE_regrid(G, GV, US, h, h_new, dzRegrid, tv, CS%ALE_CSp, CS%frac_shelf_h, PCM_cell) else - call ALE_main(G, GV, US, h, h_new, dzRegrid, u, v, tv, CS%tracer_Reg, CS%ALE_CSp, CS%OBC, & - dtdia, PCM_cell=PCM_cell) + call ALE_regrid(G, GV, US, h, h_new, dzRegrid, tv, CS%ALE_CSp, PCM_cell=PCM_cell) endif + if (showCallTree) call callTree_waypoint("new grid generated") + ! Remap all variables from the old grid h onto the new grid h_new + call ALE_remap_tracers(CS%ALE_CSp, G, GV, h, h_new, CS%tracer_Reg, showCallTree, dtdia, PCM_cell) + call ALE_remap_velocities(CS%ALE_CSp, G, GV, h, h_new, u, v, CS%OBC, dzRegrid, showCallTree, dtdia) + ! Replace the old grid with new one. All remapping must be done by this point in the code. !$OMP parallel do default(shared) do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1 h(i,j,k) = h_new(i,j,k) enddo ; enddo ; enddo - if (showCallTree) call callTree_waypoint("finished ALE_main (step_MOM_thermo)") + if (showCallTree) call callTree_waypoint("finished ALE_regrid (step_MOM_thermo)") call cpu_clock_end(id_clock_ALE) endif ! endif for the block "if ( CS%use_ALE_algorithm )" @@ -1633,13 +1637,16 @@ subroutine step_offline(forces, fluxes, sfc_state, Time_start, time_interval, CS logical :: do_vertical !< If enough time has elapsed, do the diabatic tracer sources/sinks logical :: adv_converged !< True if all the horizontal fluxes have been used + real, allocatable, dimension(:,:,:) :: h_new ! Layer thicknesses after regridding [H ~> m or kg m-2] + real, allocatable, dimension(:,:,:) :: dzRegrid ! The change in grid interface positions due to regridding, + ! in the same units as thicknesses [H ~> m or kg m-2] real :: dt_offline ! The offline timestep for advection [T ~> s] real :: dt_offline_vertical ! The offline timestep for vertical fluxes and remapping [T ~> s] logical :: skip_diffusion type(time_type), pointer :: accumulated_time => NULL() type(time_type), pointer :: vertical_time => NULL() - integer :: is, ie, js, je, isd, ied, jsd, jed + integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed, nz ! 3D pointers real, dimension(:,:,:), pointer :: & @@ -1654,7 +1661,7 @@ subroutine step_offline(forces, fluxes, sfc_state, Time_start, time_interval, CS ! Grid-related pointer assignments G => CS%G ; GV => CS%GV ; US => CS%US - is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec + is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed call cpu_clock_begin(id_clock_offline_tracer) @@ -1665,19 +1672,11 @@ subroutine step_offline(forces, fluxes, sfc_state, Time_start, time_interval, CS call enable_averaging(time_interval, Time_end, CS%diag) ! Check to see if this is the first iteration of the offline interval - if (accumulated_time == real_to_time(0.0)) then - first_iter = .true. - else ! This is probably unnecessary but is used to guard against unwanted behavior - first_iter = .false. - endif + first_iter = (accumulated_time == real_to_time(0.0)) ! Check to see if vertical tracer functions should be done - if (first_iter .or. (accumulated_time >= vertical_time)) then - do_vertical = .true. - vertical_time = accumulated_time + real_to_time(US%T_to_s*dt_offline_vertical) - else - do_vertical = .false. - endif + do_vertical = (first_iter .or. (accumulated_time >= vertical_time)) + if (do_vertical) vertical_time = accumulated_time + real_to_time(US%T_to_s*dt_offline_vertical) ! Increment the amount of time elapsed since last read and check if it's time to roll around accumulated_time = accumulated_time + real_to_time(time_interval) @@ -1756,7 +1755,28 @@ subroutine step_offline(forces, fluxes, sfc_state, Time_start, time_interval, CS ! Call ALE one last time to make sure that tracers are remapped onto the layer thicknesses ! stored from the forward run call cpu_clock_begin(id_clock_ALE) - call ALE_offline_tracer_final( G, GV, CS%h, CS%tv, h_end, CS%tracer_Reg, CS%ALE_CSp, CS%OBC) + + ! Do any necessary adjustments ot the state prior to remapping. + call pre_ALE_adjustments(G, GV, US, h_end, CS%tv, CS%tracer_Reg, CS%ALE_CSp) + + allocate(h_new(isd:ied, jsd:jed, nz), source=0.0) + allocate(dzRegrid(isd:ied, jsd:jed, nz+1), source=0.0) + + ! Generate the new grid based on the tracer grid at the end of the interval. + call ALE_regrid(G, GV, US, h_end, h_new, dzRegrid, CS%tv, CS%ALE_CSp) + + ! Remap the tracers from the previous tracer grid onto the new grid. The thicknesses that + ! are used are intended to ensure that in the case where transports don't quite conserve, + ! the offline layer thicknesses do not drift too far away from the online model. + call ALE_remap_tracers(CS%ALE_CSp, G, GV, CS%h, h_new, CS%tracer_Reg, debug=CS%debug) + + ! Update the tracer grid. + do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1 + CS%h(i,j,k) = h_new(i,j,k) + enddo ; enddo ; enddo + + deallocate(h_new, dzRegrid) + call cpu_clock_end(id_clock_ALE) call pass_var(CS%h, G%Domain) endif @@ -1872,9 +1892,9 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & ! of the maximum stable value [nondim]. real, allocatable, dimension(:,:) :: eta ! free surface height or column mass [H ~> m or kg m-2] - real, allocatable, dimension(:,:,:) :: h_new ! Layer thicknesses after regridding [H ~> m or kg m-2] + real, allocatable, dimension(:,:,:) :: h_new ! Layer thicknesses after regridding [H ~> m or kg m-2] real, allocatable, dimension(:,:,:) :: dzRegrid ! The change in grid interface positions due to regridding, - ! in the same units as thicknesses [H ~> m or kg m-2] + ! in the same units as thicknesses [H ~> m or kg m-2] logical, allocatable, dimension(:,:,:) :: PCM_cell ! If true, PCM remapping should be used in a cell. type(group_pass_type) :: tmp_pass_uv_T_S_h, pass_uv_T_S_h @@ -2800,18 +2820,21 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, restart_CSp, & call adjustGridForIntegrity(CS%ALE_CSp, G, GV, CS%h ) call pre_ALE_adjustments(G, GV, US, CS%h, CS%tv, CS%tracer_Reg, CS%ALE_CSp, CS%u, CS%v) - call callTree_waypoint("Calling ALE_main() to remap initial conditions (initialize_MOM)") + call callTree_waypoint("Calling ALE_regrid() to remap initial conditions (initialize_MOM)") allocate(h_new(isd:ied, jsd:jed, nz), source=0.0) allocate(dzRegrid(isd:ied, jsd:jed, nz+1), source=0.0) allocate(PCM_cell(isd:ied, jsd:jed, nz), source=.false.) if (use_ice_shelf) then - call ALE_main(G, GV, US, CS%h, h_new, dzRegrid, CS%u, CS%v, CS%tv, CS%tracer_Reg, CS%ALE_CSp, & - CS%OBC, frac_shelf_h=CS%frac_shelf_h, PCM_cell=PCM_cell) + call ALE_regrid(G, GV, US, CS%h, h_new, dzRegrid, CS%tv, CS%ALE_CSp, CS%frac_shelf_h, PCM_cell) else - call ALE_main(G, GV, US, CS%h, h_new, dzRegrid, CS%u, CS%v, CS%tv, CS%tracer_Reg, CS%ALE_CSp, & - CS%OBC, PCM_cell=PCM_cell) + call ALE_regrid(G, GV, US, CS%h, h_new, dzRegrid, CS%tv, CS%ALE_CSp, PCM_cell=PCM_cell) endif + if (callTree_showQuery()) call callTree_waypoint("new grid generated") + ! Remap all variables from the old grid h onto the new grid h_new + call ALE_remap_tracers(CS%ALE_CSp, G, GV, CS%h, h_new, CS%tracer_Reg, CS%debug, PCM_cell=PCM_cell) + call ALE_remap_velocities(CS%ALE_CSp, G, GV, CS%h, h_new, CS%u, CS%v, CS%OBC, dzRegrid, debug=CS%debug) + ! Replace the old grid with new one. All remapping must be done at this point. !$OMP parallel do default(shared) do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1 diff --git a/src/tracer/MOM_offline_main.F90 b/src/tracer/MOM_offline_main.F90 index 279a9a70e2..bf06fc294e 100644 --- a/src/tracer/MOM_offline_main.F90 +++ b/src/tracer/MOM_offline_main.F90 @@ -4,8 +4,9 @@ module MOM_offline_main ! This file is part of MOM6. See LICENSE.md for the license. -use MOM_ALE, only : ALE_CS, ALE_main_offline, ALE_offline_inputs +use MOM_ALE, only : ALE_CS, ALE_regrid, ALE_offline_inputs use MOM_ALE, only : pre_ALE_adjustments, ALE_update_regrid_weights +use MOM_ALE, only : ALE_remap_tracers use MOM_checksums, only : hchksum, uvchksum use MOM_coms, only : reproducing_sum use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end @@ -119,7 +120,7 @@ module MOM_offline_main real :: minimum_forcing_depth !< The smallest depth over which fluxes can be applied [H ~> m or kg m-2]. !! This is copied from diabatic_CS controlling how tracers follow freshwater fluxes - real :: Kd_max !< Runtime parameter specifying the maximum value of vertical diffusivity + real :: Kd_max !< Runtime parameter specifying the maximum value of vertical diffusivity [Z2 T-1 ~> m2 s-1] real :: min_residual !< The minimum amount of total mass flux before exiting the main advection !! routine [H L2 ~> m3 or kg] !>@{ Diagnostic manager IDs for some fields that may be of interest when doing offline transport @@ -170,8 +171,6 @@ module MOM_offline_main real, allocatable, dimension(:,:,:) :: Kd !< Vertical diffusivity [Z2 T-1 ~> m2 s-1] real, allocatable, dimension(:,:,:) :: h_end !< Thicknesses at the end of offline timestep [H ~> m or kg m-2] - real, allocatable, dimension(:,:) :: netMassIn !< Freshwater fluxes into the ocean - real, allocatable, dimension(:,:) :: netMassOut !< Freshwater fluxes out of the ocean real, allocatable, dimension(:,:) :: mld !< Mixed layer depths at thickness points [Z ~> m] ! Allocatable arrays to read in entire fields during initialization @@ -354,11 +353,17 @@ subroutine offline_advection_ale(fluxes, Time_start, time_interval, G, GV, US, C call ALE_update_regrid_weights(CS%dt_offline, CS%ALE_CSp) call pre_ALE_adjustments(G, GV, US, h_new, CS%tv, CS%tracer_Reg, CS%ALE_CSp) - ! Adjust the target grids for diagnostics, in case there have been thickness adjustments. + ! Uncomment this to adjust the target grids for diagnostics, if there have been thickness + ! adjustments, but the offline tracer code does not yet have the other corresponding calls + ! that would be needed to support remapping its output. ! call diag_update_remap_grids(CS%diag, alt_h=h_new) - call ALE_main_offline(G, GV, h_new, h_post_remap, dzRegrid, CS%tv, CS%tracer_Reg, & - CS%ALE_CSp, CS%OBC, CS%dt_offline) + call ALE_regrid(G, GV, US, h_new, h_post_remap, dzRegrid, CS%tv, CS%ALE_CSp) + + ! Remap all variables from the old grid h_new onto the new grid h_post_remap + call ALE_remap_tracers(CS%ALE_CSp, G, GV, h_new, h_post_remap, CS%tracer_Reg, & + CS%debug, dt=CS%dt_offline) + do k=1,nz ; do j=js-1,je+1 ; do i=is-1,ie+1 h_new(i,j,k) = h_post_remap(i,j,k) enddo ; enddo ; enddo @@ -760,6 +765,7 @@ subroutine offline_fw_fluxes_into_ocean(G, GV, CS, fluxes, h, in_flux_optional) real, dimension(SZI_(G),SZJ_(G)), & optional, intent(in) :: in_flux_optional !< The total time-integrated amount !! of tracer that leaves with freshwater + !! [CU H ~> Conc m or Conc kg m-2] integer :: i, j, m real, dimension(SZI_(G),SZJ_(G)) :: negative_fw !< store all negative fluxes [H ~> m or kg m-2] @@ -810,6 +816,7 @@ subroutine offline_fw_fluxes_out_ocean(G, GV, CS, fluxes, h, out_flux_optional) real, dimension(SZI_(G),SZJ_(G)), & optional, intent(in) :: out_flux_optional !< The total time-integrated amount !! of tracer that leaves with freshwater + !! [CU H ~> Conc m or Conc kg m-2] integer :: m logical :: update_h !< Flag for whether h should be updated @@ -1458,8 +1465,6 @@ subroutine offline_transport_init(param_file, CS, diabatic_CSp, G, GV, US) allocate(CS%eatr(isd:ied,jsd:jed,nz), source=0.0) allocate(CS%ebtr(isd:ied,jsd:jed,nz), source=0.0) allocate(CS%h_end(isd:ied,jsd:jed,nz), source=0.0) - allocate(CS%netMassOut(G%isd:G%ied,G%jsd:G%jed), source=0.0) - allocate(CS%netMassIn(G%isd:G%ied,G%jsd:G%jed), source=0.0) allocate(CS%Kd(isd:ied,jsd:jed,nz+1), source=0.0) if (CS%read_mld) allocate(CS%mld(G%isd:G%ied,G%jsd:G%jed), source=0.0) @@ -1532,8 +1537,6 @@ subroutine offline_transport_end(CS) deallocate(CS%eatr) deallocate(CS%ebtr) deallocate(CS%h_end) - deallocate(CS%netMassOut) - deallocate(CS%netMassIn) deallocate(CS%Kd) if (CS%read_mld) deallocate(CS%mld) if (CS%read_all_ts_uvh) then From cd2852a151fef9ed86e179165caa97f85e7a255c Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Wed, 14 Sep 2022 07:32:16 -0400 Subject: [PATCH 62/68] Document variable units in core modules Revised or extended comments to correct or more clearly document the units of 49 internal variables in modules in the core directory. All answers are bitwise identical. --- src/core/MOM_CoriolisAdv.F90 | 17 +++--- src/core/MOM_PressureForce_FV.F90 | 20 +++---- src/core/MOM_PressureForce_Montgomery.F90 | 5 +- src/core/MOM_barotropic.F90 | 6 +-- src/core/MOM_continuity.F90 | 4 +- src/core/MOM_continuity_PPM.F90 | 64 +++++++++++------------ 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/core/MOM_CoriolisAdv.F90 b/src/core/MOM_CoriolisAdv.F90 index 154db3eaa3..3289786fd0 100644 --- a/src/core/MOM_CoriolisAdv.F90 +++ b/src/core/MOM_CoriolisAdv.F90 @@ -49,10 +49,10 @@ module MOM_CoriolisAdv real :: F_eff_max_blend !< The factor by which the maximum effective Coriolis !! acceleration from any point can be increased when !! blending different discretizations with the - !! ARAKAWA_LAMB_BLEND Coriolis scheme. This must be - !! greater than 2.0, and is 4.0 by default. + !! ARAKAWA_LAMB_BLEND Coriolis scheme [nondim]. + !! This must be greater than 2.0, and is 4.0 by default. real :: wt_lin_blend !< A weighting value beyond which the blending between - !! Sadourny and Arakawa & Hsu goes linearly to 0. + !! Sadourny and Arakawa & Hsu goes linearly to 0 [nondim]. !! This must be between 1 and 1e-15, often 1/8. logical :: no_slip !< If true, no slip boundary conditions are used. !! Otherwise free slip boundary conditions are assumed. @@ -173,9 +173,10 @@ subroutine CorAdCalc(u, v, h, uh, vh, CAu, CAv, OBC, AD, G, GV, US, CS, pbv, Wav ! KEy = d/dy KE. vh_center ! Transport based on arithmetic mean h at v-points [H L2 T-1 ~> m3 s-1 or kg s-1] real, dimension(SZI_(G),SZJ_(G)) :: & - uh_min, uh_max, & ! The smallest and largest estimates of the volume - vh_min, vh_max, & ! fluxes through the faces (i.e. u*h*dy & v*h*dx) - ! [H L2 T-1 ~> m3 s-1 or kg s-1]. + uh_min, uh_max, & ! The smallest and largest estimates of the zonal volume fluxes through + ! the faces (i.e. u*h*dy) [H L2 T-1 ~> m3 s-1 or kg s-1] + vh_min, vh_max, & ! The smallest and largest estimates of the meridional volume fluxes through + ! the faces (i.e. v*h*dx) [H L2 T-1 ~> m3 s-1 or kg s-1] ep_u, ep_v ! Additional pseudo-Coriolis terms in the Arakawa and Lamb ! discretization [H-1 T-1 ~> m-1 s-1 or m2 kg-1 s-1]. real, dimension(SZIB_(G),SZJB_(G)) :: & @@ -195,8 +196,8 @@ subroutine CorAdCalc(u, v, h, uh, vh, CAu, CAv, OBC, AD, G, GV, US, CS, pbv, Wav real :: max_fv, max_fu ! The maximum or minimum of the neighboring Coriolis real :: min_fv, min_fu ! accelerations [L T-2 ~> m s-2], i.e. max(min)_fu(v)q. - real, parameter :: C1_12=1.0/12.0 ! C1_12 = 1/12 - real, parameter :: C1_24=1.0/24.0 ! C1_24 = 1/24 + real, parameter :: C1_12 = 1.0 / 12.0 ! C1_12 = 1/12 [nondim] + real, parameter :: C1_24 = 1.0 / 24.0 ! C1_24 = 1/24 [nondim] real :: max_Ihq, min_Ihq ! The maximum and minimum of the nearby Ihq [H-1 ~> m-1 or m2 kg-1]. real :: hArea_q ! The sum of area times thickness of the cells ! surrounding a q point [H L2 ~> m3 or kg]. diff --git a/src/core/MOM_PressureForce_FV.F90 b/src/core/MOM_PressureForce_FV.F90 index e700507290..a35effa5c0 100644 --- a/src/core/MOM_PressureForce_FV.F90 +++ b/src/core/MOM_PressureForce_FV.F90 @@ -103,10 +103,10 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ S_tmp ! Temporary array of salinities where layers that are lighter ! than the mixed layer have the mixed layer's properties [S ~> ppt]. real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & - S_t, & ! Top and bottom edge values for linear reconstructions - S_b, & ! of salinity within each layer [S ~> ppt]. - T_t, & ! Top and bottom edge values for linear reconstructions - T_b ! of temperature within each layer [C ~> degC]. + S_t, S_b, & ! Top and bottom edge values for linear reconstructions + ! of salinity within each layer [S ~> ppt]. + T_t, T_b ! Top and bottom edge values for linear reconstructions + ! of temperature within each layer [C ~> degC]. real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & dza, & ! The change in geopotential anomaly between the top and bottom ! of a layer [L2 T-2 ~> m2 s-2]. @@ -155,7 +155,7 @@ subroutine PressureForce_FV_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_ real :: H_to_RL2_T2 ! A factor to convert from thickness units (H) to pressure ! units [R L2 T-2 H-1 ~> Pa m-1 or Pa m2 kg-1]. ! real :: oneatm ! 1 standard atmosphere of pressure in [R L2 T-2 ~> Pa] - real, parameter :: C1_6 = 1.0/6.0 + real, parameter :: C1_6 = 1.0/6.0 ! [nondim] integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, nkmb integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state integer :: i, j, k @@ -472,10 +472,10 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm S_tmp ! Temporary array of salinities where layers that are lighter ! than the mixed layer have the mixed layer's properties [S ~> ppt]. real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: & - S_t, & ! Top and bottom edge values for linear reconstructions - S_b, & ! of salinity within each layer [S ~> ppt]. - T_t, & ! Top and bottom edge values for linear reconstructions - T_b ! of temperature within each layer [C ~> degC]. + S_t, S_b, & ! Top and bottom edge values for linear reconstructions + ! of salinity within each layer [S ~> ppt]. + T_t, T_b ! Top and bottom edge values for linear reconstructions + ! of temperature within each layer [C ~> degC]. real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: & rho_pgf, rho_stanley_pgf ! Density [kg m-3] from EOS with and without SGS T variance ! in Stanley parameterization. @@ -497,7 +497,7 @@ subroutine PressureForce_FV_Bouss(h, tv, PFu, PFv, G, GV, US, CS, ALE_CSp, p_atm logical :: use_ALE ! If true, use an ALE pressure reconstruction. logical :: use_EOS ! If true, density is calculated from T & S using an equation of state. type(thermo_var_ptrs) :: tv_tmp! A structure of temporary T & S. - real, parameter :: C1_6 = 1.0/6.0 + real, parameter :: C1_6 = 1.0/6.0 ! [nondim] integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz, nkmb integer :: i, j, k diff --git a/src/core/MOM_PressureForce_Montgomery.F90 b/src/core/MOM_PressureForce_Montgomery.F90 index 003bd2c3ec..1ae4a8709a 100644 --- a/src/core/MOM_PressureForce_Montgomery.F90 +++ b/src/core/MOM_PressureForce_Montgomery.F90 @@ -122,7 +122,8 @@ subroutine PressureForce_Mont_nonBouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pb real :: I_gEarth ! The inverse of g_Earth [T2 Z L-2 ~> s2 m-1] ! real :: dalpha - real :: Pa_to_H ! A factor to convert from R L2 T-2 to the thickness units (H). + real :: Pa_to_H ! A factor to convert from R L2 T-2 to the thickness units (H) + ! [H T2 R-1 L-2 ~> m2 s2 kg-1 or s2 m-1]. real :: alpha_Lay(SZK_(GV)) ! The specific volume of each layer [R-1 ~> m3 kg-1]. real :: dalpha_int(SZK_(GV)+1) ! The change in specific volume across each ! interface [R-1 ~> m3 kg-1]. @@ -380,7 +381,7 @@ subroutine PressureForce_Mont_Bouss(h, tv, PFu, PFv, G, GV, US, CS, p_atm, pbce, M, & ! The Montgomery potential, M = (p/rho + gz) [L2 T-2 ~> m2 s-2]. rho_star ! In-situ density divided by the derivative with depth of the ! corrected e times (G_Earth/Rho0) [L2 Z-1 T-2 ~> m s-2]. - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: e ! Interface height in m. + real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1) :: e ! Interface height [Z ~> m]. ! e may be adjusted (with a nonlinear equation of state) so that ! its derivative compensates for the adiabatic compressibility ! in seawater, but e will still be close to the interface depth. diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index 67f07db3f3..32b7a1209f 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -697,7 +697,7 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, real :: I_sum_wt_accel ! The inverse of the sum of the raw weights used to find average accelerations [nondim] real :: I_sum_wt_trans ! The inverse of the sum of the raw weights used to find average transports [nondim] real :: dt_filt ! The half-width of the barotropic filter [T ~> s]. - real :: trans_wt1, trans_wt2 ! The weights used to compute ubt_trans and vbt_trans + real :: trans_wt1, trans_wt2 ! The weights used to compute ubt_trans and vbt_trans [nondim] integer :: nfilter logical :: apply_OBCs, apply_OBC_flather, apply_OBC_open @@ -2889,7 +2889,7 @@ subroutine apply_velocity_OBCs(OBC, ubt, vbt, uhbt, vhbt, ubt_trans, vbt_trans, integer, intent(in) :: halo !< The extra halo size to use here. real, intent(in) :: dtbt !< The time step [T ~> s]. real, intent(in) :: bebt !< The fractional weighting of the future velocity - !! in determining the transport. + !! in determining the transport [nondim] logical, intent(in) :: use_BT_cont !< If true, use the BT_cont_types to calculate !! transports. logical, intent(in) :: integral_BT_cont !< If true, update the barotropic continuity @@ -4299,7 +4299,7 @@ subroutine barotropic_init(u, v, h, eta, Time, G, GV, US, param_file, diag, CS, real :: mean_SL ! The mean sea level that is used along with the bathymetry to estimate the ! geometry when LINEARIZED_BT_CORIOLIS is true or BT_NONLIN_STRESS is false [Z ~> m]. real :: det_de ! The partial derivative due to self-attraction and loading of the reference - ! geopotential with the sea surface height when tides are enabled. + ! geopotential with the sea surface height when tides are enabled [nondim]. ! This is typically ~0.09 or less. real, allocatable :: lin_drag_h(:,:) ! A spatially varying linear drag coefficient at tracer points ! that acts on the barotropic flow [Z T-1 ~> m s-1]. diff --git a/src/core/MOM_continuity.F90 b/src/core/MOM_continuity.F90 index 541dcde66a..76e1bbc623 100644 --- a/src/core/MOM_continuity.F90 +++ b/src/core/MOM_continuity.F90 @@ -71,12 +71,12 @@ subroutine continuity(u, v, hin, h, uh, vh, dt, G, GV, US, CS, OBC, pbv, uhbt, v real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & optional, intent(in) :: visc_rem_u !< Both the fraction of !! zonal momentum that remains after a time-step of viscosity, and the fraction of a time-step's - !! worth of a barotropic acceleration that a layer experiences after viscosity is applied. + !! worth of a barotropic acceleration that a layer experiences after viscosity is applied [nondim]. !! Non-dimensional between 0 (at the bottom) and 1 (far above the bottom). real, dimension(SZI_(G),SZJB_(G),SZK_(GV)), & optional, intent(in) :: visc_rem_v !< Both the fraction of !! meridional momentum that remains after a time-step of viscosity, and the fraction of a time-step's - !! worth of a barotropic acceleration that a layer experiences after viscosity is applied. + !! worth of a barotropic acceleration that a layer experiences after viscosity is applied [nondim]. !! Non-dimensional between 0 (at the bottom) and 1 (far above the bottom). real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & optional, intent(out) :: u_cor !< The zonal velocities that diff --git a/src/core/MOM_continuity_PPM.F90 b/src/core/MOM_continuity_PPM.F90 index 59d119f5d4..54eecd20c3 100644 --- a/src/core/MOM_continuity_PPM.F90 +++ b/src/core/MOM_continuity_PPM.F90 @@ -240,7 +240,7 @@ subroutine zonal_mass_flux(u, h_in, uh, dt, G, GV, US, CS, LB, OBC, por_face_are real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)), & optional, intent(out) :: u_cor !< The zonal velocities (u with a barotropic correction) - !! that give uhbt as the depth-integrated transport, m s-1. + !! that give uhbt as the depth-integrated transport [L T-1 ~> m s-1] type(BT_cont_type), optional, pointer :: BT_cont !< A structure with elements that describe the !! effective open face areas as a function of barotropic flow. @@ -249,8 +249,8 @@ subroutine zonal_mass_flux(u, h_in, uh, dt, G, GV, US, CS, LB, OBC, por_face_are real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: h_L, h_R ! Left and right face thicknesses [H ~> m or kg m-2]. real, dimension(SZIB_(G)) :: & du, & ! Corrective barotropic change in the velocity [L T-1 ~> m s-1]. - du_min_CFL, & ! Min/max limits on du correction - du_max_CFL, & ! to avoid CFL violations [L T-1 ~> m s-1] + du_min_CFL, & ! Lower limit on du correction to avoid CFL violations [L T-1 ~> m s-1] + du_max_CFL, & ! Upper limit on du correction to avoid CFL violations [L T-1 ~> m s-1] duhdu_tot_0, & ! Summed partial derivative of uh with u [H L ~> m2 or kg m-1]. uh_tot_0, & ! Summed transport with no barotropic correction [H L2 T-1 ~> m3 s-1 or kg s-1]. visc_rem_max ! The column maximum of visc_rem [nondim]. @@ -259,7 +259,7 @@ subroutine zonal_mass_flux(u, h_in, uh, dt, G, GV, US, CS, LB, OBC, por_face_are visc_rem ! A 2-D copy of visc_rem_u or an array of 1's [nondim]. real, dimension(SZIB_(G)) :: FAuI ! A list of sums of zonal face areas [H L ~> m2 or kg m-1]. real :: FA_u ! A sum of zonal face areas [H L ~> m2 or kg m-1]. - real :: I_vrm ! 1.0 / visc_rem_max, nondim. + real :: I_vrm ! 1.0 / visc_rem_max [nondim] real :: CFL_dt ! The maximum CFL ratio of the adjusted velocities divided by ! the time step [T-1 ~> s-1]. real :: I_dt ! 1.0 / dt [T-1 ~> s-1]. @@ -537,7 +537,7 @@ subroutine zonal_flux_layer(u, h, h_L, h_R, uh, duhdu, visc_rem, dt, G, US, j, & !! transport [H L2 T-1 ~> m3 s-1 or kg s-1]. real, dimension(SZIB_(G)), intent(inout) :: duhdu !< Partial derivative of uh !! with u [H L ~> m2 or kg m-1]. - real, intent(in) :: dt !< Time increment [T ~> s]. + real, intent(in) :: dt !< Time increment [T ~> s] type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type integer, intent(in) :: j !< Spatial index. integer, intent(in) :: ish !< Start of index range. @@ -549,8 +549,7 @@ subroutine zonal_flux_layer(u, h, h_L, h_R, uh, duhdu, visc_rem, dt, G, US, j, & type(ocean_OBC_type), optional, pointer :: OBC !< Open boundaries control structure. ! Local variables real :: CFL ! The CFL number based on the local velocity and grid spacing [nondim] - real :: curv_3 ! A measure of the thickness curvature over a grid length, - ! with the same units as h_in. + real :: curv_3 ! A measure of the thickness curvature over a grid length [H ~> m or kg m-2] real :: h_marg ! The marginal thickness of a flux [H ~> m or kg m-2]. integer :: i integer :: l_seg @@ -639,8 +638,7 @@ subroutine zonal_face_thickness(u, h, h_L, h_R, h_u, dt, G, GV, US, LB, vol_CFL, ! Local variables real :: CFL ! The CFL number based on the local velocity and grid spacing [nondim] - real :: curv_3 ! A measure of the thickness curvature over a grid length, - ! with the same units as h_in. + real :: curv_3 ! A measure of the thickness curvature over a grid length [H ~> m or kg m-2] real :: h_avg ! The average thickness of a flux [H ~> m or kg m-2]. real :: h_marg ! The marginal thickness of a flux [H ~> m or kg m-2]. logical :: local_open_BC @@ -772,10 +770,10 @@ subroutine zonal_flux_adjust(u, h_in, h_L, h_R, uhbt, uh_tot_0, duhdu_tot_0, & uh_err_best, & ! The smallest value of uh_err found so far [H L2 T-1 ~> m3 s-1 or kg s-1]. u_new, & ! The velocity with the correction added [L T-1 ~> m s-1]. duhdu_tot,&! Summed partial derivative of uh with u [H L ~> m2 or kg m-1]. - du_min, & ! Min/max limits on du correction based on CFL limits - du_max ! and previous iterations [L T-1 ~> m s-1]. + du_min, & ! Lower limit on du correction based on CFL limits and previous iterations [L T-1 ~> m s-1] + du_max ! Upper limit on du correction based on CFL limits and previous iterations [L T-1 ~> m s-1] real :: du_prev ! The previous value of du [L T-1 ~> m s-1]. - real :: ddu ! The change in du from the previous iteration [L T-1 ~> m s-1]. + real :: ddu ! The change in du from the previous iteration [L T-1 ~> m s-1]. real :: tol_eta ! The tolerance for the current iteration [H ~> m or kg m-2]. real :: tol_vel ! The tolerance for velocity in the current iteration [L T-1 ~> m s-1]. integer :: i, k, nz, itt, max_itts = 20 @@ -915,7 +913,7 @@ subroutine set_zonal_BT_cont(u, h_in, h_L, h_R, BT_cont, uh_tot_0, duhdu_tot_0, du0, & ! The barotropic velocity increment that gives 0 transport [L T-1 ~> m s-1]. duL, duR, & ! The barotropic velocity increments that give the westerly ! (duL) and easterly (duR) test velocities [L T-1 ~> m s-1]. - zeros, & ! An array of full of 0's. + zeros, & ! An array of full of 0 transports [H L2 T-1 ~> m3 s-1 or kg s-1] du_CFL, & ! The velocity increment that corresponds to CFL_min [L T-1 ~> m s-1]. u_L, u_R, & ! The westerly (u_L), easterly (u_R), and zero-barotropic u_0, & ! transport (u_0) layer test velocities [L T-1 ~> m s-1]. @@ -936,7 +934,7 @@ subroutine set_zonal_BT_cont(u, h_in, h_L, h_R, BT_cont, uh_tot_0, duhdu_tot_0, ! from leading to large CFL numbers. real :: min_visc_rem ! The smallest permitted value for visc_rem that is used ! in finding the barotropic velocity that changes the - ! flow direction. This is necessary to keep the inverse + ! flow direction [nondim]. This is necessary to keep the inverse ! of visc_rem from leading to large CFL numbers. real :: CFL_min ! A minimal increment in the CFL to try to ensure that the ! flow is truly upwind [nondim] @@ -1076,17 +1074,17 @@ subroutine meridional_mass_flux(v, h_in, vh, dt, G, GV, US, CS, LB, OBC, por_fac h_L, h_R ! Left and right face thicknesses [H ~> m or kg m-2]. real, dimension(SZI_(G)) :: & dv, & ! Corrective barotropic change in the velocity [L T-1 ~> m s-1]. - dv_min_CFL, & ! Min/max limits on dv correction - dv_max_CFL, & ! to avoid CFL violations + dv_min_CFL, & ! Lower limit on dv correction to avoid CFL violations [L T-1 ~> m s-1] + dv_max_CFL, & ! Upper limit on dv correction to avoid CFL violations [L T-1 ~> m s-1] dvhdv_tot_0, & ! Summed partial derivative of vh with v [H L ~> m2 or kg m-1]. vh_tot_0, & ! Summed transport with no barotropic correction [H L2 T-1 ~> m3 s-1 or kg s-1]. - visc_rem_max ! The column maximum of visc_rem. + visc_rem_max ! The column maximum of visc_rem [nondim] logical, dimension(SZI_(G)) :: do_I real, dimension(SZI_(G)) :: FAvi ! A list of sums of meridional face areas [H L ~> m2 or kg m-1]. real :: FA_v ! A sum of meridional face areas [H L ~> m2 or kg m-1]. real, dimension(SZI_(G),SZK_(GV)) :: & - visc_rem ! A 2-D copy of visc_rem_v or an array of 1's. - real :: I_vrm ! 1.0 / visc_rem_max, nondim. + visc_rem ! A 2-D copy of visc_rem_v or an array of 1's [nondim] + real :: I_vrm ! 1.0 / visc_rem_max [nondim] real :: CFL_dt ! The maximum CFL ratio of the adjusted velocities divided by ! the time step [T-1 ~> s-1]. real :: I_dt ! 1.0 / dt [T-1 ~> s-1]. @@ -1598,8 +1596,8 @@ subroutine meridional_flux_adjust(v, h_in, h_L, h_R, vhbt, vh_tot_0, dvhdv_tot_0 vh_err_best, & ! The smallest value of vh_err found so far [H L2 T-1 ~> m3 s-1 or kg s-1]. v_new, & ! The velocity with the correction added [L T-1 ~> m s-1]. dvhdv_tot,&! Summed partial derivative of vh with u [H L ~> m2 or kg m-1]. - dv_min, & ! Min/max limits on dv correction based on CFL limits - dv_max ! and previous iterations [L T-1 ~> m s-1]. + dv_min, & ! Lower limit on dv correction based on CFL limits and previous iterations [L T-1 ~> m s-1] + dv_max ! Upper limit on dv correction based on CFL limits and previous iterations [L T-1 ~> m s-1] real :: dv_prev ! The previous value of dv [L T-1 ~> m s-1]. real :: ddv ! The change in dv from the previous iteration [L T-1 ~> m s-1]. real :: tol_eta ! The tolerance for the current iteration [H ~> m or kg m-2]. @@ -1741,7 +1739,7 @@ subroutine set_merid_BT_cont(v, h_in, h_L, h_R, BT_cont, vh_tot_0, dvhdv_tot_0, dv0, & ! The barotropic velocity increment that gives 0 transport [L T-1 ~> m s-1]. dvL, dvR, & ! The barotropic velocity increments that give the southerly ! (dvL) and northerly (dvR) test velocities [L T-1 ~> m s-1]. - zeros, & ! An array of full of 0's. + zeros, & ! An array of full of 0 transports [H L2 T-1 ~> m3 s-1 or kg s-1] dv_CFL, & ! The velocity increment that corresponds to CFL_min [L T-1 ~> m s-1]. v_L, v_R, & ! The southerly (v_L), northerly (v_R), and zero-barotropic v_0, & ! transport (v_0) layer test velocities [L T-1 ~> m s-1]. @@ -1871,7 +1869,7 @@ subroutine PPM_reconstruction_x(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_ !! [H ~> m or kg m-2]. type(loop_bounds_type), intent(in) :: LB !< Active loop bounds structure. real, intent(in) :: h_min !< The minimum thickness - !! that can be obtained by a concave parabolic fit. + !! that can be obtained by a concave parabolic fit [H ~> m or kg m-2] logical, intent(in) :: monotonic !< If true, use the !! Colella & Woodward monotonic limiter. !! Otherwise use a simple positive-definite limiter. @@ -1881,8 +1879,8 @@ subroutine PPM_reconstruction_x(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_ type(ocean_OBC_type), pointer :: OBC !< Open boundaries control structure. ! Local variables with useful mnemonic names. - real, dimension(SZI_(G),SZJ_(G)) :: slp ! The slopes. - real, parameter :: oneSixth = 1./6. + real, dimension(SZI_(G),SZJ_(G)) :: slp ! The slopes per grid point [H ~> m or kg m-2] + real, parameter :: oneSixth = 1./6. ! [nondim] real :: h_ip1, h_im1 ! Neighboring thicknesses or sensibly extrapolated values [H ~> m or kg m-2] real :: dMx, dMn ! The difference between the local thickness and the maximum (dMx) or ! minimum (dMn) of the surrounding values [H ~> m or kg m-2] @@ -2007,7 +2005,7 @@ subroutine PPM_reconstruction_y(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_ !! [H ~> m or kg m-2]. type(loop_bounds_type), intent(in) :: LB !< Active loop bounds structure. real, intent(in) :: h_min !< The minimum thickness - !! that can be obtained by a concave parabolic fit. + !! that can be obtained by a concave parabolic fit [H ~> m or kg m-2] logical, intent(in) :: monotonic !< If true, use the !! Colella & Woodward monotonic limiter. !! Otherwise use a simple positive-definite limiter. @@ -2017,8 +2015,8 @@ subroutine PPM_reconstruction_y(h_in, h_L, h_R, G, LB, h_min, monotonic, simple_ type(ocean_OBC_type), pointer :: OBC !< Open boundaries control structure. ! Local variables with useful mnemonic names. - real, dimension(SZI_(G),SZJ_(G)) :: slp ! The slopes. - real, parameter :: oneSixth = 1./6. + real, dimension(SZI_(G),SZJ_(G)) :: slp ! The slopes per grid point [H ~> m or kg m-2] + real, parameter :: oneSixth = 1./6. ! [nondim] real :: h_jp1, h_jm1 ! Neighboring thicknesses or sensibly extrapolated values [H ~> m or kg m-2] real :: dMx, dMn ! The difference between the local thickness and the maximum (dMx) or ! minimum (dMn) of the surrounding values [H ~> m or kg m-2] @@ -2141,7 +2139,7 @@ subroutine PPM_limit_pos(h_in, h_L, h_R, h_min, G, iis, iie, jis, jie) real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: h_L !< Left thickness in the reconstruction [H ~> m or kg m-2]. real, dimension(SZI_(G),SZJ_(G)), intent(inout) :: h_R !< Right thickness in the reconstruction [H ~> m or kg m-2]. real, intent(in) :: h_min !< The minimum thickness - !! that can be obtained by a concave parabolic fit. + !! that can be obtained by a concave parabolic fit [H ~> m or kg m-2] integer, intent(in) :: iis !< Start of i index range. integer, intent(in) :: iie !< End of i index range. integer, intent(in) :: jis !< Start of j index range. @@ -2218,10 +2216,10 @@ end subroutine PPM_limit_CW84 !> Return the maximum ratio of a/b or maxrat. function ratio_max(a, b, maxrat) result(ratio) - real, intent(in) :: a !< Numerator - real, intent(in) :: b !< Denominator - real, intent(in) :: maxrat !< Maximum value of ratio. - real :: ratio !< Return value. + real, intent(in) :: a !< Numerator, in arbitrary units [A] + real, intent(in) :: b !< Denominator, in arbitrary units [B] + real, intent(in) :: maxrat !< Maximum value of ratio [A B-1] + real :: ratio !< Return value [A B-1] if (abs(a) > abs(maxrat*b)) then ratio = maxrat From 181e115e850d7826fb9cb82cdec95e23346bcbce Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Mon, 19 Sep 2022 18:28:00 -0400 Subject: [PATCH 63/68] *+Corrected some oblique OBC restarts Added separate restart variables for some of the oblique OBC restart variables at north-south or east-west faces. Before this fix, some of the full 3-d arrays for restarts were being overwritten for oblique OBC segments that join at adjacent corners on the north-east side of tracer points, as is noted in github.com/NOAA-GFDL/MOM6/issues/208. The discussion surrounding this issue confirms that there are cases using oblique OBCs (like some North-West Atlantic cases) that do not past the restart reproducibility tests. Some halo updates were also corrected, in accordance with the introduction of these new variables and their proper staggering locations. In a number of places, the proper case-sensitive horizontal indexing convention, as described in github.com/NOAA-GFDL/MOM6/wiki/Code-style-guide#soft-case-convention, is now being used. This commit also corrected the comments describing a number of the variables in the OBC_segment_type to make it clearer where they are discretized. This changes the names of oblique OBC-related variables in the restart files, but since the previous version did not reproduce across restarts, there does not seem to be any point in retaining those incorrect answers. All answers in the MOM6-examples test suite are bitwise identical, but this will change (fix) solutions with oblique OBCs and OBC_RAD_VEL_WT < 1. --- src/core/MOM_open_boundary.F90 | 242 +++++++++++++++++---------------- 1 file changed, 123 insertions(+), 119 deletions(-) diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index cabf9272ca..a187ac5b2f 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -116,7 +116,8 @@ module MOM_open_boundary !! Not sure who should lock it or when... end type segment_tracer_registry_type -!> Open boundary segment data structure. +!> Open boundary segment data structure. Unless otherwise noted, 2-d and 3-d arrays are discretized +!! at the same position as normal velocity points in the middle of the OBC segments. type, public :: OBC_segment_type logical :: Flather !< If true, applies Flather + Chapman radiation of barotropic gravity waves. logical :: radiation !< If true, 1D Orlanksi radiation boundary conditions are applied. @@ -178,10 +179,10 @@ module MOM_open_boundary real, allocatable :: h(:,:,:) !< The cell thickness [H ~> m or kg m-2] at OBC-points. real, allocatable :: normal_vel(:,:,:) !< The layer velocity normal to the OB !! segment [L T-1 ~> m s-1]. - real, allocatable :: tangential_vel(:,:,:) !< The layer velocity tangential to the - !! OB segment [L T-1 ~> m s-1]. - real, allocatable :: tangential_grad(:,:,:) !< The gradient of the velocity tangential - !! to the OB segment [T-1 ~> s-1]. + real, allocatable :: tangential_vel(:,:,:) !< The layer velocity tangential to the OB segment + !! [L T-1 ~> m s-1], discretized at the corner points. + real, allocatable :: tangential_grad(:,:,:) !< The gradient of the velocity tangential to the OB + !! segment [T-1 ~> s-1], discretized at the corner points. real, allocatable :: normal_trans(:,:,:) !< The layer transport normal to the OB !! segment [H L2 T-1 ~> m3 s-1]. real, allocatable :: normal_vel_bt(:,:) !< The barotropic velocity normal to @@ -189,25 +190,38 @@ module MOM_open_boundary real, allocatable :: eta(:,:) !< The sea-surface elevation along the !! segment [H ~> m or kg m-2]. real, allocatable :: grad_normal(:,:,:) !< The gradient of the normal flow along the - !! segment times the grid spacing [L T-1 ~> m s-1] + !! segment times the grid spacing [L T-1 ~> m s-1], + !! with the first index being the corner-point index + !! along the segment, and the second index being 1 (for + !! values one point into the domain) or 2 (for values + !! along the OBC itself) real, allocatable :: grad_tan(:,:,:) !< The gradient of the tangential flow along the - !! segment times the grid spacing [L T-1 ~> m s-1] - real, allocatable :: grad_gradient(:,:,:) !< The gradient of the gradient of tangential flow along - !! the segment times the grid spacing [T-1 ~> s-1] + !! segment times the grid spacing [L T-1 ~> m s-1], with the + !! first index being the velocity/tracer point index along the + !! segment, and the second being 1 for the value 1.5 points + !! inside the domain and 2 for the value half a point + !! inside the domain. + real, allocatable :: grad_gradient(:,:,:) !< The gradient normal to the segment of the gradient + !! tangetial to the segment of tangential flow along the segment + !! times the grid spacing [T-1 ~> s-1], with the first + !! index being the velocity/tracer point index along the segment, + !! and the second being 1 for the value 2 points into the domain + !! and 2 for the value 1 point into the domain. real, allocatable :: rx_norm_rad(:,:,:) !< The previous normal phase speed use for EW radiation !! OBC, in grid points per timestep [nondim] real, allocatable :: ry_norm_rad(:,:,:) !< The previous normal phase speed use for NS radiation !! OBC, in grid points per timestep [nondim] - real, allocatable :: rx_norm_obl(:,:,:) !< The previous normal radiation coefficient for EW - !! oblique OBCs [L2 T-2 ~> m2 s-2] - real, allocatable :: ry_norm_obl(:,:,:) !< The previous normal radiation coefficient for NS - !! oblique OBCs [L2 T-2 ~> m2 s-2] - real, allocatable :: cff_normal(:,:,:) !< The denominator for oblique radiation - !! for normal velocity [L2 T-2 ~> m2 s-2] + real, allocatable :: rx_norm_obl(:,:,:) !< The previous x-direction normalized radiation coefficient + !! for either EW or NS oblique OBCs [L2 T-2 ~> m2 s-2] + real, allocatable :: ry_norm_obl(:,:,:) !< The previous y-direction normalized radiation coefficient + !! for either EW or NS oblique OBCs [L2 T-2 ~> m2 s-2] + real, allocatable :: cff_normal(:,:,:) !< The denominator for oblique radiation of the normal + !! velocity [L2 T-2 ~> m2 s-2] real, allocatable :: nudged_normal_vel(:,:,:) !< The layer velocity normal to the OB segment !! that values should be nudged towards [L T-1 ~> m s-1]. real, allocatable :: nudged_tangential_vel(:,:,:) !< The layer velocity tangential to the OB segment - !! that values should be nudged towards [L T-1 ~> m s-1]. + !! that values should be nudged towards [L T-1 ~> m s-1], + !! discretized at the corner (PV) points. real, allocatable :: nudged_tangential_grad(:,:,:) !< The layer dvdx or dudy towards which nudging !! can occur [T-1 ~> s-1]. type(segment_tracer_registry_type), pointer :: tr_Reg=> NULL()!< A pointer to the tracer registry for the segment. @@ -304,9 +318,18 @@ module MOM_open_boundary !! grid points per timestep [nondim] real, allocatable :: ry_normal(:,:,:) !< Array storage for normal phase speed for NS radiation OBCs in units of !! grid points per timestep [nondim] - real, allocatable :: rx_oblique(:,:,:) !< Array storage for oblique boundary condition restarts [L2 T-2 ~> m2 s-2] - real, allocatable :: ry_oblique(:,:,:) !< Array storage for oblique boundary condition restarts [L2 T-2 ~> m2 s-2] - real, allocatable :: cff_normal(:,:,:) !< Array storage for oblique boundary condition restarts [L2 T-2 ~> m2 s-2] + real, allocatable :: rx_oblique_u(:,:,:) !< X-direction oblique boundary condition radiation speeds squared + !! at u points for restarts [L2 T-2 ~> m2 s-2] + real, allocatable :: ry_oblique_u(:,:,:) !< Y-direction oblique boundary condition radiation speeds squared + !! at u points for restarts [L2 T-2 ~> m2 s-2] + real, allocatable :: rx_oblique_v(:,:,:) !< X-direction oblique boundary condition radiation speeds squared + !! at v points for restarts [L2 T-2 ~> m2 s-2] + real, allocatable :: ry_oblique_v(:,:,:) !< Y-direction oblique boundary condition radiation speeds squared + !! at v points for restarts [L2 T-2 ~> m2 s-2] + real, allocatable :: cff_normal_u(:,:,:) !< Denominator for normalizing EW oblique boundary condition radiation + !! rates at u points for restarts [L2 T-2 ~> m2 s-2] + real, allocatable :: cff_normal_v(:,:,:) !< Denominator for normalizing NS oblique boundary condition radiation + !! rates at v points for restarts [L2 T-2 ~> m2 s-2] real, allocatable :: tres_x(:,:,:,:) !< Array storage of tracer reservoirs for restarts, in unscaled units [conc] real, allocatable :: tres_y(:,:,:,:) !< Array storage of tracer reservoirs for restarts, in unscaled units [conc] logical :: debug !< If true, write verbose checksums for debugging purposes. @@ -1794,9 +1817,11 @@ subroutine open_boundary_init(G, GV, US, param_file, OBC, restart_CS) id_clock_pass = cpu_clock_id('(Ocean OBC halo updates)', grain=CLOCK_ROUTINE) if (OBC%radiation_BCs_exist_globally) call pass_vector(OBC%rx_normal, OBC%ry_normal, G%Domain, & To_All+Scalar_Pair) - if (OBC%oblique_BCs_exist_globally) call pass_vector(OBC%rx_oblique, OBC%ry_oblique, G%Domain, & - To_All+Scalar_Pair) - if (allocated(OBC%cff_normal)) call pass_var(OBC%cff_normal, G%Domain, position=CORNER) + if (OBC%oblique_BCs_exist_globally) then + call pass_vector(OBC%rx_oblique_u, OBC%ry_oblique_v, G%Domain, To_All+Scalar_Pair) + call pass_vector(OBC%ry_oblique_u, OBC%rx_oblique_v, G%Domain, To_All+Scalar_Pair) + call pass_vector(OBC%cff_normal_u, OBC%cff_normal_v, G%Domain, To_All+Scalar_Pair) + endif if (allocated(OBC%tres_x) .and. allocated(OBC%tres_y)) then do m=1,OBC%ntr call pass_vector(OBC%tres_x(:,:,:,m), OBC%tres_y(:,:,:,m), G%Domain, To_All+Scalar_Pair) @@ -1811,45 +1836,6 @@ subroutine open_boundary_init(G, GV, US, param_file, OBC, restart_CS) enddo endif - ! The rx_normal and ry_normal arrays used with radiation OBCs are currently in units of grid - ! points per timestep, but if this were to be corrected to [L T-1 ~> m s-1] or [T-1 ~> s-1] to - ! permit timesteps to change between calls to the OBC code, the following would be needed: -! if ( OBC%radiation_BCs_exist_globally .and. (US%s_to_T_restart * US%m_to_L_restart /= 0.0) .and. & -! (US%s_to_T_restart /= US%m_to_L_restart) ) then -! vel_rescale = US%s_to_T_restart / US%m_to_L_restart -! if (query_initialized(OBC%rx_normal, "rx_normal", restart_CS)) then -! do k=1,nz ; do j=jsd,jed ; do I=IsdB,IedB -! OBC%rx_normal(I,j,k) = vel_rescale * OBC%rx_normal(I,j,k) -! enddo ; enddo ; enddo -! endif -! if (query_initialized(OBC%ry_normal, "ry_normal", restart_CS)) then -! do k=1,nz ; do J=JsdB,JedB ; do i=isd,ied -! OBC%ry_normal(i,J,k) = vel_rescale * OBC%ry_normal(i,J,k) -! enddo ; enddo ; enddo -! endif -! endif - - ! The oblique boundary condition terms have units of [L2 T-2 ~> m2 s-2] and may need to be rescaled. - if ( OBC%oblique_BCs_exist_globally .and. (US%s_to_T_restart * US%m_to_L_restart /= 0.0) .and. & - (US%s_to_T_restart /= US%m_to_L_restart) ) then - vel2_rescale = US%s_to_T_restart**2 / US%m_to_L_restart**2 - if (query_initialized(OBC%rx_oblique, "rx_oblique", restart_CS)) then - do k=1,nz ; do j=jsd,jed ; do I=IsdB,IedB - OBC%rx_oblique(I,j,k) = vel2_rescale * OBC%rx_oblique(I,j,k) - enddo ; enddo ; enddo - endif - if (query_initialized(OBC%ry_oblique, "ry_oblique", restart_CS)) then - do k=1,nz ; do J=JsdB,JedB ; do i=isd,ied - OBC%ry_oblique(i,J,k) = vel2_rescale * OBC%ry_oblique(i,J,k) - enddo ; enddo ; enddo - endif - if (query_initialized(OBC%cff_normal, "cff_normal", restart_CS)) then - do k=1,nz ; do J=JsdB,JedB ; do I=IsdB,IedB - OBC%cff_normal(I,J,k) = vel2_rescale * OBC%cff_normal(I,J,k) - enddo ; enddo ; enddo - endif - endif - end subroutine open_boundary_init logical function open_boundary_query(OBC, apply_open_OBC, apply_specified_OBC, apply_Flather_OBC, & @@ -1891,9 +1877,12 @@ subroutine open_boundary_dealloc(OBC) if (allocated(OBC%segnum_v)) deallocate(OBC%segnum_v) if (allocated(OBC%rx_normal)) deallocate(OBC%rx_normal) if (allocated(OBC%ry_normal)) deallocate(OBC%ry_normal) - if (allocated(OBC%rx_oblique)) deallocate(OBC%rx_oblique) - if (allocated(OBC%ry_oblique)) deallocate(OBC%ry_oblique) - if (allocated(OBC%cff_normal)) deallocate(OBC%cff_normal) + if (allocated(OBC%rx_oblique_u)) deallocate(OBC%rx_oblique_u) + if (allocated(OBC%ry_oblique_u)) deallocate(OBC%ry_oblique_u) + if (allocated(OBC%rx_oblique_v)) deallocate(OBC%rx_oblique_v) + if (allocated(OBC%ry_oblique_v)) deallocate(OBC%ry_oblique_v) + if (allocated(OBC%cff_normal_u)) deallocate(OBC%cff_normal_u) + if (allocated(OBC%cff_normal_v)) deallocate(OBC%cff_normal_v) if (allocated(OBC%tres_x)) deallocate(OBC%tres_x) if (allocated(OBC%tres_y)) deallocate(OBC%tres_y) deallocate(OBC) @@ -2129,12 +2118,17 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, real :: cff_new, cff_avg ! denominator in oblique [L2 T-2 ~> m2 s-2] real, allocatable, dimension(:,:,:) :: & rx_tang_rad, & ! The phase speed at u-points for tangential oblique OBCs - ! in units of grid points per timestep [nondim] + ! in units of grid points per timestep [nondim], + ! discretized at the corner (PV) points. ry_tang_rad, & ! The phase speed at v-points for tangential oblique OBCs - ! in units of grid points per timestep [nondim] - rx_tang_obl, & ! The x-coefficient for tangential oblique OBCs [L2 T-2 ~> m2 s-2] - ry_tang_obl, & ! The y-coefficient for tangential oblique OBCs [L2 T-2 ~> m2 s-2] - cff_tangential ! The denominator for tangential oblique OBCs [L2 T-2 ~> m2 s-2] + ! in units of grid points per timestep [nondim], + ! discretized at the corner (PV) points. + rx_tang_obl, & ! The x-coefficient for tangential oblique OBCs [L2 T-2 ~> m2 s-2], + ! discretized at the corner (PV) points. + ry_tang_obl, & ! The y-coefficient for tangential oblique OBCs [L2 T-2 ~> m2 s-2], + ! discretized at the corner (PV) points. + cff_tangential ! The denominator for tangential oblique OBCs [L2 T-2 ~> m2 s-2], + ! discretized at the corner (PV) points. real :: eps ! A small velocity squared [L2 T-2 ~> m2 s-2] type(OBC_segment_type), pointer :: segment => NULL() integer :: i, j, k, is, ie, js, je, m, nz, n @@ -2175,18 +2169,18 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, do k=1,GV%ke I=segment%HI%IsdB do j=segment%HI%jsd,segment%HI%jed - segment%rx_norm_obl(I,j,k) = OBC%rx_oblique(I,j,k) - segment%ry_norm_obl(I,j,k) = OBC%ry_oblique(I,j,k) - segment%cff_normal(I,j,k) = OBC%cff_normal(I,j,k) + segment%rx_norm_obl(I,j,k) = OBC%rx_oblique_u(I,j,k) + segment%ry_norm_obl(I,j,k) = OBC%ry_oblique_u(I,j,k) + segment%cff_normal(I,j,k) = OBC%cff_normal_u(I,j,k) enddo enddo elseif (segment%is_N_or_S .and. segment%oblique) then do k=1,GV%ke J=segment%HI%JsdB do i=segment%HI%isd,segment%HI%ied - segment%rx_norm_obl(i,J,k) = OBC%rx_oblique(i,J,k) - segment%ry_norm_obl(i,J,k) = OBC%ry_oblique(i,J,k) - segment%cff_normal(i,J,k) = OBC%cff_normal(i,J,k) + segment%rx_norm_obl(i,J,k) = OBC%rx_oblique_v(i,J,k) + segment%ry_norm_obl(i,J,k) = OBC%ry_oblique_v(i,J,k) + segment%cff_normal(i,J,k) = OBC%cff_normal_v(i,J,k) enddo enddo endif @@ -2269,16 +2263,16 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, ry_new = min(cff_new,max(dhdt*dhdy,-cff_new)) if (gamma_u < 1.0) then rx_avg = (1.0-gamma_u)*segment%rx_norm_obl(I,j,k) + gamma_u*rx_new - ry_avg = (1.0-gamma_u)*segment%ry_norm_obl(i,J,k) + gamma_u*ry_new - cff_avg = (1.0-gamma_u)*segment%cff_normal(i,J,k) + gamma_u*cff_new + ry_avg = (1.0-gamma_u)*segment%ry_norm_obl(I,j,k) + gamma_u*ry_new + cff_avg = (1.0-gamma_u)*segment%cff_normal(I,j,k) + gamma_u*cff_new else rx_avg = rx_new ry_avg = ry_new cff_avg = cff_new endif segment%rx_norm_obl(I,j,k) = rx_avg - segment%ry_norm_obl(i,J,k) = ry_avg - segment%cff_normal(i,J,k) = cff_avg + segment%ry_norm_obl(I,j,k) = ry_avg + segment%cff_normal(I,j,k) = cff_avg segment%normal_vel(I,j,k) = ((cff_avg*u_new(I,j,k) + rx_avg*u_new(I-1,j,k)) - & (max(ry_avg,0.0)*segment%grad_normal(J-1,2,k) + & min(ry_avg,0.0)*segment%grad_normal(J,2,k))) / & @@ -2286,9 +2280,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, if (gamma_u < 1.0) then ! Copy restart fields into 3-d arrays. This is an inefficient and temporary ! implementation as a work-around to limitations in restart capability - OBC%rx_oblique(I,j,k) = segment%rx_norm_obl(I,j,k) - OBC%ry_oblique(i,J,k) = segment%ry_norm_obl(i,J,k) - OBC%cff_normal(I,j,k) = segment%cff_normal(I,j,k) + OBC%rx_oblique_u(I,j,k) = segment%rx_norm_obl(I,j,k) + OBC%ry_oblique_u(I,j,k) = segment%ry_norm_obl(I,j,k) + OBC%cff_normal_u(I,j,k) = segment%cff_normal(I,j,k) endif elseif (segment%gradient) then segment%normal_vel(I,j,k) = u_new(I-1,j,k) @@ -2409,9 +2403,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, cff_new = max(dhdx*dhdx + dhdy*dhdy, eps) rx_new = min(dhdt*dhdx, cff_new*rx_max) ry_new = min(cff_new,max(dhdt*dhdy,-cff_new)) - rx_tang_obl(I,j,k) = rx_new - ry_tang_obl(i,J,k) = ry_new - cff_tangential(i,J,k) = cff_new + rx_tang_obl(I,J,k) = rx_new + ry_tang_obl(I,J,k) = ry_new + cff_tangential(I,J,k) = cff_new enddo endif enddo @@ -2514,7 +2508,7 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, ry_new = min(cff_new,max(dhdt*dhdy,-cff_new)) if (gamma_u < 1.0) then rx_avg = (1.0-gamma_u)*segment%rx_norm_obl(I,j,k) + gamma_u*rx_new - ry_avg = (1.0-gamma_u)*segment%ry_norm_obl(i,J,k) + gamma_u*ry_new + ry_avg = (1.0-gamma_u)*segment%ry_norm_obl(I,j,k) + gamma_u*ry_new cff_avg = (1.0-gamma_u)*segment%cff_normal(I,j,k) + gamma_u*cff_new else rx_avg = rx_new @@ -2522,8 +2516,8 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, cff_avg = cff_new endif segment%rx_norm_obl(I,j,k) = rx_avg - segment%ry_norm_obl(i,J,k) = ry_avg - segment%cff_normal(i,J,k) = cff_avg + segment%ry_norm_obl(I,j,k) = ry_avg + segment%cff_normal(I,j,k) = cff_avg segment%normal_vel(I,j,k) = ((cff_avg*u_new(I,j,k) + rx_avg*u_new(I+1,j,k)) - & (max(ry_avg,0.0)*segment%grad_normal(J-1,2,k) + & min(ry_avg,0.0)*segment%grad_normal(J,2,k))) / & @@ -2531,9 +2525,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, if (gamma_u < 1.0) then ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues ! implemented as a work-around to limitations in restart capability - OBC%rx_oblique(I,j,k) = segment%rx_norm_obl(I,j,k) - OBC%ry_oblique(i,J,k) = segment%ry_norm_obl(i,J,k) - OBC%cff_normal(I,j,k) = segment%cff_normal(I,j,k) + OBC%rx_oblique_u(I,j,k) = segment%rx_norm_obl(I,j,k) + OBC%ry_oblique_u(I,j,k) = segment%ry_norm_obl(I,j,k) + OBC%cff_normal_u(I,j,k) = segment%cff_normal(I,j,k) endif elseif (segment%gradient) then segment%normal_vel(I,j,k) = u_new(I+1,j,k) @@ -2654,9 +2648,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, cff_new = max(dhdx*dhdx + dhdy*dhdy, eps) rx_new = min(dhdt*dhdx, cff_new*rx_max) ry_new = min(cff_new,max(dhdt*dhdy,-cff_new)) - rx_tang_obl(I,j,k) = rx_new - ry_tang_obl(i,J,k) = ry_new - cff_tangential(i,J,k) = cff_new + rx_tang_obl(I,J,k) = rx_new + ry_tang_obl(I,J,k) = ry_new + cff_tangential(I,J,k) = cff_new enddo endif enddo @@ -2765,7 +2759,7 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, ry_avg = ry_new cff_avg = cff_new endif - segment%rx_norm_obl(I,j,k) = rx_avg + segment%rx_norm_obl(i,J,k) = rx_avg segment%ry_norm_obl(i,J,k) = ry_avg segment%cff_normal(i,J,k) = cff_avg segment%normal_vel(i,J,k) = ((cff_avg*v_new(i,J,k) + ry_avg*v_new(i,J-1,k)) - & @@ -2775,9 +2769,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, if (gamma_u < 1.0) then ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues ! implemented as a work-around to limitations in restart capability - OBC%rx_oblique(I,j,k) = segment%rx_norm_obl(I,j,k) - OBC%ry_oblique(i,J,k) = segment%ry_norm_obl(i,J,k) - OBC%cff_normal(i,J,k) = segment%cff_normal(i,J,k) + OBC%rx_oblique_v(i,J,k) = segment%rx_norm_obl(i,J,k) + OBC%ry_oblique_v(i,J,k) = segment%ry_norm_obl(i,J,k) + OBC%cff_normal_v(i,J,k) = segment%cff_normal(i,J,k) endif elseif (segment%gradient) then segment%normal_vel(i,J,k) = v_new(i,J-1,k) @@ -2898,9 +2892,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, cff_new = max(dhdx*dhdx + dhdy*dhdy, eps) ry_new = min(dhdt*dhdy, cff_new*ry_max) rx_new = min(cff_new,max(dhdt*dhdx,-cff_new)) - rx_tang_obl(I,j,k) = rx_new - ry_tang_obl(i,J,k) = ry_new - cff_tangential(i,J,k) = cff_new + rx_tang_obl(I,J,k) = rx_new + ry_tang_obl(I,J,k) = ry_new + cff_tangential(I,J,k) = cff_new enddo endif enddo @@ -3002,7 +2996,7 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, ry_new = min(dhdt*dhdy, cff_new*ry_max) rx_new = min(cff_new,max(dhdt*dhdx,-cff_new)) if (gamma_u < 1.0) then - rx_avg = (1.0-gamma_u)*segment%rx_norm_obl(I,j,k) + gamma_u*rx_new + rx_avg = (1.0-gamma_u)*segment%rx_norm_obl(i,J,k) + gamma_u*rx_new ry_avg = (1.0-gamma_u)*segment%ry_norm_obl(i,J,k) + gamma_u*ry_new cff_avg = (1.0-gamma_u)*segment%cff_normal(i,J,k) + gamma_u*cff_new else @@ -3010,7 +3004,7 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, ry_avg = ry_new cff_avg = cff_new endif - segment%rx_norm_obl(I,j,k) = rx_avg + segment%rx_norm_obl(i,J,k) = rx_avg segment%ry_norm_obl(i,J,k) = ry_avg segment%cff_normal(i,J,k) = cff_avg segment%normal_vel(i,J,k) = ((cff_avg*v_new(i,J,k) + ry_avg*v_new(i,J+1,k)) - & @@ -3020,9 +3014,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, if (gamma_u < 1.0) then ! Copy restart fields into 3-d arrays. This is an inefficient and temporary issues ! implemented as a work-around to limitations in restart capability - OBC%rx_oblique(I,j,k) = segment%rx_norm_obl(I,j,k) - OBC%ry_oblique(i,J,k) = segment%ry_norm_obl(i,J,k) - OBC%cff_normal(i,J,k) = segment%cff_normal(i,J,k) + OBC%rx_oblique_v(i,J,k) = segment%rx_norm_obl(i,J,k) + OBC%ry_oblique_v(i,J,k) = segment%ry_norm_obl(i,J,k) + OBC%cff_normal_v(i,J,k) = segment%cff_normal(i,J,k) endif elseif (segment%gradient) then segment%normal_vel(i,J,k) = v_new(i,J+1,k) @@ -3143,9 +3137,9 @@ subroutine radiation_open_bdry_conds(OBC, u_new, u_old, v_new, v_old, G, GV, US, cff_new = max(dhdx*dhdx + dhdy*dhdy, eps) ry_new = min(dhdt*dhdy, cff_new*ry_max) rx_new = min(cff_new,max(dhdt*dhdx,-cff_new)) - rx_tang_obl(I,j,k) = rx_new - ry_tang_obl(i,J,k) = ry_new - cff_tangential(i,J,k) = cff_new + rx_tang_obl(I,J,k) = rx_new + ry_tang_obl(I,J,k) = ry_new + cff_tangential(I,J,k) = cff_new enddo endif enddo @@ -4991,18 +4985,28 @@ subroutine open_boundary_register_restarts(HI, GV, US, OBC, Reg, param_file, res endif if (OBC%oblique_BCs_exist_globally) then - allocate(OBC%rx_oblique(HI%isdB:HI%iedB,HI%jsd:HI%jed,GV%ke), source=0.0) - allocate(OBC%ry_oblique(HI%isd:HI%ied,HI%jsdB:HI%jedB,GV%ke), source=0.0) - - vd(1) = var_desc("rx_oblique", "m2 s-2", "Radiation Speed Squared for EW oblique OBCs", 'u', 'L') - vd(2) = var_desc("ry_oblique", "m2 s-2", "Radiation Speed Squared for NS oblique OBCs", 'v', 'L') - call register_restart_pair(OBC%rx_oblique, OBC%ry_oblique, vd(1), vd(2), .false., & + allocate(OBC%rx_oblique_u(HI%isdB:HI%iedB,HI%jsd:HI%jed,GV%ke), source=0.0) + allocate(OBC%ry_oblique_u(HI%isdB:HI%iedB,HI%jsd:HI%jed,GV%ke), source=0.0) + allocate(OBC%cff_normal_u(HI%IsdB:HI%IedB,HI%jsd:HI%jed,GV%ke), source=0.0) + allocate(OBC%rx_oblique_v(HI%isd:HI%ied,HI%jsdB:HI%jedB,GV%ke), source=0.0) + allocate(OBC%ry_oblique_v(HI%isd:HI%ied,HI%jsdB:HI%jedB,GV%ke), source=0.0) + allocate(OBC%cff_normal_v(HI%isd:HI%ied,HI%jsdB:HI%jedB,GV%ke), source=0.0) + + vd(1) = var_desc("rx_oblique_u", "m2 s-2", "X-Direction Radiation Speed Squared for EW oblique OBCs", 'u', 'L') + vd(2) = var_desc("ry_oblique_v", "m2 s-2", "Y-Direction Radiation Speed Squared for NS oblique OBCs", 'v', 'L') + call register_restart_pair(OBC%rx_oblique_u, OBC%ry_oblique_v, vd(1), vd(2), .false., & + restart_CS, conversion=US%L_T_to_m_s**2) + vd(1) = var_desc("ry_oblique_u", "m2 s-2", "Y-Direction Radiation Speed Squared for EW oblique OBCs", 'u', 'L') + vd(2) = var_desc("rx_oblique_v", "m2 s-2", "X-Direction Radiation Speed Squared for NS oblique OBCs", 'v', 'L') + call register_restart_pair(OBC%ry_oblique_u, OBC%rx_oblique_v, vd(1), vd(2), .false., & restart_CS, conversion=US%L_T_to_m_s**2) - allocate(OBC%cff_normal(HI%IsdB:HI%IedB,HI%jsdB:HI%jedB,GV%ke), source=0.0) - call register_restart_field(OBC%cff_normal, "cff_normal", .false., restart_CS, & - longname="denominator for oblique OBCs", & - units="m2 s-2", conversion=US%L_T_to_m_s**2, hor_grid="q") + vd(1) = var_desc("norm_oblique_u", "m2 s-2", "Denominator for normalizing EW oblique OBC radiation rates", & + 'u', 'L') + vd(2) = var_desc("norm_oblique_v", "m2 s-2", "Denominator for normalizing NS oblique OBC radiation rates", & + 'v', 'L') + call register_restart_pair(OBC%cff_normal_u, OBC%cff_normal_v, vd(1), vd(2), .false., & + restart_CS, conversion=US%L_T_to_m_s**2) endif if (Reg%ntr == 0) return From b3a95aec2f0d2244f265b4767b578cf1cb65278b Mon Sep 17 00:00:00 2001 From: Kate Hedstrom Date: Tue, 27 Sep 2022 16:42:48 -0800 Subject: [PATCH 64/68] +Attempt to fix an imperfect restart issue. --- src/core/MOM_barotropic.F90 | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/core/MOM_barotropic.F90 b/src/core/MOM_barotropic.F90 index 32b7a1209f..0949d203ae 100644 --- a/src/core/MOM_barotropic.F90 +++ b/src/core/MOM_barotropic.F90 @@ -1061,6 +1061,30 @@ subroutine btstep(U_in, V_in, eta_in, dt, bc_accel_u, bc_accel_v, forces, pbce, enddo ; enddo enddo + if (apply_OBCs) then + do n=1,OBC%number_of_segments + if (.not. OBC%segment(n)%on_pe) cycle + I = OBC%segment(n)%HI%IsdB ; J = OBC%segment(n)%HI%JsdB + if (OBC%segment(n)%is_N_or_S .and. (J >= Jsq-1) .and. (J <= Jeq+1)) then + do i = max(Isq-1,OBC%segment(n)%HI%isd), min(Ieq+2,OBC%segment(n)%HI%ied) + if (OBC%segment(n)%direction == OBC_DIRECTION_N) then + gtot_S(i,j+1) = gtot_S(i,j) + else ! (OBC%segment(n)%direction == OBC_DIRECTION_S) + gtot_N(i,j) = gtot_N(i,j+1) + endif + enddo + elseif (OBC%segment(n)%is_E_or_W .and. (I >= Isq-1) .and. (I <= Ieq+1)) then + do j = max(Jsq-1,OBC%segment(n)%HI%jsd), min(Jeq+2,OBC%segment(n)%HI%jed) + if (OBC%segment(n)%direction == OBC_DIRECTION_E) then + gtot_W(i+1,j) = gtot_W(i,j) + else ! (OBC%segment(n)%direction == OBC_DIRECTION_W) + gtot_E(i,j) = gtot_E(i+1,j) + endif + enddo + endif + enddo + endif + if (CS%tides) then call tidal_forcing_sensitivity(G, CS%tides_CSp, det_de) if (CS%tidal_sal_bug) then From 80e7d1c4bc4cb45f4d5a0f9b89e7b7ef098d0e85 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Sun, 9 Oct 2022 23:14:38 -0400 Subject: [PATCH 65/68] *+Added G%OBCmaskCu and G%OBCmaskCv Added two new arrays (OBCmaskCu and OBCmaskCv) to the ocean_grid_type and dyn_horgrid_type to mask out values over land or at open boundary condition points. Without open boundary conditions, these arrays are identical to mask2dCu and mask2dCv. These arrays are used in some of the lateral parameterization modules to zero out certain gradient-dependent fluxes at open boundary points. With these changes, the Bering test case solutions no longer exhibit any dependence on whether DEBUG_OBC is true or false or the value of OBC_SILLY_THICK, so this commit should help to address some of the issues discussed in github.com/NOAA-GFDL/MOM6/issues/208. All answers are bitwise identical in the MOM6-examples test cases, but they change for some other tests that use open boundary conditions more extensively, and there are new arrays in some transparent types. --- src/core/MOM_grid.F90 | 11 ++- src/core/MOM_open_boundary.F90 | 36 +++++-- src/core/MOM_transcribe_grid.F90 | 6 ++ src/framework/MOM_dyn_horgrid.F90 | 9 +- src/initialization/MOM_grid_initialize.F90 | 6 ++ src/parameterizations/lateral/MOM_MEKE.F90 | 4 +- .../lateral/MOM_interface_filter.F90 | 4 +- .../lateral/MOM_lateral_mixing_coeffs.F90 | 99 ++----------------- .../lateral/MOM_mixed_layer_restrat.F90 | 12 +-- .../lateral/MOM_thickness_diffuse.F90 | 28 +++--- 10 files changed, 88 insertions(+), 127 deletions(-) diff --git a/src/core/MOM_grid.F90 b/src/core/MOM_grid.F90 index 384e4a6d2b..f3a48f3ded 100644 --- a/src/core/MOM_grid.F90 +++ b/src/core/MOM_grid.F90 @@ -90,6 +90,7 @@ module MOM_grid real ALLOCABLE_, dimension(NIMEMB_PTR_,NJMEM_) :: & mask2dCu, & !< 0 for boundary points and 1 for ocean points on the u grid [nondim]. + OBCmaskCu, & !< 0 for boundary or OBC points and 1 for ocean points on the u grid [nondim]. geoLatCu, & !< The geographic latitude at u points in degrees of latitude or m. geoLonCu, & !< The geographic longitude at u points in degrees of longitude or m. dxCu, & !< dxCu is delta x at u points [L ~> m]. @@ -102,6 +103,7 @@ module MOM_grid real ALLOCABLE_, dimension(NIMEM_,NJMEMB_PTR_) :: & mask2dCv, & !< 0 for boundary points and 1 for ocean points on the v grid [nondim]. + OBCmaskCv, & !< 0 for boundary or OBC points and 1 for ocean points on the v grid [nondim]. geoLatCv, & !< The geographic latitude at v points in degrees of latitude or m. geoLonCv, & !< The geographic longitude at v points in degrees of longitude or m. dxCv, & !< dxCv is delta x at v points [L ~> m]. @@ -573,7 +575,9 @@ subroutine allocate_metrics(G) ALLOC_(G%mask2dT(isd:ied,jsd:jed)) ; G%mask2dT(:,:) = 0.0 ALLOC_(G%mask2dCu(IsdB:IedB,jsd:jed)) ; G%mask2dCu(:,:) = 0.0 + ALLOC_(G%OBCmaskCu(IsdB:IedB,jsd:jed)) ; G%OBCmaskCu(:,:) = 0.0 ALLOC_(G%mask2dCv(isd:ied,JsdB:JedB)) ; G%mask2dCv(:,:) = 0.0 + ALLOC_(G%OBCmaskCv(isd:ied,JsdB:JedB)) ; G%OBCmaskCv(:,:) = 0.0 ALLOC_(G%mask2dBu(IsdB:IedB,JsdB:JedB)) ; G%mask2dBu(:,:) = 0.0 ALLOC_(G%geoLatT(isd:ied,jsd:jed)) ; G%geoLatT(:,:) = 0.0 ALLOC_(G%geoLatCu(IsdB:IedB,jsd:jed)) ; G%geoLatCu(:,:) = 0.0 @@ -637,8 +641,8 @@ subroutine MOM_grid_end(G) DEALLOC_(G%areaCu) ; DEALLOC_(G%IareaCu) DEALLOC_(G%areaCv) ; DEALLOC_(G%IareaCv) - DEALLOC_(G%mask2dT) ; DEALLOC_(G%mask2dCu) - DEALLOC_(G%mask2dCv) ; DEALLOC_(G%mask2dBu) + DEALLOC_(G%mask2dT) ; DEALLOC_(G%mask2dCu) ; DEALLOC_(G%OBCmaskCu) + DEALLOC_(G%mask2dCv) ; DEALLOC_(G%OBCmaskCv) ; DEALLOC_(G%mask2dBu) DEALLOC_(G%geoLatT) ; DEALLOC_(G%geoLatCu) DEALLOC_(G%geoLatCv) ; DEALLOC_(G%geoLatBu) @@ -686,6 +690,7 @@ end subroutine MOM_grid_end !! !! Each location also has a 2D mask indicating whether the entire column is land or ocean. !! `mask2dT` is 1 if the column is wet or 0 if the T-cell is land. -!! `mask2dCu` is 1 if both neighboring column are ocean, and 0 if either is land. +!! `mask2dCu` is 1 if both neighboring columns are ocean, and 0 if either is land. +!! `OBCmasku` is 1 if both neighboring columns are ocean, and 0 if either is land of if this is OBC point. end module MOM_grid diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index a187ac5b2f..1cc8505d17 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -137,7 +137,8 @@ module MOM_open_boundary logical :: specified !< Boundary normal velocity fixed to external value. logical :: specified_tan !< Boundary tangential velocity fixed to external value. logical :: specified_grad !< Boundary gradient of tangential velocity fixed to external value. - logical :: open !< Boundary is open for continuity solver. + logical :: open !< Boundary is open for continuity solver, and there are no other + !! parameterized mass fluxes at the open boundary. logical :: gradient !< Zero gradient at boundary. logical :: values_needed !< Whether or not any external OBC fields are needed. logical :: u_values_needed !< Whether or not external u OBC fields are needed. @@ -1963,16 +1964,16 @@ subroutine open_boundary_impose_land_mask(OBC, G, areaCu, areaCv, US) do j=segment%HI%jsd,segment%HI%jed if (G%mask2dCu(I,j) == 0) OBC%segnum_u(I,j) = OBC_NONE if (segment%direction == OBC_DIRECTION_W) then - G%mask2dT(i,j) = 0 + G%mask2dT(i,j) = 0.0 else - G%mask2dT(i+1,j) = 0 + G%mask2dT(i+1,j) = 0.0 endif enddo do J=segment%HI%JsdB+1,segment%HI%JedB-1 if (segment%direction == OBC_DIRECTION_W) then - G%mask2dCv(i,J) = 0 + G%mask2dCv(i,J) = 0 ; G%OBCmaskCv(i,J) = 0.0 else - G%mask2dCv(i+1,J) = 0 + G%mask2dCv(i+1,J) = 0.0 ; G%OBCmaskCv(i+1,J) = 0.0 endif enddo else @@ -1981,21 +1982,38 @@ subroutine open_boundary_impose_land_mask(OBC, G, areaCu, areaCv, US) do i=segment%HI%isd,segment%HI%ied if (G%mask2dCv(i,J) == 0) OBC%segnum_v(i,J) = OBC_NONE if (segment%direction == OBC_DIRECTION_S) then - G%mask2dT(i,j) = 0 + G%mask2dT(i,j) = 0.0 else - G%mask2dT(i,j+1) = 0 + G%mask2dT(i,j+1) = 0.0 endif enddo do I=segment%HI%IsdB+1,segment%HI%IedB-1 if (segment%direction == OBC_DIRECTION_S) then - G%mask2dCu(I,j) = 0 + G%mask2dCu(I,j) = 0.0 ; G%OBCmaskCu(I,j) = 0.0 else - G%mask2dCu(I,j+1) = 0 + G%mask2dCu(I,j+1) = 0.0 ; G%OBCmaskCu(I,j+1) = 0.0 endif enddo endif enddo + do n=1,OBC%number_of_segments + segment=>OBC%segment(n) + if (.not. (segment%on_pe .and. segment%open)) cycle + ! Set the OBCmask values to help eliminate certain terms at u- or v- OBC points. + if (segment%is_E_or_W) then + I=segment%HI%IsdB + do j=segment%HI%jsd,segment%HI%jed + G%OBCmaskCu(I,j) = 0.0 + enddo + else + J=segment%HI%JsdB + do i=segment%HI%isd,segment%HI%ied + G%OBCmaskCv(i,J) = 0.0 + enddo + endif + enddo + do n=1,OBC%number_of_segments segment=>OBC%segment(n) if (.not. segment%on_pe .or. .not. segment%specified) cycle diff --git a/src/core/MOM_transcribe_grid.F90 b/src/core/MOM_transcribe_grid.F90 index 7ab15d542e..8f8da21ef3 100644 --- a/src/core/MOM_transcribe_grid.F90 +++ b/src/core/MOM_transcribe_grid.F90 @@ -76,6 +76,7 @@ subroutine copy_dyngrid_to_MOM_grid(dG, oG, US) oG%porous_DavgU(I,j) = dG%porous_DavgU(I+ido,j+jdo) - oG%Z_ref oG%mask2dCu(I,j) = dG%mask2dCu(I+ido,j+jdo) + oG%OBCmaskCu(I,j) = dG%OBCmaskCu(I+ido,j+jdo) oG%areaCu(I,j) = dG%areaCu(I+ido,j+jdo) oG%IareaCu(I,j) = dG%IareaCu(I+ido,j+jdo) enddo ; enddo @@ -92,6 +93,7 @@ subroutine copy_dyngrid_to_MOM_grid(dG, oG, US) oG%porous_DavgV(i,J) = dG%porous_DavgV(i+ido,J+jdo) - oG%Z_ref oG%mask2dCv(i,J) = dG%mask2dCv(i+ido,J+jdo) + oG%OBCmaskCv(i,J) = dG%OBCmaskCv(i+ido,J+jdo) oG%areaCv(i,J) = dG%areaCv(i+ido,J+jdo) oG%IareaCv(i,J) = dG%IareaCv(i+ido,J+jdo) enddo ; enddo @@ -152,6 +154,7 @@ subroutine copy_dyngrid_to_MOM_grid(dG, oG, US) call pass_vector(oG%dxCu, oG%dyCv, oG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(oG%dy_Cu, oG%dx_Cv, oG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(oG%mask2dCu, oG%mask2dCv, oG%Domain, To_All+Scalar_Pair, CGRID_NE) + call pass_vector(oG%OBCmaskCu, oG%OBCmaskCv, oG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(oG%IareaCu, oG%IareaCv, oG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(oG%IareaCu, oG%IareaCv, oG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(oG%geoLatCu, oG%geoLatCv, oG%Domain, To_All+Scalar_Pair, CGRID_NE) @@ -230,6 +233,7 @@ subroutine copy_MOM_grid_to_dyngrid(oG, dG, US) dG%porous_DavgU(I,j) = oG%porous_DavgU(I+ido,j+jdo) + oG%Z_ref dG%mask2dCu(I,j) = oG%mask2dCu(I+ido,j+jdo) + dG%OBCmaskCu(I,j) = oG%OBCmaskCu(I+ido,j+jdo) dG%areaCu(I,j) = oG%areaCu(I+ido,j+jdo) dG%IareaCu(I,j) = oG%IareaCu(I+ido,j+jdo) enddo ; enddo @@ -246,6 +250,7 @@ subroutine copy_MOM_grid_to_dyngrid(oG, dG, US) dG%porous_DavgV(i,J) = oG%porous_DavgU(i+ido,J+jdo) + oG%Z_ref dG%mask2dCv(i,J) = oG%mask2dCv(i+ido,J+jdo) + dG%OBCmaskCv(i,J) = oG%OBCmaskCv(i+ido,J+jdo) dG%areaCv(i,J) = oG%areaCv(i+ido,J+jdo) dG%IareaCv(i,J) = oG%IareaCv(i+ido,J+jdo) enddo ; enddo @@ -307,6 +312,7 @@ subroutine copy_MOM_grid_to_dyngrid(oG, dG, US) call pass_vector(dG%dxCu, dG%dyCv, dG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(dG%dy_Cu, dG%dx_Cv, dG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(dG%mask2dCu, dG%mask2dCv, dG%Domain, To_All+Scalar_Pair, CGRID_NE) + call pass_vector(dG%OBCmaskCu, dG%OBCmaskCv, dG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(dG%IareaCu, dG%IareaCv, dG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(dG%IareaCu, dG%IareaCv, dG%Domain, To_All+Scalar_Pair, CGRID_NE) call pass_vector(dG%geoLatCu, dG%geoLatCv, dG%Domain, To_All+Scalar_Pair, CGRID_NE) diff --git a/src/framework/MOM_dyn_horgrid.F90 b/src/framework/MOM_dyn_horgrid.F90 index e97c8d981b..60c30d8e94 100644 --- a/src/framework/MOM_dyn_horgrid.F90 +++ b/src/framework/MOM_dyn_horgrid.F90 @@ -87,6 +87,7 @@ module MOM_dyn_horgrid real, allocatable, dimension(:,:) :: & mask2dCu, & !< 0 for boundary points and 1 for ocean points on the u grid [nondim]. + OBCmaskCu, & !< 0 for boundary or OBC points and 1 for ocean points on the u grid [nondim]. geoLatCu, & !< The geographic latitude at u points [degrees of latitude] or [m]. geoLonCu, & !< The geographic longitude at u points [degrees of longitude] or [m]. dxCu, & !< dxCu is delta x at u points [L ~> m]. @@ -99,6 +100,7 @@ module MOM_dyn_horgrid real, allocatable, dimension(:,:) :: & mask2dCv, & !< 0 for boundary points and 1 for ocean points on the v grid [nondim]. + OBCmaskCv, & !< 0 for boundary or OBC points and 1 for ocean points on the v grid [nondim]. geoLatCv, & !< The geographic latitude at v points [degrees of latitude] or [m]. geoLonCv, & !< The geographic longitude at v points [degrees of longitude] or [m]. dxCv, & !< dxCv is delta x at v points [L ~> m]. @@ -250,6 +252,8 @@ subroutine create_dyn_horgrid(G, HI, bathymetry_at_vel) allocate(G%mask2dCu(IsdB:IedB,jsd:jed), source=0.0) allocate(G%mask2dCv(isd:ied,JsdB:JedB), source=0.0) allocate(G%mask2dBu(IsdB:IedB,JsdB:JedB), source=0.0) + allocate(G%OBCmaskCu(IsdB:IedB,jsd:jed), source=0.0) + allocate(G%OBCmaskCv(isd:ied,JsdB:JedB), source=0.0) allocate(G%geoLatT(isd:ied,jsd:jed), source=0.0) allocate(G%geoLatCu(IsdB:IedB,jsd:jed), source=0.0) allocate(G%geoLatCv(isd:ied,JsdB:JedB), source=0.0) @@ -331,6 +335,7 @@ subroutine rotate_dyn_horgrid(G_in, G, US, turns) call rotate_array_pair(G_in%dx_Cv, G_in%dy_Cu, turns, G%dx_Cv, G%dy_Cu) call rotate_array_pair(G_in%mask2dCu, G_in%mask2dCv, turns, G%mask2dCu, G%mask2dCv) + call rotate_array_pair(G_in%OBCmaskCu, G_in%OBCmaskCv, turns, G%OBCmaskCu, G%OBCmaskCv) call rotate_array_pair(G_in%areaCu, G_in%areaCv, turns, G%areaCu, G%areaCv) call rotate_array_pair(G_in%IareaCu, G_in%IareaCv, turns, G%IareaCu, G%IareaCv) @@ -501,8 +506,8 @@ subroutine destroy_dyn_horgrid(G) deallocate(G%areaCu) ; deallocate(G%IareaCu) deallocate(G%areaCv) ; deallocate(G%IareaCv) - deallocate(G%mask2dT) ; deallocate(G%mask2dCu) - deallocate(G%mask2dCv) ; deallocate(G%mask2dBu) + deallocate(G%mask2dT) ; deallocate(G%mask2dCu) ; deallocate(G%OBCmaskCu) + deallocate(G%mask2dCv) ; deallocate(G%OBCmaskCv) ; deallocate(G%mask2dBu) deallocate(G%geoLatT) ; deallocate(G%geoLatCu) deallocate(G%geoLatCv) ; deallocate(G%geoLatBu) diff --git a/src/initialization/MOM_grid_initialize.F90 b/src/initialization/MOM_grid_initialize.F90 index bc004daa95..d84d2275e4 100644 --- a/src/initialization/MOM_grid_initialize.F90 +++ b/src/initialization/MOM_grid_initialize.F90 @@ -1206,6 +1206,8 @@ subroutine initialize_masks(G, PF, US) else G%mask2dCu(I,j) = 1.0 endif + ! This mask may be revised later after the open boundary positions are specified. + G%OBCmaskCu(I,j) = G%mask2dCu(I,j) enddo ; enddo do J=G%jsd,G%jed-1 ; do i=G%isd,G%ied @@ -1214,6 +1216,8 @@ subroutine initialize_masks(G, PF, US) else G%mask2dCv(i,J) = 1.0 endif + ! This mask may be revised later after the open boundary positions are specified. + G%OBCmaskCv(i,J) = G%mask2dCv(i,J) enddo ; enddo do J=G%jsd,G%jed-1 ; do I=G%isd,G%ied-1 @@ -1229,12 +1233,14 @@ subroutine initialize_masks(G, PF, US) call pass_vector(G%mask2dCu, G%mask2dCv, G%Domain, To_All+Scalar_Pair, CGRID_NE) do j=G%jsd,G%jed ; do I=G%IsdB,G%IedB + ! This open face length may be revised later. G%dy_Cu(I,j) = G%mask2dCu(I,j) * G%dyCu(I,j) G%areaCu(I,j) = G%dxCu(I,j) * G%dy_Cu(I,j) G%IareaCu(I,j) = G%mask2dCu(I,j) * Adcroft_reciprocal(G%areaCu(I,j)) enddo ; enddo do J=G%JsdB,G%JedB ; do i=G%isd,G%ied + ! This open face length may be revised later. G%dx_Cv(i,J) = G%mask2dCv(i,J) * G%dxCv(i,J) G%areaCv(i,J) = G%dyCv(i,J) * G%dx_Cv(i,J) G%IareaCv(i,J) = G%mask2dCv(i,J) * Adcroft_reciprocal(G%areaCv(i,J)) diff --git a/src/parameterizations/lateral/MOM_MEKE.F90 b/src/parameterizations/lateral/MOM_MEKE.F90 index 9b024e62b0..ead6086346 100644 --- a/src/parameterizations/lateral/MOM_MEKE.F90 +++ b/src/parameterizations/lateral/MOM_MEKE.F90 @@ -462,7 +462,7 @@ subroutine step_forward_MEKE(MEKE, h, SN_u, SN_v, visc, dt, G, GV, US, CS, hu, h !$OMP parallel do default(shared) do j=js-1,je+1 ; do I=is-2,ie+1 ! MEKE_uflux is used here as workspace with units of [L2 T-2 ~> m2 s-2]. - MEKE_uflux(I,j) = ((G%dy_Cu(I,j)*G%IdxCu(I,j)) * G%mask2dCu(I,j)) * & + MEKE_uflux(I,j) = ((G%dy_Cu(I,j)*G%IdxCu(I,j)) * G%OBCmaskCu(I,j)) * & (MEKE%MEKE(i+1,j) - MEKE%MEKE(i,j)) ! This would have units of [R Z L2 T-2 ~> kg s-2] ! MEKE_uflux(I,j) = ((G%dy_Cu(I,j)*G%IdxCu(I,j)) * & @@ -472,7 +472,7 @@ subroutine step_forward_MEKE(MEKE, h, SN_u, SN_v, visc, dt, G, GV, US, CS, hu, h !$OMP parallel do default(shared) do J=js-2,je+1 ; do i=is-1,ie+1 ! MEKE_vflux is used here as workspace with units of [L2 T-2 ~> m2 s-2]. - MEKE_vflux(i,J) = ((G%dx_Cv(i,J)*G%IdyCv(i,J)) * G%mask2dCv(i,J)) * & + MEKE_vflux(i,J) = ((G%dx_Cv(i,J)*G%IdyCv(i,J)) * G%OBCmaskCv(i,J)) * & (MEKE%MEKE(i,j+1) - MEKE%MEKE(i,j)) ! This would have units of [R Z L2 T-2 ~> kg s-2] ! MEKE_vflux(i,J) = ((G%dx_Cv(i,J)*G%IdyCv(i,J)) * & diff --git a/src/parameterizations/lateral/MOM_interface_filter.F90 b/src/parameterizations/lateral/MOM_interface_filter.F90 index feed4bd930..dd082f1558 100644 --- a/src/parameterizations/lateral/MOM_interface_filter.F90 +++ b/src/parameterizations/lateral/MOM_interface_filter.F90 @@ -284,7 +284,7 @@ subroutine filter_interface(h, e, Lsm2_u, Lsm2_v, uhD, vhD, G, GV, US, halo_size do I=is-1,ie ; uhtot(I,j) = 0.0 ; enddo do K=nz,2,-1 do I=is-1,ie - Slope = ((e(i,j,K)-e(i+1,j,K))*G%IdxCu(I,j)) * G%mask2dCu(I,j) + Slope = ((e(i,j,K)-e(i+1,j,K))*G%IdxCu(I,j)) * G%OBCmaskCu(I,j) Sfn_est = (Lsm2_u(I,j)*G%dy_Cu(I,j)) * (GV%Z_to_H * Slope) @@ -316,7 +316,7 @@ subroutine filter_interface(h, e, Lsm2_u, Lsm2_v, uhD, vhD, G, GV, US, halo_size do i=is,ie ; vhtot(i,J) = 0.0 ; enddo do K=nz,2,-1 do i=is,ie - Slope = ((e(i,j,K)-e(i,j+1,K))*G%IdyCv(i,J)) * G%mask2dCv(i,J) + Slope = ((e(i,j,K)-e(i,j+1,K))*G%IdyCv(i,J)) * G%OBCmaskCv(i,J) Sfn_est = (Lsm2_v(i,J)*G%dx_Cv(i,J)) * (GV%Z_to_H * Slope) diff --git a/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 b/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 index dc23042916..e6dd57fc2e 100644 --- a/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 +++ b/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 @@ -533,7 +533,6 @@ subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, C real :: H_u(SZIB_(G)), H_v(SZI_(G)) real :: S2_u(SZIB_(G), SZJ_(G)) real :: S2_v(SZI_(G), SZJB_(G)) - logical :: local_open_u_BC, local_open_v_BC if (.not. CS%initialized) call MOM_error(FATAL, "calc_Visbeck_coeffs_old: "// & "Module must be initialized before it is used.") @@ -546,13 +545,6 @@ subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, C is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke - local_open_u_BC = .false. - local_open_v_BC = .false. - if (associated(OBC)) then - local_open_u_BC = OBC%open_u_BCs_exist_globally - local_open_v_BC = OBC%open_v_BCs_exist_globally - endif - S2max = CS%Visbeck_S_max**2 !$OMP parallel do default(shared) @@ -593,20 +585,11 @@ subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, C enddo ; enddo do I=is-1,ie if (H_u(I)>0.) then - CS%SN_u(I,j) = G%mask2dCu(I,j) * CS%SN_u(I,j) / H_u(I) - S2_u(I,j) = G%mask2dCu(I,j) * S2_u(I,j) / H_u(I) + CS%SN_u(I,j) = G%OBCmaskCu(I,j) * CS%SN_u(I,j) / H_u(I) + S2_u(I,j) = G%OBCmaskCu(I,j) * S2_u(I,j) / H_u(I) else CS%SN_u(I,j) = 0. endif - if (local_open_u_BC) then - l_seg = OBC%segnum_u(I,j) - - if (l_seg /= OBC_NONE) then - if (OBC%segment(l_seg)%open) then - CS%SN_u(i,J) = 0. - endif - endif - endif enddo enddo @@ -638,20 +621,11 @@ subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, C enddo ; enddo do i=is,ie if (H_v(i)>0.) then - CS%SN_v(i,J) = G%mask2dCv(i,J) * CS%SN_v(i,J) / H_v(i) - S2_v(i,J) = G%mask2dCv(i,J) * S2_v(i,J) / H_v(i) + CS%SN_v(i,J) = G%OBCmaskCv(i,J) * CS%SN_v(i,J) / H_v(i) + S2_v(i,J) = G%OBCmaskCv(i,J) * S2_v(i,J) / H_v(i) else CS%SN_v(i,J) = 0. endif - if (local_open_v_BC) then - l_seg = OBC%segnum_v(i,J) - - if (l_seg /= OBC_NONE) then - if (OBC%segment(OBC%segnum_v(i,J))%open) then - CS%SN_v(i,J) = 0. - endif - endif - endif enddo enddo @@ -699,7 +673,7 @@ subroutine calc_Eady_growth_rate_2D(CS, G, GV, US, OBC, h, e, dzu, dzv, dzSxN, d real :: r_crp_dist ! The inverse of the distance over which to scale the cropping [Z-1 ~> m-1] real :: dB, dT ! Elevation variables used when cropping [Z ~> m] integer :: i, j, k, l_seg - logical :: local_open_u_BC, local_open_v_BC, crop + logical :: crop dz_neglect = GV%H_subroundoff * GV%H_to_Z D_scale = CS%Eady_GR_D_scale @@ -707,13 +681,6 @@ subroutine calc_Eady_growth_rate_2D(CS, G, GV, US, OBC, h, e, dzu, dzv, dzSxN, d r_crp_dist = 1. / max( dz_neglect, CS%cropping_distance ) crop = CS%cropping_distance>=0. ! Only filter out in-/out-cropped interface is parameter if non-negative - local_open_u_BC = .false. - local_open_v_BC = .false. - if (associated(OBC)) then - local_open_u_BC = OBC%open_u_BCs_exist_globally - local_open_v_BC = OBC%open_v_BCs_exist_globally - endif - if (CS%debug) then call uvchksum("calc_Eady_growth_rate_2D dz[uv]", dzu, dzv, G%HI, scale=US%Z_to_m, scalar_pair=.true.) call uvchksum("calc_Eady_growth_rate_2D dzS2N2[uv]", dzSxN, dzSyN, G%HI, & @@ -764,19 +731,9 @@ subroutine calc_Eady_growth_rate_2D(CS, G, GV, US, OBC, h, e, dzu, dzv, dzSxN, d enddo ; enddo endif do I=G%isc-1,G%iec - CS%SN_u(I,j) = G%mask2dCu(I,j) * ( vint_SN(I) / sum_dz(I) ) - SN_cpy(I,j) = G%mask2dCu(I,j) * ( vint_SN(I) / sum_dz(I) ) + CS%SN_u(I,j) = G%OBCmaskCu(I,j) * ( vint_SN(I) / sum_dz(I) ) + SN_cpy(I,j) = G%OBCmaskCu(I,j) * ( vint_SN(I) / sum_dz(I) ) enddo - if (local_open_u_BC) then - do I=G%isc-1,G%iec - l_seg = OBC%segnum_u(I,j) - if (l_seg /= OBC_NONE) then - if (OBC%segment(l_seg)%open) then - CS%SN_u(i,J) = 0. - endif - endif - enddo - endif enddo !$OMP parallel do default(shared) private(dnew,dz,weight,l_seg) @@ -817,18 +774,8 @@ subroutine calc_Eady_growth_rate_2D(CS, G, GV, US, OBC, h, e, dzu, dzv, dzSxN, d enddo ; enddo endif do i=G%isc-1,G%iec+1 - CS%SN_v(i,J) = G%mask2dCv(i,J) * ( vint_SN(i) / sum_dz(i) ) + CS%SN_v(i,J) = G%OBCmaskCv(i,J) * ( vint_SN(i) / sum_dz(i) ) enddo - if (local_open_v_BC) then - do i=G%isc-1,G%iec+1 - l_seg = OBC%segnum_v(i,J) - if (l_seg /= OBC_NONE) then - if (OBC%segment(l_seg)%open) then - CS%SN_v(i,J) = 0. - endif - endif - enddo - endif enddo do j = G%jsc,G%jec @@ -881,7 +828,6 @@ subroutine calc_slope_functions_using_just_e(h, G, GV, US, CS, e, calculate_slop integer :: l_seg real :: S2N2_u_local(SZIB_(G), SZJ_(G),SZK_(GV)) real :: S2N2_v_local(SZI_(G), SZJB_(G),SZK_(GV)) - logical :: local_open_u_BC, local_open_v_BC if (.not. CS%initialized) call MOM_error(FATAL, "calc_slope_functions_using_just_e: "// & "Module must be initialized before it is used.") @@ -894,13 +840,6 @@ subroutine calc_slope_functions_using_just_e(h, G, GV, US, CS, e, calculate_slop is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke - local_open_u_BC = .false. - local_open_v_BC = .false. - if (associated(OBC)) then - local_open_u_BC = OBC%open_u_BCs_exist_globally - local_open_v_BC = OBC%open_v_BCs_exist_globally - endif - one_meter = 1.0 * GV%m_to_H h_neglect = GV%H_subroundoff H_cutoff = real(2*nz) * (GV%Angstrom_H + h_neglect) @@ -972,20 +911,11 @@ subroutine calc_slope_functions_using_just_e(h, G, GV, US, CS, e, calculate_slop !SN_u(I,j) = sqrt( SN_u(I,j) / ( max(G%bathyT(i,j), G%bathyT(i+1,j)) + (G%Z_ref + GV%Angstrom_Z) ) ) !The code below behaves better than the line above. Not sure why? AJA if ( min(G%bathyT(i,j), G%bathyT(i+1,j)) + G%Z_ref > H_cutoff*GV%H_to_Z ) then - CS%SN_u(I,j) = G%mask2dCu(I,j) * sqrt( CS%SN_u(I,j) / & + CS%SN_u(I,j) = G%OBCmaskCu(I,j) * sqrt( CS%SN_u(I,j) / & (max(G%bathyT(i,j), G%bathyT(i+1,j)) + G%Z_ref) ) else CS%SN_u(I,j) = 0.0 endif - if (local_open_u_BC) then - l_seg = OBC%segnum_u(I,j) - - if (l_seg /= OBC_NONE) then - if (OBC%segment(l_seg)%open) then - CS%SN_u(I,j) = 0. - endif - endif - endif enddo enddo !$OMP parallel do default(shared) @@ -999,20 +929,11 @@ subroutine calc_slope_functions_using_just_e(h, G, GV, US, CS, e, calculate_slop !SN_v(i,J) = sqrt( SN_v(i,J) / ( max(G%bathyT(i,J), G%bathyT(i,J+1)) + (G%Z_ref + GV%Angstrom_Z) ) ) !The code below behaves better than the line above. Not sure why? AJA if ( min(G%bathyT(i,j), G%bathyT(i+1,j)) + G%Z_ref > H_cutoff*GV%H_to_Z ) then - CS%SN_v(i,J) = G%mask2dCv(i,J) * sqrt( CS%SN_v(i,J) / & + CS%SN_v(i,J) = G%OBCmaskCv(i,J) * sqrt( CS%SN_v(i,J) / & (max(G%bathyT(i,j), G%bathyT(i,j+1)) + G%Z_ref) ) else CS%SN_v(i,J) = 0.0 endif - if (local_open_v_BC) then - l_seg = OBC%segnum_v(i,J) - - if (l_seg /= OBC_NONE) then - if (OBC%segment(OBC%segnum_v(i,J))%open) then - CS%SN_v(i,J) = 0. - endif - endif - endif enddo enddo diff --git a/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 b/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 index a8efa4cf12..0aef33ddc6 100644 --- a/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 +++ b/src/parameterizations/lateral/MOM_mixed_layer_restrat.F90 @@ -393,7 +393,7 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef if (res_upscale) timescale = timescale * res_scaling_fac - uDml(I) = timescale * G%mask2dCu(I,j)*G%dyCu(I,j)*G%IdxCu(I,j) * & + uDml(I) = timescale * G%OBCmaskCu(I,j)*G%dyCu(I,j)*G%IdxCu(I,j) * & (Rml_av_fast(i+1,j)-Rml_av_fast(i,j)) * (h_vel**2 * GV%Z_to_H) ! As above but using the slow filtered MLD h_vel = 0.5*((htot_slow(i,j) + htot_slow(i+1,j)) + h_neglect) * GV%H_to_Z @@ -402,7 +402,7 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef2 if (res_upscale) timescale = timescale * res_scaling_fac - uDml_slow(I) = timescale * G%mask2dCu(I,j)*G%dyCu(I,j)*G%IdxCu(I,j) * & + uDml_slow(I) = timescale * G%OBCmaskCu(I,j)*G%dyCu(I,j)*G%IdxCu(I,j) * & (Rml_av_slow(i+1,j)-Rml_av_slow(i,j)) * (h_vel**2 * GV%Z_to_H) if (uDml(I) + uDml_slow(I) == 0.) then @@ -468,7 +468,7 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef if (res_upscale) timescale = timescale * res_scaling_fac - vDml(i) = timescale * G%mask2dCv(i,J)*G%dxCv(i,J)*G%IdyCv(i,J) * & + vDml(i) = timescale * G%OBCmaskCv(i,J)*G%dxCv(i,J)*G%IdyCv(i,J) * & (Rml_av_fast(i,j+1)-Rml_av_fast(i,j)) * (h_vel**2 * GV%Z_to_H) ! As above but using the slow filtered MLD h_vel = 0.5*((htot_slow(i,j) + htot_slow(i,j+1)) + h_neglect) * GV%H_to_Z @@ -477,7 +477,7 @@ subroutine mixedlayer_restrat_general(h, uhtr, vhtr, tv, forces, dt, MLD_in, Var timescale = 0.0625 * (absf + 2.0*mom_mixrate) / (absf**2 + mom_mixrate**2) timescale = timescale * CS%ml_restrat_coef2 if (res_upscale) timescale = timescale * res_scaling_fac - vDml_slow(i) = timescale * G%mask2dCv(i,J)*G%dxCv(i,J)*G%IdyCv(i,J) * & + vDml_slow(i) = timescale * G%OBCmaskCv(i,J)*G%dxCv(i,J)*G%IdyCv(i,J) * & (Rml_av_slow(i,j+1)-Rml_av_slow(i,j)) * (h_vel**2 * GV%Z_to_H) if (vDml(i) + vDml_slow(i) == 0.) then @@ -716,7 +716,7 @@ subroutine mixedlayer_restrat_BML(h, uhtr, vhtr, tv, forces, dt, G, GV, US, CS) timescale = timescale * CS%ml_restrat_coef ! timescale = timescale*(2?)*(L_def/L_MLI) * min(EKE/MKE,1.0 + (G%dyCv(i,j)/L_def)**2) - uDml(I) = timescale * G%mask2dCu(I,j)*G%dyCu(I,j)*G%IdxCu(I,j) * & + uDml(I) = timescale * G%OBCmaskCu(I,j)*G%dyCu(I,j)*G%IdxCu(I,j) * & (Rml_av(i+1,j)-Rml_av(i,j)) * (h_vel**2 * GV%Z_to_H) if (uDml(I) == 0) then @@ -762,7 +762,7 @@ subroutine mixedlayer_restrat_BML(h, uhtr, vhtr, tv, forces, dt, G, GV, US, CS) timescale = timescale * CS%ml_restrat_coef ! timescale = timescale*(2?)*(L_def/L_MLI) * min(EKE/MKE,1.0 + (G%dyCv(i,j)/L_def)**2) - vDml(i) = timescale * G%mask2dCv(i,J)*G%dxCv(i,J)*G%IdyCv(i,J) * & + vDml(i) = timescale * G%OBCmaskCv(i,J)*G%dxCv(i,J)*G%IdyCv(i,J) * & (Rml_av(i,j+1)-Rml_av(i,j)) * (h_vel**2 * GV%Z_to_H) if (vDml(i) == 0) then do k=1,nkml ; vhml(i,J,k) = 0.0 ; enddo diff --git a/src/parameterizations/lateral/MOM_thickness_diffuse.F90 b/src/parameterizations/lateral/MOM_thickness_diffuse.F90 index 3cab1030da..c7310e1560 100644 --- a/src/parameterizations/lateral/MOM_thickness_diffuse.F90 +++ b/src/parameterizations/lateral/MOM_thickness_diffuse.F90 @@ -233,7 +233,7 @@ subroutine thickness_diffuse(h, uhtr, vhtr, tv, dt, G, GV, US, MEKE, VarMix, CDp if (CS%MEKE_GEOMETRIC) then !$OMP do do j=js,je ; do I=is-1,ie - Khth_loc_u(I,j) = Khth_loc_u(I,j) + G%mask2dCu(I,j) * CS%MEKE_GEOMETRIC_alpha * & + Khth_loc_u(I,j) = Khth_loc_u(I,j) + G%OBCmaskCu(I,j) * CS%MEKE_GEOMETRIC_alpha * & 0.5*(MEKE%MEKE(i,j)+MEKE%MEKE(i+1,j)) / & (VarMix%SN_u(I,j) + CS%MEKE_GEOMETRIC_epsilon) enddo ; enddo @@ -319,7 +319,7 @@ subroutine thickness_diffuse(h, uhtr, vhtr, tv, dt, G, GV, US, MEKE, VarMix, CDp if (CS%MEKE_GEOMETRIC) then !$OMP do do J=js-1,je ; do i=is,ie - Khth_loc_v(i,J) = Khth_loc_v(i,J) + G%mask2dCv(i,J) * CS%MEKE_GEOMETRIC_alpha * & + Khth_loc_v(i,J) = Khth_loc_v(i,J) + G%OBCmaskCv(i,J) * CS%MEKE_GEOMETRIC_alpha * & 0.5*(MEKE%MEKE(i,j)+MEKE%MEKE(i,j+1)) / & (VarMix%SN_v(i,J) + CS%MEKE_GEOMETRIC_epsilon) enddo ; enddo @@ -956,7 +956,7 @@ subroutine thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV if (present_slope_x) then Slope = slope_x(I,j,k) else - Slope = ((e(i,j,K)-e(i+1,j,K))*G%IdxCu(I,j)) * G%mask2dCu(I,j) + Slope = ((e(i,j,K)-e(i+1,j,K))*G%IdxCu(I,j)) * G%OBCmaskCu(I,j) endif if (CS%id_slope_x > 0) CS%diagSlopeX(I,j,k) = Slope Sfn_unlim_u(I,K) = ((KH_u(I,j,K)*G%dy_Cu(I,j))*Slope) @@ -971,7 +971,7 @@ subroutine thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV enddo ! k-loop if (CS%use_FGNV_streamfn) then - do k=1,nz ; do I=is-1,ie ; if (G%mask2dCu(I,j)>0.) then + do k=1,nz ; do I=is-1,ie ; if (G%OBCmaskCu(I,j)>0.) then h_harm = max( h_neglect, & 2. * h(i,j,k) * h(i+1,j,k) / ( ( h(i,j,k) + h(i+1,j,k) ) + h_neglect ) ) c2_h_u(I,k) = CS%FGNV_scale * & @@ -980,7 +980,7 @@ subroutine thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV ! Solve an elliptic equation for the streamfunction following Ferrari et al., 2010. do I=is-1,ie - if (G%mask2dCu(I,j)>0.) then + if (G%OBCmaskCu(I,j)>0.) then do K=2,nz Sfn_unlim_u(I,K) = (1. + CS%FGNV_scale) * Sfn_unlim_u(I,K) enddo @@ -1238,7 +1238,7 @@ subroutine thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV if (present_slope_y) then Slope = slope_y(i,J,k) else - Slope = ((e(i,j,K)-e(i,j+1,K))*G%IdyCv(i,J)) * G%mask2dCv(i,J) + Slope = ((e(i,j,K)-e(i,j+1,K))*G%IdyCv(i,J)) * G%OBCmaskCv(i,J) endif if (CS%id_slope_y > 0) CS%diagSlopeY(I,j,k) = Slope Sfn_unlim_v(i,K) = ((KH_v(i,J,K)*G%dx_Cv(i,J))*Slope) @@ -1253,7 +1253,7 @@ subroutine thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV enddo ! k-loop if (CS%use_FGNV_streamfn) then - do k=1,nz ; do i=is,ie ; if (G%mask2dCv(i,J)>0.) then + do k=1,nz ; do i=is,ie ; if (G%OBCmaskCv(i,J)>0.) then h_harm = max( h_neglect, & 2. * h(i,j,k) * h(i,j+1,k) / ( ( h(i,j,k) + h(i,j+1,k) ) + h_neglect ) ) c2_h_v(i,k) = CS%FGNV_scale * & @@ -1262,7 +1262,7 @@ subroutine thickness_diffuse_full(h, e, Kh_u, Kh_v, tv, uhD, vhD, cg1, dt, G, GV ! Solve an elliptic equation for the streamfunction following Ferrari et al., 2010. do i=is,ie - if (G%mask2dCv(i,J)>0.) then + if (G%OBCmaskCv(i,J)>0.) then do K=2,nz Sfn_unlim_v(i,K) = (1. + CS%FGNV_scale) * Sfn_unlim_v(i,K) enddo @@ -1651,7 +1651,7 @@ subroutine add_detangling_Kh(h, e, Kh_u, Kh_v, KH_u_CFL, KH_v_CFL, tv, dt, G, GV de_bot(i,j) = de_bot(i,j) + h(i,j,k+1) enddo ; enddo - do j=js,je ; do I=is-1,ie ; if (G%mask2dCu(I,j) > 0.0) then + do j=js,je ; do I=is-1,ie ; if (G%OBCmaskCu(I,j) > 0.0) then if (h(i,j,k) > h(i+1,j,k)) then h2 = h(i,j,k) h1 = max( h(i+1,j,k), h2 - min(de_bot(i+1,j), de_top(i+1,j,k)) ) @@ -1663,7 +1663,7 @@ subroutine add_detangling_Kh(h, e, Kh_u, Kh_v, KH_u_CFL, KH_v_CFL, tv, dt, G, GV KH_lay_u(I,j,k) = (Kh_scale * KH_u_CFL(I,j)) * jag_Rat**2 endif ; enddo ; enddo - do J=js-1,je ; do i=is,ie ; if (G%mask2dCv(i,J) > 0.0) then + do J=js-1,je ; do i=is,ie ; if (G%OBCmaskCv(i,J) > 0.0) then if (h(i,j,k) > h(i,j+1,k)) then h2 = h(i,j,k) h1 = max( h(i,j+1,k), h2 - min(de_bot(i,j+1), de_top(i,j+1,k)) ) @@ -1689,7 +1689,7 @@ subroutine add_detangling_Kh(h, e, Kh_u, Kh_v, KH_u_CFL, KH_v_CFL, tv, dt, G, GV ! First, populate the diffusivities if (n==1) then ! This is a u-column. do i=ish,ie - do_i(I) = (G%mask2dCu(I,j) > 0.0) + do_i(I) = (G%OBCmaskCu(I,j) > 0.0) Kh_Max_max(I) = KH_u_CFL(I,j) enddo do K=1,nz+1 ; do i=ish,ie @@ -1699,7 +1699,7 @@ subroutine add_detangling_Kh(h, e, Kh_u, Kh_v, KH_u_CFL, KH_v_CFL, tv, dt, G, GV enddo ; enddo else ! This is a v-column. do i=ish,ie - do_i(i) = (G%mask2dCv(i,J) > 0.0) ; Kh_Max_max(I) = KH_v_CFL(i,J) + do_i(i) = (G%OBCmaskCv(i,J) > 0.0) ; Kh_Max_max(I) = KH_v_CFL(i,J) enddo do K=1,nz+1 ; do i=ish,ie Kh_bg(I,K) = KH_v(I,j,K) ; Kh(I,K) = Kh_bg(I,K) @@ -2003,11 +2003,11 @@ subroutine thickness_diffuse_init(Time, G, GV, US, param_file, diag, CDp, CS) allocate(CS%Kh_eta_v(G%isd:G%ied, G%JsdB:G%JedB), source=0.) do j=G%jsc,G%jec ; do I=G%isc-1,G%iec grid_sp = sqrt((2.0*G%dxCu(I,j)**2 * G%dyCu(I,j)**2) / (G%dxCu(I,j)**2 + G%dyCu(I,j)**2)) - CS%Kh_eta_u(I,j) = G%mask2dCu(I,j) * MAX(0.0, CS%Kh_eta_bg + CS%Kh_eta_vel * grid_sp) + CS%Kh_eta_u(I,j) = G%OBCmaskCu(I,j) * MAX(0.0, CS%Kh_eta_bg + CS%Kh_eta_vel * grid_sp) enddo ; enddo do J=G%jsc-1,G%jec ; do i=G%isc,G%iec grid_sp = sqrt((2.0*G%dxCv(i,J)**2 * G%dyCv(i,J)**2) / (G%dxCv(i,J)**2 + G%dyCv(i,J)**2)) - CS%Kh_eta_v(i,J) = G%mask2dCv(i,J) * MAX(0.0, CS%Kh_eta_bg + CS%Kh_eta_vel * grid_sp) + CS%Kh_eta_v(i,J) = G%OBCmaskCv(i,J) * MAX(0.0, CS%Kh_eta_bg + CS%Kh_eta_vel * grid_sp) enddo ; enddo endif From 84682aae1f3ccbbd3906413f4048c386b05e3118 Mon Sep 17 00:00:00 2001 From: Robert Hallberg Date: Thu, 13 Oct 2022 06:10:51 -0400 Subject: [PATCH 66/68] +Eliminate unused OBC arguments Eliminated OBC arguments that are no longer used by three internal routines in MOM_lateral_mixing_coeffs. All answers are bitwise identical. --- .../lateral/MOM_lateral_mixing_coeffs.F90 | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 b/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 index e6dd57fc2e..87562a9c83 100644 --- a/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 +++ b/src/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 @@ -17,7 +17,7 @@ module MOM_lateral_mixing_coeffs use MOM_variables, only : thermo_var_ptrs use MOM_verticalGrid, only : verticalGrid_type use MOM_wave_speed, only : wave_speed, wave_speed_CS, wave_speed_init -use MOM_open_boundary, only : ocean_OBC_type, OBC_NONE +use MOM_open_boundary, only : ocean_OBC_type implicit none ; private @@ -475,16 +475,16 @@ subroutine calc_slope_functions(h, tv, dt, G, GV, US, CS, OBC) call calc_isoneutral_slopes(G, GV, US, h, e, tv, dt*CS%kappa_smooth, CS%use_stanley_iso, & CS%slope_x, CS%slope_y, N2_u=N2_u, N2_v=N2_v, dzu=dzu, dzv=dzv, & dzSxN=dzSxN, dzSyN=dzSyN, halo=1, OBC=OBC) - call calc_Eady_growth_rate_2D(CS, G, GV, US, OBC, h, e, dzu, dzv, dzSxN, dzSyN, CS%SN_u, CS%SN_v) + call calc_Eady_growth_rate_2D(CS, G, GV, US, h, e, dzu, dzv, dzSxN, dzSyN, CS%SN_u, CS%SN_v) else call find_eta(h, tv, G, GV, US, e, halo_size=2) if (CS%use_stored_slopes) then call calc_isoneutral_slopes(G, GV, US, h, e, tv, dt*CS%kappa_smooth, CS%use_stanley_iso, & CS%slope_x, CS%slope_y, N2_u=N2_u, N2_v=N2_v, halo=1, OBC=OBC) - call calc_Visbeck_coeffs_old(h, CS%slope_x, CS%slope_y, N2_u, N2_v, G, GV, US, CS, OBC) + call calc_Visbeck_coeffs_old(h, CS%slope_x, CS%slope_y, N2_u, N2_v, G, GV, US, CS) else !call calc_isoneutral_slopes(G, GV, h, e, tv, dt*CS%kappa_smooth, CS%slope_x, CS%slope_y) - call calc_slope_functions_using_just_e(h, G, GV, US, CS, e, .true., OBC) + call calc_slope_functions_using_just_e(h, G, GV, US, CS, e, .true.) endif endif endif @@ -507,7 +507,7 @@ end subroutine calc_slope_functions !> Calculates factors used when setting diffusivity coefficients similar to Visbeck et al., 1997. !! This is on older implementation that is susceptible to large values of Eady growth rate !! for incropping layers. -subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, CS, OBC) +subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, CS) type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thickness [H ~> m or kg m-2] @@ -519,7 +519,6 @@ subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, C !! at v-points [L2 Z-2 T-2 ~> s-2] type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type type(VarMix_CS), intent(inout) :: CS !< Variable mixing control struct - type(ocean_OBC_type), pointer :: OBC !< Open boundaries control structure. ! Local variables real :: S2 ! Interface slope squared [nondim] @@ -647,12 +646,11 @@ subroutine calc_Visbeck_coeffs_old(h, slope_x, slope_y, N2_u, N2_v, G, GV, US, C end subroutine calc_Visbeck_coeffs_old !> Calculates the Eady growth rate (2D fields) for use in MEKE and the Visbeck schemes -subroutine calc_Eady_growth_rate_2D(CS, G, GV, US, OBC, h, e, dzu, dzv, dzSxN, dzSyN, SN_u, SN_v) +subroutine calc_Eady_growth_rate_2D(CS, G, GV, US, h, e, dzu, dzv, dzSxN, dzSyN, SN_u, SN_v) type(VarMix_CS), intent(inout) :: CS !< Variable mixing coefficients type(ocean_grid_type), intent(in) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - type(ocean_OBC_type), pointer, intent(in) :: OBC !< Open boundaries control structure. real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Interface height [Z ~> m] real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: e !< Interface height [Z ~> m] real, dimension(SZIB_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: dzu !< dz at u-points [Z ~> m] @@ -802,7 +800,7 @@ end subroutine calc_Eady_growth_rate_2D !> The original calc_slope_function() that calculated slopes using !! interface positions only, not accounting for density variations. -subroutine calc_slope_functions_using_just_e(h, G, GV, US, CS, e, calculate_slopes, OBC) +subroutine calc_slope_functions_using_just_e(h, G, GV, US, CS, e, calculate_slopes) type(ocean_grid_type), intent(inout) :: G !< Ocean grid structure type(verticalGrid_type), intent(in) :: GV !< Vertical grid structure real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Layer thickness [H ~> m or kg m-2] @@ -811,7 +809,6 @@ subroutine calc_slope_functions_using_just_e(h, G, GV, US, CS, e, calculate_slop real, dimension(SZI_(G),SZJ_(G),SZK_(GV)+1), intent(in) :: e !< Interface position [Z ~> m] logical, intent(in) :: calculate_slopes !< If true, calculate slopes !! internally otherwise use slopes stored in CS - type(ocean_OBC_type), pointer :: OBC !< Open boundaries control structure. ! Local variables real :: E_x(SZIB_(G), SZJ_(G)) ! X-slope of interface at u points [nondim] (for diagnostics) real :: E_y(SZI_(G), SZJB_(G)) ! Y-slope of interface at v points [nondim] (for diagnostics) From 7c4dfd4dfd5b1901d9a629d32c557c8486c0eab7 Mon Sep 17 00:00:00 2001 From: Marshall Ward Date: Thu, 3 Nov 2022 15:48:36 -0400 Subject: [PATCH 67/68] Conditionally deallocate spherical harmonic fields This patch moves the local `use_tides` flag of the split RK2 solver, which tracks the TIDES parameter, into the RK2 control structure (CS), and uses it to conditionally call `tidal_forcing_end(). The `tidal_forcing_init()` function conditionally allocates the spherical harmonic fields, but `tidal_forcing_end()` is always called, which will in turn always attempt to deallocate the fields. In some compilers, under certain levels of debugging, this can raise a runtime error. --- src/core/MOM_dynamics_split_RK2.F90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/MOM_dynamics_split_RK2.F90 b/src/core/MOM_dynamics_split_RK2.F90 index d95c6feea3..68f8c97669 100644 --- a/src/core/MOM_dynamics_split_RK2.F90 +++ b/src/core/MOM_dynamics_split_RK2.F90 @@ -159,6 +159,7 @@ module MOM_dynamics_split_RK2 !! end of the timestep have been stored for use in the next !! predictor step. This is used to accomodate various generations !! of restart files. + logical :: use_tides !< If true, tidal forcing is enabled. real :: be !< A nondimensional number from 0.5 to 1 that controls !! the backward weighting of the time stepping scheme [nondim] @@ -1224,7 +1225,7 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param real :: accel_rescale ! A rescaling factor for accelerations from the representation in a ! restart file to the internal representation in this run [various units ~> 1] type(group_pass_type) :: pass_av_h_uvh - logical :: use_tides, debug_truncations + logical :: debug_truncations logical :: read_uv, read_h2 integer :: i, j, k, is, ie, js, je, isd, ied, jsd, jed, nz @@ -1245,7 +1246,7 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param CS%diag => diag call log_version(param_file, mdl, version, "") - call get_param(param_file, mdl, "TIDES", use_tides, & + call get_param(param_file, mdl, "TIDES", CS%use_tides, & "If true, apply tidal momentum forcing.", default=.false.) call get_param(param_file, mdl, "BE", CS%be, & "If SPLIT is true, BE determines the relative weighting "//& @@ -1340,7 +1341,7 @@ subroutine initialize_dyn_split_RK2(u, v, h, uh, vh, eta, Time, G, GV, US, param call continuity_init(Time, G, GV, US, param_file, diag, CS%continuity_CSp) cont_stencil = continuity_stencil(CS%continuity_CSp) call CoriolisAdv_init(Time, G, GV, US, param_file, diag, CS%ADp, CS%CoriolisAdv) - if (use_tides) call tidal_forcing_init(Time, G, US, param_file, CS%tides_CSp) + if (CS%use_tides) call tidal_forcing_init(Time, G, US, param_file, CS%tides_CSp) call PressureForce_init(Time, G, GV, US, param_file, diag, CS%PressureForce_CSp, & CS%tides_CSp) call hor_visc_init(Time, G, GV, US, param_file, diag, CS%hor_visc, ADp=CS%ADp) @@ -1702,7 +1703,7 @@ subroutine end_dyn_split_RK2(CS) deallocate(CS%vertvisc_CSp) call hor_visc_end(CS%hor_visc) - call tidal_forcing_end(CS%tides_CSp) + if (CS%use_tides) call tidal_forcing_end(CS%tides_CSp) call CoriolisAdv_end(CS%CoriolisAdv) DEALLOC_(CS%diffu) ; DEALLOC_(CS%diffv) From 380864138fb936e3e0bd76bb5155d74200e89db8 Mon Sep 17 00:00:00 2001 From: Alistair Adcroft Date: Wed, 7 Dec 2022 13:58:54 -0500 Subject: [PATCH 68/68] Switch from mpich to openmpi Testing to see if GH actions is failing due to MPI installation --- .github/actions/ubuntu-setup/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/ubuntu-setup/action.yml b/.github/actions/ubuntu-setup/action.yml index 3fd2ea13cf..3f3ba5f0b6 100644 --- a/.github/actions/ubuntu-setup/action.yml +++ b/.github/actions/ubuntu-setup/action.yml @@ -13,7 +13,7 @@ runs: sudo apt-get install netcdf-bin sudo apt-get install libnetcdf-dev sudo apt-get install libnetcdff-dev - sudo apt-get install mpich - sudo apt-get install libmpich-dev + sudo apt-get install openmpi-bin + sudo apt-get install libopenmpi-dev sudo apt-get install linux-tools-common echo "::endgroup::"