diff --git a/README.md b/README.md index 3859ca9184..8ea625d704 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -MPAS-v5.2 +MPAS-v6.0 ==== The Model for Prediction Across Scales (MPAS) is a collaborative project for diff --git a/src/core_atmosphere/Makefile b/src/core_atmosphere/Makefile index ca73840fc5..2e77cf8846 100644 --- a/src/core_atmosphere/Makefile +++ b/src/core_atmosphere/Makefile @@ -34,7 +34,7 @@ physcore: mpas_atm_dimensions.o ( cd ../..; ln -sf ./src/core_atmosphere/physics/physics_wrf/files/*TBL .) ( cd ../..; ln -sf ./src/core_atmosphere/physics/physics_wrf/files/*DATA* .) -dycore: mpas_atm_dimensions.o +dycore: mpas_atm_dimensions.o physcore ( cd dynamics; $(MAKE) all PHYSICS="$(PHYSICS)" ) diagcore: physcore dycore @@ -48,7 +48,7 @@ atmcore: physcore dycore diagcore $(OBJS) mpas_atm_core_interface.o: mpas_atm_core.o -mpas_atm_core.o: dycore mpas_atm_threading.o +mpas_atm_core.o: dycore diagcore mpas_atm_threading.o mpas_atm_dimensions.o: diff --git a/src/core_atmosphere/Registry.xml b/src/core_atmosphere/Registry.xml index 752216937f..4dab5dcfef 100644 --- a/src/core_atmosphere/Registry.xml +++ b/src/core_atmosphere/Registry.xml @@ -1,5 +1,5 @@ - + @@ -429,14 +429,13 @@ + - - @@ -563,6 +562,7 @@ + @@ -845,6 +845,7 @@ + @@ -924,6 +925,7 @@ output_interval="3:00:00" runtime_format="separate_file"> + @@ -1299,14 +1301,13 @@ - - - + + @@ -1323,9 +1324,6 @@ - - @@ -1633,35 +1631,35 @@ - + - - - - - - - @@ -2547,6 +2545,12 @@ + + + + diff --git a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F index 7d5b8c01ee..f642d0fb15 100644 --- a/src/core_atmosphere/dynamics/mpas_atm_time_integration.F +++ b/src/core_atmosphere/dynamics/mpas_atm_time_integration.F @@ -394,7 +394,7 @@ subroutine atm_srk3(domain, dt, itimestep) call mpas_timer_stop('atm_compute_moist_coefficients') #ifdef DO_PHYSICS - call mpas_timer_start('physics_addtend') + call mpas_timer_start('physics_get_tend') block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'mesh', mesh) @@ -418,7 +418,7 @@ subroutine atm_srk3(domain, dt, itimestep) tend_rho_physics ) block => block % next end do - call mpas_timer_stop('physics_addtend') + call mpas_timer_stop('physics_get_tend') #endif ! @@ -594,29 +594,6 @@ subroutine atm_srk3(domain, dt, itimestep) end do call mpas_timer_stop('atm_compute_dyn_tend') -#ifdef DO_PHYSICS -! call mpas_timer_start('physics_addtend') -! block => domain % blocklist -! do while (associated(block)) -! call mpas_pool_get_subpool(block % structs, 'mesh', mesh) -! call mpas_pool_get_subpool(block % structs, 'state', state) -! call mpas_pool_get_subpool(block % structs, 'diag', diag) -! call mpas_pool_get_subpool(block % structs, 'tend', tend) -! call mpas_pool_get_subpool(block % structs, 'tend_physics', tend_physics) -! call physics_addtend( block, & -! mesh, & -! state, & -! diag, & -! tend, & -! tend_physics, & -! block % configs, & -! rk_step, & -! dynamics_substep ) -! block => block % next -! end do -! call mpas_timer_stop('physics_addtend') -#endif - !*********************************** ! need tendencies at all edges of owned cells - @@ -5508,6 +5485,7 @@ subroutine atm_init_coupled_diagnostics(state, time_lev, diag, mesh, configs, & real (kind=RKIND), dimension(:,:), pointer :: u real (kind=RKIND), dimension(:,:), pointer :: w real (kind=RKIND), dimension(:,:), pointer :: pressure_p + real (kind=RKIND), dimension(:,:), pointer :: pressure_base real (kind=RKIND), dimension(:,:), pointer :: exner real (kind=RKIND), dimension(:,:), pointer :: exner_base real (kind=RKIND), dimension(:), pointer :: fzm, fzp @@ -5540,6 +5518,7 @@ subroutine atm_init_coupled_diagnostics(state, time_lev, diag, mesh, configs, & call mpas_pool_get_array(state, 'u', u, time_lev) call mpas_pool_get_array(state, 'w', w, time_lev) call mpas_pool_get_array(diag, 'pressure_p', pressure_p) + call mpas_pool_get_array(diag, 'pressure_base', pressure_base) call mpas_pool_get_array(diag, 'exner', exner) call mpas_pool_get_array(diag, 'exner_base', exner_base) call mpas_pool_get_array(mesh, 'fzm', fzm) @@ -5620,6 +5599,7 @@ subroutine atm_init_coupled_diagnostics(state, time_lev, diag, mesh, configs, & do iCell=cellStart,cellEnd do k=1,nVertLevels exner(k,iCell) = (zz(k,iCell) * (rgas/p0) * (rtheta_p(k,iCell) + rtheta_base(k,iCell)))**rcv + exner_base(k,iCell) = (zz(k,iCell) * (rgas/p0) * (rtheta_base(k,iCell)))**rcv ! WCS addition 20180403 end do end do @@ -5629,6 +5609,7 @@ subroutine atm_init_coupled_diagnostics(state, time_lev, diag, mesh, configs, & * ( exner(k,iCell) * rtheta_p(k,iCell) & + rtheta_base(k,iCell) * (exner(k,iCell) - exner_base(k,iCell)) & ) + pressure_base(k,iCell) = zz(k,iCell) * rgas * exner_base(k,iCell) * rtheta_base(k,iCell) ! WCS addition 20180403 end do end do diff --git a/src/core_atmosphere/mpas_atm_core.F b/src/core_atmosphere/mpas_atm_core.F index 6f2a4353fa..b431e9cc1c 100644 --- a/src/core_atmosphere/mpas_atm_core.F +++ b/src/core_atmosphere/mpas_atm_core.F @@ -45,6 +45,7 @@ function atm_core_init(domain, startTimeStamp) result(ierr) type (mpas_pool_type), pointer :: diag type (field2DReal), pointer :: u_field, pv_edge_field, ru_field, rw_field character (len=StrKIND), pointer :: xtime + character (len=StrKIND), pointer :: initial_time1, initial_time2 type (MPAS_Time_Type) :: startTime integer, pointer :: nVertLevels, maxEdges, maxEdges2, num_scalars @@ -153,6 +154,13 @@ function atm_core_init(domain, startTimeStamp) result(ierr) call mpas_pool_get_array(state, 'xtime', xtime, 1) xtime = startTimeStamp + ! Initialize initial_time in second time level. We need to do this because initial state + ! is read into time level 1, and if we write output from the set of state arrays that + ! represent the original time level 2, the initial_time field will be invalid. + call mpas_pool_get_array(state, 'initial_time', initial_time1, 1) + call mpas_pool_get_array(state, 'initial_time', initial_time2, 2) + initial_time2 = initial_time1 + block => block % next end do diff --git a/src/core_atmosphere/physics/Makefile b/src/core_atmosphere/physics/Makefile index 8aa3ca3046..e8cb03f6f5 100644 --- a/src/core_atmosphere/physics/Makefile +++ b/src/core_atmosphere/physics/Makefile @@ -4,15 +4,16 @@ ifeq ($(CORE),atmosphere) COREDEF = -Dmpas endif +all: lookup_tables core_physics_init core_physics_wrf core_physics + dummy: - echo "****** make non-hydrostatic core ******" + echo "****** compiling physics ******" OBJS_init = \ mpas_atmphys_constants.o \ mpas_atmphys_date_time.o \ mpas_atmphys_functions.o \ - mpas_atmphys_utilities.o \ - mpas_atmphys_o3climatology.o + mpas_atmphys_utilities.o OBJS = \ mpas_atmphys_camrad_init.o \ @@ -23,6 +24,7 @@ OBJS = \ mpas_atmphys_driver_gwdo.o \ mpas_atmphys_driver_lsm.o \ mpas_atmphys_driver_microphysics.o \ + mpas_atmphys_driver_oml.o \ mpas_atmphys_driver_pbl.o \ mpas_atmphys_driver_radiation_lw.o \ mpas_atmphys_driver_radiation_sw.o \ @@ -30,10 +32,11 @@ OBJS = \ mpas_atmphys_finalize.o \ mpas_atmphys_init.o \ mpas_atmphys_init_microphysics.o \ + mpas_atmphys_interface.o \ mpas_atmphys_landuse.o \ mpas_atmphys_lsm_noahinit.o \ mpas_atmphys_manager.o \ - mpas_atmphys_driver_oml.o \ + mpas_atmphys_o3climatology.o \ mpas_atmphys_packages.o \ mpas_atmphys_rrtmg_lwinit.o \ mpas_atmphys_rrtmg_swinit.o \ @@ -42,30 +45,25 @@ OBJS = \ mpas_atmphys_update.o \ mpas_atmphys_vars.o -OBJS_dyn = mpas_atmphys_interface.o - -all: lookup_tables core_physics_init core_physics_wrf core_dyn core_physics - lookup_tables: ./checkout_data_files.sh core_physics_wrf: - (cd physics_wrf; make all COREDEF="$(COREDEF)") - -core_dyn: $(OBJS_dyn) - ar -ru libphys.a $(OBJS_dyn) + (cd physics_wrf; $(MAKE) all COREDEF="$(COREDEF)") core_physics_init: $(OBJS_init) ar -ru libphys.a $(OBJS_init) -core_physics: $(OBJS) +core_physics: core_physics_wrf + ($(MAKE) phys_interface COREDEF="$(COREDEF)") ar -ru libphys.a $(OBJS) +phys_interface: $(OBJS) + # DEPENDENCIES: mpas_atmphys_camrad_init.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_utilities.o \ - ./physics_wrf/module_ra_cam_support.o + mpas_atmphys_utilities.o mpas_atmphys_control.o: \ mpas_atmphys_utilities.o \ @@ -88,54 +86,36 @@ mpas_atmphys_driver.o: \ mpas_atmphys_driver_cloudiness.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_mp_thompson_cldfra3.o + mpas_atmphys_vars.o mpas_atmphys_driver_convection.o: \ mpas_atmphys_constants.o \ mpas_atmphys_utilities.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_cu_gf.mpas.o \ - ./physics_wrf/module_cu_kfeta.o \ - ./physics_wrf/module_cu_tiedtke.o \ - ./physics_wrf/module_cu_ntiedtke.o - -mpas_atmphys_finalize.o: \ - ./physics_wrf/module_mp_thompson.o - -mpas_atmphys_finalize.o: \ - ./physics_wrf/module_mp_thompson.o + mpas_atmphys_vars.o mpas_atmphys_driver_gwdo.o: \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_bl_gwdo.o + mpas_atmphys_vars.o mpas_atmphys_driver_lsm.o: \ mpas_atmphys_constants.o \ mpas_atmphys_landuse.o \ mpas_atmphys_lsm_noahinit.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_sf_noahdrv.o + mpas_atmphys_vars.o mpas_atmphys_driver_microphysics.o: \ mpas_atmphys_constants.o \ mpas_atmphys_init_microphysics.o \ mpas_atmphys_interface.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_mp_kessler.o \ - ./physics_wrf/module_mp_thompson.o \ - ./physics_wrf/module_mp_wsm6.o + mpas_atmphys_vars.o mpas_atmphys_driver_oml.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_sf_oml.o + mpas_atmphys_landuse.o \ + mpas_atmphys_vars.o mpas_atmphys_driver_pbl.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_bl_mynn.o \ - ./physics_wrf/module_bl_ysu.o + mpas_atmphys_vars.o mpas_atmphys_driver_radiation_lw.o: \ mpas_atmphys_camrad_init.o \ @@ -143,24 +123,18 @@ mpas_atmphys_driver_radiation_lw.o: \ mpas_atmphys_driver_radiation_sw.o \ mpas_atmphys_manager.o \ mpas_atmphys_rrtmg_lwinit.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_ra_cam.o \ - ./physics_wrf/module_ra_rrtmg_lw.o + mpas_atmphys_vars.o mpas_atmphys_driver_radiation_sw.o: \ mpas_atmphys_camrad_init.o \ mpas_atmphys_constants.o \ mpas_atmphys_manager.o \ mpas_atmphys_rrtmg_swinit.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_ra_cam.o \ - ./physics_wrf/module_ra_rrtmg_sw.o + mpas_atmphys_vars.o mpas_atmphys_driver_sfclayer.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_vars.o \ - ./physics_wrf/module_sf_mynn.o \ - ./physics_wrf/module_sf_sfclay.o + mpas_atmphys_vars.o mpas_atmphys_init.o: \ mpas_atmphys_driver_convection.o \ @@ -172,9 +146,6 @@ mpas_atmphys_init.o: \ mpas_atmphys_landuse.o \ mpas_atmphys_o3climatology.o -mpas_atmphys_init_microphysics.o: \ - ./physics_wrf/module_mp_thompson.o - mpas_atmphys_interface.o: \ mpas_atmphys_constants.o \ mpas_atmphys_vars.o @@ -185,8 +156,7 @@ mpas_atmphys_landuse.o: \ mpas_atmphys_lsm_noahinit.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_utilities.o \ - ./physics_wrf/module_sf_noahlsm.o + mpas_atmphys_utilities.o mpas_atmphys_manager.o: \ mpas_atmphys_constants.o \ @@ -198,18 +168,15 @@ mpas_atmphys_manager.o: \ mpas_atmphys_o3climatology.o: \ mpas_atmphys_date_time.o \ mpas_atmphys_constants.o \ - mpas_atmphys_utilities.o \ - ./physics_wrf/module_ra_cam_support.o + mpas_atmphys_utilities.o mpas_atmphys_rrtmg_lwinit.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_utilities.o \ - ./physics_wrf/module_ra_rrtmg_lw.o + mpas_atmphys_utilities.o mpas_atmphys_rrtmg_swinit.o: \ mpas_atmphys_constants.o \ - mpas_atmphys_utilities.o \ - ./physics_wrf/module_ra_rrtmg_sw.o + mpas_atmphys_utilities.o mpas_atmphys_todynamics.o: \ mpas_atmphys_constants.o \ @@ -227,7 +194,7 @@ mpas_atmphys_update.o: \ clean: $(RM) *.o *.mod *.f90 libphys.a - ( cd physics_wrf; make clean ) + ( cd physics_wrf; $(MAKE) clean ) @# Certain systems with intel compilers generate *.i files @# This removes them during the clean process $(RM) *.i @@ -236,7 +203,7 @@ clean: $(RM) $@ $*.mod ifeq "$(GEN_F90)" "true" $(CPP) $(CPPFLAGS) $(COREDEF) $(HYDROSTATIC) $(CPPINCLUDES) $< > $*.f90 - $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) -I.. -I../../framework -I../../operators -I./physics_wrf -I../../external/esmf_time_f90 + $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) -I./physics_wrf -I.. -I../../framework -I../../external/esmf_time_f90 else - $(FC) $(CPPFLAGS) $(COREDEF) $(HYDROSATIC) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) -I.. -I../../framework -I../../operators -I./physics_wrf -I../../external/esmf_time_f90 + $(FC) $(CPPFLAGS) $(COREDEF) $(HYDROSATIC) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) -I./physics_wrf -I.. -I../../framework -I../../external/esmf_time_f90 endif diff --git a/src/core_atmosphere/physics/mpas_atmphys_camrad_init.F b/src/core_atmosphere/physics/mpas_atmphys_camrad_init.F index 27830c02d9..e2c8aabcf8 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_camrad_init.F +++ b/src/core_atmosphere/physics/mpas_atmphys_camrad_init.F @@ -15,7 +15,6 @@ module mpas_atmphys_camrad_init use mpas_dmpar use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_constants,only: cp,degrad,ep_2,gravity,R_d,R_v,stbolt @@ -67,13 +66,14 @@ module mpas_atmphys_camrad_init !================================================================================================================= - subroutine camradinit(dminfo,mesh,atm_input,diag,state,time_lev) + subroutine camradinit(dminfo,mesh,atm_input,diag,diag_physics,state,time_lev) !================================================================================================================= !input arguments: type(dm_info),intent(in):: dminfo type(mpas_pool_type),intent(in):: mesh type(mpas_pool_type),intent(in):: diag + type(mpas_pool_type),intent(in):: diag_physics integer,intent(in):: time_lev @@ -146,7 +146,7 @@ subroutine camradinit(dminfo,mesh,atm_input,diag,state,time_lev) ! call mpas_log_write(' end subroutine oznini') !initialization of aerosol concentrations: - call aerosol_init(dminfo,mesh,diag,state,time_lev) + call aerosol_init(dminfo,mesh,diag,diag_physics,state,time_lev) ! call mpas_log_write(' end subroutine aerosol_init') end subroutine camradinit @@ -394,7 +394,7 @@ subroutine radaeini(dminfo,pstdx,mwdryx,mwco2x) end subroutine radaeini !================================================================================================================= - subroutine aerosol_init(dminfo,mesh,diag,state,time_lev) + subroutine aerosol_init(dminfo,mesh,diag,diag_physics,state,time_lev) !================================================================================================================= !This subroutine assumes a uniform aerosol distribution in both time and space. It should be @@ -404,6 +404,7 @@ subroutine aerosol_init(dminfo,mesh,diag,state,time_lev) type(dm_info),intent(in) :: dminfo type(mpas_pool_type),intent(in):: mesh type(mpas_pool_type),intent(in):: diag + type(mpas_pool_type),intent(in):: diag_physics integer,intent(in):: time_lev @@ -446,11 +447,12 @@ subroutine aerosol_init(dminfo,mesh,diag,state,time_lev) call mpas_pool_get_dimension(mesh,'nAerLevels',nAerLevels) call mpas_pool_get_dimension(mesh,'nVertLevels',nVertLevels) - call mpas_pool_get_array(mesh,'m_hybi',m_hybi) + call mpas_pool_get_array(diag_physics,'m_hybi',m_hybi) + call mpas_pool_get_array(diag_physics,'m_ps',m_psp) + call mpas_pool_get_array(diag_physics,'m_ps',m_psn) + call mpas_pool_get_array(diag,'pressure_base',pressure_b) - call mpas_pool_get_array(state,'m_ps',m_psp,1) - call mpas_pool_get_array(state,'m_ps',m_psn,2) call mpas_pool_get_array(state,'aerosols',aerosolcp,1) call mpas_pool_get_array(state,'aerosols',aerosolcn,2) @@ -464,7 +466,8 @@ subroutine aerosol_init(dminfo,mesh,diag,state,time_lev) psurf = 1.e05 do iCell = 1, nCells m_psp(iCell) = psurf - m_psn(iCell) = psurf +! m_psn(iCell) = psurf ! m_psp and m_psn both point to the same array for m_ps, so we only + ! need to initialize one of them enddo !mxaerl = max number of levels (from bottom) for background aerosol. Limit background aerosol diff --git a/src/core_atmosphere/physics/mpas_atmphys_control.F b/src/core_atmosphere/physics/mpas_atmphys_control.F index ace27ea81d..3ea3e96878 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_control.F +++ b/src/core_atmosphere/physics/mpas_atmphys_control.F @@ -9,7 +9,6 @@ module mpas_atmphys_control use mpas_dmpar use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_utilities diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver.F b/src/core_atmosphere/physics/mpas_atmphys_driver.F index 29eb7a943c..b120d0ccc4 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver.F @@ -8,7 +8,6 @@ !================================================================================================================= module mpas_atmphys_driver use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_driver_cloudiness @@ -189,14 +188,14 @@ subroutine physics_driver(domain,itimestep,xtime_s) call mpas_pool_get_dimension(block % dimensions, 'cellSolveThreadEnd', cellSolveThreadEnd) !allocate arrays shared by all physics parameterizations: - call allocate_forall_physics + call allocate_forall_physics(block%configs) !physics prep step: time_lev = 1 !$OMP PARALLEL DO do thread=1,nThreads - call MPAS_to_physics(mesh,state,time_lev,diag,diag_physics, & + call MPAS_to_physics(block%configs,mesh,state,time_lev,diag,diag_physics, & cellSolveThreadStart(thread), cellSolveThreadEnd(thread)) end do !$OMP END PARALLEL DO @@ -215,7 +214,7 @@ subroutine physics_driver(domain,itimestep,xtime_s) !call to short wave radiation scheme: if(l_radtsw) then time_lev = 1 - call allocate_radiation_sw(xtime_s) + call allocate_radiation_sw(block%configs,xtime_s) !$OMP PARALLEL DO do thread=1,nThreads call driver_radiation_sw(itimestep,block%configs,mesh,state,time_lev,diag_physics, & @@ -228,7 +227,7 @@ subroutine physics_driver(domain,itimestep,xtime_s) !call to long wave radiation scheme: if(l_radtlw) then time_lev = 1 - call allocate_radiation_lw(xtime_s) + call allocate_radiation_lw(block%configs,xtime_s) !$OMP PARALLEL DO do thread=1,nThreads call driver_radiation_lw(xtime_s,block%configs,mesh,state,time_lev,diag_physics, & @@ -251,19 +250,19 @@ subroutine physics_driver(domain,itimestep,xtime_s) !deallocate all radiation arrays: if(config_radt_sw_scheme.ne.'off' .or. config_radt_lw_scheme.ne.'off') & call deallocate_cloudiness - if(config_radt_sw_scheme.ne.'off') call deallocate_radiation_sw - if(config_radt_lw_scheme.ne.'off') call deallocate_radiation_lw + if(config_radt_sw_scheme.ne.'off') call deallocate_radiation_sw(block%configs) + if(config_radt_lw_scheme.ne.'off') call deallocate_radiation_lw(block%configs) !call to surface-layer scheme: if(config_sfclayer_scheme .ne. 'off') then - call allocate_sfclayer(config_frac_seaice) + call allocate_sfclayer(block%configs) !$OMP PARALLEL DO do thread=1,nThreads call driver_sfclayer(itimestep,block%configs,mesh,diag_physics,sfc_input, & cellSolveThreadStart(thread), cellSolveThreadEnd(thread)) end do !$OMP END PARALLEL DO - call deallocate_sfclayer(config_frac_seaice) + call deallocate_sfclayer(block%configs) endif !call to 1d ocean mixed-layer model @@ -283,14 +282,15 @@ subroutine physics_driver(domain,itimestep,xtime_s) !call to pbl schemes: if(config_pbl_scheme .ne. 'off' .and. config_sfclayer_scheme .ne. 'off') then - call allocate_pbl + call allocate_pbl(block%configs) !$OMP PARALLEL DO do thread=1,nThreads call driver_pbl(itimestep,block%configs,mesh,sfc_input,diag_physics,tend_physics, & cellSolveThreadStart(thread),cellSolveThreadEnd(thread)) end do !$OMP END PARALLEL DO - call deallocate_pbl + call deallocate_pbl(block%configs) + endif !call to gravity wave drag over orography scheme: @@ -308,19 +308,19 @@ subroutine physics_driver(domain,itimestep,xtime_s) !call to convection scheme: !$OMP PARALLEL DO do thread=1,nThreads - call update_convection_step1(diag_physics,tend_physics, & + call update_convection_step1(block%configs,diag_physics,tend_physics, & cellSolveThreadStart(thread),cellSolveThreadEnd(thread)) end do !$OMP END PARALLEL DO if(l_conv) then - call allocate_convection + call allocate_convection(block%configs) !$OMP PARALLEL DO do thread=1,nThreads call driver_convection(itimestep,block%configs,mesh,sfc_input,diag_physics,tend_physics, & cellSolveThreadStart(thread), cellSolveThreadEnd(thread)) end do !$OMP END PARALLEL DO - call deallocate_convection + call deallocate_convection(block%configs) endif !update diagnostics: if(config_convection_scheme .ne. 'off') then @@ -333,7 +333,7 @@ subroutine physics_driver(domain,itimestep,xtime_s) end if !deallocate arrays shared by all physics parameterizations: - call deallocate_forall_physics + call deallocate_forall_physics(block%configs) block => block % next end do diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_cloudiness.F b/src/core_atmosphere/physics/mpas_atmphys_driver_cloudiness.F index 71d9eb6c2b..9e444ae2f7 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_cloudiness.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_cloudiness.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_cloudiness use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants, only: ep_2 use mpas_atmphys_vars @@ -49,6 +49,9 @@ module mpas_atmphys_driver_cloudiness ! cloud formation, but changes to the cloud water and cloud ice mixing ratios only affect the long wave and ! short wave radiation codes. ! Laura D. Fowler (laura@ucar.edu) / 2016-07-05. +! * since we removed the local variable radt_cld_scheme from mpas_atmphys_vars.F, now defines radt_cld_scheme +! as a pointer to config_radt_cld_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. contains @@ -177,24 +180,33 @@ subroutine driver_cloudiness(configs,mesh,diag_physics,sfc_input,its,ite) !inout arguments: type(mpas_pool_type),intent(inout):: diag_physics -!local variables: +!local variables and pointers: + character(len=StrKIND),pointer:: radt_cld_scheme + integer:: i,j,k !----------------------------------------------------------------------------------------------------------------- !call mpas_log_write('') !call mpas_log_write('--- enter subroutine driver_cloudiness:') + call mpas_pool_get_config(configs,'config_radt_cld_scheme',radt_cld_scheme) + !copy MPAS arrays to local arrays: call cloudiness_from_MPAS(configs,mesh,diag_physics,sfc_input,its,ite) cld_fraction_select: select case (trim(radt_cld_scheme)) case("cld_incidence") + call mpas_timer_start('calc_cldincidence') call calc_cldincidence(cldfrac_p,qcrad_p,qirad_p,f_qc,f_qi,its,ite) + call mpas_timer_stop('calc_cldincidence') case("cld_fraction") + call mpas_timer_start('calc_cldfraction') call calc_cldfraction(cldfrac_p,t_p,pres_p,qvrad_p,qcrad_p,qirad_p,qs_p,its,ite) + call mpas_timer_stop('calc_cldfraction') case("cld_fraction_thompson") + call mpas_timer_start('cal_cldfra3') call cal_cldfra3( & cldfra = cldfrac_p , qv = qvrad_p , qc = qcrad_p , qi = qirad_p , & qs = qsrad_p , p = pres_hyd_p , t = t_p , rho = rho_p , & @@ -203,6 +215,7 @@ subroutine driver_cloudiness(configs,mesh,diag_physics,sfc_input,its,ite) ims = ims , ime = ime , jms = jms , jme = jme , kms = kds , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('cal_cldfra3') case default diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_convection.F b/src/core_atmosphere/physics/mpas_atmphys_driver_convection.F index defcc920d1..58f9bfaefc 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_convection.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_convection.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_convection use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants use mpas_atmphys_utilities @@ -88,20 +88,30 @@ module mpas_atmphys_driver_convection ! * for the kain_fritsch parameterization of convection, change the definition of dx_p to match that used in the ! Grell-Freitas and "new Tiedtke" parameterization. ! Laura D. Fowler (laura@ucar.edu) / 2016-10-18. +! * since we removed the local variable convection_scheme from mpas_atmphys_vars.F, now defines convection_scheme +! as a pointer to config_convection_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. contains !================================================================================================================= - subroutine allocate_convection + subroutine allocate_convection(configs) !================================================================================================================= -!local variables: +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local variables and pointers: + character(len=StrKIND),pointer:: convection_scheme + integer:: i,k,j !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_convection_scheme',convection_scheme) + if(.not.allocated(cu_act_flag)) allocate(cu_act_flag(ims:ime,jms:jme) ) if(.not.allocated(rthcuten_p) ) allocate(rthcuten_p(ims:ime,kms:kme,jms:jme)) if(.not.allocated(rqvcuten_p) ) allocate(rqvcuten_p(ims:ime,kms:kme,jms:jme)) @@ -252,9 +262,19 @@ subroutine allocate_convection end subroutine allocate_convection !================================================================================================================= - subroutine deallocate_convection + subroutine deallocate_convection(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: convection_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_convection_scheme',convection_scheme) + if(allocated(cu_act_flag)) deallocate(cu_act_flag) if(allocated(rthcuten_p) ) deallocate(rthcuten_p ) if(allocated(rqvcuten_p) ) deallocate(rqvcuten_p ) @@ -347,6 +367,8 @@ subroutine init_convection(mesh,configs,diag_physics) !local pointers: logical,pointer:: config_do_restart + character(len=StrKIND),pointer:: convection_scheme + integer,pointer:: nCells real(kind=RKIND),dimension(:),pointer:: nca @@ -356,7 +378,8 @@ subroutine init_convection(mesh,configs,diag_physics) !----------------------------------------------------------------------------------------------------------------- call mpas_pool_get_dimension(mesh,'nCells',nCells) - call mpas_pool_get_config(configs,'config_do_restart',config_do_restart) + call mpas_pool_get_config(configs,'config_do_restart' ,config_do_restart) + call mpas_pool_get_config(configs,'config_convection_scheme',convection_scheme) convection_select: select case(convection_scheme) @@ -402,6 +425,7 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ !local pointers: logical,pointer:: config_do_restart integer,pointer:: gfconv_closure_deep,gfconv_closure_shallow + character(len=StrKIND),pointer:: convection_scheme real(kind=RKIND),pointer:: len_disp !variables specific to Kain_Fritsch parameterization: @@ -417,8 +441,9 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ call mpas_pool_get_config(configs,'config_gfconv_closure_deep',gfconv_closure_deep) call mpas_pool_get_config(configs,'config_gfconv_closure_shallow',gfconv_closure_shallow) - call mpas_pool_get_config(configs,'config_len_disp',len_disp) - call mpas_pool_get_config(configs,'config_do_restart',config_do_restart) + call mpas_pool_get_config(configs,'config_len_disp' ,len_disp ) + call mpas_pool_get_config(configs,'config_do_restart' ,config_do_restart) + call mpas_pool_get_config(configs,'config_convection_scheme',convection_scheme) !initialize instantaneous precipitation, and copy convective tendencies from the dynamics to !the physics grid: @@ -444,6 +469,7 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ convection_select: select case(convection_scheme) case ("cu_grell_freitas") + call mpas_timer_start('Grell-Freitas') call cu_grell_freitas( & itimestep = itimestep , dt = dt_dyn , & dxCell = dx_p , areaCell = area_p , & @@ -474,6 +500,7 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ ims = ims , ime = ime , jms = jms , jme = jme , kms = kds , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('Grell-Freitas') case ("cu_kain_fritsch") if(itimestep == 1) then @@ -482,6 +509,7 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ ktau = itimestep + 1 endif + call mpas_timer_start('Kain-Fritsch') call kf_eta_cps ( & pcps = pres_hyd_p , t = t_p , & dt = dt_dyn , ktau = ktau , & @@ -512,8 +540,10 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ ims = ims , ime = ime , jms = jms , jme = jme , kms = kds , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('Kain-Fritsch') case("cu_tiedtke") + call mpas_timer_start('Tiedtke') call cu_tiedtke( & pcps = pres_hyd_p , p8w = pres2_hyd_p , & znu = znu_hyd_p , t3d = t_p , & @@ -536,8 +566,10 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ ims = ims , ime = ime , jms = jms , jme = jme , kms = kds , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('Tiedtke') case("cu_ntiedtke") + call mpas_timer_start('New_Tiedtke') call cu_ntiedtke( & pcps = pres_hyd_p , p8w = pres2_hyd_p , & t3d = t_p , dz8w = dz_p , & @@ -561,6 +593,7 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ ims = ims , ime = ime , jms = jms , jme = jme , kms = kds , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('New_Tiedtke') case default @@ -568,7 +601,7 @@ subroutine driver_convection(itimestep,configs,mesh,sfc_input,diag_physics,tend_ !copy instantaneous and accumulated precipitation, convective tendencies, and "other" arrays !specific to convection parameterization back to the dynamics grid: - call convection_to_MPAS(diag_physics,tend_physics,its,ite) + call convection_to_MPAS(configs,diag_physics,tend_physics,its,ite) !call mpas_log_write('--- end subroutine driver_convection.') !call mpas_log_write('') @@ -590,6 +623,7 @@ subroutine convection_from_MPAS(dt_dyn,configs,mesh,sfc_input,diag_physics,tend_ real(kind=RKIND),intent(in):: dt_dyn !local pointers: + character(len=StrKIND),pointer:: convection_scheme integer,dimension(:),pointer:: kpbl,k22_shallow,kbcon_shallow,ktop_shallow,ktop_deep real(kind=RKIND),dimension(:),pointer :: areaCell,meshDensity real(kind=RKIND),dimension(:),pointer :: nca,cubot,cutop,cuprec,raincv @@ -609,6 +643,8 @@ subroutine convection_from_MPAS(dt_dyn,configs,mesh,sfc_input,diag_physics,tend_ !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_convection_scheme',convection_scheme) + call mpas_pool_get_array(diag_physics,'cuprec',cuprec) call mpas_pool_get_array(diag_physics,'raincv',raincv) @@ -812,10 +848,11 @@ subroutine convection_from_MPAS(dt_dyn,configs,mesh,sfc_input,diag_physics,tend_ end subroutine convection_from_MPAS !================================================================================================================= - subroutine convection_to_MPAS(diag_physics,tend_physics,its,ite) + subroutine convection_to_MPAS(configs,diag_physics,tend_physics,its,ite) !================================================================================================================= !input arguments: + type(mpas_pool_type),intent(in):: configs integer,intent(in):: its,ite !inout arguments: @@ -826,6 +863,7 @@ subroutine convection_to_MPAS(diag_physics,tend_physics,its,ite) integer:: i,k,j !local pointers: + character(len=StrKIND),pointer:: convection_scheme integer,dimension(:),pointer:: k22_shallow,kbcon_shallow,ktop_shallow,ktop_deep real(kind=RKIND),dimension(:),pointer :: nca,cubot,cutop,cuprec,raincv real(kind=RKIND),dimension(:),pointer :: xmb_total,xmb_shallow @@ -836,6 +874,8 @@ subroutine convection_to_MPAS(diag_physics,tend_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_convection_scheme',convection_scheme) + call mpas_pool_get_array(diag_physics,'cuprec',cuprec) call mpas_pool_get_array(diag_physics,'raincv',raincv) @@ -936,10 +976,11 @@ subroutine convection_to_MPAS(diag_physics,tend_physics,its,ite) end subroutine convection_to_MPAS !================================================================================================================= - subroutine update_convection_step1(diag_physics,tend_physics,its,ite) + subroutine update_convection_step1(configs,diag_physics,tend_physics,its,ite) !================================================================================================================= !input arguments: + type(mpas_pool_type),intent(in):: configs integer,intent(in):: its,ite !inout arguments: @@ -947,6 +988,7 @@ subroutine update_convection_step1(diag_physics,tend_physics,its,ite) type(mpas_pool_type),intent(inout):: tend_physics !local pointers: + character(len=StrKIND),pointer:: convection_scheme real(kind=RKIND),dimension(:),pointer :: nca,cubot,cutop,cuprec,raincv real(kind=RKIND),dimension(:,:),pointer:: rthcuten,rqvcuten,rqccuten,rqicuten,rqrcuten,rqscuten @@ -955,6 +997,8 @@ subroutine update_convection_step1(diag_physics,tend_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_convection_scheme',convection_scheme) + convection_select: select case(convection_scheme) case ("cu_kain_fritsch") diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_gwdo.F b/src/core_atmosphere/physics/mpas_atmphys_driver_gwdo.F index 0d6596eadc..08eee06635 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_gwdo.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_gwdo.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_gwdo use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants use mpas_atmphys_vars @@ -267,6 +267,8 @@ subroutine driver_gwdo(itimestep,configs,mesh,sfc_input,diag_physics,tend_physic type(mpas_pool_type),intent(inout):: tend_physics !local variables: + character(len=StrKIND),pointer:: gwdo_scheme + integer:: i,iCell,iEdge real(kind=RKIND),dimension(:),allocatable:: dx_max @@ -274,12 +276,15 @@ subroutine driver_gwdo(itimestep,configs,mesh,sfc_input,diag_physics,tend_physic !call mpas_log_write('') !call mpas_log_write('--- enter subroutine driver_gwdo:') + call mpas_pool_get_config(configs,'config_gwdo_scheme',gwdo_scheme) + !copy MPAS arrays to local arrays: call gwdo_from_MPAS(configs,mesh,sfc_input,diag_physics,tend_physics,its,ite) gwdo_select: select case (trim(gwdo_scheme)) case("bl_ysu_gwdo") + call mpas_timer_start('GWDO_YSU') call gwdo ( & p3d = pres_hydd_p , p3di = pres2_hydd_p , pi3d = pi_p , & u3d = u_p , v3d = v_p , t3d = t_p , & @@ -297,6 +302,7 @@ subroutine driver_gwdo(itimestep,configs,mesh,sfc_input,diag_physics,tend_physic ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('GWDO_YSU') case default diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_lsm.F b/src/core_atmosphere/physics/mpas_atmphys_driver_lsm.F index 0911eb2e62..c2c902d3e4 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_lsm.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_lsm.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_lsm use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants use mpas_atmphys_landuse @@ -82,6 +82,10 @@ module mpas_atmphys_driver_lsm ! Laura D. Fowler (laura@ucar.edu) / 2016-05-11. ! * added the calculation of surface variables over seaice cells when config_frac_seaice is set to true. ! Laura D. Fowler (laura@ucar.edu) / 2016-10-03. +! * since we removed the local variable lsm_scheme from mpas_atmphys_vars.F, now defines lsm_scheme as a +! pointer to config_lsm_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. + ! ! DOCUMENTATION: @@ -685,8 +689,13 @@ subroutine init_lsm(dminfo,mesh,configs,diag_physics,sfc_input) type(mpas_pool_type),intent(inout):: diag_physics type(mpas_pool_type),intent(inout):: sfc_input +!local pointers: + character(len=StrKIND),pointer:: lsm_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_lsm_scheme',lsm_scheme) + lsm_select: select case (trim(lsm_scheme)) case ("noah") @@ -715,23 +724,25 @@ subroutine driver_lsm(itimestep,configs,mesh,diag_physics,sfc_input,its,ite) !local pointers: logical,pointer:: config_sfc_albedo + character(len=StrKIND),pointer:: lsm_scheme character(len=StrKIND),pointer:: mminlu !----------------------------------------------------------------------------------------------------------------- !call mpas_log_write('') !call mpas_log_write('--- enter subroutine driver_lsm:') - call mpas_pool_get_config(configs,'config_sfc_albedo' ,config_sfc_albedo ) + call mpas_pool_get_config(configs,'config_sfc_albedo',config_sfc_albedo) + call mpas_pool_get_config(configs,'config_lsm_scheme',lsm_scheme) call mpas_pool_get_array(sfc_input,'mminlu',mminlu) !copy MPAS arrays to local arrays: call lsm_from_MPAS(configs,mesh,diag_physics,sfc_input,its,ite) -! call mpas_log_write('--- end lsm_from_MPAS') !call to land-surface scheme: lsm_select: select case (trim(lsm_scheme)) case("noah") + call mpas_timer_start('Noah') call lsm( & dz8w = dz_p , p8w3d = pres2_hyd_p , t3d = t_p , & qv3d = qv_p , xland = xland_p , xice = xice_p , & @@ -778,6 +789,7 @@ subroutine driver_lsm(itimestep,configs,mesh,diag_physics,sfc_input,its,ite) ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('Noah') case default diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_microphysics.F b/src/core_atmosphere/physics/mpas_atmphys_driver_microphysics.F index 92157639f4..1d6e3235e6 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_microphysics.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_microphysics.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_microphysics use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants use mpas_atmphys_init_microphysics @@ -81,6 +81,9 @@ module mpas_atmphys_driver_microphysics ! * added parameterization of the WSM6 cloud microphysics from WRF version 3.8.1. To initialize WSM6 as in its ! original version, set the hail_option to 0. ! Laura D. Fowler (laura@ucar.edu) / 2016-09-19. +! * since we removed the local variable microp_scheme from mpas_atmphys_vars.F, now defines microp_scheme as a +! pointer to config_microp_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. !--- initialization option for WSM6 from WRF version 3.8.1. this option could also be set as a namelist parameter. integer,parameter:: hail_opt = 0 @@ -90,9 +93,19 @@ module mpas_atmphys_driver_microphysics !================================================================================================================= - subroutine allocate_microphysics + subroutine allocate_microphysics(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: microp_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + !sounding variables: if(.not.allocated(rho_p) ) allocate(rho_p(ims:ime,kms:kme,jms:jme) ) if(.not.allocated(th_p) ) allocate(th_p(ims:ime,kms:kme,jms:jme) ) @@ -154,9 +167,19 @@ subroutine allocate_microphysics end subroutine allocate_microphysics !================================================================================================================= - subroutine deallocate_microphysics + subroutine deallocate_microphysics(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: microp_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + !sounding variables: if(allocated(rho_p) ) deallocate(rho_p ) if(allocated(th_p) ) deallocate(th_p ) @@ -218,19 +241,25 @@ subroutine deallocate_microphysics end subroutine deallocate_microphysics !================================================================================================================= - subroutine microphysics_init(dminfo,mesh,sfc_input,diag_physics) + subroutine microphysics_init(dminfo,configs,mesh,sfc_input,diag_physics) !================================================================================================================= !input arguments: type(dm_info),intent(in):: dminfo + type(mpas_pool_type),intent(in):: configs type(mpas_pool_type),intent(in):: mesh type(mpas_pool_type),intent(in):: sfc_input !inout arguments: type(mpas_pool_type),intent(inout):: diag_physics +!local pointer: + character(len=StrKIND),pointer:: microp_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + microp_select: select case(microp_scheme) case("mp_thompson") @@ -264,6 +293,9 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten type(mpas_pool_type),intent(inout):: diag_physics type(mpas_pool_type),intent(inout):: tend +!local pointers: + character(len=StrKIND),pointer:: microp_scheme + !local variables and arrays: logical:: log_microphysics integer:: i,icell,icount,istep,j,k,kk @@ -272,22 +304,25 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten !call mpas_log_write('') !call mpas_log_write('---enter subroutine driver_microphysics:') + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + !... allocation of microphysics arrays: !$OMP MASTER - call allocate_microphysics + call allocate_microphysics(configs) !$OMP END MASTER !$OMP BARRIER !... initialization of precipitation related arrays: - call precip_from_MPAS(diag_physics,its,ite) + call precip_from_MPAS(configs,diag_physics,its,ite) !... initialization of soundings for non-hydrostatic dynamical cores. - call microphysics_from_MPAS(mesh,state,time_lev,diag,diag_physics,its,ite) + call microphysics_from_MPAS(configs,mesh,state,time_lev,diag,diag_physics,its,ite) !... call to different cloud microphysics schemes: microp_select: select case(microp_scheme) case ("mp_kessler") + call mpas_timer_start('Kessler') call kessler( & t = th_p , qv = qv_p , qc = qc_p , & qr = qr_p , rho = rho_p , pii = pi_p , & @@ -300,9 +335,11 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('Kessler') case ("mp_thompson") istep = 1 + call mpas_timer_start('Thompson') do while (istep .le. n_microp) call mp_gt_driver( & th = th_p , qv = qv_p , qc = qc_p , & @@ -322,8 +359,10 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten ) istep = istep + 1 enddo + call mpas_timer_stop('Thompson') case ("mp_wsm6") + call mpas_timer_start('WSM6') call wsm6( & th = th_p , q = qv_p , qc = qc_p , & qr = qr_p , qi = qi_p , qs = qs_p , & @@ -343,6 +382,7 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('WSM6') case default @@ -355,7 +395,7 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten !the computation of simulated radar reflectivity: if(trim(microp_scheme) == "mp_wsm6" .or. & trim(microp_scheme) == "mp_thompson") then - call compute_radar_reflectivity(diag_physics,its,ite) + call compute_radar_reflectivity(configs,diag_physics,its,ite) else call mpas_log_write('*** NOTICE: NOT computing simulated radar reflectivity') call mpas_log_write(' since WSM6 or Thompson microphysics scheme was not selected') @@ -372,12 +412,12 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten !... copy updated cloud microphysics variables from the wrf-physics grid back to the geodesic- ! dynamics grid: - call microphysics_to_MPAS(mesh,state,time_lev,diag,diag_physics,tend,itimestep,its,ite) + call microphysics_to_MPAS(configs,mesh,state,time_lev,diag,diag_physics,tend,itimestep,its,ite) !... deallocation of all microphysics arrays: !$OMP BARRIER !$OMP MASTER - call deallocate_microphysics + call deallocate_microphysics(configs) !$OMP END MASTER !call mpas_log_write('---enter subroutine driver_microphysics:') @@ -386,16 +426,18 @@ subroutine driver_microphysics(configs,mesh,state,time_lev,diag,diag_physics,ten end subroutine driver_microphysics !================================================================================================================= - subroutine precip_from_MPAS(diag_physics,its,ite) + subroutine precip_from_MPAS(configs,diag_physics,its,ite) !================================================================================================================= !input variables: + type(mpas_pool_type),intent(in):: configs integer,intent(in):: its,ite !output variables: type(mpas_pool_type),intent(inout):: diag_physics !local pointers: + character(len=StrKIND),pointer:: microp_scheme integer,pointer:: nCellsSolve real,dimension(:),pointer:: graupelncv,rainncv,snowncv,sr @@ -404,6 +446,8 @@ subroutine precip_from_MPAS(diag_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + call mpas_pool_get_array(diag_physics,'graupelncv',graupelncv) call mpas_pool_get_array(diag_physics,'rainncv' ,rainncv ) call mpas_pool_get_array(diag_physics,'snowncv' ,snowncv ) @@ -422,7 +466,7 @@ subroutine precip_from_MPAS(diag_physics,its,ite) enddo !variables specific to different cloud microphysics schemes: - microp_select_init: select case(microp_scheme) + microp_select: select case(microp_scheme) case ("mp_thompson","mp_wsm6") do j = jts, jte @@ -443,7 +487,7 @@ subroutine precip_from_MPAS(diag_physics,its,ite) case default - end select microp_select_init + end select microp_select end subroutine precip_from_MPAS @@ -453,13 +497,13 @@ subroutine precip_to_MPAS(configs,diag_physics,its,ite) !input arguments: type(mpas_pool_type),intent(in):: configs - integer,intent(in):: its,ite !inout arguments: type(mpas_pool_type),intent(inout):: diag_physics !local pointers: + character(len=StrKIND),pointer:: microp_scheme integer,dimension(:),pointer:: i_rainnc real(kind=RKIND),pointer:: config_bucket_rainnc @@ -473,6 +517,7 @@ subroutine precip_to_MPAS(configs,diag_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme ) call mpas_pool_get_config(configs,'config_bucket_rainnc',config_bucket_rainnc) call mpas_pool_get_array(diag_physics,'i_rainnc' ,i_rainnc ) @@ -538,16 +583,18 @@ subroutine precip_to_MPAS(configs,diag_physics,its,ite) end subroutine precip_to_MPAS !================================================================================================================= - subroutine compute_radar_reflectivity(diag_physics,its,ite) + subroutine compute_radar_reflectivity(configs,diag_physics,its,ite) !================================================================================================================= !input arguments: + type(mpas_pool_type),intent(in):: configs integer,intent(in):: its,ite !inout arguments: type(mpas_pool_type),intent(inout):: diag_physics !local pointers: + character(len=StrKIND),pointer:: microp_scheme real(kind=RKIND),dimension(:),pointer:: refl10cm_max,refl10cm_1km,refl10cm_1km_max !local variables and arrays: @@ -557,6 +604,8 @@ subroutine compute_radar_reflectivity(diag_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + call mpas_pool_get_array(diag_physics,'refl10cm_max',refl10cm_max) call mpas_pool_get_array(diag_physics,'refl10cm_1km',refl10cm_1km) call mpas_pool_get_array(diag_physics,'refl10cm_1km_max',refl10cm_1km_max) diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_oml.F b/src/core_atmosphere/physics/mpas_atmphys_driver_oml.F index ca000cb772..eaa898f980 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_oml.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_oml.F @@ -8,7 +8,6 @@ !================================================================================================================= module mpas_atmphys_driver_oml use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_constants diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_pbl.F b/src/core_atmosphere/physics/mpas_atmphys_driver_pbl.F index 51d62d96e3..75d075ac3f 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_pbl.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_pbl.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_pbl use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants use mpas_atmphys_vars @@ -63,15 +63,28 @@ module mpas_atmphys_driver_pbl ! * updated the call to subroutine ysu in comjunction with updating module_bl_ysu.F from WRF version 3.6.1 to ! WRF version 3.8.1 ! Laura D. Fowler (laura@ucar.edu) / 2016-10-27. +! * since we removed the local variable pbl_scheme from mpas_atmphys_vars.F, now defines pbl_scheme as a pointer +! to config_pbl_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. contains !================================================================================================================= - subroutine allocate_pbl + subroutine allocate_pbl(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: pbl_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme) + if(.not.allocated(hfx_p) ) allocate(hfx_p(ims:ime,jms:jme) ) if(.not.allocated(qfx_p) ) allocate(qfx_p(ims:ime,jms:jme) ) if(.not.allocated(ust_p) ) allocate(ust_p(ims:ime,jms:jme) ) @@ -148,9 +161,19 @@ subroutine allocate_pbl end subroutine allocate_pbl !================================================================================================================= - subroutine deallocate_pbl + subroutine deallocate_pbl(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: pbl_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme) + if(allocated(hfx_p) ) deallocate(hfx_p ) if(allocated(qfx_p) ) deallocate(qfx_p ) if(allocated(ust_p) ) deallocate(ust_p ) @@ -241,6 +264,8 @@ subroutine pbl_from_MPAS(configs,mesh,sfc_input,diag_physics,tend_physics,its,it integer:: i,k,j !local pointers: + character(len=StrKIND),pointer:: pbl_scheme + real(kind=RKIND),dimension(:),pointer:: hfx,hpbl,qfx,ust,wspd,xland,znt real(kind=RKIND),dimension(:),pointer:: delta,wstar @@ -257,6 +282,8 @@ subroutine pbl_from_MPAS(configs,mesh,sfc_input,diag_physics,tend_physics,its,it !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme) + call mpas_pool_get_array(diag_physics,'hfx' ,hfx ) call mpas_pool_get_array(diag_physics,'hpbl' ,hpbl ) call mpas_pool_get_array(diag_physics,'qfx' ,qfx ) @@ -408,9 +435,12 @@ subroutine pbl_from_MPAS(configs,mesh,sfc_input,diag_physics,tend_physics,its,it end subroutine pbl_from_MPAS !================================================================================================================= - subroutine pbl_to_MPAS(diag_physics,tend_physics,its,ite) + subroutine pbl_to_MPAS(configs,diag_physics,tend_physics,its,ite) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + !inout arguments: type(mpas_pool_type),intent(inout):: diag_physics type(mpas_pool_type),intent(inout):: tend_physics @@ -421,7 +451,10 @@ subroutine pbl_to_MPAS(diag_physics,tend_physics,its,ite) integer:: i,k,j !local pointers: + character(len=StrKIND),pointer:: pbl_scheme + integer,dimension(:),pointer:: kpbl + real(kind=RKIND),dimension(:),pointer :: hpbl real(kind=RKIND),dimension(:,:),pointer:: kzh,kzm,kzq real(kind=RKIND),dimension(:,:),pointer:: rublten,rvblten,rthblten,rqvblten,rqcblten,rqiblten, & @@ -437,6 +470,8 @@ subroutine pbl_to_MPAS(diag_physics,tend_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme) + call mpas_pool_get_array(diag_physics,'kpbl' ,kpbl ) call mpas_pool_get_array(diag_physics,'hpbl' ,hpbl ) call mpas_pool_get_array(diag_physics,'kzh' ,kzh ) @@ -554,6 +589,7 @@ subroutine driver_pbl(itimestep,configs,mesh,sfc_input,diag_physics,tend_physics !local pointers: logical,pointer:: config_do_restart + character(len=StrKIND),pointer:: pbl_scheme !local variables: integer:: initflag @@ -564,6 +600,7 @@ subroutine driver_pbl(itimestep,configs,mesh,sfc_input,diag_physics,tend_physics !call mpas_log_write('--- enter subroutine driver_pbl:') call mpas_pool_get_config(configs,'config_do_restart',config_do_restart) + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme ) !copy MPAS arrays to local arrays: call pbl_from_MPAS(configs,mesh,sfc_input,diag_physics,tend_physics,its,ite) @@ -574,6 +611,7 @@ subroutine driver_pbl(itimestep,configs,mesh,sfc_input,diag_physics,tend_physics pbl_select: select case (trim(pbl_scheme)) case("bl_ysu") + call mpas_timer_start('YSU') call ysu ( & p3d = pres_hyd_p , p3di = pres2_hyd_p , psfc = psfc_p , & th3d = th_p , t3d = t_p , dz8w = dz_p , & @@ -599,8 +637,10 @@ subroutine driver_pbl(itimestep,configs,mesh,sfc_input,diag_physics,tend_physics ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('YSU') case("bl_mynn") + call mpas_timer_start('MYNN_pbl') call mynn_bl_driver ( & p = pres_hyd_p , exner = pi_p , ps = psfc_p , & th = th_p , dz = dz_p , u = u_p , & @@ -630,13 +670,14 @@ subroutine driver_pbl(itimestep,configs,mesh,sfc_input,diag_physics,tend_physics ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('MYNN_pbl') case default end select pbl_select !copy local arrays to MPAS grid: - call pbl_to_MPAS(diag_physics,tend_physics,its,ite) + call pbl_to_MPAS(configs,diag_physics,tend_physics,its,ite) !call mpas_log_write('--- end subroutine driver_pbl.') diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_lw.F b/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_lw.F index 836f0a5afc..7365b3dcf6 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_lw.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_lw.F @@ -9,12 +9,11 @@ module mpas_atmphys_driver_radiation_lw use mpas_kind_types use mpas_pool_routines - use mpas_derived_types + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_driver_radiation_sw, only: radconst use mpas_atmphys_constants use mpas_atmphys_manager, only: gmt,curr_julday,julday,year - use mpas_atmphys_o3climatology, only: vinterp_ozn use mpas_atmphys_camrad_init use mpas_atmphys_rrtmg_lwinit use mpas_atmphys_vars @@ -22,6 +21,7 @@ module mpas_atmphys_driver_radiation_lw !wrf physics: use module_ra_cam use module_ra_rrtmg_lw + use module_ra_rrtmg_vinterp implicit none private @@ -82,24 +82,36 @@ module mpas_atmphys_driver_radiation_lw ! * in the call to rrtmg_lwrad, substituted the variables qv_p, qc_p, qi_p, and qs_p with qvrad_p, qcrad_p, ! qirad_p, and qsrad_p initialized in subroutine cloudiness_from_MPAS. ! Laura D. Fowler (laura@ucar.edu) / 2016-07-09. +! * substituted "use mpas_atmphys_o3climatology" with "use module_ra_rrtmg_vinterp" since we moved subroutine +! vinterp_ozn to is own module in physics_wrf. +! laura D. Fowler (laura@ucar.edu) / 2017-01-27. ! * in subroutines radiation_lw_from_MPAS and radiation_lw_to_MPAS, revised the initialization of re_cloud, ! re_ice, re_snow, and rre_cloud, rre_ice, and rre_snow to handle the case when the cloud microphysics ! parameterization is turned off, i.e. config_microp_scheme='off'. ! Laura D. Fowler (laura@ucar.edu) / 2017-02-10. +! * since we removed the local variable radt_lw_scheme from mpas_atmphys_vars.F, now defines radt_lw_scheme +! as a pointer to config_radt_lw_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. contains !================================================================================================================= - subroutine allocate_radiation_lw(xtime_s) + subroutine allocate_radiation_lw(configs,xtime_s) !================================================================================================================= !input arguments: + type(mpas_pool_type),intent(in):: configs real(kind=RKIND),intent(in):: xtime_s +!local pointers: + character(len=StrKIND),pointer:: radt_lw_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_radt_lw_scheme',radt_lw_scheme) + if(.not.allocated(f_ice) ) allocate(f_ice(ims:ime,kms:kme,jms:jme) ) if(.not.allocated(f_rain) ) allocate(f_rain(ims:ime,kms:kme,jms:jme) ) @@ -194,9 +206,19 @@ subroutine allocate_radiation_lw(xtime_s) end subroutine allocate_radiation_lw !================================================================================================================= - subroutine deallocate_radiation_lw + subroutine deallocate_radiation_lw(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: radt_lw_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_radt_lw_scheme',radt_lw_scheme) + if(allocated(f_ice) ) deallocate(f_ice ) if(allocated(f_rain) ) deallocate(f_rain ) if(allocated(sfc_emiss_p) ) deallocate(sfc_emiss_p ) @@ -296,6 +318,8 @@ subroutine radiation_lw_from_MPAS(xtime_s,configs,mesh,state,time_lev,diag_physi !local pointers: logical,pointer:: config_o3climatology + character(len=StrKIND),pointer:: radt_lw_scheme + character(len=StrKIND),pointer:: microp_scheme logical,pointer:: config_microp_re real(kind=RKIND),dimension(:),pointer :: latCell,lonCell @@ -313,14 +337,14 @@ subroutine radiation_lw_from_MPAS(xtime_s,configs,mesh,state,time_lev,diag_physi !----------------------------------------------------------------------------------------------------------------- - call mpas_pool_get_config(configs,'config_o3climatology',config_o3climatology) - call mpas_pool_get_config(configs,'config_microp_re' ,config_microp_re ) + call mpas_pool_get_config(configs,'config_o3climatology' ,config_o3climatology) + call mpas_pool_get_config(configs,'config_radt_lw_scheme',radt_lw_scheme ) + call mpas_pool_get_config(configs,'config_microp_scheme' ,microp_scheme ) + call mpas_pool_get_config(configs,'config_microp_re' ,config_microp_re ) call mpas_pool_get_array(mesh,'latCell',latCell) call mpas_pool_get_array(mesh,'lonCell',lonCell) - call mpas_pool_get_array(mesh,'m_hybi' ,m_hybi ) - call mpas_pool_get_array(state,'m_ps' ,m_ps ,time_lev) call mpas_pool_get_array(state,'aerosols',aerosols,time_lev) call mpas_pool_get_array(sfc_input,'skintemp',skintemp) @@ -336,6 +360,8 @@ subroutine radiation_lw_from_MPAS(xtime_s,configs,mesh,state,time_lev,diag_physi call mpas_pool_get_array(diag_physics,'cldfrac' ,cldfrac ) call mpas_pool_get_array(diag_physics,'o3clim' ,o3clim ) call mpas_pool_get_array(diag_physics,'o3vmr' ,o3vmr ) + call mpas_pool_get_array(diag_physics,'m_hybi' ,m_hybi ) + call mpas_pool_get_array(diag_physics,'m_ps' ,m_ps ) do j = jts,jte do i = its,ite @@ -406,9 +432,10 @@ subroutine radiation_lw_from_MPAS(xtime_s,configs,mesh,state,time_lev,diag_physi enddo enddo else - has_reqc = 0 - has_reqi = 0 - has_reqs = 0 + ! These are set in module mpas_atmphys_manager and should not be set again + !has_reqc = 0 + !has_reqi = 0 + !has_reqs = 0 do j = jts,jte do k = kts,kte do i = its,ite @@ -600,6 +627,8 @@ subroutine radiation_lw_to_MPAS(configs,diag_physics,tend_physics,its,ite) integer,intent(in):: its,ite !local pointers: + character(len=StrKIND),pointer:: radt_lw_scheme + character(len=StrKIND),pointer:: microp_scheme logical,pointer:: config_microp_re real(kind=RKIND),dimension(:),pointer :: glw,lwcf,lwdnb,lwdnbc,lwdnt,lwdntc,lwupb,lwupbc, & @@ -614,7 +643,9 @@ subroutine radiation_lw_to_MPAS(configs,diag_physics,tend_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- - call mpas_pool_get_config(configs,'config_microp_re',config_microp_re) + call mpas_pool_get_config(configs,'config_radt_lw_scheme',radt_lw_scheme ) + call mpas_pool_get_config(configs,'config_microp_scheme' ,microp_scheme ) + call mpas_pool_get_config(configs,'config_microp_re' ,config_microp_re) call mpas_pool_get_array(diag_physics,'glw' ,glw ) call mpas_pool_get_array(diag_physics,'lwcf' ,lwcf ) @@ -738,13 +769,15 @@ subroutine radiation_camlw_to_MPAS(diag_physics,its,ite) end subroutine radiation_camlw_to_MPAS !================================================================================================================= - subroutine init_radiation_lw(dminfo,mesh,atm_input,diag,state,time_lev) + subroutine init_radiation_lw(dminfo,configs,mesh,atm_input,diag,diag_physics,state,time_lev) !================================================================================================================= !input arguments: type(dm_info),intent(in):: dminfo + type(mpas_pool_type),intent(in):: configs type(mpas_pool_type),intent(in),optional:: mesh type(mpas_pool_type),intent(in),optional:: diag + type(mpas_pool_type),intent(in),optional:: diag_physics integer,intent(in),optional:: time_lev @@ -752,15 +785,20 @@ subroutine init_radiation_lw(dminfo,mesh,atm_input,diag,state,time_lev) type(mpas_pool_type),intent(inout),optional:: atm_input type(mpas_pool_type),intent(inout),optional:: state +!local pointers: + character(len=StrKIND),pointer:: radt_lw_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_radt_lw_scheme',radt_lw_scheme) + radiation_lw_select: select case (trim(radt_lw_scheme)) case ("rrtmg_lw") call rrtmg_initlw_forMPAS(dminfo) case("cam_lw") - call camradinit(dminfo,mesh,atm_input,diag,state,time_lev) + call camradinit(dminfo,mesh,atm_input,diag,diag_physics,state,time_lev) case default @@ -790,6 +828,7 @@ subroutine driver_radiation_lw(xtime_s,configs,mesh,state,time_lev,diag_physics, !local pointers: logical,pointer:: config_o3climatology + character(len=StrKIND),pointer:: radt_lw_scheme !local variables: integer:: o3input @@ -798,7 +837,8 @@ subroutine driver_radiation_lw(xtime_s,configs,mesh,state,time_lev,diag_physics, !----------------------------------------------------------------------------------------------------------------- !call mpas_log_write(' --- enter subroutine driver_radiation_lw: ') - call mpas_pool_get_config(configs,'config_o3climatology',config_o3climatology) + call mpas_pool_get_config(configs,'config_o3climatology' ,config_o3climatology) + call mpas_pool_get_config(configs,'config_radt_lw_scheme',radt_lw_scheme ) !copy MPAS arrays to local arrays: call radiation_lw_from_MPAS(xtime_s,configs,mesh,state,time_lev,diag_physics,atm_input,sfc_input,its,ite) @@ -810,6 +850,7 @@ subroutine driver_radiation_lw(xtime_s,configs,mesh,state,time_lev,diag_physics, o3input = 0 if(config_o3climatology) o3input = 2 + call mpas_timer_start('RRTMG_lw') call rrtmg_lwrad( & p3d = pres_hyd_p , p8w = pres2_hyd_p , pi3d = pi_p , & t3d = t_p , t8w = t2_p , dz8w = dz_p , & @@ -831,17 +872,23 @@ subroutine driver_radiation_lw(xtime_s,configs,mesh,state,time_lev,diag_physics, ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('RRTMG_lw') case ("cam_lw") xtime_m = xtime_s/60. +! This should be OMP MASTER with barrier afterwards or OMP SINGLE, since declin and solcon +! are global variables in mpas_atmphys_vars.F and race conditions may occur otherwise! +!$OMP SINGLE !... calculates solar declination: call radconst(declin,solcon,curr_julday,degrad,dpd) +!$OMP END SINGLE !... convert the radiation time_step to minutes: radt = dt_radtlw/60. ! call mpas_log_write('--- enter subroutine camrad_lw: doabsems=$l',logicArgs=(/doabsems/)) + call mpas_timer_start('CAMRAD_lw') call camrad( dolw = .true. , dosw = .false. , & p_phy = pres_hyd_p , p8w = pres2_hyd_p , & pi_phy = pi_p , t_phy = t_p , & @@ -890,6 +937,7 @@ subroutine driver_radiation_lw(xtime_s,configs,mesh,state,time_lev,diag_physics, ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('CAMRAD_lw') case default diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_sw.F b/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_sw.F index 7538d323da..35f5a42c1c 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_sw.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_radiation_sw.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_radiation_sw use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants use mpas_atmphys_manager, only: gmt,curr_julday,julday,year @@ -80,20 +80,29 @@ module mpas_atmphys_driver_radiation_sw ! * in subroutines radiation_sw_from_MPAS, revised the initialization of re_cloud, re_ice, re_snow, to ! handle the case when the cloud microphysics parameterization is turned off, i.e. config_microp_scheme='off'. ! Laura D. Fowler (laura@ucar.edu) / 2017-02-10. +! * since we removed the local variable radt_sw_scheme from mpas_atmphys_vars.F, now defines radt_sw_scheme +! as a pointer to config_radt_sw_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. contains !================================================================================================================= - subroutine allocate_radiation_sw(xtime_s) + subroutine allocate_radiation_sw(configs,xtime_s) !================================================================================================================= !input arguments: + type(mpas_pool_type),intent(in):: configs real(kind=RKIND),intent(in):: xtime_s +!local pointers: + character(len=StrKIND),pointer:: radt_sw_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_radt_sw_scheme',radt_sw_scheme) + if(.not.allocated(f_ice) ) allocate(f_ice(ims:ime,kms:kme,jms:jme) ) if(.not.allocated(f_rain) ) allocate(f_rain(ims:ime,kms:kme,jms:jme) ) @@ -193,9 +202,19 @@ subroutine allocate_radiation_sw(xtime_s) end subroutine allocate_radiation_sw !================================================================================================================= - subroutine deallocate_radiation_sw + subroutine deallocate_radiation_sw(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: radt_sw_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_radt_sw_scheme',radt_sw_scheme) + if(allocated(f_ice) ) deallocate(f_ice ) if(allocated(f_rain) ) deallocate(f_rain ) if(allocated(xlat_p) ) deallocate(xlat_p ) @@ -299,6 +318,8 @@ subroutine radiation_sw_from_MPAS(configs,mesh,state,time_lev,diag_physics,atm_i !local pointers: logical,pointer:: config_o3climatology + character(len=StrKIND),pointer:: radt_sw_scheme + character(len=StrKIND),pointer:: microp_scheme logical,pointer:: config_microp_re real(kind=RKIND),dimension(:),pointer :: latCell,lonCell @@ -311,14 +332,14 @@ subroutine radiation_sw_from_MPAS(configs,mesh,state,time_lev,diag_physics,atm_i !----------------------------------------------------------------------------------------------------------------- - call mpas_pool_get_config(configs,'config_o3climatology',config_o3climatology) - call mpas_pool_get_config(configs,'config_microp_re' ,config_microp_re ) + call mpas_pool_get_config(configs,'config_o3climatology' ,config_o3climatology) + call mpas_pool_get_config(configs,'config_radt_sw_scheme',radt_sw_scheme ) + call mpas_pool_get_config(configs,'config_microp_scheme' ,microp_scheme ) + call mpas_pool_get_config(configs,'config_microp_re' ,config_microp_re ) call mpas_pool_get_array(mesh,'latCell',latCell) call mpas_pool_get_array(mesh,'lonCell',lonCell) - call mpas_pool_get_array(mesh,'m_hybi' ,m_hybi ) - call mpas_pool_get_array(state,'m_ps' ,m_ps ,time_lev) call mpas_pool_get_array(state,'aerosols',aerosols,time_lev) call mpas_pool_get_array(sfc_input,'skintemp',skintemp) @@ -333,6 +354,8 @@ subroutine radiation_sw_from_MPAS(configs,mesh,state,time_lev,diag_physics,atm_i call mpas_pool_get_array(diag_physics,'sfc_emiss' ,sfc_emiss ) call mpas_pool_get_array(diag_physics,'cldfrac' ,cldfrac ) call mpas_pool_get_array(diag_physics,'o3clim' ,o3clim ) + call mpas_pool_get_array(diag_physics,'m_hybi' ,m_hybi ) + call mpas_pool_get_array(diag_physics,'m_ps' ,m_ps ) do j = jts,jte do i = its,ite @@ -407,9 +430,10 @@ subroutine radiation_sw_from_MPAS(configs,mesh,state,time_lev,diag_physics,atm_i enddo enddo else - has_reqc = 0 - has_reqi = 0 - has_reqs = 0 + ! These are set in module mpas_atmphys_manager and should not be set again + !has_reqc = 0 + !has_reqi = 0 + !has_reqs = 0 do j = jts,jte do k = kts,kte do i = its,ite @@ -610,13 +634,15 @@ subroutine radiation_sw_to_MPAS(diag_physics,tend_physics,its,ite) end subroutine radiation_sw_to_MPAS !================================================================================================================= - subroutine init_radiation_sw(dminfo,mesh,atm_input,diag,state,time_lev) + subroutine init_radiation_sw(dminfo,configs,mesh,atm_input,diag,diag_physics,state,time_lev) !================================================================================================================= !input arguments: type(dm_info), intent(in):: dminfo + type(mpas_pool_type),intent(in):: configs type(mpas_pool_type),intent(in),optional:: mesh type(mpas_pool_type),intent(in),optional:: diag + type(mpas_pool_type),intent(in),optional:: diag_physics integer,intent(in),optional:: time_lev @@ -624,8 +650,13 @@ subroutine init_radiation_sw(dminfo,mesh,atm_input,diag,state,time_lev) type(mpas_pool_type),intent(inout),optional:: atm_input type(mpas_pool_type),intent(inout),optional:: state +!local pointers: + character(len=StrKIND),pointer:: radt_sw_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_radt_sw_scheme',radt_sw_scheme) + !call to shortwave radiation scheme: radiation_sw_select: select case (trim(radt_sw_scheme)) @@ -633,7 +664,7 @@ subroutine init_radiation_sw(dminfo,mesh,atm_input,diag,state,time_lev) call rrtmg_initsw_forMPAS(dminfo) case("cam_sw") - call camradinit(dminfo,mesh,atm_input,diag,state,time_lev) + call camradinit(dminfo,mesh,atm_input,diag,diag_physics,state,time_lev) case default @@ -666,6 +697,7 @@ subroutine driver_radiation_sw(itimestep,configs,mesh,state,time_lev,diag_physic !local pointers: logical,pointer:: config_o3climatology + character(len=StrKIND),pointer:: radt_sw_scheme !local variables: integer:: o3input @@ -674,13 +706,17 @@ subroutine driver_radiation_sw(itimestep,configs,mesh,state,time_lev,diag_physic !----------------------------------------------------------------------------------------------------------------- !call mpas_log_write(' --- enter subroutine driver_radiation_sw: $i',intArgs=(/itimestep/)) - call mpas_pool_get_config(configs,'config_o3climatology',config_o3climatology) + call mpas_pool_get_config(configs,'config_o3climatology' ,config_o3climatology) + call mpas_pool_get_config(configs,'config_radt_sw_scheme',radt_sw_scheme ) xtime_m = xtime_s/60. !copy MPAS arrays to local arrays: call radiation_sw_from_MPAS(configs,mesh,state,time_lev,diag_physics,atm_input,sfc_input,xtime_s,its,ite) +! This should be OMP MASTER with barrier afterwards or OMP SINGLE, since declin and solcon +! are global variables in mpas_atmphys_vars.F and race conditions may occur otherwise! +!$OMP SINGLE !... calculates solar declination: !call radconst(declin,solcon,julday,degrad,dpd) call radconst(declin,solcon,curr_julday,degrad,dpd) @@ -692,6 +728,7 @@ subroutine driver_radiation_sw(itimestep,configs,mesh,state,time_lev,diag_physic !call mpas_log_write(' CURR_JULDAY = $r', realArgs=(/curr_julday/)) !call mpas_log_write(' SOLCON = $r', realArgs=(/solcon/)) !call mpas_log_write(' DECLIN = $r', realArgs=(/declin/)) +!$OMP END SINGLE !... convert the radiation time_step to minutes: radt = dt_radtsw/60. @@ -703,6 +740,7 @@ subroutine driver_radiation_sw(itimestep,configs,mesh,state,time_lev,diag_physic o3input = 0 if(config_o3climatology) o3input = 2 + call mpas_timer_start('RRTMG_sw') call rrtmg_swrad( & p3d = pres_hyd_p , p8w = pres2_hyd_p , pi3d = pi_p , & t3d = t_p , t8w = t2_p , dz8w = dz_p , & @@ -726,8 +764,10 @@ subroutine driver_radiation_sw(itimestep,configs,mesh,state,time_lev,diag_physic ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('RRTMG_sw') case ("cam_sw") + call mpas_timer_start('CAMRAD_sw') call camrad( dolw = .false. , dosw = .true. , & p_phy = pres_hyd_p , p8w = pres2_hyd_p , & pi_phy = pi_p , t_phy = t_p , & @@ -776,6 +816,7 @@ subroutine driver_radiation_sw(itimestep,configs,mesh,state,time_lev,diag_physic ims = ims , ime = ime , jms = jms , jme = jme , kms = kms , kme = kme , & its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) + call mpas_timer_stop('CAMRAD_sw') case default diff --git a/src/core_atmosphere/physics/mpas_atmphys_driver_sfclayer.F b/src/core_atmosphere/physics/mpas_atmphys_driver_sfclayer.F index d7b4c57c28..afe42154d8 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_driver_sfclayer.F +++ b/src/core_atmosphere/physics/mpas_atmphys_driver_sfclayer.F @@ -8,8 +8,8 @@ !================================================================================================================= module mpas_atmphys_driver_sfclayer use mpas_kind_types - use mpas_derived_types use mpas_pool_routines + use mpas_timer, only : mpas_timer_start, mpas_timer_stop use mpas_atmphys_constants use mpas_atmphys_vars @@ -76,17 +76,30 @@ module mpas_atmphys_driver_sfclayer ! * changed the definition of dx_p to match that used in other physics parameterizations. ! parameterizations. ! Laura D. Fowler (laura@ucar.edu) / 2016-10-18. +! * since we removed the local variable sfclayer_scheme from mpas_atmphys_vars.F, now defines sfclayer_scheme +! as a pointer to config_sfclayer_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2917-02-16. + contains !================================================================================================================= - subroutine allocate_sfclayer(config_frac_seaice) + subroutine allocate_sfclayer(configs) !================================================================================================================= - logical,intent(in):: config_frac_seaice +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + logical,pointer:: config_frac_seaice + character(len=StrKIND),pointer:: sfclayer_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_frac_seaice' ,config_frac_seaice) + call mpas_pool_get_config(configs,'config_sfclayer_scheme',sfclayer_scheme ) + if(.not.allocated(dx_p) ) allocate(dx_p(ims:ime,jms:jme) ) if(.not.allocated(br_p) ) allocate(br_p(ims:ime,jms:jme) ) if(.not.allocated(cd_p) ) allocate(cd_p(ims:ime,jms:jme) ) @@ -200,12 +213,21 @@ subroutine allocate_sfclayer(config_frac_seaice) end subroutine allocate_sfclayer !================================================================================================================= - subroutine deallocate_sfclayer(config_frac_seaice) + subroutine deallocate_sfclayer(configs) !================================================================================================================= - logical,intent(in):: config_frac_seaice +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + logical,pointer:: config_frac_seaice + character(len=StrKIND),pointer:: sfclayer_scheme + !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_frac_seaice' ,config_frac_seaice) + call mpas_pool_get_config(configs,'config_sfclayer_scheme',sfclayer_scheme ) + if(allocated(dx_p) ) deallocate(dx_p ) if(allocated(br_p) ) deallocate(br_p ) if(allocated(cd_p) ) deallocate(cd_p ) @@ -324,6 +346,7 @@ subroutine sfclayer_from_MPAS(configs,mesh,diag_physics,sfc_input,its,ite) !local pointers: logical,pointer:: config_frac_seaice + character(len=StrKIND),pointer:: sfclayer_scheme real(kind=RKIND),pointer:: len_disp real(kind=RKIND),dimension(:),pointer:: meshDensity @@ -343,8 +366,9 @@ subroutine sfclayer_from_MPAS(configs,mesh,diag_physics,sfc_input,its,ite) !----------------------------------------------------------------------------------------------------------------- !input variables: - call mpas_pool_get_config(configs,'config_len_disp' ,len_disp) - call mpas_pool_get_config(configs,'config_frac_seaice',config_frac_seaice) + call mpas_pool_get_config(configs,'config_frac_seaice' ,config_frac_seaice) + call mpas_pool_get_config(configs,'config_sfclayer_scheme',sfclayer_scheme ) + call mpas_pool_get_config(configs,'config_len_disp' ,len_disp ) call mpas_pool_get_array(mesh,'meshDensity',meshDensity) call mpas_pool_get_array(diag_physics,'hpbl' ,hpbl ) @@ -568,6 +592,7 @@ subroutine sfclayer_to_MPAS(configs,sfc_input,diag_physics,its,ite) !local pointers: logical,pointer:: config_frac_seaice + character(len=StrKIND),pointer:: sfclayer_scheme real(kind=RKIND),dimension(:),pointer:: br,cpm,chs,chs2,cqs2,flhc,flqc,gz1oz0,hfx,qfx, & qgh,qsfc,lh,mol,psim,psih,regime,rmol,ust,wspd, & @@ -585,7 +610,8 @@ subroutine sfclayer_to_MPAS(configs,sfc_input,diag_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- - call mpas_pool_get_config(configs,'config_frac_seaice',config_frac_seaice) + call mpas_pool_get_config(configs,'config_frac_seaice' ,config_frac_seaice) + call mpas_pool_get_config(configs,'config_sfclayer_scheme',sfclayer_scheme ) !inout variables: call mpas_pool_get_array(diag_physics,'br' ,br ) @@ -755,14 +781,20 @@ subroutine sfclayer_to_MPAS(configs,sfc_input,diag_physics,its,ite) end subroutine sfclayer_to_MPAS !================================================================================================================= - subroutine init_sfclayer + subroutine init_sfclayer(configs) !================================================================================================================= -!local variables: +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local variables and pointers: logical, parameter:: allowed_to_read = .false. !actually not used in subroutine sfclayinit. + character(len=StrKIND),pointer:: sfclayer_scheme !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_sfclayer_scheme',sfclayer_scheme) + sfclayer_select: select case (trim(sfclayer_scheme)) case("sf_monin_obukhov") @@ -775,7 +807,6 @@ subroutine init_sfclayer end select sfclayer_select - end subroutine init_sfclayer !================================================================================================================= @@ -795,6 +826,7 @@ subroutine driver_sfclayer(itimestep,configs,mesh,diag_physics,sfc_input,its,ite !local pointers: logical,pointer:: config_do_restart,config_frac_seaice + character(len=StrKIND),pointer:: sfclayer_scheme real(kind=RKIND),dimension(:),pointer:: areaCell !local variables: @@ -805,8 +837,9 @@ subroutine driver_sfclayer(itimestep,configs,mesh,diag_physics,sfc_input,its,ite !call mpas_log_write('') !call mpas_log_write('--- enter subroutine driver_sfclayer:') - call mpas_pool_get_config(configs,'config_do_restart' ,config_do_restart ) - call mpas_pool_get_config(configs,'config_frac_seaice',config_frac_seaice) + call mpas_pool_get_config(configs,'config_do_restart' ,config_do_restart ) + call mpas_pool_get_config(configs,'config_frac_seaice' ,config_frac_seaice) + call mpas_pool_get_config(configs,'config_sfclayer_scheme',sfclayer_scheme ) call mpas_pool_get_array(mesh,'areaCell',areaCell) @@ -821,6 +854,7 @@ subroutine driver_sfclayer(itimestep,configs,mesh,diag_physics,sfc_input,its,ite sfclayer_select: select case (trim(sfclayer_scheme)) case("sf_monin_obukhov") + call mpas_timer_start('Monin-Obukhov') call sfclay( & p3d = pres_hyd_p , psfc = psfc_p , t3d = t_p , & u3d = u_p , v3d = v_p , qv3d = qv_p , & @@ -880,8 +914,10 @@ subroutine driver_sfclayer(itimestep,configs,mesh,diag_physics,sfc_input,its,ite its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) endif + call mpas_timer_stop('Monin-Obukhov') case("sf_mynn") + call mpas_timer_start('MYNN_sfclay') call sfclay_mynn( & p3d = pres_hyd_p , pi3d = pi_p , psfcpa = psfc_p , & th3d = th_p , t3d = t_p , u3d = u_p , & @@ -945,6 +981,7 @@ subroutine driver_sfclayer(itimestep,configs,mesh,diag_physics,sfc_input,its,ite its = its , ite = ite , jts = jts , jte = jte , kts = kts , kte = kte & ) endif + call mpas_timer_stop('MYNN_sfclay') case default diff --git a/src/core_atmosphere/physics/mpas_atmphys_finalize.F b/src/core_atmosphere/physics/mpas_atmphys_finalize.F index 19424e876b..8ad9248196 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_finalize.F +++ b/src/core_atmosphere/physics/mpas_atmphys_finalize.F @@ -7,7 +7,6 @@ ! !================================================================================================================= module mpas_atmphys_finalize - use mpas_derived_types use mpas_pool_routines use module_mp_thompson diff --git a/src/core_atmosphere/physics/mpas_atmphys_init.F b/src/core_atmosphere/physics/mpas_atmphys_init.F index adcfda04fd..92112da785 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_init.F +++ b/src/core_atmosphere/physics/mpas_atmphys_init.F @@ -8,7 +8,6 @@ !================================================================================================================= module mpas_atmphys_init use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_timekeeping @@ -355,10 +354,10 @@ subroutine physics_init(dminfo,clock,configs,mesh,diag,tend,state,time_lev,diag_ !initialization of cloud microphysics processes: if(config_microp_scheme .ne. 'off') & - call microphysics_init(dminfo,mesh,sfc_input,diag_physics) + call microphysics_init(dminfo,configs,mesh,sfc_input,diag_physics) !initialization of surface layer processes: - if(config_sfclayer_scheme .ne. 'off') call init_sfclayer + if(config_sfclayer_scheme .ne. 'off') call init_sfclayer(configs) !initialization of land-surface model: !if(.not. config_do_restart) then @@ -370,10 +369,10 @@ subroutine physics_init(dminfo,clock,configs,mesh,diag,tend,state,time_lev,diag_ init_done = .false. if(config_radt_sw_scheme.ne.'off') then if(trim(config_radt_sw_scheme) .eq. 'cam_sw') then - call init_radiation_sw(dminfo,mesh,atm_input,diag,state,time_lev) + call init_radiation_sw(dminfo,configs,mesh,atm_input,diag,diag_physics,state,time_lev) init_done = .true. else - call init_radiation_sw(dminfo) + call init_radiation_sw(dminfo,configs) endif endif @@ -383,13 +382,13 @@ subroutine physics_init(dminfo,clock,configs,mesh,diag,tend,state,time_lev,diag_ if(config_radt_lw_scheme.ne.'off') then if(trim(config_radt_lw_scheme) .eq. 'cam_lw') then if(.not. init_done) then - call init_radiation_lw(dminfo,mesh,atm_input,diag,state,time_lev) + call init_radiation_lw(dminfo,configs,mesh,atm_input,diag,diag_physics,state,time_lev) else ! call mpas_log_write('') ! call mpas_log_write('--- camrad lw initialization done above') endif else - call init_radiation_lw(dminfo) + call init_radiation_lw(dminfo,configs) endif endif diff --git a/src/core_atmosphere/physics/mpas_atmphys_init_microphysics.F b/src/core_atmosphere/physics/mpas_atmphys_init_microphysics.F index fac384368b..99db47ced6 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_init_microphysics.F +++ b/src/core_atmosphere/physics/mpas_atmphys_init_microphysics.F @@ -10,7 +10,6 @@ module mpas_atmphys_init_microphysics use mpas_dmpar use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_utilities diff --git a/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F b/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F index 6ee58cfcb7..303c1ce337 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F +++ b/src/core_atmosphere/physics/mpas_atmphys_initialize_real.F @@ -9,7 +9,6 @@ module mpas_atmphys_initialize_real use mpas_kind_types use mpas_dmpar - use mpas_derived_types use mpas_pool_routines use mpas_init_atm_surface use mpas_log, only : mpas_log_write diff --git a/src/core_atmosphere/physics/mpas_atmphys_interface.F b/src/core_atmosphere/physics/mpas_atmphys_interface.F index 4d0dfbb91d..f4ec446521 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_interface.F +++ b/src/core_atmosphere/physics/mpas_atmphys_interface.F @@ -8,7 +8,6 @@ !================================================================================================================= module mpas_atmphys_interface use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_constants @@ -71,9 +70,19 @@ module mpas_atmphys_interface !================================================================================================================= - subroutine allocate_forall_physics + subroutine allocate_forall_physics(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: pbl_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme) + if(.not.allocated(psfc_p) ) allocate(psfc_p(ims:ime,jms:jme) ) if(.not.allocated(ptop_p) ) allocate(ptop_p(ims:ime,jms:jme) ) @@ -125,9 +134,19 @@ subroutine allocate_forall_physics end subroutine allocate_forall_physics !================================================================================================================= - subroutine deallocate_forall_physics + subroutine deallocate_forall_physics(configs) !================================================================================================================= +!input arguments: + type(mpas_pool_type),intent(in):: configs + +!local pointers: + character(len=StrKIND),pointer:: pbl_scheme + +!----------------------------------------------------------------------------------------------------------------- + + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme) + if(allocated(psfc_p) ) deallocate(psfc_p ) if(allocated(ptop_p) ) deallocate(ptop_p ) @@ -178,10 +197,11 @@ subroutine deallocate_forall_physics end subroutine deallocate_forall_physics !================================================================================================================= - subroutine MPAS_to_physics(mesh,state,time_lev,diag,diag_physics,its,ite) + subroutine MPAS_to_physics(configs,mesh,state,time_lev,diag,diag_physics,its,ite) !================================================================================================================= !input variables: + type(mpas_pool_type),intent(in):: configs type(mpas_pool_type),intent(in):: mesh type(mpas_pool_type),intent(in):: state type(mpas_pool_type),intent(in):: diag @@ -193,6 +213,8 @@ subroutine MPAS_to_physics(mesh,state,time_lev,diag,diag_physics,its,ite) type(mpas_pool_type),intent(inout):: diag_physics !local pointers: + character(len=StrKIND),pointer:: pbl_scheme + integer,pointer:: index_qv,index_qc,index_qr,index_qi,index_qs,index_qg integer,pointer:: index_ni @@ -224,6 +246,8 @@ subroutine MPAS_to_physics(mesh,state,time_lev,diag,diag_physics,its,ite) !call mpas_log_write('kts=$i kte=$i',intArgs=(/kts,kte/)) !initialization: + call mpas_pool_get_config(configs,'config_pbl_scheme',pbl_scheme) + call mpas_pool_get_array(mesh,'latCell',latCell) call mpas_pool_get_array(mesh,'lonCell',lonCell) call mpas_pool_get_array(mesh,'fzm' ,fzm ) @@ -446,10 +470,11 @@ subroutine MPAS_to_physics(mesh,state,time_lev,diag,diag_physics,its,ite) end subroutine MPAS_to_physics !================================================================================================================= - subroutine microphysics_from_MPAS(mesh,state,time_lev,diag,diag_physics,its,ite) + subroutine microphysics_from_MPAS(configs,mesh,state,time_lev,diag,diag_physics,its,ite) !================================================================================================================= !input variables: + type(mpas_pool_type),intent(in):: configs type(mpas_pool_type),intent(in):: mesh type(mpas_pool_type),intent(in):: state type(mpas_pool_type),intent(in):: diag @@ -459,6 +484,7 @@ subroutine microphysics_from_MPAS(mesh,state,time_lev,diag,diag_physics,its,ite) integer:: time_lev !local pointers: + character(len=StrKIND),pointer:: microp_scheme integer,pointer:: index_qv,index_qc,index_qr,index_qi,index_qs,index_qg integer,pointer:: index_ni,index_nr real(kind=RKIND),dimension(:),pointer :: nt_c,mu_c @@ -476,6 +502,8 @@ subroutine microphysics_from_MPAS(mesh,state,time_lev,diag,diag_physics,its,ite) !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + call mpas_pool_get_array(mesh,'zgrid',zgrid) call mpas_pool_get_array(mesh,'zz' ,zz ) @@ -585,10 +613,11 @@ subroutine microphysics_from_MPAS(mesh,state,time_lev,diag,diag_physics,its,ite) end subroutine microphysics_from_MPAS !================================================================================================================= - subroutine microphysics_to_MPAS(mesh,state,time_lev,diag,diag_physics,tend,itimestep,its,ite) + subroutine microphysics_to_MPAS(configs,mesh,state,time_lev,diag,diag_physics,tend,itimestep,its,ite) !================================================================================================================= !input variables: + type(mpas_pool_type),intent(in):: configs type(mpas_pool_type),intent(in):: mesh integer,intent(in):: itimestep,time_lev @@ -601,6 +630,7 @@ subroutine microphysics_to_MPAS(mesh,state,time_lev,diag,diag_physics,tend,itime type(mpas_pool_type),intent(inout):: diag_physics !local pointers: + character(len=StrKIND),pointer:: microp_scheme integer,pointer:: index_qv,index_qc,index_qr,index_qi,index_qs,index_qg integer,pointer:: index_ni,index_nr real(kind=RKIND),dimension(:),pointer :: surface_pressure,tend_sfc_pressure @@ -621,6 +651,8 @@ subroutine microphysics_to_MPAS(mesh,state,time_lev,diag,diag_physics,tend,itime !----------------------------------------------------------------------------------------------------------------- + call mpas_pool_get_config(configs,'config_microp_scheme',microp_scheme) + call mpas_pool_get_array(mesh,'zz' ,zz ) call mpas_pool_get_array(mesh,'zgrid',zgrid) diff --git a/src/core_atmosphere/physics/mpas_atmphys_landuse.F b/src/core_atmosphere/physics/mpas_atmphys_landuse.F index ca6be0ddf6..0e2aa5578b 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_landuse.F +++ b/src/core_atmosphere/physics/mpas_atmphys_landuse.F @@ -13,7 +13,6 @@ module mpas_atmphys_landuse use mpas_dmpar use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_utilities diff --git a/src/core_atmosphere/physics/mpas_atmphys_lsm_noahinit.F b/src/core_atmosphere/physics/mpas_atmphys_lsm_noahinit.F index e850a328fd..497603a94c 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_lsm_noahinit.F +++ b/src/core_atmosphere/physics/mpas_atmphys_lsm_noahinit.F @@ -15,7 +15,6 @@ module mpas_atmphys_lsm_noahinit use mpas_dmpar use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_constants diff --git a/src/core_atmosphere/physics/mpas_atmphys_manager.F b/src/core_atmosphere/physics/mpas_atmphys_manager.F index 5b26a2a04e..fe8ee5c27c 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_manager.F +++ b/src/core_atmosphere/physics/mpas_atmphys_manager.F @@ -8,7 +8,6 @@ !================================================================================================================= module mpas_atmphys_manager use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_timekeeping use mpas_stream_manager @@ -97,7 +96,34 @@ module mpas_atmphys_manager ! Laura D. Fowler (laura@ucar.edu) / 2014-09-18. ! * added initialization of variables has_reqc,has_reqi,and has_reqs needed in the calls to radiation codes ! rrtmg_lwrad and rrmtg_swrad. -! Laura D. Fowler (laura@ucar.edu) / 2016-07-007. +! Laura D. Fowler (laura@ucar.edu) / 2016-07-07. +! * in subroutine physics_run_init, removed the initialization of the local variable gwdo_scheme. gwdo_scheme +! is no longer needed and can be replaced with config_gwdo_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable lsm_scheme. lsm_scheme +! is no longer needed and can be replaced with config_lsm_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable sfclayer_scheme. +! sfclayer_scheme is no longer needed and can be replaced with config_sfclayer_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable pbl_scheme. pbl_scheme +! is no longer needed and can be replaced with config_pbl_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable radt_cld_scheme. +! radt_cld_scheme is no longer needed and can be replaced with config_radt_cld_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable radt_lw_scheme. +! radt_lw_scheme is no longer needed and can be replaced with config_radt_lw_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable radt_sw_scheme. +! radt_sw_scheme is no longer needed and can be replaced with config_radt_sw_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable convection_scheme. +! convection_scheme is no longer needed and can be replaced with config_convection_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * in subroutine physics_run_init, removed the initialization of the local variable microp_scheme. +! microp_scheme is no longer needed and can be replaced with config_microp_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. contains @@ -357,14 +383,10 @@ subroutine physics_run_init(configs,mesh,state,clock,stream_manager) !local pointers: character(len=StrKIND),pointer:: config_convection_scheme, & - config_gwdo_scheme, & config_lsm_scheme, & config_microp_scheme, & - config_pbl_scheme, & - config_radt_cld_scheme, & config_radt_lw_scheme, & - config_radt_sw_scheme, & - config_sfclayer_scheme + config_radt_sw_scheme character(len=StrKIND),pointer:: config_conv_interval, & config_pbl_interval, & @@ -397,14 +419,10 @@ subroutine physics_run_init(configs,mesh,state,clock,stream_manager) !call mpas_log_write('--- enter subroutine physics_run_init:') call mpas_pool_get_config(configs,'config_convection_scheme',config_convection_scheme) - call mpas_pool_get_config(configs,'config_gwdo_scheme' ,config_gwdo_scheme ) call mpas_pool_get_config(configs,'config_lsm_scheme' ,config_lsm_scheme ) call mpas_pool_get_config(configs,'config_microp_scheme' ,config_microp_scheme ) - call mpas_pool_get_config(configs,'config_pbl_scheme' ,config_pbl_scheme ) - call mpas_pool_get_config(configs,'config_radt_cld_scheme' ,config_radt_cld_scheme ) call mpas_pool_get_config(configs,'config_radt_lw_scheme' ,config_radt_lw_scheme ) call mpas_pool_get_config(configs,'config_radt_sw_scheme' ,config_radt_sw_scheme ) - call mpas_pool_get_config(configs,'config_sfclayer_scheme' ,config_sfclayer_scheme ) call mpas_pool_get_config(configs,'config_conv_interval' ,config_conv_interval ) call mpas_pool_get_config(configs,'config_pbl_interval' ,config_pbl_interval ) @@ -654,16 +672,6 @@ subroutine physics_run_init(configs,mesh,state,clock,stream_manager) num_months = nMonths num_soils = nSoilLevels - convection_scheme = trim(config_convection_scheme) - lsm_scheme = trim(config_lsm_scheme) - microp_scheme = trim(config_microp_scheme) - pbl_scheme = trim(config_pbl_scheme) - gwdo_scheme = trim(config_gwdo_scheme) - radt_cld_scheme = trim(config_radt_cld_scheme) - radt_lw_scheme = trim(config_radt_lw_scheme) - radt_sw_scheme = trim(config_radt_sw_scheme) - sfclayer_scheme = trim(config_sfclayer_scheme) - if(trim(config_lsm_scheme) .eq. "noah") sf_surface_physics = 2 !initialization of local physics time-steps: @@ -672,13 +680,13 @@ subroutine physics_run_init(configs,mesh,state,clock,stream_manager) !... cloud microphysics: dt_microp = dt_dyn n_microp = 1 - if(trim(microp_scheme)=='mp_thompson') then + if(trim(config_microp_scheme)=='mp_thompson') then dt_microp = 90._RKIND n_microp = max(nint(dt_dyn/dt_microp),1) dt_microp = dt_dyn / n_microp if(dt_dyn <= dt_microp) dt_microp = dt_dyn endif - call mpas_log_write('--- specifics on cloud microphysics option microp_scheme = '//trim(microp_scheme)) + call mpas_log_write('--- specifics on cloud microphysics option microp_scheme = '//trim(config_microp_scheme)) call mpas_log_write('--- dt_microp = $r', realArgs=(/dt_microp/)) call mpas_log_write('--- n_microp = $i', intArgs=(/n_microp/)) !... convection: @@ -733,9 +741,9 @@ subroutine physics_run_init(configs,mesh,state,clock,stream_manager) has_reqi = 0 has_reqs = 0 if(config_microp_re) then - if(trim(microp_scheme)=='mp_thompson' .or. & - trim(microp_scheme)=='mp_wsm6') then - if(trim(radt_lw_scheme)=='rrtmg_lw' .and. trim(radt_sw_scheme)=='rrtmg_sw') then + if(trim(config_microp_scheme)=='mp_thompson' .or. & + trim(config_microp_scheme)=='mp_wsm6') then + if(trim(config_radt_lw_scheme)=='rrtmg_lw' .and. trim(config_radt_sw_scheme)=='rrtmg_sw') then has_reqc = 1 has_reqi = 1 has_reqs = 1 diff --git a/src/core_atmosphere/physics/mpas_atmphys_o3climatology.F b/src/core_atmosphere/physics/mpas_atmphys_o3climatology.F index f68b989dc0..04d4f7f5bc 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_o3climatology.F +++ b/src/core_atmosphere/physics/mpas_atmphys_o3climatology.F @@ -8,7 +8,6 @@ !================================================================================================================= module mpas_atmphys_o3climatology use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_date_time use mpas_atmphys_constants @@ -21,8 +20,7 @@ module mpas_atmphys_o3climatology private public:: init_o3climatology, & update_o3climatology, & - o3climatology_from_MPAS, & - vinterp_ozn + o3climatology_from_MPAS integer,parameter:: latsiz = 64 integer,parameter:: lonsiz = 1 @@ -42,8 +40,6 @@ module mpas_atmphys_o3climatology ! as done for the greeness fraction in the MPAS time manager. ! o3climatology_from_MPAS: interpolates the ozone volume mixing ratio to the current Julian day ! as in the CAM radiation codes. -! vinterp_ozn : vertical interpolation of the ozone volume mixing ratios from fixed -! ozone pressure levels to the MPAS pressure levels. ! ! add-ons and modifications to sourcecode: ! ---------------------------------------- @@ -51,6 +47,8 @@ module mpas_atmphys_o3climatology ! Laura D. Fowler (laura@ucar.edu) / 2014-04-22. ! * modified sourcecode to use pools. ! Laura D. Fowler (laura@ucar.edu) / 2014-05-15. +! * moved the subroutine vinterp_ozn to its own module module_ra_rrtmg_vinterp.F in physics_wrf. +! Laura D. Fowler (laura@ucar.edu) / 2017-01-27. contains @@ -323,164 +321,6 @@ subroutine o3climatology_from_MPAS(julian,mesh,atm_input,diag_physics) end subroutine o3climatology_from_MPAS -!================================================================================================================= - subroutine vinterp_ozn (lchnk, ncol, pcols, pver, pmid_in, pin_in, levsiz, ozmix_in, o3vmr_out) -!-------------------------------------------------------------------------------------------------- -! -! Purpose: Interpolate ozone from current time-interpolated values to model levels -! -! Method: Use pressure values to determine interpolation levels -! -! Author: Bruce Briegleb -! -!----------------------------------------------------------------------------------------------------------------- -! use shr_kind_mod, only: r8 => shr_kind_r8 -! use ppgrid -! use phys_grid, only: get_lat_all_p, get_lon_all_p -! use comozp -! use abortutils, only: endrun -!----------------------------------------------------------------------------------------------------------------- - implicit none -!----------------------------------------------------------------------------------------------------------------- -! -! Arguments -! - integer, intent(in) :: lchnk ! chunk identifier - integer, intent(in) :: pcols, pver - integer, intent(in) :: ncol ! number of atmospheric columns - integer, intent(in) :: levsiz ! number of ozone layers - -!ldf begin: -! real(kind=RKIND), intent(in) :: pmid(pcols,pver) ! level pressures (mks) -! real(kind=RKIND), intent(in) :: pin(levsiz) ! ozone data level pressures (mks) -! real(kind=RKIND), intent(in) :: ozmix(pcols,levsiz)! ozone mixing ratio - -! real(kind=RKIND), intent(out) :: o3vmr(pcols,pver) ! ozone volume mixing ratio - - real(kind=RKIND), intent(in) :: pmid_in(pcols,pver) ! level pressures (mks) - real(kind=RKIND), intent(in) :: pin_in(levsiz) ! ozone data level pressures (mks) - real(kind=RKIND), intent(in) :: ozmix_in(pcols,levsiz)! ozone mixing ratio - - real(kind=RKIND), intent(out) :: o3vmr_out(pcols,pver)! ozone volume mixing ratio -!ldf end. -! -! local storage -! - integer i ! longitude index - integer k, kk, kkstart ! level indices - integer kupper(pcols) ! Level indices for interpolation - integer kount ! Counter - integer lats(pcols) ! latitude indices - integer lons(pcols) ! latitude indices - - real(kind=RKIND) dpu ! upper level pressure difference - real(kind=RKIND) dpl ! lower level pressure difference - -!ldf begin: - real(kind=RKIND):: pmid(pcols,pver) ! level pressures (mks) - real(kind=RKIND):: pin(levsiz) ! ozone data level pressures (mks) - real(kind=RKIND):: ozmix(pcols,levsiz) ! ozone mixing ratio - real(kind=RKIND):: o3vmr(pcols,pver) ! ozone volume mixing ratio -! -! Initialize latitude indices -! -! call get_lat_all_p(lchnk, ncol, lats) -! call get_lon_all_p(lchnk, ncol, lons) -! - -!ldf begin: - do k = 1,levsiz - pin(k) = pin_in(k) - enddo - do i = 1,pcols - do k = 1,levsiz - ozmix(i,k) = ozmix_in(i,k) - enddo - enddo - do i = 1,pcols - do k = 1,pver - kk = pver-k+1 - pmid(i,kk) = pmid_in(i,k) - enddo - enddo -!ldf end. - -! Initialize index array -! - do i=1,ncol - kupper(i) = 1 - end do - - do k=1,pver -! -! Top level we need to start looking is the top level for the previous k -! for all longitude points -! - kkstart = levsiz - do i=1,ncol - kkstart = min0(kkstart,kupper(i)) - end do - kount = 0 -! -! Store level indices for interpolation -! - do kk=kkstart,levsiz-1 - do i=1,ncol - if (pin(kk).lt.pmid(i,k) .and. pmid(i,k).le.pin(kk+1)) then - kupper(i) = kk - kount = kount + 1 - end if - end do -! -! If all indices for this level have been found, do the interpolation and -! go to the next level -! - if (kount.eq.ncol) then - do i=1,ncol - dpu = pmid(i,k) - pin(kupper(i)) - dpl = pin(kupper(i)+1) - pmid(i,k) - o3vmr(i,k) = (ozmix(i,kupper(i))*dpl + & - ozmix(i,kupper(i)+1)*dpu)/(dpl + dpu) - end do - goto 35 - end if - end do -! -! If we've fallen through the kk=1,levsiz-1 loop, we cannot interpolate and -! must extrapolate from the bottom or top ozone data level for at least some -! of the longitude points. -! - do i=1,ncol - if (pmid(i,k) .lt. pin(1)) then - o3vmr(i,k) = ozmix(i,1)*pmid(i,k)/pin(1) - else if (pmid(i,k) .gt. pin(levsiz)) then - o3vmr(i,k) = ozmix(i,levsiz) - else - dpu = pmid(i,k) - pin(kupper(i)) - dpl = pin(kupper(i)+1) - pmid(i,k) - o3vmr(i,k) = (ozmix(i,kupper(i))*dpl + & - ozmix(i,kupper(i)+1)*dpu)/(dpl + dpu) - end if - end do - - if (kount.gt.ncol) then -! call endrun ('VINTERP_OZN: Bad ozone data: non-monotonicity suspected') - end if -35 continue - end do - -!ldf begin: - do i = 1,pcols - do k = 1,pver - kk = pver-k+1 - o3vmr_out(i,kk) = o3vmr(i,k) - enddo - enddo -!ldf end. - - return -end subroutine vinterp_ozn - !================================================================================================================= end module mpas_atmphys_o3climatology !================================================================================================================= diff --git a/src/core_atmosphere/physics/mpas_atmphys_todynamics.F b/src/core_atmosphere/physics/mpas_atmphys_todynamics.F index 524f18de4b..c7baeb213a 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_todynamics.F +++ b/src/core_atmosphere/physics/mpas_atmphys_todynamics.F @@ -8,16 +8,14 @@ !================================================================================================================= module mpas_atmphys_todynamics use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_dmpar use mpas_atmphys_constants, only: R_d,R_v,degrad - use mpas_atmphys_vars, only: pbl_scheme,convection_scheme implicit none private - public:: physics_addtend, physics_get_tend + public:: physics_get_tend !Interface between the physics parameterizations and the non-hydrostatic dynamical core. @@ -27,7 +25,7 @@ module mpas_atmphys_todynamics ! ! subroutines in mpas_atmphys_todynamics: ! --------------------------------------- -! physics_addtend: add and mass-weigh tendencies before being added to dynamics tendencies. +! physics_get_tend: add and mass-weigh tendencies before being added to dynamics tendencies. ! tend_toEdges : interpolate wind-tendencies from centers to edges of grid-cells. ! ! add-ons and modifications to sourcecode: @@ -57,159 +55,6 @@ module mpas_atmphys_todynamics contains -!================================================================================================================= - subroutine physics_addtend(block, mesh, state, diag, tend, tend_physics, configs, rk_step, dynamics_substep) -!================================================================================================================= - - use mpas_atm_dimensions - -!input variables: - type(block_type),intent(in),target:: block - type(mpas_pool_type),intent(in):: mesh - type(mpas_pool_type),intent(in):: state - type(mpas_pool_type),intent(in):: configs - integer, intent(in):: rk_step - integer, intent(in):: dynamics_substep - -!inout variables: - type(mpas_pool_type),intent(inout):: diag - type(mpas_pool_type),intent(inout):: tend - type(mpas_pool_type),intent(inout):: tend_physics - -!local variables: - character(len=StrKIND), pointer :: config_pbl_scheme, config_convection_scheme, & - config_radt_lw_scheme, config_radt_sw_scheme - - integer:: i,iCell,k,n - integer,pointer:: index_qv, index_qc, index_qr, index_qi, index_qs, index_qg - integer,pointer:: index_ni - integer,pointer:: nCells,nCellsSolve,nEdges,nEdgesSolve - - real(kind=RKIND),dimension(:,:),pointer:: mass ! time level 2 rho_zz - real(kind=RKIND),dimension(:,:),pointer:: mass_edge ! diag rho_edge - real(kind=RKIND),dimension(:,:),pointer:: theta_m ! time level 1 - real(kind=RKIND),dimension(:,:,:),pointer:: scalars - real(kind=RKIND),dimension(:,:),pointer:: rthblten,rqvblten,rqcblten, & - rqiblten,rublten,rvblten - real(kind=RKIND),dimension(:,:),pointer:: rniblten - real(kind=RKIND),dimension(:,:),pointer:: rthcuten,rqvcuten,rqccuten, & - rqrcuten,rqicuten,rqscuten, & - rucuten,rvcuten - real(kind=RKIND),dimension(:,:),pointer:: rthratenlw,rthratensw - real(kind=RKIND),dimension(:,:),pointer:: rthdynten - - real(kind=RKIND),dimension(:,:),pointer:: tend_rtheta_adv - real(kind=RKIND),dimension(:,:),pointer:: tend_u_phys !nick - real(kind=RKIND),dimension(:,:),pointer :: tend_theta,tend_theta_euler,tend_diabatic,tend_u - real(kind=RKIND),dimension(:,:,:),pointer:: tend_scalars - real(kind=RKIND):: coeff - - real(kind=RKIND):: tem - real(kind=RKIND),dimension(:,:),pointer:: rublten_Edge,rucuten_Edge - - real(kind=RKIND),dimension(:,:),allocatable:: theta,tend_th - -!================================================================================================================= - call mpas_pool_get_dimension(mesh, 'nCells', nCells) - call mpas_pool_get_dimension(mesh, 'nCellsSolve', nCellsSolve) - call mpas_pool_get_dimension(mesh, 'nEdges', nEdges) - call mpas_pool_get_dimension(mesh, 'nEdgesSolve', nEdgesSolve) - - call mpas_pool_get_config(configs, 'config_pbl_scheme', config_pbl_scheme) - call mpas_pool_get_config(configs, 'config_convection_scheme', config_convection_scheme) - call mpas_pool_get_config(configs, 'config_radt_lw_scheme', config_radt_lw_scheme) - call mpas_pool_get_config(configs, 'config_radt_sw_scheme', config_radt_sw_scheme) - - call mpas_pool_get_array(state, 'theta_m', theta_m, 1) - call mpas_pool_get_array(state, 'scalars', scalars, 1) - call mpas_pool_get_array(state, 'rho_zz', mass, 2) - call mpas_pool_get_array(diag , 'rho_edge', mass_edge) - - call mpas_pool_get_array(diag , 'tend_rtheta_adv', tend_rtheta_adv) - - call mpas_pool_get_array(diag , 'tend_u_phys', tend_u_phys) !nick - - call mpas_pool_get_dimension(state, 'index_qv', index_qv) - call mpas_pool_get_dimension(state, 'index_qc', index_qc) - call mpas_pool_get_dimension(state, 'index_qr', index_qr) - call mpas_pool_get_dimension(state, 'index_qi', index_qi) - call mpas_pool_get_dimension(state, 'index_qs', index_qs) - call mpas_pool_get_dimension(state, 'index_qg', index_qg) - call mpas_pool_get_dimension(state, 'index_ni', index_ni) - - call mpas_pool_get_array(tend_physics, 'rublten', rublten) - call mpas_pool_get_array(tend_physics, 'rvblten', rvblten) - call mpas_pool_get_array(tend_physics, 'rublten_Edge', rublten_Edge) - call mpas_pool_get_array(tend_physics, 'rthblten', rthblten) - call mpas_pool_get_array(tend_physics, 'rqvblten', rqvblten) - call mpas_pool_get_array(tend_physics, 'rqcblten', rqcblten) - call mpas_pool_get_array(tend_physics, 'rqiblten', rqiblten) - call mpas_pool_get_array(tend_physics, 'rniblten', rniblten) - - call mpas_pool_get_array(tend_physics, 'rucuten', rucuten) - call mpas_pool_get_array(tend_physics, 'rvcuten', rvcuten) - call mpas_pool_get_array(tend_physics, 'rucuten_Edge', rucuten_Edge) - call mpas_pool_get_array(tend_physics, 'rthcuten', rthcuten) - call mpas_pool_get_array(tend_physics, 'rqvcuten', rqvcuten) - call mpas_pool_get_array(tend_physics, 'rqccuten', rqccuten) - call mpas_pool_get_array(tend_physics, 'rqrcuten', rqrcuten) - call mpas_pool_get_array(tend_physics, 'rqicuten', rqicuten) - call mpas_pool_get_array(tend_physics, 'rqscuten', rqscuten) - call mpas_pool_get_array(tend_physics, 'rthdynten', rthdynten) - - call mpas_pool_get_array(tend,'rt_diabatic_tend',tend_diabatic) - - call mpas_pool_get_array(tend_physics, 'rthratenlw', rthratenlw) - call mpas_pool_get_array(tend_physics, 'rthratensw', rthratensw) - - call mpas_pool_get_array(tend,'u' , tend_u ) - call mpas_pool_get_array(tend,'theta_m' , tend_theta ) - call mpas_pool_get_array(tend,'theta_euler' ,tend_theta_euler) - call mpas_pool_get_array(tend,'scalars_tend',tend_scalars ) - -!initialize the tendency for the potential temperature and all scalars due to PBL, convection, -!and longwave and shortwave radiation: - allocate(theta(nVertLevels,nCellsSolve) ) - allocate(tend_th(nVertLevels,nCellsSolve)) - tend_th = 0._RKIND - - tend_scalars(:,:,:) = 0._RKIND - - call physics_addtend_work(block, mesh, nCells, nEdges, nCellsSolve, nEdgesSolve, & - rk_step, dynamics_substep, & - config_pbl_scheme, config_convection_scheme, config_radt_lw_scheme, config_radt_sw_scheme, & - index_qv, index_qc, index_qr, index_qi, index_qs, index_ni, & - rublten, rvblten, mass_edge, rublten_Edge, tend_u, & - rucuten, rvcuten, rucuten_Edge, & - tend_th, tend_scalars, mass, rthblten, rqvblten, rqcblten, rqiblten, rniblten, & - rthcuten, rqvcuten, rqccuten, rqrcuten, rqicuten, rqscuten, & - rthratenlw, rthratensw, rthdynten, & - tend_u_phys, tend_rtheta_adv, tend_diabatic, & - theta, theta_m, scalars, tend_theta, tend_theta_euler & - ) - - - deallocate(theta) - deallocate(tend_th) - -! if(rk_step .eq. 3) then -! call mpas_log_write('') -! call mpas_log_write('--- enter subroutine physics_addtend:') -! call mpas_log_write('max rthblten = $r',realArgs=(/maxval(rthblten(:,1:nCellsSolve))/)) -! call mpas_log_write('min rthblten = $r',realArgs=(/minval(rthblten(:,1:nCellsSolve))/)) -! call mpas_log_write('max rthcuten = $r',realArgs=(/maxval(rthcuten(:,1:nCellsSolve))/)) -! call mpas_log_write('min rthcuten = $r',realArgs=(/minval(rthcuten(:,1:nCellsSolve))/)) -! call mpas_log_write('max rthratenlw = $r',realArgs=(/maxval(rthratenlw(:,1:nCellsSolve))/)) -! call mpas_log_write('min rthratenlw = $r',realArgs=(/minval(rthratenlw(:,1:nCellsSolve))/)) -! call mpas_log_write('max rthratensw = $r',realArgs=(/maxval(rthratensw(:,1:nCellsSolve))/)) -! call mpas_log_write('min rthratensw = $r',realArgs=(/minval(rthratensw(:,1:nCellsSolve))/)) -! call mpas_log_write('--- end subroutine physics_addtend') -! call mpas_log_write('') -! endif - - end subroutine physics_addtend - - !================================================================================================================= subroutine physics_get_tend( block, mesh, state, diag, tend, tend_physics, configs, rk_step, dynamics_substep, & tend_ru_physics, tend_rtheta_physics, tend_rho_physics ) @@ -364,14 +209,14 @@ subroutine physics_get_tend( block, mesh, state, diag, tend, tend_physics, confi config_pbl_scheme, config_convection_scheme, config_radt_lw_scheme, config_radt_sw_scheme, & index_qv, index_qc, index_qr, index_qi, index_qs, index_ni, & rublten, rvblten, mass_edge, rublten_Edge, & - tend_ru_physics, & ! change for addtend for u + tend_ru_physics, & rucuten, rvcuten, rucuten_Edge, & tend_th, tend_scalars, mass, rthblten, rqvblten, rqcblten, rqiblten, rniblten, & rthcuten, rqvcuten, rqccuten, rqrcuten, rqicuten, rqscuten, & rthratenlw, rthratensw, rthdynten, & tend_u_phys, tend_rtheta_adv, tend_diabatic, & theta_m, scalars, & - tend_rtheta_physics, & ! change for addtend for theta_m + tend_rtheta_physics, & tend_theta_euler & ) @@ -401,7 +246,7 @@ subroutine physics_get_tend( block, mesh, state, diag, tend, tend_physics, confi ! if(rk_step .eq. 3) then ! call mpas_log_write('') -! call mpas_log_write('--- enter subroutine physics_addtend:') +! call mpas_log_write('--- enter subroutine physics_get_tend:') ! call mpas_log_write('max rthblten = $r',realArgs=(/maxval(rthblten(:,1:nCellsSolve))/)) ! call mpas_log_write('min rthblten = $r',realArgs=(/minval(rthblten(:,1:nCellsSolve))/)) ! call mpas_log_write('max rthcuten = $r',realArgs=(/maxval(rthcuten(:,1:nCellsSolve))/)) @@ -410,214 +255,12 @@ subroutine physics_get_tend( block, mesh, state, diag, tend, tend_physics, confi ! call mpas_log_write('min rthratenlw = $r',realArgs=(/minval(rthratenlw(:,1:nCellsSolve))/)) ! call mpas_log_write('max rthratensw = $r',realArgs=(/maxval(rthratensw(:,1:nCellsSolve))/)) ! call mpas_log_write('min rthratensw = $r',realArgs=(/minval(rthratensw(:,1:nCellsSolve))/)) -! call mpas_log_write('--- end subroutine physics_addtend') +! call mpas_log_write('--- end subroutine physics_get_tend') ! call mpas_log_write('') ! endif end subroutine physics_get_tend - !================================================================================================== - subroutine physics_addtend_work(block, mesh, nCells, nEdges, nCellsSolve, nEdgesSolve, & - rk_step, dynamics_substep, & - config_pbl_scheme, config_convection_scheme, config_radt_lw_scheme, config_radt_sw_scheme, & - index_qv, index_qc, index_qr, index_qi, index_qs, index_ni, & - rublten, rvblten, mass_edge, rublten_Edge, tend_u, & - rucuten, rvcuten, rucuten_Edge, & - tend_th, tend_scalars, mass, rthblten, rqvblten, rqcblten, rqiblten, rniblten, & - rthcuten, rqvcuten, rqccuten, rqrcuten, rqicuten, rqscuten, & - rthratenlw, rthratensw, rthdynten, & - tend_u_phys, tend_rtheta_adv, tend_diabatic, & - theta, theta_m, scalars, tend_theta, tend_theta_euler & - ) -!================================================================================================== - - use mpas_atm_dimensions - - implicit none - - type(block_type), intent(in) :: block - type(mpas_pool_type), intent(in) :: mesh - integer, intent(in) :: nCells, nEdges, nCellsSolve, nEdgesSolve - integer, intent(in) :: rk_step, dynamics_substep - character(len=StrKIND), intent(in) :: config_pbl_scheme - character(len=StrKIND), intent(in) :: config_convection_scheme - character(len=StrKIND), intent(in) :: config_radt_lw_scheme - character(len=StrKIND), intent(in) :: config_radt_sw_scheme - integer, intent(in) :: index_qv, index_qc, index_qr, index_qi, index_qs, index_ni - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rublten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rvblten - real (kind=RKIND), dimension(nVertLevels,nEdges+1), intent(in) :: mass_edge - real (kind=RKIND), dimension(nVertLevels,nEdges+1), intent(inout) :: rublten_Edge - real (kind=RKIND), dimension(nVertLevels,nEdges+1), intent(inout) :: tend_u - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rucuten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rvcuten - real (kind=RKIND), dimension(nVertLevels,nEdges+1), intent(inout) :: rucuten_Edge - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(inout) :: tend_th - real (kind=RKIND), dimension(num_scalars,nVertLevels,nCells+1), intent(inout) :: tend_scalars - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: mass - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rthblten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqvblten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqcblten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqiblten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rniblten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rthcuten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqvcuten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqccuten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqrcuten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqicuten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rqscuten - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rthratenlw - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: rthratensw - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(inout) :: rthdynten - real (kind=RKIND), dimension(nVertLevels,nEdges+1), intent(inout) :: tend_u_phys - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: tend_rtheta_adv - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: tend_diabatic - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(inout) :: theta - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: theta_m - real (kind=RKIND), dimension(num_scalars,nVertLevels,nCells+1), intent(in) :: scalars - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(inout) :: tend_theta - real (kind=RKIND), dimension(nVertLevels,nCells+1), intent(in) :: tend_theta_euler - - integer :: i, k - real (kind=RKIND) :: coeff - - - !MGD does this need to happen for GF or Tiedtke? - if (config_convection_scheme .eq. 'cu_grell_freitas') then - ! - !save the tendency of the modified potential temperature due to horizontal and vertical advections, - !and mixing before adding tendencies due to physics processes (PBL,convection,radiation) for use in - !the Grell-GF and new Tiedtke parameterization of convection: - ! - do i = 1, nCellsSolve - do k = 1, nVertLevels - coeff = 1._RKIND/(1._RKIND + R_v/R_d*scalars(index_qv,k,i)) - rthdynten(k,i) = coeff*tend_rtheta_adv(k,i)/mass(k,i) - enddo - enddo - end if - - - !add coupled tendencies due to PBL processes: - if (config_pbl_scheme .ne. 'off') then - if (rk_step == 1 .and. dynamics_substep == 1) then - call tend_toEdges(block,mesh,rublten,rvblten,rublten_Edge) - - !MGD for PV budget? should a similar line be in the cumulus section below? - tend_u_phys(1:nVertLevels,1:nEdges) = rublten_Edge(1:nVertLevels,1:nEdges) - end if - - do i = 1, nEdgesSolve - do k = 1, nVertLevels - tend_u(k,i)=tend_u(k,i)+rublten_Edge(k,i)*mass_edge(k,i) - enddo - enddo - - do i = 1, nCellsSolve - do k = 1, nVertLevels - tend_th(k,i) = tend_th(k,i) + rthblten(k,i)*mass(k,i) - tend_scalars(index_qv,k,i) = tend_scalars(index_qv,k,i) + rqvblten(k,i)*mass(k,i) - tend_scalars(index_qc,k,i) = tend_scalars(index_qc,k,i) + rqcblten(k,i)*mass(k,i) - tend_scalars(index_qi,k,i) = tend_scalars(index_qi,k,i) + rqiblten(k,i)*mass(k,i) - enddo - enddo - - pbl_select: select case (trim(pbl_scheme)) - - case("bl_mynn") - - do i = 1, nCellsSolve - do k = 1, nVertLevels - tend_scalars(index_ni,k,i) = tend_scalars(index_ni,k,i) + rniblten(k,i)*mass(k,i) - enddo - enddo - - case default - - end select pbl_select - endif - - !add coupled tendencies due to convection: - if (config_convection_scheme .ne. 'off') then - - do i = 1, nCellsSolve - do k = 1, nVertLevels - tend_th(k,i) = tend_th(k,i) + rthcuten(k,i)*mass(k,i) - tend_scalars(index_qv,k,i) = tend_scalars(index_qv,k,i) + rqvcuten(k,i)*mass(k,i) - tend_scalars(index_qc,k,i) = tend_scalars(index_qc,k,i) + rqccuten(k,i)*mass(k,i) - tend_scalars(index_qi,k,i) = tend_scalars(index_qi,k,i) + rqicuten(k,i)*mass(k,i) - enddo - enddo - - convection_select: select case(convection_scheme) - - case('cu_grell_freitas') - - do i = 1, nCellsSolve - do k = 1, nVertLevels - coeff = 1._RKIND/(1._RKIND + R_v/R_d*scalars(index_qv,k,i)) - rthdynten(k,i) = coeff*(tend_theta(k,i)-tend_theta_euler(k,i) & - - mass(k,i)*tend_diabatic(k,i))/mass(k,i) - enddo - enddo - - - case('cu_kain_fritsch') - do i = 1, nCellsSolve - do k = 1, nVertLevels - tend_scalars(index_qr,k,i) = tend_scalars(index_qr,k,i) + rqrcuten(k,i)*mass(k,i) - tend_scalars(index_qs,k,i) = tend_scalars(index_qs,k,i) + rqscuten(k,i)*mass(k,i) - enddo - enddo - - case('cu_tiedtke') - if (rk_step == 1 .and. dynamics_substep == 1) then - call tend_toEdges(block,mesh,rucuten,rvcuten,rucuten_Edge) - - tend_u_phys(1:nVertLevels,1:nEdges) = tend_u_phys(1:nVertLevels,1:nEdges) & - + rucuten_Edge(1:nVertLevels,1:nEdges) - end if - do i = 1, nEdgesSolve - do k = 1, nVertLevels - tend_u(k,i)=tend_u(k,i)+rucuten_Edge(k,i)*mass_edge(k,i) - enddo - enddo - - case default - end select convection_select - endif - - !add coupled tendencies due to longwave radiation: - if (config_radt_lw_scheme .ne. 'off') then - do i = 1, nCellsSolve - do k = 1, nVertLevels - tend_th(k,i) = tend_th(k,i) + rthratenlw(k,i)*mass(k,i) - enddo - enddo - endif - - !add coupled tendencies due to shortwave radiation: - if (config_radt_sw_scheme .ne. 'off') then - do i = 1, nCellsSolve - do k = 1, nVertLevels - tend_th(k,i) = tend_th(k,i) + rthratensw(k,i)*mass(k,i) - enddo - enddo - endif - - !if non-hydrostatic core, convert the tendency for the potential temperature to a - !tendency for the modified potential temperature: - do i = 1, nCellsSolve - do k = 1, nVertLevels - theta(k,i) = theta_m(k,i) / (1. + R_v/R_d * scalars(index_qv,k,i)) - tend_th(k,i) = (1. + R_v/R_d * scalars(index_qv,k,i)) * tend_th(k,i) & - + R_v/R_d * theta(k,i) * tend_scalars(index_qv,k,i) - tend_theta(k,i) = tend_theta(k,i) + tend_th(k,i) - enddo - enddo - - end subroutine physics_addtend_work - !================================================================================================== subroutine physics_get_tend_work(block, mesh, nCells, nEdges, nCellsSolve, nEdgesSolve, & rk_step, dynamics_substep, & @@ -706,7 +349,7 @@ subroutine physics_get_tend_work(block, mesh, nCells, nEdges, nCellsSolve, nEdge enddo enddo - pbl_select: select case (trim(pbl_scheme)) + pbl_select: select case (trim(config_pbl_scheme)) case("bl_mynn") @@ -733,7 +376,7 @@ subroutine physics_get_tend_work(block, mesh, nCells, nEdges, nCellsSolve, nEdge enddo enddo - convection_select: select case(convection_scheme) + convection_select: select case(config_convection_scheme) case('cu_kain_fritsch') do i = 1, nCellsSolve diff --git a/src/core_atmosphere/physics/mpas_atmphys_update.F b/src/core_atmosphere/physics/mpas_atmphys_update.F index d05e37ad0b..421a90e280 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_update.F +++ b/src/core_atmosphere/physics/mpas_atmphys_update.F @@ -8,7 +8,6 @@ !================================================================================================================= module mpas_atmphys_update use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_driver_convection diff --git a/src/core_atmosphere/physics/mpas_atmphys_update_surface.F b/src/core_atmosphere/physics/mpas_atmphys_update_surface.F index 1a1125f379..684c274581 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_update_surface.F +++ b/src/core_atmosphere/physics/mpas_atmphys_update_surface.F @@ -9,7 +9,6 @@ module mpas_atmphys_update_surface use mpas_dmpar use mpas_kind_types - use mpas_derived_types use mpas_pool_routines use mpas_atmphys_date_time diff --git a/src/core_atmosphere/physics/mpas_atmphys_vars.F b/src/core_atmosphere/physics/mpas_atmphys_vars.F index bdd26ce109..012f63befa 100644 --- a/src/core_atmosphere/physics/mpas_atmphys_vars.F +++ b/src/core_atmosphere/physics/mpas_atmphys_vars.F @@ -68,22 +68,35 @@ module mpas_atmphys_vars ! * moved the declarations of arrays delta_p,wstar_p,uoce_p,and voce_p since they are now used in both modules ! module_bl_ysu.F and module_bl_mynn.F. ! Laura D. Fowler (laura@ucar.edu) / 20016-10-27. +! * removed the initialization local variable gwdo_scheme. gwdo_scheme is no longer needed and can be replaced +! with config_gwdo_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable lsm_scheme. lsm_scheme is no longer needed and can be replaced +! with config_lsm_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable sfclayer_scheme. sfclayer_scheme is no longer needed and can be +! replaced with config_sfclayer_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable pbl_scheme. pbl_scheme is no longer needed and can be replaced +! replaced with config_pbl_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable radt_cld_scheme. radt_cld_scheme is no longer needed and can be +! replaced replaced with config_radt_cld_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable radt_lw_scheme. radt_lw_scheme is no longer needed and can be +! replaced replaced with config_radt_lw_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable radt_sw_scheme. radt_sw_scheme is no longer needed and can be +! replaced replaced with config_radt_sw_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable convection_scheme. convection_scheme is no longer needed and can be +! replaced replaced with config_convection_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. +! * removed the initialization local variable microp_scheme. microp_scheme is no longer needed and can be +! replaced replaced with config_microp_scheme. +! Laura D. Fowler (laura@ucar.edu) / 2017-02-16. -!================================================================================================================= -!list of physics parameterizations: -!================================================================================================================= - - character(len=StrKIND),public:: microp_scheme - character(len=StrKIND),public:: convection_scheme - character(len=StrKIND),public:: gwdo_scheme - character(len=StrKIND),public:: lsm_scheme - character(len=StrKIND),public:: pbl_scheme - character(len=StrKIND),public:: radt_cld_scheme - character(len=StrKIND),public:: radt_lw_scheme - character(len=StrKIND),public:: radt_sw_scheme - character(len=StrKIND),public:: sfclayer_scheme - !================================================================================================================= !wrf-variables:these variables are needed to keep calls to different physics parameterizations !as in wrf model. diff --git a/src/core_atmosphere/physics/physics_wrf/Makefile b/src/core_atmosphere/physics/physics_wrf/Makefile index 68810446e6..cc9ee673b9 100644 --- a/src/core_atmosphere/physics/physics_wrf/Makefile +++ b/src/core_atmosphere/physics/physics_wrf/Makefile @@ -3,7 +3,7 @@ all: dummy physics_wrf dummy: - echo "****** compile physics_wrf ******" + echo "****** compiling physics_wrf ******" OBJS = \ libmassv.o \ @@ -26,6 +26,7 @@ OBJS = \ module_ra_cam_support.o \ module_ra_rrtmg_lw.o \ module_ra_rrtmg_sw.o \ + module_ra_rrtmg_vinterp.o \ module_sf_bem.o \ module_sf_bep.o \ module_sf_bep_bem.o \ @@ -42,41 +43,27 @@ physics_wrf: $(OBJS) # DEPENDENCIES: module_bl_mynn.o: \ - module_cam_error_function.o \ - ../mpas_atmphys_constants.o + module_cam_error_function.o module_cam_support.o: \ - module_cam_shr_kind_mod.o \ - ../mpas_atmphys_utilities.o - -module_cu_tiedtke.o: \ - ../mpas_atmphys_constants.o - -module_cu_ntiedtke.o: \ - ../mpas_atmphys_constants.o - -module_mp_radar.o: \ - ../mpas_atmphys_functions.o \ - ../mpas_atmphys_utilities.o + module_cam_shr_kind_mod.o module_mp_thompson.o: \ - module_mp_radar.o \ - ../mpas_atmphys_functions.o \ - ../mpas_atmphys_utilities.o + module_mp_radar.o + +module_mp_wsm6.o: \ + module_mp_radar.o module_ra_cam.o: \ module_cam_support.o \ - module_ra_cam_support.o \ - ../mpas_atmphys_utilities.o - -module_ra_cam_support.o: \ - ../mpas_atmphys_utilities.o + module_ra_cam_support.o module_ra_rrtmg_lw.o: \ - ../mpas_atmphys_constants.o + module_ra_rrtmg_vinterp.o module_ra_rrtmg_sw.o: \ - ../mpas_atmphys_constants.o + module_ra_rrtmg_lw.o \ + module_ra_rrtmg_vinterp.o module_sf_bep.o: \ module_sf_urban.o @@ -87,8 +74,7 @@ module_sf_bep_bem.o: \ module_sf_mynn.o: \ module_bl_mynn.o \ - module_sf_sfclay.o \ - ../mpas_atmphys_constants.o + module_sf_sfclay.o module_sf_noahdrv.o: \ module_sf_bem.o \ @@ -97,10 +83,6 @@ module_sf_noahdrv.o: \ module_sf_noahlsm.o \ module_sf_urban.o -module_sf_noahlsm.o: \ - ../mpas_atmphys_constants.o \ - ../mpas_atmphys_utilities.o - clean: $(RM) *.f90 *.o *.mod @# Certain systems with intel compilers generate *.i files @@ -110,7 +92,7 @@ clean: .F.o: ifeq "$(GEN_F90)" "true" $(CPP) $(CPPFLAGS) $(COREDEF) $(CPPINCLUDES) $< > $*.f90 - $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) -I../../../framework -I../../../operators -I.. -I../../../external/esmf_time_f90 + $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) -I.. -I../../../framework -I../../../external/esmf_time_f90 else - $(FC) $(CPPFLAGS) $(COREDEF) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) -I../../../framework -I../../../operators -I.. -I../../../external/esmf_time_f90 + $(FC) $(CPPFLAGS) $(COREDEF) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) -I.. -I../../../framework -I../../../external/esmf_time_f90 endif diff --git a/src/core_atmosphere/physics/physics_wrf/module_cam_support.F b/src/core_atmosphere/physics/physics_wrf/module_cam_support.F index fed7f26743..2f8499e13a 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_cam_support.F +++ b/src/core_atmosphere/physics/physics_wrf/module_cam_support.F @@ -7,9 +7,12 @@ MODULE module_cam_support ! Author: William.Gustafson@pnl.gov, Nov 2009 !------------------------------------------------------------------------ #if defined(mpas) - use mpas_atmphys_utilities + use mpas_atmphys_utilities,only:physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) #else use module_state_description, only: param_num_moist + use module_wrf_error +#define FATAL_ERROR(M) call wrf_error_fatal( M ) #endif use shr_kind_mod @@ -87,28 +90,7 @@ MODULE module_cam_support !!$END FUNCTION cnst_get_type_byind -#if defined(mpas) -!------------------------------------------------------------------------ -SUBROUTINE endrun(msg) -! Pass through routine to wrf_error_fatal that mimics endrun in module -! abortutils of CAM. -! -! Replaces endrun in abortutils module in CAM. -! -! Author: William.Gustafson@pnl.gov, Nov 2009 -! Modified : Balwinder.Singh@pnl.gov - Argument made optional -!------------------------------------------------------------------------ -! Argument of the subroutine is made optional to accomodate endrun calls with no argument - character(len=*), intent(in), optional :: msg - if(present(msg)) then - call physics_error_fatal(msg) - else -! The error message is written to iulog bwfore the endrun call - call physics_error_fatal(iulog) - endif -END SUBROUTINE endrun -#else !------------------------------------------------------------------------ SUBROUTINE endrun(msg) ! Pass through routine to wrf_error_fatal that mimics endrun in module @@ -119,19 +101,17 @@ SUBROUTINE endrun(msg) ! Author: William.Gustafson@pnl.gov, Nov 2009 ! Modified : Balwinder.Singh@pnl.gov - Argument made optional !------------------------------------------------------------------------ - USE module_wrf_error - ! Argument of the subroutine is made optional to accomodate endrun calls with no argument character(len=*), intent(in), optional :: msg if(present(msg)) then - call wrf_error_fatal(msg) + FATAL_ERROR(msg) else ! The error message is written to iulog bwfore the endrun call - call wrf_error_fatal(iulog) + FATAL_ERROR(iulog) endif END SUBROUTINE endrun -#endif + !------------------------------------------------------------------------ diff --git a/src/core_atmosphere/physics/physics_wrf/module_mp_thompson.F b/src/core_atmosphere/physics/physics_wrf/module_mp_thompson.F index 20190e3fd3..a2d28456b7 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_mp_thompson.F +++ b/src/core_atmosphere/physics/physics_wrf/module_mp_thompson.F @@ -61,7 +61,7 @@ MODULE module_mp_thompson use mpas_kind_types - use mpas_atmphys_functions + use mpas_atmphys_functions, only: gammp,wgamma,rslf,rsif use mpas_atmphys_utilities use module_mp_radar diff --git a/src/core_atmosphere/physics/physics_wrf/module_mp_thompson_cldfra3.F b/src/core_atmosphere/physics/physics_wrf/module_mp_thompson_cldfra3.F index bb5ded6e22..bf47aae2ef 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_mp_thompson_cldfra3.F +++ b/src/core_atmosphere/physics/physics_wrf/module_mp_thompson_cldfra3.F @@ -14,6 +14,7 @@ module module_mp_thompson_cldfra3 ! than the model-top index. ! Laura D. Fowler (laura@ucar.edu)/2016-09-23. + use mpas_atmphys_functions,only: rslf,rsif implicit none private @@ -47,8 +48,8 @@ SUBROUTINE cal_cldfra3(CLDFRA, qv, qc, qi, qs, & & ims,ime, jms,jme, kms,kme, & & its,ite, jts,jte, kts,kte) ! - USE module_mp_thompson , ONLY : rsif, rslf - IMPLICIT NONE +! USE module_mp_thompson , ONLY : rsif, rslf +! IMPLICIT NONE ! INTEGER, INTENT(IN):: ids,ide, jds,jde, kds,kde, & & ims,ime, jms,jme, kms,kme, & diff --git a/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_lw.F b/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_lw.F index 30d6c014bb..6d59bcb82e 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_lw.F +++ b/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_lw.F @@ -2634,6 +2634,15 @@ end module mcica_subcol_gen_lw ! module rrtmg_lw_cldprmc +#if defined(mpas) + use mpas_atmphys_utilities,only: physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) +#else +use module_wrf_error +#define FATAL_ERROR(M) call wrf_error_fatal( M ) +#endif + + ! -------------------------------------------------------------------------- ! | | ! | Copyright 2002-2009, Atmospheric & Environmental Research, Inc. (AER). | @@ -2853,7 +2862,7 @@ subroutine cldprmc(nlayers, inflag, iceflag, liqflag, cldfmc, & write(errmess,'(A,i5,i5,f8.2,f8.2)' ) & 'ERROR: ICE GENERALIZED EFFECTIVE SIZE OUT OF BOUNDS' & ,ig, lay, ciwpmc(ig,lay), radice - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if ncbands = 16 factor = (radice - 2._rb)/3._rb @@ -2875,7 +2884,7 @@ subroutine cldprmc(nlayers, inflag, iceflag, liqflag, cldfmc, & write(errmess,'(A,i5,i5,f8.2,f8.2)' ) & 'ERROR: SNOW GENERALIZED EFFECTIVE SIZE OUT OF BOUNDS' & ,ig, lay, cswpmc(ig,lay), radsno - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if ncbands = 16 factor = (radsno - 2._rb)/3._rb @@ -11382,8 +11391,8 @@ MODULE module_ra_rrtmg_lw #if defined(mpas) !MPAS specific (Laura D. Fowler): -use mpas_atmphys_constants,only: cp,g=>gravity -use mpas_atmphys_o3climatology,only: vinterp_ozn +use mpas_atmphys_constants,only : cp,g=>gravity +use module_ra_rrtmg_vinterp,only: vinterp_ozn !> add-ons and modifications to sourcecode: !> ---------------------------------------- @@ -11420,6 +11429,7 @@ MODULE module_ra_rrtmg_lw #else use module_model_constants, only : cp use module_wrf_error +#define FATAL_ERROR(M) call wrf_error_fatal( M ) #if (HWRF == 1) USE module_state_description, ONLY : FER_MP_HIRES, FER_MP_HIRES_ADVECT, ETAMP_HWRF #else diff --git a/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_sw.F b/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_sw.F index 414fc6b41a..a2ee96b621 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_sw.F +++ b/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_sw.F @@ -1948,6 +1948,14 @@ end module mcica_subcol_gen_sw module rrtmg_sw_cldprmc +#if defined(mpas) + use mpas_atmphys_utilities,only: physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) +#else +use module_wrf_error +#define FATAL_ERROR(M) call wrf_error_fatal( M ) +#endif + ! -------------------------------------------------------------------------- ! | | ! | Copyright 2002-2008, Atmospheric & Environmental Research, Inc. (AER). | @@ -2173,7 +2181,7 @@ subroutine cldprmc_sw(nlayers, inflag, iceflag, liqflag, cldfmc, & write(errmess,'(A,i5,i5,f8.2,f8.2)' ) & 'ERROR: ICE GENERALIZED EFFECTIVE SIZE OUT OF BOUNDS' & ,ig, lay, ciwpmc(ig,lay), radice - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if factor = (radice - 2._rb)/3._rb index = int(factor) @@ -2190,11 +2198,11 @@ subroutine cldprmc_sw(nlayers, inflag, iceflag, liqflag, cldfmc, & (fdlice3(index+1,ib) - fdlice3(index,ib)) if (fdelta(ig) .lt. 0.0_rb) then write(errmess, *) 'FDELTA LESS THAN 0.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if if (fdelta(ig) .gt. 1.0_rb) then write(errmess, *) 'FDELTA GT THAN 1.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if forwice(ig) = fdelta(ig) + 0.5_rb / ssacoice(ig) ! See Fu 1996 p. 2067 @@ -2222,7 +2230,7 @@ subroutine cldprmc_sw(nlayers, inflag, iceflag, liqflag, cldfmc, & write(errmess,'(A,i5,i5,f8.2,f8.2)' ) & 'ERROR: SNOW GENERALIZED EFFECTIVE SIZE OUT OF BOUNDS' & ,ig, lay, cswpmc(ig,lay), radsno - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if factor = (radsno - 2._rb)/3._rb index = int(factor) @@ -2239,11 +2247,11 @@ subroutine cldprmc_sw(nlayers, inflag, iceflag, liqflag, cldfmc, & (fdlice3(index+1,ib) - fdlice3(index,ib)) if (fdelta(ig) .lt. 0.0_rb) then write(errmess, *) 'FDELTA LESS THAN 0.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if if (fdelta(ig) .gt. 1.0_rb) then write(errmess, *) 'FDELTA GT THAN 1.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if forwsno(ig) = fdelta(ig) + 0.5_rb / ssacosno(ig) ! See Fu 1996 p. 2067 @@ -2251,23 +2259,23 @@ subroutine cldprmc_sw(nlayers, inflag, iceflag, liqflag, cldfmc, & ! Check to ensure all calculated quantities are within physical limits. if (extcosno(ig) .lt. 0.0_rb) then write(errmess, *) 'SNOW EXTINCTION LESS THAN 0.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if if (ssacosno(ig) .gt. 1.0_rb) then write(errmess, *) 'SNOW SSA GRTR THAN 1.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if if (ssacosno(ig) .lt. 0.0_rb) then write(errmess, *) 'SNOW SSA LESS THAN 0.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if if (gsno(ig) .gt. 1.0_rb) then write(errmess, *) 'SNOW ASYM GRTR THAN 1.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if if (gsno(ig) .lt. 0.0_rb) then write(errmess, *) 'SNOW ASYM LESS THAN 0.0' - call wrf_error_fatal(errmess) + FATAL_ERROR(errmess) end if else extcosno(ig) = 0.0_rb @@ -9810,8 +9818,8 @@ MODULE module_ra_rrtmg_sw #if defined(mpas) !MPAS specific (Laura D. Fowler): -use mpas_atmphys_constants,only: cp,g=>gravity -use mpas_atmphys_o3climatology,only: vinterp_ozn +use mpas_atmphys_constants,only : cp,g=>gravity +use module_ra_rrtmg_vinterp,only: vinterp_ozn !> add-ons and modifications to sourcecode: !> ---------------------------------------- diff --git a/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_vinterp.F b/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_vinterp.F new file mode 100644 index 0000000000..6ec6c41ccb --- /dev/null +++ b/src/core_atmosphere/physics/physics_wrf/module_ra_rrtmg_vinterp.F @@ -0,0 +1,181 @@ +!================================================================================================================= +!in module_ra_rrtmg_vinterp, the subroutine vinterp_ozn was originally the same as subroutine radozn and copied +!from module_ra_cam.F. module_ra_cam.F was itself copied from WRF 3.3.0. + +!subroutine vinterp_ozn is called from the subroutines rrtmg_lwrad (in module_ra_rrtmg_lw.F) and rrtmg_swrad (in +!module_ra_rrtmg_sw.F) for interpolation of climatological ozone onto the MPAS pressure levels. vinterp_ozn is +!also called from subroutine radiation_lw_from_MPAS (in mpas_atmphys_driver_radiation_lw.F) for diagnostic only. +!Laura D. Fowler (laura@ucar.edu)/2017-02-10. +!================================================================================================================= + module module_ra_rrtmg_vinterp + use mpas_kind_types + + implicit none + private + public:: vinterp_ozn + + + contains + + +!================================================================================================================= + subroutine vinterp_ozn (lchnk, ncol, pcols, pver, pmid_in, pin_in, levsiz, ozmix_in, o3vmr_out) +!-------------------------------------------------------------------------------------------------- +! +! Purpose: Interpolate ozone from current time-interpolated values to model levels +! +! Method: Use pressure values to determine interpolation levels +! +! Author: Bruce Briegleb +! +!----------------------------------------------------------------------------------------------------------------- +! use shr_kind_mod, only: r8 => shr_kind_r8 +! use ppgrid +! use phys_grid, only: get_lat_all_p, get_lon_all_p +! use comozp +! use abortutils, only: endrun +!----------------------------------------------------------------------------------------------------------------- + implicit none +!----------------------------------------------------------------------------------------------------------------- +! +! Arguments +! + integer, intent(in) :: lchnk ! chunk identifier + integer, intent(in) :: pcols, pver + integer, intent(in) :: ncol ! number of atmospheric columns + integer, intent(in) :: levsiz ! number of ozone layers + +!ldf begin: +! real(kind=RKIND), intent(in) :: pmid(pcols,pver) ! level pressures (mks) +! real(kind=RKIND), intent(in) :: pin(levsiz) ! ozone data level pressures (mks) +! real(kind=RKIND), intent(in) :: ozmix(pcols,levsiz)! ozone mixing ratio + +! real(kind=RKIND), intent(out) :: o3vmr(pcols,pver) ! ozone volume mixing ratio + + real(kind=RKIND), intent(in) :: pmid_in(pcols,pver) ! level pressures (mks) + real(kind=RKIND), intent(in) :: pin_in(levsiz) ! ozone data level pressures (mks) + real(kind=RKIND), intent(in) :: ozmix_in(pcols,levsiz)! ozone mixing ratio + + real(kind=RKIND), intent(out) :: o3vmr_out(pcols,pver)! ozone volume mixing ratio +!ldf end. +! +! local storage +! + integer i ! longitude index + integer k, kk, kkstart ! level indices + integer kupper(pcols) ! Level indices for interpolation + integer kount ! Counter + integer lats(pcols) ! latitude indices + integer lons(pcols) ! latitude indices + + real(kind=RKIND) dpu ! upper level pressure difference + real(kind=RKIND) dpl ! lower level pressure difference + +!ldf begin: + real(kind=RKIND):: pmid(pcols,pver) ! level pressures (mks) + real(kind=RKIND):: pin(levsiz) ! ozone data level pressures (mks) + real(kind=RKIND):: ozmix(pcols,levsiz) ! ozone mixing ratio + real(kind=RKIND):: o3vmr(pcols,pver) ! ozone volume mixing ratio +! +! Initialize latitude indices +! +! call get_lat_all_p(lchnk, ncol, lats) +! call get_lon_all_p(lchnk, ncol, lons) +! + +!ldf begin: + do k = 1,levsiz + pin(k) = pin_in(k) + enddo + do i = 1,pcols + do k = 1,levsiz + ozmix(i,k) = ozmix_in(i,k) + enddo + enddo + do i = 1,pcols + do k = 1,pver + kk = pver-k+1 + pmid(i,kk) = pmid_in(i,k) + enddo + enddo +!ldf end. + +! Initialize index array +! + do i=1,ncol + kupper(i) = 1 + end do + + do k=1,pver +! +! Top level we need to start looking is the top level for the previous k +! for all longitude points +! + kkstart = levsiz + do i=1,ncol + kkstart = min0(kkstart,kupper(i)) + end do + kount = 0 +! +! Store level indices for interpolation +! + do kk=kkstart,levsiz-1 + do i=1,ncol + if (pin(kk).lt.pmid(i,k) .and. pmid(i,k).le.pin(kk+1)) then + kupper(i) = kk + kount = kount + 1 + end if + end do +! +! If all indices for this level have been found, do the interpolation and +! go to the next level +! + if (kount.eq.ncol) then + do i=1,ncol + dpu = pmid(i,k) - pin(kupper(i)) + dpl = pin(kupper(i)+1) - pmid(i,k) + o3vmr(i,k) = (ozmix(i,kupper(i))*dpl + & + ozmix(i,kupper(i)+1)*dpu)/(dpl + dpu) + end do + goto 35 + end if + end do +! +! If we've fallen through the kk=1,levsiz-1 loop, we cannot interpolate and +! must extrapolate from the bottom or top ozone data level for at least some +! of the longitude points. +! + do i=1,ncol + if (pmid(i,k) .lt. pin(1)) then + o3vmr(i,k) = ozmix(i,1)*pmid(i,k)/pin(1) + else if (pmid(i,k) .gt. pin(levsiz)) then + o3vmr(i,k) = ozmix(i,levsiz) + else + dpu = pmid(i,k) - pin(kupper(i)) + dpl = pin(kupper(i)+1) - pmid(i,k) + o3vmr(i,k) = (ozmix(i,kupper(i))*dpl + & + ozmix(i,kupper(i)+1)*dpu)/(dpl + dpu) + end if + end do + + if (kount.gt.ncol) then +! call endrun ('VINTERP_OZN: Bad ozone data: non-monotonicity suspected') + end if +35 continue + end do + +!ldf begin: + do i = 1,pcols + do k = 1,pver + kk = pver-k+1 + o3vmr_out(i,kk) = o3vmr(i,k) + enddo + enddo +!ldf end. + + return +end subroutine vinterp_ozn + +!================================================================================================================= + end module module_ra_rrtmg_vinterp +!================================================================================================================= diff --git a/src/core_atmosphere/physics/physics_wrf/module_sf_bem.F b/src/core_atmosphere/physics/physics_wrf/module_sf_bem.F index 2619567359..544b802f37 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_sf_bem.F +++ b/src/core_atmosphere/physics/physics_wrf/module_sf_bem.F @@ -3,9 +3,9 @@ MODULE module_sf_bem ! Variables and constants used in the BEM module ! ----------------------------------------------------------------------- -use mpas_abort, only : mpas_dmpar_global_abort #ifdef mpas -#define FATAL_ERROR(M) call mpas_dmpar_global_abort( M ) +use mpas_atmphys_utilities, only: physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) #else #define FATAL_ERROR(M) write(0,*) M ; stop #endif diff --git a/src/core_atmosphere/physics/physics_wrf/module_sf_bep.F b/src/core_atmosphere/physics/physics_wrf/module_sf_bep.F index 8bf465b461..9434dc8fac 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_sf_bep.F +++ b/src/core_atmosphere/physics/physics_wrf/module_sf_bep.F @@ -1,9 +1,9 @@ MODULE module_sf_bep -use mpas_abort, only : mpas_dmpar_global_abort #ifdef mpas -#define FATAL_ERROR(M) call mpas_dmpar_global_abort( M ) -#else +use mpas_atmphys_utilities, only: physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) +#else #define FATAL_ERROR(M) write(0,*) M ; stop #endif diff --git a/src/core_atmosphere/physics/physics_wrf/module_sf_bep_bem.F b/src/core_atmosphere/physics/physics_wrf/module_sf_bep_bem.F index e5c4418373..5235fd3723 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_sf_bep_bem.F +++ b/src/core_atmosphere/physics/physics_wrf/module_sf_bep_bem.F @@ -1,9 +1,9 @@ MODULE module_sf_bep_bem -use mpas_abort, only : mpas_dmpar_global_abort #ifdef mpas -#define FATAL_ERROR(M) call mpas_dmpar_global_abort( M ) -#else +use mpas_atmphys_utilities, only: physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) +#else #define FATAL_ERROR(M) write(0,*) M ; stop #endif diff --git a/src/core_atmosphere/physics/physics_wrf/module_sf_noahlsm.F b/src/core_atmosphere/physics/physics_wrf/module_sf_noahlsm.F index c4e680b1e8..a854f41f88 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_sf_noahlsm.F +++ b/src/core_atmosphere/physics/physics_wrf/module_sf_noahlsm.F @@ -3,9 +3,11 @@ MODULE module_sf_noahlsm #if defined(mpas) !MPAS specific (Laura D. Fowler): use mpas_atmphys_constants, rhowater => rho_w -use mpas_atmphys_utilities +use mpas_atmphys_utilities,only: physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) #else - USE module_model_constants +USE module_model_constants +#define FATAL_ERROR(M) write(0,*) M ; stop #endif !MPAS specific end. @@ -492,7 +494,7 @@ SUBROUTINE SFLX (FFROZP,ICE,ISURBAN,DT,ZLVL,NSOIL,SLDPTH, & !C ELSE SNDENS = SNEQV / SNOWH IF(SNDENS > 1.0) THEN -! CALL wrf_error_fatal ( 'Physical snow depth is less than snow water equiv.' ) + FATAL_ERROR( 'Physical snow depth is less than snow water equiv.' ) ENDIF CALL CSNOW (SNCOND,SNDENS) END IF @@ -2419,7 +2421,7 @@ SUBROUTINE REDPRM (VEGTYP,SOILTYP,SLOPETYP,CFACTR,CMCMAX,RSMAX, & IF (NROOT .gt. NSOIL) THEN WRITE (err_message,*) 'Error: too many root layers ', & NSOIL,NROOT -! CALL wrf_error_fatal ( err_message ) + FATAL_ERROR( err_message ) ! ---------------------------------------------------------------------- ! CALCULATE ROOT DISTRIBUTION. PRESENT VERSION ASSUMES UNIFORM ! DISTRIBUTION BASED ON SOIL LAYER DEPTHS. diff --git a/src/core_atmosphere/physics/physics_wrf/module_sf_urban.F b/src/core_atmosphere/physics/physics_wrf/module_sf_urban.F index 66b38e7db8..d2ac6a0b48 100644 --- a/src/core_atmosphere/physics/physics_wrf/module_sf_urban.F +++ b/src/core_atmosphere/physics/physics_wrf/module_sf_urban.F @@ -1,5 +1,12 @@ MODULE module_sf_urban +#ifdef mpas +use mpas_atmphys_utilities, only: physics_error_fatal +#define FATAL_ERROR(M) call physics_error_fatal( M ) +#else +#define FATAL_ERROR(M) write(0,*) M ; stop +#endif + !=============================================================================== ! Single-Layer Urban Canopy Model for WRF Noah-LSM ! Original Version: 2002/11/06 by Hiroyuki Kusaka @@ -503,8 +510,7 @@ SUBROUTINE urban(LSOLAR, & ! L if(ahoption==1) AH=AH*ahdiuprf(tloc) IF( ZDC+Z0C+2. >= ZA) THEN -! CALL wrf_error_fatal ("ZDC + Z0C + 2m is larger than the 1st WRF level "// & -! "Stop in subroutine urban - change ZDC and Z0C" ) + FATAL_ERROR("ZDC + Z0C + 2m is larger than the 1st WRF level - Stop in subroutine urban - change ZDC and Z0C" ) END IF IF(.NOT.LSOLAR) THEN @@ -1508,7 +1514,7 @@ SUBROUTINE urban_param_init(DZR,DZB,DZG,num_soil_layers, & IOSTAT=IOSTATUS) IF (IOSTATUS > 0) THEN -! CALL wrf_error_fatal('ERROR OPEN URBPARM.TBL') + FATAL_ERROR('ERROR OPEN URBPARM.TBL') ENDIF READLOOP : do @@ -1525,118 +1531,118 @@ SUBROUTINE urban_param_init(DZR,DZB,DZG,num_soil_layers, & read(string(indx+1:),*) icate IF (.not. ALLOCATED(ZR_TBL)) then ALLOCATE( ZR_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ZR_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ZR_TBL in urban_param_init') ALLOCATE( SIGMA_ZED_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0)CALL wrf_error_fatal('Error allocating SIGMA_ZED_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating SIGMA_ZED_TBL in urban_param_init') ALLOCATE( Z0C_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating Z0C_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating Z0C_TBL in urban_param_init') ALLOCATE( Z0HC_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating Z0HC_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating Z0HC_TBL in urban_param_init') ALLOCATE( ZDC_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ZDC_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ZDC_TBL in urban_param_init') ALLOCATE( SVF_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating SVF_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating SVF_TBL in urban_param_init') ALLOCATE( R_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating R_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating R_TBL in urban_param_init') ALLOCATE( RW_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating RW_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating RW_TBL in urban_param_init') ALLOCATE( HGT_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating HGT_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating HGT_TBL in urban_param_init') ALLOCATE( AH_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating AH_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating AH_TBL in urban_param_init') ALLOCATE( BETR_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating BETR_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating BETR_TBL in urban_param_init') ALLOCATE( BETB_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating BETB_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating BETB_TBL in urban_param_init') ALLOCATE( BETG_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating BETG_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating BETG_TBL in urban_param_init') ALLOCATE( CAPR_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating CAPR_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating CAPR_TBL in urban_param_init') ALLOCATE( CAPB_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating CAPB_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating CAPB_TBL in urban_param_init') ALLOCATE( CAPG_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating CAPG_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating CAPG_TBL in urban_param_init') ALLOCATE( AKSR_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating AKSR_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating AKSR_TBL in urban_param_init') ALLOCATE( AKSB_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating AKSB_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating AKSB_TBL in urban_param_init') ALLOCATE( AKSG_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating AKSG_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating AKSG_TBL in urban_param_init') ALLOCATE( ALBR_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ALBR_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ALBR_TBL in urban_param_init') ALLOCATE( ALBB_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ALBB_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ALBB_TBL in urban_param_init') ALLOCATE( ALBG_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ALBG_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ALBG_TBL in urban_param_init') ALLOCATE( EPSR_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating EPSR_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating EPSR_TBL in urban_param_init') ALLOCATE( EPSB_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating EPSB_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating EPSB_TBL in urban_param_init') ALLOCATE( EPSG_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating EPSG_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating EPSG_TBL in urban_param_init') ALLOCATE( Z0R_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating Z0R_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating Z0R_TBL in urban_param_init') ALLOCATE( Z0B_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating Z0B_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating Z0B_TBL in urban_param_init') ALLOCATE( Z0G_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating Z0G_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating Z0G_TBL in urban_param_init') ALLOCATE( AKANDA_URBAN_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating AKANDA_URBAN_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating AKANDA_URBAN_TBL in urban_param_init') ALLOCATE( Z0HB_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating Z0HB_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating Z0HB_TBL in urban_param_init') ALLOCATE( Z0HG_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating Z0HG_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating Z0HG_TBL in urban_param_init') ALLOCATE( TRLEND_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating TRLEND_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating TRLEND_TBL in urban_param_init') ALLOCATE( TBLEND_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating TBLEND_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating TBLEND_TBL in urban_param_init') ALLOCATE( TGLEND_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating TGLEND_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating TGLEND_TBL in urban_param_init') ALLOCATE( FRC_URB_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating FRC_URB_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating FRC_URB_TBL in urban_param_init') ! ALLOCATE( ROOF_WIDTH(ICATE), stat=allocate_status ) - ! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ROOF_WIDTH in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ROOF_WIDTH in urban_param_init') ! ALLOCATE( ROAD_WIDTH(ICATE), stat=allocate_status ) - ! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ROAD_WIDTH in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ROAD_WIDTH in urban_param_init') !for BEP ALLOCATE( NUMDIR_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating NUMDIR_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating NUMDIR_TBL in urban_param_init') ALLOCATE( STREET_DIRECTION_TBL(MAXDIRS , ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating STREET_DIRECTION_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating STREET_DIRECTION_TBL in urban_param_init') ALLOCATE( STREET_WIDTH_TBL(MAXDIRS , ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating STREET_WIDTH_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating STREET_WIDTH_TBL in urban_param_init') ALLOCATE( BUILDING_WIDTH_TBL(MAXDIRS , ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating BUILDING_WIDTH_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating BUILDING_WIDTH_TBL in urban_param_init') ALLOCATE( NUMHGT_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating NUMHGT_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating NUMHGT_TBL in urban_param_init') ALLOCATE( HEIGHT_BIN_TBL(MAXHGTS , ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating HEIGHT_BIN_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating HEIGHT_BIN_TBL in urban_param_init') ALLOCATE( HPERCENT_BIN_TBL(MAXHGTS , ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating HPERCENT_BIN_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating HPERCENT_BIN_TBL in urban_param_init') ALLOCATE( COP_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating COP_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating COP_TBL in urban_param_init') ALLOCATE( PWIN_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating PWIN_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating PWIN_TBL in urban_param_init') ALLOCATE( BETA_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating BETA_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating BETA_TBL in urban_param_init') ALLOCATE( SW_COND_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating SW_COND_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating SW_COND_TBL in urban_param_init') ALLOCATE( TIME_ON_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating TIME_ON_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating TIME_ON_TBL in urban_param_init') ALLOCATE( TIME_OFF_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating TIME_OFF_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating TIME_OFF_TBL in urban_param_init') ALLOCATE( TARGTEMP_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating TARGTEMP_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating TARGTEMP_TBL in urban_param_init') ALLOCATE( GAPTEMP_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating GAPTEMP_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating GAPTEMP_TBL in urban_param_init') ALLOCATE( TARGHUM_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating TARGHUM_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating TARGHUM_TBL in urban_param_init') ALLOCATE( GAPHUM_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating GAPHUM_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating GAPHUM_TBL in urban_param_init') ALLOCATE( PERFLO_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating PERFLO_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating PERFLO_TBL in urban_param_init') ALLOCATE( HSESF_TBL(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating HSESF_TBL in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating HSESF_TBL in urban_param_init') endif numdir_tbl = 0 street_direction_tbl = -1.E36 @@ -1653,12 +1659,12 @@ SUBROUTINE urban_param_init(DZR,DZB,DZG,num_soil_layers, & read(string(indx+1:),*) sigma_zed_tbl(1:icate) else if (name == "ROOF_WIDTH") then ALLOCATE( ROOF_WIDTH(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ROOF_WIDTH in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ROOF_WIDTH in urban_param_init') read(string(indx+1:),*) roof_width(1:icate) else if (name == "ROAD_WIDTH") then ALLOCATE( ROAD_WIDTH(ICATE), stat=allocate_status ) -! if(allocate_status /= 0) CALL wrf_error_fatal('Error allocating ROAD_WIDTH in urban_param_init') + if(allocate_status /= 0) FATAL_ERROR('Error allocating ROAD_WIDTH in urban_param_init') read(string(indx+1:),*) road_width(1:icate) else if (name == "AH") then read(string(indx+1:),*) ah_tbl(1:icate) @@ -1771,7 +1777,7 @@ SUBROUTINE urban_param_init(DZR,DZB,DZG,num_soil_layers, & if ( pctsum /= 100.) then write (*,'(//,"Building height percentages for category ", I2, " must sum to 100.0")') k write (*,'("Currently, they sum to ", F6.2,/)') pctsum -! CALL wrf_error_fatal('pctsum is not equal to 100.') + FATAL_ERROR('pctsum is not equal to 100.') endif else if ( name == "Z0R") then read(string(indx+1:),*) Z0R_tbl(1:icate) @@ -1803,7 +1809,7 @@ SUBROUTINE urban_param_init(DZR,DZB,DZG,num_soil_layers, & read(string(indx+1:),*) hsesf_tbl(1:icate) !end BEP else -! CALL wrf_error_fatal('URBPARM.TBL: Unrecognized NAME = "'//trim(name)//'" in Subr URBAN_PARAM_INIT') + FATAL_ERROR('URBPARM.TBL: Unrecognized NAME = "'//trim(name)//'" in Subr URBAN_PARAM_INIT') endif enddo READLOOP diff --git a/src/core_atmosphere/utils/Makefile b/src/core_atmosphere/utils/Makefile index 885a4e23ce..03034c7418 100644 --- a/src/core_atmosphere/utils/Makefile +++ b/src/core_atmosphere/utils/Makefile @@ -4,7 +4,7 @@ all: build_tables mv build_tables ../../.. build_tables: build_tables.o atmphys_build_tables_thompson.o - $(LINKER) $(LDFLAGS) -o build_tables build_tables.o atmphys_build_tables_thompson.o -L../../framework -L../physics -lphys -lframework + $(LINKER) $(LDFLAGS) -o build_tables build_tables.o atmphys_build_tables_thompson.o -L../../framework -L../physics -lphys -lframework $(LIBS) -L../../external/esmf_time_f90 -lesmf_time build_tables.o: \ diff --git a/src/core_init_atmosphere/Registry.xml b/src/core_init_atmosphere/Registry.xml index fbbe4c8569..f40b22459a 100644 --- a/src/core_init_atmosphere/Registry.xml +++ b/src/core_init_atmosphere/Registry.xml @@ -1,5 +1,5 @@ - + @@ -79,7 +79,7 @@ - @@ -133,6 +133,11 @@ description="The land use classification to use (case 7 only)" possible_values="`USGS' or `MODIFIED\_IGBP\_MODIS\_NOAH'"/> + + - @@ -379,6 +383,7 @@ packages="initial_conds" immutable="true"> + @@ -426,7 +431,6 @@ - @@ -470,26 +474,12 @@ - - - - - - - - - - - - - - @@ -503,16 +493,7 @@ - - - - - - - - - @@ -599,7 +580,6 @@ - @@ -653,6 +633,8 @@ + diff --git a/src/core_init_atmosphere/mpas_init_atm_cases.F b/src/core_init_atmosphere/mpas_init_atm_cases.F index 01ba712d0a..25b2b9719d 100644 --- a/src/core_init_atmosphere/mpas_init_atm_cases.F +++ b/src/core_init_atmosphere/mpas_init_atm_cases.F @@ -4642,8 +4642,12 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state ! es = svp1*10.*exp(svp2*(t(k,iCell)-svpt0)/(t(k,iCell)-svp3)) ! es = min(es,0.99*0.01*pressure(k,iCell)) ! rs = 0.622*es*100. / (pressure(k,iCell)-es*100.) + + ! + ! Note: the RH field provided by ungrib should always be with respect to liquid water, + ! hence, we can always call rslf; see the routine fix_gfs_rh in WPS/ungrib/src/rrpr.F . + ! rs = rslf(pressure(k,iCell),t(k,iCell)) - if(t(k,iCell) .lt. svpt0) rs = rsif(pressure(k,iCell),t(k,iCell)) scalars(index_qv,k,iCell) = 0.01_RKIND*rs*relhum(k,iCell) enddo enddo @@ -4777,35 +4781,42 @@ subroutine init_atm_case_gfs(block, mesh, nCells, nEdges, nVertLevels, fg, state end do - rw= 0.0 - w = 0.0 - do iEdge = 1,nEdges + rw(:,:) = 0.0 - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) + do iCell=1,nCellsSolve - if (cell1 <= nCellsSolve .or. cell2 <= nCellsSolve ) then - do k = 2, nVertLevels - flux = (fzm(k)*ru(k,iEdge)+fzp(k)*ru(k-1,iEdge)) - rw(k,cell2) = rw(k,cell2) + (fzm(k)*zz(k,cell2)+fzp(k)*zz(k-1,cell2))*zb(k,2,iEdge)*flux - rw(k,cell1) = rw(k,cell1) - (fzm(k)*zz(k,cell1)+fzp(k)*zz(k-1,cell1))*zb(k,1,iEdge)*flux + do i=1,nEdgesOnCell(iCell) + iEdge=edgesOnCell(i,iCell) - if (config_theta_adv_order ==3) then - rw(k,cell2) = rw(k,cell2) & - - sign(1.0_RKIND,ru(k,iEdge))*config_coef_3rd_order* & - (fzm(k)*zz(k,cell2)+fzp(k)*zz(k-1,cell2))*zb3(k,2,iEdge)*flux - rw(k,cell1) = rw(k,cell1) & - + sign(1.0_RKIND,ru(k,iEdge))*config_coef_3rd_order* & - (fzm(k)*zz(k,cell1)+fzp(k)*zz(k-1,cell1))*zb3(k,1,iEdge)*flux - end if + do k = 2, nVertLevels + flux = (fzm(k)*ru(k,iEdge)+fzp(k)*ru(k-1,iEdge)) + if (iCell == cellsOnEdge(1,iEdge)) then + rw(k,iCell) = rw(k,iCell) - (fzm(k)*zz(k,iCell)+fzp(k)*zz(k-1,iCell))*zb(k,1,iEdge)*flux + else + rw(k,iCell) = rw(k,iCell) + (fzm(k)*zz(k,iCell)+fzp(k)*zz(k-1,iCell))*zb(k,2,iEdge)*flux + end if + + if (config_theta_adv_order ==3) then + if (iCell == cellsOnEdge(1,iEdge)) then + rw(k,iCell) = rw(k,iCell) & + + sign(1.0_RKIND,ru(k,iEdge))*config_coef_3rd_order* & + (fzm(k)*zz(k,iCell)+fzp(k)*zz(k-1,iCell))*zb3(k,1,iEdge)*flux + else + rw(k,iCell) = rw(k,iCell) & + - sign(1.0_RKIND,ru(k,iEdge))*config_coef_3rd_order* & + (fzm(k)*zz(k,iCell)+fzp(k)*zz(k-1,iCell))*zb3(k,2,iEdge)*flux + end if + end if + + end do end do - end if end do + ! Compute w from rho_zz and rw - do iCell=1,nCells + do iCell=1,nCellsSolve do k=2,nVertLevels w(k,iCell) = rw(k,iCell) / (fzp(k) * rho_zz(k-1,iCell) + fzm(k) * rho_zz(k,iCell)) end do diff --git a/src/core_init_atmosphere/mpas_init_atm_core.F b/src/core_init_atmosphere/mpas_init_atm_core.F index 48f07f5483..ee0d52ed76 100644 --- a/src/core_init_atmosphere/mpas_init_atm_core.F +++ b/src/core_init_atmosphere/mpas_init_atm_core.F @@ -26,6 +26,7 @@ function init_atm_core_init(domain, startTimeStamp) result(ierr) type (block_type), pointer :: block type (mpas_pool_type), pointer :: state, mesh character (len=StrKIND), pointer :: xtime + character (len=StrKIND), pointer :: initial_time character (len=StrKIND), pointer :: config_start_time real (kind=RKIND), pointer :: sphere_radius integer :: ierr @@ -38,11 +39,13 @@ function init_atm_core_init(domain, startTimeStamp) result(ierr) call mpas_pool_get_subpool(block % structs, 'state', state) call mpas_pool_get_subpool(block % structs, 'mesh', mesh) call mpas_pool_get_array(state, 'xtime', xtime) + call mpas_pool_get_array(state, 'initial_time', initial_time) call mpas_pool_get_config(mesh, 'sphere_radius', sphere_radius) call mpas_pool_get_config(block % configs, 'config_start_time', config_start_time) startTimeStamp = config_start_time xtime = config_start_time + initial_time = config_start_time domain % sphere_radius = a ! Appears in output files sphere_radius = a ! Used in setting up test cases diff --git a/src/core_init_atmosphere/mpas_init_atm_gwd.F b/src/core_init_atmosphere/mpas_init_atm_gwd.F index e6f51ee709..3bc260c3c2 100644 --- a/src/core_init_atmosphere/mpas_init_atm_gwd.F +++ b/src/core_init_atmosphere/mpas_init_atm_gwd.F @@ -16,6 +16,8 @@ module mpas_init_atm_gwd private + integer, parameter :: I1KIND = selected_int_kind(2) + real (kind=RKIND), parameter :: Re = 6371229.0_RKIND ! Earth radius in MPAS-Atmosphere real (kind=RKIND), parameter :: Pi = 2.0_RKIND * asin(1.0_RKIND) real (kind=RKIND), parameter :: rad2deg = 180.0_RKIND / Pi @@ -23,8 +25,11 @@ module mpas_init_atm_gwd integer, parameter :: topo_x = 43200 ! x-dimension of global 30-arc-second topography array integer, parameter :: topo_y = 21600 ! y-dimension of global 30-arc-second topography array real (kind=RKIND), parameter :: pts_per_degree = real(topo_x,RKIND) / 360.0_RKIND - real (kind=RKIND), parameter :: start_lat = -90.0_RKIND - real (kind=RKIND), parameter :: start_lon = -180.0_RKIND + + ! The following are set at the beginning of the compute_gwd_fields routine depending + ! on the source of topography data to be used + real (kind=RKIND) :: start_lat + real (kind=RKIND) :: start_lon ! Nominal delta-x (in meters) for sub-grid topography cells real (kind=RKIND), parameter :: sg_delta = 2.0 * Pi * Re / (360.0_RKIND * real(pts_per_degree,RKIND)) @@ -34,7 +39,15 @@ module mpas_init_atm_gwd real (kind=RKIND), dimension(:,:), pointer :: dxm ! Size (meters) in zonal direction of a grid cell real (kind=RKIND) :: box_mean ! Mean value of topography in box integer :: nx, ny ! Dimensions of box covering grid cell + integer (kind=I1KIND), dimension(:,:), pointer :: landuse ! Global 30-arc-second landuse + integer (kind=I1KIND), dimension(:,:), pointer :: box_landuse ! Subset of landuse covering a grid cell + ! NB: At present, only the USGS GLCC land cover dataset is supported, so we can assume 16 == water + ! See the read_global_30s_landuse function + integer (kind=I1KIND), parameter :: WATER = 16 + + integer (kind=I1KIND), dimension(:), pointer :: hlanduse ! Dominant land mask (0 or 1) + real (kind=RKIND) :: hc ! critical height contains @@ -81,17 +94,51 @@ function compute_gwd_fields(domain) result(iErr) real (kind=RKIND), dimension(:), pointer :: var2d, con, oa1, oa2, oa3, oa4, ol1, ol2, ol3, ol4 real (kind=RKIND), dimension(:), pointer :: elvmax, htheta, hgamma, hsigma character(len=StrKIND), pointer :: config_geog_data_path + character(len=StrKIND), pointer :: config_topo_data + character(len=StrKIND) :: geog_sub_path + + ! Variables for smoothing variance + integer, dimension(:,:), pointer:: cellsOnCell + integer (kind=I1KIND) :: sum_landuse + real (kind=RKIND) :: sum_var allocate(topo(topo_x,topo_y)) + allocate(landuse(topo_x,topo_y)) call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', mesh) call mpas_pool_get_subpool(domain % blocklist % structs, 'state', state) call mpas_pool_get_config(mesh, 'sphere_radius', sphere_radius) call mpas_pool_get_config(domain % configs, 'config_geog_data_path', config_geog_data_path) + call mpas_pool_get_config(domain % configs, 'config_topo_data', config_topo_data) call mpas_pool_get_config(domain % configs, 'config_gwd_cell_scaling', config_gwd_cell_scaling) + select case(trim(config_topo_data)) + case('GTOPO30') + call mpas_log_write('--- Using GTOPO30 terrain dataset for GWDO static fields') + geog_sub_path = 'topo_30s/' + start_lat = -90.0_RKIND + start_lon = -180.0_RKIND + case('GMTED2010') + call mpas_log_write('--- Using GMTED2010 terrain dataset for GWDO static fields') + geog_sub_path = 'topo_gmted2010_30s/' + + ! NB: the GMTED2010 data on disk actually has start_lon = 0.0, but the read_global_30s_topo() + ! routine will shift the dataset when writing to the topo array so that the start_lon seen + ! by the rest of this code is -180.0. + start_lat = -90.0_RKIND + start_lon = -180.0_RKIND + case('default') + call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) + call mpas_log_write('Invalid topography dataset '''//trim(config_topo_data) & + //''' selected for config_topo_data', messageType=MPAS_LOG_ERR) + call mpas_log_write(' Possible options are: ''GTOPO30'', ''GMTED2010''', messageType=MPAS_LOG_ERR) + call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) + call mpas_log_write('Please correct the namelist.', messageType=MPAS_LOG_CRIT) + end select + call mpas_log_write('') + ! ! Retrieve pointers to arrays holding the latitudes and longitudes of ! cells, and arrays that will hold the computed GWDO statistics @@ -102,6 +149,7 @@ function compute_gwd_fields(domain) result(iErr) call mpas_pool_get_array(mesh, 'lonCell', lonCell) call mpas_pool_get_array(mesh, 'nEdgesOnCell', nEdgesOnCell) call mpas_pool_get_array(mesh, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(mesh, 'cellsOnCell', cellsOnCell) call mpas_pool_get_array(mesh, 'dcEdge', dcEdge) call mpas_pool_get_array(mesh, 'var2d', var2d) call mpas_pool_get_array(mesh, 'con', con) @@ -118,12 +166,20 @@ function compute_gwd_fields(domain) result(iErr) ! call mpas_pool_get_array(mesh, 'gamma', hgamma) ! call mpas_pool_get_array(mesh, 'sigma', hsigma) - iErr = read_global_30s_topo(config_geog_data_path) + allocate(hlanduse(nCells+1)) ! +1, since we access hlanduse(cellsOnCell(i,iCell)) later on for iCell=1,nCells + + iErr = read_global_30s_topo(config_geog_data_path, geog_sub_path) if (iErr /= 0) then call mpas_log_write('Error reading global 30-arc-sec topography for GWD statistics', messageType=MPAS_LOG_ERR) return end if + iErr = read_global_30s_landuse(config_geog_data_path) + if (iErr /= 0) then + call mpas_log_write('Error reading global 30-arc-sec landuse for GWD statistics', messageType=MPAS_LOG_ERR) + return + end if + ! ! It is possible that this code is called before the mesh fields have been scaled ! up to "Earth-sized". Because we need "Earth" distances to cut out bounding @@ -185,17 +241,48 @@ function compute_gwd_fields(domain) result(iErr) oa2(iCell) = get_oa2() oa3(iCell) = get_oa3() oa4(iCell) = get_oa4() + + ! Critical height, to be used in OL computation + ! See Appendix of Kim, Y-J, 1996: Representation of Sub-Grid Scale Orographic Effects + ! in a General Circulation Model. J. Climate, 9, 2698-2717. + hc = 1116.2_RKIND - 0.878_RKIND * var2d(iCell) + ol1(iCell) = get_ol1() ol2(iCell) = get_ol2() ol3(iCell) = get_ol3() ol4(iCell) = get_ol4() + + hlanduse(iCell) = get_dom_landmask() ! get dominant land mask in cell + ! elvmax(iCell) = get_elvmax() ! htheta(iCell) = get_htheta() ! hgamma(iCell) = get_hgamma() ! hsigma(iCell) = get_hsigma() end do + + ! Smooth variance at isolated points + do iCell = 1,nCells + sum_landuse = 0_I1KIND + sum_var = 0.0_RKIND + do i=1,nEdgesOnCell(iCell) + sum_landuse = sum_landuse + hlanduse(cellsOnCell(i,iCell)) + sum_var = sum_var + var2d(cellsOnCell(i,iCell)) + end do + + if (sum_landuse == int(nEdgesOnCell(iCell),kind=I1KIND) .and. hlanduse(iCell) == 0_I1KIND) then + call mpas_log_write('smoothing out a water point, iCell = $i', intArgs=(/iCell/)) + var2d(iCell) = sum_var / real(nEdgesOnCell(iCell),kind=RKIND) + else if (sum_landuse == 0_I1KIND .and. hlanduse(iCell) == 1_I1KIND) then + call mpas_log_write('smoothing out a land point, iCell = $i', intArgs=(/iCell/)) + var2d(iCell) = sum_var / real(nEdgesOnCell(iCell),kind=RKIND) + end if + end do + + deallocate(topo) + deallocate(landuse) + deallocate(hlanduse) iErr = 0 @@ -208,17 +295,18 @@ end function compute_gwd_fields ! !> \brief Reads global 30-arc-second topography into 'topo' module variable !> \author Michael Duda - !> \date 31 October 2016 + !> \date 28 August 2017 !> \details !> This subroutine reads the global 30-arc-second topography from the subdirectory - !> 'topo_30s' of the path provided as an argument. + !> identified by the 'sub_path' argument within the 'path' provided as the first argument. ! !----------------------------------------------------------------------- - function read_global_30s_topo(path) result(iErr) + function read_global_30s_topo(path, sub_path) result(iErr) implicit none character(len=*), intent(in) :: path + character(len=*), intent(in) :: sub_path integer :: iErr @@ -227,7 +315,7 @@ function read_global_30s_topo(path) result(iErr) integer, parameter :: tile_bdr = 3 ! number of layers of border/halo points surrounding each tile integer :: istatus - integer :: ix, iy + integer :: ix, iy, ishift, ix_shift integer :: isigned, endian, wordsize, nx, ny, nz real (kind=R4KIND) :: scalefactor real (kind=R4KIND), dimension(:,:,:), allocatable :: tile @@ -243,9 +331,19 @@ function read_global_30s_topo(path) result(iErr) ny = tile_y + 2*tile_bdr nz = 1 + ishift = 0 + + ! + ! For GMTED2010 data, the dataset starts at 0.0 longitude, but we need to shift the starting location + ! in the topo array to -180.0, so we introduce an offset in the x-coordinate of topo_x/2 + ! + if (trim(sub_path) == 'topo_gmted2010_30s/') then + ishift = topo_x / 2 + end if + do iy=1,topo_y,tile_y do ix=1,topo_x,tile_x - write(filename,'(a,i5.5,a1,i5.5,a1,i5.5,a1,i5.5)') trim(path)//'/topo_30s/', ix, '-', (ix+tile_x-1), '.', & + write(filename,'(a,i5.5,a1,i5.5,a1,i5.5,a1,i5.5)') trim(path)//trim(sub_path), ix, '-', (ix+tile_x-1), '.', & iy, '-', (iy+tile_y-1) call read_geogrid(filename, len_trim(filename), tile, nx, ny, nz, isigned, endian, & scalefactor, wordsize, istatus) @@ -255,7 +353,8 @@ function read_global_30s_topo(path) result(iErr) return end if - topo(ix:(ix+tile_x-1),iy:(iy+tile_y-1)) = tile((tile_bdr+1):(tile_x+tile_bdr),(tile_bdr+1):(tile_y+tile_bdr),1) + ix_shift = mod((ix-1) + ishift, topo_x) + 1 + topo(ix_shift:(ix_shift+tile_x-1),iy:(iy+tile_y-1)) = tile((tile_bdr+1):(tile_x+tile_bdr),(tile_bdr+1):(tile_y+tile_bdr),1) end do end do @@ -267,6 +366,107 @@ function read_global_30s_topo(path) result(iErr) end function read_global_30s_topo + !*********************************************************************** + ! + ! function read_global_30s_landuse + ! + !> \brief Reads global 30-arc-second landuse into 'landuse' module variable + !> \author Michael Duda + !> \date 14 March 2017 + !> \details + !> This subroutine reads the global 30-arc-second USGS landuse from + !> the subdirectory 'landuse_30s' of the path provided as an argument. + ! + !----------------------------------------------------------------------- + function read_global_30s_landuse(path) result(iErr) + + implicit none + + character(len=*), intent(in) :: path + + integer :: iErr + + integer, parameter :: tile_x = 1200 ! x-dimension of each tile of global 30-arc-second landuse + integer, parameter :: tile_y = 1200 ! y-dimension of each tile of global 30-arc-second landuse + + integer :: istatus + integer :: ix, iy + integer :: isigned, endian, wordsize, nx, ny, nz + real (kind=R4KIND) :: scalefactor + real (kind=R4KIND), dimension(:,:,:), allocatable :: tile + character(len=StrKIND) :: filename + + allocate(tile(tile_x,tile_y,1)) + + isigned = 1 + endian = 0 + wordsize = 1 + scalefactor = 1.0 + nx = tile_x + ny = tile_y + nz = 1 + + do iy=1,topo_y,tile_y + do ix=1,topo_x,tile_x + write(filename,'(a,i5.5,a1,i5.5,a1,i5.5,a1,i5.5)') trim(path)//'/landuse_30s/', ix, '-', (ix+tile_x-1), '.', & + iy, '-', (iy+tile_y-1) + call read_geogrid(filename, len_trim(filename), tile, nx, ny, nz, isigned, endian, & + scalefactor, wordsize, istatus) + if (istatus /= 0) then + call mpas_log_write('Error reading landuse tile '//trim(filename)) + iErr = 1 + return + end if + + landuse(ix:(ix+tile_x-1),iy:(iy+tile_y-1)) = int(tile(1:tile_x,1:tile_y,1), kind=I1KIND) + + end do + end do + + deallocate(tile) + + iErr = 0 + + end function read_global_30s_landuse + + + !*********************************************************************** + ! + ! function get_dom_landmask + ! + !> \brief Returns the dominant land mask in a cell + !> \author May Wong + !> \date + !> \details 1 = land, 0 = water + ! + !----------------------------------------------------------------------- + integer (kind=I1KIND) function get_dom_landmask( ) + + implicit none + + integer :: i, j + real (kind=RKIND) :: xland + xland = 0.0_RKIND + + ! Get dominant land/water mask in the box + do j=1,ny + do i=1,nx + if (box_landuse(i,j) /= WATER) then + xland = xland + 1.0_RKIND + end if + end do + end do + xland = xland / real(nx*ny,kind=RKIND) + + if (xland >= 0.5_RKIND) then + get_dom_landmask = 1_I1KIND + else + get_dom_landmask = 0_I1KIND + end if + + end function get_dom_landmask + + !*********************************************************************** ! ! subroutine get_box @@ -296,6 +496,7 @@ subroutine get_box(lat, lon, dx) integer :: i, j, ii, jj, ic, jc real (kind=RKIND) :: sg_lat + ! ! Get number of points to extract in the zonal direction ! @@ -323,6 +524,9 @@ subroutine get_box(lat, lon, dx) if (associated(box)) deallocate(box) allocate(box(nx,ny)) + if (associated(box_landuse)) deallocate(box_landuse) + allocate(box_landuse(nx,ny)) + if (associated(dxm)) deallocate(dxm) allocate(dxm(nx,ny)) @@ -353,6 +557,7 @@ subroutine get_box(lat, lon, dx) end do box(i,j) = topo(ii,jj) + box_landuse(i,j) = landuse(ii,jj) sg_lat = (start_lat + (real(jj-1,RKIND) + 0.5) / pts_per_degree) / rad2deg ! Add 0.5 for cell center dxm(i,j) = sg_delta * cos(sg_lat) box_mean = box_mean + box(i,j) @@ -360,6 +565,7 @@ subroutine get_box(lat, lon, dx) end do end do + ! ! Compute mean topography in the extracted box ! @@ -402,7 +608,7 @@ end function get_var ! ! function get_con ! - !> \brief Computes orographic convexityof sub-grid-scale terrain + !> \brief Computes orographic convexity of sub-grid-scale terrain !> \author Michael Duda !> \date 29 May 2015 !> \details @@ -413,26 +619,63 @@ real (kind=RKIND) function get_con() implicit none integer :: i, j - real (kind=RKIND) :: s2, s4, var + real (kind=RKIND) :: s2, s4, var, xland, mean_land, mean_water, oro s2 = 0.0 s4 = 0.0 + mean_land = 0.0 + mean_water = 0.0 + xland = 0.0 + + ! + ! Compute grid-box mean + ! + do j=1,ny + do i=1,nx + if (box_landuse(i,j) /= WATER) then + xland = xland + 1.0 + mean_land = mean_land + box(i,j) + else + mean_water = mean_water + box(i,j) + end if + end do + end do + if (xland > 0.0) then + mean_land = mean_land / xland + end if + if (xland < real(nx*ny,kind=RKIND)) then + mean_water = mean_water / (real(nx*ny,kind=RKIND) - xland) + end if + xland = xland / real(nx*ny,kind=RKIND) + + if (xland >= 0.5_RKIND) then + oro = mean_land + else + oro = mean_water + end if do j=1,ny do i=1,nx s2 = s2 + (box(i,j) - box_mean)**2 - s4 = s4 + (box(i,j) - box_mean)**4 + s4 = s4 + (box(i,j) - oro)**4 end do end do var = s2 / real(nx*ny,RKIND) - - if (abs(var) < 1.0e-5) then + + if (sqrt(var) < 1.0) then get_con = 0.0 else get_con = s4 / (var**2 * real(nx*ny,RKIND)) end if + ! + ! Zero-ing all convexity statistics over dominantly water points. + ! + if (xland < 0.5_RKIND) then + get_con = 0.0 + end if + end function get_con @@ -623,7 +866,7 @@ real (kind=RKIND) function get_ol1() do j=ny/4,3*ny/4 do i=1,nx - if (box(i,j) > box_mean) nw = nw + 1 + if (box(i,j) > hc) nw = nw + 1 nt = nt + 1 end do end do @@ -656,7 +899,7 @@ real (kind=RKIND) function get_ol2() do j=1,ny do i=nx/4,3*nx/4 - if (box(i,j) > box_mean) nw = nw + 1 + if (box(i,j) > hc) nw = nw + 1 nt = nt + 1 end do end do @@ -689,13 +932,13 @@ real (kind=RKIND) function get_ol3() do j=1,ny/2 do i=1,nx/2 - if (box(i,j) > box_mean) nw = nw + 1 + if (box(i,j) > hc) nw = nw + 1 nt = nt + 1 end do end do do j=ny/2+1,ny do i=nx/2+1,nx - if (box(i,j) > box_mean) nw = nw + 1 + if (box(i,j) > hc) nw = nw + 1 nt = nt + 1 end do end do @@ -728,13 +971,13 @@ real (kind=RKIND) function get_ol4() do j=ny/2+1,ny do i=1,nx/2 - if (box(i,j) > box_mean) nw = nw + 1 + if (box(i,j) > hc) nw = nw + 1 nt = nt + 1 end do end do do j=1,ny/2 do i=nx/2+1,nx - if (box(i,j) > box_mean) nw = nw + 1 + if (box(i,j) > hc) nw = nw + 1 nt = nt + 1 end do end do diff --git a/src/core_init_atmosphere/mpas_init_atm_static.F b/src/core_init_atmosphere/mpas_init_atm_static.F index e0e396b8e6..253d681f94 100644 --- a/src/core_init_atmosphere/mpas_init_atm_static.F +++ b/src/core_init_atmosphere/mpas_init_atm_static.F @@ -43,6 +43,7 @@ subroutine init_atm_static(mesh, dims, configs) character(len=StrKIND) :: fname character(len=StrKIND), pointer :: config_geog_data_path character(len=StrKIND), pointer :: config_landuse_data + character(len=StrKIND), pointer :: config_topo_data character(len=StrKIND+1) :: geog_data_path ! same as config_geog_data_path, but guaranteed to have a trailing slash character(len=StrKIND+1) :: geog_sub_path ! subdirectory names in config_geog_data_path, with trailing slash @@ -59,6 +60,9 @@ subroutine init_atm_static(mesh, dims, configs) real(kind=4):: scalefactor real(kind=4),dimension(:,:,:),allocatable:: rarray + real(kind=RKIND):: start_lat + real(kind=RKIND):: start_lon + real(kind=RKIND):: lat,lon,x,y real(kind=RKIND):: lat_pt,lon_pt real(kind=RKIND),dimension(:,:),allocatable :: soiltemp_1deg @@ -90,7 +94,6 @@ subroutine init_atm_static(mesh, dims, configs) real (kind=RKIND), dimension(:,:), pointer :: albedo12m integer, dimension(:), pointer :: lu_index integer, dimension(:), pointer :: soilcat_top - integer, dimension(:), pointer :: soilcat_bot integer, dimension(:), pointer :: landmask character(len=StrKIND), pointer :: mminlu @@ -102,6 +105,7 @@ subroutine init_atm_static(mesh, dims, configs) call mpas_pool_get_config(configs, 'config_geog_data_path', config_geog_data_path) call mpas_pool_get_config(configs, 'config_landuse_data', config_landuse_data) + call mpas_pool_get_config(configs, 'config_topo_data', config_topo_data) write(geog_data_path, '(a)') config_geog_data_path i = len_trim(geog_data_path) @@ -144,7 +148,6 @@ subroutine init_atm_static(mesh, dims, configs) call mpas_pool_get_array(mesh, 'lu_index', lu_index) call mpas_pool_get_array(mesh, 'mminlu', mminlu) call mpas_pool_get_array(mesh, 'soilcat_top', soilcat_top) - call mpas_pool_get_array(mesh, 'soilcat_bot', soilcat_bot) call mpas_pool_get_array(mesh, 'landmask', landmask) call mpas_pool_get_array(mesh, 'soiltemp', soiltemp) call mpas_pool_get_array(mesh, 'snoalb', snoalb) @@ -236,13 +239,32 @@ subroutine init_atm_static(mesh, dims, configs) nhs(:) = 0 ter(:) = 0.0 + start_lat = -89.99583 + select case(trim(config_topo_data)) + case('GTOPO30') + call mpas_log_write('Using GTOPO30 terrain dataset') + geog_sub_path = 'topo_30s/' + start_lon = -179.99583 + case('GMTED2010') + call mpas_log_write('Using GMTED2010 terrain dataset') + geog_sub_path = 'topo_gmted2010_30s/' + start_lon = 0.004166667 + case('default') + call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) + call mpas_log_write('Invalid topography dataset '''//trim(config_topo_data) & + //''' selected for config_topo_data', messageType=MPAS_LOG_ERR) + call mpas_log_write(' Possible options are: ''GTOPO30'', ''GMTED2010''', messageType=MPAS_LOG_ERR) + call mpas_log_write('*****************************************************************', messageType=MPAS_LOG_ERR) + call mpas_log_write('Please correct the namelist.', messageType=MPAS_LOG_CRIT) + end select + do jTileStart = 1,20401,ny-6 jTileEnd = jTileStart + ny - 1 - 6 do iTileStart=1,42001,nx-6 iTileEnd = iTileStart + nx - 1 - 6 - write(fname,'(a,i5.5,a1,i5.5,a1,i5.5,a1,i5.5)') trim(geog_data_path)// & - 'topo_30s/',iTileStart,'-',iTileEnd,'.',jTileStart,'-',jTileEnd + write(fname,'(a,i5.5,a1,i5.5,a1,i5.5,a1,i5.5)') trim(geog_data_path)//trim(geog_sub_path), & + iTileStart,'-',iTileEnd,'.',jTileStart,'-',jTileEnd call mpas_log_write(trim(fname)) call read_geogrid(fname,len_trim(fname),rarray,nx,ny,nz,isigned,endian, & @@ -252,8 +274,8 @@ subroutine init_atm_static(mesh, dims, configs) iPoint = 1 do j=4,ny-3 do i=4,nx-3 - lat_pt = -89.99583 + (jTileStart + j - 5) * 0.0083333333 - lon_pt = -179.99583 + (iTileStart + i - 5) * 0.0083333333 + lat_pt = start_lat + (jTileStart + j - 5) * 0.0083333333 + lon_pt = start_lon + (iTileStart + i - 5) * 0.0083333333 lat_pt = lat_pt * PI / 180.0 lon_pt = lon_pt * PI / 180.0 @@ -412,78 +434,17 @@ subroutine init_atm_static(mesh, dims, configs) call mpas_log_write('--- end interpolate SOILCAT_TOP') -! -! Interpolate SOILCAT_BOT -! - nx = 1200 - ny = 1200 - nz = 1 - isigned = 1 - endian = 0 - wordsize = 1 - scalefactor = 1.0 - allocate(rarray(nx,ny,nz)) - allocate(ncat(16,nCells)) - ncat(:,:) = 0 - soilcat_bot(:) = 0.0 - - do jTileStart = 1,20401,ny - jTileEnd = jTileStart + ny - 1 - - do iTileStart = 1,42001,nx - iTileEnd = iTileStart + nx - 1 - write(fname,'(a,i5.5,a1,i5.5,a1,i5.5,a1,i5.5)') trim(geog_data_path)// & - 'soiltype_bot_30s/',iTileStart,'-',iTileEnd,'.',jTileStart,'-',jTileEnd - call mpas_log_write(trim(fname)) - - call read_geogrid(fname,len_trim(fname),rarray,nx,ny,nz,isigned,endian, & - scalefactor,wordsize,istatus) - call init_atm_check_read_error(istatus, fname) - - iPoint = 1 - do j=1,ny - do i=1,nx - lat_pt = -89.99583 + (jTileStart + j - 2) * 0.0083333333 - lon_pt = -179.99583 + (iTileStart + i - 2) * 0.0083333333 - lat_pt = lat_pt * PI / 180.0 - lon_pt = lon_pt * PI / 180.0 - - iPoint = nearest_cell(lat_pt,lon_pt,iPoint,nCells,maxEdges, & - nEdgesOnCell,cellsOnCell, & - latCell,lonCell) - ncat(int(rarray(i,j,1)),iPoint) = ncat(int(rarray(i,j,1)),iPoint) + 1 - end do - end do - - end do - end do - - do iCell =1,nCells - soilcat_bot(iCell) = 1 - do i = 2,16 - if(ncat(i,iCell) > ncat(soilcat_bot(iCell),iCell)) then - soilcat_bot(iCell) = i - end if - end do - end do - deallocate(rarray) - deallocate(ncat) - call mpas_log_write('--- end interpolate SOILCAT_BOT') - - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! KLUDGE TO FIX SOIL TYPE OVER ANTARCTICA !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! where (lu_index == isice_lu) soilcat_top = 16 - where (lu_index == isice_lu) soilcat_bot = 16 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! CORRECT INCONSISTENT SOIL AND LAND USE DATA !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! do iCell = 1,nCells if (lu_index(iCell) == iswater_lu .or. & - soilcat_top(iCell) == 14 .or. & - soilcat_bot(iCell) == 14) then + soilcat_top(iCell) == 14) then if (lu_index(iCell) /= iswater_lu) then call mpas_log_write('Turning lu_index into water at $i', intArgs=(/iCell/)) lu_index(iCell) = iswater_lu @@ -492,10 +453,6 @@ subroutine init_atm_static(mesh, dims, configs) call mpas_log_write('Turning soilcat_top into water at $i', intArgs=(/iCell/)) soilcat_top(iCell) = 14 end if - if (soilcat_bot(iCell) /= 14) then - call mpas_log_write('Turning soilcat_bot into water at $i', intArgs=(/iCell/)) - soilcat_bot(iCell) = 14 - end if end if end do diff --git a/src/core_landice/.gitignore b/src/core_landice/.gitignore new file mode 100644 index 0000000000..b2c8b2f700 --- /dev/null +++ b/src/core_landice/.gitignore @@ -0,0 +1 @@ +*.f90 diff --git a/src/core_landice/Makefile b/src/core_landice/Makefile index c9a963a155..d7dc35bd0a 100644 --- a/src/core_landice/Makefile +++ b/src/core_landice/Makefile @@ -1,29 +1,33 @@ -.SUFFIXES: .F .o -OBJS = mpas_li_core.o \ - mpas_li_core_interface.o \ - mpas_li_time_integration.o \ - mpas_li_time_integration_fe.o \ - mpas_li_diagnostic_vars.o \ - mpas_li_tendency.o \ - mpas_li_setup.o \ - mpas_li_velocity.o \ - mpas_li_sia.o \ - mpas_li_mask.o +.SUFFIXES: .F .o .cpp +.PHONY: mode_forward shared analysis_members + +SHARED_INCLUDES = -I$(PWD)/../framework -I$(PWD)/../external/esmf_time_f90 -I$(PWD)/../operators +SHARED_INCLUDES += -I$(PWD)/shared -I$(PWD)/analysis_members -I$(PWD)/mode_forward all: core_landice -core_landice: $(OBJS) - ar -ru libdycore.a $(OBJS) +shared: + (cd shared; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(SHARED_INCLUDES)") -core_reg: - $(CPP) $(CPPFLAGS) $(CPPINCLUDES) Registry.xml > Registry_processed.xml +analysis_members: shared + (cd analysis_members; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(SHARED_INCLUDES)") + + +mode_forward: shared analysis_members + (cd mode_forward; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(SHARED_INCLUDES)") + +core_landice: mode_forward shared analysis_members + ar -ru libdycore.a `find . -type f -name "*.o"` core_input_gen: if [ ! -e default_inputs ]; then mkdir default_inputs; fi (cd default_inputs; $(NL_GEN) ../Registry_processed.xml namelist.landice ) (cd default_inputs; $(ST_GEN) ../Registry_processed.xml streams.landice stream_list.landice. listed ) +core_reg: + $(CPP) $(CPPFLAGS) $(CPPINCLUDES) Registry.xml > Registry_processed.xml + gen_includes: $(CPP) $(CPPFLAGS) $(CPPINCLUDES) Registry.xml > Registry_processed.xml (if [ ! -d inc ]; then mkdir -p inc; fi) # To generate *.inc files @@ -34,36 +38,6 @@ post_build: cp default_inputs/* $(ROOT_DIR)/default_inputs/. ( cd $(ROOT_DIR)/default_inputs; for FILE in `ls -1`; do if [ ! -e ../$$FILE ]; then cp $$FILE ../.; fi; done ) -mpas_li_core_interface.o: mpas_li_core.o - -mpas_li_core.o: mpas_li_time_integration.o \ - mpas_li_setup.o \ - mpas_li_velocity.o \ - mpas_li_diagnostic_vars.o \ - mpas_li_mask.o - -mpas_li_setup.o: - -mpas_li_time_integration.o: mpas_li_time_integration_fe.o - -mpas_li_time_integration_fe.o: mpas_li_velocity.o \ - mpas_li_tendency.o \ - mpas_li_diagnostic_vars.o \ - mpas_li_setup.o - -mpas_li_tendency.o: mpas_li_setup.o - -mpas_li_diagnostic_vars.o: mpas_li_mask.o \ - mpas_li_velocity.o - -mpas_li_velocity.o: mpas_li_sia.o \ - mpas_li_setup.o - -mpas_li_sia.o: mpas_li_mask.o \ - mpas_li_setup.o - -mpas_li_mask.o: mpas_li_setup.o - clean: $(RM) *.o *.mod *.f90 libdycore.a $(RM) Registry_processed.xml @@ -71,8 +45,6 @@ clean: @# This removes them during the clean process $(RM) *.i $(RM) -r default_inputs - -.F.o: - $(RM) $@ $*.mod - $(CPP) $(CPPFLAGS) $(CPPINCLUDES) $< > $*.f90 - $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) -I../framework -I../operators -I../external/esmf_time_f90 + (cd shared; $(MAKE) clean) + (cd mode_forward; $(MAKE) clean) + (cd analysis_members; $(MAKE) clean) diff --git a/src/core_landice/Registry.xml b/src/core_landice/Registry.xml index edab7c4c36..1cd119bc0b 100644 --- a/src/core_landice/Registry.xml +++ b/src/core_landice/Registry.xml @@ -1,5 +1,5 @@ - + @@ -21,6 +21,9 @@ + @@ -33,9 +36,21 @@ - + + + + @@ -44,20 +59,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -99,18 +242,42 @@ + + + + + + - + /> + + + + - + + + + + + + + + + + + + + + + + @@ -243,9 +451,10 @@ + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + @@ -294,22 +568,40 @@ clobber_mode="replace_files"> - - - + + + + + + + + + - + - - - - + + + + + @@ -318,80 +610,19 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - + + @@ -446,21 +677,36 @@ - + + + + + + @@ -479,62 +725,439 @@ - + + + + - - - - - - - - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#include "Registry_subglacial_hydro.xml" + +#include "analysis_members/Registry_analysis_members.xml" diff --git a/src/core_landice/Registry_subglacial_hydro.xml b/src/core_landice/Registry_subglacial_hydro.xml new file mode 100644 index 0000000000..6df919885b --- /dev/null +++ b/src/core_landice/Registry_subglacial_hydro.xml @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_landice/analysis_members/Makefile b/src/core_landice/analysis_members/Makefile new file mode 100644 index 0000000000..2cead89188 --- /dev/null +++ b/src/core_landice/analysis_members/Makefile @@ -0,0 +1,25 @@ +.SUFFIXES: .F .c .o + +OBJS = mpas_li_analysis_driver.o + +MEMBERS = mpas_li_global_stats.o \ + mpas_li_regional_stats.o + +all: $(OBJS) + +mpas_li_analysis_driver.o: $(MEMBERS) + +clean: + $(RM) *.o *.i *.mod *.f90 + +.F.o: + $(RM) $@ $*.mod +ifeq "$(GEN_F90)" "true" + $(CPP) $(CPPFLAGS) $(CPPINCLUDES) $< > $*.f90 + $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) +else + $(FC) $(CPPFLAGS) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) +endif + +.c.o: + $(CC) $(CPPFLAGS) $(CFLAGS) $(CINCLUDES) -c $< diff --git a/src/core_landice/analysis_members/Registry_TEMPLATE.xml b/src/core_landice/analysis_members/Registry_TEMPLATE.xml new file mode 100644 index 0000000000..7781514365 --- /dev/null +++ b/src/core_landice/analysis_members/Registry_TEMPLATE.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_landice/analysis_members/Registry_analysis_members.xml b/src/core_landice/analysis_members/Registry_analysis_members.xml new file mode 100644 index 0000000000..8b088454a0 --- /dev/null +++ b/src/core_landice/analysis_members/Registry_analysis_members.xml @@ -0,0 +1,2 @@ +#include "Registry_global_stats.xml" +#include "Registry_regional_stats.xml" diff --git a/src/core_landice/analysis_members/Registry_global_stats.xml b/src/core_landice/analysis_members/Registry_global_stats.xml new file mode 100644 index 0000000000..dcd8b131c0 --- /dev/null +++ b/src/core_landice/analysis_members/Registry_global_stats.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_landice/analysis_members/Registry_regional_stats.xml b/src/core_landice/analysis_members/Registry_regional_stats.xml new file mode 100644 index 0000000000..e33203221d --- /dev/null +++ b/src/core_landice/analysis_members/Registry_regional_stats.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_landice/analysis_members/mpas_li_TEMPLATE.F b/src/core_landice/analysis_members/mpas_li_TEMPLATE.F new file mode 100644 index 0000000000..7129d97d2c --- /dev/null +++ b/src/core_landice/analysis_members/mpas_li_TEMPLATE.F @@ -0,0 +1,361 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_TEM_PLATE +! +!> \brief MPAS land ice analysis mode member: TEM_PLATE +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> MPAS land ice analysis mode member: TEM_PLATE +!> In order to add a new analysis member, do the following: +!> 1. Copy these to your new analysis member name: +!> cp mpas_li_TEMPLATE.F mpas_li_your_new_name.F +!> cp Registry_TEMPLATE.xml Registry_your_new_name.xml +!> +!> 2. In those two new files, replace the following text: +!> tempLate, TEM_PLATE, FILL_IN_AUTHOR, FILL_IN_DATE +!> Typically tempLate uses camel case (variable names), like yourNewName, +!> while TEM_PLATE uses underscores (subroutine names), like your_new_name. +!> note: do not replace 'filename_template' in Registry_li_yourNewName.xml +!> +!> 3. Add a #include line for your registry to +!> Registry_analysis_members.xml +!> +!> 4. In mpas_li_analysis_driver.F, add a use statement for your new analysis member. +!> In addition, add lines for your analysis member, and replace TEM_PLATE +!> and temPlate as described in step 2. There should be 5 places that need additions: +!> - Adding the analysis member name to the analysis member list +!> - Adding an init if test can subroutine call +!> - Adding a compute if test can subroutine call +!> - Adding a restart if test can subroutine call +!> - Adding a finalize if test can subroutine call +!> +!> 5. In src/core_landice/analysis_members/Makefile, add your +!> new analysis member to the list of members. See another analysis member +!> in that file for an example. +!> NOTE: If your analysis member depends on other files, add a dependency +!> line for the member and list them there. See okubo weiss for an example. +!> +!----------------------------------------------------------------------- + +module li_TEM_PLATE + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use li_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_init_TEM_PLATE, & + li_compute_TEM_PLATE, & + li_restart_TEM_PLATE, & + li_finalize_TEM_PLATE + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_init_TEM_PLATE +! +!> \brief Initialize MPAS-Land Ice analysis member +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_init_TEM_PLATE(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_init_TEM_PLATE!}}} + +!*********************************************************************** +! +! routine li_compute_TEM_PLATE +! +!> \brief Compute MPAS-Land Ice analysis member +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This routine conducts all computation required for this +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_compute_TEM_PLATE(domain, memberName, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: temPlateAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: temPlateAM + + ! Here are some example variables which may be needed for your analysis member + integer, pointer :: nVertLevels, nCellsSolve, nEdgesSolve, nVerticesSolve, num_tracers + integer :: iTracer, k, iCell + integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop, maxLevelVertexBot + + real (kind=RKIND), dimension(:), pointer :: areaCell, dcEdge, dvEdge + + err = 0 + + dminfo = domain % dminfo + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'temPlateAM', temPlateAMPool) + + ! Here are some example variables which may be needed for your analysis member + call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) + + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(block % dimensions, 'nVerticesSolve', nVerticesSolve) + + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + call mpas_pool_get_array(meshPool, 'maxLevelVertexBot', maxLevelVertexBot) + + ! Computations which are functions of nCells, nEdges, or nVertices + ! must be placed within this block loop + ! Here are some example loops + do iCell = 1,nCellsSolve + do k = 1, maxLevelCell(iCell) + do iTracer = 1, num_tracers + ! computations on tracers(iTracer,k, iCell) + end do + end do + end do + + block => block % next + end do + + ! mpi gather/scatter calls may be placed here. + ! Here are some examples. See mpas_oac_global_stats.F for further details. +! call mpas_dmpar_sum_real_array(dminfo, nVariables, sumSquares(1:nVariables), reductions(1:nVariables)) +! call mpas_dmpar_min_real_array(dminfo, nMins, mins(1:nMins), reductions(1:nMins)) +! call mpas_dmpar_max_real_array(dminfo, nMaxes, maxes(1:nMaxes), reductions(1:nMaxes)) + + ! Even though some variables do not include an index that is decomposed amongst + ! domain partitions, we assign them within a block loop so that all blocks have the + ! correct values for writing output. + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'temPlateAM', temPlateAMPool) + + ! assignment of final temPlateAM variables could occur here. + + block => block % next + end do + + end subroutine li_compute_TEM_PLATE!}}} + +!*********************************************************************** +! +! routine li_restart_TEM_PLATE +! +!> \brief Save restart for MPAS-Land Ice analysis member +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_restart_TEM_PLATE(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_restart_TEM_PLATE!}}} + +!*********************************************************************** +! +! routine li_finalize_TEM_PLATE +! +!> \brief Finalize MPAS-Land Ice analysis member +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_finalize_TEM_PLATE(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_finalize_TEM_PLATE!}}} + +end module li_TEM_PLATE + +! vim: foldmethod=marker diff --git a/src/core_landice/analysis_members/mpas_li_analysis_driver.F b/src/core_landice/analysis_members/mpas_li_analysis_driver.F new file mode 100644 index 0000000000..40a91e5ad2 --- /dev/null +++ b/src/core_landice/analysis_members/mpas_li_analysis_driver.F @@ -0,0 +1,825 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_analysis_driver +! +!> \brief Driver for MPAS Land Ice analysis members +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This is the driver for the MPAS Land Ice members. +! +!----------------------------------------------------------------------- + +module li_analysis_driver + + use mpas_derived_types + use mpas_pool_routines + use mpas_timekeeping + use mpas_timer + use mpas_stream_manager + use mpas_abort + + use li_constants + use li_global_stats + use li_regional_stats + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_analysis_setup_packages, & + li_analysis_init, & + li_analysis_compute_startup, & + li_analysis_compute, & + li_analysis_write, & + li_analysis_restart, & + li_analysis_finalize + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + + character (len=*), parameter :: initTimerPrefix = 'init_' + character (len=*), parameter :: computeTimerPrefix = 'compute_' + character (len=*), parameter :: writeTimerPrefix = 'write_' + character (len=*), parameter :: alarmTimerPrefix = 'reset_alarm_' + character (len=*), parameter :: restartTimerPrefix = 'restart_' + character (len=*), parameter :: finalizeTimerPrefix = 'finalize_' + character (len=*), parameter :: computeAlarmSuffix = 'CMPALRM' + type (mpas_pool_type), pointer :: analysisMemberList + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_analysis_setup_packages +! +!> \brief Setup packages for MPAS-Land Ice analysis driver +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This routine is intended to configure the packages for all +!> Land Ice analysis members. +! +!----------------------------------------------------------------------- + + subroutine li_analysis_setup_packages(configPool, packagePool, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: configPool + type (mpas_pool_type), intent(in) :: packagePool + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: err_tmp + + character (len=StrKIND) :: configName, packageName + logical, pointer :: config_AM_enable + logical, pointer :: AMPackageActive + type (mpas_pool_iterator_type) :: poolItr + integer :: nameLength + + err = 0 + + call mpas_pool_create_pool(analysisMemberList) + call mpas_pool_add_config(analysisMemberList, 'globalStats', 1) + call mpas_pool_add_config(analysisMemberList, 'regionalStats', 1) + + ! DON'T EDIT BELOW HERE + + ! Iterate over all analysis members to setup packages + call mpas_pool_begin_iteration(analysisMemberList) + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + call mpas_pool_get_config(configPool, configName, config_AM_enable) + + if ( config_AM_enable ) then + packageName = poolItr % memberName(1:nameLength) // 'AMPKGActive' + call mpas_pool_get_package(packagePool, packageName, AMPackageActive) + AMPackageActive = .true. + end if + end do + + end subroutine li_analysis_setup_packages!}}} + +!*********************************************************************** +! +! routine li_analysis_init +! +!> \brief Initialize MPAS-Land Ice analysis driver +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This routine calls all initializations required for the +!> MPAS-Land Ice analysis driver. +! +!----------------------------------------------------------------------- + + subroutine li_analysis_init(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: err_tmp + + character (len=StrKIND) :: configName, alarmName, streamName, timerName + logical, pointer :: config_AM_enable + character (len=StrKIND), pointer :: config_AM_compute_interval, config_AM_stream_name + integer :: nameLength + type (mpas_pool_iterator_type) :: poolItr + + logical :: streamFound + character (len=StrKIND) :: referenceTimeString, outputIntervalString + type (MPAS_Time_Type) :: referenceTime + type (MPAS_TimeInterval_type) :: alarmTimeStep + + err = 0 + + call mpas_timer_start('analysis_init', .false.) + + call mpas_pool_begin_iteration(analysisMemberList) + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + call mpas_pool_get_config(domain % configs, configName, config_AM_enable) + + if ( config_AM_enable ) then + timerName = trim(initTimerPrefix) // poolItr % memberName(1:nameLength) + call mpas_timer_start(timerName, .false.) + call li_init_analysis_members(domain, poolItr % memberName, err_tmp) + err = ior(err, err_tmp) + + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_compute_interval' + call mpas_pool_get_config(domain % configs, configName, config_AM_compute_interval) + + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' + call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) + + if ( config_AM_compute_interval == 'dt' ) then + alarmTimeStep = mpas_get_clock_timestep(domain % clock, err_tmp) + call mpas_get_timeInterval(alarmTimeStep, timeString=config_AM_compute_interval, ierr=err_tmp) + end if + + ! Verify stream exists before trying to use output_interval + if ( config_AM_stream_name /= 'none' ) then + streamFound = .false. + + call mpas_stream_mgr_begin_iteration(domain % streamManager) + do while ( mpas_stream_mgr_get_next_stream(domain % streamManager, streamName) ) + if ( trim(streamName) == trim(config_AM_stream_name) ) then + streamFound = .true. + end if + end do + + if ( .not. streamFound ) then + call mpas_dmpar_global_abort('MPAS-landice: ERROR: Stream ' & + // trim(config_AM_stream_name) // ' does not exist. Exiting...') + end if + end if + + + if ( config_AM_compute_interval /= 'output_interval' .and. config_AM_stream_name /= 'none') then + alarmName = poolItr % memberName(1:nameLength) // computeAlarmSuffix + call mpas_set_timeInterval(alarmTimeStep, timeString=config_AM_compute_interval, ierr=err_tmp) + call MPAS_stream_mgr_get_property(domain % streamManager, config_AM_stream_name, & + MPAS_STREAM_PROPERTY_REF_TIME, referenceTimeString, err_tmp) + call mpas_set_time(referenceTime, dateTimeString=referenceTimeString, ierr=err_tmp) + call mpas_add_clock_alarm(domain % clock, alarmName, referenceTime, alarmTimeStep, ierr=err_tmp) + call mpas_reset_clock_alarm(domain % clock, alarmName, ierr=err_tmp) + end if + call mpas_timer_stop(timerName) + end if + end do + + call mpas_timer_stop('analysis_init') + + end subroutine li_analysis_init!}}} + +!*********************************************************************** +! +! routine li_analysis_compute_startup +! +!> \brief Driver for MPAS-Land Ice analysis computations +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This routine calls all computation subroutines required for the +!> MPAS-Land Ice analysis driver. +! +!----------------------------------------------------------------------- + + subroutine li_analysis_compute_startup(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: timeLevel, err_tmp + + character (len=StrKIND) :: configName, timerName + character (len=StrKIND), pointer :: config_AM_stream_name + logical, pointer :: config_AM_enable, config_AM_write_on_startup, config_AM_compute_on_startup + type (mpas_pool_iterator_type) :: poolItr + integer :: nameLength + + err = 0 + + call mpas_timer_start('analysis_compute', .false.) + + timeLevel=1 + + call mpas_pool_begin_iteration(analysisMemberList) + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + call mpas_pool_get_config(domain % configs, configName, config_AM_enable) + + if ( config_AM_enable ) then + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_compute_on_startup' + call mpas_pool_get_config(domain % configs, configName, config_AM_compute_on_startup) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_write_on_startup' + call mpas_pool_get_config(domain % configs, configName, config_AM_write_on_startup) + + if ( config_AM_compute_on_startup ) then + timerName = trim(computeTimerPrefix) // poolItr % memberName(1:nameLength) + call mpas_timer_start(timerName, .false.) + call li_compute_analysis_members(domain, timeLevel, poolItr % memberName, err_tmp) + call mpas_timer_stop(timerName) + err = ior(err, err_tmp) + + if ( config_AM_write_on_startup ) then + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' + call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) + if ( config_AM_stream_name /= 'none' ) then + call mpas_stream_mgr_write(domain % streamManager, streamID=config_AM_stream_name, & + forceWriteNow=.true., ierr=err_tmp) + end if + end if + else + if ( config_AM_write_on_startup ) then + call mpas_log_write('write_on_startup called without compute_on_startup for analysis member: ' & + // poolItr % memberName(1:nameLength) // '. Skipping output...', MPAS_LOG_WARN) + end if + end if + end if + end do + + call mpas_timer_stop('analysis_compute') + + end subroutine li_analysis_compute_startup!}}} + +!*********************************************************************** +! +! routine li_analysis_compute +! +!> \brief Driver for MPAS-Land Ice analysis computations +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This routine calls all computation subroutines required for the +!> MPAS-Land Ice analysis driver. +! +!----------------------------------------------------------------------- + + subroutine li_analysis_compute(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: timeLevel, err_tmp + + character (len=StrKIND) :: configName, alarmName, timerName + character (len=StrKIND), pointer :: config_AM_stream_name, config_AM_compute_interval + logical, pointer :: config_AM_enable + type (mpas_pool_iterator_type) :: poolItr + integer :: nameLength + + err = 0 + + call mpas_timer_start('analysis_compute', .false.) + + timeLevel=1 + + call mpas_pool_begin_iteration(analysisMemberList) + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + call mpas_pool_get_config(domain % configs, configName, config_AM_enable) + + if ( config_AM_enable ) then + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_compute_interval' + call mpas_pool_get_config(domain % configs, configName, config_AM_compute_interval) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' + call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) + + ! Build name of alarm for analysis member + alarmName = poolItr % memberName(1:nameLength) // computeAlarmSuffix + timerName = trim(computeTimerPrefix) // poolItr % memberName(1:nameLength) + + ! Compute analysis member just before output + if ( config_AM_compute_interval == 'output_interval' .and. config_AM_stream_name /= 'none') then + if ( mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID=config_AM_stream_name, & + direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) ) then + call mpas_timer_start(timerName, .false.) + call li_compute_analysis_members(domain, timeLevel, poolItr % memberName, err_tmp) + call mpas_timer_stop(timerName) + end if + else if ( mpas_is_alarm_ringing(domain % clock, alarmName, ierr=err_tmp) ) then + call mpas_reset_clock_alarm(domain % clock, alarmName, ierr=err_tmp) + call mpas_timer_start(timerName, .false.) + call li_compute_analysis_members(domain, timeLevel, poolItr % memberName, err_tmp) + call mpas_timer_stop(timerName) + end if + end if + end do + + call mpas_timer_stop('analysis_compute') + + end subroutine li_analysis_compute!}}} + +!*********************************************************************** +! +! routine li_analysis_restart +! +!> \brief Save restart for MPAS-Land Ice analysis driver +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This routine calls all subroutines required to prepare to save +!> the restart state for the MPAS-Land Ice analysis driver. +! +!----------------------------------------------------------------------- + + subroutine li_analysis_restart(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: err_tmp + + character (len=StrKIND) :: configName, timerName + type (mpas_pool_iterator_type) :: poolItr + logical, pointer :: config_AM_enable + integer :: nameLength + + err = 0 + + call mpas_timer_start('analysis_restart', .false.) + + call mpas_pool_begin_iteration(analysisMemberList) + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + call mpas_pool_get_config(domain % configs, configName, config_AM_enable) + + if ( config_AM_enable ) then + timerName = trim(restartTimerPrefix) // poolItr % memberName(1:nameLength) + call mpas_timer_start(timerName, .false.) + call li_restart_analysis_members(domain, poolItr % memberName, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop(timerName) + end if + end do + + call mpas_timer_stop('analysis_restart') + + end subroutine li_analysis_restart!}}} + +!*********************************************************************** +! +! routine li_analysis_write +! +!> \brief Driver for MPAS-Land Ice analysis output +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This routine calls all output writing subroutines required for the +!> MPAS-Land Ice analysis driver. +! +!----------------------------------------------------------------------- + + subroutine li_analysis_write(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: err_tmp + + character (len=StrKIND) :: configName, timerName + character (len=StrKIND), pointer :: config_AM_stream_name + logical, pointer :: config_AM_enable + type (mpas_pool_iterator_type) :: poolItr + integer :: nameLength + + err = 0 + + call mpas_timer_start('analysis_write', .false.) + + call mpas_pool_begin_iteration(analysisMemberList) + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + call mpas_pool_get_config(domain % configs, configName, config_AM_enable) + + if ( config_AM_enable ) then + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' + call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) + if ( config_AM_stream_name /= 'none' ) then + timerName = trim(writeTimerPrefix) // poolItr % memberName(1:nameLength) + call mpas_timer_start(timerName, .false.) + call mpas_stream_mgr_write(domain % streamManager, streamID=config_AM_stream_name, ierr=err_tmp) + call mpas_timer_stop(timerName) + timerName = trim(alarmTimerPrefix) // poolItr % memberName(1:nameLength) + call mpas_timer_start(timerName, .false.) + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID=config_AM_stream_name, ierr=err_tmp) + call mpas_timer_stop(timerName) + end if + end if + end do + + call mpas_timer_stop('analysis_write') + + end subroutine li_analysis_write!}}} + +!*********************************************************************** +! +! routine li_analysis_finalize +! +!> \brief Finalize MPAS-Land Ice analysis driver +!> \author S. Price +!> \date 9/10/2015 +!> \details +!> This routine calls all finalize routines required for the +!> MPAS-Land Ice analysis driver. +! +!----------------------------------------------------------------------- + + subroutine li_analysis_finalize(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: err_tmp + + character (len=StrKIND) :: configName, timerName + logical, pointer :: config_AM_enable + type (mpas_pool_iterator_type) :: poolItr + integer :: nameLength + + err = 0 + + call mpas_timer_start('analysis_finalize', .false.) + + call mpas_pool_begin_iteration(analysisMemberList) + + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + call mpas_pool_get_config(domain % configs, configName, config_AM_enable) + + if ( config_AM_enable ) then + timerName = trim(finalizeTimerPrefix) // poolItr % memberName(1:nameLength) + call mpas_timer_start(timerName, .false.) + call li_finalize_analysis_members(domain, poolItr % memberName, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop(timerName) + end if + end do + + call mpas_timer_stop('analysis_finalize') + + end subroutine li_analysis_finalize!}}} + +!*********************************************************************** +! +! routine li_init_analysis_members +! +!> \brief Analysis member initialization driver +!> \author Doug Jacobsen +!> \date 07/01/2015 +!> \details +!> This private routine calls the correct init routine for each analysis member. +! +!----------------------------------------------------------------------- + subroutine li_init_analysis_members(domain, analysisMemberName, iErr)!{{{ + type (domain_type), intent(inout) :: domain !< Input: Domain information + character (len=*), intent(in) :: analysisMemberName !< Input: Name of analysis member + integer, intent(out) :: iErr !< Output: Error code + + integer :: nameLength, err_tmp + + iErr = 0 + + nameLength = len_trim(analysisMemberName) + + if ( analysisMemberName(1:nameLength) == 'globalStats' ) then + call li_init_global_stats(domain, analysisMemberName, err_tmp) + end if + if ( analysisMemberName(1:nameLength) == 'regionalStats' ) then + call li_init_regional_stats(domain, analysisMemberName, err_tmp) + end if + + iErr = ior(iErr, err_tmp) + + end subroutine li_init_analysis_members!}}} + +!*********************************************************************** +! +! routine li_compute_analysis_members +! +!> \brief Analysis member compute driver +!> \author Doug Jacobsen +!> \date 07/01/2015 +!> \details +!> This private routine calls the correct compute routine for each analysis member. +! +!----------------------------------------------------------------------- + subroutine li_compute_analysis_members(domain, timeLevel, analysisMemberName, iErr)!{{{ + type (domain_type), intent(inout) :: domain !< Input: Domain information + integer, intent(in) :: timeLevel !< Input: Time level to compute with in analysis member + character (len=*), intent(in) :: analysisMemberName !< Input: Name of analysis member + integer, intent(out) :: iErr !< Output: Error code + + integer :: nameLength, err_tmp + + iErr = 0 + + nameLength = len_trim(analysisMemberName) + + if ( analysisMemberName(1:nameLength) == 'globalStats' ) then + call li_compute_global_stats(domain, analysisMemberName, timeLevel, err_tmp) + end if + if ( analysisMemberName(1:nameLength) == 'regionalStats' ) then + call li_compute_regional_stats(domain, analysisMemberName, timeLevel, err_tmp) + end if + + iErr = ior(iErr, err_tmp) + + end subroutine li_compute_analysis_members!}}} + +!*********************************************************************** +! +! routine li_restart_analysis_members +! +!> \brief Analysis member restart driver +!> \author Doug Jacobsen +!> \date 07/01/2015 +!> \details +!> This private routine calls the correct restart routine for each analysis member. +! +!----------------------------------------------------------------------- + subroutine li_restart_analysis_members(domain, analysisMemberName, iErr)!{{{ + type (domain_type), intent(inout) :: domain !< Input: Domain information + character (len=*), intent(in) :: analysisMemberName !< Input: Name of analysis member + integer, intent(out) :: iErr !< Output: Error code + + integer :: nameLength, err_tmp + + iErr = 0 + + nameLength = len_trim(analysisMemberName) + + if ( analysisMemberName(1:nameLength) == 'globalStats' ) then + call li_restart_global_stats(domain, analysisMemberName, err_tmp) + end if + if ( analysisMemberName(1:nameLength) == 'regionalStats' ) then + call li_restart_regional_stats(domain, analysisMemberName, err_tmp) + end if + + iErr = ior(iErr, err_tmp) + + end subroutine li_restart_analysis_members!}}} + +!*********************************************************************** +! +! routine li_finalize_analysis_members +! +!> \brief Analysis member finalize driver +!> \author Doug Jacobsen +!> \date 07/01/2015 +!> \details +!> This private routine calls the correct finalize routine for each analysis member. +! +!----------------------------------------------------------------------- + subroutine li_finalize_analysis_members(domain, analysisMemberName, iErr)!{{{ + type (domain_type), intent(inout) :: domain !< Input: Domain information + character (len=*), intent(in) :: analysisMemberName !< Input: Name of analysis member + integer, intent(out) :: iErr !< Output: Error code + + integer :: nameLength, err_tmp + + iErr = 0 + + nameLength = len_trim(analysisMemberName) + + if ( analysisMemberName(1:nameLength) == 'globalStats' ) then + call li_finalize_global_stats(domain, analysisMemberName, err_tmp) + end if + if ( analysisMemberName(1:nameLength) == 'regionalStats' ) then + call li_finalize_regional_stats(domain, analysisMemberName, err_tmp) + end if + + iErr = ior(iErr, err_tmp) + + end subroutine li_finalize_analysis_members!}}} + +end module li_analysis_driver + +! vim: foldmethod=marker diff --git a/src/core_landice/analysis_members/mpas_li_global_stats.F b/src/core_landice/analysis_members/mpas_li_global_stats.F new file mode 100644 index 0000000000..d8e8a20d1a --- /dev/null +++ b/src/core_landice/analysis_members/mpas_li_global_stats.F @@ -0,0 +1,619 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! mpas_li_global_stats +! +!> \brief MPAS land ice analysis mode member: mpas_li_global_stats +!> \author Stephen Price +!> \date 8-30-2015 +!> \details +!> +!> +!----------------------------------------------------------------------- +module li_global_stats + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use li_mask + use li_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_init_global_stats, & + li_compute_global_stats, & + li_restart_global_stats, & + li_finalize_global_stats + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_init_global_stats +! +!> \brief Initialize MPAS-Land Ice analysis member +!> \author S. Price +!> \date 9/9/2015 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_init_global_stats(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_init_global_stats!}}} + +!*********************************************************************** +! +! routine li_compute_global_stats +! +!> \brief Compute MPAS-Land Ice analysis member +!> \author S. Price +!> \date 9/9/2015 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_compute_global_stats(domain, memberName, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: globalStatsAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: globalStatsAM + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool + + ! arrays, vars needed from other pools for calculations here + real (kind=RKIND), pointer :: config_ice_density + real (kind=RKIND), pointer :: deltat + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: sfcMassBal + real (kind=RKIND), dimension(:), pointer :: basalMassBal + real (kind=RKIND), dimension(:), pointer :: groundedBasalMassBal + real (kind=RKIND), dimension(:), pointer :: floatingBasalMassBal + real (kind=RKIND), dimension(:), pointer :: calvingThickness + real (kind=RKIND), dimension(:), pointer :: surfaceSpeed + real (kind=RKIND), dimension(:), pointer :: basalSpeed + real (kind=RKIND), dimension(:,:), pointer :: layerNormalVelocity + real (kind=RKIND), dimension(:,:), pointer :: layerThicknessEdge + integer, dimension(:), pointer :: cellMask + integer, dimension(:), pointer :: edgeMask + integer, dimension(:,:), pointer :: cellsOnEdge + integer, pointer :: nCellsSolve + integer, pointer :: nEdgesSolve + integer, pointer :: nVertLevels + + ! config options needed + real (kind=RKIND), pointer :: config_sea_level + real (kind=RKIND), pointer :: rhoi ! config_ice_density + real (kind=RKIND), pointer :: rhow ! config_ocean_density + + ! Local counters + integer :: k, iCell, iEdge + integer :: iCell1, iCell2 + + ! scalars to be calculated here from global reductions + real (kind=RKIND), pointer :: totalIceArea, totalIceVolume + real (kind=RKIND), pointer :: volumeAboveFloatation + real (kind=RKIND), pointer :: groundedIceArea, groundedIceVolume + real (kind=RKIND), pointer :: floatingIceArea, floatingIceVolume + real (kind=RKIND), pointer :: iceThicknessMax, iceThicknessMin, iceThicknessMean + real (kind=RKIND), pointer :: totalSfcMassBal, totalBasalMassBal + real (kind=RKIND), pointer :: totalGroundedBasalMassBal, totalFloatingBasalMassBal + real (kind=RKIND), pointer :: avgNetAccumulation + real (kind=RKIND), pointer :: avgGroundedBasalMelt + real (kind=RKIND), pointer :: avgSubshelfMelt + real (kind=RKIND), pointer :: totalCalvingFlux + real (kind=RKIND), pointer :: groundingLineFlux + real (kind=RKIND), pointer :: surfaceSpeedMax + real (kind=RKIND), pointer :: basalSpeedMax + + ! scalar reductions over all blocks on this processor + real (kind=RKIND) :: blockSumIceArea, blockSumIceVolume + real (kind=RKIND) :: blockSumVAF + real (kind=RKIND) :: blockSumGroundedIceArea, blockSumGroundedIceVolume + real (kind=RKIND) :: blockSumFloatingIceArea, blockSumFloatingIceVolume + real (kind=RKIND) :: blockThickMin, blockThickMax + real (kind=RKIND) :: blockSumSfcMassBal, blockSumBasalMassBal + real (kind=RKIND) :: blockSumGroundedBasalMassBal, blockSumFloatingBasalMassBal + real (kind=RKIND) :: blockSumCalvingFlux + real (kind=RKIND) :: blockMaxSurfaceSpeed + real (kind=RKIND) :: blockMaxBasalSpeed + real (kind=RKIND) :: blockGLflux + + ! Local variables for calculations + real (kind=RKIND) :: fluxSign + + ! variables for processing stats + integer, parameter :: kMaxVariables = 16 ! Increase if number of stats increase + integer :: nVars + real (kind=RKIND), dimension(kMaxVariables) :: reductions, sums, mins, maxes + + err = 0 + + dminfo = domain % dminfo + + ! initialize sums over blocks to 0 + blockSumIceArea = 0.0_RKIND + blockSumIceVolume = 0.0_RKIND + blockSumVAF = 0.0_RKIND + blockSumGroundedIceArea = 0.0_RKIND + blockSumGroundedIceVolume = 0.0_RKIND + blockSumFloatingIceArea = 0.0_RKIND + blockSumFloatingIceVolume = 0.0_RKIND + blockSumSfcMassBal = 0.0_RKIND + blockSumBasalMassBal = 0.0_RKIND + blockSumGroundedBasalMassBal = 0.0_RKIND + blockSumFloatingBasalMassBal = 0.0_RKIND + blockSumCalvingFlux = 0.0_RKIND + blockGLflux = 0.0_RKIND + + ! initialize max to 0, min to large number + blockThickMin = 100000.0_RKIND + blockThickMax = 0.0_RKIND + blockMaxSurfaceSpeed = 0.0_RKIND + blockMaxBasalSpeed = 0.0_RKIND + + ! Get needed configs + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhow) + + ! loop over blocks + block => domain % blocklist + do while (associated(block)) + + ! get structs from pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'globalStatsAM', globalStatsAMPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + + ! get values and arrays from standard pools + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'basalMassBal', basalMassBal) + call mpas_pool_get_array(geometryPool, 'groundedBasalMassBal', groundedBasalMassBal) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(velocityPool, 'surfaceSpeed', surfaceSpeed) + call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) + call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) + + ! loop over cells + do iCell = 1,nCellsSolve + + ! sums of ice area and volume over cells (m^2 and m^3) + blockSumIceArea = blockSumIceArea + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) + blockSumIceVolume = blockSumIceVolume + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * thickness(iCell) + + blockSumVAF = blockSumVAF + real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) * areaCell(iCell) * & + ( thickness(iCell) + (rhow / rhoi) * min(0.0_RKIND, (bedTopography(iCell) - config_sea_level)) ) + + blockSumGroundedIceArea = blockSumGroundedIceArea + real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) + blockSumGroundedIceVolume = blockSumGroundedIceVolume + real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * thickness(iCell) + + blockSumFloatingIceArea = blockSumFloatingIceArea + real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) + blockSumFloatingIceVolume = blockSumFloatingIceVolume + real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * thickness(iCell) + + ! max, min thickness values (m) + if( thickness(iCell) > blockThickMax)then + blockThickMax = thickness(iCell) + endif + if( thickness(iCell) < blockThickMin .and. thickness(iCell) > 0.0_RKIND)then + blockThickMin = thickness(iCell) + endif + + ! sfc and basal mass balance (kg yr^{-1}) + !SFP: These calculations need to be tested still + blockSumSfcMassBal = blockSumSfcMassBal + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * sfcMassBal(iCell) * scyr + blockSumBasalMassBal = blockSumBasalMassBal + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * basalMassBal(iCell) * scyr + blockSumGroundedBasalMassBal = blockSumGroundedBasalMassBal + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * groundedBasalMassBal(iCell) * scyr + blockSumFloatingBasalMassBal = blockSumFloatingBasalMassBal + real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * floatingBasalMassBal(iCell) * scyr + + ! mass lass due do calving (kg yr^{-1}) + blockSumCalvingFlux = blockSumCalvingFlux + calvingThickness(iCell) * & + areaCell(iCell) * config_ice_density / ( deltat / scyr ) + + ! max surface speed + if (surfaceSpeed(iCell) > blockMaxSurfaceSpeed) then + blockMaxSurfaceSpeed = surfaceSpeed(iCell) + endif + + ! max basal speed + if (basalSpeed(iCell) > blockMaxBasalSpeed) then + blockMaxBasalSpeed = basalSpeed(iCell) + endif + + end do ! end loop over cells + + ! Loop over edges + do iEdge = 1, nEdgesSolve + if (li_mask_is_grounding_line(edgeMask(iEdge))) then + ! Determine sign of this edge relative to GL + ! (+=grounded to floating, -=floating to grounded) + iCell1 = cellsOnEdge(1,iEdge) + iCell2 = cellsOnEdge(2,iEdge) + if (li_mask_is_grounded_ice(cellMask(iCell1))) then + fluxSign = 1.0_RKIND + else + fluxSign = -1.0_RKIND + endif + + ! Loop over levels + do k = 1, nVertLevels + ! Flux across GL, units = kg/yr + blockGLflux = blockGLflux + fluxSign * layerNormalVelocity(k, iEdge) * dvEdge(iEdge) * layerThicknessEdge(k, iEdge) & + * scyr * config_ice_density ! convert from m^3/s to kg/yr + end do ! end loop over levels + end if ! if GL + + end do ! end loop over edges + + block => block % next + end do ! end loop over blocks + + + ! --- Perform Reductions --- + ! For each type of reduction (sum, min, max), set up an array so we only need + ! a single reduction for each type, rather than a reduction for each variable + ! since these global communications can be expensive on many processors. + ! For now, the mapping of variable names to indices is handled manually, + ! but if the number of variables grows, we could consider using a pool (a dictionary) + ! or some other strategy for automating the relationships. + ! Once the reduction is complete, stick the reduced value into the globalStats pool member. + ! Even though some (most?) variables do not include an index that is decomposed amongst + ! domain partitions, we assign them within a block loop so that all blocks have the + ! correct values for writing output. + + ! --------------------------- + ! compute sums (and means) over all procs + ! --------------------------- + sums = 0.0_RKIND + reductions = 0.0_RKIND + + ! Fill the array with the values accumulated over all blocks on this processor. + ! Note that the indices here need to be manually matched when unpacking the reduced + ! values after the global reduction. + ! Instructions: To add a new field, add a new sums(??) line with a larger index. + ! Then update nVars to the new value. Make sure kMaxVariables is large enough above. + ! Then unpack the new index in the block loop into the appropriate variable. + + sums(1) = blockSumIceArea + sums(2) = blockSumIceVolume + sums(3) = blockSumGroundedIceArea + sums(4) = blockSumGroundedIceVolume + sums(5) = blockSumFloatingIceArea + sums(6) = blockSumFloatingIceVolume + sums(7) = blockSumSfcMassBal + sums(8) = blockSumBasalMassBal + sums(9) = blockSumGroundedBasalMassBal + sums(10) = blockSumFloatingBasalMassBal + sums(11) = blockSumCalvingFlux + sums(12) = blockSumVAF + sums(13) = blockGLflux + nVars = 13 + + call mpas_dmpar_sum_real_array(dminfo, nVars, sums(1:nVars), reductions(1:nVars)) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'globalStatsAM', globalStatsAMPool) + + call mpas_pool_get_array(globalStatsAMPool, 'totalIceArea', totalIceArea) + call mpas_pool_get_array(globalStatsAMPool, 'totalIceVolume', totalIceVolume) + call mpas_pool_get_array(globalStatsAMPool, 'volumeAboveFloatation', volumeAboveFloatation) + call mpas_pool_get_array(globalStatsAMPool, 'floatingIceArea', floatingIceArea) + call mpas_pool_get_array(globalStatsAMPool, 'floatingIceVolume', floatingIceVolume) + call mpas_pool_get_array(globalStatsAMPool, 'groundedIceArea', groundedIceArea) + call mpas_pool_get_array(globalStatsAMPool, 'groundedIceVolume', groundedIceVolume) + call mpas_pool_get_array(globalStatsAMPool, 'iceThicknessMean', iceThicknessMean) + call mpas_pool_get_array(globalStatsAMPool, 'totalSfcMassBal', totalSfcMassBal) + call mpas_pool_get_array(globalStatsAMPool, 'avgNetAccumulation', avgNetAccumulation) + call mpas_pool_get_array(globalStatsAMPool, 'totalBasalMassBal', totalBasalMassBal) + call mpas_pool_get_array(globalStatsAMPool, 'totalGroundedBasalMassBal', totalGroundedBasalMassBal) + call mpas_pool_get_array(globalStatsAMPool, 'avgGroundedBasalMelt', avgGroundedBasalMelt) + call mpas_pool_get_array(globalStatsAMPool, 'totalFloatingBasalMassBal', totalFloatingBasalMassBal) + call mpas_pool_get_array(globalStatsAMPool, 'avgSubshelfMelt', avgSubshelfMelt) + call mpas_pool_get_array(globalStatsAMPool, 'totalCalvingFlux', totalCalvingFlux) + call mpas_pool_get_array(globalStatsAMPool, 'groundingLineFlux', groundingLineFlux) + + totalIceArea = reductions(1) + totalIceVolume = reductions(2) + groundedIceArea = reductions(3) + groundedIceVolume = reductions(4) + floatingIceArea = reductions(5) + floatingIceVolume = reductions(6) + totalSfcMassBal = reductions(7) + totalBasalMassBal = reductions(8) + totalGroundedBasalMassBal = reductions(9) + totalFloatingBasalMassBal = reductions(10) + totalCalvingFlux = reductions(11) + volumeAboveFloatation = reductions(12) + groundingLineFlux = reductions(13) + + iceThicknessMean = totalIceVolume / totalIceArea + avgNetAccumulation = totalSfcMassBal / totalIceArea / rhoi + avgGroundedBasalMelt = -1.0_RKIND * totalGroundedBasalMassBal / groundedIceArea / rhoi + avgSubshelfMelt = -1.0_RKIND * totalFloatingBasalMassBal / floatingIceArea / rhoi + + block => block % next + end do + + ! --------------------------- + ! compute mins + ! --------------------------- + mins = 0.0_RKIND + reductions = 0.0_RKIND + mins(1) = blockThickMin + nVars = 1 + call mpas_dmpar_min_real_array(dminfo, nVars, mins(1:nVars), reductions(1:nVars)) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'globalStatsAM', globalStatsAMPool) + + call mpas_pool_get_array(globalStatsAMPool, 'iceThicknessMin', iceThicknessMin) + + iceThicknessMin = reductions(1) + + block => block % next + end do + + ! --------------------------- + ! compute maxes + ! --------------------------- + maxes = 0.0_RKIND + reductions = 0.0_RKIND + maxes(1) = blockThickMax + maxes(2) = blockMaxSurfaceSpeed * scyr ! convert units to match Registry + maxes(3) = blockMaxBasalSpeed * scyr ! convert units to match Registry + nVars = 3 + call mpas_dmpar_max_real_array(dminfo, nVars, maxes(1:nVars), reductions(1:nVars)) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'globalStatsAM', globalStatsAMPool) + + call mpas_pool_get_array(globalStatsAMPool, 'iceThicknessMax', iceThicknessMax) + call mpas_pool_get_array(globalStatsAMPool, 'surfaceSpeedMax', surfaceSpeedMax) + call mpas_pool_get_array(globalStatsAMPool, 'basalSpeedMax', basalSpeedMax) + + iceThicknessMax = reductions(1) + surfaceSpeedMax = reductions(2) + basalSpeedMax = reductions(3) + + block => block % next + end do + + + + end subroutine li_compute_global_stats!}}} + +!*********************************************************************** +! +! routine li_restart_global_stats +! +!> \brief Save restart for MPAS-Land Ice analysis member +!> \author S. Price +!> \date 9/9/2015 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_restart_global_stats(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_restart_global_stats!}}} + +!*********************************************************************** +! +! routine li_finalize_global_stats +! +!> \brief Finalize MPAS-Land Ice analysis member +!> \author S. Price +!> \date 9/9/2015 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_finalize_global_stats(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_finalize_global_stats!}}} + +end module li_global_stats + +! vim: foldmethod=marker diff --git a/src/core_landice/analysis_members/mpas_li_regional_stats.F b/src/core_landice/analysis_members/mpas_li_regional_stats.F new file mode 100644 index 0000000000..f83bddd3de --- /dev/null +++ b/src/core_landice/analysis_members/mpas_li_regional_stats.F @@ -0,0 +1,675 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! mpas_li_regional_stats +! +!> \brief MPAS land ice analysis mode member: mpas_li_regional_stats +!> \author Stephen Price +!> \date 4-29-2016 +!> \details +!> +!> +!----------------------------------------------------------------------- +module li_regional_stats + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use li_mask + use li_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_init_regional_stats, & + li_compute_regional_stats, & + li_restart_regional_stats, & + li_finalize_regional_stats + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_init_regional_stats +! +!> \brief Initialize MPAS-Land Ice analysis member +!> \author S. Price +!> \date 9/9/2015 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_init_regional_stats(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_init_regional_stats!}}} + +!*********************************************************************** +! +! routine li_compute_regional_stats +! +!> \brief Compute MPAS-Land Ice analysis member +!> \author S. Price +!> \date 4-29-2016 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_compute_regional_stats(domain, memberName, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: regionalStatsAMPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: regionalStatsAM + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: regionsPool + type (mpas_pool_type), pointer :: velocityPool + + ! arrays, vars needed from other pools for calculations here + real (kind=RKIND), pointer :: config_ice_density + real (kind=RKIND), pointer :: deltat + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: sfcMassBal + real (kind=RKIND), dimension(:), pointer :: basalMassBal + real (kind=RKIND), dimension(:), pointer :: groundedBasalMassBal + real (kind=RKIND), dimension(:), pointer :: floatingBasalMassBal + real (kind=RKIND), dimension(:), pointer :: calvingThickness + real (kind=RKIND), dimension(:), pointer :: surfaceSpeed + real (kind=RKIND), dimension(:), pointer :: basalSpeed + real (kind=RKIND), dimension(:,:), pointer :: layerNormalVelocity + real (kind=RKIND), dimension(:,:), pointer :: layerThicknessEdge + + ! config options needed + real (kind=RKIND), pointer :: config_sea_level + real (kind=RKIND), pointer :: rhoi ! config_ice_density + real (kind=RKIND), pointer :: rhow ! config_ocean_density + + integer, dimension(:,:), pointer :: regionCellMasks + integer, dimension(:), pointer :: cellMask + integer, dimension(:), pointer :: edgeMask + integer, dimension(:,:), pointer :: cellsOnEdge + integer, pointer :: nRegions, nRegionGroups !, maxRegionsInGroup !! maxRegionsInGroup not needed / used yet + integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels + integer :: k, iCell, iCell1, iCell2, iEdge + integer :: iRegion, iGroup + + ! scalars to be calculated here from regional sums + real (kind=RKIND), dimension(:), pointer :: regionalIceArea, regionalIceVolume + real (kind=RKIND), dimension(:), pointer :: regionalVolumeAboveFloatation + real (kind=RKIND), dimension(:), pointer :: regionalGroundedIceArea, regionalGroundedIceVolume + real (kind=RKIND), dimension(:), pointer :: regionalFloatingIceArea, regionalFloatingIceVolume + real (kind=RKIND), dimension(:), pointer :: regionalIceThicknessMin, regionalIceThicknessMax, regionalIceThicknessMean + real (kind=RKIND), dimension(:), pointer :: regionalSumSfcMassBal, regionalSumBasalMassBal + real (kind=RKIND), dimension(:), pointer :: regionalSumGroundedBasalMassBal, regionalSumFloatingBasalMassBal + real (kind=RKIND), dimension(:), pointer :: regionalSumCalvingFlux + real (kind=RKIND), dimension(:), pointer :: regionalSumGroundingLineFlux + real (kind=RKIND), dimension(:), pointer :: regionalAvgNetAccumulation + real (kind=RKIND), dimension(:), pointer :: regionalAvgGroundedBasalMelt + real (kind=RKIND), dimension(:), pointer :: regionalAvgSubshelfMelt + real (kind=RKIND), dimension(:), pointer :: regionalSurfaceSpeedMax + real (kind=RKIND), dimension(:), pointer :: regionalBasalSpeedMax + + ! storage for sums over blocks + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionIceArea, blockSumRegionIceVolume + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionVAF + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionGroundedIceArea, blockSumRegionGroundedIceVolume + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionFloatingIceArea, blockSumRegionFloatingIceVolume + real (kind=RKIND), dimension(:), allocatable :: blockRegionThickMin, blockRegionThickMax + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionSfcMassBal, blockSumRegionBasalMassBal + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionGroundedBasalMassBal, blockSumRegionFloatingBasalMassBal + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionCalvingFlux + real (kind=RKIND), dimension(:), allocatable :: blockSumRegionGLflux + real (kind=RKIND), dimension(:), allocatable :: blockRegionMaxSurfaceSpeed + real (kind=RKIND), dimension(:), allocatable :: blockRegionMaxBasalSpeed + + ! local variables + real (kind=RKIND) :: fluxSign + + ! variables for processing stats + integer, parameter :: kMaxVariables = 16 ! Increase if number of stats increase + integer :: nVars + real (kind=RKIND), dimension(kMaxVariables) :: reductions, sums, mins, maxes + + err = 0 + + dminfo = domain % dminfo + + ! Get needed configs + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhow) + + ! loop over blocks + block => domain % blocklist + do while (associated(block)) + + !! get dimensions assocated with region masks + call mpas_pool_get_dimension(block % dimensions, 'nRegions', nRegions) + call mpas_pool_get_dimension(block % dimensions, 'nRegionGroups', nRegionGroups) + + ! get structs from pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'regions', regionsPool) +! call mpas_pool_get_subpool(block % structs, 'regionalStatsAM', regionalStatsAMPool) + + ! get values and arrays from standard pools + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'basalMassBal', basalMassBal) + call mpas_pool_get_array(geometryPool, 'groundedBasalMassBal', groundedBasalMassBal) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(velocityPool, 'surfaceSpeed', surfaceSpeed) + call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) + call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) + + ! get region cell masks from regionMasks.nc input file + call mpas_pool_get_array(regionsPool, 'regionCellMasks', regionCellMasks) + + ! allocate & initialize sums over blocks to 0 + allocate(blockSumRegionIceArea(nRegions)); allocate(blockSumRegionIceVolume(nRegions)) + allocate(blockSumRegionVAF(nRegions)) + allocate(blockSumRegionGroundedIceArea(nRegions)); allocate(blockSumRegionGroundedIceVolume(nRegions)) + allocate(blockSumRegionFloatingIceArea(nRegions)); allocate(blockSumRegionFloatingIceVolume(nRegions)) + allocate(blockRegionThickMin(nRegions)); allocate(blockRegionThickMax(nRegions)) + allocate(blockRegionMaxBasalSpeed(nRegions)); allocate(blockRegionMaxSurfaceSpeed(nRegions)) + allocate(blockSumRegionSfcMassBal(nRegions)); allocate(blockSumRegionBasalMassBal(nRegions)) + allocate(blockSumRegionGroundedBasalMassBal(nRegions)); allocate(blockSumRegionFloatingBasalMassBal(nRegions)) + allocate(blockSumRegionCalvingFlux(nRegions)); allocate(blockSumRegionGLflux(nRegions)) + + blockSumRegionIceArea = 0.0_RKIND; blockSumRegionIceVolume = 0.0_RKIND + blockSumRegionVAF = 0.0_RKIND + blockSumRegionGroundedIceArea = 0.0_RKIND; blockSumRegionGroundedIceVolume = 0.0_RKIND + blockSumRegionFloatingIceArea = 0.0_RKIND; blockSumRegionFloatingIceVolume = 0.0_RKIND + blockRegionThickMin = 10000.0_RKIND; blockRegionThickMax = 0.0_RKIND + blockRegionMaxBasalSpeed = 0.0_RKIND; blockRegionMaxSurfaceSpeed = 0.0_RKIND + blockSumRegionSfcMassBal = 0.0_RKIND; blockSumRegionBasalMassBal = 0.0_RKIND + blockSumRegionGroundedBasalMassBal = 0.0_RKIND; blockSumRegionFloatingBasalMassBal = 0.0_RKIND + blockSumRegionCalvingFlux = 0.0_RKIND; blockSumRegionGLflux = 0.0_RKIND + + do iCell = 1,nCellsSolve ! loop over cells + ! do iGroup = 1,nRegionGroups ! loop over groups + do iRegion = 1,nRegions ! loop over regions + + ! regional sums of ice area over cells with ice (m^2) + blockSumRegionIceArea(iRegion) = blockSumRegionIceArea(iRegion) + ( real(regionCellMasks(iRegion,iCell),RKIND) & + * areaCell(iCell) * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) ) + + ! regional sums of ice volume over cells with ice (m^3) + blockSumRegionIceVolume(iRegion) = blockSumRegionIceVolume(iRegion) + ( real(regionCellMasks(iRegion,iCell),RKIND) & + * areaCell(iCell) * thickness(iCell) * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) ) + + ! regional sums of ice volume above floatation over cells with ice (m^3) + blockSumRegionVAF(iRegion) = blockSumRegionVAF(iRegion) + real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) & + * real(regionCellMasks(iRegion,iCell),RKIND) * areaCell(iCell) * ( thickness(iCell) + (rhow / rhoi) & + * min(0.0_RKIND, (bedTopography(iCell) - config_sea_level)) ) + + ! regional sums of grounded ice area over cells with ice (m^2) + blockSumRegionGroundedIceArea(iRegion) = blockSumRegionGroundedIceArea(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) * areaCell(iCell) & + * real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) ) + + ! regional sums of grounded ice volume over cells with ice (m^3) + blockSumRegionGroundedIceVolume(iRegion) = blockSumRegionGroundedIceVolume(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) * areaCell(iCell) & + * thickness(iCell) * real(li_mask_is_grounded_ice_int(cellMask(iCell)),RKIND) ) + + ! regional sums of floating ice area over cells with ice (m^2) + blockSumRegionFloatingIceArea(iRegion) = blockSumRegionFloatingIceArea(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) * areaCell(iCell) & + * real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) ) + + ! regional sums of floating ice volume over cells with ice (m^3) + blockSumRegionFloatingIceVolume(iRegion) = blockSumRegionFloatingIceVolume(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) * areaCell(iCell) & + * thickness(iCell) * real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) ) + + ! regional sum of sfc mass balance (kg yr^{-1}) + blockSumRegionSfcMassBal(iRegion) = blockSumRegionSfcMassBal(iRegion) + ( real(regionCellMasks(iRegion,iCell),RKIND) & + * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) * areaCell(iCell) * sfcMassBal(iCell) * scyr ) + + ! regional sum of basal mass balance (kg yr^{-1}) + blockSumRegionBasalMassBal(iRegion) = blockSumRegionBasalMassBal(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * basalMassBal(iCell) * scyr ) + + ! regional sum of grounded basal mass balance (kg yr^{-1}) + blockSumRegionFloatingBasalMassBal(iRegion) = blockSumRegionFloatingBasalMassBal(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) * real(li_mask_is_floating_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * floatingBasalMassBal(iCell) * scyr ) + + ! regional sum of floating basal mass balance (kg yr^{-1}) + blockSumRegionGroundedBasalMassBal(iRegion) = blockSumRegionGroundedBasalMassBal(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) & + * areaCell(iCell) * groundedBasalMassBal(iCell) * scyr ) + + ! regional sum of mass lass due do calving (kg yr^{-1}) + blockSumRegionCalvingFlux(iRegion) = blockSumRegionCalvingFlux(iRegion) + & + ( real(regionCellMasks(iRegion,iCell),RKIND) & + * calvingThickness(iCell) * areaCell(iCell) * config_ice_density / ( deltat / scyr ) ) + + ! max, min thickness values (m) + if( ( thickness(iCell) * real(regionCellMasks(iRegion,iCell),RKIND) * & + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) ) > blockRegionThickMax(iRegion) ) then + blockRegionThickMax(iRegion) = thickness(iCell) + endif + if( ( thickness(iCell) * real(regionCellMasks(iRegion,iCell),RKIND) * & + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) ) < blockRegionThickMin(iRegion) .and. & + ( thickness(iCell) * real(regionCellMasks(iRegion,iCell),RKIND) & + * real(li_mask_is_ice_int(cellMask(iCell)),RKIND) ) > 0.0_RKIND )then + blockRegionThickMin(iRegion) = thickness(iCell) + endif + + ! max basal and sfc speeds (m/yr) + if( ( surfaceSpeed(iCell) * real(regionCellMasks(iRegion,iCell),RKIND) * & + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) ) > blockRegionMaxSurfaceSpeed(iRegion) ) then + blockRegionMaxSurfaceSpeed(iRegion) = surfaceSpeed(iCell) + endif + if( ( basalSpeed(iCell) * real(regionCellMasks(iRegion,iCell),RKIND) * & + real(li_mask_is_ice_int(cellMask(iCell)),RKIND) ) > blockRegionMaxBasalSpeed(iRegion) ) then + blockRegionMaxBasalSpeed(iRegion) = basalSpeed(iCell) + endif + + end do ! end loop over regions + ! end do ! end loop over groups + end do ! end loop over cells + + + do iEdge = 1,nEdgesSolve ! loop over edges + ! do iGroup = 1,nRegionGroups ! loop over groups + do iRegion = 1,nRegions ! loop over regions + + if (li_mask_is_grounding_line(edgeMask(iEdge))) then + ! Determine sign of this edge relative to GL + ! (+=grounded to floating, -=floating to grounded) + iCell1 = cellsOnEdge(1,iEdge) + iCell2 = cellsOnEdge(2,iEdge) + if (li_mask_is_grounded_ice(cellMask(iCell1))) then + fluxSign = 1.0_RKIND + else + fluxSign = -1.0_RKIND + endif + + ! first need to determine if the edge flux is being calc. on is part of the region of interest + ! Use cellsOnEdge indices from above to index relevant regionCellMasks values and sum + if(regionCellMasks(iRegion,iCell1) + regionCellMasks(iRegion,iCell2) > 0) then + + ! Loop over levels + do k = 1, nVertLevels + ! Flux across GL, units = kg/yr + blockSumRegionGLflux(iRegion) = blockSumRegionGLflux(iRegion) + fluxSign * layerNormalVelocity(k, iEdge) * & + dvEdge(iEdge) * layerThicknessEdge(k, iEdge) & + * scyr * config_ice_density ! convert from m^3/s to kg/yr + end do ! end loop over levels + + end if ! if edge is on cell in region of interest + + end if ! if GL + + end do ! end loop over regions + ! end do ! end loop over groups + end do ! end loop over edges + + block => block % next + + end do ! end loop over blocks + + ! --- Perform Reductions --- + ! For each type of reduction (sum, min, max), set up an array so we only need + ! a single reduction for each type, rather than a reduction for each variable + ! since these global communications can be expensive on many processors. + ! For now, the mapping of variable names to indices is handled manually, + ! but if the number of variables grows, we could consider using a pool (a dictionary) + ! or some other strategy for automating the relationships. + ! Once the reduction is complete, stick the reduced value into the globalStats pool member. + ! Even though some (most?) variables do not include an index that is decomposed amongst + ! domain partitions, we assign them within a block loop so that all blocks have the + ! correct values for writing output. + + ! --------------------------- + ! compute sums (and means) over all procs + ! --------------------------- + + do iRegion = 1,nRegions ! loop over regions + + sums = 0.0_RKIND + reductions = 0.0_RKIND + + sums(1) = blockSumRegionIceArea(iRegion) + sums(2) = blockSumRegionIceVolume(iRegion) + sums(3) = blockSumRegionGroundedIceArea(iRegion) + sums(4) = blockSumRegionGroundedIceVolume(iRegion) + sums(5) = blockSumRegionFloatingIceArea(iRegion) + sums(6) = blockSumRegionFloatingIceVolume(iRegion) + sums(7) = blockSumRegionSfcMassBal(iRegion) + sums(8) = blockSumRegionBasalMassBal(iRegion) + sums(9) = blockSumRegionGroundedBasalMassBal(iRegion) + sums(10) = blockSumRegionFloatingBasalMassBal(iRegion) + sums(11) = blockSumRegionCalvingFlux(iRegion) + sums(12) = blockSumRegionVAF(iRegion) + sums(13) = blockSumRegionGLflux(iRegion) + nVars = 13 + + call mpas_dmpar_sum_real_array(dminfo, nVars, sums(1:nVars), reductions(1:nVars)) + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'regionalStatsAM', regionalStatsAMPool) + + ! get values from regional stats pool + call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceArea', regionalIceArea) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceVolume', regionalIceVolume) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalVolumeAboveFloatation', regionalVolumeAboveFloatation) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalGroundedIceArea', regionalGroundedIceArea) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalGroundedIceVolume', regionalGroundedIceVolume) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalFloatingIceArea', regionalFloatingIceArea) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalFloatingIceVolume', regionalFloatingIceVolume) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceThicknessMean', regionalIceThicknessMean) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumSfcMassBal', regionalSumSfcMassBal) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalAvgNetAccumulation', regionalAvgNetAccumulation) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumBasalMassBal', regionalSumBasalMassBal) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumGroundedBasalMassBal', regionalSumGroundedBasalMassBal) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalAvgGroundedBasalMelt', regionalAvgGroundedBasalMelt) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumFloatingBasalMassBal', regionalSumFloatingBasalMassBal) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalAvgSubshelfMelt', regionalAvgSubshelfMelt) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumCalvingFlux', regionalSumCalvingFlux) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSumGroundingLineFlux', regionalSumGroundingLineFlux) + + regionalIceArea(iRegion) = reductions(1) + regionalIceVolume(iRegion) = reductions(2) + regionalGroundedIceArea(iRegion) = reductions(3) + regionalGroundedIceVolume(iRegion) = reductions(4) + regionalFloatingIceArea(iRegion) = reductions(5) + regionalFloatingIceVolume(iRegion) = reductions(6) + regionalSumSfcMassBal(iRegion) = reductions(7) + regionalSumBasalMassBal(iRegion) = reductions(8) + regionalSumGroundedBasalMassBal(iRegion) = reductions(9) + regionalSumFloatingBasalMassBal(iRegion) = reductions(10) + regionalSumCalvingFlux(iRegion) = reductions(11) + regionalVolumeAboveFloatation(iRegion) = reductions(12) + regionalSumGroundingLineFlux(iRegion) = reductions(13) + + regionalIceThicknessMean(iRegion) = regionalIceVolume(iRegion) / regionalIceArea(iRegion) + regionalAvgNetAccumulation(iRegion) = regionalSumSfcMassBal(iRegion) / regionalIceArea(iRegion) / rhoi + regionalAvgGroundedBasalMelt(iRegion) = -1.0_RKIND * regionalSumGroundedBasalMassBal(iRegion) / & + regionalGroundedIceArea(iRegion) / rhoi + regionalAvgSubshelfMelt(iRegion) = -1.0_RKIND * regionalSumFloatingBasalMassBal(iRegion) / & + regionalFloatingIceArea(iRegion) / rhoi + + block => block % next + end do + + ! --------------------------- + ! compute mins + ! --------------------------- + mins = 0.0_RKIND + reductions = 0.0_RKIND + mins(1) = blockRegionThickMin(iRegion) + nVars = 1 + call mpas_dmpar_min_real_array(dminfo, nVars, mins(1:nVars), reductions(1:nVars)) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'regionalStatsAM', regionalStatsAMPool) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceThicknessMin', regionalIceThicknessMin) + regionalIceThicknessMin(iRegion) = reductions(1) + block => block % next + end do + + ! --------------------------- + ! compute maxes + ! --------------------------- + maxes = 0.0_RKIND + reductions = 0.0_RKIND + maxes(1) = blockRegionThickMax(iRegion) + maxes(2) = blockRegionMaxSurfaceSpeed(iRegion)* scyr ! convert units to match Registry + maxes(3) = blockRegionMaxBasalSpeed(iRegion)* scyr ! convert units to match Registry + nVars = 3 + call mpas_dmpar_max_real_array(dminfo, nVars, maxes(1:nVars), reductions(1:nVars)) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'regionalStatsAM', regionalStatsAMPool) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalIceThicknessMax', regionalIceThicknessMax) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalSurfaceSpeedMax', regionalSurfaceSpeedMax) + call mpas_pool_get_array(regionalStatsAMPool, 'regionalBasalSpeedMax', regionalBasalSpeedMax) + regionalIceThicknessMax(iRegion) = reductions(1) + regionalSurfaceSpeedMax(iRegion) = reductions(2) + regionalBasalSpeedMax(iRegion) = reductions(3) + block => block % next + end do + + end do ! loop over regions + + ! deallocate storage for sums over blocks + deallocate(blockSumRegionIceArea); deallocate(blockSumRegionIceVolume) + deallocate(blockSumRegionVAF) + deallocate(blockSumRegionGroundedIceArea); deallocate(blockSumRegionGroundedIceVolume) + deallocate(blockSumRegionFloatingIceArea); deallocate(blockSumRegionFloatingIceVolume) + deallocate(blockRegionThickMin); deallocate(blockRegionThickMax) + deallocate(blockRegionMaxBasalSpeed); deallocate(blockRegionMaxSurfaceSpeed) + deallocate(blockSumRegionSfcMassBal); deallocate(blockSumRegionBasalMassBal) + deallocate(blockSumRegionGroundedBasalMassBal); deallocate(blockSumRegionFloatingBasalMassBal) + deallocate(blockSumRegionCalvingFlux); deallocate(blockSumRegionGLflux) + + end subroutine li_compute_regional_stats!}}} + +!*********************************************************************** +! +! routine li_restart_regional_stats +! +!> \brief Save restart for MPAS-Land Ice analysis member +!> \author S. Price +!> \date 9/9/2015 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_restart_regional_stats(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_restart_regional_stats!}}} + +!*********************************************************************** +! +! routine li_finalize_regional_stats +! +!> \brief Finalize MPAS-Land Ice analysis member +!> \author S. Price +!> \date 9/9/2015 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Land Ice analysis member. +! +!----------------------------------------------------------------------- + + subroutine li_finalize_regional_stats(domain, memberName, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: memberName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine li_finalize_regional_stats!}}} + +end module li_regional_stats + +! vim: foldmethod=marker diff --git a/src/core_landice/build_options.mk b/src/core_landice/build_options.mk index ccccacd5dc..fb4a7d1855 100644 --- a/src/core_landice/build_options.mk +++ b/src/core_landice/build_options.mk @@ -1,7 +1,49 @@ -PWD=$(shell pwd) +ifeq "$(ROOT_DIR)" "" + ROOT_DIR=$(shell pwd)/src +endif EXE_NAME=landice_model NAMELIST_SUFFIX=landice +FCINCLUDES += -I$(ROOT_DIR)/core_landice/mode_forward -I$(ROOT_DIR)/core_landice/shared -I$(ROOT_DIR)/core_landice/analysis_members override CPPFLAGS += -DCORE_LANDICE +# =================================== +# Check if building with LifeV, Albany, and/or PHG external libraries + +# LifeV can solve L1L2 or FO +ifeq "$(LIFEV)" "true" + EXTERNAL_DYCORE_FLAG += -DLIFEV + EXTERNAL_DYCORE_FLAG += -DUSE_EXTERNAL_L1L2 + EXTERNAL_DYCORE_FLAG += -DUSE_EXTERNAL_FIRSTORDER + EXTERNAL_DYCORE_FLAG += -DMPAS_LI_BUILD_INTERFACE +endif # LIFEV IF + +# Albany can only solve FO at present +ifeq "$(ALBANY)" "true" + EXTERNAL_DYCORE_FLAG += -DUSE_EXTERNAL_FIRSTORDER + EXTERNAL_DYCORE_FLAG += -DMPAS_LI_BUILD_INTERFACE +endif # ALBANY IF + +# Currently LifeV AND Albany is not allowed +ifeq "$(LIFEV)" "true" +ifeq "$(ALBANY)" "true" + $(error Compiling with both LifeV and Albany is not allowed at this time.) +endif +endif + +# PHG currently requires LifeV +ifeq "$(PHG)" "true" +ifneq "$(LIFEV)" "true" + $(error Compiling with PHG requires LifeV at this time.) +endif +endif +# PHG can only Stokes at present +ifeq "$(PHG)" "true" + EXTERNAL_DYCORE_FLAG += -DUSE_EXTERNAL_STOKES + EXTERNAL_DYCORE_FLAG += -DMPAS_LI_BUILD_INTERFACE +endif # PHG IF + +override CPPFLAGS += $(EXTERNAL_DYCORE_FLAG) +# =================================== + report_builds: @echo "CORE=landice" diff --git a/src/core_landice/external_albany_version b/src/core_landice/external_albany_version new file mode 100644 index 0000000000..4f00bb26c2 --- /dev/null +++ b/src/core_landice/external_albany_version @@ -0,0 +1 @@ +d3968fc9e9b4f825f87d31564ea67d3e4fab656b diff --git a/src/core_landice/external_trilinos_version b/src/core_landice/external_trilinos_version new file mode 100644 index 0000000000..a7fd78f3de --- /dev/null +++ b/src/core_landice/external_trilinos_version @@ -0,0 +1 @@ +9630bc5b179a0c5e1537923e3450ff79bb3ae78d diff --git a/src/core_landice/mode_forward/Interface_velocity_solver.cpp b/src/core_landice/mode_forward/Interface_velocity_solver.cpp new file mode 100644 index 0000000000..505268788b --- /dev/null +++ b/src/core_landice/mode_forward/Interface_velocity_solver.cpp @@ -0,0 +1,2290 @@ +/* +Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +and the University Corporation for Atmospheric Research (UCAR). + +Unless noted otherwise source code is licensed under the BSD license. +Additional copyright and license information can be found in the LICENSE file +distributed with this code, or at http://mpas-dev.github.com/license.html +*/ + +// =================================================== +//! Includes +// =================================================== + +#include +#include +#include +#include "Interface_velocity_solver.hpp" +//#include +//#include +//#include + +// =================================================== +//! Namespaces +// =================================================== + +//typedef std::list exchangeList_Type; + +// ice_problem pointer + +int Ordering = 0; //ordering ==0 means that the mesh is extruded layerwise, whereas ordering==1 means that the mesh is extruded columnwise. +MPI_Comm comm, reducedComm; +bool isDomainEmpty = true; +bool initialize_velocity = true; +bool first_time_step = true; +int nCells_F, nEdges_F, nVertices_F; +int nCellsSolve_F, nEdgesSolve_F, nVerticesSolve_F; +int nVertices, nEdges, nTriangles, nGlobalVertices, nGlobalEdges, + nGlobalTriangles; +int maxNEdgesOnCell_F; +int const *cellsOnEdge_F, *cellsOnVertex_F, *verticesOnCell_F, + *verticesOnEdge_F, *edgesOnCell_F, *indexToCellID_F, *nEdgesOnCells_F, + *verticesMask_F, *cellsMask_F, *dirichletCellsMask_F, *floatingEdgesMask_F; +std::vector layersRatio, levelsNormalizedThickness; +int nLayers; +double const *xCell_F, *yCell_F, *zCell_F, *xVertex_F, *yVertex_F, *zVertex_F, *areaTriangle_F; +std::vector xCellProjected, yCellProjected, zCellProjected; +const double unit_length = 1000; +const double T0 = 273.15; +const double secondsInAYear = 31536000.0; // This may vary slightly in MPAS, but this should be close enough for how this is used. +double minThickness = 1e-3; //[km] +const double minBeta = 1e-5; +double rho_ice; +double rho_ocean; +//unsigned char dynamic_ice_bit_value; +//unsigned char ice_present_bit_value; +int dynamic_ice_bit_value; +int ice_present_bit_value; + +// global variables used for handling logging +char albany_log_filename[128]; +int original_stdout; // the location of stdout before we captured it +int original_stderr; // the location of stderr before we captured it +int Interface_stdout; // the location of stdout as we use it here + +//void *phgGrid = 0; +std::vector edgesToReceive, fCellsToReceive, indexToTriangleID, + verticesOnTria, trianglesOnEdge, trianglesPositionsOnEdge, verticesOnEdge, + trianglesProcIds, reduced_ranks; +std::vector indexToVertexID, vertexToFCell, triangleToFVertex, indexToEdgeID, edgeToFEdge, + mask, fVertexToTriangleID, fCellToVertex, floatingEdgesIds, dirichletNodesIDs; +std::vector temperatureOnTetra, dissipationHeatOnTetra, velocityOnVertices, velocityOnCells, + elevationData, thicknessData, betaData, bedTopographyData, temperatureData, smbData, thicknessOnCells; +std::vector isVertexBoundary, isBoundaryEdge; + +// only needed for creating ASCII mesh +std::vector thicknessUncertaintyData; +std::vector smbUncertaintyData; +std::vector bmbData, bmbUncertaintyData; +std::vector observedVeloXData, observedVeloYData, observedVeloUncertaintyData; +std::vector observedDHDtData, observedDHDtUncertaintyData; +std::vector indexToCellIDData; + +int numBoundaryEdges; +double radius; + +exchangeList_Type const *sendCellsList_F = 0, *recvCellsList_F = 0; +exchangeList_Type const *sendEdgesList_F = 0, *recvEdgesList_F = 0; +exchangeList_Type const *sendVerticesList_F = 0, *recvVerticesList_F = 0; +exchangeList_Type sendCellsListReversed, recvCellsListReversed, + sendEdgesListReversed, recvEdgesListReversed; + +exchange::exchange(int _procID, int const* vec_first, int const* vec_last, + int fieldDim) : + procID(_procID), vec(vec_first, vec_last), buffer( + fieldDim * (vec_last - vec_first)), doubleBuffer( + fieldDim * (vec_last - vec_first)) { +} + +extern "C" { + +// =================================================== +//! Interface functions +// =================================================== + +int velocity_solver_init_mpi(int* fComm) { + // get MPI_Comm from Fortran + comm = MPI_Comm_f2c(*fComm); + reducedComm = MPI_COMM_NULL; // initialize to null so we can check if set + + return 0; +} + + +void velocity_solver_set_parameters(double const* gravity_F, double const* ice_density_F, double const* ocean_density_F, + double const* sea_level_F, double const* flowParamA_F, + double const* enhancementFactor_F, double const* flowLawExponent_F, double const* dynamic_thickness_F, + double const* clausius_clapeyron_coeff, + int const* li_mask_ValueDynamicIce, int const* li_mask_ValueIce, + bool const* use_GLP_F) { + // This function sets parameter values used by MPAS on the C/C++ side + rho_ice = *ice_density_F; + rho_ocean = *ocean_density_F; + dynamic_ice_bit_value = *li_mask_ValueDynamicIce; + ice_present_bit_value = *li_mask_ValueIce; + velocity_solver_set_physical_parameters__(*gravity_F, rho_ice, *ocean_density_F, *sea_level_F/unit_length, *flowParamA_F*std::pow(unit_length,4)*secondsInAYear, + *enhancementFactor_F, *flowLawExponent_F, *dynamic_thickness_F/unit_length, *use_GLP_F, *clausius_clapeyron_coeff); +} + + + +void velocity_solver_export_2d_data(double const* lowerSurface_F, + double const* thickness_F, double const* beta_F) { + if (isDomainEmpty) + return; +#ifdef LIFEV + import2DFields(lowerSurface_F, thickness_F, beta_F, minThickneess); + velocity_solver_export_2d_data__(reducedComm, elevationData, thicknessData, + betaData, indexToVertexID); +#endif +} + +void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, + int const* _nVertices_F, int const* _nLevels, int const* _nCellsSolve_F, + int const* _nEdgesSolve_F, int const* _nVerticesSolve_F, + int const* _maxNEdgesOnCell_F, double const* radius_F, + int const* _cellsOnEdge_F, int const* _cellsOnVertex_F, + int const* _verticesOnCell_F, int const* _verticesOnEdge_F, + int const* _edgesOnCell_F, int const* _nEdgesOnCells_F, + int const* _indexToCellID_F, + double const* _xCell_F, double const* _yCell_F, double const* _zCell_F, + double const* _xVertex_F, double const* _yVertex_F, double const* _zVertex_F, + double const* _areaTriangle_F, + int const* sendCellsArray_F, int const* recvCellsArray_F, + int const* sendEdgesArray_F, int const* recvEdgesArray_F, + int const* sendVerticesArray_F, int const* recvVerticesArray_F) { + + nCells_F = *_nCells_F; + nEdges_F = *_nEdges_F; + nVertices_F = *_nVertices_F; + nLayers = *_nLevels-1; + nCellsSolve_F = *_nCellsSolve_F; + nEdgesSolve_F = *_nEdgesSolve_F; + nVerticesSolve_F = *_nVerticesSolve_F; + maxNEdgesOnCell_F = *_maxNEdgesOnCell_F; + radius = *radius_F; + cellsOnEdge_F = _cellsOnEdge_F; + cellsOnVertex_F = _cellsOnVertex_F; + verticesOnCell_F = _verticesOnCell_F; + verticesOnEdge_F = _verticesOnEdge_F; + edgesOnCell_F = _edgesOnCell_F; + nEdgesOnCells_F = _nEdgesOnCells_F; + indexToCellID_F = _indexToCellID_F; + xCell_F = _xCell_F; + yCell_F = _yCell_F; + zCell_F = _zCell_F; + xVertex_F = _xVertex_F; + yVertex_F = _yVertex_F; + zVertex_F = _zVertex_F; + areaTriangle_F = _areaTriangle_F; + mask.resize(nVertices_F); + + thicknessOnCells.resize(nCellsSolve_F); + + sendCellsList_F = new exchangeList_Type(unpackMpiArray(sendCellsArray_F)); + recvCellsList_F = new exchangeList_Type(unpackMpiArray(recvCellsArray_F)); + sendEdgesList_F = new exchangeList_Type(unpackMpiArray(sendEdgesArray_F)); + recvEdgesList_F = new exchangeList_Type(unpackMpiArray(recvEdgesArray_F)); + sendVerticesList_F = new exchangeList_Type( + unpackMpiArray(sendVerticesArray_F)); + recvVerticesList_F = new exchangeList_Type( + unpackMpiArray(recvVerticesArray_F)); + + trianglesProcIds.resize(nVertices_F); + getProcIds(trianglesProcIds, recvVerticesList_F); + + if (radius > 10) { + xCellProjected.resize(nCells_F); + yCellProjected.resize(nCells_F); + zCellProjected.assign(nCells_F, 0.); + for (int i = 0; i < nCells_F; i++) { + double r = std::sqrt( + xCell_F[i] * xCell_F[i] + yCell_F[i] * yCell_F[i] + + zCell_F[i] * zCell_F[i]); + xCellProjected[i] = radius * std::asin(xCell_F[i] / r); + yCellProjected[i] = radius * std::asin(yCell_F[i] / r); + } + xCell_F = &xCellProjected[0]; + yCell_F = &yCellProjected[0]; + zCell_F = &zCellProjected[0]; + } +} + +void velocity_solver_init_l1l2(double const* levelsRatio_F) { +#ifdef LIFEV + velocityOnVertices.resize(2 * nVertices * (nLayers + 1), 0.); + velocityOnCells.resize(2 * nCells_F * (nLayers + 1), 0.); + + if (isDomainEmpty) + return; + + layersRatio.resize(nLayers); + // !!Indexing of layers is reversed + for (int i = 0; i < nLayers; i++) + layersRatio[i] = levelsRatio_F[nLayers - 1 - i]; + //std::copy(levelsRatio_F, levelsRatio_F+nLayers, layersRatio.begin()); + mapCellsToVertices(velocityOnCells, velocityOnVertices, 2, nLayers, Ordering); + + velocity_solver_init_l1l2__(layersRatio, velocityOnVertices, initialize_velocity); + initialize_velocity = false; +#endif +} + + + + +void velocity_solver_solve_l1l2(double const* lowerSurface_F, + double const* thickness_F, double const* beta_F, double const* temperature_F, + double* const dirichletVelocityXValue, double* const dirichletVelocitYValue, + double* u_normal_F, double* xVelocityOnCell, double* yVelocityOnCell) { + +#ifdef LIFEV + + std::fill(u_normal_F, u_normal_F + nEdges_F * (nLayers+1), 0.); + + double localSum(0), sum(0); + + for (int i = 0; i < nCellsSolve_F; i++) { + localSum = std::max(localSum, + std::fabs(thickness_F[i] - thicknessOnCells[i])); + } + + MPI_Allreduce(&localSum, &sum, 1, MPI_DOUBLE, MPI_MAX, comm); + + std::cout << "Thickness change: " << sum << std::endl; + std::copy(thickness_F, thickness_F + nCellsSolve_F, &thicknessOnCells[0]); + + if (!isDomainEmpty) { + std::vector temperatureData(nLayers * nVertices); + + import2DFields(lowerSurface_F, thickness_F, beta_F, minThickness); + + for (int index = 0; index < nVertices; index++) { + int iCell = vertexToFCell[index]; + for (int il = 0; il < nLayers; il++) { + temperatureData[index + il * nVertices] = temperature_F[iCell * nLayers + + (nLayers - il - 1)]; + } + } + + + velocity_solver_solve_l1l2__(elevationData, thicknessData, betaData, + temperatureData, indexToVertexID, velocityOnVertices); + } + + + mapVerticesToCells (velocityOnVertices, &velocityOnCells[0], 2, nLayers, Ordering); + + //computing x, yVelocityOnCell + int sizeVelOnCell = nCells_F * (nLayers + 1); + for(int iCell=0; iCell regulThk(thicknessData); + for (int index = 0; index < nVertices; index++) + regulThk[index] = std::max(1e-4, thicknessData[index]); + + std::vector mpasIndexToVertexID(nVertices); + for (int i = 0; i < nVertices; i++) { + mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; + } +#ifdef LIFEV + velocity_solver_export_l1l2_velocity__(layersRatio, elevationData, regulThk, mpasIndexToVertexID, reducedComm); +#endif +} + +void velocity_solver_init_fo(double const *levelsRatio_F) { + + velocityOnVertices.resize(2 * nVertices * (nLayers + 1), 0.); + velocityOnCells.resize(2 * nCells_F * (nLayers + 1), 0.); + + if (isDomainEmpty) + return; + + layersRatio.resize(nLayers); + // !!Indexing of layers is reversed + for (int i = 0; i < nLayers; i++) + layersRatio[i] = levelsRatio_F[nLayers - 1 - i]; + //std::copy(levelsRatio_F, levelsRatio_F+nLayers, layersRatio.begin()); + + + mapCellsToVertices(velocityOnCells, velocityOnVertices, 2, nLayers, Ordering); + +#ifdef LIFEV + velocity_solver_init_fo__(layersRatio, velocityOnVertices, indexToVertexID, initialize_velocity); +#endif + // iceProblemPtr->initializeSolverFO(layersRatio, velocityOnVertices, thicknessData, elevationData, indexToVertexID, initialize_velocity); + initialize_velocity = false; +} + +void velocity_solver_solve_fo(double const* bedTopography_F, double const* lowerSurface_F, + double const* thickness_F, double const* beta_F, + double const* smb_F, double const* temperature_F, + double* const dirichletVelocityXValue, double* const dirichletVelocitYValue, + double* u_normal_F, double* dissipation_heat_F, + double* xVelocityOnCell, double* yVelocityOnCell, double const* deltat, + int *error) { + + std::fill(u_normal_F, u_normal_F + nEdges_F * (nLayers+1), 0.); + //import velocity from initial guess and from dirichlet values. + int sizeVelOnCell = nCells_F * (nLayers + 1); + for(int iCell=0; iCell bdExtensionMap; + import2DFields(bdExtensionMap, bedTopography_F, lowerSurface_F, thickness_F, beta_F, temperature_F, smb_F, minThickness); + + std::vector regulThk(thicknessData); + for (int index = 0; index < nVertices; index++) + regulThk[index] = std::max(1e-4, thicknessData[index]); + + importP0Temperature(); + + dissipationHeatOnTetra.resize(3 * nLayers * indexToTriangleID.size()); + + std::cout << "\n\nTimeStep: "<< *deltat << "\n\n"<< std::endl; + + double dt = (*deltat)/secondsInAYear; + int albany_error; + velocity_solver_solve_fo__(nLayers, nGlobalVertices, nGlobalTriangles, + Ordering, first_time_step, indexToVertexID, indexToTriangleID, minBeta, + regulThk, levelsNormalizedThickness, elevationData, thicknessData, + betaData, bedTopographyData, smbData, + temperatureOnTetra, dissipationHeatOnTetra, velocityOnVertices, + albany_error, dt); + *error=albany_error; + } + + exportDissipationHeat(dissipation_heat_F); + + std::vector mpasIndexToVertexID(nVertices); + for (int i = 0; i < nVertices; i++) { + mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; + } + + mapVerticesToCells(velocityOnVertices, &velocityOnCells[0], 2, nLayers, + Ordering); + + //computing x, yVelocityOnCell + for(int iCell=0; iCell velOnEdges(nEdges * (nLayers+1)); + for (int i = 0; i < nEdges; i++) { + for (int il = 0; il < nLayers+1; il++) { + velOnEdges[i * (nLayers+1) + il] = u_normal_F[edgeToFEdge[i] * (nLayers+1) + il]; + } + } + + allToAll(u_normal_F, &sendEdgesListReversed, &recvEdgesListReversed, nLayers+1); + + allToAll(u_normal_F, sendEdgesList_F, recvEdgesList_F, nLayers+1); + + first_time_step = false; + +#ifdef LIFEV + + std::vector edgesProcId(nEdges_F), trianglesProcIds(nVertices_F); + getProcIds(edgesProcId, recvEdgesList_F); + getProcIds(trianglesProcIds, recvVerticesList_F); + + int localSumInt(0), sumInt(0); + + for (int i = 0; i < nEdges; i++) { + for (int il = 0; il < 1; il++) { + if (std::fabs( + velOnEdges[i * (nLayers+1) + il] + - u_normal_F[edgeToFEdge[i] * nLayers + il]) > 1e-9) + // if(edgeToFEdge[i]>nEdgesSolve_F) + { + localSumInt++; + int edge = edgeToFEdge[i]; + int gEdge = indexToEdgeID[i]; + ID fVertex0 = verticesOnEdge_F[2 * edge] - 1; + ID fVertex1 = verticesOnEdge_F[2 * edge + 1] - 1; + ID triaId0 = fVertexToTriangleID[fVertex0]; + ID triaId1 = fVertexToTriangleID[fVertex1]; + ID procTria0 = trianglesProcIds[fVertex0]; + ID procTria1 = trianglesProcIds[fVertex1]; + std::cout << "vs( " << velOnEdges[i * (nLayers+1) + il] << ", " + << u_normal_F[edgeToFEdge[i] * nLayers + il] << ") "; + std::cout << "edge: " << edge << ", gEdge: " << gEdge << ", on proc: " + << edgesProcId[edgeToFEdge[i]]; + if (triaId0 != NotAnId) { + std::cout << ". first tria0: " << triaId0 << " on proc: " + << procTria0; + } + if (triaId1 != NotAnId) { + std::cout << ".. second tria0:" << std::endl; + } + if ((triaId0 == NotAnId) || (triaId1 == NotAnId)) { + std::cout << ". and to Tria: " << triaId1 << " on proc: " << procTria1 + << std::endl; + } + + } + + //localSum = std::max(localSum, std::fabs(velOnEdges[i*nLayers+il] - u_normal_F[edgeToFEdge[i]*nLayers+il])); + } + } + + MPI_Allreduce(&localSumInt, &sumInt, 1, MPI_INT, MPI_SUM, comm); + + int localNum(sendEdgesListReversed.size()), num(0); + + MPI_Allreduce(&localNum, &num, 1, MPI_INT, MPI_SUM, comm); + + std::cout << "Edges change: " << sumInt << " " << num << std::endl; + +#endif + + + +} + + +void velocity_solver_export_fo_velocity() { + + if (isDomainEmpty) + return; + + velocity_solver_export_fo_velocity__(reducedComm); +} + +void velocity_solver_finalize() { + velocity_solver_finalize__(); + delete sendCellsList_F; + delete recvCellsList_F; + delete sendEdgesList_F; + delete recvEdgesList_F; + delete sendVerticesList_F; + delete recvVerticesList_F; +} + +/*duality: + * + * mpas(F) | lifev + * ---------|--------- + * cell | vertex + * vertex | triangle + * edge | edge + * + */ + +void velocity_solver_compute_2d_grid(int const* _verticesMask_F, int const* _cellsMask_F, int const* _dirichletCellsMask_F, int const* _floatingEdgesMask_F) { + int numProcs, me; + + verticesMask_F = _verticesMask_F; + cellsMask_F = _cellsMask_F; + verticesMask_F = _verticesMask_F; + dirichletCellsMask_F = _dirichletCellsMask_F; + floatingEdgesMask_F = _floatingEdgesMask_F; + + MPI_Comm_size(comm, &numProcs); + MPI_Comm_rank(comm, &me); + std::vector partialOffset(numProcs + 1), globalOffsetTriangles( + numProcs + 1), globalOffsetVertices(numProcs + 1), globalOffsetEdge( + numProcs + 1); + + triangleToFVertex.clear(); + triangleToFVertex.reserve(nVertices_F); + std::vector fVertexToTriangle(nVertices_F, NotAnId); + bool changed = false; + for (int i(0); i < nVerticesSolve_F; i++) { + if ((verticesMask_F[i] & dynamic_ice_bit_value) && !isGhostTriangle(i)) { + fVertexToTriangle[i] = triangleToFVertex.size(); + triangleToFVertex.push_back(i); + } + changed = changed || (verticesMask_F[i] != mask[i]); + } + + for (int i(0); i < nVertices_F; i++) + mask[i] = verticesMask_F[i]; + + if (changed) + std::cout << "mask changed!!" << std::endl; + + if ((me == 0) && (triangleToFVertex.size() == 0)) + for (int i(0); i < nVerticesSolve_F; i++) { + if (!isGhostTriangle(i)) { + fVertexToTriangle[i] = triangleToFVertex.size(); + triangleToFVertex.push_back(i); + break; + } + } + + nTriangles = triangleToFVertex.size(); + + initialize_iceProblem(nTriangles); + + //Compute the global number of triangles, and the localOffset on the local processor, such that a globalID = localOffset + index + int localOffset(0); + nGlobalTriangles = 0; + computeLocalOffset(nTriangles, localOffset, nGlobalTriangles); + + //Communicate the globalIDs, computed locally, to the other processors. + indexToTriangleID.resize(nTriangles); + + //To make local, not used + fVertexToTriangleID.assign(nVertices_F, NotAnId); + // std::vector fVertexToTriangleID(nVertices_F, NotAnId); + for (int index(0); index < nTriangles; index++) + fVertexToTriangleID[triangleToFVertex[index]] = index + localOffset; + + allToAll(fVertexToTriangleID, sendVerticesList_F, recvVerticesList_F); + + for (int index(0); index < nTriangles; index++) + indexToTriangleID[index] = fVertexToTriangleID[triangleToFVertex[index]]; + + //Compute triangle edges + std::vector fEdgeToEdge(nEdges_F), edgesToSend, trianglesProcIds( + nVertices_F); + getProcIds(trianglesProcIds, recvVerticesList_F); + + int interfaceSize(0); + + std::vector fEdgeToEdgeID(nEdges_F, NotAnId); + edgesToReceive.clear(); + edgeToFEdge.clear(); + isBoundaryEdge.clear(); + trianglesOnEdge.clear(); + + edgesToReceive.reserve(nEdges_F - nEdgesSolve_F); + edgeToFEdge.reserve(nEdges_F); + trianglesOnEdge.reserve(nEdges_F * 2); + edgesToSend.reserve(nEdgesSolve_F); + isBoundaryEdge.reserve(nEdges_F); + + //first, we compute boundary edges (boundary edges must be the first edges) + for (int i = 0; i < nEdges_F; i++) { + ID fVertex1(verticesOnEdge_F[2 * i] - 1), fVertex2( + verticesOnEdge_F[2 * i + 1] - 1); + ID triaId_1 = fVertexToTriangleID[fVertex1]; + ID triaId_2 = fVertexToTriangleID[fVertex2]; + bool isboundary = (triaId_1 == NotAnId) || (triaId_2 == NotAnId); + + ID iTria1 = fVertexToTriangle[fVertex1]; + ID iTria2 = fVertexToTriangle[fVertex2]; + if (iTria1 == NotAnId) + std::swap(iTria1, iTria2); + bool belongsToLocalTriangle = (iTria1 != NotAnId) || (iTria2 != NotAnId); + + if (belongsToLocalTriangle) { + if (isboundary) { + fEdgeToEdge[i] = edgeToFEdge.size(); + edgeToFEdge.push_back(i); + trianglesOnEdge.push_back(iTria1); + trianglesOnEdge.push_back(iTria2); + isBoundaryEdge.push_back(true); + } else + interfaceSize += (iTria2 == NotAnId); + } + } + + numBoundaryEdges = edgeToFEdge.size(); + + //procOnInterfaceEdge contains the pairs . + std::vector < std::pair > procOnInterfaceEdge; + procOnInterfaceEdge.reserve(interfaceSize); + + //then, we compute the other edges + for (int i = 0; i < nEdges_F; i++) { + + ID fVertex1(verticesOnEdge_F[2 * i] - 1), fVertex2( + verticesOnEdge_F[2 * i + 1] - 1); + ID iTria1 = fVertexToTriangle[fVertex1]; + ID iTria2 = fVertexToTriangle[fVertex2]; + + ID triaId_1 = fVertexToTriangleID[fVertex1]; //global Triangle + ID triaId_2 = fVertexToTriangleID[fVertex2]; //global Triangle + + if (iTria1 == NotAnId) { + std::swap(iTria1, iTria2); + std::swap(fVertex1, fVertex2); + } + + bool belongsToAnyTriangle = (triaId_1 != NotAnId) || (triaId_2 != NotAnId); + bool isboundary = (triaId_1 == NotAnId) || (triaId_2 == NotAnId); + bool belongsToLocalTriangle = (iTria1 != NotAnId); + bool isMine = i < nEdgesSolve_F; + + if (belongsToLocalTriangle && !isboundary) { + fEdgeToEdge[i] = edgeToFEdge.size(); + edgeToFEdge.push_back(i); + trianglesOnEdge.push_back(iTria1); + trianglesOnEdge.push_back(iTria2); + isBoundaryEdge.push_back(false); + if (iTria2 == NotAnId) + procOnInterfaceEdge.push_back( + std::make_pair(fEdgeToEdge[i], trianglesProcIds[fVertex2])); + } + + if (belongsToAnyTriangle && isMine) { + edgesToSend.push_back(i); + if (!belongsToLocalTriangle) + edgesToReceive.push_back(i); + } + + } + + //Compute the global number of edges, and the localOffset on the local processor, such that a globalID = localOffset + index + computeLocalOffset(edgesToSend.size(), localOffset, nGlobalEdges); + + //Communicate the globalIDs, computed locally, to the other processors. + for (ID index = 0; index < edgesToSend.size(); index++) + fEdgeToEdgeID[edgesToSend[index]] = index + localOffset; + + allToAll(fEdgeToEdgeID, sendEdgesList_F, recvEdgesList_F); + + nEdges = edgeToFEdge.size(); + indexToEdgeID.resize(nEdges); + floatingEdgesIds.clear(); + floatingEdgesIds.reserve(nEdges); + for (int index = 0; index < nEdges; index++) { + int fEdge = edgeToFEdge[index]; + indexToEdgeID[index] = fEdgeToEdgeID[fEdge]; + if((floatingEdgesMask_F[fEdge]!=0)&&(index fCellsToSend; + fCellsToSend.reserve(nCellsSolve_F); + + vertexToFCell.clear(); + vertexToFCell.reserve(nCells_F); + + fCellToVertex.assign(nCells_F, NotAnId); + std::vector fCellToVertexID(nCells_F, NotAnId); + + fCellsToReceive.clear(); + + // if(! isDomainEmpty) + // { + fCellsToReceive.reserve(nCells_F - nCellsSolve_F); + for (int i = 0; i < nCells_F; i++) { + bool isMine = i < nCellsSolve_F; + bool belongsToLocalTriangle = false; + bool belongsToAnyTriangle = false; + int nEdg = nEdgesOnCells_F[i]; + for (int j = 0; j < nEdg; j++) { + ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * i + j] - 1); + ID iTria = fVertexToTriangle[fVertex]; + ID triaId = fVertexToTriangleID[fVertex]; + belongsToLocalTriangle = belongsToLocalTriangle || (iTria != NotAnId); + belongsToAnyTriangle = belongsToAnyTriangle || (triaId != NotAnId); + } + + if (belongsToAnyTriangle && isMine) { + fCellsToSend.push_back(i); + if (!belongsToLocalTriangle) + fCellsToReceive.push_back(i); + } + + if (belongsToLocalTriangle) { + fCellToVertex[i] = vertexToFCell.size(); + vertexToFCell.push_back(i); + } + } + // } + + //Compute the global number of vertices, and the localOffset on the local processor, such that a globalID = localOffset + index + computeLocalOffset(fCellsToSend.size(), localOffset, nGlobalVertices); + + //Communicate the globalIDs, computed locally, to the other processors. + for (int index = 0; index < int(fCellsToSend.size()); index++) + fCellToVertexID[fCellsToSend[index]] = index + localOffset; + + allToAll(fCellToVertexID, sendCellsList_F, recvCellsList_F); + + nVertices = vertexToFCell.size(); + int vertexColumnShift = (Ordering == 1) ? 1 : nGlobalVertices; + int vertexLayerShift = (Ordering == 0) ? 1 : nLayers + 1; + + + std::cout << "\n nvertices: " << nVertices << " " << nGlobalVertices << "\n" + << std::endl; + + indexToVertexID.resize(nVertices); + dirichletNodesIDs.clear(); + dirichletNodesIDs.reserve(nVertices); //need to improve storage efficiency + for (int index = 0; index < nVertices; index++) { + int fCell = vertexToFCell[index]; + indexToVertexID[index] = fCellToVertexID[fCell]; + for(int il=0; il< nLayers+1; ++il) + { + int imask_F = il+(nLayers+1)*fCell; + if(dirichletCellsMask_F[imask_F]!=0) + dirichletNodesIDs.push_back((nLayers-il)*vertexColumnShift+indexToVertexID[index]*vertexLayerShift); + } + } + + createReverseCellsExchangeLists(sendCellsListReversed, recvCellsListReversed, + fVertexToTriangleID, fCellToVertexID); + + //construct the local vector vertices on triangles making sure the area is positive + verticesOnTria.resize(nTriangles * 3); + double x[3], y[3], z[3]; + for (int index = 0; index < nTriangles; index++) { + int iTria = triangleToFVertex[index]; + + for (int j = 0; j < 3; j++) { + int iCell = cellsOnVertex_F[3 * iTria + j] - 1; + verticesOnTria[3 * index + j] = fCellToVertex[iCell]; + x[j] = xCell_F[iCell]; + y[j] = yCell_F[iCell]; + // z[j] = zCell_F[iCell]; + } + if (signedTriangleArea(x, y) < 0) + std::swap(verticesOnTria[3 * index + 1], verticesOnTria[3 * index + 2]); + } + + //construct the local vector vertices on edges + trianglesPositionsOnEdge.resize(2 * nEdges); + isVertexBoundary.assign(nVertices, false); + + verticesOnEdge.resize(2 * nEdges); + + //contains the local id of a triangle and the global id of the edges of the triangle. + //dataForGhostTria[4*i] contains the triangle id + //dataForGhostTria[4*i+1+k] contains the global id of the edge (at position k = 0,1,2) of the triangle. + //Possible Optimization: for our purposes it would be enough to store two of the three edges of a triangle. + std::vector dataForGhostTria(nVertices_F * 4, NotAnId); + + //* + for (int iV = 0; iV < nVertices; iV++) { + int fCell = vertexToFCell[iV]; + int nEdg = nEdgesOnCells_F[fCell]; + int j = 0; + bool isBoundary; + do { + int fVertex = verticesOnCell_F[maxNEdgesOnCell_F * fCell + j++] - 1; + isBoundary = !(verticesMask_F[fVertex] & dynamic_ice_bit_value); + } while ((j < nEdg) && (!isBoundary)); + isVertexBoundary[iV] = isBoundary; + } + /*/ + for(int index=0; index verticesCoords(3 * nVertices); + + for (int index = 0; index < nVertices; index++) { + int iCell = vertexToFCell[index]; + verticesCoords[index * 3] = xCell_F[iCell] / unit_length; + verticesCoords[index * 3 + 1] = yCell_F[iCell] / unit_length; + verticesCoords[index * 3 + 2] = zCell_F[iCell] / unit_length; + } + + velocity_solver_compute_2d_grid__(nGlobalTriangles, + nGlobalVertices, nGlobalEdges, indexToVertexID, verticesCoords, + isVertexBoundary, verticesOnTria,isBoundaryEdge, trianglesOnEdge, + trianglesPositionsOnEdge, verticesOnEdge, indexToEdgeID, + indexToTriangleID, procOnInterfaceEdge ); +#else + velocity_solver_compute_2d_grid__(reducedComm); +#endif + + /* + + //initialize the mesh + iceProblemPtr->mesh2DPtr.reset (new RegionMesh() ); + + //construct the mesh nodes + constructNodes ( * (iceProblemPtr->mesh2DPtr), indexToVertexID, verticesCoords, isVertexBoundary, nGlobalVertices, 3); + + //construct the mesh elements + constructElements ( * (iceProblemPtr->mesh2DPtr), indexToTriangleID, verticesOnTria, nGlobalTriangles); + + //construct the mesh facets + constructFacets ( * (iceProblemPtr->mesh2DPtr), isBoundaryEdge, trianglesOnEdge, trianglesPositionsOnEdge, verticesOnEdge, indexToEdgeID, procOnInterfaceEdge, nGlobalEdges, 3); + + Switch sw; + std::vector elSign; + checkVolumes ( * (iceProblemPtr->mesh2DPtr), elSign, sw ); + */ +} + +void velocity_solver_extrude_3d_grid(double const* levelsRatio_F, + double const* lowerSurface_F, double const* thickness_F) { + + if (isDomainEmpty) + return; + + layersRatio.resize(nLayers); + // !!Indexing of layers is reversed + for (int i = 0; i < nLayers; i++) + layersRatio[i] = levelsRatio_F[nLayers - 1 - i]; + //std::copy(levelsRatio_F, levelsRatio_F+nLayers, layersRatio.begin()); + + levelsNormalizedThickness.resize(nLayers + 1); + + levelsNormalizedThickness[0] = 0; + for (int i = 0; i < nLayers; i++) + levelsNormalizedThickness[i + 1] = levelsNormalizedThickness[i] + + layersRatio[i]; + + std::vector mpasIndexToVertexID(nVertices); + for (int i = 0; i < nVertices; i++) + mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; + + //construct the local vector of coordinates + std::vector verticesCoords(3 * nVertices); + + for (int index = 0; index < nVertices; index++) { + int iCell = vertexToFCell[index]; + verticesCoords[index * 3] = xCell_F[iCell] / unit_length; + verticesCoords[index * 3 + 1] = yCell_F[iCell] / unit_length; + verticesCoords[index * 3 + 2] = zCell_F[iCell] / unit_length; + } + + velocity_solver_extrude_3d_grid__(nLayers, nGlobalTriangles, nGlobalVertices, + nGlobalEdges, Ordering, reducedComm, indexToVertexID, mpasIndexToVertexID, + verticesCoords, isVertexBoundary, verticesOnTria, isBoundaryEdge, + trianglesOnEdge, trianglesPositionsOnEdge, verticesOnEdge, indexToEdgeID, + indexToTriangleID, dirichletNodesIDs, floatingEdgesIds); + } + + +// Function to set up how the MPAS log file will be used by Albany +void interface_init_log(){ + int me, tasks; + MPI_Comm_rank(comm, &me); + MPI_Comm_size(comm, &tasks); + + char fname[128]; + char oformat[128]; + + strcpy(oformat, "log.albany."); + + if(tasks < 1E4) { + strcat(oformat, "%4.4i"); + } + else if (tasks < 1E5) { + strcat(oformat, "%5.5i"); + } + else if (tasks < 1E6) { + strcat(oformat, "%6.6i"); + } + else if (tasks < 1E7) { + strcat(oformat, "%7.7i");; + } + else if (tasks < 1E8) { + strcat(oformat, "%8.8i"); + } + else if (tasks < 1E9) { + strcat(oformat, "%9.9i"); + } + else { + if(me == 0){ + fprintf(stderr, "Error opening Albany stdout for 1E9 tasks or more\n"); + } + return; + } + strcat(oformat, ".out"); + + if (me == 0) { + sprintf(albany_log_filename, oformat, me); + } else { + strcpy(albany_log_filename, "/dev/null"); + } + + Interface_stdout = open(albany_log_filename, O_CREAT|O_WRONLY|O_TRUNC,0644); + if(Interface_stdout >= 0) { + write(Interface_stdout, "-- Beginning log file for output from Albany velocity solver --", 63); + write(Interface_stdout, " ", 1); + fsync(Interface_stdout); + } else { + std::cerr << "Error opening Albany stdout file." << std::endl; + } + +} + +// Function to redirect Albany stdout to the MPAS log file +void interface_redirect_stdout(int const* iTimestep) { + /* Save current stdout for use later */ + //fsync(Interface_stdout); + fflush(stdout); + fflush(stderr); + original_stdout = dup(1); + original_stderr = dup(2); + dup2(Interface_stdout, 1); + dup2(Interface_stdout, 2); + if (*iTimestep >= 0) { + std::cout << std::endl; + std::cout << std::endl; + std::cout << "--- Beginning Albany velocity solve for timestep " << *iTimestep << " ---" << std::endl; + std::cout << std::endl; + } +} + + +// Function to return to stdout to its previous location +void interface_reset_stdout() { + /* Restore stdout */ + //fsync(Interface_stdout); + fflush(stdout); + fflush(stderr); + dup2(original_stdout, 1); + dup2(original_stderr, 2); + close(original_stdout); + close(original_stderr); +} + + +} + +//This function computes the normal velocity on the edges midpoints of MPAS 2d cells. +//The function rely on the assumption that the locations of the edges midpoints are inside the union of the triangles that share the dual of that edge. +//The normal velocity is computed assuming to have linear/bilinear finite elements on the prisms. +//The normal velocity will be second order accurate for other finite elements (e.g. linear or quadratic on Tetrahedra). + +void get_prism_velocity_on_FEdges(double * uNormal, + const std::vector& velocityOnCells, + const std::vector& edgeToFEdge) { + + //using layout of velocityOnCells + int columnShift = 1; + int layerShift = (nLayers + 1); + + UInt nPoints3D = nCells_F * (nLayers + 1); + + // Loop over all edges of the triangulation - MPAS will decide which edges it should use. + for (int i = 0; i < nEdges; i++) { + + //identifying vertices on the edge + ID lId0 = verticesOnEdge[2 * i]; + ID lId1 = verticesOnEdge[2 * i + 1]; + int iCell0 = vertexToFCell[lId0]; + int iCell1 = vertexToFCell[lId1]; + + //computing normal to the cell edge (dual of triangular edge) + double nx = xCell_F[iCell1] - xCell_F[iCell0]; + double ny = yCell_F[iCell1] - yCell_F[iCell0]; + double n = sqrt(nx * nx + ny * ny); + nx /= n; + ny /= n; + + //identifying triangles that shares the edge + ID iEdge = edgeToFEdge[i]; + ID fVertex0 = verticesOnEdge_F[2 * iEdge] - 1; + ID fVertex1 = verticesOnEdge_F[2 * iEdge + 1] - 1; + double t0[2*3], t1[2*3]; //t0[0] contains the x-coords of vertices of triangle 0 and t0[1] its y-coords. + for (int j = 0; j < 3; j++) { + int iCell = cellsOnVertex_F[3 * fVertex0 + j] - 1; + t0[0 + 2 * j] = xCell_F[iCell]; + t0[1 + 2 * j] = yCell_F[iCell]; + iCell = cellsOnVertex_F[3 * fVertex1 + j] - 1; + t1[0 + 2 * j] = xCell_F[iCell]; + t1[1 + 2 * j] = yCell_F[iCell]; + } + + //getting triangle circumcenters (vertices of MPAS cells). + double bcoords[3]; + + ID iCells[3]; //iCells[k] is the array of cells indexes of triangle k on iEdge + + //computing midpoint of fortran edge + double e_mid[2]; + e_mid[0] = 0.5*(xVertex_F[fVertex0] + xVertex_F[fVertex1]); + e_mid[1] = 0.5*(yVertex_F[fVertex0] + yVertex_F[fVertex1]); + + if((verticesMask_F[fVertex0] & dynamic_ice_bit_value) && belongToTria(e_mid, t0, bcoords)) { + // triangle1 is in the mesh AND midpoint is in triangle1 + for (int j = 0; j < 3; j++) + iCells[j] = cellsOnVertex_F[3 * fVertex0 + j] - 1; + } + else if((verticesMask_F[fVertex1] & dynamic_ice_bit_value) && belongToTria(e_mid, t1, bcoords)) { + //triangle2 is in the mesh AND midpoint is in triangle2 + for (int j = 0; j < 3; j++) + iCells[j] = cellsOnVertex_F[3 * fVertex1 + j] - 1; + } + else if(i& velocityOnVertices, + double* velocityOnCells, int fieldDim, int numLayers, int ordering) { + int lVertexColumnShift = (ordering == 1) ? 1 : nVertices; + int vertexLayerShift = (ordering == 0) ? 1 : numLayers + 1; + + int nVertices3D = nVertices * (numLayers + 1); + + // 0 entire field so no values from previous solve are left behind + // if the ice extent has retreated. + std::fill(velocityOnCells, velocityOnCells + nCells_F * (numLayers+1) * fieldDim, 0.); + + for (UInt j = 0; j < nVertices3D; ++j) { + int ib = (ordering == 0) * (j % lVertexColumnShift) + + (ordering == 1) * (j / vertexLayerShift); + int il = (ordering == 0) * (j / lVertexColumnShift) + + (ordering == 1) * (j % vertexLayerShift); + + int iCell = vertexToFCell[ib]; + int cellIndex = iCell * (numLayers + 1) + il; + int vertexIndex = j; + for (int dim = 0; dim < fieldDim; dim++) { + velocityOnCells[cellIndex] = velocityOnVertices[vertexIndex]; + cellIndex += nCells_F * (numLayers + 1); + vertexIndex += nVertices3D; + } + } + + for (int dim = 0; dim < fieldDim; dim++) { + allToAll(&velocityOnCells[dim * nCells_F * (numLayers + 1)], + &sendCellsListReversed, &recvCellsListReversed, (numLayers + 1)); + allToAll(&velocityOnCells[dim * nCells_F * (numLayers + 1)], + sendCellsList_F, recvCellsList_F, (numLayers + 1)); + } +} + +void createReverseCellsExchangeLists(exchangeList_Type& sendListReverse_F, + exchangeList_Type& receiveListReverse_F, + const std::vector& fVertexToTriangleID, + const std::vector& fCellToVertexID) { + sendListReverse_F.clear(); + receiveListReverse_F.clear(); + //std::map > sendMap; + std::map > sendMap, receiveMap; + std::vector cellsProcId(nCells_F), trianglesProcIds(nVertices_F); + getProcIds(cellsProcId, recvCellsList_F); + getProcIds(trianglesProcIds, recvVerticesList_F); + + //std::cout << "SendList " ; + for (int i = 0; i < nVertices; i++) { + int iCell = vertexToFCell[i]; + if (iCell < nCellsSolve_F) + continue; + bool belongToTriaOnSameProc = false; + int j(0); + int nEdg = nEdgesOnCells_F[iCell]; + do { + ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * iCell + j] - 1); + ID triaId = fVertexToTriangleID[fVertex]; + belongToTriaOnSameProc = (triaId != NotAnId) + && (trianglesProcIds[fVertex] == cellsProcId[iCell]); + } while ((belongToTriaOnSameProc == false) && (++j < nEdg)); + if (!belongToTriaOnSameProc) { + sendMap[cellsProcId[iCell]].insert( + std::make_pair(fCellToVertexID[iCell], iCell)); + // std::cout<< "(" << cellsProcId[iCell] << "," << iCell << ") "; + } + + } + //std::cout < >::const_iterator it = sendMap.begin(); + it != sendMap.end(); it++) { + std::vector sendVec(it->second.size()); + int i = 0; + for (std::map::const_iterator iter = it->second.begin(); + iter != it->second.end(); iter++) + sendVec[i++] = iter->second; + sendListReverse_F.push_back( + exchange(it->first, &sendVec[0], &sendVec[0] + sendVec.size())); + } + + //std::cout << "ReceiveList " ; + for (UInt i = 0; i < fCellsToReceive.size(); i++) { + int iCell = fCellsToReceive[i]; + int nEdg = nEdgesOnCells_F[iCell]; + for (int j = 0; j < nEdg; j++) { + ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * iCell + j] - 1); + ID triaId = fVertexToTriangleID[fVertex]; + if (triaId != NotAnId) { + receiveMap[trianglesProcIds[fVertex]].insert( + std::make_pair(fCellToVertexID[iCell], iCell)); + // std::cout<< "(" << trianglesProcIds[fVertex] << "," << iCell << ") "; + } + } + } + //std::cout < >::const_iterator it = + receiveMap.begin(); it != receiveMap.end(); it++) { + std::vector receiveVec(it->second.size()); + int i = 0; + for (std::map::const_iterator iter = it->second.begin(); + iter != it->second.end(); iter++) + receiveVec[i++] = iter->second; + receiveListReverse_F.push_back( + exchange(it->first, &receiveVec[0], + &receiveVec[0] + receiveVec.size())); + } +} + +void createReverseEdgesExchangeLists(exchangeList_Type& sendListReverse_F, + exchangeList_Type& receiveListReverse_F, + const std::vector& fVertexToTriangleID, + const std::vector& fEdgeToEdgeID) { + sendListReverse_F.clear(); + receiveListReverse_F.clear(); + //std::map > sendMap; + std::map > sendMap, receiveMap; + std::vector edgesProcId(nEdges_F), trianglesProcIds(nVertices_F); + getProcIds(edgesProcId, recvEdgesList_F); + getProcIds(trianglesProcIds, recvVerticesList_F); + + //std::cout << "EdgesSendList " ; + for (int i = 0; i < nEdges; i++) { + int iEdge = edgeToFEdge[i]; + if (iEdge < nEdgesSolve_F) + continue; + bool belongToTriaOnSameProc = false; + int j(0); + do { + ID fVertex(verticesOnEdge_F[2 * iEdge + j] - 1); + ID triaId = fVertexToTriangleID[fVertex]; + belongToTriaOnSameProc = (triaId != NotAnId) + && (trianglesProcIds[fVertex] == edgesProcId[iEdge]); + } while ((belongToTriaOnSameProc == false) && (++j < 2)); + if (!belongToTriaOnSameProc) { + sendMap[edgesProcId[iEdge]].insert( + std::make_pair(fEdgeToEdgeID[iEdge], iEdge)); + //std::cout<< "(" << edgesProcId[iEdge] << "," << iEdge << ") "; + } + } + //std::cout < >::const_iterator it = sendMap.begin(); + it != sendMap.end(); it++) { + std::vector sendVec(it->second.size()); + int i = 0; + for (std::map::const_iterator iter = it->second.begin(); + iter != it->second.end(); iter++) + sendVec[i++] = iter->second; + sendListReverse_F.push_back( + exchange(it->first, &sendVec[0], &sendVec[0] + sendVec.size())); + } + + //std::cout << "EdgesReceiveList " ; + for (UInt i = 0; i < edgesToReceive.size(); i++) { + int iEdge = edgesToReceive[i]; + for (int j = 0; j < 2; j++) { + ID fVertex(verticesOnEdge_F[2 * iEdge + j] - 1); + ID triaId = fVertexToTriangleID[fVertex]; + if (triaId != NotAnId) { + receiveMap[trianglesProcIds[fVertex]].insert( + std::make_pair(fEdgeToEdgeID[iEdge], iEdge)); + // std::cout<< "(" << trianglesProcIds[fVertex] << "," << iEdge << ") "; + } + } + } + // std::cout < >::const_iterator it = + receiveMap.begin(); it != receiveMap.end(); it++) { + std::vector receiveVec(it->second.size()); + int i = 0; + for (std::map::const_iterator iter = it->second.begin(); + iter != it->second.end(); iter++) + receiveVec[i++] = iter->second; + receiveListReverse_F.push_back( + exchange(it->first, &receiveVec[0], + &receiveVec[0] + receiveVec.size())); + } +} + +void mapCellsToVertices(const std::vector& velocityOnCells, + std::vector& velocityOnVertices, int fieldDim, int numLayers, + int ordering) { + int lVertexColumnShift = (ordering == 1) ? 1 : nVertices; + int vertexLayerShift = (ordering == 0) ? 1 : numLayers + 1; + + int nVertices3D = nVertices * (numLayers + 1); + for (UInt j = 0; j < nVertices3D; ++j) { + int ib = (ordering == 0) * (j % lVertexColumnShift) + + (ordering == 1) * (j / vertexLayerShift); + int il = (ordering == 0) * (j / lVertexColumnShift) + + (ordering == 1) * (j % vertexLayerShift); + + int iCell = vertexToFCell[ib]; + int cellIndex = iCell * (numLayers + 1) + il; + int vertexIndex = j; + for (int dim = 0; dim < fieldDim; dim++) { + velocityOnVertices[vertexIndex] = velocityOnCells[cellIndex]; + cellIndex += nCells_F * (numLayers + 1); + vertexIndex += nVertices3D; + } + } +} + +bool isGhostTriangle(int i, double relTol) { + double x[3], y[3], area; + + for (int j = 0; j < 3; j++) { + int iCell = cellsOnVertex_F[3 * i + j] - 1; + x[j] = xCell_F[iCell]; + y[j] = yCell_F[iCell]; + } + + area = std::fabs(signedTriangleArea(x, y)); + return false; //(std::fabs(areaTriangle_F[i]-area)/areaTriangle_F[i] > relTol); +} + +double signedTriangleArea(const double* x, const double* y) { + double u[2] = { x[1] - x[0], y[1] - y[0] }; + double v[2] = { x[2] - x[0], y[2] - y[0] }; + + return 0.5 * (u[0] * v[1] - u[1] * v[0]); +} + +double signedTriangleAreaOnSphere(const double* x, const double* y, + const double *z) { + double u[3] = { x[1] - x[0], y[1] - y[0], z[1] - z[0] }; + double v[3] = { x[2] - x[0], y[2] - y[0], z[2] - z[0] }; + + double crossProduct[3] = { u[1] * v[2] - u[2] * v[1], u[2] * v[0] + - u[0] * v[2], u[0] * v[1] - u[1] * v[0] }; + double area = 0.5 + * std::sqrt( + crossProduct[0] * crossProduct[0] + crossProduct[1] * crossProduct[1] + + crossProduct[2] * crossProduct[2]); + return + (crossProduct[0] * x[0] + crossProduct[1] * y[0] + crossProduct[2] * z[0] + > 0) ? area : -area; +} + +//TO BE FIXED, Access To verticesOnCell_F is not correct +void extendMaskByOneLayer(int const* verticesMask_F, + std::vector& extendedFVerticesMask) { + extendedFVerticesMask.resize(nVertices_F); + extendedFVerticesMask.assign(&verticesMask_F[0], + &verticesMask_F[0] + nVertices_F); + for (int i = 0; i < nCells_F; i++) { + bool belongsToMarkedTriangle = false; + int nEdg = nEdgesOnCells_F[i]; + for (UInt k = 0; k < nEdg && !belongsToMarkedTriangle; k++) + belongsToMarkedTriangle = belongsToMarkedTriangle + || verticesMask_F[verticesOnCell_F[maxNEdgesOnCell_F * i + k] - 1]; + if (belongsToMarkedTriangle) + for (UInt k = 0; k < nEdg; k++) { + ID fVertex(verticesOnCell_F[maxNEdgesOnCell_F * i + k] - 1); + extendedFVerticesMask[fVertex] = !isGhostTriangle(fVertex); + } + } +} + +void import2DFields(std::map bdExtensionMap, double const* bedTopography_F, double const * lowerSurface_F, double const * thickness_F, + double const * beta_F, double const * temperature_F, double const * smb_F, double eps) { + + elevationData.assign(nVertices, 1e10); + thicknessData.assign(nVertices, 1e10); + bedTopographyData.assign(nVertices, 1e10); + if (beta_F != 0) + betaData.assign(nVertices, 1e10); + if(temperature_F != 0) + temperatureData.assign(nLayers * nTriangles, 1e10); + if (smb_F != 0) + smbData.assign(nVertices, 1e10); + + + //import fields + for (int index = 0; index < nVertices; index++) { + int iCell = vertexToFCell[index]; + thicknessData[index] = std::max(thickness_F[iCell] / unit_length, eps); + bedTopographyData[index] = bedTopography_F[iCell] / unit_length; + elevationData[index] = lowerSurface_F[iCell] / unit_length + thicknessData[index]; + if (beta_F != 0) + betaData[index] = beta_F[iCell] / unit_length; + if (smb_F != 0) + smbData[index] = smb_F[iCell] / unit_length * secondsInAYear/rho_ice; + } + + if(temperature_F != 0) { + for (int index = 0; index < nTriangles; index++) { + for (int il = 0; il < nLayers; il++) { + double temperature = 0; + int ilReversed = nLayers - il - 1; + int nPoints = 0; + for (int iVertex = 0; iVertex < 3; iVertex++) { + int v = verticesOnTria[iVertex + 3 * index]; + int iCell = vertexToFCell[v]; + //compute temperature by averaging tmeperature values of triangles vertices where ice is present + if (cellsMask_F[iCell] & ice_present_bit_value) { + temperature += temperature_F[iCell * nLayers + ilReversed]; + nPoints++; + } + } + if (nPoints == 0) //if triangle is in an ice-free area, set the temperature to T0 + temperatureData[index+il*nTriangles] = T0; + else + temperatureData[index+il*nTriangles] = temperature / nPoints; + } + } + } + + + //extend thickness elevation and basal friction data to the border for floating vertices + std::set::const_iterator iter; + + for (int iV = 0; iV < nVertices; iV++) { + int fCell = vertexToFCell[iV]; + if (isVertexBoundary[iV] && !(cellsMask_F[fCell] & dynamic_ice_bit_value)) { + int c; + int nEdg = nEdgesOnCells_F[fCell]; + bool isFloating = false; + for (int j = 0; (j < nEdg)&&(!isFloating); j++) { + int fEdge = edgesOnCell_F[maxNEdgesOnCell_F * fCell + j] - 1; + isFloating = (floatingEdgesMask_F[fEdge] != 0); + } + if(isFloating) { + + // -- floating margin -- + // Identify the lowest elevation neighboring cell with ice + // Scalar values will be mapped from that location to here. + double elevTemp =1e10; + bool foundNeighbor = false; + for (int j = 0; j < nEdg; j++) { + int fEdge = edgesOnCell_F[maxNEdgesOnCell_F * fCell + j] - 1; + //bool keep = (mask[verticesOnEdge_F[2 * fEdge] - 1] & dynamic_ice_bit_value) + // && (mask[verticesOnEdge_F[2 * fEdge + 1] - 1] & dynamic_ice_bit_value); + //if (!keep) + // continue; + + int c0 = cellsOnEdge_F[2 * fEdge] - 1; + int c1 = cellsOnEdge_F[2 * fEdge + 1] - 1; + c = (fCellToVertex[c0] == iV) ? c1 : c0; + //if(!(cellsMask_F[c] & ice_present_bit_value)) continue; + if((cellsMask_F[c] & dynamic_ice_bit_value)) { + double elev = thickness_F[c] + lowerSurface_F[c]; // - 1e-8*std::sqrt(pow(xCell_F[c0],2)+std::pow(yCell_F[c0],2)); + std::cout << " elev="< -eps) && + ( bcoords[1] > -eps) && + ( bcoords[2] > -eps ); +} + +int prismType(long long int const* prismVertexMpasIds, int& minIndex) +{ + int PrismVerticesMap[6][6] = {{0, 1, 2, 3, 4, 5}, {1, 2, 0, 4, 5, 3}, {2, 0, 1, 5, 3, 4}, {3, 5, 4, 0, 2, 1}, {4, 3, 5, 1, 0, 2}, {5, 4, 3, 2, 1, 0}}; + minIndex = std::min_element (prismVertexMpasIds, prismVertexMpasIds + 3) - prismVertexMpasIds; + + int v1 (prismVertexMpasIds[PrismVerticesMap[minIndex][1]]); + int v2 (prismVertexMpasIds[PrismVerticesMap[minIndex][2]]); + + return v1 > v2; +} + + void tetrasFromPrismStructured (long long int const* prismVertexMpasIds, long long int const* prismVertexGIds, long long int tetrasIdsOnPrism[][4]) + { + int PrismVerticesMap[6][6] = {{0, 1, 2, 3, 4, 5}, {1, 2, 0, 4, 5, 3}, {2, 0, 1, 5, 3, 4}, {3, 5, 4, 0, 2, 1}, {4, 3, 5, 1, 0, 2}, {5, 4, 3, 2, 1, 0}}; + + int tetraOfPrism[2][3][4] = {{{0, 1, 2, 5}, {0, 1, 5, 4}, {0, 4, 5, 3}}, {{0, 1, 2, 4}, {0, 4, 2, 5}, {0, 4, 5, 3}}}; + + int tetraAdjacentToPrismLateralFace[2][3][2] = {{{1, 2}, {0, 1}, {0, 2}}, {{0, 2}, {0, 1}, {1, 2}}}; + int tetraFaceIdOnPrismLateralFace[2][3][2] = {{{0, 0}, {1, 1}, {2, 2}}, {{0, 0}, {1, 1}, {2, 2}}}; + int tetraAdjacentToBottomFace = 0; //does not depend on type; + int tetraAdjacentToUpperFace = 2; //does not depend on type; + int tetraFaceIdOnBottomFace = 3; //does not depend on type; + int tetraFaceIdOnUpperFace = 0; //does not depend on type; + + int minIndex; + int prismT = prismType(prismVertexMpasIds, minIndex); + + long long int reorderedPrismLIds[6]; + + for (int ii = 0; ii < 6; ii++) + { + reorderedPrismLIds[ii] = prismVertexGIds[PrismVerticesMap[minIndex][ii]]; + } + + for (int iTetra = 0; iTetra < 3; iTetra++) + for (int iVertex = 0; iVertex < 4; iVertex++) + { + tetrasIdsOnPrism[iTetra][iVertex] = reorderedPrismLIds[tetraOfPrism[prismT][iTetra][iVertex]]; + } + } + + + void computeMap() + { + int PrismVerticesMap[6][6] = {{0, 1, 2, 3, 4, 5}, {1, 2, 0, 4, 5, 3}, {2, 0, 1, 5, 3, 4}, {3, 5, 4, 0, 2, 1}, {4, 3, 5, 1, 0, 2}, {5, 4, 3, 2, 1, 0}}; + + int tetraOfPrism[2][3][4] = {{{0, 1, 2, 5}, {0, 1, 5, 4}, {0, 4, 5, 3}}, {{0, 1, 2, 4}, {0, 4, 2, 5}, {0, 4, 5, 3}}}; + + int TetraFaces[4][3] = {{0 , 1 , 3}, {1 , 2 , 3}, {0 , 3 , 2}, {0 , 2 , 1}}; + + int PrismFaces[5][4] = {{0 , 1 , 4 , 3}, {1 , 2 , 5 , 4}, {0 , 3 , 5 , 2}, {0 , 2 , 1 , -1}, {3 , 4 , 5, -1}}; + + + for(int pType=0; pType<2; ++pType){ + std::cout<< "pType: " << pType < v2; + + for (int iTetra = 0; iTetra < 3; iTetra++) + for (int iVertex = 0; iVertex < 4; iVertex++) + { + tetrasIdsOnPrism[iTetra][iVertex] = prismVertexGIds[tetraOfPrism[prismType][iTetra][iVertex]]; + } + + // return; + + int reorderedPrismLIds[6]; + + for (int ii = 0; ii < 6; ii++) + { + reorderedPrismLIds[ii] = prismVertexGIds[PrismVerticesMap[minIndex][ii]]; + } + + for (int iTetra = 0; iTetra < 3; iTetra++) + for (int iVertex = 0; iVertex < 4; iVertex++) + { + tetrasIdsOnPrism[iTetra][iVertex] = reorderedPrismLIds[tetraOfPrism[prismType][iTetra][iVertex]]; + } + } + + + + + void setBdFacesOnPrism (const std::vector > >& prismStruct, const std::vector& prismFaceIds, std::vector& tetraPos, std::vector& facePos) + { + int numTriaFaces = prismFaceIds.size() - 2; + tetraPos.assign(numTriaFaces,-1); + facePos.assign(numTriaFaces,-1); + + + for (int iTetra (0), k (0); (iTetra < 3 && k < numTriaFaces); iTetra++) + { + bool found; + for (int jFaceLocalId = 0; jFaceLocalId < 4; jFaceLocalId++ ) + { + found = true; + for (int ip (0); ip < 3 && found; ip++) + { + int localId = prismStruct[iTetra][jFaceLocalId][ip]; + int j = 0; + found = false; + while ( (j < prismFaceIds.size()) && !found ) + { + found = (localId == prismFaceIds[j]); + j++; + } + } + if (found) + { + tetraPos[k] = iTetra; + facePos[k] = jFaceLocalId; + k += found; + break; + } + } + } + } + + void procsSharingVertex(const int vertex, std::vector& procIds) { + int fCell = vertexToFCell[vertex]; + procIds.clear(); + int nEdg = nEdgesOnCells_F[fCell]; + int me; + MPI_Comm_rank(comm, &me); + procIds.reserve(nEdg); + for(int i=0; i regulThk(thicknessData); +// for (int index = 0; index < nVertices; index++) +// regulThk[index] = std::max(1e-4, thicknessData[index]); //TODO Make limit a parameter +// +// importP0Temperature(temperature_F); +// +// std::cout << "\n\nTimeStep: "<< *deltat << "\n\n"<< std::endl; +// +// std::vector mpasIndexToVertexID(nVertices); +// for (int i = 0; i < nVertices; i++) { +// mpasIndexToVertexID[i] = indexToCellID_F[vertexToFCell[i]]; +// } +// +// mapVerticesToCells(velocityOnVertices, &velocityOnCells[0], 2, nLayers, +// Ordering); + + + // Write out ASCII format + + std::cout << "Writing mesh to albany.msh." << std::endl; + // msh file + std::ofstream outfile; + outfile.open ("albany.msh", std::ios::out | std::ios::trunc); + if (outfile.is_open()) { + + int nVerticesBoundaryEdge = 0; + for (int index = 0; index < nVertices; index++) { + if (isBoundaryEdge[index]) nVerticesBoundaryEdge += 1; + } + std::vector verticesOnBoundaryEdge; + verticesOnBoundaryEdge.resize(2 * nVerticesBoundaryEdge); + int iVerticesBoundaryEdge = 0; + for (int index = 0; index < nVertices; index++) { + if (isBoundaryEdge[index]) { + verticesOnBoundaryEdge[0 + 2 * iVerticesBoundaryEdge] = verticesOnEdge[0 + 2 * index]; + verticesOnBoundaryEdge[1 + 2 * iVerticesBoundaryEdge] = verticesOnEdge[1 + 2 * index]; + iVerticesBoundaryEdge += 1; + } + } + //std::cout<<"final count: "< bdExtensionMap; // local map to be created by import2DFields + import2DFields(bdExtensionMap, bedTopography_F, lowerSurface_F, thickness_F, beta_F, temperature_F, smb_F, minThickness); + + import2DFieldsObservations(bdExtensionMap, + thicknessUncertainty_F, + smbUncertainty_F, + bmb_F, bmbUncertainty_F, + observedSurfaceVelocityX_F, observedSurfaceVelocityY_F, observedSurfaceVelocityUncertainty_F, + observedThicknessTendency_F, observedThicknessTendencyUncertainty_F, + indexToCellID_F); + + + // Write out individual fields + write_ascii_mesh_field_int(indexToCellIDData, "mpas_cellID"); + write_ascii_mesh_field(thicknessData, "thickness"); + write_ascii_mesh_field(thicknessUncertaintyData, "thickness_uncertainty"); + + write_ascii_mesh_field(elevationData, "surface_height"); + + write_ascii_mesh_field(betaData, "basal_friction"); + + write_ascii_mesh_field(smbData, "surface_mass_balance"); + write_ascii_mesh_field(smbUncertaintyData, "surface_mass_balance_uncertainty"); + + write_ascii_mesh_field(bmbData, "basal_mass_balance"); + write_ascii_mesh_field(bmbUncertaintyData, "basal_mass_balance_uncertainty"); + + // These two fields are more complicated than the others so cannot use the function to write out + + std::cout << "Writing temperature.ascii." << std::endl; + outfile.open ("temperature.ascii", std::ios::out | std::ios::trunc); + if (outfile.is_open()) { + outfile << nTriangles << " " << nLayers << "\n"; //number of triangles and number of layers on first line + + double midLayer = 0; + for (int il = 0; il < nLayers; il++) { //sigma coordinates for temperature + midLayer += layersRatio[il]/2.0; + outfile << midLayer << "\n"; + midLayer += layersRatio[il]/2.0; + } + + for(int il = 0; il fieldData, std::string filenamebase) { + + std::string filename = filenamebase+".ascii"; + std::cout << "Writing " << filename << std::endl; + std::ofstream outfile; + outfile.open (filename.c_str(), std::ios::out | std::ios::trunc); + if (outfile.is_open()) { + outfile << nVertices << "\n"; //number of vertices on first line + for(int i = 0; i < nVertices; ++i) + outfile << fieldData[i] << "\n"; + outfile.close(); + } + else { + std::cout << "Error: Failed to open "+filename << std::endl; + } + } + + + void write_ascii_mesh_field_int(std::vector fieldData, std::string filenamebase) { + + std::string filename = filenamebase+".ascii"; + std::cout << "Writing " << filename << std::endl; + std::ofstream outfile; + outfile.open (filename.c_str(), std::ios::out | std::ios::trunc); + if (outfile.is_open()) { + outfile << nVertices << "\n"; //number of vertices on first line + for(int i = 0; i < nVertices; ++i) + outfile << fieldData[i] << "\n"; + outfile.close(); + } + else { + std::cout << "Error: Failed to open "+filename << std::endl; + } + } + diff --git a/src/core_landice/mode_forward/Interface_velocity_solver.hpp b/src/core_landice/mode_forward/Interface_velocity_solver.hpp new file mode 100644 index 0000000000..574a87a285 --- /dev/null +++ b/src/core_landice/mode_forward/Interface_velocity_solver.hpp @@ -0,0 +1,325 @@ +/* +Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +and the University Corporation for Atmospheric Research (UCAR). + +Unless noted otherwise source code is licensed under the BSD license. +Additional copyright and license information can be found in the LICENSE file +distributed with this code, or at http://mpas-dev.github.com/license.html +*/ + +// =================================================== +//! Includes +// =================================================== +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MPASLI_EXTERNAL_INTERFACE_DISABLE_MANGLING +#define velocity_solver_init_mpi velocity_solver_init_mpi_ +#define velocity_solver_finalize velocity_solver_finalize_ +#define velocity_solver_init_l1l2 velocity_solver_init_l1l2_ +#define velocity_solver_solve_l1l2 velocity_solver_solve_l1l2_ +#define velocity_solver_init_fo velocity_solver_init_fo_ +#define velocity_solver_solve_fo velocity_solver_solve_fo_ +#define velocity_solver_init_stokes velocity_solver_init_stokes_ +#define velocity_solver_solve_stokes velocity_solver_solve_stokes_ +#define velocity_solver_compute_2d_grid velocity_solver_compute_2d_grid_ +#define velocity_solver_set_grid_data velocity_solver_set_grid_data_ +#define velocity_solver_extrude_3d_grid velocity_solver_extrude_3d_grid_ +#define velocity_solver_export_l1l2_velocity velocity_solver_export_l1l2_velocity_ +#define velocity_solver_export_2d_data velocity_solver_export_2d_data_ +#define velocity_solver_export_fo_velocity velocity_solver_export_fo_velocity_ +#define velocity_solver_estimate_SS_SMB velocity_solver_estimate_ss_smb_ +#define interface_init_log interface_init_log_ +#define interface_redirect_stdout interface_redirect_stdout_ +#define interface_reset_stdout interface_reset_stdout_ +#define write_ascii_mesh write_ascii_mesh_ +#endif + +//#include +//#include + +//#include + +struct exchange { + const int procID; + const std::vector vec; + mutable std::vector buffer; + mutable std::vector doubleBuffer; + mutable MPI_Request reqID; + + exchange(int _procID, int const* vec_first, int const* vec_last, + int fieldDim = 1); +}; + +typedef std::list exchangeList_Type; + +typedef unsigned int ID; +typedef unsigned int UInt; +const ID NotAnId = std::numeric_limits::max(); + +// =================================================== +//! Interface function +// =================================================== +extern "C" { + +int velocity_solver_init_mpi(int* fComm); + +void velocity_solver_finalize(); + +void velocity_solver_set_parameters(double const* gravity_F, double const* ice_density_F, double const* ocean_density_F, + double const* sea_level_F, double const* flowParamA_F, + double const* enhancementFactor_F, double const* flowLawExponent_F, double const* dynamic_thickness_F, + double const* clausius_clapeyron_coeff, + int const* li_mask_ValueDynamicIce, int const* li_mask_ValueIce, + bool const* use_GLP_F); + +void velocity_solver_init_l1l2(double const* levelsRatio); + +void velocity_solver_init_fo(double const* levelsRatio); + +void velocity_solver_solve_l1l2(double const* lowerSurface_F, + double const* thickness_F, double const* beta_F, double const* temperature_F, + double* const dirichletVelocityXValue = 0, double* const dirichletVelocitYValue = 0, + double* u_normal_F = 0, + double* xVelocityOnCell = 0, double* yVelocityOnCell = 0); + +void velocity_solver_solve_fo(double const* bedTopography_F, double const* lowerSurface_F, + double const* thickness_F, double const* beta_F, double const* smb_F, double const* temperature_F, + double* const dirichletVelocityXValue = 0, double* const dirichletVelocitYValue = 0, + double* u_normal_F = 0, double* dissipation_heat_F = 0, + double* xVelocityOnCell = 0, double* yVelocityOnCell = 0, double const * deltat = 0, + int *error = 0 ); + + +void velocity_solver_compute_2d_grid(int const* verticesMask_F, int const* _cellsMask_F, int const* dirichletNodesMask_F, int const* floatingEdgeMask_F); + +void velocity_solver_set_grid_data(int const* _nCells_F, int const* _nEdges_F, + int const* _nVertices_F, int const* _nLayers, int const* _nCellsSolve_F, + int const* _nEdgesSolve_F, int const* _nVerticesSolve_F, + int const* _maxNEdgesOnCell_F, double const* radius_F, + int const* _cellsOnEdge_F, int const* _cellsOnVertex_F, + int const* _verticesOnCell_F, int const* _verticesOnEdge_F, + int const* _edgesOnCell_F, int const* _nEdgesOnCells_F, + int const* _indexToCellID_F, + double const* _xCell_F, double const* _yCell_F, double const* _zCell_F, + double const* _xVertex_F, double const* _yVertex_F, double const* _zVertex_F, + double const* _areaTriangle_F, + int const* sendCellsArray_F, int const* recvCellsArray_F, + int const* sendEdgesArray_F, int const* recvEdgesArray_F, + int const* sendVerticesArray_F, int const* recvVerticesArray_F); + +void velocity_solver_extrude_3d_grid(double const* levelsRatio_F, + double const* lowerSurface_F, double const* thickness_F); + +void velocity_solver_export_l1l2_velocity(); + +void velocity_solver_export_fo_velocity(); + +//void velocity_solver_estimate_SS_SMB (const double* u_normal_F, double* sfcMassBal); + +void interface_init_log(); + +void interface_redirect_stdout(int const* iTimestep); + +void interface_reset_stdout(); + +void write_ascii_mesh(int const* indexToCellID_F, + double const* bedTopography_F, double const* lowerSurface_F, + double const* beta_F, double const* temperature_F, + double const* thickness_F, double const* thicknessUncertainty_F, + double const* smb_F, double const* smbUncertainty_F, + double const* bmb_F, double const* bmbUncertainty_F, + double const* observedSurfaceVelocityX_F, double const* observedSurfaceVelocityY_F, + double const* observedVelocityUncertainty_F, + double const* observedThicknessTendency_F, double const * observedThicknessTendencyUncertainty_F); + +} // extern "C" + +extern void velocity_solver_finalize__(); + +#ifdef LIFEV +extern void velocity_solver_init_l1l2__(const std::vector& layersRatio, const std::vector& velocityOnVertices, bool initialize_velocity); + +extern void velocity_solver_solve_l1l2__(const std::vector& elevationData, + const std::vector& thicknessData, const std::vector& betaData, + const std::vector& temperatureData, const std::vector& indexToVertexID, + std::vector& velocityOnVertices); + +extern void velocity_solver_init_fo__(const std::vector& layersRatio, const std::vector& velocityOnVertices, const std::vector& indexToVertexID, bool initialize_velocity); + +extern void velocity_solver_export_l1l2_velocity__(const std::vector& layersRatio, const std::vector& elevationData, const std::vector& regulThk, + const std::vector& mpasIndexToVertexID, MPI_Comm reducedComm); + +#endif + +extern void velocity_solver_set_physical_parameters__(double const& gravity, double const& ice_density, double const& ocean_density, double const& sea_level, double const& flowParamA, + double const& enhancementFactor, double const& flowLawExponent, double const& dynamic_thickness, bool const& useGLP, double const& clausiusClapeyronCoeff); + +extern void velocity_solver_solve_fo__(int nLayers, int nGlobalVertices, + int nGlobalTriangles, bool ordering, bool first_time_step, + const std::vector& indexToVertexID, + const std::vector& indexToTriangleID, double minBeta, + const std::vector& regulThk, + const std::vector& levelsNormalizedThickness, + const std::vector& elevationData, + const std::vector& thicknessData, + const std::vector& betaData, + const std::vector& bedTopographyData, + const std::vector& smbData, + const std::vector& temperatureOnTetra, + std::vector& dissipationHeatOnTetra, + std::vector& velocityOnVertices, + int& error, + const double& deltat = 0.0); + + +#ifdef LIFEV +extern void velocity_solver_compute_2d_grid__(int nGlobalTriangles, + int nGlobalVertices, int nGlobalEdges, + const std::vector& indexToVertexID, + const std::vector& verticesCoords, + const std::vector& isVertexBoundary, + const std::vector& verticesOnTria, + const std::vector& isBoundaryEdge, + const std::vector& trianglesOnEdge, + const std::vector& trianglesPositionsOnEdge, + const std::vector& verticesOnEdge, + const std::vector& indexToEdgeID, + const std::vector& indexToTriangleID, + const std::vector < std::pair >& procOnInterfaceEdge); + +#else +extern void velocity_solver_compute_2d_grid__(MPI_Comm); +#endif + + +extern void velocity_solver_export_2d_data__(MPI_Comm reducedComm, + const std::vector& elevationData, + const std::vector& thicknessData, + const std::vector& betaData, + const std::vector& indexToVertexID); + +extern void velocity_solver_extrude_3d_grid__(int nLayers, int nGlobalTriangles, + int nGlobalVertices, int nGlobalEdges, int Ordering, MPI_Comm reducedComm, + const std::vector& indexToVertexID, + const std::vector& mpasIndexToVertexID, + const std::vector& verticesCoords, + const std::vector& isVertexBoundary, + const std::vector& verticesOnTria, + const std::vector& isBoundaryEdge, + const std::vector& trianglesOnEdge, + const std::vector& trianglesPositionsOnEdge, + const std::vector& verticesOnEdge, + const std::vector& indexToEdgeID, + const std::vector& indexToTriangleID, + const std::vector& dirichletNodes, + const std::vector&floatingEdges); + +//extern void velocity_solver_export_l1l2_velocity__(); + +extern void velocity_solver_export_fo_velocity__(MPI_Comm reducedComm); + + +#ifdef LIFEV +extern int velocity_solver_initialize_iceProblem__(bool keep_proc, MPI_Comm reducedComm); +#endif + +//extern void velocity_solver_estimate_SS_SMB__ (const double* u_normal_F, double* sfcMassBal); + +exchangeList_Type unpackMpiArray(int const* array); + +bool isGhostTriangle(int i, double relTol = 1e-1); + +double signedTriangleArea(const double* x, const double* y); + +double signedTriangleArea(const double* x, const double* y, const double* z); + +void createReducedMPI(int nLocalEntities, MPI_Comm& reduced_comm_id); + +void import2DFields(std::map bdExtensionMap, double const* bedTopography_F, double const* lowerSurface_F, double const* thickness_F, + double const* beta_F = 0, double const* temperature_F = 0, double const* smb_F = 0, double eps = 0); + +void import2DFieldsObservations(std::map bdExtensionMap, + double const * lowerSurface_F, + double const * thickness_F, double const * thicknessUncertainty_F, + double const * smbUncertainty_F, + double const * bmb_F, double const * bmbUncertainty_F, + double const * observedSurfaceVelocityX_F, double const * observedSurfaceVelocityY_F, + double const * observedSurfaceVelocityUncertainty_F, + double const * observedThicknessTendency_F, double const * observedThicknessTendencyUncertainty_F, + int const * indexToCellID_F); + +void write_ascii_mesh_field(std::vector fieldData, std::string filenamebase); + +void write_ascii_mesh_field_int(std::vector fieldData, std::string filenamebase); + +std::vector extendMaskByOneLayer(int const* verticesMask_F); + +void extendMaskByOneLayer(int const* verticesMask_F, + std::vector& extendedFVerticesMask); + +void importP0Temperature(); + +void exportDissipationHeat(double * dissipationHeat_F); + +void get_prism_velocity_on_FEdges(double* uNormal, + const std::vector& velocityOnCells, + const std::vector& edgeToFEdge); + +int initialize_iceProblem(int nTriangles); + +void createReverseCellsExchangeLists(exchangeList_Type& sendListReverse_F, + exchangeList_Type& receiveListReverse_F, + const std::vector& fVertexToTriangleID, + const std::vector& fCellToVertexID); + +void createReverseEdgesExchangeLists(exchangeList_Type& sendListReverse_F, + exchangeList_Type& receiveListReverse_F, + const std::vector& fVertexToTriangleID, + const std::vector& fEdgeToEdgeID); + +void mapCellsToVertices(const std::vector& velocityOnCells, + std::vector& velocityOnVertices, int fieldDim, int numLayers, + int ordering); + +void mapVerticesToCells(const std::vector& velocityOnVertices, + double* velocityOnCells, int fieldDim, int numLayers, int ordering); + +void computeLocalOffset(int nLocalEntities, int& localOffset, + int& nGlobalEntities); + +void getProcIds(std::vector& field, int const* recvArray); + +void getProcIds(std::vector& field, exchangeList_Type const* recvList); + +void allToAll(std::vector& field, int const* sendArray, + int const* recvArray, int fieldDim = 1); + +void allToAll(std::vector& field, exchangeList_Type const* sendList, + exchangeList_Type const* recvList, int fieldDim = 1); + +void allToAll(double* field, exchangeList_Type const* sendList, + exchangeList_Type const* recvList, int fieldDim = 1); + +int prismType(long long int const* prismVertexMpasIds, int& minIndex); +void tetrasFromPrismStructured (long long int const* prismVertexMpasIds, long long int const* prismVertexGIds, long long int tetrasIdsOnPrism[][4]); +void computeMap(); + +void setBdFacesOnPrism (const std::vector > >& prismStruct, const std::vector& prismFaceIds, std::vector& tetraPos, std::vector& facePos); +void tetrasFromPrismStructured (int const* prismVertexMpasIds, int const* prismVertexGIds, int tetrasIdsOnPrism[][4]); +void procsSharingVertex(const int vertex, std::vector& procIds); + +bool belongToTria(double const* x, double const* t, double bcoords[3], double eps = 1e-3); + + + diff --git a/src/core_landice/mode_forward/Makefile b/src/core_landice/mode_forward/Makefile new file mode 100644 index 0000000000..5543e557dd --- /dev/null +++ b/src/core_landice/mode_forward/Makefile @@ -0,0 +1,85 @@ + +.SUFFIXES: .F .o .cpp + +OBJS = mpas_li_core.o \ + mpas_li_core_interface.o \ + mpas_li_time_integration.o \ + mpas_li_time_integration_fe.o \ + mpas_li_diagnostic_vars.o \ + mpas_li_advection.o \ + mpas_li_calving.o \ + mpas_li_statistics.o \ + mpas_li_velocity.o \ + mpas_li_thermal.o \ + mpas_li_sia.o \ + mpas_li_velocity_simple.o \ + mpas_li_velocity_external.o \ + mpas_li_subglacial_hydro.o + +ifneq (, $(findstring MPAS_LI_BUILD_INTERFACE,$(CPPFLAGS))) + OBJS += Interface_velocity_solver.o +endif + +all: $(OBJS) + +mpas_li_core_interface.o: mpas_li_core.o + +mpas_li_core.o: mpas_li_time_integration.o \ + mpas_li_velocity.o \ + mpas_li_thermal.o \ + mpas_li_diagnostic_vars.o \ + mpas_li_statistics.o + +mpas_li_time_integration.o: mpas_li_time_integration_fe.o + +mpas_li_time_integration_fe.o: mpas_li_advection.o \ + mpas_li_calving.o \ + mpas_li_thermal.o \ + mpas_li_diagnostic_vars.o \ + mpas_li_velocity.o \ + mpas_li_subglacial_hydro.o + +mpas_li_advection.o: mpas_li_thermal.o \ + mpas_li_diagnostic_vars.o + +mpas_li_calving.o: mpas_li_thermal.o \ + mpas_li_advection.o + +mpas_li_thermal.o: + +mpas_li_diagnostic_vars.o: mpas_li_thermal.o + +mpas_li_velocity.o: mpas_li_sia.o \ + mpas_li_velocity_simple.o \ + mpas_li_velocity_external.o \ + mpas_li_advection.o + +mpas_li_sia.o: mpas_li_diagnostic_vars.o + +mpas_li_subglacial_hydro.o: mpas_li_diagnostic_vars.o + +mpas_li_velocity_simple.o: + +mpas_li_statistics.o: mpas_li_diagnostic_vars.o + +mpas_li_velocity_external.o: Interface_velocity_solver.o + +Interface_velocity_solver.o: + +clean: + $(RM) *.o *.mod *.f90 + @# Certain systems with intel compilers generate *.i files + @# This removes them during the clean process + $(RM) *.i + +.F.o: + $(RM) $@ $*.mod +ifeq "$(GEN_F90)" "true" + $(CPP) $(CPPFLAGS) $(CPPINCLUDES) $< > $*.f90 + $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) +else + $(FC) $(CPPFLAGS) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) +endif + +.cpp.o: + $(CXX) $(CXXFLAGS) -c $*.cpp $(CXINCLUDES) $(CPPINCLUDES) -lmpi_cxx -lstdc++ $(CPPFLAGS) diff --git a/src/core_landice/mode_forward/mpas_li_advection.F b/src/core_landice/mode_forward/mpas_li_advection.F new file mode 100644 index 0000000000..f2d0ed0229 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_advection.F @@ -0,0 +1,1676 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_advection +! +!> \brief MPAS land ice advection solver +!> \author William Lipscomb +!> \date December 2015 +!> \details +!> This module contains the routines for advecting ice thickness +!> and tracers for land ice. +!----------------------------------------------------------------------- + +module li_advection + + use mpas_derived_types + use mpas_pool_routines + use mpas_log + + use li_setup + use li_mask + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + public :: li_advection_thickness_tracers, & + li_layer_normal_velocity, & + li_update_geometry + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + contains + + +!*********************************************************************** +! +! subroutine li_advection_thickness_tracers +! +!> \brief Advection for ice thickness and tracers +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> This routine (1) computes new values of ice thickness and tracers under +!> horizontal advection, (2) applies surface and basal mass balance +!> terms, and (3) remaps tracers back onto the standard vertical grid. +!> Based on subroutine li_tendency_thickness by Matthew Hoffman +!> Note: The thickness and tracer fields must have had halo updates +!> before calling this subroutine. +!----------------------------------------------------------------------- + + subroutine li_advection_thickness_tracers(& + dt, & + meshPool, & + velocityPool, & + geometryPool, & + thermalPool, & + scratchPool, & + err, & + advectTracersIn) + + use li_setup, only: li_calculate_layerThickness + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), intent(in) :: & + dt !< Input: time step (s) + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + logical, intent(in), optional :: & + advectTracersIn !< Input: if true, then advect tracers as well as thickness + !TODO: Default is false; change to true? + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: & + velocityPool !< Input/output: velocity information + ! (needs to be inout for li_calculate_mask call + + type (mpas_pool_type), intent(inout) :: & + geometryPool !< Input/output: geometry information to be updated + + type (mpas_pool_type), intent(inout) :: & + thermalPool !< Input/output: temperature/enthalpy information to be updated + + type (mpas_pool_type), intent(inout) :: & + scratchPool !< Input/output: work arrays for tracer advection + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: & + err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer, pointer :: nVertLevels + + real (kind=RKIND), dimension(:), pointer :: & + thickness, & ! ice thickness (updated in this subroutine) + bedTopography, & ! bed topography + sfcMassBal, & ! surface mass balance + basalMassBal, & ! basal mass balance + groundedBasalMassBal, & ! basal mass balance for grounded ice + floatingBasalMassBal, & ! basal mass balance for floating ice + dynamicThickening ! dynamic thickening rate + + real (kind=RKIND), dimension(:,:), pointer :: & + temperature, & ! interior ice temperature + waterfrac, & ! interior water fraction + enthalpy ! interior ice enthalpy + + real (kind=RKIND), dimension(:,:), pointer :: & + layerNormalVelocity, & ! normal component of velocity on cell edges at layer midpoints + layerThickness, & ! thickness of each layer + layerThicknessOld, & ! old layer thickness + layerThicknessEdge ! layer thickness on upstream edge of cell + + real (kind=RKIND), dimension(:,:,:), pointer :: & + advectedTracers, & ! values of advected tracers + advectedTracersOld ! old values of advected tracers + + real (kind=RKIND), dimension(:,:), pointer :: & + surfaceTracers, & ! tracer values for new ice at upper surface + basalTracers ! tracer values for new ice at lower surface + + type (field3DReal), pointer :: & + advectedTracersField, & ! scratch field containing values of advected tracers + advectedTracersOldField ! scratch field containing old values of advected tracers + + type (field2DReal), pointer :: & + layerThicknessOldField ! scratch field containing old values of layer thickness + + type (field2DReal), pointer :: & + surfaceTracersField, & ! scratch field containing values of surface tracers + basalTracersField ! scratch field containing values of basal tracers + + integer, dimension(:), pointer :: & + cellMask ! integer bitmask for cells + + character (len=StrKIND), pointer :: & + config_thickness_advection ! method for advecting thickness and tracers + + logical, pointer :: & + config_print_thickness_advection_info !TODO - change to config_print_advection_info? + + real (kind=RKIND), pointer :: & + config_ice_density, & ! rhoi + config_sea_level ! sea level relative to z = 0 + + logical :: advectTracers ! if true, then advect tracers as well as thickness + + integer :: err_tmp + + !WHL - debug + integer, dimension(:), pointer :: indexToCellID, indexToEdgeID + integer, pointer :: nCells + integer, pointer :: config_stats_cell_ID + integer :: iEdge, iEdgeOnCell + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell + + !WHL - debug - for test-case diagnostics + logical, parameter :: dome_test = .false. + logical, parameter :: circular_shelf_test = .false. + integer :: nCellsPerRow + integer :: nRows + integer :: i, k, iRow, iCell, iLayer + + if (circular_shelf_test) then + nCellsPerRow = 40 + nRows = 46 + elseif (dome_test) then + nCellsPerRow = 30 + nRows = 34 + endif + + err = 0 + err_tmp = 0 + + ! Decide whether to advect tracers + ! If not, then advect thickness only + + if (present(advectTracersIn)) then + advectTracers = advectTracersIn + else + advectTracers = .false. + endif + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + ! get arrays from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'basalMassBal', basalMassBal) + call mpas_pool_get_array(geometryPool, 'groundedBasalMassBal', groundedBasalMassBal) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + call mpas_pool_get_array(geometryPool, 'layerThickness', layerThickness) + call mpas_pool_get_array(geometryPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'dynamicThickening', dynamicThickening) + + ! get arrays from the velocity pool + call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) + + ! get arrays from the thermal pool + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) + + ! get config variables + call mpas_pool_get_config(liConfigs, 'config_thickness_advection', config_thickness_advection) + call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + + !WHL - debug + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + call mpas_pool_get_array(meshPool, 'indexToEdgeID', indexToEdgeID) + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + + ! get fields from the scratch pool + ! Note: The advectedTracers field is a scratch field containing only the tracers that need to be advected. + ! For example, it contains temperature or enthalpy (depending on config_thermal_solver) but not both. + + call mpas_pool_get_field(scratchPool, 'workTracerLevelCell', advectedTracersField) + call mpas_allocate_scratch_field(advectedTracersField, .true.) + advectedTracers => advectedTracersField % array + + call mpas_pool_get_field(scratchPool, 'workLevelCell', layerThicknessOldField) + call mpas_allocate_scratch_field(layerThicknessOldField, .true.) + layerThicknessOld => layerThicknessOldField % array + + call mpas_pool_get_field(scratchPool, 'workTracerLevelCell2', advectedTracersOldField) + call mpas_allocate_scratch_field(advectedTracersOldField, .true.) + advectedTracersOld => advectedTracersOldField % array + + call mpas_pool_get_field(scratchPool, 'workTracerCell', surfaceTracersField) + call mpas_allocate_scratch_field(surfaceTracersField, .true.) + surfaceTracers => surfaceTracersField % array + + call mpas_pool_get_field(scratchPool, 'workTracerCell2', basalTracersField) + call mpas_allocate_scratch_field(basalTracersField, .true.) + basalTracers => basalTracersField % array + + ! given the old thickness, compute the thickness in each layer + call li_calculate_layerThickness(meshPool, thickness, layerThickness) + + !----------------------------------------------------------------- + ! Horizontal transport of thickness and tracers + !----------------------------------------------------------------- + + select case (trim(config_thickness_advection)) + + case ('fo') + + if (config_print_thickness_advection_info) then + call mpas_log_write('Using first-order upwind for thickness advection') + call mpas_log_write('advectTracers = $l', logicArgs=(/advectTracers/)) + if (dome_test) then + call mpas_log_write(' ') + call mpas_log_write('Dome test diagnostics:') + endif + endif + + + if (advectTracers) then + + ! Copy the old tracer values into the advectedTracers array. + ! This requires setting a tracer pointer to either temperature or enthalpy, + ! depending on the value of config_thermal_solver. + ! Currently (as of Nov. 2015), this is the only tracer transported, but others + ! may be added later. + + call tracer_setup(& + meshPool, & + geometryPool, & + thermalPool, & + advectedTracers, & + surfaceTracers, & + basalTracers) + + else ! no tracer advection; pass a tracer array full of zeroes + + advectedTracers(:,:,:) = 0.0_RKIND + + endif + + ! Transport thickness and tracers using a first-order upwind scheme + ! Note: For the enthalpy scheme, temperature and waterfrac are the primary prognostic + ! variables to be updated, but enthalpy is the advected tracer (for reasons of + ! energy conservation). + + if (config_print_thickness_advection_info) then + + do iCell = 1, nCells + if (indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write('Before thickness advection, iCell (local=$i, global=$i), thickness=$r, layer 1 tracer:=$r', & + intArgs=(/iCell, indexToCellID(iCell)/), realArgs=(/thickness(iCell), advectedTracers(1,1,iCell)/)) + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iEdgeOnCell,iCell) + call mpas_log_write('iEdge (local=$i, global=$i), layerNormalVelocity=$r:', & + intArgs=(/iEdge, indexToEdgeID(iEdge)/), realArgs=(/layerNormalVelocity(1,iEdge)/)) + enddo + endif + enddo + endif + + ! copy old (input) values of layer thickness and tracers to scratch arrays + layerThicknessOld(:,:) = layerThickness(:,:) + advectedTracersOld(:,:,:) = advectedTracers(:,:,:) + + ! compute new values of layer thickness and tracers + call advect_thickness_tracers_upwind(& + dt, & + meshPool, & + layerNormalVelocity, & + layerThicknessEdge, & + layerThicknessOld, & + advectedTracersOld, & + layerThickness, & + advectedTracers, & + err) + + if (config_print_thickness_advection_info) then + do iCell = 1, nCells + if (indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write(' ') + call mpas_log_write('After thickness advection, iCell (local, global), new layer thickness, tracer 1:') + do k = 1, nVertLevels + call mpas_log_write("$i $i $r $r", intArgs=(/iCell, indexToCellID(iCell)/), & + realArgs=(/layerThickness(k,iCell), advectedTracers(1,k,iCell)/)) + enddo + endif + enddo + endif + + ! Calculate dynamicThickening (layerThickness is updated by advection at this point, while thickness is still old) + dynamicThickening = (sum(layerThickness, 1) - thickness) / dt * scyr ! units of m/yr + + ! Update the thickness and cellMask before applying the mass balance. + ! The update is needed because the SMB and BMB depend on whether ice is present. + + thickness = sum(layerThickness, 1) + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + + !----------------------------------------------------------------- + ! Add the surface and basal mass balance to the layer thickness + !----------------------------------------------------------------- + + ! Zero out any positive surface mass balance for ice-free ocean cells + + where ( sfcMassBal > 0.0_RKIND .and. bedTopography < config_sea_level .and. .not.(li_mask_is_ice(cellMask) ) ) + + sfcMassBal = 0.0_RKIND + + end where + + ! Combine various basal mass balance fields based on mask. + ! Grounded and floating basal mass balance should come from the thermal solver. + + ! TODO: more complicated treatment at GL? + + where ( li_mask_is_grounded_ice(cellMask) ) + + basalMassBal = groundedBasalMassBal + + elsewhere ( li_mask_is_floating_ice(cellMask) ) + + ! Currently, floating and grounded ice are mutually exclusive. + ! This could change if the GL is parameterized, in which case this logic may need adjustment. + basalMassBal = floatingBasalMassBal + + elsewhere ( .not. (li_mask_is_ice(cellMask) ) ) + + ! We don't allow a positive basal mass balance where ice is not already present. + basalMassBal = 0.0_RKIND + + end where + + call apply_mass_balance(& + dt, & + config_ice_density, & + sfcMassBal, & + basalMassBal, & + surfaceTracers, & + basalTracers, & + layerThickness, & + advectedTracers) + + ! Update the thickness and cellMask before the vertical remap calculation. + ! Note: The thickness field (rather than layerThickness) is the primary prognostic. + ! It should be returned from each subroutine that alters the thickness, or + ! updated immediately afterward. + + thickness = sum(LayerThickness, 1) + + !WHL - debug - Get mass balance for test cell + if (config_print_thickness_advection_info) then + do iCell = 1, nCells + if (indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write(' ') + call mpas_log_write('After apply_mass_balance, iCell=$i, thickness=$r', intArgs=(/iCell/), realArgs=(/thickness(iCell)/) ) + call mpas_log_write('cellMask=$i, is ice=$l, is grounded=$l, is floating=$l', & + intArgs=(/cellMask(iCell)/), logicArgs=(/li_mask_is_ice(cellMask(iCell)), li_mask_is_grounded_ice(cellMask(iCell)), & + li_mask_is_floating_ice(cellMask(iCell)) /) ) + call mpas_log_write('basalMassBal=$r, grounded=$r, floating=$r', realArgs=(/ basalMassBal(iCell)*31536000./917., & + groundedBasalMassBal(iCell)*31536000./917., floatingBasalMassBal(iCell)*31536000./917. /) ) + endif + enddo + endif + + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + + ! Remap tracers to the standard vertical sigma coordinate + ! Note: If tracers are not being advected, then this subroutine simply restores the + ! layer thickness to sigma coordinate values. + + call vertical_remap(thickness, cellMask, meshPool, layerThickness, advectedTracers, err_tmp) + err = ior(err, err_tmp) + + if (config_print_thickness_advection_info) then + do iCell = 1, nCells + if (indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write(' ') + call mpas_log_write('After vertical remap, iCell, new layer thickness, tracer 1:') + do k = 1, nVertLevels + call mpas_log_write("$i $r $r", intArgs=(/iCell/), realArgs=(/layerThickness(k,iCell), advectedTracers(1,k,iCell)/)) + enddo + endif + enddo + endif + + if (advectTracers) then + + ! Copy the advectedTracersNew values into the thermal tracer arrays + ! (temperature, waterfrac, enthalpy) + + call tracer_finish(& + meshPool, & + geometryPool, & + thermalPool, & + advectedTracers) + + endif + + case ('none') + + ! Do nothing + + case default + + call mpas_log_write(trim(config_thickness_advection) // ' is not a valid option for thickness/tracer advection.', MPAS_LOG_ERR) + err_tmp = 1 + + end select + + err = ior(err,err_tmp) + + ! clean up + call mpas_deallocate_scratch_field(advectedTracersField, .true.) + call mpas_deallocate_scratch_field(advectedTracersOldField, .true.) + call mpas_deallocate_scratch_field(layerThicknessOldField, .true.) + call mpas_deallocate_scratch_field(basalTracersField, .true.) + call mpas_deallocate_scratch_field(surfaceTracersField, .true.) + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_advection_thickness_tracers", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_advection_thickness_tracers + + +!*********************************************************************** +! +! subroutine li_update_geometry +! +!> \brief Calculates the lower and upper surface +!> \author Matt Hoffman +!> \date 23 May 2017 +!> \details +!> This routine calculates lowerSurface and upperSurface from thickness +!> cellMask, and bedTopography. It is a standalone routine so it can be called +!> consistently from multiple places as needed. +!----------------------------------------------------------------------- + + subroutine li_update_geometry(geometryPool) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: geometryPool !< Input/Output: geometry object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND), pointer :: config_sea_level, config_ice_density, config_ocean_density + real (kind=RKIND), dimension(:), pointer :: thickness, upperSurface, & + lowerSurface, bedTopography + integer, dimension(:), pointer :: cellMask + integer, pointer :: nCells + integer :: iCell + + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) + + call mpas_pool_get_dimension(geometryPool, 'nCells', nCells) + + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + + + ! Lower surface is based on floatation for floating ice. + ! For grounded ice (and non-ice areas) it is the bed. + where ( li_mask_is_floating_ice(cellMask) ) + lowerSurface = config_sea_level - thickness * (config_ice_density / config_ocean_density) + elsewhere + lowerSurface = bedTopography + end where + ! Make sure lowerSurface calculation is reasonable. This check could be deleted once this has been throroughly tested. + !do iCell = 1, nCells + ! if (lowerSurface(iCell) < bedTopography(iCell)) then + ! call mpas_log_write('lowerSurface less than bedTopography at cell:i $i', intArgs=(/iCell/)) + ! err = 1 + ! endif + !end do + + ! Upper surface is the lower surface plus the thickness + upperSurface(:) = lowerSurface(:) + thickness(:) + + !-------------------------------------------------------------------- + end subroutine li_update_geometry + + + +!*********************************************************************** +! Private subroutines: +!*********************************************************************** + +!*********************************************************************** +! +! subroutine apply_mass_balance +! +!> \brief Apply surface and basal mass balance +!> \author William Lipscomb +!> \date February 2016 +!> \details +!> This routine applies the surface and basal mass balance, with a +!> conservative treatment of tracers such as temperature. +! +!----------------------------------------------------------------------- + + subroutine apply_mass_balance(& + dt, & + rhoi, & + sfcMassBal, & + basalMassBal, & + surfaceTracers, & + basalTracers, & + layerThickness, & + advectedTracers) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), intent(in) :: & + dt, & !< Input: time step (s) + rhoi !< Input: ice density (kg/m^3) + + real (kind=RKIND), dimension(:), intent(in) :: & + sfcMassBal, & !< Input: surface mass balance (kg/m^2/s) + basalMassBal !< Input: basal mass balance (kg/m^2/s) + + real(kind=RKIND), dimension(:,:), intent(in) :: & + surfaceTracers, & !< Input: tracer values of new ice at upper surface + basalTracers !< Input: tracer values of new ice at lower surface + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:,:), intent(inout) :: & + layerThickness ! ice thickness in each layer (m) + + real(kind=RKIND), dimension(:,:,:), intent(inout) :: & + advectedTracers ! tracer values in each layer + + ! local variables + + real (kind=RKIND) :: & + sfcAccum, basalAccum, & ! surface and basal accumulation (m), positive for ice gain + sfcAblat, basalAblat ! surface and basal ablation (m), positive for ice loss + + real (kind=RKIND), dimension(:), allocatable :: & + thckTracerProducts ! thickness-tracer products + + integer :: nCells ! number of cells + integer :: nLayers ! number of layers + integer :: nTracers ! number of tracers + + integer :: iCell, iLayer, iTracer + + nLayers = size(layerThickness,1) + nCells = size(layerThickness,2) + nTracers = size(advectedTracers,1) + + allocate(thckTracerProducts(nTracers)) + + ! apply surface mass balance + ! If positive, then add the SMB to the top layer, conserving mass*tracer products + ! If negative, then melt from the top down until the melting term is used up or the ice is gone + + do iCell = 1, nCells + + ! initialize accumulation/ablation terms + + sfcAccum = 0.0_RKIND + sfcAblat = 0.0_RKIND + basalAccum = 0.0_RKIND + basalAblat = 0.0_RKIND + + ! apply surface mass balance + + if (sfcMassBal(iCell) > 0.0_RKIND) then + + ! surface accumulation + ! modify tracers conservatively in top layer + + sfcAccum = sfcMassBal(iCell) * dt / rhoi + + ! compute mass-tracer products in top layer + thckTracerProducts(:) = layerThickness(1,iCell)*advectedTracers(:,1,iCell) & + + sfcAccum * surfaceTracers(:,iCell) + + ! new thickness in top layer + layerThickness(1,iCell) = layerThickness(1,iCell) + sfcAccum + + ! new tracers in top layer + advectedTracers(:,1,iCell) = thckTracerProducts(:) / layerThickness(1,iCell) + + elseif (sfcMassBal(iCell) < 0.0_RKIND) then + + ! surface ablation from the top down + + sfcAblat = -sfcMassBal(iCell) * dt /rhoi ! positive for melting + + do iLayer = 1, nLayers + if (sfcAblat > layerThickness(iLayer,iCell)) then ! melt the entire layer + sfcAblat = sfcAblat - layerThickness(iLayer,iCell) + layerThickness(iLayer,iCell) = 0.0_RKIND + advectedTracers(:,iLayer,iCell) = 0.0_RKIND + else ! melt part of the layer + layerThickness(iLayer,iCell) = layerThickness(iLayer,iCell) - sfcAblat + sfcAblat = 0.0_RKIND + exit + endif + enddo + + !TODO - If remaining sfcAblat > 0, then keep track of it to conserve energy + + endif ! sfcMassBal > 0 + + ! apply basal mass balance + + if (basalMassBal(iCell) > 0.0_RKIND) then + + ! basal freeze-on + ! modify tracers conservatively in top layer + + basalAccum = basalMassBal(iCell) * dt / rhoi + + ! compute mass-tracer products in bottom layer + thckTracerProducts(:) = layerThickness(nLayers,iCell)*advectedTracers(:,nLayers,iCell) & + + basalAccum * basalTracers(:,iCell) + + ! new thickness in top layer + layerThickness(nLayers,iCell) = layerThickness(nLayers,iCell) + basalAccum + + ! new tracers in top layer + advectedTracers(:,nLayers,iCell) = thckTracerProducts(:) / layerThickness(nLayers,iCell) + + elseif (basalMassBal(iCell) < 0.0_RKIND) then + + ! surface ablation from the bottom up + + basalAblat = -basalMassBal(iCell) * dt /rhoi ! positive for melting + + do iLayer = nLayers, 1, -1 + if (basalAblat > layerThickness(iLayer,iCell)) then ! melt the entire layer + basalAblat = basalAblat - layerThickness(iLayer,iCell) + layerThickness(iLayer,iCell) = 0.0_RKIND + advectedTracers(:,iLayer,iCell) = 0.0_RKIND + else ! melt part of the layer + layerThickness(iLayer,iCell) = layerThickness(iLayer,iCell) - basalAblat + basalAblat = 0.0_RKIND + exit + endif + enddo + + !TODO - If remaining basalAblat > 0, then keep track of it to conserve energy + + endif ! basalMassBal > 0 + + enddo ! iCell + + deallocate(thckTracerProducts) + + end subroutine apply_mass_balance + +!*********************************************************************** +! +! subroutine tracer_setup +! +!> \brief Assemble a 3D array for tracer advection +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> This routine assembles a 3D array for tracer advection. +!> Each tracer in the array is transported conservatively, along +!> with the layer thickness field. +! +!----------------------------------------------------------------------- + + subroutine tracer_setup(& + meshPool, & + geometryPool, & + thermalPool, & + advectedTracers, & + surfaceTracers, & + basalTracers) + + use li_thermal, only: li_temperature_to_enthalpy + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + type (mpas_pool_type), intent(in) :: & + geometryPool !< Input: geometry information + + type (mpas_pool_type), intent(inout) :: & + thermalPool !< Input: temperature/enthalpy information + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(out) :: & + advectedTracers ! tracers to be advected + ! dimension 1 = maxTracers, 2 = nVertLevels, 3 = nCells + + real (kind=RKIND), dimension(:,:), intent(out) :: & + surfaceTracers, & ! tracer values for new ice at upper surface + basalTracers ! tracer values for new ice at lower surface + ! dimension 1 = maxTracers, 2 = nCells + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + character (len=StrKIND), pointer :: & + config_thermal_solver + + integer, pointer :: & + nCellsSolve ! number of locally owned cells + + real (kind=RKIND), dimension(:,:), pointer :: & + temperature, & ! interior ice temperature + waterfrac, & ! interior water fraction + enthalpy ! interior ice enthalpy + + real (kind=RKIND), dimension(:), pointer :: & + surfaceAirTemperature, & ! surface air temperature + basalTemperature ! basal ice temperature + + real (kind=RKIND), dimension(:), pointer :: & + layerCenterSigma ! sigma coordinate at midpoint of each layer + + real (kind=RKIND), dimension(:), pointer :: & + thickness ! ice thickness + + real (kind=RKIND), pointer :: & + config_ice_density ! ice density + + integer :: iCell, iTracer + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + ! get arrays from mesh pool + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + + ! get arrays from geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + ! get arrays from thermal pool + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) + call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) + call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) + + ! get config variables + call mpas_pool_get_config(liConfigs, 'config_thermal_solver', config_thermal_solver) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + + ! initialize + advectedTracers(:,:,:) = 0.0_RKIND + + ! Notes: + ! (1) For the enthalpy solver, it is necessary to transport enthalpy (rather than + ! temperature and waterfrac separately) in order to conserve energy. + ! (2) Temperature and waterfrac must be up to date (including halo cells) + ! before calling this subroutine. + ! (3) Surface and basal temperature (or enthalpy) are not transported, but their + ! values are applied to new accumulation at either surface. + ! (4) In most cases the surface temperature is equal to min(surfaceAirTemperature, 273.15), + ! so we could set surfaceTracers to surfaceTemperature. But if a positive SMB + ! is applied to a previously ice-free cell, then surfaceTemperature has not yet been + ! initialized to the correct value, so we explicitly use min(surfaceAirTemperature, 273.15). + + iTracer = 0 ! initialize the tracer index + + if (trim(config_thermal_solver) == 'enthalpy') then ! advect enthalpy + + ! Rather than assume that enthalpy is up to date, recompute it from temperature and waterfrac + + do iCell = 1, nCellsSolve + + call li_temperature_to_enthalpy(& + layerCenterSigma, & + thickness(iCell), & + temperature(:,iCell), & + waterfrac(:,iCell), & + enthalpy(:,iCell)) + + enddo + + ! increment the tracer index + iTracer = iTracer + 1 + + ! copy enthalpy to the tracer array + advectedTracers(iTracer,:,:) = enthalpy(:,:) + + ! set enthalpy of new ice at upper and lower surfaces + ! convert temperature to enthalpy assuming waterfrac = 0 for new ice + surfaceTracers(iTracer,:) = min(surfaceAirTemperature(:), kelvin_to_celsius) * config_ice_density*cp_ice + basalTracers(iTracer,:) = basalTemperature(:) * config_ice_density*cp_ice + + elseif (trim(config_thermal_solver) == 'temperature') then ! advect temperature + + ! increment the tracer index + iTracer = iTracer + 1 + + ! copy temperature to the tracer array + advectedTracers(iTracer,:,:) = temperature(:,:) + + ! set enthalpy of new ice at upper and lower surfaces + surfaceTracers(iTracer,:) = min(surfaceAirTemperature(:), kelvin_to_celsius) + basalTracers(iTracer,:) = basalTemperature(:) + + endif + + ! Note: Other tracers (e.g., ice age or damage) can be added here as needed. + ! May need to increase maxTracers in the Registry. + + end subroutine tracer_setup + +!*********************************************************************** +! +! subroutine tracer_finish +! +!> \brief Copy the new tracer values into prognostic arrays +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> Given the new tracers after advection, this routine copies values +!> into the appropriate prognostic arrays (e.g, temperature or enthalpy). +! +!----------------------------------------------------------------------- + + subroutine tracer_finish(& + meshPool, & + geometryPool, & + thermalPool, & + advectedTracers) + + use li_thermal, only: li_enthalpy_to_temperature + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + advectedTracers !< Input: advected tracers + ! dimension 1 = maxTracers, 2 = nVertLevels, 3 = nCells + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + type (mpas_pool_type), intent(in) :: & + geometryPool !< Input: geometry information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: & + thermalPool !< Input/output: temperature/enthalpy information + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + character (len=StrKIND), pointer :: & + config_thermal_solver + + integer, pointer :: & + nCellsSolve ! number of locally owned cells + + real (kind=RKIND), dimension(:), pointer :: & + layerCenterSigma ! sigma coordinate at midpoint of each layer + + real (kind=RKIND), dimension(:), pointer :: & + thickness ! ice thickness + + real (kind=RKIND), dimension(:,:), pointer :: & + temperature, & ! interior ice temperature + waterfrac, & ! water fraction + enthalpy ! interior ice enthalpy + + integer :: iCell, iTracer + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + ! get arrays from mesh pool + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + + ! get arrays from geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + ! get arrays from thermal pool + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) + + ! get config variables + call mpas_pool_get_config(liConfigs, 'config_thermal_solver', config_thermal_solver) + + iTracer = 1 + + if (trim(config_thermal_solver) == 'enthalpy') then + + ! update the enthalpy + enthalpy(:,:) = advectedTracers(iTracer,:,:) + + ! given the enthalpy, compute the temperature and water fraction + + do iCell = 1, nCellsSolve + + call li_enthalpy_to_temperature(& + layerCenterSigma, & + thickness(iCell), & + enthalpy(:,iCell), & + temperature(:,iCell), & + waterfrac(:,iCell)) + + enddo + + elseif (trim(config_thermal_solver) == 'temperature') then + + ! update the temperature + temperature(:,:) = advectedTracers(iTracer,:,:) + + endif + + ! Note: Other tracers (e.g., ice age or damage) can be added here as needed. + ! May need to increase maxTracers in the Registry. + + end subroutine tracer_finish + +!*********************************************************************** + +! routine advect_thickness_tracers_upwind +! +!> \brief Advect thickness and tracers using a first-order upwind scheme. +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> This routine computes new values of the thickness and tracers in each ice layer +!> under horizontal advection using a first-order upwind scheme. +!> Based on subroutine tend_layerThickness_fo_upwind by Matthew Hoffman +! +!----------------------------------------------------------------------- + + subroutine advect_thickness_tracers_upwind(& + dt, & + meshPool, & + layerNormalVelocity, & + layerThicknessEdge, & + layerThicknessOld, & + tracersOld, & + layerThicknessNew, & + tracersNew, & + err, & + advectTracersIn) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), intent(in) :: & + dt !< Input: time step (s) + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerNormalVelocity,& !< Input: normal velocity averaged from interfaces to layer midpoints + layerThicknessEdge !< Input: layer thickness on upstream edge + + logical, intent(in), optional :: & + advectTracersIn !< Input: if true, then advect tracers as well as thickness + ! Default (if not passed in) is to advect tracers + + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThicknessOld !< Input: layer thickness + + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + tracersOld !< Input: tracer values + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(out) :: & + layerThicknessNew !< Output: layer thickness + + real (kind=RKIND), dimension(:,:,:), intent(out) :: & + tracersNew !< Output: tracer values + + integer, intent(out) :: & + err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer, pointer :: & + nCellsSolve, & ! number of locally owned cells + nVertLevels ! number of vertical layers + + integer, dimension(:), pointer :: & + nEdgesOnCell ! number of edges on each cell + + integer, dimension(:,:), pointer :: & + cellsOnEdge, & ! index for 2 cells on each edge + edgesOnCell, & ! index for edges on each cell + edgeSignOnCell ! sign of edges on each cell + + real (kind=RKIND), dimension(:), pointer :: & + dvEdge, & ! distance between vertices on each edge + dcEdge, & ! distance between cell centers on each edge + areaCell ! area of each cell + + logical, pointer :: & + config_print_thickness_advection_info + + integer, dimension(:), pointer :: & + indexToCellID, & ! global index for local cells + indexToEdgeID ! global index for local edges + + integer, pointer :: & + config_stats_cell_ID ! global index of diagnostic cell + + real (kind=RKIND) :: & + invAreaCell, & ! 1.0/areaCell + thicknessFluxEdge, & ! thickness flux on an edge + thicknessTendency, & ! net thickness tendency for a cell + newThickness ! new layer thickness + + integer :: iEdge, iCell, iCell1, iCell2, iEdgeOnCell, k + + integer :: nTracers ! number of tracers + + logical :: advectTracers ! if true, then advect tracers as well as thickness + + real (kind=RKIND), dimension(:), allocatable :: & + tracersEdge, &! upstream tracer values on an edge + thicknessTracerTendency, &! net thickness*tracer tendency for a cell + newThicknessTracers ! new values of thickness*tracer + + real (kind=RKIND), parameter :: bigNumber = 1.0e16_RKIND + ! This is ~300 million years in seconds, but it is small enough not to overflow + + real(kind=RKIND) :: velSign ! = 1.0_RKIND or -1.0_RKIND depending on sign of velocity + + integer :: err_tmp + + ! Variables for optional conservation check + ! Note: This conservation check workw on one process only. + ! For multiprocess runs, the conservation check would require global sums + ! and would need to be outside a block loop. + logical, parameter :: checkConservation = .false. + real (kind=RKIND) :: initVolumeSum, finalVolumeSum, difference + real (kind=RKIND) :: initEnergySum, finalEnergySum + real (kind=RKIND), parameter :: eps11 = 1.e-11_RKIND + + err = 0 + + if (present(advectTracersIn)) then + advectTracers = advectTracersIn + else + advectTracers = .true. + endif + + nTracers = size(tracersOld,1) + + ! allocate temporary arrays + allocate(tracersEdge(nTracers)) + allocate(thicknessTracerTendency(nTracers)) + allocate(newThicknessTracers(nTracers)) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + ! get mesh arrays + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + call mpas_pool_get_array(meshPool, 'indexToEdgeID', indexToEdgeID) + + ! get config variables + call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + + if (checkConservation) then + + ! compute initial ice volume (= area*thickness) and energy (= area*thickness*tracer1) + initVolumeSum = 0.0_RKIND + initEnergySum = 0.0_RKIND + do iCell = 1, nCellsSolve + initVolumeSum = initVolumeSum + areaCell(iCell) * sum(layerThicknessOld(:,iCell)) + initEnergySum = initEnergySum + areaCell(iCell) * sum(layerThicknessOld(:,iCell)*tracersOld(1,:,iCell)) + enddo + + endif + + ! Note: This loop structure (nCells loop outside nEdgesOnCell loop) results in double calculation of fluxes + ! across each edge. But upwind advection is cheap, so the extra cost is minimal. + + ! loop over locally owned cells + do iCell = 1, nCellsSolve + + invAreaCell = 1.0_RKIND / areaCell(iCell) + + if (indexToCellID(iCell) == config_stats_cell_ID .and. config_print_thickness_advection_info) then + call mpas_log_write('In advect_thickness_tracer, iCell (local=$i, global=$i) =', intArgs=(/iCell, indexToCellID(iCell)/)) + call mpas_log_write('k, iEdgeOnCell, layerNormalVelocity, layerThicknessEdge, ' // & + 'thicknessFluxEdge, thicknessTendency, thicknessTracerTendency:') + endif + + ! loop over layers + do k = 1, nVertLevels + + ! initialize the tendencies for this layer + thicknessTendency = 0.0_RKIND + thicknessTracerTendency(:) = 0.0_RKIND + + ! compute fluxes for each edge of the cell + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + + iEdge = edgesOnCell(iEdgeOnCell, iCell) + iCell1 = cellsOnEdge(1,iEdge) + iCell2 = cellsOnEdge(2,iEdge) + + ! Increment the thickness and thickness*tracer tendencies + + thicknessFluxEdge = layerNormalVelocity(k,iEdge) * dvEdge(iEdge) * layerThicknessEdge(k,iEdge) + thicknessTendency = thicknessTendency & + + edgeSignOnCell(iEdgeOnCell,iCell) * thicknessFluxEdge * invAreaCell + + if (advectTracers) then + + velSign = sign(1.0_RKIND, layerNormalVelocity(k,iEdge)) + + ! Assign the upwind tracer values to each edge + ! Note: layerThicknessEdge is passed in and does not need to be computed here + + tracersEdge(:) = max(velSign * tracersOld(:,k,iCell1), & + velSign * (-1.0_RKIND) * tracersOld(:,k,iCell2)) + + thicknessTracerTendency(:) = thicknessTracerTendency(:) & + + edgeSignOnCell(iEdgeOnCell,iCell) * thicknessFluxEdge * invAreaCell * tracersEdge(:) + + endif ! advectTracers + + !WHL - debug + if (indexToCellID(iCell) == config_stats_cell_ID .and. k==1 .and. config_print_thickness_advection_info) then + call mpas_log_write("$i $i $r $r $r $r $r", intArgs=(/k, iEdgeOnCell/), realArgs=(/layerNormalVelocity(k,iEdge), & + layerThicknessEdge(k,iEdge), thicknessFluxEdge, thicknessTendency, thicknessTracerTendency(1)/) ) + endif + + enddo ! iEdgeOnCell + + ! Compute the new layer thickness and tracers + + if (advectTracers) then + + ! new thickness*tracer products + newThicknessTracers(:) = layerThicknessOld(k,iCell)*tracersOld(:,k,iCell) + thicknessTracerTendency(:) * dt + + ! new layer thickness + layerThicknessNew(k,iCell) = layerThicknessOld(k,iCell) + thicknessTendency * dt + + ! new tracers + if (layerThicknessNew(k,iCell) > 0.0_RKIND) then + tracersNew(:,k,iCell) = newThicknessTracers(:) / layerThicknessNew(k,iCell) + else + tracersNew(:,k,iCell) = 0.0_RKIND + endif + + else ! advecting thickness only + + layerThicknessNew(k,iCell) = layerThicknessOld(k,iCell) + thicknessTendency * dt + + endif ! advectTracers + + enddo ! k + + enddo ! iCell + + + if (checkConservation) then + + ! compute final ice volume (= area*thickness) and energy (= area*thickness*tracer1) + finalVolumeSum = 0.0_RKIND + finalEnergySum = 0.0_RKIND + do iCell = 1, nCellsSolve + finalVolumeSum = finalVolumeSum + areaCell(iCell) * sum(layerThicknessNew(:,iCell)) + finalEnergySum = finalEnergySum + areaCell(iCell) * sum(layerThicknessNew(:,iCell)*tracersNew(1,:,iCell)) + enddo + + if (config_print_thickness_advection_info) then + call mpas_log_write('init volume, final volume, difference: $r $r $r' , & + realArgs=(/initVolumeSum, finalVolumeSum, finalVolumeSum - initVolumeSum/)) + call mpas_log_write('init energy, final energy, difference: $r $r $r', & + realArgs=(/initEnergySum, finalEnergySum, finalEnergySum - initEnergySum/)) + endif + + !TODO - Make these fatal errors instead of warnings? + difference = abs(finalVolumeSum - initVolumeSum) + if (difference/initVolumeSum > eps11) then + call mpas_log_write('upwind advection, mass conservation error', MPAS_LOG_WARN) + call mpas_log_write('init volume, final volume, difference: $r $r $r', & + realArgs=(/initVolumeSum, finalVolumeSum, finalVolumeSum - initVolumeSum/)) + endif + + difference = abs(finalEnergySum - initEnergySum) + if (difference/initEnergySum > eps11) then + call mpas_log_write('upwind advection, mass*tracer conservation error', MPAS_LOG_WARN) + call mpas_log_write('init energy, final energy, difference: $r $r $r', & + realArgs=(/initEnergySum, finalEnergySum, finalEnergySum - initEnergySum/)) + endif + + endif + + ! clean up + deallocate(tracersEdge) + deallocate(thicknessTracerTendency) + deallocate(newThicknessTracers) + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in advect_thickness_tracers_upwind.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + + end subroutine advect_thickness_tracers_upwind + +!*********************************************************************** + +! routine li_layer_normal_velocity +! +!> \brief Compute layer normal velocity and diagnose the advective CFL limit +!> \author William Lipscomb +!> \date January 2016 +!> \details +!> This routine computed layer normal velocities on each edge in preparation +!> for advection. Given these velocities, it diagnoses the advective CFL limit +!> for the edges on this block. The logic here was originally part of +!> subroutine advect_thickness_tracers_upwind, but was moved to a separate +!> subroutine so that an adaptive timestep could be diagnosed at the start +!> of the time step. +! +!----------------------------------------------------------------------- + + subroutine li_layer_normal_velocity(& + meshPool, & + normalVelocity, & + layerNormalVelocity, & + minOfMaxAllowableDt, & + err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + real (kind=RKIND), dimension(:,:), intent(in) :: & + normalVelocity !< Input: normal velocity on cell edges + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(out) :: & + layerNormalVelocity !< Output: normal velocity on cell edges, averaged to layer midpoints + + real (kind=RKIND), intent(out) :: & + minOfMaxAllowableDt !< Output: maximum allowable dt based on CFL condition + + integer, intent(out) :: & + err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer, pointer :: & + nEdgesSolve, & ! dnumber of locally owned edges + nVertLevels ! number of vertical layers + + real (kind=RKIND), dimension(:), pointer :: & + dcEdge ! distance between cell centers on each edge + + logical, pointer :: & + config_print_thickness_advection_info + + integer, dimension(:), pointer :: & + indexToEdgeID ! global index for edges + + real (kind=RKIND) :: & + maxAllowableDt ! max allowable dt based on advective CFL condition + + integer :: iEdge, k + + real (kind=RKIND), parameter :: bigNumber = 1.0e16_RKIND + ! This is ~300 million years in seconds, but is small enough not to overflow + + err = 0 + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + ! get mesh arrays + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'indexToEdgeID', indexToEdgeID) + + ! get config variables + call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) + + ! initialize output variables + minOfMaxAllowableDt = bigNumber + + ! loop over local edges + do iEdge = 1, nEdgesSolve + + ! loop over layers + do k = 1, nVertLevels + + ! average normal velocities from layer interfaces to layer midpoints for advection + layerNormalVelocity(k,iEdge) = 0.5_RKIND * (normalVelocity(k,iEdge) + normalVelocity(k+1,iEdge)) + + ! Check for potential CFL violation + if (abs(layerNormalVelocity(k,iEdge)) > 0.0_RKIND) then + maxAllowableDt = (0.5_RKIND * dcEdge(iEdge)) / abs(layerNormalVelocity(k,iEdge)) + else + maxAllowableDt = bigNumber + endif + + minOfMaxAllowableDt = min(minOfMaxAllowableDt, maxAllowableDt) + + enddo ! k + + enddo ! iEdge + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_layer_normal_velocity.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + + end subroutine li_layer_normal_velocity + +!----------------------------------------------------------------------- +! +! subroutine vertical_remap +! +!> \brief Vertical remapping of thickness and tracers +!> \author Matt Hoffman +!> \date October 2013; revised November 2015 +!> \details +!> This routine performs vertical remapping of thickness and tracers from one vertical +!> coordinate system to another, as is required for our sigma coordinate system. +!> The remapping is first-order accurate. +!> This uses code from the CISM glissade_transport.F90 module written by Bill Lipscomb. +!> I have altered the array structures to work with MPAS and refactored it. +!> It now does all calculations column-wise, so it can be vectorized using +!> OpenMP over either blocks or cells. +! +!----------------------------------------------------------------------- + subroutine vertical_remap(thickness, cellMask, meshPool, layerThickness, tracers, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: LI mesh information + + real(kind=RKIND), dimension(:), intent(in) :: & + thickness !< Input: ice thickness + + integer, dimension(:), intent(in) :: & + cellMask !< Input: mask for cells (needed for determining presence/absence of ice) + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(inout) :: & + layerThickness !< Input/Output: thickness of layers (to be updated) + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tracers !< Input/Output: tracer values (to be updated) + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + ! pointers to mesh arrays + real (kind=RKIND), dimension(:), pointer :: layerThicknessFractions, layerInterfaceSigma + + ! local arrays + real (kind=RKIND), dimension(:), allocatable :: layerInterfaceSigma_Input + real (kind=RKIND), dimension(:,:), allocatable :: hTsum + + ! counters, mesh variables, index variables + integer, pointer :: nCells, nVertLevels + integer :: nTracers, iCell, k, k1, k2, nt + + ! stuff for making calculations + real(kind=RKIND) :: zhi, zlo, hOverlap + + ! variables for optional conservation check + ! set to 'true' for now, since it is inexpensive and might catch problems + logical, parameter :: checkConservation = .true. + real (kind=RKIND) :: initEnergySum, finalEnergySum, difference + real (kind=RKIND), parameter :: eps11 = 1.e-11_RKIND + + err = 0 + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + nTracers = size(tracers, 1) + + call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) + call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) + + allocate(layerInterfaceSigma_Input(nVertLevels+1)) + allocate(hTsum(nTracers, nVertLevels)) + + ! loop over cells + do iCell = 1, nCells + + if (checkConservation) then ! compute sum of layerThickness*tracer1 in the column + initEnergySum = sum(layerThickness(:,iCell)*tracers(1,:,iCell)) + endif + + if (thickness(iCell) > 0.0_RKIND) then + + ! *** Calculate vertical sigma coordinates of each layer interface for the input non-sigma state + ! (we already have the desired new sigma-based state as layerInterfaceSigma) + + layerInterfaceSigma_Input(1) = 0.0_RKIND + do k = 2, nVertLevels + layerInterfaceSigma_Input(k) = layerInterfaceSigma_Input(k-1) + layerThickness(k-1, iCell) / thickness(iCell) + end do + layerInterfaceSigma_Input(nVertLevels+1) = 1.0_RKIND + + ! *** Compute new layer thicknesses (layerInterfaceSigma coordinates) + + do k = 1, nVertLevels + layerThickness(k,iCell) = layerThicknessFractions(k) * thickness(iCell) + end do + + !----------------------------------------------------------------- + ! Compute sum of h*T for each new layer (k2) by integrating + ! over the regions of overlap with old layers (k1). + ! Note: It might be worth trying a more efficient + ! search algorithm if the number of layers is large. + ! This algorithm scales as nlyr^2. + !----------------------------------------------------------------- + + do k2 = 1, nVertLevels + hTsum(:,k2) = 0.0_RKIND + do k1 = 1, nVertLevels + do nt = 1, nTracers + zhi = min (layerInterfaceSigma_Input(k1+1), layerInterfaceSigma(k2+1)) + zlo = max (layerInterfaceSigma_Input(k1), layerInterfaceSigma(k2)) + hOverlap = max (zhi-zlo, 0.0_RKIND) * thickness(iCell) + hTsum(nt,k2) = htsum(nt,k2) + tracers(nt,k1,iCell) * hOverlap + enddo ! nt + enddo ! k1 + enddo ! k2 + + !----------------------------------------------------------------- + ! Compute tracer values in new layers + !----------------------------------------------------------------- + + do k = 1, nVertLevels + do nt = 1, nTracers + tracers(nt,k,iCell) = hTsum(nt,k) / (layerThickness(k, iCell)) + enddo ! nt + enddo ! k + + endif ! thickness > 0.0 + + if (checkConservation) then ! compute new sum of layerThickness*tracer1 in the column + finalEnergySum = sum(layerThickness(:,iCell)*tracers(1,:,iCell)) + difference = abs(finalEnergySum - initEnergySum) + if (initEnergySum > eps11) then + if (difference/initEnergySum > eps11) then + call mpas_log_write('vertical_remap, mass*tracer conservation error, iCell = $i', MPAS_LOG_WARN, intArgs=(/iCell/)) + call mpas_log_write('init energy, final energy, difference: $r $r $r', & + realArgs=(/initEnergySum, finalEnergySum, finalEnergySum - initEnergySum/)) + endif + endif + endif + + enddo ! nCellsSolve + + ! clean up + deallocate(layerInterfaceSigma_Input) + deallocate(hTsum) + + end subroutine vertical_remap + +!*********************************************************************** + + end module li_advection + diff --git a/src/core_landice/mode_forward/mpas_li_calving.F b/src/core_landice/mode_forward/mpas_li_calving.F new file mode 100644 index 0000000000..7970ea1f67 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_calving.F @@ -0,0 +1,1399 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_calving +! +!> \brief MPAS land ice calving scheme +!> \author William Lipscomb +!> \date September 2015 +!> \details +!> This module contains several options for calving ice. +! +!----------------------------------------------------------------------- + +module li_calving + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_log + + use li_setup + use li_mask + use li_constants + + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_calve_ice, li_restore_calving_front + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + contains +!*********************************************************************** + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_calve_ice +! +!> \brief MPAS land ice calving scheme +!> \author William Lipscomb +!> \date September 2015 +!> \details +!> This routine contains several options for calving ice: +!> (0) Do nothing +!> (1) Calve all floating ice +!> (2) Calve ice based on a topographic threshold +!> (3) Calve ice based on an ice thickness threshold +!----------------------------------------------------------------------- + + subroutine li_calve_ice(domain, err) + + use li_advection + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: scratchPool + + ! calving-relevant config options + character (len=StrKIND), pointer :: config_calving + logical, pointer :: config_print_calving_info, config_data_calving + real(kind=RKIND), pointer :: config_calving_timescale + + integer, pointer :: nCells + + real (kind=RKIND), pointer :: deltat !< time step (s) + + integer, dimension(:), pointer :: & + indexToCellID ! list of global cell IDs + + real (kind=RKIND) :: & + calvingFraction ! fraction of ice that calves in each column; depends on calving_timescale + + real (kind=RKIND), dimension(:), pointer :: & + thickness, & ! ice thickness + bedTopography ! bed topography (negative below sea level) + + real (kind=RKIND), dimension(:), pointer :: & + calvingThickness ! thickness of ice that calves (computed in this subroutine) + ! typically the entire ice thickness, but will be a fraction of the thickness + ! if calving_timescale > dt + + type (field1dReal), pointer :: originalThicknessField + + real (kind=RKIND), dimension(:), pointer :: originalThickness + + integer :: iCell + + integer :: err_tmp + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_calving', config_calving) + call mpas_pool_get_config(liConfigs, 'config_calving_timescale', config_calving_timescale) + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + call mpas_pool_get_config(liConfigs, 'config_data_calving', config_data_calving) + + if (trim(config_calving) == 'none') then + return ! do nothing + endif + + ! Get deltat from first block (same on all blocks) + block => domain % blocklist + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + + ! based on the calving timescale, set the fraction of ice that calves + if (config_calving_timescale > 0.0_RKIND) then + calvingFraction = min(deltat/config_calving_timescale, 1.0_RKIND) + !WHL - debug + if (config_print_calving_info) then + call mpas_log_write('Calving a fraction of the ice thickness at each timestep') + call mpas_log_write('deltat (yr) = $r, calvingFraction = $r', realArgs=(/deltat/scyr, calvingFraction/)) + endif + else + calvingFraction = 1.0_RKIND ! calve the entire thickness in eligible columns + endif + + if (config_print_calving_info) then + call mpas_log_write('Do ice calving, option = ' // trim(config_calving)) + call mpas_log_write('Calving timscale (yr) = $r', realArgs=(/config_calving_timescale / scyr/)) + endif + + ! In data calving mode we need to calculate calving flux but not have it be applied. + ! However, the eigencalving method requires multiple applications of the calvingThickness + ! to the thickness. So the simplest method to apply data calving is to store the old + ! thickness and then set it back when we are done. + if (config_data_calving) then + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_field(scratchPool, 'workCell2', originalThicknessField) + call mpas_allocate_scratch_field(originalThicknessField, single_block_in = .false.) + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(scratchPool, 'workCell2', originalThickness) + + ! Store old thickness here + originalThickness(:) = thickness(:) + + block => block % next + end do + endif + + ! compute calvingThickness based on the calving_config option + ! the calvingThickness field gets applied to the thickness state field + ! after this if-construct + if (trim(config_calving) == 'thickness_threshold') then + + call thickness_calving(domain, calvingFraction, err_tmp) + err = ior(err, err_tmp) + + elseif (trim(config_calving) == 'floating') then + + call floating_calving(domain, calvingFraction, err_tmp) + err = ior(err, err_tmp) + + elseif (trim(config_calving) == 'topographic_threshold') then + + call topographic_calving(domain, calvingFraction, err_tmp) + err = ior(err, err_tmp) + + elseif (trim(config_calving) == 'eigencalving') then + + call eigencalving(domain, err_tmp) + err = ior(err, err_tmp) + + else + + call mpas_log_write("Invalid option for config_calving specified: " // trim(config_calving), MPAS_LOG_ERR) + err = 1 + + endif + + + ! Final operations after calving has been applied. + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + ! In data calving mode we just calculate what should be calved but don't actually calve it. + ! So set thickness back to original value. + if (config_data_calving) then + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_array(scratchPool, 'workCell2', originalThickness) + thickness(:) = originalThickness(:) + endif + + ! Optionally, print a list of cells with calving + if (config_print_calving_info) then + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + + call mpas_log_write(' ') + call mpas_log_write('Global cell ID, bedTopography, calvingThickness:') + do iCell = 1, nCells + if (calvingThickness(iCell) > 0.0_RKIND) then + call mpas_log_write("$i $r $r", intArgs=(/indexToCellID(iCell)/), realArgs=(/bedTopography(iCell), calvingThickness(iCell)/)) + endif + enddo + endif ! config_print_calving_info + + ! Update mask and geometry + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + call li_update_geometry(geometryPool) + + block => block % next + end do + + if (config_data_calving) then + call mpas_deallocate_scratch_field(originalThicknessField, single_block_in=.false.) + endif + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_calve_ice.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_calve_ice + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_restore_calving_front +! +!> \brief MPAS land ice restore the calving front +!> \author William Lipscomb +!> \date September 2015 +!> \details +!> This routine restores the calving front to its initial position: +!> (1) It removes any floating ice that has advanced beyond the initial front. +!> (2) It adds back a thin layer of ice wherever the ice has retreated from +!> the initial front. +!----------------------------------------------------------------------- + + subroutine li_restore_calving_front(domain, err) + + use li_thermal, only: li_init_linear_temperature_in_column + use li_advection + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: velocityPool + + integer, pointer :: nCellsSolve, nVertLevels + + logical, pointer :: & + config_print_calving_info + + real (kind=RKIND), pointer :: & + config_sea_level, & + config_dynamic_thickness + + integer, dimension(:), pointer :: & + cellMask ! bit mask describing whether ice is floating, dynamically active, etc. + + real(kind=RKIND), dimension(:), pointer :: & + layerCenterSigma ! vertical sigma coordinate at layer midpoints + + ! geometry and calving fields + real (kind=RKIND), dimension(:), pointer :: & + thickness, & ! ice thickness + bedTopography, & ! elevation of the bed + calvingThickness, & ! thickness of ice that calves + ! > 0 for cells below sea level that were initially ice-free and now have ice + restoreThickness ! thickness of ice that is added to restore the calving front to its initial position + ! > 0 for cells below sea level that were initially ice-covered and now have very thin or no ice + + real (kind=RKIND) :: & + restoreThicknessMin ! small thickness to which ice is restored should it fall below this thickness + + ! thermal fields + ! These are needed to initialize the temperature profile in restored columns. + real (kind=RKIND), dimension(:,:), pointer :: & + temperature, & ! interior ice temperature + waterfrac ! interior water fraction + + real (kind=RKIND), dimension(:), pointer :: & + surfaceAirTemperature, & ! surface air temperature + surfaceTemperature, & ! surface ice temperature + basalTemperature ! basal ice temperature + + integer :: iCell, err_tmp + + !WHL - debug + logical, parameter :: circular_shelf = .false. + integer, parameter :: ncellsPerRow = 40 + integer, parameter :: nRows = 46 + integer :: i, iRow + integer :: k + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) ! required for cellMask computation + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + ! get required fields from the mesh pool + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + + ! get required fields from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'restoreThickness', restoreThickness) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + ! get required fields from the thermal pool + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) + call mpas_pool_get_array(thermalPool, 'surfaceTemperature', surfaceTemperature) + call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) + + ! get config variables + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) + + if (config_print_calving_info) then + call mpas_log_write('Restore calving front') + call mpas_log_write('max thickness (m) = $r', realArgs=(/maxval(thickness)/)) + + !WHL - debug - for circular shelf test case +! if (circular_shelf) then +! call mpas_log_write('Initial ice thickness' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stdoutUnit,'(a3)',advance='no') ' ' +! endif +!!! do i = 1, nCellsPerRow +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +!!! write(stdoutUnit,'(i5)',advance='no') iCell +! write(stdoutUnit,'(f8.2)',advance='no') thickness(iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo +! endif ! circular_shelf + + endif + + ! set restoreThicknessMin + ! It should be less than config_dynamic_thickness so that the restored ice remains dynamically inactive, + ! even with a certain amount of natural variability. + ! It should also be large enough to permit stable thermal calculations. + ! For now, setting it to 1/10 of config_dynamic_thickness + + restoreThicknessMin = 0.1_RKIND * config_dynamic_thickness + + ! calculate masks - so we know where the calving front was located initially + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! initialize + calvingThickness = 0.0_RKIND + restoreThickness = 0.0_RKIND + + ! loop over locally owned cells + do iCell = 1, nCellsSolve + + if (bedTopography(iCell) < config_sea_level) then + + ! The bed is below sea level; test for calving-front advance and retreat. + + if (li_mask_is_initial_ice(cellMask(iCell)) .and. thickness(iCell) < restoreThicknessMin) then + + ! Ice was present in this cell initially, but now is either very thin or absent; + ! reset the thickness to restoreThicknessMin. + ! Note: Mass is not conserved. + ! Save the difference (restoreThicknessMin - thickness) so as to keep track of energy non-conservation. + + if (config_print_calving_info) then + call mpas_log_write('Restore ice: iCell=$i, thickness=$r', intArgs=(/iCell/), realArgs=(/thickness(iCell)/)) + endif + + restoreThickness(iCell) = restoreThicknessMin - thickness(iCell) + thickness(iCell) = restoreThicknessMin + + ! Initial a linear temperature profile in the column + ! Note: Energy is not conserved. + + call li_init_linear_temperature_in_column(& + nVertLevels, & + layerCenterSigma, & + thickness(iCell), & + surfaceAirTemperature(iCell), & + temperature(:,iCell), & + waterfrac(:,iCell), & + surfaceTemperature(iCell), & + basalTemperature(iCell)) + + elseif (.not.li_mask_is_initial_ice(cellMask(iCell)) .and. thickness(iCell) > 0.0_RKIND) then + + ! This cell was initially ice-free but now has ice. + ! Remove the ice and add it to calvingThickness. + + if (config_print_calving_info) then + call mpas_log_write('Remove ice: iCell=$i, thickness=$r', intArgs=(/iCell/), realArgs=(/thickness(iCell)/)) + endif + + calvingThickness(iCell) = thickness(iCell) + thickness(iCell) = 0.0_RKIND + + endif ! li_mask_is_initial_ice + + endif ! bedTopography < config_sea_level + + enddo ! iCell + + block => block % next + enddo + + ! Update mask and geometry + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + call li_update_geometry(geometryPool) + + block => block % next + end do + + if (config_print_calving_info) then + call mpas_log_write('Restored the initial calving front') + + !WHL - debug - for circular shelf test case +! if (circular_shelf) then +! write(stdoutUnit,*) 'Final ice thickness' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stdoutUnit,'(a3)',advance='no') ' ' +! endif +!!! do i = 1, nCellsPerRow +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +!!! write(stdoutUnit,'(i5)',advance='no') iCell +! write(stdoutUnit,'(f8.2)',advance='no') thickness(iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo +! endif ! circular shelf + + endif + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_restore_calving_front.", MPAS_LOG_ERR) + endif + + + end subroutine li_restore_calving_front + +!*********************************************************************** +!*********************************************************************** +! Private subroutines: +!*********************************************************************** +!*********************************************************************** + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine thickness_calving +! +!> \brief Calve ice based on thickness threshold +!> \author William Lipscomb, moved to separate subroutine by Matt Hoffman Feb. 2018 +!> \date September 2015 +!> \details calve ice thinner than a thickness threshold +!> Note: The thickness-threshold option is different from the others. +!> For the other options, we look at each cell and determine whether it meets the calving-law criteria +!> (e.g., ice is floating, or the topography lies below a given level). +!> If a cell meets the criteria and lies in the calving domain (e.g., at the margin), it is calved. +!> For the thickness-threshold option, ice thinner than config_calving_thickness is calved, +!> but only if it lies beyond a protected ring of thin ice at the floating margin. +!> The reason for this more complicated approach is that we do not want to remove all floating ice +!> thinner than the calving thickness, because then we would remove thin ice that has just +!> been advected from active cells at the margin, and the calving front would be unable to advance. +!> By protecting a ring of inactive ice (thickness < config_calving_thickness) at the margin, +!> we allow ice in these cells to thicken and become active, thus advancing the calving front. +!> The calving front retreats when active floating ice thins to become inactive, removing protection +!> from previously protected cells. +!> +!> Specifically, the rules are as follows: +!> - Mark cells as active-for-calving if either (1) grounded, with thickness > config_dynamic_thickness +!> or (2) floating, with thickness > config_calving_thickness. +!> - Mark cells as ocean if not land and not active. +!> - Mark cells as lying on the inactive margin if not active, but with an active neighbor. +!> - Calve ice in ocean cells that are not on the inactive margin. + +!----------------------------------------------------------------------- + subroutine thickness_calving(domain, calvingFraction, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(in) :: calvingFraction !< fraction of possible ice to calve on this timestep + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + + real(kind=RKIND), pointer :: & + config_calving_thickness, & + config_sea_level, & + config_dynamic_thickness, & + config_ice_density, & + config_ocean_density + + logical, pointer :: config_print_calving_info + + type (field1dInteger), pointer :: activeForCalvingMaskField + integer, dimension(:), pointer :: activeForCalvingMask ! = 1 for grounded cells thicker than config_dynamic_thickness; + ! = 1 for floating cells thicker than config_calving_thickness; + ! = 0 elsewhere + + type (field1dInteger), pointer :: inactiveMarginMaskField + integer, dimension(:), pointer :: inactiveMarginMask + ! = 1 for inactive cells (thin or no ice) that have 1 or more active neighbors + + type (field1dInteger), pointer :: oceanMaskField + integer, dimension(:), pointer :: oceanMask ! = 1 for cells that are not land and do not have active ice + ! includes floating cells with inactive icea + + type (mpas_pool_type), pointer :: meshPool, geometryPool, velocityPool, scratchPool + + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + + integer, dimension(:,:), pointer :: & + cellsOnCell ! list of cells that neighbor each cell + + real (kind=RKIND), dimension(:), pointer :: & + thickness, & ! ice thickness + bedTopography ! bed topography (negative below sea level) + + real (kind=RKIND), dimension(:), pointer :: & + calvingThickness ! thickness of ice that calves (computed in this subroutine) + + integer, pointer :: nCells + integer :: iCell, iCellOnCell, iCellNeighbor + real (kind=RKIND) :: flotationThickness ! thickness at which marine-based ice starts to float + + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_calving_thickness', config_calving_thickness) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + + + if (config_print_calving_info) then + call mpas_log_write('Calving thickness (m) = $r', realArgs=(/config_calving_thickness/)) + call mpas_log_write('Dynamic thickness (m) = $r', realArgs=(/config_dynamic_thickness/)) + endif + + ! Make sure config_calving_thickness > config_dynamic_thickness. + ! Otherwise the algorithm will not work. + if (config_calving_thickness < config_dynamic_thickness) then + call mpas_log_write('config_calving_thickness (m) = $r', realArgs=(/config_calving_thickness/), messageType=MPAS_LOG_ERR) + call mpas_log_write('config_dynamic_thickness (m) = $r', realArgs=(/config_dynamic_thickness/), messageType=MPAS_LOG_ERR) + call mpas_log_write('Must have config_calving_thickness > config_dynamic_thickness', messageType=MPAS_LOG_ERR) + err = 1 + endif + + ! block loop + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) ! required for cellMask computation + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + ! get required fields from the mesh pool + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + + ! get required fields from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + + ! get scratch fields for calving + ! 'true' flag means to allocate the field for a single block + call mpas_pool_get_field(scratchPool, 'iceCellMask', activeForCalvingMaskField) + call mpas_allocate_scratch_field(activeForCalvingMaskField, .true.) + activeForCalvingMask => activeForCalvingMaskField % array + + call mpas_pool_get_field(scratchPool, 'iceCellMask2', inactiveMarginMaskField) + call mpas_allocate_scratch_field(inactiveMarginMaskField, .true.) + inactiveMarginMask => inactiveMarginMaskField % array + + call mpas_pool_get_field(scratchPool, 'iceCellMask3', oceanMaskField) + call mpas_allocate_scratch_field(oceanMaskField, .true.) + oceanMask => oceanMaskField % array + + ! Initialize + calvingThickness = 0.0_RKIND + + ! Identify cells that are active-for-calving: + ! (1) Grounded ice with thickness > config_dynamic_thickness + ! (2) Floating ice with thickness > config_calving_thickness + + activeForCalvingMask(:) = 0 + + do iCell = 1, nCells + + if (bedTopography(iCell) >= config_sea_level) then ! land cell + if (thickness(iCell) > config_dynamic_thickness) then ! active for calving + activeForCalvingMask(iCell) = 1 + endif + else ! marine cell, topography below sea level + flotationThickness = (config_ocean_density/config_ice_density) * (config_sea_level - bedTopography(iCell)) + if (thickness(iCell) < flotationThickness) then ! floating + if (thickness(iCell) > config_calving_thickness) then + activeForCalvingMask(iCell) = 1 + endif + else ! grounded marine ice + if (thickness(iCell) > config_dynamic_thickness) then ! active for calving + activeForCalvingMask(iCell) = 1 + endif + endif ! floating or grounded + endif ! land or marine + + enddo ! iCell + + ! Identify cells that are inactive but border active-for-calving cells + + inactiveMarginMask(:) = 0 + + do iCell = 1, nCells + if (activeForCalvingMask(iCell) == 0) then ! inactive + + ! check whether any neighbor cells are active + do iCellOnCell = 1, nEdgesOnCell(iCell) + iCellNeighbor = cellsOnCell(iCellOnCell,iCell) + if (activeForCalvingMask(iCellNeighbor) == 1) then ! neighbor cell is active + inactiveMarginMask(iCell) = 1 + exit + endif + enddo ! iCellOnCell + + endif ! inactive + enddo ! iCell + + ! Identify ocean cells (not land and not active ice, but including inactive floating ice) + + where (bedTopography < config_sea_level .and. activeForCalvingMask == 0) + oceanMask = 1 + elsewhere + oceanMask = 0 + endwhere + +! if (config_print_calving_info) then +! +! write(stdoutUnit,*) 'Active-for-calving mask' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stdoutUnit,'(a3)',advance='no') ' ' +! endif +!! do i = 1, nCellsPerRow +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +!! write(stdoutUnit,'(i5)',advance='no') iCell +! write(stdoutUnit,'(i8)',advance='no') activeForCalvingMask(iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo +! +! write(stdoutUnit,*) 'Inactive margin mask' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stdoutUnit,'(a3)',advance='no') ' ' +! endif +!! do i = 1, nCellsPerRow +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +!! write(stdoutUnit,'(i5)',advance='no') iCell +! write(stdoutUnit,'(i8)',advance='no') inactiveMarginMask(iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo +! +! endif ! config_print_calving_info + + ! Calve ice in ocean cells that are not on the protected inactive margin + + where (oceanMask == 1 .and. inactiveMarginMask == 0 .and. thickness > 0.0_RKIND) + calvingThickness = thickness * calvingFraction + endwhere + + ! === apply calving === + thickness(:) = thickness(:) - calvingThickness(:) + + block => block % next + enddo + + ! clean up + call mpas_deallocate_scratch_field(activeForCalvingMaskField, .true.) + call mpas_deallocate_scratch_field(inactiveMarginMaskField, .true.) + call mpas_deallocate_scratch_field(oceanMaskField, .true.) + + end subroutine thickness_calving + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine floating_calving +! +!> \brief Calve any ice that is floating +!> \author William Lipscomb, moved to separate subroutine by Matt Hoffman Feb. 2018 +!> \date September 2015 +!> \details +!----------------------------------------------------------------------- + subroutine floating_calving(domain, calvingFraction, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(in) :: calvingFraction !< fraction of possible ice to calve on this timestep + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + real (kind=RKIND), dimension(:), pointer :: calvingThickness ! thickness of ice that calves (computed in this subroutine) + real (kind=RKIND), dimension(:), pointer :: thickness + integer, dimension(:), pointer :: cellMask + + err = 0 + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + calvingThickness = 0.0_RKIND + + ! Note: The floating_ice mask includes all floating ice, both inactive and active + where (li_mask_is_floating_ice(cellMask)) + calvingThickness = thickness * calvingFraction + endwhere + + ! === apply calving === + thickness(:) = thickness(:) - calvingThickness(:) + + block => block % next + enddo + + end subroutine floating_calving + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine topographic_calving +! +!> \brief Calve any floating ice existing where ocean bathymetry is deeper than a threshold +!> \author William Lipscomb, moved to separate subroutine by Matt Hoffman Feb. 2018 +!> \date September 2015 +!> \details calve ice where the bed topography lies below a threshold depth +!----------------------------------------------------------------------- + subroutine topographic_calving(domain, calvingFraction, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(in) :: calvingFraction !< fraction of possible ice to calve on this timestep + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + real (kind=RKIND), dimension(:), pointer :: calvingThickness ! thickness of ice that calves (computed in this subroutine) + real(kind=RKIND), pointer :: config_calving_topography + real(kind=RKIND), pointer :: config_sea_level + logical, pointer :: config_print_calving_info + real (kind=RKIND), dimension(:), pointer :: bedTopography, thickness + integer, dimension(:), pointer :: cellMask + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + call mpas_pool_get_config(liConfigs, 'config_calving_topography', config_calving_topography) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + + if (config_print_calving_info) then + call mpas_log_write('Calving topographic threshold (m) = $r', realArgs=(/config_calving_topography/)) + endif + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + calvingThickness = 0.0_RKIND + + where ( (li_mask_is_floating_ice(cellMask)) .and. (bedTopography < config_calving_topography + config_sea_level) ) + calvingThickness = thickness * calvingFraction + endwhere + + ! === apply calving === + thickness(:) = thickness(:) - calvingThickness(:) + + block => block % next + enddo + + end subroutine topographic_calving + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine eigencalving +! +!> \brief Calve ice from the calving front based on a strain rate threshold +!> \author Matthew Hoffman +!> \date Feb. 2018 +!> \details Calve using eigencalving scheme in which calving rate is taken +!> proportional to the product of principle strain rates, if both extensional, +!> and zero otherwise. Described in detail in: +!> Levermann, A., T. Albrecht, R. Winkelmann, M. A. Martin, M. Haseloff, and +!> I. Joughin (2012), Kinematic first-order calving law implies potential for +!> abrupt ice-shelf retreat, Cryosph., 6(2), 273–286, doi:10.5194/tc-6-273-2012. +!----------------------------------------------------------------------- + subroutine eigencalving(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: scratchPool + real(kind=RKIND), pointer :: config_calving_eigencalving_parameter_scalar_value + character (len=StrKIND), pointer :: config_calving_eigencalving_parameter_source + logical, pointer :: config_print_calving_info + real (kind=RKIND), pointer :: config_sea_level + real(kind=RKIND), pointer :: config_calving_thickness + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: eigencalvingParameter + real (kind=RKIND), dimension(:), pointer :: calvingThickness + real (kind=RKIND), dimension(:), pointer :: requiredCalvingVolumeRate + real (kind=RKIND), dimension(:), pointer :: calvingVelocity + real (kind=RKIND), dimension(:), pointer :: eMax, eMin + integer, dimension(:), pointer :: cellMask + type (field1dInteger), pointer :: calvingFrontMaskField + integer, dimension(:), pointer :: calvingFrontMask + real (kind=RKIND), pointer :: deltat !< time step (s) + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:,:), pointer :: edgesOnCell + real (kind=RKIND), dimension(:), pointer :: dvEdge + integer, pointer :: nCells + integer :: iCell, jCell, iNeighbor + real(kind=RKIND) :: cellCalvingFrontLength, cellCalvingFrontHeight + logical :: dynamicNeighbor + integer :: err_tmp + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_print_calving_info', config_print_calving_info) + call mpas_pool_get_config(liConfigs, 'config_calving_eigencalving_parameter_scalar_value', & + config_calving_eigencalving_parameter_scalar_value) + call mpas_pool_get_config(liConfigs, 'config_calving_eigencalving_parameter_source', config_calving_eigencalving_parameter_source) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_calving_thickness', config_calving_thickness) + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get fields + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'eigencalvingParameter', eigencalvingParameter) + call mpas_pool_get_array(geometryPool, 'requiredCalvingVolumeRate', requiredCalvingVolumeRate) + call mpas_pool_get_array(geometryPool, 'calvingVelocity', calvingVelocity) + call mpas_pool_get_array(velocityPool, 'eMax', eMax) + call mpas_pool_get_array(velocityPool, 'eMin', eMin) + + call mpas_pool_get_field(scratchPool, 'iceCellMask2', calvingFrontMaskField) + call mpas_allocate_scratch_field(calvingFrontMaskField, .true.) + calvingFrontMask => calvingFrontMaskField % array + + ! get parameter value + if (trim(config_calving_eigencalving_parameter_source) == 'scalar') then + eigencalvingParameter = config_calving_eigencalving_parameter_scalar_value + elseif (trim(config_calving_eigencalving_parameter_source) == 'data') then + ! do nothing - use value from input file + else + err = 1 + call mpas_log_write("Invalid value specified for option config_calving_eigencalving_parameter_source" // & + config_calving_eigencalving_parameter_source, MPAS_LOG_ERR) + endif + + if (config_print_calving_info) then + call mpas_log_write("eigencalvingParameter (m s) value range: Min=$r, Max=$r", & + realArgs=(/minval(eigencalvingParameter), maxval(eigencalvingParameter)/)) + endif + + ! make mask for effective calving front. + ! This is last dynamic cell, but also make sure it has a neighbor that is open ocean or thin floating ice. + call calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) + + calvingVelocity(:) = 0.0_RKIND + requiredCalvingVolumeRate(:) = 0.0_RKIND + ! First calculate the front retreat rate (Levermann eq. 1) + calvingVelocity(:) = eigencalvingParameter(:) * max(0.0_RKIND, eMax(:)) * max(0.0_RKIND, eMin(:)) & ! m/s + * real(li_mask_is_floating_ice_int(cellMask(:)), kind=RKIND) ! calculate only for floating ice - map of "potential" calving rate + do iCell = 1, nCells + if (calvingFrontMask(iCell) == 1) then + + ! convert to a volume flux per cell masking some assumptions + ! get front length from edge lengths abutting ocean or thin ice + ! get front height from max thickness of neighbors (since thickness near the edge could be screwy) + cellCalvingFrontLength = 0.0_RKIND + cellCalvingFrontHeight = 0.0_RKIND + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if ( (li_mask_is_floating_ice(cellMask(jCell)) .and. .not. li_mask_is_dynamic_ice(cellMask(jCell))) .or. & ! thin ice + (.not. li_mask_is_ice(cellMask(jCell)) .and. bedTopography(jCell) < config_sea_level) ) then ! open ocean + cellCalvingFrontLength = cellCalvingFrontLength + dvEdge(edgesOnCell(iNeighbor, iCell)) + endif + cellCalvingFrontHeight = max(cellCalvingFrontHeight, thickness(jCell)) + enddo + + requiredCalvingVolumeRate(iCell) = calvingVelocity(iCell) * cellCalvingFrontLength * cellCalvingFrontHeight ! m^3/s + endif + enddo + + call distribute_calving_flux(meshPool, geometryPool, scratchPool, calvingFrontMask, err_tmp) + err = ior(err, err_tmp) + + ! === apply calving === + thickness(:) = thickness(:) - calvingThickness(:) + + ! update mask + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + call calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) + + + ! Now also remove thin floating, dynamic ice (based on chosen thickness threshold) after mask is updated. + + !where ((li_mask_is_floating_ice(cellMask) .and. li_mask_is_dynamic_ice(cellMask) .and. thickness block % next + enddo + + end subroutine eigencalving + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine distribute_calving_flux +! +!> \brief Apply a specified calving flux along the calving front +!> \author Matthew Hoffman +!> \date Feb. 2018 +!> \details Applies a specified calving flux along the calving front. +!> The calving volume needs to be distributed in three ways: +!> 1. We need to first remove any "thin" ice in front of this cell +!> 2. Then we remove ice from this cell +!> 3. If there is still additional ice to be removed, we have to recursively +!> remove ice "inland" of this cell until the total required mass is removed +!----------------------------------------------------------------------- + subroutine distribute_calving_flux(meshPool, geometryPool, scratchPool, calvingFrontMask, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh pool + type (mpas_pool_type), pointer, intent(in) :: scratchPool !< Input: scratch pool + integer, dimension(:), intent(in) :: calvingFrontMask + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer, intent(inout) :: geometryPool !< Input: geometry pool + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- +! logical, pointer :: config_print_calving_info + real (kind=RKIND), dimension(:), pointer :: requiredCalvingVolumeRate !< Input: the specified calving flux at calving front margin cells + real (kind=RKIND), dimension(:), pointer :: calvingThickness !< Output: the applied calving rate as a thickness + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: areaCell + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), pointer :: deltat !< time step (s) + integer, pointer :: nCells + integer :: iCell, iNeighbor, jCell + real(kind=RKIND) :: volumeLeft + real(kind=RKIND) :: removeVolumeHere + type (field1dReal), pointer :: cellVolumeField + real(kind=RKIND), dimension(:), pointer :: cellVolume + real(kind=RKIND), dimension(:), pointer :: uncalvedVolume + integer :: inwardNeighbors + integer :: uncalvedCount + real(kind=RKIND) :: uncalvedTotal + + err = 0 + + ! get fields + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + call mpas_pool_get_array(geometryPool, 'requiredCalvingVolumeRate', requiredCalvingVolumeRate) + call mpas_pool_get_array(geometryPool, 'uncalvedVolume', uncalvedVolume) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + + call mpas_pool_get_field(scratchPool, 'workCell', cellVolumeField) + call mpas_allocate_scratch_field(cellVolumeField, .true.) + cellVolume => cellVolumeField % array + + calvingThickness(:) = 0.0_RKIND + uncalvedVolume(:) = 0.0_RKIND + + cellVolume(:) = areaCell(:) * thickness(:) + + ! The calving volume needs to be distributed in three ways: + ! 1. We need to first remove any "thin" ice in front of this cell + ! 2. Then we remove ice from this cell + ! 3. If there is still additional ice to be removed, we have to recursively + ! remove ice "inland" of this cell until the total required mass is removed + do iCell = 1, nCells + if (calvingFrontMask(iCell) == 1) then + volumeLeft = requiredCalvingVolumeRate(iCell) * deltat ! units m^3 + + ! First remove ice from "thin" neighbors + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell)) .and. .not. li_mask_is_dynamic_ice(cellMask(jCell))) then + ! this is a thin neighbor - remove as much ice from here as we can TODO: could distribute this evenly amongst neighbors + removeVolumeHere = min(volumeLeft, cellVolume(jCell)) ! how much we want to remove here + calvingThickness(jCell) = calvingThickness(jCell) + removeVolumeHere / areaCell(jCell) ! apply to the field that will be used, in thickness units + cellVolume(jCell) = cellVolume(jCell) - removeVolumeHere ! update accounting on cell volume + volumeLeft = volumeLeft - removeVolumeHere ! update accounting on how much left to distribute from current iCell + endif + enddo + + if (volumeLeft > 0.0_RKIND) then + ! Now remove ice from iCell + removeVolumeHere = min(volumeLeft, cellVolume(iCell)) + calvingThickness(iCell) = calvingThickness(iCell) + removeVolumeHere / areaCell(iCell) ! apply to the field that will be used in thickness units + cellVolume(iCell) = cellVolume(iCell) - removeVolumeHere ! update accounting on cell volume + volumeLeft = volumeLeft - removeVolumeHere ! update accounting on how much left to distribute from current iCell + endif + + if (volumeLeft > 0.0_RKIND) then + ! Now remove ice from neighbors inward on shelf + ! first count up how many there are + inwardNeighbors = 0 + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell)) .and. li_mask_is_dynamic_ice(cellMask(jCell)) & + .and. (.not. li_mask_is_dynamic_margin(jCell))) then + inwardNeighbors = inwardNeighbors + 1 + endif + enddo + ! Now distribute the flux evenly amongst the neighbors + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell)) .and. li_mask_is_dynamic_ice(cellMask(jCell)) & + .and. (.not. li_mask_is_dynamic_margin(jCell))) then + ! this is thick neighbor that is not itself a margin - remove as much ice from here as we can + removeVolumeHere = min(volumeLeft / real(inwardNeighbors, kind=RKIND), cellVolume(jCell)) ! how much we want to remove here + calvingThickness(jCell) = calvingThickness(jCell) + removeVolumeHere / areaCell(jCell) ! apply to the field that will be used in thickness units + cellVolume(jCell) = cellVolume(jCell) - removeVolumeHere ! update accounting on cell volume + volumeLeft = volumeLeft - removeVolumeHere ! update accounting on how much left to distribute from current iCell + endif + enddo + !TODO: need to recursively distribute across neighbors until fully depleted :( + endif + + ! If we didn't calve enough ice, record that to allow assessment of how bad that is. + if (volumeLeft > 0.0_RKIND) then + uncalvedVolume(iCell) = 0.0_RKIND + endif + + endif ! if cell is calving margin + enddo ! cell loop + + if (maxval(uncalvedVolume) > 0.0_RKIND) then + + uncalvedTotal = sum(uncalvedVolume) + uncalvedCount = count(uncalvedVolume > 0.0_RKIND) + + call mpas_log_write("distribute_calving_flux failed to distribute all required ice - ice was left after depleting all neighbors." & + // " Search needs to be expanded to neighbors' neighbors.") + call mpas_log_write(" On this processor: $i cells contain uncalved ice, for a total uncalved volume of $r m^3 ($r%).", & + MPAS_LOG_WARN, intArgs=(/uncalvedCount/), & + realArgs=(/uncalvedTotal, 100.0_RKIND * uncalvedTotal/(requiredCalvingVolumeRate(iCell) * deltat)/)) + endif + + call mpas_deallocate_scratch_field(cellVolumeField, .true.) + + end subroutine distribute_calving_flux + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine calculate_calving_front_mask +! +!> \brief Calculate mask indicating position of effective calving front +!> \author Matthew Hoffman +!> \date Feb. 2018 +!> \details Mmake mask for effective calving front. +!> This is last dynamic floating cell, but also make sure it has a neighbor that is open ocean or thin floating ice. +!----------------------------------------------------------------------- + subroutine calculate_calving_front_mask(meshPool, geometryPool, calvingFrontMask) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh pool + type (mpas_pool_type), intent(in) :: geometryPool !< Input: geometry pool + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, dimension(:) :: calvingFrontMask !< Output: calving front mask + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer, pointer :: nCells + integer :: iCell, iNeighbor, jCell, jNeighbor, kCell + logical :: oceanNeighbor + integer, dimension(:), pointer :: nEdgesOnCell ! number of cells that border each cell + integer, dimension(:,:), pointer :: cellsOnCell ! list of cells that neighbor each cell + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), pointer :: config_sea_level + + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + + ! get fields + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + + calvingFrontMask = 0 !initialize + + do iCell = 1, nCells + if ( (li_mask_is_floating_ice(cellMask(iCell))) .and. (li_mask_is_dynamic_margin(cellMask(iCell))) ) then + oceanNeighbor = .false. + do iNeighbor = 1, nEdgesOnCell(iCell) + jCell = cellsOnCell(iNeighbor, iCell) + if (li_mask_is_floating_ice(cellMask(jCell)) .and. .not. li_mask_is_dynamic_ice(cellMask(jCell))) then + ! make sure this neighbor is adjacent to open ocean (and not thin floating ice up against the coast) + do jNeighbor = 1, nEdgesOnCell(jCell) + kCell = cellsOnCell(jNeighbor, jCell) + if (.not. li_mask_is_ice(cellMask(kCell)) .and. bedTopography(kCell) < config_sea_level) then + oceanNeighbor = .true. ! iCell neighbors thin ice that in turn neighbors open ocean + endif + enddo + endif + if (.not. li_mask_is_ice(cellMask(jCell)) .and. bedTopography(jCell) < config_sea_level) then + oceanNeighbor = .true. ! this is an open ocean neighbor + endif + enddo + if (oceanNeighbor) then + calvingFrontMask(iCell) = 1 + endif + endif + enddo + + end subroutine calculate_calving_front_mask + + +end module li_calving + + diff --git a/src/core_landice/mpas_li_core.F b/src/core_landice/mode_forward/mpas_li_core.F similarity index 50% rename from src/core_landice/mpas_li_core.F rename to src/core_landice/mode_forward/mpas_li_core.F index a565eb08d4..f8ebbb81db 100644 --- a/src/core_landice/mpas_li_core.F +++ b/src/core_landice/mode_forward/mpas_li_core.F @@ -1,4 +1,4 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) ! and the University Corporation for Atmospheric Research (UCAR). ! ! Unless noted otherwise source code is licensed under the BSD license. @@ -11,10 +11,14 @@ module li_core use mpas_timekeeping use mpas_log + use li_analysis_driver + use li_velocity + use li_setup + use li_mask + implicit none private - type (MPAS_Clock_type), pointer :: clock !-------------------------------------------------------------------- ! @@ -25,7 +29,8 @@ module li_core public :: li_core_init, & li_core_run, & li_core_finalize, & - li_simulation_clock_init + li_simulation_clock_init, & + li_core_initial_solve !-------------------------------------------------------------------- ! @@ -55,17 +60,21 @@ module li_core !----------------------------------------------------------------------- function li_core_init(domain, startTimeStamp) result(err) - + use mpas_derived_types use mpas_pool_routines use mpas_stream_manager use li_velocity + use li_velocity_external + use li_thermal use li_setup + use li_constants + use li_subglacial_hydro !!! use mpas_tracer_advection !!! use li_global_diagnostics - + implicit none - + !----------------------------------------------------------------- ! ! input variables @@ -77,7 +86,7 @@ function li_core_init(domain, startTimeStamp) result(err) ! input/output variables ! !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/output: Domain + type (domain_type), intent(inout) :: domain !< Input/output: Domain !----------------------------------------------------------------- ! @@ -85,7 +94,7 @@ function li_core_init(domain, startTimeStamp) result(err) ! !----------------------------------------------------------------- character(len=*), intent(out) :: startTimeStamp !< Output: starting time stamp - + !----------------------------------------------------------------- ! ! local variables @@ -94,8 +103,15 @@ function li_core_init(domain, startTimeStamp) result(err) type (block_type), pointer :: block type (MPAS_Time_Type) :: startTime integer :: i, err, err_tmp, globalErr - logical, pointer :: config_do_restart + real (kind=RKIND), pointer :: deltat ! variable in each block + real (kind=RKIND) :: dtSeconds ! local variable + type (MPAS_Pool_type), pointer :: meshPool + type (MPAS_TimeInterval_type) :: timeStepInterval + character (len=StrKIND), pointer :: xtime, simulationStartTime + real (kind=RKIND), pointer :: daysSinceStart + integer, dimension(:), pointer :: cellProcID + type (MPAS_Time_type) :: xtime_timeType, simulationStartTime_timeType err = 0 @@ -103,28 +119,19 @@ function li_core_init(domain, startTimeStamp) result(err) globalErr = 0 call mpas_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) + ! ! Initialize config option settings as needed ! call li_setup_config_options( domain, err_tmp ) err = ior(err, err_tmp) - ! - ! Set "local" clock to point to the clock contained in the domain type - ! - clock => domain % clock - - ! - ! Set startTimeStamp based on the start time of the simulation clock - ! - startTime = mpas_get_clock_time(clock, MPAS_START_TIME, err_tmp) - call mpas_get_time(startTime, dateTimeString=startTimeStamp) - err = ior(err, err_tmp) - if (config_do_restart) then call mpas_stream_mgr_read(domain % streamManager, streamID='restart', ierr=err_tmp) + err = ior(err, err_tmp) else call mpas_stream_mgr_read(domain % streamManager, streamID='input', ierr=err_tmp) + err = ior(err, err_tmp) end if call MPAS_stream_mgr_reset_alarms(domain % streamManager, streamID='restart', ierr=err_tmp) err = ior(err, err_tmp) @@ -133,6 +140,65 @@ function li_core_init(domain, startTimeStamp) result(err) call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) err = ior(err, err_tmp) + ! + ! Read the remaining input streams + ! + call mpas_timer_start('io_read', .false.) + call mpas_stream_mgr_read(domain % streamManager, ierr=err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop('io_read') + call mpas_timer_start('reset_io_alarms', .false.) + call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_INPUT, ierr=err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop('reset_io_alarms') + + ! === + ! Initialize some time stuff on each block + ! === + ! Set startTimeStamp based on the start time of the simulation clock + startTime = mpas_get_clock_time(domain % clock, MPAS_START_TIME, err_tmp) + call mpas_get_time(startTime, dateTimeString=startTimeStamp) ! Get the start time as a time stamp + err = ior(err, err_tmp) + + timeStepInterval = mpas_get_clock_timestep(domain % clock, ierr=err_tmp) ! get timestep interval object + err = ior(err,err_tmp) + call mpas_get_timeInterval(timeStepInterval, StartTimeIn=startTime, dt=dtSeconds, ierr=err_tmp) ! Get config dt in seconds + err = ior(err,err_tmp) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + ! Assign initial time stamp + call mpas_pool_get_array(meshPool, 'xtime', xtime) + xtime = startTimeStamp + + ! Set simulationStartTime on a cold start only - it should have been read from restart otherwise + call mpas_pool_get_array(meshPool, 'simulationStartTime', simulationStartTime) + if (trim(simulationStartTime)=="no_date_available") then + if (config_do_restart) then + call mpas_log_write("simulationStartTime is undefined but this is a restart, " // & + "so setting its value to the current start time which may be incorrect.", MPAS_LOG_WARN) + endif + simulationStartTime = startTimeStamp + elseif ((trim(simulationStartTime)/="no_date_available") .and. (.not. config_do_restart)) then + call mpas_log_write("This is a cold start but simulationStartTime is already defined, " // & + "so its value may be incorrect.", MPAS_LOG_WARN) + endif + + ! compute time since start of simulation, in days + call mpas_pool_get_array(meshPool, 'daysSinceStart',daysSinceStart) + call mpas_set_time(xtime_timeType, dateTimeString=xtime) + call mpas_set_time(simulationStartTime_timeType, dateTimeString=simulationStartTime) + call mpas_get_timeInterval(xtime_timeType - simulationStartTime_timeType,dt=daysSinceStart) + daysSinceStart = daysSinceStart / seconds_per_day + + ! Initialize dt in seconds + call mpas_pool_get_array(meshPool, 'deltat', deltat) + deltat = dtSeconds + + block => block % next + end do ! === ! === Initialize modules === @@ -144,19 +210,50 @@ function li_core_init(domain, startTimeStamp) result(err) !!! call mpas_tracer_advection_init(err_tmp) ! Calling signature may be incorrect here. !!! err = ior(err,err_tmp) - ! === ! === Initialize blocks === ! === block => domain % blocklist do while (associated(block)) - call landice_init_block(block, startTimeStamp, domain % dminfo) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'cellProcID', cellProcID) + cellProcID(:) = domain % dminfo % my_proc_id + + call landice_init_block(block, domain % dminfo, err_tmp) + err = ior(err, err_tmp) block => block % next end do + ! initialize thermal solver + ! Note: This subroutine includes a loop over blocks + call li_thermal_init(domain, err_tmp) + err = ior(err, err_tmp) + + ! Initialize subglacial hydrology solver + call li_SGH_init(domain, err_tmp) + err = ior(err, err_tmp) + + ! initialize analysis driver + call li_analysis_init(domain, err_tmp) + err = ior(err, err_tmp) + + ! halo update for reconstruction coefficients + ! Note: Results on multiple processors may be incorrect without this update + + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'coeffs_reconstruct') + call mpas_timer_stop("halo updates") + + ! === + ! Perform albany mesh write if requested - Note: model terminates after this. + ! I think this has to come here so we 1. have the vertical coordinate initialized first, + ! which occurs in landice_init_block and 2. have li_velocity_init. + ! === + call li_velocity_external_write_albany_mesh(domain) ! check for errors and exit + call mpas_dmpar_max_int(domain % dminfo, err, globalErr) ! Find out if any blocks got an error if (globalErr > 0) then call mpas_log_write("An error has occurred in li_core_init. Aborting...", MPAS_LOG_CRIT) @@ -180,17 +277,21 @@ end function li_core_init !----------------------------------------------------------------------- function li_core_run(domain) result(err) - + use mpas_derived_types use mpas_pool_routines use mpas_kind_types use mpas_stream_manager use mpas_timer + use mpas_io_streams, only: MPAS_STREAM_LATEST_BEFORE + use mpas_io_units use li_diagnostic_vars use li_setup + use li_statistics + use li_time_integration implicit none - + !----------------------------------------------------------------- ! ! input variables @@ -202,7 +303,7 @@ function li_core_run(domain) result(err) ! input/output variables ! !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/output: Domain + type (domain_type), intent(inout) :: domain !< Input/output: Domain !----------------------------------------------------------------- ! @@ -215,18 +316,19 @@ function li_core_run(domain) result(err) ! local variables ! !----------------------------------------------------------------- - integer :: itimestep + integer, pointer :: timestepNumber type (block_type), pointer :: block - type (mpas_pool_type), pointer :: statePool - logical, pointer :: config_do_restart, config_write_output_on_startup + type (mpas_pool_type), pointer :: geometryPool, meshPool + integer, pointer :: config_stats_interval !< interval (number of timesteps) for writing stats character(len=StrKIND), pointer :: config_restart_timestamp_name - + character(len=StrKIND), pointer :: config_velocity_solver + ! Variables needed for printing timestamps type (MPAS_Time_Type) :: currTime character(len=StrKIND) :: timeStamp - integer :: err, err_tmp, globalErr - logical :: solveVelo - type (MPAS_TimeInterval_type) :: timeStepInterval !< time step as an interval - real (kind=RKIND) :: dtSeconds !< time step in seconds + logical :: isRinging + integer :: restartTimestampUnit + + integer :: err, err_tmp, err_tmp2, globalErr err = 0 @@ -234,104 +336,63 @@ function li_core_run(domain) result(err) globalErr = 0 ! Get Pool stuff that will be needed - call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) - call mpas_pool_get_config(liConfigs, 'config_write_output_on_startup', config_write_output_on_startup) call mpas_pool_get_config(liConfigs, 'config_restart_timestamp_name', config_restart_timestamp_name) + call mpas_pool_get_config(liConfigs, 'config_stats_interval', config_stats_interval) + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) call mpas_timer_start("land ice core run") - currTime = mpas_get_clock_time(clock, MPAS_NOW, err_tmp) - err = ior(err, err_tmp) - call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=err_tmp) - err = ior(err, err_tmp) - call mpas_log_write('Initial timestep ' // trim(timeStamp)) - - - ! === - ! === Calculate Initial state - ! === - call mpas_timer_start("initial state calculation") - - ! On a restart, we already have the exact velocity field we need, - ! so don't do the expensive calculation again. - if (config_do_restart) then - solveVelo = .false. - else - ! Otherwise, we need to calculate velocity for the initial state - ! (Note: even if the velocity is supplied, we should still calculate it - ! to ensure it is consistent with the current geometry/B.C. If the - ! velocity solver is iterative, the supplied field will be used as an - ! initial guess, so the solution should be quick. - solveVelo = .true. - endif - - call li_calculate_diagnostic_vars(domain, timeLevel=1, solveVelo=solveVelo, err=err_tmp) - err = ior(err, err_tmp) - - call mpas_timer_stop("initial state calculation") + ! initialize time step number. initial time is 0 + block => domain % blocklist + do while(associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'timestepNumber', timestepNumber) + timestepNumber = 0 + block => block % next + end do ! === - ! === Write Initial Output + ! Solve initial state before beginning time stepping ! === - call mpas_timer_start("write output") - if (config_write_output_on_startup) then - call mpas_stream_mgr_write(domain % streamManager, 'output', forceWriteNow=.true., ierr=err_tmp) - endif - call mpas_timer_stop("write output") - - ! === error check and exit - call mpas_dmpar_max_int(domain % dminfo, err, globalErr) ! Find out if any blocks got an error - if (globalErr > 0) then - call mpas_log_write("An error has occurred in li_core_run before time-stepping. Aborting...", MPAS_LOG_CRIT) - endif - - + err_tmp = li_core_initial_solve(domain) + err = ior(err,err_tmp) ! li_core_initial_solve would abort if there was an error, but being safe. ! During integration, time level 1 stores the model state at the beginning of the ! time step, and time level 2 stores the state advanced dt in time by timestep(...) - itimestep = 0 ! === ! === Time step loop ! === - do while (.not. mpas_is_clock_stop_time(clock)) - - itimestep = itimestep + 1 + do while (.not. mpas_is_clock_stop_time(domain % clock)) - ! Get the interval at this point in time - currently this does not change during the simulation, but re-calculating it explicitly for generality - timeStepInterval = mpas_get_clock_timestep(clock, ierr=err_tmp) - err = ior(err,err_tmp) - ! Convert the clock's time interval into a dt in seconds to be used by the time stepper, using the currTime as the start time for this interval. - ! (We want to do this conversion before advancing the clock because the dt in seconds may change - ! as the base time changes, and we want the old time as the base time. - ! For example, the number of seconds in a year will be longer in a leap year.) - call mpas_get_timeInterval(timeStepInterval, StartTimeIn=currTime, dt=dtSeconds, ierr=err_tmp) - err = ior(err,err_tmp) - - call mpas_advance_clock(clock) - - currTime = mpas_get_clock_time(clock, MPAS_NOW, err_tmp) - call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=err_tmp) - err = ior(err, err_tmp) - call mpas_log_write('Doing timestep ' // trim(timeStamp)) + ! Update time step number + block => domain % blocklist + do while(associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'timestepNumber', timestepNumber) + timestepNumber = timestepNumber + 1 + block => block % next + end do + call mpas_log_write('Starting timestep number $i', intArgs=(/timestepNumber/), flushNow=.true.) ! === ! === Perform Timestep ! === call mpas_timer_start("time integration") - call landice_timestep(domain, itimestep, dtSeconds, timeStamp, err_tmp) + call li_timestep(domain, err_tmp) err = ior(err,err_tmp) - ! Move time level 2 fields back into time level 1 for next time step - block => domain % blocklist - do while(associated(block)) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_shift_time_levels(statePool) - block => block % next - end do - call mpas_timer_stop("time integration") + ! Write statistics at designated interval + if (config_stats_interval > 0) then + if (mod(timestepNumber, config_stats_interval) == 0) then + call mpas_timer_start("compute_statistics") + call li_compute_statistics(domain, timestepNumber) + call mpas_timer_stop("compute_statistics") + end if + end if + call mpas_timer_stop("time integration") ! === ! === Read time-varying inputs, if present (i.e., forcing) @@ -348,28 +409,98 @@ function li_core_run(domain) result(err) call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_INPUT, ierr=err_tmp) err = ior(err, err_tmp) + ! call analysis driver compute, restart, write subroutines + ! (note: alarms and timers are handled by the analysis member code) + call li_analysis_compute(domain, err_tmp) + err = ior(err, err_tmp) + call li_analysis_restart(domain, err_tmp) + err = ior(err, err_tmp) + call li_analysis_write(domain, err_tmp) + err = ior(err, err_tmp) ! === ! === Write Output and/or Restart, if needed ! === - call mpas_timer_start("write output") - ! Update the restart_timestamp file with the new time, if needed. - if ( mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID='restart', direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) ) then - open(22, file=config_restart_timestamp_name, form='formatted', status='replace') - write(22, *) timeStamp - close(22) - end if - err = ior(err, err_tmp) - ! These calls will handle ALL output streams that need writing. - ! [Could add them individually, as the ocean does, if some other actions are needed when a - ! specific alarm is ringing (e.g., global stats calculated only when output stream gets written)] + + call mpas_timer_start("write streams") call mpas_stream_mgr_write(domain % streamManager, ierr=err_tmp) + if (err_tmp > 0) then + call mpas_log_write('Error writing output or restart streams.', MPAS_LOG_ERR) + endif err = ior(err, err_tmp) - call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) + ! Update the restart_timestamp file with the new time, if needed. + if ( domain % dminfo % my_proc_id == 0 ) then ! Only have 1 task write this file. + ! Do this only after writing has completed successfully. + ! (Otherwise we would be setting the restart_timestamp to an invalid time level.) + ! (This ignores the situation where restart wrote successfully but one or more other output streams did not.) + if (err_tmp == 0) then + isRinging = mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID='restart', & + direction=MPAS_STREAM_OUTPUT, ierr=err_tmp2) + if (err_tmp2 > 0) then + call mpas_log_write('Error checking restart stream alarm.', MPAS_LOG_ERR) + endif + err = ior(err, err_tmp2) + + if (isRinging) then + ! Need updated timestamp for writing restart timestamp + currTime = mpas_get_clock_time(domain % clock, MPAS_NOW, err_tmp2) + if (err_tmp2 > 0) then + call mpas_log_write('Error in mpas_get_clock_time when writing restart timestamp.', MPAS_LOG_ERR) + endif + err = ior(err, err_tmp2) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=err_tmp2) + if (err_tmp2 > 0) then + call mpas_log_write('Error in mpas_get_time when writing restart timestamp.', MPAS_LOG_ERR) + endif + err = ior(err, err_tmp2) + + ! write the timestamp to file + call mpas_new_unit(restartTimestampUnit) + open(restartTimestampUnit, file=config_restart_timestamp_name, form='formatted', status='replace', iostat=err_tmp2) + if (err_tmp2 > 0) then + call mpas_log_write('Error opening file to write restart timestamp:' // & + trim(config_restart_timestamp_name), MPAS_LOG_ERR) + endif + err = ior(err, err_tmp2) + write(restartTimestampUnit, *, iostat=err_tmp2) timeStamp + if (err_tmp2 > 0) then + call mpas_log_write('Error writing restart timestamp to file ' // & + trim(config_restart_timestamp_name), MPAS_LOG_ERR) + endif + err = ior(err, err_tmp2) + close(restartTimestampUnit) + call mpas_release_unit(restartTimestampUnit) + end if ! isRinging + end if ! err_tmp==0 + end if ! master task + ! Now that we are done here, reset output alarms (can be called whether or not it was ringing) + call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) + if (err_tmp > 0) then + call mpas_log_write('Error resetting output-direction stream alarms.', MPAS_LOG_ERR) + endif err = ior(err, err_tmp) - call mpas_timer_stop("write output") + call mpas_timer_stop("write streams") + + ! === (end of output section) === + ! Move time level 1 fields (current values) into time level 2 (old values) for next time step + ! (for those fields with multiple time levels) + block => domain % blocklist + do while(associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_shift_time_levels(geometryPool) + block => block % next + end do + + ! Reset the alarm for checking for force setting of the adaptive timestep interval + if (mpas_is_alarm_ringing(domain % clock, 'adaptiveTimestepForceInterval', ierr=err_tmp)) then + err = ior(err, err_tmp) + call mpas_reset_clock_alarm(domain % clock, 'adaptiveTimestepForceInterval', ierr=err_tmp) + err = ior(err, err_tmp) + endif + err = ior(err, err_tmp) + ! === error check and exit call mpas_dmpar_max_int(domain % dminfo, err, globalErr) ! Find out if any blocks got an error if (globalErr > 0) then @@ -396,11 +527,12 @@ end function li_core_run ! !----------------------------------------------------------------------- function li_core_finalize(domain) result(err) - + use mpas_derived_types use mpas_decomp use li_velocity, only: li_velocity_finalize - + use li_subglacial_hydro, only: li_SGH_finalize + implicit none !----------------------------------------------------------------- @@ -414,7 +546,7 @@ function li_core_finalize(domain) result(err) ! input/output variables ! !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/output: Domain + type (domain_type), intent(inout) :: domain !< Input/output: Domain !----------------------------------------------------------------- ! @@ -428,74 +560,86 @@ function li_core_finalize(domain) result(err) ! !----------------------------------------------------------------- integer :: err, err_tmp, globalErr - + + call mpas_timer_start("land ice finalize") + err = 0 err_tmp = 0 globalErr = 0 + ! velocity finalize call li_velocity_finalize(domain, err_tmp) err = ior(err, err_tmp) - call mpas_destroy_clock(clock, err_tmp) + ! subglacial hydro finalize + call li_SGH_finalize(domain, err_tmp) + err = ior(err, err_tmp) + + ! call finalize subroutine in analysis driver + call li_analysis_finalize(domain, err_tmp) + err = ior(err, err_tmp) + + call mpas_destroy_clock(domain % clock, err_tmp) err = ior(err, err_tmp) call mpas_decomp_destroy_decomp_list(domain % decompositions) + err = ior(err, err_tmp) + ! === error check and exit call mpas_dmpar_max_int(domain % dminfo, err, globalErr) ! Find out if any blocks got an error if (globalErr > 0) then call mpas_log_write("An error has occurred in li_core_finalize. Aborting...", MPAS_LOG_CRIT) endif - !-------------------------------------------------------------------- - end function li_core_finalize - + call mpas_timer_stop("land ice finalize") -!*********************************************************************** -!*********************************************************************** -! Private subroutines: -!*********************************************************************** -!*********************************************************************** + end function li_core_finalize + !-------------------------------------------------------------------- !*********************************************************************** ! -! routine landice_init_block +! function li_core_initial_solve ! -!> \brief Initializes blocks for the land ice core +!> \brief Performs the initial diagnostic solve for the LI core !> \author Matt Hoffman -!> \date 11 September 2013 +!> \date 23 September 2015 !> \details -!> This routine initializes blocks for the land ice core. +!> This routine performs the initial diagnostic solve for the LI core. +!> Rather than inlining these calculations, there are done in this +!> routine to keep them modular. +!> This has been made public so it can be called from an ESM. ! !----------------------------------------------------------------------- - subroutine landice_init_block(block, startTimeStamp, dminfo) + function li_core_initial_solve(domain) result(err) use mpas_derived_types use mpas_pool_routines - use mpas_rbf_interpolation - use mpas_vector_reconstruction + use mpas_kind_types + use mpas_stream_manager + use mpas_timer + use li_diagnostic_vars use li_setup - use li_mask - use li_velocity - + use li_statistics + use li_advection + use mpas_io_streams, only: MPAS_STREAM_LATEST_BEFORE + implicit none - + !----------------------------------------------------------------- ! ! input variables ! !----------------------------------------------------------------- - type (dm_info), intent(in) :: dminfo !< Input: Domain info - character(len=*), intent(in) :: startTimeStamp !< Input: time stamp at start !----------------------------------------------------------------- ! ! input/output variables ! !----------------------------------------------------------------- - type (block_type), intent(inout) :: block !< Input/output: Block object + type (domain_type), intent(inout) :: domain !< Input/output: Domain !----------------------------------------------------------------- ! @@ -508,174 +652,291 @@ subroutine landice_init_block(block, startTimeStamp, dminfo) ! local variables ! !----------------------------------------------------------------- - type (mpas_pool_type), pointer :: meshPool - type (mpas_pool_type), pointer :: statePool - character (len=StrKIND), pointer :: xtime + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool, meshPool, velocityPool + logical, pointer :: config_do_restart, config_write_output_on_startup, config_write_stats_on_startup + character(len=StrKIND), pointer :: config_velocity_solver + ! Variables needed for printing timestamps type (MPAS_Time_Type) :: currTime - integer :: err, err_tmp - integer :: iCell, iLevel, i + character(len=StrKIND) :: timeStamp + + integer :: err, err_tmp, globalErr + logical :: solveVelo + + integer, dimension(:), pointer :: vertexMask err = 0 err_tmp = 0 + globalErr = 0 + + ! Get Pool stuff that will be needed + call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) + call mpas_pool_get_config(liConfigs, 'config_write_output_on_startup', config_write_output_on_startup) + call mpas_pool_get_config(liConfigs, 'config_write_stats_on_startup', config_write_stats_on_startup) + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + + currTime = mpas_get_clock_time(domain % clock, MPAS_NOW, err_tmp) + err = ior(err, err_tmp) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=err_tmp) + err = ior(err, err_tmp) + call mpas_log_write('Initial timestep ' // trim(timeStamp)) - ! Get pool stuff - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) ! === - ! === Call init routines === + ! === Calculate Initial state ! === - call li_setup_vertical_grid(meshPool, err_tmp) - err = ior(err, err_tmp) + call mpas_timer_start("initial state calculation") - call li_setup_sign_and_index_fields(meshPool) + ! On a restart, we already have the exact velocity field we need, + ! so don't do the expensive calculation again. + if (config_do_restart) then + solveVelo = .false. + else + ! Otherwise, we need to calculate velocity for the initial state + ! (Note: even if the velocity is supplied, we should still calculate it + ! to ensure it is consistent with the current geometry/B.C. If the + ! velocity solver is iterative, the supplied field will be used as an + ! initial guess, so the solution should be quick. + solveVelo = .true. + endif + + ! Update mask and geometry + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + call li_update_geometry(geometryPool) -! This was needed to init FCT once. -!!! ! Init for FCT tracer advection -!!! mesh % maxLevelCell % array = mesh % nVertLevels ! Needed for FCT tracer advection -!!! mesh % maxLevelEdgeTop % array = mesh % nVertLevels ! Needed for FCT tracer advection -!!! mesh % maxLevelEdgeBot % array = mesh % nVertLevels ! Needed for FCT tracer advection -!!! call ocn_initialize_advection_rk(mesh, err) -!!! call mpas_ocn_tracer_advection_coefficients(mesh, err_tmp) -!!! err = ior(err, err_tmp) + block => block % next + end do - call mpas_timer_start("initialize velocity") - call li_velocity_block_init(block, err_tmp) + ! Initial velocity solve + call li_velocity_solve(domain, solveVelo=solveVelo, err=err_tmp) err = ior(err, err_tmp) - call mpas_timer_stop("initialize velocity") - ! Init for reconstruction of velocity - call mpas_rbf_interp_initialize(meshPool) - call mpas_init_reconstruct(meshPool) + ! Calculate diagnostic vars + call li_calculate_diagnostic_vars(domain, err=err_tmp) + err = ior(err, err_tmp) - ! Assign initial time stamp - call mpas_pool_get_array(statePool, 'xtime', xtime, timeLevel=1) - xtime = startTimeStamp + call mpas_timer_stop("initial state calculation") - ! Mask init identifies initial ice extent - call li_calculate_mask_init(meshPool, statePool, timeLevel=1, err=err_tmp) - err = ior(err, err_tmp) + if (config_write_stats_on_startup) then + call mpas_timer_start("compute_statistics") + call li_compute_statistics(domain, 0) ! itimestep = 0 + call mpas_timer_stop("compute_statistics") + endif - ! Make sure all time levels have a copy of the initial state - call mpas_pool_initialize_time_levels(statePool) + ! compute analysis members on startup if option activated (checked inside routine) + ! If we are doing a restart, any required output from this time level should already be written, so don't recalculate/rewrite + ! Note: for debugging inexact restarts, it can be useful to enable this write on a restart + if (.not. config_do_restart) then + call mpas_timer_start("analysis member startup calculations") + call li_analysis_compute_startup(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("analysis member startup calculations") + endif - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in init_block.", MPAS_LOG_ERR) + ! === + ! === Write Initial Output + ! === + + if (config_write_output_on_startup) then + ! If we are doing a restart, any required output from this time level should already be written, so don't rewrite + ! Note: for debugging inexact restarts, it can be useful to enable this write on a restart + if (.not. config_do_restart) then + call mpas_timer_start("write output") + call mpas_stream_mgr_write(domain % streamManager, 'output', forceWriteNow=.true., ierr=err_tmp) + call mpas_timer_stop("write output") + endif endif + + ! Move time level 1 fields (current values) into time level 2 (old values) for next time step + ! (for those fields with multiple time levels) + block => domain % blocklist + do while(associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_shift_time_levels(geometryPool) + block => block % next + end do + + if (config_do_restart .and. (trim(config_velocity_solver) /= 'sia')) then + ! On a restart with the HO dycore, we need to make sure the FEM mesh will be rebuilt + ! on the first time step. Force this by setting the vertexMask at the end of the + ! initial time to 0. (Do this after writing output.) + block => domain % blocklist + do while(associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel=2) ! Get the old vertexMask + vertexMask = 0 + block => block % next + end do + endif + + ! === error check and exit + call mpas_dmpar_max_int(domain % dminfo, err, globalErr) ! Find out if any blocks got an error + if (globalErr > 0) then + call mpas_log_write("An error has occurred in li_core_initial_solve. Aborting...", MPAS_LOG_CRIT) + endif + + + end function li_core_initial_solve !-------------------------------------------------------------------- - end subroutine landice_init_block + + + +!*********************************************************************** +!*********************************************************************** +! Private subroutines: +!*********************************************************************** +!*********************************************************************** !*********************************************************************** ! -! routine landice_timestep +! routine landice_init_block ! -!> \brief Performs a time step +!> \brief Initializes blocks for the land ice core !> \author Matt Hoffman !> \date 11 September 2013 !> \details -!> This routine performs a time step for the land ice core. +!> This routine initializes blocks for the land ice core. ! !----------------------------------------------------------------------- - subroutine landice_timestep(domain, itimestep, dt, timeStamp, err) - + subroutine landice_init_block(block, dminfo, err) + use mpas_derived_types - use li_time_integration - use mpas_timer -!!! use li_global_diagnostics - + use mpas_pool_routines + use mpas_rbf_interpolation + use mpas_vector_reconstruction + use li_setup + use li_mask + use li_velocity + implicit none - + !----------------------------------------------------------------- ! ! input variables ! !----------------------------------------------------------------- - integer, intent(in) :: itimestep !< Input: time step number - real (kind=RKIND), intent(in) :: dt !< Input: time step, in seconds - character(len=*), intent(in) :: timeStamp !< Input: time stamp of current time step + type (dm_info), intent(in) :: dminfo !< Input: Domain info !----------------------------------------------------------------- ! ! input/output variables ! !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/output: Domain + type (block_type), intent(inout) :: block !< Input/output: Block object !----------------------------------------------------------------- ! ! output variables ! !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag + integer, intent(out) :: err !< error flag !----------------------------------------------------------------- ! ! local variables ! !----------------------------------------------------------------- - type (block_type), pointer :: block_ptr + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + integer, dimension(:), pointer :: vertexMask + real (kind=RKIND), dimension(:), pointer :: thickness, thicknessOld + character (len=StrKIND), pointer :: config_velocity_solver + logical, pointer :: config_do_velocity_reconstruction_for_external_dycore + logical, pointer :: config_adaptive_timestep_include_DCFL + logical, pointer :: config_SGH integer :: err_tmp - err = 0 err_tmp = 0 + ! Get pool stuff + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + call mpas_pool_get_config(liConfigs, 'config_do_velocity_reconstruction_for_external_dycore', & + config_do_velocity_reconstruction_for_external_dycore) + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_include_DCFL', config_adaptive_timestep_include_DCFL) + call mpas_pool_get_config(liConfigs, 'config_SGH', config_SGH) + + ! Copy data from first time level into all other time levels + call mpas_pool_initialize_time_levels(geometryPool) - call li_timestep(domain, dt, timeStamp, err_tmp) - err = ior(err,err_tmp) - -!!! if (config_stats_interval .gt. 0) then -!!! if(mod(itimestep, config_stats_interval) == 0) then -!!! block_ptr => domain % blocklist -!!! if(associated(block_ptr % next)) then -!!! write(0,*) 'Error: computeGlobalDiagnostics assumes ',& -!!! 'that there is only one block per processor.' -!!! end if -!!! -!!! call mpas_timer_start("global_diagnostics") -!!! call li_compute_global_diagnostics(domain % dminfo, & -!!! block_ptr % state % time_levs(2) % state, block_ptr % mesh, & -!!! itimestep, dt) -!!! call mpas_timer_stop("global_diagnostics") -!!! end if -!!! end if - - !TODO: replace the above code block with this if we desire to convert config_stats_interval to use alarms - !if (mpas_is_alarm_ringing(clock, statsAlarmID, ierr=ierr)) then - ! call mpas_reset_clock_alarm(clock, statsAlarmID, ierr=ierr) - - ! block_ptr => domain % blocklist - ! if(associated(block_ptr % next)) then - ! write(0,*) 'Error: computeGlobalDiagnostics assumes ',& - ! 'that there is only one block per processor.' - ! end if - - ! call mpas_timer_start("global_diagnostics") - ! call sw_compute_global_diagnostics(domain % dminfo, & - ! block_ptr % state % time_levs(2) % state, block_ptr % mesh, & - ! timeStamp, dt) - ! call mpas_timer_stop("global_diagnostics") - !end if + ! Initialize vertexMask on time level 2 to 0, so diagnostic_solve_before_velocity in li_diagnostic_vars + ! says that the vertexMask has changed (needed by external dycore) + call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel = 2) + vertexMask = 0 + + ! === + ! === Call init routines === + ! === + call li_setup_vertical_grid(meshPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + call li_setup_sign_and_index_fields(meshPool) + +! This was needed to init FCT once. +!!! ! Init for FCT tracer advection +!!! mesh % maxLevelCell % array = mesh % nVertLevels ! Needed for FCT tracer advection +!!! mesh % maxLevelEdgeTop % array = mesh % nVertLevels ! Needed for FCT tracer advection +!!! mesh % maxLevelEdgeBot % array = mesh % nVertLevels ! Needed for FCT tracer advection +!!! call ocn_initialize_advection_rk(mesh, err) +!!! call mpas_ocn_tracer_advection_coefficients(mesh, err_tmp) +!!! err = ior(err, err_tmp) + + ! Init for reconstruction of velocity + ! Note: mpas_init_reconstruct fails with the MISMIP3d periodic mesh + ! because x_period and y_period are not set properly. + ! That should be fixed for that test, but a workaround is to + ! disable these two calls for those runs. + call mpas_rbf_interp_initialize(meshPool) + call mpas_init_reconstruct(meshPool) + + ! Initialize velocity solver + !WHL - This is now after the call to mpas_init_reconstruct, so that the reconstruction coefficients are available. + call mpas_timer_start("initialize velocity") + call li_velocity_block_init(block, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("initialize velocity") + + ! Higher-order velo solvers needs vertex-to-cell interp routine initialized + if ( (trim(config_velocity_solver) == 'L1L2') .or. & + (trim(config_velocity_solver) == 'FO') .or. & + (trim(config_velocity_solver) == 'Stokes') ) then + call li_setup_wachspress_vertex_to_cell_weights(meshPool) + endif + + ! Mask init identifies initial ice extent + call li_calculate_mask_init(geometryPool, err=err_tmp) + err = ior(err, err_tmp) + + ! Initialize thicknessOld variable used to calculate dHdt + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'thicknessOld', thicknessOld) + thicknessOld = thickness ! === error check if (err > 0) then - call mpas_log_write("An error has occurred in mpas_timestep.", MPAS_LOG_ERR) + call mpas_log_write("An error has occurred in landice_init_block.", MPAS_LOG_ERR) endif - - end subroutine landice_timestep - - + !-------------------------------------------------------------------- + end subroutine landice_init_block + + !*********************************************************************** ! -! routine li_simulation_clock_init +! routine simulation_clock_init ! !> \brief Initializes the simulation clock !> \author ?? @@ -697,7 +958,7 @@ subroutine li_simulation_clock_init(core_clock, configs, ierr) ! input variables ! !----------------------------------------------------------------- - + !----------------------------------------------------------------- ! @@ -721,23 +982,30 @@ subroutine li_simulation_clock_init(core_clock, configs, ierr) !----------------------------------------------------------------- type (MPAS_Time_Type) :: startTime, stopTime, alarmStartTime type (MPAS_TimeInterval_type) :: runDuration, timeStep, alarmTimeStep - character (len=StrKIND), pointer :: config_start_time, config_run_duration, config_stop_time, config_output_interval, config_restart_interval ! MPAS standard configs + type (MPAS_TimeInterval_type) :: adaptDtForceInterval + character (len=StrKIND), pointer :: config_start_time, config_run_duration, config_stop_time, & + config_output_interval, config_restart_interval ! MPAS standard configs character (len=StrKIND), pointer :: config_dt ! MPAS LI-specific config option + character (len=StrKIND), pointer :: config_adaptive_timestep_force_interval ! MPAS LI-specific config option character (len=StrKIND), pointer :: config_restart_timestamp_name character (len=StrKIND) :: restartTimeStamp !< string to be read from file + integer, pointer :: config_year_digits integer :: err_tmp - ierr = 0 err_tmp = 0 + ! Adjust number of digits representing the year + call mpas_pool_get_config(configs, 'config_year_digits', config_year_digits) + call mpas_timekeeping_set_year_width(config_year_digits) + + ! Get configs needed for rest of routine call mpas_pool_get_config(configs, 'config_dt', config_dt) call mpas_pool_get_config(configs, 'config_start_time', config_start_time) call mpas_pool_get_config(configs, 'config_run_duration', config_run_duration) call mpas_pool_get_config(configs, 'config_stop_time', config_stop_time) - call mpas_pool_get_config(configs, 'config_output_interval', config_output_interval) - call mpas_pool_get_config(configs, 'config_restart_interval', config_restart_interval) call mpas_pool_get_config(configs, 'config_restart_timestamp_name', config_restart_timestamp_name) + call mpas_pool_get_config(configs, 'config_adaptive_timestep_force_interval', config_adaptive_timestep_force_interval) ! Set time to the user-specified start time OR use a restart time from file @@ -756,7 +1024,8 @@ subroutine li_simulation_clock_init(core_clock, configs, ierr) call mpas_set_timeInterval(timeStep, timeString=config_dt, ierr=err_tmp) ierr = ior(ierr,err_tmp) - + ! Setup start/stop/duration times + ! config_run_duration takes precedence over config_stop_time if (trim(config_run_duration) /= "none") then call mpas_set_timeInterval(runDuration, timeString=config_run_duration, ierr=err_tmp) ierr = ior(ierr,err_tmp) @@ -773,6 +1042,10 @@ subroutine li_simulation_clock_init(core_clock, configs, ierr) else if (trim(config_stop_time) /= "none") then call mpas_set_time(curr_time=stopTime, dateTimeString=config_stop_time, ierr=err_tmp) ierr = ior(ierr,err_tmp) + if (stopTime .lt. startTime) then + call mpas_log_write('config_stop_time is earlier than config_start_time!', MPAS_LOG_ERR) + ierr = 1 + endif call mpas_create_clock(core_clock, startTime=startTime, timeStep=timeStep, stopTime=stopTime, ierr=err_tmp) ierr = ior(ierr,err_tmp) else @@ -780,6 +1053,20 @@ subroutine li_simulation_clock_init(core_clock, configs, ierr) ierr = 1 end if + ! Set up the adaptiveTimestepForceInterval alarm. + ! This is only needed if the adaptive time stepper is being used, but can be set up regardless. + call mpas_set_timeInterval(adaptDtForceInterval, timeString=config_adaptive_timestep_force_interval, ierr=err_tmp) + ierr = ior(ierr,err_tmp) + call mpas_add_clock_alarm(core_clock, 'adaptiveTimestepForceInterval', alarmTime=startTime, & + alarmTimeInterval=adaptDtForceInterval, ierr=err_tmp) + ierr = ior(ierr,err_tmp) + ! Reset the alarm for checking for force setting of the adaptive timestep interval + if (mpas_is_alarm_ringing(core_clock, 'adaptiveTimestepForceInterval', ierr=err_tmp)) then + ierr = ior(ierr, err_tmp) + call mpas_reset_clock_alarm(core_clock, 'adaptiveTimestepForceInterval', ierr=err_tmp) + ierr = ior(ierr, err_tmp) + endif + ierr = ior(ierr, err_tmp) ! === error check if (ierr > 0) then diff --git a/src/core_landice/mpas_li_core_interface.F b/src/core_landice/mode_forward/mpas_li_core_interface.F similarity index 75% rename from src/core_landice/mpas_li_core_interface.F rename to src/core_landice/mode_forward/mpas_li_core_interface.F index 1bdaa86723..665be262f9 100644 --- a/src/core_landice/mpas_li_core_interface.F +++ b/src/core_landice/mode_forward/mpas_li_core_interface.F @@ -1,4 +1,4 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) ! and the University Corporation for Atmospheric Research (UCAR). ! ! Unless noted otherwise source code is licensed under the BSD license. @@ -10,11 +10,12 @@ module li_core_interface use mpas_derived_types use mpas_pool_routines use mpas_dmpar - use mpas_constants use mpas_attlist - use mpas_io_units use mpas_log + use li_core + use li_analysis_driver + use li_constants public @@ -26,7 +27,7 @@ module li_core_interface !> \brief Land ice core setup routine !> \author Doug Jacobsen !> \date 03/18/2015 - !> \details + !> \details !> This routine is intended to setup the necessary variables within a core_type !> for the land ice core. ! @@ -51,7 +52,7 @@ subroutine li_setup_core(core)!{{{ core % Conventions = 'MPAS' core % source = 'MPAS' -#include "inc/core_variables.inc" +#include "../inc/core_variables.inc" end subroutine li_setup_core!}}} @@ -63,7 +64,7 @@ end subroutine li_setup_core!}}} !> \brief Land ice domain setup routine !> \author Doug Jacobsen !> \date 03/18/2015 - !> \details + !> \details !> This routine is intended to setup the necessary variables within a domain_type !> for the land ice core. ! @@ -71,7 +72,7 @@ end subroutine li_setup_core!}}} subroutine li_setup_domain(domain)!{{{ type (domain_type), pointer :: domain -#include "inc/domain_variables.inc" +#include "../inc/domain_variables.inc" end subroutine li_setup_domain!}}} @@ -82,7 +83,7 @@ end subroutine li_setup_domain!}}} !> \brief Pacakge setup function !> \author Doug Jacobsen !> \date September 2011 -!> \details +!> \details !> This function is intended to correctly configure the packages for this MPAS !> core. It can use any Fortran logic to properly configure packages, and it !> can also make use of any namelist options. All variables in the model are @@ -97,8 +98,53 @@ function li_setup_packages(configPool, packagePool, iocontext) result(ierr) type (mpas_io_context_type), intent(inout) :: iocontext integer :: ierr + ! Local variables + character (len=StrKIND), pointer :: config_velocity_solver + logical, pointer :: config_SGH + logical, pointer :: config_adaptive_timestep_include_DCFL + logical, pointer :: config_write_albany_ascii_mesh + + logical, pointer :: higherOrderVelocityActive + logical, pointer :: SIAvelocityActive + logical, pointer :: hydroActive + logical, pointer :: observationsActive + ierr = 0 + call mpas_pool_get_config(configPool, 'config_velocity_solver', config_velocity_solver) + call mpas_pool_get_config(configPool, 'config_SGH', config_SGH) + call mpas_pool_get_config(configPool, 'config_write_albany_ascii_mesh', config_write_albany_ascii_mesh) + + call mpas_pool_get_package(packagePool, 'SIAvelocityActive', SIAvelocityActive) + call mpas_pool_get_package(packagePool, 'higherOrderVelocityActive', higherOrderVelocityActive) + call mpas_pool_get_package(packagePool, 'hydroActive', hydroActive) + call mpas_pool_get_package(packagePool, 'observationsActive', observationsActive) + + if (trim(config_velocity_solver) == 'sia') then + SIAvelocityActive = .true. + call mpas_log_write('The SIAVelocity package and associated variables and streams have been ' // & + 'enabled because the SIA velocity solver is selected.') + else + higherOrderVelocityActive = .true. + call mpas_log_write("The 'higherOrderVelocity' package and associated variables have been " // & + "enabled because a higher-order velocity solver is selected.") + end if + + if (config_SGH) then + hydroActive = .true. + call mpas_log_write("The 'hydro' package and assocated variables have been enabled because 'config_SGH' is set to .true.") + endif + + if (config_write_albany_ascii_mesh) then + observationsActive = .true. + call mpas_log_write("The 'observations' package and assocated variables have been enabled because " // & + "'config_write_albany_ascii_mesh' is set to .true.") + endif + + ! call setup packages in analysis driver + call li_analysis_setup_packages(configPool, packagePool, ierr) + + end function li_setup_packages @@ -109,12 +155,12 @@ end function li_setup_packages !> \brief Pacakge setup function !> \author Michael Duda !> \date 6 August 2014 -!> \details +!> \details !> The purpose of this function is to allow the core to set up a simulation !> clock that will be used by the I/O subsystem for timing reads and writes !> of I/O streams. -!> This function is called from the superstructure after the framework -!> has been initialized but before any fields have been allocated and +!> This function is called from the superstructure after the framework +!> has been initialized but before any fields have been allocated and !> initial fields have been read from input files. However, all namelist !> options are available. ! @@ -182,10 +228,10 @@ end function li_setup_log!}}} !> \brief Returns the name of the stream containing mesh information !> \author Michael Duda !> \date 8 August 2014 - !> \details + !> \details !> This function returns the name of the I/O stream containing dimensions, - !> attributes, and mesh fields needed by the framework bootstrapping - !> routine. At the time this routine is called, only namelist options + !> attributes, and mesh fields needed by the framework bootstrapping + !> routine. At the time this routine is called, only namelist options !> are available. ! !----------------------------------------------------------------------- @@ -204,7 +250,7 @@ function li_get_mesh_stream(configs, stream) result(ierr) call mpas_pool_get_config(configs, 'config_do_restart', config_do_restart) if (.not. associated(config_do_restart)) then - call mpas_log_write('ERROR: config_do_restart is not associated.', MPAS_LOG_CRIT) + call mpas_log_write('config_do_restart is not associated.', MPAS_LOG_CRIT) else if (config_do_restart) then write(stream,'(a)') 'restart' else @@ -221,7 +267,7 @@ end function li_get_mesh_stream !> \brief Decomposition setup function !> \author Doug Jacobsen !> \date 04/08/2015 - !> \details + !> \details !> This function is intended to create the decomposition list within a !> domain type, and register any decompositons the core wants within it. ! @@ -260,7 +306,7 @@ end function li_setup_decompositions !> \brief Land ice block setup function !> \author Doug Jacobsen !> \date 03/18/2015 - !> \details + !> \details !> This function is a wrapper function to properly setup a block to be a !> land ice core block. ! @@ -274,17 +320,17 @@ function li_setup_block(block) result(iErr)!{{{ call li_generate_structs(block, block % structs, block % dimensions, block % packages) end function li_setup_block!}}} -#include "inc/setup_immutable_streams.inc" +#include "../inc/setup_immutable_streams.inc" -#include "inc/block_dimension_routines.inc" +#include "../inc/block_dimension_routines.inc" -#include "inc/define_packages.inc" +#include "../inc/define_packages.inc" -#include "inc/structs_and_variables.inc" +#include "../inc/structs_and_variables.inc" -#include "inc/namelist_call.inc" +#include "../inc/namelist_call.inc" -#include "inc/namelist_defines.inc" +#include "../inc/namelist_defines.inc" end module li_core_interface diff --git a/src/core_landice/mode_forward/mpas_li_diagnostic_vars.F b/src/core_landice/mode_forward/mpas_li_diagnostic_vars.F new file mode 100644 index 0000000000..82c9db2659 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_diagnostic_vars.F @@ -0,0 +1,521 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_diagnostic_vars +! +!> \brief MPAS land ice module for calculating diagnostic variables +!> \author Matt Hoffman +!> \date 17 April 2011 +!> \details +!> This module contains various subroutines for +!> calculating diagnostic (time-independent) variables +!> for the land ice core. +!> These calculations should be general so as to be independent +!> of time integration scheme. +! +!----------------------------------------------------------------------- + +module li_diagnostic_vars + + use mpas_derived_types + use mpas_pool_routines + use mpas_timer + use mpas_log + + use li_mask + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + public :: li_calculate_diagnostic_vars, & + li_calculate_apparent_diffusivity, & + li_calculate_flowParamA + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_calculate_diagnostic_vars +! +!> \brief Calculates diagnostic variables +!> \author Matthew Hoffman +!> \date 12 September 2013 +!> \details +!> This routine calculates diagnostic variables using the current prognostic +!> variables. These should only be variables that are output for convenience +!> but otherwise not needed by the model. +!> +! +!----------------------------------------------------------------------- + + subroutine li_calculate_diagnostic_vars(domain, err) + + use li_thermal, only: li_compute_pressure_melting_point_fields + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + ! Note: domain is passed in because halo updates are needed in this routine + ! and halo updates have to happen outside block loops, which requires domain. + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: thermalPool + integer, pointer :: nCells + real (kind=RKIND), dimension(:), pointer :: layerCenterSigma + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:,:), pointer :: pmpTemperature + real (kind=RKIND), dimension(:), pointer :: basalPmpTemperature + real (kind=RKIND), dimension(:), pointer :: thicknessOld + real (kind=RKIND), dimension(:), pointer :: dHdt + real (kind=RKIND), pointer :: deltat + + integer :: err_tmp + + + err = 0 + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'thicknessOld', thicknessOld) + call mpas_pool_get_array(geometryPool, 'dHdt', dHdt) + call mpas_pool_get_array(thermalPool, 'pmpTemperature', pmpTemperature) + call mpas_pool_get_array(thermalPool, 'basalPmpTemperature', basalPmpTemperature) + + ! Calculate diagnostic PMP temperature + ! Note: this is only to allow these fields to be output; + ! the thermal module performs these calculations internally as needed. + ! Note: The PMP temperature calculated here will differ from the PMP values + ! in the thermal solve, because the thermal solver occurs before + ! advection occurs. + call li_compute_pressure_melting_point_fields(nCells, thickness, layerCenterSigma, & + pmpTemperature, basalPmpTemperature) + + ! Calculate dHdt. Do it here after all thickness adjustments are complete + dHdt = (thickness - thicknessOld) / deltat * scyr ! Units are m/yr + thicknessOld = thickness ! Reset thicknessOld for next time step + + block => block % next + end do + + + ! === error check and exit + if (err == 1) then + print *, "An error has occurred in li_calculate_diagnostic_vars. Aborting..." + !call mpas_dmpar_global_abort(dminfo) + endif + + !-------------------------------------------------------------------- + end subroutine li_calculate_diagnostic_vars + + +!*********************************************************************** +! +! subroutine li_calculate_apparent_diffusivity +! +!> \brief Computes apparent diffusivity +!> \author Matt Hoffman +!> \date 19 April 2012 +!> \details +!> This routine computes the apparent diffusivity. +!> Estimate diffusivity using the relation that the 2-d flux Q=-D grad h and Q=UH, +!> where h is surface elevation, D is diffusivity, U is 2-d velocity vector, and H is thickness +!> Solving for D = UH/-grad h +!> DCFL: dt = 0.5 * dx**2 / D = 0.5 * dx**2 * slopemag / flux_downslope +! +!----------------------------------------------------------------------- + subroutine li_calculate_apparent_diffusivity(meshPool, velocityPool, scratchPool, geometryPool, allowableDiffDt) + use mpas_vector_reconstruction + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + type (mpas_pool_type), intent(in) :: & + velocityPool !< Input: velocity information + + type (mpas_pool_type), intent(in) :: & + scratchPool !< Input: scratch information + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: & + geometryPool !< Input: geometry information + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real(kind=RKIND), intent(out) :: allowableDiffDt !< Output: allowable timestep based on diffusive CFL + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + logical, pointer :: config_print_thickness_advection_info + real (kind=RKIND), dimension(:), pointer :: normalSlopeEdge + type (field1dReal), pointer :: slopeReconstructXField, slopeReconstructYField, slopeReconstructZField + !< Only needed for calling mpas_reconstruct, but not actually used here + type (field1dReal), pointer :: slopeCellAxis1Field + type (field1dReal), pointer :: slopeCellAxis2Field + real (kind=RKIND), dimension(:), pointer :: slopeCellAxis1, slopeCellAxis2 + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:,:), pointer :: layerThickness + real (kind=RKIND), dimension(:,:), pointer :: uReconstructAxis1, uReconstructAxis2 + real (kind=RKIND), dimension(:), pointer :: apparentDiffusivity + real (kind=RKIND), dimension(:), pointer :: dcEdge + integer, dimension(:), pointer :: cellMask + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell + integer, pointer :: nCells, nVertLevels + real (kind=RKIND) :: allowableDtHere + real (kind=RKIND) :: fluxVeloAxis1, fluxVeloAxis2 + real (kind=RKIND) :: fluxDownslope + real (kind=RKIND) :: slopeCellMagnitude + real (kind=RKIND) :: dCell + integer :: iCell, iEdge, iLevel + real (kind=RKIND), parameter :: bigNumber = 1.0e16_RKIND + !<- This is ~300 million years in seconds, but it is small enough not too overflow + real (kind=RKIND), parameter :: smallNumber = 1.0e-36_RKIND + logical :: divideSingularityFound + + ! Note: This routine could be broken into 2: one to calculate diffusivity + ! and another to get the diffusive CFL timestep. In that case, the first (and possibly the second) + ! could be moved to diagnostic_variable_solve_after_velocity. However, since + ! diffusivity is only used for this check, I don't think it makes sense to separate these + ! calculations for now. + + ! get needed variables + call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(geometryPool, 'normalSlopeEdge', normalSlopeEdge) + call mpas_pool_get_array(geometryPool, 'thickness', thickness, timeLevel=1) + call mpas_pool_get_array(geometryPool, 'layerThickness', layerThickness, timeLevel=1) + call mpas_pool_get_array(geometryPool, 'apparentDiffusivity', apparentDiffusivity) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(velocityPool, 'uReconstructZonal', uReconstructAxis1) + call mpas_pool_get_array(velocityPool, 'uReconstructMeridional', uReconstructAxis2) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_field(scratchPool, 'workCell', slopeReconstructXField) + call mpas_allocate_scratch_field(slopeReconstructXField, .true.) + call mpas_pool_get_field(scratchPool, 'workCell2', slopeReconstructYField) + call mpas_allocate_scratch_field(slopeReconstructYField, .true.) + call mpas_pool_get_field(scratchPool, 'workCell3', slopeReconstructZField) + call mpas_allocate_scratch_field(slopeReconstructZField, .true.) + call mpas_pool_get_field(scratchPool, 'slopeCellX', slopeCellAxis1Field) + call mpas_allocate_scratch_field(slopeCellAxis1Field, .true.) + slopeCellAxis1 => slopeCellAxis1Field % array + call mpas_pool_get_field(scratchPool, 'slopeCellY', slopeCellAxis2Field) + call mpas_allocate_scratch_field(slopeCellAxis2Field, .true.) + slopeCellAxis2 => slopeCellAxis2Field % array + + ! given thickness, compute layerThickness + call li_calculate_layerThickness(meshPool, thickness, layerThickness) + + ! Initialize output + allowableDiffDt = bigNumber + + ! Approximate slope at cell centers + ! reconstruct routines set uReconstructZonal = uReconstructX; uReconstructMeridional = uReconstructY + ! for planar meshes, so those variables can be used as orthogonal components of the vector + ! in either the plane or sphere. This avoids needing to add logic for if we are on a sphere or not. + call mpas_reconstruct(meshPool, normalSlopeEdge, & + slopeReconstructXField % array, slopeReconstructYField % array, slopeReconstructZField % array, & + slopeCellAxis1, slopeCellAxis2) + + + ! Approximate flux at cell centers + divideSingularityFound = .false. + do iCell = 1, nCells + slopeCellMagnitude = sqrt(slopeCellAxis1(iCell)**2 + slopeCellAxis2(iCell)**2) + smallNumber + + if ( (slopeCellMagnitude < 1.0e-4_RKIND) .and. & + (max(maxval(uReconstructAxis1(:,iCell)), maxval(uReconstructAxis2(:,iCell))) < 3.18e-8_RKIND) ) then + ! 3.18e-8=1 m/yr in m/s + ! Ignore diffusivity near 'divide-singularities' + apparentDiffusivity(iCell) = 0.0_RKIND + divideSingularityFound = .true. + else + fluxDownslope = 0.0_RKIND + do iLevel = 1, nVertLevels + fluxVeloAxis1 = (uReconstructAxis1(iLevel, iCell) + uReconstructAxis1(iLevel+1, iCell)) * 0.5_RKIND + fluxVeloAxis2 = (uReconstructAxis2(iLevel, iCell) + uReconstructAxis2(iLevel+1, iCell)) * 0.5_RKIND + fluxDownslope = fluxDownslope + & + (-1.0_RKIND * slopeCellAxis1(iCell) * fluxVeloAxis1 - slopeCellAxis2(iCell) * fluxVeloAxis2) & + * layerThickness(iLevel, iCell) / slopeCellMagnitude + enddo + apparentDiffusivity(iCell) = abs(fluxDownslope) / slopeCellMagnitude + endif + + ! Calculate allowable timestep based on DCFL + if ( li_mask_is_grounded_ice(cellMask(iCell)) .and. li_mask_is_dynamic_ice(cellMask(iCell)) ) then + ! Find shortest distance to a neighboring cell center, dCell + dCell = minval(dcEdge(1:nEdgesOnCell(iCell))) + allowableDtHere = 0.5_RKIND * dCell**2 / (apparentDiffusivity(iCell) + smallNumber) + else + allowableDtHere = bigNumber + endif + allowableDiffDt = min(allowableDiffDt, allowableDtHere) + enddo + + if (divideSingularityFound .and. config_print_thickness_advection_info) then + call mpas_log_write('Notice: In calculating apparentDiffusivity, one or more cells have been ignored ' // & + 'due to flat slope and low velocity (assumed to be a divide where diffusivity is undefined).') + endif + + call mpas_deallocate_scratch_field(slopeReconstructXField, .true.) + call mpas_deallocate_scratch_field(slopeReconstructYField, .true.) + call mpas_deallocate_scratch_field(slopeReconstructZField, .true.) + call mpas_deallocate_scratch_field(slopeCellAxis1Field, .true.) + call mpas_deallocate_scratch_field(slopeCellAxis2Field, .true.) + !-------------------------------------------------------------------- + end subroutine li_calculate_apparent_diffusivity + + + +!*********************************************************************** +! +! subroutine li_calculate_flowParamA +! +!> \brief Calculates the flow law parameter A based on temperature +!> \author Matt Hoffman +!> \date 23 Jan 2014 +!> \details +!> This routine calculates the flow law parameter A based on temperature +!> depending on what option is chosen. +!> The default option is a constant A assigned from config_default_flowParamA. +!> The PB1982 option uses this equation from \emph{Paterson and Budd} [1982] +!> and \emph{Paterson} [1994] (copied from CISM): +!> \[ +!> A(T^{*})=A0 \exp \left(\frac{-Q}{RT^{*}}\right) +!> \] +!> This is equation 9 in {\em Payne and Dongelmans}. $A0$ is a constant of proportionality, +!> $Q$ is the activation energy for for ice creep, and $R$ is the universal gas constant. +!> The pressure-corrected temperature, $T^{*}$ is given by: +!> \[ +!> T^{*} = T - T_{pmp} + T_0 +!> \] +!> \[ +!> T_{pmp} = T_0 - \sigma \rho g H \Phi +!> \] +!> $T$ is the ice temperature, $T_0$ is the triple point of water, +!> $\rho$ is the ice density, and $\Phi$ is the (constant) rate of change of +!> melting point temperature with pressure. +!> +!> The CP2010 option uses this equation from the 4th Edition of Physics of Glaciers (Eq. 3.35): +!> \[ +!> A(T^{*})=A0 \exp \left(\frac{-Q}{R} ( \frac{1}{T^{*}} - \frac{1}{T_t})\right) +!> \] +!> where the variables are the same as above and $T_t$ is the pressure corrected +!> transition temperature (-10 deg C at 0 pressure). +!> Values for $A0, Q, \Phi$ differ from PB1982. +!> +!> All options are adjusted by the enhancement factor (which defaults to 1.0). +!----------------------------------------------------------------------- + subroutine li_calculate_flowParamA(meshPool, temperature, thickness, flowParamA, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + real (kind=RKIND), dimension(:,:), intent(in) :: & + temperature !< Input: temperature + real (kind=RKIND), dimension(:), intent(in) :: & + thickness !< Input: thickness + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + integer, intent(out) :: err + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(out) :: & + flowParamA !< Input: flowParamA + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer, pointer :: nCells, nVertLevels + character (len=StrKIND), pointer :: config_flowParamA_calculation + real (kind=RKIND), pointer :: config_default_flowParamA, & + config_enhancementFactor, & + config_dynamic_thickness, & + config_ice_density + integer :: iCell, iLevel, err_tmp + real (kind=RKIND), dimension(:), pointer :: layerCenterSigma + real (kind=RKIND) :: A0, Q, pressureMeltPointSlope + real (kind=RKIND) :: temperatureCorrected, transitionTemperatureCorrected + + err = 0 + err_tmp = 0 + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + + call mpas_pool_get_config(liConfigs, 'config_flowParamA_calculation', config_flowParamA_calculation) + call mpas_pool_get_config(liConfigs, 'config_enhancementFactor', config_enhancementFactor) + call mpas_pool_get_config(liConfigs, 'config_default_flowParamA', config_default_flowParamA) + call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + + + select case(config_flowParamA_calculation) + !----------------------------------------------------------------- + case('constant') + flowParamA = config_default_flowParamA + !----------------------------------------------------------------- + case('PB1982') + pressureMeltPointSlope = 9.7456e-8_RKIND + do iCell = 1, nCells + if (thickness(iCell) > 0.0_RKIND) then + do iLevel = 1, nVertLevels + ! Calculate the pressure-corrected temperature + temperatureCorrected = min(273.15_RKIND, temperature(iLevel,iCell) + pressureMeltPointSlope * & + thickness(iCell) * config_ice_density * gravity * layerCenterSigma(iLevel) ) + temperatureCorrected = max(223.15_RKIND, temperatureCorrected) + ! Calculate flow A + if (temperatureCorrected > 263.15_RKIND) then + A0 = 1.733e3_RKIND + Q = 139.0e3_RKIND + else + A0 = 3.613e-13_RKIND + Q = 60.0e3_RKIND + endif + flowParamA(iLevel,iCell) = A0 * exp(-1.0_RKIND * Q / (idealGasConstant * temperatureCorrected)) + enddo ! levels + else + flowParamA(:,iCell) = 0.0_RKIND ! non-ice cells get 0 + endif ! if dynamic ice + enddo ! cells + !----------------------------------------------------------------- + case('CP2010') + pressureMeltPointSlope = 7.0e-8_RKIND + do iCell = 1, nCells + if (thickness(iCell) > 0.0_RKIND) then ! SIA solver could make use of A on thin ice + ! if doing 2nd order averaging of flwa onto edges (otherwise this could be the dynamic thickness limit) + do iLevel = 1, nVertLevels + ! Calculate the pressure-corrected temperature + temperatureCorrected = min(273.15_RKIND, temperature(iLevel,iCell) + pressureMeltPointSlope * & + thickness(iCell) * config_ice_density * gravity * layerCenterSigma(iLevel) ) + temperatureCorrected = max(223.15_RKIND, temperatureCorrected) + transitionTemperatureCorrected = 263.15_RKIND + pressureMeltPointSlope * & + thickness(iCell) * config_ice_density * gravity * layerCenterSigma(iLevel) + ! Calculate flow A + A0 = 3.5e-25_RKIND + if (temperatureCorrected > 263.15_RKIND) then + Q = 115.0e3_RKIND + else + Q = 6.0e4_RKIND + endif + flowParamA(iLevel,iCell) = A0 * exp(-1.0_RKIND * Q / idealGasConstant * & + (1.0_RKIND/temperatureCorrected - 1.0_RKIND/transitionTemperatureCorrected)) + enddo ! levels + else + flowParamA(:,iCell) = 0.0_RKIND ! non-ice cells get 0 + endif ! if dynamic ice + enddo ! cells + !----------------------------------------------------------------- + end select + + !print *,'max flwa', maxval(flowParamA) + !print *,'config_enhancementFactor', config_enhancementFactor + + ! Include enhancement factor + flowParamA = flowParamA * config_enhancementFactor + + err = ior(err, err_tmp) + + end subroutine li_calculate_flowParamA + + +!*********************************************************************** +!*********************************************************************** +! Private subroutines: +!*********************************************************************** +!*********************************************************************** + + + +end module li_diagnostic_vars + diff --git a/src/core_landice/mode_forward/mpas_li_sia.F b/src/core_landice/mode_forward/mpas_li_sia.F new file mode 100644 index 0000000000..dd67a365b0 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_sia.F @@ -0,0 +1,507 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_sia +! +!> \MPAS land-ice SIA velocity driver +!> \author Matt Hoffman +!> \date 16 March 2012 +!> \details +!> This module contains the routines for calculating velocity using the shallow ice approximation. +!> +! +!----------------------------------------------------------------------- + +module li_sia + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_log + use li_mask + use li_setup + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + public :: li_sia_init, & + li_sia_finalize, & + li_sia_block_init, & + li_sia_solve + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_sia_init +! +!> \brief Initializes SIA velocity solver +!> \author Matt Hoffman/Xylar Asay-Davis +!> \date 16 March 2012 +!> \details +!> This routine initializes the SIA ice velocity solver. +! +!----------------------------------------------------------------------- + + subroutine li_sia_init(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + + ! No init is needed. + err = 0 + + !-------------------------------------------------------------------- + + end subroutine li_sia_init + + + +!*********************************************************************** +! +! routine li_sia_block_init +! +!> \brief Initializes blocks for SIA velocity solver +!> \author Matt Hoffman/Xylar Asay-Davis +!> \date 16 March 2012 +!> \details +!> This routine initializes each block of the SIA ice velocity solver. +! +!----------------------------------------------------------------------- + + subroutine li_sia_block_init(block, err) + + use mpas_geometry_utils, only: mpas_calculate_barycentric_weights_for_points + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (block_type), intent(inout) :: & + block !< Input/Output: block object + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + integer :: iCell, iLevel, i, iVertex, err_tmp + integer, pointer :: nVertices + character (len=StrKIND), pointer :: config_sia_tangent_slope_calculation + integer, dimension(:,:), pointer :: baryCellsOnVertex + real (kind=RKIND), dimension(:,:), pointer :: baryWeightsOnVertex + real (kind=RKIND), dimension(:), pointer :: xVertex, yVertex, zVertex + type (field1dInteger), pointer :: vertexIndicesField + + ! No block init needed. + err = 0 + err_tmp = 0 + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_array(meshPool, 'baryCellsOnVertex', baryCellsOnVertex) + call mpas_pool_get_array(meshPool, 'baryWeightsOnVertex', baryWeightsOnVertex) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + call mpas_pool_get_config(liConfigs, 'config_sia_tangent_slope_calculation', config_sia_tangent_slope_calculation) + call mpas_pool_get_field(scratchPool, 'vertexIndices', vertexIndicesField) + call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + + ! The SIA solver may need to setup these weights for calculating upperSurfaceVertex + if (trim(config_sia_tangent_slope_calculation) == 'from_vertex_barycentric') then + call mpas_allocate_scratch_field(vertexIndicesField, .true.) + do iVertex = 1, nVertices + vertexIndicesField % array(iVertex) = iVertex + enddo + call mpas_calculate_barycentric_weights_for_points(meshPool, & + xVertex(1:nVertices), yVertex(1:nVertices), zVertex(1:nVertices), & + vertexIndicesField % array(1:nVertices), & + baryCellsOnVertex(:, 1:nVertices), baryWeightsOnVertex(:, 1:nVertices), err_tmp) + ! TODO: Until framework can handle periodic meshs gracefully, this will return an error + ! for periodic meshes. This error means that the velocity solver will be very wrong across + ! the periodicity, but it will be fine everywhere else. For now, just print a warning but + ! don't make this a fatal error. + !err = ior(err, err_tmp) + if (err_tmp > 0) then + call mpas_log_write("The 'from_vertex_barycentric' option for 'config_sia_tangent_slope_calculation' " & + // "does NOT work across the periodicity in periodic meshes. However, it does work within the interior " & + // "of the mesh.", MPAS_LOG_WARN) + endif + call mpas_deallocate_scratch_field(vertexIndicesField, .true.) + endif + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_sia_block_init.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_sia_block_init + + + +!*********************************************************************** +! +! subroutine li_sia_solve +! +!> \brief Computes velocity using Shallow Ice Appoximation +!> \author Matt Hoffman +!> \date 21 May 2012 +!> \details +!> This routine computes the normal velocity on edges for each layer +!> using the Shallow Ice Approximation. It calculates ice thickness on +!> on an edge using the average of the two neighboring cells (2nd order). +! +!----------------------------------------------------------------------- + subroutine li_sia_solve(meshPool, geometryPool, thermalPool, velocityPool, err) + + use mpas_vector_operations, only: mpas_tangential_vector_1d + use mpas_geometry_utils, only: mpas_cells_to_points_using_baryweights + + use li_setup, only: li_cells_to_vertices_1dfield_using_kiteAreas + use li_constants, only: gravity + use li_thermal + use li_diagnostic_vars + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + type (mpas_pool_type), intent(in) :: & + geometryPool !< Input: geometry information + + type (mpas_pool_type), intent(in) :: & + thermalPool !< Input: thermal information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: & + velocityPool !< Input/Output: velocity information + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), pointer :: thickness, layerInterfaceSigma + real (kind=RKIND), dimension(:), pointer :: slopeEdge, normalSlopeEdge + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, flowParamA + real (kind=RKIND), dimension(:,:), pointer :: temperature + integer, dimension(:,:), pointer :: cellsOnEdge + integer, dimension(:), pointer :: edgeMask, cellMask + integer, pointer :: nVertInterfaces, nEdgesSolve + integer, pointer :: nEdges, nVertices + integer :: iLevel, iEdge + integer :: cell1, cell2 + real (kind=RKIND) :: thicknessEdge, flwaLevelEdge + real (kind=RKIND) :: positionIndependentFactor ! The portion of the velocity calculation that + ! is completely independent of position + real (kind=RKIND) :: levelIndependentFactor ! The portion of the velocity calculation that depends on + ! horizontal location but not vertical position + real (kind=RKIND), pointer :: rhoi ! ice density + real (kind=RKIND), pointer :: n ! flow law exponent, n + character (len=StrKIND), pointer :: config_sia_tangent_slope_calculation + integer, dimension(:,:), pointer :: verticesOnEdge + real (kind=RKIND), dimension(:), pointer :: dvEdge, tangentSlopeEdge + real (kind=RKIND), dimension(:), pointer :: upperSurfaceVertex + real (kind=RKIND), dimension(:), pointer :: upperSurface + integer, dimension(:,:), pointer :: baryCellsOnVertex + real (kind=RKIND), dimension(:,:), pointer :: baryWeightsOnVertex + integer :: cell1_is_dynamic, cell2_is_dynamic + integer :: err_tmp + + err = 0 + err_tmp = 0 + + ! Set needed variables and pointers + call mpas_pool_get_dimension(meshPool, 'nVertInterfaces', nVertInterfaces) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) + + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + call mpas_pool_get_array(velocityPool, 'flowParamA', flowParamA) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'slopeEdge', slopeEdge) + call mpas_pool_get_array(geometryPool, 'normalSlopeEdge', normalSlopeEdge) + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + + + ! Get parameters specified in the namelist + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', n) + + + ! ----------- + ! First prepare some necessary fields + ! ----------- + + + ! Calculate flowA + call li_calculate_flowParamA(meshPool, temperature(:,:), thickness, flowParamA, err_tmp) + if (err_tmp > 0) call mpas_log_write('li_calculate_flowParamA returned an error', MPAS_LOG_ERR) + err = ior(err, err_tmp) + + call mpas_pool_get_config(liConfigs, 'config_sia_tangent_slope_calculation', config_sia_tangent_slope_calculation) + call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(geometryPool, 'slopeEdge', slopeEdge) + call mpas_pool_get_array(geometryPool, 'tangentSlopeEdge', tangentSlopeEdge) + call mpas_pool_get_array(geometryPool, 'upperSurfaceVertex', upperSurfaceVertex) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + call mpas_pool_get_array(meshPool, 'baryCellsOnVertex', baryCellsOnVertex) + call mpas_pool_get_array(meshPool, 'baryWeightsOnVertex', baryWeightsOnVertex) + + ! Calculate upperSurfaceVertex if needed + select case (trim(config_sia_tangent_slope_calculation)) + case ('from_vertex_barycentric') + call mpas_cells_to_points_using_baryweights(meshPool, baryCellsOnVertex(:, 1:nVertices), & + baryWeightsOnVertex(:, 1:nVertices), upperSurface, upperSurfaceVertex(1:nVertices), err_tmp) + if (err_tmp > 0) call mpas_log_write('mpas_cells_to_points_using_baryweights returned an error', MPAS_LOG_ERR) + err = ior(err, err_tmp) + case ('from_vertex_barycentric_kiteareas') + call li_cells_to_vertices_1dfield_using_kiteAreas(meshPool, upperSurface, upperSurfaceVertex) + end select + + ! Calculate tangent slope + select case (trim(config_sia_tangent_slope_calculation)) + case ('from_vertex_barycentric', 'from_vertex_barycentric_kiteareas') + do iEdge = 1, nEdges + ! Only calculate slope for edges that have ice on at least one side. + if ( li_mask_is_dynamic_ice(edgeMask(iEdge)) ) then + tangentSlopeEdge(iEdge) = ( upperSurfaceVertex(verticesOnEdge(1,iEdge)) - & + upperSurfaceVertex(verticesOnEdge(2,iEdge)) ) / dvEdge(iEdge) + else + tangentSlopeEdge(iEdge) = 0.0_RKIND + endif + end do ! edges + case ('from_normal_slope') + call mpas_tangential_vector_1d(normalSlopeEdge, meshPool, & + includeHalo=.true., tangentialVector=tangentSlopeEdge) + case default + call mpas_log_write('Invalid value for config_sia_tangent_slope_calculation.', MPAS_LOG_ERR) + err = 1 + end select + + ! Now calculate the slope magnitude + slopeEdge = sqrt(normalSlopeEdge**2 + tangentSlopeEdge**2) + + ! Note: the outer halo may be wrong, but that's ok as long as numhalos>1 + ! because the velocity on the 0-halo will still be correct. + + + ! ----------------- + ! Now solve velocity + ! ----------------- + positionIndependentFactor = -0.5_RKIND * (rhoi * gravity)**n ! could be calculated once on init + + ! Loop over edges + do iEdge = 1, nEdgesSolve + + ! Only calculate velocity for edges that are part of the dynamic ice sheet.(thick ice) + ! Also, the velocity calculation should be valid for non-ice edges (i.e. returns 0). + if ( li_mask_is_dynamic_ice(edgeMask(iEdge)) ) then + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + cell1_is_dynamic = li_mask_is_dynamic_ice_int(cellMask(cell1)) + cell2_is_dynamic = li_mask_is_dynamic_ice_int(cellMask(cell2)) + + ! Calculate thickness on edge - 2nd order + thicknessEdge = (thickness(cell1) + thickness(cell2) ) * 0.5_RKIND +! thicknessEdge = (thickness(cell1) * cell1_is_dynamic + thickness(cell2) * cell2_is_dynamic) & +! / (cell1_is_dynamic + cell2_is_dynamic) +! <- this version does an upwind thickness on margin edges only. Most Halfar error stats are higher by about 10-20% + ! Also tried upwind everywhere [for dome can be hacked with: thicknessEdge = max(thickness(cell1), thickness(cell2) ] + !This results in Halfar errors that are about 5x larger than centered difference + + levelIndependentFactor = slopeEdge(iEdge)**(n-1) * normalSlopeEdge(iEdge) * thicknessEdge**(n+1) + + normalVelocity(nVertInterfaces, iEdge) = 0.0_RKIND ! Assume no sliding + + do iLevel = nVertInterfaces-1, 1, -1 ! Loop upwards from second lowest level to surface + ! Calculate flwa on edge for this level - 2nd order, except can't do centered difference + ! into areas where flwa may not be valid, so excluding the downwind flwa value in non-dynamic cells +! flwaLevelEdge = (flowParamA(iLevel, cell1) + flowParamA(iLevel, cell2) ) * 0.5_RKIND + flwaLevelEdge = (flowParamA(iLevel, cell1) * cell1_is_dynamic + & + flowParamA(iLevel, cell2) * cell2_is_dynamic) / & + (cell1_is_dynamic + cell2_is_dynamic) + + ! Calculate SIA velocity for this layer interface by adding on incremental velocity for the layer below + ! (This requires that flwa be constant over that layer, which it is.) + normalVelocity(iLevel, iEdge) = normalVelocity(iLevel+1, iEdge) + & + positionIndependentFactor * levelIndependentFactor * flwaLevelEdge * & + ( (layerInterfaceSigma(iLevel))**(n+1) - (layerInterfaceSigma(iLevel+1))**(n+1) ) + end do + else + normalVelocity(:,iEdge) = 0.0_RKIND ! zero velocity on non-dynamic edges + endif + end do ! edges + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_sia_solve.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + + end subroutine li_sia_solve + + + + +!*********************************************************************** +! +! routine li_sia_finalize +! +!> \brief finalizes SIA velocity solver +!> \author Matt Hoffman/Xylar Asay-Davis +!> \date 16 March 2012 +!> \details +!> This routine initializes the SIA ice velocity solver. +! +!----------------------------------------------------------------------- + + subroutine li_sia_finalize(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + + !-------------------------------------------------------------------- + + end subroutine li_sia_finalize + + + + ! private subroutines + + + + +!*********************************************************************** + +end module li_sia + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_landice/mode_forward/mpas_li_statistics.F b/src/core_landice/mode_forward/mpas_li_statistics.F new file mode 100644 index 0000000000..47a7be44e8 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_statistics.F @@ -0,0 +1,922 @@ +! Copyright (c) 2015-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_statistics +! +!> \MPAS land ice global and local statistics +!> \author William Lipscomb +!> \date 22 January 2015 +!> \details +!> This module contains routines for computing glocal and local +!> statistics and other diagnostic info. +!> It is based on a similar module in CISM. +! +!----------------------------------------------------------------------- + +module li_statistics + + use mpas_derived_types + use mpas_dmpar + use mpas_timer + use mpas_log + + use li_setup + use li_mask + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_compute_statistics + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + contains + +!*********************************************************************** +! +! routine li_compute_statistics +! +!> \brief Computes global and local statistics +!> \author William Lipscomb +!> \date 22 January 2015 +!> \details +!> This routine computes global statistics for the full domain, along with +!> local statistics and diagnostic info for a user-specified grid cell. +!> +!----------------------------------------------------------------------- + + subroutine li_compute_statistics(domain, itimestep) + + implicit none + + ! Input/output arguments + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + integer, intent(in) :: itimestep !< Input: current time step counter + + ! Local variables + + type (block_type), pointer :: block + type (dm_info), pointer :: dminfo + + ! pools + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: scratchPool + + ! mesh dimensions + integer, pointer :: nVertLevels, nVertInterfaces, nCellsSolve, nEdgesSolve + + ! mesh arrays + integer, dimension(:), pointer :: indexToCellID, indexToEdgeID + integer, dimension(:,:), pointer :: cellsOnEdge, edgesOnCell + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:), pointer :: layerCenterSigma + real (kind=RKIND), dimension(:), pointer :: bedTopography, sfcMassBal + + ! state variables + character (len=StrKIND), pointer :: xtime + integer, dimension(:), pointer :: cellMask + integer, dimension(:), pointer :: edgeMask + real (kind=RKIND), dimension(:), pointer :: upperSurface + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: surfaceTemperature, basalTemperature + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, normalVelocity + real (kind=RKIND), dimension(:,:), pointer :: temperature + + ! scratch variables + type (field1dInteger), pointer :: iceCellMaskField + integer, dimension(:), pointer :: iceCellMask + + type (field1dInteger), pointer :: iceEdgeMaskField + integer, dimension(:), pointer :: iceEdgeMask + + type (field2dReal), pointer :: workLevelCellField + real (kind=RKIND), dimension(:,:), pointer :: workLevelCell + + ! config variables + real (kind=RKIND), pointer :: rhoi ! ice density (kg/m^3) + integer, pointer :: statsCellID ! global ID of cell for which we write stats/diagnostics + + ! other pointers +! integer, pointer :: indexTemperature + + ! work variables and arrays + real (kind=RKIND) :: localSum, localMin, localMax, localVertSumMin, localVertSumMax + real (kind=RKIND) :: localAbsoluteMax + integer :: localMinlocElement, localMinlocLevel + integer :: localMaxlocElement, localMaxlocLevel + integer :: localVertSumMinlocElement, localVertSumMaxlocElement + real (kind=RKIND), dimension(:), allocatable :: workLevel + + ! sums and max/mins on local processor + real (kind=RKIND) :: iceAreaSum, iceVolumeSum, iceEnergySum + real (kind=RKIND) :: thicknessMax, thicknessMin + real (kind=RKIND) :: temperatureMax, temperatureMin + real (kind=RKIND) :: velocityMax, basalVelocityMax + + ! cells, edges and levels where max/min values are located (cell/edge indices are global) + integer :: thicknessMinlocCell, thicknessMaxlocCell + integer :: temperatureMinlocCell, temperatureMinlocLevel + integer :: temperatureMaxlocCell, temperatureMaxlocLevel + integer :: velocityMaxlocEdge, velocityMaxlocLevel + integer :: basalVelocityMaxlocEdge + + ! global sums and max/mins + real (kind=RKIND) :: globalIceAreaSum, globalIceVolumeSum, globalIceEnergySum + real (kind=RKIND) :: globalThicknessMax, globalThicknessMin, globalThicknessMean + real (kind=RKIND) :: globalTemperatureMax, globalTemperatureMin, globalTemperatureMean + real (kind=RKIND) :: globalVelocityMax, globalBasalVelocityMax + + ! diagnostic info for user-specified grid cell + integer :: diagnosticCellLocal, diagnosticBlockIDLocal, diagnosticProcIDLocal + real (kind=RKIND) :: diagnosticUpperSurfaceLocal, diagnosticThicknessLocal, diagnosticBedTopographyLocal + real (kind=RKIND) :: diagnosticSfcMassBalLocal, diagnosticSurfaceTemperatureLocal, diagnosticBasalTemperatureLocal + + integer :: diagnosticCell, diagnosticBlockID, diagnosticProcID + real (kind=RKIND) :: diagnosticUpperSurface, diagnosticThickness, diagnosticBedTopography + real (kind=RKIND) :: diagnosticSfcMassBal, diagnosticSurfaceTemperature, diagnosticBasalTemperature + real (kind=RKIND), dimension(:), allocatable :: diagnosticSpeed, diagnosticTemperature + + integer :: iCell, iCell1, iCell2, iEdge, kLevel + integer :: proc + + character (len=StrKIND) :: msg !< local variable to build messages + + block => domain % blocklist + dminfo => domain % dminfo + + ! initialize statistics for this processor + ! If we have > 1 block/proc, these are summed or reduced over the block + + iceAreaSum = 0.0_RKIND + iceVolumeSum = 0.0_RKIND + iceEnergySum = 0.0_RKIND + thicknessMax = -huge(0.0_RKIND) + thicknessMin = huge(0.0_RKIND) + temperatureMax = -huge(0.0_RKIND) + temperatureMin = huge(0.0_RKIND) + velocityMax = -huge(0.0_RKIND) + basalVelocityMax = -huge(0.0_RKIND) + + thicknessMinlocCell = 0 + thicknessMaxlocCell = 0 + temperatureMinlocCell = 0 + temperatureMinlocLevel = 0 + velocityMaxlocEdge = 0 + velocityMaxlocLevel = 0 + basalVelocityMaxlocEdge = 0 + + ! initialize info for diagnostic grid cell + ! These values will be overwritten on the processor owning this grid cell + diagnosticCell = 0 + diagnosticBlockID = 0 + diagnosticProcID = 0 + diagnosticUpperSurface = 0.0_RKIND + diagnosticThickness = 0.0_RKIND + diagnosticBedTopography = 0.0_RKIND + diagnosticSfcMassBal = 0.0_RKIND + diagnosticSurfaceTemperature = 0.0_RKIND + diagnosticBasalTemperature = 0.0_RKIND + + do while (associated(block)) + + ! pools + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! mesh dimensions + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nVertInterfaces', nVertInterfaces) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + ! mesh arrays + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + call mpas_pool_get_array(meshPool, 'indexToEdgeID', indexToEdgeID) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + + ! LI mesh arrays + call mpas_pool_get_array(meshPool, 'xtime', xtime) + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + + ! Geometry arrays + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness, timeLevel = 1) + call mpas_pool_get_array(geometryPool, 'layerThickness', layerThickness, timeLevel = 1) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + + ! scratch arrays + call mpas_pool_get_field(scratchPool, 'iceCellMask', iceCellMaskField) + call mpas_pool_get_field(scratchPool, 'iceEdgeMask', iceEdgeMaskField) + call mpas_pool_get_field(scratchPool, 'workLevelCell', workLevelCellField) + + ! velocity arrays + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + + ! thermal arrays + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'surfaceTemperature', surfaceTemperature) + call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) + + ! config settings + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', statsCellID) + + call mpas_allocate_scratch_field(iceCellMaskField, .true.) + iceCellMask => iceCellMaskField % array + call mpas_allocate_scratch_field(iceEdgeMaskField, .true.) + iceEdgeMask => iceEdgeMaskField % array + call mpas_allocate_scratch_field(workLevelCellField, .true.) + workLevelCell => workLevelCellField % array + + ! compute ice cell mask (= 1 for cells where ice is present, else = 0) + ! Note: Global sums are taken only over cells with mask = 1 + + where (li_mask_is_ice(cellMask)) + iceCellMask = 1 + elsewhere + iceCellMask = 0 + endwhere + + ! compute ice edge mask (= 1 for edges of cells where ice is present, else = 0) + + where (li_mask_is_ice(edgeMask)) + iceEdgeMask = 1 + elsewhere + iceEdgeMask = 0 + endwhere + + ! given thickness, compute layerThickness + call li_calculate_layerThickness(meshPool, thickness, layerThickness) + + ! Compute statistics on local block + + ! max and min ice thickness + ! optional maxloc/minloc arguments give the local cell IDs where max/mins are located + + call li_compute_field_local_stats(dminfo, & + nVertLevels, nCellsSolve, & + layerThickness, & + iceCellMask(1:nCellsSolve), & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax, & + localVertSumMinlocElement = localVertSumMinlocElement, & + localVertSumMaxlocElement = localVertSumMaxlocElement) + + if (localVertSumMin < thicknessMin) then + thicknessMin = localVertSumMin + thicknessMinlocCell = indexToCellID(localVertSumMinlocElement) + endif + + if (localVertSumMax > thicknessMax) then + thicknessMax = localVertSumMax + thicknessMaxlocCell = indexToCellID(localVertSumMaxlocElement) + endif + + ! work array whose vertical sum = 1 everywhere + workLevelCell(1,:) = 1.0_RKIND + workLevelCell(2:nVertLevels,:) = 0.0_RKIND + + ! total ice area + call li_compute_field_area_weighted_local_stats & + (dminfo, & + nVertLevels, nCellsSolve, & + areaCell(1:nCellsSolve), & + workLevelCell(:,1:nCellsSolve), & + iceCellMask(1:nCellsSolve), & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax) + + iceAreaSum = iceAreaSum + localSum + + ! work array with a value of 1 in each layer + workLevelCell(:,:) = 1.0_RKIND + + ! total ice volume + call li_compute_field_volume_weighted_local_stats & + (dminfo, & + nVertLevels, nCellsSolve, & + areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), & + workLevelCell(:,1:nCellsSolve), & + iceCellMask(1:nCellsSolve), & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax) + + iceVolumeSum = iceVolumeSum + localSum + + ! max and min temperature + ! optional maxloc/minloc arguments give the local cell IDs where max/mins are located + + call li_compute_field_local_stats(dminfo, & + nVertLevels, nCellsSolve, & + temperature(:,1:nCellsSolve), & + iceCellMask(1:nCellsSolve), & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax, & + localMinlocElement, localMinlocLevel, & + localMaxlocElement, localMaxlocLevel) + + if (localMin < temperatureMin) then + temperatureMin = localMin + temperatureMinlocCell = indexToCellID(localMinlocElement) + temperatureMinlocLevel = localMinlocLevel + endif + + if (localMax > temperatureMax) then + temperatureMax = localMax + temperatureMaxlocCell = indexToCellID(localMaxlocElement) + temperatureMaxlocLevel = localMaxlocLevel + endif + + ! total ice energy (relative to 0 deg C) + !TODO - Compute ice energy differently if using the enthalpy scheme + + call li_compute_field_volume_weighted_local_stats & + (dminfo, & + nVertLevels, nCellsSolve, & + areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), & + temperature(:,1:nCellsSolve), & + iceCellMask(1:nCellsSolve), & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax) + + iceEnergySum = iceEnergySum + localSum*rhoi*cp_ice + + ! normal velocity at cell edges; find the maximum magnitude + + call li_compute_field_local_stats(dminfo, & + nVertInterfaces, nEdgesSolve, & + normalVelocity(:,1:nEdgesSolve), & + iceEdgeMask(1:nEdgesSolve), & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax, & + localMinlocElement, localMinlocLevel, & + localMaxlocElement, localMaxlocLevel) + + localAbsoluteMax = max(localMax, -localMin) ! take maximum magnitude, independent of sign + + if (localAbsoluteMax > velocityMax) then + velocityMax = localAbsoluteMax + if (localMax > abs(localMin)) then + velocityMaxlocEdge = indexToEdgeID(localMaxlocElement) + velocityMaxlocLevel = localMaxlocLevel + else + velocityMaxlocEdge = indexToEdgeId(localMinlocElement) + velocityMaxlocLevel = localMinlocLevel + endif + endif + + ! basal velocity at cell edges; find the maximum magnitude + ! Note: If velocity is located at layer midpoints, this is actually the + ! velocity in the lowest layer + + call li_compute_field_local_stats(dminfo, & + 1, nEdgesSolve, & + normalVelocity(nVertInterfaces,1:nEdgesSolve), & + iceEdgeMask(1:nEdgesSolve), & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax, & + localMinlocElement, localMinlocLevel, & + localMaxlocElement, localMaxlocLevel) + + localAbsoluteMax = max(localMax, -localMin) ! take maximum magnitude, independent of sign + + if (localAbsoluteMax > basalVelocityMax) then + basalVelocityMax = localAbsoluteMax + if (localMax > abs(localMin)) then + basalVelocityMaxlocEdge = indexToEdgeID(localMaxlocElement) + else + basalVelocityMaxlocEdge = indexToEdgeID(localMinlocElement) + endif + endif + + ! allocate and initialize some diagnostic arrays if not done already + if (.not. allocated(diagnosticSpeed)) then + allocate(diagnosticSpeed(nVertInterfaces)) + diagnosticSpeed(:) = 0.0_RKIND + endif + + if (.not. allocated(diagnosticTemperature)) then + allocate(diagnosticTemperature(nVertLevels)) + diagnosticTemperature(:) = 0.0_RKIND + endif + + ! Determine whether the user-specified diagnostic cell is on this block + ! If so, then set some diagnostics to be broadcast later to the head processor + + do iCell = 1, nCellsSolve + if (indexToCellId(iCell) == statsCellID) then ! this is the diagnostic cell + diagnosticCell = iCell + diagnosticBlockID = block % localBlockID + diagnosticProcID = dminfo % my_proc_id + diagnosticUpperSurface = upperSurface(iCell) +!! diagnosticThickness = sum(layerThickness(:,iCell)) + diagnosticThickness = thickness(iCell) + diagnosticBedTopography = bedTopography(iCell) + diagnosticSfcMassBal = sfcMassBal(iCell) * scyr / 1000.0_RKIND ! convert from kg/m^2/s to m/yr + diagnosticSurfaceTemperature = surfaceTemperature(iCell) + diagnosticBasalTemperature = basalTemperature(iCell) + iEdge = edgesOnCell(1,iCell) ! arbitrarily choose edge #1 for velocity diagnostics + ! alternatively, could write out the cell-center velocity + diagnosticSpeed(:) = normalVelocity(:,iEdge) * scyr ! convert from m/s to m/yr + diagnosticTemperature(:) = temperature(:,iCell) + endif + enddo + + ! clean up + call mpas_deallocate_scratch_field(iceCellMaskField, .true.) + call mpas_deallocate_scratch_field(iceEdgeMaskField, .true.) + call mpas_deallocate_scratch_field(workLevelCellField, .true.) + + block => block % next + enddo ! block loop + + ! Compute global statistics + ! TODO: Reduce the number of global reductions in this subroutine? + ! This could be done by packing quantities into arrays. + + ! global sums + call mpas_dmpar_sum_real(dminfo, iceAreaSum, globalIceAreaSum) + call mpas_dmpar_sum_real(dminfo, iceVolumeSum, globalIceVolumeSum) + call mpas_dmpar_sum_real(dminfo, iceEnergySum, globalIceEnergySum) + + ! global means + !TODO - Replace temperature mean with enthalpy mean if using enthalpy scheme + + if (globalIceAreaSum > 0.0_RKIND) then + globalThicknessMean = globalIceVolumeSum / globalIceAreaSum + else + globalThicknessMean = 0.0_RKIND + endif + + if (globalIceVolumeSum > 0.0_RKIND) then + globalTemperatureMean = globalIceEnergySum / (globalIceVolumeSum * rhoi * cp_ice) + else + globalTemperatureMean = 0.0_RKIND + endif + + ! global max/mins of state variables + ! First determine the global max/min and the proc on which it resides + ! Then broadcast the global max/min and its cell/edge/level location to all processors + + call mpas_dmpar_minloc_real(dminfo, thicknessMin, globalThicknessMin, proc) + call mpas_dmpar_bcast_real (dminfo, globalThicknessMin, proc) + call mpas_dmpar_bcast_int (dminfo, thicknessMinlocCell, proc) + + call mpas_dmpar_maxloc_real(dminfo, thicknessMax, globalThicknessMax, proc) + call mpas_dmpar_bcast_real (dminfo, globalThicknessMax, proc) + call mpas_dmpar_bcast_int (dminfo, thicknessMaxlocCell, proc) + + call mpas_dmpar_minloc_real(dminfo, temperatureMin, globalTemperatureMin, proc) + call mpas_dmpar_bcast_real (dminfo, globalTemperatureMin, proc) + call mpas_dmpar_bcast_int (dminfo, temperatureMinlocCell, proc) + call mpas_dmpar_bcast_int (dminfo, temperatureMinlocLevel, proc) + + call mpas_dmpar_maxloc_real(dminfo, temperatureMax, globalTemperatureMax, proc) + call mpas_dmpar_bcast_real (dminfo, globalTemperatureMax, proc) + call mpas_dmpar_bcast_int (dminfo, temperatureMaxlocCell, proc) + call mpas_dmpar_bcast_int (dminfo, temperatureMaxlocLevel, proc) + + call mpas_dmpar_maxloc_real(dminfo, velocityMax, globalVelocityMax, proc) + call mpas_dmpar_bcast_real (dminfo, globalVelocityMax, proc) + call mpas_dmpar_bcast_int (dminfo, velocityMaxlocEdge, proc) + call mpas_dmpar_bcast_int (dminfo, velocityMaxlocLevel, proc) + + call mpas_dmpar_maxloc_real(dminfo, basalVelocityMax, globalBasalVelocityMax, proc) + call mpas_dmpar_bcast_real (dminfo, globalBasalVelocityMax, proc) + call mpas_dmpar_bcast_int (dminfo, basalVelocityMaxlocEdge, proc) + + ! global reductions for user-specified diagnostic cell + ! Note: These reductions are done with global sums rather than broadcasts. + ! Global sums will work provided that the quantity of interest + ! has nonzero values on only a single processor. + diagnosticCellLocal = diagnosticCell + diagnosticBlockIDLocal = diagnosticBlockID + diagnosticProcIDLocal = diagnosticProcID + + diagnosticUpperSurfaceLocal = diagnosticUpperSurface + diagnosticThicknessLocal = diagnosticThickness + diagnosticBedTopographyLocal = diagnosticBedTopography + diagnosticSfcMassBalLocal = diagnosticSfcMassBal + diagnosticSurfaceTemperatureLocal = diagnosticSurfaceTemperature + diagnosticBasalTemperatureLocal = diagnosticBasalTemperature + + call mpas_dmpar_sum_int (dminfo, diagnosticCellLocal, diagnosticCell) + call mpas_dmpar_sum_int (dminfo, diagnosticBlockIDLocal, diagnosticBlockID) + call mpas_dmpar_sum_int (dminfo, diagnosticProcIDLocal, diagnosticProcID) + + call mpas_dmpar_sum_real (dminfo, diagnosticUpperSurfaceLocal, diagnosticUpperSurface) + call mpas_dmpar_sum_real (dminfo, diagnosticThicknessLocal, diagnosticThickness) + call mpas_dmpar_sum_real (dminfo, diagnosticBedTopographyLocal, diagnosticBedTopography) + call mpas_dmpar_sum_real (dminfo, diagnosticSfcMassBalLocal, diagnosticSfcMassBal) + call mpas_dmpar_sum_real (dminfo, diagnosticSurfaceTemperatureLocal, diagnosticSurfaceTemperature) + call mpas_dmpar_sum_real (dminfo, diagnosticBasalTemperatureLocal, diagnosticBasalTemperature) + + allocate (workLevel(nVertInterfaces)) + call mpas_dmpar_sum_real_array(dminfo, nVertInterfaces, diagnosticSpeed, workLevel) + diagnosticSpeed(:) = workLevel(:) + deallocate(workLevel) + + allocate (workLevel(nVertLevels)) + call mpas_dmpar_sum_real_array(dminfo, nVertLevels, diagnosticTemperature, workLevel) + diagnosticTemperature(:) = workLevel(:) + deallocate(workLevel) + + ! Write global and local stats to the log file + + if (dminfo % my_proc_id == IO_NODE) then + call mpas_log_write(' ') + call mpas_log_write('------------------------------------------------------------') + call mpas_log_write(' ') + write(msg, '(a25,a20)') 'Global statistics: time =', trim(xtime) + call mpas_log_write(msg) + write(msg, '(a25,i8)') ' timestep =', itimestep + call mpas_log_write(msg) + call mpas_log_write(' ') + write(msg, '(a32,e24.16)') 'Total ice area (km^2) ', & + globalIceAreaSum*1.0e-6_RKIND ! convert from m^2 to km^2 + call mpas_log_write(msg) + write(msg,'(a32,e24.16)') 'Total ice volume (km^3) ', & + globalIceVolumeSum*1.0e-9_RKIND ! convert from m^3 to km^3 + call mpas_log_write(msg) + write(msg,'(a32,e24.16)') 'Total ice energy (J) ', & + globalIceEnergySum + call mpas_log_write(msg) + write(msg,'(a32,f24.16,i8)') 'Max thickness (m), cell ', & + globalThicknessMax, thicknessMaxlocCell + call mpas_log_write(msg) + write(msg,'(a32,f24.16,i8)') 'Min thickness (m), cell ', & + globalThicknessMin, thicknessMinlocCell + call mpas_log_write(msg) + write(msg,'(a32,f24.16)') 'Mean thickness (m) ', & + globalthicknessMean + call mpas_log_write(msg) + write(msg,'(a32,f24.16,i8,i4)') 'Max temperature (C), cell, level', & + globalTemperatureMax - kelvin_to_celsius, temperatureMaxlocCell, temperatureMaxlocLevel + call mpas_log_write(msg) + write(msg,'(a32,f24.16,i8,i4)') 'Min temperature (C), cell, level', & + globalTemperatureMin - kelvin_to_celsius , temperatureMinlocCell, temperatureMinlocLevel + call mpas_log_write(msg) + write(msg,'(a32,f24.16)') 'Mean temperature (C) ', & + globalTemperatureMean - kelvin_to_celsius + call mpas_log_write(msg) + write(msg,'(a32,f24.16,i8,i4)') 'Max velocity (m/yr), edge, level', & + globalVelocityMax * scyr, velocityMaxlocEdge, velocityMaxlocLevel + call mpas_log_write(msg) + write(msg,'(a32,f24.16,i8,i4)') 'Max basal velo (m/yr), edge ', & + globalBasalVelocityMax * scyr, basalVelocityMaxlocEdge + call mpas_log_write(msg) + call mpas_log_write(' ') + write(msg,'(a30,i6)') 'Column diagnostics: cell ID = ', statsCellID + call mpas_log_write(msg) + write(msg,'(a30,3i6)') 'Local cell ID, block, proc = ', diagnosticCell, diagnosticBlockID, diagnosticProcID + call mpas_log_write(msg) + call mpas_log_write(' ') + write(msg,'(a25,f24.16)') 'Upper surface (m) ', diagnosticUpperSurface + call mpas_log_write(msg) + write(msg,'(a25,f24.16)') 'Thickness (m) ', diagnosticThickness + call mpas_log_write(msg) + write(msg,'(a25,f24.16)') 'Bed topography (m) ', diagnosticBedTopography + call mpas_log_write(msg) + write(msg,'(a25,f24.16)') 'Sfc mass balance (m/yr) ', diagnosticSfcMassBal + call mpas_log_write(msg) + call mpas_log_write(' ') + write(msg,'(a55)') 'Sigma Ice speed (m/yr) Ice temperature (C)' + call mpas_log_write(msg) + write(msg,'(f6.4, a25, f24.16)') 0.0_RKIND, '------', diagnosticSurfaceTemperature - kelvin_to_celsius + call mpas_log_write(msg) + do kLevel = 1, nVertLevels + write(msg,'(f6.4, f25.16, f24.16)') & + layerCenterSigma(kLevel), diagnosticSpeed(kLevel), diagnosticTemperature(kLevel) - kelvin_to_celsius + call mpas_log_write(msg) + end do + write(msg,'(f6.4, a25, f24.16)') 1.0_RKIND, '------', diagnosticBasalTemperature - kelvin_to_celsius + call mpas_log_write(msg) + call mpas_log_write(' ') + endif ! my_proc_id = IO_NODE + + ! clean up + deallocate(diagnosticTemperature) + deallocate(diagnosticSpeed) + + end subroutine li_compute_statistics + +!*********************************************************************** +! +! routine li_compute_field_local_stats +! +!> \brief Computes statistics for a field on a single block +!> \author William Lipscomb +!> \date 22 January 2015 +!> \details +!> This routine computes statistics (sum, max/min, vertical sum max/min) +!> for a real array on a single block. +! +!----------------------------------------------------------------------- + + subroutine li_compute_field_local_stats(dminfo, & + nVertLevels, nElements, & + field, mask, & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax, & + localMinlocElement, localMinlocLevel, & + localMaxlocElement, localMaxlocLevel, & + localVertSumMinlocElement, & + localVertSumMaxlocElement) + + ! Compute field statistics without area or volume weighting + + implicit none + + ! Input/output arguments + type (dm_info), intent(in) :: dminfo + integer, intent(in) :: nVertLevels, nElements + + real (kind=RKIND), dimension(nVertLevels, nElements), intent(in) :: & + field ! input field for which statistics are computed + + integer, dimension(nElements), intent(in) :: & + mask ! = 0 or 1; compute stats only over region where mask = 1 + + real (kind=RKIND), intent(out) :: localSum, localMin, localMax + real (kind=RKIND), intent(out) :: localVertSumMin, localVertSumMax + + integer, intent(out), optional :: localMinlocElement, localMinlocLevel + integer, intent(out), optional :: localMaxlocElement, localMaxlocLevel + integer, intent(out), optional :: localVertSumMinlocElement + integer, intent(out), optional :: localVertSumMaxlocElement + + ! Local variables + integer :: i, k + + localSum = 0.0_RKIND + do i = 1, nElements + localSum = localSum + real(mask(i),RKIND) * sum(field(:,i)) + end do + + if (present(localMinlocElement) .and. present(localMinlocLevel)) then + localMin = 1.0e34_RKIND + localMinlocElement = 0 + localMinlocLevel = 0 + do i = 1, nElements + do k = 1, nVertLevels + if (field(k,i) < localMin) then + localMin = field(k,i) + localMinlocElement = i + localMinlocLevel = k + endif + enddo + enddo + else + localMin = minval(field) + endif + + if (present(localMaxlocElement) .and. present(localMaxlocLevel)) then + localMax = -1.0e34_RKIND + localMaxlocElement = 0 + localMaxlocLevel = 0 + do i = 1, nElements + do k = 1, nVertLevels + if (field(k,i) > localMax) then + localMax = field(k,i) + localMaxlocElement = i + localMaxlocLevel = k + endif + enddo + enddo + else + localMax = maxval(field) + endif + + if (present(localVertSumMinlocElement)) then + localVertSumMin = 1.0e34_RKIND + localVertSumMinlocElement = 0 + do i = 1, nElements + if (sum(field(:,i)) < localVertSumMin) then + localVertSumMin = sum(field(:,i)) + localVertSumMinlocElement = i + endif + enddo + else + localVertSumMin = minval(sum(field,1)) + endif + + if (present(localVertSumMaxlocElement)) then + localVertSumMax = -1.0e34_RKIND + localVertSumMaxlocElement = 0 + do i = 1, nElements + if (sum(field(:,i)) > localVertSumMax) then + localVertSumMax = sum(field(:,i)) + localVertSumMaxlocElement = i + endif + enddo + else + localVertSumMax = maxval(sum(field,1)) + endif + + end subroutine li_compute_field_local_stats + +!*********************************************************************** +! +! routine li_compute_field_area_weighted_local_stats +! +!> \brief Computes area-weighted statistics for a field on a single block +!> \author William Lipscomb +!> \date 22 January 2015 +!> \details +!> This routine computes statistics (sum, max/min, vertical sum max/min) +!> for a real array on a single block. The sum is weighted by the input +!> field 'areas' (typically the grid cell area). +! +!----------------------------------------------------------------------- + + subroutine li_compute_field_area_weighted_local_stats(dminfo, & + nVertLevels, nElements, & + areas, & + field, mask, & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax) + + ! Compute field statistics weighted by area + + implicit none + + ! Input/output arguments + type (dm_info), intent(in) :: dminfo + integer, intent(in) :: nVertLevels, nElements + + real (kind=RKIND), dimension(nElements), intent(in) :: & + areas ! grid cell areas + + real (kind=RKIND), dimension(nVertLevels, nElements), intent(in) :: & + field ! input field for which statistics are computed + + integer, dimension(nElements), intent(in) :: & + mask ! = 0 or 1; compute stats only over region where mask = 1 + + real (kind=RKIND), intent(out) :: localSum, localMin, localMax + real (kind=RKIND), intent(out) :: localVertSumMin, localVertSumMax + + ! Local variables + integer :: i + + localSum = 0.0_RKIND + do i = 1, nElements + localSum = localSum + real(mask(i),RKIND) * areas(i) * sum(field(:,i)) + end do + + localMin = minval(field) + localMax = maxval(field) + + localVertSumMin = minval(sum(field,1)) + localVertSumMax = maxval(sum(field,1)) + + end subroutine li_compute_field_area_weighted_local_stats + +!*********************************************************************** +! +! routine li_compute_field_volume_weighted_local_stats +! +!> \brief Computes volume-weighted statistics for a field on a single block +!> \author William Lipscomb +!> \date 22 January 2015 +!> \details +!> This routine computes statistics (sum, max/min, vertical sum max/min) +!> for a real array on a single block. The sum is weighted by the product +!> of the input fields 'areas' (typically the grid cell area) and 'layerThickness'. +! +!----------------------------------------------------------------------- + + subroutine li_compute_field_volume_weighted_local_stats(dminfo, & + nVertLevels, nElements, & + areas, layerThickness, & + field, mask, & + localSum, & + localMin, localMax, & + localVertSumMin, localVertSumMax) + + implicit none + + ! Input/output arguments + type (dm_info), intent(in) :: dminfo + integer, intent(in) :: nVertLevels, nElements + + real (kind=RKIND), dimension(nElements), intent(in) :: & + areas ! element areas + + real (kind=RKIND), dimension(nVertLevels, nElements), intent(in) :: & + layerThickness ! ice thickness in each layer + + real (kind=RKIND), dimension(nVertLevels, nElements), intent(in) :: & + field ! input field for which statistics are computed + + integer, dimension(nElements), intent(in) :: & + mask ! = 0 or 1; compute stats only over region where mask = 1 + + real (kind=RKIND), intent(out) :: localSum, localMin, localMax + real (kind=RKIND), intent(out) :: localVertSumMin, localVertSumMax + + ! Local variables + integer :: i + + localSum = 0.0_RKIND + do i = 1, nElements + localSum = localSum + real(mask(i),RKIND) * areas(i) * sum(layerThickness(:,i)*field(:,i)) + end do + + localMin = minval(field) + localMax = maxval(field) + + localVertSumMin = minval(sum(layerThickness*field,1)) + localVertSumMax = maxval(sum(layerThickness*field,1)) + + end subroutine li_compute_field_volume_weighted_local_stats + +! The remaining code is from an older module by Matt Hoffman. +! Keeping it here for reference. +!===================================================================================== + +! ! 6. Write out your global stat to the file +! if (dminfo % my_proc_id == IO_NODE) then +! fileID = land_ice_get_free_unit() +! +! if (config_write_initial_stats .and. (timeIndex == 0)) then +! open(fileID, file='GlobalIntegrals.txt',STATUS='unknown') +! elseif ( .not.(config_write_initial_stats) .and. (timeIndex/config_stats_interval == 1) ) then +! open(fileID, file='GlobalIntegrals.txt',STATUS='unknown') +! else +! open(fileID, file='GlobalIntegrals.txt',POSITION='append') +! endif +!! write(fileID,'(1i0, 100es24.16)') timeIndex, timeIndex*dt, globalFluidThickness, & +!! globalPotentialVorticity, globalPotentialEnstrophy, & +!! globalEnergy, globalCoriolisEnergyTendency, globalKineticEnergyTendency+globalPotentialEnergyTendency, & +!! globalKineticEnergy, globalPotentialEnergy +! +! endif + +! integer function land_ice_get_free_unit() +! implicit none + +! integer :: index +! logical :: isOpened + +! land_ice_get_free_unit = 0 +! do index = 1,99 +! if((index /= 5) .and. (index /= 6)) then +! inquire(unit = index, opened = isOpened) +! if( .not. isOpened) then +! land_ice_get_free_unit = index +! return +! end if +! end if +! end do +! end function land_ice_get_free_unit + + end module li_statistics diff --git a/src/core_landice/mode_forward/mpas_li_subglacial_hydro.F b/src/core_landice/mode_forward/mpas_li_subglacial_hydro.F new file mode 100644 index 0000000000..0bb10c0312 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_subglacial_hydro.F @@ -0,0 +1,1757 @@ +! Copyright (c) 2016-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_SGH_driver +! +!> \brief MPAS land ice SGH primary routines +!> \author Matt Hoffman +!> \date 27 June 2016 +!> \details +!> This module contains the main driver routines for +!> for subglacial hydro. +! +!----------------------------------------------------------------------- + +module li_subglacial_hydro + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_timer + use mpas_log + + use li_setup + use li_constants + use li_mask + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_SGH_init, & + li_SGH_solve, & + li_SGH_finalize + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + contains + + +!*********************************************************************** +! +! routine li_SGH_init +! +!> \brief Initialize SGH +!> \author Matt Hoffman +!> \date 27 June 2016 +!> \details +!> This routine initializes the subglacial hydro model +!----------------------------------------------------------------------- + subroutine li_SGH_init(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + logical, pointer :: config_SGH + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool + real (kind=RKIND), pointer :: deltatSGH + real (kind=RKIND), dimension(:), pointer :: waterThickness + real (kind=RKIND), dimension(:), pointer :: tillWaterThickness + real (kind=RKIND), dimension(:), pointer :: waterPressure + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: bedTopography + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), pointer :: tillMax + real (kind=RKIND), pointer :: rhoi, rhoo + real (kind=RKIND), pointer :: config_sea_level + integer, pointer :: config_num_halos + integer :: err_tmp + + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_SGH', config_SGH) + if (.not. config_SGH) then + ! If SGH is not active, skip everything + return + endif + + call mpas_timer_start("hydro init") + + call mpas_log_write('Beginning subglacial hydro init.') + + ! Check number of halos + call mpas_pool_get_config(liConfigs, 'config_num_halos', config_num_halos) + if (config_num_halos < 1) then + call mpas_log_write("Subglacial hydrology requires config_num_halos >= 1", MPAS_LOG_ERR) + err = ior(err, 1) + endif + + call mpas_pool_get_config(liConfigs, 'config_SGH_till_max', tillMax) + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhoo) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + + call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) + + ! Until init is done properly, make this tiny. It will be updated at the end of the first subcycle. + ! TODO: Set time step appropriately on first subcycle of init + deltatSGH = 1.0e-4_RKIND ! in seconds + + ! remove invalid values - not necessary on restart, but shouldn't hurt + call mpas_pool_get_array(hydroPool, 'waterThickness', waterThickness) + waterThickness = max(0.0_RKIND, waterThickness) + + call mpas_pool_get_array(hydroPool, 'tillWaterThickness', tillWaterThickness) + tillWaterThickness = max(0.0_RKIND, tillWaterThickness) + tillWaterThickness = min(tillMax, tillWaterThickness) + + call mpas_pool_get_array(hydroPool, 'waterPressure', waterPressure) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + waterPressure = max(0.0_RKIND, waterPressure) + waterPressure = min(waterPressure, rhoi * gravity * thickness) + ! set pressure correctly under floating ice and open ocean + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + where ( (li_mask_is_floating_ice(cellMask)) .or. & + ( (.not. li_mask_is_ice(cellMask)) .and. (bedTopography < config_sea_level) ) ) + waterPressure = rhoo * gravity * (config_sea_level - bedTopography) + end where + + ! Mask needs to be initialized for pressure calcs to be correct + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + ! Initialize diagnostic pressure variables + call calc_pressure_diag_vars(block, err_tmp) + err = ior(err, err_tmp) + + block => block % next + end do + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_SGH_init.", MPAS_LOG_ERR) + endif + + call mpas_timer_stop("hydro init") + + !-------------------------------------------------------------------- + end subroutine li_SGH_init + + + +!*********************************************************************** +! +! routine li_SGH_solve +! +!> \brief Solve and update SGH for current time step +!> \author Matt Hoffman +!> \date 27 June 2016 +!> \details +!> This routine solves and updates the subglacial hydro model +!> for the current ISM time step. +!----------------------------------------------------------------------- + subroutine li_SGH_solve(domain, err) + use mpas_vector_reconstruction + use li_diagnostic_vars + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + logical, pointer :: config_SGH + logical, pointer :: config_SGH_chnl_active + character (len=StrKIND), pointer :: config_SGH_basal_melt + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: velocityPool + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:,:), pointer :: temperature, flowParamA + real (kind=RKIND), dimension(:), pointer :: Wtill, WtillOld + real (kind=RKIND), dimension(:), pointer :: basalMeltInput + real (kind=RKIND), dimension(:), pointer :: groundedBasalMassBal + real (kind=RKIND), dimension(:), pointer :: basalHeatFlux + real (kind=RKIND), dimension(:), pointer :: basalFrictionFlux + real (kind=RKIND), dimension(:), pointer :: externalWaterInput + real (kind=RKIND), dimension(:), pointer :: divergence + real (kind=RKIND), dimension(:), pointer :: waterFlux + real (kind=RKIND), dimension(:), pointer :: waterThickness + real (kind=RKIND), dimension(:), pointer :: waterThicknessOld + real (kind=RKIND), dimension(:), pointer :: waterThicknessTendency + real (kind=RKIND), dimension(:), pointer :: divergenceChannel + real (kind=RKIND), dimension(:), pointer :: channelAreaChangeCell + real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:), pointer :: waterVelocity + real (kind=RKIND), dimension(:), pointer :: waterVelocityCellX + real (kind=RKIND), dimension(:), pointer :: waterVelocityCellY + real (kind=RKIND), dimension(:), allocatable :: cellJunk + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell + integer, dimension(:,:), pointer :: cellsOnEdge + integer, dimension(:,:), pointer :: edgeSignOnCell + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), pointer :: deltatSGH + real (kind=RKIND), pointer :: masterDeltat + real (kind=RKIND), pointer :: Cd + real (kind=RKIND), pointer :: tillMax + integer, pointer :: nCellsSolve + integer, pointer :: nCells + integer :: iCell, iEdge, iEdgeOnCell + real (kind=RKIND) :: timeLeft ! subcycling time remaining until master dt is reached + integer :: numSubCycles ! number of subcycles + integer :: err_tmp + + + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_SGH', config_SGH) + if (.not. config_SGH) then + ! If SGH is not active, skip everything + return + endif + + call mpas_log_write('Beginning subglacial hydro solve.') + call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_active', config_SGH_chnl_active) + call mpas_pool_get_config(liConfigs, 'config_SGH_till_drainage', Cd) + call mpas_pool_get_config(liConfigs, 'config_SGH_till_max', tillMax) + call mpas_pool_get_config(liConfigs, 'config_SGH_basal_melt', config_SGH_basal_melt) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(velocityPool, 'flowParamA', flowParamA) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + call li_calculate_flowParamA(meshPool, temperature, thickness, flowParamA, err_tmp) + err = ior(err, err_tmp) + + block => block % next + end do + + ! initialize while loop + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) ! can get from any block + call mpas_pool_get_array(meshPool, 'deltat', masterDeltat) + timeLeft = masterDeltat ! in seconds + numSubCycles = 0 + ! ============= + ! ============= + ! ============= + ! subcycle hydro model until master dt is reached + ! ============= + ! ============= + ! ============= + do while (timeLeft > 0.0_RKIND) + numSubCycles = numSubCycles + 1 + + + ! ============= + ! Calculate time-varying forcing, as needed + ! ============= + block => domain % blocklist + do while (associated(block)) + + ! Decide where the basal melt input comes from + ! Either prescribed, from the temperature model, or calculated here + if (trim(config_SGH_basal_melt) == 'file') then + ! do nothing + elseif (trim(config_SGH_basal_melt) == 'thermal') then + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_array(hydroPool, 'basalMeltInput', basalMeltInput) + call mpas_pool_get_array(geometryPool,'groundedBasalMassBal',groundedBasalMassBal) + basalMeltInput = -1.0 * groundedBasalMassBal ! TODO: Ensure positive flux? + elseif (trim(config_SGH_basal_melt) == 'basal_heat') then + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_array(hydroPool, 'basalMeltInput', basalMeltInput) + call mpas_pool_get_array(thermalPool, 'basalHeatFlux', basalHeatFlux) + call mpas_pool_get_array(thermalPool, 'basalFrictionFlux', basalFrictionFlux) + basalMeltInput = (basalHeatFlux + basalFrictionFlux) / latent_heat_ice + else + call mpas_log_write("Unknown value provided for config_SGH_basal_melt: " // trim(config_SGH_basal_melt), MPAS_LOG_ERR) + err = ior(err, 1) + endif + + ! SHMIP forcing will override basalMeltInput. + call shmip_timevarying_forcing(block, err_tmp) + err = ior(err, err_tmp) + + block => block % next + end do + + ! Perform halo update, if needed + if ((trim(config_SGH_basal_melt) == 'thermal') .or. (trim(config_SGH_basal_melt) == 'basal_heat')) then + call mpas_timer_start("halo updates") + ! (groundedBasalMassBal will not have had a halo update, + ! because a halo update for it is unneeded except for the SGH model.) + call mpas_dmpar_field_halo_exch(domain, 'basalMeltInput') + call mpas_timer_stop("halo updates") + endif + + + ! ============= + ! Update till water layer thickness + ! ============= + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + + call mpas_pool_get_array(hydroPool, 'tillWaterThickness', Wtill) + call mpas_pool_get_array(hydroPool, 'tillWaterThicknessOld', WtillOld) + call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) + call mpas_pool_get_array(hydroPool, 'basalMeltInput', basalMeltInput) + call mpas_pool_get_array(hydroPool, 'externalWaterInput', externalWaterInput) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + WtillOld = Wtill + Wtill = Wtill + deltatSGH * ( (basalMeltInput + externalWaterInput) / rho_water - Cd) + Wtill = Wtill * li_mask_is_grounded_ice_int(cellMask) ! zero Wtill in non-grounded locations + Wtill = min(Wtill, tillmax) + Wtill = max(0.0_RKIND, Wtill) + + block => block % next + end do + + + ! ============= + ! Calculate edge quantities and advective fluxes + ! ============= + block => domain % blocklist + do while (associated(block)) + + call calc_edge_quantities(block, err_tmp) + err = ior(err, err_tmp) + + block => block % next + end do + ! Update halos on edge quantities + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'waterFlux') + ! intermediate fields will be out of date, but will be correct in output files + ! waterVelocity needs to be updated in order to get waterVelocityCellX/Y correct + call mpas_dmpar_field_halo_exch(domain, 'waterVelocity') + call mpas_timer_stop("halo updates") + + ! Calculate reconstructed velocities on cell centers for viz + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(hydroPool, 'waterVelocity', waterVelocity) + call mpas_pool_get_array(hydroPool, 'waterVelocityCellX', waterVelocityCellX) + call mpas_pool_get_array(hydroPool, 'waterVelocityCellY', waterVelocityCellY) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + allocate(cellJunk(nCells+1)) + call mpas_reconstruct(meshPool, waterVelocity, waterVelocityCellX, waterVelocityCellY, & + cellJunk, cellJunk, cellJunk) + deallocate(cellJunk) + waterVelocityCellX = waterVelocityCellX * li_mask_is_grounded_ice_int(cellMask) ! zero in non-grounded locations + waterVelocityCellY = waterVelocityCellY * li_mask_is_grounded_ice_int(cellMask) ! zero in non-grounded locations + + block => block % next + end do + + + ! ============= + ! Update Channel fields + ! ============= + if (config_SGH_chnl_active) then + block => domain % blocklist + do while (associated(block)) + + call update_channel(block, err_tmp) + err = ior(err, err_tmp) + + block => block % next + end do + ! Update halos on channel + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'channelChangeRate') + call mpas_dmpar_field_halo_exch(domain, 'channelDischarge') + call mpas_timer_stop("halo updates") + endif + + + ! ============= + ! Calculate adaptive time step + ! ============= + call check_timestep(domain, timeLeft, err_tmp) + err = ior(err, err_tmp) + + + ! ============= + ! Compute flux divergence + ! ============= + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(hydroPool, 'divergence', divergence) + call mpas_pool_get_array(hydroPool, 'waterFlux', waterFlux) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + divergence(:) = 0.0_RKIND ! zero div before starting + ! loop over locally owned cells + do iCell = 1, nCellsSolve + if (li_mask_is_grounded_ice(cellMask(iCell))) then ! can skip for non-grounded ice + ! compute fluxes for each edge of the cell + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iEdgeOnCell, iCell) + ! add on advective & diffusive fluxes + divergence(iCell) = divergence(iCell) - waterFlux(iEdge) * dvEdge(iEdge) * edgeSignOnCell(iEdgeOnCell, iCell) + end do ! edges + end if + end do ! cells + divergence(1:nCellsSolve) = divergence(1:nCellsSolve) / areaCell(1:nCellsSolve) + + block => block % next + end do + ! Update halos on divergence + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'divergence') + call mpas_timer_stop("halo updates") + + + ! ============= + ! Update channel area now that we have time step + ! ============= + if (config_SGH_chnl_active) then + block => domain % blocklist + do while (associated(block)) + + call evolve_channel(block, err_tmp) + err = ior(err, err_tmp) + + block => block % next + end do + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'divergenceChannel') + call mpas_dmpar_field_halo_exch(domain, 'channelAreaChangeCell') + call mpas_dmpar_field_halo_exch(domain, 'channelArea') + call mpas_timer_stop("halo updates") + endif + + + ! ============= + ! Calculate pressure field + ! ============= + block => domain % blocklist + do while (associated(block)) + + call calc_pressure(block, err_tmp) + err = ior(err, err_tmp) + + block => block % next + end do + + + ! ============= + ! Update water layer thickness + ! ============= + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_array(hydroPool, 'waterThickness', waterThickness) + call mpas_pool_get_array(hydroPool, 'waterThicknessOld', waterThicknessOld) + call mpas_pool_get_array(hydroPool, 'waterThicknessTendency', waterThicknessTendency) + call mpas_pool_get_array(hydroPool, 'tillWaterThickness', Wtill) + call mpas_pool_get_array(hydroPool, 'tillWaterThicknessOld', WtillOld) + call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) + call mpas_pool_get_array(hydroPool, 'basalMeltInput', basalMeltInput) + call mpas_pool_get_array(hydroPool, 'externalWaterInput', externalWaterInput) + call mpas_pool_get_array(hydroPool, 'divergence', divergence) + call mpas_pool_get_array(hydroPool, 'divergenceChannel', divergenceChannel) + call mpas_pool_get_array(hydroPool, 'channelAreaChangeCell', channelAreaChangeCell) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + waterThicknessOld = waterThickness + waterThickness = waterThicknessOld + deltatSGH * ( (basalMeltInput + externalWaterInput) / rho_water - divergence & + - divergenceChannel - channelAreaChangeCell & + - (Wtill - WtillOld) / deltatSGH) + waterThickness = waterThickness * li_mask_is_grounded_ice_int(cellMask) ! zero in non-grounded locations + waterThickness = max(0.0_RKIND, waterThickness) + divergence = divergence * li_mask_is_grounded_ice_int(cellMask) ! zero in non-grounded locations for more convenient viz + waterThicknessTendency = (waterThickness - waterThicknessOld) / deltatSGH + + block => block % next + end do + + + ! ============= + ! ============= + ! ============= + end do ! while timeLeft>0 + ! ============= + ! ============= + ! ============= + + call mpas_log_write("Hydro model subcycled $i times.", intArgs=(/numSubCycles/)) + + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_SGH_solve.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_SGH_solve + + + +!*********************************************************************** +! +! routine li_SGH_finalize +! +!> \brief Finalize SGH +!> \author Matt Hoffman +!> \date 27 June 2016 +!> \details +!> This routine finalizes the subglacial hydro model +!----------------------------------------------------------------------- + subroutine li_SGH_finalize(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + logical, pointer :: config_SGH + type (block_type), pointer :: block + integer :: err_tmp + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_SGH', config_SGH) + if (.not. config_SGH) then + return + endif + + block => domain % blocklist + do while (associated(block)) + + block => block % next + end do + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_SGH_finalize.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_SGH_finalize + + + + !-------------------------------------------------------------------- + !-------------------------------------------------------------------- + ! Local routines + !-------------------------------------------------------------------- + !-------------------------------------------------------------------- + + + +!*********************************************************************** +! +! routine calc_edge_quantities +! +!> \brief Calculate SGH fields on edges +!> \author Matt Hoffman +!> \date 27 June 2016 +!> \details +!> This routine calculates needed SGH fields on edges +!----------------------------------------------------------------------- + subroutine calc_edge_quantities(block, err) + + use mpas_geometry_utils, only: mpas_cells_to_points_using_baryweights + use mpas_vector_operations, only: mpas_tangential_vector_1d + use li_setup, only: li_cells_to_vertices_1dfield_using_kiteAreas + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), intent(inout) :: block !< Input/Output: block object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: hydroPool + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: hydropotentialBase + real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseVertex + real (kind=RKIND), dimension(:), pointer :: waterPressure + real (kind=RKIND), dimension(:), pointer :: waterThicknessEdge + real (kind=RKIND), dimension(:), pointer :: waterThicknessEdgeUpwind + real (kind=RKIND), dimension(:), pointer :: waterThickness + real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseSlopeNormal + real (kind=RKIND), dimension(:), pointer :: waterPressureSlopeNormal + real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseSlopeTangent + real (kind=RKIND), dimension(:), pointer :: gradMagPhiEdge + real (kind=RKIND), dimension(:), pointer :: effectiveConducEdge + real (kind=RKIND), dimension(:), pointer :: diffusivity + real (kind=RKIND), dimension(:), pointer :: dcEdge + real (kind=RKIND), dimension(:), pointer :: dvEdge + real (kind=RKIND), dimension(:), pointer :: waterVelocity + real (kind=RKIND), dimension(:), pointer :: waterFlux + real (kind=RKIND), dimension(:), pointer :: waterFluxAdvec + real (kind=RKIND), dimension(:), pointer :: waterFluxDiffu + integer, dimension(:), pointer :: waterFluxMask + integer, dimension(:,:), pointer :: edgeSignOnCell + integer, dimension(:), pointer :: cellMask + integer, dimension(:), pointer :: edgeMask + integer, dimension(:,:), pointer :: cellsOnEdge + integer, dimension(:,:), pointer :: verticesOnEdge + integer, dimension(:,:), pointer :: baryCellsOnVertex + real (kind=RKIND), dimension(:,:), pointer :: baryWeightsOnVertex + real (kind=RKIND), pointer :: alpha, beta + real (kind=RKIND), pointer :: conduc_coeff + character (len=StrKIND), pointer :: config_SGH_tangent_slope_calculation + integer, pointer :: nEdges + integer, pointer :: nCells + integer, pointer :: nVertices + integer :: iEdge, cell1, cell2 + real (kind=RKIND) :: velSign + integer :: numGroundedCells + integer :: err_tmp + + err = 0 + err_tmp = 0 + + + ! Get pools things + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + + call mpas_pool_get_config(liConfigs, 'config_SGH_alpha', alpha) + call mpas_pool_get_config(liConfigs, 'config_SGH_beta', beta) + call mpas_pool_get_config(liConfigs, 'config_SGH_conduc_coeff', conduc_coeff) + call mpas_pool_get_config(liConfigs, 'config_SGH_tangent_slope_calculation', config_SGH_tangent_slope_calculation) + + call mpas_pool_get_array(hydroPool, 'waterThickness', waterThickness) + call mpas_pool_get_array(hydroPool, 'waterPressure', waterPressure) + call mpas_pool_get_array(hydroPool, 'hydropotentialBase', hydropotentialBase) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(hydroPool, 'waterThicknessEdge', waterThicknessEdge) + call mpas_pool_get_array(hydroPool, 'waterThicknessEdgeUpwind', waterThicknessEdgeUpwind) + call mpas_pool_get_array(hydroPool, 'hydropotentialBaseSlopeNormal', hydropotentialBaseSlopeNormal) + call mpas_pool_get_array(hydroPool, 'waterPressureSlopeNormal', waterPressureSlopeNormal) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + call mpas_pool_get_array(hydroPool, 'hydropotentialBaseSlopeTangent', hydropotentialBaseSlopeTangent) + call mpas_pool_get_array(hydroPool, 'gradMagPhiEdge', gradMagPhiEdge) + call mpas_pool_get_array(hydroPool, 'effectiveConducEdge', effectiveConducEdge) + call mpas_pool_get_array(hydroPool, 'diffusivity', diffusivity) + call mpas_pool_get_array(hydroPool, 'waterVelocity', waterVelocity) + call mpas_pool_get_array(hydroPool, 'waterFlux', waterFlux) + call mpas_pool_get_array(hydroPool, 'waterFluxAdvec', waterFluxAdvec) + call mpas_pool_get_array(hydroPool, 'waterFluxDiffu', waterFluxDiffu) + call mpas_pool_get_array(hydroPool, 'waterFluxMask', waterFluxMask) + + + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + + !waterThicknessEdge(iEdge) = 0.5_RKIND * ( waterThickness(cell1) + waterThickness(cell2) ) + ! This version ignores the thickness where there is no grounded ice (one-sided average at margin) + numGroundedCells = li_mask_is_grounded_ice_int(cellMask(cell1)) + li_mask_is_grounded_ice_int(cellMask(cell2)) + if (numGroundedCells > 0) then + ! Assuming here that waterThickness has been zeroed in non-grounded locations + waterThicknessEdge(iEdge) = ( waterThickness(cell1) + waterThickness(cell2) ) / real(numGroundedCells) + else + waterThicknessEdge(iEdge) = 0.0_RKIND + endif + + hydropotentialBaseSlopeNormal(iEdge) = (hydropotentialBase(cell2) - hydropotentialBase(cell1)) / dcEdge(iEdge) + waterPressureSlopeNormal(iEdge) = (waterPressure(cell2) - waterPressure(cell1)) / dcEdge(iEdge) + end do + + + ! Calculate tangent slope of hydropotentialBase - three possible methods to consider + + ! Calculate hydropotentialBaseVertex if needed + call mpas_pool_get_array(hydroPool, 'hydropotentialBaseVertex', hydropotentialBaseVertex) ! this array could be protected by logic if desired + select case (trim(config_SGH_tangent_slope_calculation)) + case ('from_vertex_barycentric') + call mpas_pool_get_array(meshPool, 'baryCellsOnVertex', baryCellsOnVertex) + call mpas_pool_get_array(meshPool, 'baryWeightsOnVertex', baryWeightsOnVertex) + call mpas_cells_to_points_using_baryweights(meshPool, baryCellsOnVertex(:, 1:nVertices), & + baryWeightsOnVertex(:, 1:nVertices), hydropotentialBase, hydropotentialBaseVertex(1:nVertices), err_tmp) + err = ior(err, err_tmp) + case ('from_vertex_barycentric_kiteareas') + call li_cells_to_vertices_1dfield_using_kiteAreas(meshPool, hydropotentialBase, hydropotentialBaseVertex) + end select + + ! Now perform tangent slope calculation based on method chosen + select case (trim(config_SGH_tangent_slope_calculation)) + case ('from_vertex_barycentric', 'from_vertex_barycentric_kiteareas') + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) + do iEdge = 1, nEdges + ! Only calculate slope for edges that have ice on at least one side. + if ( li_mask_is_ice(edgeMask(iEdge)) ) then + hydropotentialBaseSlopeTangent(iEdge) = ( hydropotentialBaseVertex(verticesOnEdge(1,iEdge)) - & + hydropotentialBaseVertex(verticesOnEdge(2,iEdge)) ) / dvEdge(iEdge) + else + hydropotentialBaseSlopeTangent(iEdge) = 0.0_RKIND + endif + end do ! edges + case ('from_normal_slope') + call mpas_tangential_vector_1d(hydropotentialBaseSlopeNormal, meshPool, & + includeHalo=.false., tangentialVector=hydropotentialBaseSlopeTangent) + case default + call mpas_log_write('Invalid value for config_SGH_tangent_slope_calculation.', MPAS_LOG_ERR) + err = 1 + end select + + ! calculate magnitude of gradient of Phi + gradMagPhiEdge = sqrt(hydropotentialBaseSlopeNormal**2 + hydropotentialBaseSlopeTangent**2) + + ! calculate effective conductivity on edges + ! OLD: USE REGULARIZATION: effectiveConducEdge(:) = conduc_coeff * waterThicknessEdge(:)**(alpha-1.0_RKIND) * (gradMagPhiEdge(:)+1.0e-10_RKIND)**(beta - 2.0_RKIND) ! 1e-10 used for regularization + ! Do not calculate the conductivity where it is tiny to avoid blowups + where(gradMagPhiEdge < 0.01_RKIND) + effectiveConducEdge(:) = 0.0_RKIND + elsewhere + effectiveConducEdge(:) = conduc_coeff * waterThicknessEdge(:)**(alpha-1.0_RKIND) * gradMagPhiEdge(:)**(beta - 2.0_RKIND) + end where + + ! calculate diffusivity on edges + diffusivity(:) = rho_water * gravity * effectiveConducEdge(:) * waterThicknessEdge(:) + + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + waterVelocity(iEdge) = -1.0_RKIND * effectiveConducEdge(iEdge) * hydropotentialBaseSlopeNormal(iEdge) + velSign = sign(1.0_RKIND, waterVelocity(iEdge)) + waterThicknessEdgeUpwind(iEdge) = max(velSign * waterThickness(cell1), & + velSign * (-1.0_RKIND) * waterThickness(cell2)) + + ! advective flux + waterFluxAdvec(iEdge) = waterVelocity(iEdge) * waterThicknessEdgeUpwind(iEdge) + + ! diffusive flux + numGroundedCells = li_mask_is_grounded_ice_int(cellMask(cell1)) + li_mask_is_grounded_ice_int(cellMask(cell2)) + if (numGroundedCells < 2) then + waterFluxDiffu(iEdge) = 0.0_RKIND + else + waterFluxDiffu(iEdge) = -1.0_RKIND * diffusivity(iEdge) * (waterThickness(cell2) - waterThickness(cell1)) & + / dcEdge(iEdge) + endif + end do + where (waterFluxMask == 2) + waterFluxAdvec = 0.0_RKIND + waterFluxDiffu = 0.0_RKIND + waterVelocity = 0.0_RKIND + end where + waterFlux(:) = waterFluxAdvec(:) + waterFluxDiffu(:) + + !-------------------------------------------------------------------- + end subroutine calc_edge_quantities + + + + +!*********************************************************************** +! +! routine check_timestep +! +!> \brief Calculate SGH timesteps and check current timestep +!> \author Matt Hoffman +!> \date 7 July 2016 +!> \details +!> This routine calculates the three timesteps associated with the +!> SGH solver and compares them to the current model timestep. +!----------------------------------------------------------------------- + subroutine check_timestep(domain, timeLeft, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + real (kind=RKIND), intent(inout) :: timeLeft !< Input/Output: time remaining for subcycling (seconds) + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: hydroPool + real (kind=RKIND), dimension(:), pointer :: waterVelocity + real (kind=RKIND), dimension(:), pointer :: channelVelocity + real (kind=RKIND), dimension(:), pointer :: diffusivity + real (kind=RKIND), dimension(:), pointer :: channelDiffusivity + real (kind=RKIND), dimension(:), pointer :: dcEdge + real (kind=RKIND), pointer :: deltatSGH + real (kind=RKIND), pointer :: deltatSGHadvec + real (kind=RKIND), pointer :: deltatSGHdiffu + real (kind=RKIND), pointer :: deltatSGHpressure + real (kind=RKIND), pointer :: deltatSGHadvecChannel + real (kind=RKIND), pointer :: deltatSGHdiffuChannel + real (kind=RKIND), pointer :: porosity + type (block_type), pointer :: block + real (kind=RKIND), pointer :: deltat + integer, pointer :: nEdgesSolve + logical, pointer :: config_SGH_chnl_active + ! in the following variables, "Proc" indicates the value on the current processor, + ! and "Block" indicates value on current block + real (kind=RKIND) :: dtSGHadvecBlock, dtSGHadvecProc + real (kind=RKIND) :: dtSGHdiffuBlock, dtSGHdiffuProc + real (kind=RKIND) :: dtSGHpressureBlock, dtSGHpressureProc + real (kind=RKIND) :: dtSGHadvecChanBlock, dtSGHadvecChanProc + real (kind=RKIND) :: dtSGHdiffuChanBlock, dtSGHdiffuChanProc + integer :: err_tmp + real(kind=RKIND), parameter :: bigNumber = 1.0e36_RKIND + real(kind=RKIND), pointer :: CFLfraction + real(kind=RKIND), pointer :: maxDt + real(kind=RKIND) :: proposedDt + real(kind=RKIND) :: masterDt + real(kind=RKIND), dimension(5) :: localMinValues, reducedMinValues + + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_SGH_englacial_porosity', porosity) + call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_active', config_SGH_chnl_active) + + dtSGHadvecProc = bigNumber + dtSGHdiffuProc = bigNumber + dtSGHpressureProc = bigNumber + dtSGHadvecChanProc = bigNumber + dtSGHdiffuChanProc = bigNumber + + ! --- + ! Find local (block) limiting dt for various CFL conditions + ! --- + block => domain % blocklist + do while (associated(block)) + + ! Get pools things + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) + call mpas_pool_get_array(hydroPool, 'deltatSGHadvec', deltatSGHadvec) + call mpas_pool_get_array(hydroPool, 'deltatSGHdiffu', deltatSGHdiffu) + call mpas_pool_get_array(hydroPool, 'deltatSGHpressure', deltatSGHpressure) + call mpas_pool_get_array(hydroPool, 'deltatSGHadvecChannel', deltatSGHadvecChannel) + call mpas_pool_get_array(hydroPool, 'deltatSGHdiffuChannel', deltatSGHdiffuChannel) + call mpas_pool_get_array(hydroPool, 'waterVelocity', waterVelocity) + call mpas_pool_get_array(hydroPool, 'channelVelocity', channelVelocity) + call mpas_pool_get_array(hydroPool, 'diffusivity', diffusivity) + call mpas_pool_get_array(hydroPool, 'channelDiffusivity', channelDiffusivity) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + ! Calculate advective CFL-limited time step + dtSGHadvecBlock = 0.5_RKIND * minval(dcEdge(1:nEdgesSolve) / (abs(waterVelocity(1:nEdgesSolve)) & + + 1.0e-12_RKIND)) ! regularize + dtSGHadvecProc = min(dtSGHadvecProc, dtSGHadvecBlock) + + ! Calculate diffusive CFL-limited time step + dtSGHdiffuBlock = 0.25_RKIND * minval(dcEdge(1:nEdgesSolve)**2 / (diffusivity(1:nEdgesSolve) + 1.0e-12_RKIND)) + dtSGHdiffuProc = min(dtSGHdiffuProc, dtSGHdiffuBlock) + + ! Calculate pressure limited time step + dtSGHpressureBlock = 1.0_RKIND * minval(porosity * dcEdge(1:nEdgesSolve)**2 & + / (2.0_RKIND * diffusivity(1:nEdgesSolve) + 1.0e-12_RKIND)) + dtSGHpressureProc = min(dtSGHpressureProc, dtSGHpressureBlock) + + if (config_SGH_chnl_active) then + ! Calculate channel advection limited time step + dtSGHadvecChanBlock = 0.5_RKIND * minval(dcEdge(1:nEdgesSolve) / (abs(channelVelocity(1:nEdgesSolve)) + 1.0e-12_RKIND)) ! regularize + dtSGHadvecChanProc = min(dtSGHadvecChanProc, dtSGHadvecChanBlock) + ! Calculate channel diffusion limited time step + dtSGHdiffuChanBlock = 0.25_RKIND * minval(dcEdge(1:nEdgesSolve)**2 / (channelDiffusivity(1:nEdgesSolve) + 1.0e-12_RKIND)) + dtSGHdiffuChanProc = min(dtSGHdiffuChanProc, dtSGHdiffuChanBlock) + endif + + ! Master deltat is needed below, so grab it in this block loop + call mpas_pool_get_array(meshPool, 'deltat', deltat) + + block => block % next + end do + + + ! --- + ! reduce across procs + ! --- + localMinValues(1) = dtSGHadvecProc + localMinValues(2) = dtSGHdiffuProc + localMinValues(3) = dtSGHpressureProc + if (config_SGH_chnl_active) then + localMinValues(4) = dtSGHadvecChanProc + localMinValues(5) = dtSGHdiffuChanProc + call mpas_timer_start("global reduce") + call mpas_dmpar_min_real_array(domain % dminfo, 5, localMinValues, reducedMinValues) + call mpas_timer_stop("global reduce") + deltatSGHadvecChannel = reducedMinValues(4) + deltatSGHdiffuChannel = reducedMinValues(5) + else + call mpas_timer_start("global reduce") + call mpas_dmpar_min_real_array(domain % dminfo, 3, localMinValues(1:3), reducedMinValues(1:3)) + call mpas_timer_stop("global reduce") + endif + deltatSGHadvec = reducedMinValues(1) + deltatSGHdiffu = reducedMinValues(2) + deltatSGHpressure = reducedMinValues(3) + + + ! --- + ! Find and set the new hydro subcycling dt + ! --- + call mpas_pool_get_config(liConfigs, 'config_SGH_adaptive_timestep_fraction', CFLfraction) + call mpas_pool_get_config(liConfigs, 'config_SGH_max_adaptive_timestep', maxDt) + + ! Find smallest of 3 or 5 limiting time steps + proposedDt = min(deltatSGHadvec, deltatSGHdiffu, deltatSGHpressure) + if (config_SGH_chnl_active) then + proposedDt = min(proposedDt, deltatSGHadvecChannel, deltatSGHdiffuChannel) + endif + proposedDt = proposedDt * CFLfraction + + + ! Write out dt info on the final subcycle + if (proposedDt >= timeLeft) then + if (config_SGH_chnl_active) then + call mpas_log_write("deltatSGH: used=$r, advec=$r, diffu=$r, pressure=$r, advecChannel=$r, diffuChannel=$r", & + realArgs=(/deltatSGH, deltatSGHadvec, deltatSGHdiffu, deltatSGHpressure, deltatSGHadvecChannel, deltatSGHdiffuChannel/)) + else + call mpas_log_write("deltatSGH: used=$r, advec=$r, diffu=$r, pressure=$r", & + realArgs=(/deltatSGH, deltatSGHadvec, deltatSGHdiffu, deltatSGHpressure/)) + endif + endif + + + ! Don't exceed the maximum allowed hydro time step + proposedDt = min(proposedDt, maxDt) + ! Don't let SGH time step exceed time left in the master model dt + proposedDt = min(proposedDt, timeLeft) + + !call mpas_log_write(' Setting SGH time step to: $r seconds', realArgs=(/proposedDt/)) + + timeLeft = timeLeft - proposedDt + !call mpas_log_write("dt=$r, new TIMELEFT=$r", realArgs=(/proposedDt, timeLeft/)) + + + ! --- + ! Assign new time step to all blocks + ! --- + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) + deltatSGH = proposedDt + + block => block % next + end do + + + ! compare timesteps + if (deltatSGH > deltatSGHadvec) then + call mpas_log_write("deltatSGH > deltatSGHadvec $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHadvec/)) + endif + if (deltatSGH > deltatSGHdiffu) then + call mpas_log_write("deltatSGH > deltatSGHdiffu $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHdiffu/)) + endif + if (deltatSGH > deltatSGHpressure) then + call mpas_log_write("deltatSGH > deltatSGHpressure $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHpressure/)) + endif + if (config_SGH_chnl_active .and. (deltatSGH > deltatSGHadvecChannel)) then + call mpas_log_write("deltatSGH > deltatSGHadvecChannel $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHadvecChannel/)) + endif + if (config_SGH_chnl_active .and. (deltatSGH > deltatSGHdiffuChannel)) then + call mpas_log_write("deltatSGH > deltatSGHdiffuChannel $r > $r", MPAS_LOG_WARN, realArgs=(/deltatSGH, deltatSGHdiffuChannel/)) + endif + + !-------------------------------------------------------------------- + end subroutine check_timestep + + + + +!*********************************************************************** +! +! routine calc_pressure +! +!> \brief Calculate SGH water pressure +!> \author Matt Hoffman +!> \date 5 July 2016 +!> \details +!> This routine calculates SGH water pressure +!----------------------------------------------------------------------- + subroutine calc_pressure(block, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), intent(inout) :: block !< Input/Output: block object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: velocityPool + real (kind=RKIND), dimension(:), pointer :: waterPressure + real (kind=RKIND), dimension(:), pointer :: waterPressureOld + real (kind=RKIND), dimension(:), pointer :: waterPressureTendency + real (kind=RKIND), dimension(:), pointer :: waterThickness + real (kind=RKIND), dimension(:), pointer :: effectivePressure + real (kind=RKIND), dimension(:), pointer :: zeroOrderSum + real (kind=RKIND), dimension(:), pointer :: closingRate + real (kind=RKIND), dimension(:), pointer :: openingRate + real (kind=RKIND), dimension(:), pointer :: basalMeltInput + real (kind=RKIND), dimension(:), pointer :: externalWaterInput + real (kind=RKIND), dimension(:), pointer :: Wtill, WtillOld + real (kind=RKIND), dimension(:), pointer :: divergence + real (kind=RKIND), dimension(:), pointer :: basalSpeed + real (kind=RKIND), dimension(:,:), pointer :: flowParamA + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: divergenceChannel + real (kind=RKIND), dimension(:), pointer :: channelAreaChangeCell + real (kind=RKIND), dimension(:), pointer :: bedTopography + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), pointer :: deltatSGH + real (kind=RKIND), pointer :: bedRough, bedRoughMax + real (kind=RKIND), pointer :: rhoi + real (kind=RKIND), pointer :: creepCoeff + real (kind=RKIND), pointer :: porosity + integer, pointer :: nVertLevels + character (len=StrKIND), pointer :: config_SGH_pressure_calc + real (kind=RKIND), pointer :: config_sea_level + real (kind=RKIND), pointer :: rhoo + integer :: err_tmp + + err = 0 + err_tmp = 0 + + + ! Get pools things + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhoo) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_SGH_bed_roughness', bedRough) + call mpas_pool_get_config(liConfigs, 'config_SGH_bed_roughness_max', bedRoughMax) + call mpas_pool_get_config(liConfigs, 'config_SGH_creep_coefficient', creepCoeff) + call mpas_pool_get_config(liConfigs, 'config_SGH_englacial_porosity', porosity) + call mpas_pool_get_config(liConfigs, 'config_SGH_pressure_calc', config_SGH_pressure_calc) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) + call mpas_pool_get_array(hydroPool, 'waterPressure', waterPressure) + call mpas_pool_get_array(hydroPool, 'waterPressureOld', waterPressureOld) + call mpas_pool_get_array(hydroPool, 'waterPressureTendency', waterPressureTendency) + call mpas_pool_get_array(hydroPool, 'waterThickness', waterThickness) + call mpas_pool_get_array(hydroPool, 'zeroOrderSum', zeroOrderSum) + call mpas_pool_get_array(hydroPool, 'closingRate', closingRate) + call mpas_pool_get_array(hydroPool, 'openingRate', openingRate) + call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) + call mpas_pool_get_array(hydroPool, 'basalMeltInput', basalMeltInput) + call mpas_pool_get_array(hydroPool, 'externalWaterInput', externalWaterInput) + call mpas_pool_get_array(hydroPool, 'tillWaterThickness', Wtill) + call mpas_pool_get_array(hydroPool, 'tillWaterThicknessOld', WtillOld) + call mpas_pool_get_array(hydroPool, 'divergence', divergence) + call mpas_pool_get_array(hydroPool, 'divergenceChannel', divergenceChannel) + call mpas_pool_get_array(hydroPool, 'channelAreaChangeCell', channelAreaChangeCell) + call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) + call mpas_pool_get_array(velocityPool, 'flowParamA', flowParamA) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + + openingRate(:) = bedRough * basalSpeed(:) * (bedRoughMax - waterThickness(:)) + !openingRate(:) = bedRough * basalSpeed(:) * (bedRoughMax - waterThickness(:)) + & + ! basalMeltInput(:) / rhoi ! Hewitt 2011 opening + openingRate = max(0.0_RKIND, openingRate) + + closingRate(:) = creepCoeff * flowParamA(nVertLevels, :) * effectivePressure(:)**3 * waterThickness(:) +! closingRate(:) = waterThickness(:) * effectivePressure(:) / 1.0e13_RKIND +! ! Hewitt 2011 creep closure form. Denominator is ice viscosity + + zeroOrderSum = closingRate - openingRate + (basalMeltInput + externalWaterInput) / rho_water - & + (Wtill - WtillOld) / deltatSGH + + waterPressureOld = waterPressure + + select case (trim(config_SGH_pressure_calc)) + case ('cavity') + + where (li_mask_is_floating_ice(cellMask)) + waterPressure = rhoi * gravity * thickness + elsewhere (.not. li_mask_is_ice(cellMask)) + waterPressure = 0.0_RKIND + elsewhere + waterPressure = (zeroOrderSum - divergence - divergenceChannel - channelAreaChangeCell) * rho_water * gravity * deltatSGH / porosity + waterPressureOld + end where + + case ('overburden') + where (li_mask_is_floating_ice(cellMask)) + waterPressure = rhoi * gravity * thickness + elsewhere (.not. li_mask_is_ice(cellMask)) + waterPressure = 0.0_RKIND + elsewhere + waterPressure = rhoi * gravity * thickness + end where + + case default + call mpas_log_write("Invalid option specified for config_SGH_pressure_calc:" // config_SGH_pressure_calc, MPAS_LOG_ERR) + err = ior(err, 1) + end select + + waterPressure = max(0.0_RKIND, waterPressure) + waterPressure = min(waterPressure, rhoi * gravity * thickness) + ! set pressure correctly under floating ice and open ocean + where ( (li_mask_is_floating_ice(cellMask)) .or. & + ((.not. li_mask_is_ice(cellMask)) .and. (bedTopography < config_sea_level) ) ) + waterPressure = rhoo * gravity * (config_sea_level - bedTopography) + end where + + waterPressureTendency = (waterPressure - waterPressureOld) / deltatSGH + + call calc_pressure_diag_vars(block, err_tmp) + err = ior(err, err_tmp) + + !-------------------------------------------------------------------- + end subroutine calc_pressure + + +!*********************************************************************** +! +! routine calc_pressure_diag_vars +! +!> \brief Calculate SGH diagnostic variables related to pressure +!> \author Matt Hoffman +!> \date 5 July 2016 +!> \details +!> This routine calculates variables related to water pressure +!----------------------------------------------------------------------- + subroutine calc_pressure_diag_vars(block, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), intent(inout) :: block !< Input/Output: block object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: hydroPool + real (kind=RKIND), pointer :: rhoi, rhoo + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: waterPressure + real (kind=RKIND), dimension(:), pointer :: bedTopography + real (kind=RKIND), dimension(:), pointer :: hydropotentialBase + real (kind=RKIND), dimension(:), pointer :: waterThickness + real (kind=RKIND), dimension(:), pointer :: hydropotential + real (kind=RKIND), dimension(:), pointer :: effectivePressure + integer, dimension(:), pointer :: cellMask + real (kind=RKIND), pointer :: config_sea_level + + err = 0 + + ! Get pools things + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', rhoo) + + call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(hydroPool, 'waterPressure', waterPressure) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(hydroPool, 'hydropotentialBase', hydropotentialBase) + call mpas_pool_get_array(hydroPool, 'waterThickness', waterThickness) + call mpas_pool_get_array(hydroPool, 'hydropotential', hydropotential) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + effectivePressure = rhoi * gravity * thickness - waterPressure ! this should evalute to 0 for floating ice if Pw set correctly there. + where (.not. li_mask_is_ice(cellmask)) + effectivePressure = 0.0_RKIND ! zero effective pressure where no ice to avoid confusion + end where + + hydropotentialBase = rho_water * gravity * bedTopography + waterPressure + ! This is still correct under ice shelves/open ocean because waterPressure has been set appropriately there already. + ! Note this leads to a nonuniform hydropotential at sea level that is a function of the ocean depth. + ! That is what we want because we use this as a boundary condition on the subglacial system, + ! and we want the subglacial system to feel the pressure of the ocean column at its edge. + + ! hydropotential with water thickness + hydropotential = hydropotentialBase + rho_water * gravity * waterThickness + + !-------------------------------------------------------------------- + end subroutine calc_pressure_diag_vars + + + +!*********************************************************************** +! +! routine update_channel +! +!> \brief Calculate SGH channel area +!> \author Matt Hoffman +!> \date 28 July 2016 +!> \details +!> This routine updates the channel area in the subglacial hydrology model. +!> It uses the conduit space evolution equation. +!----------------------------------------------------------------------- + subroutine update_channel(block, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), intent(inout) :: block !< Input/Output: block object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers +!! type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: geometryPool + real (kind=RKIND), pointer :: Kc + real (kind=RKIND), pointer :: alpha_c + real (kind=RKIND), pointer :: beta_c + real (kind=RKIND), pointer :: creep_coeff + real (kind=RKIND), pointer :: rhoi + real (kind=RKIND), pointer :: config_SGH_incipient_channel_width + logical, pointer :: config_SGH_include_pressure_melt + + real (kind=RKIND), dimension(:), pointer :: channelArea + real (kind=RKIND), dimension(:), pointer :: channelMelt + real (kind=RKIND), dimension(:), pointer :: channelPressureFreeze + real (kind=RKIND), dimension(:), pointer :: channelDischarge + real (kind=RKIND), dimension(:), pointer :: channelVelocity + real (kind=RKIND), dimension(:), pointer :: gradMagPhiEdge + real (kind=RKIND), dimension(:), pointer :: waterFlux + real (kind=RKIND), dimension(:), pointer :: hydropotentialBaseSlopeNormal + real (kind=RKIND), dimension(:), pointer :: waterPressureSlopeNormal + real (kind=RKIND), dimension(:), pointer :: channelOpeningRate + real (kind=RKIND), dimension(:), pointer :: channelClosingRate + real (kind=RKIND), dimension(:), pointer :: channelChangeRate + real (kind=RKIND), dimension(:), pointer :: flowParamAChannel + real (kind=RKIND), dimension(:), pointer :: channelEffectivePressure + real (kind=RKIND), dimension(:), pointer :: effectivePressure + real (kind=RKIND), dimension(:), pointer :: channelDiffusivity + integer, dimension(:), pointer :: waterFluxMask + integer, dimension(:), pointer :: edgeMask + real (kind=RKIND), dimension(:,:), pointer :: flowParamA + integer, dimension(:,:), pointer :: cellsOnEdge + integer, pointer :: nVertLevels + + integer, pointer :: nEdgesSolve + integer :: iEdge, cell1, cell2 + + + err = 0 + + ! Get pools things + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) +! call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) + call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_conduc_coeff', Kc) + call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_alpha', alpha_c) + call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_beta', beta_c) + call mpas_pool_get_config(liConfigs, 'config_SGH_chnl_creep_coefficient', creep_coeff) + call mpas_pool_get_config(liConfigs, 'config_SGH_incipient_channel_width', config_SGH_incipient_channel_width) + call mpas_pool_get_config(liConfigs, 'config_SGH_include_pressure_melt', config_SGH_include_pressure_melt) + + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(hydroPool, 'channelArea', channelArea) + call mpas_pool_get_array(hydroPool, 'channelMelt', channelMelt) + call mpas_pool_get_array(hydroPool, 'channelPressureFreeze', channelPressureFreeze) + call mpas_pool_get_array(hydroPool, 'channelDischarge', channelDischarge) + call mpas_pool_get_array(hydroPool, 'channelVelocity', channelVelocity) + call mpas_pool_get_array(hydroPool, 'gradMagPhiEdge', gradMagPhiEdge) + call mpas_pool_get_array(hydroPool, 'hydropotentialBaseSlopeNormal', hydropotentialBaseSlopeNormal) + call mpas_pool_get_array(hydroPool, 'waterPressureSlopeNormal', waterPressureSlopeNormal) + call mpas_pool_get_array(hydroPool, 'waterFlux', waterFlux) + call mpas_pool_get_array(hydroPool, 'channelOpeningRate', channelOpeningRate) + call mpas_pool_get_array(hydroPool, 'channelClosingRate', channelClosingRate) + call mpas_pool_get_array(hydroPool, 'channelChangeRate', channelChangeRate) + call mpas_pool_get_array(hydroPool, 'flowParamAChannel', flowParamAChannel) + call mpas_pool_get_array(hydroPool, 'channelEffectivePressure', channelEffectivePressure) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(velocityPool, 'flowParamA', flowParamA) + call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) + call mpas_pool_get_array(hydroPool, 'waterFluxMask', waterFluxMask) + call mpas_pool_get_array(hydroPool, 'channelDiffusivity', channelDiffusivity) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + + ! Calculate terms needed for opening (melt) rate + + where(gradMagPhiEdge < 0.01_RKIND) + channelDischarge(:) = 0.0_RKIND + elsewhere + channelDischarge = -1.0_RKIND * Kc * channelArea**alpha_c * gradMagPhiEdge**(beta_c - 2.0_RKIND) * hydropotentialBaseSlopeNormal + end where + + where (waterFluxMask == 2) + channelDischarge = 0.0_RKIND + channelArea = 0.0_RKIND + end where + + ! Note: an edge with only one grounded cell neighbor is called floating, so this logic retains channel vars on those edges to allow channel discharge across GL + where (.not. ( (li_mask_is_grounded_ice(edgeMask)) .or. (li_mask_is_grounding_line(edgeMask)) ) ) + channelArea = 0.0_RKIND + channelDischarge = 0.0_RKIND + end where + + channelVelocity = channelDischarge / (channelArea + 1.0e-12_RKIND) + + ! diffusivity used only to limit channel dt right now + where(gradMagPhiEdge < 0.01_RKIND) + channelDiffusivity = 0.0_RKIND + elsewhere + channelDiffusivity = abs(rho_water * gravity * channelArea * & + Kc * channelArea**(alpha_c - 1.0_RKIND) * gradMagPhiEdge**(beta_c - 2.0_RKIND)) + end where + + channelMelt = (abs(channelDischarge * hydropotentialBaseSlopeNormal) & ! channel dissipation + + abs(waterFlux * hydropotentialBaseSlopeNormal * config_SGH_incipient_channel_width) & ! some sheet dissipation + ) / latent_heat_ice + channelPressureFreeze = -1.0_RKIND * iceMeltingPointPressureDependence * cp_freshwater * rho_water * & + (channelDischarge + waterFlux * config_SGH_incipient_channel_width) & + * waterPressureSlopeNormal / latent_heat_ice + + if (config_SGH_include_pressure_melt) then + channelOpeningRate = (channelMelt - channelPressureFreeze) / rhoi + else + channelOpeningRate = channelMelt / rhoi + endif + + ! Calculate terms needed for closing (creep) rate + ! Need cell center quantities on edges + do iEdge = 1, nEdgesSolve + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + + ! Not sure if these ought to be upwind average, but using centered + flowParamAChannel(iEdge) = 0.5_RKIND * ( flowParamA(nVertLevels, cell1) + flowParamA(nVertLevels, cell2) ) + channelEffectivePressure(iEdge) = 0.5_RKIND * (effectivePressure(cell1) + effectivePressure(cell2)) + end do + channelClosingRate(:) = creep_coeff * channelArea(:) * flowParamAChannel(:) * channelEffectivePressure(:)**3 + + where (waterFluxMask == 2) + channelOpeningRate = 0.0_RKIND + channelClosingRate = 0.0_RKIND + end where + channelChangeRate = channelOpeningRate - channelClosingRate + + !-------------------------------------------------------------------- + end subroutine update_channel + + +!*********************************************************************** +! +! routine evolve_channel +! +!> \brief Evolve SGH channel area, calculate changes on cells +!> \author Matt Hoffman +!> \date 28 July 2016 +!> \details +!> This routine updates the channel area in the subglacial hydrology model. +!> It uses the conduit space evolution equation. +!----------------------------------------------------------------------- + subroutine evolve_channel(block, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), intent(inout) :: block !< Input/Output: block object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: meshPool + real (kind=RKIND), dimension(:), pointer :: channelArea + real (kind=RKIND), dimension(:), pointer :: channelDischarge + real (kind=RKIND), dimension(:), pointer :: channelChangeRate + real (kind=RKIND), dimension(:), pointer :: divergenceChannel + real (kind=RKIND), dimension(:), pointer :: channelAreaChangeCell + real (kind=RKIND), pointer :: deltatSGH + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell + integer, dimension(:,:), pointer :: cellsOnEdge + integer, dimension(:,:), pointer :: edgeSignOnCell + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:), pointer :: dcEdge + integer, pointer :: nCellsSolve + integer :: iCell, iEdgeOnCell, iEdge + + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(hydroPool, 'deltatSGH', deltatSGH) + call mpas_pool_get_array(hydroPool, 'channelArea', channelArea) + call mpas_pool_get_array(hydroPool, 'channelDischarge', channelDischarge) + call mpas_pool_get_array(hydroPool, 'channelChangeRate', channelChangeRate) + call mpas_pool_get_array(hydroPool, 'divergenceChannel', divergenceChannel) + call mpas_pool_get_array(hydroPool, 'channelAreaChangeCell', channelAreaChangeCell) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + ! Calculate flux divergence on cells and channel area change on cells + divergenceChannel(:) = 0.0_RKIND ! zero div before accumulating + channelAreaChangeCell(:) = 0.0_RKIND ! zero before accumulating + ! loop over locally owned cells + do iCell = 1, nCellsSolve + ! TODO: could limit to grounded cells only + ! compute fluxes for each edge of the cell + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iEdgeOnCell, iCell) + ! add on advective & diffusive fluxes + divergenceChannel(iCell) = divergenceChannel(iCell) - channelDischarge(iEdge) * edgeSignOnCell(iEdgeOnCell, iCell) + channelAreaChangeCell(iCell) = channelChangeRate(iEdge) * dcEdge(iEdge) * 0.5_RKIND ! only half of channel is in this cell + end do ! edges + end do ! cells + divergenceChannel(1:nCellsSolve) = divergenceChannel(1:nCellsSolve) / areaCell(1:nCellsSolve) + channelAreaChangeCell(1:nCellsSolve) = channelAreaChangeCell(1:nCellsSolve) / areaCell(1:nCellsSolve) + + ! Now update channel area + channelArea = channelChangeRate * deltatSGH + channelArea + ! If sheet dissipation contributes to channel, there should be no need for a minimum channel size + channelArea = max(1.0e-8_RKIND, channelArea) ! make some tiny value when it goes negative + + !-------------------------------------------------------------------- + end subroutine evolve_channel + + +!*********************************************************************** +! +! routine shmip_timevarying_forcing +! +!> \brief Calculate time-varying forcings for SHMIP experiments +!> \author Matt Hoffman +!> \date 18 January 2017 +!> \details +!> This routine calculates time-varying forcings for the Subglacial +!> Hydrology Model Intercomparison Project's Experiments C and D. +!> I chose to include these in model source code rather than through +!> a forcing module because 1) this allows the SHMIP analytic forcing +!> functions to be reproduced exactly and 2) with the short hydro model +!> time steps, using a forcing i/o module would require a lot of file +!> reading during each run. +!----------------------------------------------------------------------- + subroutine shmip_timevarying_forcing(block, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), intent(inout) :: block !< Input/Output: block object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pools pointers + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: meshPool + character (len=StrKIND), pointer :: config_SGH_shmip_forcing + real(kind=RKIND), pointer :: daysSinceStart + real (kind=RKIND), dimension(:), pointer :: externalWaterInput + real (kind=RKIND), dimension(:), pointer :: basalMeltInput + real (kind=RKIND), dimension(:), pointer :: upperSurface + real (kind=RKIND), dimension(:), pointer :: areaCell + ! Test case specific variables + real(kind=RKIND) :: ra ! relative amplitude for versions of C experiment + real(kind=RKIND) :: temperature0 ! temperature at 0m elevation for D experiment + real(kind=RKIND) :: DT ! temperature offset for versions of D experiment + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_SGH_shmip_forcing', config_SGH_shmip_forcing) + + if (config_SGH_shmip_forcing=='none') then + ! Default is to do nothing and skip rest of routine + return + endif + + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'daysSinceStart', daysSinceStart) + call mpas_pool_get_array(hydroPool, 'externalWaterInput', externalWaterInput) + + select case (config_SGH_shmip_forcing(1:1)) ! Check on first character only + case ('C') + ! Note: Case C assumes that moulin locations are input via externalWaterInput + ! and have a very small value that will not affect the sum of + ! externalWaterInput+basalMeltInput. + ! The actual forcing is stored in basalMeltInput. Note this is the moulin + ! and the basal contributions combined, unlike in the other tests + ! where I keep those separate. + + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(hydroPool, 'basalMeltInput', basalMeltInput) + + select case (config_SGH_shmip_forcing(2:2)) ! get experiment number + case ('1') + ra = 0.25_RKIND + case ('2') + ra = 0.5_RKIND + case ('3') + ra = 1.0_RKIND + case ('4') + ra = 2.0_RKIND + case default + call mpas_log_write("Unknown value for config_SGH_shmip_forcing:" // config_SGH_shmip_forcing, MPAS_LOG_ERR) + err = ior(err, 1) + end select + + ! basalMeltInput calculated in SHMIP m/s units here + where (externalWaterInput > 0.0_RKIND) + ! 0.9/areaCell is the value being input into each moulin in B5 + basalMeltInput = 0.9_RKIND / areaCell * (1.0_RKIND - ra * sin(2.0_RKIND * pii * daysSinceStart)) + elsewhere + ! where the input file does not identify a moulin, set source term to 0 + basalMeltInput = 0.0_RKIND + end where + ! instructions say to zero negative values + where (basalMeltInput < 0.0_RKIND) + basalMeltInput = 0.0_RKIND + end where + ! Add in basal melting contribution specified by instructions AND convert from m/s to kg/m2/s + basalMeltInput = (basalMeltInput + 7.93e-11_RKIND) * 1000.0_RKIND + + case ('D') + select case (config_SGH_shmip_forcing(2:2)) ! get experiment number + case ('1') + DT = -4.0_RKIND + case ('2') + DT = -2.0_RKIND + case ('3') + DT = 0.0_RKIND + case ('4') + DT = 2.0_RKIND + case ('5') + DT = 4.0_RKIND + case default + call mpas_log_write("Unknown value for config_SGH_shmip_forcing:" // config_SGH_shmip_forcing, MPAS_LOG_ERR) + err = ior(err, 1) + end select + + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + temperature0 = -16.0_RKIND * cos(2.0_RKIND * pii / 365.0 * daysSinceStart) - 5.0_RKIND + DT + externalWaterInput(:) = (upperSurface(:) * (-0.0075_RKIND) + temperature0) * (0.01_RKIND / 86400.0_RKIND) * 1000.0_RKIND ! the 1000 factor converts from m/s to kg/m2/s + where (externalWaterInput < 0.0_RKIND) + externalWaterInput = 0.0_RKIND + end where + + case default + call mpas_log_write("Unknown value for config_SGH_shmip_forcing:" // config_SGH_shmip_forcing, MPAS_LOG_ERR) + err = ior(err, 1) + end select + + + !-------------------------------------------------------------------- + end subroutine shmip_timevarying_forcing + + +end module li_subglacial_hydro diff --git a/src/core_landice/mode_forward/mpas_li_thermal.F b/src/core_landice/mode_forward/mpas_li_thermal.F new file mode 100644 index 0000000000..a3774f1224 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_thermal.F @@ -0,0 +1,3316 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_thermal +! +!> \brief MPAS land ice vertical temperature/enthalpy solver +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This module contains solvers for the vertical temperature +!> and/or enthalpy profile. +! +!----------------------------------------------------------------------- + +module li_thermal + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timer + use mpas_abort + use mpas_log + + use li_setup + use li_mask + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_thermal_init, li_thermal_solver, & + li_init_linear_temperature_in_column, & + li_temperature_to_enthalpy, li_enthalpy_to_temperature, & + li_compute_pressure_melting_point_fields, & + li_basal_melt_floating_ice + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + real (kind=RKIND), save :: rhoi ! ice density (kg m^{-3}), copied from config_ice_density + real (kind=RKIND), save :: rhoo ! ocean density (kg m^{-3}), copied from config_ocean_density + + real (kind=RKIND), dimension(:,:), allocatable :: dsigmaTerm ! vertical grid quantities + + ! max and min allowed temperatures (Kelvin) + ! Note: kelvin_to_celsius = 273.15 (perhaps it should be called celsius_to_kelvin?) + + real (kind=RKIND), parameter :: & + maxtempThreshold = 100._RKIND + kelvin_to_celsius, & + mintempThreshold = -100._RKIND + kelvin_to_celsius + +!*********************************************************************** + contains +!*********************************************************************** + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_thermal_init +! +!> \brief MPAS land ice initialize vertical temperature +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine initializes the vertical temperature profile in each column +!> and computes some quantities required by the thermal solver. +!----------------------------------------------------------------------- + + subroutine li_thermal_init(domain, err) + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: thermalPool + + ! config options + + logical, pointer :: & + config_print_thermal_info, & + config_do_restart + + character(len=StrKIND), pointer :: & + config_thermal_solver, & ! solver option ('temperature' or 'enthalpy') + config_temperature_init, & ! temperature initialization option ('linear' or 'file') + config_surface_air_temperature_source, & ! surface air temperature initialization option ('constant' or 'file' or 'lapse') + config_basal_heat_flux_source ! basal heat flux initialization option ('constant' or 'file') + + real (kind=RKIND), pointer :: & + config_ice_density, & ! ice density + config_ocean_density ! ocean density + + real (kind=RKIND), pointer :: & + config_surface_air_temperature_value, & ! constant value of surface air temperature + config_surface_air_temperature_lapse_rate, & ! optional lapse rate to apply to constant value of surface air temperature + config_basal_heat_flux_value ! constant value of basal heat flux + + integer, pointer :: & + config_stats_cell_ID ! cell ID for diagnostic output + + integer, pointer :: & + nCellsSolve, & ! number of locally owned cells + nVertLevels ! number of vertical layers + + integer, dimension(:), pointer :: & + indexToCellID ! global ID for each local cell + + real (kind=RKIND), dimension(:), pointer :: & + layerCenterSigma, & ! sigma coordinate at midpoint of each layer + layerInterfaceSigma ! sigma coordinate at layer interfaces (including top and bottom) + + real (kind=RKIND), dimension(:), pointer :: & + thickness, & ! ice thickness + upperSurface ! ice upper surface + + real (kind=RKIND), dimension(:,:), pointer :: & + temperature, & ! interior ice temperature (K) + waterfrac, & ! interior water fraction (unitless) + enthalpy ! interior ice enthalpy (J m^{-3}) + + real (kind=RKIND), dimension(:), pointer :: & + surfaceAirTemperature, & ! surface air temperature (K) + surfaceTemperature, & ! surface ice temperature (K) + basalTemperature ! basal ice temperature (K) + + real (kind=RKIND), dimension(:), pointer :: & + basalHeatFlux ! basal heat flux into the ice (W m^{-2}, positive upward) + + integer :: k, iLayer + + integer :: iCell + + integer :: err_tmp + + real(kind=RKIND) :: temperatureValue + + real(kind=RKIND), dimension(:), allocatable :: pmpTemperatureCol + + !WHL - debug - for test-case diagnostics + logical, parameter :: circular_shelf_test = .false. + logical, parameter :: dome_test = .false. + + integer :: ncellsPerRow + integer :: nRows + integer :: i, iRow + real(kind=RKIND), parameter :: surfaceAirTemperatureCelsius = -15.0_RKIND + + + call mpas_timer_start("thermal init") + + if (circular_shelf_test) then + nCellsPerRow = 40 + nRows = 46 + elseif (dome_test) then + nCellsPerRow = 30 + nRows = 34 + endif + + err = 0 + + ! get config options + call mpas_pool_get_config(liConfigs, 'config_thermal_solver', config_thermal_solver) + call mpas_pool_get_config(liConfigs, 'config_temperature_init', config_temperature_init) + call mpas_pool_get_config(liConfigs, 'config_surface_air_temperature_source', config_surface_air_temperature_source) + call mpas_pool_get_config(liConfigs, 'config_surface_air_temperature_value', config_surface_air_temperature_value) + call mpas_pool_get_config(liConfigs, 'config_surface_air_temperature_lapse_rate', config_surface_air_temperature_lapse_rate) + call mpas_pool_get_config(liConfigs, 'config_basal_heat_flux_source', config_basal_heat_flux_source) + call mpas_pool_get_config(liConfigs, 'config_basal_heat_flux_value', config_basal_heat_flux_value) + call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) + call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + + ! set some physical constants + ! (to avoid calling mpas_pool_get_config repeatedly in this module) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) + rhoi = config_ice_density + rhoo = config_ocean_density + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + ! get arrays from the mesh pool + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + + ! get arrays from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + + ! get arrays from the thermal pool + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) + call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) + call mpas_pool_get_array(thermalPool, 'surfaceTemperature', surfaceTemperature) + call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) + call mpas_pool_get_array(thermalPool, 'basalHeatFlux', basalHeatFlux) + + if (config_print_thermal_info) then + call mpas_log_write('Initialize thermal solver, config_temperature_init = ' // trim(config_temperature_init)) + endif + + ! init for surfaceAirTemperature + !TODO - Allow for reading surfaceAirTemperature from an external file + if (trim(config_surface_air_temperature_source) == 'constant') then + surfaceAirTemperature(:) = config_surface_air_temperature_value + if (config_print_thermal_info) & + call mpas_log_write('Initialize sfc air temp: $r', realArgs=(/config_surface_air_temperature_value/)) + elseif (trim(config_surface_air_temperature_source) == 'lapse') then + surfaceAirTemperature(:) = config_surface_air_temperature_value - config_surface_air_temperature_lapse_rate * upperSurface(:) + if (config_print_thermal_info) & + call mpas_log_write('Initialize sfc air temp to $r with lapse rate of $r', & + realArgs=(/config_surface_air_temperature_value, config_surface_air_temperature_lapse_rate/)) + endif + + ! init for basalHeatFlux + !TODO - Allow for reading basalHeatFlux from an external file + if (trim(config_basal_heat_flux_source) == 'constant') then + basalHeatFlux(:) = config_basal_heat_flux_value + if (config_print_thermal_info) & + call mpas_log_write('Initialize basal heat flux: $r', realArgs=(/config_basal_heat_flux_value/)) + endif + + ! Precompute some grid quantities used in the vertical temperature solve + + allocate(dsigmaTerm(nVertLevels,2)) + dsigmaTerm(:,:) = 0.0_RKIND + + k = 1 + dsigmaTerm(k,1) = 1.0_RKIND/( (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) * & + (layerCenterSigma(k) - layerInterfaceSigma(k)) ) + + do k = 2, nVertLevels + dsigmaTerm(k,1) = 1.0_RKIND/( (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) * & + (layerCenterSigma(k) - layerCenterSigma(k-1)) ) + enddo + + do k = 1, nVertLevels-1 + dsigmaTerm(k,2) = 1.0_RKIND/( (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) * & + (layerCenterSigma(k+1) - layerCenterSigma(k)) ) + end do + + k = nVertLevels + dsigmaTerm(k,2) = 1.0_RKIND/( (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) * & + (layerInterfaceSigma(k+1) - layerCenterSigma(k)) ) + + if (config_print_thermal_info) then + call mpas_log_write('dsigmaTerm coefficients:') + do k = 1, nVertLevels + call mpas_log_write("$i $r $r", intArgs=(/k/), realArgs=(/dsigmaTerm(k,1), dsigmaTerm(k,2)/)) + enddo + endif + + !WHL - debug - Temporary initialization for test cases + + if (config_print_thermal_info .and. (circular_shelf_test .or. dome_test)) then + + call mpas_log_write(' ') + + if (circular_shelf_test) then + + call mpas_log_write('Circular shelf test:') + + elseif (dome_test) then + + call mpas_log_write('Dome test:') +! write(stdoutUnit,*) ' ' +! write(stdoutUnit,*) 'Cells with ice: iCell, thickness' +! do iCell = 1, nCellsSolve +! if (thickness(iCell) > 0.0_RKIND) then +! write(stdoutUnit,*) iCell, thickness(iCell) +! endif +! enddo + + endif + + !WHL - debug dome - Adjust thickness slightly so the central cell has the same thickness as in CISM +!! thickness(:) = thickness(:) * 707.1068115234375000_RKIND / 707.10678118654755_RKIND + +! call mpas_log_write(' ') +! call mpas_log_write('Initial thickness') +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stdoutUnit,'(a3)',advance='no') ' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stdoutUnit,'(f8.2)',advance='no') thickness(iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo +! +! write(stdoutUnit,*) ' ' +! write(stdoutUnit,*) 'Initial surfaceAirTemperature' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stdoutUnit,'(a3)',advance='no') ' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stdoutUnit,'(f8.2)',advance='no') surfaceAirTemperature(iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo + + endif ! circ shelf or dome + + + ! Initialize vertical temperature profile. + ! Three possibilities: + ! (1) Set up a linear temperature profile, with T = artm at the surface and T <= Tpmp + ! at the bed (config_temperature_init = 'linear'). + ! A parameter (pmpt_offset) controls how far below Tpmp the initial bed temp is set. + ! (2) Read ice temperature from an initial input file (config_temperature_init = 'file'). + ! (3) Read ice temperature from a restart file. + ! + ! The default is (1). + ! If restarting, we always do (3). + ! If (2) or (3), then the temperature should already have been read in, and there is + ! nothing to do here (except possibly to set waterfrac). + + if (config_do_restart) then + + ! nothing to do; temperature was read from the restart file + !TODO - Make sure waterfrac is also read, if needed + if (config_print_thermal_info) then + call mpas_log_write('Initialized ice temperature from the restart file') + endif + + elseif (trim(config_temperature_init) == 'file') then + + ! Temperature was read from the input file + if (config_print_thermal_info) then + call mpas_log_write('Initialized ice temperature from the input file') + endif + + ! initialize waterfrac, in case we are using the enthalpy solver + !TODO - Allow waterfrac to be read from the input file? + waterfrac(:,:) = 0.0_RKIND + + elseif (trim(config_temperature_init) == 'sfc_air_temperature') then + + allocate(pmpTemperatureCol(nVertLevels)) + do iCell = 1, nCellsSolve + ! set the surface temperature to the surface air temperature or 273.15, whichever is less + surfaceTemperature(iCell) = min(surfaceAirTemperature(iCell), kelvin_to_celsius) + + ! set the column temperature to surface air temp or PMP, whichever is less + call pressure_melting_point_column(layerCenterSigma, thickness(iCell), pmpTemperatureCol) + do k = 1, nVertLevels + temperature(k,iCell) = min(surfaceAirTemperature(iCell), pmpTemperatureCol(k) + kelvin_to_celsius) + enddo + + ! set the basal temperature to surface air temp or PMP, whichever is less + call pressure_melting_point(thickness(iCell), temperatureValue) + basalTemperature(iCell) = min(surfaceAirTemperature(iCell), temperatureValue + kelvin_to_celsius) + enddo + deallocate(pmpTemperatureCol) + + waterfrac(:,:) = 0.0_RKIND + + if (config_print_thermal_info) then + call mpas_log_write('Initialized ice column temperature to the surface air temperature') + endif + + elseif (trim(config_temperature_init) == 'linear') then + + ! set up a linear temperature profile in each column + ! T = surfaceAirTemperature at the ice surface, and T <= Tpmp at the bed + + ! initialize T = 273.15 K = 0 C everywhere + + temperature(:,:) = kelvin_to_celsius ! = 273.15 + + do iCell = 1, nCellsSolve + + call li_init_linear_temperature_in_column(& + nVertLevels, & + layerCenterSigma, & + thickness(iCell), & + surfaceAirTemperature(iCell), & + temperature(:,iCell), & + waterfrac(:,iCell), & + surfaceTemperature(iCell), & + basalTemperature(iCell)) + + enddo ! iCell + + if (config_print_thermal_info) then + call mpas_log_write('Initialized a linear temperature profile in each column') + + !WHL - debug +! if (circular_shelf_test .or. dome_test) then +! +! call mpas_log_write(' ') +! call mpas_log_write('Initial layer 1 temperature') +! iLayer = 1 +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stdoutUnit,'(a3)',advance='no') ' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stdoutUnit,'(f8.2)',advance='no') temperature(iLayer,iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo +! +! write(stdoutUnit,*) ' ' +! write(stdoutUnit,*) 'Initial basal temperature' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! call mpas_log_write(' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stdoutUnit,'(f8.2)',advance='no') basalTemperature(iCell) +! enddo +! write(stdoutUnit,*) ' ' +! enddo +! +! do iCell = 1, nCellsSolve +! if (indexToCellID(iCell) == config_stats_cell_ID) then +! write(stdoutUnit,*) ' ' +! write(stdoutUnit,*) 'Diagnostic cell =', iCell +! write(stdoutUnit,*) 'Thickness =', thickness(iCell) +! write(stdoutUnit,*) ' ' +! write(stdoutUnit,*) 'Temperature profile (C):' +! write(stdoutUnit,*) 'Sfc:', surfaceTemperature(iCell) - kelvin_to_celsius +! do iLayer = 1, nVertLevels +! write(stdoutUnit,*) iLayer, layerCenterSigma(iLayer), temperature(iLayer,iCell) - kelvin_to_celsius +! enddo +! write(stdoutUnit,*) 'Bed:', basalTemperature(iCell) - kelvin_to_celsius +! endif +! enddo +! +! endif ! shelf or dome test + + endif ! config_print_thermal_info + + endif ! restart file, input file, or linear + + block => block % next + enddo + + !TODO - Add a debug check for bad values + ! E.g., make sure the temperature read from a file is in Kelvin and not Celsius + + ! halo updates + call mpas_timer_start("halo updates") + + call mpas_dmpar_field_halo_exch(domain, 'surfaceTemperature') + call mpas_dmpar_field_halo_exch(domain, 'basalTemperature') + call mpas_dmpar_field_halo_exch(domain, 'temperature') + + if (trim(config_thermal_solver) == 'enthalpy') then + ! prognostic variables are temperature and waterfrac, so need to update waterfrac too + call mpas_dmpar_field_halo_exch(domain, 'waterfrac') + endif + + call mpas_timer_stop("halo updates") + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_thermal_init.", MPAS_LOG_ERR) + endif + + call mpas_timer_stop("thermal init") + + !-------------------------------------------------------------------- + end subroutine li_thermal_init + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! li_init_linear_temperature_in_column +! +!> \brief MPAS initialize a linear temperature profile in a column +!> \author William Lipscomb +!> \date February 2016 +!> \details +!> This routine initializes a linear temperature profile in an ice column, +!> with the surface ice temperature set to the surface air temperature, +!> and the basal ice temperature set to the pressure melting point minus +!> an offset. +!----------------------------------------------------------------------- + + subroutine li_init_linear_temperature_in_column(& + nVertLevels, & + layerCenterSigma, & + thickness, & + surfaceAirTemperature, & + temperature, & + waterfrac, & + surfaceTemperature, & + basalTemperature) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + integer, intent(in) :: & + nVertLevels !< Input: number of ice layers + + real(kind=RKIND), dimension(nVertLevels), intent(in) :: & + layerCenterSigma !< Input: vertical sigma coordinate at layer midpoints + + real(kind=RKIND), intent(in) :: & + thickness, & !< Input: ice thickness + surfaceAirTemperature !< Input: surface air temperature + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(nVertLevels), intent(out) :: & + temperature, & !< Output: interior ice temperature at midpoint of each layer + waterfrac !< Output: interior water fraction at midpoint of each layer + + real(kind=RKIND), intent(out) :: & + surfaceTemperature, & !< Output: surface ice temperature + basalTemperature !< Output: basal ice temperature + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(nVertLevels) :: & + pmptemp ! pressure melting point temp in ice interior + + real (kind=RKIND) :: & + pmptemp_bed ! pressure melting point temp at bed + + integer :: nVert + + !TODO - Make pmpt_offset a namelist parameter? + real (kind=RKIND), parameter :: & + pmpt_offset = 2.0_RKIND ! offset of initial Tbed from pressure melting point temperature (K) + ! Note: pmtp_offset is positive for T < Tpmp + + + ! set the surface ice temperature to the surface air temperature (or 273.15, whichever is less) + surfaceTemperature = min(surfaceAirTemperature, kelvin_to_celsius) + + ! compute the pressure melting point temperature in the column and at the bed + ! (in degrees C) + + call pressure_melting_point_column(& + layerCenterSigma, & + thickness, & + pmptemp) + + call pressure_melting_point(& + thickness, & + pmptemp_bed) + + ! convert to Kelvin + pmptemp(:) = pmptemp(:) + kelvin_to_celsius + pmptemp_bed = pmptemp_bed + kelvin_to_celsius + + ! set the basal temperature to slightly below the pressure melting point temperature + basalTemperature = pmptemp_bed - pmpt_offset + + ! set the interior temperatures + ! make sure T <= Tpmp - pmpt_offset in column interior + + temperature(:) = surfaceTemperature + & + (basalTemperature - surfaceTemperature) * layerCenterSigma(:) + + temperature(:) = min(temperature(:), pmptemp(:) - pmpt_offset) + + ! set waterfrac = 0 + waterfrac(:) = 0.0_RKIND + + end subroutine li_init_linear_temperature_in_column + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_thermal_solver +! +!> \brief MPAS land ice solver for vertical temperature/enthalpy +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine is the driver for the vertical temperature/enthalpy +!> calculation in each ice column. The following options are supported: +!> (1) Do nothing (config_thermal_solver = 'none') +!> (2) Standard prognostic temperature solve (config_thermal_solver = 'temperature') +!> (3) Prognostic solve for enthalpy (config_thermal_solver = 'enthalpy') + +!----------------------------------------------------------------------- + + subroutine li_thermal_solver(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: velocityPool ! needed for mask subroutine + type (mpas_pool_type), pointer :: scratchPool + + integer, pointer :: & + nCellsSolve, & ! number of locally owned cells + nVertLevels ! number of vertical layers + + logical, pointer :: & + config_print_thermal_info ! if true, print debug info + + character(len=StrKIND), pointer :: & + config_thermal_solver ! option for thermal solver + + character (len=StrKIND), pointer :: config_velocity_solver + + character(len=StrKIND), pointer :: & + config_surface_air_temperature_source ! surface air temperature initialization option + + real(kind=RKIND), pointer :: & + config_thermal_thickness, & ! minimum thickness (m) for temperature calculations + config_sea_level ! sea level (m) relative to z = 0 + + real (kind=RKIND), pointer :: & + config_surface_air_temperature_value, & ! constant value of surface air temperature + config_surface_air_temperature_lapse_rate ! optional lapse rate to apply to constant value of surface air temperature + + integer, pointer :: & + config_stats_cell_ID ! global ID for diagnostic cell + + integer, dimension(:), pointer :: & + cellMask, & ! bit mask describing whether ice is floating, dynamically active, etc. + indexToCellID ! list of global cell IDs + + type (field1dInteger), pointer :: thermalCellMaskField + + integer, dimension(:), pointer :: & + thermalCellMask ! mask for thermal calculations + ! = 1 where thickness > config_thermal_thickness, elsewhere = 0 + + real (kind=RKIND), pointer :: deltat !< time step in seconds + + real (kind=RKIND), dimension(:), pointer :: & + xCell ! x coordinate for each cell (m) + + real (kind=RKIND), dimension(:), pointer :: & + layerCenterSigma, & ! sigma coordinate at midpoint of each layer + layerInterfaceSigma ! sigma coordinate at layer interfaces (including top and bottom) + + real (kind=RKIND), dimension(:), pointer :: & + surfaceTemperature, & ! surface ice temperature (K) + basalTemperature, & ! basal ice temperature (K) + surfaceAirTemperature, & ! surface air temperature (K) + basalHeatFlux, & ! basal heat flux into the ice (W m^{-2}, positive upward) + basalFrictionFlux, & ! basal frictional flux into the ice (W m^{-2}) + surfaceConductiveFlux, & ! conductive heat flux at the upper surface (W m^{-2}, positive down) + basalConductiveFlux, & ! conductive heat flux at the lower surface (W m^{-2}, positive down) + groundedBasalMassBal, & ! basal mass balance for grounded ice + floatingBasalMassBal, & ! basal mass balance for floating ice + basalWaterThickness, & ! basal water thickness (m) + thickness, & ! ice thickness (m) + upperSurface ! ice upper surface (m) + + real (kind=RKIND), dimension(:,:), pointer :: & + temperature, & ! interior ice temperature (K) + waterfrac, & ! interior water fraction (unitless) + enthalpy, & ! interior ice enthalpy (J m^{-3}) + heatDissipation ! interior heat dissipation (deg/s) + + real(kind=RKIND), dimension(:), allocatable :: & + subdiagonal, diagonal, superdiagonal, & ! tridiagonal matrix elements + rhs ! matrix right-hand side + + real(kind=RKIND), dimension(:), allocatable :: & + diffusivity ! diffusivity at interfaces (m^2/s) for enthalpy solver + ! = iceConductivity / (rhoi*cp_ice) for cold ice + + real(kind=RKIND), dimension(:), allocatable :: & + solution ! solution of tridiagonal matrix problem + + real(kind=RKIND) :: & + surfaceEnthalpy, & ! surface ice enthalpy + basalEnthalpy, & ! basal ice enthalpy + depth, & ! depth within ice column + dTtop, dTbot, & ! temperature differences + denth_top, denth_bot, & ! enthalpy differences + columnHeatDissipation, & ! integrated heat dissipation in column + maxtemp, mintemp, & ! max and min temperatures in column + initialEnergy, & ! initial energy in ice column (J m^{-2}) + finalEnergy, & ! final energy in ice column (J m^{-2}) + deltaEnergy ! change in energy + + integer :: iCell, err_tmp + + logical :: verboseColumn + + integer :: k + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_thermal_solver', config_thermal_solver) + + !WHL - Commenting out these lines because we may want to compute basal melting for floating ice + ! even when config_thermal_solver = 'none' (as in the MISMIP+ experiments). + if (trim(config_thermal_solver) == 'none') then + return ! nothing to do + endif + + ! get rest of config variables + call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) + call mpas_pool_get_config(liConfigs, 'config_thermal_thickness', config_thermal_thickness) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + call mpas_pool_get_config(liConfigs, 'config_surface_air_temperature_value', config_surface_air_temperature_value) + call mpas_pool_get_config(liConfigs, 'config_surface_air_temperature_lapse_rate', config_surface_air_temperature_lapse_rate) + call mpas_pool_get_config(liConfigs, 'config_surface_air_temperature_source', config_surface_air_temperature_source) + + + if (config_print_thermal_info) then + call mpas_log_write('Solving for temperature, config_thermal_solver = ' // trim(config_thermal_solver)) + endif + + + ! check that deltat is valid + ! Note: With an adaptive time step, deltat is set to 0 at the start of the time step. + ! Here we make sure the thermal solver is not called while deltat = 0. + + ! Get dt - same on all blocks so just grab first one + block => domain % blocklist + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + + if (deltat <= 0.0_RKIND) then + call mpas_log_write('li_thermal_solver was called with invalid deltat = $r', MPAS_LOG_ERR, realArgs=(/deltat/)) + call mpas_log_write("An error has occurred in li_thermal. Aborting...", MPAS_LOG_CRIT) + endif + + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + ! get fields from the mesh pool + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) ! diagnostic only + + ! get fields from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'groundedBasalMassBal', groundedBasalMassBal) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + call mpas_pool_get_array(geometryPool, 'basalWaterThickness', basalWaterThickness) + + ! get fields from the thermal pool + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'waterfrac', waterfrac) + call mpas_pool_get_array(thermalPool, 'enthalpy', enthalpy) + call mpas_pool_get_array(thermalPool, 'surfaceTemperature', surfaceTemperature) + call mpas_pool_get_array(thermalPool, 'basalTemperature', basalTemperature) + call mpas_pool_get_array(thermalPool, 'surfaceAirTemperature', surfaceAirTemperature) + call mpas_pool_get_array(thermalPool, 'surfaceConductiveFlux', surfaceConductiveFlux) + call mpas_pool_get_array(thermalPool, 'basalConductiveFlux', basalConductiveFlux) + call mpas_pool_get_array(thermalPool, 'basalHeatFlux', basalHeatFlux) + call mpas_pool_get_array(thermalPool, 'basalFrictionFlux', basalFrictionFlux) + call mpas_pool_get_array(thermalPool, 'heatDissipation', heatDissipation) + + ! get fields from the scratch pool + call mpas_pool_get_field(scratchPool, 'iceCellMask', thermalCellMaskField) + call mpas_allocate_scratch_field(thermalCellMaskField, .true.) + thermalCellMask => thermalCellMaskField % array + + ! get config variables + call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) + call mpas_pool_get_config(liConfigs, 'config_thermal_solver', config_thermal_solver) + call mpas_pool_get_config(liConfigs, 'config_thermal_thickness', config_thermal_thickness) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + + if (config_print_thermal_info) then + call mpas_log_write('Solving for temperature, config_thermal_solver = ' // trim(config_thermal_solver)) + endif + + ! calculate masks - so we know where the ice is floating + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! calculate a mask to identify ice that is thick enough to be thermally active + do iCell = 1, nCellsSolve + if (thickness(iCell) > config_thermal_thickness) then + thermalCellMask(iCell) = 1 + else + thermalCellMask(iCell) = 0 + endif + enddo + + + ! === Calculate mechanical heating terms === + + if (trim(config_velocity_solver) == 'sia') then + ! Compute interior heat dissipation + call heat_dissipation_sia(domain, err_tmp) + err = ior(err, err_tmp) + endif ! sia + ! (FO dissipation is calculated within dycore) + + ! Compute heat flux due to basal friction + ! appropriate for SIA or FO dycore, assuming Taub=beta*ub + call basal_friction(domain, err_tmp) + err = ior(err, err_tmp) + + + ! === Update surfaceTemperature if using a lapse rate === + if (trim(config_surface_air_temperature_source) == 'lapse') then + surfaceAirTemperature(:) = config_surface_air_temperature_value - config_surface_air_temperature_lapse_rate * upperSurface(:) + endif + + ! ================================ + select case(config_thermal_solver) + ! ================================ + + case ('none') + + ! do nothing - handled above + + case ('temperature', 'enthalpy') + + if (config_print_thermal_info) then + call mpas_log_write('Solving for temperature, config_thermal_solver = ' // trim(config_thermal_solver) ) + endif + + ! Convert temperature from Kelvin to Celsius to avoid repeated use of kelvin_to_celsius below + ! (Convert back at the end.) + temperature(:,:) = temperature(:,:) - kelvin_to_celsius + surfaceAirTemperature(:) = surfaceAirTemperature(:) - kelvin_to_celsius + surfaceTemperature(:) = surfaceTemperature(:) - kelvin_to_celsius + basalTemperature(:) = basalTemperature(:) - kelvin_to_celsius + + ! allocate some vertical arrays + allocate(subdiagonal(nVertLevels+2)) ! temperature/enthalpy in each layer, plus surface and basal temperature + allocate(diagonal(nVertLevels+2)) + allocate(superdiagonal(nVertLevels+2)) + allocate(rhs(nVertLevels+2)) + allocate(solution(nVertLevels+2)) + allocate(diffusivity(nVertLevels+1)) + + if (config_print_thermal_info) then + call mpas_log_write(' ') + do iCell = 1, nCellsSolve + if (indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('surfaceAirTemperature = $r', realArgs=(/surfaceAirTemperature(iCell)/)) + call mpas_log_write(' ') + call mpas_log_write('Initial column temperatures, iCell = $i', intArgs=(/iCell/)) + call mpas_log_write("0 $r", realArgs=(/surfaceTemperature(iCell)/)) + do k = 1, nVertLevels + call mpas_log_write("$i $r", intArgs=(/k/), realArgs=(/temperature(k,iCell)/)) + enddo + call mpas_log_write("$i $r", intArgs=(/nVertLevels+1/), realArgs=(/basalTemperature(iCell)/)) + call mpas_log_write('Loop over cells') + endif + enddo + endif + + ! loop over locally owned cells + do iCell = 1, nCellsSolve + + if (config_print_thermal_info .and. indexToCellID(iCell) == config_stats_cell_ID) then + verboseColumn = .true. + else + verboseColumn = .false. + endif + + if (thermalCellMask(iCell) == 1) then ! thermally active ice is present + + ! Set surface temperature (Celsius) + + surfaceTemperature(iCell) = min(0.0_RKIND, surfaceAirTemperature(iCell)) + + ! For floating ice, set the basal temperature to the freezing temperature of seawater. + ! Values based on Ocean Water Freezing Point Calculator with S = 35 PSU + if (li_mask_is_floating_ice(cellMask(iCell))) then + depth = thickness(iCell) * rhoi/rhoo + basalTemperature(iCell) = oceanFreezingTempSurface + oceanFreezingTempDepthDependence * depth ! Celsius + endif + + if (trim(config_thermal_solver) == 'enthalpy') then + + ! Given temperature and waterfrac in ice interior, compute enthalpy + + call li_temperature_to_enthalpy(& + layerCenterSigma, & + thickness(iCell), & + temperature(:,iCell), & + waterfrac(:,iCell), & + enthalpy(:,iCell)) + + surfaceEnthalpy = surfaceTemperature(iCell) * rhoi*cp_ice + basalEnthalpy = basalTemperature(iCell) * rhoi*cp_ice + + if (verboseColumn) then + call mpas_log_write(' ') + call mpas_log_write('Before prognostic enthalpy, iCell = $i', intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('Temperature (C), waterfrac, enthalpy/(rhoi*cp_ice):') + call mpas_log_write('0 $r', realArgs=(/surfaceEnthalpy/(rhoi*cp_ice)/)) + do k = 1, nVertLevels + call mpas_log_write('$i $r $r $r', intArgs=(/k/), realArgs= & + (/temperature(k,iCell), waterfrac(k,iCell), enthalpy(k,iCell)/(rhoi*cp_ice)/)) + enddo + call mpas_log_write('$i $r', intArgs=(/nVertLevels+1/), realArgs=(/basalEnthalpy/(rhoi*cp_ice)/)) + endif + + ! Compute initial internal energy in column (for energy conservation check) + initialEnergy = 0.0_RKIND + do k = 1, nVertLevels + initialEnergy = initialEnergy + enthalpy(k,iCell) * (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) + enddo + initialEnergy = initialEnergy * thickness(iCell) + + ! Compute matrix elements using enthalpy gradient method + + call enthalpy_matrix_elements(& + deltat, & + nVertLevels, & + layerCenterSigma, & + dsigmaTerm, & + li_mask_is_floating_ice_int(cellMask(iCell)), & + thickness(iCell), & + temperature(:,iCell), & + surfaceTemperature(iCell), & + basalTemperature(iCell), & + waterfrac(:,iCell), & + enthalpy(:,iCell), & + heatDissipation(:,iCell), & + basalHeatFlux(iCell), & + basalFrictionFlux(iCell), & + diffusivity, & + subdiagonal, & + diagonal, & + superdiagonal, & + rhs) + + if (verboseColumn) then + call mpas_log_write(' ') + call mpas_log_write('After matrix elements, iCell = $i', intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write('k, subd, diag, supd, rhs/(rhoi*ci):') + do k = 1, nVertLevels+2 + call mpas_log_write('$i $r $r $r $r', intArgs=(/k-1/), realArgs= & + (/subdiagonal(k), diagonal(k), superdiagonal(k), rhs(k)/(rhoi*cp_ice)/)) + enddo + endif + + ! solve the tridiagonal system + ! Note: Temperature is indexed from 1 to nVertLevels, whereas the matrix elements + ! are indexed from 1 to nVertLevels+2. + ! Matrix row 1 corresponds to surface temperature, and matrix row nVertLevels+2 + ! corresponds to the basal temperature. + + call tridiag_solver(& + subdiagonal, & + diagonal, & + superdiagonal, & + solution, & + rhs) + + ! Copy the solution into the enthalpy variables + surfaceEnthalpy = solution(1) + enthalpy(:,iCell) = solution(2:nVertLevels+1) + basalEnthalpy = solution(nVertLevels+2) + + ! Compute conductive fluxes = (diffusivity/thickness * denth/dsigma) at upper and lower surfaces; + ! positive down. + + ! Here diffusivity = iceConductivity / (rhoi*cp_ice) for cold ice, with a smaller value for temperate ice. + ! Assume implicit backward Euler time step. + ! Note: These fluxes should be computed before calling glissade_enth2temp (which might change basalEnthalpy). + + denth_top = enthalpy(1,iCell) - surfaceEnthalpy + denth_bot = basalEnthalpy - enthalpy(nVertLevels,iCell) + + surfaceConductiveFlux(iCell) = -diffusivity(1)/thickness(iCell) * denth_top/layerCenterSigma(1) + basalConductiveFlux(iCell) = -diffusivity(nVertLevels+1)/thickness(iCell) * & + denth_bot/(1.0_RKIND - layerCenterSigma(nVertLevels)) + + ! convert enthalpy in ice interior back to temperature and waterfrac + + call li_enthalpy_to_temperature(& + layerCenterSigma, & + thickness(iCell), & + enthalpy(:,iCell), & + temperature(:,iCell), & + waterfrac(:,iCell), & + surfaceEnthalpy, & + surfaceTemperature(iCell), & + basalEnthalpy, & + basalTemperature(iCell)) + + if (verboseColumn) then + call mpas_log_write(' ') + call mpas_log_write('After prognostic enthalpy, iCell = $i', intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('Temperature, waterfrac, enthalpy/(rhoi*cp_ice):') + call mpas_log_write('0 $r', realArgs=(/surfaceEnthalpy/(rhoi*cp_ice)/)) + do k = 1, nVertLevels + call mpas_log_write('$i $r $r $r', intArgs=(/k/), realArgs= & + (/temperature(k,iCell), waterfrac(k,iCell), enthalpy(k,iCell)/(rhoi*cp_ice)/)) + enddo + call mpas_log_write('$i $r', intArgs=(/nVertLevels+1/), realArgs=(/basalEnthalpy/(rhoi*cp_ice)/)) + endif + + ! Compute final internal energy in column (for energy conservation check) + finalEnergy = 0.0_RKIND + do k = 1, nVertLevels + finalEnergy = finalEnergy + enthalpy(k,iCell) * (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) + enddo + finalEnergy = finalEnergy * thickness(iCell) + + else ! temperature solver + + if (verboseColumn) then + call mpas_log_write(' ') + call mpas_log_write('Before prognostic temperature, iCell = $i', intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('0 $r', realArgs=(/surfaceTemperature(iCell)/)) + do k = 1, nVertLevels + call mpas_log_write('$i $r', intArgs=(/k/), realArgs=(/temperature(k,iCell)/)) + enddo + call mpas_log_write('$i $r', intArgs=(/nVertLevels+1/), realArgs=(/basalTemperature(iCell)/)) + endif + + ! Compute initial internal energy in column (for energy conservation check) + initialEnergy = 0.0_RKIND + do k = 1, nVertLevels + initialEnergy = initialEnergy + temperature(k,iCell) * (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) + enddo + initialEnergy = initialEnergy * thickness(iCell) * rhoi*cp_ice + + ! Compute matrix elements + + call temperature_matrix_elements(& + deltat, & + nVertLevels, & + layerCenterSigma, & + dsigmaTerm, & + li_mask_is_floating_ice_int(cellMask(iCell)), & + thickness(iCell), & + temperature(:,iCell), & + surfaceTemperature(iCell), & + basalTemperature(iCell), & + heatDissipation(:,iCell), & + basalHeatFlux(iCell), & + basalFrictionFlux(iCell), & + subdiagonal, & + diagonal, & + superdiagonal, & + rhs) + + if (verboseColumn) then + call mpas_log_write(' ') + call mpas_log_write('deltat = $r', realArgs=(/deltat/)) + call mpas_log_write('After matrix elements, iCell = $i', intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write('k, subd, diag, supd, rhs:') + do k = 1, nVertLevels+2 + call mpas_log_write('$i $r $r $r $r', intArgs=(/k-1/), realArgs= & + (/subdiagonal(k), diagonal(k), superdiagonal(k), rhs(k)/)) + enddo + endif + + ! Solve the tridiagonal system + ! Note: Temperature is indexed from 1 to nVertLevels, whereas the matrix elements + ! are indexed from 1 to nVertLevels+2. + ! Matrix row 1 corresponds to surface temperature, and matrix row nVertLevels+2 + ! corresponds to the basal temperature. + + call tridiag_solver(& + subdiagonal, & + diagonal, & + superdiagonal, & + solution, & + rhs) + + ! Copy the solution into the temperature variables + surfaceTemperature(iCell) = solution(1) + temperature(:,iCell) = solution(2:nVertLevels+1) + basalTemperature(iCell) = solution(nVertLevels+2) + + ! Compute conductive flux = (k/H * dT/dsigma) at upper and lower surfaces; positive down + ! Assume implicit backward Euler time step. + + dTtop = temperature(1,iCell) - surfaceTemperature(iCell) + dTbot = basalTemperature(iCell) - temperature(nVertLevels,iCell) + + surfaceConductiveFlux(iCell) = (-iceConductivity/thickness(iCell) ) * dTtop / layerCenterSigma(1) + basalConductiveFlux(iCell) = (-iceConductivity/thickness(iCell) ) * dTbot / & + (1.0_RKIND - layerCenterSigma(nVertLevels)) + + if (verboseColumn) then + call mpas_log_write(' ') + call mpas_log_write('After prognostic temperature, iCell = $i', intArgs=(/indexToCellID(iCell)/)) + call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('0 $r', realArgs=(/surfaceTemperature(iCell)/)) + do k = 1, nVertLevels + call mpas_log_write('$i $r', intArgs=(/k/), realArgs=(/temperature(k,iCell)/)) + enddo + call mpas_log_write('$i $r', intArgs=(/nVertLevels+1/), realArgs=(/basalTemperature(iCell)/)) + endif + + ! Compute final internal energy in column (for energy conservation check) + finalEnergy = 0.0_RKIND + do k = 1, nVertLevels + finalEnergy = finalEnergy + temperature(k,iCell) * (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) + enddo + finalEnergy = finalEnergy * thickness(iCell) * rhoi*cp_ice + + endif ! temperature or enthalpy solver + + ! Compute total dissipation rate in column (W/m^2) + columnHeatDissipation = 0.0_RKIND + do k = 1, nVertLevels + columnHeatDissipation = columnHeatDissipation & + + heatDissipation(k,iCell) * (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) + enddo + columnHeatDissipation = columnHeatDissipation * thickness(iCell)*rhoi*cp_ice + + if (verboseColumn) then + call mpas_log_write(' ') + call mpas_log_write('heatDissipation: $r', realArgs=(/heatDissipation(:,iCell)/)) + call mpas_log_write('columnHeatDissipation: $r', realArgs=(/columnHeatDissipation/)) + endif + + ! Verify that the net input of energy into the column is equal to the change in internal energy. + + deltaEnergy = (surfaceConductiveFlux(iCell) - basalConductiveFlux(iCell) + columnHeatDissipation) * deltat + + !TODO - Confirm that this is a reasonable error threshold + if (abs((finalEnergy - initialEnergy - deltaEnergy) / deltat) > 1.0e-8_RKIND) then + + if (verboseColumn) then + call mpas_log_write('Ice thickness: $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('config_thermal_thickness: $r', realArgs=(/config_thermal_thickness/)) + call mpas_log_write(' ') + call mpas_log_write('Interior fluxes:') + call mpas_log_write('sfc conductive flx (W/m^2, positive down)=$r', realArgs=(/surfaceConductiveFlux(iCell)/)) + call mpas_log_write('bed conductive flx (W/m^2, positive down)=$r', realArgs=(/basalConductiveFlux(iCell)/)) + call mpas_log_write('column heat dissipation (W/m^2) =$r', realArgs=(/columnHeatDissipation/)) + call mpas_log_write('Net flux (W/m^2) =$r', realArgs=(/deltaEnergy/deltat/)) + call mpas_log_write(' ') + call mpas_log_write('deltaEnergy (J/m^2) =$r', realArgs=(/deltaEnergy/)) + call mpas_log_write('initialEnergy (J/m^2) =$r', realArgs=(/initialEnergy/)) + call mpas_log_write('finalEnergy (J/m^2) =$r', realArgs=(/finalEnergy/)) + call mpas_log_write(' ') + call mpas_log_write('Energy imbalance (J/m^2)=$r', realArgs=(/finalEnergy - initialEnergy - deltaEnergy/)) + call mpas_log_write(' ') + call mpas_log_write('Basal fluxes:') + call mpas_log_write('frictional =$r', realArgs=(/basalFrictionFlux(iCell)/)) + call mpas_log_write('geothermal =$r', realArgs=(/basalHeatFlux(iCell)/)) + call mpas_log_write('flux for bottom melting =$r', & + realArgs=(/basalFrictionFlux(iCell) + basalHeatFlux(iCell) + basalConductiveFlux(iCell)/)) + endif ! verboseColumn + + call mpas_log_write('li_thermal, energy conservation error: iCell=$i, imbalance=$r (W/m2):', MPAS_LOG_WARN, & + intArgs=(/indexToCellID(iCell)/), realArgs=(/(finalEnergy - initialEnergy - deltaEnergy)/deltat/)) + err = 0 + + endif ! energy conservation error + + else ! thermalCellMask = 0; ice is not thermally active + + ! Set temperature of thin ice to 0 C + !TODO - For cells that have just crossed the config_thermal_thickness threshold, energy is not conserved here. + ! Keep track of the energy difference? + + surfaceTemperature(iCell) = 0.0_RKIND + basalTemperature(iCell) = 0.0_RKIND + temperature(:,iCell) = 0.0_RKIND + waterfrac(:,iCell) = 0.0_RKIND + enthalpy(:,iCell) = 0.0_RKIND + + endif ! thickness > config_thermal_thickness + + enddo ! iCell + + ! Compute basal melt rate for grounded ice. + ! Note: + ! * This subroutine needs to be called only if the temperature/enthalpy is prognostic. + ! * It assumes Celsius units, so it should be called before converting back to Kelvin. + ! * It includes internal melting. + ! For the standard temperature scheme, temperatures above the pressure melting point + ! are reset to Tpmp, with excess heat contributing to basal melt. + ! For the enthalpy scheme, internal meltwater in excess of the prescribed maximum + ! fraction (0.01 by default) is drained to the bed. + + call basal_melt_grounded_ice(& + config_thermal_solver, & + deltat, & + nCellsSolve, & + nVertLevels, & + layerInterfaceSigma, & + layerCenterSigma, & + thermalCellMask, & + li_mask_is_floating_ice_int(cellMask), & + thickness, & + temperature, & + basalTemperature, & + waterfrac, & + enthalpy, & + basalFrictionFlux, & + basalHeatFlux, & + basalConductiveFlux, & + basalWaterThickness, & + groundedBasalMassBal) + + ! Convert temperatures from Celsius back to Kelvin + temperature(:,:) = temperature(:,:) + kelvin_to_celsius + surfaceAirTemperature(:) = surfaceAirTemperature(:) + kelvin_to_celsius + surfaceTemperature(:) = surfaceTemperature(:) + kelvin_to_celsius + basalTemperature(:) = basalTemperature(:) + kelvin_to_celsius + + ! Check for temperatures that are physically unrealistic. + ! Thresholds are set at the top of this module. + + do iCell = 1, nCellsSolve + + maxtemp = maxval(temperature(:,iCell)) + mintemp = minval(temperature(:,iCell)) + + if (maxtemp > maxtempThreshold) then + call mpas_log_write('maxtemp > maxtempThreshold: iCell=$i, maxtemp = $r', intArgs=(/iCell/), realArgs=(/maxtemp/)) + call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('temperature:') + do k = 1, nVertLevels + call mpas_log_write('$i $r', intArgs=(/k/), realArgs=(/temperature(k,iCell)/)) + enddo + call mpas_log_write("An error has occurred in li_thermal. Aborting...", MPAS_LOG_CRIT) + endif + + if (mintemp < mintempThreshold) then + call mpas_log_write('mintemp < mintempThreshold: iCell=$i, mintemp = $r', intArgs=(/iCell/), realArgs=(/mintemp/)) + call mpas_log_write('thickness = $r', realArgs=(/thickness(iCell)/)) + call mpas_log_write('temperature:') + do k = 1, nVertLevels + call mpas_log_write('$i $r', intArgs=(/k/), realArgs=(/temperature(k,iCell)/)) + enddo + call mpas_log_write("An error has occurred in li_thermal. Aborting...", MPAS_LOG_CRIT) + endif + + enddo ! iCell + + end select ! config_thermal_solver + + ! It is possible that internal melting was computed above for floating ice and assigned + ! to the groundedBasalMassBal array. If so, then add it to floatingBasalMassBal. + ! Note: Subroutine basal_melt_floating_ice should be called earlier in the time step, before adding this term. + + do iCell = 1, nCellsSolve + + if (thermalCellMask(iCell) == 1 .and. li_mask_is_floating_ice(cellMask(iCell)) .and. & + groundedBasalMassBal(iCell) /= 0.0_RKIND) then + floatingBasalMassBal(iCell) = floatingBasalMassBal(iCell) + groundedBasalMassBal(iCell) + groundedBasalMassBal(iCell) = 0.0_RKIND + endif + + if (config_print_thermal_info .and. indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write('iCell=$i, basal mass balance (m/yr): grounded=$r, floating=$r', & + intArgs=(/iCell/), realArgs=(/groundedBasalMassBal(iCell)*scyr/rhoi, floatingBasalMassBal(iCell)*scyr/rhoi/) ) + endif + + enddo + + ! clean up + call mpas_deallocate_scratch_field(thermalCellMaskField, .true.) + if (allocated(subdiagonal)) deallocate(subdiagonal) + if (allocated(diagonal)) deallocate(diagonal) + if (allocated(superdiagonal)) deallocate(superdiagonal) + if (allocated(rhs)) deallocate(rhs) + if (allocated(solution)) deallocate(solution) + if (allocated(diffusivity)) deallocate(diffusivity) + + block => block % next + enddo ! associated(block) + + ! halo updates + ! Note: This subroutine does not change the ice thickness, so no thickness update is needed. + + call mpas_timer_start("halo updates") + + call mpas_dmpar_field_halo_exch(domain, 'surfaceTemperature') + call mpas_dmpar_field_halo_exch(domain, 'basalTemperature') + call mpas_dmpar_field_halo_exch(domain, 'temperature') + + if (trim(config_thermal_solver) == 'enthalpy') then + ! prognostic variables are temperature and waterfrac, so need to update waterfrac too + call mpas_dmpar_field_halo_exch(domain, 'waterfrac') + endif + + call mpas_timer_stop("halo updates") + + ! === error check + if (err > 0) then + call mpas_log_write('An error has occurred in li_thermal_solver', MPAS_LOG_ERR) + endif + + end subroutine li_thermal_solver + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_basal_melt_floating_ice +! +!> \brief MPAS land ice solver for basal melt of floating ice +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> This routine computes basal melting for floating ice. +!> The following options are supported: +!> (1) Do nothing (config_basal_mass_bal_float = 'none') +!> (2) Read melt rate from a file (config_basal_mass_bal_float = 'file') +!> (2) Prescribed constant basal melt rate (config_basal_mass_bal_float = 'constant') +!> (3) Basal melt rate as in MISMIP+ (config_basal_mass_bal_float = 'mismip') + +!----------------------------------------------------------------------- + + subroutine li_basal_melt_floating_ice(domain, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool ! needed for mask subroutine + type (mpas_pool_type), pointer :: scratchPool + + integer, pointer :: & + nCellsSolve ! number of locally owned cells + + logical, pointer :: & + config_print_thermal_info ! if true, print debug info + + character(len=StrKIND), pointer :: & + config_basal_mass_bal_float ! option for basal mass balance of floating ice + + real(kind=RKIND), pointer :: & + config_thermal_thickness, & ! minimum thickness (m) for temperature calculations + config_sea_level, & ! sea level (m) relative to z = 0 + config_bmlt_float_flux, & ! constant heat flux (W/m^2) applied to the base of floating ice; positive upward + config_bmlt_float_xlimit ! x value (m) defining region where bmlt_float_flux is applied; melt only where abs(x) > xlimit + + integer, dimension(:), pointer :: & + cellMask ! bit mask describing whether ice is floating, dynamically active, etc. + + type (field1dInteger), pointer :: thermalCellMaskField + + integer, dimension(:), pointer :: & + thermalCellMask ! mask for thermal calculations + ! = 1 where thickness > config_thermal_thickness, elsewhere = 0 + + real (kind=RKIND), dimension(:), pointer :: & + xCell ! x coordinate for each cell (m) + + real (kind=RKIND), dimension(:), pointer :: & + floatingBasalMassBal, & ! basal mass balance for floating ice + thickness, & ! ice thickness (m) + lowerSurface, & ! lower surface elevation (m) + bedTopography ! bed topography (m; negative below sea level) + + real(kind=RKIND), pointer :: daysSinceStart + + integer :: iCell, err_tmp + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_float', config_basal_mass_bal_float) + + if (trim(config_basal_mass_bal_float) == 'none') then + + ! Zero entire field + + ! block loop + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + + floatingBasalMassBal = 0.0_RKIND + + block => block % next + enddo ! associated(block) + + elseif (trim(config_basal_mass_bal_float) == 'file') then + + return ! already set; nothing to do + + endif + + ! get rest of config variables + call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) + call mpas_pool_get_config(liConfigs, 'config_thermal_thickness', config_thermal_thickness) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_bmlt_float_flux', config_bmlt_float_flux) + call mpas_pool_get_config(liConfigs, 'config_bmlt_float_xlimit', config_bmlt_float_xlimit) + + if (config_print_thermal_info) then + call mpas_log_write('Solving for basal melting of floating ice, config_basal_mass_bal_float = ' // & + trim(config_basal_mass_bal_float) ) + endif + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + ! get fields from the mesh pool + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'daysSinceStart',daysSinceStart) + + ! get fields from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + + ! get fields from the scratch pool + call mpas_pool_get_field(scratchPool, 'iceCellMask', thermalCellMaskField) + call mpas_allocate_scratch_field(thermalCellMaskField, .true.) + thermalCellMask => thermalCellMaskField % array + + ! calculate masks - so we know where the ice is floating + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + err = ior(err, err_tmp) + + ! calculate a mask to identify ice that is thick enough to be thermally active + do iCell = 1, nCellsSolve + if (thickness(iCell) > config_thermal_thickness) then + thermalCellMask(iCell) = 1 + else + thermalCellMask(iCell) = 0 + endif + enddo + + ! Compute basal melting for floating ice. + + call basal_melt_floating_ice(& + config_basal_mass_bal_float, & + nCellsSolve, & + xCell, & + daysSinceStart, & + thermalCellMask, & + li_mask_is_floating_ice_int(cellMask), & + lowerSurface, & + bedTopography, & + config_sea_level, & + config_bmlt_float_flux, & + config_bmlt_float_xlimit, & + floatingBasalMassBal, & + err_tmp) + err = ior(err, err_tmp) + + !WHL - debug + ! write(stdoutUnit,*) 'Computed basal melt for floating ice' + ! write(stdoutUnit,*) ' ' + ! write(stdoutUnit,*) 'iCell, thickness, basal mbal:' + ! do iCell = 1, nCellsSolve + ! if (li_mask_is_floating_ice(cellMask(iCell))) then + ! write(stdoutUnit,*) iCell, thickness(iCell), floatingBasalMassBal(iCell)*scyr/rhoi + ! endif + ! enddo + ! write(stdoutUnit,*) 'Done with basal melt for floating ice' + + ! clean up + call mpas_deallocate_scratch_field(thermalCellMaskField, .true.) + + block => block % next + enddo ! associated(block) + + + end subroutine li_basal_melt_floating_ice + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine heat_dissipation_sia +! +!> \brief MPAS land ice heat dissipation for SIA velocity solver +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes heat dissipation associated with strain heating +!> in the ice interior, based on the shallow-ice approximation. +!----------------------------------------------------------------------- + + subroutine heat_dissipation_sia(domain, err) + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: scratchPool + + logical, pointer :: & + config_print_thermal_info ! if true, print debug info + + integer, pointer :: & + nCells, & ! number of cells + nEdges, & ! number of edges + nVertLevels ! number of vertical layers + + integer, dimension(:), pointer :: & + nEdgesOnCell ! number of edges on each cell + + integer, dimension(:,:), pointer :: & + cellsOnEdge, & ! indices for 2 cells on each edge + edgesOnCell ! indices for edges on each cell + + real(kind=RKIND), dimension(:), pointer :: & + areaCell ! area of each cell + + real(kind=RKIND), dimension(:), pointer :: & + dcEdge, & ! distance between neighboring cells across edge + dvEdge ! distance between eighboring vertices along edge + + real(kind=RKIND), dimension(:), pointer :: & + layerCenterSigma ! vertical coordinate at center of each layer + + real(kind=RKIND), dimension(:), pointer :: & + thickness ! ice thickness in cells + + real(kind=RKIND), dimension(:), pointer :: & + slopeEdge ! surface slope at edges + + real(kind=RKIND), dimension(:,:), pointer :: & + flowParamA ! flow factor in each layer of each cell, Pa^(-n) s^(-1) + + real(kind=RKIND), dimension(:,:), pointer :: & + heatDissipation ! interior heat dissipation in each layer of each cell (deg/s) + ! output from this subroutine + + type (field2dReal), pointer :: heatDissipationEdgeField + + real (kind=RKIND), dimension(:,:), pointer :: & + heatDissipationEdge ! heat dissipation on edges + + integer, dimension(:), pointer :: & + indexToCellID ! list of global cell IDs + + integer, pointer :: & + config_stats_cell_ID ! global ID for diagnostic cell + + real (kind=RKIND), pointer :: & + config_flowLawExponent ! flow law exponent + + real (kind=RKIND) :: & + thicknessEdge, & ! thickness averaged to edge + weightEdge ! edge weight for averaging to cell center + + real (kind=RKIND), dimension(:), allocatable :: & + flowParamAEdge ! flow parameter averaged to edge + + integer :: iCell, iCell1, iCell2, iEdge, iEdgeOnCell, iLayer + + ! Here are notes from the Glimmer calculation of heat dissipation: + ! + ! "Two methods of doing this calculation: + ! 1. find dissipation at u-pts and then average + ! 2. find dissipation at H-pts by averaging quantities from u-pts + ! (2) works best for eismint divide (symmetry) but (1) may be better for full expts" + ! + ! Glimmer uses (2). + ! + ! For MPAS LI we use the C-grid variant of (1); we find the dissipation at edges and then average to cell centers. + ! The heating rate phi, defined on an edge, is given by + ! + ! phi = 2 * A(T) * (sigma * rhoi * g * H * |grad(s)|)^(n+1) + ! + ! where A(T) is the flow factor, sigma is the vertical coordinate of the layer, + ! H is the ice thickness averaged to the edge, and grad(s) is the surface elevation gradient. + ! + ! phi has units of W m^{-3}. + ! The heat dissipation in deg/s is given by phi / (rhoi * cp_ice). + + err = 0 + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! get fields from the mesh pool + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) ! diagnostic only + + ! get fields from the geometry pool + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'slopeEdge', slopeEdge) + + ! get fields from the velocity pool + call mpas_pool_get_array(velocityPool, 'flowParamA', flowParamA) + + ! get fields from the thermal pool + call mpas_pool_get_array(thermalPool, 'heatDissipation', heatDissipation) + + ! get config parameters + call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', config_flowLawExponent) + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) + + ! get scratch fields + call mpas_pool_get_field(scratchPool, 'workLevelEdge', heatDissipationEdgeField) + call mpas_allocate_scratch_field(heatDissipationEdgeField, .true.) + heatDissipationEdge => heatDissipationEdgeField % array + + allocate(flowParamAEdge(nVertLevels)) + + if (config_print_thermal_info) then + call mpas_log_write('Compute SIA heat dissipation') + if (maxval(thickness) == 0.0_RKIND) then + return ! no thicknessm so just return + endif + endif + + ! compute the heat dissipation on edges + + do iEdge = 1, nEdges + + ! identify the cells on this edge + iCell1 = cellsOnEdge(1,iEdge) + iCell2 = cellsOnEdge(2,iEdge) + + if (iCell1 >= 1 .and. iCell1 <= nCells .and. iCell2 >= 1 .and. iCell2 <= nCells) then ! both cells exist + + ! average the thickness and flow parameter to the edge + thicknessEdge = 0.5_RKIND * (thickness(iCell1) + thickness(iCell2)) + flowParamAEdge(:) = 0.5_RKIND * (flowParamA(:,iCell1) + flowParamA(:,iCell2)) + + ! compute the dissipation at each level + !TODO - Verify that this equation gives the right answer + heatDissipationEdge(:,iEdge) = 2.0_RKIND * flowParamAEdge(:) * & + (layerCenterSigma(:) * rhoi * gravity * thicknessEdge * & + abs(slopeEdge(iEdge))) ** (config_flowLawExponent + 1.0_RKIND) + + if (config_print_thermal_info .and. indexToCellID(iCell1) == config_stats_cell_ID) then +! write(stdoutUnit,*) ' ' +! write(stdoutUnit,*) 'Heat dissipation, iEdge, iCell1 =', iEdge, iCell1 +! write(stdoutUnit,*) 'thicknessEdge:', thicknessEdge +! write(stdoutUnit,*) 'flowParamAEdge:', flowParamAEdge(:) +! write(stdoutUnit,*) 'slopeEdge:', slopeEdge(iEdge) +! write(stdoutUnit,*) 'heatDissipationEdge:', heatDissipationEdge(:,iEdge) + endif + + else ! one neighbor cell does not exist + !TODO = Confirm that the dissipation is not needed at such edges + + heatDissipationEdge(:,iEdge) = 0.0_RKIND + + endif + + enddo ! iEdge + + ! average the heat dissipation to cell centers + + do iCell = 1, nCells + + heatDissipation(:,iCell) = 0.0_RKIND + + if (config_print_thermal_info .and. indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write(' ') + call mpas_log_write('Heat dissipation, iCell:local=$i, global ID=$i', intArgs=(/iCell, indexToCellID(iCell)/)) + call mpas_log_write('iEdgeOnCell, iEdge, weightEdge, heatDissipationEdge (layer 1)') + endif + + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + + iEdge = edgesOnCell(iEdgeOnCell,iCell) + weightEdge = 0.25_RKIND*dcEdge(iEdge)*dvEdge(iEdge) / areaCell(iCell) + + heatDissipation(:,iCell) = heatDissipation(:,iCell) + weightEdge * heatDissipationEdge(:,iEdge) + + if (config_print_thermal_info .and. indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write('$i $i $r $r $r', intArgs=(/iEdgeOnCell, iEdge/), realArgs= & + (/weightEdge, heatDissipationEdge(1,iEdge), heatDissipation(1,iCell)/)) + endif + + enddo ! iEdgeOnCell + + if (config_print_thermal_info .and. indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write('heatDissipation (deg/s) for cell $i', intArgs=(/iCell/)) + do iLayer = 1, nVertLevels + call mpas_log_write('$i $r', intArgs=(/iLayer/), realArgs=(/heatDissipation(iLayer,iCell) / (rhoi*cp_ice)/)) + enddo + endif + + enddo ! iCell + + ! convert units from W/m^3 to deg/s + + heatDissipation(:,:) = heatDissipation(:,:) / (rhoi * cp_ice) + + ! clean up + deallocate(flowParamAEdge) + call mpas_deallocate_scratch_field(heatDissipationEdgeField, .true.) + + block => block % next + enddo ! associated(block) + + end subroutine heat_dissipation_sia + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine basal_friction +! +!> \brief MPAS heat flux due to basal friction for SIA dynamics +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes the heat flux due to basal friction at the +!> base of the ice, based on the shallow-ice approximation. +!----------------------------------------------------------------------- + + subroutine basal_friction(domain, err) + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: thermalPool + + logical, pointer :: & + config_print_thermal_info ! if true, print debug info + + integer, pointer :: & + config_stats_cell_ID ! global ID for diagnostic cell + + integer, dimension(:), pointer :: & + indexToCellID ! list of global cell IDs + + integer, pointer :: & + nCellsSolve ! number of cells + + real(kind=RKIND), dimension(:), pointer :: & + basalSpeed, & ! basal ice speed, reconstructed at cell centers (m s^{-1}) + betaSolve, & ! basal traction parameter (Pa m^{-1} s); use betaSolve to treat floating ice correctly + basalFrictionFlux ! heat flux due to basal friction (W^{m-2}), computed in this subroutine + + integer :: iCell + + !---------------------------------------------------------------- + ! Compute the heat flux due to basal friction, given the basal speed + ! and basal friction parameter (beta) fields. + ! + ! Assume a sliding law of the form + ! + ! tau_b = beta * basalSpeed + ! + ! The frictional heat flux (W/m^2) is given by (e.g., Cuffey & Paterson, p. 418) + ! + ! basalFrictionFlux = tau_b * basalSpeed = beta * basalSpeed**2 + ! + ! Note: Currently (Nov. 2015), beta is used by the HO solver only, + ! and the SIA solver assumes no slip (basalSpeed = 0). + ! TODO: Modify the SIA solver to allow sliding. + !---------------------------------------------------------------- + + err = 0 + + ! block loop + block => domain % blocklist + do while (associated(block)) + + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + ! get fields from the mesh pool + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) ! diagnostic only + + ! get fields from the velocity pool + call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) + + call mpas_pool_get_array(velocityPool, 'betaSolve', betaSolve) + + ! get fields from the thermal pool + call mpas_pool_get_array(thermalPool, 'basalFrictionFlux', basalFrictionFlux) + + ! get config parameters + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + call mpas_pool_get_config(liConfigs, 'config_print_thermal_info', config_print_thermal_info) + + if (config_print_thermal_info) call mpas_log_write('Compute basal friction flux') + + ! Compute basal frictional heating for each cell + basalFrictionFlux(:) = betaSolve(:) * basalSpeed(:)**2 * scyr ! TODO: If beta units are changed to be SI, remove this factor + + ! Optional debugging output + if (config_print_thermal_info) then + do iCell = 1, nCellsSolve + if (indexToCellID(iCell) == config_stats_cell_ID) then + call mpas_log_write('iCell=$i, betaSolve=$r, basalSpeed=$r, basalFrictionFlux=$r', & + intArgs=(/iCell/), realArgs=(/betaSolve(iCell), basalSpeed(iCell), basalFrictionFlux(iCell)/)) + endif + enddo + endif + + block => block % next + enddo ! associated(block) + + end subroutine basal_friction + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_temperature_to_enthalpy +! +!> \brief MPAS convert temperature to enthalpy +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes the enthalpy in each layer of an ice column, +!> given the temperature and water fraction. +!----------------------------------------------------------------------- + + subroutine li_temperature_to_enthalpy(& + layerCenterSigma, & + thickness, & + temperature, & + waterfrac, & + enthalpy) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(in) :: & + layerCenterSigma !< Input: sigma coordinate at midpoint of each layer + + real (kind=RKIND), intent(in) :: & + thickness !< Input: ice thickness + + real (kind=RKIND), dimension(:), intent(in) :: & + temperature, & !< Input: interior ice temperature + waterfrac !< Input: interior water fraction + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(out) :: & + enthalpy !< Output: interior ice enthalpy + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(size(layerCenterSigma)) :: & + pmpTemperature ! pressure melting point temperature + + integer :: k, nVertLevels + + nVertLevels = size(layerCenterSigma) + + ! Find pressure melting point temperature in column + + call pressure_melting_point_column(& + layerCenterSigma, & + thickness, & + pmpTemperature) + + ! Solve for enthalpy + + do k = 1, nVertLevels + enthalpy(k) = (1.0_RKIND - waterfrac(k)) * rhoi * cp_ice * temperature(k) & + + waterfrac(k) * rho_water * (cp_ice * pmpTemperature(k) + latent_heat_ice) + end do + + end subroutine li_temperature_to_enthalpy + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_enthalpy_to_temperature +! +!> \brief MPAS convert enthalpy to temperature +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes the temperature and water fraction in each layer +!> of an ice column, given the enthalpy. +!----------------------------------------------------------------------- + + subroutine li_enthalpy_to_temperature(& + layerCenterSigma, & + thickness, & + enthalpy, & + temperature, & + waterfrac, & + surfaceEnthalpy, & + surfaceTemperature, & + basalEnthalpy, & + basalTemperature) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(in) :: & + layerCenterSigma !< Input: sigma coordinate at midpoint of each layer + + real (kind=RKIND), intent(in) :: & + thickness !< Input: ice thickness + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(inout) :: & + enthalpy !< Input/output: interior ice enthalpy + + real (kind=RKIND), intent(inout) , optional :: & + surfaceEnthalpy, & !< Input/output: surface ice enthalpy + basalEnthalpy !< Input/output: basal ice enthalpy + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(out) :: & + temperature !< Output: interior ice temperature + + real (kind=RKIND), dimension(:), intent(out) :: & + waterfrac !< Output: interior water fraction + + real (kind=RKIND), intent(out), optional :: & + surfaceTemperature, & !< Output: surface ice temperature + basalTemperature !< Output: basal ice temperature + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(size(layerCenterSigma)) :: pmpTemperature + real (kind=RKIND), dimension(size(layerCenterSigma)) :: pmpEnthalpy + + real (kind=RKIND) :: pmpTemperatureBed + real (kind=RKIND) :: pmpEnthalpyBed + + integer :: k, nVertLevels + + nVertLevels = size(layerCenterSigma) + + ! Find pressure melting point temperature in ice interior + call pressure_melting_point_column(& + layerCenterSigma, & + thickness, & + pmpTemperature) + + ! find pressure melting point temperature at bed + call pressure_melting_point(& + thickness, & + pmpTemperatureBed) + + ! Find pressure melting point enthalpy + pmpEnthalpy(:) = pmpTemperature(:) * rhoi*cp_ice + pmpEnthalpyBed = pmpTemperatureBed * rhoi*cp_ice + + ! Solve for temperature and waterfrac in ice interior + + ! ice interior + + do k = 1, nVertLevels + if (enthalpy(k) >= pmpEnthalpy(k)) then ! temperate ice + temperature(k) = pmpTemperature(k) + waterfrac(k) = (enthalpy(k) - pmpenthalpy(k)) / & + ((rho_water-rhoi)*cp_ice*pmpTemperature(k) + rho_water*latent_heat_ice) + else ! cold ice + temperature(k) = enthalpy(k) / (rhoi*cp_ice) + waterfrac(k) = 0.0_RKIND + endif + enddo ! k + + ! surface temperature (optional) + + if (present(surfaceEnthalpy) .and. present(surfaceTemperature)) then + + if (surfaceEnthalpy >= 0.0_RKIND) then ! temperate ice + surfaceTemperature = 0.0_RKIND + ! Reset surfaceEnthalpy to agree with the surface temperature. + ! This is consistent with energy conservation because the top surface + ! is infinitesimally thin. + surfaceEnthalpy = 0.0_RKIND + else ! cold ice + surfaceTemperature = surfaceEnthalpy / (rhoi*cp_ice) + endif + + endif + + ! basal temperature (optional) + + if (present(basalEnthalpy) .and. present(basalTemperature)) then + + k = nVertLevels + 1 + if (basalEnthalpy >= pmpEnthalpyBed) then ! temperate ice + basalTemperature = pmpTemperatureBed + ! Reset basalEnthalpy to agree with the surface temperature. + ! This is consistent with energy conservation because the lower surface + ! is infinitesimally thin. + basalEnthalpy = pmpEnthalpyBed + else ! cold ice + basalTemperature = basalEnthalpy / (rhoi*cp_ice) + endif + + endif + + end subroutine li_enthalpy_to_temperature + +!*********************************************************************** +!*********************************************************************** +! Private subroutines: +!*********************************************************************** +!*********************************************************************** + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine temperature_matrix_elements +! +!> \brief MPAS matrix elements for vertical temperature solver +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes matrix elements for the tridiagonal solve +!> of the vertical temperature profile. +!----------------------------------------------------------------------- + + subroutine temperature_matrix_elements(& + deltat, & + nVertLevels, & + layerCenterSigma, & + dsigmaTerm, & + floatingMask, & + thickness, & + temperature, & + surfaceTemperature, & + basalTemperature, & + heatDissipation, & + basalHeatFlux, & + basalFrictionFlux, & + subd, & + diag, & + supd, & + rhs) + + ! Note: Matrix elements (subd, supd, diag, rhs) are indexed from 1 to nVertLevels+2, + ! whereas temperature is indexed from 1 to nVertLevels. + ! The first row of the matrix is the equation for surfaceTemperature, and + ! the last row is the equation for basalTemperature. + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real(kind=RKIND), intent(in) :: & + deltat !< Input: time step (s) + + integer, intent(in) :: & + nVertLevels !< Input: number of vertical layers + + real(kind=RKIND), dimension(nVertLevels), intent(in) :: & + layerCentersigma !< Input: sigma coordinate at layer midpoints + + real(kind=RKIND), dimension(:,:), intent(in) :: & + dsigmaTerm !< Input: vertical grid quantities + + integer, intent(in) :: & + floatingMask !< Input: = 1 where ice is floating, else = 0 + + real(kind=RKIND), intent(in) :: & + thickness !< Input: ice thickness (m) + + real(kind=RKIND), dimension(nVertLevels), intent(in) :: & + temperature !< Input: ice temperature (deg C) + + real(kind=RKIND), intent(in) :: & + surfaceTemperature, & !< Input: surface ice temperature (deg C) + basalTemperature !< Input: basal ice temperature (deg C) + + real(kind=RKIND), dimension(nVertLevels), intent(in) :: & + heatDissipation !< Input: interior heat dissipation (deg/s) + + real(kind=RKIND), intent(in) :: & + basalHeatFlux !< Input: geothermal heat flux (W m-2), positive up + + real(kind=RKIND), intent(in) :: & + basalFrictionFlux !< Input: basal friction heat flux (W m-2), >= 0 + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:), intent(out) :: & + subd, diag, supd, rhs !< Output: tridiagonal matrix elements + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real(kind=RKIND) :: pmpTemperatureBed ! pressure melting temperature at bed + real(kind=RKIND) :: factor ! term in tridiagonal matrix + real(kind=RKIND) :: dsigmaBot ! bottom layer thickness in sigma coords + + ! Compute subdiagonal, diagonal, and superdiagonal matrix elements, + ! along with the right-hand-side vector + + ! upper boundary: set to surfaceTemperature + + subd(1) = 0.0_RKIND + diag(1) = 1.0_RKIND + supd(1) = 0.0_RKIND + rhs(1) = surfaceTemperature + + ! ice interior, layers 1:nVertLevels (matrix elements 2:nVertLevels+1) + + factor = deltat * iceConductivity / (rhoi*cp_ice) / thickness**2 + + subd(2:nVertLevels+1) = -factor * dsigmaTerm(1:nVertLevels,1) + supd(2:nVertLevels+1) = -factor * dsigmaTerm(1:nVertLevels,2) + diag(2:nVertLevels+1) = 1.0_RKIND - subd(2:nVertLevels+1) - supd(2:nVertLevels+1) + rhs(2:nVertLevels+1) = temperature(1:nVertLevels) + heatDissipation(1:nVertLevels)*deltat + + ! basal boundary: + ! for grounded ice, a heat flux is applied + ! for floating ice, the basal temperature is held constant + + !Note: If T(upn) < T_pmp, then require dT/dsigma = H/k * (G + taub*ubas) + ! That is, net heat flux at lower boundary must equal zero. + ! If T(upn) >= Tpmp, then set T(upn) = Tpmp + + if (floatingMask == 1) then + + subd(nVertLevels+2) = 0.0_RKIND + diag(nVertLevels+2) = 1.0_RKIND + supd(nVertLevels+2) = 0.0_RKIND + rhs(nVertLevels+2) = basalTemperature + + else ! grounded ice + + call pressure_melting_point(thickness, pmpTemperatureBed) + + if (abs(basalTemperature - pmpTemperatureBed) < 0.001_RKIND) then + + ! hold basal temperature at pressure melting point + + subd(nVertLevels+2) = 0.0_RKIND + diag(nVertLevels+2) = 1.0_RKIND + supd(nVertLevels+2) = 0.0_RKIND + rhs(nVertLevels+2) = pmpTemperatureBed + + else ! frozen at bed + ! maintain balance of heat sources and sinks + ! (conductive flux, geothermal flux, and basal friction) + + ! Note: basalHeatFlux is generally >= 0, since defined as positive up + + ! calculate dsigma for the bottom layer between the basal boundary and the temperature point above + dsigmaBot = 1.0_RKIND - layerCentersigma(nVertLevels) + + ! backward Euler flux basal boundary condition + subd(nVertLevels+2) = -1.0_RKIND + diag(nVertLevels+2) = 1.0_RKIND + supd(nVertLevels+2) = 0.0_RKIND + rhs(nVertLevels+2) = (basalFrictionFlux + basalHeatFlux) * dsigmaBot*thickness / iceConductivity + + endif ! melting or frozen + + end if ! floating or grounded + + end subroutine temperature_matrix_elements + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine enthalpy_matrix_elements +! +!> \brief MPAS matrix elements for vertical enthalpy solver +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes matrix elements for the tridiagonal solve +!> of the vertical enthalpy profile. +!----------------------------------------------------------------------- + + subroutine enthalpy_matrix_elements(& + deltat, & + nVertLevels, & + layerCentersigma, & + dsigmaTerm, & + floatingMask, & + thickness, & + temperature, & + surfaceTemperature, & + basalTemperature, & + waterfrac, & + enthalpy, & + heatDissipation, & + basalHeatFlux, & + basalFrictionFlux, & + diffusivity, & + subd, & + diag, & + supd, & + rhs) + + ! Note: Matrix elements (subd, supd, diag, rhs) are indexed from 1 to nVertLevels+2, + ! whereas temperature and enthalpy are indexed from 1 to nVertLevels. + ! The first row of the matrix is the equation for surface enthalpy, and + ! the last row is the equation for basal enthalpy. + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real(kind=RKIND), intent(in) :: & + deltat !< Input: time step (s) + + integer, intent(in) :: & + nVertLevels !< Input: number of vertical layers + + real(kind=RKIND), dimension(nVertLevels), intent(in) :: & + layerCentersigma !< Input: sigma coordinate at layer midpoints + + real(kind=RKIND), dimension(:,:), intent(in) :: & + dsigmaTerm !< Input: vertical grid quantities + + integer, intent(in) :: & + floatingMask !< Input: = 1 where ice is floating, else = 0 + + real(kind=RKIND), intent(in) :: & + thickness !< Input: ice thickness (m) + + real(kind=RKIND), dimension(nVertLevels), intent(in) :: & + temperature, & !< Input: ice temperature (deg C) + waterfrac, & !< Input: water fraction (unitless) + enthalpy !< Input: specific enthalpy (J/m^3) + + real(kind=RKIND), intent(in) :: & + surfaceTemperature, & !< Input: surface ice temperature (deg C) + basalTemperature !< Input: basal ice temperature (deg C) + + real(kind=RKIND), dimension(nVertLevels), intent(in) :: & + heatDissipation !< Input: interior heat dissipation (deg/s) + + real(kind=RKIND), intent(in) :: & + basalHeatFlux !< Input: geothermal heat flux (W m-2), positive up + + real(kind=RKIND), intent(in) :: & + basalFrictionFlux !< Input: basal friction heat flux (W m-2), >= 0 + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:), intent(out) :: & + diffusivity !< Output: half-node diffusivity (m^2/s) for enthalpy + ! located at layer interfaces + + real(kind=RKIND), dimension(:), intent(out) :: & + subd, diag, supd, rhs !< Output: matrix elements + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(nVertLevels) :: & + pmpTemperature ! pressure melting point temperature in interior (deg C) + + real(kind=RKIND) :: & + pmpTemperatureBed ! pressure melting point temperature at bed (deg C) + + real(kind=RKIND), dimension(0:nVertLevels+1) :: & + enthalpyTemp ! temperature part of specific enthalpy (J/m^3) + + real(kind=RKIND) :: surfaceEnthalpy ! surface enthalpy = surfaceTemperature*rhoi*cp_ice + real(kind=RKIND) :: basalEnthalpy ! basal enthalpy = basalTemperature*rhoi*cp_ice + real(kind=RKIND) :: dsigmaBot ! bottom layer thickness in sigma coords + real(kind=RKIND) :: diffusivityCold ! cold ice diffusivity + real(kind=RKIND) :: diffusivityTemperate ! temperate ice diffusivity + real(kind=RKIND) :: factor ! term in tridiagonal matrix + real(kind=RKIND) :: dEnthalpy ! enthalpy difference between adjacent layers + real(kind=RKIND) :: dEnthalpyTemp ! difference in temperature component of enthalpy between adjacent layers + real(kind=RKIND) :: avgFactor ! factor for averaging diffusivity, 0 <= avgFactor <= 1 + + integer :: k + + logical, parameter :: & + harmonic_avg = .false. ! if true, take the harmonic average of diffusivity in adjacent layers + ! if false, take the arithmetic average + + diffusivityCold = iceConductivity / (rhoi*cp_ice) + diffusivityTemperate = diffusivityCold / 100.0_RKIND + + ! set enthalpy at surface and bed + surfaceEnthalpy = surfaceTemperature * rhoi*cp_ice + basalEnthalpy = basalTemperature * rhoi*cp_ice + + ! find pmpTemperature for this column (interior nodes and boundary) + + call pressure_melting_point_column(& + layerCenterSigma, & + thickness, & + pmpTemperature) + + call pressure_melting_point(& + thickness, & + pmpTemperatureBed) + + !-------------------------------------------------------------------- + ! Compute the enthalpy diffusivity at layer interfaces. + ! Let d(enth)/dz = the gradient of enthalpy + ! Can write + ! d(enth)/dz = d(enth_T)/dz + d(enth_w)/dz, + ! where + ! enth_T = (1-phi_w) * rhoi*ci*T + ! enth_w = phi_w * rhow*(L + ci*Tpmp) + ! phi_w = water fraction + ! + ! Now let f = d(enth_T)/dz / d(enth)/dz + ! (f -> 0 if f is computed to be negative) + ! For cold ice, f = 1 and diffusivity = diffusivityCold + ! For temperate ice, f ~ 0 and diffusivity = diffusivityTemperate + ! At the interface between cold and temperate ice, + ! f ~ 0 if the temperate ice has large phi_w, but + ! f ~ 1 if the temperate ice has close to zero phi_w. + ! Two ways to average: + ! (1) arithmetic average: diffusivity = f*diffusivityCold + (1-f)*diffusivityTemperate + ! (2) harmonic average: diffusivity = 1 / (f/diffusivityCold + (1-f)/diffusivityTemperate). + ! Both methods have the same asymptotic values at f = 0 or 1, + ! but the arithmetic average gives greater diffusivity for + ! intermediate values. + ! + ! Still to be determined which is more accurate. + ! The harmonic average allows large temperature gradients between the + ! bottom layer and the next layer up; the arithmetic average gives + ! smoother gradients. + ! Currently (as of Oct. 2015), the arithmetic average is the default. + !-------------------------------------------------------------------- + ! + ! At each temperature point, compute the temperature part of the enthalpy. + ! Note: enthalpyTemp = enthalpy for cold ice, enthalpyTemp < enthalpy for temperate ice + + enthalpyTemp(0) = surfaceEnthalpy + do k = 1, nVertLevels + enthalpyTemp(k) = (1.0_RKIND - waterfrac(k)) * rhoi*cp_ice*temperature(k) + enddo + enthalpyTemp(nVertLevels+1) = basalEnthalpy + + ! Compute factors relating the temperature gradient to the total enthalpy gradient. + ! Use these factors to average the diffusivity between adjacent temperature points. + + do k = 1, nVertLevels+1 + + if (k == 1) then + dEnthalpy = enthalpy(k) - surfaceEnthalpy + elseif (k == nVertLevels+1) then + dEnthalpy = basalEnthalpy - enthalpy(k-1) + else + dEnthalpy = enthalpy(k) - enthalpy(k-1) + endif + + dEnthalpyTemp = enthalpyTemp(k) - enthalpyTemp(k-1) ! = dEnthalpy in cold ice, < dEnthalpy in temperate ice + + if (abs(dEnthalpy) > 1.e-20_RKIND * rho_water * latent_heat_ice) then + avgFactor = max(0.0_RKIND, dEnthalpyTemp/dEnthalpy) + avgFactor = min(1.0_RKIND, avgFactor) + else + avgFactor = 0.0_RKIND + endif + + if (harmonic_avg) then ! take a harmonic average + ! This gives slower cooling of temperate layers and allows + ! large temperature gradients between cold and temperate layers + diffusivity(k) = 1.0_RKIND / ((avgFactor/diffusivityCold) + (1.0_RKIND - avgFactor)/diffusivityTemperate) + else ! take an arithmetic average + ! This gives faster cooling of temperate layers and smaller gradients + diffusivity(k) = avgFactor*diffusivityCold + (1.0_RKIND - avgFactor)*diffusivityTemperate + endif + + end do + + ! Compute subdiagonal, diagonal, and superdiagonal matrix elements + ! Assume backward Euler time stepping + + ! upper boundary: set to surfaceEnthalpy = surface temperature*rhoi*cp_ice + subd(1) = 0.0_RKIND + diag(1) = 1.0_RKIND + supd(1) = 0.0_RKIND + rhs(1) = surfaceEnthalpy + + ! ice interior, layers 1:nVertLevels (matrix elements 2:nVertLevels+1) + + factor = deltat / thickness**2 + + subd(2:nVertLevels+1) = -factor * diffusivity(1:nVertLevels) * dsigmaTerm(1:nVertLevels,1) + supd(2:nVertLevels+1) = -factor * diffusivity(2:nVertLevels+1) * dsigmaTerm(1:nVertLevels,2) + diag(2:nVertLevels+1) = 1.0_RKIND - subd(2:nVertLevels+1) - supd(2:nVertLevels+1) + rhs(2:nVertLevels+1) = enthalpy(1:nVertLevels) + heatDissipation(1:nVertLevels) * deltat * rhoi * cp_ice + + ! Note: heatDissipation has units of phi/rhoi/cp_ice, where phi has units of W m^-3.. + ! For an enthalpy calculation, we want just phi, hence heatDissipation * rhoi * cp_ice + + ! Basal boundary: + ! For grounded ice, a heat flux is applied. + ! For floating ice, the basal temperature is held constant. + ! If basalTemperature < T_pmp, then require dT/dsigma = H/k * (G + taub*ubas) + ! That is, net heat flux at lower boundary must equal zero. + ! If basalTemperature >= Tpmp, then set basalTemperature = Tpmp + + if (floatingMask == 1) then + + subd(nVertLevels+2) = 0.0_RKIND + diag(nVertLevels+2) = 1.0_RKIND + supd(nVertLevels+2) = 0.0_RKIND + rhs(nVertLevels+2) = basalEnthalpy + + else ! grounded ice + + if (abs(temperature(nVertLevels) - pmpTemperature(nVertLevels)) < 0.001_RKIND) then + + ! Positive-thickness basal temperate boundary Layer + !WHL - Not sure whether this condition is ideal. It implies that basalEnthalpy = enthalpy(nVertLevels). + + subd(nVertLevels+2) = -1.0_RKIND + diag(nVertLevels+2) = 1.0_RKIND + supd(nVertLevels+2) = 0.0_RKIND + rhs(nVertLevels+2) = 0.0_RKIND + + elseif (abs(basalTemperature - pmpTemperatureBed) < 0.001_RKIND) then ! melting + + ! Zero-thickness basal temperate boundary layer + ! Hold basal temperature at pressure melting point + subd(nVertLevels+2) = 0.0_RKIND + diag(nVertLevels+2) = 1.0_RKIND + supd(nVertLevels+2) = 0.0_RKIND + rhs(nVertLevels+2) = pmpTemperatureBed * rhoi*cp_ice + + else ! cold ice at bed + + ! Frozen at bed: Maintain balance of heat sources and sinks + ! (conductive flux, geothermal flux, and basal friction) + + ! Note: basalHeatFlux is generally >= 0, since defined as positive up + + ! calculate dsigma for the bottom layer between the basal boundary and the temperature point above + dsigmaBot = (1.0_RKIND - layerCentersigma(nVertLevels)) + + subd(nVertLevels+2) = -1.0_RKIND + diag(nVertLevels+2) = 1.0_RKIND + supd(nVertLevels+2) = 0.0_RKIND + rhs(nVertLevels+2) = (basalFrictionFlux + basalHeatFlux) * dsigmaBot * thickness *rhoi * cp_ice / iceConductivity + + endif ! melting or frozen + + end if ! floating or grounded + + end subroutine enthalpy_matrix_elements + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine basal_melt_grounded_ice +! +!> \brief MPAS melt rate at base of grounded ice +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes the melt rate at the base of grounded ice. +!> Internal melting is included in this melt rate. +!----------------------------------------------------------------------- + + subroutine basal_melt_grounded_ice(& + config_thermal_solver, & + deltat, & + nCellsSolve, & + nVertLevels, & + layerInterfaceSigma, & + layerCenterSigma, & + iceMask, & + floatingMask, & + thickness, & + temperature, & + basalTemperature, & + waterfrac, & + enthalpy, & + basalFrictionFlux, & + basalHeatFlux, & + basalConductiveFlux, & + basalWaterThickness, & + groundedBasalMassBal) + + !----------------------------------------------------------------- + ! Note: For the temperature solver, any internal temperatures above + ! the pressure melting point are reset to Tpmp. Excess energy + ! is applied toward melting with immediate drainage to the bed. + ! For the enthalpy solver, any meltwater in excess of the maximum allowed + ! meltwater fraction (0.01 by default) is drained to the bed. + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + character(len=StrKIND), intent(in) :: & + config_thermal_solver !< Input: thermal solver option (temperature or enthalpy) + + real(kind=RKIND), intent(in) :: & + deltat !< Input: time step (s) + + integer, intent(in) :: & + nCellsSolve !< Input: number of locally owned cells + + integer, intent(in) :: & + nVertLevels !< Input: number of vertical layers + + real(kind=RKIND), dimension(:), intent(in) :: & + layerInterfaceSigma !< Input: vertical coordinate at layer interfaces + + real(kind=RKIND), dimension(:), intent(in) :: & + layerCenterSigma !< Input: vertical coordinate at layer centers + + real(kind=RKIND), dimension(:,:), intent(in) :: & + enthalpy !< Input: enthalpy + + real(kind=RKIND), dimension(:), intent(in) :: & + thickness, & !< Input: ice thickness (m) + basalFrictionFlux, & !< Input: basal frictional heating flux (W m-2), >= 0 + basalHeatFlux, & !< Input: geothermal heating flux (W m-2), positive up + basalConductiveFlux, & !< Input: heat conducted from ice interior to bed (W m-2), positive down + basalWaterThickness !< Input: thickness of basal water layer (m) + + integer, dimension(:), intent(in) :: & + iceMask, & !< Input: = 1 where ice exists (thickness > config_thermal_thickness), else = 0 + floatingMask !< Input: = 1 where ice is floating, else = 0 + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:,:), intent(inout) :: & + temperature !< Input/output: temperature (deg C) + + real(kind=RKIND), dimension(:), intent(inout) :: & + basalTemperature !< Input/output: basal temperature (deg C) + + real(kind=RKIND), dimension(:,:), intent(inout) :: & + waterfrac !< Input/output: water fraction + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:), intent(out):: & + groundedBasalMassBal !< Output: basal mass balance for grounded ice (kg/m^2/s): < 0 for melting, > 0 for freeze-on + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + logical, pointer :: config_thermal_calculate_bmb + integer :: k, iCell + + real(kind=RKIND), dimension(nVertLevels) :: & + pmpTemperature ! pressure melting point temperature in ice interior + + real(kind=RKIND) :: pmpTemperatureBed ! pressure melting point temperature at bed + real(kind=RKIND) :: netBasalFlux ! heat flux available for basal melting (W/m^2) + real(kind=RKIND) :: layerThickness ! layer thickness (m) + real(kind=RKIND) :: meltEnergy ! energy available for internal melting (J/m^2) + real(kind=RKIND) :: internalMeltRate ! internal melt rate, transferred to bed (m/s) + real(kind=RKIND) :: excessWater ! thickness of excess meltwater (m) + + real(kind=RKIND), parameter :: & + maxWaterfrac = 0.01_RKIND ! maximum allowed water fraction; excess drains to bed + + real(kind=RKIND), parameter :: & + eps11 = 1.0e-11_RKIND ! small number + + + ! Compute melt rate for grounded ice + + groundedBasalMassBal(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + + if (iceMask(iCell) == 1 .and. floatingMask(iCell) == 0) then ! ice is present and grounded + + ! Compute basal mass balance + ! Note: basalMassBal < 0 for melting, > 0 for freeze-on + ! basalFrictionFlux >= 0 by definition + ! basalHeatFlux is positive up, so generally basalHeatFlux >= 0 + ! basalConductiveFlux is positive down, so basalConductiveFlux < 0 for heat flowing + ! from the bed toward the surface + ! + ! This equation allows for freeze-on (basalMassBal > 0) if the conductive term + ! (basalConductiveFlux, positive down) is carrying enough heat away from the boundary. + ! But freeze-on requires a local water supply, basalWaterThickness > 0. + ! When basalWaterThickness = 0, we reset the bed temperature to a value slightly below the melting point. + ! + !TODO - For the enthalpy scheme, deal with the rare case that the bottom layer melts completely, + ! and overlying layers with a different enthalpy also melt. + + netBasalFlux = basalFrictionFlux(iCell) + basalHeatFlux(iCell) + basalConductiveFlux(iCell) ! W/m^2 + + if (abs(netBasalFlux) < eps11) then ! netBasalFlux might be slightly different from zero + ! because of rounding errors; if so, then zero out + netBasalFlux = 0.0_RKIND + endif + + if (trim(config_thermal_solver) == 'enthalpy') then + groundedBasalMassBal(iCell) = -netBasalFlux / (latent_heat_ice * rhoi - enthalpy(nVertLevels,iCell)) + else ! temperature solver + groundedBasalMassBal(iCell) = -netBasalFlux / (latent_heat_ice * rhoi) ! m/s + endif + + endif ! ice is present and grounded + + ! Add internal melting + ! Note: It is possible to have internal melting for floating ice. + ! If so, this melting will be switched later from groundedBasalMassBal to floatingBasalMassBal + + if (iceMask(iCell) == 1) then ! ice is present + + if (trim(config_thermal_solver) == 'enthalpy') then + + ! Add internal melting associated with waterfrac > maxWaterfrac (1%) + !TODO - Add correction for rhoi/rhow here? Or melting ice that is already partly melted? + + do k = 1, nVertLevels + if (waterfrac(k,iCell) > maxWaterfrac) then + + ! compute melt rate associated with excess water + excessWater = (waterfrac(k,iCell) - maxWaterfrac) * thickness(iCell) * (layerInterfaceSigma(k+1) & + - layerInterfaceSigma(k)) ! m + internalMeltRate = excessWater / deltat + + ! transfer meltwater to the bed + ! Note: It is possible to have internal melting for floating ice. + ! If so, then this melting will later be switched from groundedBasalMassBall to floatingBasalMassBal. + groundedBasalMassBal(iCell) = groundedBasalMassBal(iCell) - internalMeltRate ! m/s + + ! reset waterfrac to max value + waterfrac(k,iCell) = maxWaterfrac + + endif ! waterfrac > maxWaterfrac + enddo ! k + + elseif (config_thermal_solver == 'temperature') then + + ! Add internal melting associated with T > Tpmp + + call pressure_melting_point_column(& + layerCentersigma, & + thickness(iCell), & + pmpTemperature) + + do k = 1, nVertLevels + if (temperature(k,iCell) > pmpTemperature(k)) then + + ! compute excess energy available for melting + layerThickness = thickness(iCell) * (layerInterfaceSigma(k+1) - layerInterfaceSigma(k)) ! m + meltEnergy = rhoi*cp_ice * (temperature(k,iCell) - pmpTemperature(k)) * layerThickness ! J/m^2 + internalMeltRate = meltEnergy / (rhoi * latent_heat_ice * deltat) ! m/s + + ! transfer meltwater to the bed + ! Note: It is possible to have internal melting for floating ice. + ! If so, then this melting will later be switched from groundedBasalMassBall to floatingBasalMassBal. + groundedBasalMassBal(iCell) = groundedBasalMassBal(iCell) - internalMeltRate ! m/s + + ! reset T to Tpmp + temperature(k,iCell) = pmpTemperature(k) + + endif ! temperature > pmpTemperature + enddo ! k + + endif ! config_thermal_solver + + endif ! ice is present + + ! Cap basal temperature at pressure melting point + + if (iceMask(iCell) == 1 .and. floatingMask(iCell) == 0) then ! ice is present and grounded + + call pressure_melting_point(& + thickness(iCell), & + pmpTemperatureBed) + + basalTemperature(iCell) = min (basalTemperature(iCell), pmpTemperatureBed) + + ! If freeze-on was computed above (basalMassBal > 0) and Tbed = Tpmp but no basal water is present, + ! then set basalTemperature < Tpmp. + ! Note: In the matrix element subroutines, we solve for Tbed (instead of holding it at Tpmp) when Tbed < -0.001. + ! With an offset here of 0.01, we will solve for T_bed at the next timestep. + ! Note: I don't think energy conservation is violated here, because no energy is associated with + ! the infinitesimally thin layer at the bed. + + if (groundedBasalMassBal(iCell) > 0.0_RKIND .and. basalWaterThickness(iCell) == 0.0_RKIND .and. & + basalTemperature(iCell) >= pmpTemperatureBed) then + basalTemperature(iCell) = pmpTemperatureBed - 0.01_RKIND + endif + + endif ! ice is present and grounded + + ! change units from m/s to kg/m2/s + groundedBasalMassBal(iCell) = groundedBasalMassBal(iCell) * rhoi + + enddo ! iCell + + ! Check if we want thermal calculation to apply to BMB + ! In some idealized configurations, we want to evolve temperature (and possibly have it + ! coupled to velocity), but we do not want the geometry to evolve based on the temeprature + ! evolution. In this case, we zero groundedBasalMassBal. This is cleaner than avoiding + ! its calculation in the lines above, because in this situation we still want the various + ! temperature fields to be "set back" to appropriate values as if melt had occurred. + ! This violates conservation of energy but is desired for some tests. + call mpas_pool_get_config(liConfigs, 'config_thermal_calculate_bmb', config_thermal_calculate_bmb) + if (.not. config_thermal_calculate_bmb) then + groundedBasalMassBal(:) = 0.0_RKIND + endif + + end subroutine basal_melt_grounded_ice + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine basal_melt_floating_ice +! +!> \brief MPAS melt rate at base of floating ice +!> \author William Lipscomb +!> \date November 2015 +!> \details +!> This routine computes the melt rate at the base of floating ice. +!----------------------------------------------------------------------- + + subroutine basal_melt_floating_ice(& + config_basal_mass_bal_float, & + nCellsSolve, & + xCell, & + daysSinceStart, & + iceMask, & + floatingMask, & + lowerSurface, & + bedTopography, & + config_sea_level, & + config_bmlt_float_flux, & + config_bmlt_float_xlimit, & + floatingBasalMassBal, & + err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + character(len=strKIND), intent(in) :: & + config_basal_mass_bal_float !< Input: option for computing basal mass balance for floating ice + + integer, intent(in) :: & + nCellsSolve !< Input: number of locally owned cells + + real(kind=RKIND), dimension(:), intent(in) :: & + xCell !< Input: x coordinate for each cell + + real(kind=RKIND), pointer, intent(in) :: daysSinceStart + + real(kind=RKIND), dimension(:), intent(in) :: & + lowerSurface, & !< Input: lower surface elevation (m) + bedTopography !< Input: elevation of bed topography (m) + + real(kind=RKIND), intent(in) :: & + config_sea_level !< Input: sea level relative to z = 0 (m) + + integer, dimension(:), intent(in) :: & + iceMask, & !< Input: = 1 where ice exists (thickness > config_thermal_thickness), else = 0 + floatingMask !< Input: = 1 where ice is floating, else = 0 + + ! inputs for constant melt rate as in MISMIP+ Ice2 experiments + real(kind=RKIND), intent(in) :: & + config_bmlt_float_flux, & !< Input: constant heat flux (W/m^2) applied to the base of floating ice; positive upward + !< MISMIP+ default value = 975.17 W/m^2 (gives melt rate of 100 m/yr) + config_bmlt_float_xlimit !< Input: x value (m) defining region where bmlt_float_flux is applied; + !< melt only where abs(x) > xlimit + !< MISMIP+ default value = 480 km + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:), intent(out):: & + floatingBasalMassBal !< Output: basal mass balance for floating ice + + integer, intent(out) :: err + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real(kind=RKIND) :: & + bmlt_float_rate ! constant basal melt rate (m/s) + ! = config_bmlt_float_flux / (rhoi*latent_heat_ice) + integer :: iCell + + real(kind=RKIND) :: hCavity ! depth of ice cavity beneath floating ice (m) + real(kind=RKIND) :: zDraft ! draft of floating ice (m below sea level) + + ! basal melting parameters for MISMIP+ experiment + ! Note: These could be made user-configurable, but are hardwired for now because there are no plans + ! to run MISMIP+ with different values + real(kind=RKIND), parameter :: & + bmlt_float_omega = 0.20_RKIND / scyr, & ! time scale for basal melting (s^-1) + ! MISMIP+ default value = 0.2 yr^-1 + bmlt_float_h0 = 75._RKIND, & ! scale for sub-shelf cavity thickness (m) + ! MISMIP+ default value = 75 m + bmlt_float_z0 = -100._RKIND ! scale for ice draft (m) + ! MISMIP+ default value = -100 m + + ! basal melt parameters for Seroussi param. + real (kind=RKIND) :: slopeSer ! slope of relation between depth and melt rate + real (kind=RKIND) :: interceptSer ! depth at which melting goes to 0 + real (kind=RKIND) :: maxMeltSer ! maximum allowable melt rate + real (kind=RKIND) :: sillDepth ! depth below which melt rate no longer increases + real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_amplitude + real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_period + real (kind=RKIND), pointer :: config_basal_mass_bal_seroussi_phase + + + ! Compute melt rate for floating ice + err = 0 + + ! initialize to zero melt + floatingBasalMassBal(:) = 0.0_RKIND + if (trim(config_basal_mass_bal_float) == 'none') then + ! Do nothing, handled in calling routine + + elseif (trim(config_basal_mass_bal_float) == 'file') then + ! Do nothing, handled in calling routine + + elseif (trim(config_basal_mass_bal_float) == 'constant') then + + ! set melt rate to a constant value for floating ice + ! allow basal melt in ice-free ocean cells, in case ice is advected to those cells by the transport scheme + + bmlt_float_rate = config_bmlt_float_flux / (rhoi*latent_heat_ice) ! convert W/m^2 to m/s + + floatingBasalMassBal(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + if ( floatingMask(iCell) == 1 .or. & + (bedTopography(iCell) < config_sea_level .and. iceMask(iCell) == 0) ) then + ! ice is present and floating, or ice-free ocean + + ! Provided xCell > bmlt_float_xlimit, prescribe a uniform basal melt rate. + ! The default is 0.0, but for MISMIP+ the prescribed value of xlimit is 480 km. + if (abs(xCell(iCell)) >= config_bmlt_float_xlimit) then ! basal melting is allowed + floatingBasalMassBal(iCell) = -bmlt_float_rate + endif + + endif ! ice is present and floating + + enddo ! iCell + + ! change units from m/s to kg/m2/s + floatingBasalMassBal(:) = floatingBasalMassBal(:) * rhoi + + elseif (trim(config_basal_mass_bal_float) == 'mismip') then + + ! compute melt rate (m/s) based on bed depth and cavity thickness + ! The MISMIP+ formula is as follows: + ! + ! bmlt_float = omega * tanh(H_c/H_0) * max(z_0 - z_d, 0) + ! + ! where H_c = lsrf - topg is the cavity thickness + ! z_d = lsrf - eus is the ice draft + ! omega = a time scale = 0.2 yr^{-1} by default + ! H_0 = 75 m by default + ! z_0 = -100 m by default + + ! allow basal melt in ice-free ocean cells, in case ice is advected to those cells by the transport scheme + + floatingBasalMassBal(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + + if ( floatingMask(iCell) == 1 .or. & + (bedTopography(iCell) < config_sea_level .and. iceMask(iCell) == 0) ) then + ! ice is present and floating, or ice-free ocean + + hCavity = lowerSurface(iCell) - bedTopography(iCell) + zDraft = lowerSurface(iCell) - config_sea_level + floatingBasalMassBal(iCell) = -bmlt_float_omega * tanh(hCavity/bmlt_float_h0) * max(bmlt_float_z0 - & + zDraft, 0.0_RKIND) + + endif ! ice is present and floating + enddo ! iCell + + ! change units from m/s to kg/m2/s + floatingBasalMassBal(:) = floatingBasalMassBal(:) * rhoi + + elseif (trim(config_basal_mass_bal_float) == 'seroussi') then + + ! Melt rate parameterization from: + ! Seroussi, H., Y. Nakayama, E. Larour, D. Menemenlis, M. Morlighem, E. Rignot, and A. Khazendar (2017), Continued retreat of Thwaites Glacier, West Antarctica, controlled by bed topography and ocean circulation, Geophys. Res. Lett., 1-9, doi:10.1002/2017GL072910. + ! for Thwaites Glacier. + ! Specifically, this is a linear fit of melt with shelf draft from the Supplemental Information, Figure S1. + ! The linear relation is modified by a: + ! * depth above which there is no melt (Antarctic Surface Water saturation) + ! * a maximum melt rate (Circumpolar Deep Water saturation) + ! * a depth below which melt stops increasing (minimum sill height) + + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_amplitude', config_basal_mass_bal_seroussi_amplitude) ! meters + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_period', config_basal_mass_bal_seroussi_period) ! years + call mpas_pool_get_config(liConfigs, 'config_basal_mass_bal_seroussi_phase', config_basal_mass_bal_seroussi_phase) ! cycles + + slopeSer = 0.088_RKIND ! slope of relation between depth and melt rate (melt (m/yr) per depth (m)) + interceptSer = -100.0_RKIND ! depth (m) at which melting goes to 0 (negative meaning below sea level) + maxMeltSer = 50.0_RKIND ! maximum allowable melt rate (m/yr) (positive meaning melting) + sillDepth = -650.0_RKIND ! depth below which melt stops increasing (m) (negative meaning below sea level) + + if (config_basal_mass_bal_seroussi_period <= 0.0_RKIND) then + call mpas_log_write("Value for config_basal_mass_bal_seroussi_period has to be a positive real value.", MPAS_LOG_ERR) + err = ior(err, 1) + endif + + ! Modify intercept height for variability parameters + interceptSer = interceptSer + config_basal_mass_bal_seroussi_amplitude * & + sin( (2.0_RKIND * pii / config_basal_mass_bal_seroussi_period) * (daysSinceStart/365.0_RKIND) & + + 2.0_RKIND * pii * config_basal_mass_bal_seroussi_phase) + + ! Initialize before computing + floatingBasalMassBal(:) = 0.0_RKIND + + do iCell = 1, nCellsSolve + + ! Shut off melt at an arbitrary shallow depth to discourage ice from disappearing. + if ( (floatingMask(iCell) == 1) .and. (lowerSurface(iCell) < -10.0_RKIND) ) then + ! ice is present and floating + + zDraft = lowerSurface(iCell) - config_sea_level + ! Coefficients for m/yr melt rate (in units of Seroussi figure but without negative meaning melting) + floatingBasalMassBal(iCell) = max(-1.0_RKIND * maxMeltSer, min(0.0_RKIND, slopeSer * (max(zDraft, sillDepth) - interceptSer))) + + endif ! ice is present + enddo ! iCell + + ! change units from m/yr to kg/m2/s + floatingBasalMassBal(:) = floatingBasalMassBal(:) * rhoi / scyr + + else + + call mpas_log_write('Unknown option selected for config_basal_mass_bal_float:' // trim(config_basal_mass_bal_float), MPAS_LOG_ERR) + err = ior(err, 1) + + endif ! config_basal_mass_bal_float + + + end subroutine basal_melt_floating_ice + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine pressure_melting_point_column +! +!> \brief MPAS compute pressure melting point temperature +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes the pressure melting point temperature in each layer +!> of an ice column, given the thickness. +!----------------------------------------------------------------------- + + subroutine pressure_melting_point_column(& + layerCenterSigma, & + thickness, & + pmpTemperature) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(in) :: & + layerCenterSigma !< Input: sigma coordinate at midpoint of each layer + + real (kind=RKIND), intent(in) :: & + thickness !< Input: ice thickness + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(out) :: & + pmpTemperature !< Output: pressure melting point temperature (deg C) + + ! Compute the pressure melting point temperature in the column + + pmpTemperature(:) = - iceMeltingPointPressureDependence * rhoi * gravity * thickness * layerCenterSigma(:) + + end subroutine pressure_melting_point_column + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine pressure_melting_point +! +!> \brief MPAS compute pressure melting point temperature +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine computes the pressure melting point temperature at a +!> given depth in an ice column. +!----------------------------------------------------------------------- + + subroutine pressure_melting_point(& + depth, & + pmpTemperature) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real (kind=RKIND), intent(in) :: & + depth !< Input: depth in column + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real (kind=RKIND), intent(out) :: & + pmpTemperature !< Output: pressure melting point temperature (deg C) + + ! Compute the pressure melting point temperature + + pmpTemperature = - iceMeltingPointPressureDependence * rhoi * gravity * depth + + end subroutine pressure_melting_point + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine li_compute_pressure_melting_point_fields +! +!> \brief MPAS compute pressure melting point temperature for entire domain +!> \author Matthew Hoffman +!> \date February 2016 +!> \details +!> This routine computes the pressure melting point temperature for the +!> entire interior ice domain and the basal boundary. +!> It makes use of existing routines for PMP calculations. +!> This is created for diagnostic output purposes, and is not used by the +!> thermal solver. +!----------------------------------------------------------------------- + + subroutine li_compute_pressure_melting_point_fields(& + nCells, & + thickness, & + layerCenterSigma, & + pmpTemperature, & + basalPmpTemperature) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, intent(in) :: nCells !< Input: number of cells + + real (kind=RKIND), dimension(:), intent(in) :: & + thickness !< Input: thickness field + + real (kind=RKIND), dimension(:), intent(in) :: & + layerCenterSigma !< Input: layer sigma levels + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(out) :: & + pmpTemperature !< Output: pressure melting point temperature (K) for interior ice + + real (kind=RKIND), dimension(:), intent(out) :: & + basalPmpTemperature !< Output: pressure melting point temperature (K) at ice-bed interface + + ! Local variables + integer :: iCell + + do iCell = 1, nCells + ! Calculate internal ice PMP + call pressure_melting_point_column(layerCenterSigma, thickness(iCell), pmpTemperature(:,iCell)) + ! Calculate basal PMP + call pressure_melting_point(thickness(iCell), basalPmpTemperature(iCell)) + enddo + + ! Convert from Celsius to Kelvin + pmpTemperature = pmpTemperature + kelvin_to_celsius + basalPmpTemperature = basalPmpTemperature + kelvin_to_celsius + + end subroutine li_compute_pressure_melting_point_fields + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ! routine tridiag_solver +! +!> \brief MPAS solve tridiagonal matrix +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine solves a tridiagonal matrix equation, given the matrix +!> coefficients and right-hand side. +!----------------------------------------------------------------------- + + !TODO - Move the tridiag solver to a utility module? + subroutine tridiag_solver(a,b,c,x,y) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:), intent(in) :: a !< Input: Lower diagonal; a(1) is ignored + real(kind=RKIND), dimension(:), intent(in) :: b !< Input: Main diagonal + real(kind=RKIND), dimension(:), intent(in) :: c !< Input: Upper diagonal; c(n) is ignored + real(kind=RKIND), dimension(:), intent(in) :: y !< Input: Right-hand side + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:), intent(out) :: x !< Output: Unknown vector + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(size(a)) :: aa + real(kind=RKIND), dimension(size(a)) :: bb + + integer :: n, i + + n = size(a) + + aa(1) = c(1) / b(1) + bb(1) = y(1) / b(1) + + do i = 2, n + aa(i) = c(i) / (b(i)-a(i)*aa(i-1)) + bb(i) = (y(i)-a(i)*bb(i-1)) / (b(i)-a(i)*aa(i-1)) + end do + + x(n) = bb(n) + + do i = n-1, 1, -1 + x(i) = bb(i) - aa(i)*x(i+1) + end do + + end subroutine tridiag_solver + + !*********************************************************************** + + end module li_thermal + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + + + diff --git a/src/core_landice/mode_forward/mpas_li_time_integration.F b/src/core_landice/mode_forward/mpas_li_time_integration.F new file mode 100644 index 0000000000..5eb958fbbe --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_time_integration.F @@ -0,0 +1,234 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_time_integration +! +!> \brief MPAS land ice time integration driver +!> \author Matt Hoffman +!> \date 17 April 2011 +!> \details +!> This module contains the main driver routine for calling +!> time integration schemes +! +!----------------------------------------------------------------------- + +module li_time_integration + + use mpas_derived_types + use mpas_pool_routines + use mpas_timekeeping + use mpas_log + + use li_time_integration_fe + use li_setup + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_timestep + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + contains + + +!*********************************************************************** +! +! routine li_timestep +! +!> \brief Advance model state forward in time by the specified time step +!> \author Matt Hoffman +!> \date 20 April 2012 +!> \details +!> This routine advances model state forward in time by the specified time step. +!> Input: domain - current model state in time level 1 plus mesh data +!> Output: domain - upon exit, time level 2 contains +!> model state advanced forward in time by dt seconds +!----------------------------------------------------------------------- + subroutine li_timestep(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + ! Pools pointers + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + character (len=StrKIND), pointer :: xtime + real (kind=RKIND), pointer :: daysSinceStart + character (len=StrKIND), pointer :: simulationStartTime + character (len=StrKIND), pointer :: config_time_integration + logical, pointer :: config_adaptive_timestep + real (kind=RKIND), pointer :: deltat !< the current time step in seconds, on each block + + ! Other local variables + type (MPAS_TimeInterval_type) :: timeStepInterval !< the current time step as an interval + real (kind=RKIND) :: dtSeconds !< the current time step in seconds, local variable + type (MPAS_Time_Type) :: currTime !< current time as time type + character(len=StrKIND) :: timeStamp !< current time as a string + type (MPAS_Time_type) :: simulationStartTime_timeType + + integer :: err_tmp + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_time_integration', config_time_integration) + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep', config_adaptive_timestep) + + currTime = mpas_get_clock_time(domain % clock, MPAS_NOW, err_tmp) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=err_tmp) + + ! === + ! === Non-adaptive timestep: Get dt in seconds + ! === + if (.not. config_adaptive_timestep) then + ! Get the interval at this point in time - will be fixed for nonadaptive timestep, but need to get it out of the clock + timeStepInterval = mpas_get_clock_timestep(domain % clock, ierr=err_tmp) + err = ior(err,err_tmp) + ! Convert the clock's time interval into a dt in seconds to be used by the time stepper, + ! using the currTime as the start time for this interval. + ! (We want to do this conversion before doint the timestep and advancing the clock because the dt + ! in seconds may change as the base time changes, and we want the old time as the base time. + ! For example, the number of seconds in a year will be longer in a leap year. + ! That is why nonadaptive timesteps have to be handled before the timestep, while + ! adaptive timesteps are handled after the timestep. + ! It may be possible to have them handled in the same place within li_tendency.F if we want to embed it that deeply.) + call mpas_get_timeInterval(timeStepInterval, StartTimeIn=currTime, dt=dtSeconds, ierr=err_tmp) + err = ior(err,err_tmp) + else + ! initialize the dt to 0 when using the adaptive timestepper + ! it will be set by the time stepper but needs to avoid triggering an error in tend_layerThickness_fo_upwind + dtSeconds = 0.0_RKIND + endif + + ! Assign current value to variabels in all blocks + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + deltat = dtSeconds + + block => block % next + end do + + + ! === + ! === Perform timestep + ! === + !call mpas_log_write('Using ' // trim(config_time_integration) // ' time integration.') + select case (config_time_integration) + case ('forward_euler') + call li_time_integrator_forwardeuler(domain, err_tmp) + case ('rk4') + call mpas_log_write(trim(config_time_integration) // ' is not currently supported.', MPAS_LOG_ERR) + err_tmp = 1 + case default + call mpas_log_write(trim(config_time_integration) // ' is not a valid land ice time integration option.', MPAS_LOG_ERR) + err_tmp = 1 + end select + err = ior(err,err_tmp) + + + ! === + ! === Adaptive timestep: update clock information + ! === + ! Set time step in clock object since the time step could have changed + ! Need to get value out of a block, but all blocks should have the same value, so just use first block + if (config_adaptive_timestep) then + block => domain % blocklist + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + ! convert dt in seconds to timeInterval type + call mpas_set_timeInterval(timeStepInterval, dt=deltat, ierr=err_tmp) + err = ior(err,err_tmp) + ! update the clock with the timeInterval + call mpas_set_clock_timestep(domain % clock, timeStepInterval, err_tmp) + err = ior(err,err_tmp) + endif + + ! === + ! === Update clock information + ! === + ! Advance clock - needed to wait until after time step is completed in case the dt has changed! + call mpas_advance_clock(domain % clock) + currTime = mpas_get_clock_time(domain % clock, MPAS_NOW, err_tmp) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=err_tmp) + err = ior(err, err_tmp) + call mpas_log_write(' Completed timestep. New time is: ' // trim(timeStamp), flushNow=.true.) + + + block => domain % blocklist + do while (associated(block)) + ! Assign the time stamp for this time step + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'xtime', xtime) + xtime = timeStamp + + ! compute time since start of simulation, in days + call mpas_pool_get_array(meshPool, 'simulationStartTime', simulationStartTime) + call mpas_pool_get_array(meshPool, 'daysSinceStart',daysSinceStart) + call mpas_set_time(simulationStartTime_timeType, dateTimeString=simulationStartTime) + call mpas_get_timeInterval(currTime - simulationStartTime_timeType, dt=daysSinceStart) + daysSinceStart = daysSinceStart / seconds_per_day + + block => block % next + end do + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_timestep.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_timestep + + +end module li_time_integration diff --git a/src/core_landice/mode_forward/mpas_li_time_integration_fe.F b/src/core_landice/mode_forward/mpas_li_time_integration_fe.F new file mode 100644 index 0000000000..18178aac71 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_time_integration_fe.F @@ -0,0 +1,956 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_time_integration_fe +! +!> \brief MPAS land ice Forward Euler time integration scheme +!> \author Matt Hoffman +!> \date 17 April 2011 +!> \details +!> This module contains the Forward Euler time integration scheme +! +!----------------------------------------------------------------------- + +module li_time_integration_fe + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timer + use mpas_vector_reconstruction + use mpas_log + + use li_advection + use li_calving, only: li_calve_ice, li_restore_calving_front + use li_thermal, only: li_thermal_solver, li_basal_melt_floating_ice + use li_diagnostic_vars + use li_setup + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_time_integrator_forwardeuler + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + contains +!*********************************************************************** + + +!*********************************************************************** +! +! routine li_time_integrator_forwardeuler +! +!> \brief Forward Euler time integration scheme +!> \author Matthew Hoffman +!> \date 10 January 2012 +!> \details +!> This routine performs Forward Euler time integration. +! +!----------------------------------------------------------------------- + subroutine li_time_integrator_forwardeuler(domain, err) + + use li_subglacial_hydro + use li_velocity + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: & + domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + integer :: err_tmp + + logical, pointer :: config_restore_calving_front + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_restore_calving_front', config_restore_calving_front) + +! === Prepare for advection (including CFL checks) =========== +! This has to come first currently, because it sets the time step! + call mpas_timer_start("advection prep") + call prepare_advection(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("advection prep") + +! === Basal melting for floating ice =========== + call mpas_timer_start("basal melting for floating ice") + call li_basal_melt_floating_ice(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("basal melting for floating ice") + +! === Implicit column physics (vertical temperature diffusion) =========== + call mpas_timer_start("vertical therm") + call li_thermal_solver(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("vertical therm") + +! === Compute new state for prognostic variables ================================== + call mpas_timer_start("advect thickness and tracers") + call advection_solver(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("advect thickness and tracers") + +! === Update subglacial hydrology =========== +! It's not clear where the best place to call this should be. +! Seems sensible to put it after thermal evolution is complete to get updated basal melting source term. +! Also seems (might be?) better to put it after geometry evolution. +! We want it before the velocity solve since the hydro model can control the velo basal b.c. + call mpas_timer_start("subglacial hydro") + call li_SGH_solve(domain, err_tmp) + err = ior(err, err_tmp) + call mpas_timer_stop("subglacial hydro") + +! === Calve ice ======================== + call mpas_timer_start("calve_ice") + + if (config_restore_calving_front) then + + ! restore the calving front to its initial position; calving options are ignored + call li_restore_calving_front(domain, err_tmp) + err = ior(err, err_tmp) + + else + + ! ice calving + call li_calve_ice(domain, err_tmp) + err = ior(err, err_tmp) + + endif + + call mpas_timer_stop("calve_ice") + + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'cellMask') + call mpas_dmpar_field_halo_exch(domain, 'edgeMask') + call mpas_dmpar_field_halo_exch(domain, 'vertexMask') + call mpas_timer_stop("halo updates") + +! === Solve Velocity ===================== + ! During time-stepping, we always solveVelo + call li_velocity_solve(domain, solveVelo=.true., err=err_tmp) + err = ior(err, err_tmp) + +! === Calculate diagnostic variables for new state ===================== + + call li_calculate_diagnostic_vars(domain, err=err_tmp) + err = ior(err, err_tmp) + + +! === Cleanup & Misc. ============================= + + ! === error check + if (err == 1) then + call mpas_log_write("An error has occurred in li_time_integrator_forwardeuler.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_time_integrator_forwardeuler + + + +!*********************************************************************** +!*********************************************************************** +! Private subroutines: +!*********************************************************************** +!*********************************************************************** + +!*********************************************************************** +! +! routine prepare_advection +! +!> \brief Preparation for advection, including CFL diagnostics +!> \author Matthew Hoffman and William Lipscomb +!> \date January 2016 +!> \details +!> This routine does preparatory calculations for advection of thickness +!> and tracers: +!> (1) Compute layer normal velocities. +!> (2) Compute the advective CFL limit (and optionally, the diffusive CFL limit). +!> (3) If config_adaptive_timestep = .true., then set deltat based on CFL info. +!> These calculations were previously done at the same time as advection, but +!> now are done at the start of the timestep to support an adaptive time step. +!----------------------------------------------------------------------- + + subroutine prepare_advection(domain, err) + + use mpas_timekeeping + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (dm_info), pointer :: dminfo + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: scratchPool + + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity + real (kind=RKIND), dimension(:,:), pointer :: layerNormalVelocity + + logical, pointer :: config_print_thickness_advection_info + logical, pointer :: config_adaptive_timestep + logical, pointer :: config_adaptive_timestep_include_DCFL + + integer :: & + allowableAdvecDtProcNumberHere, & + allowableAdvecDtProcNumber + + real (kind=RKIND) :: & + allowableAdvecDt, & + allowableAdvecDtOnProc, & + allowableAdvecDtAllProcs + + type (MPAS_TimeInterval_type) :: & + allowableAdvecDtOnProcInterval, & + allowableAdvecDtAllProcsInterval + + character (len=StrKIND) :: & + allowableAdvecDtOnProcString, & + allowableAdvecDtAllProcsString + + integer :: & + allowableDiffDtProcNumberHere, & + allowableDiffDtProcNumber + + real (kind=RKIND) :: & + allowableDiffDt, & + allowableDiffDtOnProc, & + allowableDiffDtAllProcs + + type (MPAS_TimeInterval_type) :: & + allowableDiffDtOnProcInterval, & + allowableDiffDtAllProcsInterval + + character (len=StrKIND) :: & + allowableDiffDtOnProcString, & + allowableDiffDtAllProcsString + + real (kind=RKIND), pointer :: & + allowableDtACFL, & + allowableDtDCFL + + real (kind=RKIND), pointer :: deltat ! variable in blocks + + real (kind=RKIND) :: dtSeconds ! local variable + + integer :: err_tmp + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep', config_adaptive_timestep) + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_include_DCFL', config_adaptive_timestep_include_DCFL) + + allowableAdvecDtAllProcs = 0.0_RKIND + allowableDiffDtAllProcs = 0.0_RKIND + + dminfo => domain % dminfo + + ! Initialize + + err = 0 + + allowableAdvecDtOnProc = 1.0e36_RKIND ! set to large number + allowableDiffDtOnProc = 1.0e36_RKIND ! set to large number + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + call mpas_pool_get_array(velocityPool, 'layerNormalVelocity', layerNormalVelocity) + + ! compute normal velocities and advective CFL limit for this block + + call li_layer_normal_velocity( & + meshPool, & + normalVelocity, & + layerNormalVelocity, & + allowableAdvecDt, & + err_tmp) + + err = ior(err, err_tmp) + + allowableAdvecDtOnProc = min(allowableAdvecDtOnProc, allowableAdvecDt) + + ! Calculate diffusive CFL timestep, if needed + ! This used to be only calculated if (config_adaptive_timestep_include_DCFL) but for simplicity, + ! now it is always calculated. That allows assessment of the DCFL even when it is not being obeyed + ! by the adaptive time stepper. Timer is added here to monitor if this unnecessary calculation becomes significant. + call mpas_timer_start("calculate apparent diffusivity") + call li_calculate_apparent_diffusivity(meshPool, velocityPool, scratchPool, geometryPool, allowableDiffDt) + allowableDiffDtOnProc = min(allowableDiffDtOnProc, allowableDiffDt) + call mpas_timer_stop("calculate apparent diffusivity") + + ! Note: The ACFL and DCFL timesteps could be calculated in + ! diagnostic_variables_solve_after_velocity. In that case, we could also add + ! variables to store their values, rather than just relying on the values + ! written to the log files. However, the current logic only calculates these + ! values if certain config options are set, so that would need to be dealt with. + ! If the ACFL and DCFL timesteps are moved to diagnostic_variables_solve_after_velocity, + ! Then the setting of the timestep value could happen at the beginning of the timestep, + ! probably in li_timestep rather than here. That might be cleaner, but I have + ! persisted with doing it here, because since the calculation of ACFL/DCFL have a lot + ! in common with the advection calculation, and it seems kind of silly to do those calculations + ! on the previous time step. That said, the calculations are pretty cheap. + + block => block % next + end do + + + ! Local advective CFL info + call mpas_set_timeInterval(allowableAdvecDtOnProcInterval, dt=allowableAdvecDtOnProc, ierr=err_tmp) + err = ior(err,err_tmp) + call mpas_get_timeInterval(allowableAdvecDtOnProcInterval, timeString=allowableAdvecDtOnProcString, ierr=err_tmp) + err = ior(err,err_tmp) + + if (config_print_thickness_advection_info) then + call mpas_log_write(' Maximum allowable time step on THIS processor based on advective CFL is (Days_hhh:mmm:sss): ' & + // trim(allowableAdvecDtOnProcString)) + endif + + + ! Local diffusive CFL info + ! This used to be only calculated if (config_adaptive_timestep_include_DCFL) but for simplicity, + ! now it is always calculated. That allows assessment of the DCFL even when it is not being obeyed + ! by the adaptive time stepper. + call mpas_set_timeInterval(allowableDiffDtOnProcInterval, dt=allowableDiffDtOnProc, ierr=err_tmp) + err = ior(err,err_tmp) + call mpas_get_timeInterval(allowableDiffDtOnProcInterval, timeString=allowableDiffDtOnProcString, ierr=err_tmp) + err = ior(err,err_tmp) + + if (config_print_thickness_advection_info) then + call mpas_log_write(' Maximum allowable time step on THIS processor based on diffusive CFL is (Days_hhh:mmm:sss): ' & + // trim(allowableDiffDtOnProcString)) + endif + + + ! These calculation could be (and used to be) restricted to only if we are + ! printing advection debug information or adaptive timestepping, because + ! it requires 2 unnecessary MPI communications for each of the ACFL and DCFL + ! However, those communications are probably small relative to other costs in + ! the model, so now they always happen. The timer has been added to allow + ! assessment of that assumption. + call mpas_timer_start("calculate global CFL limits") + + ! Determine ACFL limit on all procs + call mpas_dmpar_min_real(dminfo, allowableAdvecDtOnProc, allowableAdvecDtAllProcs) + + ! Determine which processor has the limiting CFL + if (allowableAdvecDtOnProc == allowableAdvecDtAllProcs) then + allowableAdvecDtProcNumberHere = dminfo % my_proc_id + else + allowableAdvecDtProcNumberHere = -1 + endif + + call mpas_dmpar_max_int(dminfo, allowableAdvecDtProcNumberHere, allowableAdvecDtProcNumber) + call mpas_set_timeInterval(allowableAdvecDtAllProcsInterval, dt=allowableAdvecDtAllProcs, ierr=err_tmp) + err = ior(err,err_tmp) + call mpas_get_timeInterval(allowableAdvecDtAllProcsInterval, timeString=allowableAdvecDtAllProcsString, ierr=err_tmp) + err = ior(err,err_tmp) + + ! Repeat for diffusive CFL + ! This used to be only calculated if (config_adaptive_timestep_include_DCFL) but for simplicity, + ! now it is always calculated. That allows assessment of the DCFL even when it is not being obeyed + ! by the adaptive time stepper. + + ! Determine DCFL limit on all procs + call mpas_dmpar_min_real(dminfo, allowableDiffDtOnProc, allowableDiffDtAllProcs) + + ! Determine which processor has the limiting CFL + if (allowableDiffDtOnProc == allowableDiffDtAllProcs) then + allowableDiffDtProcNumberHere = dminfo % my_proc_id + else + allowableDiffDtProcNumberHere = -1 + endif + + call mpas_dmpar_max_int(dminfo, allowableDiffDtProcNumberHere, allowableDiffDtProcNumber) + call mpas_set_timeInterval(allowableDiffDtAllProcsInterval, dt=allowableDiffDtAllProcs, ierr=err_tmp) + err = ior(err,err_tmp) + call mpas_get_timeInterval(allowableDiffDtAllProcsInterval, timeString=allowableDiffDtAllProcsString, ierr=err_tmp) + err = ior(err,err_tmp) + + call mpas_timer_stop("calculate global CFL limits") + + + ! Write messages if they are turned on + if (config_print_thickness_advection_info) then + call mpas_log_write(' Maximum allowable time step for all processors based on advective CFL is (Days_hhh:mmm:sss): ' & + // trim(allowableAdvecDtAllProcsString) // ' Time step is limited by processor number $i', & + intArgs=(/allowableAdvecDtProcNumber/)) + if (config_adaptive_timestep_include_DCFL) then + call mpas_log_write(' Maximum allowable time step for all processors based on diffusive CFL is (Days_hhh:mmm:sss): '& + // trim(allowableDiffDtAllProcsString) // ' Time step is limited by processor number $i', & + intArgs=(/allowableDiffDtProcNumber/)) + endif + endif + + + ! Set adaptive timestep if needed + if (config_adaptive_timestep) then + call set_timestep(allowableAdvecDtAllProcs, allowableDiffDtAllProcs, domain % clock, dtSeconds, err_tmp) + err = ior(err,err_tmp) + ! Set new value on all blocks + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + + deltat = dtSeconds + + block => block % next + end do + else + ! If not adaptive, Get dt from any block to check for CFL violation below + block => domain % blocklist + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + end if + + + ! Check for CFL error before finishing + if (deltat > allowableAdvecDtOnProc) then + call mpas_log_write('Advective CFL violation on this processor. ' // & + 'Maximum allowable time step for this processor is (Days_hhh:mmm:sss): ' // trim(allowableAdvecDtOnProcString), MPAS_LOG_ERR) + err = ior(err,1) + endif + + ! Local diffusive CFL info + if ( (config_adaptive_timestep_include_DCFL) .and. (deltat > allowableDiffDtOnProc) ) then + call mpas_log_write('Diffusive CFL violation on this processor. ' // & + 'Maximum allowable time step for this processor is (Days_hhh:mmm:sss): ' // trim(allowableDiffDtOnProcString), MPAS_LOG_WARN) + endif + + if (err > 0) then + call mpas_log_write('Error in calculating thickness advection (possibly CFL violation)', MPAS_LOG_ERR) + endif + + + ! set CFL variables if they have been calculated - every block should be set to the same value! + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'allowableDtACFL', allowableDtACFL) + allowableDtACFL = allowableAdvecDtAllProcs + call mpas_pool_get_array(meshPool, 'allowableDtDCFL', allowableDtDCFL) + allowableDtDCFL = allowableDiffDtAllProcs + + block => block % next + end do + + + ! Halo updates + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'layerNormalVelocity') + call mpas_timer_stop("halo updates") + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in prepare_advection.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine prepare_advection + +!*********************************************************************** +! +! routine advection_solver +! +!> \brief Advect thickness and tracers +!> \author Matthew Hoffman and William Lipscomb +!> \date September 2013; revised December 2015 +!> \details +!> This routine advects thickness and tracers as part of forward Euler +!> time integration. +!> Note: This routine replaces much of the old subroutines calculate_tendencies +!> and update_prognostics. The CFL diagnostics that were previously +!> in calculate tendencies are now in prepare_advection. +!----------------------------------------------------------------------- + + subroutine advection_solver(domain, err) + + use mpas_timekeeping + use li_mask + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (dm_info), pointer :: dminfo + type (block_type), pointer :: block + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: scratchPool + + real (kind=RKIND), pointer :: deltat ! variable in blocks + real (kind=RKIND), dimension(:), pointer :: thicknessOld + real (kind=RKIND), dimension(:), pointer :: thicknessNew + real (kind=RKIND), dimension(:), pointer :: thickness + + real (kind=RKIND), dimension(:,:), pointer :: temperature + real (kind=RKIND), dimension(:,:), pointer :: waterfrac + real (kind=RKIND), dimension(:,:), pointer :: enthalpy + + integer, pointer :: nCells + + character (len=StrKIND), pointer :: config_thickness_advection + character (len=StrKIND), pointer :: config_tracer_advection + + logical, pointer :: config_print_thickness_advection_info + + !TODO - Replace masktmp with a scratch field? + integer, dimension(:), allocatable :: masktmp ! Temporary mask for assessing new thickness field + + integer :: err_tmp + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_thickness_advection', config_thickness_advection) + call mpas_pool_get_config(liConfigs, 'config_tracer_advection', config_tracer_advection) + call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) + + dminfo => domain % dminfo + + ! Halo updates + ! Note: The layer thickness and tracers must be up to date in halos before calling the advection subroutines. + ! The thermal tracers (temperature, waterfrac, enthalpy) are updated at the end of li_thermal_solver. + ! But thickness (which is used by subroutine li_advection_thickness_tracers) needs an update here. TODO: confirm this + + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'thickness') + call mpas_timer_stop("halo updates") + + + ! === + ! === Calculate layerThicknessEdge, which is needed for advection + ! === + if (trim(config_thickness_advection) == 'fo' .or. trim(config_tracer_advection) == 'fo') then + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call calculate_layerThicknessEdge(meshPool, geometryPool, velocityPool, err_tmp) + err = ior(err,err_tmp) + + block => block % next + end do + + ! Halo update layerThicknessEdge - outer halo may be wrong due to requiring velocity + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'layerThicknessEdge') + call mpas_timer_stop("halo updates") + endif ! fo advection option + + + + ! === + ! === Advect thickness and tracers + ! === + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + + if (trim(config_thickness_advection) == 'fo' .and. trim(config_tracer_advection) == 'fo') then + + ! Note: This subroutine requires that thickness and tracers are correct in halos + + if (config_print_thickness_advection_info) then + call mpas_log_write('Advect thickness and tracers, dt=$r', realArgs=(/deltat/)) + endif + + call li_advection_thickness_tracers(& + deltat, & + meshPool, & + velocityPool, & + geometryPool, & + thermalPool, & + scratchPool, & + err_tmp, & + advectTracersIn = .true.) + + err = ior(err,err_tmp) + + elseif (trim(config_thickness_advection) == 'fo' .and. trim(config_tracer_advection) == 'none') then + + if (config_print_thickness_advection_info) then + call mpas_log_write('Advect thickness (but not tracers), dt=$r', realArgs=(/deltat/)) + endif + + call li_advection_thickness_tracers(& + deltat, & + meshPool, & + velocityPool, & + geometryPool, & + thermalPool, & + scratchPool, & + err_tmp, & + advectTracersIn = .false.) + + !call mpas_log_write("errtmp=$i",intArgs=(/err_tmp/)) + err = ior(err,err_tmp) + + endif + + block => block % next + end do + + ! Reset negative thicknesses to zero if needed + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(geometryPool, 'thickness', thickness, timeLevel=1) + + + allocate( masktmp(nCells + 1) ) + masktmp = 0 + + where (thickness < 0.0_RKIND) + masktmp = 1 + thickness = 0.0_RKIND + end where + + if (config_print_thickness_advection_info) then + + if (sum(masktmp) > 0) then + call mpas_log_write(' Cells with negative thickness (set to 0): $i', intArgs=(/sum(masktmp)/)) + endif + + ! Count how many cells have ice. + masktmp = 0 + where (thickness > 0.0_RKIND) + masktmp = 1 + end where + call mpas_log_write(' Cells with nonzero thickness: $i', intArgs=(/sum(masktmp)/)) + + endif + + deallocate(masktmp) + + block => block % next + end do + + ! Halo updates + call mpas_timer_start("halo updates") + + call mpas_dmpar_field_halo_exch(domain, 'thickness') + call mpas_dmpar_field_halo_exch(domain, 'temperature') + call mpas_dmpar_field_halo_exch(domain, 'waterfrac') + call mpas_dmpar_field_halo_exch(domain, 'enthalpy') + + call mpas_timer_stop("halo updates") + + ! Update mask and geometry + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + call li_update_geometry(geometryPool) + + block => block % next + end do + + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in advection_solver.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine advection_solver + +!*********************************************************************** +! +! routine set_timestep +! +!> \brief Adjusts the time step based on the CFL condition. +!> \author Matthew Hoffman +!> \date 23 Jan 2014 +!> \details +!> This routine sdjusts the time step based on the CFL condition. +! +!----------------------------------------------------------------------- + subroutine set_timestep(allowableAdvecDt, allowableDiffDt, clock, dtSeconds, err) + use mpas_timekeeping + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(in) :: allowableAdvecDt + real (kind=RKIND), intent(in) :: allowableDiffDt + type (MPAS_Clock_type), intent(in) :: clock + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(out) :: dtSeconds !< Output: time step in seconds determined by this routine + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + logical, pointer :: config_adaptive_timestep_include_DCFL + real (kind=RKIND), pointer :: config_adaptive_timestep_CFL_fraction + real (kind=RKIND), pointer :: config_max_adaptive_timestep + real (kind=RKIND), pointer :: config_min_adaptive_timestep + type (MPAS_Time_type) :: nextForceTime, currTime + type (MPAS_TimeInterval_type) :: intervalToNextForceTime + real (kind=RKIND) :: secondsToNextForceTime + real (kind=RKIND) :: allowableDt + real (kind=RKIND) :: proposedDt + integer :: err_tmp + + err = 0 + err_tmp = 0 + + + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_CFL_fraction', config_adaptive_timestep_CFL_fraction) + call mpas_pool_get_config(liConfigs, 'config_max_adaptive_timestep', config_max_adaptive_timestep) + call mpas_pool_get_config(liConfigs, 'config_min_adaptive_timestep', config_min_adaptive_timestep) + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_include_DCFL', config_adaptive_timestep_include_DCFL) + + if (config_adaptive_timestep_include_DCFL) then + allowableDt = min(allowableAdvecDt, allowableDiffDt) + else + allowableDt = allowableAdvecDt + endif + + ! Take minimum of the max adaptive timestep setting and the allowable dt from the CFL condition + proposedDt = min(allowableDt * config_adaptive_timestep_CFL_fraction, config_max_adaptive_timestep) + ! Round down the proposed dt to avoid complications with fractional seconds + ! (some timekeeping-related functionality, like restarts, don't support them) + ! (need to perform floor with an 8-bit integer to allow up 293 billion years in seconds) + proposedDt = real(floor(proposedDt, KIND=8), RKIND) + + ! Check if we need to force a timestep length to hit the target interval + currTime = mpas_get_clock_time(clock, MPAS_NOW, err_tmp) + !print *, 'curr', currTime % t % YR, currTime % t % basetime % S, currTime % t % basetime % Sn, currTime % t % basetime % Sd + err = ior(err,err_tmp) + nextForceTime = mpas_alarm_get_next_ring_time(clock, 'adaptiveTimestepForceInterval') + !print *, 'ring', nextForceTime % t % YR, nextForceTime % t % basetime % S, nextForceTime % t % basetime % Sn, & + ! nextForceTime % t % basetime % Sd + intervalToNextForceTime = nextForceTime - currTime + !print *, 'int', intervalToNextForceTime % ti % YR, intervalToNextForceTime % ti % MM, & + ! intervalToNextForceTime % ti % basetime % S, intervalToNextForceTime % ti % basetime % Sn, & + ! intervalToNextForceTime % ti % basetime % Sd + ! Due to roundoff errors, we might be just shy of the desired time. + ! To avoid this, add one to the numerator of the fractional seconds to + ! make sure we get pushed over the edge. The way ESMF does fractional + ! seconds, this means we get the desired interval to better than 1 part per 100 million seconds + ! Note that even though this is a *very* tiny fudge factor, it does not + ! affect conservation within MPAS-LI, but it could have a very, very tiny + ! effect on a climate model that thinks we ran for, say, 10 years, but we + ! actually ran for 10 years +/- 1e-8 seconds. + !intervalToNextForceTime % ti % basetime % Sn = intervalToNextForceTime % ti % basetime % Sn + 1 + ! Note: commenting above line because it should no longer be relevant after rounding down to full second on line 815, + ! but we still include a check for fractional seconds. + call mpas_get_timeInterval(intervalToNextForceTime, dt=secondsToNextForceTime, ierr=err_tmp) + err = ior(err,err_tmp) + if (secondsToNextForceTime - real(floor(secondsToNextForceTime, KIND=8), RKIND) /= 0.0_RKIND) then + call mpas_log_write("set_timestep found secondsToNextForceTime not equal to 0.0: $r, decimal part=$r", MPAS_LOG_ERR, realArgs=(/secondsToNextForceTime, secondsToNextForceTime - real(floor(secondsToNextForceTime, KIND=8), RKIND)/)) + err = ior(err, 1) + endif + !print *, proposedDt, secondsToNextForceTime + + ! --- Actually set the dt here --- + dtSeconds = min(proposedDt, secondsToNextForceTime) + + call mpas_log_write(' Setting time step (days) to: $r', realArgs=(/dtSeconds / (86400.0_RKIND)/)) + if (dtSeconds < config_min_adaptive_timestep) then + call mpas_log_write('New deltat is less than config_min_adaptive_timestep.', MPAS_LOG_ERR) + err = ior(err, 1) + endif + + !-------------------------------------------------------------------- + end subroutine set_timestep + + +!*********************************************************************** +! +! routine calculate_layerThicknessEdge +! +!> \brief Calculates layerThicknessEdge +!> \author Matthew Hoffman +!> \date 23 May 2017 +!> \details +!> This routine calculates FO upwind thickness on 3d layer edges. +! +!----------------------------------------------------------------------- + subroutine calculate_layerThicknessEdge(meshPool, geometryPool, velocityPool, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer, intent(inout) :: meshPool !< Input/Output: mesh pool + type (mpas_pool_type), pointer, intent(inout) :: geometryPool !< Input/Output: geometryPool + type (mpas_pool_type), pointer, intent(inout) :: velocityPool !< Input/Output: velocityPool + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, layerThicknessEdge, normalVelocity + integer, dimension(:,:), pointer :: cellsOnEdge + integer, pointer :: nEdges, nVertLevels + integer :: iEdge, cell1, cell2, k + real (kind=RKIND) :: VelSign + + err = 0 + + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(geometryPool, 'thickness', thickness, timeLevel = 1) + call mpas_pool_get_array(geometryPool, 'layerThickness', layerThickness, timeLevel = 1) + call mpas_pool_get_array(geometryPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + + ! Note: SIA velocity solver uses its own local calculation of h_edge that is always 2nd order. + ! Note: ocn_diagnostic_solve in mpas_ocn_tendency.F has 2, 3, & 4th order calculations for h_edge that can be used. + + ! given thickness, compute layerThickness + call li_calculate_layerThickness(meshPool, thickness, layerThickness) + + ! If using FO-Upwind then h_edge must be FO. + do iEdge=1,nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + do k=1, nVertLevels + ! Calculate h on edges using first order + VelSign = sign(1.0_RKIND, normalVelocity(k, iEdge)) + layerThicknessEdge(k,iEdge) = max(VelSign * layerThickness(k, cell1), & + VelSign * (-1.0_RKIND) * layerThickness(k, cell2)) + ! + velocity goes from index 1 to 2 in the cellsOnEdge array. + ! Doug does the calculation as: h_edge = max(VelSign, 0.0) * h1 - min(VelSign, 0.0) * h2 + !!! ! Calculate h on edges using second order + !!! layerThicknessEdge(k,iEdge) = 0.5_RKIND * (layerThickness(k, cell1) + layerThickness(k, cell2)) + end do + end do + + ! Note: The outmost layerThicknessEdge may be wrong if its upwind cell is off this block - + ! halo update should be done if this variable will be used. + + !WHL - debug - Commented out, but might be useful for future debugging +! if (config_print_thickness_advection_info) then +! call mpas_log_write(' ') +! call mpas_log_write('End of timestep: iCell (global), new thickness:' +! do iCell = 1, nCells +! if (thickness(iCell) > 0.0_RKIND) then +! call mpas_log_write('$i $r', intArgs=(/indexToCellID(iCell)/), realArgs=(/thickness(iCell)/)) +! endif +! enddo +! endif + + !-------------------------------------------------------------------- + end subroutine calculate_layerThicknessEdge + + +end module li_time_integration_fe + diff --git a/src/core_landice/mode_forward/mpas_li_velocity.F b/src/core_landice/mode_forward/mpas_li_velocity.F new file mode 100644 index 0000000000..3cf93c79b5 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_velocity.F @@ -0,0 +1,990 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_velocity +! +!> \MPAS land-ice velocity driver +!> \author William Lipscomb +!> \date 10 January 2012 +!> \details +!> This module contains the routines for calling dycores +!> for calculating ice velocity. +!> +! +!----------------------------------------------------------------------- + + +module li_velocity + + use mpas_derived_types + use mpas_pool_routines + use mpas_timer + use mpas_log + + use li_velocity_external + use li_velocity_simple + use li_sia + use li_setup + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_velocity_init, & + li_velocity_finalize, & + li_velocity_block_init, & + li_velocity_solve + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_velocity_init +! +!> \brief Initializes velocity solver +!> \author Xylar Asay-Davis +!> \date 18 January 2012 +!> \details +!> This routine initializes the ice velocity solver. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_init(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + character (len=StrKIND), pointer :: config_velocity_solver + integer :: err_tmp + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + + call mpas_log_write('Using ' // trim(config_velocity_solver) // ' velocity solver.') + select case (config_velocity_solver) + case ('none') + ! Do nothing + case ('sia') + call li_sia_init(domain, err) + case ('simple') + call li_velocity_simple_init(domain, err) + case ('L1L2', 'FO', 'Stokes') + call li_velocity_external_init(domain, err) + case default + call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) + err = 1 + end select + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_init.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_init + + + +!*********************************************************************** +! +! routine li_velocity_block_init +! +!> \brief Initializes velocity solver +!> \author William Lipscomb +!> \date 10 January 2012 +!> \details +!> This routine initializes each block of the ice velocity solver. +! +!----------------------------------------------------------------------- + subroutine li_velocity_block_init(block, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (block_type), intent(inout) :: & + block !< Input/Output: block object + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + character (len=StrKIND), pointer :: config_velocity_solver + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + + select case (config_velocity_solver) + case ('none') + ! Do nothing + case ('sia') + call li_sia_block_init(block, err) + case ('simple') + call li_velocity_simple_block_init(block, err) + case ('L1L2', 'FO', 'Stokes') + call li_velocity_external_block_init(block, err) + case default + call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) + err = 1 + end select + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_block_init.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + + end subroutine li_velocity_block_init + + + +!*********************************************************************** +! +! routine li_velocity_solve +! +!> \brief Solver for calculating ice velocity +!> \author William Lipscomb +!> \date 10 January 2012 +!> \details +!> This routine calls velocity solvers. +! +!----------------------------------------------------------------------- + subroutine li_velocity_solve(domain, solveVelo, err) + + use mpas_vector_reconstruction + use li_mask + use li_advection + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + logical, intent(in) :: solveVelo !< Input: logical determining if + !< velocity should be solved, or if an existing solution should be + !< used to calculate the diagnostic fields related to velocty + !< (e.g. on a restart) + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + ! Note: domain is passed in because halo updates are needed in this routine + ! and halo updates have to happen outside block loops, which requires domain. + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: thermalPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: velocityPool + ! pointers to get from pools + character (len=StrKIND), pointer :: config_velocity_solver + logical, pointer :: config_do_velocity_reconstruction_for_external_dycore + logical, pointer :: config_print_velocity_cleanup_details + real (kind=RKIND), pointer :: config_dynamic_thickness + logical, pointer :: config_adaptive_timestep_include_DCFL + integer, pointer :: nEdgesSolve + integer, pointer :: nEdges + integer, pointer :: nVertInterfaces + integer, dimension(:), pointer :: edgeMask, cellMask, vertexMask, vertexMaskOld + integer, dimension(:,:), pointer :: dirichletVelocityMaskOld, dirichletVelocityMaskNew + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, normalVelocityInitial + real (kind=RKIND), dimension(:,:), pointer :: uReconstructX, uReconstructY, uReconstructZ, & + uReconstructZonal, uReconstructMeridional + real (kind=RKIND), dimension(:,:), pointer :: edgeNormalVectors + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: surfaceSpeed, basalSpeed + real (kind=RKIND), dimension(:), pointer :: normalSlopeEdge + real (kind=RKIND), dimension(:), pointer :: dcEdge + integer, dimension(:), pointer :: floatingEdges + integer, dimension(:,:), pointer :: cellsOnEdge + integer, dimension(:,:), pointer :: cellsOnVertex + integer, dimension(:,:), pointer :: verticesOnEdge + real (kind=RKIND), dimension(:), pointer :: upperSurface + integer, dimension(:), pointer :: indexToEdgeID + integer, pointer :: anyDynamicVertexMaskChanged + integer, pointer :: dirichletMaskChanged + ! truly local variables + integer :: cell1, cell2 + integer :: cell3, cell4, thisCell + integer :: vertex1, vertex2 + integer :: iEdge + integer :: iCell + integer :: uphillMarginEdgesFixed + integer :: err_tmp + integer :: k + real (kind=RKIND) :: maxThicknessOnProc, maxThicknessAllProcs + real (kind=RKIND) :: xVelEdge, yVelEdge + integer :: blockDynamicVertexMaskChanged, procDynamicVertexMaskChanged + integer :: blockDirichletMaskChanged, procDirichletMaskChanged + + call mpas_timer_start("velocity solve") + + err_tmp = 0 + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + call mpas_pool_get_config(liConfigs, 'config_do_velocity_reconstruction_for_external_dycore', & + config_do_velocity_reconstruction_for_external_dycore) + call mpas_pool_get_config(liConfigs, 'config_print_velocity_cleanup_details', config_print_velocity_cleanup_details) + call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) + call mpas_pool_get_config(liConfigs, 'config_adaptive_timestep_include_DCFL', config_adaptive_timestep_include_DCFL) + + uphillMarginEdgesFixed = 0 + + + ! In some situations (first time level of a restart), we don't want to actually solve + ! velocity again, but we do want to calculate the diagnostic fields related to velocity. + if (solveVelo) then + + + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'thickness') + call mpas_timer_stop("halo updates") + + ! Update mask just to be safe (might be redundant) + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + call li_calculate_mask(meshPool, velocityPool, geometryPool, err_tmp) + call li_update_geometry(geometryPool) + + block => block % next + end do + + ! Update halos on masks - the outermost cells/edges/vertices may be wrong for mask components that need neighbor information + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'cellMask') + call mpas_dmpar_field_halo_exch(domain, 'edgeMask') + call mpas_dmpar_field_halo_exch(domain, 'vertexMask') + call mpas_timer_stop("halo updates") + + ! This first block for calculating normalSlopeEdge is also needed for the DCFL calculation + ! that could occur with any velocity solver + if ( (trim(config_velocity_solver) == 'sia') .or. (config_adaptive_timestep_include_DCFL) ) then + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'normalSlopeEdge', normalSlopeEdge) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + ! Calculate normal slope + do iEdge = 1, nEdges + ! Only calculate slope for edges that have ice on at least one side. + if ( li_mask_is_dynamic_ice(edgeMask(iEdge)) ) then + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + ! Calculate slope at edge + normalSlopeEdge(iEdge) = (upperSurface(cell1) - upperSurface(cell2) ) / dcEdge(iEdge) + else + normalSlopeEdge(iEdge) = 0.0_RKIND + endif + end do ! edges + + block => block % next + end do + endif + + if ( (trim(config_velocity_solver) == 'L1L2') .or. & + (trim(config_velocity_solver) == 'FO') .or. & + (trim(config_velocity_solver) == 'Stokes') ) then + + ! External solvers may not be able to cope with no ice in the domain, so determine if that is the case + ! Don't bother checking this with SIA because it requires an extra global reduce + maxThicknessOnProc = 0.0_RKIND ! initialize to + procDynamicVertexMaskChanged = 0 + procDirichletMaskChanged = 0 + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + maxThicknessOnProc = max(maxThicknessOnProc, maxval(thickness)) + + ! The interface expects an array where 1's are floating edges and 0's are non-floating edges. + call mpas_pool_get_array(velocityPool, 'floatingEdges', floatingEdges) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + floatingEdges = li_mask_is_floating_ice_int(edgeMask) + call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel=1) + call li_calculate_extrapolate_floating_edgemask(meshPool, vertexMask, floatingEdges) + + ! Determine if the vertex mask changed during this time step for this block (needed for external dycores) + ! TODO: There may be some aspects of the mask that are ok change for external dycores, + ! but for now just check the whole thing. + call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMaskOld, timeLevel=2) + if ( sum(li_mask_is_dynamic_ice_int(vertexMask) - li_mask_is_dynamic_ice_int(vertexMaskOld)) /= 0 ) then + blockDynamicVertexMaskChanged = 1 + else + blockDynamicVertexMaskChanged = 0 + endif + !print *, 'blockVertexMaskChanged ', blockVertexMaskChanged + ! Determine if any blocks on this processor had a change to the vertex mask + procDynamicVertexMaskChanged = max(procDynamicVertexMaskChanged, blockDynamicVertexMaskChanged) + !print *,'procVertexMaskChanged', procVertexMaskChanged + + ! Also check to see if the Dirichlet b.c. mask has changed + call mpas_pool_get_array(velocityPool, 'dirichletVelocityMask', dirichletVelocityMaskOld, timeLevel=2) + call mpas_pool_get_array(velocityPool, 'dirichletVelocityMask', dirichletVelocityMaskNew, timeLevel=1) + if ( sum(dirichletVelocityMaskNew - dirichletVelocityMaskOld) /= 0 ) then + blockDirichletMaskChanged = 1 + else + blockDirichletMaskChanged = 0 + endif + ! Determine if any blocks on this processor had a change to the vertex mask + procDirichletMaskChanged = max(procDirichletMaskChanged, blockDirichletMaskChanged) + + ! Set beta for solver to use + ! (this could potentially be applied to an SIA solver, so it is calculated in this module) + call calculate_beta(block, err_tmp) + err = ior(err, err_tmp) + + + block => block % next + end do + + ! check for thickness on any proc + call mpas_dmpar_max_real(domain % dminfo, maxThicknessOnProc, maxThicknessAllProcs) + ! Update halos on mask - the outermost cells/edges/vertices may be wrong for + ! mask components that need neighbor information + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'floatingEdges') + call mpas_timer_stop("halo updates") + + ! Determine if the vertex mask has changed on any processor and store the value for later use + ! (need to exit the block loop to do so) + ! TODO Update all blocks with result (if ever support multiple blocks) + call mpas_pool_get_array(velocityPool, 'anyDynamicVertexMaskChanged', anyDynamicVertexMaskChanged) + call mpas_dmpar_max_int(domain % dminfo, procDynamicVertexMaskChanged, anyDynamicVertexMaskChanged) + !print *,'anyDynamicVertexMaskChanged', anyDynamicVertexMaskChanged + ! Do the same for the Dirichlet b.c. mask + call mpas_pool_get_array(velocityPool, 'dirichletMaskChanged', dirichletMaskChanged) + call mpas_dmpar_max_int(domain % dminfo, procDirichletMaskChanged, dirichletMaskChanged) + !print *,'dirichletMaskChanged', dirichletMaskChanged + + endif + + + + ! External solvers do not support multiple blocks but the MPAS SIA solver does. + block => domain % blocklist + do while (associated(block)) + ! Get variables from pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(meshPool, 'indexToEdgeID', indexToEdgeID) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) + call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + + + ! Solve velocity + select case (config_velocity_solver) + case ('none') + + ! Do nothing + + case ('sia') + + call li_sia_solve(meshPool, geometryPool, thermalPool, velocityPool, err_tmp) + + case ('L1L2', 'FO', 'Stokes') + + if (maxThicknessAllProcs < config_dynamic_thickness) then + ! External dycores may not be able to handle case when there is no ice + call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) + call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) + call mpas_pool_get_array(velocityPool, 'uReconstructZ', uReconstructZ) + normalVelocity = 0.0_RKIND + uReconstructX = 0.0_RKIND + uReconstructY = 0.0_RKIND + uReconstructZ = 0.0_RKIND + call mpas_log_write("Notice: Skipping velocity solve because there is no dynamic ice in domain.", MPAS_LOG_WARN) + else + call li_velocity_external_solve(meshPool, geometryPool, thermalPool, scratchPool, velocityPool, err_tmp) + endif + + case('simple') + + ! Set the normal velocities to the values computed at initialization + ! Note: The reason these velocities are reset to the initial values during each time step is that + ! they may have been altered during the previous time step (e.g., set to zero for non-dynamic edges). + call mpas_pool_get_array(velocityPool, 'normalVelocityInitial', normalVelocityInitial) + normalVelocity = normalVelocityInitial + + ! Fix up these velocities by setting them to zero on non-dynamic edges + do iEdge = 1, nEdgesSolve + if (.not.(li_mask_is_dynamic_ice(edgeMask(iEdge)))) then + normalVelocity(:,iEdge) = 0.0_RKIND + endif + enddo + + case default + + call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) + err = 1 + call mpas_timer_stop("velocity solve") + return + + end select + err = ior(err, err_tmp) + + ! Some "quality control" of normalVelocity + do iEdge = 1, nEdgesSolve + + ! Don't allow normalVelocity on edges where an unglaciated cell with + ! higher elevation neighbors a glaciated cell. Some velocity solvers + ! could generate a nonzero velocity on these edges. In the case of a + ! velocity directed into the ice sheet, this probably does no harm + ! for advection because there is no ice to advect in, but it could result + ! in overly restrictive advective CFL conditions. In the case of velocity + ! directed out of the ice sheet, this would result in uphill flow which is + ! highly unlikely to be physically correct. (It could be possible in a HO + ! stress balance where stress transfer 'overrides' the driving stress, but + ! this seems unlikely to be significant.) Therefore, always zero velocity + ! in these situations. + if ( li_mask_is_dynamic_margin(edgeMask(iEdge)) ) then + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + if ( ( li_mask_is_dynamic_ice(cellMask(cell1)) .and. & + upperSurface(cell2) > upperSurface(cell1) ) .or. & + ( li_mask_is_dynamic_ice(cellMask(cell2)) .and. & + upperSurface(cell1) > upperSurface(cell2) ) ) then + if (config_print_velocity_cleanup_details) then + call mpas_log_write("Notice: Nonzero velocity has been calculated on an 'uphill' margin edge. " // & + "normalVelocity here has been set to 0. Location is edge index: $i", intArgs=(/indexToEdgeID(iEdge)/)) + endif + normalVelocity(:, iEdge) = 0.0_RKIND + uphillMarginEdgesFixed = uphillMarginEdgesFixed + 1 + endif + endif + + enddo + + block => block % next + end do + + if (uphillMarginEdgesFixed > 0) then + call mpas_log_write("Notice: Nonzero velocity has been calculated on 'uphill' margin edge(s). normalVelocity has " & + // "been set to 0 at these location(s). Number of edges affected on this processor: $i", & + intArgs=(/uphillMarginEdgesFixed/)) + endif + + ! --- + ! --- update halos on velocity + ! --- + call mpas_timer_start("halo updates") + call mpas_dmpar_field_halo_exch(domain, 'normalVelocity') + call mpas_timer_stop("halo updates") + + endif ! if solveVelo + + + ! -- Now that velocity is solved, update diagnostic fields related to velocity -- + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + + ! --- + ! --- Calculate reconstructed velocities + ! --- + ! do this after velocity halo update in case velocities on the 1-halo edge are wrong (depends on velocity solver) + ! Still do this even if we didn't calculate velocity because on a restart these will be defined at the initial time. + call mpas_pool_get_dimension(meshPool, 'nVertInterfaces', nVertInterfaces) + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) + call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) + call mpas_pool_get_array(velocityPool, 'uReconstructZ', uReconstructZ) + call mpas_pool_get_array(velocityPool, 'uReconstructZonal', uReconstructZonal) + call mpas_pool_get_array(velocityPool, 'uReconstructMeridional', uReconstructMeridional) + call mpas_pool_get_array(velocityPool, 'surfaceSpeed', surfaceSpeed) + call mpas_pool_get_array(velocityPool, 'basalSpeed', basalSpeed) + + ! Velocities need to be reconstructed at cell centers for the native SIA dycore and for prescribed simple velocities. + ! External dycores return their native velocities at cell center locations, + ! but these can optionally be overwritten by reconstructed velocities for testing. + if ( (trim(config_velocity_solver) == 'sia') .or. & + (trim(config_velocity_solver) == 'simple') .or. & + config_do_velocity_reconstruction_for_external_dycore ) then + call mpas_reconstruct(meshPool, normalVelocity, & + uReconstructX, uReconstructY, uReconstructZ, & + uReconstructZonal, uReconstructMeridional ) + elseif (trim(config_velocity_solver) == 'none') then + ! The none velocity solver may have data velocity in uReconstructX/Y. + ! Fill out normalVelocity in case we want to advect with this field + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_array(meshPool, 'edgeNormalVectors', edgeNormalVectors) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + do iEdge=1,nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + do k=1,nVertInterfaces + ! average neighboring cell-centered vectors to the edge + xVelEdge = 0.5_RKIND*(uReconstructX(k,cell1) + uReconstructX(k,cell2)) + yVelEdge = 0.5_RKIND*(uReconstructY(k,cell1) + uReconstructY(k,cell2)) + ! normal component at edge: take dot products with unit vectors at edge + normalVelocity(k,iEdge) = xVelEdge * edgeNormalVectors(1,iEdge) + yVelEdge * edgeNormalVectors(2,iEdge) + enddo + enddo + else + ! For 2-d meshes, these are set by mpas_reconstruct, so set them for HO dycores + uReconstructZonal = uReconstructX + uReconstructMeridional = uReconstructY + end if + + ! --- + ! --- Calculate diagnostic speed arrays + ! --- + surfaceSpeed = sqrt(uReconstructX(1,:)**2 + uReconstructY(1,:)**2) + basalSpeed = sqrt(uReconstructX(nVertInterfaces,:)**2 + uReconstructY(nVertInterfaces,:)**2) + + ! --- + ! --- Calculate strain rates on cell centers + ! --- + call calculate_strain_rates(meshPool, velocityPool, err_tmp) + err = ior(err, err_tmp) + + ! --- + ! --- Remove floating ice where velocity is unrealistically fast + ! --- (Removed ice is added to calving flux) + ! --- (TODO: make this a namelist option?) + ! --- + call remove_fast_ice(surfaceSpeed, geometryPool) + + block => block % next + end do + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_solve.", MPAS_LOG_ERR) + endif + + call mpas_timer_stop("velocity solve") + + !-------------------------------------------------------------------- + end subroutine li_velocity_solve + + + +!*********************************************************************** +! +! routine li_velocity_finalize +! +!> \brief Finalizes velocity solver +!> \author Xylar Asay-Davis +!> \date 18 January 2012 +!> \details +!> This routine finalizes the ice velocity solver. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_finalize(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + character (len=StrKIND), pointer :: config_velocity_solver + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + + select case (config_velocity_solver) + case ('none') + ! Do nothing + case ('sia') + call li_sia_finalize(domain, err) + case ('simple') + call li_velocity_simple_finalize(err) + case ('L1L2', 'FO', 'Stokes') + call li_velocity_external_finalize(err) + case default + call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) + err = 1 + return + end select + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_finalize.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_finalize + + + +!*********************************************************************** +! private subroutines +!*********************************************************************** + + +!*********************************************************************** +! +! routine calculate_beta +! +!> \brief Calulates the beta field to be used by velocity solver +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \details +!> This routine calculates the beta field to be used by the velocity solver. +!> The resulting field to be used is stored as 'betaSolve'. +!> Additional methods for calculating beta can be added as they are developed. +! +!----------------------------------------------------------------------- + + subroutine calculate_beta(block, err) + + use li_mask + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (block_type), pointer, intent(inout) :: block + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer :: hydroPool + type (mpas_pool_type), pointer :: geometryPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: meshPool + real (kind=RKIND), dimension(:), pointer :: betaSolve, beta + real (kind=RKIND), dimension(:), pointer :: effectivePressure + integer, dimension(:), pointer :: cellMask + logical, pointer :: config_use_glp + logical, pointer :: config_beta_use_effective_pressure + logical, pointer :: hydroActive + real (kind=RKIND) :: betaAccum + integer :: nBetaValues + integer :: iCell, iCell2 + integer :: neighbor + integer, pointer :: nCells + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnCell + + err = 0 + + call mpas_pool_get_config(liConfigs, 'config_use_glp', config_use_glp) + call mpas_pool_get_config(liConfigs, 'config_beta_use_effective_pressure', config_beta_use_effective_pressure) + + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + call mpas_pool_get_array(velocityPool, 'betaSolve', betaSolve) + call mpas_pool_get_array(velocityPool, 'beta', beta) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + + betaSolve = beta + + if (config_beta_use_effective_pressure) then + call mpas_pool_get_package(liPackages, 'hydroActive', hydroActive) + if (.not. hydroActive) then + call mpas_log_write("config_beta_use_effective_pressure can only be used if the subglacial hydrology model is active.", & + MPAS_LOG_ERR) + err = ior(err, 1) + return + endif + call mpas_pool_get_subpool(block % structs, 'hydro', hydroPool) + call mpas_pool_get_array(hydroPool, 'effectivePressure', effectivePressure) + betaSolve = betaSolve * effectivePressure + + ! if using N-based friction with GLP, copy the value of beta across the grounding line + ! to the floating side for the GLP to work properly + if (config_use_glp) then + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + + do iCell = 1, nCells + if (li_mask_is_floating_ice(cellMask(iCell))) then + nBetaValues = 0 + betaAccum = 0.0_RKIND + do iCell2 = 1, nEdgesOnCell(iCell) + neighbor = cellsOnCell(iCell2, iCell) + if (li_mask_is_grounded_ice(cellMask(neighbor))) then + betaAccum = betaAccum + betaSolve(neighbor) + nBetaValues = nBetaValues + 1 + endif + enddo + if (nBetaValues > 0) then + betaSolve(iCell) = betaAccum / real(nBetaValues, kind=RKIND) ! take average of neighboring betas ! TODO: could do this in log space + endif + endif ! if iCell is floating + enddo ! cell loop + endif ! is using GLP with hydro friction law + ! TODO: not sure if halo update needed here. + + end if ! if config_beta_use_effective_pressure + + + if (.not. config_use_glp) then + where (li_mask_is_floating_ice(cellMask)) + betaSolve = 0.0_RKIND + end where + endif + + + !-------------------------------------------------------------------- + end subroutine calculate_beta + + +!*********************************************************************** +! +! routine calculate_strain_rates +! +!> \brief Calulates strain rates on cell centers +!> \author Matt Hoffman +!> \date Feb. 2018 +!> \details +!> This routine calculates strain rates on cell centers. +! +!----------------------------------------------------------------------- + + subroutine calculate_strain_rates(meshPool, velocityPool, err) + + use li_setup + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh object + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: & + velocityPool !< Input: mesh object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real(kind=RKIND), dimension(:), pointer :: exx, eyy, exy, eyx, eTheta, eMax, eMin + real(kind=RKIND), dimension(:,:), pointer :: uReconstructX, uReconstructY + integer :: err_tmp + + err = 0 + + call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) + call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) + call mpas_pool_get_array(velocityPool, 'exx', exx) + call mpas_pool_get_array(velocityPool, 'eyy', eyy) + call mpas_pool_get_array(velocityPool, 'exy', exy) + call mpas_pool_get_array(velocityPool, 'eyx', eyx) + call mpas_pool_get_array(velocityPool, 'eTheta', eTheta) + call mpas_pool_get_array(velocityPool, 'eMax', eMax) + call mpas_pool_get_array(velocityPool, 'eMin', eMin) + + ! Calculate strain rates + call li_compute_gradient_2d(meshPool, uReconstructX(1,:), exx, exy, err_tmp) + err = ior(err, err_tmp) + + call li_compute_gradient_2d(meshPool, uReconstructY(1,:), eyx, eyy, err_tmp) + err = ior(err, err_tmp) + + ! Calculate principal strain rate angle + eTheta = 0.5_RKIND * atan( (exy + eyx) / (exx - eyy + 1.0e-42_RKIND) ) + + ! Calculate principal strain rates + eMax = 0.5_RKIND * (exx + eyy) + sqrt( (0.5_RKIND * (exx - eyy))**2 + (0.25_RKIND*(exy + eyx))**2) + eMin = 0.5_RKIND * (exx + eyy) - sqrt( (0.5_RKIND * (exx - eyy))**2 + (0.25_RKIND*(exy + eyx))**2) + + !-------------------------------------------------------------------- + end subroutine calculate_strain_rates + +!*********************************************************************** +! +! routine remove_fast_ice +! +!> \brief Removes floating ice with a fast surface speed, presumably an iceberg +!> \author Matt Hoffman +!> \date March 2018 +!> \details +!> Inspired by BISICLES/Dan Martin. Could/should make an option to disable. +! +!----------------------------------------------------------------------- + + subroutine remove_fast_ice(surfaceSpeed, geometryPool) + + use li_mask + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real(kind=RKIND), dimension(:) :: surfaceSpeed + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: geometryPool + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real(kind=RKIND), dimension(:), pointer :: thickness, calvingThickness + integer, dimension(:), pointer :: cellMask + real(kind=RKIND), parameter :: highSpeed = 0.00318471337_RKIND ! 100,000 m/yr in m/s + + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'calvingThickness', calvingThickness) + + where (li_mask_is_floating_ice(cellMask) .and. (surfaceSpeed > highSpeed)) + ! "fast" ice that is removed is added to the calving flux + calvingThickness = calvingThickness + thickness + thickness = 0.0_RKIND + end where + + !-------------------------------------------------------------------- + end subroutine remove_fast_ice + +!*********************************************************************** + +end module li_velocity + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_landice/mode_forward/mpas_li_velocity_external.F b/src/core_landice/mode_forward/mpas_li_velocity_external.F new file mode 100644 index 0000000000..ecf6e20d07 --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_velocity_external.F @@ -0,0 +1,1269 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_velocity_external +! +!> \MPAS land-ice velocity driver for external dycores +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \version SVN:$Id:$ +!> \details +!> This module contains the routines for interfacing with +!> external velocity solvers. These currently are LifeV (L1L2, First order), +!> Albany (First order), and PHG (Stokes). +!> +! +!----------------------------------------------------------------------- + +module li_velocity_external + + use, intrinsic :: iso_c_binding + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timer + use mpas_log + + use li_setup + use li_constants + use li_mask + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: li_velocity_external_init, & + li_velocity_external_block_init, & + li_velocity_external_solve, & + li_velocity_external_finalize, & + li_velocity_external_write_albany_mesh + + interface + ! Note: Could add all interface routines to this interface... + ! For now, just trying it with this new routine. + subroutine velocity_solver_set_parameters(gravity, config_ice_density, config_ocean_density, config_sea_level, & + config_default_flowParamA, config_enhancementFactor, & + config_flowLawExponent, config_dynamic_thickness, iceMeltingPointPressureDependence, & + li_mask_ValueDynamicIce, li_mask_ValueIce, config_use_glp) & + bind(C, name="velocity_solver_set_parameters") + + use iso_c_binding, only: C_INT, C_DOUBLE, C_BOOL + + INTEGER(C_INT) :: li_mask_ValueDynamicIce, li_mask_ValueIce + REAL(C_DOUBLE) :: config_ice_density, config_ocean_density, config_sea_level, config_default_flowParamA, & + config_enhancementFactor, config_flowLawExponent, config_dynamic_thickness, gravity, & + iceMeltingPointPressureDependence + LOGICAL(C_BOOL) :: config_use_glp + end subroutine velocity_solver_set_parameters + + end interface + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + + + +contains + + + +!*********************************************************************** +! +! routine li_velocity_external_init +! +!> \brief Initializes velocity solver +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \version SVN:$Id$ +!> \details +!> This routine initializes the ice velocity solver in +!> external velocity solvers. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_external_init(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer, pointer :: config_num_halos, config_number_of_blocks + character (len=StrKIND), pointer :: config_velocity_solver + integer :: err_tmp + + err = 0 + err_tmp = 0 + + call mpas_pool_get_config(liConfigs, 'config_num_halos', config_num_halos) + call mpas_pool_get_config(liConfigs, 'config_number_of_blocks', config_number_of_blocks) + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + + ! Check for configuration options that are incompatible with external velocity solver conventions + if (config_num_halos < 2) then + call mpas_log_write("External velocity solvers require that config_num_halos >= 2", MPAS_LOG_ERR) + err_tmp = 1 + endif + err = ior(err,err_tmp) + + if (config_number_of_blocks /= 0) then + call mpas_log_write("External velocity solvers require that config_number_of_blocks=0", MPAS_LOG_ERR) + err_tmp = 1 + endif + err = ior(err,err_tmp) + + ! Check if we are on a sphere - not supported by external dycores + if (domain % on_a_sphere) then + call mpas_log_write("External velocity solvers cannot be run with a spherical mesh.", MPAS_LOG_ERR) + err_tmp = 1 + endif + err = ior(err,err_tmp) + + ! These calls are needed for setting up the external velocity solvers +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + !call external first order solver to set the grid of the velocity solver + call mpas_log_write("Initializing external velocity solver.", flushNow=.true.) + + call velocity_solver_init_mpi(domain % dminfo % comm) + + call interface_init_log()!domain % logInfo % outputLog % isActive, trim(domain % logInfo % outputLog % fileName) // CHAR(0)) + +#else + err = 1 + call mpas_log_write("To run with an external velocity solver you must compile MPAS with one.", MPAS_LOG_ERR) +#endif + + + if (config_velocity_solver == 'Stokes') then +#ifdef USE_EXTERNAL_STOKES + call interface_phg_init(domain, err) +#else + call mpas_log_write("External Stokes library needed to run Stokes dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + endif + err = ior(err,err_tmp) + + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_external_init.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_external_init + + + +!*********************************************************************** +! +! routine li_velocity_external_block_init +! +!> \brief Initializes blocks for external velocity solver use +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \version SVN:$Id$ +!> \details +!> This routine initializes each block of the ice velocity solver in the +!> external velocity solver. +!> Note: LifeV/Albany/PHG only support one block per processor, but this has (hopefully) +!> been written to work if that were to change. (That's why all these external dycore init +!> calls are in li_velocity_external_block_init instead of li_velocity_external_init.) +! +!----------------------------------------------------------------------- + + subroutine li_velocity_external_block_init(block, err) + + use li_mask + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (block_type), intent(in) :: & + block !< Input: mesh information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), pointer :: meshPool + integer, pointer :: nCells, nEdges, nVertices, nCellsSolve, nEdgesSolve, nVerticesSolve, nVertInterfaces, maxNEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnEdge, cellsOnVertex, verticesOnCell, verticesOnEdge, edgesOnCell + integer, dimension(:), pointer :: indexToCellID, indexToEdgeID, indexToVertexID, nEdgesOnCell + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell, xVertex, yVertex, zVertex, areaTriangle + real (kind=RKIND), pointer :: radius + type (field1DInteger), pointer :: indexToCellIDField, indexToEdgeIDField, indexToVertexIDField + real (kind=RKIND), pointer :: config_ice_density, config_ocean_density, config_sea_level, config_default_flowParamA, & + config_enhancementFactor, config_flowLawExponent, config_dynamic_thickness + logical, pointer :: config_use_glp + + ! halo exchange arrays + integer, dimension(:), pointer :: sendCellsArray, & + recvCellsArray, & + sendVerticesArray, & + recvVerticesArray, & + sendEdgesArray, & + recvEdgesArray + + err = 0 + + !extract data from domain + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + call mpas_pool_get_dimension(meshPool, 'nVertInterfaces', nVertInterfaces) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxNEdgesOnCell) + call mpas_pool_get_config(meshPool, 'sphere_radius', radius) + + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + call mpas_pool_get_array(meshPool, 'indexToEdgeID', indexToEdgeID) + call mpas_pool_get_array(meshPool, 'indexToVertexID', indexToVertexID) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + call mpas_pool_get_array(meshPool, 'areaTriangle', areaTriangle) + + call mpas_pool_get_field(meshPool, 'indexToCellID', indexToCellIDField) + call mpas_pool_get_field(meshPool, 'indexToEdgeID', indexToEdgeIDField) + call mpas_pool_get_field(meshPool, 'indexToVertexID', indexToVertexIDField) + + ! build send and receive arrays using exchange_list + call array_from_exchange_list(indexToCellIDField, sendCellsArray, recvCellsArray) + call array_from_exchange_list(indexToEdgeIDField, sendEdgesArray, recvEdgesArray) + call array_from_exchange_list(indexToVertexIDField, sendVerticesArray, recvVerticesArray) + + +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + ! These calls are needed for using any of the external velocity solvers + + !zCell is supposed to be zero when working on planar geometries (radius = 0) + !nVertLevels should be equal to nVertLevelsSolve (no splitting of the domain in the vertical direction) + call mpas_timer_start("velocity_solver_set_grid_data") + call mpas_log_write("Initializing external velocity solver grid data.", flushNow=.true.) + + call interface_redirect_stdout(-1) ! time level of -1 prevents message of what time level this is + + call velocity_solver_set_grid_data(nCells, nEdges, nVertices, nVertInterfaces, & + nCellsSolve, nEdgesSolve, nVerticesSolve, maxNEdgesOnCell, radius, & + cellsOnEdge, cellsOnVertex, verticesOnCell, verticesOnEdge, edgesOnCell, & + nEdgesOnCell, indexToCellID, & + xCell, yCell, zCell, xVertex, yVertex, zVertex, areaTriangle, & + sendCellsArray, recvCellsArray, & + sendEdgesArray, recvEdgesArray, & + sendVerticesArray, recvVerticesArray) + call mpas_timer_stop("velocity_solver_set_grid_data") +#else + call mpas_log_write("To run with an external velocity solver you must compile MPAS with one.", MPAS_LOG_ERR) + err = 1 +#endif + + !these can be deallocated because they have been copied on the c++ side + deallocate(sendCellsArray, & + recvCellsArray, & + sendVerticesArray, & + recvVerticesArray, & + sendEdgesArray, & + recvEdgesArray) + + ! Set physical parameters needed on the other side + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_default_flowParamA', config_default_flowParamA) + call mpas_pool_get_config(liConfigs, 'config_enhancementFactor', config_enhancementFactor) + call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', config_flowLawExponent) + call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) + call mpas_pool_get_config(liConfigs, 'config_use_glp', config_use_glp) +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + call velocity_solver_set_parameters(gravity, config_ice_density, config_ocean_density, config_sea_level, & + config_default_flowParamA, config_enhancementFactor, & + config_flowLawExponent, config_dynamic_thickness, & + iceMeltingPointPressureDependence, & + li_mask_ValueAlbanyActive, li_mask_ValueIce, & + logical(config_use_glp, KIND=1) ) + + call interface_reset_stdout() +#endif + + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_external_block_init.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_external_block_init + + + +!*********************************************************************** +! +! routine li_velocity_external_solve +! +!> \brief Interface to call external velocity solvers +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \version SVN:$Id$ +!> \details +!> This routine calls external first-order velocity solvers and/or Stokes velocity solvers. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_external_solve(meshPool, geometryPool, thermalPool, scratchPool, velocityPool, err) + + use li_mask + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + type (mpas_pool_type), intent(in) :: & + geometryPool !< Input: geometry information + + type (mpas_pool_type), intent(in) :: & + thermalPool !< Input: thermal information + + type (mpas_pool_type), intent(in) :: & + scratchPool !< Input: scratch information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: & + velocityPool !< Input/Output: velocity information + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), pointer :: & + thickness, bedTopography, lowerSurface, upperSurface, layerThicknessFractions, betaSolve, sfcMassBal + real (kind=RKIND), dimension(:,:), pointer :: & + normalVelocity, uReconstructX, uReconstructY, uReconstructZ + real (kind=RKIND), dimension(:,:), pointer :: temperature + real (kind=RKIND), pointer :: deltat + integer, dimension(:), pointer :: vertexMask, cellMask, edgeMask, floatingEdges + integer, dimension(:,:), pointer :: dirichletVelocityMask + character (len=StrKIND), pointer :: config_velocity_solver + logical, pointer :: config_always_compute_fem_grid + logical, pointer :: config_output_external_velocity_solver_data + real (kind=RKIND), pointer :: config_ice_density + integer, pointer :: anyDynamicVertexMaskChanged + integer, pointer :: dirichletMaskChanged + integer, pointer :: nEdges + integer, pointer :: timestepNumber + type (field2dReal), pointer :: dissipationVertexField + real (kind=RKIND), dimension(:,:), pointer :: heatDissipation ! on cells + integer :: iEdge + real(kind=RKIND), parameter :: secondsInYear = 365.0_RKIND * 24.0_RKIND * 3600.0_RKIND + !< The value of seconds in a year assumed by external dycores + integer, target :: err_tmp + integer, pointer :: err_albany + + err = 0 + err_tmp = 0 + + ! configs + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + call mpas_pool_get_config(liConfigs, 'config_always_compute_fem_grid', config_always_compute_fem_grid) + call mpas_pool_get_config(liConfigs, 'config_output_external_velocity_solver_data', & + config_output_external_velocity_solver_data) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + + ! Mesh variables + call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) + call mpas_pool_get_array(meshPool, 'deltat', deltat) + call mpas_pool_get_array(meshPool, 'timestepNumber', timestepNumber) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + + ! Geometry variables + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'upperSurface', upperSurface) + call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel = 1) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + + ! Thermal variables + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + call mpas_pool_get_array(thermalPool, 'heatDissipation', heatDissipation) + + ! Velocity variables + call mpas_pool_get_array(velocityPool, 'normalVelocity', normalVelocity) + call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) + call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) + call mpas_pool_get_array(velocityPool, 'uReconstructZ', uReconstructZ) + call mpas_pool_get_array(velocityPool, 'betaSolve', betaSolve) + call mpas_pool_get_array(velocityPool, 'anyDynamicVertexMaskChanged', anyDynamicVertexMaskChanged) + call mpas_pool_get_array(velocityPool, 'dirichletMaskChanged', dirichletMaskChanged) + call mpas_pool_get_array(velocityPool, 'dirichletVelocityMask', dirichletVelocityMask, timeLevel = 1) + call mpas_pool_get_array(velocityPool, 'floatingEdges', floatingEdges) + +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + ! Capture Albany output + call interface_redirect_stdout(timestepNumber) +#endif + + ! ================================================================== + ! External dycore calls to be made only when vertex mask changes + ! ================================================================== + + ! Note these functions will always be called on the first solve because we + ! initialize vertexMask to garbage which sets anyDynamicVertexMaskChanged to 1. + if ((anyDynamicVertexMaskChanged == 1) .or. (config_always_compute_fem_grid) .or. & + (dirichletMaskChanged == 1) ) then + call mpas_log_write("Generating new external velocity solver FEM grid.", flushNow=.true.) + call generate_fem_grid(config_velocity_solver, vertexMask, cellMask, dirichletVelocityMask, & + floatingEdges, layerThicknessFractions, lowerSurface, thickness, err) + endif + + + ! ================================================================== + ! External dycore calls to be made every time step (solve velocity!) + ! ================================================================== + + ! convert from m/s (used by MPAS) to m/yr (used by external dycores) + normalVelocity = normalVelocity * secondsInYear ! this is intent(out) by dycores, but setting anyway for consistency + uReconstructX = uReconstructX * secondsInYear + uReconstructY = uReconstructY * secondsInYear + + call mpas_log_write("Beginning velocity solve using external velocity solver.", flushNow=.true.) + + select case (config_velocity_solver) + case ('L1L2') ! =============================================== +#ifdef USE_EXTERNAL_L1L2 + call mpas_timer_start("velocity_solver_solve_L1L2") + call velocity_solver_solve_L1L2(lowerSurface, thickness, betaSolve, temperature, & + uReconstructX, uReconstructY, & ! Dirichlet boundary values to apply where dirichletVelocityMask=1 + normalVelocity, uReconstructX, uReconstructY) ! return values + call mpas_timer_stop("velocity_solver_solve_L1L2") + + if (config_output_external_velocity_solver_data) then + ! Optional calls to have LifeV output data files + call mpas_timer_start("velocity_solver export") + call velocity_solver_export_2d_data(lowerSurface, thickness, betaSolve) + call velocity_solver_export_L1L2_velocity(); + call mpas_timer_stop("velocity_solver export") + endif +#else + call mpas_log_write("External LifeV library needed to run L1L2 dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + + case ('FO') ! =============================================== +#ifdef USE_EXTERNAL_FIRSTORDER + + ! Allocate scratch var for dissipation on vertices + ! (Since it is a 3d field, making it scratch) + call mpas_pool_get_field(scratchPool, 'workLevelVertex', dissipationVertexField) + call mpas_allocate_scratch_field(dissipationVertexField, .true.) + + + call mpas_timer_start("velocity_solver_solve_FO") + err_albany => err_tmp + call velocity_solver_solve_FO(bedTopography, lowerSurface, thickness, & + betaSolve, sfcMassBal, temperature, & + uReconstructX, uReconstructY, & ! Dirichlet boundary values to apply where dirichletVelocityMask=1 + normalVelocity, dissipationVertexField % array, uReconstructX, uReconstructY, & ! return values + deltat, err_albany) ! return values +! call velocity_solver_estimate_SS_SMB(normalVelocity, mesh % sfcMassBal % array) ! this was used only for some ice2sea experiments, and is not a general routine to use + call mpas_timer_stop("velocity_solver_solve_FO") + + if (err_tmp > 0) then + call mpas_log_write("Albany velocity solve encountered an error! Check log.albany.0000.out for more information.", MPAS_LOG_ERR) + endif + err = ior(err,err_tmp) + + ! Now interpolate from vertices to cell centers + call li_interpolate_vertex_to_cell_2d(meshPool, dissipationVertexField % array, heatDissipation) + heatDissipation = heatDissipation / (config_ice_density * cp_ice) + call mpas_deallocate_scratch_field(dissipationVertexField, .true.) + + if (config_output_external_velocity_solver_data) then + call mpas_timer_start("velocity_solver export") + call velocity_solver_export_FO_velocity() + call mpas_timer_stop("velocity_solver export") + endif +#else + call mpas_log_write("External library needed to run FO dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + + case ('Stokes') ! =============================================== +#ifdef USE_EXTERNAL_STOKES + call mpas_timer_start("velocity_solver_solve_stokes") + call velocity_solver_solve_stokes(lowerSurface, thickness, betaSolve, temperature, & + uReconstructX, uReconstructY, & ! Dirichlet boundary values to apply where dirichletVelocityMask=1 + normalVelocity, uReconstructX, uReconstructY, uReconstructZ) ! return values + uReconstructZ = uReconstructZ / (365.0_RKIND * 24.0_RKIND * 3600.0_RKIND) ! convert from m/yr to m/s + call mpas_timer_stop("velocity_solver_solve_stokes") +#else + call mpas_log_write("External Stokes library needed to run stokes dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + end select + call mpas_log_write("Completed velocity solve using external velocity solver.") + + ! convert from m/yr (used by external dycores) to m/s (used by MPAS) + normalVelocity = normalVelocity / secondsInYear + uReconstructX = uReconstructX / secondsInYear + uReconstructY = uReconstructY / secondsInYear + + ! The external solver will calculate normalVelocity for all edges, + ! but some of those edges are not dynamically active according to MPASLI's + ! Voronoi grid conventions. This zeros velocity on those edges. + ! (Note: the choice of edges to get reconstructed used to be controlled + ! inside the interface, but as logic got more complicated with Dirichlet + ! b.c., that became unwieldly. Look in the mask routine to see the logic + ! for which edges are dynamic.) + do iEdge = 1, nEdges + if (.not. li_mask_is_dynamic_ice(edgeMask(iEdge)) ) normalVelocity(:,iEdge) = 0.0e0_RKIND + end do + +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + call interface_reset_stdout() +#endif + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_external_solve.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_external_solve + + + +!*********************************************************************** +! +! routine li_velocity_external_finalize +! +!> \brief Finalizes external velocity solvers +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \version SVN:$Id$ +!> \details +!> This routine finalizes the ice velocity solver in the external libraries. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_external_finalize(err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + ! This call is needed for using any of the external velocity solvers + ! call velocity_solver_finalize() +#else + call mpas_log_write("To run with an external velocity solver you must compile MPAS with one.", MPAS_LOG_ERR) + err = 1 + return +#endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_external_finalize + + +!*********************************************************************** +! +! routine li_velocity_external_write_albany_mesh +! +!> \brief Calls C++ code to write albany mesh in ascii format +!> \author Matt Hoffman +!> \date 4 May 2017 +!> \version SVN:$Id$ +!> \details +!> +! +!----------------------------------------------------------------------- + +subroutine li_velocity_external_write_albany_mesh(domain) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + logical, pointer :: config_write_albany_ascii_mesh + character (len=StrKIND), pointer :: config_velocity_solver + real (kind=RKIND), dimension(:), pointer :: & + bedTopography, lowerSurface, upperSurface, layerThicknessFractions, beta + real (kind=RKIND), dimension(:), pointer :: thickness, thicknessUncertainty + real (kind=RKIND), dimension(:), pointer :: sfcMassBal, sfcMassBalUncertainty + real (kind=RKIND), dimension(:), pointer :: floatingBasalMassBal, floatingBasalMassBalUncertainty + real (kind=RKIND), dimension(:), pointer :: & + observedSurfaceVelocityX, observedSurfaceVelocityY, observedSurfaceVelocityUncertainty + real (kind=RKIND), dimension(:), pointer :: observedThicknessTendency, observedThicknessTendencyUncertainty + real (kind=RKIND), dimension(:,:), pointer :: temperature + integer, dimension(:), pointer :: vertexMask, cellMask, edgeMask, floatingEdges, indexToCellID + integer, dimension(:,:), pointer :: dirichletVelocityMask + type (mpas_pool_type), pointer :: meshPool, geometryPool, thermalPool, observationsPool, velocityPool + real (kind=RKIND), pointer :: config_sea_level, config_ice_density, config_ocean_density + integer :: err + + call mpas_pool_get_config(liConfigs, 'config_write_albany_ascii_mesh', config_write_albany_ascii_mesh) + if (.not. config_write_albany_ascii_mesh) then + return ! do nothing + endif + +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + ! Capture Albany output + call interface_redirect_stdout(-1) ! time level of -1 prevents message of what time level this is + + ! --- + ! Call C++ routine to calculate FEM mesh and write it out + ! --- + + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) + + if (trim(config_velocity_solver) /= 'FO') then + call mpas_log_write("config_velocity solver needs to be set to 'FO' for config_write_albany_ascii_mesh to work.", & + MPAS_LOG_CRIT) + endif + ! check nProcs + if (domain % dminfo % nProcs /= 1) then + call mpas_log_write("config_write_albany_ascii_mesh currently only works on 1 processor.", MPAS_LOG_CRIT) + endif + ! check nBlocks + if (domain % dminfo % total_blocks /= 1) then + call mpas_log_write("config_write_albany_ascii_mesh currently only works on 1 block per processor.", MPAS_LOG_CRIT) + endif + + ! get the needed fields out of pools + ! NOTE: Assuming one block per processor! + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'geometry', geometryPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'thermal', thermalPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'observations', observationsPool) + + ! Mesh variables + call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + + ! Geometry variables + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'lowerSurface', lowerSurface) + call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + call mpas_pool_get_array(geometryPool, 'floatingBasalMassBal', floatingBasalMassBal) + call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel = 1) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + + ! Velocity variables + call mpas_pool_get_array(velocityPool, 'beta', beta) + call mpas_pool_get_array(velocityPool, 'floatingEdges', floatingEdges) + call mpas_pool_get_array(velocityPool, 'dirichletVelocityMask', dirichletVelocityMask, timeLevel = 1) + + ! Thermal variables + call mpas_pool_get_array(thermalPool, 'temperature', temperature) + + ! Observation variables + call mpas_pool_get_array(observationsPool, 'observedSurfaceVelocityX', observedSurfaceVelocityX) + call mpas_pool_get_array(observationsPool, 'observedSurfaceVelocityY', observedSurfaceVelocityY) + call mpas_pool_get_array(observationsPool, 'observedSurfaceVelocityUncertainty', observedSurfaceVelocityUncertainty) + call mpas_pool_get_array(observationsPool, 'observedThicknessTendency', observedThicknessTendency) + call mpas_pool_get_array(observationsPool, 'observedThicknessTendencyUncertainty', observedThicknessTendencyUncertainty) + call mpas_pool_get_array(observationsPool, 'thicknessUncertainty', thicknessUncertainty) + call mpas_pool_get_array(observationsPool, 'sfcMassBalUncertainty', sfcMassBalUncertainty) + call mpas_pool_get_array(observationsPool, 'floatingBasalMassBalUncertainty', floatingBasalMassBalUncertainty) + + !---- + + ! Calculate diagnostic variables to get 1. lowerSurface, 2. mask fields 3. floatingedges and updated edgemask. + ! We could call diagnostic_solve_before_velocity to do all that, but the way + ! code is currently organized, that would be a circular dependency.a + + call li_calculate_mask(meshPool, velocityPool, geometryPool, err) + + ! Lower surface is based on floatation for floating ice. For grounded ice (and non-ice areas) it is the bed. + where ( li_mask_is_floating_ice(cellMask) ) + lowerSurface = config_sea_level - thickness * (config_ice_density / config_ocean_density) + elsewhere + lowerSurface = bedTopography + end where + + floatingEdges = li_mask_is_floating_ice_int(edgeMask) + call li_calculate_extrapolate_floating_edgemask(meshPool, vertexMask, floatingEdges) + + ! Create FEM mesh + call mpas_log_write("Generating new external velocity solver FEM grid.", flushNow=.true.) + call generate_fem_grid(config_velocity_solver, vertexMask, cellMask, dirichletVelocityMask, & + floatingEdges, layerThicknessFractions, lowerSurface, thickness, err) + + ! call the C++ routine to write the mesh + call mpas_log_write("Writing Albany ASCII mesh.", flushNow=.true.) + call write_ascii_mesh(indexToCellID, bedTopography, lowerSurface, & + beta, temperature, & + thickness, thicknessUncertainty, & + sfcMassBal, sfcMassBalUncertainty, & + floatingBasalMassBal, floatingBasalMassBalUncertainty, & + observedSurfaceVelocityX, observedSurfaceVelocityY, observedSurfaceVelocityUncertainty, & + observedThicknessTendency, observedThicknessTendencyUncertainty) + + !---- + + call interface_reset_stdout() + + ! kill the model so we don't actually run the forward model + call mpas_log_write("Write of Albany ASCII mesh complete. Terminating execution normally.", MPAS_LOG_CRIT) + +#else + call mpas_log_write("To run with an external velocity solver you must compile MPAS with one.", MPAS_LOG_ERR) + err = 1 + return +#endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_external_write_albany_mesh + + + + +!*********************************************************************** +! private subroutines +!*********************************************************************** + + + +!*********************************************************************** +! +! routine interface_stokes_init +! +!> \brief Initializes stokes external velocity solver +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \details +!> This routine initializes the ice velocity solver in the stokes +!> external library (currently only PHG). +! +!----------------------------------------------------------------------- + + subroutine interface_stokes_init(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + +#ifdef USE_EXTERNAL_STOKES + ! This call is needed for using any of the PHG velocity solvers + call phg_init(domain % dminfo % comm) +#else + call mpas_log_write("External Stokes library needed to run stokes dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + + !-------------------------------------------------------------------- + end subroutine interface_stokes_init + + + +!*********************************************************************** +! +! routine generate_fem_grid +! +!> \brief Calls to interface to set FEM grid +!> \author Matt Hoffman +!> \date 2 April 2015 +!> \details +!> This routine calls functions in the C interface that generate the FEM grid. +! +!----------------------------------------------------------------------- + + subroutine generate_fem_grid(config_velocity_solver, vertexMask, cellMask, dirichletVelocityMask, floatingEdges, & + layerThicknessFractions, lowerSurface, thickness, err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + character (len=StrKIND), pointer :: config_velocity_solver + integer, pointer, dimension(:), intent(in) :: vertexMask, cellMask, floatingEdges + integer, pointer, dimension(:,:), intent(in) :: dirichletVelocityMask + real(kind=RKIND), pointer, dimension(:), intent(in) :: layerThicknessFractions, & + lowerSurface, thickness + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + + err = 0 + +#if defined(USE_EXTERNAL_L1L2) || defined(USE_EXTERNAL_FIRSTORDER) || defined(USE_EXTERNAL_STOKES) + call mpas_timer_start("velocity_solver_compute_2d_grid") + call velocity_solver_compute_2d_grid(vertexMask, cellMask, dirichletVelocityMask, floatingEdges) + call mpas_timer_stop("velocity_solver_compute_2d_grid") +#else + call mpas_log_write("To run with an external velocity solver you must compile MPAS with one.", MPAS_LOG_ERR) + err = 1 + return +#endif + + select case (config_velocity_solver) + case ('L1L2') ! =============================================== +#ifdef USE_EXTERNAL_L1L2 + call mpas_timer_start("velocity_solver_init_L1L2") + !call velocity_solver_extrude_3d_grid(layerThicknessFractions, lowerSurface, thickness) + call velocity_solver_init_L1L2(layerThicknessFractions) + call mpas_timer_stop("velocity_solver_init_L1L2") +#else + call mpas_log_write("External LifeV library needed to run L1L2 dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + + case ('FO') ! =============================================== +#ifdef USE_EXTERNAL_FIRSTORDER + call mpas_timer_start("velocity_solver_extrude_3d_grid") + call velocity_solver_extrude_3d_grid(layerThicknessFractions, lowerSurface, thickness) + call mpas_timer_stop("velocity_solver_extrude_3d_grid") + call mpas_timer_start("velocity_solver_init_FO") + call velocity_solver_init_FO(layerThicknessFractions) + call mpas_timer_stop("velocity_solver_init_FO") +#else + call mpas_log_write("External library needed to run FO dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + + case ('Stokes') ! =============================================== +#ifdef USE_EXTERNAL_STOKES + call mpas_timer_start("velocity_solver_extrude_3d_grid") + call velocity_solver_extrude_3d_grid(layerThicknessFractions, lowerSurface, thickness) + call mpas_timer_stop("velocity_solver_extrude_3d_grid") + call mpas_timer_start("velocity_solver_init_stokes") + call velocity_solver_init_stokes(layerThicknessFractions) + call mpas_timer_stop("velocity_solver_init_stokes") +#else + call mpas_log_write("External Stokes library needed to run stokes dycore.", MPAS_LOG_ERR) + err = 1 + return +#endif + end select + + !-------------------------------------------------------------------- + end subroutine generate_fem_grid + + + +!*********************************************************************** +! +! routine array_from_exchange_list +! +!> \brief Converts the MPAS Exchange Lists to flat arrays for external use +!> \author Matt Hoffman +!> \date 3 October 2013 +!> \version SVN:$Id$ +!> \details +!> This routine converts the MPAS Exchange Lists (type mpas_multihalo_exchange_list) +!> to flat arrays for use by external dycores. +!----------------------------------------------------------------------- + + subroutine array_from_exchange_list(field, sendArray, recvArray) + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (field1DInteger), pointer, intent(in) :: field !< Input: the field that holds the MPAS exchange lists. + ! Any 1d integer fields will work, but it is suggested to use one of indexToCellID, indexToEdgeID, or IndexToVertexID + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + integer, dimension(:), pointer :: sendArray !< Input/Output: the flat array of elements to send, + !< should be unallocated on input + integer, dimension(:), pointer :: recvArray !< Input/Output: the flat array of elements to receive, + !< should be unallocated on input + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- +! integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (mpas_communication_list), pointer :: sendCommList, recvCommList ! Communication lists that will be setup + !from mpas_multihalo_exchange_list's as an intermediate step to flat arrays + type (mpas_communication_list), pointer :: commListPtr ! A temporary comm list pointer + integer :: nHaloLayers, iHalo + integer, dimension(:), pointer :: haloLayers ! an array of halo layers, needed to setup comm lists - we want all of them + type (field1DInteger), pointer :: fieldCursor + integer :: nAdded, bufferOffset, i + type (mpas_exchange_list), pointer :: exchListPtr + + + ! ======================================================================== + ! Step 1: Generate communication lists from the mpas_multihalo_exchange_list's + ! (this step is written to be compatible with multiple blocks per processor, + ! even though that is not supported for external dycores.) + ! This is done because communication lists have the various halos collapsed + ! into a single list. + ! ======================================================================== + + ! == First prepare communication lists + nHaloLayers = size(field % sendList % halos) + allocate(haloLayers(nHaloLayers)) + do iHalo = 1, nHaloLayers + haloLayers(iHalo) = iHalo + end do + ! Built new send/receive communication lists that have procID & nList filled out. + call mpas_dmpar_build_comm_lists(field % sendList, field % recvList, haloLayers, & + field % dimsizes, sendCommList, recvCommList) + + + ! == Next populate the commLists' ibuffer field with the element indices to communicate + + ! NOTE: Looping over the various block's via the field linked list is NOT needed + ! because packing the communication list with indices will be garbage + ! if there is more than one block per processor since the indices are block specific. + ! External dycores currently only support one block per processor and + ! this subroutine would need substantial modification to support more. + ! However I am keeping the code that traverses blocks + ! because this section is taken from mpas_dmpar_exch_halo_field1d_integer + ! and retaining it makes comparison to that subroutine easier. The only + ! difference is the assignements to the ibuffers. + ! A check for 1 block per proc is in li_velocity_external_init. + + + ! Allocate space in send lists, and copy data into buffer + commListPtr => sendCommList + do while(associated(commListPtr)) ! Traverse all the processors to be sent to. + allocate(commListPtr % ibuffer(commListPtr % nList)) + nullify(commListPtr % rbuffer) + bufferOffset = 0 + do iHalo = 1, nHaloLayers + nAdded = 0 + + fieldCursor => field + do while(associated(fieldCursor)) ! This is the linked list traversal that is NOT needed. + exchListPtr => fieldCursor % sendList % halos(haloLayers(iHalo)) % exchList + do while(associated(exchListPtr)) + if (exchListPtr % endPointID == commListPtr % procID) then + do i = 1, exchListPtr % nList + commListPtr % ibuffer(exchListPtr % destList(i) + bufferOffset) = exchListPtr % srcList(i) + ! local indices to go into the send communication list + + nAdded = nAdded + 1 + + end do + end if + + exchListPtr => exchListPtr % next + end do + + fieldCursor => fieldCursor % next + end do + bufferOffset = bufferOffset + nAdded + end do + + commListPtr => commListPtr % next + end do + + + ! Allocate space in recv lists, and copy data into buffer + commListPtr => recvCommList + do while(associated(commListPtr)) ! Traverse all the processors to receive from. + allocate(commListPtr % ibuffer(commListPtr % nList)) + nullify(commListPtr % rbuffer) + bufferOffset = 0 + do iHalo = 1, nHaloLayers + nAdded = 0 + fieldCursor => field + do while(associated(fieldCursor)) ! This is the linked list traversal that is NOT needed. + exchListPtr => fieldCursor % recvList % halos(haloLayers(iHalo)) % exchList + do while(associated(exchListPtr)) + if (exchListPtr % endPointID == commListPtr % procID) then + do i = 1, exchListPtr % nList + commListPtr % ibuffer( exchListPtr % srcList(i) + bufferOffset ) = exchListPtr % destList(i) + ! buffer index to go into the receive communication list + + end do + nAdded = max(nAdded, maxval(exchListPtr % srcList)) + end if + exchListPtr => exchListPtr % next + end do + + fieldCursor => fieldCursor % next + end do + bufferOffset = bufferOffset + nAdded + end do + commListPtr => commListPtr % next + end do + + + ! ======================================================================== + ! Step 2: Flatten the communication lists to flat arrays + ! ======================================================================== + call fill_exchange_array(sendCommList, sendArray) + call fill_exchange_array(recvCommList, recvArray) + + + ! Clean up + call mpas_dmpar_destroy_communication_list(sendCommList) + call mpas_dmpar_destroy_communication_list(recvCommList) + deallocate(haloLayers) + + end subroutine array_from_exchange_list +!*********************************************************************** + + +!*********************************************************************** +! +! routine fill_exchange_array +! +!> \brief Fills the flat array for external use with information from an MPAS communication list +!> \author Matt Hoffman +!> \date 15 October 2013 +!> \version SVN:$Id$ +!> \details +!> This routine converts the MPAS Communication Lists (type mpas_communication_list) +!> to flat arrays for use by external dycores. The arrays have this format: +!> +!> Pos 1: total size of array +!> For each processor to be communicated with: +!> Pos 1: processor ID +!> Pos 2: nList (number of elements in this processor's sub-list +!> Pos 3 to 3+nList-1: local indices of elements to be communicated (using 0-based indexing for C/C++) +!----------------------------------------------------------------------- + subroutine fill_exchange_array(commList, commArray) + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_communication_list), pointer, intent(in) :: & + commList !< Input: Communication list to be flattened + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + integer, dimension(:), pointer :: commArray !< Input/Output: the flat array of elements to communicate, + !< should be unallocated on input + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- +! integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: arraySize ! size of array to house the send or receive list (sendArray, recvArray) + integer :: offset ! offset for adding metadata about each processor into the flat commArray + integer :: i + type (mpas_communication_list), pointer :: commListPtr + !< A temporary comm list pointer for traversing linked lists + + arraySize = 1 !in first position we will store the size of the array + commListPtr => commList + do while (associated(commListPtr)) + ! for each processor to be communicated with, we will store the procID, nList, + ! and then the list of local indices to be communicated + arraySize = arraySize + commListPtr % nlist + 2 + commListPtr => commListPtr % next + end do + + allocate(commArray(arraySize)) + + commArray(1) = arraySize + offset = 2 ! we will store the procID, nList before the list of local indices + commListPtr => commList + do while (associated(commListPtr)) + commArray(offset) = commListPtr % procID ! store procID + offset = offset + 1 + commArray(offset) = commListPtr % nlist ! store nList + do i = 1 , commListPtr % nlist + commArray(i+offset) = commListPtr % ibuffer(i) -1 + ! add the list of elements to be communicated, switching to 0-based indexing for C/C++ + end do + offset = offset + commListPtr % nlist + 1 + + commListPtr => commListPtr % next + end do + + end subroutine fill_exchange_array +!*********************************************************************** + + +end module li_velocity_external + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_landice/mode_forward/mpas_li_velocity_simple.F b/src/core_landice/mode_forward/mpas_li_velocity_simple.F new file mode 100644 index 0000000000..16e0c7073a --- /dev/null +++ b/src/core_landice/mode_forward/mpas_li_velocity_simple.F @@ -0,0 +1,644 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_velocity_simple +! +!> \MPAS land-ice simple velocity driver +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This module contains the routines for calculating simple velocity fields +!> (e.g., uniform in x direction, radially symmetric). +!> +! +!----------------------------------------------------------------------- + +module li_velocity_simple + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_log + + use li_mask + use li_setup + use li_constants + + implicit none + private + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + public :: li_velocity_simple_init, & + li_velocity_simple_finalize, & + li_velocity_simple_block_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine li_velocity_simple_init +! +!> \brief Initializes simple velocity +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine initializes the simple velocity cases. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_simple_init(domain, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + !-------------------------------------------------------------------- + + end subroutine li_velocity_simple_init + + + +!*********************************************************************** +! +! routine li_velocity_simple_block_init +! +!> \brief Initializes blocks for simple velocity +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine initializes each block with a simple velocity field +!> (uniform velocity in a straight line, or radially symmetric). +!> NOTE: This subroutine assumes flow in a plane with all z components = 0. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_simple_block_init(block, err) + + use mpas_vector_reconstruction + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (block_type), intent(inout) :: & + block !< Input/Output: block object + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: velocityPool + type (mpas_pool_type), pointer :: scratchPool + + integer, pointer :: nCells, nEdges + integer, pointer :: nCellsSolve, nEdgesSolve + integer, pointer :: nVertInterfaces + integer, pointer :: config_stats_cell_ID + + character(len=StrKind), pointer :: config_simple_velocity_type + + !NOTE: Assume a planar mesh, so z coordinates are not needed + real (kind=RKIND), dimension(:), pointer :: & + xCell, yCell, & ! cell center coordinates + xEdge, yEdge, & ! edge midpoint coordinates + dcEdge ! distance between the 2 cell centers on each side of an edge + + ! prescribed velocity at cell centers + type (field1dReal), pointer :: uVelocityXField + type (field1dReal), pointer :: uVelocityYField + real (kind=RKIND), dimension(:), pointer :: uVelocityX + real (kind=RKIND), dimension(:), pointer :: uVelocityY + + real (kind=RKIND), dimension(:,:), pointer :: & + normalVelocityInitial, & ! normal component of velocity on edges + uReconstructX, uReconstructY, uReconstructZ, & ! x/y/z velocity components at cell center + uReconstructZonal, uReconstructMeridional ! zonal and meridional velocity components at cell center + + integer, dimension(:,:), pointer :: cellsOnEdge ! indices for the 2 cells on each edge + + real (kind=RKIND), dimension(2) :: unitNormalVector ! x/y components of normal vector on an edge + + real (kind=RKIND) :: magnitude, radius, speed, xDiff, yDiff + + real (kind=RKIND) :: uEdgeX, uEdgeY ! x/y components of velocity at edge midpoints + + integer :: err_tmp + + integer :: iLevel, iEdge, iCell, iCell1, iCell2 + + real (kind=RKIND), parameter :: flowSpeed = 1000._RKIND/scyr ! flow speed (m/s) + ! applies to uniform flow + real (kind=RKIND), parameter :: flowTheta = 0.0_RKIND ! direction of flow (0 < theta < 2*pi) + ! applied to uniform flow (not radial) + + !Note: For radial flow, the user may want to reset these parameters + real (kind=RKIND), parameter :: flowGradient = 1.2e-3_RKIND/scyr ! du/dr for radial flow + + real (kind=RKIND), parameter :: xCenter = 0.0_RKIND ! x coordinate of center of radial flow + real (kind=RKIND), parameter :: yCenter = 0.0_RKIND ! y coordinate of center of radial flow + + !WHL - debug diagnostics only + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell ! index for each edge on a cell + real(kind=RKIND), dimension(:,:,:), pointer :: & + coeffsReconstruct ! coefficients for reconstructing edge-based fields at cell centers + integer :: iEdgeOnCell + + logical, parameter :: velocity_simple_bug_check = .false. + + !-------------------------------- + !WHL - optional thickness, SMB and topography tweaking for the radial velocity field and circular-shelf test case + !TODO - Remove these options after testing + + character(len=StrKIND), pointer :: config_calving + + logical, parameter :: radialMelting = .false. +!! logical, parameter :: radialMelting = .true. + type (mpas_pool_type), pointer :: geometryPool + real (kind=RKIND), dimension(:), pointer :: thickness + real (kind=RKIND), dimension(:), pointer :: sfcMassBal + real (kind=RKIND), dimension(:), pointer :: bedTopography + + real (kind=RKIND), parameter :: & + maxRadius = 21000.0_RKIND ! ice radius (m) for circular shelf problem + + real (kind=RKIND), parameter :: & + maxMelt = 100.0_RKIND * 910.0_RKIND / scyr ! max melt rate, kg/m2/s (converted from 100 m/yr) + ! for radial melting option + real (kind=RKIND), parameter :: & + spikeTopography = -880.0_RKIND ! elevation of spike that grounds the ice + ! for config_calving = 'floating' + + integer, parameter :: ncellsPerRow = 40 + integer, parameter :: nRows = 46 + integer :: i, iRow + + character (len=strKind) :: msg + + !-------------------------------- + + ! No block init needed. + err = 0 + err_tmp = 0 + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'velocity', velocityPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + ! Set needed variables and pointers + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVertInterfaces', nVertInterfaces) + + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'xEdge', xEdge) + call mpas_pool_get_array(meshPool, 'yEdge', yEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + call mpas_pool_get_array(velocityPool, 'normalVelocityInitial', normalVelocityInitial) + call mpas_pool_get_array(velocityPool, 'uReconstructX', uReconstructX) + call mpas_pool_get_array(velocityPool, 'uReconstructY', uReconstructY) + call mpas_pool_get_array(velocityPool, 'uReconstructZ', uReconstructZ) + call mpas_pool_get_array(velocityPool, 'uReconstructZonal', uReconstructZonal) + call mpas_pool_get_array(velocityPool, 'uReconstructMeridional', uReconstructMeridional) + + call mpas_pool_get_field(scratchPool, 'workCell', uVelocityXField) + call mpas_allocate_scratch_field(uVelocityXField, .true.) + uVelocityX => uVelocityXField % array + + call mpas_pool_get_field(scratchPool, 'workCell2', uVelocityYField) + call mpas_allocate_scratch_field(uVelocityYField, .true.) + uVelocityY => uVelocityYField % array + + call mpas_pool_get_config(liConfigs, 'config_stats_cell_ID', config_stats_cell_ID) + call mpas_pool_get_config(liConfigs, 'config_simple_velocity_type', config_simple_velocity_type) + + !WHL - debug diagnostics only + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'coeffs_reconstruct', coeffsReconstruct) + + uVelocityX(:) = 0.0_RKIND + uVelocityY(:) = 0.0_RKIND + + ! prescribe the x and y velocity components at cell centers (with no vertical variation) + + if (trim(config_simple_velocity_type) == 'uniform') then + + uVelocityX(:) = flowSpeed * cos(flowTheta) + uVelocityY(:) = flowSpeed * sin(flowTheta) + + elseif (trim(config_simple_velocity_type) == 'radial') then + + do iCell = 1, nCells + xDiff = xCell(iCell) - xCenter + yDiff = yCell(iCell) - yCenter + radius = sqrt(xDiff**2 + yDiff**2) + if (radius > 0.0_RKIND) then + speed = flowGradient * radius + uVelocityX(iCell) = speed * xDiff/radius + uVelocityY(iCell) = speed * yDiff/radius + else + uVelocityX(iCell) = 0.0_RKIND + uVelocityY(iCell) = 0.0_RKIND + endif + enddo + + endif + + ! given the velocity components at cell centers, compute the normal velocity component on edges + + normalVelocityInitial(:,:) = 0.0_RKIND + + do iEdge = 1, nEdgesSolve + + iLevel = 1 + iCell1 = cellsOnEdge(1,iEdge) + iCell2 = cellsOnEdge(2,iEdge) + + ! average the velocity from the neighboring cells to the edge + uEdgeX = 0.5_RKIND * (uVelocityX(iCell1) + uVelocityX(iCell2)) + uEdgeY = 0.5_RKIND * (uVelocityY(iCell1) + uVelocityY(iCell2)) + + ! Compute the components of the normal vector on the edge + + unitNormalVector(1) = xEdge(iEdge) - xCell(iCell1) + unitNormalVector(2) = yEdge(iEdge) - yCell(iCell1) + magnitude = sqrt(unitNormalVector(1)**2 + unitNormalVector(2)**2) + + ! Note: The magnitude should be dcEdge/2. + ! But this may not be the case for edges at the border of a periodic domain; + ! for these cells, the magnitude may be comparable to the domain size. + ! For such edges, create the normal vector from iCell2 instead. + if (magnitude > dcEdge(iEdge)) then +! write(stderrUnit,*) 'Use iCell2 instead: iEdge, iCell1, iCell2, magnitude, dcEdge =', & +! iEdge, iCell1, iCell2, magnitude, dcEdge(iEdge) + unitNormalVector(1) = -(xEdge(iEdge) - xCell(iCell2)) + unitNormalVector(2) = -(yEdge(iEdge) - yCell(iCell2)) + magnitude = sqrt(unitNormalVector(1)**2 + unitNormalVector(2)**2) + endif + + unitNormalVector(:) = unitNormalVector(:)/magnitude + + ! Compute the dot product of the velocity with the normal vector + ! Set to the same value everywhere in the column + normalVelocityInitial(:,iEdge) = uEdgeX*unitNormalVector(1) + uEdgeY*unitNormalVector(2) + + enddo ! iEdge + + !WHL - debug + iCell = config_stats_cell_ID + iLevel = 1 + call mpas_log_write(' ') + write(msg,*) 'Prescribed velocity, iCell, uvel, vvel (m/yr):', iCell, uVelocityX(iCell)*scyr, uVelocityY(iCell)*scyr + call mpas_log_write(msg) + write(msg,*) 'xCell, yCell:', xCell(iCell), yCell(iCell) + call mpas_log_write(msg) + call mpas_log_write('iEdgeOnCell, cellsOnEdge, normalVelocity:') + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iEdgeOnCell,iCell) + write(msg,*) iEdgeOnCell, cellsOnEdge(:,iEdge), normalVelocityInitial(iLevel,iEdge)*scyr + call mpas_log_write(msg) + enddo + + if (velocity_simple_bug_check) then + + ! Make sure we can recover the cell-center velocity to a good approximation + call mpas_reconstruct(meshPool, normalVelocityInitial, & + uReconstructX, uReconstructY, uReconstructZ, & + uReconstructZonal, uReconstructMeridional ) + + ! Loop over cells, comparing the reconstructed velocity to the prescribed velocity + ! Note: Currently, the reconstruction coefficients are not correct for cells at the edge of a periodic domain, + ! so errors will be generated even though the normal velocities are correct. + ! For this reason I have commented out the warning messages. + + do iCell = 1, nCellsSolve + + speed = sqrt(uVelocityX(iCell)**2 + uVelocityY(iCell)**2) + + if (iCell == config_stats_cell_ID) then + iLevel = 1 + call mpas_log_write(' ') + write(msg,*) 'Velocity reconstruction, iCell =', iCell + call mpas_log_write(msg) + write(msg,*) 'Initial velocity:', uVelocityX(iCell), uVelocityY(iCell) + call mpas_log_write(msg) + write(msg,*) 'Reconstructed velocity:', uReconstructX(iLevel,iCell), uReconstructY(iLevel,iCell) + call mpas_log_write('Reconstruction coefficients:') + call mpas_log_write(' ') + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + write(msg,*) iEdgeOnCell, coeffsReconstruct(:,iEdgeOnCell,iCell) + call mpas_log_write(msg) + enddo + call mpas_log_write(' ') + call mpas_log_write('iEdgeOnCell, cellsOnEdge, normalVelocity:') + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iEdgeOnCell,iCell) + write(msg,*) iEdgeOnCell, cellsOnEdge(:,iEdge), normalVelocityInitial(iLevel,iEdge) + call mpas_log_write(msg) + enddo + endif + + iLevel = 1 ! Check at one level only, since the velocity is vertically uniform + + if (abs(uReconstructX(iLevel,iCell) - uVelocityX(iCell)) > 1.e-8_RKIND*speed .or. & + abs(uReconstructY(iLevel,iCell) - uVelocityY(iCell)) > 1.e-8_RKIND*speed) then + + xDiff = abs(uReconstructX(iLevel,iCell) - uVelocityX(iCell)) / speed + yDiff = abs(uReconstructY(iLevel,iCell) - uVelocityY(iCell)) / speed + + call mpas_log_write(' ') + write(msg,*) 'WARNING: Reconstructed velocity not equal to uniform velocity, iCell, xDiff, yDiff=', & + iCell, xDiff, yDiff + call mpas_log_write(msg) + write(msg,*) 'Prescribed velocity: ', uVelocityX(iCell), uVelocityY(iCell) + call mpas_log_write(msg) + write(msg,*) 'Reconstructed velocity:', uReconstructX(iLevel,iCell), uReconstructY(iLevel,iCell) + call mpas_log_write(msg) + call mpas_log_write('iEdgeOnCell, cellsOnEdge, normal velocity:') + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(iEdgeOnCell,iCell) + write(msg,*) iEdgeOnCell, cellsOnEdge(:,iEdge), normalVelocityInitial(iLevel,iEdge) + call mpas_log_write(msg) + enddo + err = 1 + endif + + enddo ! iCell + + endif ! bug check + + + !-------------------------------- + !TODO - Remove these options after testing the calving scheme + + call mpas_pool_get_config(liConfigs, 'config_calving', config_calving) + + if (radialMelting) then ! force the calving front to retreat + + call mpas_log_write('Setting up radially symmetric melting') + write(msg,*) 'Melt rate at periphery (m/yr) =', maxMelt * scyr / 910.0_RKIND + call mpas_log_write(msg) + + ! Zero out the normal velocities, since we are testing ice retreat + normalVelocityInitial(:,:) = 0.0_RKIND + + ! Set the thickness and melt rate + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + call mpas_pool_get_array(geometryPool, 'sfcMassBal', sfcMassBal) + + do iCell = 1, nCells + xDiff = xCell(iCell) - xCenter + yDiff = yCell(iCell) - yCenter + radius = sqrt(xDiff**2 + yDiff**2) + ! set thickness to taper away from the center + thickness(iCell) = thickness(iCell) * (1.0_RKIND - radius/maxRadius) + ! set melting to increase away from the center + sfcMassBal(iCell) = maxMelt * (-radius/maxRadius) + enddo + +! call mpas_log_write(' ') +! call mpas_log_write('thickness (m):') +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stderrUnit,'(a3)',advance='no') ' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stderrUnit,'(f8.2)',advance='no') thickness(iCell) +! enddo +! write(stderrUnit,*) ' ' +! enddo +! +! write(stderrUnit,*) ' ' +! write(stderrUnit,*) 'melt rate (m/yr):' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stderrUnit,'(a3)',advance='no') ' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stderrUnit,'(f8.2)',advance='no') -sfcMassBal(iCell)*scyr/910.0_RKIND +! enddo +! write(stderrUnit,*) ' ' +! enddo + + endif ! radialMelting + + if (trim(config_calving) == 'topographic_threshold') then + + call mpas_log_write('Setting topography to drop off at periphery') + + ! Set the bed topography to drop off near the periphery of the ice + ! so as to check that the calving topographic threshold option is working. + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + + do iCell = 1, nCells + xDiff = xCell(iCell) - xCenter + yDiff = yCell(iCell) - yCenter + radius = sqrt(xDiff**2 + yDiff**2) + if (radius > 0.9_RKIND*maxRadius) then ! close to the edge + bedTopography(iCell) = bedTopography(iCell) * radius/(0.9_RKIND*maxRadius) + endif + enddo + +! write(stderrUnit,*) ' ' +! write(stderrUnit,*) 'bedTopography (m):' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stderrUnit,'(a3)',advance='no') ' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stderrUnit,'(f8.2)',advance='no') -bedTopography(iCell) +! enddo +! write(stderrUnit,*) ' ' +! enddo + + elseif (trim(config_calving) == 'floating') then + + call mpas_log_write('Setting topography to be mostly grounded') + write(msg,*) 'Spike depth =', spikeTopography + call mpas_log_write(msg) + + ! Put in a large spike that grounds most of the ice, but leaves the peripheral ice floating, + ! so as to check that the calving no-float option is working. + call mpas_pool_get_subpool(block % structs, 'geometry', geometryPool) + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + + do iCell = 1, nCells + xDiff = xCell(iCell) - xCenter + yDiff = yCell(iCell) - yCenter + radius = sqrt(xDiff**2 + yDiff**2) + if (radius < 0.9_RKIND*maxRadius) then ! inner part of ice shelf + bedTopography(iCell) = spikeTopography + endif + enddo + +! write(stderrUnit,*) ' ' +! write(stderrUnit,*) 'bed depth (m):' +! do iRow = nRows, 1, -1 +! if (mod(iRow,2) == 0) then ! indent for even-numbered rows +! write(stderrUnit,'(a3)',advance='no') ' ' +! endif +! do i = nCellsPerRow/2 - 2, nCellsPerRow +! iCell = (iRow-1)*nCellsPerRow + i +! write(stderrUnit,'(f8.2)',advance='no') -bedTopography(iCell) +! enddo +! write(stderrUnit,*) ' ' +! enddo + + endif ! config_calving + !-------------------------------- + + ! clean up + call mpas_allocate_scratch_field(uVelocityXField, .true.) + call mpas_allocate_scratch_field(uVelocityYField, .true.) + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_velocity_uniform_init.", MPAS_LOG_ERR) + endif + + !-------------------------------------------------------------------- + end subroutine li_velocity_simple_block_init + + +!*********************************************************************** +! +! routine li_velocity_simple_finalize +! +!> \brief finalizes simple velocity +!> \author William Lipscomb +!> \date October 2015 +!> \details +!> This routine finalizes the simple velocity cases. +! +!----------------------------------------------------------------------- + + subroutine li_velocity_simple_finalize(err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + + !-------------------------------------------------------------------- + + end subroutine li_velocity_simple_finalize + + + + ! private subroutines + + + + +!*********************************************************************** + + end module li_velocity_simple + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_landice/mpas_li_diagnostic_vars.F b/src/core_landice/mpas_li_diagnostic_vars.F deleted file mode 100644 index 092665e2db..0000000000 --- a/src/core_landice/mpas_li_diagnostic_vars.F +++ /dev/null @@ -1,888 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_diagnostic_vars -! -!> \brief MPAS land ice module for calculating diagnostic variables -!> \author Matt Hoffman -!> \date 17 April 2011 -!> \details -!> This module contains various subroutines for -!> calculating diagnostic (time-independent) variables -!> for the land ice core. -!> These calculations should be general so as to be independent -!> of time integration scheme. -! -!----------------------------------------------------------------------- - -module li_diagnostic_vars - - use mpas_derived_types - use mpas_pool_routines - use mpas_timer - use mpas_log - use li_velocity - use li_mask - - implicit none - private - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - public :: li_calculate_diagnostic_vars - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - - -!*********************************************************************** - -contains - -!*********************************************************************** -! -! routine li_calculate_diagnostic_vars -! -!> \brief Calculates diagnostic variables -!> \author Matthew Hoffman -!> \date 12 September 2013 -!> \details -!> This routine calculates diagnostic variables using the current prognostic -!> variables. This is done in 3 parts: -!> 1. diagnostic solve part 1; 2. solve velocity; 3. diagnostic solve part 2 -!> Note: If the velocity solver requires an initial guess, it will be taken -!> from the timeLevel argument. Therefore the normalVelocity in that time level -!> should be updated with the guess prior to calling this subroutine, if necessary. -! -!----------------------------------------------------------------------- - - subroutine li_calculate_diagnostic_vars(domain, timeLevel, solveVelo, err) - - use mpas_vector_reconstruction - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - integer, intent(in) :: timeLevel !< Input: Time level on which to calculate diagnostic variables - logical, intent(in) :: solveVelo !< Input: Whether or not to solve velocity - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - type (block_type), pointer :: block - type (mpas_pool_type), pointer :: meshPool - type (mpas_pool_type), pointer :: statePool - real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, uReconstructX, uReconstructY, uReconstructZ, uReconstructZonal, uReconstructMeridional - integer :: err_tmp -!!! integer :: blockVertexMaskChanged, procVertexMaskChanged, anyVertexMaskChanged - - - err = 0 - - ! === - ! === Diagnostic solve of variables prior to velocity - ! === - - call mpas_timer_start("calc. diagnostic vars except vel") - call diagnostic_solve_before_velocity(domain, timeLevel, err_tmp) ! perhaps velocity solve should move in here. - err = ior(err, err_tmp) -! This information is only needed for some external dycores. This can be added back in when they are implemented. -! Should make this conditional to avoid unnecessary MPI comms. -!!! block => domain % blocklist -!!! do while (associated(block)) -!!! stateNew => block % state % time_levs(2) % state -!!! stateOld => block % state % time_levs(1) % state -!!! ! Determine if the vertex mask changed during this time step for this block (needed for LifeV) -!!! ! \todo: there may be some aspects of the mask that are ok change for LifeV, but for now just check the whole thing. -!!! if ( sum(stateNew % vertexMask % array - stateOld % vertexMask % array) /= 0 ) then -!!! blockVertexMaskChanged = 1 -!!! else -!!! blockVertexMaskChanged = 0 -!!! endif -!!! !print *, 'blockVertexMaskChanged ', blockVertexMaskChanged - -!!! ! Determine if any blocks on this processor had a change to the vertex mask -!!! procVertexMaskChanged = max(procVertexMaskChanged, blockVertexMaskChanged) - -!!! block => block % next -!!! end do -!!! -!!! ! Determine if the vertex mask has changed on any processor (need to exit the block loop to do so) -!!! call mpas_dmpar_max_int(dminfo, procVertexMaskChanged, anyVertexMaskChanged) - - call mpas_timer_stop("calc. diagnostic vars except vel") - - - - ! === - ! === Diagnostic solve of velocity - ! === - if (solveVelo) then - call mpas_timer_start("velocity solve") - - ! TODO Once multiple blocks are supported, this section will need to change. - ! LifeV does not support multiple blocks but the MPAS SIA could. - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - -!!! ! Assign the vertex-changed flag to each block -!!! stateNew % anyVertexMaskChanged % scalar = anyVertexMaskChanged -!!! !print *, 'anyVertexMaskChanged: ', anyVertexMaskChanged - - call li_velocity_solve(meshPool, statePool, timeLevel, err_tmp) ! ****** Calculate Velocity ****** - err = ior(err, err_tmp) - - block => block % next - end do - - ! update halos on velocity - call mpas_timer_start("halo updates") - call mpas_dmpar_field_halo_exch(domain, 'normalVelocity', timeLevel=timeLevel) - call mpas_timer_stop("halo updates") - - call mpas_timer_stop("velocity solve") - endif - - - ! === - ! === Diagnostic solve of variables after velocity - ! === - - call mpas_timer_start("calc. diagnostic vars except vel") - - ! Calculate reconstructed velocities - ! do this after velocity halo update in case velocities on the 1-halo edge are wrong (depends on velocity solver) - ! Still do this even if we didn't calculate velocity because on a restart these will be defined at the initial time. - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'uReconstructX', uReconstructX, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'uReconstructY', uReconstructY, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'uReconstructZ', uReconstructZ, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'uReconstructZonal', uReconstructZonal, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'uReconstructMeridional', uReconstructMeridional, timeLevel=timeLevel) - - call mpas_reconstruct(meshPool, normalVelocity, & - uReconstructX, uReconstructY, uReconstructZ, & - uReconstructZonal, uReconstructMeridional ) - - block => block % next - end do - - - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call diagnostic_solve_after_velocity(meshPool, statePool, timeLevel, err) ! Some diagnostic variables require velocity to compute - err = ior(err, err_tmp) - - block => block % next - end do - - call mpas_timer_start("halo updates") - call mpas_dmpar_field_halo_exch(domain, 'layerThicknessEdge', timeLevel=timeLevel) - call mpas_timer_stop("halo updates") - - call mpas_timer_stop("calc. diagnostic vars except vel") - - ! === error check and exit - if (err == 1) then - print *, "An error has occurred in li_calculate_diagnostic_vars. Aborting..." - !call mpas_dmpar_abort(dminfo) - endif - - !-------------------------------------------------------------------- - end subroutine li_calculate_diagnostic_vars - - - - -!*********************************************************************** -!*********************************************************************** -! Private subroutines: -!*********************************************************************** -!*********************************************************************** - - -!*********************************************************************** -! -! subroutine diagnostic_solve_before_velocity -! -!> \brief Computes diagnostic variables prior to velocity -!> \author Matt Hoffman -!> \date 19 April 2012 -!> \details -!> This routine computes the diagnostic variables for land ice -!> that are needed before velocity is solved. -! -!----------------------------------------------------------------------- - subroutine diagnostic_solve_before_velocity(domain, timeLevel, err)!{{{ - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - integer, intent(in) :: timeLevel !< Input: Time level on which to calculate diagnostic variables - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (domain_type), intent(inout) :: & - domain !< Input/Output: Domain object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - type (block_type), pointer :: block - type (mpas_pool_type), pointer :: meshPool - type (mpas_pool_type), pointer :: statePool - real (kind=RKIND), dimension(:), pointer :: thickness, upperSurface, & - lowerSurface, bedTopography, upperSurfaceVertex - integer, dimension(:), pointer :: cellMask - real (kind=RKIND), dimension(:,:), pointer :: layerThickness - real (kind=RKIND), dimension(:,:,:), pointer :: tracers - integer, pointer :: nCells - real (kind=RKIND), pointer :: config_sea_level, config_ice_density, config_ocean_density - real (kind=RKIND) :: thisThk - integer :: iCell, iLevel - integer :: err_tmp - - - err = 0 - err_tmp = 0 - - block => domain % blocklist - do while (associated(block)) - ! Mesh information - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - - ! Calculate masks - needs to happen before calculating lower surface so we know where the ice is floating - call li_calculate_mask(meshPool, statePool, timeLevel, err_tmp) - err = ior(err, err_tmp) - - block => block % next - end do - - ! Update halos on masks - the outermost cells/edges/vertices may be wrong for mask components that need neighbor information - call mpas_timer_start("halo updates") - call mpas_dmpar_field_halo_exch(domain, 'cellMask', timeLevel=timeLevel) - call mpas_dmpar_field_halo_exch(domain, 'edgeMask', timeLevel=timeLevel) - call mpas_dmpar_field_halo_exch(domain, 'vertexMask', timeLevel=timeLevel) - call mpas_timer_stop("halo updates") - -!!! ! Update beta before the velocity solve occurs, now that we have the new state and its mask. -!!! ! It's ok to overwrite the beta values with 0's here, because each time step -!!! ! we get a fresh copy of the array from the annual_forcing subroutine. -!!! ! Note: some velocity solvers may do this on their own, but we are doing it here for completeness. -!!! where ( MASK_IS_FLOATING(cellMask) ) -!!! beta = 0.0_RKIND -!!! end where - -!!! ! Give non-ice cells a temperature of 0 -!!! do iCell = 1, nCells -!!! if ( MASK_IS_NOT_ICE(cellMask(iCell)) ) then -!!! state % tracers % array( state%index_temperature, :, iCell) = 0.0_RKIND -!!! end if -!!! end do - - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - - call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) - call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) - call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) - - call mpas_pool_get_array(statePool, 'cellMask', cellMask, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'thickness', thickness, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'upperSurface', upperSurface, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'upperSurfaceVertex', upperSurfaceVertex, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'lowerSurface', lowerSurface, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel=timeLevel) - call mpas_pool_get_array(meshPool, 'bedTopography', bedTopography) - - ! Lower surface is based on floatation for floating ice. For grounded ice (and non-ice areas) it is the bed. - where ( li_mask_is_floating_ice(cellMask) ) - lowerSurface = config_sea_level - thickness * (config_ice_density / config_ocean_density) - elsewhere - lowerSurface = bedTopography - end where - ! Make sure lowerSurface calculation is reasonable. This check could be deleted once this has been throroughly tested. - do iCell = 1, nCells - if (lowerSurface(iCell) < bedTopography(iCell)) then - call mpas_log_write('lowerSurface less than bedTopography at cell: $i', MPAS_LOG_ERR, intArgs=(/iCell/)) - err = 1 - endif - end do - - ! Upper surface is the lower surface plus the thickness - upperSurface(:) = lowerSurface(:) + thickness(:) - call cells_to_vertices_2dfield(meshPool, upperSurface, upperSurfaceVertex) ! (Needed only for SIA solver) - ! Note: the outer halo may be wrong, but that's ok as long as numhalos>1 because the velocity on the 0-halo will still be correct. - - ! Do vertical remapping of layerThickness and tracers - call vertical_remap(thickness, cellMask, meshPool, layerThickness, tracers, err) - err = ior(err, err_tmp) - - block => block % next - end do - - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in diagnostic_solve_before_velocity.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine diagnostic_solve_before_velocity - - - -!*********************************************************************** -! -! subroutine diagnostic_solve_after_velocity -! -!> \brief Computes diagnostic variables that require knowing velocity -!> \author Matt Hoffman -!> \date 19 April 2012 -!> \details -!> This routine computes the diagnostic variables that require knowing velocity for land ice -! -!----------------------------------------------------------------------- - subroutine diagnostic_solve_after_velocity(meshPool, statePool, timeLevel, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - integer, intent(in) :: timeLevel !< Input: Time level on which to calculate diagnostic variables - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - type (mpas_pool_type), intent(inout) :: & - statePool !< Input/Output: state for which to update diagnostic variables - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - real (kind=RKIND), dimension(:,:), pointer :: layerThickness, layerThicknessEdge, normalVelocity - integer, dimension(:,:), pointer :: cellsOnEdge - integer, pointer :: nEdges, nVertLevels - character (len=StrKIND), pointer :: config_thickness_advection - integer :: iEdge, cell1, cell2, k - real (kind=RKIND) :: VelSign - - err = 0 - - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - - call mpas_pool_get_config(liConfigs, 'config_thickness_advection', config_thickness_advection) - - call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'layerThicknessEdge', layerThicknessEdge, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=timeLevel) - - ! Calculate h_edge. This is used by both thickness and tracer advection on the following Forward Euler time step. - ! Note: FO-Upwind thickness advection does not explicitly use h_edge but a FO h_edge is implied. - ! Note: SIA velocity solver uses its own local calculation of h_edge that is always 2nd order. - ! Note: ocn_diagnostic_solve in mpas_ocn_tendency.F has 2, 3, & 4th order calculations for h_edge that can be used. - ! NOTE: This calculates FO upwind h edge - - ! Both thickness and layerThickness should be updated by this time. - if (config_thickness_advection == 'fo') then - ! If using FO-Upwind then h_edge must be FO. - do iEdge=1,nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - do k=1, nVertLevels - ! Calculate h on edges using first order - VelSign = sign(1.0_RKIND, normalVelocity(k, iEdge)) - layerThicknessEdge(k,iEdge) = max(VelSign * layerThickness(k, cell1), VelSign * (-1.0_RKIND) * layerThickness(k, cell2)) - ! + velocity goes from index 1 to 2 in the cellsOnEdge array. - ! Doug does the calculation as: h_edge = max(VelSign, 0.0) * h1 - min(VelSign, 0.0) * h2 - !!! ! Calculate h on edges using second order - !!! layerThicknessEdge(k,iEdge) = 0.5_RKIND * (layerThickness(k, cell1) + layerThickness(k, cell2)) - end do - ! thickness_edge is not currently in registry and not currenly needed. If it is, uncomment the next line - !h_edge = max(thickness(cell1), thickness(cell2)) - !!!h_edge = (thickness(k) + thickness(k) ) / 2.0 ! 2nd order - end do - else - !write(6,*) 'layerThicknessEdge not calculated!' - endif - - ! Note: the outmost layerThicknessEdge may be wrong if its upwind cell is off this block - halo update should be done if this variable will be used. - - end subroutine diagnostic_solve_after_velocity - - - -!*********************************************************************** -! -! subroutine vertical_remap_cism_loops -! -!> \brief Vertical remapping of thickness and tracers -!> \author Matt Hoffman -!> \date 06 February 2013 -!> \details -!> This routine performs vertical remapping of thickness and tracers from one vertical -!> coordinate system to another, as is required for our sigma coordinate system. -!> The remapping is first-order accurate. -!> This uses code from the CISM glissade_transport.F90 module written by Bill Lipscomb. -!> I have altered the array structures to work with MPAS. Indexing/looping order is a bit -!> of a hodgepodge at the moment and should be optimized. -!> -!> NOTE: This version (with _cism_loops suffix) uses the looping with minimal changes -!> from the CISM code. The other version has been refactored to be more efficient -!> for MPAS. This version is intended to be deleted once tracer advection is -!> added to the model and the two versions can be compared. (I can't test the new -!> version until tracer advection exists!) -! -!----------------------------------------------------------------------- - subroutine vertical_remap_cism_loops(layerThickness, thickness, tracers, meshPool, err) - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:), intent(in) :: & - thickness !< Input: - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:,:), intent(inout) :: & - layerThickness !< Input: - - real (kind=RKIND), dimension(:,:,:), intent(inout) :: & - tracers !< Input: - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - ! pointers to mesh arrays - real (kind=RKIND), dimension(:), pointer :: layerThicknessFractions, layerInterfaceSigma - ! local arrays - real (kind=RKIND), dimension(:), allocatable :: recipThickness - real (kind=RKIND), dimension(:,:), allocatable :: layerInterfaceSigma_Input - real (kind=RKIND), dimension(:,:,:), allocatable :: hTsum - ! counters, mesh variables, index variables - integer, pointer :: nCells, nVertLevels - integer :: nTracers, iCell, k, k1, k2, nt - ! stuff for making calculations - real(kind=RKIND) :: thisThk, zhi, zlo, hOverlap - - err = 0 - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - nTracers = size(tracers, 1) - - call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) - call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) - - allocate(recipThickness(nCells+1)) - allocate(layerInterfaceSigma_Input(nVertLevels+1, nCells+1)) - allocate(hTsum(nCells+1, nTracers, nVertLevels)) - - ! *** Calculate reciprocal thickness so we don't divide by 0 - where (thickness > 0.0_RKIND) - recipThickness = 1.0_RKIND / thickness - elsewhere - recipThickness = 0.0_RKIND - end where - - ! *** Calculate vertical sigma coordinates of each layer interface for the input non-sigma state and desired new sigma-based state (we already have that as mesh % layerInterfaceSigma) - layerInterfaceSigma_Input(1,:) = 0.0_RKIND - do k = 2, nVertLevels - layerInterfaceSigma_Input(k,:) = layerInterfaceSigma_Input(k-1,:) + layerThickness(k-1,:) * recipThickness(:) - end do - layerInterfaceSigma_Input(nVertLevels+1,:) = 1.0_RKIND - - ! *** Compute new layer thicknesses (layerInterfaceSigma coordinates) - do iCell = 1, nCells - thisThk = thickness(iCell) - do k = 1, nVertLevels - layerThickness(k,iCell) = layerThicknessFractions(k) * thisThk - end do - ! Check for conservation of mass. Put any residual in the top layer. - layerThickness(1,iCell) = layerThickness(1,iCell) + (thisThk - sum(layerThickness(:,iCell)) ) - end do - ! TODO This conservation check may make layerThicknesses inconsistent with the sigma levels (which are used below in tracer remapping. - - !----------------------------------------------------------------- - ! Compute sum of h*T for each new layer (k2) by integrating - ! over the regions of overlap with old layers (k1). - ! Note: It might be worth trying a more efficient - ! search algorithm if the number of layers is large. - ! This algorithm scales as nlyr^2. - ! Also, may want to rearrange loop order if there are many tracers. - !----------------------------------------------------------------- - - do k2 = 1, nVertLevels - hTsum(:,:,k2) = 0.d0 - do k1 = 1, nVertLevels - do nt = 1, nTracers - do iCell = 1, nCells - zhi = min (layerInterfaceSigma_Input(k1+1,iCell), layerInterfaceSigma(k2+1)) - zlo = max (layerInterfaceSigma_Input(k1,iCell), layerInterfaceSigma(k2)) - hOverlap = max (zhi-zlo, 0.0_RKIND) * thickness(iCell) - hTsum(iCell,nt,k2) = htsum(iCell,nt,k2) & - + tracers(nt,k1,iCell) * hOverlap - enddo ! iCell - enddo ! nt - enddo ! k1 - enddo ! k2 - - !----------------------------------------------------------------- - ! Compute tracer values in new layers - !----------------------------------------------------------------- - - do k = 1, nVertLevels - do nt = 1, nTracers - do iCell = 1, nCells - if (layerThickness(k, iCell) > 0.0_RKIND) then - tracers(nt,k,iCell) = hTsum(iCell,nt,k) / layerThickness(k, iCell) - else - tracers(nt,k,iCell) = 0.0_RKIND - endif - enddo ! iCell - enddo ! nt - enddo ! k - - deallocate(recipThickness) - deallocate(layerInterfaceSigma_Input) - deallocate(hTsum) - - end subroutine vertical_remap_cism_loops - - - - -!*********************************************************************** -! -! subroutine vertical_remap -! -!> \brief Vertical remapping of thickness and tracers -!> \author Matt Hoffman -!> \date 02 October 2013 -!> \details -!> This routine performs vertical remapping of thickness and tracers from one vertical -!> coordinate system to another, as is required for our sigma coordinate system. -!> The remapping is first-order accurate. -!> This uses code from the CISM glissade_transport.F90 module written by Bill Lipscomb. -!> I have altered the array structures to work with MPAS and refactored it. -!> It now does all calculations column-wise, so it can be vectorized using -!> OpenMP over either blocks or cells. -!> I have dealt with 0 thickness in non-ice cells using eps and the cellMask, -!> rather than using if/where-statements. -! -!----------------------------------------------------------------------- - subroutine vertical_remap(thickness, cellMask, meshPool, layerThickness, tracers, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:), intent(in) :: & - thickness !< Input: ice thickness (already updated) - - integer, dimension(:), intent(in) :: & - cellMask !< Input: mask for cells (needed for determining presence/absence of ice) - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:,:), intent(inout) :: & - layerThickness !< Input/Output: thickness of layers (to be updated) - - real (kind=RKIND), dimension(:,:,:), intent(inout) :: & - tracers !< Input/Output: tracer values (to be updated) - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - ! pointers to mesh arrays - real (kind=RKIND), dimension(:), pointer :: layerThicknessFractions, layerInterfaceSigma - ! local arrays - real (kind=RKIND), dimension(:), allocatable :: layerInterfaceSigma_Input - real (kind=RKIND), dimension(:,:), allocatable :: hTsum - ! counters, mesh variables, index variables - integer, pointer :: nCells, nVertLevels - integer :: nTracers, iCell, k, k1, k2, nt - ! stuff for making calculations - real(kind=RKIND) :: thisThk, zhi, zlo, hOverlap - real(kind=RKIND), parameter :: eps = 1.0d-30 ! Make a tiny value so we don't corrupt any cells with ice - - err = 0 - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - nTracers = size(tracers, 1) - - call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) - call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) - - allocate(layerInterfaceSigma_Input(nVertLevels+1)) - allocate(hTsum(nTracers, nVertLevels)) - - do iCell = 1, nCells ! Do all calculations column-wise - - thisThk = thickness(iCell) - - ! *** Calculate vertical sigma coordinates of each layer interface for the input non-sigma state - ! (we already have the desired new sigma-based state as mesh % layerInterfaceSigma) - layerInterfaceSigma_Input(1) = 0.0_RKIND - do k = 2, nVertLevels - layerInterfaceSigma_Input(k) = layerInterfaceSigma_Input(k-1) + layerThickness(k-1, iCell) / (thisThk + eps) - ! Note: including eps to avoid divide by 0, but need to mask out cells with no ice below in calculating - ! layerThickness and tracer values. - end do - layerInterfaceSigma_Input(nVertLevels+1) = 1.0_RKIND - - ! *** Compute new layer thicknesses (layerInterfaceSigma coordinates) - - do k = 1, nVertLevels - layerThickness(k,iCell) = layerThicknessFractions(k) * (thisThk + eps) - ! Note: layerThickness will have garbage values in non-ice cells due to eps - end do - ! Check for conservation of mass. Put any residual in the top layer. - layerThickness(1,iCell) = layerThickness(1,iCell) + (thisThk - sum(layerThickness(:,iCell)) ) - ! TODO This conservation check may make layerThicknesses inconsistent with the sigma levels (which are used below in tracer remapping. - - !----------------------------------------------------------------- - ! Compute sum of h*T for each new layer (k2) by integrating - ! over the regions of overlap with old layers (k1). - ! Note: It might be worth trying a more efficient - ! search algorithm if the number of layers is large. - ! This algorithm scales as nlyr^2. - !----------------------------------------------------------------- - - do k2 = 1, nVertLevels - hTsum(:,k2) = 0.d0 - do k1 = 1, nVertLevels - do nt = 1, nTracers - zhi = min (layerInterfaceSigma_Input(k1+1), layerInterfaceSigma(k2+1)) - zlo = max (layerInterfaceSigma_Input(k1), layerInterfaceSigma(k2)) - hOverlap = max (zhi-zlo, 0.0_RKIND) * thisThk - hTsum(nt,k2) = htsum(nt,k2) + tracers(nt,k1,iCell) * hOverlap - enddo ! nt - enddo ! k1 - enddo ! k2 - - !----------------------------------------------------------------- - ! Compute tracer values in new layers - !----------------------------------------------------------------- - - do k = 1, nVertLevels - do nt = 1, nTracers - tracers(nt,k,iCell) = hTsum(nt,k) / layerThickness(k, iCell) * real(li_mask_is_ice_int(cellMask(iCell)), RKIND) - layerThickness(k,iCell) = layerThickness(k,iCell) * real(li_mask_is_ice_int(cellMask(iCell)), RKIND) - ! Note: the last term is used to multiply by 0 where there is no ice in order to clear out garbage introduced by eps - enddo ! nt - enddo ! k - - enddo ! nCells - - - deallocate(layerInterfaceSigma_Input) - deallocate(hTsum) - - end subroutine vertical_remap - - - -!*********************************************************************** -! -! subroutine cells_to_vertices_2dfield -! -!> \brief Converts a 2d scalar field from cells to vertices -!> \author Matt Hoffman -!> \date 21 May 2012 -!> \details -!> This routine converts a 2d scalar field from cells to vertices. -!----------------------------------------------------------------------- - subroutine cells_to_vertices_2dfield(meshPool, fieldCells, fieldVertices) - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - real (kind=RKIND), dimension(:), intent(in) :: & - fieldCells !< Input: field on cells - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:), intent(out) :: & - fieldVertices !< Input: field on vertices - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - real (kind=RKIND), dimension(:,:), pointer :: kiteAreasOnVertex - integer, dimension(:,:), pointer :: cellsOnVertex - integer, pointer :: nVertices, vertexDegree - integer :: iCell, icell2, iVertex, cellIndex - real (kind=RKIND) :: fVertexAccum, baryweight, weightAccum - - ! Get needed items from mesh pool - call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) - call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) - - call mpas_pool_get_array(meshPool, 'kiteAreasOnVertex', kiteAreasOnVertex) - call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) - - ! Calculate h on vertices using barycentric interpolation - do iVertex = 1, nVertices ! Loop over vertices - fVertexAccum = 0.0_RKIND - weightAccum = 0.0_RKIND - ! Loop over cells on this vertex - do iCell = 1, vertexDegree - cellIndex = cellsOnVertex(iCell, iVertex) - baryweight = 0.0_RKIND - do iCell2 = 1, vertexDegree - if (iCell2 /= icell) baryweight = baryweight + 0.5 * kiteAreasOnVertex(iCell2, iVertex) - enddo - fVertexAccum = fVertexAccum + baryweight * fieldCells(cellIndex) ! add the contribution from this cell's kite - weightAccum = weightAccum + kiteAreasOnVertex(iCell, iVertex) ! This doesn't match areaTriangle for some weird vertices - enddo - fieldVertices(iVertex) = fVertexAccum / weightAccum ! I assume this should never be 0... - enddo - - end subroutine cells_to_vertices_2dfield - -end module li_diagnostic_vars - diff --git a/src/core_landice/mpas_li_mask.F b/src/core_landice/mpas_li_mask.F deleted file mode 100644 index 761690b4f3..0000000000 --- a/src/core_landice/mpas_li_mask.F +++ /dev/null @@ -1,516 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_mask -! -!> \MPAS land-ice mask calculations -!> \author Matt Hoffman -!> \date 10 May 2012 -!> \details -!> This module contains the routines for calculating masks for land ice -!> -! -!----------------------------------------------------------------------- - -module li_mask - - use mpas_derived_types - use mpas_pool_routines - use mpas_dmpar - use mpas_log - use li_setup - - implicit none - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - integer, parameter :: li_mask_ValueIce = 32 ! Giving this the highest current value so it is obvious during visualization - integer, parameter :: li_mask_ValueDynamicIce = 2 - integer, parameter :: li_mask_ValueFloating = 4 - integer, parameter :: li_mask_ValueMargin = 8 ! This is the last cell with ice. - integer, parameter :: li_mask_ValueDynamicMargin = 16 ! This is the last dynamically active cell with ice - integer, parameter :: li_mask_ValueInitialIceExtent = 1 - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - ! all subroutines and functions in this module are public! - - - ! interfaces without a suffix return logicals - ! interfaces with names that end with '_int' return 0/1 - ! TODO Eventually we may decide to only keep and maintain one of these return types. - - interface li_mask_is_ice - module procedure li_mask_is_ice_logout_1d - module procedure li_mask_is_ice_logout_0d - end interface - - - interface li_mask_is_ice_int - module procedure li_mask_is_ice_intout_1d - module procedure li_mask_is_ice_intout_0d - end interface - - - interface li_mask_is_dynamic_ice - module procedure li_mask_is_dynamic_ice_logout_1d - module procedure li_mask_is_dynamic_ice_logout_0d - end interface - - - interface li_mask_is_floating_ice - module procedure li_mask_is_floating_ice_logout_1d - module procedure li_mask_is_floating_ice_logout_0d - end interface - - - interface li_mask_is_grounded_ice - module procedure li_mask_is_grounded_ice_logout_1d - module procedure li_mask_is_grounded_ice_logout_0d - end interface - - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - - -!*********************************************************************** - -contains - - - -!*********************************************************************** -! -! routine li_calculate_mask_init -! -!> \brief Calculates masks for land ice for info needed from initial condition only -!> \author Matt Hoffman -!> \date 25 June 2012 -!> \details -!> This routine Calculates masks for land ice for info needed from initial condition only. -! -!----------------------------------------------------------------------- - - subroutine li_calculate_mask_init(meshPool, statePool, timeLevel, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - integer, intent(in) :: & - timeLevel !< Input: time level for which to init mask - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(inout) :: & - statePool !< Input/Output: state information - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - integer, dimension(:), pointer :: cellMask - real(KIND=RKIND), dimension(:), pointer :: thickness - logical, pointer :: config_do_restart - - err = 0 - - ! Assign pointers and variables - call mpas_pool_get_array(statePool, 'cellMask', cellMask, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'thickness', thickness, timeLevel=timeLevel) - - call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) - - - if (config_do_restart .eqv. .false.) then ! We only want to set this bit of the mask when a new simulation starts, but not during a restart. - ! Initialize cell mask to 0 everywhere before we assign anything to it. - cellMask = 0 - where (thickness > 0.0) - cellMask = ior(cellMask, li_mask_ValueInitialIceExtent) - end where - endif - - - !-------------------------------------------------------------------- - - end subroutine li_calculate_mask_init - - - -!*********************************************************************** -! -! routine land_ice_calculate_mask -! -!> \brief Calculates masks for land ice -!> \author Matt Hoffman -!> \date 10 May 2012 -!> \details -!> This routine Calculates masks for land ice. -! -!----------------------------------------------------------------------- - - subroutine li_calculate_mask(meshPool, statePool, timeLevel, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - integer, intent(in) :: & - timeLevel !< Input: time level for which to calculate mask - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(inout) :: & - statePool !< Input/Output: state information - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - integer, pointer :: nCells, nVertices, nEdges, vertexDegree - real(KIND=RKIND), dimension(:), pointer :: thickness, bedTopography - integer, dimension(:), pointer :: nEdgesOnCell, cellMask, vertexMask, edgeMask - integer, dimension(:,:), pointer :: cellsOnCell, cellsOnVertex, cellsOnEdge - real (kind=RKIND), pointer :: config_ice_density, config_ocean_density, & - config_sea_level, config_dynamic_thickness - - integer :: i, j, iCell - logical :: isMargin - logical :: aCellOnVertexHasIce, aCellOnVertexHasNoIce, aCellOnVertexHasDynamicIce, aCellOnVertexHasNoDynamicIce, aCellOnVertexIsFloating - logical :: aCellOnEdgeHasIce, aCellOnEdgeHasNoIce, aCellOnEdgeHasDynamicIce, aCellOnEdgeHasNoDynamicIce, aCellOnEdgeIsFloating - - - err = 0 - - ! Assign pointers and variables - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) - - call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) - call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) - call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) - call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - call mpas_pool_get_array(meshPool, 'bedTopography', bedTopography) - - call mpas_pool_get_array(statePool, 'cellMask', cellMask, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'edgeMask', edgeMask, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'vertexMask', vertexMask, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'thickness', thickness, timeLevel=timeLevel) - - call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) - call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) - call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) - call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) - - ! ==== - ! Calculate cellMask values=========================== - ! ==== - - ! Set mask to 0 everywhere, but need to preserve bits the initial ice extent bit - do i=1, nCells - cellMask(i) = iand(cellMask(i), li_mask_ValueInitialIceExtent) - enddo - - ! Identify cells with ice - where (thickness > 0) - cellMask = ior(cellMask, li_mask_ValueIce) - end where - - ! Identify cells where the ice is above the ice dynamics thickness limit - where ( thickness > config_dynamic_thickness ) - cellMask = ior(cellMask, li_mask_ValueDynamicIce) - end where - ! see below for an additional check - - ! Is it floating? (ice thickness equal to floatation is considered floating) - ! For now floating ice and grounded ice are mutually exclusive. - ! This may change if a ground line parameterization is added. - where ( li_mask_is_ice(cellMask) .and. (config_ice_density / config_ocean_density * thickness) <= (config_sea_level - bedTopography) ) - cellMask = ior(cellMask, li_mask_ValueFloating) - end where - - ! Identify the margin - ! For a cell, we define the margin as the last cell with ice (the cell has ice and at least one neighbor is a non-ice cell) - do i=1,nCells - if (li_mask_is_ice(cellMask(i))) then - isMargin = .false. - do j=1,nEdgesOnCell(i) ! Check if any neighbors are non-ice - isMargin = ( isMargin .or. (.not. li_mask_is_ice(cellMask(cellsOnCell(j,i)))) ) - enddo - if (isMargin) then - cellMask(i) = ior(cellMask(i), li_mask_ValueMargin) - endif - endif - enddo - - ! Identify the dynamic margin - ! For a cell, we define the dynamic margin as the last cell with dynamic ice (the cell is dynamic and at least one neighboring cell is not dynamic) - do i=1,nCells - if (li_mask_is_dynamic_ice(cellMask(i))) then - isMargin = .false. - do j=1,nEdgesOnCell(i) ! Check if any neighbors are not dynamic - isMargin = ( isMargin .or. (.not. li_mask_is_dynamic_ice(cellMask(cellsOnCell(j,i)))) ) - enddo - if (isMargin) then - cellMask(i) = ior(cellMask(i), li_mask_ValueDynamicMargin) - endif - endif - enddo - - - ! ==== - ! Calculate vertexMask values based on cellMask values=========================== - ! ==== - ! Bit: Vertices with ice are ones with at least one adjacent cell with ice - ! Bit: Vertices with dynamic ice are ones with at least one adjacent cell with dynamic ice - ! Bit: Floating vertices have at least one neighboring cell floating - ! Bit: Vertices on margin are vertices with at least one neighboring cell with ice and at least one neighboring cell without ice - ! Bit: Vertices on dynamic margin are vertices with at least one neighboring cell with dynamic ice and at least one neighboring cell without dynamic ice - vertexMask = 0 - do i = 1,nVertices - aCellOnVertexHasIce = .false. - aCellOnVertexHasNoIce = .false. - aCellOnVertexHasDynamicIce = .false. - aCellOnVertexHasNoDynamicIce = .false. - aCellOnVertexIsFloating = .false. - do j = 1, vertexDegree ! vertexDegree is usually 3 (e.g. CVT mesh) but could be something else (e.g. 4 for quad mesh) - iCell = cellsOnVertex(j,i) - aCellOnVertexHasIce = (aCellOnVertexHasIce .or. li_mask_is_ice(cellMask(iCell))) - aCellOnVertexHasNoIce = (aCellOnVertexHasNoIce .or. (.not. li_mask_is_ice(cellMask(iCell)))) - aCellOnVertexHasDynamicIce = (aCellOnVertexHasDynamicIce .or. li_mask_is_dynamic_ice(cellMask(iCell))) - aCellOnVertexHasNoDynamicIce = (aCellOnVertexHasNoDynamicIce .or. (.not. (li_mask_is_dynamic_ice(cellMask(iCell))))) - aCellOnVertexIsFloating = (aCellOnVertexIsFloating .or. li_mask_is_floating_ice(cellMask(iCell))) - end do - if (aCellOnVertexHasIce) then - vertexMask(i) = ior(vertexMask(i), li_mask_ValueIce) - endif - if (aCellOnVertexHasDynamicIce) then - vertexMask(i) = ior(vertexMask(i), li_mask_ValueDynamicIce) - endif - if (aCellOnVertexIsFloating) then - vertexMask(i) = ior(vertexMask(i), li_mask_ValueFloating) - endif - if (aCellOnVertexHasIce .and. aCellOnVertexHasNoIce) then - vertexMask(i) = ior(vertexMask(i), li_mask_ValueMargin) ! vertex with both 1+ ice cell and 1+ non-ice cell as neighbors - endif - if (aCellOnVertexHasDynamicIce .and. aCellOnVertexHasNoDynamicIce) then - vertexMask(i) = ior(vertexMask(i), li_mask_ValueDynamicMargin) ! vertex with both 1+ dynamic ice cell(s) and 1+ non-dynamic cell(s) as neighbors - endif - end do - - - ! ==== - ! Calculate edgeMask values based on cellMask values=========================== - ! ==== - ! Bit: Edges with ice are ones with at least one adjacent cell with ice - ! Bit: Edges with dynamic ice are ones with at least one adjacent cell with dynamic ice - ! Bit: Floating Edges have at least one neighboring cell floating - ! Bit: Edges on margin are vertices with one neighboring cell with ice and one neighboring cell without ice - ! Bit: Edges on dynamic margin are vertices with at least one neighboring cell with dynamic ice and at least one neighboring cell without dynamic ice - edgeMask = 0 - do i = 1,nEdges - aCellOnEdgeHasIce = .false. - aCellOnEdgeHasNoIce = .false. - aCellOnEdgeHasDynamicIce = .false. - aCellOnEdgeHasNoDynamicIce = .false. - aCellOnEdgeIsFloating = .false. - do j = 1, 2 - iCell = cellsOnEdge(j,i) - aCellOnEdgeHasIce = (aCellOnEdgeHasIce .or. li_mask_is_ice(cellMask(iCell))) - aCellOnEdgeHasNoIce = (aCellOnEdgeHasNoIce .or. (.not. li_mask_is_ice(cellMask(iCell)))) - aCellOnEdgeHasDynamicIce = (aCellOnEdgeHasDynamicIce .or. li_mask_is_dynamic_ice(cellMask(iCell))) - aCellOnEdgeHasNoDynamicIce = (aCellOnEdgeHasNoDynamicIce .or. (.not. (li_mask_is_dynamic_ice(cellMask(iCell))))) - aCellOnEdgeIsFloating = (aCellOnEdgeIsFloating .or. li_mask_is_floating_ice(cellMask(iCell))) - end do - if (aCellOnEdgeHasIce) then - edgeMask(i) = ior(edgeMask(i), li_mask_ValueIce) - endif - if (aCellOnEdgeHasDynamicIce) then - edgeMask(i) = ior(edgeMask(i), li_mask_ValueDynamicIce) - endif - if (aCellOnEdgeIsFloating) then - edgeMask(i) = ior(edgeMask(i), li_mask_ValueFloating) - endif - if (aCellOnEdgeHasIce .and. aCellOnEdgeHasNoIce) then - edgeMask(i) = ior(edgeMask(i), li_mask_ValueMargin) - endif - if (aCellOnEdgeHasDynamicIce .and. aCellOnEdgeHasNoDynamicIce) then - edgeMask(i) = ior(edgeMask(i), li_mask_ValueDynamicMargin) - endif - - end do - - ! vertexMask and edgeMask needs halo updates before they can be used. Halo updates need to occur outside of block loops. - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_calculate_mask.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine li_calculate_mask - - - - ! =================================== - ! Functions for decoding bitmasks - will work with cellMask, edgeMask, or vertexMask - ! =================================== - ! Only adding the minimum needed for now. These should be added as needed. - ! functions with names that include '_logout' return logical types - ! -- these should be used with 'if' and 'where' statements - ! functions with names that include '_intout' return integers types with 0 for false, 1 for true. - ! -- these should be used when multiplying against numeric arrays - - - ! -- Functions that check for presence of ice -- - function li_mask_is_ice_logout_1d(mask) - integer, dimension(:), intent(in) :: mask - logical, dimension(size(mask)) :: li_mask_is_ice_logout_1d - - li_mask_is_ice_logout_1d = (iand(mask, li_mask_ValueIce) == li_mask_ValueIce) - end function li_mask_is_ice_logout_1d - - function li_mask_is_ice_logout_0d(mask) - integer, intent(in) :: mask - logical :: li_mask_is_ice_logout_0d - - li_mask_is_ice_logout_0d = (iand(mask, li_mask_ValueIce) == li_mask_ValueIce) - end function li_mask_is_ice_logout_0d - - - function li_mask_is_ice_intout_1d(mask) - integer, dimension(:), intent(in) :: mask - integer, dimension(size(mask)) :: li_mask_is_ice_intout_1d - - li_mask_is_ice_intout_1d = iand(mask, li_mask_ValueIce) / li_mask_ValueIce - end function li_mask_is_ice_intout_1d - - function li_mask_is_ice_intout_0d(mask) - integer, intent(in) :: mask - integer :: li_mask_is_ice_intout_0d - - li_mask_is_ice_intout_0d = iand(mask, li_mask_ValueIce) / li_mask_ValueIce - end function li_mask_is_ice_intout_0d - - - ! -- Functions that check for presence of dynamic ice -- - function li_mask_is_dynamic_ice_logout_1d(mask) - integer, dimension(:), intent(in) :: mask - logical, dimension(size(mask)) :: li_mask_is_dynamic_ice_logout_1d - - li_mask_is_dynamic_ice_logout_1d = (iand(mask, li_mask_ValueDynamicIce) == li_mask_ValueDynamicIce) - end function li_mask_is_dynamic_ice_logout_1d - - function li_mask_is_dynamic_ice_logout_0d(mask) - integer, intent(in) :: mask - logical :: li_mask_is_dynamic_ice_logout_0d - - li_mask_is_dynamic_ice_logout_0d = (iand(mask, li_mask_ValueDynamicIce) == li_mask_ValueDynamicIce) - end function li_mask_is_dynamic_ice_logout_0d - - - ! -- Functions that check for presence of floating ice -- - function li_mask_is_floating_ice_logout_1d(mask) - integer, dimension(:), intent(in) :: mask - logical, dimension(size(mask)) :: li_mask_is_floating_ice_logout_1d - - li_mask_is_floating_ice_logout_1d = (iand(mask, li_mask_ValueFloating) == li_mask_ValueFloating) - end function li_mask_is_floating_ice_logout_1d - - function li_mask_is_floating_ice_logout_0d(mask) - integer, intent(in) :: mask - logical :: li_mask_is_floating_ice_logout_0d - - li_mask_is_floating_ice_logout_0d = (iand(mask, li_mask_ValueFloating) == li_mask_ValueFloating) - end function li_mask_is_floating_ice_logout_0d - - - ! -- Functions that check for presence of grounded ice -- - function li_mask_is_grounded_ice_logout_1d(mask) - integer, dimension(:), intent(in) :: mask - logical, dimension(size(mask)) :: li_mask_is_grounded_ice_logout_1d - - li_mask_is_grounded_ice_logout_1d = ( (iand(mask, li_mask_ValueFloating) /= li_mask_ValueFloating) & - .and. (li_mask_is_ice(mask)) ) - end function li_mask_is_grounded_ice_logout_1d - - function li_mask_is_grounded_ice_logout_0d(mask) - integer, intent(in) :: mask - logical :: li_mask_is_grounded_ice_logout_0d - - li_mask_is_grounded_ice_logout_0d = ( (iand(mask, li_mask_ValueFloating) /= li_mask_ValueFloating) & - .and. (li_mask_is_ice(mask)) ) - end function li_mask_is_grounded_ice_logout_0d - - - - - - -!*********************************************************************** -! Private subroutines: -!*********************************************************************** - -! - no private subroutines - (module is not declared private) - - -end module li_mask - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| - diff --git a/src/core_landice/mpas_li_setup.F b/src/core_landice/mpas_li_setup.F deleted file mode 100644 index d7ed1042aa..0000000000 --- a/src/core_landice/mpas_li_setup.F +++ /dev/null @@ -1,307 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_setup -! -!> \brief MPAS land ice setup module -!> \author Matt Hoffman -!> \date 17 April 2011 -!> \details -!> This module contains various subroutines for -!> setting up the land ice core. -! -!----------------------------------------------------------------------- -module li_setup - - use mpas_derived_types - use mpas_pool_routines - use mpas_kind_types - use mpas_dmpar - use mpas_log - - implicit none - private - save - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - type (mpas_pool_type), pointer :: liConfigs !< Public parameter: pool of config options - - public :: liConfigs - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - public :: li_setup_config_options, & - li_setup_vertical_grid, & - li_setup_sign_and_index_fields - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - - -!*********************************************************************** - -contains - - -!*********************************************************************** -! -! routine li_setup_config_options -! -!> \brief Makes any setup changes needed based on chosen config options -!> \author Matt Hoffman -!> \date 16 April 2014 -!> \details -!> This routine makes any adjustments as needed based on which -!> config options were chosen. -! -!----------------------------------------------------------------------- - - subroutine li_setup_config_options( domain, err ) - - use mpas_timekeeping - - !----------------------------------------------------------------- - ! input variables - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! input/output variables - !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! output variables - !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! local variables - !----------------------------------------------------------------- - integer, pointer :: config_year_digits - - err = 0 - - ! Make config pool publicly available in this module - liConfigs => domain % configs - - ! --- - ! Config-specific setup occurs below - ! --- - - ! Adjust number of digits representing the year - call mpas_pool_get_config(liConfigs, 'config_year_digits', config_year_digits) - call mpas_timekeeping_set_year_width(config_year_digits) - - !-------------------------------------------------------------------- - end subroutine li_setup_config_options - - - -!*********************************************************************** -! -! routine li_setup_vertical_grid -! -!> \brief Initializes vertical coord system -!> \author Matt Hoffman -!> \date 20 April 2012 -!> \details -!> This routine initializes the vertical coord system. -! -!----------------------------------------------------------------------- - - subroutine li_setup_vertical_grid(meshPool, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - type (mpas_pool_type), intent(inout) :: meshPool !< Input/Output: mesh object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - ! Pool pointers - integer, pointer :: nVertLevels ! Dimensions - real (kind=RKIND), dimension(:), pointer :: layerThicknessFractions, layerCenterSigma, layerInterfaceSigma - ! Truly locals - integer :: k - real (kind=RKIND) :: fractionTotal - - ! Get pool stuff - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - ! layerThicknessFractions is provided by input - call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) - call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) - call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) - - ! Check that layerThicknessFractions are valid - ! TODO - switch to having the user input the sigma levels instead??? - fractionTotal = sum(layerThicknessFractions) - if (fractionTotal /= 1.0_RKIND) then - if (abs(fractionTotal - 1.0_RKIND) > 0.001_RKIND) then - call mpas_log_write('The sum of layerThicknessFractions is different from 1.0 by more than 0.001.', MPAS_LOG_ERR) - err = 1 - end if - call mpas_log_write('Adjusting upper layerThicknessFrac by small amount because sum of layerThicknessFractions is slightly different from 1.0.') - ! TODO - distribute the residual amongst all layers (and then put the residual of that in a single layer - layerThicknessFractions(1) = layerThicknessFractions(1) - (fractionTotal - 1.0_RKIND) - endif - - ! layerCenterSigma is the fractional vertical position (0-1) of each layer center, with 0.0 at the ice surface and 1.0 at the ice bed - ! layerInterfaceSigma is the fractional vertical position (0-1) of each layer interface, with 0.0 at the ice surface and 1.0 at the ice bed. Interface 1 is the surface, interface 2 is between layers 1 and 2, etc., and interface nVertLevels+1 is the bed. - layerCenterSigma(1) = 0.5_RKIND * layerThicknessFractions(1) - layerInterfaceSigma(1) = 0.0_RKIND - do k = 2, nVertLevels - layerCenterSigma(k) = layerCenterSigma(k-1) + 0.5_RKIND * layerThicknessFractions(k-1) & - + 0.5_RKIND * layerThicknessFractions(k) - layerInterfaceSigma(k) = layerInterfaceSigma(k-1) + layerThicknessFractions(k-1) - end do - layerInterfaceSigma(nVertLevels+1) = 1.0_RKIND - - !-------------------------------------------------------------------- - end subroutine li_setup_vertical_grid - - - -!*********************************************************************** -! -! routine li_setup_sign_and_index_fields -! -!> \brief Determines signs for various mesh items -!> \author Matt Hoffman - based on code by Doug Jacobsen -!> \date 20 April 2012 -!> \details -!> This routine determines the sign for various mesh items. -! -!----------------------------------------------------------------------- - subroutine li_setup_sign_and_index_fields(meshPool) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - type (mpas_pool_type), intent(inout) :: meshPool !< Input/Output: mesh object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - ! Pool pointers - integer, pointer :: nCells !, nVertices, vertexDegree - integer, dimension(:), pointer :: nEdgesOnCell - integer, dimension(:,:), pointer :: edgesOnCell, cellsOnEdge !, edgesOnVertex, cellsOnVertex, verticesOnCell, verticesOnEdge - integer, dimension(:,:), pointer :: edgeSignOnCell !, edgeSignOnVertex, kiteIndexOnCell - ! Truly locals - integer :: iCell, iEdge, iVertex, i, j, k - - ! Get pool stuff - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - ! layerThicknessFractions is provided by input - call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) - call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) - call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) - - edgeSignOnCell = 0.0_RKIND - !edgeSignOnVertex = 0.0_RKIND - !kiteIndexOnCell = 0.0_RKIND - ! If needed, edgeSignOnVertex and kiteIndexOnCell can also be setup here. - - do iCell = 1, nCells - do i = 1, nEdgesOnCell(iCell) - iEdge = edgesOnCell(i, iCell) - !iVertex = verticesOnCell(i, iCell) - - ! Vector points from cell 1 to cell 2 - if(iCell == cellsOnEdge(1, iEdge)) then - edgeSignOnCell(i, iCell) = -1 - else - edgeSignOnCell(i, iCell) = 1 - end if - - !do j = 1, vertexDegree - ! if(cellsOnVertex(j, iVertex) == iCell) then - ! kiteIndexOnCell(i, iCell) = j - ! end if - !end do - end do - end do - - !do iVertex = 1, nVertices - ! do i = 1, vertexDegree - ! iEdge = edgesOnVertex(i, iVertex) - ! - ! ! Vector points from vertex 1 to vertex 2 - ! if(iVertex == verticesOnEdge(1, iEdge)) then - ! edgeSignOnVertex(i, iVertex) = -1 - ! else - ! edgeSignOnVertex(i, iVertex) = 1 - ! end if - ! end do - !end do - - !-------------------------------------------------------------------- - end subroutine li_setup_sign_and_index_fields - - - -!*********************************************************************** -!*********************************************************************** -! Private subroutines: -!*********************************************************************** -!*********************************************************************** - - - -end module li_setup diff --git a/src/core_landice/mpas_li_sia.F b/src/core_landice/mpas_li_sia.F deleted file mode 100644 index 04c2e7bcd5..0000000000 --- a/src/core_landice/mpas_li_sia.F +++ /dev/null @@ -1,357 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_sia -! -!> \MPAS land-ice SIA velocity driver -!> \author Matt Hoffman -!> \date 16 March 2012 -!> \details -!> This module contains the routines for calculating velocity using the shallow ice approximation. -!> -! -!----------------------------------------------------------------------- - -module li_sia - - use mpas_derived_types - use mpas_pool_routines - use mpas_dmpar - use mpas_log - use li_mask - use li_setup - - implicit none - private - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - public :: li_sia_init, & - li_sia_finalize, & - li_sia_block_init, & - li_sia_solve - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - - -!*********************************************************************** - -contains - -!*********************************************************************** -! -! routine li_sia_init -! -!> \brief Initializes SIA velocity solver -!> \author Matt Hoffman/Xylar Asay-Davis -!> \date 16 March 2012 -!> \details -!> This routine initializes the SIA ice velocity solver. -! -!----------------------------------------------------------------------- - - subroutine li_sia_init(domain, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - - ! No init is needed. - err = 0 - - !-------------------------------------------------------------------- - - end subroutine li_sia_init - - - -!*********************************************************************** -! -! routine li_sia_block_init -! -!> \brief Initializes blocks for SIA velocity solver -!> \author Matt Hoffman/Xylar Asay-Davis -!> \date 16 March 2012 -!> \details -!> This routine initializes each block of the SIA ice velocity solver. -! -!----------------------------------------------------------------------- - - subroutine li_sia_block_init(block, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - type (block_type), intent(inout) :: & - block !< Input/Output: block object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - ! No block init needed. - err = 0 - - !-------------------------------------------------------------------- - end subroutine li_sia_block_init - - - -!*********************************************************************** -! -! subroutine li_sia_solve -! -!> \brief Computes velocity using Shallow Ice Appoximation -!> \author Matt Hoffman -!> \date 21 May 2012 -!> \details -!> This routine computes the normal velocity on edges for each layer -!> using the Shallow Ice Approximation. It calculates ice thickness on -!> on an edge using the average of the two neighboring cells (2nd order). -! -!----------------------------------------------------------------------- - subroutine li_sia_solve(meshPool, statePool, timeLevel, err) - use mpas_constants, only: gravity - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - integer, intent(in) :: & - timeLevel !< Input: time level from which to calculate velocity - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(inout) :: & - statePool !< Input: state information - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:), pointer :: thickness, layerCenterSigma, dcEdge, dvEdge, upperSurface, upperSurfaceVertex - real (kind=RKIND), dimension(:,:), pointer :: normalVelocity - integer, dimension(:,:), pointer :: cellsOnEdge, verticesOnEdge - integer, dimension(:), pointer :: edgeMask - integer, pointer :: nVertLevels, nEdges, nVertices, vertexDegree - integer :: iLevel, iEdge, iCell, iVertex, cell1, cell2, cellIndex - real (kind=RKIND) :: basalVelocity, slopeOnEdge, & - normalSlopeOnEdge, tangentSlopeOnEdge, & - layerCenterHeightOnEdge, thicknessEdge, hVertexAccum - real (kind=RKIND), pointer :: rhoi ! ice density - real (kind=RKIND), pointer :: ratefactor ! flow law parameter, A - real (kind=RKIND), pointer :: n ! flow law exponent, n - - err = 0 - - ! Set needed variables and pointers - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) - call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) - - call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) - call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) - call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) - call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) - - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'thickness', thickness, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'edgeMask', edgeMask, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'upperSurface', upperSurface, timeLevel=timeLevel) - call mpas_pool_get_array(statePool, 'upperSurfaceVertex', upperSurfaceVertex, timeLevel=timeLevel) - - - ! Get parameters specified in the namelist - call mpas_pool_get_config(liConfigs, 'config_ice_density', rhoi) - call mpas_pool_get_config(liConfigs, 'config_flowLawExponent', n) - call mpas_pool_get_config(liConfigs, 'config_default_flowParamA', ratefactor) ! units of s^{-1} Pa^{-n} - ! Calculate ratefactor (A) at edge - TODO This should be calculated external to this subroutine and as a function of temperature - - basalVelocity = 0.0_RKIND ! Assume no sliding - - ! Loop over edges - do iEdge = 1, nEdges - ! Only calculate velocity for edges that are part of the dynamic ice sheet.(thick ice) - ! Also, the velocity calculation should be valid for non-ice edges (i.e. returns 0). - if ( li_mask_is_dynamic_ice(edgeMask(iEdge)) ) then - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - ! Calculate slope at edge - ! This could/should be calculated externally to this subroutine - normalSlopeOnEdge = (upperSurface(cell1) - upperSurface(cell2) ) / dcEdge(iEdge) - tangentSlopeOnEdge = ( upperSurfaceVertex(verticesOnEdge(1,iEdge)) - upperSurfaceVertex(verticesOnEdge(2,iEdge)) ) / dvEdge(iEdge) - slopeOnEdge = (normalSlopeOnEdge**2 + tangentSlopeOnEdge**2)**0.5 - ! Calculate thickness on edge - 2nd order - thicknessEdge = (thickness(cell1) + thickness(cell2) ) * 0.5_RKIND - ! Loop over layers - do iLevel = 1, nVertLevels - ! Determine the height of each layer above the bed - layerCenterHeightOnEdge = thicknessEdge * (1.0_RKIND - layerCenterSigma(iLevel) ) - ! Calculate SIA velocity - normalVelocity(iLevel,iEdge) = basalVelocity + & - 0.5_RKIND * ratefactor * (rhoi * gravity)**n * slopeOnEdge**(n-1) * normalSlopeOnEdge * & - (thicknessEdge**(n+1) - (thicknessEdge - layerCenterHeightOnEdge)**(n+1)) - end do ! Levels - else - normalVelocity(:,iEdge) = 0.0_RKIND - endif - end do ! edges - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_sia_solve.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - - end subroutine li_sia_solve - - - - -!*********************************************************************** -! -! routine li_sia_finalize -! -!> \brief finalizes SIA velocity solver -!> \author Matt Hoffman/Xylar Asay-Davis -!> \date 16 March 2012 -!> \details -!> This routine initializes the SIA ice velocity solver. -! -!----------------------------------------------------------------------- - - subroutine li_sia_finalize(domain, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (domain_type), intent(inout) :: domain - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - err = 0 - - - !-------------------------------------------------------------------- - - end subroutine li_sia_finalize - - - - ! private subroutines - - - - -!*********************************************************************** - -end module li_sia - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_landice/mpas_li_tendency.F b/src/core_landice/mpas_li_tendency.F deleted file mode 100644 index 27c823974f..0000000000 --- a/src/core_landice/mpas_li_tendency.F +++ /dev/null @@ -1,936 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_tendency -! -!> \brief MPAS land ice tendency driver -!> \author Matt Hoffman -!> \date 17 April 2011 -!> \details -!> This module contains the routines for computing -!> various tendencies for land ice. As well as routines -!> for computing diagnostic variables. -! -!----------------------------------------------------------------------- - -module li_tendency - - use mpas_derived_types - use mpas_pool_routines - use mpas_constants - use mpas_dmpar - use mpas_log - use li_setup -!!! use li_mask -!!! use mpas_tracer_advection - - implicit none - private - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - public :: li_tendency_thickness, & - li_tendency_tracers, & - li_apply_calving - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - - - contains - - -!*********************************************************************** -! -! subroutine li_tendency_thickness -! -!> \brief Computes tendency term from horizontal advection of thickness -!> \author Matt Hoffman -!> \date 16 April 2012 -!> \details -!> This routine computes the horizontal advection tendency for -!> thickness based on current state and user choices of forcings. Based on -!> ocn_thick_hadv_tend in the ocean core. -! -!----------------------------------------------------------------------- - - subroutine li_tendency_thickness(meshPool, statePool, layerThickness_tend, dt, dminfo, allowableDt, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - real (kind=RKIND), intent(in) :: & - dt !< Input: dt - - type (dm_info), pointer, intent(in) :: & - dminfo !< Input: domain info - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(inout) :: & - statePool !< Input: state to use to calculate tendency (old time level) - ! Note: state needs to be inout (rather than just in) so that adjust_marine_boundary_fluxes can modify it. - - real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: & - layerThickness_tend !< Input/Output: layer thickness tendency - -!!! real (kind=RKIND), dimension(:), pointer, intent(inout) :: & -!!! iceArea_tend !< Input/Output: ice area tendency - - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - real (kind=RKIND), intent(out) :: allowableDt - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - integer, pointer :: nVertLevels - real (kind=RKIND), dimension(:), pointer :: sfcMassBal - real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, layerThicknessEdge -!!! real (kind=RKIND), dimension(:), pointer :: iceArea, areaCell, marineBasalMassBal - integer, dimension(:), pointer :: cellMask, edgeMask - character (len=StrKIND), pointer :: config_thickness_advection - real (kind=RKIND), pointer :: config_ice_density - integer :: err_tmp - - - err = 0 - err_tmp = 0 - - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_array(meshPool, 'sfcMassBal', sfcMassBal) - ! Assuming tendency will always be calculated using time level 1! - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=1) - call mpas_pool_get_array(statePool, 'layerThicknessEdge', layerThicknessEdge, timeLevel=1) - call mpas_pool_get_array(statePool, 'cellMask', cellMask, timeLevel=1) - call mpas_pool_get_array(statePool, 'edgeMask', edgeMask, timeLevel=1) -!!! marineBasalMassBal => mesh % marineBasalMassBal % array -!!! iceArea => state % iceArea % array -!!! areaCell => mesh % areaCell % array - call mpas_pool_get_config(liConfigs, 'config_thickness_advection', config_thickness_advection) - call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) - - ! 0 tendency - layerThickness_tend = 0.0_RKIND -!!! iceArea_tend = 0.0_RKIND - - select case (trim(config_thickness_advection)) - case ('fo') !=================================================== - !print *,'Using FO Upwind for thickness advection' - -!!! ! Alternate call to calculate thickness tendency for entire ice column (deprecated) -!!! !call li_tend_h_fo_upwind(mesh, state % normalVelocity % array, & -!!! ! state % layerThickness % array, state % thickness % array, thickness_tend, dt, err) - - call tend_layerthickness_fo_upwind(meshPool, normalVelocity, & - layerThicknessEdge, edgeMask, layerThickness_tend, dt, allowableDt, err_tmp) - - - !!! Experimental implementation of using FCT for thickness tendency calculation - ! This requires additional work, including a separate tendency calculation for - ! advancing cells using FO-Upwind. - !case ('FCT') !=================================================== - ! MJH TEMP TRYING IT WITH THICKNESS - ! stateOld % sup_thickness % array(1,:,:) = stateOld % layerThickness % array !!!!! MJH TEMP - ! block % tend % sup_thickness % array = 0.0 -!!! NOTE FOR FUTURE: These two calls to tracer_advection have changed since this code was written - they are now in framework and the calling signature has changed! - ! call mpas_tracer_advection_tend(stateOld % sup_thickness % array, uh , & - ! wTop, stateOld % sup_thickness % array(1,:,:), stateOld % sup_thickness % array(1,:,:), & - ! dt/SecondsInYear , mesh, 0.0*layerThickness_tend, block % tend % sup_thickness % array) - - ! Doug suggested actually doing it this way: - ! call mpas_tracer_advection_tend(stateOld % sup_thickness % array, normalVelocity , & - ! wTop, stateOld % sup_thickness % array(1,:,:) * 0.0 + 1.0 , stateOld % sup_thickness % array(1,:,:) * 0.0 + 1.0, & - ! dt/SecondsInYear , mesh, 0.0*layerThickness_tend, block % tend % sup_thickness % array) - - ! where (stateOld % sup_thickness % array .gt. 1.0e-9) - ! block % tend % sup_thickness % array = block % tend % sup_thickness % array / stateOld % sup_thickness % array - ! else where - ! block % tend % sup_thickness % array = 0.0 - ! end where - ! assign to the thickness tend i actually use - ! layerThickness_tend = block % tend % sup_thickness % array(1,:,:) - - case ('none') !=================================================== - ! Do nothing - case default !=================================================== - call mpas_log_write(trim(config_thickness_advection) // ' is not a valid thickness advection option.', MPAS_LOG_ERR) - err_tmp = 1 - end select !=================================================== - err = ior(err,err_tmp) - - - ! Add the MB to the tendencies - select case (trim(config_thickness_advection)) - case ('None') !=================================================== - ! Do nothing - don't add the MB - case default -! Commenting BMB out for now. -!!! ! Make some potential adjustments to BMB before applying them. -!!! ! It's ok to overwrite the values with 0's here, because each time step -!!! ! we get a fresh copy of the array from the annual_forcing subroutine. -!!! ! 1. make adjustments for where the ice is grounded and floating. -!!! ! TODO: more complicated treatment at GL? -!!! where ( li_mask_is_grounded_ice(cellMask) ) -!!! ! Apply marineBasalMassBal to floating ice only. -!!! marineBasalMassBal = 0.0_RKIND -!!! elsewhere ( li_mask_is_floating_ice(cellMask) ) -!!! ! Currently, floating and grounded ice are mutually exclusive. -!!! ! This could change if the GL is parameterized, in which case this logic may need adjustment. -!!! ! Grounded BMB should come from the temperature solver. -!!! ! < PLACEHOLDER > -!!! elsewhere ( .not. (li_mask_is_ice(cellMask) ) -!!! ! We don't allow a positive BMB where ice is not already present. -!!! mesh % marineBasalMassBal % array = 0.0_RKIND -!!! end where - - ! Add surface mass balance to tendency - ! TODO: Need to decide how to deal with negative SMB that eliminates top layer or all ice (check for negative thickness?) - layerThickness_tend(1,:) = layerThickness_tend(1,:) + sfcMassBal / config_ice_density ! (tendency in meters per year) - ! TODO THIS MIGHT RESULT IN NEGATIVE LAYER THICKNESS! - -!!! ! Add basal mass balance to tendency -!!! ! TODO: Need to decide how to deal with negative BMB that eliminates top layer or all ice (check for negative thickness?) -!!! layerThickness_tend(nVertLevels,:) = layerThickness_tend(nVertLevels,:) & -!!! + mesh % marineBasalMassBal % array ! (tendency in meters per year) -!!! ! TODO Add in grounded ice basal mass balance once temperature diffusion is calculated -!!! ! TODO THIS MIGHT RESULT IN NEGATIVE LAYER THICKNESS! - end select - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_tendency_thickness.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine li_tendency_thickness - - - -!*********************************************************************** -! -! subroutine li_tendency_tracers -! -!> \brief Computes tendency term from horizontal advection of thickness -!> \author Matt Hoffman -!> \date 16 April 2012 -!> \details -!> This routine computes the horizontal advection tendency for -!> thickness based on current state and user choices of forcings. Based on -!> ocn_thick_hadv_tend in the ocean core. -! -!----------------------------------------------------------------------- - - subroutine li_tendency_tracers(meshPool, statePool, layerThickness_tend, tracer_tendency, dt, dminfo, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - type (mpas_pool_type), intent(in) :: & - statePool !< Input: state to use to calculate tendency - - real (kind=RKIND), dimension(:,:), pointer, intent(in) :: & - layerThickness_tend !< Input/Output: layer thickness tendency - - real (kind=RKIND), intent(in) :: & - dt !< Input: dt - - type (dm_info), pointer, intent(in) :: & - dminfo !< Input: domain info - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:,:,:), pointer, intent(inout) :: & - tracer_tendency !< Input/Output: tracers tendency - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, layerThickness, layerThicknessEdge - real (kind=RKIND), dimension(:,:), allocatable :: wTop, uh - real (kind=RKIND), dimension(:,:,:), pointer :: tracers - integer :: iEdge, k - -! Commenting out this entire subroutine for now. -!!! ! 0 tendency -!!! tracer_tendency = 0.0_RKIND - -!!! select case (config_tracer_advection) -!!! case ('FO') !=================================================== -!!! ! Assign pointers, etc. -!!! layerThicknessEdge => state % layerThicknessEdge % array -!!! normalVelocity => state % normalVelocity % array -!!! tracers => state % tracers % array -!!! allocate(uh(mesh % nVertLevels, mesh % nEdges + 1)) - -!!! ! FO code needs u*h on edges - this could be calculated elsewhere and saved, potentially (e.g. it's already calculated locally in the FO upwind thickness routine) -!!! ! layerThicknessEdge and normalVelocity should match those used in thickness advection! -!!! do iEdge = 1, mesh % nEdges -!!! do k = 1, mesh % nVertLevels -!!! uh(k, iEdge) = normalVelocity(k, iEdge) * layerThicknessEdge(k, iEdge) -!!! end do -!!! end do - -!!! ! Call the FO! -!!! call tracer_advection_tend_fo(tracers, uh, mesh, tracer_tendency, err) - -!!! deallocate(uh) - -!!! case ('FCT') !=================================================== -!!! ! Assign pointers, etc. -!!! layerThickness => state % layerThickness % array -!!! layerThicknessEdge => state % layerThicknessEdge % array -!!! normalVelocity => state % normalVelocity % array -!!! tracers => state % tracers % array -!!! allocate(wTop(mesh % nVertLevels + 1, mesh % nCells + 1)) -!!! allocate(uh(mesh % nVertLevels, mesh % nEdges + 1)) - -!!! ! Setup 0 vertical velocity field - vertical velocity not needed with sigma coordinates -!!! wTop = 0.0_RKIND - -!!! ! prepare for FCT call - -!!! ! This is a kluge because the FCT code will divide by zero if thickness is zero \todo: deal with more elegantly! -!!! where (layerThickness == 0.0_RKIND) -!!! layerThickness = 1.0e-12_RKIND -!!! end where - -!!! ! FCT code needs u*h on edges - this could be calculated elsewhere and saved, potentially (e.g. it's already calculated locally in the FO upwind thickness routine) -!!! ! layerThicknessEdge and normalVelocity should match those used in thickness advection! -!!! do iEdge = 1, mesh % nEdges -!!! do k = 1, mesh % nVertLevels -!!! uh(k, iEdge) = normalVelocity(k, iEdge) * layerThicknessEdge(k, iEdge) -!!! end do -!!! end do - -!!! !print *,'uh', maxval(uh), minval(uh) -!!! !print *,'wTop', maxval(wTop), minval(wTop) -!!! !print *,'layerThicknessOld', maxval(layerThicknessOld), minval(layerThicknessOld) -!!! !print *,'layerThickness_tend', maxval(layerThickness_tend), minval(layerThickness_tend) -!!! !print *,' temp min/max:', minval(stateOld % tracers % array(stateOld%index_temperature,:,1:mesh%nCells)), maxval(stateOld % tracers % array(stateOld%index_temperature,:,1:mesh%nCells)) - -!!! ! Call the FCT! (this will likely move from the ocean core to operators eventually) -!!! call mpas_ocn_tracer_advection_tend(tracers, uh , & -!!! wTop, layerThickness, layerThickness, dt/SecondsInYear, & -!!! mesh, layerThickness_tend, tracer_tendency) -!!! !print *,'tracer_tendency', maxval(tracer_tendency), minval(tracer_tendency) - -!!! ! Set thickness back to 0 where needed. This is a kluge because the FCT code will divide by zero if thickness is zero \todo: deal with more elegantly! -!!! where (layerThickness == 1.0e-12_RKIND) -!!! layerThickness = 0.0_RKIND -!!! end where - - -!!! deallocate(wTop) -!!! deallocate(uh) - -!!! case ('None') !=================================================== -!!! ! Do nothing -!!! case default !=================================================== -!!! write(0,*) trim(config_tracer_advection), ' is not a valid tracer advection option.' -!!! call mpas_dmpar_abort(dminfo) -!!! end select !=================================================== - - !-------------------------------------------------------------------- - end subroutine li_tendency_tracers - - - -!*********************************************************************** -! -! subroutine li_apply_calving -! -!> \brief Applies a calving 'law' to any marine-terminating ice -!> \author Matt Hoffman -!> \date 19 February 2013 -!> \details -!> This routine is a driver for applying various calving laws to any -!> marine-terminating ice. -! -!----------------------------------------------------------------------- - subroutine li_apply_calving(meshPool, statePool, err)!{{{ - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(inout) :: & - statePool !< Input/Output: state for which to update diagnostic variables - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - integer, dimension(:), pointer :: cellMask, nEdgesOnCell - integer, dimension(:,:), pointer :: cellsOnCell - real (kind=RKIND), dimension(:), pointer :: thickness, iceArea, areaCell - real (kind=RKIND), dimension(:,:), pointer :: layerThickness - real (kind=RKIND), dimension(:,:,:), pointer :: tracers - integer :: iCell, nCells, numCalvedCells, nThickEdges, iEdge - real (kind=RKIND) :: physicalThickness - -! Commenting out this entire subroutine for now. -!!! areaCell => mesh % areaCell % array -!!! nEdgesOnCell => mesh % nEdgesOnCell % array -!!! cellsOnCell => mesh % cellsOnCell % array - -!!! cellMask => state % cellMask % array -!!! thickness => state % thickness % array -!!! iceArea => state % iceArea % array -!!! layerThickness => state % layerThickness % array -!!! tracers => state % tracers % array - -!!! nCells = mesh % nCells - -!!! select case (config_calving_law) - -!!! !==================================== -!!! !case ('ocean-kill') - -!!! !==================================== -!!! case ('critical-thickness') -!!! ! This case loops around the floating ice margin, removing any ice below -!!! ! a critical thickness. Multiple loops are needed until the entire region -!!! ! of too-thin ice has been found. -!!! ! This could be moved to its own subroutine... -!!! numCalvedCells = 0 - -!!! ! Loop across the entire domain multiple times until the inward progression of too-thin ice has been totally mapped -!!! do - -!!! ! Update the mask before we look for the margin, since the margin may have advanced. -!!! call li_calculate_mask(mesh, state, err) - -!!! do iCell = 1, nCells -!!! if ( (MASK_IS_FLOATING(cellMask(iCell))) .and. (MASK_IS_MARGIN(cellMask(iCell))) ) then -!!! ! Convert to physical thickness - only needed if CFBC is on but this is always defined. -!!! physicalThickness = thickness(iCell) * areaCell(iCell) / iceArea(iCell) ! iceArea should always be > 0 since we are only checking on ice cells. -!!! if (iceArea(iCell) == 0.0) then -!!! write(0,*) 'iceArea is 0 on a cell with ice!' -!!! err = 1 -!!! endif -!!! if (physicalThickness <= config_calving_critical_thickness) then -!!! numCalvedCells = numCalvedCells + 1 -!!! ! TODO tally a sum of mass lost to calving -!!! ! Make this cell ice-free and set tracers there to 0 -!!! thickness(iCell) = 0.0_RKIND -!!! layerThickness(:,iCell) = 0.0_RKIND -!!! tracers(:,:,iCell) = 0.0_RKIND -!!! iceArea(iCell) = 0.0_RKIND -!!! end if -!!! end if -!!! end do - -!!! if (numCalvedCells == 0) then -!!! ! if we haven't calved any cells this time though the loop, then we have mapped the extent of the too-thin ice -!!! exit -!!! else -!!! numCalvedCells = 0 -!!! end if - -!!! end do ! start the search again - -!!! ! clean up - remove any hanging pieces that will not be dynamically active anymore. The mask should already be updated with current ice extent so we don't need to do it again. -!!! do iCell = 1, nCells -!!! if ( (MASK_IS_FLOATING(cellMask(iCell))) .and. (MASK_IS_MARGIN(cellMask(iCell))) ) then -!!! nThickEdges = 0 -!!! do iEdge = 1, nEdgesOnCell(iCell) -!!! if ( MASK_IS_THICK_ICE(cellMask(cellsOnCell(iEdge, iCell))) ) then -!!! nThickEdges = nThickEdges + 1 -!!! endif -!!! end do -!!! if (nThickEdges == 0) then -!!! ! Make this cell ice-free and set tracers there to 0 -!!! thickness(iCell) = 0.0_RKIND -!!! layerThickness(:,iCell) = 0.0_RKIND -!!! tracers(:,:,iCell) = 0.0_RKIND -!!! iceArea(iCell) = 0.0_RKIND -!!! end if -!!! end if -!!! end do - -!!! ! Update the mask one last time (probably not needed because li_diagnostic solve is coming up) -!!! call li_calculate_mask(mesh, state, err) - -!!! !==================================== -!!! !case ('eigencalving') - -!!! end select - - !-------------------------------------------------------------------- - end subroutine li_apply_calving - - - -!*********************************************************************** -!*********************************************************************** -! Private subroutines: -!*********************************************************************** -!*********************************************************************** - - - -!*********************************************************************** -! -! subroutine tend_layerThickness_fo_upwind -! -!> \brief Computes tendency term from horizontal advection of thickness layers -!> \author Matt Hoffman -!> \date 16 April 2012 -!> \details -!> This routine computes the horizontal advection tendency for each -!> thickness layer using first-order upwinding. Based on -!> ocn_thick_hadv_tend in the ocean core. This is an alternative to lice_tend_h -!> that calculates the tendency for each layer, which would then need to be -!> added up to calculate the change in thickness. The two methods yield identical -!> results. -! -!----------------------------------------------------------------------- - subroutine tend_layerThickness_fo_upwind(meshPool, normalVelocity, layerThicknessEdge, edgeMask, tend, dt, MinOfMaxAllowableDt, err)!{{{ - - use mpas_timekeeping - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - real (kind=RKIND), dimension(:,:), intent(in) :: & - normalVelocity !< Input: velocity - - real (kind=RKIND), dimension(:,:), intent(in) :: & - layerThicknessEdge !< Input: thickness of each layer on edges - - integer, dimension(:), intent(in) :: & - edgeMask !< Input: mask on edges - - real (kind=RKIND) :: dt - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:,:), intent(inout) :: & - tend !< Input/Output: velocity tendency - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - real (kind=RKIND) :: MinOfMaxAllowableDt - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - integer, pointer :: nCells, nVertLevels - integer, dimension(:,:), pointer :: edgesOnCell, edgeSignOnCell - integer, dimension(:), pointer :: nEdgesOnCell - real (kind=RKIND), dimension(:), pointer :: dvEdge, areaCell, dcEdge - logical, pointer :: config_print_thickness_advection_info - real (kind=RKIND) :: invAreaCell, flux, maxAllowableDt - integer :: iEdge, iCell, i, k - type (MPAS_TimeInterval_type) :: allowableDtMinStringInterval - character (len=StrKIND) :: allowableDtMinString - integer :: err_tmp - - ! Only needed for optional check for mass conservation - !real (kind=RKIND) :: tendVolSum - - err = 0 - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) - call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) - call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) - call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) - call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) - call mpas_pool_get_array(meshPool, 'areaCell', areaCell) - call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) - - MinOfMaxAllowableDt = 1.0e36_RKIND - - do iCell = 1, nCells - invAreaCell = 1.0 / areaCell(iCell) - do i = 1, nEdgesOnCell(iCell) - iEdge = edgesOnCell(i, iCell) - do k = 1, nVertLevels - - if (abs(normalVelocity(k, iEdge)) > 0.0_RKIND) then - maxAllowableDt = (0.5_RKIND * dcEdge(iEdge)) / abs(normalVelocity(k, iEdge)) ! in years - else - maxAllowableDt = 1.0e36_RKIND - endif - if ( maxAllowableDt < dt ) then - !write(0,*) 'CFL violation at level, edge', k, iEdge - err = err + 1 - endif - MinOfMaxAllowableDt = min(MinOfMaxAllowableDt, maxAllowableDt) - - flux = normalVelocity(k, iEdge) * dvEdge(iEdge) * layerThicknessEdge(k, iEdge) - tend(k, iCell) = tend(k, iCell) + edgeSignOnCell(i, iCell) * flux * invAreaCell - end do - end do - end do - - ! Build a time string of the maximum allowable dt calculated - ! (We only need this if a CFL violation occurred or config_print_thickness_advection_info is true) - call mpas_set_timeInterval(allowableDtMinStringInterval, dt=MinOfMaxAllowableDt, ierr=err_tmp) - err = ior(err,err_tmp) - call mpas_get_timeInterval(allowableDtMinStringInterval, timeString=allowableDtMinString, ierr=err_tmp) - err = ior(err,err_tmp) - - if (err > 0) then - call mpas_log_write('CFL violation on this processor on $i level-edges! Maximum allowable time step (seconds) for this processor is (Days_hhh:mmm:sss): ' // trim(allowableDtMinString), MPAS_LOG_ERR, intArgs=(/err/)) - err = 1 - endif - - if (config_print_thickness_advection_info) then - call mpas_log_write(' Maximum allowable time step (s) on THIS processor is (Days_hhh:mmm:sss): ' // trim(allowableDtMinString)) - endif - - ! Optional check for mass conservation - !tendVolSum = 0.0_RKIND - !do iCell=1, mesh % nCells - ! tendVolSum = tendVolSum + sum(tend(:,iCell)) * areaCell(iCell) - !end do - !print *,'SUM OF VOLUME TENDENCY ======', tendVolSum - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in tend_layerThickness_fo_upwind.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - - end subroutine tend_layerThickness_fo_upwind!}}} - - - -!*********************************************************************** -! -! subroutine tracer_advection_tend_fo -! -!> \brief tracer_advection_tend_fo -!> \author Matt Hoffman -!> \date 06 February 2013 -!> \details -!> - -! -!----------------------------------------------------------------------- - subroutine tracer_advection_tend_fo(tracers, uh, meshPool, tracer_tendency, err) - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - real (kind=RKIND), dimension(:,:,:), intent(in) :: & - tracers !< Input: - - real (kind=RKIND), dimension(:,:), intent(in) :: & - uh !< Input: - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:,:,:), intent(inout) :: & - tracer_tendency !< Input: layer tracer tendencies - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - ! pointers to mesh arrays - integer, dimension(:), pointer :: nEdgesOnCell - integer, dimension(:,:), pointer :: edgesOnCell, cellsOnEdge - real (kind=RKIND), dimension(:), pointer :: areaCell, dvEdge - ! counters, mesh variables, index variables - integer :: nTracers, nCells, nVertLevels - integer :: iTracer, iCell, iEdge, thisEdge, k - ! stuff for making calculations - real (kind=RKIND) :: q, flux - -! Commenting out this entire subroutine for now. -!!! nCells = mesh % nCells -!!! nVertLevels = mesh % nVertLevels -!!! nTracers = size(tracers, 1) - -!!! nEdgesOnCell => mesh % nEdgesOnCell % array -!!! edgesOnCell => mesh % edgesOnCell % array -!!! cellsOnEdge => mesh % cellsOnEdge % array -!!! areaCell => mesh % areaCell % array -!!! dvEdge => mesh % dvEdge % array - -!!! do iTracer = 1, nTracers -!!! do iCell = 1, nCells -!!! do iEdge = 1, nEdgesOnCell(iCell) -!!! ! What is this edge's index? -!!! thisEdge = edgesOnCell(iEdge, iCell) -!!! do k = 1, nVertLevels -!!! ! Find q-edge using in an upwind sense -!!! if (cellsOnEdge(1, thisEdge) == iCell ) then -!!! if ( uh(k,thisEdge) < 0.0_RKIND) then -!!! ! iCell is the downwind cell so use the other cell for q -!!! q = tracers(iTracer, k, cellsOnEdge(2, thisEdge) ) -!!! flux = -1.0_RKIND * dvEdge(thisEdge) * uh(k, thisEdge) * q -!!! else -!!! ! iCell is up the upwind cell or the flux is 0 -!!! q = tracers(iTracer, k, iCell) -!!! flux = -1.0_RKIND * dvEdge(thisEdge) * uh(k, thisEdge) * q -!!! end if -!!! else ! if (cellsOnEdge(2, thisEdge) == iCell ) then -!!! if ( uh(k,thisEdge) > 0.0_RKIND) then -!!! ! iCell is the downwind cell so use the other cell for q -!!! q = tracers(iTracer, k, cellsOnEdge(1, thisEdge) ) -!!! flux = dvEdge(thisEdge) * uh(k, thisEdge) * q -!!! else -!!! ! iCell is up the upwind cell or the flux is 0 -!!! q = tracers(iTracer, k, iCell) -!!! flux = dvEdge(thisEdge) * uh(k, thisEdge) * q -!!! end if -!!! end if - -!!! tracer_tendency(iTracer, k, iCell) = tracer_tendency(iTracer, k, iCell) + flux / areaCell(iCell) -!!! end do ! vert levs -!!! end do ! edges -!!! end do ! cells -!!! end do ! tracers - - end subroutine tracer_advection_tend_fo - - - -! This subroutine calculates the tendency for the entire thickness column rather than layer-by-layer. -! Tracer advection requires knowing thickness tendencies layer-by-layer, so this has been -! deprecated. -!!*********************************************************************** -!! -!! subroutine li_tend_h_fo_upwind -!! -!!> \brief Computes tendency term from horizontal advection of thickness -!!> \author Matt Hoffman -!!> \date 16 April 2012 -!!> \details -!!> This routine computes the horizontal advection tendency for -!!> thickness based on current state and user choices of forcings. Based on -!!> ocn_thick_hadv_tend in the ocean core. -!! -!!----------------------------------------------------------------------- - -! subroutine li_tend_h_fo_upwind(mesh, normalVelocity, layerThickness, thickness, thickness_tend, dt, err)!{{{ - -! !----------------------------------------------------------------- -! ! -! ! input variables -! ! -! !----------------------------------------------------------------- - -! real (kind=RKIND), dimension(:,:), intent(in) :: & -! normalVelocity !< Input: velocity - -! real (kind=RKIND), dimension(:,:), intent(in) :: & -! layerThickness !< Input: thickness of each layer - -! real (kind=RKIND), dimension(:), intent(in) :: & -! thickness !< Input: thickness - -! type (mesh_type), intent(in) :: & -! mesh !< Input: mesh information - -! real (kind=RKIND) :: dt - -! !----------------------------------------------------------------- -! ! -! ! input/output variables -! ! -! !----------------------------------------------------------------- - -! real (kind=RKIND), dimension(:), intent(inout) :: & -! thickness_tend !< Input/Output: thickness tendency - -! !----------------------------------------------------------------- -! ! -! ! output variables -! ! -! !----------------------------------------------------------------- - -! integer, intent(out) :: err !< Output: error flag - -! !----------------------------------------------------------------- -! ! -! ! local variables -! ! -! !----------------------------------------------------------------- - -! integer :: iEdge, nEdges, cell1, cell2, nVertLevels, k, CellUpwind, CellDownwind - -! integer, dimension(:,:), pointer :: cellsOnEdge - -! real (kind=RKIND) :: flux, VelSign, h_edge, ubar, maxAllowableDt -! real (kind=RKIND), dimension(:), pointer :: dvEdge, areaCell, dcEdge - -! err = 0 -! maxAllowableDt = 1.0e36 - -! nEdges = mesh % nEdges -! nVertLevels = mesh % nVertLevels -! cellsOnEdge => mesh % cellsOnEdge % array -! dvEdge => mesh % dvEdge % array -! dcEdge => mesh % dcEdge % array -! areaCell => mesh % areaCell % array - -! !print *,'Max velocity magn.:', maxval(abs(normalVelocity)) - -! ! Zero the tendency before accumulating -! thickness_tend = 0.0 - -! do iEdge=1,nEdges -! cell1 = cellsOnEdge(1,iEdge) -! cell2 = cellsOnEdge(2,iEdge) - -! VelSign = sign(1.0, normalVelocity(1, iEdge)) -! if (VelSign .gt. 0.0) then -! CellUpwind = cell1 -! CellDownwind = cell2 -! else -! CellUpwind = cell2 -! CellDownwind = cell1 -! endif -! if (thickness(cellUpwind) .gt. 0.0) then ! Don't calculate for non-ice cells - would result in divide by 0 -! h_edge = thickness(CellUpwind) -! flux = 0.0 -! do k=1, nVertLevels -! ! Calculate thickness averaged velocity - this make be calculated externally and passed in -! flux = flux + layerThickness(k, cellUpwind) * abs(normalVelocity(k, iEdge)) -! enddo -! ubar = flux / thickness(cellUpwind) -! if ( (abs(ubar) * dt/SecondsInYear) .gt. (0.5 * dcEdge(iEdge))) then -! !maxAllowableDt = min(maxAllowableDt, (0.5 * dcEdge)/ubar ) -! write(0,*) 'CFL violation at edge', iEdge -! err = err + 1 -! endif -! thickness_tend(cellUpwind) = thickness_tend(cellUpwind) - flux * dvEdge(iEdge) / areaCell(cellUpwind) -! thickness_tend(cellDownwind) = thickness_tend(cellDownwind) + flux * dvEdge(iEdge) / areaCell(cellDownwind) -! endif -! end do - -! if (err .gt. 0) then -! write(6,*) 'CFL violation at ', err, ' edges! Maximum time step should be ', maxAllowableDt -! err = 1 -! endif -! !-------------------------------------------------------------------- - -! end subroutine li_tend_h_fo_upwind !}}} - - - -end module li_tendency - diff --git a/src/core_landice/mpas_li_time_integration.F b/src/core_landice/mpas_li_time_integration.F deleted file mode 100644 index 60df339f53..0000000000 --- a/src/core_landice/mpas_li_time_integration.F +++ /dev/null @@ -1,152 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_time_integration -! -!> \brief MPAS land ice time integration driver -!> \author Matt Hoffman -!> \date 17 April 2011 -!> \details -!> This module contains the main driver routine for calling -!> time integration schemes -! -!----------------------------------------------------------------------- - -module li_time_integration - - use mpas_derived_types - use mpas_pool_routines - use mpas_constants - use mpas_dmpar - use mpas_log - use li_time_integration_fe - use li_setup - - implicit none - private - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - - public :: li_timestep - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - -!*********************************************************************** - contains - - -!*********************************************************************** -! -! routine li_timestep -! -!> \brief Advance model state forward in time by the specified time step -!> \author Matt Hoffman -!> \date 20 April 2012 -!> \details -!> This routine advances model state forward in time by the specified time step. -!> Input: domain - current model state in time level 1 plus mesh data -!> Output: domain - upon exit, time level 2 contains -!> model state advanced forward in time by dt seconds -!----------------------------------------------------------------------- - subroutine li_timestep(domain, dt, timeStamp, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - real (kind=RKIND), intent(in) :: dt !< Input: time step - character(len=*), intent(in) :: timeStamp !< Input: current time stamp - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - type (block_type), pointer :: block - type (mpas_pool_type), pointer :: statePool - character (len=StrKIND), pointer :: xtime - character (len=StrKIND), pointer :: config_time_integration - integer :: err_tmp - - err = 0 - err_tmp = 0 - - call mpas_pool_get_config(liConfigs, 'config_time_integration', config_time_integration) - - !write(*,*) 'Using ', trim(config_time_integration), ' time integration.' - select case (config_time_integration) - case ('forward_euler') - call li_time_integrator_forwardeuler(domain, dt, err_tmp) - case ('rk4') - call mpas_log_write(trim(config_time_integration) // ' is not currently supported.', MPAS_LOG_ERR) - call mpas_dmpar_abort(domain % dminfo) - err_tmp = 1 - case default - call mpas_log_write(trim(config_time_integration) // ' is not a valid land ice time integration option.', MPAS_LOG_ERR) - err_tmp = 1 - end select - err = ior(err,err_tmp) - - block => domain % blocklist - do while (associated(block)) - ! Assign the time stamp for this time step - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_array(statePool, 'xtime', xtime, timeLevel=1) ! xtime only has one time level, but stating is explicitly here to avoid confusion later. - xtime = timeStamp - -! ! Abort the simulation if NaNs occur in the velocity field -! if (isNaN(sum(block % state % time_levs(2) % state % u % array))) then -! write(0,*) 'Abort: NaN detected' -! call mpas_dmpar_abort(dminfo) -! endif - - block => block % next - end do - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_timestep.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine li_timestep - - -end module li_time_integration diff --git a/src/core_landice/mpas_li_time_integration_fe.F b/src/core_landice/mpas_li_time_integration_fe.F deleted file mode 100644 index 8e1ffbb6ba..0000000000 --- a/src/core_landice/mpas_li_time_integration_fe.F +++ /dev/null @@ -1,462 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_time_integration_fe -! -!> \brief MPAS land ice Forward Euler time integration scheme -!> \author Matt Hoffman -!> \date 17 April 2011 -!> \details -!> This module contains the Forward Euler time integration scheme -! -!----------------------------------------------------------------------- - -module li_time_integration_fe - - use mpas_derived_types - use mpas_pool_routines - use mpas_constants - use mpas_dmpar - use mpas_timer - use mpas_vector_reconstruction - use mpas_log - use li_velocity, only: li_velocity_solve - use li_tendency - use li_diagnostic_vars - use li_setup - - implicit none - private - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - - public :: li_time_integrator_forwardeuler - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - -!*********************************************************************** - contains -!*********************************************************************** - - -!*********************************************************************** -! -! routine li_time_integrator_forwardeuler -! -!> \brief Forward Euler time integration scheme -!> \author Matthew Hoffman -!> \date 10 January 2012 -!> \details -!> This routine performs Forward Euler time integration. -! -!----------------------------------------------------------------------- - subroutine li_time_integrator_forwardeuler(domain, deltat, err) - - !----------------------------------------------------------------- - ! input variables - !----------------------------------------------------------------- - real (kind=RKIND), intent(in) :: deltat !< Input: time step - - !----------------------------------------------------------------- - ! input/output variables - !----------------------------------------------------------------- - type (domain_type), intent(inout) :: & - domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! output variables - !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! local variables - !----------------------------------------------------------------- - type (block_type), pointer :: block - integer :: err_tmp - - - ! During integration, time level 1 stores the model state at the beginning of the - ! time step, and time level 2 stores the state advanced dt in time by timestep(...) - ! (time level 1 should not be modified.) - -!!! procVertexMaskChanged = 0 - -! === Implicit column physics (vertical temperature diffusion) =========== - !call () - -! === Calculate Tendencies ======================== - call mpas_timer_start("calculate tendencies") - call calculate_tendencies(domain, deltat, err_tmp) - err = ior(err, err_tmp) - call mpas_timer_stop("calculate tendencies") - - -! === Compute new state for prognostic variables ================================== -! (once implicit column physics are added (i.e. temp diffusion), these calculations will need to be adjusted to apply to the new values as needed) - call mpas_timer_start("calc. new prognostic vars") - call update_prognostics(domain, deltat, err_tmp) - err = ior(err, err_tmp) - call mpas_timer_stop("calc. new prognostic vars") - - -! === Calculate diagnostic variables for new state ===================== - - ! Some velocity solvers require an initial guess. - ! If needed, that guess should be inserted into normalVelocity - ! in time level 2 before calling li_calculate_diagnostic_vars. - - call li_calculate_diagnostic_vars(domain, timeLevel=2, solveVelo=.true., err=err_tmp) - err = ior(err, err_tmp) - - -! === Cleanup & Misc. ============================= - - ! === error check - if (err == 1) then - call mpas_log_write("An error has occurred in li_time_integrator_forwardeuler.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine li_time_integrator_forwardeuler - - - -!*********************************************************************** -!*********************************************************************** -! Private subroutines: -!*********************************************************************** -!*********************************************************************** - - -!*********************************************************************** -! -! routine calculate_tendencies -! -!> \brief Calculates tendencies -!> \author Matthew Hoffman -!> \date 12 September 2013 -!> \details -!> This routine calculates tendencies of thickness (and eventually tracers) -!> to use with the Forward Euler time integrator. -! -!----------------------------------------------------------------------- - - subroutine calculate_tendencies(domain, deltat, err) - - use mpas_timekeeping - - !----------------------------------------------------------------- - ! input variables - !----------------------------------------------------------------- - real (kind=RKIND) :: deltat - - !----------------------------------------------------------------- - ! input/output variables - !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! output variables - !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! local variables - !----------------------------------------------------------------- - type (dm_info), pointer :: dminfo - type (block_type), pointer :: block - type (mpas_pool_type), pointer :: statePool - type (mpas_pool_type), pointer :: meshPool - type (mpas_pool_type), pointer :: tendPool - - real (kind=RKIND), dimension(:,:), pointer :: layerThickness_tend - integer :: allowableDtProcNumber, allowableDtMinProcNumber - real (kind=RKIND) :: allowableDt, allowableDtMin - logical, pointer :: config_print_thickness_advection_info - type (MPAS_TimeInterval_type) :: allowableDtMinStringInterval - character (len=StrKIND) :: allowableDtMinString - integer :: err_tmp - - integer :: y, m, d, hh, mm, ss - - err = 0 - - call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) - - dminfo => domain % dminfo - - ! === - ! === Thickness tendencies - ! === - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'tend', tendPool) - call mpas_pool_get_array(tendPool, 'layerThickness', layerThickness_tend) - - ! Calculate thickness tendency using state at time n ========= - call li_tendency_thickness(meshPool, statePool, layerThickness_tend, deltat, dminfo, allowableDt, err_tmp) - err = ior(err,err_tmp) - - block => block % next - end do - - ! Now that we have exited the block loop, do any needed halo updates. - ! update halos on thickness tend - call mpas_timer_start("halo updates") - call mpas_dmpar_field_halo_exch(domain, 'tend_layerThickness') - call mpas_timer_stop("halo updates") - - ! If we are printing advection debug information, - ! then find out what the CFL limit is. Don't do this otherwise because it - ! is requires 2 unnecessary MPI communications. - if (config_print_thickness_advection_info) then - ! Determine CFL limit on all procs - call mpas_dmpar_min_real(dminfo, allowableDt, allowableDtMin) - ! Determine which processor has the limiting CFL - if (allowableDt .eq. allowableDtMin) then - allowableDtProcNumber = dminfo % my_proc_id - else - allowableDtProcNumber = -1 - endif - call mpas_dmpar_max_int(dminfo, allowableDtProcNumber, allowableDtMinProcNumber) - call mpas_set_timeInterval(allowableDtMinStringInterval, dt=allowableDtMin, ierr=err_tmp) - err = ior(err,err_tmp) - call mpas_get_timeInterval(allowableDtMinStringInterval, timeString=allowableDtMinString, ierr=err_tmp) - err = ior(err,err_tmp) - call mpas_log_write(' Maximum allowable time step (yr) for all processors is (Days_hhh:mmm:sss): ' // trim(allowableDtMinString) // ' Time step is limited by processor number $i', intArgs=(/allowableDtMinProcNumber/)) - endif - - if (err .gt. 0) then - call mpas_log_write('Error in calculating thickness tendency (possibly CFL violation)', MPAS_LOG_ERR) - endif - - - ! === - ! === Tracer tendencies - ! === -! Commenting out tracer tendencies -!!! ! Tracer tendencies -!!! block => domain % blocklist -!!! do while (associated(block)) -!!! ! Mesh information -!!! mesh => block % mesh -!!! ! State at time n -!!! stateOld => block % state % time_levs(1) % state -!!! ! Tendencies -!!! layerThickness_tend => block % tend % layerThickness % array -!!! tracer_tendency => block % tend % tracers % array - -!!! ! Calculate tracer tendencies ========== -!!! ! There could be a negative layer thickness with SMB turned on! -!!! call li_tendency_tracers(mesh, stateOld, layerThickness_tend, tracer_tendency, dt, dminfo, err) - -!!! block => block % next -!!! end do - -!!! if (err == 1) then -!!! call mpas_dmpar_global_abort("An error has occurred in li_tendency_tracers. Aborting...") -!!! endif - -!!! ! update halos on tracer tend -!!! select case (config_tracer_advection) -!!! case ('None') !=================================================== -!!! ! Do nothing - no need to waste time doing a halo update if not advecting tracers! The tendency will be 0 everywhere -!!! case default -!!! call mpas_dmpar_field_halo_exch(domain, 'tendTracers') -!!! end select - - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in calculate_tendencies.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine calculate_tendencies - - - -!*********************************************************************** -! -! routine update_prognostics -! -!> \brief Updates prognostic variables -!> \author Matthew Hoffman -!> \date 12 September 2013 -!> \details -!> This routine updates prognostic variables using a -!> Forward Euler time integrator. -! -!----------------------------------------------------------------------- - - subroutine update_prognostics(domain, deltat, err) - - !----------------------------------------------------------------- - ! input variables - !----------------------------------------------------------------- - real (kind=RKIND) :: deltat - - !----------------------------------------------------------------- - ! input/output variables - !----------------------------------------------------------------- - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! output variables - !----------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! local variables - !----------------------------------------------------------------- - type (dm_info), pointer :: dminfo - type (block_type), pointer :: block - type (mpas_pool_type), pointer :: meshPool, statePool, tendPool - integer, pointer :: nCells - logical, pointer :: config_print_thickness_advection_info - - real (kind=RKIND), dimension(:), pointer :: thicknessNew, layerThicknessFractions - real (kind=RKIND), dimension(:,:), pointer :: layerThicknessOld, layerThicknessNew, layerThickness_tend -!!! real (kind=RKIND), dimension(:,:,:), pointer :: tracer_tendency, tracersNew, tracersOld -!!! integer, dimension(:), pointer :: cellMaskOld - integer, dimension(:), allocatable :: masktmp ! Temporary mask for assessing new thickness field - - err = 0 - - dminfo => domain % dminfo - call mpas_pool_get_config(liConfigs, 'config_print_thickness_advection_info', config_print_thickness_advection_info) - - block => domain % blocklist - do while (associated(block)) - ! Mesh information - - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'tend', tendPool) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) - - ! State at time n - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessOld, timeLevel=1) -!!! tracersOld => stateOld % tracers % array -!!! cellMaskOld => stateOld % cellMask % array - - ! State at time n+1 (advanced by dt by Forward Euler) - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, timeLevel=2) - call mpas_pool_get_array(statePool, 'thickness', thicknessNew, timeLevel=2) -!!! tracersNew => stateNew % tracers % array - - ! Tendencies - call mpas_pool_get_array(tendPool, 'layerThickness', layerThickness_tend) -!!! tracer_tendency => block % tend % tracers % array - - - ! Update thickness ====================== - - ! Commented out usage for advecting thickness as a column - !!!thicknessNew = thicknessOld + thickness_tend * dt / SecondsInYear - ! Commented out usage for using FCT for thickness - !!!stateNew % sup_thickness % array(1,:,:) = (stateOld % tracers % array(stateOld % index_temperature,:,:) * layerThicknessOld + tracer_tendency(stateOld % index_temperature, :, :) * dt / SecondsInYear) / (layerThicknessNew+1.0e-12) - - - layerThicknessNew = layerThicknessOld + layerThickness_tend * deltat - thicknessNew = sum(layerThicknessNew, 1) - - - !Optionally print some information about the new thickness - !print *, 'thickness_tend maxval:', maxval(thickness_tend(1:mesh % nCellsSolve)) - !print *, 'thicknessOld maxval:', maxval(thicknessOld(1:mesh % nCellsSolve)) - !print *, ' thicknessNew maxval:', maxval(thicknessNew(1:mesh % nCellsSolve)) - allocate( masktmp(nCells + 1) ) - masktmp = 0 - -!!! ! if holding advance within initial extent of ice, set thickness to 0 anywhere it has expanded beyond initial extent -!!! if (config_allow_additional_advance .eqv. .false.) then -!!! where ( MASK_WAS_INITIALLY_NOT_ICE(cellMaskOld) ) -!!! thicknessNew = 0.0_RKIND -!!! end where -!!! endif - - ! reset negative thickness to 0. This should not happen unless negative MB is larger than entire ice column. - where (thicknessNew < 0.0_RKIND) - masktmp = 1 - thicknessNew = 0.0_RKIND -!!! stateNew % iceArea % array = 0.0_RKIND - end where - - if (config_print_thickness_advection_info) then - if (sum(masktmp) > 0) then - call mpas_log_write(' Cells with negative thickness (set to 0): $i', intArgs=(/sum(masktmp)/)) - endif - - ! Note how many cells have ice. - masktmp = 0 - where (thicknessNew > 0.0_RKIND) - masktmp = 1 - end where - call mpas_log_write(' Cells with nonzero thickness: $i', intArgs=(/sum(masktmp)/)) - endif - deallocate(masktmp) - - - -!!! ! Calculate new tracer values ================= -!!! if (config_tracer_advection .ne. 'None') then -!!! do iTracer = 1, size(tracersNew, 1) -!!! where (layerThicknessNew > 0.0_RKIND) -!!! tracersNew(iTracer,:,:) = (tracersOld(iTracer,:,:) * layerThicknessOld & -!!! + tracer_tendency(iTracer,:,:) * dt / SecondsInYear) / (layerThicknessNew) -!!! elsewhere -!!! ! May or may not want to assign tracer values to non-ice cells -!!! tracersNew(iTracer,:,:) = 0.0_RKIND -!!! end where -!!! end do -!!! endif - -!!! ! Apply calving after we have updated the new state - TODO Is this the right place? -!!! call li_apply_calving(mesh, stateNew, err) - - - block => block % next - end do - - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in update_prognostics.", MPAS_LOG_ERR) - endif - - - !-------------------------------------------------------------------- - end subroutine update_prognostics - - - -end module li_time_integration_fe - - diff --git a/src/core_landice/mpas_li_velocity.F b/src/core_landice/mpas_li_velocity.F deleted file mode 100644 index 9ea9769013..0000000000 --- a/src/core_landice/mpas_li_velocity.F +++ /dev/null @@ -1,386 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! li_velocity -! -!> \MPAS land-ice velocity driver -!> \author William Lipscomb -!> \date 10 January 2012 -!> \details -!> This module contains the routines for calling dycores -!> for calculating ice velocity. -!> -! -!----------------------------------------------------------------------- - - -module li_velocity - - use mpas_derived_types - use mpas_pool_routines - use mpas_log -!!! use li_lifev - use li_sia - use li_setup - - implicit none - private - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - - public :: li_velocity_init, & - li_velocity_finalize, & - li_velocity_block_init, & - li_velocity_solve - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - -!*********************************************************************** - -contains - -!*********************************************************************** -! -! routine li_velocity_init -! -!> \brief Initializes velocity solver -!> \author Xylar Asay-Davis -!> \date 18 January 2012 -!> \details -!> This routine initializes the ice velocity solver. -! -!----------------------------------------------------------------------- - - subroutine li_velocity_init(domain, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - character (len=StrKIND), pointer :: config_velocity_solver - integer :: err_tmp - - err = 0 - - call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) - - call mpas_log_write('Using ' // trim(config_velocity_solver) // ' dynamical core.') - select case (config_velocity_solver) - case ('sia') - call li_sia_init(domain, err) -!!! case ('L1L2') -!!! call li_lifev_init(domain, err) -!!! case ('FO') -!!! call li_lifev_init(domain, err) -!!! case ('Stokes') -!!! call li_lifev_init(domain, err) -!!! call li_phg_init(domain, err) - case default - call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) - err = 1 - return - end select - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_timestep.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine li_velocity_init - - - -!*********************************************************************** -! -! routine li_velocity_block_init -! -!> \brief Initializes velocity solver -!> \author William Lipscomb -!> \date 10 January 2012 -!> \details -!> This routine initializes each block of the ice velocity solver. -! -!----------------------------------------------------------------------- - subroutine li_velocity_block_init(block, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (block_type), intent(inout) :: & - block !< Input/Output: block object - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - character (len=StrKIND), pointer :: config_velocity_solver - - err = 0 - - call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) - - select case (config_velocity_solver) - case ('sia') - call li_sia_block_init(block, err) -!!! case ('L1L2') -!!! call li_lifev_block_init(block, err) -!!! case ('FO') -!!! call li_lifev_block_init(block, err) -!!! case ('Stokes') -!!! call li_lifev_block_init(block, err) - case default - call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) - err = 1 - return - end select - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_velocity_block_init.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - - end subroutine li_velocity_block_init - - - -!*********************************************************************** -! -! routine li_velocity_solve -! -!> \brief Solver for calculating ice velocity -!> \author William Lipscomb -!> \date 10 January 2012 -!> \details -!> This routine calls velocity solvers. -! -!----------------------------------------------------------------------- - subroutine li_velocity_solve(meshPool, statePool, timeLevel, err) - - use li_sia - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - integer, intent(in) :: timeLevel !< Input: Time level on which to calculate diagnostic variables - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(inout) :: & - statePool !< Input: state information - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - character (len=StrKIND), pointer :: config_velocity_solver -! integer :: iEdge, nEdges -! real (kind=RKIND), dimension(:,:), pointer :: normalVelocity -! integer, dimension(:), pointer :: edgeMask - - err = 0 - - call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) - -! nEdges = mesh % nEdges -! normalVelocity => state % normalVelocity % array -! edgeMask => state % edgeMask % array - - select case (config_velocity_solver) - case ('sia') - call li_sia_solve(meshPool, statePool, timeLevel, err) -!!! case ('L1L2') -!!! call li_lifev_solve(mesh, state, timeLevel, err) -!!! case ('FO') -!!! call li_lifev_solve(mesh, state, timeLevel, err) -!!! case ('Stokes') -!!! call li_lifev_solve(mesh, state, timeLevel, err) - case default - call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) - err = 1 - return - end select - - -!!! do iEdge = 1, nEdges -!!! if ( MASK_IS_THIN_ICE(edgeMask(iEdge)) .and. (maxval(abs(normalVelocity(:,iEdge))) /= 0.0_RKIND) ) then -!!! err = 1 -!!! normalVelocity(:,iEdge) = 0.0_RKIND ! this is a hack because the rest of the code requires this, but this condition should really cause a fatal error. -!!! endif -!!! enddo -!!! if (err == 1) then -!!! write(0,*) 'Velocity has been calculated on non-dynamic edges. There is a problem with the velocity solver. Velocity on those edges have been set to 0, but this should be a fatal error.' -!!! err = 0 ! a hack to let the code continue until this can be fixed in the velocity solver -!!! end if - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_velocity_solve.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine li_velocity_solve - - - -!*********************************************************************** -! -! routine li_velocity_finalize -! -!> \brief Finalizes velocity solver -!> \author Xylar Asay-Davis -!> \date 18 January 2012 -!> \details -!> This routine finalizes the ice velocity solver. -! -!----------------------------------------------------------------------- - - subroutine li_velocity_finalize(domain, err) - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (domain_type), intent(inout) :: domain !< Input/Output: domain object - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - character (len=StrKIND), pointer :: config_velocity_solver - - err = 0 - - call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) - - select case (config_velocity_solver) - case ('sia') - call li_sia_finalize(domain, err) -!!! case ('L1L2') -!!! call li_lifev_finalize(domain, err) -!!! case ('FO') -!!! call li_lifev_finalize(domain, err) -!!! case ('Stokes') -!!! call li_lifev_finalize(domain, err) - case default - call mpas_log_write(trim(config_velocity_solver) // ' is not a valid land ice velocity solver option.', MPAS_LOG_ERR) - err = 1 - return - end select - - ! === error check - if (err > 0) then - call mpas_log_write("An error has occurred in li_velocity_finalize.", MPAS_LOG_ERR) - endif - - !-------------------------------------------------------------------- - end subroutine li_velocity_finalize - - - -!*********************************************************************** - -end module li_velocity - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_landice/shared/Makefile b/src/core_landice/shared/Makefile new file mode 100644 index 0000000000..b9e1f68208 --- /dev/null +++ b/src/core_landice/shared/Makefile @@ -0,0 +1,34 @@ + +.SUFFIXES: .F .o .cpp + +OBJS = mpas_li_constants.o \ + mpas_li_mask.o \ + mpas_li_setup.o + +all: $(OBJS) + +mpas_li_constants.o: + +mpas_li_setup.o: + +mpas_li_mask.o: mpas_li_setup.o + + + +clean: + $(RM) *.o *.mod *.f90 + @# Certain systems with intel compilers generate *.i files + @# This removes them during the clean process + $(RM) *.i + +.F.o: + $(RM) $@ $*.mod +ifeq "$(GEN_F90)" "true" + $(CPP) $(CPPFLAGS) $(CPPINCLUDES) $< > $*.f90 + $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) +else + $(FC) $(CPPFLAGS) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) +endif + +.cpp.o: + $(CXX) $(CXXFLAGS) -c $*.cpp $(CXINCLUDES) $(CPPINCLUDES) -lmpi_cxx -lstdc++ $(CPPFLAGS) diff --git a/src/core_landice/shared/mpas_li_constants.F b/src/core_landice/shared/mpas_li_constants.F new file mode 100644 index 0000000000..156642d421 --- /dev/null +++ b/src/core_landice/shared/mpas_li_constants.F @@ -0,0 +1,77 @@ +! Copyright (c) 2015-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_constants +! +!> \brief MPAS land ice specific constants +!> \author Matthew Hoffman +!> \date 17 Feb. 2015 +!> \details +!> This module contains constants specific to the land ice model. +! +!----------------------------------------------------------------------- + +module li_constants + + use mpas_derived_types + use mpas_kind_types + +#ifdef MPAS_ESM_SHR_CONST + use shr_const_mod, only: & + cp_ice => SHR_CONST_CPICE,& + latent_heat_ice => SHR_CONST_LATICE,& + triple_point => SHR_CONST_TKTRIP,& + rho_water => SHR_CONST_RHOFW, & + cp_freshwater => SHR_CONST_CPFW, & + pii => SHR_CONST_PI, & + gravity => SHR_CONST_G + implicit none + save + +#else + + implicit none + save + + ! physical constants + real (kind=RKIND), parameter, public :: cp_ice = 2009.0_RKIND !< heat capacity of ice (J/kg/K) + real (kind=RKIND), parameter, public :: latent_heat_ice = 335.0e3_RKIND !< Latent heat of melting of ice (J/kg) + real (kind=RKIND), parameter, public :: triple_point = 273.16_RKIND !< Triple point of water (K) + real (kind=RKIND), parameter, public :: rho_water = 1000.0_RKIND !< Density of fresh water (kg m^-3) + real (kind=RKIND), parameter, public :: cp_freshwater = 4.188e3_RKIND !< heat capacity of freshwater (J/kg/K) + real (kind=RKIND), parameter, public :: pii = 3.141592653589793_RKIND !< Constant: Pi + real (kind=RKIND), parameter, public :: gravity = 9.80616_RKIND !< Constant: Acceleration due to gravity [m s-2] + +#endif + + real (kind=RKIND), parameter, public :: idealGasConstant = 8.314_RKIND !< ideal gas constant (J mol^-1 K^-1) + real (kind=RKIND), parameter, public :: iceConductivity = 2.1_RKIND !< thermal conductivity of ice (W m^-1 K^-1) + + real (kind=RKIND), parameter, public :: & + oceanFreezingTempSurface = -1.92_RKIND, & !< Freezing temperature of seawater (deg C) at surface pressure, + !< given S = 35 PSU + oceanFreezingTempDepthDependence = -7.53e-4_RKIND !< Rate of change of freezing temperature of seawater + !< with depth (deg m^-1), given S = 35 PSU + !< These values are from the Ocean Water Freezing Point Calculator, + !< http://www.csgnetwork.com/h2ofreezecalc.html (25 Nov. 2014) + + real (kind=RKIND), parameter, public :: & + iceMeltingPointPressureDependence = 9.7456e-8_RKIND ! Dependence of ice melting point on pressure (K Pa^-1) + + ! conversion factors + real (kind=RKIND), parameter, public :: kelvin_to_celsius = 273.15_RKIND !< factor to convert Kelvin to Celsius + real (kind=RKIND), parameter, public :: scyr = 31536000.0_RKIND !< seconds in a 365-day year; used for diagnostics + real (kind=RKIND), parameter, public :: seconds_per_day = 86400.0_RKIND !< Conversion from days to seconds + +!*********************************************************************** + + +!*********************************************************************** + +end module li_constants diff --git a/src/core_landice/shared/mpas_li_mask.F b/src/core_landice/shared/mpas_li_mask.F new file mode 100644 index 0000000000..4436e0a511 --- /dev/null +++ b/src/core_landice/shared/mpas_li_mask.F @@ -0,0 +1,948 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_mask +! +!> \MPAS land-ice mask calculations +!> \author Matt Hoffman +!> \date 10 May 2012 +!> \details +!> This module contains the routines for calculating masks for land ice +!> +! +!----------------------------------------------------------------------- + +module li_mask + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timer + use li_setup + + implicit none + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + integer, parameter :: li_mask_ValueIce = 32 + !< Giving this the highest current value so it is obvious during visualization + integer, parameter :: li_mask_ValueDynamicIce = 2 + integer, parameter :: li_mask_ValueFloating = 4 + integer, parameter :: li_mask_ValueMargin = 8 ! This is the last cell with ice. + integer, parameter :: li_mask_ValueDynamicMargin = 16 ! This is the last dynamically active cell with ice + integer, parameter :: li_mask_ValueInitialIceExtent = 1 + integer, parameter :: li_mask_ValueAlbanyActive = 64 ! These are locations that Albany includes in its solution + integer, parameter :: li_mask_ValueAlbanyMarginNeighbor = 128 ! This the first cell beyond the last active albany cell + integer, parameter :: li_mask_ValueGroundingLine = 256 + !< This is grounded cell that has a floating neighbor, or vertex/edge on that boundary + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + ! all subroutines and functions in this module are public! + + + ! interfaces without a suffix return logicals + ! interfaces with names that end with '_int' return 0/1 + ! TODO Eventually we may decide to only keep and maintain one of these return types. + + interface li_mask_is_ice + module procedure li_mask_is_ice_logout_1d + module procedure li_mask_is_ice_logout_0d + end interface + + + interface li_mask_is_ice_int + module procedure li_mask_is_ice_intout_1d + module procedure li_mask_is_ice_intout_0d + end interface + + + interface li_mask_is_dynamic_ice + module procedure li_mask_is_dynamic_ice_logout_1d + module procedure li_mask_is_dynamic_ice_logout_0d + end interface + + + interface li_mask_is_albany_active + module procedure li_mask_is_albany_active_logout_1d + module procedure li_mask_is_albany_active_logout_0d + end interface + + + interface li_mask_is_dynamic_ice_int + module procedure li_mask_is_dynamic_ice_intout_1d + module procedure li_mask_is_dynamic_ice_intout_0d + end interface + + + interface li_mask_is_margin + module procedure li_mask_is_margin_logout_1d + module procedure li_mask_is_margin_logout_0d + end interface + + + interface li_mask_is_dynamic_margin + module procedure li_mask_is_dynamic_margin_logout_1d + module procedure li_mask_is_dynamic_margin_logout_0d + end interface + + + interface li_mask_is_dynamic_margin_int + module procedure li_mask_is_dynamic_margin_intout_1d + module procedure li_mask_is_dynamic_margin_intout_0d + end interface + + + interface li_mask_is_albany_margin_neighbor + module procedure li_mask_is_albany_margin_neighbor_logout_1d + module procedure li_mask_is_albany_margin_neighbor_logout_0d + end interface + + + interface li_mask_is_floating_ice + module procedure li_mask_is_floating_ice_logout_1d + module procedure li_mask_is_floating_ice_logout_0d + end interface + + + interface li_mask_is_floating_ice_int + module procedure li_mask_is_floating_ice_intout_1d + module procedure li_mask_is_floating_ice_intout_0d + end interface + + interface li_mask_is_grounded_ice + module procedure li_mask_is_grounded_ice_logout_1d + module procedure li_mask_is_grounded_ice_logout_0d + end interface + + interface li_mask_is_initial_ice + module procedure li_mask_is_initial_ice_logout_1d + module procedure li_mask_is_initial_ice_logout_0d + end interface + + interface li_mask_is_grounded_ice_int + module procedure li_mask_is_grounded_ice_intout_1d + module procedure li_mask_is_grounded_ice_intout_0d + end interface + + interface li_mask_is_grounding_line + module procedure li_mask_is_grounding_line_logout_1d + module procedure li_mask_is_grounding_line_logout_0d + end interface + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + + +!*********************************************************************** + +contains + + + +!*********************************************************************** +! +! routine li_calculate_mask_init +! +!> \brief Calculates masks for land ice for info needed from initial condition only +!> \author Matt Hoffman +!> \date 25 June 2012 +!> \details +!> This routine Calculates masks for land ice for info needed from initial condition only. +! +!----------------------------------------------------------------------- + + subroutine li_calculate_mask_init(geometryPool, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: & + geometryPool !< Input/Output: geometry information + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer, dimension(:), pointer :: cellMask + real(KIND=RKIND), dimension(:), pointer :: thickness + logical, pointer :: config_do_restart + + err = 0 + + ! Assign pointers and variables + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) + + if (config_do_restart .eqv. .false.) then ! We only want to set this bit of the mask when a new simulation starts, + ! but not during a restart. + ! Initialize cell mask to 0 everywhere before we assign anything to it. + cellMask = 0 + where (thickness > 0.0_RKIND) + cellMask = ior(cellMask, li_mask_ValueInitialIceExtent) + end where + endif + + !-------------------------------------------------------------------- + + end subroutine li_calculate_mask_init + + + +!*********************************************************************** +! +! routine land_ice_calculate_mask +! +!> \brief Calculates masks for land ice +!> \author Matt Hoffman +!> \date 10 May 2012 +!> \details +!> This routine Calculates masks for land ice. +! +!----------------------------------------------------------------------- + + subroutine li_calculate_mask(meshPool, velocityPool, geometryPool, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + type (mpas_pool_type), intent(inout) :: & + velocityPool !< Input: velocity information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: & + geometryPool !< Input/Output: geometry information + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer, pointer :: nCells, nVertices, nEdges, vertexDegree + integer, pointer :: nVertInterfaces + real(KIND=RKIND), dimension(:), pointer :: thickness, bedTopography + integer, dimension(:), pointer :: nEdgesOnCell, cellMask, vertexMask, edgeMask + integer, dimension(:,:), pointer :: cellsOnCell, cellsOnVertex, cellsOnEdge, dirichletVelocityMask + real (kind=RKIND), pointer :: config_ice_density, config_ocean_density, & + config_sea_level, config_dynamic_thickness + character (len=StrKIND), pointer :: config_velocity_solver + + integer :: i, j, iCell + logical :: isMargin + logical :: isAlbanyMarginNeighbor + logical :: aCellOnVertexHasIce, aCellOnVertexHasNoIce, aCellOnVertexHasDynamicIce, aCellOnVertexHasNoDynamicIce, & + aCellOnVertexIsFloating, aCellOnVertexIsAlbanyActive + logical :: aCellOnVertexIsGrounded + logical :: aCellOnEdgeHasIce, aCellOnEdgeHasNoIce, aCellOnEdgeHasDynamicIce, aCellOnEdgeHasNoDynamicIce, & + aCellOnEdgeIsFloating + logical :: aCellOnEdgeIsGrounded + integer :: numCellsOnVertex + integer :: numDiriDynamicCells, numDiriNondynamicCells, numExtendedCells + logical :: validVertex + real (kind=RKIND) :: thinnestNeighborHeight + integer :: iCellNeighbor + logical :: openOceanNeighbor + + call mpas_timer_start('calculate mask') + + err = 0 + + ! Assign pointers and variables + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) + call mpas_pool_get_dimension(meshPool, 'nVertInterfaces', nVertInterfaces) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + + call mpas_pool_get_array(geometryPool, 'bedTopography', bedTopography) + call mpas_pool_get_array(geometryPool, 'cellMask', cellMask) + call mpas_pool_get_array(geometryPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(geometryPool, 'vertexMask', vertexMask, timeLevel=1) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + call mpas_pool_get_config(liConfigs, 'config_ice_density', config_ice_density) + call mpas_pool_get_config(liConfigs, 'config_ocean_density', config_ocean_density) + call mpas_pool_get_config(liConfigs, 'config_sea_level', config_sea_level) + call mpas_pool_get_config(liConfigs, 'config_dynamic_thickness', config_dynamic_thickness) + call mpas_pool_get_config(liConfigs, 'config_velocity_solver', config_velocity_solver) + + if ( .not. ((trim(config_velocity_solver) == 'sia') .or. (trim(config_velocity_solver) == 'none')) ) then + call mpas_pool_get_array(velocityPool, 'dirichletVelocityMask', dirichletVelocityMask, timeLevel = 1) + endif + + ! ==== + ! Calculate cellMask values=========================== + ! ==== + + !call mpas_timer_start('calculate mask cell') + ! Set mask to 0 everywhere, but need to preserve bits the initial ice extent bit + do i=1, nCells + cellMask(i) = iand(cellMask(i), li_mask_ValueInitialIceExtent) + enddo + + ! Identify cells with ice + where (thickness > 0) + cellMask = ior(cellMask, li_mask_ValueIce) + end where + + ! Is it floating? (ice thickness equal to floatation is considered floating) + ! For now floating ice and grounded ice are mutually exclusive. + ! This may change if a grounding line parameterization is added. + where ( li_mask_is_ice(cellMask) .and. (config_ice_density / config_ocean_density * thickness) <= & + (config_sea_level - bedTopography) ) + cellMask = ior(cellMask, li_mask_ValueFloating) + end where + + ! Identify the margin + ! For a cell, we define the margin as the last cell with ice (the cell has ice and at least one neighbor is a non-ice cell) + do i=1,nCells + if (li_mask_is_ice(cellMask(i))) then + isMargin = .false. + do j=1,nEdgesOnCell(i) ! Check if any neighbors are non-ice + isMargin = ( isMargin .or. (.not. li_mask_is_ice(cellMask(cellsOnCell(j,i)))) ) + enddo + if (isMargin) then + cellMask(i) = ior(cellMask(i), li_mask_ValueMargin) + endif + endif + enddo + + ! Identify cells where the ice is above the ice dynamics thickness limit + where ( thickness > config_dynamic_thickness ) + cellMask = ior(cellMask, li_mask_ValueDynamicIce) + end where + ! Exclude floating margin cells that are thinner than all their non-margin neighbors + do i=1,nCells + if (li_mask_is_margin(cellMask(i)) .and. li_mask_is_floating_ice(cellMask(i))) then + openOceanNeighbor = .false. + thinnestNeighborHeight = 1.0e36_RKIND + do j=1,nEdgesOnCell(i) ! Find height of thickest neighbor + iCellNeighbor = cellsOnCell(j,i) + if (thickness(iCellNeighbor) > 0.0_RKIND .and. .not. li_mask_is_margin(cellMask(iCellNeighbor))) then + thinnestNeighborHeight = min(thinnestNeighborHeight, thickness(iCellNeighbor)) + endif + if ((bedTopography(iCellNeighbor) < config_sea_level) .and. (.not. li_mask_is_ice(cellMask(iCellNeighbor)))) then + openOceanNeighbor = .true. + endif + enddo + if ((openOceanNeighbor) .and. (thickness(i) < 0.95_RKIND * thinnestNeighborHeight)) then + ! Consider this NOT dynamic if the ice shelf margin is not almost as tall as it's inland neighbors. + ! Only do this for a non-dynamic cell adjacent to open ocean. + ! (if the floating cell was marked as a margin because it is adjacent to ice-free land, + ! we want to still consider it as dynamic) + cellMask(i) = iand(cellMask(i), not(li_mask_ValueDynamicIce)) ! (turn off this bit) + endif + endif + enddo + + ! Identify cells that Albany would consider as active + if ( (trim(config_velocity_solver) == 'L1L2') .or. & + (trim(config_velocity_solver) == 'FO') .or. & + (trim(config_velocity_solver) == 'Stokes') ) then + ! HO external FEM dycore + ! Identify cells where the ice is above the ice dynamics thickness limit but not with a dirichletVelocity condition set + where ( li_mask_is_dynamic_ice(cellMask) .and. & ! same as for SIA case + (maxval(dirichletVelocityMask(1:nVertInterfaces-1, :), dim=1) == 0) ) ! but exclude dirichletVelocityMask + ! locations set as lateral b.c. We don't want to consider dirichlet b.c. on the basal boundary, + ! so we ignore the basal level + cellMask = ior(cellMask, li_mask_ValueAlbanyActive) + end where + endif + + ! Identify the dynamic margin + ! For a cell, we define the dynamic margin as the last cell with dynamic ice + ! (the cell is dynamic and at least one neighboring cell is not dynamic) + do i=1,nCells + if (li_mask_is_dynamic_ice(cellMask(i))) then + isMargin = .false. + do j=1,nEdgesOnCell(i) ! Check if any neighbors are not dynamic + isMargin = ( isMargin .or. (.not. li_mask_is_dynamic_ice(cellMask(cellsOnCell(j,i)))) ) + enddo + if (isMargin) then + cellMask(i) = ior(cellMask(i), li_mask_ValueDynamicMargin) + endif + endif + enddo + + ! Identify first cell outside of active albany extent + if ( (trim(config_velocity_solver) == 'L1L2') .or. & + (trim(config_velocity_solver) == 'FO') .or. & + (trim(config_velocity_solver) == 'Stokes') ) then + do i=1,nCells + if ( (.not. li_mask_is_albany_active(cellMask(i)))) then ! check non albany cells only + isAlbanyMarginNeighbor = .false. + do j=1,nEdgesOnCell(i) ! Check if any neighbors are dynamic-ice + isAlbanyMarginNeighbor = ( isAlbanyMarginNeighbor .or. (li_mask_is_albany_active(cellMask(cellsOnCell(j,i)))) ) + enddo + if (isAlbanyMarginNeighbor) then + cellMask(i) = ior(cellMask(i), li_mask_ValueAlbanyMarginNeighbor) + endif + endif + enddo + endif + + ! Identify the grounding line + ! For a cell, we define the GL as a grounded cell with ice with at least one neighbor with floating ice or open ocean + do i=1,nCells + if (li_mask_is_grounded_ice(cellMask(i))) then ! only need to check grounded cells + do j=1,nEdgesOnCell(i) ! Check if any neighbors are floating or open ocean + iCellNeighbor = cellsOnCell(j,i) + if (li_mask_is_floating_ice(cellMask(iCellNeighbor)) .or. & + ((bedTopography(iCellNeighbor) < config_sea_level) .and. (.not. li_mask_is_ice(cellMask(iCellNeighbor)))) ) then + cellMask(i) = ior(cellMask(i), li_mask_ValueGroundingLine) + endif + cycle ! no need to look at additional neighbors + enddo + endif + enddo + + !call mpas_timer_stop('calculate mask cell') + + ! ==== + ! Calculate vertexMask values based on cellMask values=========================== + ! ==== + ! Bit: Vertices with ice are ones with at least one adjacent cell with ice + ! Bit: Vertices with dynamic ice are ones with at least one adjacent cell with dynamic ice + ! Bit: Floating vertices have at least one neighboring cell floating + ! Bit: Vertices on margin are vertices with at least one neighboring cell with ice + ! and at least one neighboring cell without ice + ! Bit: Vertices on dynamic margin are vertices with at least one neighboring cell with + ! dynamic ice and at least one neighboring cell without dynamic ice + ! NOTE: Vertices are only considered 'valid' if they have three valid neighboring + ! cells (i.e., the cells exist in the mesh). This allows external dycores to use the + ! vertexMask to get information about triangles in the Delaunay triangulation. + ! (This is done in a way which does not assume vertexMask==3.) + ! Bit: GL is a vertex with at least one neighboring cell grounded ice and at least one neighboring cell floating ice + + !call mpas_timer_start('calculate mask vertex') + + vertexMask = 0 + do i = 1,nVertices + aCellOnVertexHasIce = .false. + aCellOnVertexHasNoIce = .false. + aCellOnVertexHasDynamicIce = .false. + aCellOnVertexHasNoDynamicIce = .false. + aCellOnVertexIsFloating = .false. + aCellOnVertexIsGrounded = .false. + do j = 1, vertexDegree ! vertexDegree is usually 3 (e.g. CVT mesh) but could be something else (e.g. 4 for quad mesh) + iCell = cellsOnVertex(j,i) + aCellOnVertexHasIce = (aCellOnVertexHasIce .or. li_mask_is_ice(cellMask(iCell))) + aCellOnVertexHasNoIce = (aCellOnVertexHasNoIce .or. (.not. li_mask_is_ice(cellMask(iCell)))) + aCellOnVertexHasDynamicIce = (aCellOnVertexHasDynamicIce .or. li_mask_is_dynamic_ice(cellMask(iCell))) + aCellOnVertexHasNoDynamicIce = (aCellOnVertexHasNoDynamicIce .or. (.not. (li_mask_is_dynamic_ice(cellMask(iCell))))) + aCellOnVertexIsFloating = (aCellOnVertexIsFloating .or. li_mask_is_floating_ice(cellMask(iCell))) + aCellOnVertexIsGrounded = (aCellOnVertexIsGrounded .or. li_mask_is_grounded_ice(cellMask(iCell))) + end do + if (aCellOnVertexHasIce) then + vertexMask(i) = ior(vertexMask(i), li_mask_ValueIce) + endif + if (aCellOnVertexHasDynamicIce) then + vertexMask(i) = ior(vertexMask(i), li_mask_ValueDynamicIce) + endif + if (aCellOnVertexIsFloating) then + vertexMask(i) = ior(vertexMask(i), li_mask_ValueFloating) + endif + if (aCellOnVertexIsFloating .and. aCellOnVertexIsGrounded) then + vertexMask(i) = ior(vertexMask(i), li_mask_ValueGroundingLine) + endif + if (aCellOnVertexHasIce .and. aCellOnVertexHasNoIce) then + vertexMask(i) = ior(vertexMask(i), li_mask_ValueMargin) + ! vertex with both 1+ ice cell and 1+ non-ice cell as neighbors + endif + if (aCellOnVertexHasDynamicIce .and. aCellOnVertexHasNoDynamicIce) then + vertexMask(i) = ior(vertexMask(i), li_mask_ValueDynamicMargin) + ! vertex with both 1+ dynamic ice cell(s) and 1+ non-dynamic cell(s) as neighbors + endif + end do + + + ! Re-loop over vertices for information only needed by Albany + ! This is 10x more expensive to conditionally include in the above loop. + if ( (trim(config_velocity_solver) == 'L1L2') .or. & + (trim(config_velocity_solver) == 'FO') .or. & + (trim(config_velocity_solver) == 'Stokes') ) then + do i = 1,nVertices + aCellOnVertexIsAlbanyActive = .false. + numCellsOnVertex = 0 + validVertex = .false. + numDiriDynamicCells = 0 + numDiriNondynamicCells = 0 + numExtendedCells = 0 + do j = 1, vertexDegree ! vertexDegree is usually 3 (e.g. CVT mesh) but could be something else (e.g. 4 for quad mesh) + iCell = cellsOnVertex(j,i) + if (iCell < nCells+1) then + numCellsOnVertex = numCellsOnVertex + 1 + endif + aCellOnVertexIsAlbanyActive = (aCellOnVertexIsAlbanyActive .or. li_mask_is_albany_active(cellMask(iCell))) + + !if (li_mask_is_dynamic_ice(cellMask(iCell)) .and. .not. li_mask_is_albany_active(cellMask(iCell))) then + !!< this finds diri cells + + if ( (maxval(dirichletVelocityMask(1:nVertInterfaces-1, iCell)) > 0) .and. & + (li_mask_is_dynamic_ice(cellMask(iCell)) ) ) then + numDiriDynamicCells = numDiriDynamicCells + 1 + elseif ( (maxval(dirichletVelocityMask(1:nVertInterfaces-1, iCell)) > 0) .and. & + (.not. li_mask_is_dynamic_ice(cellMask(iCell)) ) ) then + numDiriNondynamicCells = numDiriNondynamicCells + 1 + elseif (li_mask_is_albany_margin_neighbor(cellMask(iCell))) then + numExtendedCells = numExtendedCells + 1 + endif + end do + if (numCellsOnVertex == vertexDegree) validVertex = .true. + if (aCellOnVertexIsAlbanyActive .and. validVertex) vertexMask(i) = ior(vertexMask(i), li_mask_ValueAlbanyActive) + if ( (numDiriDynamicCells == 1) .and. (numDiriNondynamicCells == 1) .and. & + (numExtendedCells == 1) .and. validVertex) then + ! This is a special case needed for MISMIP + vertexMask(i) = ior(vertexMask(i), li_mask_ValueAlbanyActive) + vertexMask(i) = ior(vertexMask(i), li_mask_ValueAlbanyMarginNeighbor) + endif + end do ! vertices loop + endif ! HO velocity solver + + !call mpas_timer_stop('calculate mask vertex') + + ! ==== + ! Calculate edgeMask values based on cellMask values=========================== + ! ==== + ! Bit: Edges with ice are ones with at least one adjacent cell with ice + ! Bit: Edges with dynamic ice are ones with at least one adjacent cell with dynamic ice + ! Bit: Floating Edges have at least one neighboring cell floating + ! Bit: Edges on margin are edges with one neighboring cell with ice and one neighboring cell without ice + ! Bit: Edges on dynamic margin are edges with one neighboring cell with dynamic ice and + ! one neighboring cell without dynamic ice + ! Bit: GL is an edge with one cell grounded ice and one cell floating ice + + !call mpas_timer_start('calculate mask edge') + + edgeMask = 0 + do i = 1,nEdges + aCellOnEdgeHasIce = .false. + aCellOnEdgeHasNoIce = .false. + aCellOnEdgeHasDynamicIce = .false. + aCellOnEdgeHasNoDynamicIce = .false. + aCellOnEdgeIsFloating = .false. + aCellOnEdgeIsGrounded = .false. + do j = 1, 2 + iCell = cellsOnEdge(j,i) + aCellOnEdgeHasIce = (aCellOnEdgeHasIce .or. li_mask_is_ice(cellMask(iCell))) + aCellOnEdgeHasNoIce = (aCellOnEdgeHasNoIce .or. (.not. li_mask_is_ice(cellMask(iCell)))) + aCellOnEdgeHasDynamicIce = (aCellOnEdgeHasDynamicIce .or. li_mask_is_dynamic_ice(cellMask(iCell))) + aCellOnEdgeHasNoDynamicIce = (aCellOnEdgeHasNoDynamicIce .or. (.not. (li_mask_is_dynamic_ice(cellMask(iCell))))) + aCellOnEdgeIsFloating = (aCellOnEdgeIsFloating .or. li_mask_is_floating_ice(cellMask(iCell))) + aCellOnEdgeIsGrounded = (aCellOnEdgeIsGrounded .or. li_mask_is_grounded_ice(cellMask(iCell))) + end do + if (aCellOnEdgeHasIce) then + edgeMask(i) = ior(edgeMask(i), li_mask_ValueIce) + endif + if (aCellOnEdgeHasDynamicIce) then + edgeMask(i) = ior(edgeMask(i), li_mask_ValueDynamicIce) + edgeMask(i) = ior(edgeMask(i), li_mask_ValueAlbanyActive) + !< Note: Albany does not use edgeMask, but setting this anyway. + endif + if (aCellOnEdgeIsFloating) then + edgeMask(i) = ior(edgeMask(i), li_mask_ValueFloating) + endif + if (aCellOnEdgeIsFloating .and. aCellOnEdgeIsGrounded) then + edgeMask(i) = ior(edgeMask(i), li_mask_ValueGroundingLine) + endif + if (aCellOnEdgeHasIce .and. aCellOnEdgeHasNoIce) then + edgeMask(i) = ior(edgeMask(i), li_mask_ValueMargin) + endif + if (aCellOnEdgeHasDynamicIce .and. aCellOnEdgeHasNoDynamicIce) then + edgeMask(i) = ior(edgeMask(i), li_mask_ValueDynamicMargin) + endif + + end do + !call mpas_timer_stop('calculate mask edge') + + ! vertexMask and edgeMask needs halo updates before they can be used. Halo updates need to occur outside of block loops. + + ! === error check + if (err > 0) then + call mpas_log_write("An error has occurred in li_calculate_mask.", MPAS_LOG_ERR) + endif + + call mpas_timer_stop('calculate mask') + + !-------------------------------------------------------------------- + end subroutine li_calculate_mask + + +!*********************************************************************** +! +! routine li_calculate_extrapolate_floating_edgemask +! +!> \brief Extrapolates floating edges forward as needed by external FEM dycores +!> \author Matt Hoffman +!> \date 29 January 2015 +!> \details +!> External FEM dycores include the first non-ice cells in their mesh. They +!> also use a mask to apply floating lateral boundary conditions on edges. +!> Because they include extra cell center locations in their meshes, the triangle +!> edges connecting these extra nodes will not be covered by the standard +!> MPAS edge mask. This routine deals with this problem by 'extrapolating' +!> the floating edge mask forward to cover the edges connecting these extra nodes. +!> It does so by looping over edges, and setting as floating any edge that has +!> at least one neighboring vertex that is 'floating'. This makes use of the +!> convention that "Floating vertices have at least one neighboring cell floating". +! +!----------------------------------------------------------------------- + + subroutine li_calculate_extrapolate_floating_edgemask(meshPool, vertexMask, floatingEdges) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + integer, dimension(:), intent(inout) :: & + vertexMask !< Input/Output: vertexMask + + integer, dimension(:), intent(inout) :: & + floatingEdges !< Input/Output: 0/1 mask of floating edges + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer, dimension(:,:), pointer :: verticesOnEdge + integer, pointer :: nEdges + integer :: iEdge + + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) + + ! Build floatingEdges mask that is extended forward one extra edge + do iEdge = 1, nEdges + floatingEdges(iEdge) = maxval(li_mask_is_floating_ice_int(vertexMask(verticesOnEdge(:, iEdge)))) + enddo + + ! Now includes vertices that include 2 cells with ice and one extended floating cell + ! This will be when the two cells with ice were Dirichlet cells + end subroutine li_calculate_extrapolate_floating_edgemask + + + ! =================================== + ! Functions for decoding bitmasks - will work with cellMask, edgeMask, or vertexMask + ! =================================== + ! Only adding the minimum needed for now. These should be added as needed. + ! functions with names that include '_logout' return logical types + ! -- these should be used with 'if' and 'where' statements + ! functions with names that include '_intout' return integers types with 0 for false, 1 for true. + ! -- these should be used when multiplying against numeric arrays + + + ! -- Functions that check for presence of ice -- + function li_mask_is_ice_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_ice_logout_1d + + li_mask_is_ice_logout_1d = (iand(mask, li_mask_ValueIce) == li_mask_ValueIce) + end function li_mask_is_ice_logout_1d + + function li_mask_is_ice_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_ice_logout_0d + + li_mask_is_ice_logout_0d = (iand(mask, li_mask_ValueIce) == li_mask_ValueIce) + end function li_mask_is_ice_logout_0d + + + function li_mask_is_ice_intout_1d(mask) + integer, dimension(:), intent(in) :: mask + integer, dimension(size(mask)) :: li_mask_is_ice_intout_1d + + li_mask_is_ice_intout_1d = iand(mask, li_mask_ValueIce) / li_mask_ValueIce + end function li_mask_is_ice_intout_1d + + function li_mask_is_ice_intout_0d(mask) + integer, intent(in) :: mask + integer :: li_mask_is_ice_intout_0d + + li_mask_is_ice_intout_0d = iand(mask, li_mask_ValueIce) / li_mask_ValueIce + end function li_mask_is_ice_intout_0d + + + ! -- Functions that check for presence of dynamic ice -- + function li_mask_is_dynamic_ice_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_dynamic_ice_logout_1d + + li_mask_is_dynamic_ice_logout_1d = (iand(mask, li_mask_ValueDynamicIce) == li_mask_ValueDynamicIce) + end function li_mask_is_dynamic_ice_logout_1d + + function li_mask_is_dynamic_ice_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_dynamic_ice_logout_0d + + li_mask_is_dynamic_ice_logout_0d = (iand(mask, li_mask_ValueDynamicIce) == li_mask_ValueDynamicIce) + end function li_mask_is_dynamic_ice_logout_0d + + function li_mask_is_dynamic_ice_intout_1d(mask) + integer, dimension(:), intent(in) :: mask + integer, dimension(size(mask)) :: li_mask_is_dynamic_ice_intout_1d + + li_mask_is_dynamic_ice_intout_1d = iand(mask, li_mask_ValueDynamicIce) / li_mask_ValueDynamicIce + end function li_mask_is_dynamic_ice_intout_1d + + function li_mask_is_dynamic_ice_intout_0d(mask) + integer, intent(in) :: mask + integer :: li_mask_is_dynamic_ice_intout_0d + + li_mask_is_dynamic_ice_intout_0d = iand(mask, li_mask_ValueDynamicIce) / li_mask_ValueDynamicIce + end function li_mask_is_dynamic_ice_intout_0d + + + ! -- Functions that check for presence of margin -- + function li_mask_is_margin_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_margin_logout_1d + + li_mask_is_margin_logout_1d = (iand(mask, li_mask_ValueMargin) == li_mask_ValueMargin) + end function li_mask_is_margin_logout_1d + + function li_mask_is_margin_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_margin_logout_0d + + li_mask_is_margin_logout_0d = (iand(mask, li_mask_ValueMargin) == li_mask_ValueMargin) + end function li_mask_is_margin_logout_0d + + + ! -- Functions that check for presence of active albany -- + function li_mask_is_albany_active_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_albany_active_logout_1d + + li_mask_is_albany_active_logout_1d = (iand(mask, li_mask_ValueAlbanyActive) == li_mask_ValueAlbanyActive) + end function li_mask_is_albany_active_logout_1d + + function li_mask_is_albany_active_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_albany_active_logout_0d + + li_mask_is_albany_active_logout_0d = (iand(mask, li_mask_ValueAlbanyActive) == li_mask_ValueAlbanyActive) + end function li_mask_is_albany_active_logout_0d + + + ! -- Functions that check for presence of dynamic margin -- + function li_mask_is_dynamic_margin_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_dynamic_margin_logout_1d + + li_mask_is_dynamic_margin_logout_1d = (iand(mask, li_mask_ValueDynamicMargin) == li_mask_ValueDynamicMargin) + end function li_mask_is_dynamic_margin_logout_1d + + function li_mask_is_dynamic_margin_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_dynamic_margin_logout_0d + + li_mask_is_dynamic_margin_logout_0d = (iand(mask, li_mask_ValueDynamicMargin) == li_mask_ValueDynamicMargin) + end function li_mask_is_dynamic_margin_logout_0d + + function li_mask_is_dynamic_margin_intout_1d(mask) + integer, dimension(:), intent(in) :: mask + integer, dimension(size(mask)) :: li_mask_is_dynamic_margin_intout_1d + + li_mask_is_dynamic_margin_intout_1d = iand(mask, li_mask_ValueDynamicMargin) / li_mask_ValueDynamicMargin + end function li_mask_is_dynamic_margin_intout_1d + + function li_mask_is_dynamic_margin_intout_0d(mask) + integer, intent(in) :: mask + integer :: li_mask_is_dynamic_margin_intout_0d + + li_mask_is_dynamic_margin_intout_0d = iand(mask, li_mask_ValueDynamicMargin) / li_mask_ValueDynamicMargin + end function li_mask_is_dynamic_margin_intout_0d + + + ! -- Functions that check for presence of albany margin neighbor -- + function li_mask_is_albany_margin_neighbor_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_albany_margin_neighbor_logout_1d + + li_mask_is_albany_margin_neighbor_logout_1d = (iand(mask, li_mask_ValueAlbanyMarginNeighbor) == & + li_mask_ValueAlbanyMarginNeighbor) + end function li_mask_is_albany_margin_neighbor_logout_1d + + function li_mask_is_albany_margin_neighbor_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_albany_margin_neighbor_logout_0d + + li_mask_is_albany_margin_neighbor_logout_0d = (iand(mask, li_mask_ValueAlbanyMarginNeighbor) == & + li_mask_ValueAlbanyMarginNeighbor) + end function li_mask_is_albany_margin_neighbor_logout_0d + + + ! -- Functions that check for presence of floating ice -- + function li_mask_is_floating_ice_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_floating_ice_logout_1d + + li_mask_is_floating_ice_logout_1d = (iand(mask, li_mask_ValueFloating) == li_mask_ValueFloating) + end function li_mask_is_floating_ice_logout_1d + + function li_mask_is_floating_ice_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_floating_ice_logout_0d + + li_mask_is_floating_ice_logout_0d = (iand(mask, li_mask_ValueFloating) == li_mask_ValueFloating) + end function li_mask_is_floating_ice_logout_0d + + function li_mask_is_floating_ice_intout_1d(mask) + integer, dimension(:), intent(in) :: mask + integer, dimension(size(mask)) :: li_mask_is_floating_ice_intout_1d + + li_mask_is_floating_ice_intout_1d = iand(mask, li_mask_ValueFloating) / li_mask_ValueFloating + end function li_mask_is_floating_ice_intout_1d + + function li_mask_is_floating_ice_intout_0d(mask) + integer, intent(in) :: mask + integer :: li_mask_is_floating_ice_intout_0d + + li_mask_is_floating_ice_intout_0d = iand(mask, li_mask_ValueFloating) / li_mask_ValueFloating + end function li_mask_is_floating_ice_intout_0d + + ! -- Functions that check for presence of grounded ice -- + function li_mask_is_grounded_ice_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_grounded_ice_logout_1d + + li_mask_is_grounded_ice_logout_1d = ( (iand(mask, li_mask_ValueFloating) /= li_mask_ValueFloating) & + .and. (li_mask_is_ice(mask)) ) + end function li_mask_is_grounded_ice_logout_1d + + function li_mask_is_grounded_ice_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_grounded_ice_logout_0d + + li_mask_is_grounded_ice_logout_0d = ( (iand(mask, li_mask_ValueFloating) /= li_mask_ValueFloating) & + .and. (li_mask_is_ice(mask)) ) + end function li_mask_is_grounded_ice_logout_0d + + function li_mask_is_grounded_ice_intout_1d(mask) + integer, dimension(:), intent(in) :: mask + integer, dimension(size(mask)) :: li_mask_is_grounded_ice_intout_1d + + where( li_mask_is_ice(mask) ) + li_mask_is_grounded_ice_intout_1d = int(mask*0 + 1) - ( iand(mask, li_mask_ValueFloating) / li_mask_ValueFloating ) + elsewhere + li_mask_is_grounded_ice_intout_1d = 0 + endwhere + end function li_mask_is_grounded_ice_intout_1d + + function li_mask_is_grounded_ice_intout_0d(mask) + integer, intent(in) :: mask + integer :: li_mask_is_grounded_ice_intout_0d + + if( li_mask_is_ice(mask) )then + li_mask_is_grounded_ice_intout_0d = 1 - ( iand(mask, li_mask_ValueFloating) / li_mask_ValueFloating ) + else + li_mask_is_grounded_ice_intout_0d = 0 + endif + end function li_mask_is_grounded_ice_intout_0d + + function li_mask_is_grounding_line_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_grounding_line_logout_1d + + li_mask_is_grounding_line_logout_1d = (iand(mask, li_mask_ValueGroundingLine) == li_mask_ValueGroundingLine) + end function li_mask_is_grounding_line_logout_1d + + function li_mask_is_grounding_line_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_grounding_line_logout_0d + + li_mask_is_grounding_line_logout_0d = (iand(mask, li_mask_ValueGroundingLine) == li_mask_ValueGroundingLine) + end function li_mask_is_grounding_line_logout_0d + + function li_mask_is_initial_ice_logout_1d(mask) + integer, dimension(:), intent(in) :: mask + logical, dimension(size(mask)) :: li_mask_is_initial_ice_logout_1d + + li_mask_is_initial_ice_logout_1d = (iand(mask, li_mask_ValueInitialIceExtent) == li_mask_ValueInitialIceExtent) + end function li_mask_is_initial_ice_logout_1d + + function li_mask_is_initial_ice_logout_0d(mask) + integer, intent(in) :: mask + logical :: li_mask_is_initial_ice_logout_0d + + li_mask_is_initial_ice_logout_0d = (iand(mask, li_mask_ValueInitialIceExtent) == li_mask_ValueInitialIceExtent) + end function li_mask_is_initial_ice_logout_0d + + + +!*********************************************************************** +! Private subroutines: +!*********************************************************************** + +! - no private subroutines - (module is not declared private) + + +end module li_mask + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + diff --git a/src/core_landice/shared/mpas_li_setup.F b/src/core_landice/shared/mpas_li_setup.F new file mode 100644 index 0000000000..94f38212ef --- /dev/null +++ b/src/core_landice/shared/mpas_li_setup.F @@ -0,0 +1,750 @@ +! Copyright (c) 2013-2018, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! li_setup +! +!> \brief MPAS land ice setup module +!> \author Matt Hoffman +!> \date 17 April 2011 +!> \details +!> This module contains various subroutines for +!> setting up the land ice core. +! +!----------------------------------------------------------------------- +module li_setup + + use mpas_derived_types + use mpas_pool_routines + use mpas_kind_types + use mpas_dmpar + use mpas_log + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + type (mpas_pool_type), public, pointer :: liConfigs !< Public parameter: pool of config options + type (mpas_pool_type), public, pointer :: liPackages + + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + public :: li_setup_config_options, & + li_setup_vertical_grid, & + li_setup_sign_and_index_fields, & + li_setup_wachspress_vertex_to_cell_weights, & + li_interpolate_vertex_to_cell_2d, & + li_cells_to_vertices_1dfield_using_kiteAreas, & + li_calculate_layerThickness, & + li_compute_gradient_2d + + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + + +!*********************************************************************** + +contains + + +!*********************************************************************** +! +! routine li_setup_config_options +! +!> \brief Makes any setup changes needed based on chosen config options +!> \author Matt Hoffman +!> \date 16 April 2014 +!> \details +!> This routine makes any adjustments as needed based on which +!> config options were chosen. +! +!----------------------------------------------------------------------- + + subroutine li_setup_config_options( domain, err ) + + use mpas_timekeeping + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain !< Input/Output: domain object + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + character(len=StrKIND), pointer :: config_thermal_solver + character(len=StrKIND), pointer :: config_tracer_advection + + err = 0 + + ! Make config pool publicly available in this module + liConfigs => domain % configs + liPackages => domain % packages + + ! --- + ! Config-specific setup occurs here + ! --- + + ! If thermal evolution is enabled, force tracer advection to be on + call mpas_pool_get_config(liConfigs, 'config_thermal_solver', config_thermal_solver) + call mpas_pool_get_config(liConfigs, 'config_tracer_advection', config_tracer_advection) + if (( (trim(config_thermal_solver) == 'temperature') .or. & + (trim(config_thermal_solver) == 'enthalpy') ) .and. & + (trim(config_tracer_advection) /= 'fo') )then + call mpas_log_write("Setting config_tracer_advection='fo' because a thermal solver has been selected. " // & + "(config_tracer_advection was set to: " // trim(config_tracer_advection) // ")", MPAS_LOG_WARN) + config_tracer_advection = 'fo' + endif + + + ! --- + ! Print this run's configs to the log file + ! --- + call mpas_log_write("") + call mpas_log_write("MPASLI is using the following configuration:") + call mpas_log_write("============================================") + call mpas_pool_print_summary(liConfigs, MPAS_POOL_CONFIG) + call mpas_log_write("============================================") + call mpas_log_write("") + + !-------------------------------------------------------------------- + end subroutine li_setup_config_options + + + +!*********************************************************************** +! +! routine li_setup_vertical_grid +! +!> \brief Initializes vertical coord system +!> \author Matt Hoffman +!> \date 20 April 2012 +!> \details +!> This routine initializes the vertical coord system. +! +!----------------------------------------------------------------------- + + subroutine li_setup_vertical_grid(meshPool, geometryPool, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: meshPool !< Input/Output: mesh object + type (mpas_pool_type), intent(inout) :: geometryPool !< Input/Output: geometry object + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + ! Pool pointers + integer, pointer :: nVertLevels ! Dimensions + real (kind=RKIND), dimension(:), pointer :: layerThicknessFractions, layerCenterSigma, layerInterfaceSigma + real (kind=RKIND), dimension(:), pointer :: thickness + logical, pointer :: config_do_restart + ! Truly locals + integer :: k + real (kind=RKIND) :: fractionTotal + + ! Get pool stuff + call mpas_pool_get_config(liConfigs, 'config_do_restart', config_do_restart) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + ! layerThicknessFractions is provided by input + call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) + call mpas_pool_get_array(meshPool, 'layerCenterSigma', layerCenterSigma) + call mpas_pool_get_array(meshPool, 'layerInterfaceSigma', layerInterfaceSigma) + call mpas_pool_get_array(geometryPool, 'thickness', thickness) + + ! Check that layerThicknessFractions are valid + ! TODO - switch to having the user input the sigma levels instead??? + if (.not. config_do_restart) then ! This would be applied an additional time on each restart, + ! messing up the vertical levels (very, very slightly) + fractionTotal = sum(layerThicknessFractions) + if (fractionTotal /= 1.0_RKIND) then + if (abs(fractionTotal - 1.0_RKIND) > 0.001_RKIND) then + call mpas_log_write('The sum of layerThicknessFractions is different from 1.0 by more than 0.001.', MPAS_LOG_ERR) + err = 1 + end if + call mpas_log_write('Adjusting upper layerThicknessFrac by small amount because sum of ' // & + 'layerThicknessFractions is slightly different from 1.0.') + layerThicknessFractions(1) = layerThicknessFractions(1) - (fractionTotal - 1.0_RKIND) + endif + endif + + ! layerCenterSigma is the fractional vertical position (0-1) of each layer center, + ! with 0.0 at the ice surface and 1.0 at the ice bed + ! layerInterfaceSigma is the fractional vertical position (0-1) of each layer interface, + ! with 0.0 at the ice surface and 1.0 at the ice bed. + ! Interface 1 is the surface, interface 2 is between layers 1 and 2, etc., and interface nVertLevels+1 is the bed. + layerCenterSigma(1) = 0.5_RKIND * layerThicknessFractions(1) + layerInterfaceSigma(1) = 0.0_RKIND + do k = 2, nVertLevels + layerCenterSigma(k) = layerCenterSigma(k-1) + 0.5_RKIND * layerThicknessFractions(k-1) & + + 0.5_RKIND * layerThicknessFractions(k) + layerInterfaceSigma(k) = layerInterfaceSigma(k-1) + layerThicknessFractions(k-1) + end do + layerInterfaceSigma(nVertLevels+1) = 1.0_RKIND + + !-------------------------------------------------------------------- + end subroutine li_setup_vertical_grid + + + +!*********************************************************************** +! +! routine li_setup_sign_and_index_fields +! +!> \brief Determines signs for various mesh items +!> \author Matt Hoffman - based on code by Doug Jacobsen +!> \date 20 April 2012 +!> \details +!> This routine determines the sign for various mesh items. +! +!----------------------------------------------------------------------- + subroutine li_setup_sign_and_index_fields(meshPool) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh object + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + ! Pool pointers + integer, pointer :: nCells !, nVertices, vertexDegree + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell, cellsOnEdge !, edgesOnVertex, cellsOnVertex, verticesOnCell, verticesOnEdge + integer, dimension(:,:), pointer :: edgeSignOnCell !, edgeSignOnVertex, kiteIndexOnCell + ! Truly locals + integer :: iCell, iEdge, iVertex, i, j, k + + ! Get pool stuff + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + + edgeSignOnCell = 0.0_RKIND + !edgeSignOnVertex = 0.0_RKIND + !kiteIndexOnCell = 0.0_RKIND + ! If needed, edgeSignOnVertex and kiteIndexOnCell can also be setup here. + + do iCell = 1, nCells + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + !iVertex = verticesOnCell(i, iCell) + + ! Vector points from cell 1 to cell 2 + if(iCell == cellsOnEdge(1, iEdge)) then + edgeSignOnCell(i, iCell) = -1 + else + edgeSignOnCell(i, iCell) = 1 + end if + + !do j = 1, vertexDegree + ! if(cellsOnVertex(j, iVertex) == iCell) then + ! kiteIndexOnCell(i, iCell) = j + ! end if + !end do + end do + end do + + !do iVertex = 1, nVertices + ! do i = 1, vertexDegree + ! iEdge = edgesOnVertex(i, iVertex) + ! + ! ! Vector points from vertex 1 to vertex 2 + ! if(iVertex == verticesOnEdge(1, iEdge)) then + ! edgeSignOnVertex(i, iVertex) = -1 + ! else + ! edgeSignOnVertex(i, iVertex) = 1 + ! end if + ! end do + !end do + + !-------------------------------------------------------------------- + end subroutine li_setup_sign_and_index_fields + + +!*********************************************************************** +! +! routine li_setup_wachspress_vertex_to_cell_weights +! +!> \brief Calculates weights to interpolate from vertices to cell centers +!> \author Matt Hoffman +!> \date 29 Aug 2016 +!> \details +!> This routine determines the sign for various mesh items. +! +!----------------------------------------------------------------------- + subroutine li_setup_wachspress_vertex_to_cell_weights(meshPool) + use mpas_geometry_utils + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout), target :: meshPool !< Input: mesh object + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pool pointers + real(kind=RKIND), dimension(:,:), pointer :: wachspressWeightVertex + real(kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell + real(kind=RKIND), dimension(:), pointer :: xVertex, yVertex, zVertex + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: verticesOnCell + integer, pointer :: nCells, maxEdges + integer :: iCell, iVertex, v + integer :: nVerticesOnThisCell + real(kind=RKIND), dimension(:,:), allocatable :: vertexCoordsOnCell + type (mpas_pool_type), pointer :: meshPoolPointer + + ! Get pool stuff + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) + call mpas_pool_get_array(meshPool, 'wachspressWeightVertex', wachspressWeightVertex) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + + meshPoolPointer => meshPool ! mpas_wachspress_coordinates expected a pointer to the mesh pool instead of just the mesh pool itself + + allocate(vertexCoordsOnCell(3, maxEdges)) + + do iCell = 1, nCells + nVerticesOnThisCell = nEdgesOnCell(iCell) + do v = 1, nVerticesOnThisCell + iVertex = verticesOnCell(v, iCell) + vertexCoordsOnCell(:, v) = (/ xVertex(iVertex), yVertex(iVertex), zVertex(iVertex) /) + enddo + wachspressWeightVertex(1:nVerticesOnThisCell, iCell) = mpas_wachspress_coordinates( & + nVerticesOnThisCell, vertexCoordsOnCell(:, 1:nVerticesOnThisCell), & + (/ xCell(iCell), yCell(iCell), zCell(iCell) /), meshPoolPointer) + end do + + deallocate(vertexCoordsOnCell) + !-------------------------------------------------------------------- + end subroutine li_setup_wachspress_vertex_to_cell_weights + + +!*********************************************************************** +! +! routine li_interpolate_vertex_to_cell_2d +! +!> \brief Interpolates from vertices to cell centers using Wachspress functions +!> \author Matt Hoffman +!> \date 29 Aug 2016 +!> \details +!> This routine interpolated from vertices to cell center values using +!> Wachspress functions in MPAS operators. +! +!----------------------------------------------------------------------- + subroutine li_interpolate_vertex_to_cell_2d(meshPool, vertexValue, cellValue) + use mpas_geometry_utils + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh object + + real(kind=RKIND), dimension(:,:), pointer, intent(in) :: vertexValue !< value on vertices + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real(kind=RKIND), dimension(:,:), pointer, intent(in) :: cellValue !< value on cells + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + ! Pool pointers + real(kind=RKIND), dimension(:,:), pointer :: wachspressWeightVertex + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: verticesOnCell + integer, pointer :: nCells, maxEdges + integer :: iCell, iVertex, v + integer :: nVerticesOnThisCell + + ! Get pool stuff + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) + call mpas_pool_get_array(meshPool, 'wachspressWeightVertex', wachspressWeightVertex) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + + cellValue(:,:) = 0.0_RKIND + do iCell = 1, nCells + nVerticesOnThisCell = nEdgesOnCell(iCell) + do v = 1, nVerticesOnThisCell + iVertex = verticesOnCell(v, iCell) + cellValue(:, iCell) = cellValue(:, iCell) + wachspressWeightVertex(v, iCell) * vertexValue(:, iVertex) + enddo + end do + + !-------------------------------------------------------------------- + end subroutine li_interpolate_vertex_to_cell_2d + + +!*********************************************************************** +! +! subroutine li_cells_to_vertices_1dfield_using_kiteAreas +! +!> \brief Converts a 1d scalar field from cells to vertices +!> \author Matt Hoffman +!> \date 21 May 2012 +!> \details +!> This routine converts a 1d scalar field from cells to vertices. +!> It will give garbage values on obtuse triangles! But it does work +!> on periodic meshes. +!> TODO: It would be more efficient to calculate the weights once on init and then only +!> perform the interp. in this routine. +!----------------------------------------------------------------------- + subroutine li_cells_to_vertices_1dfield_using_kiteAreas(meshPool, fieldCells, fieldVertices) + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + real (kind=RKIND), dimension(:), intent(in) :: & + fieldCells !< Input: field on cells + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(out) :: & + fieldVertices !< Input: field on vertices + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), pointer :: kiteAreasOnVertex + integer, dimension(:,:), pointer :: cellsOnVertex + integer, pointer :: nVertices, vertexDegree + integer :: iCell, icell2, iVertex, cellIndex + real (kind=RKIND) :: fVertexAccum, baryweight, weightAccum + + ! Get needed items from mesh pool + call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) + + call mpas_pool_get_array(meshPool, 'kiteAreasOnVertex', kiteAreasOnVertex) + call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) + + ! Calculate h on vertices using barycentric interpolation + do iVertex = 1, nVertices ! Loop over vertices + fVertexAccum = 0.0_RKIND + weightAccum = 0.0_RKIND + ! Loop over cells on this vertex + do iCell = 1, vertexDegree + cellIndex = cellsOnVertex(iCell, iVertex) + baryweight = 0.0_RKIND + do iCell2 = 1, vertexDegree + if (iCell2 /= icell) baryweight = baryweight + 0.5 * kiteAreasOnVertex(iCell2, iVertex) + enddo + fVertexAccum = fVertexAccum + baryweight * fieldCells(cellIndex) ! add the contribution from this cell's kite + weightAccum = weightAccum + kiteAreasOnVertex(iCell, iVertex) ! This doesn't match areaTriangle for obtuse triangles!!! + enddo + fieldVertices(iVertex) = fVertexAccum / weightAccum ! I assume this should never be 0... + enddo + + end subroutine li_cells_to_vertices_1dfield_using_kiteAreas + + +!*********************************************************************** +! +! subroutine li_calculate_layerThickness +! +!> \brief Calculates the thickness of each layer, given the total thickness +!> \author William Lipscomb +!> \date 16 Feb 2016 +!> \details +!> This routine calculates layerThickness in each cell and column, +!> given thickness in each cell. The calculation is very simple, but is +!> packaged in a subroutine to ensure that it is done the same way +!> in different parts of the code. +!----------------------------------------------------------------------- + + subroutine li_calculate_layerThickness(meshPool, thickness, layerThickness) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh object + + real(kind=RKIND), dimension(:), intent(in) :: & + thickness !< Input: ice thickness + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real(kind=RKIND), dimension(:,:), intent(out) :: & + layerThickness !< Output: thickness of each layer + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer, pointer :: & + nCells, & ! number of cells + nVertLevels ! number of vertical layers + + real (kind=RKIND), dimension(:), pointer :: & + layerThicknessFractions ! fractional thickness in each layer + + integer :: iCell, k + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'layerThicknessFractions', layerThicknessFractions) + + do iCell = 1, nCells + do k = 1, nVertLevels + layerThickness(k,iCell) = thickness(iCell) * layerThicknessFractions(k) + enddo + enddo + + end subroutine li_calculate_layerThickness + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine li_compute_gradient_2d +! +!> \brief compute the gradient of a 2D scalar field +!> \author Matt Hoffman, William Lipscomb +!> \date April 2015, Feb 2018 +!> \details +!> This routine computes the x and y coordinates of the gradient of a +!> 2D scalar field. The gradient is located at cell centers. +!> Modified from a version in sea ice incremental remapping. +! +!----------------------------------------------------------------------- + + subroutine li_compute_gradient_2d(& + meshPool, & + field, & + xGrad, yGrad, & + err) + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh object + + real(kind=RKIND), dimension(:), intent(in) :: & + field !< Input: scalar field defined at cell centers + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real(kind=RKIND), dimension(:), intent(out) :: & + xGrad, & !< Output: gradient in x-direction + yGrad !< Output: gradient in y-direction + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer, pointer :: & + nCells, & !< number of cells + maxEdges !< max number of edges per cell + + integer, dimension(:), pointer :: & + nEdgesOnCell !< number of edges per cell + + integer, dimension(:,:), pointer :: & + edgesOnCell, & !< index for each edge of a given cell + cellsOnCell, & !< index for each cell neighbor of a given cell + cellsOnEdge !< index for each cell neighbor of a given edge + + real(kind=RKIND), dimension(:), pointer :: & + dcEdge !< distance between the 2 cell centers on each side of an edge + + real(kind=RKIND), dimension(:,:,:), pointer :: & + coeffs_reconstruct !< coefficients for reconstructing the gradient at a cell center, + !< given normal components on edges + + integer :: iCell, iEdge, iEdgeOnCell, iCellNeighbor + + real(kind=RKIND) :: & + signGradient ! = 1 or -1, depending on which direction is taken as positive at a given edge + + real(kind=RKIND), dimension(:), allocatable :: & + normalGrad ! normal components of the gradient, defined on cell edges. For one cell. + + err = 0 + + ! get needed variables + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'coeffs_reconstruct', coeffs_reconstruct) + + ! find dimensions and allocate arrays + allocate(normalGrad(maxEdges)) + + ! initialize the gradient + xGrad(:) = 0.0_RKIND + yGrad(:) = 0.0_RKIND + + ! loop over cells + do iCell = 1, nCells + + ! initialize normal gradient components on edges of this cell + normalGrad(:) = 0.0_RKIND + + ! loop over edges of this cell + do iEdgeOnCell = 1, nEdgesOnCell(iCell) + + iCellNeighbor = cellsOnCell(iEdgeOnCell, iCell) + + ! compute the normal component of the gradient on this edge + if (iCellNeighbor >= 1 .and. iCellNeighbor <= nCells) then ! there is a cell neighbor on this edge + iEdge = edgesOnCell(iEdgeOnCell,iCell) + if (iCell == cellsOnEdge(1,iEdge)) then + signGradient = 1.0_RKIND + else + signGradient = -1.0_RKIND + end if + normalGrad(iEdgeOnCell) = signGradient * (field(iCellNeighbor) - field(iCell)) / dcEdge(iEdge) + else ! there is no cell neighbor on this edge + ! set gradient component = 0 + normalGrad(iEdgeOnCell) = 0.0_RKIND + endif + + ! add the contribution of this normal component to the reconstructed + ! gradient at the cell center (in global x/y/z coordinates) + xGrad(iCell) = xGrad(iCell) + coeffs_reconstruct(1,iEdgeOnCell,iCell) * normalGrad(iEdgeOnCell) + yGrad(iCell) = yGrad(iCell) + coeffs_reconstruct(2,iEdgeOnCell,iCell) * normalGrad(iEdgeOnCell) + enddo ! iEdgeOnCell + + enddo ! iCell + + ! cleanup + deallocate(normalGrad) + + end subroutine li_compute_gradient_2d + + +!*********************************************************************** +!*********************************************************************** +! Private subroutines: +!*********************************************************************** +!*********************************************************************** + + + +end module li_setup diff --git a/src/core_ocean/.gitignore b/src/core_ocean/.gitignore index 91183f2924..e12616ecd7 100644 --- a/src/core_ocean/.gitignore +++ b/src/core_ocean/.gitignore @@ -1,6 +1,8 @@ # Ignore all cvmix code. cvmix .cvmix_all +BGC +.BGC_all .*.zip # Ignore processed registry files. diff --git a/src/core_ocean/Makefile b/src/core_ocean/Makefile index b5a10764ce..24ae631991 100644 --- a/src/core_ocean/Makefile +++ b/src/core_ocean/Makefile @@ -2,11 +2,12 @@ OCEAN_SHARED_INCLUDES = -I$(PWD)/../framework -I$(PWD)/../external/esmf_time_f90 -I$(PWD)/../operators -OCEAN_SHARED_INCLUDES += -I$(PWD)/shared -I$(PWD)/analysis_members -I$(PWD)/cvmix -I$(PWD)/mode_forward -I$(PWD)/mode_analysis +OCEAN_SHARED_INCLUDES += -I$(PWD)/BGC -I$(PWD)/shared -I$(PWD)/analysis_members -I$(PWD)/cvmix -I$(PWD)/mode_forward -I$(PWD)/mode_analysis -I$(PWD)/mode_init -all: shared libcvmix analysis_members +all: shared libcvmix analysis_members libBGC (cd mode_forward; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(OCEAN_SHARED_INCLUDES)" all ) (cd mode_analysis; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(OCEAN_SHARED_INCLUDES)" all ) + (cd mode_init; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(OCEAN_SHARED_INCLUDES)" all ) (cd driver; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(OCEAN_SHARED_INCLUDES)" all ) if [ -e libdycore.a ]; then \ ($(RM) libdycore.a) \ @@ -21,9 +22,11 @@ core_input_gen: (cd default_inputs; $(NL_GEN) ../Registry_processed.xml namelist.ocean ) (cd default_inputs; $(NL_GEN) ../Registry_processed.xml namelist.ocean.forward mode=forward ) (cd default_inputs; $(NL_GEN) ../Registry_processed.xml namelist.ocean.analysis mode=analysis ) + (cd default_inputs; $(NL_GEN) ../Registry_processed.xml namelist.ocean.init mode=init ) (cd default_inputs; $(ST_GEN) ../Registry_processed.xml streams.ocean stream_list.ocean. mutable ) (cd default_inputs; $(ST_GEN) ../Registry_processed.xml streams.ocean.forward stream_list.ocean.forward. mutable mode=forward ) (cd default_inputs; $(ST_GEN) ../Registry_processed.xml streams.ocean.analysis stream_list.ocean.analysis. mutable mode=analysis ) + (cd default_inputs; $(ST_GEN) ../Registry_processed.xml streams.ocean.init stream_list.ocean.init. mutable mode=init ) gen_includes: $(CPP) $(CPPFLAGS) $(CPPINCLUDES) Registry.xml > Registry_processed.xml @@ -33,11 +36,22 @@ gen_includes: post_build: if [ ! -e $(ROOT_DIR)/default_inputs ]; then mkdir $(ROOT_DIR)/default_inputs; fi cp default_inputs/* $(ROOT_DIR)/default_inputs/. - ( cd $(ROOT_DIR)/default_inputs; for FILE in `ls -1`; do if [ ! -e ../$$FILE ]; then cp $$FILE ../.; fi; done ) + ( cp $(ROOT_DIR)/default_inputs/namelist.ocean $(ROOT_DIR)/namelist.ocean ) + ( cp $(ROOT_DIR)/default_inputs/namelist.ocean.forward $(ROOT_DIR)/namelist.ocean.forward ) + ( cp $(ROOT_DIR)/default_inputs/namelist.ocean.analysis $(ROOT_DIR)/namelist.ocean.analysis ) + ( cp $(ROOT_DIR)/default_inputs/namelist.ocean.init $(ROOT_DIR)/namelist.ocean.init ) + ( cp $(ROOT_DIR)/default_inputs/streams.ocean $(ROOT_DIR)/streams.ocean ) + ( cp $(ROOT_DIR)/default_inputs/streams.ocean.forward $(ROOT_DIR)/streams.ocean.forward ) + ( cp $(ROOT_DIR)/default_inputs/streams.ocean.analysis $(ROOT_DIR)/streams.ocean.analysis ) + ( cp $(ROOT_DIR)/default_inputs/streams.ocean.init $(ROOT_DIR)/streams.ocean.init ) cvmix_source: get_cvmix.sh - (chmod a+x get_cvmix.sh; ./get_cvmix.sh) - (cd cvmix; make clean) + (/bin/bash ./get_cvmix.sh) + (cd cvmix) + +BGC_source: get_BGC.sh + (/bin/bash ./get_BGC.sh) + (cd BGC) libcvmix: cvmix_source if [ -d cvmix ]; then \ @@ -46,7 +60,14 @@ libcvmix: cvmix_source (exit 1) \ fi -shared: libcvmix +libBGC: BGC_source + if [ -d BGC ]; then \ + (cd BGC; make all FC="$(FC)" FCFLAGS="$(FFLAGS)" FINCLUDES="$(FINCLUDES)") \ + else \ + (exit 1) \ + fi + +shared: libcvmix libBGC (cd shared; $(MAKE) FCINCLUDES="$(FCINCLUDES) $(OCEAN_SHARED_INCLUDES)") analysis_members: libcvmix shared @@ -56,8 +77,15 @@ clean: if [ -d cvmix ]; then \ (cd cvmix; make clean) \ fi + if [ -d inc ]; then \ + ($(RM) -r inc) \ + fi + if [ -d BGC ]; then \ + (cd BGC; make clean) \ + fi (cd mode_forward; $(MAKE) clean) (cd mode_analysis; $(MAKE) clean) + (cd mode_init; $(MAKE) clean) (cd driver; $(MAKE) clean) (cd analysis_members; $(MAKE) clean) (cd shared; $(MAKE) clean) diff --git a/src/core_ocean/Registry.xml b/src/core_ocean/Registry.xml index ba2c65f36d..2f3efb48b8 100644 --- a/src/core_ocean/Registry.xml +++ b/src/core_ocean/Registry.xml @@ -1,13 +1,19 @@ - + + + @@ -20,12 +26,18 @@ + + @@ -38,21 +50,60 @@ - + + + + + + + + + + + - + @@ -65,7 +116,7 @@ description="Path to the filename for restart timestamps to be read and written from." possible_values="Path to a file." /> - @@ -90,15 +141,15 @@ - + + + + + + + + + + + + + + + + + + + + + + @@ -151,10 +253,6 @@ description="Maximum thickness allowed. This is a factor times the resting thickness, i.e., maximum thickness = config_max_thickness_factor*$h^{rest}$." possible_values="any positive real value, but typically 2-4." /> - - + - - @@ -249,6 +343,10 @@ description="Coefficient for horizontal biharmonic operator on momentum." possible_values="any positive real" /> + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -417,7 +455,7 @@ description="Prandtl number to be used within the CVMix parameterization suite" possible_values="Any non-negative real value." /> - @@ -453,6 +491,14 @@ description="If true, shear-based mixing is computed using CVMix" possible_values="True or False" /> + + + + + - - + - - - - + + - + + + - + + + + + + - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + - - + + + + + + + + + + + - + - - + + + - + + + + + + + + + + + + + + + + + + - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -921,26 +1461,44 @@ - + + + + + + + - + + + + + + + + + + + + - - + @@ -962,11 +1520,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + - - - - - - - - - - - - - - - - - - - - + - - - + + @@ -1093,20 +1685,24 @@ - - - - - + + + + + + + + + - - - - + - + + + + + + + - - - - - @@ -1174,17 +1767,42 @@ description="layer thickness" /> + + + + + + + + + - - + - + @@ -1204,6 +1822,12 @@ description="baroclinic velocity, used in split-explicit time-stepping" packages="splitTimeIntegrator" /> + + + + + - - @@ -1437,6 +2061,10 @@ + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + @@ -1705,27 +2370,27 @@ description="component of horizontal velocity used to transport mass and tracers in the northward direction" packages="forwardMode;analysisMode" /> - - - - - - @@ -1777,7 +2442,7 @@ + /> - + + - - - - - - - - - - - - - - + - - - + + + + + + + + + - - - - - - - - - - + + + - - @@ -2005,30 +2661,34 @@ constituent fields, depending on the forcing options selected. ********************************************************************* --> - - - - - - - - + + + + + + + - + + /> - - - - - + + - - - - - - @@ -2153,15 +2789,12 @@ packages="forwardMode;analysisMode" /> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + - - @@ -2218,90 +2957,90 @@ type="real" dimensions="nVertLevels nCells Time" units="kg m^{-3}" description="Density computed by displacing SST and SSS to every vertical layer within the column" /> - - - - - - - - - - - - - - - - @@ -2396,7 +3135,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#include "mode_init/Registry.xml" +#include "tracer_groups/Registry_tracers.xml" #include "analysis_members/Registry_analysis_members.xml" diff --git a/src/core_ocean/analysis_members/Makefile b/src/core_ocean/analysis_members/Makefile index 1b6097cabb..a9f301c380 100644 --- a/src/core_ocean/analysis_members/Makefile +++ b/src/core_ocean/analysis_members/Makefile @@ -10,7 +10,22 @@ MEMBERS = mpas_ocn_global_stats.o \ mpas_ocn_meridional_heat_transport.o \ mpas_ocn_test_compute_interval.o \ mpas_ocn_high_frequency_output.o \ - mpas_ocn_zonal_mean.o + mpas_ocn_zonal_mean.o \ + mpas_ocn_lagrangian_particle_tracking_interpolations.o \ + mpas_ocn_particle_list.o \ + mpas_ocn_lagrangian_particle_tracking_reset.o \ + mpas_ocn_lagrangian_particle_tracking.o \ + mpas_ocn_eliassen_palm.o \ + mpas_ocn_time_filters.o \ + mpas_ocn_mixed_layer_depths.o \ + mpas_ocn_pointwise_stats.o \ + mpas_ocn_debug_diagnostics.o \ + mpas_ocn_time_series_stats.o \ + mpas_ocn_regional_stats.o \ + mpas_ocn_rpn_calculator.o \ + mpas_ocn_transect_transport.o\ + mpas_ocn_eddy_product_variables.o\ + mpas_ocn_moc_streamfunction.o all: $(OBJS) @@ -18,6 +33,12 @@ mpas_ocn_analysis_driver.o: $(MEMBERS) mpas_ocn_okubo_weiss.o: mpas_ocn_okubo_weiss_eigenvalues.o +mpas_ocn_particle_list.o: + +mpas_ocn_lagrangian_particle_tracking_reset.o: + +mpas_ocn_lagrangian_particle_tracking.o: mpas_ocn_particle_list.o mpas_ocn_lagrangian_particle_tracking_interpolations.o mpas_ocn_lagrangian_particle_tracking_reset.o + clean: $(RM) *.o *.i *.mod *.f90 @@ -25,9 +46,9 @@ clean: $(RM) $@ $*.mod ifeq "$(GEN_F90)" "true" $(CPP) $(CPPFLAGS) $(CPPINCLUDES) $< > $*.f90 - $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) + $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) else - $(FC) $(CPPFLAGS) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) + $(FC) $(CPPFLAGS) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) endif .c.o: diff --git a/src/core_ocean/analysis_members/Registry_TEMPLATE.xml b/src/core_ocean/analysis_members/Registry_TEMPLATE.xml index 7781514365..463168db49 100644 --- a/src/core_ocean/analysis_members/Registry_TEMPLATE.xml +++ b/src/core_ocean/analysis_members/Registry_TEMPLATE.xml @@ -1,11 +1,11 @@ - @@ -46,12 +46,12 @@ filename_template="analysis_members/temPlate.$Y-$M-$D.nc" filename_interval="01-00-00_00:00:00" output_interval="00-00-01_00:00:00" - reference_time="0000-01-01_00:00:00" + reference_time="0001-01-01_00:00:00" packages="temPlateAMPKG" clobber_mode="truncate" runtime_format="single_file"> - - - + + + diff --git a/src/core_ocean/analysis_members/Registry_analysis_members.xml b/src/core_ocean/analysis_members/Registry_analysis_members.xml index 8359f533a9..3e7aecf297 100644 --- a/src/core_ocean/analysis_members/Registry_analysis_members.xml +++ b/src/core_ocean/analysis_members/Registry_analysis_members.xml @@ -7,3 +7,23 @@ #include "Registry_meridional_heat_transport.xml" #include "Registry_test_compute_interval.xml" #include "Registry_high_frequency_output.xml" +#include "Registry_time_filters.xml" +#include "Registry_lagrangian_particle_tracking.xml" +#include "Registry_eliassen_palm.xml" +#include "Registry_mixed_layer_depths.xml" +#include "Registry_regional_stats_daily.xml" +#include "Registry_regional_stats_weekly.xml" +#include "Registry_regional_stats_monthly.xml" +#include "Registry_regional_stats_custom.xml" +#include "Registry_regional_stats.xml" +#include "Registry_time_series_stats_daily.xml" +#include "Registry_time_series_stats_monthly.xml" +#include "Registry_time_series_stats_climatology.xml" +#include "Registry_time_series_stats_custom.xml" +#include "Registry_time_series_stats.xml" +#include "Registry_pointwise_stats.xml" +#include "Registry_debug_diagnostics.xml" +#include "Registry_rpn_calculator.xml" +#include "Registry_transect_transport.xml" +#include "Registry_eddy_product_variables.xml" +#include "Registry_moc_streamfunction.xml" diff --git a/src/core_ocean/analysis_members/Registry_debug_diagnostics.xml b/src/core_ocean/analysis_members/Registry_debug_diagnostics.xml new file mode 100644 index 0000000000..5c2b61b8f1 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_debug_diagnostics.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_eddy_product_variables.xml b/src/core_ocean/analysis_members/Registry_eddy_product_variables.xml new file mode 100644 index 0000000000..dfbd38e643 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_eddy_product_variables.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_eliassen_palm.xml b/src/core_ocean/analysis_members/Registry_eliassen_palm.xml new file mode 100644 index 0000000000..14d7720cd1 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_eliassen_palm.xml @@ -0,0 +1,687 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_global_stats.xml b/src/core_ocean/analysis_members/Registry_global_stats.xml index 2332939a3a..28e236e098 100644 --- a/src/core_ocean/analysis_members/Registry_global_stats.xml +++ b/src/core_ocean/analysis_members/Registry_global_stats.xml @@ -23,7 +23,7 @@ description="subdirectory to write eddy census text files" possible_values="any valid directory name" /> - @@ -32,6 +32,34 @@ + + + + + + + - - + + + + + + + + + + + + + + @@ -114,7 +184,7 @@ - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -222,7 +376,7 @@ - - + + + + + + + + + + + + + + @@ -276,7 +472,7 @@ - - + + + + + + + + + + + + + + @@ -306,10 +544,10 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -360,10 +640,10 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_high_frequency_output.xml b/src/core_ocean/analysis_members/Registry_high_frequency_output.xml index fa992c2c54..719c53b421 100644 --- a/src/core_ocean/analysis_members/Registry_high_frequency_output.xml +++ b/src/core_ocean/analysis_members/Registry_high_frequency_output.xml @@ -7,7 +7,7 @@ description="Timestamp determining how often analysis member computation should be performed." possible_values="Any valid time stamp, 'dt', or 'output_interval'" /> - @@ -24,20 +24,271 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/src/core_ocean/analysis_members/Registry_lagrangian_particle_tracking.xml b/src/core_ocean/analysis_members/Registry_lagrangian_particle_tracking.xml new file mode 100644 index 0000000000..21ba816f5d --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_lagrangian_particle_tracking.xml @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_layer_volume_weighted_averages.xml b/src/core_ocean/analysis_members/Registry_layer_volume_weighted_averages.xml index 4a3ba39a9e..7dda8ff9d8 100644 --- a/src/core_ocean/analysis_members/Registry_layer_volume_weighted_averages.xml +++ b/src/core_ocean/analysis_members/Registry_layer_volume_weighted_averages.xml @@ -1,5 +1,5 @@ - - @@ -78,6 +78,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -354,6 +552,7 @@ - @@ -31,35 +31,52 @@ description="maximum bin boundary value. If set to -1.0e34, the maximum value in the domain is found." possible_values="Any real number." /> + - + + + + @@ -70,6 +87,14 @@ + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_mixed_layer_depths.xml b/src/core_ocean/analysis_members/Registry_mixed_layer_depths.xml new file mode 100644 index 0000000000..a52315a2f3 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_mixed_layer_depths.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_moc_streamfunction.xml b/src/core_ocean/analysis_members/Registry_moc_streamfunction.xml new file mode 100644 index 0000000000..bff0cabc8c --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_moc_streamfunction.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_okubo_weiss.xml b/src/core_ocean/analysis_members/Registry_okubo_weiss.xml index 951136698d..b78ddabe3a 100644 --- a/src/core_ocean/analysis_members/Registry_okubo_weiss.xml +++ b/src/core_ocean/analysis_members/Registry_okubo_weiss.xml @@ -1,87 +1,87 @@ - - - - - - - - - - - + + + + + + + + + + @@ -91,72 +91,73 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_regional_stats.xml b/src/core_ocean/analysis_members/Registry_regional_stats.xml new file mode 100644 index 0000000000..681db44504 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_regional_stats.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_regional_stats_custom.xml b/src/core_ocean/analysis_members/Registry_regional_stats_custom.xml new file mode 100644 index 0000000000..4be5e27d30 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_regional_stats_custom.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_regional_stats_daily.xml b/src/core_ocean/analysis_members/Registry_regional_stats_daily.xml new file mode 100644 index 0000000000..3133988014 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_regional_stats_daily.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_regional_stats_monthly.xml b/src/core_ocean/analysis_members/Registry_regional_stats_monthly.xml new file mode 100644 index 0000000000..00c9ceaf05 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_regional_stats_monthly.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_regional_stats_weekly.xml b/src/core_ocean/analysis_members/Registry_regional_stats_weekly.xml new file mode 100644 index 0000000000..b54ac57866 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_regional_stats_weekly.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_rpn_calculator.xml b/src/core_ocean/analysis_members/Registry_rpn_calculator.xml new file mode 100644 index 0000000000..d9d45e4101 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_rpn_calculator.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_surface_area_weighted_averages.xml b/src/core_ocean/analysis_members/Registry_surface_area_weighted_averages.xml index 77112b2d73..3c035f2853 100644 --- a/src/core_ocean/analysis_members/Registry_surface_area_weighted_averages.xml +++ b/src/core_ocean/analysis_members/Registry_surface_area_weighted_averages.xml @@ -8,24 +8,24 @@ - @@ -238,15 +238,15 @@ - + /> + /> + /> @@ -348,6 +348,7 @@ - @@ -37,8 +37,8 @@ packages="testComputeIntervalAMPKG" clobber_mode="truncate" runtime_format="single_file"> - - + + diff --git a/src/core_ocean/analysis_members/Registry_time_filters.xml b/src/core_ocean/analysis_members/Registry_time_filters.xml new file mode 100644 index 0000000000..afdab8acee --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_time_filters.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_time_series_stats.xml b/src/core_ocean/analysis_members/Registry_time_series_stats.xml new file mode 100644 index 0000000000..05bea61856 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_time_series_stats.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_time_series_stats_climatology.xml b/src/core_ocean/analysis_members/Registry_time_series_stats_climatology.xml new file mode 100644 index 0000000000..af7a332768 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_time_series_stats_climatology.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_time_series_stats_custom.xml b/src/core_ocean/analysis_members/Registry_time_series_stats_custom.xml new file mode 100644 index 0000000000..38ef3c0ea6 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_time_series_stats_custom.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_time_series_stats_daily.xml b/src/core_ocean/analysis_members/Registry_time_series_stats_daily.xml new file mode 100644 index 0000000000..cb00042228 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_time_series_stats_daily.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_time_series_stats_monthly.xml b/src/core_ocean/analysis_members/Registry_time_series_stats_monthly.xml new file mode 100644 index 0000000000..b269669c05 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_time_series_stats_monthly.xml @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_transect_transport.xml b/src/core_ocean/analysis_members/Registry_transect_transport.xml new file mode 100644 index 0000000000..7c733532d9 --- /dev/null +++ b/src/core_ocean/analysis_members/Registry_transect_transport.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_water_mass_census.xml b/src/core_ocean/analysis_members/Registry_water_mass_census.xml index 277da0089d..223b0d54c3 100644 --- a/src/core_ocean/analysis_members/Registry_water_mass_census.xml +++ b/src/core_ocean/analysis_members/Registry_water_mass_census.xml @@ -17,40 +17,48 @@ - + + @@ -87,9 +95,40 @@ units="kg m^{-3}" description="volume-weighted vertical position of each (T,S) bin" /> + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/analysis_members/Registry_zonal_mean.xml b/src/core_ocean/analysis_members/Registry_zonal_mean.xml index 8bb0dc3538..250923cd78 100644 --- a/src/core_ocean/analysis_members/Registry_zonal_mean.xml +++ b/src/core_ocean/analysis_members/Registry_zonal_mean.xml @@ -15,7 +15,7 @@ description="Interval that determines frequency of computation for the zonal mean analysis member." possible_values="Any valid time stamp, 'dt', or 'output_interval'" /> - @@ -57,7 +57,7 @@ description="Zonal mean of component of horizontal velocity in the northward direction" /> - In order to add a new analysis member, do the following: !> 1. Copy these to your new analysis member name: !> cp mpas_ocn_TEMPLATE.F mpas_ocn_your_new_name.F -!> cp Registry_ocn_TEMPLATE.xml Registry_ocn_your_new_name.xml +!> cp Registry_TEMPLATE.xml Registry_your_new_name.xml !> !> 2. In those two new files, replace the following text: -!> tempLate, TEM_PLATE, FILL_IN_AUTHOR, FILL_IN_DATE -!> Typically tempLate uses camel case (variable names), like yourNewName, +!> temPlate, TEM_PLATE, FILL_IN_AUTHOR, FILL_IN_DATE +!> Typically temPlate uses camel case (variable names), like yourNewName, !> while TEM_PLATE uses underscores (subroutine names), like your_new_name. -!> note: do not replace 'filename_template' in Registry_ocn_yourNewName.xml -!> +!> note: do not replace 'filename_template' in Registry_yourNewName.xml +!> !> 3. Add a #include line for your registry to !> Registry_analysis_members.xml !> @@ -36,10 +36,10 @@ !> - Adding a compute if test can subroutine call !> - Adding a restart if test can subroutine call !> - Adding a finalize if test can subroutine call -!> +!> !> 5. In src/core_ocean/analysis_members/Makefile, add your !> new analysis member to the list of members. See another analysis member -!> in that file for an example. +!> in that file for an example. !> NOTE: If your analysis member depends on other files, add a dependency !> line for the member and list them there. See okubo weiss for an example. !> @@ -186,7 +186,6 @@ subroutine ocn_compute_TEM_PLATE(domain, timeLevel, err)!{{{ type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: scratchPool type (mpas_pool_type), pointer :: diagnosticsPool - type (mpas_pool_type), pointer :: temPlateAM ! Here are some example variables which may be needed for your analysis member integer, pointer :: nVertLevels, nCellsSolve, nEdgesSolve, nVerticesSolve, num_tracers @@ -237,7 +236,7 @@ subroutine ocn_compute_TEM_PLATE(domain, timeLevel, err)!{{{ end do ! mpi gather/scatter calls may be placed here. - ! Here are some examples. See mpas_oac_global_stats.F for further details. + ! Here are some examples. See mpas_ocn_global_stats.F for further details. ! call mpas_dmpar_sum_real_array(dminfo, nVariables, sumSquares(1:nVariables), reductions(1:nVariables)) ! call mpas_dmpar_min_real_array(dminfo, nMins, mins(1:nMins), reductions(1:nMins)) ! call mpas_dmpar_max_real_array(dminfo, nMaxes, maxes(1:nMaxes), reductions(1:nMaxes)) diff --git a/src/core_ocean/analysis_members/mpas_ocn_analysis_driver.F b/src/core_ocean/analysis_members/mpas_ocn_analysis_driver.F index c225b126bb..c17407cec8 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_analysis_driver.F +++ b/src/core_ocean/analysis_members/mpas_ocn_analysis_driver.F @@ -36,6 +36,18 @@ module ocn_analysis_driver use ocn_meridional_heat_transport use ocn_test_compute_interval use ocn_high_frequency_output + use ocn_time_filters + use ocn_lagrangian_particle_tracking + use ocn_eliassen_palm + use ocn_mixed_layer_depths + use ocn_time_series_stats + use ocn_pointwise_stats + use ocn_debug_diagnostics + use ocn_regional_stats + use ocn_rpn_calculator + use ocn_transect_transport + use ocn_eddy_product_variables + use ocn_moc_streamfunction ! use ocn_TEM_PLATE implicit none @@ -55,6 +67,7 @@ module ocn_analysis_driver !-------------------------------------------------------------------- public :: ocn_analysis_setup_packages, & + ocn_analysis_bootstrap, & ocn_analysis_init, & ocn_analysis_compute_startup, & ocn_analysis_compute, & @@ -69,13 +82,25 @@ module ocn_analysis_driver !-------------------------------------------------------------------- + character (len=*), parameter :: initReadTimerPrefix = 'init_read_' character (len=*), parameter :: initTimerPrefix = 'init_' character (len=*), parameter :: computeTimerPrefix = 'compute_' + character (len=*), parameter :: computeStartupTimerPrefix = 'compute_startup_' character (len=*), parameter :: writeTimerPrefix = 'write_' character (len=*), parameter :: alarmTimerPrefix = 'reset_alarm_' character (len=*), parameter :: restartTimerPrefix = 'restart_' character (len=*), parameter :: finalizeTimerPrefix = 'finalize_' character (len=*), parameter :: computeAlarmSuffix = 'CMPALRM' + + character (len=StrKIND), parameter :: timeSeriesDailyTAG = 'Daily' + character (len=StrKIND), parameter :: timeSeriesMonthlyTAG = 'Monthly' + character (len=StrKIND), parameter :: timeSeriesClimatologyTAG = 'Climatology' + character (len=StrKIND), parameter :: timeSeriesCustomTAG = 'Custom' + character (len=StrKIND), parameter :: regionalStatsDailyTAG = 'Daily' + character (len=StrKIND), parameter :: regionalStatsMonthlyTAG = 'Monthly' + character (len=StrKIND), parameter :: regionalStatsWeeklyTAG = 'Weekly' + character (len=StrKIND), parameter :: regionalStatsCustomTAG = 'Custom' + type (mpas_pool_type), pointer :: analysisMemberList !*********************************************************************** @@ -95,7 +120,7 @@ module ocn_analysis_driver ! !----------------------------------------------------------------------- - subroutine ocn_analysis_setup_packages(configPool, packagePool, err)!{{{ + subroutine ocn_analysis_setup_packages(configPool, packagePool, iocontext, err)!{{{ !----------------------------------------------------------------- ! @@ -103,8 +128,9 @@ subroutine ocn_analysis_setup_packages(configPool, packagePool, err)!{{{ ! !----------------------------------------------------------------- - type (mpas_pool_type), intent(in) :: configPool - type (mpas_pool_type), intent(in) :: packagePool + type (mpas_pool_type), intent(inout) :: configPool + type (mpas_pool_type), intent(inout) :: packagePool + type (mpas_io_context_type), intent(inout) :: iocontext !----------------------------------------------------------------- ! @@ -138,7 +164,6 @@ subroutine ocn_analysis_setup_packages(configPool, packagePool, err)!{{{ call mpas_pool_create_pool(analysisMemberList) call mpas_pool_add_config(analysisMemberList, 'globalStats', 1) - call mpas_pool_add_config(analysisMemberList, 'testComputeInterval', 1) call mpas_pool_add_config(analysisMemberList, 'layerVolumeWeightedAverage', 1) call mpas_pool_add_config(analysisMemberList, 'meridionalHeatTransport', 1) call mpas_pool_add_config(analysisMemberList, 'okuboWeiss', 1) @@ -146,6 +171,24 @@ subroutine ocn_analysis_setup_packages(configPool, packagePool, err)!{{{ call mpas_pool_add_config(analysisMemberList, 'waterMassCensus', 1) call mpas_pool_add_config(analysisMemberList, 'zonalMean', 1) call mpas_pool_add_config(analysisMemberList, 'highFrequencyOutput', 1) + call mpas_pool_add_config(analysisMemberList, 'timeFilters', 1) + call mpas_pool_add_config(analysisMemberList, 'lagrPartTrack', 1) + call mpas_pool_add_config(analysisMemberList, 'eliassenPalm', 1) + call mpas_pool_add_config(analysisMemberList, 'mixedLayerDepths', 1) + call mpas_pool_add_config(analysisMemberList, 'rpnCalculator', 1) + call mpas_pool_add_config(analysisMemberList, 'timeSeriesStatsDaily', 1) + call mpas_pool_add_config(analysisMemberList, 'timeSeriesStatsMonthly', 1) + call mpas_pool_add_config(analysisMemberList, 'timeSeriesStatsClimatology', 1) + call mpas_pool_add_config(analysisMemberList, 'timeSeriesStatsCustom', 1) + call mpas_pool_add_config(analysisMemberList, 'regionalStatsDaily', 1) + call mpas_pool_add_config(analysisMemberList, 'regionalStatsWeekly', 1) + call mpas_pool_add_config(analysisMemberList, 'regionalStatsMonthly', 1) + call mpas_pool_add_config(analysisMemberList, 'regionalStatsCustom', 1) + call mpas_pool_add_config(analysisMemberList, 'pointwiseStats', 1) + call mpas_pool_add_config(analysisMemberList, 'debugDiagnostics', 1) + call mpas_pool_add_config(analysisMemberList, 'transectTransport', 1) + call mpas_pool_add_config(analysisMemberList, 'eddyProductVariables', 1) + call mpas_pool_add_config(analysisMemberList, 'mocStreamfunction', 1) ! call mpas_pool_add_config(analysisMemberList, 'temPlate', 1) ! DON'T EDIT BELOW HERE @@ -166,6 +209,169 @@ subroutine ocn_analysis_setup_packages(configPool, packagePool, err)!{{{ end subroutine ocn_analysis_setup_packages!}}} +!*********************************************************************** +! +! routine ocn_analysis_bootstrap +! +!> \brief Bootstrap analysis members (pre-init configuration) +!> \author Doug Jacobsen +!> \date 10/08/2015 +!> \details +!> This routine will read either a restart or an input stream for each analysis member. +!> The stream names that will be read are controlled via the analysis member's +!> - config_AM_${AM}_restart_stream +!> - config_AM_${AM}_input_stream +!> namelist options. +!> +!> If the AM doesn't specify either of these, it will be ignored. If the AM +!> specifies only the restart stream, it will only be read if the config_do_restart flag +!> for the model is set to true. If the AM specifies both, the restart_stream will be read if +!> config_do_restart is true, and the input_stream will be read if config_do_restart is false. +!> +!> After this call, alarms on both streams are reset. +!> +!> Additionally, if a bootstrap subroutine has been defined properly for the +!> analysis member, it will be called here. +! +!----------------------------------------------------------------------- + + subroutine ocn_analysis_bootstrap(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: err_tmp + + character (len=StrKIND) :: configName, alarmName, restartStreamName, inputStreamName, timerName + logical, pointer :: config_AM_enable, config_do_restart + character (len=StrKIND), pointer :: config_AM_restart_stream, config_AM_input_stream + integer :: nameLength + type (mpas_pool_iterator_type) :: poolItr + + logical :: streamFound + character (len=StrKIND) :: referenceTimeString, outputIntervalString + type (MPAS_Time_Type) :: referenceTime + type (MPAS_TimeInterval_type) :: alarmTimeStep + + integer :: poolErrorLevel + + err = 0 + + poolErrorLevel = mpas_pool_get_error_level() + call mpas_pool_set_error_level(MPAS_POOL_SILENT) + + call mpas_timer_start('analysis_bootstrap') + + call mpas_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) + + call mpas_pool_begin_iteration(analysisMemberList) + do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) + nameLength = len_trim(poolItr % memberName) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_enable' + + call mpas_pool_get_config(domain % configs, configName, config_AM_enable) + + if ( config_AM_enable ) then + timerName = trim(initReadTimerPrefix) // poolItr % memberName(1:nameLength) + call mpas_timer_start(timerName) + +#ifdef MPAS_DEBUG + call mpas_log_write( ' Bootstrapping AM ' // poolItr % memberName(1:nameLength)) +#endif + call ocn_bootstrap_analysis_members(domain, poolItr % memberName(1:nameLength), ierr=err) + + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_restart_stream' + nullify(config_AM_restart_stream) + call mpas_pool_get_config(domain % configs, configName, config_AM_restart_stream) + + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_input_stream' + nullify(config_AM_input_stream) + call mpas_pool_get_config(domain % configs, configName, config_AM_input_stream) + + ! Verify the restart stream exists + if ( associated(config_AM_restart_stream) ) then + if ( trim(config_AM_restart_stream) == 'none' ) then + ! If the stream is set to 'none' nullify the config, so it doesn't get read in + nullify(config_AM_restart_stream) + else if ( .not. mpas_stream_mgr_stream_exists(domain % streamManager, config_AM_restart_stream) ) then + call mpas_log_write('Stream named ''' // trim(config_AM_restart_stream) // & + ''' does not exist in config for analysis member ''' // & + trim(poolItr % memberName(1:nameLength)) // '''', MPAS_LOG_CRIT) + end if + end if + + ! Verify the input stream exists + if ( associated(config_AM_input_stream) ) then + if ( trim(config_AM_input_stream) == 'none' ) then + ! If the stream is set to 'none' nullify the config, so it doesn't get read in + nullify(config_AM_input_stream) + else if ( .not. mpas_stream_mgr_stream_exists(domain % streamManager, config_AM_input_stream) ) then + call mpas_log_write('Stream named ''' // trim(config_AM_input_stream) // & + ''' does not exist in config for analysis member ''' // & + trim(poolItr % memberName(1:nameLength)) // '''', MPAS_LOG_CRIT) + end if + end if + + ! Handle reading of streams that exist. + if ( associated(config_AM_restart_stream) .and. associated(config_AM_input_stream) ) then + + if ( config_do_restart ) then + call mpas_stream_mgr_read(domain % streamManager, streamID=config_AM_restart_stream, ierr=err) + else + call mpas_stream_mgr_read(domain % streamManager, streamID=config_AM_input_stream, ierr=err) + end if + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID=config_AM_restart_stream, & + direction=MPAS_STREAM_INPUT, ierr=err) + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID=config_AM_input_stream, & + direction=MPAS_STREAM_INPUT, ierr=err) + else if ( associated(config_AM_restart_stream) ) then + if ( config_do_restart ) then + call mpas_stream_mgr_read(domain % streamManager, streamID=config_AM_restart_stream, ierr=err) + end if + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID=config_AM_restart_stream, & + direction=MPAS_STREAM_INPUT, ierr=err) + else if ( associated(config_AM_input_stream) ) then + if ( .not. config_do_restart ) then + call mpas_stream_mgr_read(domain % streamManager, streamID=config_AM_input_stream, ierr=err) + end if + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID=config_AM_input_stream, & + direction=MPAS_STREAM_INPUT, ierr=err) + end if + call mpas_timer_stop(timerName) + end if + end do + + call mpas_timer_stop('analysis_bootstrap') + + call mpas_pool_set_error_level(poolErrorLevel) + + end subroutine ocn_analysis_bootstrap!}}} + !*********************************************************************** ! ! routine ocn_analysis_init @@ -213,18 +419,18 @@ subroutine ocn_analysis_init(domain, err)!{{{ character (len=StrKIND) :: configName, alarmName, streamName, timerName logical, pointer :: config_AM_enable - character (len=StrKIND), pointer :: config_AM_compute_interval, config_AM_stream_name + character (len=StrKIND), pointer :: config_AM_compute_interval, config_AM_output_stream, config_start_time integer :: nameLength type (mpas_pool_iterator_type) :: poolItr logical :: streamFound character (len=StrKIND) :: referenceTimeString, outputIntervalString - type (MPAS_Time_Type) :: referenceTime + type (MPAS_Time_Type) :: referenceTime type (MPAS_TimeInterval_type) :: alarmTimeStep err = 0 - call mpas_timer_start('analysis_init', .false.) + call mpas_timer_start('analysis_init') call mpas_pool_begin_iteration(analysisMemberList) do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) @@ -233,16 +439,19 @@ subroutine ocn_analysis_init(domain, err)!{{{ call mpas_pool_get_config(domain % configs, configName, config_AM_enable) if ( config_AM_enable ) then +#ifdef MPAS_DEBUG + call mpas_log_write( ' Initializing AM ' // poolItr % memberName(1:nameLength)) +#endif timerName = trim(initTimerPrefix) // poolItr % memberName(1:nameLength) - call mpas_timer_start(timerName, .false.) + call mpas_timer_start(timerName) call ocn_init_analysis_members(domain, poolItr % memberName, err_tmp) err = ior(err, err_tmp) configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_compute_interval' call mpas_pool_get_config(domain % configs, configName, config_AM_compute_interval) - configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' - call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_output_stream' + call mpas_pool_get_config(domain % configs, configName, config_AM_output_stream) if ( config_AM_compute_interval == 'dt' ) then alarmTimeStep = mpas_get_clock_timestep(domain % clock, err_tmp) @@ -250,30 +459,54 @@ subroutine ocn_analysis_init(domain, err)!{{{ end if ! Verify stream exists before trying to use output_interval - if ( config_AM_stream_name /= 'none' ) then + if ( config_AM_output_stream /= 'none' ) then streamFound = .false. - + call mpas_stream_mgr_begin_iteration(domain % streamManager) do while ( mpas_stream_mgr_get_next_stream(domain % streamManager, streamName) ) - if ( trim(streamName) == trim(config_AM_stream_name) ) then + if ( trim(streamName) == trim(config_AM_output_stream) ) then streamFound = .true. end if end do - + if ( .not. streamFound ) then - call mpas_log_write('ERROR: Stream ' // trim(config_AM_stream_name) // ' does not exist. Exiting...') + call mpas_log_write('Stream ' // trim(config_AM_output_stream) // ' does not exist. Exiting...', MPAS_LOG_CRIT) end if end if - - if ( config_AM_compute_interval /= 'output_interval' .and. config_AM_stream_name /= 'none') then + if ( config_AM_compute_interval == 'output_interval' .and. config_AM_output_stream == 'none') then + call mpas_log_write('Analysis member has compute_interval of ''output_interval'' ' // & + 'without an output stream.', MPAS_LOG_CRIT) + end if + + if ( config_AM_compute_interval /= 'output_interval' ) then alarmName = poolItr % memberName(1:nameLength) // computeAlarmSuffix call mpas_set_timeInterval(alarmTimeStep, timeString=config_AM_compute_interval, ierr=err_tmp) - call MPAS_stream_mgr_get_property(domain % streamManager, config_AM_stream_name, MPAS_STREAM_PROPERTY_REF_TIME, referenceTimeString, err_tmp) - call mpas_set_time(referenceTime, dateTimeString=referenceTimeString, ierr=err_tmp) + if ( config_AM_output_stream /= 'none' ) then + call MPAS_stream_mgr_get_property(domain % streamManager, config_AM_output_stream, & + MPAS_STREAM_PROPERTY_REF_TIME, referenceTimeString, err_tmp) + call mpas_set_time(referenceTime, dateTimeString=referenceTimeString, ierr=err_tmp) + else + call mpas_pool_get_config(domain % configs, 'config_start_time', config_start_time) + + ! TODO FIXME I'm not sure what it's supposed to be + ! but 'file' is causing the code to fail + if (trim(config_start_time) == 'file') then + ! FIXME big kludge + call mpas_set_time(referenceTime, & + dateTimeString='0000-01-01_00:00:00', ierr=err_tmp) + else + ! FIXME this is what it was without the if-else + ! I suppose it's supposed to actually read it from + ! the file first + call mpas_set_time(referenceTime, dateTimeString=config_start_time, ierr=err_tmp) + end if + + end if call mpas_add_clock_alarm(domain % clock, alarmName, referenceTime, alarmTimeStep, ierr=err_tmp) call mpas_reset_clock_alarm(domain % clock, alarmName, ierr=err_tmp) end if + call mpas_timer_stop(timerName) end if end do @@ -328,14 +561,14 @@ subroutine ocn_analysis_compute_startup(domain, err)!{{{ integer :: timeLevel, err_tmp character (len=StrKIND) :: configName, timerName - character (len=StrKIND), pointer :: config_AM_stream_name + character (len=StrKIND), pointer :: config_AM_output_stream logical, pointer :: config_AM_enable, config_AM_write_on_startup, config_AM_compute_on_startup type (mpas_pool_iterator_type) :: poolItr integer :: nameLength err = 0 - call mpas_timer_start('analysis_compute', .false.) + call mpas_timer_start('analysis_compute_startup') timeLevel=1 @@ -352,29 +585,46 @@ subroutine ocn_analysis_compute_startup(domain, err)!{{{ call mpas_pool_get_config(domain % configs, configName, config_AM_write_on_startup) if ( config_AM_compute_on_startup ) then - timerName = trim(computeTimerPrefix) // poolItr % memberName(1:nameLength) - call mpas_timer_start(timerName, .false.) + timerName = trim(computeStartupTimerPrefix) // poolItr % memberName(1:nameLength) +#ifdef MPAS_DEBUG + call mpas_log_write( ' Computing AM ' // poolItr % memberName(1:nameLength)) +#endif + call mpas_timer_start(timerName) call ocn_compute_analysis_members(domain, timeLevel, poolItr % memberName, err_tmp) call mpas_timer_stop(timerName) err = ior(err, err_tmp) + end if - if ( config_AM_write_on_startup ) then - configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' - call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) - if ( config_AM_stream_name /= 'none' ) then - call mpas_stream_mgr_write(domain % streamManager, streamID=config_AM_stream_name, forceWriteNow=.true., ierr=err_tmp) - end if + if ( config_AM_write_on_startup ) then + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_output_stream' + call mpas_pool_get_config(domain % configs, configName, config_AM_output_stream) + if ( config_AM_output_stream /= 'none' ) then +#ifdef MPAS_DEBUG + call mpas_log_write( ' Writing AM ' // poolItr % memberName(1:nameLength)) +#endif + call mpas_stream_mgr_write(domain % streamManager, streamID=config_AM_output_stream, & + forceWriteNow=.true., ierr=err_tmp) end if else if ( config_AM_write_on_startup ) then - call mpas_log_write(' *** WARNING: write_on_startup called without compute_on_startup for analysis member: ' & - // poolItr % memberName(1:nameLength) // '. Skipping output...') + call mpas_log_write('write_on_startup called without compute_on_startup for analysis member: ' & + // poolItr % memberName(1:nameLength) // '. Skipping output...', MPAS_LOG_WARN) end if end if + + ! Reset configs to false, so we don't write or compute the state every coupling interval for use within ACME. + ! This ensures that these startup options are only done once. + if ( config_AM_compute_on_startup) then + config_AM_compute_on_startup = .false. + end if + if ( config_AM_write_on_startup ) then + config_AM_write_on_startup = .false. + end if + end if end do - call mpas_timer_stop('analysis_compute') + call mpas_timer_stop('analysis_compute_startup') end subroutine ocn_analysis_compute_startup!}}} @@ -424,14 +674,14 @@ subroutine ocn_analysis_compute(domain, err)!{{{ integer :: timeLevel, err_tmp character (len=StrKIND) :: configName, alarmName, timerName - character (len=StrKIND), pointer :: config_AM_stream_name, config_AM_compute_interval + character (len=StrKIND), pointer :: config_AM_output_stream, config_AM_compute_interval logical, pointer :: config_AM_enable type (mpas_pool_iterator_type) :: poolItr integer :: nameLength err = 0 - call mpas_timer_start('analysis_compute', .false.) + call mpas_timer_start('analysis_compute') timeLevel=1 @@ -444,23 +694,30 @@ subroutine ocn_analysis_compute(domain, err)!{{{ if ( config_AM_enable ) then configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_compute_interval' call mpas_pool_get_config(domain % configs, configName, config_AM_compute_interval) - configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' - call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_output_stream' + call mpas_pool_get_config(domain % configs, configName, config_AM_output_stream) ! Build name of alarm for analysis member alarmName = poolItr % memberName(1:nameLength) // computeAlarmSuffix timerName = trim(computeTimerPrefix) // poolItr % memberName(1:nameLength) ! Compute analysis member just before output - if ( config_AM_compute_interval == 'output_interval' .and. config_AM_stream_name /= 'none') then - if ( mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID=config_AM_stream_name, direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) ) then - call mpas_timer_start(timerName, .false.) + if ( config_AM_compute_interval == 'output_interval' .and. config_AM_output_stream /= 'none') then + if ( mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID=config_AM_output_stream, & + direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) ) then +#ifdef MPAS_DEBUG + call mpas_log_write( ' Computing AM ' // poolItr % memberName(1:nameLength)) +#endif + call mpas_timer_start(timerName) call ocn_compute_analysis_members(domain, timeLevel, poolItr % memberName, err_tmp) call mpas_timer_stop(timerName) end if else if ( mpas_is_alarm_ringing(domain % clock, alarmName, ierr=err_tmp) ) then call mpas_reset_clock_alarm(domain % clock, alarmName, ierr=err_tmp) - call mpas_timer_start(timerName, .false.) +#ifdef MPAS_DEBUG + call mpas_log_write( ' Computing AM ' // poolItr % memberName(1:nameLength)) +#endif + call mpas_timer_start(timerName) call ocn_compute_analysis_members(domain, timeLevel, poolItr % memberName, err_tmp) call mpas_timer_stop(timerName) end if @@ -523,7 +780,7 @@ subroutine ocn_analysis_restart(domain, err)!{{{ err = 0 - call mpas_timer_start('analysis_restart', .false.) + call mpas_timer_start('analysis_restart') call mpas_pool_begin_iteration(analysisMemberList) do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) @@ -532,8 +789,11 @@ subroutine ocn_analysis_restart(domain, err)!{{{ call mpas_pool_get_config(domain % configs, configName, config_AM_enable) if ( config_AM_enable ) then +#ifdef MPAS_DEBUG + call mpas_log_write( ' Preparing AM ' // poolItr % memberName(1:nameLength) // ' for restart write') +#endif timerName = trim(restartTimerPrefix) // poolItr % memberName(1:nameLength) - call mpas_timer_start(timerName, .false.) + call mpas_timer_start(timerName) call ocn_restart_analysis_members(domain, poolItr % memberName, err_tmp) err = ior(err, err_tmp) call mpas_timer_stop(timerName) @@ -591,15 +851,21 @@ subroutine ocn_analysis_write(domain, err)!{{{ integer :: err_tmp - character (len=StrKIND) :: configName, timerName - character (len=StrKIND), pointer :: config_AM_stream_name + character (len=StrKIND) :: configName, timerName, outputTimeString + character (len=StrKIND), pointer :: config_AM_output_stream + character (len=StrKIND), pointer :: config_AM_backwardOffset, config_AM_forwardOffset logical, pointer :: config_AM_enable type (mpas_pool_iterator_type) :: poolItr + type (mpas_time_type) :: outputTime, nowTime + type (mpas_timeinterval_type) :: offsetInt integer :: nameLength + integer :: poolErrorLevel + err = 0 - call mpas_timer_start('analysis_write', .false.) + call mpas_timer_start('analysis_write') + nowTime = mpas_get_clock_time(domain % clock, MPAS_NOW, ierr=err_tmp) call mpas_pool_begin_iteration(analysisMemberList) do while ( mpas_pool_get_next_member(analysisMemberList, poolItr) ) @@ -608,16 +874,51 @@ subroutine ocn_analysis_write(domain, err)!{{{ call mpas_pool_get_config(domain % configs, configName, config_AM_enable) if ( config_AM_enable ) then - configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_stream_name' - call mpas_pool_get_config(domain % configs, configName, config_AM_stream_name) - if ( config_AM_stream_name /= 'none' ) then + + poolErrorLevel = mpas_pool_get_error_level() + call mpas_pool_set_error_level(MPAS_POOL_SILENT) + + nullify(config_AM_backwardOffset) + nullify(config_AM_forwardOffset) +#ifdef MPAS_DEBUG + call mpas_log_write( ' Writing AM ' // poolItr % memberName(1:nameLength)) +#endif + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_output_stream' + call mpas_pool_get_config(domain % configs, configName, config_AM_output_stream) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_backward_output_offset' + call mpas_pool_get_config(domain % configs, configName, config_AM_backwardOffset) + configName = 'config_AM_' // poolItr % memberName(1:nameLength) // '_forward_output_offset' + call mpas_pool_get_config(domain % configs, configName, config_AM_forwardOffset) + + call mpas_pool_set_error_level(poolErrorLevel) + if ( config_AM_output_stream /= 'none' ) then timerName = trim(writeTimerPrefix) // poolItr % memberName(1:nameLength) - call mpas_timer_start(timerName, .false.) - call mpas_stream_mgr_write(domain % streamManager, streamID=config_AM_stream_name, ierr=err_tmp) + call mpas_timer_start(timerName) + + if ( associated(config_AM_backwardOffset) ) then + if ( associated(config_AM_forwardOffset) ) then + call mpas_log_write( 'Both backward and forward offsets are set for AM ' // & + poolItr % memberName(1:nameLength), MPAS_LOG_WARN) + call mpas_log_write( ' will only use backward offset. Forward offset will be ignored.') + end if + call mpas_set_timeinterval(offsetInt, timeString=config_AM_backwardOffset, ierr=err_tmp) + outputTime = nowTime - offsetInt + call mpas_get_time(outputTime, dateTimeString=outputTimeString, ierr=err_tmp) + else if ( associated(config_AM_forwardOffset) ) then + call mpas_set_timeinterval(offsetInt, timeString=config_AM_backwardOffset, ierr=err_tmp) + outputTime = nowTime + offsetInt + call mpas_stream_mgr_write(domain % streamManager, streamID=config_AM_output_stream, ierr=err_tmp) + else + outputTime = nowTime + end if + + call mpas_get_time(outputTime, dateTimeString=outputTimeString, ierr=err_tmp) + call mpas_stream_mgr_write(domain % streamManager, streamID=config_AM_output_stream, & + writeTime=outputTimeString, ierr=err_tmp) call mpas_timer_stop(timerName) timerName = trim(alarmTimerPrefix) // poolItr % memberName(1:nameLength) - call mpas_timer_start(timerName, .false.) - call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID=config_AM_stream_name, ierr=err_tmp) + call mpas_timer_start(timerName) + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID=config_AM_output_stream, ierr=err_tmp) call mpas_timer_stop(timerName) end if end if @@ -679,7 +980,7 @@ subroutine ocn_analysis_finalize(domain, err)!{{{ err = 0 - call mpas_timer_start('analysis_finalize', .false.) + call mpas_timer_start('analysis_finalize') call mpas_pool_begin_iteration(analysisMemberList) @@ -689,8 +990,11 @@ subroutine ocn_analysis_finalize(domain, err)!{{{ call mpas_pool_get_config(domain % configs, configName, config_AM_enable) if ( config_AM_enable ) then +#ifdef MPAS_DEBUG + call mpas_log_write( ' Finalizing AM ' // poolItr % memberName(1:nameLength)) +#endif timerName = trim(finalizeTimerPrefix) // poolItr % memberName(1:nameLength) - call mpas_timer_start(timerName, .false.) + call mpas_timer_start(timerName) call ocn_finalize_analysis_members(domain, poolItr % memberName, err_tmp) err = ior(err, err_tmp) call mpas_timer_stop(timerName) @@ -701,6 +1005,55 @@ subroutine ocn_analysis_finalize(domain, err)!{{{ end subroutine ocn_analysis_finalize!}}} +!*********************************************************************** +! +! routine ocn_bootstrap_analysis_members +! +!> \brief Analysis member initialization driver +!> \author Doug Jacobsen +!> \date 07/01/2015 +!> \details +!> This private routine calls the correct init routine for each analysis member. +! +!----------------------------------------------------------------------- + subroutine ocn_bootstrap_analysis_members(domain, analysisMemberName, iErr)!{{{ + type (domain_type), intent(inout) :: domain !< Input: Domain information + character (len=*), intent(in) :: analysisMemberName !< Input: Name of analysis member + integer, intent(out) :: iErr !< Output: Error code + + integer :: nameLength, err_tmp + + iErr = 0 + err_tmp = 0 + + nameLength = len_trim(analysisMemberName) + + !if ( analysisMemberName(1:nameLength) == 'testComputeInterval' ) then + ! call ocn_bootstrap_test_compute_interval(domain, err_tmp) + if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsDaily' ) then + call ocn_bootstrap_time_series_stats(domain, timeSeriesDailyTAG, & + err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsMonthly' ) then + call ocn_bootstrap_time_series_stats(domain, timeSeriesMonthlyTAG, & + err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsClimatology' ) then + call ocn_bootstrap_time_series_stats(domain, timeSeriesClimatologyTAG, & + err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsCustom' ) then + call ocn_bootstrap_time_series_stats(domain, timeSeriesCustomTAG, & + err_tmp) + else if ( analysisMemberName(1:nameLength) == 'pointwiseStats' ) then + call ocn_bootstrap_pointwise_stats(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'transectTransport' ) then + call ocn_init_transect_transport(domain, err_tmp) +! else if ( analysisMemberName(1:nameLength) == 'temPlate' ) then +! call ocn_init_TEM_PLATE(domain, err_tmp) + end if + + iErr = ior(iErr, err_tmp) + + end subroutine ocn_bootstrap_analysis_members!}}} + !*********************************************************************** ! ! routine ocn_init_analysis_members @@ -739,10 +1092,49 @@ subroutine ocn_init_analysis_members(domain, analysisMemberName, iErr)!{{{ call ocn_init_water_mass_census(domain, err_tmp) else if ( analysisMemberName(1:nameLength) == 'zonalMean' ) then call ocn_init_zonal_mean(domain, err_tmp) - else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then - call ocn_init_high_frequency_output(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then + call ocn_init_high_frequency_output(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeFilters' ) then + call ocn_init_time_filters(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'lagrPartTrack' ) then + call ocn_init_lagrangian_particle_tracking(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eliassenPalm' ) then + call ocn_init_eliassen_palm(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mixedLayerDepths' ) then + call ocn_init_mixed_layer_depths(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'pointwiseStats' ) then + call ocn_init_pointwise_stats(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'debugDiagnostics' ) then + call ocn_init_debug_diagnostics(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'transectTransport' ) then + call ocn_init_transect_transport(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eddyProductVariables' ) then + call ocn_init_eddy_product_variables(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mocStreamfunction' ) then + call ocn_init_moc_streamfunction(domain, err_tmp) ! else if ( analysisMemberName(1:nameLength) == 'temPlate' ) then ! call ocn_init_TEM_PLATE(domain, err_tmp) + ! rpn is third to last + else if ( analysisMemberName(1:nameLength) == 'rpnCalculator' ) then + call ocn_init_rpn_calculator(domain, err_tmp) + ! regional is second to last + else if ( analysisMemberName(1:nameLength) == 'regionalStatsDaily' ) then + call ocn_init_regional_stats(domain, regionalStatsDailyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsMonthly' ) then + call ocn_init_regional_stats(domain, regionalStatsMonthlyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsWeekly' ) then + call ocn_init_regional_stats(domain, regionalStatsWeeklyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsCustom' ) then + call ocn_init_regional_stats(domain, regionalStatsCustomTAG, err_tmp) + ! time is last + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsDaily' ) then + call ocn_init_time_series_stats(domain, timeSeriesDailyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsMonthly' ) then + call ocn_init_time_series_stats(domain, timeSeriesMonthlyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsClimatology' ) then + call ocn_init_time_series_stats(domain, timeSeriesClimatologyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsCustom' ) then + call ocn_init_time_series_stats(domain, timeSeriesCustomTAG, err_tmp) end if iErr = ior(iErr, err_tmp) @@ -788,10 +1180,53 @@ subroutine ocn_compute_analysis_members(domain, timeLevel, analysisMemberName, i call ocn_compute_water_mass_census(domain, timeLevel, err_tmp) else if ( analysisMemberName(1:nameLength) == 'zonalMean' ) then call ocn_compute_zonal_mean(domain, timeLevel, err_tmp) - else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then - call ocn_compute_high_frequency_output(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then + call ocn_compute_high_frequency_output(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeFilters' ) then + call ocn_compute_time_filters(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'lagrPartTrack' ) then + call ocn_compute_lagrangian_particle_tracking(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eliassenPalm' ) then + call ocn_compute_eliassen_palm(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mixedLayerDepths' ) then + call ocn_compute_mixed_layer_depths(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'pointwiseStats' ) then + call ocn_compute_pointwise_stats(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'debugDiagnostics' ) then + call ocn_compute_debug_diagnostics(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'transectTransport' ) then + call ocn_compute_transect_transport(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eddyProductVariables' ) then + call ocn_compute_eddy_product_variables(domain, timeLevel, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mocStreamfunction' ) then + call ocn_compute_moc_streamfunction(domain, timeLevel, err_tmp) ! else if ( analysisMemberName(1:nameLength) == 'temPlate' ) then ! call ocn_compute_TEM_PLATE(domain, timeLevel, err_tmp) + ! rpn is third to last + else if ( analysisMemberName(1:nameLength) == 'rpnCalculator' ) then + call ocn_compute_rpn_calculator(domain, timeLevel, err_tmp) + ! regional is second to last + else if ( analysisMemberName(1:nameLength) == 'regionalStatsDaily' ) then + call ocn_compute_regional_stats(domain, timeLevel, regionalStatsDailyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsMonthly' ) then + call ocn_compute_regional_stats(domain, timeLevel, regionalStatsMonthlyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsWeekly' ) then + call ocn_compute_regional_stats(domain, timeLevel, regionalStatsWeeklyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsCustom' ) then + call ocn_compute_regional_stats(domain, timeLevel, regionalStatsCustomTAG, err_tmp) + ! time is last + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsDaily' ) then + call ocn_compute_time_series_stats(domain, timeLevel, & + timeSeriesDailyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsMonthly' ) then + call ocn_compute_time_series_stats(domain, timeLevel, & + timeSeriesMonthlyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsClimatology' ) then + call ocn_compute_time_series_stats(domain, timeLevel, & + timeSeriesClimatologyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsCustom' ) then + call ocn_compute_time_series_stats(domain, timeLevel, & + timeSeriesCustomTAG, err_tmp) end if iErr = ior(iErr, err_tmp) @@ -836,10 +1271,49 @@ subroutine ocn_restart_analysis_members(domain, analysisMemberName, iErr)!{{{ call ocn_restart_water_mass_census(domain, err_tmp) else if ( analysisMemberName(1:nameLength) == 'zonalMean' ) then call ocn_restart_zonal_mean(domain, err_tmp) - else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then - call ocn_restart_high_frequency_output(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then + call ocn_restart_high_frequency_output(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeFilters' ) then + call ocn_restart_time_filters(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'lagrPartTrack' ) then + call ocn_restart_lagrangian_particle_tracking(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eliassenPalm' ) then + call ocn_restart_eliassen_palm(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mixedLayerDepths' ) then + call ocn_restart_mixed_layer_depths(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'pointwiseStats' ) then + call ocn_restart_pointwise_stats(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'debugDiagnostics' ) then + call ocn_restart_debug_diagnostics(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'transectTransport' ) then + call ocn_restart_transect_transport(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eddyProductVariables' ) then + call ocn_restart_eddy_product_variables(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mocStreamfunction' ) then + call ocn_restart_moc_streamfunction(domain, err_tmp) ! else if ( analysisMemberName(1:nameLength) == 'temPlate' ) then ! call ocn_restart_TEM_PLATE(domain, err_tmp) + ! rpn is third to last + else if ( analysisMemberName(1:nameLength) == 'rpnCalculator' ) then + call ocn_restart_rpn_calculator(domain, err_tmp) + ! regional is second to last + else if ( analysisMemberName(1:nameLength) == 'regionalStatsDaily' ) then + call ocn_restart_regional_stats(domain, regionalStatsDailyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsMonthly' ) then + call ocn_restart_regional_stats(domain, regionalStatsMonthlyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsWeekly' ) then + call ocn_restart_regional_stats(domain, regionalStatsWeeklyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsCustom' ) then + call ocn_restart_regional_stats(domain, regionalStatsCustomTAG, err_tmp) + ! time is last + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsDaily' ) then + call ocn_restart_time_series_stats(domain, timeSeriesDailyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsMonthly' ) then + call ocn_restart_time_series_stats(domain, timeSeriesMonthlyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsClimatology' ) then + call ocn_restart_time_series_stats(domain, timeSeriesClimatologyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsCustom' ) then + call ocn_restart_time_series_stats(domain, timeSeriesCustomTAG, err_tmp) end if iErr = ior(iErr, err_tmp) @@ -884,10 +1358,53 @@ subroutine ocn_finalize_analysis_members(domain, analysisMemberName, iErr)!{{{ call ocn_finalize_water_mass_census(domain, err_tmp) else if ( analysisMemberName(1:nameLength) == 'zonalMean' ) then call ocn_finalize_zonal_mean(domain, err_tmp) - else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then - call ocn_finalize_high_frequency_output(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'highFrequencyOutput' ) then + call ocn_finalize_high_frequency_output(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeFilters' ) then + call ocn_finalize_time_filters(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'lagrPartTrack' ) then + call ocn_finalize_lagrangian_particle_tracking(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eliassenPalm' ) then + call ocn_finalize_eliassen_palm(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mixedLayerDepths' ) then + call ocn_finalize_mixed_layer_depths(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'pointwiseStats' ) then + call ocn_finalize_pointwise_stats(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'debugDiagnostics' ) then + call ocn_finalize_debug_diagnostics(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'transectTransport' ) then + call ocn_finalize_transect_transport(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'eddyProductVariables' ) then + call ocn_finalize_eddy_product_variables(domain, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'mocStreamfunction' ) then + call ocn_finalize_moc_streamfunction(domain, err_tmp) ! else if ( analysisMemberName(1:nameLength) == 'temPlate' ) then ! call ocn_finalize_TEM_PLATE(domain, err_tmp) + ! rpn is third to last + else if ( analysisMemberName(1:nameLength) == 'rpnCalculator' ) then + call ocn_finalize_rpn_calculator(domain, err_tmp) + ! regional is second to last + else if ( analysisMemberName(1:nameLength) == 'regionalStatsDaily' ) then + call ocn_finalize_regional_stats(domain, regionalStatsDailyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsMonthly' ) then + call ocn_finalize_regional_stats(domain, regionalStatsMonthlyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsWeekly' ) then + call ocn_finalize_regional_stats(domain, regionalStatsWeeklyTAG, err_tmp) + else if ( analysisMemberName(1:nameLength) == 'regionalStatsCustom' ) then + call ocn_finalize_regional_stats(domain, regionalStatsCustomTAG, err_tmp) + ! time is last + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsDaily' ) then + call ocn_finalize_time_series_stats(domain, timeSeriesDailyTAG, & + err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsMonthly' ) then + call ocn_finalize_time_series_stats(domain, timeSeriesMonthlyTAG, & + err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsClimatology' ) then + call ocn_finalize_time_series_stats(domain, timeSeriesClimatologyTAG, & + err_tmp) + else if ( analysisMemberName(1:nameLength) == 'timeSeriesStatsCustom' ) then + call ocn_finalize_time_series_stats(domain, timeSeriesCustomTAG, & + err_tmp) end if iErr = ior(iErr, err_tmp) diff --git a/src/core_ocean/analysis_members/mpas_ocn_debug_diagnostics.F b/src/core_ocean/analysis_members/mpas_ocn_debug_diagnostics.F new file mode 100644 index 0000000000..06f83336d2 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_debug_diagnostics.F @@ -0,0 +1,511 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_debug_diagnostics +! +!> \brief MPAS ocean analysis mode member: debug_diagnostics +!> \author Mark Petersen +!> \date March 2016 +!> \details +!> MPAS ocean analysis mode member: debug_diagnostics +!> Compute diagnostics used for debugging. +!> +!----------------------------------------------------------------------- + +module ocn_debug_diagnostics + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + use mpas_io_units + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_debug_diagnostics, & + ocn_compute_debug_diagnostics, & + ocn_restart_debug_diagnostics, & + ocn_finalize_debug_diagnostics + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_debug_diagnostics +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date March 2016 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_debug_diagnostics(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_init_debug_diagnostics!}}} + +!*********************************************************************** +! +! routine ocn_compute_debug_diagnostics +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date March 2016 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_debug_diagnostics(domain, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: debugDiagnosticsAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + !type (mpas_pool_type), pointer :: debugDiagnosticsAM + + ! Here are some example variables which may be needed for your analysis member + integer :: iEdge, c1, c2, k + integer, pointer :: nEdges + integer, dimension(:), pointer :: maxLevelEdgeTop + integer, dimension(:,:), pointer :: cellsOnEdge + + real (kind=RKIND) :: dzVert1, dzVert2, dzEdgeK, dzEdgeKp1, rx1, localMaxRx1 + real (kind=RKIND), pointer :: globalRx1Max + real (kind=RKIND), dimension(:), pointer :: rx1MaxCell + real (kind=RKIND), dimension(:,:), pointer :: zMid + + logical, pointer :: config_AM_debugDiagnostics_check_state + + err = 0 + + call mpas_pool_get_config(domain % configs, 'config_AM_debugDiagnostics_check_state', & + config_AM_debugDiagnostics_check_state) + + localMaxRx1 = 0.0_RKIND + + dminfo = domain % dminfo + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + !call mpas_pool_get_subpool(block % structs, 'debugDiagnosticsAM', debugDiagnosticsAMPool) + + if ( config_AM_debugDiagnostics_check_state ) then + call ocn_test_ocean_state(dminfo, meshPool, diagnosticsPool, statePool) + end if + + ! Here are some example variables which may be needed for your analysis member + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + + !----------------------------------------------------------------- + ! + ! Compute Haney number, rx1 + ! + !----------------------------------------------------------------- + + call mpas_pool_get_array(diagnosticsPool, 'rx1MaxCell', rx1MaxCell) + call mpas_pool_get_array(diagnosticsPool, 'globalRx1Max', globalRx1Max) + + ! These could be included for edge or cell fields with depth: + ! call mpas_pool_get_array(diagnosticsPool, 'rx1Edge', rx1Edge) + ! call mpas_pool_get_array(diagnosticsPool, 'rx1Cell', rx1Cell) + ! call mpas_pool_get_array(diagnosticsPool, 'rx1MaxEdge', rx1MaxEdge) + ! rx1Edge(:,:) = 0.0_RKIND + ! rx1Cell(:,:) = 0.0_RKIND + ! rx1MaxEdge(:) = 0.0_RKIND + + rx1MaxCell(:) = 0.0_RKIND + do iEdge = 1,nEdges + c1 = cellsOnEdge(1,iEdge) + c2 = cellsOnEdge(2,iEdge) + do k = 1,maxLevelEdgeTop(iEdge)-1 + dzVert1 = zMid(k,c1)-zMid(k+1,c1) + dzVert2 = zMid(k,c2)-zMid(k+1,c2) + dzEdgeK = zMid(k,c2)-zMid(k,c1) + dzEdgeKp1 = zMid(k+1,c2)-zMid(k+1,c1) + + rx1 = abs(dzEdgeK+dzEdgeKp1)/(dzVert1+dzVert2) + + rx1MaxCell(c1) = max(rx1MaxCell(c1),rx1) + rx1MaxCell(c2) = max(rx1MaxCell(c2),rx1) + + ! These could be included for edge or cell fields with depth: + ! rx1Edge(k,iEdge) = rx1 + ! rx1Cell(k,c1) = max(rx1Cell(k,c1),rx1) + ! rx1Cell(k,c2) = max(rx1Cell(k,c2),rx1) + ! rx1MaxEdge(iEdge) = max(rx1MaxEdge(iEdge),rx1) + end do + end do + + localMaxRx1 = max(localMaxRx1,maxval(rx1MaxCell)) + + block => block % next + end do + call mpas_dmpar_max_real(dminfo, localMaxRx1, globalRx1Max) + + end subroutine ocn_compute_debug_diagnostics!}}} + +!*********************************************************************** +! +! routine ocn_restart_debug_diagnostics +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date March 2016 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_debug_diagnostics(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_debug_diagnostics!}}} + +!*********************************************************************** +! +! routine ocn_finalize_debug_diagnostics +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date March 2016 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_debug_diagnostics(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_debug_diagnostics!}}} + + subroutine ocn_test_ocean_state(dminfo, meshPool, diagnosticsPool, statePool)!{{{ + + type (dm_info) :: dminfo + type (mpas_pool_type), pointer :: statePool, meshPool, diagnosticsPool, tracersPool + character (len=StrKIND), pointer :: xtime + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity + real (kind=RKIND), dimension(:,:), pointer :: kineticEnergyCell + real (kind=RKIND), dimension(:,:), pointer :: layerThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + integer, dimension(:), pointer :: maxLevelCell + integer, dimension(:), pointer :: indexToCellID + integer, pointer :: indexTemperature + integer, pointer :: indexSalinity + integer, pointer :: nCellsSolve + real (kind=RKIND) :: nanCheck, workValue, workLat, workLon + integer :: workGlobalID(2), errorUnit, mpiRank, iCell, k + logical :: errorFlag + character(len=StrKIND) :: charMPIRank, charFilename + + !get all pointers that might be needed + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(diagnosticsPool, 'xtime', xtime) + call mpas_pool_get_array(diagnosticsPool, 'kineticEnergyCell', kineticEnergyCell) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 2) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 2) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 2) + + !assume that no abnormal values exist + errorFlag = .false. + + !now step through tests to see if abnormal values do exist + !if so, reset errorFlag to be true + + ! test for abnormal values + do iCell=1,nCellsSolve + do k=1,maxLevelCell(iCell) + if(kineticEnergyCell(k,iCell).gt.4.0_RKIND) errorFlag=.true. + if(activeTracers(indexTemperature,k,iCell).lt.-1.9_RKIND) errorFlag=.true. + if(activeTracers(indexTemperature,k,iCell).gt.33.0_RKIND) errorFlag=.true. + if(activeTracers(indexSalinity,k,iCell).lt.0.0_RKIND) errorFlag=.true. + if(layerThickness(k,iCell).lt.1.0e-2_RKIND) errorFlag=.true. + enddo + enddo + + !if an errorFlag exists, then + ! 1) open a file + ! 2) step through tests again and write the file + ! 3) close file + + if(errorFlag) then + + !find an open unit + call mpas_new_unit(errorUnit) + mpiRank = dminfo % my_proc_id + charFilename = 'mpas_ocean_state_test_' + if( mpiRank.le. 9) write(charMPIRank,'(I1)') mpiRank + if(mpiRank.gt. 9 .and. mpiRank.le. 99) write(charMPIRank,'(I2)') mpiRank + if(mpiRank.gt. 99 .and. mpiRank.le. 999) write(charMPIRank,'(I3)') mpiRank + if(mpiRank.gt. 999 .and. mpiRank.le. 9999) write(charMPIRank,'(I4)') mpiRank + if(mpiRank.gt. 9999 .and. mpiRank.le. 99999) write(charMPIRank,'(I5)') mpiRank + if(mpiRank.gt.99999 .and. mpiRank.le.999999) write(charMPIRank,'(I6)') mpiRank + charFilename = trim(charFilename) // trim(charMPIRank) + open(unit=errorUnit, file=charFilename, form='formatted', status='unknown', position='append') + + !write time + write(errorUnit,'(a80)') trim(xtime) + + !test to see if cell kinetic energy is greater than 4.0 m2/s2 + do iCell=1,nCellsSolve + do k=1,maxLevelCell(iCell) + if(kineticEnergyCell(k,iCell).gt.4.0_RKIND) then + workValue = kineticEnergyCell(k,iCell) + workGlobalID(1) = k + workGlobalID(2) = indexToCellID(iCell) + workLat = latCell(iCell) + workLon = lonCell(iCell) + write(errorUnit, 10) 'KE= ', workValue, 'cell= ', workGlobalID(2), & + 'k= ',workGlobalID(1), 'lat = ', workLat, 'lon= ', workLon + 10 format(a4,e10.3, 3x,a6,i8, 3x,a3,i4, 3x,a6,f6.2, 3x,a6,f6.2) + endif + enddo + enddo + + !test to see if cell temperature is less than -1.9C + do iCell=1,nCellsSolve + do k=1,maxLevelCell(iCell) + if(activeTracers(indexTemperature,k,iCell).lt.-1.9_RKIND) then + workValue = activeTracers(indexTemperature,k,iCell) + workGlobalID(1) = k + workGlobalID(2) = indexToCellID(iCell) + workLat = latCell(iCell) + workLon = lonCell(iCell) + write(errorUnit, 10) 'T= ', workValue, 'cell= ', workGlobalID(2), & + 'k= ',workGlobalID(1), 'lat = ', workLat, 'lon= ', workLon + endif + enddo + enddo + + !test to see if cell temperature is greater than 33.0 + do iCell=1,nCellsSolve + do k=1,maxLevelCell(iCell) + if(activeTracers(indexTemperature,k,iCell).gt.33.0_RKIND) then + workValue = activeTracers(indexTemperature,k,iCell) + workGlobalID(1) = k + workGlobalID(2) = indexToCellID(iCell) + workLat = latCell(iCell) + workLon = lonCell(iCell) + write(errorUnit, 10) 'T= ', workValue, 'cell= ', workGlobalID(2), & + 'k= ',workGlobalID(1), 'lat = ', workLat, 'lon= ', workLon + endif + enddo + enddo + + !test to see if cell salinity is less than 0 + do iCell=1,nCellsSolve + do k=1,maxLevelCell(iCell) + if(activeTracers(indexSalinity,k,iCell).lt.0.0_RKIND) then + workValue = activeTracers(indexSalinity,k,iCell) + workGlobalID(1) = k + workGlobalID(2) = indexToCellID(iCell) + workLat = latCell(iCell) + workLon = lonCell(iCell) + write(errorUnit, 10) 'S= ', workValue, 'cell= ', workGlobalID(2), & + 'k= ',workGlobalID(1), 'lat = ', workLat, 'lon= ', workLon + endif + enddo + enddo + + !test to see if cell thickness is less than 1.0e-2 + do iCell=1,nCellsSolve + do k=1,maxLevelCell(iCell) + if(layerThickness(k,iCell).lt.1.0e-2_RKIND) then + workValue = layerThickness(k,iCell) + workGlobalID(1) = k + workGlobalID(2) = indexToCellID(iCell) + workLat = latCell(iCell) + workLon = lonCell(iCell) + write(errorUnit, 10) 'S= ', workValue, 'cell= ', workGlobalID(2), & + 'k= ',workGlobalID(1), 'lat = ', workLat, 'lon= ', workLon + endif + enddo + enddo + + write(errorUnit,*) '' + + !close unit + close(errorUnit) + call mpas_release_unit(errorUnit) + + endif ! if(errorFlag) + + end subroutine ocn_test_ocean_state!}}} + +end module ocn_debug_diagnostics + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_eddy_product_variables.F b/src/core_ocean/analysis_members/mpas_ocn_eddy_product_variables.F new file mode 100644 index 0000000000..d0b1ea97e6 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_eddy_product_variables.F @@ -0,0 +1,331 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_eddy_product_variables +! +!> \brief MPAS ocean analysis mode member: eddy_product_variables +!> \author Mark Petersen +!> \date Oct 2016 +!> \details +!> MPAS ocean analysis mode member: eddy_product_variables +!> computes: +!> SSH^2 SSHSquared +!> u^2 velocityZonalSquared +!> v^2 velocityMeridionalSquared +!> uT velocityZonalTimesTemperature +!> vT velocityMeridionalTimesTemperature +!> +!> In future, could additionally compute: +!> uS velocityZonalTimesSalinity +!> vS velocityMeridionalTimesSalinity +!> uw velocityZonalTimesVertVel +!> vw velocityMeridionalTimesVerVel +!> +!> They you just stick these in the time-average AM. We already time average u,v, and all tracers. +!> Then compute etc as a post processing step, same as POP, as follows: +!> +!> = - +!> <(u')^2> = - ^2 +!> +!----------------------------------------------------------------------- + +module ocn_eddy_product_variables + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_eddy_product_variables, & + ocn_compute_eddy_product_variables, & + ocn_restart_eddy_product_variables, & + ocn_finalize_eddy_product_variables + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_eddy_product_variables +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Oct 2016 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_eddy_product_variables(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_init_eddy_product_variables!}}} + +!*********************************************************************** +! +! routine ocn_compute_eddy_product_variables +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Oct 2016 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_eddy_product_variables(domain, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: eddyProductVariablesAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: diagnosticsPool + + integer, pointer :: nVertLevels, nCellsSolve + integer :: iTracer, k, iCell + integer, pointer :: index_temperature, index_salinity + integer, dimension(:), pointer :: maxLevelCell + + real (kind=RKIND), dimension(:), pointer :: ssh, SSHSquared + real (kind=RKIND), dimension(:,:), pointer :: velocityZonal, velocityMeridional, & + velocityZonalSquared, velocityMeridionalSquared, velocityZonalTimesTemperature, velocityMeridionalTimesTemperature + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + err = 0 + + dminfo = domain % dminfo + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'eddyProductVariablesAM', eddyProductVariablesAMPool) + + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + + call mpas_pool_get_array(statePool, 'ssh',ssh, 1) + call mpas_pool_get_array(diagnosticsPool, 'velocityZonal', velocityZonal) + call mpas_pool_get_array(diagnosticsPool, 'velocityMeridional', velocityMeridional) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(eddyProductVariablesAMPool, 'SSHSquared', SSHSquared) + call mpas_pool_get_array(eddyProductVariablesAMPool, 'velocityZonalSquared', velocityZonalSquared) + call mpas_pool_get_array(eddyProductVariablesAMPool, 'velocityMeridionalSquared', velocityMeridionalSquared) + call mpas_pool_get_array(eddyProductVariablesAMPool, 'velocityZonalTimesTemperature', velocityZonalTimesTemperature) + call mpas_pool_get_array(eddyProductVariablesAMPool, 'velocityMeridionalTimesTemperature', & + velocityMeridionalTimesTemperature) + + do iCell = 1,nCellsSolve + SSHSquared(iCell) = ssh(iCell)**2 + do k = 1, maxLevelCell(iCell) + velocityZonalSquared(k,iCell) = velocityZonal(k,iCell)**2 + velocityMeridionalSquared(k,iCell) = velocityMeridional(k,iCell)**2 + velocityZonalTimesTemperature(k,iCell) = velocityZonal(k,iCell)*activeTracers(index_temperature,k,iCell) + velocityMeridionalTimesTemperature(k,iCell) = velocityMeridional(k,iCell)*activeTracers(index_temperature,k,iCell) + end do + end do + + block => block % next + end do + + end subroutine ocn_compute_eddy_product_variables!}}} + +!*********************************************************************** +! +! routine ocn_restart_eddy_product_variables +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Oct 2016 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_eddy_product_variables(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_eddy_product_variables!}}} + +!*********************************************************************** +! +! routine ocn_finalize_eddy_product_variables +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Oct 2016 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_eddy_product_variables(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_eddy_product_variables!}}} + +end module ocn_eddy_product_variables + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_eliassen_palm.F b/src/core_ocean/analysis_members/mpas_ocn_eliassen_palm.F new file mode 100644 index 0000000000..31a6602f1b --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_eliassen_palm.F @@ -0,0 +1,2888 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_eliassen_palm +! +!> \brief MPAS ocean analysis core member: Eliassen-Palm Flux Tensor +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This module contains the routines for computing the Eliassen and Palm Flux Tensor +!> in buoyancy coordinates, and related quantities. +! +!----------------------------------------------------------------------- + +module ocn_eliassen_palm + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use mpas_pool_routines + use mpas_constants + use ocn_constants + use ocn_diagnostics_routines + use ocn_equation_of_state + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_eliassen_palm, & + ocn_compute_eliassen_palm, & + ocn_restart_eliassen_palm, & + ocn_finalize_eliassen_palm + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + real (kind=RKIND), parameter :: epsilonEPFT=1.0e-15_RKIND + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_eliassen_palm +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_eliassen_palm(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer :: err_tmp + integer :: k + real (KIND=RKIND) :: deltaBuoyancy, deltaDensity + + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: amEPFTPool + + integer :: nBuoyancyLayers + real (kind=RKIND), dimension(:), pointer :: potentialDensityMidRef + real (kind=RKIND), dimension(:), pointer :: potentialDensityTopRef + real (kind=RKIND), dimension(:), pointer :: buoyancyMidRef + real (kind=RKIND), dimension(:), pointer :: buoyancyInterfaceRef + + logical, pointer :: amEPFTActive + logical, pointer :: config_AM_eliassenPalm_compute_on_startup + integer, pointer :: config_AM_eliassenPalm_nBuoyancyLayers + real (kind=RKIND), pointer :: config_AM_eliassenPalm_rhomax_buoycoor + real (kind=RKIND), pointer :: config_AM_eliassenPalm_rhomin_buoycoor + + integer, pointer :: nSamplesEA + + real (kind=RKIND), dimension(:,:), pointer :: buoyancyMaskEA + real (kind=RKIND), dimension(:,:), pointer :: sigmaEA + real (kind=RKIND), dimension(:,:), pointer :: heightMidBuoyCoorEA + real (kind=RKIND), dimension(:,:), pointer :: montgPotBuoyCoorEA + real (kind=RKIND), dimension(:,:), pointer :: montgPotGradZonalEA + real (kind=RKIND), dimension(:,:), pointer :: montgPotGradMeridEA + real (kind=RKIND), dimension(:,:), pointer :: heightMidBuoyCoorSqEA + real (kind=RKIND), dimension(:,:), pointer :: heightMGradZonalEA + real (kind=RKIND), dimension(:,:), pointer :: heightMGradMeridEA + real (kind=RKIND), dimension(:,:), pointer :: usigmaEA + real (kind=RKIND), dimension(:,:), pointer :: vsigmaEA + real (kind=RKIND), dimension(:,:), pointer :: varpisigmaEA + real (kind=RKIND), dimension(:,:), pointer :: uusigmaEA + real (kind=RKIND), dimension(:,:), pointer :: vvsigmaEA + real (kind=RKIND), dimension(:,:), pointer :: uvsigmaEA + real (kind=RKIND), dimension(:,:), pointer :: uvarpisigmaEA + real (kind=RKIND), dimension(:,:), pointer :: vvarpisigmaEA + + err = 0 + + call mpas_pool_get_config(domain % configs, 'config_AM_eliassenPalm_compute_on_startup', & + config_AM_eliassenPalm_compute_on_startup) + call mpas_pool_get_config(domain % configs, 'config_AM_eliassenPalm_nBuoyancyLayers', & + config_AM_eliassenPalm_nBuoyancyLayers) + call mpas_pool_get_config(domain % configs, 'config_AM_eliassenPalm_rhomax_buoycoor', & + config_AM_eliassenPalm_rhomax_buoycoor) + call mpas_pool_get_config(domain % configs, 'config_AM_eliassenPalm_rhomin_buoycoor', & + config_AM_eliassenPalm_rhomin_buoycoor) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'eliassenPalmAM', amEPFTPool) + + !----------------------------------------------------------------- + ! set up pointers + !----------------------------------------------------------------- + call mpas_pool_get_array(amEPFTPool, 'potentialDensityMidRef', potentialDensityMidRef) + call mpas_pool_get_array(amEPFTPool, 'potentialDensityTopRef', potentialDensityTopRef) + call mpas_pool_get_array(amEPFTPool, 'buoyancyMidRef', buoyancyMidRef) + call mpas_pool_get_array(amEPFTPool, 'buoyancyInterfaceRef', buoyancyInterfaceRef) + + !----------------------------------------------------------------- + ! compute buoyancy and density increment of each layer + ! at present we use layer interfaces that are evenly-spaced in buoyancy space + !----------------------------------------------------------------- + nBuoyancyLayers = config_AM_eliassenPalm_nBuoyancyLayers + deltaDensity = (config_AM_eliassenPalm_rhomax_buoycoor & + - config_AM_eliassenPalm_rhomin_buoycoor) / config_AM_eliassenPalm_nBuoyancyLayers + deltaBuoyancy = -gravity * deltaDensity / rho_sw + + !----------------------------------------------------------------- + ! compute density/bouyancy at top of each layer + !----------------------------------------------------------------- + do k = 1, nBuoyancyLayers + potentialDensityTopRef(k) = config_AM_eliassenPalm_rhomin_buoycoor + deltaDensity * (k-1) + buoyancyInterfaceRef(k) = -gravity & + * (config_AM_eliassenPalm_rhomin_buoycoor - rho_sw) / rho_sw & + + deltaBuoyancy * (k-1) + end do + k=nBuoyancyLayers + buoyancyInterfaceRef(k+1) = buoyancyInterfaceRef(k) + deltaBuoyancy + + !----------------------------------------------------------------- + ! compute density/bouyancy for each layer + !----------------------------------------------------------------- + do k = 1, nBuoyancyLayers-1 + potentialDensityMidRef(k) = 0.5_RKIND*(potentialDensityTopRef(k) + potentialDensityTopRef(k+1)) + buoyancyMidRef(k) = 0.5_RKIND*(buoyancyInterfaceRef(k) + buoyancyInterfaceRef(k+1)) + end do + k=nBuoyancyLayers + potentialDensityMidRef(k) = 0.5*(potentialDensityTopRef(k) + config_AM_eliassenPalm_rhomax_buoycoor) + buoyancyMidRef(k) = 0.5_RKIND*(buoyancyInterfaceRef(k) + buoyancyInterfaceRef(k+1)) + + block => block % next + + end do + + + end subroutine ocn_init_eliassen_palm!}}} + +!*********************************************************************** +! +! routine ocn_compute_eliassen_palm +! +!> \brief Compute Eliassen-Palm flux tensor +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This routine conducts all computations required for the EPFT analysis member. +!> Each time this AM is called, the instananeous ocean state is interpolated +!> onto the target buoyancy values. The state is then accumulated into the +!> ensemble average variable arrays (varEA). Based on the current +!> estimate of the ensemble average, thickness-weight velocities are estimated +!> along with the computation of the Eliassen-Palm flux tensor. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_eliassen_palm(domain, timeLevel, err)!{{{ + + use mpas_vector_reconstruction + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! define types that live inside of domain + !----------------------------------------------------------------- + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: am_epftPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: diagnosticsPool + + !----------------------------------------------------------------- + ! define pointers to namelist config variables local to the EPFT module + !----------------------------------------------------------------- + logical, pointer :: config_AM_eliassenPalm_debug + real (kind=RKIND), pointer :: config_AM_eliassenPalm_rhomin_buoycoor + real (kind=RKIND), pointer :: config_AM_eliassenPalm_rhomax_buoycoor + + !----------------------------------------------------------------- + ! define local scalars holding length of dimensions + !----------------------------------------------------------------- + integer, pointer :: nVertLevels, nBuoyancyLayers, nBuoyancyLayersP1 + integer, pointer :: nEdges, nCells, nCellsSolve ! nCellsSolve does not include halo + + !----------------------------------------------------------------- + ! define buoyancy coordinates and fields related to the vertical direction + !----------------------------------------------------------------- + integer, dimension(:), pointer :: maxLevelCell + real(KIND=RKIND), dimension(:), pointer :: potentialDensityMidRef + real(KIND=RKIND), dimension(:), pointer :: potentialDensityTopRef + real(KIND=RKIND), dimension(:), pointer :: buoyancyMidRef + real(KIND=RKIND), dimension(:), pointer :: buoyancyInterfaceRef + real(KIND=RKIND), dimension(:), pointer :: bottomDepth + + !----------------------------------------------------------------- + ! define mesh variables + !----------------------------------------------------------------- + real(KIND=RKIND), dimension(:), pointer :: fCell + integer, dimension(:,:), pointer :: cellMask + + !----------------------------------------------------------------- + ! define fields related to the Ensemble Average (EA) + !----------------------------------------------------------------- + integer, pointer :: nSamplesEA + real(KIND=RKIND), dimension(:,:), pointer :: buoyancyMaskEA + real(KIND=RKIND), dimension(:,:), pointer :: sigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: heightMidBuoyCoorEA + real(KIND=RKIND), dimension(:,:), pointer :: montgPotGradZonalEA + real(KIND=RKIND), dimension(:,:), pointer :: montgPotGradMeridEA + real(KIND=RKIND), dimension(:,:), pointer :: heightMidBuoyCoorSqEA + real(KIND=RKIND), dimension(:,:), pointer :: montgPotBuoyCoorEA + real(KIND=RKIND), dimension(:,:), pointer :: heightMGradZonalEA + real(KIND=RKIND), dimension(:,:), pointer :: heightMGradMeridEA + real(KIND=RKIND), dimension(:,:), pointer :: usigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: vsigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: varpisigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: uusigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: vvsigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: uvsigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: uvarpisigmaEA + real(KIND=RKIND), dimension(:,:), pointer :: vvarpisigmaEA + + !----------------------------------------------------------------- + ! define the Thickness-Weighted Average (TWA) velocity + !----------------------------------------------------------------- + real(KIND=RKIND), dimension(:,:), pointer :: uTWA + real(KIND=RKIND), dimension(:,:), pointer :: vTWA + real(KIND=RKIND), dimension(:,:), pointer :: varpiTWA + real(KIND=RKIND), dimension(:,:), pointer :: duTWAdz + real(KIND=RKIND), dimension(:,:), pointer :: dvTWAdz + + !----------------------------------------------------------------- + ! define Ertel's potential vorticity and related fields + !----------------------------------------------------------------- + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPV + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVGradZonal + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVGradMerid + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVTendency + real(KIND=RKIND), dimension(:,:,:), pointer :: ErtelPVFlux + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVFlux1 + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVFlux2 + + !----------------------------------------------------------------- + ! define the Eliassen-Palm flux tensor and related fields + !----------------------------------------------------------------- + real(KIND=RKIND), dimension(:,:,:,:), pointer :: EPFT + real(KIND=RKIND), dimension(:,:,:), pointer :: divEPFT + real(KIND=RKIND), dimension(:,:), pointer :: divEPFT1 + real(KIND=RKIND), dimension(:,:), pointer :: divEPFT2 + real(KIND=RKIND), dimension(:,:), pointer :: divEPFTshear1 + real(KIND=RKIND), dimension(:,:), pointer :: divEPFTshear2 + real(KIND=RKIND), dimension(:,:), pointer :: divEPFTdrag1 + real(KIND=RKIND), dimension(:,:), pointer :: divEPFTdrag2 + real(KIND=RKIND), dimension(:,:), pointer :: uuTWACorr + real(KIND=RKIND), dimension(:,:), pointer :: vvTWACorr + real(KIND=RKIND), dimension(:,:), pointer :: uvTWACorr + real(KIND=RKIND), dimension(:,:), pointer :: epeTWA + real(KIND=RKIND), dimension(:,:), pointer :: eddyFormDragZonal + real(KIND=RKIND), dimension(:,:), pointer :: eddyFormDragMerid + + !----------------------------------------------------------------- + ! define scratch fields used as work variables and for testing + !----------------------------------------------------------------- + type(field1DInteger), pointer :: firstLayerBuoyCoorField + type(field1DInteger), pointer :: lastLayerBuoyCoorField + type(field2DReal), pointer :: heightMidBuoyCoorField + type(field2DReal), pointer :: heightTopBuoyCoorField + type(field2DReal), pointer :: heightInterfaceBuoyCoorField + type(field2DReal), pointer :: sigmaField + type(field2DReal), pointer :: montgPotBuoyCoorField + type(field2DReal), pointer :: montgPotNormalGradOnEdgeField + type(field2DReal), pointer :: uMidBuoyCoorField + type(field2DReal), pointer :: vMidBuoyCoorField + type(field2DReal), pointer :: densityMidBuoyCoorField + type(field2DReal), pointer :: densityTopBuoyCoorField + type(field2DReal), pointer :: buoyancyMaskField + type(field2DReal), pointer :: montgPotGradXField + type(field2DReal), pointer :: montgPotGradYField + type(field2DReal), pointer :: montgPotGradZField + type(field2DReal), pointer :: montgPotGradZonalField + type(field2DReal), pointer :: montgPotGradMeridField + type(field2DReal), pointer :: wrk3DnVertLevelsP1Field + type(field2DReal), pointer :: wrk3DnVertLevelsField + type(field2DReal), pointer :: wrk3DBuoyCoorField + type(field2DReal), pointer :: ErtelPVNormalGradOnEdgeField + type(field2DReal), pointer :: ErtelPVGradXField + type(field2DReal), pointer :: ErtelPVGradYField + type(field2DReal), pointer :: ErtelPVGradZField + type(field3DReal), pointer :: wrkVectorField + type(field4DReal), pointer :: wrkTensorField + + type(field2DReal), pointer :: array1_3DField + type(field2DReal), pointer :: array2_3DField + type(field2DReal), pointer :: array3_3DField + type(field2DReal), pointer :: array1_3DbuoyField + type(field2DReal), pointer :: array2_3DbuoyField + type(field2DReal), pointer :: PVMidBuoyCoorField + type(field2DReal), pointer :: PVMidBuoyCoorEAField + type(field2DReal), pointer :: uMidBuoyCoorEAField + type(field2DReal), pointer :: vMidBuoyCoorEAField + type(field2DReal), pointer :: uPVMidBuoyCoorEAField + type(field2DReal), pointer :: vPVMidBuoyCoorEAField + type(field3DReal), pointer :: PVFluxTestField + + !----------------------------------------------------------------- + ! define pointers to scratch fields + !----------------------------------------------------------------- + integer, dimension(:), pointer :: firstLayerBuoyCoor + integer, dimension(:), pointer :: lastLayerBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: heightMidBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: heightTopBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: heightInterfaceBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: sigma + real(KIND=RKIND), dimension(:,:), pointer :: montgPotBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: montgPotNormalGradOnEdge + real(KIND=RKIND), dimension(:,:), pointer :: uMidBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: vMidBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: densityMidBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: densityTopBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: buoyancyMask + real(KIND=RKIND), dimension(:,:), pointer :: montgPotGradX + real(KIND=RKIND), dimension(:,:), pointer :: montgPotGradY + real(KIND=RKIND), dimension(:,:), pointer :: montgPotGradZ + real(KIND=RKIND), dimension(:,:), pointer :: montgPotGradZonal + real(KIND=RKIND), dimension(:,:), pointer :: montgPotGradMerid + real(KIND=RKIND), dimension(:,:), pointer :: wrk3DnVertLevelsP1 + real(KIND=RKIND), dimension(:,:), pointer :: wrk3DnVertLevels + real(KIND=RKIND), dimension(:,:), pointer :: wrk3DBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVNormalGradOnEdge + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVGradX + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVGradY + real(KIND=RKIND), dimension(:,:), pointer :: ErtelPVGradZ + real(KIND=RKIND), dimension(:,:,:), pointer :: wrkVector + real(KIND=RKIND), dimension(:,:,:,:), pointer :: wrkTensor + + real(KIND=RKIND), dimension(:,:), pointer :: array1_3D + real(KIND=RKIND), dimension(:,:), pointer :: array2_3D + real(KIND=RKIND), dimension(:,:), pointer :: array3_3D + real(KIND=RKIND), dimension(:,:), pointer :: array1_3Dbuoy + real(KIND=RKIND), dimension(:,:), pointer :: array2_3Dbuoy + real(KIND=RKIND), dimension(:,:), pointer :: PVMidBuoyCoor + real(KIND=RKIND), dimension(:,:), pointer :: PVMidBuoyCoorEA + real(KIND=RKIND), dimension(:,:), pointer :: uMidBuoyCoorEA + real(KIND=RKIND), dimension(:,:), pointer :: vMidBuoyCoorEA + real(KIND=RKIND), dimension(:,:), pointer :: uPVMidBuoyCoorEA + real(KIND=RKIND), dimension(:,:), pointer :: vPVMidBuoyCoorEA + real(KIND=RKIND), dimension(:,:,:), pointer :: PVFluxTest + + !----------------------------------------------------------------- + ! define some arrays in z-coordinates, obtained from diagnostics and forcing + !----------------------------------------------------------------- + real(KIND=RKIND), dimension(:), pointer :: atmosphericPressure + real(KIND=RKIND), dimension(:,:), pointer :: zMid + real(KIND=RKIND), dimension(:,:), pointer :: zTop + real(KIND=RKIND), dimension(:,:), pointer :: density + real(KIND=RKIND), dimension(:,:), pointer :: potentialDensity + real(KIND=RKIND), dimension(:,:), pointer :: pressure + real(KIND=RKIND), dimension(:,:), pointer :: velocityZonal + real(KIND=RKIND), dimension(:,:), pointer :: velocityMeridional + real(KIND=RKIND), dimension(:,:), pointer :: relativeVorticityCell ! jas used for testing + !real(KIND=RKIND), dimension(:,:), pointer :: wCellCenter + + !----------------------------------------------------------------- + ! define local test variables + !----------------------------------------------------------------- + ! jas to do : move these to scratch in Registry_epft + integer :: nCellsCum + real(KIND=RKIND) :: RMSlocal1, RMSglobal1 + real(KIND=RKIND) :: RMSlocal2, RMSglobal2 + real(KIND=RKIND) :: RMSPVFlux1local, RMSPVFlux1global + real(KIND=RKIND) :: RMSPVFlux2local, RMSPVFlux2global + + !----------------------------------------------------------------- + ! define local work variables + !----------------------------------------------------------------- + integer :: nCellsGlobal, k, i + + err = 0 + + nCellsCum = 0 + RMSlocal1 = 0.0_RKIND + RMSlocal2 = 0.0_RKIND + RMSglobal1 = 0.0_RKIND + RMSglobal2 = 0.0_RKIND + RMSPVFlux1local = 0.0_RKIND + RMSPVFlux2local = 0.0_RKIND + RMSPVFlux1global = 0.0_RKIND + RMSPVFlux2global = 0.0_RKIND + + dminfo = domain % dminfo + + !-------------------------------------------------- + ! get config variables + !-------------------------------------------------- + call mpas_pool_get_config(domain % configs, 'config_AM_eliassenPalm_debug', & + config_AM_eliassenPalm_debug) + call mpas_pool_get_config(domain % configs, 'config_AM_eliassenPalm_rhomin_buoycoor', & + config_AM_eliassenPalm_rhomin_buoycoor) + call mpas_pool_get_config(domain % configs, 'config_AM_eliassenPalm_rhomax_buoycoor', & + config_AM_eliassenPalm_rhomax_buoycoor) + + if(config_AM_eliassenPalm_debug) then + call mpas_log_write( 'starting ocn_compute_epft') + end if + + block => domain % blocklist + do while (associated(block)) + + !-------------------------------------------------- + ! assign pointers for each pool + !-------------------------------------------------- + call mpas_pool_get_subpool(block % structs, 'eliassenPalmAM', am_epftPool) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'eliassenPalmAMPKGScratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + + !-------------------------------------------------- + ! assign pointers for mesh-related variables + !-------------------------------------------------- + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'cellMask', cellMask) ! used for tests + + + !-------------------------------------------------- + ! get scratch field pointers + !-------------------------------------------------- + call mpas_pool_get_field(scratchPool, 'firstLayerBuoyCoor', firstLayerBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'lastLayerBuoyCoor', lastLayerBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'heightMidBuoyCoor', heightMidBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'heightTopBuoyCoor', heightTopBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'heightInterfaceBuoyCoor', heightInterfaceBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'sigma', sigmaField) + call mpas_pool_get_field(scratchPool, 'montgPotBuoyCoor', montgPotBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'montgPotNormalGradOnEdge', montgPotNormalGradOnEdgeField) + call mpas_pool_get_field(scratchPool, 'uMidBuoyCoor', uMidBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'vMidBuoyCoor', vMidBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'densityMidBuoyCoor', densityMidBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'densityTopBuoyCoor', densityTopBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'buoyancyMask', buoyancyMaskField) + call mpas_pool_get_field(scratchPool, 'montgPotGradX', montgPotGradXField) + call mpas_pool_get_field(scratchPool, 'montgPotGradY', montgPotGradYField) + call mpas_pool_get_field(scratchPool, 'montgPotGradZ', montgPotGradZField) + call mpas_pool_get_field(scratchPool, 'montgPotGradZonal', montgPotGradZonalField) + call mpas_pool_get_field(scratchPool, 'montgPotGradMerid', montgPotGradMeridField) + call mpas_pool_get_field(scratchPool, 'wrk3DnVertLevelsP1', wrk3DnVertLevelsP1Field) + call mpas_pool_get_field(scratchPool, 'wrk3DnVertLevels', wrk3DnVertLevelsField) + call mpas_pool_get_field(scratchPool, 'wrk3DBuoyCoor', wrk3DBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'ErtelPVNormalGradOnEdge', ErtelPVNormalGradOnEdgeField) + call mpas_pool_get_field(scratchPool, 'ErtelPVGradX', ErtelPVGradXField) + call mpas_pool_get_field(scratchPool, 'ErtelPVGradY', ErtelPVGradYField) + call mpas_pool_get_field(scratchPool, 'ErtelPVGradZ', ErtelPVGradZField) + call mpas_pool_get_field(scratchPool, 'wrkVector', wrkVectorField) + call mpas_pool_get_field(scratchPool, 'wrkTensor', wrkTensorField) + + call mpas_pool_get_field(scratchPool, 'array1_3D', array1_3DField) + call mpas_pool_get_field(scratchPool, 'array2_3D', array2_3DField) + call mpas_pool_get_field(scratchPool, 'array3_3D', array3_3DField) + call mpas_pool_get_field(scratchPool, 'array1_3Dbuoy', array1_3DbuoyField) + call mpas_pool_get_field(scratchPool, 'array2_3Dbuoy', array2_3DbuoyField) + call mpas_pool_get_field(scratchPool, 'PVMidBuoyCoor', PVMidBuoyCoorField) + call mpas_pool_get_field(scratchPool, 'PVMidBuoyCoorEA', PVMidBuoyCoorEAField) + call mpas_pool_get_field(scratchPool, 'uMidBuoyCoorEA', uMidBuoyCoorEAField) + call mpas_pool_get_field(scratchPool, 'vMidBuoyCoorEA', vMidBuoyCoorEAField) + call mpas_pool_get_field(scratchPool, 'uPVMidBuoyCoorEA', uPVMidBuoyCoorEAField) + call mpas_pool_get_field(scratchPool, 'vPVMidBuoyCoorEA', vPVMidBuoyCoorEAField) + call mpas_pool_get_field(scratchPool, 'PVFluxTest', PVFluxTestField) + + !-------------------------------------------------- + ! allocate scratch field variables + !-------------------------------------------------- + call mpas_allocate_scratch_field(firstLayerBuoyCoorField, .true.) + call mpas_allocate_scratch_field(lastLayerBuoyCoorField, .true.) + call mpas_allocate_scratch_field(heightMidBuoyCoorField, .true.) + call mpas_allocate_scratch_field(heightTopBuoyCoorField, .true.) + call mpas_allocate_scratch_field(heightInterfaceBuoyCoorField, .true.) + call mpas_allocate_scratch_field(sigmaField, .true.) + call mpas_allocate_scratch_field(montgPotBuoyCoorField, .true.) + call mpas_allocate_scratch_field(montgPotNormalGradOnEdgeField, .true.) + call mpas_allocate_scratch_field(uMidBuoyCoorField, .true.) + call mpas_allocate_scratch_field(vMidBuoyCoorField, .true.) + call mpas_allocate_scratch_field(densityMidBuoyCoorField, .true.) + call mpas_allocate_scratch_field(densityTopBuoyCoorField, .true.) + call mpas_allocate_scratch_field(buoyancyMaskField, .true.) + call mpas_allocate_scratch_field(montgPotGradXField, .true.) + call mpas_allocate_scratch_field(montgPotGradYField, .true.) + call mpas_allocate_scratch_field(montgPotGradZField, .true.) + call mpas_allocate_scratch_field(montgPotGradZonalField, .true.) + call mpas_allocate_scratch_field(montgPotGradMeridField, .true.) + call mpas_allocate_scratch_field(wrk3DnVertLevelsP1Field, .true.) + call mpas_allocate_scratch_field(wrk3DnVertLevelsField, .true.) + call mpas_allocate_scratch_field(wrk3DBuoyCoorField, .true.) + call mpas_allocate_scratch_field(ErtelPVNormalGradOnEdgeField, .true.) + call mpas_allocate_scratch_field(ErtelPVGradXField, .true.) + call mpas_allocate_scratch_field(ErtelPVGradYField, .true.) + call mpas_allocate_scratch_field(ErtelPVGradZField, .true.) + call mpas_allocate_scratch_field(wrkVectorField, .true.) + call mpas_allocate_scratch_field(wrkTensorField, .true.) + + call mpas_allocate_scratch_field(array1_3DField, .true.) + call mpas_allocate_scratch_field(array2_3DField, .true.) + call mpas_allocate_scratch_field(array3_3DField, .true.) + call mpas_allocate_scratch_field(array1_3DbuoyField, .true.) + call mpas_allocate_scratch_field(array2_3DbuoyField, .true.) + call mpas_allocate_scratch_field(PVMidBuoyCoorField, .true.) + call mpas_allocate_scratch_field(PVMidBuoyCoorEAField, .true.) + call mpas_allocate_scratch_field(uMidBuoyCoorEAField, .true.) + call mpas_allocate_scratch_field(vMidBuoyCoorEAField, .true.) + call mpas_allocate_scratch_field(uPVMidBuoyCoorEAField, .true.) + call mpas_allocate_scratch_field(vPVMidBuoyCoorEAField, .true.) + call mpas_allocate_scratch_field(PVFluxTestField, .true.) + + !-------------------------------------------------- + ! assign pointers for scratch and test variables + !-------------------------------------------------- + firstLayerBuoyCoor => firstLayerBuoyCoorField % array + lastLayerBuoyCoor => lastLayerBuoyCoorField % array + heightMidBuoyCoor => heightMidBuoyCoorField % array + heightTopBuoyCoor => heightTopBuoyCoorField % array + heightInterfaceBuoyCoor => heightInterfaceBuoyCoorField % array + sigma => sigmaField % array + montgPotBuoyCoor => montgPotBuoyCoorField % array + montgPotNormalGradOnEdge=> montgPotNormalGradOnEdgeField % array + uMidBuoyCoor => uMidBuoyCoorField % array + vMidBuoyCoor => vMidBuoyCoorField % array + densityMidBuoyCoor => densityMidBuoyCoorField % array + densityTopBuoyCoor => densityTopBuoyCoorField % array + buoyancyMask => buoyancyMaskField % array + montgPotGradX => montgPotGradXField % array + montgPotGradY => montgPotGradYField % array + montgPotGradZ => montgPotGradZField % array + montgPotGradZonal => montgPotGradZonalField % array + montgPotGradMerid => montgPotGradMeridField % array + wrk3DnVertLevelsP1 => wrk3DnVertLevelsP1Field % array + wrk3DnVertLevels => wrk3DnVertLevelsField % array + wrk3DBuoyCoor => wrk3DBuoyCoorField % array + ErtelPVNormalGradOnEdge => ErtelPVNormalGradOnEdgeField % array + ErtelPVGradX => ErtelPVGradXField % array + ErtelPVGradY => ErtelPVGradYField % array + ErtelPVGradZ => ErtelPVGradZField % array + wrkVector => wrkVectorField % array + wrkTensor => wrkTensorField % array + + array1_3D => array1_3DField % array + array2_3D => array2_3DField % array + array3_3D => array3_3DField % array + array1_3Dbuoy => array1_3DbuoyField % array + array2_3Dbuoy => array2_3DbuoyField % array + PVMidBuoyCoor => PVMidBuoyCoorField % array + PVMidBuoyCoorEA => PVMidBuoyCoorEAField % array + uMidBuoyCoorEA => uMidBuoyCoorEAField % array + vMidBuoyCoorEA => vMidBuoyCoorEAField % array + uPVMidBuoyCoorEA => uPVMidBuoyCoorEAField % array + vPVMidBuoyCoorEA => vPVMidBuoyCoorEAField % array + PVFluxTest => PVFluxTestField % array + + !-------------------------------------------------- + ! assign pointers used from forcing pool + !-------------------------------------------------- + call mpas_pool_get_array(forcingPool, 'atmosphericPressure', atmosphericPressure) + + !-------------------------------------------------- + ! get diagnostic variables + !-------------------------------------------------- + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(diagnosticsPool, 'zTop', zTop) + call mpas_pool_get_array(diagnosticsPool, 'density', density) + call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', potentialDensity) + call mpas_pool_get_array(diagnosticsPool, 'pressure', pressure) + call mpas_pool_get_array(diagnosticsPool, 'velocityZonal', velocityZonal) + call mpas_pool_get_array(diagnosticsPool, 'velocityMeridional', velocityMeridional) + + !-------------------------------------------------- + ! variables that define the vertical coordinate system in density/buoyancy space + !-------------------------------------------------- + call mpas_pool_get_dimension(block % dimensions, 'nBuoyancyLayers', nBuoyancyLayers) + call mpas_pool_get_dimension(block % dimensions, 'nBuoyancyLayersP1', nBuoyancyLayersP1) + call mpas_pool_get_array(am_epftPool, 'potentialDensityMidRef', potentialDensityMidRef) + call mpas_pool_get_array(am_epftPool, 'potentialDensityTopRef', potentialDensityTopRef) + call mpas_pool_get_array(am_epftPool, 'buoyancyMidRef', buoyancyMidRef) + call mpas_pool_get_array(am_epftPool, 'buoyancyInterfaceRef', buoyancyInterfaceRef) + + !-------------------------------------------------- + ! assign pointers for ensemble average (EA) and thickness-weighted averaged state + !-------------------------------------------------- + call mpas_pool_get_array(am_epftPool, 'nSamplesEA', nSamplesEA) + call mpas_pool_get_array(am_epftPool, 'buoyancyMaskEA', buoyancyMaskEA) + call mpas_pool_get_array(am_epftPool, 'sigmaEA', sigmaEA) + call mpas_pool_get_array(am_epftPool, 'heightMidBuoyCoorEA', heightMidBuoyCoorEA) + call mpas_pool_get_array(am_epftPool, 'heightMidBuoyCoorSqEA', heightMidBuoyCoorSqEA) + call mpas_pool_get_array(am_epftPool, 'montgPotBuoyCoorEA', montgPotBuoyCoorEA) + call mpas_pool_get_array(am_epftPool, 'montgPotGradZonalEA', montgPotGradZonalEA) + call mpas_pool_get_array(am_epftPool, 'montgPotGradMeridEA', montgPotGradMeridEA) + call mpas_pool_get_array(am_epftPool, 'heightMGradZonalEA', HeightMGradZonalEA) + call mpas_pool_get_array(am_epftPool, 'heightMGradMeridEA', HeightMGradMeridEA) + call mpas_pool_get_array(am_epftPool, 'usigmaEA', usigmaEA) + call mpas_pool_get_array(am_epftPool, 'vsigmaEA', vsigmaEA) + call mpas_pool_get_array(am_epftPool, 'varpisigmaEA', varpisigmaEA) + call mpas_pool_get_array(am_epftPool, 'uusigmaEA', uusigmaEA) + call mpas_pool_get_array(am_epftPool, 'vvsigmaEA', vvsigmaEA) + call mpas_pool_get_array(am_epftPool, 'uvsigmaEA', uvsigmaEA) + call mpas_pool_get_array(am_epftPool, 'uvarpisigmaEA', uvarpisigmaEA) + call mpas_pool_get_array(am_epftPool, 'vvarpisigmaEA', vvarpisigmaEA) + + !-------------------------------------------------- + ! assign pointers for thickness-weighted averaged state + !-------------------------------------------------- + call mpas_pool_get_array(am_epftPool, 'uTWA', uTWA) + call mpas_pool_get_array(am_epftPool, 'vTWA', vTWA) + call mpas_pool_get_array(am_epftPool, 'varpiTWA', varpiTWA) + call mpas_pool_get_array(am_epftPool, 'duTWAdz', duTWAdz) + call mpas_pool_get_array(am_epftPool, 'dvTWAdz', dvTWAdz) + + !-------------------------------------------------- + ! Eliassen-Palm Flux Tensor and related products + !-------------------------------------------------- + call mpas_pool_get_array(am_epftPool, 'EPFT', EPFT) + call mpas_pool_get_array(am_epftPool, 'divEPFT', divEPFT) + call mpas_pool_get_array(am_epftPool, 'divEPFT1', divEPFT1) + call mpas_pool_get_array(am_epftPool, 'divEPFT2', divEPFT2) + call mpas_pool_get_array(am_epftPool, 'divEPFTshear1', divEPFTshear1) + call mpas_pool_get_array(am_epftPool, 'divEPFTshear2', divEPFTshear2) + call mpas_pool_get_array(am_epftPool, 'divEPFTdrag1', divEPFTdrag1) + call mpas_pool_get_array(am_epftPool, 'divEPFTdrag2', divEPFTdrag2) + call mpas_pool_get_array(am_epftPool, 'uuTWACorr', uuTWACorr) + call mpas_pool_get_array(am_epftPool, 'vvTWACorr', vvTWACorr) + call mpas_pool_get_array(am_epftPool, 'uvTWACorr', uvTWACorr) + call mpas_pool_get_array(am_epftPool, 'epeTWA', epeTWA) + call mpas_pool_get_array(am_epftPool, 'eddyFormDragZonal', eddyFormDragZonal) + call mpas_pool_get_array(am_epftPool, 'eddyFormDragMerid', eddyFormDragMerid) + + call mpas_pool_get_array(am_epftPool, 'ErtelPVFlux' , ErtelPVFlux) + call mpas_pool_get_array(am_epftPool, 'ErtelPVFlux1', ErtelPVFlux1) + call mpas_pool_get_array(am_epftPool, 'ErtelPVFlux2', ErtelPVFlux2) + call mpas_pool_get_array(am_epftPool, 'ErtelPVTendency', ErtelPVTendency) + call mpas_pool_get_array(am_epftPool, 'ErtelPV', ErtelPV) + call mpas_pool_get_array(am_epftPool, 'ErtelPVGradZonal', ErtelPVGradZonal) + call mpas_pool_get_array(am_epftPool, 'ErtelPVGradMerid', ErtelPVGradMerid) + + ! Compute potentialDensity over the entire block to ensure it's valid for EPFT computation + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, & + nCells, 1, 'absolute', potentialDensity, & + err) !, timeLevelIn=1) + + !-------------------------------------------------- + ! Get variables associated to diabatic processes + !-------------------------------------------------- + !jas issue diabatic terms + ! diabaticHeating(nVertLevels,nCells)! "vertical velocity" in buoyancy space + !wCellCenter = 0.0 + ! Get diabaticTimeTendency of a buoyancy surface, omega with funny hat, if any. + !call any existing MPAS-O subroutines for this + + + !------------------------------------------------------------- + ! begin computation + !------------------------------------------------------------- + + !------------------------------------------------------------- + ! compute firstLayerBuoyCoor and lastLayerBuoyCoor + ! firstLayerBuoyCoor == top buoyancy coordinate to exist in each column + ! lastLayerBuoyCoor == bottom buoyancy coordinate to exist in each column + ! buoyancyMask == 1.0 between layers firstLayerBuoyCoor and lastLayerBuoyCoor + !------------------------------------------------------------- + call get_masks_in_buoyancy_coordinates(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, potentialDensityMidRef, & + firstLayerBuoyCoor, lastLayerBuoyCoor, buoyancyMask) + + !------------------------------------------------------------- + ! TEST for general consistency + !------------------------------------------------------------- + if(config_AM_eliassenPalm_debug) then + print *, ' ' + print *, 'timeLevel:', timeLevel + print *, 'nBuoyancyLayers:', timeLevel + print *, ' ' + print *, 'config_AM_eliassenPalm_rhomin_buoycoor, config_AM_eliassenPalm_rhomax_buoycoor' + print *, config_AM_eliassenPalm_rhomin_buoycoor, config_AM_eliassenPalm_rhomax_buoycoor + print *, '(config_AM_eliassenPalm_rhomax_buoycoor - config_AM_eliassenPalm_rhomin_buoycoor)/nBuoyancyLayers' + print *, (config_AM_eliassenPalm_rhomax_buoycoor - config_AM_eliassenPalm_rhomin_buoycoor)/nBuoyancyLayers + print *, 'potentialDensityTopRef (nBuoyancyLayers)' + print *, potentialDensityTopRef + print *, 'potentialDensityTopRef(2:nBuoyancyLayers)-potentialDensityTopRef(:nBuoyancyLayers-1)' + print *, potentialDensityTopRef(2:nBuoyancyLayers)-potentialDensityTopRef(:nBuoyancyLayers-1) + print *, 'potentialDensityMidRef (nBuoyancyLayers)' + print *, potentialDensityMidRef + print *, 'potentialDensityMidRef(2:nBuoyancyLayers)-potentialDensityMidRef(:nBuoyancyLayers-1)' + print *, potentialDensityMidRef(2:nBuoyancyLayers)-potentialDensityMidRef(:nBuoyancyLayers-1) + print *, 'nCells,nBuoyancyLayers', nCells,nBuoyancyLayers + print *, 'nCells*nBuoyancyLayers', nCells*nBuoyancyLayers + print *, 'No. valid cells in buoyancy coords sum(buoyancyMask)', sum(buoyancyMask) + print *, 'nCells*nVertLevels', nCells*nVertLevels + print *, 'sum(cellMask)', sum(cellMask) + print *, 'minval(potentialDensity), maxval(potentialDensity)' + print *, minval(potentialDensity), maxval(potentialDensity) + print *, 'minval(density), maxval(density)' + print *, minval(density), maxval(density) + endif + + !------------------------------------------------------------- + ! INTERPOLATION TEST 1 + ! stratified, horizontally uniform + ! Interpolating from z, rho to z, rho + !------------------------------------------------------------- + if(config_AM_eliassenPalm_debug) then + do i = 1, nCells + array1_3D(:,i) = -zMid(:,nCells/2) + array2_3D(:,i) = potentialDensity(:,nCells/2) + end do + print *, ' ' + print *, 'TEST1: Testing interpolatoin function' + print *, 'Interpolating from (z, rho) to (z, rho)' + print *, 'call linear_interp_1d_field_along_column(nVertLevels, nCells, ' & + // 'nVertLevels, maxLevelCell, array1_3D, array2_3D, array1_3D(:,1), array3_3D)' + + print *, 'sum(array1_3D)/nCells + sum(zMid(:,nCells/2))' + print *, sum(array1_3D)/nCells + sum(zMid(:,nCells/2)) + print *, 'sum(array1_3D)/nCells - sum(array1_3D(:,1))' + print *, sum(array1_3D)/nCells - sum(array1_3D(:,1)) + + call linear_interp_1d_field_along_column(nVertLevels, nCells, nVertLevels, & + maxLevelCell, array1_3D, array2_3D, array1_3D(:,1), array3_3D) + print *, 'array1_3D(:,1)' + print *, array1_3D(:,1) + print *, 'zMid(:,nCells/2)' + print *, zMid(:,nCells/2) + print *, 'array2_3D(:,1)' + print *, array2_3D(:,1) + print *, 'array3_3D(:,1)' + print *, array3_3D(:,1) + print *, 'array2_3D(:,1)-array3_3D(:,1)' + print *, array2_3D(:,1)-array3_3D(:,1) + + do i = 1,nCells + do k = 1, maxLevelCell(i) + RMSlocal1 = RMSlocal1 + & + ((array3_3D(k,i) - array2_3D(k,i)))**2 + !((array3_3D(k,i) - array2_3D(k,i))/array2_3D(k,i))**2 + end do + end do + endif + + !------------------------------------------------------------- + ! INTERPOLATION TEST 2 + ! Define a stratification where potential density varies linearly with depth. + ! Using reference potential density that varies linearly with index. + ! Interpolate z from that potential density to reference potential density. + ! Compare to expected values. + !------------------------------------------------------------- + if(config_AM_eliassenPalm_debug) then + do i = 1,nCells + do k = 1, nVertLevels + array1_3D(k,i) = config_AM_eliassenPalm_rhomin_buoycoor*1.02_RKIND + & + (zMid(k,i)-zMid(1,i)) * & + (config_AM_eliassenPalm_rhomax_buoycoor*0.98_RKIND & + - config_AM_eliassenPalm_rhomin_buoycoor*1.02_RKIND) / & + (zMid(nVertLevels,i) - zMid(1,i)) + array2_3D(k,i) = config_AM_eliassenPalm_rhomin_buoycoor*1.02_RKIND + & + (zTop(k,i)-zMid(1,i)) * & + (config_AM_eliassenPalm_rhomax_buoycoor*0.98_RKIND & + - config_AM_eliassenPalm_rhomin_buoycoor*1.02_RKIND) / & + (zMid(nVertLevels,i) - zMid(1,i)) + end do + end do + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, array1_3D, zMid, potentialDensityMidRef, array1_3Dbuoy) + + do i = 1,nCells + do k = 1, nBuoyancyLayers + array2_3Dbuoy(k,i) = zMid(1,i) + & + (potentialDensityMidRef(k) - config_AM_eliassenPalm_rhomin_buoycoor*1.02_RKIND) * & + (zMid(nVertLevels,i) - zMid(1,i)) / & + (config_AM_eliassenPalm_rhomax_buoycoor*0.98_RKIND & + - config_AM_eliassenPalm_rhomin_buoycoor*1.02_RKIND) + end do + end do + do i = 1,nCells + do k = 1, nBuoyancyLayers + RMSlocal2 = RMSlocal2 + & + ((array1_3Dbuoy(k,i) - array2_3Dbuoy(k,i))/array2_3Dbuoy(k,i))**2 + end do + end do + print *, ' ' + print *, 'TEST2: Testing interpolation function' + print *, 'interpolating a linear function' + print *, 'array1_3Dbuoy(:,nCells/2)' + print *, array1_3Dbuoy(:,nCells/2) + print *, 'array2_3Dbuoy(:,nCells/2)' + print *, array2_3Dbuoy(:,nCells/2) + print *, 'array1_3Dbuoy(:,nCells/2) - array2_3Dbuoy(:,nCells/2)' + print *, array1_3Dbuoy(:,nCells/2) - array2_3Dbuoy(:,nCells/2) + endif + + + !------------------------------------------------------------- + ! check to see if at any point in the domain: + ! potentialDensity < potentialDensityTopRef(1) + ! potentialDensity > potentialDensityTopRef(nBuoyancyLayersP1) + ! either case means that buoyancy coordinate does not span the fluid domain + !------------------------------------------------------------- + call check_potentialDensityRef_range(nVertLevels, nCells, maxLevelCell, potentialDensity) + + !------------------------------------------------------------- + ! interpolate state variable from z-space into buoyancy-space + !------------------------------------------------------------- + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, zMid, & + potentialDensityMidRef, heightMidBuoyCoor) + + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, zMid, & + potentialDensityTopRef, heightTopBuoyCoor) + + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, velocityZonal, & + potentialDensityMidRef, uMidBuoyCoor) + + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, velocityMeridional, & + potentialDensityMidRef, vMidBuoyCoor) + + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, density, & + potentialDensityMidRef, densityMidBuoyCoor) + + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, density, & + potentialDensityTopRef, densityTopBuoyCoor) + + ! Diabatic terms + !call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + ! maxLevelCell, potentialDensity, wCellCenter, potentialDensityTopRef, wMidBuoyCoor) + + !------------------------------------------------------------- + ! fill in data above firstLayerBuoyCoor and below lastLayerBuoyCoor + !------------------------------------------------------------- + do i = 1, nCells + do k = 1, firstLayerBuoyCoor(i)-1 + heightMidBuoyCoor(k,i) = zTop(1,i) + heightTopBuoyCoor(k,i) = zTop(1,i) + uMidBuoyCoor(k,i) = velocityZonal(1,i) + vMidBuoyCoor(k,i) = velocityMeridional(1,i) + densityMidBuoyCoor(k,i) = density(1,i) + densityTopBuoyCoor(k,i) = density(1,i) + ! diabatic + !wMidBuoyCoor(k,i) = wCellCenter(1,i) + end do + do k = lastLayerBuoyCoor(i) + 1, nBuoyancyLayers + heightMidBuoyCoor(k,i) = -bottomDepth(i) + heightTopBuoyCoor(k,i) = -bottomDepth(i) + uMidBuoyCoor(k,i) = velocityZonal(maxLevelCell(i),i) + vMidBuoyCoor(k,i) = velocityMeridional(maxLevelCell(i),i) + densityMidBuoyCoor(k,i) = density(maxLevelCell(i),i) + densityTopBuoyCoor(k,i) = density(maxLevelCell(i),i) + ! diabatic + !wMidBuoyCoor(k,i) = wCellCenter(maxLevelCell(i),i) + end do + heightInterfaceBuoyCoor(1:nBuoyancyLayers,i) = heightTopBuoyCoor(1:nBuoyancyLayers,i) + heightInterfaceBuoyCoor(nBuoyancyLayers+1,i) = -bottomDepth(i) + end do + + !------------------------------------------------------------- + ! compute sigma, aka "layer thickness", units of s^2 + !------------------------------------------------------------- + call computeSigma(nCells, nBuoyancyLayers, & + heightInterfaceBuoyCoor, buoyancyInterfaceRef, sigma) + + !------------------------------------------------------------- + ! using data interpolated to buoyancy space, compute Montgomery potential + !------------------------------------------------------------- + call computeMontgomeryPotential(nBuoyancyLayers, nCells, atmosphericPressure, & + densityMidBuoyCoor, potentialDensityMidRef, heightInterfaceBuoyCoor, montgPotBuoyCoor) + + !------------------------------------------------------------- + ! compute the normal derivative of Montgomery potential at cell edges + !------------------------------------------------------------- + call computeNormalGradientOnEdge(nBuoyancyLayers, nCells, nEdges, & + meshPool, montgPotBuoyCoor, montgPotNormalGradOnEdge) + + !------------------------------------------------------------- + ! reconstruct full gradient vector at cell centers + !------------------------------------------------------------- + call mpas_reconstruct(meshPool, montgPotNormalGradOnEdge, & + montgPotGradX, montgPotGradY, montgPotGradZ, & + montgPotGradZonal, montgPotGradMerid, includeHalos=.true.) + + !------------------------------------------------------------- + ! Increment first-order running ensemble average fields + !------------------------------------------------------------- + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, buoyancyMask, buoyancyMaskEA) + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, sigma, sigmaEA) + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, heightMidBuoyCoor, heightMidBuoyCoorEA) + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, montgPotBuoyCoor, montgPotBuoyCoorEA) + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, montgPotGradZonal, montgPotGradZonalEA) + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, montgPotGradMerid, montgPotGradMeridEA) + + !------------------------------------------------------------- + ! Increment second-order running ensemble average fields + !------------------------------------------------------------- + wrk3DBuoyCoor = heightMidBuoyCoor * heightMidBuoyCoor + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, heightMidBuoyCoorSqEA) + + wrk3DBuoyCoor = heightMidBuoyCoor * montgPotGradZonal + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, heightMGradZonalEA) + + wrk3DBuoyCoor = heightMidBuoyCoor * montgPotGradMerid + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, heightMGradMeridEA) + + wrk3DBuoyCoor = uMidBuoyCoor * sigma + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, usigmaEA) + + wrk3DBuoyCoor = vMidBuoyCoor * sigma + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, vsigmaEA) + + ! Diabatic terms + !wrk3DBuoyCoor = wMidBuoyCoor * sigma + !call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, varpisigmaEA) + varpisigmaEA = 0.0_RKIND + + !------------------------------------------------------------- + ! Increment third-order running ensemble average fields + !------------------------------------------------------------- + wrk3DBuoyCoor = uMidBuoyCoor * uMidBuoyCoor * sigma + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, uusigmaEA) + + wrk3DBuoyCoor = vMidBuoyCoor * vMidBuoyCoor * sigma + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, vvsigmaEA) + + wrk3DBuoyCoor = uMidBuoyCoor * vMidBuoyCoor * sigma + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, uvsigmaEA) + + ! Diabatic terms + !wrk3DBuoyCoor = uMidBuoyCoor * wMidBuoyCoor * sigma + !call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, uvarpisigmaEA) + uvarpisigmaEA = 0.0_RKIND + + ! Diabatic terms + !wrk3DBuoyCoor = vMidBuoyCoor * wMidBuoyCoor* sigma + !call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, wrk3DBuoyCoor, vvarpisigmaEA) + vvarpisigmaEA = 0.0_RKIND + + !------------------------------------------------------------- + ! update number of samples in ensemble average + !------------------------------------------------------------- + nSamplesEA = nSamplesEA + 1 + + !------------------------------------------------------------- + ! compute the thickness-weighted average velocity + ! based on current estimate of ensemble-average state + !------------------------------------------------------------- + call calculateTWA(nBuoyancyLayers, nCells, nBuoyancyLayers, sigmaEA, usigmaEA, uTWA) + call calculateTWA(nBuoyancyLayers, nCells, nBuoyancyLayers, sigmaEA, vsigmaEA, vTWA) + ! Diabatic terms + !call calculateTWA(nBuoyancyLayers, nCells, nBuoyancyLayers, sigmaEA, varpisigmaEA, varpiTWA) + varpiTWA = 0.0_RKIND + + !------------------------------------------------------------- + ! compute the Eliassen-Palm flux tensor + ! based on current estimate of ensemble-average state + !------------------------------------------------------------- + call calculateEPFTfromTWA(nBuoyancyLayers, nCells, & + sigmaEA, heightMidBuoyCoorEA, & + heightMidBuoyCoorSqEA, montgPotGradZonalEA, montgPotGradMeridEA, & + heightMGradZonalEA, heightMGradMeridEA, uTWA, vTWA, varpiTWA, & + uusigmaEA, vvsigmaEA, uvsigmaEA, uvarpisigmaEA, vvarpisigmaEA, EPFT) + + !------------------------------------------------------------- + ! Calculate eddy correlations + !------------------------------------------------------------- + call calculateCorrelationfromTWA(nBuoyancyLayers, nCells, & + sigmaEA, uTWA, uTWA, uuSigmaEA, uuTWACorr) + call calculateCorrelationfromTWA(nBuoyancyLayers, nCells, & + sigmaEA, vTWA, vTWA, vvSigmaEA, vvTWACorr) + call calculateCorrelationfromTWA(nBuoyancyLayers, nCells, & + sigmaEA, uTWA, vTWA, uvSigmaEA, uvTWACorr) + call calculateEPEfromTWA(nBuoyancyLayers, nCells, & + sigmaEA, heightMidBuoyCoorEA, heightMidBuoyCoorSqEA, epeTWA) + call calculateEddyFormDragFromTWA(nBuoyancyLayers, nCells, sigmaEA, & + heightMidBuoyCoorEA, montgPotGradZonalEA, heightMGradZonalEA, eddyFormDragZonal) + call calculateEddyFormDragFromTWA(nBuoyancyLayers, nCells, sigmaEA, & + heightMidBuoyCoorEA, montgPotGradMeridEA, heightMGradMeridEA, eddyFormDragMerid) + + !------------------------------------------------------------- + ! compute the total force from the EPFT: div(EPFT) + !------------------------------------------------------------- + call calculateDivEPFT(config_AM_eliassenPalm_debug, & + domain % on_a_sphere, rho_sw, nBuoyancyLayers, nCells, nEdges, & + meshPool, buoyancyMidRef, sigmaEA, buoyancyMaskEA, EPFT, divEPFT) + ! decompose the vector into its components for output + divEPFT1 = divEPFT(1,:,:) + divEPFT2 = divEPFT(2,:,:) + + !------------------------------------------------------------- + ! compute the force from horizontal shear component of the EPFT + !------------------------------------------------------------- + wrkTensor = 0.0_RKIND + wrkTensor(1:2,1:2,:,:) = EPFT(1:2,1:2,:,:) + call calculateDivEPFT(config_AM_eliassenPalm_debug, & + domain % on_a_sphere, rho_sw, nBuoyancyLayers, nCells, nEdges, & + meshPool, buoyancyMidRef, sigmaEA, buoyancyMaskEA, wrkTensor, wrkVector) + divEPFTshear1 = wrkVector(1,:,:) + divEPFTshear2 = wrkVector(2,:,:) + + !------------------------------------------------------------- + ! compute the force from vertical form drag component of the EPFT + !------------------------------------------------------------- + wrkTensor = 0.0_RKIND + wrkTensor(3,1:2,:,:) = EPFT(3,1:2,:,:) + call calculateDivEPFT(config_AM_eliassenPalm_debug, & + domain % on_a_sphere, rho_sw, nBuoyancyLayers, nCells, nEdges, & + meshPool, buoyancyMidRef, sigmaEA, buoyancyMaskEA, wrkTensor, wrkVector) + divEPFTdrag1 = wrkVector(1,:,:) + divEPFTdrag2 = wrkVector(2,:,:) + + !------------------------------------------------------------- + ! transform div(EPFT) into a flux of Ertel PV + !------------------------------------------------------------- + call calculateErtelPVFlux(nCells, nBuoyancyLayers, sigmaEA, divEPFT, ErtelPVFlux) + ErtelPVFlux1 = ErtelPVFlux(1,:,:) + ErtelPVFlux2 = ErtelPVFlux(2,:,:) + + !------------------------------------------------------------- + ! compute Ertel PV tendency from Ertel PV fluxes, div(ErtelPVFlux) + !------------------------------------------------------------- + call calculateErtelPVTendencyFromPVFlux(config_AM_eliassenPalm_debug, & + domain % on_a_sphere, nBuoyancyLayers, nCells, nEdges, & + meshPool, sigmaEA, ErtelPVFlux, ErtelPVTendency) + + !------------------------------------------------------------- + ! compute Ertel PV based on EA and TWA fields + !------------------------------------------------------------- + call computeErtelPV(nCells, nBuoyancyLayers, nEdges, meshPool, & + fCell, uTWA, vTWA, sigmaEA, ErtelPV) + + !------------------------------------------------------------- + ! compute the normal derivative of EPV at cell edges + !------------------------------------------------------------- + call computeNormalGradientOnEdge(nBuoyancyLayers, nCells, nEdges, & + meshPool, ErtelPV, ErtelPVNormalGradOnEdge) + + !------------------------------------------------------------- + ! reconstruct full gradient vector at cell centers + !------------------------------------------------------------- + call mpas_reconstruct(meshPool, ErtelPVNormalGradOnEdge, & + ErtelPVGradX, ErtelPVGradY, ErtelPVGradZ, ErtelPVGradZonal, ErtelPVGradMerid, includeHalos=.true.) + + !------------------------------------------------------------- + ! compute the vertical derivative of uTWA + !------------------------------------------------------------- + call computeVerticalDerivative(nCells, nBuoyancyLayers, & + firstLayerBuoyCoor, lastLayerBuoyCoor, heightMidBuoyCoor, uTWA, duTWAdz) + + !------------------------------------------------------------- + ! compute the vertical derivative of vTWA + !------------------------------------------------------------- + call computeVerticalDerivative(nCells, nBuoyancyLayers, & + firstLayerBuoyCoor, lastLayerBuoyCoor, heightMidBuoyCoor, vTWA, dvTWAdz) + + !------------------------------------------------------------- + ! Compute the geometric decomposition in terms of angles and + ! eccentricities using the entries of EPFT. + ! (not yet implemented) + !------------------------------------------------------------- + !call eddyGeomDecompEPFT(EPFT, ...) + + + + !------------------------------------------------------------- + ! TEST: + ! calculate potential vorticity fluxes using curl of u + !------------------------------------------------------------- + if(config_AM_eliassenPalm_debug) then + + call mpas_pool_get_array(diagnosticsPool, 'relativeVorticityCell', relativeVorticityCell) + + ! store relVortMidBuoyCoor in array1_3Dbuoy + call linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, relativeVorticityCell, & + potentialDensityMidRef, array1_3Dbuoy) + + do i = 1,nCells + do k=firstLayerBuoyCoor(i), lastLayerBuoyCoor(i) + PVMidBuoyCoor(k,i) = (fCell(i) + array1_3Dbuoy(k,i) ) / sigma(k,i) + end do + end do + + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, & + uMidBuoyCoor, uMidBuoyCoorEA) + + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, & + vMidBuoyCoor, vMidBuoyCoorEA) + + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, & + PVMidBuoyCoor, PVMidBuoyCoorEA) + + wrk3DBuoyCoor = uMidBuoyCoor * PVMidBuoyCoor + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, & + wrk3DBuoyCoor, uPVMidBuoyCoorEA) + + wrk3DBuoyCoor = vMidBuoyCoor * PVMidBuoyCoor + call updateEnsembleAverage(nBuoyancyLayers, nCells, nSamplesEA, & + wrk3DBuoyCoor, vPVMidBuoyCoorEA) + + PVFluxTest(1,:,:) = uPVMidBuoyCoorEA - uMidBuoyCoorEA * PVMidBuoyCoorEA + PVFluxTest(2,:,:) = vPVMidBuoyCoorEA - vMidBuoyCoorEA * PVMidBuoyCoorEA + + do i = 1,nCells + do k = firstLayerBuoyCoor(i), lastLayerBuoyCoor(i) + RMSPVFlux1Local = RMSPVFlux1local + & + ( ErtelPVFlux(1,k,i) - PVFLuxTest(1,k,i) )**2 + RMSPVFlux2Local = RMSPVFlux2local + & + ( ErtelPVFlux(2,k,i) - PVFLuxTest(2,k,i) )**2 + end do + end do + + end if + + + !------------------------------------------------------------- + ! deallocate scratch space and test space variables + !------------------------------------------------------------- + call mpas_deallocate_scratch_field(firstLayerBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(lastLayerBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(heightMidBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(heightTopBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(heightInterfaceBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(sigmaField, .true.) + call mpas_deallocate_scratch_field(montgPotBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(montgPotNormalGradOnEdgeField, .true.) + call mpas_deallocate_scratch_field(uMidBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(vMidBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(densityMidBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(densityTopBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(buoyancyMaskField, .true.) + call mpas_deallocate_scratch_field(montgPotGradXField, .true.) + call mpas_deallocate_scratch_field(montgPotGradYField, .true.) + call mpas_deallocate_scratch_field(montgPotGradZField, .true.) + call mpas_deallocate_scratch_field(montgPotGradZonalField, .true.) + call mpas_deallocate_scratch_field(montgPotGradMeridField, .true.) + call mpas_deallocate_scratch_field(wrk3DnVertLevelsP1Field, .true.) + call mpas_deallocate_scratch_field(wrk3DnVertLevelsField, .true.) + call mpas_deallocate_scratch_field(wrk3DBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(ErtelPVNormalGradOnEdgeField, .true.) + call mpas_deallocate_scratch_field(ErtelPVGradXField, .true.) + call mpas_deallocate_scratch_field(ErtelPVGradYField, .true.) + call mpas_deallocate_scratch_field(ErtelPVGradZField, .true.) + call mpas_deallocate_scratch_field(wrkVectorField, .true.) + call mpas_deallocate_scratch_field(wrkTensorField, .true.) + + call mpas_deallocate_scratch_field(array1_3DField, .true.) + call mpas_deallocate_scratch_field(array2_3DField, .true.) + call mpas_deallocate_scratch_field(array3_3DField, .true.) + call mpas_deallocate_scratch_field(array1_3DbuoyField, .true.) + call mpas_deallocate_scratch_field(array2_3DbuoyField, .true.) + call mpas_deallocate_scratch_field(PVMidBuoyCoorField, .true.) + call mpas_deallocate_scratch_field(PVMidBuoyCoorEAField, .true.) + call mpas_deallocate_scratch_field(uMidBuoyCoorEAField, .true.) + call mpas_deallocate_scratch_field(vMidBuoyCoorEAField, .true.) + call mpas_deallocate_scratch_field(uPVMidBuoyCoorEAField, .true.) + call mpas_deallocate_scratch_field(vPVMidBuoyCoorEAField, .true.) + call mpas_deallocate_scratch_field(PVFluxTestField, .true.) + + !------------------------------------------------------------- + ! update test variables + !------------------------------------------------------------- + nCellsCum = nCellsCum + nCells + + !------------------------------------------------------------- + ! move to the next block + !------------------------------------------------------------- + block => block % next + + end do + + !------------------------------------------------------------- + ! TESTS: tallying up tests across processors. + !------------------------------------------------------------- + if(config_AM_eliassenPalm_debug) then + RMSglobal1 = 1.0e+36_RKIND + call mpas_dmpar_sum_int(dminfo, nCellsCum, nCellsGlobal) + call mpas_dmpar_sum_real(dminfo, RMSlocal1, RMSglobal1) + call mpas_dmpar_sum_real(dminfo, RMSlocal2, RMSglobal2) + + if (dminfo % my_proc_id == IO_NODE) then + print *, ' ' + print *, 'RKIND=', RKIND + print *, 'rms relative error interp test1:',sqrt(RMSglobal1/nCellsGlobal) + print *, 'rms relative error interp test2:',sqrt(RMSglobal2/nCellsGlobal) + + print *, ' ' + endif + + call mpas_dmpar_sum_real(dminfo, sum(abs(ErtelPVFlux(1,:,:))), RMSglobal1) + call mpas_dmpar_max_real(dminfo, maxval(abs(ErtelPVFlux(1,:,:))), RMSglobal2) + if (dminfo % my_proc_id == IO_NODE) then + print *, 'Checking ErtelPVFlux' + print *, 'Global sum(abs(ErtelPVFlux(1,:,:))) = ', RMSglobal1 + print *, 'Global max(abs(ErtelPVFlux(1,:,:))) = ', RMSglobal2 + endif + + call mpas_dmpar_sum_real(dminfo, sum(abs(ErtelPVFlux(2,:,:))), RMSglobal1) + call mpas_dmpar_max_real(dminfo, maxval(abs(ErtelPVFlux(2,:,:))), RMSglobal2) + if (dminfo % my_proc_id == IO_NODE) then + print *, 'Global sum(abs(ErtelPVFlux(2,:,:))) = ', RMSglobal1 + print *, 'Global max(abs(ErtelPVFlux(2,:,:))) = ', RMSglobal2 + endif + + call mpas_dmpar_sum_real(dminfo, sum(abs(ErtelPVFlux(3,:,:))), RMSglobal1) + call mpas_dmpar_max_real(dminfo, maxval(abs(ErtelPVFlux(3,:,:))), RMSglobal2) + if (dminfo % my_proc_id == IO_NODE) then + print *, 'Global sum(abs(ErtelPVFlux(3,:,:))) = ', RMSglobal1 + print *, 'Global max(abs(ErtelPVFlux(3,:,:))) = ', RMSglobal2 + endif + + call mpas_dmpar_sum_real(dminfo, RMSPVFlux1Local, RMSPVFlux1global) + call mpas_dmpar_sum_real(dminfo, RMSPVFlux2Local, RMSPVFlux2global) + if (dminfo % my_proc_id == IO_NODE) then + print *, 'rms relative error test PVFlux1:',sqrt(RMSPVFlux1global/nCellsGlobal) + print *, 'rms relative error test PVFlux2:',sqrt(RMSPVFLux2global/nCellsGlobal) + + print *, ' ' + endif + + endif + + if(config_AM_eliassenPalm_debug) then + call mpas_log_write( 'exiting ocn_compute_epft') + end if + + end subroutine ocn_compute_eliassen_palm!}}} + +!*********************************************************************** +! +! routine ocn_restart_eliassen_palm +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_eliassen_palm(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_eliassen_palm!}}} + +!*********************************************************************** +! +! routine ocn_finalize_eliassen_palm +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Juan A. Saenz +!> \date May 2015 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_eliassen_palm(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_eliassen_palm!}}} + + + +!*********************************************************************** +! Local routines start here +!*********************************************************************** + + +!*********************************************************************** +! +! subroutine get_masks_in_buoyancy_coordinates +! +!> \brief Get masks in buoyancy coordinates +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> firstLayerBuoyCoor(iCell): the index of the smallest reference density that +!> is >= the smallest actual density in a column. +!> lastLayerBuoyCoor(iCell): the index of the largest reference density that is <= the +!> largest actual density in a column. +!> Set masks in buoyancy coordinates: +!> mask = 1: cell is a valid ocean cell +!> mask = 0: cell is not a valid ocean cell +!> Required: potentialDensityMidRef monotonically increases with index value +! +!----------------------------------------------------------------------- + subroutine get_masks_in_buoyancy_coordinates(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, potentialDensity, potentialDensityMidRef, & + firstLayerBuoyCoor, lastLayerBuoyCoor, buoyancyMask)!{{{ + + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nVertLevels, nCells, nBuoyancyLayers + integer, dimension(nCells), intent(in) :: maxLevelCell + real (kind=RKIND), dimension(nVertLevels, nCells), intent(in) :: potentialDensity + real (kind=RKIND), dimension(nBuoyancyLayers), intent(in) :: potentialDensityMidRef + + !----------------------------------------------------------------- + ! intent(out) + !----------------------------------------------------------------- + integer, dimension(nCells), intent(out) :: firstLayerBuoyCoor + integer, dimension(nCells), intent(out) :: lastLayerBuoyCoor + real (kind=RKIND), dimension(nBuoyancyLayers, nCells), intent(out) :: buoyancyMask + + !----------------------------------------------------------------- + ! Local variables + !----------------------------------------------------------------- + integer :: iCell, maxLevel, kB, kBBottom, kBTop + + !----------------------------------------------------------------- + ! initialize fields assuming no density layers exist + !----------------------------------------------------------------- + firstLayerBuoyCoor = nBuoyancyLayers + lastLayerBuoyCoor = 1 + buoyancyMask = 0.0_RKIND + + !----------------------------------------------------------------- + ! loop over all cells + ! when searching from the top down + ! find first target density greater than density in top model layer + ! when searching from the bottom up + ! find first target density less than density in bottom model layer + !----------------------------------------------------------------- + do iCell = 1, nCells + + ! find the bottom model layer for this cell + maxLevel = maxLevelCell(iCell) + + ! search top down + do kB = 1, nBuoyancyLayers + if (potentialDensityMidRef(kB) >= potentialDensity(1,iCell) ) then + firstLayerBuoyCoor(iCell) = kB + exit + endif + enddo + + ! search bottom up + do kB = nBuoyancyLayers, 1, -1 + if (potentialDensityMidRef(kB) <= potentialDensity(maxLevel,iCell) ) then + lastLayerBuoyCoor(iCell) = kB + exit + endif + enddo + + ! set mask to 1 inside the range + do kB = firstLayerBuoyCoor(iCell), lastLayerBuoyCoor(iCell) + buoyancyMask(kB,iCell) = 1.0_RKIND + enddo + + enddo + + end subroutine get_masks_in_buoyancy_coordinates!}}} + + +!*********************************************************************** +! +! subroutine check_potentialDensityRef_range +! +!> \brief Check if the range of values in potentialDensityTopRef contains current state +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> Check if the range of values in potentialDensityTopRef contains all values in +!> potentialDensity of the current state. +!> If not, print a warning. +! +!----------------------------------------------------------------------- + subroutine check_potentialDensityRef_range(nVertLevels, nCells, maxLevelCell, & + potentialDensity)!{{{ + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nVertLevels, nCells + integer, dimension(nCells), intent(in) :: maxLevelCell + real (kind=RKIND), dimension(nVertLevels, nCells), intent(in) :: potentialDensity + + !----------------------------------------------------------------- + ! Local variables + !----------------------------------------------------------------- + integer :: k, iCell, iCellMinBound, iCellMaxBound + logical :: printWarning + + real (kind=RKIND), pointer :: config_AM_eliassenPalm_rhomin_buoycoor, & + config_AM_eliassenPalm_rhomax_buoycoor + + call mpas_pool_get_config(ocnConfigs, 'config_AM_eliassenPalm_rhomin_buoycoor', & + config_AM_eliassenPalm_rhomin_buoycoor) + call mpas_pool_get_config(ocnConfigs, 'config_AM_eliassenPalm_rhomax_buoycoor', & + config_AM_eliassenPalm_rhomax_buoycoor) + + printWarning = .false. + iCellMinBound = -1 + iCellMaxBound = -1 + + do iCell = 1, nCells + if (potentialDensity(1,iCell) < config_AM_eliassenPalm_rhomin_buoycoor) then + printWarning = .true. + iCellMinBound = iCell + exit + end if + if (potentialDensity(maxLevelCell(iCell),iCell) > config_AM_eliassenPalm_rhomax_buoycoor) then + printWarning = .true. + iCellMaxBound = iCell + exit + end if + enddo + + if (printWarning) then + call mpas_log_write( ' *** WARNING: in eliassen_palm analysis member, subroutine check_potentialDensityRef_range.', MPAS_LOG_WARN) + call mpas_log_write( ' One or more columns in the ocean domain have densities that are not') + call mpas_log_write( ' contained in the defined buoyancy space of the EPFT module') + if (iCellMinBound.gt.0) call mpas_log_write( ' Fluid is lighter than min buoyancy at cell: $i',MPAS_LOG_OUT,intArgs=(/ iCellMinBound /) ) + if (iCellMaxBound.gt.0) call mpas_log_write( ' Fluid is lighter than max buoyancy at cell: $i',MPAS_LOG_OUT,intArgs=(/ iCellMaxBound /) ) + end if + + end subroutine check_potentialDensityRef_range!}}} + + +!*********************************************************************** +! +! subroutine linear_interp_1d_field_along_column +! +!> \brief One-dimensional interpolation in buoyancy coordinates +!> \author Juan A. Saenz, Todd Ringler +!> \date 17 December 2013 +!> \details +!> Interpolate a field yFieldIn residing on xFieldIn onto xColumnOut and store +!> and return in yFieldOut. +!> Interpolation is done using one-dimensional interpolation along xColumnOut. +!> Required: xFieldIn monotonically increases with index value +! +!----------------------------------------------------------------------- + + subroutine linear_interp_1d_field_along_column(nVertLevels, nCells, nBuoyancyLayers, & + maxLevelCell, xFieldIn, yFieldIn, xColumnOut, yFieldOut)!{{{ + + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nVertLevels, nCells, nBuoyancyLayers + integer, dimension(nCells), intent(in) :: maxLevelCell + real (kind=RKIND), dimension(nVertLevels, nCells), intent(in) :: xFieldIn + real (kind=RKIND), dimension(nVertLevels, nCells), intent(in) :: yFieldIn + real (kind=RKIND), dimension(nBuoyancyLayers), intent(in) :: xColumnOut + + !----------------------------------------------------------------- + ! intent(out) + !----------------------------------------------------------------- + real (kind=RKIND), dimension(nBuoyancyLayers, nCells), intent(out) :: yFieldOut + + !----------------------------------------------------------------- + ! Local variables + !----------------------------------------------------------------- + integer :: iCell, maxLevel, kB, kBBottom, kBTop, kDataAbove, kDataBelow, kData + real (kind=RKIND) :: dx, dy + real (kind=RKIND), dimension(nVertLevels, nCells) :: xSrc ! source data + real (kind=RKIND), dimension(nBuoyancyLayers) :: xDst ! destination data + + ! jas issue: the code below works for monotonically decreasing arrays. + ! however above it expects arguments that are monotonically increasing arrays. + xSrc = -xFieldIn + xDst = -xColumnOut + + !----------------------------------------------------------------- + ! test for monoticity of xSrc + !----------------------------------------------------------------- + ! jas issue : to do + + !----------------------------------------------------------------- + ! initialize intent(out) + !----------------------------------------------------------------- + yFieldOut = 0.0_RKIND + + !----------------------------------------------------------------- + ! loop over all columns + !----------------------------------------------------------------- + do iCell = 1, nCells + + ! find the index of the bottom level of a column + maxLevel = maxLevelCell(iCell) + + ! Monotonically decreasing xSrc required + ! Find index of first element in xDst that is inside xSrc(:,iCell) + kBTop = 1 + do kB = 1, nBuoyancyLayers + ! the following line ensures that + ! if all xDst > xSrc(1,iCell) then kBTop = nBuoyancyLayers + kBTop = kB + if (xDst(kB) <= xSrc(1,iCell) ) then + exit + endif + enddo + + !find last target buoyancy level inside column + kBBottom = nBuoyancyLayers + do kB = nBuoyancyLayers, 1, -1 + ! the following line ensures that + ! if all xDst < xSrc(1,iCell) then kBBottom = 1 + kBBottom = kB + if (xDst(kB) >= xSrc(maxLevel,iCell) ) then + exit + endif + enddo + + ! For the target x levels outside the x range in a column: + ! set data from 1:kBTop-1 to surface values + do kB = 1, kBTop-1 + yFieldOut(kB,iCell) = yFieldIn(1,iCell) + enddo + !set data from kBBottom+1:nBuoyancyLayers to bottom values + do kB = kBBottom+1, nBuoyancyLayers + yFieldOut(kB,iCell) = yFieldIn(maxLevel,iCell) + enddo + + ! The interpolation: + ! for the target buoyancy levels within the buoyancy range in a column: + kDataAbove = 1 + kDataBelow = kDataAbove + 1 + do kB = kBTop, kBBottom + ! for each xDst(kB) value, find the corresponding upper and lower + ! xSrc value in the field data, then interpolate y between those values. + if (xDst(kB) < xSrc(kDataBelow,iCell)) then + do kData = kDataBelow, maxLevel + if (xDst(kB) > xSrc(kData,iCell) ) then + kDataBelow=kData + kDataAbove=kDataBelow-1 + exit + endif + enddo + endif + + dx = xSrc(kDataBelow,iCell) - xSrc(kDataAbove,iCell) + dy = yFieldIn(kDataBelow,iCell) - yFieldIn(kDataAbove,iCell) + yFieldOut(kB,iCell) = yFieldIn(kDataAbove,iCell) + & + (xDst(kB)-xSrc(kDataAbove,iCell)) * dy/dx + enddo + + enddo + + end subroutine linear_interp_1d_field_along_column!}}} + + +!*********************************************************************** +! +! subroutine computeSigma +! +!> \brief Calculate the inverse of the derivative of buoy wrt z +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This subroutine calculates the inverse of the derivative of buoy wrt z. +! +!----------------------------------------------------------------------- + + subroutine computeSigma(nCells, nLayers, & + heightInterface, buoyInterface, sigma)!{{{ + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nCells, nLayers + real (kind=RKIND), dimension(:,:), intent(in) :: heightInterface + real (kind=RKIND), dimension(:), intent(in) :: buoyInterface + + !----------------------------------------------------------------- + ! intent(out) + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(out) :: sigma + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: iCell, k + + !----------------------------------------------------------------- + ! initialize sigma assuming zero thickness layers everywhere + !----------------------------------------------------------------- + sigma = 0.0_RKIND + + !----------------------------------------------------------------- + ! loop over all column, sigam = delta z / delta b + ! note: positive z points "up", i.e. from k+1 to k + ! note: positive b points "up", i.e. from k+1 to k + !----------------------------------------------------------------- + do iCell = 1, nCells + do k = 1,nLayers + sigma(k,iCell) = (heightInterface(k+1,iCell) - heightInterface(k,iCell)) / & + (buoyInterface(k+1) - buoyInterface(k)) + enddo + enddo + + end subroutine computeSigma!}}} + + + +!*********************************************************************** +! +! subroutine computeMontgomeryPotential +! +!> \brief Compute the Montgomery potential +!> \author Juan A. Saenz, Todd Ringler +!> \date May 20015 +!> \details +!> This subroutine computes the Montgomery potential using eqn 2.10 in +!> R.L. Higdon and R.A. Szoeke (1997), J. Comp. Phys. 135, 30–53, Article No. CP975733 +! +!> Montgomery Potential (MP) in layer k is MP(k-1) + pInterface(k)*deltaAlpha +!> where deltaAlpha is (1/potDens(k) - 1/potDens(k-1)) +!> and pInterface(k) is the pressure at interface k, i.e. at top of layer k. +!> +!> Montgomery potential of a layer is constant across layer +!----------------------------------------------------------------------- + + subroutine computeMontgomeryPotential(nLayers, nCells, pSurface, & + density, potDens, heightInterface, MontgomeryPotential)!{{{ + + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nLayers, nCells + real (kind=RKIND), dimension(nCells), intent(in) :: pSurface + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: density + real (kind=RKIND), dimension(nLayers), intent(in) :: potDens + real (kind=RKIND), dimension(nLayers+1, nCells), intent(in) :: heightInterface + + !----------------------------------------------------------------- + ! intent(out) + !----------------------------------------------------------------- + real (kind=RKIND), dimension(nLayers, nCells), intent(out) :: MontgomeryPotential + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: iCell, k + real (kind=RKIND) :: pInterfacek ! pressure at interface k, i.e. at top of layer k + + !----------------------------------------------------------------- + ! initialize intent(out) + !----------------------------------------------------------------- + MontgomeryPotential = 0.0_RKIND + + !----------------------------------------------------------------- + ! loop over all columns + !----------------------------------------------------------------- + do iCell = 1, nCells + + !----------------------------------------------------------------- + ! compute Montgomery potential in top buoyancy layer + ! at present, assume atmosphere surface pressure is zero (or a constant) + !----------------------------------------------------------------- + pInterfacek = 0.0_RKIND + k = 1 + MontgomeryPotential(k,iCell) = pInterfacek/potDens(k) + gravity*heightInterface(k,iCell) + + !----------------------------------------------------------------- + ! compute Montgomery potential by accumulating jump across each layer interace + ! Jump == pressure at interface * (alpha (below interface) - alpha (above interface)) + !----------------------------------------------------------------- + do k = 2, nLayers + pInterfacek = pInterfacek + & + gravity * ( heightInterface(k-1,iCell)-heightInterface(k,iCell) ) * density(k-1,iCell) + MontgomeryPotential(k,iCell) = MontgomeryPotential(k-1,iCell) + & + pInterfacek * ( 1/potDens(k) - 1/potDens(k-1) ) + enddo + + enddo + + end subroutine computeMontgomeryPotential!}}} + + + +!*********************************************************************** +! +! subroutine computeNormalGradientOnEdge +! +!> \brief Compute the gradient of a quantity that exists on cell centers +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This subroutine computes the normal derivative of a scalar +!> quantity that exists on cell centers. Routine assumes that +!> data is valid throughout the entire column, as is the case +!> when working in buoyancy coordinates +! +!----------------------------------------------------------------------- + + subroutine computeNormalGradientOnEdge(nBLayers, nCells, nEdges, & + meshPool, field, normalGradOnEdge)!{{{ + + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nBLayers, nCells, nEdges + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + real (kind=RKIND), dimension(:,:), intent(in) :: field + + !----------------------------------------------------------------- + ! intent(out) + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(out) :: normalGradOnEdge + + !----------------------------------------------------------------- + !local variables + !----------------------------------------------------------------- + integer :: iEdge, k, cell1, cell2, kMin, kMax + integer, pointer :: nBuoyancyLayers + integer, dimension(:,:), pointer :: cellsOnEdge + integer, dimension(:,:), pointer :: boundaryEdge + real (kind=RKIND), dimension(:), pointer :: dcEdge + real (kind=RKIND) :: invLength + + !----------------------------------------------------------------- + ! assign pointers + !----------------------------------------------------------------- + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'boundaryEdge', boundaryEdge) + + call mpas_pool_get_dimension(meshPool, 'nBuoyancyLayers', nBuoyancyLayers) + + !----------------------------------------------------------------- + ! initialize intent(out) + !----------------------------------------------------------------- + normalGradOnEdge = 0.0_RKIND + + !----------------------------------------------------------------- + ! loop over edges, compute derivative as (cell2 - cell1) / dc + !----------------------------------------------------------------- + do iEdge = 1, nEdges + ! do not compute the normal derivative at land/sea interface + if (boundaryEdge(1,iEdge) == 1) then + normalGradOnEdge(:,iEdge) = 0.0_RKIND + else + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + invLength = 1.0_RKIND / dcEdge(iEdge) + do k = 1, nBuoyancyLayers + normalGradOnEdge(k,iEdge) = ( field(k,cell2) - field(k,cell1) )*invLength + enddo + end if + enddo + + end subroutine computeNormalGradientOnEdge!}}} + + + +!*********************************************************************** +! +! subroutine updateEnsembleAverage +! +!> \brief Update ensemble average +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This subroutine updates the ensemble average +! +!----------------------------------------------------------------------- + + subroutine updateEnsembleAverage(nLayers, nCells, nSamples, A, Abar)!{{{ + + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nLayers, nCells, nSamples + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: A + + !----------------------------------------------------------------- + ! intent(inout) + !----------------------------------------------------------------- + real (kind=RKIND), dimension(nLayers, nCells), intent(inout) :: Abar + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: iCell, k + + !----------------------------------------------------------------- + ! Abar is the current estimate of the ensemble average + ! on input, Abar was built using nSamples of A + ! To update Abar, we multiple Abar times nSamples, add in the + ! current value (A), then normalize by (nSamples + 1) + !----------------------------------------------------------------- + do iCell = 1, nCells + do k = 1, nLayers + Abar(k,iCell) = (nSamples * Abar(k,iCell) + A(k,iCell)) / (nSamples + 1.0_RKIND) + enddo + enddo + + end subroutine updateEnsembleAverage!}}} + + +!*********************************************************************** +! +! subroutine calculateTWA +! +!> \brief Calculate the thickness weighted average +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This subroutine calculates the thickness weighted average +! +!----------------------------------------------------------------------- + subroutine calculateTWA(nLayers, nCells, nBuoyancyLayers, sigmaEA, & + varSigmaEA, varTWA)!{{{ + + !----------------------------------------------------------------- + ! intent(in) + !----------------------------------------------------------------- + integer, intent(in) :: nLayers, nCells, nBuoyancyLayers + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: sigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: varSigmaEA + + !----------------------------------------------------------------- + ! intent(inout) + !----------------------------------------------------------------- + real (kind=RKIND), dimension(nLayers, nCells), intent(out) :: varTWA + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: iCell, k + + !----------------------------------------------------------------- + ! initialize intent(out) + !----------------------------------------------------------------- + varTWA = 0.0_RKIND + + do iCell = 1, nCells + do k = 1,nBuoyancyLayers + varTWA(k,iCell) = varSigmaEA(k,iCell) / max(epsilonEPFT,sigmaEA(k,iCell)) + enddo + enddo + + end subroutine calculateTWA!}}} + + +!*********************************************************************** +! +! subroutine calculateEPFTfromTWA +! +!> \brief Calculate the Eliassen-Palm flux tensor from TWAs +!> \author Juan A. Saenz +!> \date January 2014 +!> \details +!> This subroutine calculates the Eliassen and Palm flux tensor from thickness +!> weighted averages. +!> EPTF_pq(x,y,z) is represented as EPFT(p,q,k,i) +!----------------------------------------------------------------------- + + subroutine calculateEPFTfromTWA(nLayers, nCells, & + sigmaEA, heightEA, heightSqEA, MxEA, MyEA, HMxEA, HMyEA, uTWA, vTWA, varpiTWA, & + uuSigmaEA, vvSigmaEA, uvSigmaEA, uvarpisigmaEA, vvarpisigmaEA, Etensor)!{{{ + implicit none + integer, intent(in) :: nLayers, nCells + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: sigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: heightEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: heightSqEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: MxEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: MyEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: HMxEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: HMyEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: uTWA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: vTWA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: varpiTWA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: uuSigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: vvSigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: uvSigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: uvarpisigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: vvarpisigmaEA + real (kind=RKIND), dimension(3, 3, nLayers, nCells), intent(out) :: Etensor + + ! local variables + integer :: iCell, kLayer + real (kind=RKIND) :: sigma + real (kind=RKIND) :: uppupp, vppvpp, uppvpp, uppwpp, vppwpp + real (kind=RKIND) :: HpHp, HpMxp, HpMyp + real (kind=RKIND) :: dummy1, dummy2, dummy3 + + Etensor = 0.0_RKIND + + do iCell = 1, nCells + do kLayer = 1,nLayers + + sigma = max(sigmaEA(kLayer,iCell), epsilonEPFT) + + uppupp = uuSigmaEA(kLayer,iCell) / sigma - uTWA(kLayer,iCell)*uTWA(kLayer,iCell) + vppvpp = vvSigmaEA(kLayer,iCell) / sigma - vTWA(kLayer,iCell)*vTWA(kLayer,iCell) + uppvpp = uvSigmaEA(kLayer,iCell) / sigma - uTWA(kLayer,iCell)*vTWA(kLayer,iCell) + uppwpp = uvarpisigmaEA(kLayer,iCell) / sigma - uTWA(kLayer,iCell)*varpiTWA(kLayer,iCell) + vppwpp = vvarpisigmaEA(kLayer,iCell) / sigma - vTWA(kLayer,iCell)*varpiTWA(kLayer,iCell) + + HpHp = heightSqEA(kLayer,iCell) - heightEA(kLayer,iCell)*heightEA(kLayer,iCell) + HpMxp = HMxEA(kLayer,iCell) - heightEA(kLayer,iCell) * MxEA(kLayer,iCell) + HpMyp = HMyEA(kLayer,iCell) - heightEA(kLayer,iCell) * MyEA(kLayer,iCell) + + !EPTF_pq(x,y,z) is represented as EPFT(p,q,kLayer,iCell) + !column 1: Eu + Etensor(1,1,kLayer,iCell) = uppupp + 0.5_RKIND * HpHp / sigma + Etensor(2,1,kLayer,iCell) = uppvpp + Etensor(3,1,kLayer,iCell) = uppwpp + HpMxp / sigma + + !column 2: Ev + Etensor(1,2,kLayer,iCell) = uppvpp + Etensor(2,2,kLayer,iCell) = vppvpp + 0.5_RKIND * HpHp / sigma + Etensor(3,2,kLayer,iCell) = vppwpp + HpMyp / sigma + + !column 3: Ew + Etensor(1,3,kLayer,iCell) = 0.0_RKIND + Etensor(2,3,kLayer,iCell) = 0.0_RKIND + Etensor(3,3,kLayer,iCell) = 0.0_RKIND + + enddo + enddo + + end subroutine calculateEPFTfromTWA!}}} + + +!*********************************************************************** +! +! subroutine calculateCorrelationfromTWA +! +!> \brief Calculate the eddy correlation from TWAs +!> \author Juan A. Saenz +!> \date July 2015 +!> \details +!> This subroutine calculates the eddy kinetic energy from thickness +!> weighted averages. +!----------------------------------------------------------------------- + + subroutine calculateCorrelationfromTWA(nLayers, nCells, & + sigmaEA, uTWA, vTWA, uvSigmaEA, uvCorr)!{{{ + implicit none + integer, intent(in) :: nLayers, nCells + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: sigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: uTWA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: vTWA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: uvSigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(out) :: uvCorr + + ! local variables + integer :: iCell, kLayer + real (kind=RKIND) :: sigma + real (kind=RKIND) :: uppupp, vppvpp + + uvCorr = 0.0_RKIND ! jas issue: assign a mask value instead + + do iCell = 1, nCells + do kLayer = 1,nLayers + sigma = max(sigmaEA(kLayer,iCell), epsilonEPFT) + uvCorr(kLayer, iCell) = uvSigmaEA(kLayer,iCell) / sigma - uTWA(kLayer,iCell)*vTWA(kLayer,iCell) + enddo + enddo + + end subroutine calculateCorrelationfromTWA!}}} + + +!*********************************************************************** +! +! subroutine calculateEPEfromTWA +! +!> \brief Calculate the eddy potential energy from TWAs +!> \author Juan A. Saenz +!> \date July 2015 +!> \details +!> This subroutine calculates the eddy potential energy from thickness +!> weighted averages. +!----------------------------------------------------------------------- + + subroutine calculateEPEfromTWA(nLayers, nCells, & + sigmaEA, heightEA, heightSqEA, epe)!{{{ + implicit none + integer, intent(in) :: nLayers, nCells + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: sigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: heightEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: heightSqEA + real (kind=RKIND), dimension(nLayers, nCells), intent(out) :: epe + + ! local variables + integer :: iCell, kLayer + real (kind=RKIND) :: sigma + real (kind=RKIND) :: HpHp + + epe = 0.0_RKIND + + do iCell = 1, nCells + do kLayer = 1,nLayers + + sigma = max(sigmaEA(kLayer,iCell), epsilonEPFT) + HpHp = heightSqEA(kLayer,iCell) - heightEA(kLayer,iCell)*heightEA(kLayer,iCell) + + epe(kLayer,iCell) = 0.5_RKIND * HpHp / sigma + + enddo + enddo + + end subroutine calculateEPEfromTWA!}}} + + +!*********************************************************************** +! +! subroutine calculateEddyFormDragfromTWA +! +!> \brief Calculate the eddy form drag from TWAs +!> \author Juan A. Saenz +!> \date July 2015 +!> \details +!> This subroutine calculates the eddy form drag from thickness +!> weighted averages. +!----------------------------------------------------------------------- + + subroutine calculateEddyFormDragfromTWA(nLayers, nCells, & + sigmaEA, heightEA, MxEA, HMxEA, formDragX)!{{{ + implicit none + integer, intent(in) :: nLayers, nCells + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: sigmaEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: heightEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: MxEA + real (kind=RKIND), dimension(nLayers, nCells), intent(in) :: HMxEA + real (kind=RKIND), dimension(nLayers, nCells), intent(out) :: formDragX + + ! local variables + integer :: iCell, kLayer + real (kind=RKIND) :: sigma + real (kind=RKIND) :: HpMxp + + formDragX = 0.0_RKIND + + do iCell = 1, nCells + do kLayer = 1,nLayers + + sigma = max(sigmaEA(kLayer,iCell), epsilonEPFT) + HpMxp = HMxEA(kLayer,iCell) - heightEA(kLayer,iCell) * MxEA(kLayer,iCell) + + formDragX(kLayer,iCell) = HpMxp / sigma + enddo + enddo + + end subroutine calculateEddyFormDragfromTWA!}}} + + +!*********************************************************************** +! +! subroutine calculateDivEPFT +! +!> \brief Calculate the divergence of EPFT +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This subroutine calculates the divergence of the Eliassen-Palm flux tensor. +!> This is done by calculating the divergence of the column vectors in the tensor. +!> The divergence of a vector v in buoyancy coordinates is given by (Young 2012): +!> div of v = sigma^-1 (sigma * v_i)_xi +! +!----------------------------------------------------------------------- + + subroutine calculateDivEPFT(debugFlag, onASphere, rho0, nLayers, nCells, nEdges, & + meshPool, buoyancyMidRef, sigmaEA, buoyancyMaskEA, tensorCellIn, vectorCellOut)!{{{ + + use mpas_vector_operations + + logical, intent(in) :: debugFlag + logical, intent(in) :: onASphere + integer, intent(in) :: nLayers, nCells, nEdges + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), intent(in) :: rho0 ! config_density0 + real (kind=RKIND), dimension(:), intent(in) :: buoyancyMidRef + real (kind=RKIND), dimension(:,:), intent(in) :: sigmaEA + real (kind=RKIND), dimension(:,:), intent(in) :: buoyancyMaskEA + real (kind=RKIND), dimension(:,:,:,:), intent(in) :: tensorCellIn + real (kind=RKIND), dimension(:,:,:), intent(out) :: vectorCellOut + + ! local variables + logical :: includeHalo + integer :: q, iCell, kLayer, iComponent + real (kind=RKIND) :: wrk, wrkAbove, wrkBelow, sigma, db + real (kind=RKIND), dimension(:), pointer :: latCell + real (kind=RKIND), dimension(:), pointer :: lonCell + integer, dimension(:,:), pointer :: edgeSignOnCell + real (kind=RKIND), dimension(:,:), allocatable :: scalarWrk1 + real (kind=RKIND), dimension(:,:,:), allocatable :: vectorCellWrk1 + real (kind=RKIND), dimension(:,:,:), allocatable :: vectorCellWrk2 + real (kind=RKIND), dimension(:,:,:), allocatable :: vectorEdgeWrk1 + real (kind=RKIND), dimension(:), allocatable :: vertVector + + ! variables used for testing and debugging + real (kind=RKIND), dimension(:), allocatable :: divExact + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell + integer, dimension(:,:), pointer :: boundaryCell + + if (debugFlag) then + allocate(divExact(nCells+1)) + end if + + allocate(scalarWrk1(nLayers,nCells+1)) + allocate(vectorCellWrk1(3,nLayers,nCells+1)) + allocate(vectorCellWrk2(3,nLayers,nCells+1)) + allocate(vectorEdgeWrk1(3,nLayers,nEdges+1)) + allocate(vertVector(nLayers)) + + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + call mpas_pool_get_array(meshPool, 'boundaryCell', boundaryCell) + + includeHalo = .true. + + ! initialize work and intent(out) + vectorCellOut = 0.0_RKIND + + ! loop over all three column vectors + do q = 1, 3 + + scalarWrk1 = 0.0_RKIND + + ! horizontal derivatives + vectorCellWrk1 = tensorCellIn(:,q,:,:) + + ! weight the vector with sigmaEA(:,:) + do iComponent = 1,3 + vectorCellWrk1(iComponent,:,:) = sigmaEA(:,:)*vectorCellWrk1(iComponent,:,:) + enddo + + + ! use q=3 as a test vector + if (q.eq.3 .and. debugFlag) then + do iCell = 1,nCells + vectorCellWrk1(1,:,iCell) = xCell(iCell) + vectorCellWrk1(2,:,iCell) = yCell(iCell) + vectorCellWrk1(3,:,iCell) = zCell(iCell) + ! the analytical divergence: + divExact(iCell) = -1.0_RKIND*sin(lonCell(iCell)) * & + (1.0_RKIND + 2.0_RKIND*sin(latCell(iCell))) !+ 3.0*sin(latCell(iCell)) + enddo + endif + + ! zero the vertical component of vectorCellWrk1 + ! the vertical will be treated seperately below + vectorCellWrk1(3,:,:) = 0.0_RKIND + + if (onASphere) then + + ! convert from lat/lon to Cartesian (x,y,z) + do iCell = 1,nCells + do kLayer = 1,nLayers + call mpas_vector_LonLatR_to_R3(vectorCellWrk1(:,kLayer,iCell), & + lonCell(iCell), latCell(iCell), vectorCellWrk2(:,kLayer,iCell)) + end do + end do + + ! copy vector measured in x,y,z back into Wrk1 + vectorCellWrk1 = vectorCellWrk2 + + end if + + ! average the vector from cell centers to cell edges + call mpas_vector_R3Cell_to_Edge(vectorCellWrk1, meshPool, & + vectorEdgeWrk1) + + ! computed the divergence via the weak, line-integral form + call mpas_divergence_in_r3_buoyancy(vectorEdgeWrk1, meshPool, & + edgeSignOnCell, includeHalo, scalarWrk1) + + ! use q=3 as a test vector + if (q.eq.3 .and. debugFlag) then + print *, ' ' + print *, 'calculateDivEPFT:' + print *, 'layer, RMS relative error on layer :' + do kLayer = 1,nLayers + wrk = sqrt( & + sum( & + ( & + (divExact(:)-scalarWrk1(kLayer,:))/ max(abs(divExact(:)),1.0e-15_RKIND) & + )**2 * & + (1.0_RKIND - boundaryCell(1,:)) & + ) / nCells ) + print *, kLayer, wrk + enddo + endif + + + ! Normalize by sigma, after having taken the derivative of sigma * v_i + if (q < 3 .or. .not. debugFlag) then + do iCell = 1,nCells + do kLayer = 1,nLayers + sigma = max(sigmaEA(kLayer,iCell), epsilonEPFT) + scalarWrk1(kLayer,iCell) = scalarWrk1(kLayer,iCell) / sigma + end do + end do + end if + + + ! vertical derivative + do iCell = 1,nCells + + ! copy the vertical component of EPFT into a work array + vertVector(:) = tensorCellIn(3,q,:,iCell) + + ! use q=3 as a test vector + if (q.eq.3 .and. debugFlag) then + vertVector(:) = 0.0_RKIND + endif + + + do kLayer = 1,nLayers + + wrk = 0.0_RKIND + + if(kLayer.eq.1) then + wrkAbove=sigmaEA(kLayer,iCell)*vertVector(kLayer) + wrkBelow=sigmaEA(kLayer+1,iCell)*vertVector(kLayer+1) + db = buoyancyMidRef(kLayer)-buoyancyMidRef(kLayer+1) + else if (kLayer.eq.nLayers) then + wrkAbove=sigmaEA(kLayer-1,iCell)*vertVector(kLayer-1) + wrkBelow=sigmaEA(kLayer,iCell)*vertVector(kLayer) + db = buoyancyMidRef(kLayer-1)-buoyancyMidRef(kLayer) + else + wrkAbove=sigmaEA(kLayer-1,iCell)*vertVector(kLayer-1) + wrkBelow=sigmaEA(kLayer+1,iCell)*vertVector(kLayer+1) + db = buoyancyMidRef(kLayer-1)-buoyancyMidRef(kLayer+1) + endif + + sigma = max(sigmaEA(kLayer,iCell), epsilonEPFT) + wrk = (wrkAbove - wrkBelow) / db / sigma + + scalarWrk1(kLayer,iCell) = scalarWrk1(kLayer,iCell) + wrk + + end do ! kLayer = 1,nLayers + + end do ! iCell = 1,nCells + + vectorCellOut(q,:,:) = scalarWrk1 + + end do !do q=1,3 + + if (debugFlag) then + deallocate(divExact) + end if + deallocate(scalarWrk1) + deallocate(vectorCellWrk1) + deallocate(vectorCellWrk2) + deallocate(vectorEdgeWrk1) + deallocate(vertVector) + + end subroutine calculateDivEPFT!}}} + + + +!*********************************************************************** +! +! subroutine calculateErtelPVFlux +! +!> \brief Calculate the Ertel potential vorticity fluxes +!> \author Juan A. Saenz +!> \date January 2014 +!> \details +!> This subroutine calculates the Ertel potential vorticity fluxes +!> using the divergence of EPFT, as outlined in eqn 129 of Young 2012. +! +!----------------------------------------------------------------------- + + subroutine calculateErtelPVFlux(nCells, nBuoyancyLayers, & + sigma, divEPFT, ErtelPVFlux)!{{{ + + integer, intent(in) :: nCells, nBuoyancyLayers + real (kind=RKIND), dimension(:,:), intent(in) :: sigma + real (kind=RKIND), dimension(:,:,:), intent(in) :: divEPFT + real (kind=RKIND), dimension(:,:,:), intent(out) :: ErtelPVFlux + + ! local variables + integer :: i, k + + ErtelPVFlux(1,:,:) = divEPFT(2,:,:) + ErtelPVFlux(2,:,:) = -1.0_RKIND * divEPFT(1,:,:) + ErtelPVFlux(3,:,:) = 0.0_RKIND + + do i = 1, nCells + do k = 1,nBuoyancyLayers + ErtelPVFlux(:,k,i) = ErtelPVFlux(:,k,i) / max(sigma(k,i),epsilonEPFT) + end do + end do + + end subroutine calculateErtelPVFlux + + +!*********************************************************************** +! +! subroutine calculateErtelPVTendencyFromPVFlux +! +!> \brief Calculate the Ertel PV tendency from Ertel PV flux +!> \author Juan A. Saenz, Todd Ringler +!> \date May 2015 +!> \details +!> This subroutine calculates the Ertel PV tendency as the divergence of +!> the Ertel PV flux, where the latter only has horizontal components. +! +!----------------------------------------------------------------------- + + subroutine calculateErtelPVTendencyFromPVFlux(debugFlag, onASphere, nLayers, nCells, nEdges, & + meshPool, sigma, vectorCell, divVectorCell)!{{{ + + use mpas_vector_operations + + logical, intent(in) :: debugFlag + logical, intent(in) :: onASphere + integer, intent(in) :: nLayers, nCells, nEdges + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), dimension(:,:), intent(in) :: sigma + real (kind=RKIND), dimension(:,:,:), intent(in) :: vectorCell + real (kind=RKIND), dimension(:,:), intent(out) :: divVectorCell + + ! local variables + logical :: includeHalo + integer :: i, k, iComponent + real (kind=RKIND), dimension(:), pointer :: latCell + real (kind=RKIND), dimension(:), pointer :: lonCell + integer, dimension(:,:), pointer :: edgeSignOnCell, boundaryCell + real (kind=RKIND), dimension(:,:,:), allocatable :: vectorCellWrk1 + real (kind=RKIND), dimension(:,:,:), allocatable :: vectorCellWrk2 + real (kind=RKIND), dimension(:,:,:), allocatable :: vectorEdgeWrk1 + + ! test variables + real (kind=RKIND) :: wrk + real (kind=RKIND), dimension(:), allocatable :: divExact + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell + + if (debugFlag) then + allocate(divExact(nCells+1)) + end if + + + allocate(vectorCellWrk1(3,nLayers,nCells+1)) + allocate(vectorCellWrk2(3,nLayers,nCells+1)) + allocate(vectorEdgeWrk1(3,nLayers,nEdges)) !jas issue Todd had set this to nEdges+1 + + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + call mpas_pool_get_array(meshPool, 'boundaryCell', boundaryCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + + includeHalo = .true. + + divVectorCell = 0.0_RKIND + + ! copy vector into work array + vectorCellWrk1 = vectorCell + + ! weight the vector with sigma(:,:) + do iComponent = 1,3 + vectorCellWrk1(iComponent,:,:) = sigma(:,:)*vectorCellWrk1(iComponent,:,:) + enddo + + + if (debugFlag) then + do i= 1,nCells + vectorCellWrk1(1,:,i) = xCell(i) + vectorCellWrk1(2,:,i) = yCell(i) + vectorCellWrk1(3,:,i) = zCell(i) + ! the analytical divergence: + divExact(i) = -1.0_RKIND*sin(lonCell(i)) * & + (1.0_RKIND + 2.0_RKIND*sin(latCell(i))) !+ 3.0*sin(latCell(i)) + enddo + endif + + + if (onASphere) then + ! copy vector into work array + do i = 1,nCells + do k = 1,nLayers + call mpas_vector_LonLatR_to_R3(vectorCellWrk1(:,k,i), & + lonCell(i), latCell(i), vectorCellWrk2(:,k,i)) + end do + end do + + ! copy transformed vector back to Wrk1 + vectorCellWrk1 = vectorCellWrk2 + end if + + ! average vector from cell centers to cell edges + call mpas_vector_R3Cell_to_Edge(vectorCellWrk1, meshPool, & + vectorEdgeWrk1) + + ! computed divergence via weak-form, line integral + call mpas_divergence_in_r3_buoyancy(vectorEdgeWrk1, meshPool, edgeSignOnCell, & + includeHalo, divVectorCell) + + + if (debugFlag) then + print *, ' ' + print *, 'calculateErtelPVTendencyFromPVFlux:' + print *, 'k, RMS relative error on layer:' + do k= 1,nLayers + wrk = sqrt( & + sum( & + ( & + (divExact(:)-divVectorCell(k,:))/ max(abs(divExact(:)),1.0e-15_RKIND) & + )**2 * & + (1.0_RKIND - boundaryCell(1,:)) & + ) / nCells ) + print *, k, wrk + enddo + endif + + + if (.not. debugFlag) then + do i = 1,nCells + do k = 1,nLayers + divVectorCell(k,i) = divVectorCell(k,i) / max(sigma(k,i), epsilonEPFT) + end do + end do + end if + + if (debugFlag) then + deallocate(divExact) + end if + deallocate(vectorCellWrk1) + deallocate(vectorCellWrk2) + deallocate(vectorEdgeWrk1) + + end subroutine calculateErtelPVTendencyFromPVFlux!}}} + + + +!*********************************************************************** +! +! subroutine computeErtelPV +! +!> \brief Calculate Ertel potential vorticity on buoyancy surfaces +!> \author Juan A. Saenz +!> \date January 2014 +!> \details +!> This subroutine calculates Ertel potential voriticity in buoyancy surfaces +! +!----------------------------------------------------------------------- + + subroutine computeErtelPV(nCells, nLayers, nEdges, meshPool, & + fCell, uCell, vCell, sigma, ErtelPV) + + use mpas_vector_reconstruction + + integer, intent(in) :: nCells, nLayers, nEdges + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), dimension(:), intent(in) :: fCell + real (kind=RKIND), dimension(:,:), intent(in) :: uCell, vCell + real (kind=RKIND), dimension(:,:), intent(in) :: sigma + real (kind=RKIND), dimension(:,:), intent(out) :: ErtelPV + + ! local variables + integer :: i, k + real (kind=RKIND), dimension(:,:), allocatable :: velNormalGradOnEdge + real (kind=RKIND), dimension(:,:), allocatable :: velGradX, velGradY, velGradZ + real (kind=RKIND), dimension(:,:), allocatable :: velGradZonal, velGradMerid + real (kind=RKIND), dimension(:,:), allocatable :: vGradZonal, uGradMerid + + allocate(velNormalGradOnEdge(nLayers, nEdges)) ! jas issue this one seems correct + allocate(velGradX(nLayers, nCells)) ! jas issue gets sent to routine where looping over nCellsSolve + allocate(velGradY(nLayers, nCells)) ! jas issue gets sent to routine where looping over nCellsSolve + allocate(velGradZ(nLayers, nCells)) ! jas issue gets sent to routine where looping over nCellsSolve + allocate(velGradZonal(nLayers, nCells)) ! jas issue gets sent to routine where looping over nCellsSolve + allocate(velGradMerid(nLayers, nCells)) ! jas issue gets sent to routine where looping over nCellsSolve + allocate(uGradMerid(nLayers, nCells)) ! jas issue gets assigned array of size is nCells + allocate(vGradZonal(nLayers, nCells)) ! jas issue gets assigned array of size is nCells + + ! calculate derivative of uTWA with respect to the meridional direction + call computeNormalGradientOnEdge(nLayers, nCells, nEdges, & + meshPool, uCell, velNormalGradOnEdge) + call mpas_reconstruct(meshPool, velNormalGradOnEdge, & + velGradX, velGradY, velGradZ, & + velGradZonal, velGradMerid, includeHalos=.true.) + uGradMerid = velGradMerid + + ! calculate derivative of vTWA with respect to the zonal direction + call computeNormalGradientOnEdge(nLayers, nCells, nEdges, & + meshPool, vCell, velNormalGradOnEdge) + call mpas_reconstruct(meshPool, velNormalGradOnEdge, & + velGradX, velGradY, velGradZ, & + velGradZonal, velGradMerid, includeHalos=.true.) + vGradZonal = velGradZonal + + ErtelPV = 0.0_RKIND + + do i = 1, nCells + do k = 1,nLayers + ErtelPV(k,i) = (fCell(i) + vGradZonal(k,i) - uGradMerid(k,i))/max(sigma(k,i),epsilonEPFT) + end do + end do + + deallocate(velNormalGradOnEdge) + deallocate(velGradX) + deallocate(velGradY) + deallocate(velGradZ) + deallocate(velGradZonal) + deallocate(velGradMerid) + deallocate(vGradZonal) + deallocate(uGradMerid) + + + end subroutine computeErtelPV + + +!*********************************************************************** +! +! subroutine computeVerticalDerivative +! +!> \brief Calculate the the vertical derivative, in depth coordinates, of a scalar +!> \author Juan A. Saenz +!> \date July, 2015 +!> \details +!> This subroutine calculates the vertical derivative, in depth coordinates, of a scalar. +!> The scalar is assumed to exist in the middle of a cell layer. +!> The vertical derivative in the middle of the cell layer is returned. +! +!----------------------------------------------------------------------- + + subroutine computeVerticalDerivative(nCells, nLayers, & + firstLayer, lastLayer, heightMid, field, derivativeField)!{{{ + integer, intent(in) :: nCells, nLayers + integer, dimension(nCells), intent(in) :: firstLayer, lastLayer + real (kind=RKIND), dimension(:,:), intent(in) :: heightMid + real (kind=RKIND), dimension(:,:), intent(in) :: field + real (kind=RKIND), dimension(:,:), intent(out) :: derivativeField + + ! local variables + integer :: iCell, kLayer + real (kind=RKIND) :: wrkAbove, wrkBelow, dz + + derivativeField(nLayers, 1:nCells) = 0.0_RKIND + + do iCell = 1,nCells + + if ( lastLayer(iCell) > firstLayer(iCell) ) then + wrkAbove = field(firstLayer(iCell),iCell) + wrkBelow = field(firstLayer(iCell)+1,iCell) + dz = heightMid(firstLayer(iCell),iCell)-heightMid(firstLayer(iCell)+1,iCell) + + derivativeField(firstLayer(iCell), iCell) = (wrkAbove - wrkBelow) / dz + + do kLayer = firstLayer(iCell)+1, lastLayer(iCell)-1 + + wrkAbove = field(kLayer-1,iCell) + wrkBelow = field(kLayer+1,iCell) + dz = heightMid(kLayer-1,iCell)-heightMid(kLayer+1,iCell) + + derivativeField(kLayer, iCell) = (wrkAbove - wrkBelow) / dz + + end do ! kLayer = firstLayer(iCell)+1, lastLayer(iCell)-1 + + wrkAbove = field(lastLayer(iCell)-1,iCell) + wrkBelow = field(lastLayer(iCell),iCell) + dz = heightMid(lastLayer(iCell)-1,iCell)-heightMid(lastLayer(iCell),iCell) + + derivativeField(lastLayer(iCell), iCell) = (wrkAbove - wrkBelow) / dz + end if + end do ! iCell = 1,nCells + end subroutine computeVerticalDerivative!}}} + + +!*********************************************************************** +! +! subroutine eddyGeomDecompEPFT +! +!> \brief Calculate the eddy geometric decomposition from EPFT +!> \author Juan A. Saenz +!> \date January 2014 +!> \details +!> This subroutine calculates the eddy geometric decomposition from EPFT +! +!----------------------------------------------------------------------- + + subroutine eddyGeomDecompEPFT()!sigmaRef, ErtelPVFlux, ErtelPVTendency)!{{{ + ! Compute the geometric decomposition in terms of angles and eccentricities using + ! the entries of EPFT. + + end subroutine eddyGeomDecompEPFT!}}} + + +!*********************************************************************** +! +! routine mpas_divergence_in_r3_buoyancy +! +!> \brief MPAS 3D divergence routine +!> \author Todd Ringler +!> \date 02/07/14 +!> \details +!> This routine computes the of an input vector. +!----------------------------------------------------------------------- + subroutine mpas_divergence_in_r3_buoyancy(vectorR3Edge, meshPool, & + edgeSignOnCell, includeHalo, divCell)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + vectorR3Edge !< Input: vector at edge, R3, indices (direction,verticalIndex,edgeIndex) + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + integer, dimension(:,:), intent(in) :: & + edgeSignOnCell !< Input: Direction of vector connecting cells + + logical, intent(in) :: & + includeHalo !< Input: If true, halo cells and edges are included in computation + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(out) :: & + divCell !< Output: scalar divergence, indices (verticalIndex,edgeIndex) + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iEdge, iCell, i, k, p + integer, pointer :: nVertLevels, nCells + + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: edgesOnCell + + real (kind=RKIND) :: invAreaCell + real (kind=RKIND) :: edgeNormalDotVector + real (kind=RKIND), dimension(:), pointer :: dvEdge, areaCell + real (kind=RKIND), dimension(:,:), pointer :: edgeNormalVectors + + call mpas_pool_get_dimension(meshPool, 'nBuoyancyLayers', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'edgeNormalVectors', edgeNormalVectors) + + divCell(:,:) = 0.0_RKIND + do iCell = 1, nCells + invAreaCell = 1.0_RKIND / areaCell(iCell) + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + do k = 1, nVertLevels + edgeNormalDotVector = 0.0_RKIND + do p=1,3 + edgeNormalDotVector = edgeNormalDotVector + & + edgeNormalVectors(p,iEdge)*vectorR3Edge(p,k,iEdge) + enddo + divCell(k,iCell) = divCell(k,iCell) - & + edgeSignOnCell(i,iCell) * dvEdge(iEdge) * invAreaCell * & + edgeNormalDotVector + end do + end do + end do + + end subroutine mpas_divergence_in_r3_buoyancy!}}} + + +!*********************************************************************** +! +! routine mpas_vector_R3Cell_to_Edge +! +!> \brief MPAS 3D divergence routine +!> \author Todd Ringler +!> \date 02/07/14 +!> \details +!> This routine averages a vector field from cells to edges +!----------------------------------------------------------------------- + subroutine mpas_vector_R3Cell_to_Edge(vectorCell, meshPool, & + vectorEdge) + + real (kind=RKIND), dimension(:,:,:), intent(in) :: vectorCell + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + real (kind=RKIND), dimension(:,:,:), intent(out) :: vectorEdge + + !local variables + integer :: iEdge, k, cell1, cell2 + integer, pointer :: nEdges, nBuoyancyLayers + integer, dimension(:,:), pointer :: cellsOnEdge, boundaryEdge + + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'boundaryEdge', boundaryEdge) + + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nBuoyancyLayers', nBuoyancyLayers) + + vectorEdge = 0.0_RKIND + + do iEdge = 1, nEdges + ! Enforce vector value of zero on boundary edges, e.g. no slip for velocities + if (boundaryEdge(1,iEdge) == 1) then + vectorEdge(:,:,iEdge) = 0.0_RKIND + else + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + do k = 1, nBuoyancyLayers + vectorEdge(:,k,iEdge) = 0.5_RKIND*( vectorCell(:,k,cell2) + vectorCell(:,k,cell1) ) + enddo + end if + enddo + + end subroutine mpas_vector_R3Cell_to_Edge!}}} + + +end module ocn_eliassen_palm + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_global_stats.F b/src/core_ocean/analysis_members/mpas_ocn_global_stats.F index c989190b72..182867ee5a 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_global_stats.F +++ b/src/core_ocean/analysis_members/mpas_ocn_global_stats.F @@ -118,12 +118,13 @@ subroutine ocn_init_global_stats(domain, err)!{{{ open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_readme.txt',STATUS='UNKNOWN', POSITION='rewind') write (fileID,'(a)') 'readme file for MPAS-Ocean global statistics' - write (fileID,'(/,a)') 'stats_time.txt. contains: timeIndex, timestamp, CFLNumberGlobal' - write (fileID,'(/,a)') 'All other stats_*.txt. contain the following columns. Rows correspond to timestamps in rows of stats_time.txt' + write (fileID,'(/,a)') 'stats_time.txt. contains: timestamp, time (in days), CFLNumberGlobal' + write (fileID,'(/,a)') 'All other stats_*.txt. contain the following columns. Rows correspond to timestamps ' & + // 'in rows of stats_time.txt' write (fileID,'(a)') "See user's guide for units associated with these variables." i=1 - write (fileID,'(i5,a)') i,'. time, in days, using a 360 day calendar'; i=i+1 + write (fileID,'(i5,a)') i,'. time, in days'; i=i+1 write (fileID,'(i5,a)') i,'. layerThickness'; i=i+1 write (fileID,'(i5,a)') i,'. normalVelocity'; i=i+1 write (fileID,'(i5,a)') i,'. tangentialVelocity'; i=i+1 @@ -131,7 +132,8 @@ subroutine ocn_init_global_stats(domain, err)!{{{ write (fileID,'(i5,a)') i,'. relativeVorticity'; i=i+1 write (fileID,'(i5,a)') i,'. enstrophy = relativeVorticity**2'; i=i+1 write (fileID,'(i5,a)') i,'. kineticEnergyCell'; i=i+1 - write (fileID,'(i5,a)') i,'. normalizedAbsoluteVorticity = (relative vorticity + planetary vorticity)/layer thickness'; i=i+1 + write (fileID,'(i5,a)') i,'. normalizedAbsoluteVorticity = (relative vorticity + planetary vorticity)/layer ' & + // 'thickness'; i=i+1 write (fileID,'(i5,a)') i,'. pressure'; i=i+1 write (fileID,'(i5,a)') i,'. montgomeryPotential'; i=i+1 write (fileID,'(i5,a)') i,'. vertVelocityTop vertical velocity'; i=i+1 @@ -140,10 +142,12 @@ subroutine ocn_init_global_stats(domain, err)!{{{ write (fileID,'(i5,a)') i,'. highFreqThickness'; i=i+1 write (fileID,'(i5,a)') i,'. Tracers: usually T, S, then others in remaining columns' - write (fileID,'(/,a)') 'A chain of simple unix commands may be used to access a specific part of the data. For example,' - write (fileID,'(a)') 'to view the last three values of column seven in the global average, use:' - write (fileID,'(a)') "cat stats_avg.txt | awk '{print $7}' | tail -n3" - + write (fileID,'(/,a)') 'A chain of simple unix commands may be used to access a specific part of the data. ' & + // 'For example,' + write (fileID,'(a)') 'to view the last three values of column eight (KE) in the global average, use:' + write (fileID,'(a)') "cat stats_avg.txt | awk '{print $8}' | tail -n3" + write (fileID,'(a)') "cat stats_max.txt | awk '{print $8}' | tail -n3" + close (fileID) endif @@ -218,47 +222,76 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ type (dm_info) :: dminfo type (block_type), pointer :: block type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: scratchPool type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: tracersSurfaceFluxPool + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool integer :: err_tmp integer :: nCellsGlobal, nEdgesGlobal, nVerticesGlobal, iTracer - integer :: elementIndex, variableIndex, nVariables, nSums, nMaxes, nMins + integer :: iEdge, variableIndex, nVariables, nSums, nMaxes, nMins integer :: k, i, fileID integer :: timeYYYY, timeMM, timeDD, timeH, timeM, timeS - integer, pointer :: nVertLevels, nCellsSolve, nEdgesSolve, nVerticesSolve, num_tracers - character*1 timeChar - integer, parameter :: kMaxVariables = 1024 ! this must be a little more than double the number of variables to be reduced - integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop, maxLevelVertexBot - - character (len=StrKIND), pointer :: xtime - - real (kind=RKIND) :: volumeCellGlobal, volumeEdgeGlobal, CFLNumberGlobal, localCFL, localSum, areaCellGlobal, areaEdgeGlobal, areaTriangleGlobal, time_days - real (kind=RKIND), dimension(:), pointer :: areaCell, dcEdge, dvEdge, areaTriangle, areaEdge - real (kind=RKIND), dimension(:,:), pointer :: layerThickness, normalVelocity, tangentialVelocity, layerThicknessEdge, relativeVorticity, kineticEnergyCell, & - normalizedRelativeVorticityEdge, normalizedPlanetaryVorticityEdge, pressure, montgomeryPotential, vertAleTransportTop, vertVelocityTop, & - lowFreqDivergence, highFreqThickness, density - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + integer, pointer :: nVertLevels, nCellsSolve, nEdgesSolve, nVerticesSolve, num_activeTracers - real (kind=RKIND), dimension(:), pointer :: minGlobalStats,maxGlobalStats,sumGlobalStats, averages, rms, verticalSumMins, verticalSumMaxes + integer, parameter :: kMaxVariables = 1024 ! this must be a little more than double the number of variables to be reduced + integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop, maxLevelVertexBot, & + landiceMask + + character (len=StrKIND), pointer :: xtime, simulationStartTime + type (MPAS_Time_type) :: xtime_timeType, simulationStartTime_timeType + type (MPAS_TimeInterval_type) :: timeStep + + real (kind=RKIND) :: localCFL, localSum, dt, currentVolume + real (kind=RKIND), pointer :: volumeCellGlobal, volumeEdgeGlobal, CFLNumberGlobal, areaCellGlobal, & + areaEdgeGlobal, areaTriangleGlobal, totalVolumeChange, netFreshwaterInput, & + absoluteFreshWaterConservation, relativeFreshWaterConservation, & + landIceFloatingAreaSum + real (kind=RKIND), dimension(:), pointer :: areaCell, dcEdge, dvEdge, areaTriangle, areaEdge, landIceFloatingArea + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, normalVelocity, tangentialVelocity, layerThicknessEdge, & + relativeVorticity, kineticEnergyCell, normalizedRelativeVorticityEdge, & + normalizedPlanetaryVorticityEdge, pressure, montgomeryPotential, & + vertAleTransportTop, vertVelocityTop, lowFreqDivergence, highFreqThickness, & + density, layerThicknessPreviousTimestep, activeTracersSurfaceFlux, & + activeTracersPistonVelocity, activeTracersSurfaceRestoringValue + + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + real (kind=RKIND), dimension(:), pointer :: evaporationFlux, snowFlux + real (kind=RKIND), dimension(:), pointer :: seaIceFreshWaterFlux, riverRunoffFlux, iceRunoffFlux + real (kind=RKIND), dimension(:), pointer :: rainFlux, landIceFreshwaterFlux + real (kind=RKIND), dimension(:), pointer :: accumulatedLandIceMass, accumulatedLandIceHeat, & + accumulatedLandIceFrazilMass + + real (kind=RKIND), dimension(:,:), pointer :: frazilLayerThicknessTendency + + real (kind=RKIND), pointer :: daysSinceStartOfSim + real (kind=RKIND), dimension(:), pointer :: minGlobalStats,maxGlobalStats,sumGlobalStats, averages, rms, verticalSumMins, & + verticalSumMaxes real (kind=RKIND), dimension(kMaxVariables) :: sumSquares, reductions, sums, mins, maxes - real (kind=RKIND), dimension(kMaxVariables) :: sums_tmp, sumSquares_tmp, mins_tmp, maxes_tmp, averages_tmp, verticalSumMins_tmp, verticalSumMaxes_tmp + real (kind=RKIND), dimension(kMaxVariables) :: sums_tmp, sumSquares_tmp, mins_tmp, maxes_tmp, averages_tmp, & + verticalSumMins_tmp, verticalSumMaxes_tmp real (kind=RKIND), dimension(:,:), allocatable :: enstrophy, normalizedAbsoluteVorticity, workArray - logical, pointer :: thicknessFilterActive, globalStatsAMPKGActive + ! package flags + logical, pointer :: frazilIcePkgActive, landIceFluxesPkgActive + logical, pointer :: thicknessFilterActive + logical, pointer :: config_AM_globalStats_text_file character (len=StrKIND), pointer :: config_AM_globalStats_directory - err = 0 - - call mpas_pool_get_package(ocnPackages, 'globalStatsAMPKGActive', globalStatsAMPKGActive) + real (kind=RKIND), dimension(:), allocatable :: restoringSaltFlux - if ( .not. globalStatsAMPKGActive ) return + err = 0 dminfo = domain % dminfo + call mpas_pool_get_package(ocnPackages, 'landIceFluxesPKGActive', landIceFluxesPkgActive) + call mpas_pool_get_package(ocnPackages, 'frazilIceActive', frazilIcePkgActive) call mpas_pool_get_package(ocnPackages, 'thicknessFilterActive', thicknessFilterActive) ! write out data to Analysis Member output @@ -270,15 +303,30 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ call mpas_pool_get_array(globalStatsAMPool, 'avgGlobalStats', averages) call mpas_pool_get_array(globalStatsAMPool, 'vertSumMinGlobalStats', verticalSumMins) call mpas_pool_get_array(globalStatsAMPool, 'vertSumMaxGlobalStats', verticalSumMaxes) - - sums = 0.0 - sumSquares = 0.0 - mins = 1.0e34 - maxes = -1.0e34 - averages = 0.0 - verticalSumMins = 1.0e34 - verticalSumMaxes = -1.0e34 - reductions = 0.0 + call mpas_pool_get_array(globalStatsAMPool, 'areaCellGlobal', areaCellGlobal) + call mpas_pool_get_array(globalStatsAMPool, 'areaEdgeGlobal', areaEdgeGlobal) + call mpas_pool_get_array(globalStatsAMPool, 'areaTriangleGlobal', areaTriangleGlobal) + call mpas_pool_get_array(globalStatsAMPool, 'volumeCellGlobal', volumeCellGlobal) + call mpas_pool_get_array(globalStatsAMPool, 'volumeEdgeGlobal', volumeEdgeGlobal) + call mpas_pool_get_array(globalStatsAMPool, 'CFLNumberGlobal', CFLNumberGlobal) + call mpas_pool_get_array(globalStatsAMPool, 'landIceFloatingAreaSum', landIceFloatingAreaSum) + + call mpas_pool_get_array(globalStatsAMPool, 'totalVolumeChange', totalVolumeChange) + call mpas_pool_get_array(globalStatsAMPool, 'netFreshwaterInput', netFreshwaterInput) + call mpas_pool_get_array(globalStatsAMPool, 'absoluteFreshWaterConservation', absoluteFreshWaterConservation) + call mpas_pool_get_array(globalStatsAMPool, 'relativeFreshWaterConservation', relativeFreshWaterConservation) + + sums = 0.0_RKIND + sumSquares = 0.0_RKIND + mins = 1.0e34_RKIND + maxes = -1.0e34_RKIND + averages = 0.0_RKIND + verticalSumMins = 1.0e34_RKIND + verticalSumMaxes = -1.0e34_RKIND + reductions = 0.0_RKIND + + timeStep = mpas_get_clock_timestep(domain % clock, ierr=err_tmp) + call mpas_get_timeInterval(timeStep, dt=dt) block => domain % blocklist do while (associated(block)) @@ -288,12 +336,17 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ call mpas_pool_get_dimension(block % dimensions, 'nVerticesSolve', nVerticesSolve) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'globalStatsAM', globalStatsAMPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceFlux', tracersSurfaceFluxPool) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) - call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) + call mpas_pool_get_dimension(tracersPool, 'num_activeTracers', num_activeTracers) call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) @@ -304,8 +357,9 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ call mpas_pool_get_array(meshPool, 'maxLevelVertexBot', maxLevelVertexBot) call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessPreviousTimestep, 2) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 1) - call mpas_pool_get_array(statePool, 'tracers', tracers, 1) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) if(thicknessFilterActive) then call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergence, 1) call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThickness, 1) @@ -322,19 +376,52 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ call mpas_pool_get_array(diagnosticsPool, 'tangentialVelocity', tangentialVelocity) call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) call mpas_pool_get_array(diagnosticsPool, 'kineticEnergyCell', kineticEnergyCell) - call mpas_pool_get_array(diagnosticsPool, 'xtime', xtime) + call mpas_pool_get_array(diagnosticsPool, 'simulationStartTime',simulationStartTime) + call mpas_pool_get_array(diagnosticsPool, 'daysSinceStartOfSim',daysSinceStartOfSim) + + call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) + call mpas_pool_get_array(forcingPool, 'snowFlux', snowFlux) + call mpas_pool_get_array(forcingPool, 'seaIceFreshWaterFlux', seaIceFreshWaterFlux) + call mpas_pool_get_array(forcingPool, 'riverRunoffFlux', riverRunoffFlux) + call mpas_pool_get_array(forcingPool, 'iceRunoffFlux', iceRunoffFlux) + call mpas_pool_get_array(forcingPool, 'rainFlux', rainFlux) + call mpas_pool_get_array(forcingPool, 'frazilLayerThicknessTendency', frazilLayerThicknessTendency) + call mpas_pool_get_array(forcingPool, 'landIceFreshwaterFlux', landIceFreshwaterFlux) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + + call mpas_pool_get_array(tracersSurfaceFluxPool, 'activeTracersSurfaceFlux', activeTracersSurfaceFlux) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', & + activeTracersPistonVelocity) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersSurfaceRestoringValue', & + activeTracersSurfaceRestoringValue) + + + call mpas_pool_get_array(statePool, 'accumulatedLandIceMass', accumulatedLandIceMass, 1) + call mpas_pool_get_array(statePool, 'accumulatedLandIceHeat', accumulatedLandIceHeat, 1) + call mpas_pool_get_array(statePool, 'accumulatedLandIceFrazilMass', accumulatedLandIceFrazilMass, 1) allocate(areaEdge(1:nEdgesSolve)) areaEdge = dcEdge(1:nEdgesSolve)*dvEdge(1:nEdgesSolve) + allocate(landIceFloatingArea(1:nCellsSolve)) + if ( associated(landIceMask) ) then + landIceFloatingArea = landIceMask(1:nCellsSolve)*areaCell(1:nCellsSolve) + else + landIceFloatingArea = 0. + end if + allocate(workArray(nVertLevels,nCellsSolve)) variableIndex = 0 ! layerThickness variableIndex = variableIndex + 1 - call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -344,9 +431,14 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! normalVelocity variableIndex = variableIndex + 1 - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), layerThicknessEdge(:,1:nEdgesSolve), & - normalVelocity(:,1:nEdgesSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, & + maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), & + layerThicknessEdge(:,1:nEdgesSolve), & + normalVelocity(:,1:nEdgesSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -356,9 +448,14 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! tangentialVelocity variableIndex = variableIndex + 1 - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), layerThicknessEdge(:,1:nEdgesSolve), & - tangentialVelocity(:,1:nEdgesSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, & + maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), & + layerThicknessEdge(:,1:nEdgesSolve), & + tangentialVelocity(:,1:nEdgesSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -368,8 +465,12 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! layerThicknessEdge variableIndex = variableIndex + 1 - call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), layerThicknessEdge(:,1:nEdgesSolve), & - sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, & + maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), & + layerThicknessEdge(:,1:nEdgesSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -379,8 +480,14 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! relativeVorticity variableIndex = variableIndex + 1 - call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nVerticesSolve, maxLevelVertexBot(1:nVerticesSolve), areaTriangle(1:nVerticesSolve), relativeVorticity(:,1:nVerticesSolve), & - sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nVerticesSolve, & + maxLevelVertexBot(1:nVerticesSolve), & + areaTriangle(1:nVerticesSolve), & + relativeVorticity(:,1:nVerticesSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -392,9 +499,13 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ allocate(enstrophy(nVertLevels,nVerticesSolve)) enstrophy(:,:)=relativeVorticity(:,1:nVerticesSolve)**2 variableIndex = variableIndex + 1 - call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nVerticesSolve, maxLevelVertexBot(1:nVerticesSolve), areaTriangle(1:nVerticesSolve), & - enstrophy(:,:), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), & - verticalSumMins_tmp(variableIndex), verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nVerticesSolve, & + maxLevelVertexBot(1:nVerticesSolve), & + areaTriangle(1:nVerticesSolve), enstrophy(:,:), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) deallocate(enstrophy) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) @@ -405,9 +516,14 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! kineticEnergyCell variableIndex = variableIndex + 1 - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - kineticEnergyCell(:,1:nCellsSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), & + kineticEnergyCell(:,1:nCellsSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -417,11 +533,17 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! normalizedAbsoluteVorticity allocate(normalizedAbsoluteVorticity(nVertLevels,nEdgesSolve)) - normalizedAbsoluteVorticity(:,:) = normalizedRelativeVorticityEdge(:,1:nEdgesSolve) + normalizedPlanetaryVorticityEdge(:,1:nEdgesSolve) + normalizedAbsoluteVorticity(:,:) = normalizedRelativeVorticityEdge(:,1:nEdgesSolve) & + + normalizedPlanetaryVorticityEdge(:,1:nEdgesSolve) variableIndex = variableIndex + 1 - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), layerThicknessEdge(:,1:nEdgesSolve), & - normalizedAbsoluteVorticity(:,1:nEdgesSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nEdgesSolve, & + maxLevelEdgeTop(1:nEdgesSolve), areaEdge(1:nEdgesSolve), & + layerThicknessEdge(:,1:nEdgesSolve), & + normalizedAbsoluteVorticity(:,1:nEdgesSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) deallocate(normalizedAbsoluteVorticity) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) @@ -432,9 +554,13 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! pressure variableIndex = variableIndex + 1 - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - pressure(:,1:nCellsSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), pressure(:,1:nCellsSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -444,9 +570,14 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! montgomeryPotential variableIndex = variableIndex + 1 - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - montgomeryPotential(:,1:nCellsSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), & + montgomeryPotential(:,1:nCellsSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -457,9 +588,13 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! vertVelocityTop vertical velocity variableIndex = variableIndex + 1 workArray = vertVelocityTop(1:nVertLevels,1:nCellsSolve) - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - workArray, sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), workArray, & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -470,9 +605,13 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! vertAleTransportTop vertical velocity variableIndex = variableIndex + 1 workArray = vertAleTransportTop(1:nVertLevels,1:nCellsSolve) - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - workArray, sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), workArray, & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -483,38 +622,78 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! lowFreqDivergence variableIndex = variableIndex + 1 if (thicknessFilterActive) then - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - lowFreqDivergence(:,1:nCellsSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), & + lowFreqDivergence(:,1:nCellsSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) verticalSumMins(variableIndex) = min(verticalSumMins(variableIndex), verticalSumMins_tmp(variableIndex)) verticalSumMaxes(variableIndex) = max(verticalSumMaxes(variableIndex), verticalSumMaxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + verticalSumMins_tmp(variableIndex) = 0.0_RKIND + verticalSumMaxes_tmp(variableIndex) = 0.0_RKIND + sums(variableIndex) = 0.0_RKIND + sumSquares(variableIndex) = 0.0_RKIND + mins(variableIndex) = 0.0_RKIND + maxes(variableIndex) = 0.0_RKIND + verticalSumMins(variableIndex) = 0.0_RKIND + verticalSumMaxes(variableIndex) = 0.0_RKIND end if ! highFreqThickness variableIndex = variableIndex + 1 if (thicknessFilterActive) then - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - highFreqThickness(:,1:nCellsSolve), sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), & + highFreqThickness(:,1:nCellsSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) verticalSumMins(variableIndex) = min(verticalSumMins(variableIndex), verticalSumMins_tmp(variableIndex)) verticalSumMaxes(variableIndex) = max(verticalSumMaxes(variableIndex), verticalSumMaxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + verticalSumMins_tmp(variableIndex) = 0.0_RKIND + verticalSumMaxes_tmp(variableIndex) = 0.0_RKIND + sums(variableIndex) = 0.0_RKIND + sumSquares(variableIndex) = 0.0_RKIND + mins(variableIndex) = 0.0_RKIND + maxes(variableIndex) = 0.0_RKIND + verticalSumMins(variableIndex) = 0.0_RKIND + verticalSumMaxes(variableIndex) = 0.0_RKIND end if - ! Tracers - do iTracer=1,num_tracers + ! active Tracers + do iTracer=1,num_activeTracers variableIndex = variableIndex + 1 - workArray = Tracers(iTracer,:,1:nCellsSolve) - call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), layerThickness(:,1:nCellsSolve), & - workArray, sums_tmp(variableIndex), sumSquares_tmp(variableIndex), mins_tmp(variableIndex), maxes_tmp(variableIndex), verticalSumMins_tmp(variableIndex), & - verticalSumMaxes_tmp(variableIndex)) + workArray = activeTracers(iTracer,:,1:nCellsSolve) + call ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThickness(:,1:nCellsSolve), workArray, & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) @@ -522,6 +701,339 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ verticalSumMins(variableIndex) = min(verticalSumMins(variableIndex), verticalSumMins_tmp(variableIndex)) verticalSumMaxes(variableIndex) = max(verticalSumMaxes(variableIndex), verticalSumMaxes_tmp(variableIndex)) enddo + + ! layerThickness from previous timestep + variableIndex = variableIndex + 1 + call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + layerThicknessPreviousTimestep(:,1:nCellsSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = min(verticalSumMins(variableIndex), verticalSumMins_tmp(variableIndex)) + verticalSumMaxes(variableIndex) = max(verticalSumMaxes(variableIndex), verticalSumMaxes_tmp(variableIndex)) + + ! frazilLayerThicknessTendency + variableIndex = variableIndex + 1 + if ( associated(frazilLayerThicknessTendency) ) then + call ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLevels, nCellsSolve, & + maxLevelCell(1:nCellsSolve), areaCell(1:nCellsSolve), & + frazilLayerThicknessTendency(:,1:nCellsSolve), & + sums_tmp(variableIndex), sumSquares_tmp(variableIndex), & + mins_tmp(variableIndex), maxes_tmp(variableIndex), & + verticalSumMins_tmp(variableIndex), & + verticalSumMaxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + verticalSumMins_tmp(variableIndex) = 0.0_RKIND + verticalSumMaxes_tmp(variableIndex) = 0.0_RKIND + end if + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = min(verticalSumMins(variableIndex), verticalSumMins_tmp(variableIndex)) + verticalSumMaxes(variableIndex) = max(verticalSumMaxes(variableIndex), verticalSumMaxes_tmp(variableIndex)) + + ! evaporationFlux + variableIndex = variableIndex + 1 + if ( associated(evaporationFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + evaporationFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + + ! rainFlux + variableIndex = variableIndex + 1 + if ( associated(rainFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + rainFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + + ! snowFlux + variableIndex = variableIndex + 1 + if ( associated(snowFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + snowFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + + ! seaIceFreshWaterFlux + variableIndex = variableIndex + 1 + if ( associated(seaIceFreshWaterFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + seaIceFreshWaterFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + + ! riverRunoffFlux + variableIndex = variableIndex + 1 + if ( associated(riverRunoffFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + riverRunoffFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + + ! iceRunoffFlux + variableIndex = variableIndex + 1 + if ( associated(iceRunoffFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + iceRunoffFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + ! temperature flux + variableIndex = variableIndex + 1 + if ( associated(activeTracersSurfaceFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + activeTracersSurfaceFlux(1,1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + ! salinity flux + variableIndex = variableIndex + 1 + if ( associated(activeTracersSurfaceFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + activeTracersSurfaceFlux(2,1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + ! salinity restoring flux + variableIndex = variableIndex + 1 + if ( associated(activeTracersPistonVelocity) .and. associated(activeTracersSurfaceRestoringValue) ) then + allocate (restoringSaltFlux(nCellsSolve)) + restoringSaltFlux = activeTracersPistonVelocity(2,1:nCellsSolve)* & + activeTracersSurfaceRestoringValue(2,1:nCellsSolve) + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + areaCell(1:nCellsSolve), & + restoringSaltFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + deallocate (restoringSaltFlux) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + ! landIceFreshwaterFlux + variableIndex = variableIndex + 1 + if ( associated(landIceFreshwaterFlux) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + landIceFloatingArea, & + landIceFreshwaterFlux(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + ! accumulatedLandIceMass + variableIndex = variableIndex + 1 + if ( associated(accumulatedLandIceMass) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + landIceFloatingArea, & + accumulatedLandIceMass(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + ! accumulatedLandIceHeat + variableIndex = variableIndex + 1 + if ( associated(accumulatedLandIceHeat) ) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + landIceFloatingArea, & + accumulatedLandIceHeat(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + ! accumulatedLandIceFrazilMass + variableIndex = variableIndex + 1 + if (landIceFluxesPkgActive .and. frazilIcePkgActive) then + call ocn_compute_field_area_weighted_local_stats_surface(dminfo, nCellsSolve, & + landIceFloatingArea, & + accumulatedLandIceFrazilMass(1:nCellsSolve), sums_tmp(variableIndex), & + sumSquares_tmp(variableIndex), mins_tmp(variableIndex), & + maxes_tmp(variableIndex)) + else + sums_tmp(variableIndex) = 0.0_RKIND + sumSquares_tmp(variableIndex) = 0.0_RKIND + mins_tmp(variableIndex) = 0.0_RKIND + maxes_tmp(variableIndex) = 0.0_RKIND + end if + + sums(variableIndex) = sums(variableIndex) + sums_tmp(variableIndex) + sumSquares(variableIndex) = sumSquares(variableIndex) + sumSquares_tmp(variableIndex) + mins(variableIndex) = min(mins(variableIndex), mins_tmp(variableIndex)) + maxes(variableIndex) = max(maxes(variableIndex), maxes_tmp(variableIndex)) + verticalSumMins(variableIndex) = mins(variableIndex) + verticalSumMaxes(variableIndex) = maxes(variableIndex) + + deallocate(workArray) nVariables = variableIndex @@ -547,17 +1059,18 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ nSums = nSums + 1 sums(nSums) = sums(nSums) + nVerticesSolve - localCFL = 0.0 - do elementIndex = 1,nEdgesSolve - ! note: dt not connected. dt will not be available in post processing mode. - !localCFL = max(localCFL, maxval(dt*normalVelocity(:,elementIndex)/dcEdge(elementIndex))) + nSums = nSums + 1 + sums(nSums) = sums(nSums) + sum(landIceFloatingArea) + + localCFL = 0.0_RKIND + do iEdge = 1,nEdgesSolve + localCFL = max(localCFL, maxval(dt*abs(normalVelocity(1:maxLevelEdgeTop(iEdge),iEdge))/dcEdge(iEdge))) end do nMaxes = nMaxes + 1 maxes(nMaxes) = localCFL - do i = 1, nVariables - mins(nMins+i) = min(mins(nMins+i),verticalSumMins_tmp(i)) - maxes(nMaxes+i) = max(maxes(nMaxes+i),verticalSumMaxes_tmp(i)) + mins(nMins+i) = min(mins(nMins+i),verticalSumMins(i)) + maxes(nMaxes+i) = max(maxes(nMaxes+i),verticalSumMaxes(i)) end do nMins = nMins + nVariables @@ -565,6 +1078,8 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ deallocate(areaEdge) + deallocate(landIceFloatingArea) + block => block % next end do @@ -577,6 +1092,7 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ nCellsGlobal = int(reductions(nVariables+4)) nEdgesGlobal = int(reductions(nVariables+5)) nVerticesGlobal = int(reductions(nVariables+6)) + landIceFloatingAreaSum = reductions(nVariables+7) call mpas_dmpar_sum_real_array(dminfo, nVariables, sumSquares(1:nVariables), reductions(1:nVariables)) sumSquares(1:nVariables) = reductions(1:nVariables) @@ -595,16 +1111,14 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ ! compute the averages (slightly different depending on how the sum was computed) variableIndex = 0 - ! time, in days, using a 360 day calendar - read (xtime, '(i4,10(a1,i2))') timeYYYY, timeChar, timeMM, timeChar, timeDD, timeChar, timeH, timeChar, timeM, timeChar, timeS - ! subtract 31.0 because calendar starts on 00-01-01 - time_days = timeYYYY*360.0 + timeMM*30.0 + timeDD + (timeH + (timeM + timeS/60.0)/60.0)/24.0 - 31.0 - ! layerThickness variableIndex = variableIndex + 1 averages(variableIndex) = sums(variableIndex)/(areaCellGlobal*nVertLevels) rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal*nVertLevels)) + ! set current ocean volume to be used for volume change diagnostic + currentVolume = sums(variableIndex) + ! normalVelocity variableIndex = variableIndex + 1 averages(variableIndex) = sums(variableIndex)/volumeEdgeGlobal @@ -682,13 +1196,143 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ rms(variableIndex) = 0.0_RKIND end if - ! Tracers - do iTracer=1,num_tracers + ! active tracers + do iTracer=1,num_activeTracers variableIndex = variableIndex + 1 averages(variableIndex) = sums(variableIndex)/volumeCellGlobal rms(variableIndex) = sqrt(sumSquares(variableIndex)/volumeCellGlobal) enddo + ! layerThickness from previous timestep + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal*nVertLevels) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal*nVertLevels)) + + ! calculate change in ocean volume + totalVolumeChange = currentVolume - sums(variableIndex) + + ! frazilLayerThicknessTendency + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal*nVertLevels) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal*nVertLevels)) + + ! start accumulating fresh water inputs + netFreshwaterInput = dt * sums(variableIndex) + + ! evaporationFlux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! continue accumulating fresh water inputs + netFreshwaterInput = netFreshwaterInput + sums(variableIndex) * dt/rho_sw + + ! rainFlux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! continue accumulating fresh water inputs + netFreshwaterInput = netFreshwaterInput + sums(variableIndex) * dt/rho_sw + + ! snowFlux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! continue accumulating fresh water inputs + netFreshwaterInput = netFreshwaterInput + sums(variableIndex) * dt/rho_sw + + ! seaIceFreshWaterFlux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! continue accumulating fresh water inputs + netFreshwaterInput = netFreshwaterInput + sums(variableIndex) * dt/rho_sw + + ! riverRunoffFlux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! continue accumulating fresh water inputs + netFreshwaterInput = netFreshwaterInput + sums(variableIndex) * dt/rho_sw + + ! iceRunoffFlux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! continue accumulating fresh water inputs + netFreshwaterInput = netFreshwaterInput + sums(variableIndex) * dt/rho_sw + + ! temperature flux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! salinity flux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! salinity restoring flux + variableIndex = variableIndex + 1 + averages(variableIndex) = sums(variableIndex)/(areaCellGlobal) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(areaCellGlobal)) + + ! landIceFreshwaterFlux + variableIndex = variableIndex + 1 + if (associated(landIceFreshwaterFlux)) then + averages(variableIndex) = sums(variableIndex)/(landIceFloatingAreaSum) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(landIceFloatingAreaSum)) + else + averages(variableIndex) = 0.0_RKIND + rms(variableIndex) = 0.0_RKIND + end if + + ! continue accumulating fresh water inputs + netFreshwaterInput = netFreshwaterInput + sums(variableIndex) * dt/rho_sw + + ! accumulatedLandIceMass + variableIndex = variableIndex + 1 + if (associated(accumulatedLandIceMass)) then + averages(variableIndex) = sums(variableIndex)/(landIceFloatingAreaSum) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(landIceFloatingAreaSum)) + else + averages(variableIndex) = 0.0_RKIND + rms(variableIndex) = 0.0_RKIND + end if + + ! accumulatedLandIceHeat + variableIndex = variableIndex + 1 + if (associated(accumulatedLandIceHeat)) then + averages(variableIndex) = sums(variableIndex)/(landIceFloatingAreaSum) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(landIceFloatingAreaSum)) + else + averages(variableIndex) = 0.0_RKIND + rms(variableIndex) = 0.0_RKIND + end if + + ! accumulatedLandIceFrazilMass + variableIndex = variableIndex + 1 + if (landIceFluxesPkgActive .and. frazilIcePkgActive) then + averages(variableIndex) = sums(variableIndex)/(landIceFloatingAreaSum) + rms(variableIndex) = sqrt(sumSquares(variableIndex)/(landIceFloatingAreaSum)) + else + averages(variableIndex) = 0.0_RKIND + rms(variableIndex) = 0.0_RKIND + end if + + ! calculate fresh water conservation check quantities + absoluteFreshWaterConservation = totalVolumeChange - netFreshwaterInput + if (abs(totalVolumeChange) < 1e-12_RKIND) then + relativeFreshWaterConservation = 0.0_RKIND + else + relativeFreshWaterConservation = (totalVolumeChange - netFreshwaterInput)/totalVolumeChange + endif + minGlobalStats(1:nVariables) = mins(1:nVariables) maxGlobalStats(1:nVariables) = maxes(1:nVariables) sumGlobalStats(1:nVariables) = sums(1:nVariables) @@ -704,22 +1348,22 @@ subroutine ocn_compute_global_stats(domain, timeLevel, err)!{{{ if (dminfo % my_proc_id == IO_NODE) then fileID = getFreeUnit() open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_min.txt',STATUS='UNKNOWN', POSITION='append') - write (fileID,'(100es24.14)') time_days, mins(1:nVariables) + write (fileID,'(100es24.14)') daysSinceStartOfSim, mins(1:nVariables) close (fileID) open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_max.txt',STATUS='UNKNOWN', POSITION='append') - write (fileID,'(100es24.14)') time_days, maxes(1:nVariables) + write (fileID,'(100es24.14)') daysSinceStartOfSim, maxes(1:nVariables) close (fileID) open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_sum.txt',STATUS='UNKNOWN', POSITION='append') - write (fileID,'(100es24.14)') time_days, sums(1:nVariables) + write (fileID,'(100es24.14)') daysSinceStartOfSim, sums(1:nVariables) close (fileID) open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_rms.txt',STATUS='UNKNOWN', POSITION='append') - write (fileID,'(100es24.14)') time_days, rms(1:nVariables) + write (fileID,'(100es24.14)') daysSinceStartOfSim, rms(1:nVariables) close (fileID) open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_avg.txt',STATUS='UNKNOWN', POSITION='append') - write (fileID,'(100es24.14)') time_days, averages(1:nVariables) + write (fileID,'(100es24.14)') daysSinceStartOfSim, averages(1:nVariables) close (fileID) open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_time.txt',STATUS='UNKNOWN', POSITION='append') - write (fileID,'(a)') trim(xtime) + write (fileID,'(a,2es24.14)') trim(xtime), daysSinceStartOfSim, CFLNumberGlobal close (fileID) open(fileID,file=trim(config_AM_globalStats_directory)//'/stats_colmin.txt',STATUS='UNKNOWN', POSITION='append') write (fileID,'(100es24.14)') verticalSumMins(1:nVariables) @@ -843,12 +1487,12 @@ subroutine ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLe integer :: elementIndex real (kind=RKIND) :: colSum, colRMS, colSumAbs - localSum = 0.0 - localRMS = 0.0 - localMin = 1.0e34 - localMax = -1.0e34 - localVertSumMin = 1.0e34 - localVertSumMax = -1.0e34 + localSum = 0.0_RKIND + localRMS = 0.0_RKIND + localMin = 1.0e34_RKIND + localMax = -1.0e34_RKIND + localVertSumMin = 1.0e34_RKIND + localVertSumMax = -1.0e34_RKIND do elementIndex = 1, nElements colSum = sum(field(1:maxLevel(elementIndex),elementIndex)) @@ -863,8 +1507,9 @@ subroutine ocn_compute_field_area_weighted_local_stats_max_level(dminfo, nVertLe end subroutine ocn_compute_field_area_weighted_local_stats_max_level!}}} - subroutine ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nElements, maxLevel, areas, layerThickness, field, &!{{{ - localSum, localRMS, localMin, localMax, localVertSumMin, localVertSumMax) + subroutine ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVertLevels, nElements, maxLevel, areas, & !{{{ + layerThickness, field, localSum, localRMS, localMin, & + localMax, localVertSumMin, localVertSumMax) implicit none @@ -881,17 +1526,19 @@ subroutine ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVert real (kind=RKIND) :: thicknessWeightedColSum, thicknessWeightedColRMS, thicknessWeightedColSumAbs real (kind=RKIND), dimension(nVertLevels, nElements) :: hTimesField - localSum = 0.0 - localRMS = 0.0 - localMin = 1.0e34 - localMax = -1.0e34 - localVertSumMin = 1.0e34 - localVertSumMax = -1.0e34 + localSum = 0.0_RKIND + localRMS = 0.0_RKIND + localMin = 1.0e34_RKIND + localMax = -1.0e34_RKIND + localVertSumMin = 1.0e34_RKIND + localVertSumMax = -1.0e34_RKIND do elementIndex = 1, nElements - thicknessWeightedColSum = sum(layerThickness(1:maxLevel(elementIndex),elementIndex)*field(1:maxLevel(elementIndex),elementIndex)) + thicknessWeightedColSum = sum(layerThickness(1:maxLevel(elementIndex),elementIndex) & + * field(1:maxLevel(elementIndex),elementIndex)) localSum = localSum + areas(elementIndex) * thicknessWeightedColSum - thicknessWeightedColRMS = sum(layerThickness(1:maxLevel(elementIndex),elementIndex)*field(1:maxLevel(elementIndex),elementIndex)**2) + thicknessWeightedColRMS = sum(layerThickness(1:maxLevel(elementIndex),elementIndex) & + * field(1:maxLevel(elementIndex),elementIndex)**2) localRMS = localRMS + areas(elementIndex) * thicknessWeightedColRMS localMin = min(localMin,minval(field(1:maxLevel(elementIndex),elementIndex))) localMax = max(localMax,maxval(field(1:maxLevel(elementIndex),elementIndex))) @@ -901,6 +1548,32 @@ subroutine ocn_compute_field_volume_weighted_local_stats_max_level(dminfo, nVert end subroutine ocn_compute_field_volume_weighted_local_stats_max_level!}}} + subroutine ocn_compute_field_area_weighted_local_stats_surface(dminfo, nElements, areas, field, &!{{{ + localSum, localRMS, localMin, localMax) + + implicit none + + type (dm_info), intent(in) :: dminfo + integer, intent(in) :: nElements + real (kind=RKIND), dimension(nElements), intent(in) :: areas, field + real (kind=RKIND), intent(out) :: localSum, localRMS, localMin, localMax + + integer :: elementIndex + + localSum = 0.0_RKIND + localRMS = 0.0_RKIND + localMin = 1.0e34_RKIND + localMax = -1.0e34_RKIND + + do elementIndex = 1, nElements + localSum = localSum + areas(elementIndex) * field(elementIndex) + localRMS = localRMS + areas(elementIndex) * field(elementIndex)**2 + localMin = min(localMin,field(elementIndex)) + localMax = max(localMax,field(elementIndex)) + end do + + end subroutine ocn_compute_field_area_weighted_local_stats_surface!}}} + end module ocn_global_stats ! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_high_frequency_output.F b/src/core_ocean/analysis_members/mpas_ocn_high_frequency_output.F index 38e3675394..ca798ffb89 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_high_frequency_output.F +++ b/src/core_ocean/analysis_members/mpas_ocn_high_frequency_output.F @@ -1,4 +1,4 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +!Copyright (c) 2013, Los Alamos National Security, LLC (LANS) ! and the University Corporation for Atmospheric Research (UCAR). ! ! Unless noted otherwise source code is licensed under the BSD license. @@ -10,7 +10,7 @@ ! ocn_high_frequency_output ! !> \brief MPAS ocean analysis mode member: high_frequency_output -!> \author Todd Ringler +!> \author Todd Ringler and Phillip Wolfram !> \date 2015/06/12 !> \details !> MPAS ocean analysis mode member: high_frequency_output @@ -64,7 +64,7 @@ module ocn_high_frequency_output ! routine ocn_init_high_frequency_output ! !> \brief Initialize MPAS-Ocean analysis member -!> \author Todd Ringler +!> \author Todd Ringler and Phillip Wolfram !> \date 2015/06/12 !> \details !> This routine conducts all initializations required for the @@ -111,7 +111,7 @@ end subroutine ocn_init_high_frequency_output!}}} ! routine ocn_compute_high_frequency_output ! !> \brief Compute MPAS-Ocean analysis member -!> \author Todd Ringler +!> \author Todd Ringler and Phillip Wolfram !> \date 2015/06/12 !> \details !> This routine conducts all computation required for this @@ -158,12 +158,67 @@ subroutine ocn_compute_high_frequency_output(domain, timeLevel, err)!{{{ type (mpas_pool_type), pointer :: diagnosticsPool type (mpas_pool_type), pointer :: forcingPool type (mpas_pool_type), pointer :: highFrequencyOutputAMPool - - integer :: iLevel, iLevelTarget - integer, pointer :: nVertLevels - real (kind=RKIND), dimension(:), pointer :: refBottomDepth, kineticEnergyAt100m, relativeVorticityAt100m - real (kind=RKIND), dimension(:,:), pointer :: kineticEnergyCell, relativeVorticityCell, tracersAtSurface - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: scratchPool + + integer :: iLevel, iLevelTarget, iCell, iEdge, i, cell1, cell2, k, eoe + integer :: iLevel0100, iLevel0250, iLevel0700, iLevel2000 + real (kind=RKIND) :: sumLayerThickness + integer, pointer :: nVertLevels, nCells, nEdges + integer, dimension(:), pointer :: nEdgesOnCell, maxLevelEdgeTop, maxLevelEdgeBot, maxLevelCell, nEdgesOnEdge + integer, dimension(:,:), pointer :: edgesOnCell, cellsOnEdge, edgesOnEdge + + real (kind=RKIND) :: invAreaCell1, layerThicknessEdge1, coeff, weightedNormalVel, cellArea + real (kind=RKIND), dimension(:), pointer :: refBottomDepth, kineticEnergyAt250m, kineticEnergyAtSurface, relativeVorticityAt250m + real (kind=RKIND), dimension(:), pointer :: divergenceAt250m, relativeVorticityVertexAt250m + real (kind=RKIND), dimension(:), pointer :: divergenceAtBottom,relativeVorticityAtBottom,kineticEnergyAtBottom + real (kind=RKIND), dimension(:), pointer :: vertVelAt250m + real (kind=RKIND), dimension(:), pointer :: BruntVaisalaFreqTopAtSFC, BruntVaisalaFreqTopAt250m, BruntVaisalaFreqTopAtBottom + + real (kind=RKIND), dimension(:), pointer :: normalVelAtSFC, normalVelAt250m, normalVelAtBottom + real (kind=RKIND), dimension(:), pointer :: tangentialVelAtSFC, tangentialVelAt250m, tangentialVelAtBottom + real (kind=RKIND), dimension(:), pointer :: zonalVelAtSFC, zonalVelAt250m, zonalVelAtBottom + real (kind=RKIND), dimension(:), pointer :: meridionalVelAtSFC, meridionalVelAt250m, meridionalVelAtBottom + + real (kind=RKIND), dimension(:), pointer :: zonalAreaWeightedCellVelAtSFC, zonalAreaWeightedCellVelAt250m, zonalAreaWeightedCellVelAtBottom + real (kind=RKIND), dimension(:), pointer :: meridionalAreaWeightedCellVelAtSFC, meridionalAreaWeightedCellVelAt250m, meridionalAreaWeightedCellVelAtBottom + + real (kind=RKIND), dimension(:), pointer :: normalBarotropicVel + real (kind=RKIND), dimension(:), pointer :: tangentialBarotropicVel + real (kind=RKIND), dimension(:), pointer :: zonalBarotropicVel + real (kind=RKIND), dimension(:), pointer :: meridionalBarotropicVel + + real (kind=RKIND), dimension(:), pointer :: normalBaroclinicVelAtSFC, normalBaroclinicVelAt250m, normalBaroclinicVelAtBottom + real (kind=RKIND), dimension(:), pointer :: tangentialBaroclinicVelAtSFC, tangentialBaroclinicVelAt250m, tangentialBaroclinicVelAtBottom + real (kind=RKIND), dimension(:), pointer :: zonalBaroclinicVelAtSFC, zonalBaroclinicVelAt250m, zonalBaroclinicVelAtBottom + real (kind=RKIND), dimension(:), pointer :: meridionalBaroclinicVelAtSFC, meridionalBaroclinicVelAt250m, meridionalBaroclinicVelAtBottom + + real (kind=RKIND), dimension(:), pointer :: normalGMBolusVelAtSFC, normalGMBolusVelAt250m, normalGMBolusVelAtBottom + real (kind=RKIND), dimension(:), pointer :: tangentialGMBolusVelAtSFC, tangentialGMBolusVelAt250m, tangentialGMBolusVelAtBottom + real (kind=RKIND), dimension(:), pointer :: zonalGMBolusVelAtSFC, zonalGMBolusVelAt250m, zonalGMBolusVelAtBottom + real (kind=RKIND), dimension(:), pointer :: meridionalGMBolusVelAtSFC, meridionalGMBolusVelAt250m, meridionalGMBolusVelAtBottom + + real (kind=RKIND), dimension(:), pointer :: angleEdge + real (kind=RKIND), dimension(:,:), pointer :: weightsOnEdge + real (kind=RKIND), dimension(:), pointer :: vertTransportVelocityAt250m, vertGMvelocityAt250m + real (kind=RKIND), dimension(:), pointer :: vertVelSFC, vertTransportVelocitySFC, vertGMvelocitySFC + real (kind=RKIND), dimension(:), pointer :: barotropicSpeed, columnIntegratedSpeed, dvEdge, dcEdge, areaCell + real (kind=RKIND), dimension(:,:), pointer :: kineticEnergyCell, relativeVorticityCell + real (kind=RKIND), dimension(:,:), pointer :: activeTracersAt250m, activeTracersAvgTopto0100, activeTracersAvg0100to0250 + real (kind=RKIND), dimension(:,:), pointer :: activeTracersAtSurface, activeTracersAtBottom + real (kind=RKIND), dimension(:,:), pointer :: activeTracersAvg0250to0700, activeTracersAvg0700to2000, activeTracersAvg2000toBottom + real (kind=RKIND), dimension(:,:), pointer :: relativeVorticity, divergence, layerThickness + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, tangentialVelocity, BruntVaisalaFreqTop + real (kind=RKIND), dimension(:,:), pointer :: vertVelocityTop, vertGMBolusVelocityTop, vertTransportVelocityTop + real (kind=RKIND), dimension(:,:), pointer :: normalGMBolusVelocity + real (kind=RKIND), dimension(:,:), pointer :: normalBaroclinicVelocity ! baroclinic/barotropic pressume split explicit + real (kind=RKIND), dimension(:), pointer :: normalBarotropicVelocity ! baroclinic/barotropic pressume split explicit + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + character (len=StrKIND), pointer :: config_time_integrator + + ! scratch space + type (field1DReal), pointer :: normalThicknessFluxSumField, layerThicknessSumEdgeField + real (kind=RKIND), dimension(:), pointer :: normalThicknessFluxSum, layerThicknessSumEdge err = 0 @@ -172,6 +227,8 @@ subroutine ocn_compute_high_frequency_output(domain, timeLevel, err)!{{{ block => domain % blocklist do while (associated(block)) ! get dimensions + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) ! get pointers to pools @@ -179,6 +236,8 @@ subroutine ocn_compute_high_frequency_output(domain, timeLevel, err)!{{{ call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_subpool(domain % blocklist % structs, 'highFrequencyOutputAM', highFrequencyOutputAMPool) ! get static data from mesh pool @@ -187,33 +246,447 @@ subroutine ocn_compute_high_frequency_output(domain, timeLevel, err)!{{{ ! get arrays that will be 'sliced' and put into high frequency output call mpas_pool_get_array(diagnosticsPool, 'kineticEnergyCell', kineticEnergyCell, timeLevel) call mpas_pool_get_array(diagnosticsPool, 'relativeVorticityCell', relativeVorticityCell, timeLevel) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) + call mpas_pool_get_array(diagnosticsPool, 'relativeVorticity', relativeVorticity, timeLevel) + call mpas_pool_get_array(diagnosticsPool, 'divergence', divergence, timeLevel) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) + call mpas_pool_get_array(diagnosticsPool, 'vertGMBolusVelocityTop', vertGMBolusVelocityTop) + call mpas_pool_get_array(diagnosticsPool, 'vertTransportVelocityTop', vertTransportVelocityTop) + call mpas_pool_get_array(diagnosticsPool, 'vertVelocityTop', vertVelocityTop) + call mpas_pool_get_array(diagnosticsPool, 'tangentialVelocity', tangentialVelocity) + call mpas_pool_get_array(diagnosticsPool, 'BruntVaisalaFreqTop', BruntVaisalaFreqTop) + call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) ! get arrays that can be written to output at high freqency - call mpas_pool_get_array(highFrequencyOutputAMPool, 'kineticEnergyAt100m', kineticEnergyAt100m) - call mpas_pool_get_array(highFrequencyOutputAMPool, 'relativeVorticityAt100m', relativeVorticityAt100m) - call mpas_pool_get_array(highFrequencyOutputAMPool, 'tracersAtSurface', tracersAtSurface) - - ! - ! note for future build out - ! HERE: interpolate vertically to target z-level or isopycnal or some other surface - ! - - ! for now, just get close enough - iLevelTarget = 1 + call mpas_pool_get_array(highFrequencyOutputAMPool, 'kineticEnergyAt250m', kineticEnergyAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'kineticEnergyAtSurface', kineticEnergyAtSurface) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'relativeVorticityAt250m', relativeVorticityAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'divergenceAt250m', divergenceAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'relativeVorticityAtBottom', relativeVorticityAtBottom) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'divergenceAtBottom', divergenceAtBottom) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'kineticEnergyAtBottom', kineticEnergyAtBottom) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'relativeVorticityVertexAt250m', relativeVorticityVertexAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAtSurface', activeTracersAtSurface) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAt250m', activeTracersAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAtBottom', activeTracersAtBottom) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAvgTopto0100', activeTracersAvgTopto0100) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAvg0100to0250', activeTracersAvg0100to0250) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAvg0250to0700', activeTracersAvg0250to0700) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAvg0700to2000', activeTracersAvg0700to2000) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'activeTracersAvg2000toBottom', activeTracersAvg2000toBottom) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'vertGMvelocitySFC', vertGMvelocitySFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'vertTransportVelocitySFC', vertTransportVelocitySFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'vertVelAt250m', vertVelAt250m) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalVelAtSFC', normalVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalVelAt250m', normalVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalVelAtBottom', normalVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialVelAtSFC', tangentialVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialVelAt250m', tangentialVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialVelAtBottom', tangentialVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalVelAtSFC', zonalVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalVelAt250m', zonalVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalVelAtBottom', zonalVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalVelAtSFC', meridionalVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalVelAt250m', meridionalVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalVelAtBottom', meridionalVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalAreaWeightedCellVelAtSFC', zonalAreaWeightedCellVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalAreaWeightedCellVelAt250m', zonalAreaWeightedCellVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalAreaWeightedCellVelAtBottom', zonalAreaWeightedCellVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalAreaWeightedCellVelAtSFC', meridionalAreaWeightedCellVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalAreaWeightedCellVelAt250m', meridionalAreaWeightedCellVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalAreaWeightedCellVelAtBottom', meridionalAreaWeightedCellVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalBarotropicVel', normalBarotropicVel) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialBarotropicVel', tangentialBarotropicVel) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalBarotropicVel', zonalBarotropicVel) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalBarotropicVel', meridionalBarotropicVel) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalBaroclinicVelAtSFC', normalBaroclinicVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalBaroclinicVelAt250m', normalBaroclinicVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalBaroclinicVelAtBottom', normalBaroclinicVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialBaroclinicVelAtSFC', tangentialBaroclinicVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialBaroclinicVelAt250m', tangentialBaroclinicVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialBaroclinicVelAtBottom', tangentialBaroclinicVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalBaroclinicVelAtSFC', zonalBaroclinicVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalBaroclinicVelAt250m', zonalBaroclinicVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalBaroclinicVelAtBottom', zonalBaroclinicVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalBaroclinicVelAtSFC', meridionalBaroclinicVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalBaroclinicVelAt250m', meridionalBaroclinicVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalBaroclinicVelAtBottom', meridionalBaroclinicVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalGMBolusVelAtSFC', normalGMBolusVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalGMBolusVelAt250m', normalGMBolusVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'normalGMBolusVelAtBottom', normalGMBolusVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialGMBolusVelAtSFC', tangentialGMBolusVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialGMBolusVelAt250m', tangentialGMBolusVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'tangentialGMBolusVelAtBottom', tangentialGMBolusVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalGMBolusVelAtSFC', zonalGMBolusVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalGMBolusVelAt250m', zonalGMBolusVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'zonalGMBolusVelAtBottom', zonalGMBolusVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalGMBolusVelAtSFC', meridionalGMBolusVelAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalGMBolusVelAt250m', meridionalGMBolusVelAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'meridionalGMBolusVelAtBottom', meridionalGMBolusVelAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'BruntVaisalaFreqTopAtSFC', BruntVaisalaFreqTopAtSFC) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'BruntVaisalaFreqTopAt250m', BruntVaisalaFreqTopAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'BruntVaisalaFreqTopAtBottom', BruntVaisalaFreqTopAtBottom) + + call mpas_pool_get_array(highFrequencyOutputAMPool, 'vertGMvelocityAt250m', vertGMvelocityAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'vertTransportVelocityAt250m', vertTransportVelocityAt250m) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'vertVelSFC', vertVelSFC) + + ! split explicit specific arrays + call mpas_pool_get_config(ocnConfigs, 'config_time_integrator', config_time_integrator) + if ( config_time_integrator == trim('split_explicit')) then + call mpas_pool_get_array(statePool, 'normalBaroclinicVelocity', normalBaroclinicVelocity, 1) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocity, 1) + endif + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 1) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeBot', maxLevelEdgeBot) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'angleEdge', angleEdge) + call mpas_pool_get_array(meshPool, 'edgesOnEdge', edgesOnEdge) + call mpas_pool_get_array(meshPool, 'weightsOnEdge', weightsOnEdge) + call mpas_pool_get_array(meshPool, 'nEdgesOnEdge', nEdgesOnEdge) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'barotropicSpeed', barotropicSpeed) + call mpas_pool_get_array(highFrequencyOutputAMPool, 'columnIntegratedSpeed', columnIntegratedSpeed) + + ! find vertical level that is just above the 100 m reference level + iLevel0100 = 1 do iLevel=2,nVertLevels if(refBottomDepth(iLevel) > 100.0_RKIND) then - iLevelTarget = iLevel-1 + iLevel0100 = iLevel-1 + exit + endif + enddo + + ! find vertical level that is just above the 250 m reference level + iLevel0250 = 1 + do iLevel=iLevel0100,nVertLevels + if(refBottomDepth(iLevel) > 250.0_RKIND) then + iLevel0250 = iLevel-1 + exit + endif + enddo + + ! find vertical level that is just above the 700 m reference level + iLevel0700 = 1 + do iLevel=iLevel0250,nVertLevels + if(refBottomDepth(iLevel) > 700.0_RKIND) then + iLevel0700 = iLevel-1 + exit + endif + enddo + + ! find vertical level that is just above the 2000 m reference level + iLevel2000 = 1 + do iLevel=iLevel0700,nVertLevels + if(refBottomDepth(iLevel) > 2000.0_RKIND) then + iLevel2000 = iLevel-1 exit endif enddo ! copy data into high frequency output fields - ! tracer data will be converted to new tracer infrastrcture (and this line removed) before June 23 2015. - kineticEnergyAt100m(:) = kineticEnergyCell(iLevelTarget,:) - relativeVorticityAt100m(:) = relativeVorticityCell(iLevelTarget,:) - tracersAtSurface(1,:) = tracers(1,1,:) - tracersAtSurface(2,:) = tracers(2,1,:) + kineticEnergyAt250m(:) = kineticEnergyCell(iLevel0250,:) + relativeVorticityAt250m(:) = relativeVorticityCell(iLevel0250,:) + divergenceAt250m(:) = divergence(iLevel0250,:) + relativeVorticityVertexAt250m(:) = relativeVorticity(iLevel0250,:) + kineticEnergyAtSurface(:) = kineticEnergyCell(1,:) + activeTracersAtSurface(1,:) = activeTracers(1,1,:) + activeTracersAtSurface(2,:) = activeTracers(2,1,:) + activeTracersAt250m(1,:) = activeTracers(1,iLevel0250,:) + activeTracersAt250m(2,:) = activeTracers(2,iLevel0250,:) + vertGMvelocitySFC(:) = vertGMBolusVelocityTop(1,:) + vertTransportVelocitySFC(:) = vertTransportVelocityTop(1,:) + vertVelSFC(:) = vertVelocityTop(1,:) + vertGMvelocityAt250m(:) = vertGMBolusVelocityTop(iLevel0250,:) + vertTransportVelocityAt250m(:) = vertTransportVelocityTop(iLevel0250,:) + vertVelAt250m(:) = vertVelocityTop(iLevel0250,:) + + do iEdge = 1, nEdges + normalGMBolusVelAtSFC(iEdge) = normalGMBolusVelocity(1,iEdge) + normalGMBolusVelAt250m(iEdge) = normalGMBolusVelocity(iLevel0250,iEdge) + normalGMBolusVelAtBottom(iEdge) = normalGMBolusVelocity(maxLevelEdgeBot(iEdge),iEdge) + + tangentialVelAtSFC(iEdge) = tangentialVelocity(1,iEdge) + tangentialVelAt250m(iEdge) = tangentialVelocity(iLevel0250,iEdge) + tangentialVelAtBottom(iEdge) = tangentialVelocity(maxLevelEdgeBot(iEdge),iEdge) + + normalVelAtSFC(iEdge) = normalVelocity(1,iEdge) + normalVelAt250m(iEdge) = normalVelocity(iLevel0250,iEdge) + normalVelAtBottom(iEdge) = normalVelocity(maxLevelEdgeBot(iEdge),iEdge) + + zonalVelAtSFC(iEdge) = normalVelAtSFC(iEdge)*cos(angleEdge(iEdge)) & + - tangentialVelAtSFC(iEdge)*sin(angleEdge(iEdge)) + meridionalVelAtSFC(iEdge) = normalVelAtSFC(iEdge)*sin(angleEdge(iEdge)) & + + tangentialVelAtSFC(iEdge)*cos(angleEdge(iEdge)) + + zonalVelAt250m(iEdge) = normalVelAt250m(iEdge)*cos(angleEdge(iEdge)) & + - tangentialVelAt250m(iEdge)*sin(angleEdge(iEdge)) + meridionalVelAt250m(iEdge) = normalVelAt250m(iEdge)*sin(angleEdge(iEdge)) & + + tangentialVelAt250m(iEdge)*cos(angleEdge(iEdge)) + + zonalVelAtBottom(iEdge) = normalVelAtBottom(iEdge)*cos(angleEdge(iEdge)) & + - tangentialVelAtBottom(iEdge)*sin(angleEdge(iEdge)) + meridionalVelAtBottom(iEdge) = normalVelAtBottom(iEdge)*sin(angleEdge(iEdge)) & + + tangentialVelAtBottom(iEdge)*cos(angleEdge(iEdge)) + end do + + do iEdge = 1, nEdges + tangentialGMBolusVelAtSFC(iEdge) = 0.0_RKIND + tangentialGMBolusVelAt250m(iEdge) = 0.0_RKIND + tangentialGMBolusVelAtBottom(iEdge) = 0.0_RKIND + do i = 1, nEdgesOnEdge(iEdge) + eoe = edgesOnEdge(i,iEdge) + tangentialGMBolusVelAtSFC(iEdge) = tangentialGMBolusVelAtSFC(iEdge) + weightsOnEdge(i,iEdge) * normalGMBolusVelAtSFC(eoe) + tangentialGMBolusVelAt250m(iEdge) = tangentialGMBolusVelAt250m(iEdge) + weightsOnEdge(i,iEdge) * normalGMBolusVelAt250m(eoe) + tangentialGMBolusVelAtBottom(iEdge) = tangentialGMBolusVelAtBottom(iEdge) + weightsOnEdge(i,iEdge) * normalGMBolusVelAtBottom(eoe) + end do + + zonalGMBolusVelAtSFC(iEdge) = normalGMBolusVelAtSFC(iEdge)*cos(angleEdge(iEdge)) & + - tangentialGMBolusVelAtSFC(iEdge)*sin(angleEdge(iEdge)) + meridionalGMBolusVelAtSFC(iEdge) = normalGMBolusVelAtSFC(iEdge)*sin(angleEdge(iEdge)) & + + tangentialGMBolusVelAtSFC(iEdge)*cos(angleEdge(iEdge)) + + zonalGMBolusVelAt250m(iEdge) = normalGMBolusVelAt250m(iEdge)*cos(angleEdge(iEdge)) & + - tangentialGMBolusVelAt250m(iEdge)*sin(angleEdge(iEdge)) + meridionalGMBolusVelAt250m(iEdge) = normalGMBolusVelAt250m(iEdge)*sin(angleEdge(iEdge)) & + + tangentialGMBolusVelAt250m(iEdge)*cos(angleEdge(iEdge)) + + zonalGMBolusVelAtBottom(iEdge) = normalGMBolusVelAtBottom(iEdge)*cos(angleEdge(iEdge)) & + - tangentialGMBolusVelAtBottom(iEdge)*sin(angleEdge(iEdge)) + meridionalGMBolusVelAtBottom(iEdge) = normalGMBolusVelAtBottom(iEdge)*sin(angleEdge(iEdge)) & + + tangentialGMBolusVelAtBottom(iEdge)*cos(angleEdge(iEdge)) + end do + + do iCell=1,nCells + relativeVorticityAtBottom(iCell) = relativeVorticityCell(maxLevelCell(iCell),iCell) + divergenceAtBottom(iCell) = divergence(maxLevelCell(iCell),iCell) + kineticEnergyAtBottom(iCell) = kineticEnergyCell(maxLevelCell(iCell),iCell) + activeTracersAtBottom(1,iCell) = activeTracers(1,maxLevelCell(iCell),iCell) + activeTracersAtBottom(2,iCell) = activeTracers(2,maxLevelCell(iCell),iCell) + + BruntVaisalaFreqTopAtSFC(iCell) = BruntVaisalaFreqTop(1,iCell) + BruntVaisalaFreqTopAt250m(iCell) = BruntVaisalaFreqTop(iLevel0250,iCell) + BruntVaisalaFreqTopAtBottom(iCell) = BruntVaisalaFreqTop(maxLevelCell(iCell),iCell) + end do + + do iCell = 1, nCells + zonalAreaWeightedCellVelAtSFC(iCell) = 0.0_RKIND + zonalAreaWeightedCellVelAt250m(iCell) = 0.0_RKIND + zonalAreaWeightedCellVelAtBottom(iCell) = 0.0_RKIND + + meridionalAreaWeightedCellVelAtSFC(iCell) = 0.0_RKIND + meridionalAreaWeightedCellVelAt250m(iCell) = 0.0_RKIND + meridionalAreaWeightedCellVelAtBottom(iCell) = 0.0_RKIND + + cellArea = 0.0_RKIND + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + ! note, we don't necessarily have d_{i,j} computed (Fringer, 2006), so we allow averaging between cells via 0.5_RKIND*dcEdge(iEdge) to compute + ! segment of cell area d_{i,j}-- the key is that the edge-normal velocities are a convex combination + cellArea = cellArea + 0.5_RKIND*dcEdge(iEdge)*dvEdge(iEdge) + + weightedNormalVel = normalVelAtSFC(iEdge)*0.5_RKIND*dcEdge(iEdge)*dvEdge(iEdge) + zonalAreaWeightedCellVelAtSFC(iCell) = zonalAreaWeightedCellVelAtSFC(iCell) + cos(angleEdge(iEdge)) * weightedNormalVel + meridionalAreaWeightedCellVelAtSFC(iCell) = meridionalAreaWeightedCellVelAtSFC(iCell) + sin(angleEdge(iEdge)) * weightedNormalVel + + weightedNormalVel = normalVelAt250m(iEdge)*0.5_RKIND*dcEdge(iEdge)*dvEdge(iEdge) + zonalAreaWeightedCellVelAt250m(iCell) = zonalAreaWeightedCellVelAt250m(iCell) + cos(angleEdge(iEdge)) * weightedNormalVel + meridionalAreaWeightedCellVelAt250m(iCell) = meridionalAreaWeightedCellVelAt250m(iCell) + sin(angleEdge(iEdge)) * weightedNormalVel + + weightedNormalVel = normalVelAtBottom(iEdge)*0.5_RKIND*dcEdge(iEdge)*dvEdge(iEdge) + zonalAreaWeightedCellVelAtBottom(iCell) = zonalAreaWeightedCellVelAtBottom(iCell) + cos(angleEdge(iEdge)) * weightedNormalVel + meridionalAreaWeightedCellVelAtBottom(iCell) = meridionalAreaWeightedCellVelAtBottom(iCell) + sin(angleEdge(iEdge)) * weightedNormalVel + end do + zonalAreaWeightedCellVelAtSFC(iCell) = zonalAreaWeightedCellVelAtSFC(iCell)/cellArea + meridionalAreaWeightedCellVelAtSFC(iCell) = meridionalAreaWeightedCellVelAtSFC(iCell)/cellArea + + zonalAreaWeightedCellVelAt250m(iCell) = zonalAreaWeightedCellVelAt250m(iCell)/cellArea + meridionalAreaWeightedCellVelAt250m(iCell) = meridionalAreaWeightedCellVelAt250m(iCell)/cellArea + + zonalAreaWeightedCellVelAtBottom(iCell) = zonalAreaWeightedCellVelAtBottom(iCell)/cellArea + meridionalAreaWeightedCellVelAtBottom(iCell) = meridionalAreaWeightedCellVelAtBottom(iCell)/cellArea + end do + + if ( config_time_integrator == trim('split_explicit')) then + + do iEdge = 1, nEdges + normalBarotropicVel(iEdge) = normalBarotropicVelocity(iEdge) + normalBaroclinicVelAtSFC(iEdge) = normalBaroclinicVelocity(1,iEdge) + normalBaroclinicVelAt250m(iEdge) = normalBaroclinicVelocity(iLevel0250,iEdge) + normalBaroclinicVelAtBottom(iEdge) = normalBaroclinicVelocity(maxLevelEdgeBot(iEdge),iEdge) + end do + + do iEdge = 1, nEdges + tangentialBarotropicVel(iEdge) = 0.0_RKIND + tangentialBaroclinicVelAtSFC(iEdge) = 0.0_RKIND + tangentialBaroclinicVelAt250m(iEdge) = 0.0_RKIND + tangentialBaroclinicVelAtBottom(iEdge) = 0.0_RKIND + do i = 1, nEdgesOnEdge(iEdge) + eoe = edgesOnEdge(i,iEdge) + tangentialBarotropicVel(iEdge) = tangentialBarotropicVel(iEdge) + weightsOnEdge(i,iEdge) * normalBarotropicVel(eoe) + tangentialBaroclinicVelAtSFC(iEdge) = tangentialBaroclinicVelAtSFC(iEdge) + weightsOnEdge(i,iEdge) * normalBaroclinicVelAtSFC(eoe) + tangentialBaroclinicVelAt250m(iEdge) = tangentialBaroclinicVelAt250m(iEdge) + weightsOnEdge(i,iEdge) * normalBaroclinicVelAt250m(eoe) + tangentialBaroclinicVelAtBottom(iEdge) = tangentialBaroclinicVelAtBottom(iEdge) + weightsOnEdge(i,iEdge) * normalBaroclinicVelAtBottom(eoe) + end do + + zonalBarotropicVel(iEdge) = normalBarotropicVel(iEdge)*cos(angleEdge(iEdge)) & + - tangentialBarotropicVel(iEdge)*sin(angleEdge(iEdge)) + meridionalBarotropicVel(iEdge) = normalBarotropicVel(iEdge)*sin(angleEdge(iEdge)) & + + tangentialBarotropicVel(iEdge)*cos(angleEdge(iEdge)) + + zonalBaroclinicVelAtSFC(iEdge) = normalBaroclinicVelAtSFC(iEdge)*cos(angleEdge(iEdge)) & + - tangentialBaroclinicVelAtSFC(iEdge)*sin(angleEdge(iEdge)) + meridionalBaroclinicVelAtSFC(iEdge) = normalBaroclinicVelAtSFC(iEdge)*sin(angleEdge(iEdge)) & + + tangentialBaroclinicVelAtSFC(iEdge)*cos(angleEdge(iEdge)) + + zonalBaroclinicVelAt250m(iEdge) = normalBaroclinicVelAt250m(iEdge)*cos(angleEdge(iEdge)) & + - tangentialBaroclinicVelAt250m(iEdge)*sin(angleEdge(iEdge)) + meridionalBaroclinicVelAt250m(iEdge) = normalBaroclinicVelAt250m(iEdge)*sin(angleEdge(iEdge)) & + + tangentialBaroclinicVelAt250m(iEdge)*cos(angleEdge(iEdge)) + + zonalBaroclinicVelAtBottom(iEdge) = normalBaroclinicVelAtBottom(iEdge)*cos(angleEdge(iEdge)) & + - tangentialBaroclinicVelAtBottom(iEdge)*sin(angleEdge(iEdge)) + meridionalBaroclinicVelAtBottom(iEdge) = normalBaroclinicVelAtBottom(iEdge)*sin(angleEdge(iEdge)) & + + tangentialBaroclinicVelAtBottom(iEdge)*cos(angleEdge(iEdge)) + end do + + endif + + ! + ! compute layer averaged tracers + ! + activeTracersAvgTopto0100(:,:) = -1.0e34_RKIND + activeTracersAvg0100to0250(:,:) = -1.0e34_RKIND + activeTracersAvg0250to0700(:,:) = -1.0e34_RKIND + activeTracersAvg0700to2000(:,:) = -1.0e34_RKIND + activeTracersAvg2000toBottom(:,:) = -1.0e34_RKIND + do iCell = 1, nCells + sumLayerThickness = layerThickness(1,iCell) + activeTracersAvgTopto0100(:,iCell) = activeTracers(:,1,iCell)*layerThickness(1,iCell) + do k=2, min(maxLevelCell(iCell),iLevel0100) + sumLayerThickness = sumLayerThickness + layerThickness(k,iCell) + activeTracersAvgTopto0100(:,iCell) = activeTracersAvgTopto0100(:,iCell) + activeTracers(:,k,iCell)*layerThickness(k,iCell) + enddo + activeTracersAvgTopto0100(:,iCell) = activeTracersAvgTopto0100(:,iCell) / max(sumLayerThickness,1.0_RKIND) + + if (iLevel0100+1.le.maxLevelCell(iCell)) then + sumLayerThickness = layerThickness(iLevel0100+1,iCell) + activeTracersAvg0100to0250(:,iCell) = activeTracers(:,iLevel0100+1,iCell)*layerThickness(iLevel0100,iCell) + do k=iLevel0100+2, min(maxLevelCell(iCell),iLevel0250) + sumLayerThickness = sumLayerThickness + layerThickness(k,iCell) + activeTracersAvg0100to0250(:,iCell) = activeTracersAvg0100to0250(:,iCell) + activeTracers(:,k,iCell)*layerThickness(k,iCell) + enddo + activeTracersAvg0100to0250(:,iCell) = activeTracersAvg0100to0250(:,iCell) / max(sumLayerThickness,1.0_RKIND) + endif + + if (iLevel0250+1.le.maxLevelCell(iCell)) then + sumLayerThickness = layerThickness(iLevel0250+1,iCell) + activeTracersAvg0250to0700(:,iCell) = activeTracers(:,iLevel0250+1,iCell)*layerThickness(iLevel0250+1,iCell) + do k=iLevel0250+2, min(maxLevelCell(iCell),iLevel0700) + sumLayerThickness = sumLayerThickness + layerThickness(k,iCell) + activeTracersAvg0250to0700(:,iCell) = activeTracersAvg0250to0700(:,iCell) + activeTracers(:,k,iCell)*layerThickness(k,iCell) + enddo + activeTracersAvg0250to0700(:,iCell) = activeTracersAvg0250to0700(:,iCell) / max(sumLayerThickness,1.0_RKIND) + endif + + if (iLevel0700+1.le.maxLevelCell(iCell)) then + sumLayerThickness = layerThickness(iLevel0700+1,iCell) + activeTracersAvg0700to2000(:,iCell) = activeTracers(:,iLevel0700+1,iCell)*layerThickness(iLevel0700+1,iCell) + do k=iLevel0700+2, min(maxLevelCell(iCell),iLevel2000) + sumLayerThickness = sumLayerThickness + layerThickness(k,iCell) + activeTracersAvg0700to2000(:,iCell) = activeTracersAvg0700to2000(:,iCell) + activeTracers(:,k,iCell)*layerThickness(k,iCell) + enddo + activeTracersAvg0700to2000(:,iCell) = activeTracersAvg0700to2000(:,iCell) / max(sumLayerThickness,1.0_RKIND) + endif + + if (iLevel2000+1.le.maxLevelCell(iCell)) then + sumLayerThickness = layerThickness(iLevel2000+1,iCell) + activeTracersAvg2000toBottom(:,iCell) = activeTracers(:,iLevel2000+1,iCell)*layerThickness(iLevel2000+1,iCell) + do k=iLevel2000+2, maxLevelCell(iCell) + sumLayerThickness = sumLayerThickness + layerThickness(k,iCell) + activeTracersAvg2000toBottom(:,iCell) = activeTracersAvg2000toBottom(:,iCell) + activeTracers(:,k,iCell)*layerThickness(k,iCell) + enddo + activeTracersAvg2000toBottom(:,iCell) = activeTracersAvg2000toBottom(:,iCell) / max(sumLayerThickness,1.0_RKIND) + endif + enddo + + ! + ! compute barotropic kinetic energy + ! + + ! scratch arrays + call mpas_pool_get_field(scratchPool, 'normalThicknessFluxSum', normalThicknessFluxSumField) + call mpas_allocate_scratch_field(normalThicknessFluxSumField, .true.) + normalThicknessFluxSum => normalThicknessFluxSumField % array + + call mpas_pool_get_field(scratchPool, 'layerThicknessSumEdge', layerThicknessSumEdgeField) + call mpas_allocate_scratch_field(layerThicknessSumEdgeField, .true.) + layerThicknessSumEdge => layerThicknessSumEdgeField % array + + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + + ! normal Barotropic Velocity = sum(h*u)/sum(h) on each edge + layerThicknessEdge1 = 0.5_RKIND*( layerThickness(1,cell1) + layerThickness(1,cell2) ) + normalThicknessFluxSum(iEdge) = layerThicknessEdge1 * normalVelocity(1,iEdge) + layerThicknessSumEdge(iEdge) = layerThicknessEdge1 + + do k=2, maxLevelEdgeTop(iEdge) + layerThicknessEdge1 = 0.5_RKIND*( layerThickness(k,cell1) + layerThickness(k,cell2) ) + + normalThicknessFluxSum(iEdge) = normalThicknessFluxSum(iEdge) + layerThicknessEdge1 * normalVelocity(k,iEdge) + layerThicknessSumEdge(iEdge) = layerThicknessSumEdge(iEdge) + layerThicknessEdge1 + enddo + enddo + + do iCell = 1, nCells + invAreaCell1 = 1.0_RKIND / areaCell(iCell) + barotropicSpeed(iCell) = 0.0_RKIND + !columnIntegratedSpeed(iCell) = 0.0_RKIND + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + + coeff = 0.25_RKIND * dcEdge(iEdge) * dvEdge(iEdge) * invAreaCell1 + ! this is kinetic energy, in units of m^2/sec^2 + barotropicSpeed(iCell) = barotropicSpeed(iCell) & + + coeff * (normalThicknessFluxSum(iEdge) / layerThicknessSumEdge(iEdge))**2 + + end do + barotropicSpeed(iCell) = sqrt(2.0_RKIND*barotropicSpeed(iCell)) + + ! columnIntegratedSpeed = sum(h*sqrt(2*ke)), where ke is kineticEnergyCell + ! and the sum is over the full column at cell centers. + columnIntegratedSpeed(iCell) = layerThickness(1,iCell)*sqrt( 2.0_RKIND * kineticEnergyCell(1,iCell) ) + do k=2, maxLevelCell(iCell) + columnIntegratedSpeed(iCell) = columnIntegratedSpeed(iCell) & + + layerThickness(k,iCell)*sqrt( 2.0_RKIND * kineticEnergyCell(k,iCell) ) + enddo + + end do block => block % next end do @@ -225,7 +698,7 @@ end subroutine ocn_compute_high_frequency_output!}}} ! routine ocn_restart_high_frequency_output ! !> \brief Save restart for MPAS-Ocean analysis member -!> \author Todd Ringler +!> \author Todd Ringler and Phillip Wolfram !> \date 2015/06/12 !> \details !> This routine conducts computation required to save a restart state @@ -272,7 +745,7 @@ end subroutine ocn_restart_high_frequency_output!}}} ! routine ocn_finalize_high_frequency_output ! !> \brief Finalize MPAS-Ocean analysis member -!> \author Todd Ringler +!> \author Todd Ringler and Phillip Wolfram !> \date 2015/06/12 !> \details !> This routine conducts all finalizations required for this diff --git a/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking.F b/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking.F new file mode 100644 index 0000000000..1ac184bd8c --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking.F @@ -0,0 +1,2831 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_lagrangian_particle_tracking +! +!> \brief MPAS ocean analysis mode member: lagrangian_particle_tracking +!> \author Phillip J. Wolfram +!> \date 02/20/14 and 07/23/2015 +!> \details +!> MPAS ocean analysis core member: lagrangian particle tracking +!> module computes Lagrangian particle trajectories and associated +!> diagnostics +!----------------------------------------------------------------------- +#define COMMA , +#ifdef MPAS_DEBUG +#define LIGHT_DEBUG_WRITE(Z) print *, Z +#define LIGHT_DEBUG_ALL_WRITE(Z) print *, Z +#else +#define LIGHT_DEBUG_WRITE(Z) ! print *, Z +#define LIGHT_DEBUG_ALL_WRITE(Z) ! print *, Z +#endif + +#define LIGHT_ERROR_WRITE(Z) print *, Z +#define LIGHT_WARNING_WRITE(Z) print *, Z + +module ocn_lagrangian_particle_tracking + + use mpas_timer + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + + use ocn_particle_list + use ocn_lagrangian_particle_tracking_interpolations + use ocn_lagrangian_particle_tracking_reset + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_lagrangian_particle_tracking, & + ocn_compute_lagrangian_particle_tracking, & + ocn_restart_lagrangian_particle_tracking, & + ocn_finalize_lagrangian_particle_tracking + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + ! neighboring processors and arrays for send numbers / recvs, total number of neighboring processors to a particular processor + ! allocated in init, deallocated in finalize + ! these globals could be moved to the framework component, as well + ! as quite a few of the subroutines + integer, dimension(:), pointer :: g_compProcNeighsNearby => null(), g_compProcNeighs => null(), g_ioProcNeighs=>null() + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_lagrangian_particle_tracking +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 02/20/14 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_lagrangian_particle_tracking(domain, err)!{{{ + + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + logical :: config_AM_lagrPartTrack_reset_particles + character (len=StrKIND), pointer :: config_AM_lagrPartTrack_reset_criteria + + err = 0 + + LIGHT_DEBUG_WRITE('starting ocn_init_lagrangian_particle_tracking') + !call mpas_timer_start("totalLPT") + call mpas_timer_start("initLPT") + + ! resets likely are unnecessary + !call mpas_stream_mgr_reset_alarms(stream_manager, streamID='lagrPartTrackInput', ierr=err) + !call mpas_stream_mgr_reset_alarms(stream_manager, streamID='lagrPartTrackRestart', ierr=err) + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! init + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! build up the particles lists and fill them with their data + call mpas_particle_list_build_and_assign_particle_list(domain, err) + ! now we have built the particlelist for a given block + LIGHT_DEBUG_WRITE('finished building and allocating particles in init') + + ! parallel code: + + ! get "MPI halos" for communication of particles in halo during computational step + call mpas_particle_list_build_computation_halos(domain, err, g_compProcNeighsNearby) + LIGHT_DEBUG_WRITE('finished building and computational halos') + + ! get "MPI halos" for IO communication during write and restart steps (ioBlock to currentBlocks) + ! AllToAll Computation! + call mpas_particle_list_build_halos(domain, err, 'currentBlock', g_ioProcNeighs) + LIGHT_DEBUG_WRITE('g_ioProcNeighs=' COMMA g_ioProcNeighs) + LIGHT_DEBUG_WRITE('finished building io halos') + + ! transfer particles to their appropriate blocks (currentBlock) via MPI + ! note, don't necessarily need to have g_ionSend and g_ionRecv comeout +#ifdef MPAS_DEBUG + call mpas_timer_start("trans_from_block_to_blockLPT") + call mpas_particle_list_test_numparticles_to_neighprocs(domain % dminfo % my_proc_id, g_compProcNeighsNearby, g_ioProcNeighs) +#endif + ! move particles to the 'currentBlock' + call mpas_particle_list_transfer_particles_from_block_to_named_block(domain, err, .True., .False., 'currentBlock', & + g_ioProcNeighs) +#ifdef MPAS_DEBUG + call mpas_timer_stop("trans_from_block_to_blockLPT") + !call MPI_Barrier(domain % dminfo % comm, err) +#endif + + ! tests to make sure all the values are ok !{{{ +#ifdef MPAS_DEBUG + call mpas_particle_list_test_neighscalc(domain, err) + call mpas_particle_list_test_numparticles_to_neighprocs(domain % dminfo % my_proc_id, g_compProcNeighsNearby, g_ioProcNeighs) + call mpas_particle_list_test_num_current_particlelist(domain) +#endif + LIGHT_DEBUG_WRITE('g_compProcNeighsNearby = ' COMMA g_compProcNeighsNearby) + !}}} + + ! now set sums for autocorrelation calculation to be zero + call zero_autocorrelation_sums(domain) + + ! previous compute startup calls + call intialize_wachspress_coefficients(domain, err) + call initalize_fields(domain, err) + call compute_velocity_on_potentialdensity_surface(domain,err,1) + call compute_velocity_on_potentialdensity_surface(domain,err,2) + call initialize_particle_properties(domain,2,err) + call write_lagrangian_particle_tracking(domain, err) + + ! set up particle reset condition + call ocn_setup_particle_reset_condition(domain, err) + + LIGHT_DEBUG_WRITE('finished ocn_init_lagrangian_particle_tracking') + call mpas_timer_stop("initLPT") + + !call mpas_timer_stop("totalLPT") + + end subroutine ocn_init_lagrangian_particle_tracking!}}} + + +!*********************************************************************** +! +! routine ocn_compute_lagrangian_particle_tracking +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 02/20/14 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine ocn_compute_lagrangian_particle_tracking(domain, timeLevel, err)!{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: lagrPartTrackFieldsPool, lagrPartTrackCellsPool, lagrPartTrackScratchPool + integer, dimension(:), pointer :: cellOwnerBlock + integer, pointer :: currentBlock, ioBlock, indexToParticleID, transfered + type (mpas_particle_list_type), pointer :: particlelist + type (mpas_particle_type), pointer :: particle + + real (kind=RKIND), dimension(3) :: particlePosition, particleVelocity + real (kind=RKIND), pointer :: xParticle, yParticle, zParticle, lonVel, latVel, buoyancyParticle, sumU, sumV, sumUU, & + sumUV, sumVV + real (kind=RKIND), dimension(3) :: xSubStep, diffSubStep, diffParticlePosition + real (kind=RKIND), pointer :: zLevelParticle + real (kind=RKIND), dimension(:,:), pointer :: zTop, vertVelocityTop, zMid, areaBArray + real (kind=RKIND), dimension(:), pointer :: bottomDepth + type (field2DReal), pointer :: normalVelocity, uVertexVelocity, vVertexVelocity, wVertexVelocity, layerThickness + + real (kind=RKIND), dimension(:,:), pointer :: uVertexVelocityArray, vVertexVelocityArray, wVertexVelocityArray, & + buoyancyTimeInterp, potentialDensity + + real(kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell + real(kind=RKIND), dimension(:), pointer :: xVertex, yVertex, zVertex + + integer, dimension(:,:), pointer :: verticesOnCell, boundaryVertex + integer, dimension(:), pointer :: maxLevelCell + + integer theVertex, iLevel, iLevelBuoyancy, aVertex, & + nSteps, timeStep, subStep, subStepOrder, timeInterpOrder, aTimeLevel, nCellVertices, blockProc, arrayIndex + integer, pointer :: nCells, nVertLevels, iCell + integer, dimension(:), pointer :: nCellVerticesArray + integer, dimension(:,:), pointer :: cellsOnCell + logical, pointer :: onSphere + logical :: config_AM_lagrPartTrack_reset_particles + character (len=StrKIND), pointer :: config_AM_lagrPartTrack_reset_criteria, & + config_AM_lagrPartTrack_compute_interval, config_AM_lagrPartTrack_output_stream + + real (kind=RKIND), dimension(4) :: kWeightK, kWeightKVert + real (kind=RKIND), dimension(3,4) :: kWeightX + real (kind=RKIND), dimension(4) :: kWeightXVert + real (kind=RKIND), dimension(4) :: kWeightT, kWeightTVert + real (kind=RKIND), dimension(3,5) :: kCoeff + real (kind=RKIND), dimension(5) :: kCoeffVert + real (kind=RKIND), dimension(2) :: timeCoeff + real (kind=RKIND) :: dt, dtSim, tSubStep + real (kind=RKIND), pointer :: dtParticle + real (kind=RKIND) :: zSubStep + real (kind=RKIND) :: diffSubStepVert, diffParticlePositionVert, particleVelocityVert, verticalVelocityInterp + real (kind=RKIND) :: buoyancyInterp + real (kind=RKIND), pointer :: sphereRadius + integer, pointer :: verticalTreatment, vertexReconstMethod, timeIntegration, indexLevel, filterNum + character(len=StrKIND), pointer :: config_dt + type (MPAS_timeInterval_type) :: timeStepESMF + logical :: resetParticle, resetParticleAny + integer :: err_tmp + + err = 0 + + !! don't do compute for debugging purposes + !call mpas_log_write( 'Computing Lagrangian Particle Tracking -- NO COMPUTATION!') + !return + + dminfo = domain % dminfo + + LIGHT_DEBUG_WRITE('Computing Lagrangian Particle Tracking...') + !call mpas_timer_start("totalLPT") + call mpas_timer_start("computeLPT") + + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_filter_number', filterNum) + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_reset_criteria', config_AM_lagrPartTrack_reset_criteria) + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_compute_interval', config_AM_lagrPartTrack_compute_interval) + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_output_stream', config_AM_lagrPartTrack_output_stream) + + if (trim(config_AM_lagrPartTrack_reset_criteria) == 'none') then + config_AM_lagrPartTrack_reset_particles = .False. + else + config_AM_lagrPartTrack_reset_particles = .True. + end if + + ! get the most recent velocities on the potential density surfaces +#ifdef MPAS_DEBUG + call mpas_timer_start("velocity_pot_density_LPT") +#endif + call compute_velocity_on_potentialdensity_surface(domain,err,2) +#ifdef MPAS_DEBUG + call mpas_timer_stop("velocity_pot_density_LPT") +#endif + + resetParticleAny = .False. + + block => domain % blocklist + do while (associated(block)) !{{{ +#ifdef MPAS_DEBUG + call mpas_timer_start("memtasksLPT") +#endif + ! allocate scratch memory / setup pointers / get block !{{{ + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackFields', lagrPartTrackFieldsPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackScratch', lagrPartTrackScratchPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackCells', lagrPartTrackCellsPool) + + ! particlelist should be stored in the structs pool probably (need to seriously rework the code!) + particlelist => block % particlelist + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + call mpas_pool_get_array(lagrPartTrackCellsPool, 'wachspressAreaB', areaBArray) + call mpas_pool_get_array(diagnosticsPool, 'zTop', zTop) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(diagnosticsPool, 'vertVelocityTop', vertVelocityTop) + call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', buoyancyTimeInterp) + + ! note, originally this was diagnostics % state % normalVelocity (without time level), but + ! now there is a time level so selection of the correct time level appears to be tricky + ! the issue is large, nearly NAN normalVelocities with timeLevel=2 + call mpas_pool_get_field(statePool, 'normalVelocity', normalVelocity, timeLevel=timeLevel) + ! potentially a costly function call, but ensures halos are ok + !call mpas_dmpar_exch_halo_field(normalVelocity) + call mpas_pool_get_field(statePool, 'layerThickness', layerThickness, timeLevel=timeLevel) + + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'boundaryVertex', boundaryVertex) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nCellVerticesArray) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + + call mpas_pool_get_config(meshPool, 'on_a_sphere', onSphere) + call mpas_pool_get_config(meshPool, 'sphere_radius', sphereRadius) + !}}} + + ! get the appropriate time scale for particle integration, e.g., dtSim + if (trim(config_AM_lagrPartTrack_compute_interval) == 'dt') then + ! from time step value + ! would eventually need to correspond to domain dt, but for now this is a + ! global constant + call mpas_pool_get_config(block % configs, 'config_dt', config_dt) + call mpas_set_timeInterval(timeStepESMF, timeString=config_dt, ierr=err) + ! using the output interval + else if (trim(config_AM_lagrPartTrack_compute_interval) == 'output') then + LIGHT_ERROR_WRITE('LIGHT output mode is not yet supported!') + !! from output interval + !call MPAS_stream_mgr_get_property(domain % streamManager, config_AM_lagrPartTrack_output_stream, & + ! MPAS_STREAM_PROPERTY_REF_TIME, config_dt, err) + !write(*,*) 'config_AM_lagrPartTrack_output_stream = ', config_AM_lagrPartTrack_output_stream + !write(*,*) 'config_dt= ', config_dt + !call mpas_set_timeInterval(timeStepESMF, timeString=config_dt, ierr=err) + else + ! using specified interval from timestring + call mpas_set_timeInterval(timeStepESMF, timeString=trim(config_AM_lagrPartTrack_compute_interval), ierr=err) + end if + call mpas_get_timeInterval(timeStepESMF, dt=dtSim) + LIGHT_DEBUG_WRITE('dtSim = ' COMMA dtSim) + + !{{{ + ! flip previous time level to back (switching memory pointers) + call mpas_pool_shift_time_levels(lagrPartTrackFieldsPool) + call mpas_pool_get_field(lagrPartTrackFieldsPool, 'uVertexVelocity', uVertexVelocity, timeLevel=2) + call mpas_pool_get_field(lagrPartTrackFieldsPool, 'vVertexVelocity', vVertexVelocity, timeLevel=2) + call mpas_pool_get_field(lagrPartTrackFieldsPool, 'wVertexVelocity', wVertexVelocity, timeLevel=2) +#ifdef MPAS_DEBUG + call mpas_timer_stop("memtasksLPT") +#endif + +#ifdef MPAS_DEBUG + call mpas_timer_start("reconst_filter_LPT") +#endif + ! need to handle periodicity within functions below for vertex reconstruction + call ocn_vertex_reconstruction(filterNum, meshPool, lagrPartTrackScratchPool, lagrPartTrackCellsPool, & + layerThickness % array, normalVelocity % array, & + uVertexVelocity, vVertexVelocity, wVertexVelocity) +#ifdef MPAS_DEBUG + call mpas_timer_stop("reconst_filter_LPT") + LIGHT_DEBUG_ALL_WRITE('uVertexVelocity=' COMMA uVertexVelocity % array) + LIGHT_DEBUG_ALL_WRITE('vVertexVelocity=' COMMA vVertexVelocity % array) + LIGHT_DEBUG_ALL_WRITE('wVertexVelocity=' COMMA wVertexVelocity % array) +#endif + !}}} + + LIGHT_DEBUG_WRITE('beginning particle loop with particlelist associated = ' COMMA associated(particlelist)) +#ifdef MPAS_DEBUG + call mpas_particle_list_test_num_current_particlelist(domain) +#endif + + !!!!!!!!!! LOOP OVER PARTICLES !!!!!!!!!! + ! update the particle position (just from initialized value for now) + ! this is a loop over particle list and its datastructures + do while(associated(particlelist)) !{{{ + ! get pointers / option values + particle => particlelist % particle + + ! get values {{{ +#ifdef MPAS_DEBUG + call mpas_timer_start("memtasksLPT") +#endif + call mpas_pool_get_array(particle % haloDataPool, 'xParticle', xParticle) + call mpas_pool_get_array(particle % haloDataPool, 'yParticle', yParticle) + call mpas_pool_get_array(particle % haloDataPool, 'zParticle', zParticle) + particlePosition(1) = xParticle + particlePosition(2) = yParticle + particlePosition(3) = zParticle + + call mpas_pool_get_array(particle % haloDataPool, 'zLevelParticle', zLevelParticle) + + call mpas_pool_get_array(particle % haloDataPool, 'verticalTreatment', verticalTreatment) + call mpas_pool_get_array(particle % haloDataPool, 'vertexReconstMethod', vertexReconstMethod) + call mpas_pool_get_array(particle % haloDataPool, 'indexLevel', indexLevel) + call mpas_pool_get_array(particle % haloDataPool, 'timeIntegration', timeIntegration) + call mpas_pool_get_array(particle % haloDataPool, 'dtParticle', dtParticle) + call mpas_pool_get_array(particle % haloDataPool, 'buoyancyParticle', buoyancyParticle) + call mpas_pool_get_array(particle % haloDataPool, 'indexToParticleID', indexToParticleID) + call mpas_pool_get_array(particle % haloDataPool, 'currentCell', iCell) + + call mpas_pool_get_array(particle % haloDataPool, 'lonVel', lonVel) + call mpas_pool_get_array(particle % haloDataPool, 'latVel', latVel) + call mpas_pool_get_array(particle % haloDataPool, 'sumU', sumU) + call mpas_pool_get_array(particle % haloDataPool, 'sumV', sumV) + call mpas_pool_get_array(particle % haloDataPool, 'sumUU', sumUU) + call mpas_pool_get_array(particle % haloDataPool, 'sumUV', sumUV) + call mpas_pool_get_array(particle % haloDataPool, 'sumVV', sumVV) +#ifdef MPAS_DEBUG + call mpas_timer_stop("memtasksLPT") +#endif + ! process timers / particle reset functions here (need a timer and ability to reset particle to + ! some set of initial values for the reset + !}}} + + !!!!!!!!!! COMPUTE TIME STEP INFORMATION !!!!!!!!!! +#ifdef MPAS_DEBUG + call mpas_timer_start("time_step_LPT") +#endif + ! adjust time step for consistency with integer number of steps + nSteps = ceiling(dtSim/dtParticle) + dt = dtSim/nSteps + + !!!!!!!!!! ASSIGN TEMPORAL INTEGRATION COEFFICIENTS !!!!!!!!!! + ! kCoeff is (3,subStepOrder+1) + ! kWeightX is subStepOrder + ! kWeightK is subStepOrder + ! kWeightT is subStepOrder + select case (timeIntegration) !{{{ + case(1) ! EE integration + kWeightK(1) = 0.0_RKIND + + kWeightT(1) = 0.0_RKIND + + kWeightX(:,1) = 1.0_RKIND + + subStepOrder = 1 + case(2) ! RK2 integration + kWeightK(1) = 0.0_RKIND + kWeightK(2) = 0.5_RKIND + + kWeightT(1) = 0.0_RKIND + kWeightT(2) = 0.5_RKIND + + kWeightX(:,1) = 0.0_RKIND + kWeightX(:,2) = 1.0_RKIND + + subStepOrder = 2 + case(4) ! RK4 integration + kWeightK(1) = 0.0_RKIND + kWeightK(2) = 0.5_RKIND + kWeightK(3) = 0.5_RKIND + kWeightK(4) = 1.0_RKIND + + kWeightT(1) = 0.0_RKIND + kWeightT(2) = 0.5_RKIND + kWeightT(3) = 0.5_RKIND + kWeightT(4) = 1.0_RKIND + + kWeightX(:,1) = 1.0_RKIND/6.0_RKIND + kWeightX(:,2) = 1.0_RKIND/3.0_RKIND + kWeightX(:,3) = 1.0_RKIND/3.0_RKIND + kWeightX(:,4) = 1.0_RKIND/6.0_RKIND + + subStepOrder = 4 + case default ! RK2 integration + kWeightK(1) = 0.0_RKIND + kWeightK(2) = 0.5_RKIND + + kWeightT(1) = 0.0_RKIND + kWeightT(2) = 0.5_RKIND + + kWeightX(:,1) = 0.0_RKIND + kWeightX(:,2) = 1.0_RKIND + + subStepOrder = 2 + end select !}}} + + ! use same integration coefficients for the vertical + kWeightKVert = kWeightK + kWeightXVert = kWeightX(1,:) + kWeightTVert = kWeightT + kCoeffVert = kCoeff(1,:) + + !!!!!!!!!! LOOP OVER TIME STEPS !!!!!!!!!! + do timeStep = 1, nSteps !{{{ + ! kCoeff is (3,subStepOrder+1) + kCoeff = 0.0_RKIND + kCoeffVert = 0.0_RKIND + ! compute first + do subStep = 1, subStepOrder !{{{ + + !!!!!!!!!! COMPUTE PARTICLE SUBSTEP POSITIONS USE FOR VELOCITY !!!!!!!!!! + ! horizontal + xSubStep = particlePosition + diffSubStep = kWeightK(subStep) * kCoeff(:,subStep) + + if(kWeightK(subStep) /= 0.0_RKIND) then + ! project substep to correct spherical shell because diffSubStep isn't 0 and particle moves +#ifdef MPAS_DEBUG + call mpas_timer_start("particle_horizontal_movementLPT") +#endif + call particle_horizontal_movement(meshPool, xSubStep, diffSubStep) +#ifdef MPAS_DEBUG + call mpas_timer_stop("particle_horizontal_movementLPT") +#endif + end if + + ! vertical + zSubStep = zLevelParticle + diffSubStepVert = kWeightKVert(subStep) * kCoeffVert(subStep) + + if(kWeightKVert(subStep) /= 0.0_RKIND) then + zSubStep = zSubStep + diffSubStepVert + end if + + ! get new time step (tm = (timestep-1)*dt) + tSubStep = (timeStep-1 + kWeightT(subStep)) * dt + + !!!!!!!!!! GET SPECIFIC CELL INDICES AND GEOMETRY !!!!!!!!!! + + ! determine cell location +#ifdef MPAS_DEBUG + call mpas_timer_start("get_validated_cell_idLPT") +#endif + LIGHT_DEBUG_WRITE('beginning of substeps') + call get_validated_cell_id(nCells, xCell,yCell,zCell , xVertex,yVertex,zVertex, & + xSubStep(1),xSubStep(2),xSubStep(3), meshPool, & + nCellVerticesArray, verticesOnCell, iCell, nCellVertices, cellsOnCell) + LIGHT_DEBUG_WRITE('iCell=' COMMA iCell) +#ifdef MPAS_DEBUG + call mpas_timer_stop("get_validated_cell_idLPT") +#endif + + if(verticalTreatment /= 4) then + ! other cases using zSubStep for iLevel and vertical interpolation +#ifdef MPAS_DEBUG + call mpas_timer_start("mpas_get_vertical_idLPT") +#endif + iLevel = mpas_get_vertical_id(maxLevelCell(iCell), zSubStep, zMid(:,iCell)) +#ifdef MPAS_DEBUG + call mpas_timer_stop("mpas_get_vertical_idLPT") +#endif + LIGHT_DEBUG_WRITE('iLevel=' COMMA iLevel) + end if + + !!!!!!!!!! TEMPORALLY INTERPOLATE TIME FIELD !!!!!!!!!! + ! use these coefficients to just get the velocity at n + !timeInterpOrder = 1 + !timeCoeff(1) =1.0_RKIND + ! use these coefficients to just get the velocity at n+1 + !timeInterpOrder = 2 + !timeCoeff(1) =0.0_RKIND + !timeCoeff(2) =1.0_RKIND + ! get interpolation coefficients for linear interpolation in time + timeInterpOrder = 2 + timeCoeff(1) = tSubStep / dtSim + timeCoeff(2) = 1.0_RKIND - timeCoeff(1) + + ! ensure that buoyancy is fixed for each run + buoyancyInterp = buoyancyParticle + ! return interpolated horizontal velocity "particleVelocity" and vertical velocity "particleVelocityVert" + +#ifdef MPAS_DEBUG + call mpas_timer_start("velocity_time_interpolationLPT") +#endif + call velocity_time_interpolation(particleVelocity, particleVelocityVert, & + diagnosticsPool, lagrPartTrackFieldsPool, & + timeInterpOrder, timeCoeff, iCell, iLevel, buoyancyInterp, maxLevelCell, & + verticalTreatment, indexLevel, nCellVertices, verticesOnCell, boundaryVertex, & + xSubStep, zSubStep, zMid, zTop, vertVelocityTop, xVertex, yVertex, zVertex, meshPool, areaBArray) +#ifdef MPAS_DEBUG + call mpas_timer_stop("velocity_time_interpolationLPT") +#endif + + !!!!!!!!!! FORM INTEGRATION WEIGHTS kj !!!!!!!!!! + kCoeff(:,subStep+1) = dt * particleVelocity + kCoeffVert(subStep+1) = dt * particleVelocityVert + end do + + !!!!!!!!!! UPDATE PARTICLE POSITIONS !!!!!!!!!! + !!!!!!!!!! HORIZONTAL AND VERTICAL CONSIDERED SEPARATELY !!!!!!!!!! + diffParticlePosition = 0.0_RKIND + diffParticlePositionVert = 0.0_RKIND + do subStep = 1, subStepOrder + ! first complete particle integration + diffParticlePosition = diffParticlePosition + kWeightX(:,subStep) * kCoeff(:,subStep+1) + diffParticlePositionVert = diffParticlePositionVert + kWeightXVert(subStep) * kCoeffVert(subStep+1) + end do + ! now, make sure particle position is still on same spherical shell as before +#ifdef MPAS_DEBUG + call mpas_timer_start("particle_horizontal_movementLPT") +#endif + call particle_horizontal_movement(meshPool, particlePosition, diffParticlePosition) +#ifdef MPAS_DEBUG + call mpas_timer_stop("particle_horizontal_movementLPT") +#endif + ! now can do any vertical movements independent of the horizontal movement + ! that was just calculated ( probably need to have more output from the vertical_treatment + ! and aggregate here + zLevelParticle = zLevelParticle + diffParticlePositionVert + + end do !}}} +#ifdef MPAS_DEBUG + call mpas_timer_stop("time_step_LPT") +#endif + !}}} + + !!!!!!!!!! PERFORM SAMPLING (VELOCITY, TEMP, SALINITY, ETC) !!!!!!!!!! + ! this could be done just before the output anyway + + ! need iCell computed for final position + ! need scalar values interpolated in time to yield single value + ! probably need to store zMid too, including flipping it + ! get updated cell location +#ifdef MPAS_DEBUG + call mpas_timer_start("get_validated_cell_idLPT") +#endif + LIGHT_DEBUG_WRITE('do sampling') + call get_validated_cell_id(nCells, xCell,yCell,zCell , xVertex,yVertex,zVertex, & + particlePosition(1),particlePosition(2),particlePosition(3), meshPool, & + nCellVerticesArray, verticesOnCell, iCell, nCellVertices, cellsOnCell) +#ifdef MPAS_DEBUG + call mpas_timer_stop("get_validated_cell_idLPT") +#endif + + if(verticalTreatment == 4) then !('buoyancySurface') !{{{ + !! determine index level (don't need validated version because that will "fix" values which we may not want + !! however, if the particle's target buoyancy surface doesn't exist then we will need to + !! ensure that vertical location is valid, placing particles outside of range of zMid back inside + !! we don't validate this because we want the code to fail, at least initially, in the case that + !! the particle is in a cell that does not have the proper buoyancy target because this implies + !! that the buoyancy tracking mode has completely failed. + !! need to make sure it is validated for buoyancy particles + iLevelBuoyancy = mpas_get_vertical_id(maxLevelCell(iCell), buoyancyInterp, buoyancyTimeInterp(:,iCell)) + ! interpolate the scalars now (assumes that scalar value is constant within a particular cell) + call interp_cell_scalars(iLevelBuoyancy, maxLevelCell(iCell), buoyancyInterp, buoyancyTimeInterp(:,iCell), & + zMid(:,iCell), zLevelParticle) + !}}} + else + ! make sure final zLevelParticle is ok so that it can't extent past zMid range +#ifdef MPAS_DEBUG + call mpas_timer_start("mpas_get_vertical_idLPT") +#endif + iLevel = mpas_get_vertical_id(maxLevelCell(iCell), zLevelParticle, zMid(:,iCell)) +#ifdef MPAS_DEBUG + call mpas_timer_stop("mpas_get_vertical_idLPT") +#endif + end if + + ! compute necessary information for autocorrelation !{{{ + ! get the updated velocity + ! ensure that buoyancy is fixed for each run + buoyancyInterp = buoyancyParticle + ! we just need the last part of the velocity field interpolation because we are at the end of the timestep + timeInterpOrder = 2 + timeCoeff(1) = 0.0_RKIND + timeCoeff(2) = 1.0_RKIND + ! return interpolated horizontal velocity "particleVelocity" and vertical velocity "particleVelocityVert" + ! noting we use the final positions +#ifdef MPAS_DEBUG + call mpas_timer_start("velocity_time_interpolationLPT") +#endif + call velocity_time_interpolation(particleVelocity, particleVelocityVert, & + diagnosticsPool, lagrPartTrackFieldsPool, & + timeInterpOrder, timeCoeff, iCell, iLevel, buoyancyInterp, maxLevelCell, & + verticalTreatment, indexLevel, nCellVertices, verticesOnCell, boundaryVertex, & + particlePosition, zLevelParticle, zMid, zTop, vertVelocityTop, xVertex, yVertex, zVertex, & + meshPool, areaBArray) +#ifdef MPAS_DEBUG + call mpas_timer_stop("velocity_time_interpolationLPT") +#endif + ! convert horizontal velocity to lat/lon velocity + + ! store velocity for use in computing normalized autocorrelation offline +#ifdef MPAS_DEBUG + call mpas_timer_start("mpas_convert_xyz_velocity_to_latlonLPT") +#endif + call mpas_convert_xyz_velocity_to_latlon(lonVel, latVel, particlePosition, particleVelocity) +#ifdef MPAS_DEBUG + call mpas_timer_stop("mpas_convert_xyz_velocity_to_latlonLPT") +#endif + + ! now store components needed to compute integral timescale +#ifdef MPAS_DEBUG + call mpas_timer_start("storeSingleParticleStats") +#endif + sumU = sumU + lonVel + sumV = sumV + latVel + sumUU = sumUU + lonVel*lonVel + sumUV = sumUV + lonVel*latVel + sumVV = sumVV + latVel*latVel +#ifdef MPAS_DEBUG + call mpas_timer_stop("storeSingleParticleStats") +#endif + !}}} + + ! properly store particle position (because we can't store arrays directly for particles and must + ! work entirely in vectors + xParticle = particlePosition(1) + yParticle = particlePosition(2) + zParticle = particlePosition(3) + + !!!!!!!!!! PASS PARTICLES FROM PROCESSOR TO PROCESSOR !!!!!!!!!! + !{{{ + ! 1. determine if iCell is on halo (just set each particle's currentBlock to the correct currentBlock + ! 2. determine owning block in halo, update particle's currentBlock + + ! determine currentBlock ownership of iCell + ! and set cellOwnerBlock to be current block +#ifdef MPAS_DEBUG + call mpas_timer_start("particleAssignments") +#endif + + resetParticle = .False. + if (config_AM_lagrPartTrack_reset_particles) then ! need to link to currentBlockReset for communication + ! determine if particles should be reset based on different criteria. If so, reset them. + call ocn_evaluate_particle_reset_condition(domain, block, particle, dtSim, iCell, resetParticle, err) + end if + resetParticleAny = resetParticleAny .or. resetParticle + + if (.not. resetParticle) then + ! update halo fields for particles moving from adjacent computational halos + call mpas_particle_list_update_particle_block(domain, block, particle, 'lagrPartTrackCells', iCell) + end if + +#ifdef MPAS_DEBUG + call mpas_timer_stop("particleAssignments") +#endif + !}}} + + ! get next particle to process on the list + particlelist => particlelist % next + end do !}}} + + ! get next block + block => block % next + end do !}}} + + !!!!!!!!!! PASS PARTICLES FROM PROCESSOR TO PROCESSOR !!!!!!!!!! + ! MPI calls + ! 3. place particle on temporary list to be sent to processor, removing particle from present list + ! 4. pass list of particles to owning block (outside block loop so that all blocks can be processed) + ! 5. delete all particles on the temporary lists (potentially if on different proc) + ! because they have been permenantly moved to block owning the halo + ! + ! Items 3-5 should be able to be described in terms of current code + ! noting that the most important thing is to ensure that the particle's currentBlock is + ! updated. Then, a routine can be called to make sure particles are placed on their appropriate + ! currentBlocks + + ! particle transfer can then occur from computational processor to computational processor +#ifdef MPAS_DEBUG + call mpas_timer_start("trans_from_block_to_blockLPT") +#endif + if (config_AM_lagrPartTrack_reset_particles .and. resetParticleAny) then + ! need to link to currentBlockReset for communication + call mpas_particle_list_build_halos(domain, err, 'currentBlockReset', g_compProcNeighs) + ! take the union of this halo with the particle computation halos to build complete computational halo + call mpas_particle_list_self_union_halo_lists(g_compProcNeighs, g_compProcNeighsNearby, domain % dminfo % nprocs, & + domain % dminfo % my_proc_id) + else + allocate(g_compProcNeighs(size(g_compProcNeighsNearby))) + g_compProcNeighs = g_compProcNeighsNearby + end if + call mpas_particle_list_transfer_particles_from_block_to_named_block(domain, err, .True., .False., 'currentBlock', & + g_compProcNeighs) + deallocate(g_compProcNeighs) +#ifdef MPAS_DEBUG + call mpas_timer_stop("trans_from_block_to_blockLPT") +#endif + + ! do IO communications if this is an output time step + if (mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID='lagrPartTrackOutput', & + direction=MPAS_STREAM_OUTPUT, ierr=err)) then + call write_lagrangian_particle_tracking(domain, err) + end if + + call mpas_timer_stop("computeLPT") + !call mpas_timer_stop("totalLPT") + + LIGHT_DEBUG_WRITE('finished computing Lagrangian Particle Tracking') + + end subroutine ocn_compute_lagrangian_particle_tracking!}}} + +!*********************************************************************** +! +! routine ocn_restart_lagrangian_particle_tracking +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 02/20/14 and 07/23/15 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine ocn_restart_lagrangian_particle_tracking(domain, err)!{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + !call mpas_timer_start("totalLPT") + + ! do restart if this is a restart step + if (mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID='lagrPartTrackRestart', & + direction=MPAS_STREAM_OUTPUT, ierr=err)) then + call mpas_timer_start("restartLPT") + + LIGHT_DEBUG_WRITE('start ocn_restart_lagrangian_particle_tracking') + ! transfer particles to their appropriate blocks (ioBlock) via MPI + ! note, don't necessarily need to have g_ionSend and g_ionRecv comeout + call mpas_timer_start("build_halos_LPT") + call mpas_particle_list_build_halos(domain, err, 'ioBlock', g_ioProcNeighs) + call mpas_timer_stop("build_halos_LPT") + call mpas_timer_start("transfer_particles_LPT") + call mpas_particle_list_transfer_particles_from_block_to_named_block(domain, err, .True., .True., 'ioBlock', & + g_ioProcNeighs) + call mpas_timer_stop("transfer_particles_LPT") + deallocate(g_ioProcNeighs) + + ! write out all the data, sorting to make sure that shuffled particles + ! are ouptut correctly (done separately in each function, could be + ! pulled out as an optimization) + ! write halo data out, but don't need nonhalo data because it is + ! computed for output (diagnostic, not prognostic) + call mpas_timer_start("write_halo_LPT") + call mpas_particle_list_write_halo_data(domain, err) + call mpas_timer_stop("write_halo_LPT") + !call mpas_particle_list_write_nonhalo_data(domain, err) + + ! need to now remove the io particles (remove particles that don't have the + ! correct currentBlock) + call mpas_timer_start("remove_block_particles_LPT") + call mpas_particle_list_remove_particles_not_on_current_block(domain,err) + call mpas_timer_stop("remove_block_particles_LPT") + + LIGHT_DEBUG_WRITE('end ocn_restart_lagrangian_particle_tracking') + call mpas_timer_stop("restartLPT") + end if + + !call mpas_timer_stop("totalLPT") + + end subroutine ocn_restart_lagrangian_particle_tracking!}}} + +!*********************************************************************** +! +! routine write_lagrangian_particle_tracking +! +!> \brief Driver for MPAS-Ocean analysis output +!> \author Phillip Wolfram +!> \date 02/20/14 +!> \details +!> This routine writes all output for this MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine write_lagrangian_particle_tracking(domain, err)!{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + LIGHT_DEBUG_WRITE('start write_lagrangian_particle_tracking') + !call mpas_timer_start("totalLPT") + call mpas_timer_start("writeLPT") + + !call mpas_particle_list_test_num_current_particlelist(domain) + + ! transfer particles to their appropriate blocks (ioBlock) via MPI + ! note, don't necessarily need to have g_ionSend and g_ionRecv comeout + !call mpas_log_write( 'g_ioProcNeighs = $i',MPAS_LOG_OUT, intArgs=(/ g_ioProcNeighs /) ) +#ifdef MPAS_DEBUG + call mpas_timer_start("trans_from_block_to_blockLPT") +#endif + ! depreciated (can just use update_halo_io to keep g_ioProcNeighs up to date) + !! get "MPI halos" for IO communication during write and restart steps (currentBlock to ioBlock) + call mpas_particle_list_build_halos(domain, err, 'ioBlock', g_ioProcNeighs) + ! transfer the data + call mpas_particle_list_transfer_particles_from_block_to_named_block(domain, err, .False., .True., 'ioBlock', & + g_ioProcNeighs) + deallocate(g_ioProcNeighs) +#ifdef MPAS_DEBUG + call mpas_timer_stop("trans_from_block_to_blockLPT") +#endif + + ! write out all the data, sorting to make sure that shuffled particles + ! are ouptut correctly (done separately in each function, could be + ! pulled out as an optimization) + call mpas_particle_list_write_halo_data(domain, err) + call mpas_particle_list_write_nonhalo_data(domain, err) + +#ifdef MPAS_DEBUG + call mpas_particle_list_test_num_current_particlelist(domain) +#endif + ! need to now remove the io particles (remove particles that don't have the + ! correct currentBlock) + call mpas_particle_list_remove_particles_not_on_current_block(domain,err) + +#ifdef MPAS_DEBUG + call mpas_particle_list_test_num_current_particlelist(domain) +#endif + LIGHT_DEBUG_WRITE('end write_lagrangian_particle_tracking') + call mpas_timer_stop("writeLPT") + !call mpas_timer_stop("totalLPT") + + + end subroutine write_lagrangian_particle_tracking!}}} + +!*********************************************************************** +! +! routine ocn_finalize_lagrangian_particle_tracking +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 02/20/14 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine ocn_finalize_lagrangian_particle_tracking(domain, err)!{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (block_type), pointer :: block + integer :: timeLev + + !call mpas_timer_start("totalLPT") + call mpas_timer_start("finalizeLPT") + err = 0 + + LIGHT_DEBUG_WRITE('start ocn_finalize_lagrangian_particle_tracking') + block => domain % blocklist + do while (associated(block)) + call mpas_particle_list_destroy_particle_list(block % particlelist) + block => block % next + end do + + deallocate(g_compProcNeighsNearby) + + LIGHT_DEBUG_WRITE('end ocn_finalize_lagrangian_particle_tracking') + call mpas_timer_stop("finalizeLPT") + !call mpas_timer_stop("totalLPT") + + end subroutine ocn_finalize_lagrangian_particle_tracking!}}} + +!----------------------------------------------------------------------- +! +! PRIVATE SUBROUTINES +! +!----------------------------------------------------------------------- +!{{{ + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! SUBROUTINE GET_VALIDATED_CELL_ID + ! + ! Computes the validated cell ID for a particular location base on proximity to point. + ! Phillip Wolfram 06/18/2014 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + subroutine get_validated_cell_id(nCells, xCell,yCell,zCell , xVertex,yVertex,zVertex, & + xSubStep,ySubStep,zSubStep, meshPool, nCellVerticesArray, verticesOnCell, & + iCell, nCellVertices, cellsOnCell) + + implicit none + + ! intent (in) + integer, intent(in) :: nCells !< number of cells + integer, dimension(:,:), pointer, intent(in) :: verticesOnCell !< vertex indices on cell + integer, dimension(:), pointer, intent(in) :: nCellVerticesArray + real (kind=RKIND), dimension(:), intent(in) :: xCell,yCell,zCell !< spatial location of cell centers + real (kind=RKIND), dimension(:), intent(in) :: xVertex,yVertex,zVertex !< spatial location of cell vertices + real (kind=RKIND), intent(in) :: xSubStep,ySubStep,zSubStep + type (mpas_pool_type), intent(in), pointer :: meshPool ! meshPool pointer + integer, dimension(:,:), intent(in) :: cellsOnCell ! cell connectivity + + !intent (out) + integer, intent(inout) :: iCell + integer, intent(out) :: nCellVertices + character (len=StrKIND) :: message +#ifdef MPAS_DEBUG + logical, pointer :: is_periodic + real(kind=RKIND), pointer :: x_period, y_period + logical, pointer :: on_a_sphere + real(kind=RKIND), dimension(:), pointer :: xtmp, ytmp + integer :: i + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_config(meshPool, 'is_periodic', is_periodic) + call mpas_pool_get_config(meshPool, 'x_period', x_period) + call mpas_pool_get_config(meshPool, 'y_period', y_period) +#endif + + ! get cell index +!#ifdef MPAS_DEBUG +! iCell = -1 +!#endif + call mpas_get_nearby_cell_index(nCells, xCell,yCell,zCell , & + xSubStep,ySubStep,zSubStep, meshPool, iCell, cellsOnCell, nCellVerticesArray) + + nCellVertices = nCellVerticesArray(iCell) + +#ifdef MPAS_DEBUG + ! check to make sure the horizontal location is valid, otherwise report an error + ! call mpas_log_write( 'max verticesOnCell = $i nVertices = $i',MPAS_LOG_OUT, intArgs=(/ maxval(verticesOnCell(:,iCell)), size(xVertex) /) ) + if (on_a_sphere .or. .not. is_periodic) then + if(.not. point_in_cell(nCellVertices, & + xVertex(verticesOnCell(1:nCellVertices,iCell)), & + yVertex(verticesOnCell(1:nCellVertices,iCell)), & + zVertex(verticesOnCell(1:nCellVertices,iCell)), & + xSubStep,ySubStep,zSubStep , on_a_sphere)) then + write(message, *) 'Point (', xSubStep, ySubStep, zSubStep , ') is horizontally outside cell ', iCell + LIGHT_DEBUG_WRITE(message) + write(message, *) 'Cell (', xCell(iCell), yCell(iCell), zCell(iCell), ') with index ', iCell + LIGHT_DEBUG_WRITE(message) + LIGHT_DEBUG_WRITE('xVertex = ' COMMA xVertex(verticesOnCell(1:nCellVertices,iCell))) + LIGHT_DEBUG_WRITE('yVertex = ' COMMA yVertex(verticesOnCell(1:nCellVertices,iCell))) + LIGHT_DEBUG_WRITE('zVertex = ' COMMA zVertex(verticesOnCell(1:nCellVertices,iCell))) + end if + else + allocate(xtmp(nCellVertices), ytmp(nCellVertices)) + do i = 1, nCellVertices + xtmp(i) = mpas_fix_periodicity(xVertex(verticesOnCell(i,iCell)), xSubStep, x_period) + ytmp(i) = mpas_fix_periodicity(yVertex(verticesOnCell(i,iCell)), ySubStep, y_period) + end do + if(.not. point_in_cell(nCellVertices, xtmp, ytmp, & + zVertex(verticesOnCell(1:nCellVertices,iCell)), & + xSubStep,ySubStep,zSubStep , on_a_sphere)) then + write(message, *) 'Point (', xSubStep, ySubStep, zSubStep, ') is horizontally outside cell ', iCell + LIGHT_DEBUG_WRITE(message) + write(message, *) 'Cell (', xCell(iCell), yCell(iCell), zCell(iCell), ') with index ', iCell + LIGHT_DEBUG_WRITE(message) + LIGHT_DEBUG_WRITE('xVertex = ' COMMA xtmp) + LIGHT_DEBUG_WRITE('yVertex = ' COMMA ytmp) + LIGHT_DEBUG_WRITE('zVertex = ' COMMA zVertex(verticesOnCell(1:nCellVertices,iCell))) + end if + deallocate(xtmp, ytmp) + end if +#endif + + end subroutine get_validated_cell_id + +#ifdef MPAS_DEBUG + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! FUNCTION POINT_IN_CELL + ! + ! Check to make sure point (xp,yp,zp) is within cell iCell (implicit via xv,yv,zv) + ! Phillip Wolfram 05/01/2014 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + logical function point_in_cell(nVertices, xv,yv,zv , xp,yp,zp, on_a_sphere) !{{{ + implicit none + + integer, intent(in) :: nVertices ! number of vertices for cell + real (kind=RKIND), dimension(:), intent(in) :: xv,yv,zv ! cell vertex locations + real (kind=RKIND), intent(in) :: xp,yp,zp ! point location + logical, intent(in) :: on_a_sphere ! flag designating if we are on a sphere + + integer :: aPoint, v1, v0 + real (kind=RKIND) :: pointRadius ! magnitude of a point radius + real (kind=RKIND), dimension(3) :: pPoint ! point vector + real (kind=RKIND), dimension(3, nVertices) :: pVertices ! vertices vectors + real (kind=RKIND), dimension(3) :: vec1, vec2, crossProd ! temporary vectors + integer, dimension(nVertices+1) :: cyclePlusOne + + ! normalize locations to same spherical shell (unit) for direct comparison + if (on_a_sphere) then + pointRadius = sqrt(xp*xp + yp*yp + zp*zp) + pPoint = (/ xp/pointRadius , yp/pointRadius , zp/pointRadius /) + do aPoint = 1, nVertices + pointRadius = sqrt(xv(aPoint)*xv(aPoint) + yv(aPoint)*yv(aPoint) + zv(aPoint)*zv(aPoint)) + pVertices(:,aPoint) = (/ xv(aPoint), yv(aPoint), zv(aPoint) /) / pointRadius + end do + else + pPoint = (/ xp,yp,zp /) + do aPoint = 1, nVertices + pVertices(:, aPoint) = (/ xv(aPoint), yv(aPoint), zv(aPoint) /) + end do + end if + + ! build up vertex cycle for the cell + do aPoint = 1, nVertices-1 + cyclePlusOne(aPoint) = aPoint + 1 + end do + cyclePlusOne(nVertices) = 1 + + ! check, using cross-products, that point is within the cell, assuming it is to start + point_in_cell = .true. + do aPoint = 1, nVertices + ! get indices of points + v0 = aPoint + v1 = cyclePlusOne(aPoint) + + ! compute the local vectors + vec1 = pVertices(:,v1) - pVertices(:,v0) + vec2 = pPoint - pVertices(:,v0) + + ! compute the cross product and dot with normal, if negative we are outside cell + ! we only need to fail on a single test! + call mpas_cross_product_in_r3(vec1,vec2,crossProd) + if (on_a_sphere) then + if(sum(crossProd*pVertices(:,v0)) < 0) then + point_in_cell = .false. + end if + else + if(crossProd(3) < 0) then + point_in_cell = .false. + end if + end if + + end do + + end function point_in_cell!}}} +#endif + +!*********************************************************************** +! +! routine initalize_fields +! +!> \brief Initialize fields +!> \author Phillip Wolfram +!> \date 05/22/2014 +!> \details +!> This routine inializes the fields necessary for particle tracking +! +!----------------------------------------------------------------------- + subroutine initalize_fields(domain, err)!{{{ + + implicit none + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: lagrPartTrackFieldsPool, lagrPartTrackScratchPool, lagrPartTrackCellsPool + real(kind=RKIND), dimension(:,:), pointer :: normalVelocity, layerThickness + integer :: timeLev + integer, pointer :: filterNum + type (field2DReal), pointer :: uVV, vVV, wVV + real (kind=RKIND), dimension(:,:), pointer :: potentialDensity + + !call mpas_log_write( 'inialize_vertex_velocity start') + + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_filter_number', filterNum) + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! setup pointers / get block + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackFields', lagrPartTrackFieldsPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackScratch', lagrPartTrackScratchPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackCells', lagrPartTrackCellsPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + + !! initialize field for RBF (needed depending upon calling pattern of analysis member) + !call mpas_initialize_vectors(meshPool) + !call mpas_init_reconstruct(meshPool) + + ! initialize fresh memory for both levels + do timeLev = 1, 2 + ! connect variables to pointer array + call mpas_pool_get_field(lagrPartTrackFieldsPool, 'uVertexVelocity', uVV, timeLevel=timeLev) + call mpas_pool_get_field(lagrPartTrackFieldsPool, 'vVertexVelocity', vVV, timeLevel=timeLev) + call mpas_pool_get_field(lagrPartTrackFieldsPool, 'wVertexVelocity', wVV, timeLevel=timeLev) + + ! initialize, but could potentially remove these lines + uVV % array = 0.0_RKIND + vVV % array = 0.0_RKIND + wVV % array = 0.0_RKIND + + ! make sure memory has been allocated +#ifdef MPAS_DEBUG + if(.not.associated(uVV) .or. & + .not.associated(vVV) .or. & + .not.associated(wVV)) then + LIGHT_DEBUG_WRITE('[u,v,w]VertexVelocity memory not allocated!') + end if +#endif + + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=timeLev) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel=timeLev) + + ! get new time level velocity (linear RBF) + !call mpas_log_write( 'uVV= $f',MPAS_LOG_OUT, realArgs=(/ uVV % array /) ) ! This is an array, not sure if log will work. +#ifdef MPAS_DEBUG + call mpas_timer_start("init_reconst_filter_LPT") +#endif + call ocn_vertex_reconstruction(filterNum, meshPool, lagrPartTrackScratchPool, lagrPartTrackCellsPool, & + layerThickness, normalVelocity, uVV, vVV, wVV) +#ifdef MPAS_DEBUG + call mpas_timer_stop("init_reconst_filter_LPT") +#endif + + end do + + block => block % next + end do + !call mpas_log_write( 'inialize_vertex_velocity end') + + end subroutine initalize_fields!}}} + +!*********************************************************************** +! +! routine initalize_wachspress_coefficients +! +!> \brief Initialize Wachspress coefficients +!> \author Phillip Wolfram +!> \date 01/26/2015 +!> \details +!> This routine inializes the B_i Wachspress coefficients which are +!> static in time +! +!----------------------------------------------------------------------- + subroutine intialize_wachspress_coefficients(domain, err) !{{{ + + implicit none + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackCellsPool + type (mpas_pool_type), pointer :: meshPool + integer :: nVertices, iCell, i, im1, i0, ip1, iVertex + integer, pointer :: nCells + real (kind=RKIND), dimension(:), allocatable :: xv,yv,zv + real (kind=RKIND), dimension(3) :: v1, v2, v3 + real (kind=RKIND), pointer :: radiusLocal + integer, dimension(:,:), pointer :: verticesOnCell + integer, dimension(:), pointer :: nCellVerticesArray + real (kind=RKIND), dimension(:), pointer :: xVertex, yVertex, zVertex + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell + real (kind=RKIND), dimension(:,:), pointer :: areaBArray + logical, pointer :: on_a_sphere, is_periodic + real(kind=RKIND), pointer :: x_period, y_period + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! setup pointers / get block + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackCells', lagrPartTrackCellsPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_config(meshPool, 'is_periodic', is_periodic) + call mpas_pool_get_config(meshPool, 'x_period', x_period) + call mpas_pool_get_config(meshPool, 'y_period', y_period) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nCellVerticesArray) + call mpas_pool_get_config(meshPool, 'sphere_radius', radiusLocal) + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + call mpas_pool_get_array(lagrPartTrackCellsPool, 'wachspressAreaB', areaBArray) + + ! compute B_i coefficients + areaBArray = 0.0_RKIND + do iCell = 1, nCells + nVertices = nCellVerticesArray(iCell) + allocate(xv(nVertices), yv(nVertices), zv(nVertices)) + if (on_a_sphere .or. .not. is_periodic) then + xv = xVertex(verticesOnCell(:,iCell)) + yv = yVertex(verticesOnCell(:,iCell)) + zv = zVertex(verticesOnCell(:,iCell)) + else + do iVertex=1,nVertices + xv(iVertex) = mpas_fix_periodicity(xVertex(verticesOnCell(iVertex,iCell)), & + xCell(iCell), x_period) + yv(iVertex) = mpas_fix_periodicity(yVertex(verticesOnCell(iVertex,iCell)), & + yCell(iCell), y_period) + zv(iVertex) = zVertex(verticesOnCell(iVertex,iCell)) + end do + end if + do i = 1, nVertices + ! compute first area B_i + ! get vertex indices + im1 = mod(nVertices + i - 2, nVertices) + 1 + i0 = mod(nVertices + i - 1, nVertices) + 1 + ip1 = mod(nVertices + i , nVertices) + 1 + + ! precompute B_i areas + ! always the same because B_i independent of xp,yp,zp + v1(1) = xv(im1) + v1(2) = yv(im1) + v1(3) = zv(im1) + v2(1) = xv(i0) + v2(2) = yv(i0) + v2(3) = zv(i0) + v3(1) = xv(ip1) + v3(2) = yv(ip1) + v3(3) = zv(ip1) + areaBArray(iCell, i) = mpas_triangle_signed_area(v1, v2, v3, meshPool) + end do + deallocate(xv, yv, zv) + + end do + + + block => block % next + end do + + end subroutine intialize_wachspress_coefficients !}}} + +!*********************************************************************** +! +! routine compute_velocity_on_potentialdensity_surface +! +!> \brief compute_velocity_on_potentialdensity_surface +!> \author Phillip Wolfram +!> \date 09/15/2014 +!> \details +!> This routine interpolates the velocity field onto the potential +!> density surface +! +!----------------------------------------------------------------------- + subroutine compute_velocity_on_potentialdensity_surface(domain,err,aTimeLevel) !{{{ + + implicit none + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + integer, intent(in) :: aTimeLevel + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool, diagnosticsPool, lagrPartTrackCellsPool + real (kind=RKIND), dimension(:,:), pointer :: zonVel, merVel, depth, normalVelocityMer, normalVelocityZon + real (kind=RKIND), dimension(:), pointer :: buoyancySurfaceValues + integer, pointer :: nBuoyancySurfaces, nCells + real (kind=RKIND), dimension(:,:), pointer :: buoyancy, zMid + integer :: iLevel, aBuoyancySurface, iCell + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND) :: phiInterp !< location to interpolate + real (kind=RKIND) :: alpha + integer :: iHigh, iLow, aval + real (kind=RKIND) :: eps=1e-14_RKIND + + LIGHT_DEBUG_WRITE('compute_velocity_on_potentialdensity_surface start') + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! get pools + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackCells', lagrPartTrackCellsPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + ! connect variables to pointer array + call mpas_pool_get_array(diagnosticsPool, 'velocityMeridional', normalVelocityMer) + call mpas_pool_get_array(diagnosticsPool, 'velocityZonal', normalVelocityZon) + call mpas_pool_get_array(lagrPartTrackCellsPool, 'buoyancySurfaceVelocityZonal', zonVel) + call mpas_pool_get_array(lagrPartTrackCellsPool, 'buoyancySurfaceVelocityMeridional', merVel) + call mpas_pool_get_array(lagrPartTrackCellsPool, 'buoyancySurfaceDepth', depth) + call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', buoyancy) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nBuoyancySurfaces', nBuoyancySurfaces) + call mpas_pool_get_array(lagrPartTrackCellsPool,'buoyancySurfaceValues', buoyancySurfaceValues) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + LIGHT_DEBUG_ALL_WRITE('nBuoyancySurfaces =' COMMA nBuoyancySurfaces) + LIGHT_DEBUG_ALL_WRITE('buoyancySurfaceValues=' COMMA buoyancySurfaceValues) + LIGHT_DEBUG_ALL_WRITE('size(buoyancy)=' COMMA size(buoyancy)) + LIGHT_DEBUG_ALL_WRITE('shape(buoyancy)=' COMMA shape(buoyancy)) + zonVel = -9999.0_RKIND + merVel = -9999.0_RKIND + ! for each buoyancy surface + do aBuoyancySurface = 1, nBuoyancySurfaces + ! for each cell + do iCell = 1, nCells + + phiInterp = buoyancySurfaceValues(aBuoyancySurface) + + ! get correct vertical levels + + iLevel = mpas_get_vertical_id(maxLevelCell(iCell), phiInterp, buoyancy(:,iCell)) + + if(iLevel < 1) then + ! top level + if (iLevel == 0) then + aval = maxloc(buoyancy(1:maxLevelCell(iCell),iCell),1) + ! bottom level + else if (iLevel == -1) then + aval = minloc(buoyancy(1:maxLevelCell(iCell),iCell),1) + end if + zonVel(aBuoyancySurface, iCell) = normalVelocityZon(aval,iCell) + merVel(aBuoyancySurface, iCell) = normalVelocityMer(aval,iCell) + depth(aBuoyancySurface, iCell) = zMid(aval,iCell) + else + ! perform the interpolation + call get_bounding_indices(iLow, iHigh, phiInterp, buoyancy(:,iCell), iLevel, maxLevelCell(iCell)) + ! get alpha between points + if(abs(buoyancy(iHigh,iCell) - buoyancy(iLow,iCell)) < eps) then + ! we really can't distinguish between each of these points numerically, just take the + ! average of both + alpha = 0.5_RKIND + else + alpha = (phiInterp - buoyancy(iLow,iCell))/(buoyancy(iHigh,iCell) - buoyancy(iLow,iCell)) + end if + + ! interpolate to the correct surface + zonVel(aBuoyancySurface, iCell) = alpha * normalVelocityZon(iHigh, iCell) + & + (1.0_RKIND - alpha) * normalVelocityZon(iLow, iCell) + merVel(aBuoyancySurface, iCell) = alpha * normalVelocityMer(iHigh, iCell) + & + (1.0_RKIND - alpha) * normalVelocityMer(iLow, iCell) + depth(aBuoyancySurface, iCell) = alpha * zMid(iHigh, iCell) + & + (1.0_RKIND - alpha) * zMid(iLow, iCell) + end if + + end do + + end do + + block => block % next + end do + LIGHT_DEBUG_WRITE('compute_velocity_on_potentialdensity_surface end') + + end subroutine compute_velocity_on_potentialdensity_surface !}}} + +!*********************************************************************** +! +! routine initialize_particle_properties +! +!> \brief Initialize particle properties +!> \author Phillip Wolfram +!> \date 09/24/2014 +!> \details +!> This routine initializes particle data for +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine initialize_particle_properties(domain, timeLevel, err)!{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: lagrPartTrackFieldsPool, lagrPartTrackCellsPool, lagrPartTrackScratchPool + integer, dimension(:), pointer :: cellOwnerBlock + integer, pointer :: currentBlock, ioBlock, indexToParticleID, transfered + integer :: currentProc, ioProc + type (mpas_particle_list_type), pointer :: particlelist + type (mpas_particle_type), pointer :: particle + + real (kind=RKIND), dimension(3) :: particlePosition, particleVelocity + real (kind=RKIND), pointer :: xParticle, yParticle, zParticle, lonVel, latVel, buoyancyParticle, sumU, sumV, sumUU, & + sumUV, sumVV + real (kind=RKIND), dimension(3) :: xSubStep, diffSubStep, diffParticlePosition + real (kind=RKIND), pointer :: zLevelParticle + real (kind=RKIND), dimension(:,:), pointer :: zTop, vertVelocityTop, zMid, areaBArray + real (kind=RKIND), dimension(:), pointer :: bottomDepth + type (field2DReal), pointer :: normalVelocity, uVertexVelocity, vVertexVelocity, wVertexVelocity, layerThickness + + real (kind=RKIND), dimension(:,:), pointer :: uVertexVelocityArray, vVertexVelocityArray, wVertexVelocityArray, buoyancy, & + buoyancyTimeInterp, potentialDensity + + real(kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell + real(kind=RKIND), dimension(:), pointer :: xVertex, yVertex, zVertex + + integer, dimension(:,:), pointer :: verticesOnCell, boundaryVertex + integer, dimension(:), pointer :: maxLevelCell + + integer theVertex, iLevel, iLevelBuoyancy, aVertex, & + nSteps, timeStep, subStep, subStepOrder, timeInterpOrder, aTimeLevel, nCellVertices, blockProc, arrayIndex + integer, pointer :: nCells, nVertLevels + integer, dimension(:), pointer :: nCellVerticesArray + integer, dimension(:,:), pointer :: cellsOnCell + logical, dimension(:,:), pointer :: ioProcRecvList + logical, dimension(:), pointer :: ioProcSendList + logical, pointer :: onSphere + + real (kind=RKIND), dimension(4) :: kWeightK, kWeightKVert + real (kind=RKIND), dimension(3,4) :: kWeightX + real (kind=RKIND), dimension(4) :: kWeightXVert + real (kind=RKIND), dimension(4) :: kWeightT, kWeightTVert + real (kind=RKIND), dimension(3,5) :: kCoeff + real (kind=RKIND), dimension(5) :: kCoeffVert + real (kind=RKIND), dimension(2) :: timeCoeff + real (kind=RKIND) :: dt, dtSim, tSubStep + real (kind=RKIND), pointer :: dtParticle + real (kind=RKIND) :: zSubStep + real (kind=RKIND) :: diffSubStepVert, diffParticlePositionVert, particleVelocityVert, verticalVelocityInterp + real (kind=RKIND) :: buoyancyInterp + real (kind=RKIND), pointer :: sphereRadius + integer, pointer :: verticalTreatment, vertexReconstMethod, timeIntegration, indexLevel, filterNum, iCell + + err = 0 + + dminfo = domain % dminfo + + block => domain % blocklist + do while (associated(block)) !{{{ + ! allocate scratch memory / setup pointers / get block + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackFields', lagrPartTrackFieldsPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackScratch', lagrPartTrackScratchPool) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackCells', lagrPartTrackCellsPool) + + ! particlelist should be stored in the structs pool probably (need to seriously rework the code!) + particlelist => block % particlelist + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + call mpas_pool_get_array(lagrPartTrackCellsPool, 'wachspressAreaB', areaBArray) + call mpas_pool_get_array(diagnosticsPool, 'zTop', zTop) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(diagnosticsPool, 'vertVelocityTop', vertVelocityTop) + call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', buoyancyTimeInterp) + + call mpas_pool_get_field(statePool, 'normalVelocity', normalVelocity, timeLevel=timeLevel) + call mpas_dmpar_exch_halo_field(normalVelocity) + call mpas_pool_get_field(statePool, 'layerThickness', layerThickness, timeLevel=timeLevel) + + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'boundaryVertex', boundaryVertex) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nCellVerticesArray) + + call mpas_pool_get_config(meshPool, 'on_a_sphere', onSphere) + call mpas_pool_get_config(meshPool, 'sphere_radius', sphereRadius) + + !!!!!!!!!! LOOP OVER PARTICLES !!!!!!!!!! + ! update the particle position (just from initialized value for now) + ! this is a loop over particle list and its datastructures + do while(associated(particlelist)) !{{{ + ! get pointers / option values + particle => particlelist % particle + + ! get values {{{ + call mpas_pool_get_array(particle % haloDataPool, 'xParticle', xParticle) + call mpas_pool_get_array(particle % haloDataPool, 'yParticle', yParticle) + call mpas_pool_get_array(particle % haloDataPool, 'zParticle', zParticle) + call mpas_pool_get_array(particle % haloDataPool, 'currentCell', iCell) + particlePosition(1) = xParticle + particlePosition(2) = yParticle + particlePosition(3) = zParticle + + call mpas_pool_get_array(particle % haloDataPool, 'zLevelParticle', zLevelParticle) + + call mpas_pool_get_array(particle % haloDataPool, 'verticalTreatment', verticalTreatment) + call mpas_pool_get_array(particle % haloDataPool, 'vertexReconstMethod', vertexReconstMethod) + call mpas_pool_get_array(particle % haloDataPool, 'indexLevel', indexLevel) + call mpas_pool_get_array(particle % haloDataPool, 'timeIntegration', timeIntegration) + call mpas_pool_get_array(particle % haloDataPool, 'dtParticle', dtParticle) + call mpas_pool_get_array(particle % haloDataPool, 'buoyancyParticle', buoyancyParticle) + call mpas_pool_get_array(particle % haloDataPool, 'indexToParticleID', indexToParticleID) + + call mpas_pool_get_array(particle % haloDataPool, 'lonVel', lonVel) + call mpas_pool_get_array(particle % haloDataPool, 'latVel', latVel) + call mpas_pool_get_array(particle % haloDataPool, 'sumU', sumU) + call mpas_pool_get_array(particle % haloDataPool, 'sumV', sumV) + call mpas_pool_get_array(particle % haloDataPool, 'sumUU', sumUU) + call mpas_pool_get_array(particle % haloDataPool, 'sumUV', sumUV) + call mpas_pool_get_array(particle % haloDataPool, 'sumVV', sumVV) + + !}}} + + !!!!!!!!!! PERFORM SAMPLING (VELOCITY, TEMP, SALINITY, ETC) !!!!!!!!!! + ! this could be done just before the output anyway + + ! need iCell computed for final position + ! need scalar values interpolated in time to yield single value + ! probably need to store zMid too, including flipping it + ! get updated cell location +#ifdef MPAS_DEBUG + call mpas_timer_start("get_validated_cell_idLPT") +#endif + LIGHT_DEBUG_WRITE('sampling initialization') + call get_validated_cell_id(nCells, xCell,yCell,zCell , xVertex,yVertex,zVertex, & + particlePosition(1),particlePosition(2),particlePosition(3), meshPool, & + nCellVerticesArray, verticesOnCell, iCell, nCellVertices, cellsOnCell) +#ifdef MPAS_DEBUG + call mpas_timer_stop("get_validated_cell_idLPT") +#endif + + if(verticalTreatment == 4) then !('buoyancySurface') !{{{ + iLevelBuoyancy = mpas_get_vertical_id(maxLevelCell(iCell), buoyancyParticle, buoyancyTimeInterp(:,iCell)) + + ! interpolate the scalars now (assumes that scalar value is constant within a particular cell) + call interp_cell_scalars(iLevelBuoyancy, maxLevelCell(iCell), buoyancyParticle, buoyancyTimeInterp(:,iCell), & + zMid(:,iCell), zLevelParticle) + else + ! make sure final zLevelParticle is ok so that it can't extent past zMid range + + iLevel = mpas_get_vertical_id(maxLevelCell(iCell), zLevelParticle, zMid(:,iCell)) + end if + + ! compute necessary information for autocorrelation !{{{ + ! get the updated velocity + ! ensure that buoyancy is fixed for each run + buoyancyInterp = buoyancyParticle + ! we just need the last part of the velocity field interpolation because we are at the end of the timestep + timeInterpOrder = 1 + timeCoeff(1) = 1.0_RKIND + timeCoeff(2) = 0.0_RKIND + ! return interpolated horizontal velocity "particleVelocity" and vertical velocity "particleVelocityVert" + ! noting we use the final positions + call velocity_time_interpolation(particleVelocity, particleVelocityVert, & + diagnosticsPool, lagrPartTrackFieldsPool, & + timeInterpOrder, timeCoeff, iCell, iLevel, buoyancyInterp, maxLevelCell, & + verticalTreatment, indexLevel, nCellVertices, verticesOnCell, boundaryVertex, & + particlePosition, zLevelParticle, zMid, zTop, vertVelocityTop, xVertex, yVertex, zVertex, & + meshPool, areaBArray) + ! convert horizontal velocity to lat/lon velocity + + LIGHT_DEBUG_WRITE(iLevel COMMA particleVelocity) + + ! store velocity for use in computing normalized autocorrelation offline + + call mpas_convert_xyz_velocity_to_latlon(lonVel, latVel, particlePosition, particleVelocity) + !}}} + + ! get next particle to process on the list + particlelist => particlelist % next + end do !}}} + + ! get next block + block => block % next !}}} + end do !}}} + + end subroutine initialize_particle_properties !}}} + +!*********************************************************************** +! +! routine particle_vertical_treatment +! +!> \brief Vertical treatment to obtain correct horizontal velocity field +!> \author Phillip Wolfram +!> \date 03/31/2014 +!> \details +!> This routine returns the vertex values which will be used in the +!> Wachspress interoplant (uvCell) based on +!> vertex velocities uVertexVelocity, vVertexVelocity, wVertexVelocity +!> for a given cell which has nCellVertices which are determined from +!> the list verticesOnCell. +!> The routine collapses the vertical to a scalar. +! +!----------------------------------------------------------------------- + subroutine particle_vertical_treatment(verticalTreatment, indexLevel, nCellVertices, verticesOnCell, & !{{{ + uVertexVelocity, vVertexVelocity, wVertexVelocity, & + uvCell, boundaryVertex, iLevel, nVertLevels, & + zLoc, zMid, zTop, phiInterp, phiMid, vertVelocityTop, vertVelocityInterp) + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(in) :: & + uVertexVelocity, vVertexVelocity, wVertexVelocity !< vertex velocities + integer, dimension(:), intent(in) :: verticesOnCell !< list of vertex indices on cell + integer, intent(in) :: nCellVertices !< current cell and the number of cell vertices + integer, intent(in) :: iLevel !< vertical level / cell of zLoc + integer, intent(in) :: nVertLevels !< number of vertical levels + integer, intent(in) :: verticalTreatment !< vertical treatment encoded as int + integer, intent(in) :: indexLevel !< value of index for fixed index space + integer, dimension(:), intent(in) :: boundaryVertex !< boundary vertices for particular level + real (kind=RKIND), intent(in) :: zLoc !< location to interpolate + real (kind=RKIND), dimension(:), intent(in) :: zTop !< elevation of cell top + real (kind=RKIND), dimension(:), intent(in) :: zMid !< elevation of cell middle + real (kind=RKIND), intent(in) :: phiInterp !< buoyancy value to interpolate + real (kind=RKIND), dimension(:), intent(in) :: phiMid !< buoyancy values at cell mid points + real (kind=RKIND), dimension(:), intent(in) :: vertVelocityTop !< velocity at top of cell + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(out) :: uvCell !< components of vertex velocity (vertically selected) + real (kind=RKIND), intent(out) :: vertVelocityInterp ! vertically interpolated velocity + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: aVertex, theVertex + + vertVelocityInterp = 0.0_RKIND + + verticalTreatmentCase: select case (verticalTreatment) + + case (1) verticalTreatmentCase !('indexLevel') !{{{ + ! get vertically interpolanted values for vertexes in cell + ! and form polygon vertex values + do aVertex = 1, nCellVertices + theVertex = verticesOnCell(aVertex) + ! assume that we only care about the top level for a surface drifter + ! assumes that velocity is constant in top half of the cell + uvCell(1,aVertex) = uVertexVelocity(indexLevel,theVertex) + uvCell(2,aVertex) = vVertexVelocity(indexLevel,theVertex) + uvCell(3,aVertex) = wVertexVelocity(indexLevel,theVertex) + end do + + !! ensure that the boundary condition is enforced + !call zero_boundary_nodal_values(nCellVertices, verticesOnCell, & + ! boundaryVertex, uvUcell, uvVcell, uvWcell) + + ! no vertical motion, just horizontal motion + return + !}}} + + case (2) verticalTreatmentCase !('fixedZLevel') !{{{ + + ! interpolate the horizontal velocity based on z-levels + call interp_nodal_vectors(ncellvertices, verticesoncell, & + ilevel, nVertLevels, zLoc, zmid, & + uvertexvelocity, vvertexvelocity, wvertexvelocity, uvCell) + + !! ensure that there is zero nodal velocity on the boundary + !call zero_boundary_nodal_values(nCellVertices, verticesOnCell, & + ! boundaryVertex, uvUcell, uvVcell, uvWcell) + + ! no vertical motion, just horizontal motion + return + !}}} + + case (3) verticalTreatmentCase !('passiveFloat') !{{{ + + ! interpolate the vertical velocity + vertVelocityInterp = interp_vert_velocity_to_zlevel( & + iLevel, zLoc, zTop, vertVelocityTop) + + ! interpolate the horizontal velocity based on z-levels + call interp_nodal_vectors(ncellvertices, verticesoncell, & + ilevel, nVertLevels, zLoc, zmid, & + uvertexvelocity, vvertexvelocity, wvertexvelocity, uvCell) + + !! ensure that there is zero nodal velocity on the boundary + !call zero_boundary_nodal_values(nCellVertices, verticesOnCell, & + ! boundaryVertex, uvUcell, uvVcell, uvWcell) + + return + !}}} + + case (4) verticalTreatmentCase !('buoyancySurface') !{{{ + ! no vertical velocity required because there is not vertical integration for position + + ! interpolate the horizontal velocity + call interp_nodal_vectors(ncellvertices, verticesoncell, & + ilevel, nVertLevels, phiInterp, phiMid, & + uvertexvelocity, vvertexvelocity, wvertexvelocity, uvCell) + + ! ensure that there is zero nodal velocity on the boundary + !call zero_boundary_nodal_values(nCellVertices, verticesOnCell, & + ! boundaryVertex, uvUcell, uvVcell, uvWcell) + + ! no vertical motion, just horizontal motion + return + !}}} + + case (5) verticalTreatmentCase !('argoFloat') !{{{ + + + !}}} + + case default verticalTreatmentCase !{{{ + LIGHT_ERROR_WRITE('Vertical treatment for particle integration unknown (' COMMA verticalTreatment COMMA ')!') + return + !}}} + + end select verticalTreatmentCase + + end subroutine particle_vertical_treatment!}}} + +!*********************************************************************** +! +! routine velocity_time_interpolation +! +!> \brief Compute velocity interpolations, including in time +!> \author Phillip Wolfram +!> \date 09/12/2014 +!> \details +!> This routine interpolates velocity in time and space to a particular +!> location xSubStep +! +!----------------------------------------------------------------------- + subroutine velocity_time_interpolation(particleVelocity, particleVelocityVert, & + diagnosticsPool, lagrPartTrackFieldsPool, & + timeInterpOrder, timeCoeff, iCell, iLevel, buoyancyInterp, maxLevelCell, & + verticalTreatment, indexLevel, nCellVertices, verticesOnCell, boundaryVertex, & + xSubStep, zSubStep, zMid, zTop, vertVelocityTop, xVertex, yVertex, zVertex, meshPool, areaBArray) !{{{ + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (mpas_pool_type), pointer, intent(in) :: diagnosticsPool, lagrPartTrackFieldsPool + integer, intent(in) :: timeInterpOrder + real (kind=RKIND), dimension(2), intent(in) :: timeCoeff + integer, intent(in) :: iCell + integer, dimension(:), intent(in) :: maxLevelCell + integer, intent(in) :: verticalTreatment + integer, pointer, intent(in) :: indexLevel + integer, intent(in) :: nCellVertices + integer, dimension(:,:), intent(in) :: verticesOnCell + integer, dimension(:,:), intent(in) :: boundaryVertex + real (kind=RKIND), intent(in) :: zSubStep + real (kind=RKIND), dimension(:,:), intent(in) :: zMid + real (kind=RKIND), dimension(:,:), intent(in) :: zTop + real (kind=RKIND), dimension(:,:), intent(in) :: vertVelocityTop + real (kind=RKIND), dimension(:,:), intent(in) :: areaBArray + real (kind=RKIND), dimension(:), intent(in) :: xVertex, yVertex, zVertex + real (kind=RKIND), dimension(3), intent(in) :: xSubStep + type (mpas_pool_type), pointer, intent(in) :: meshPool + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(in) :: buoyancyInterp + integer, intent(inout) :: iLevel + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(3), intent(out) :: particleVelocity + real (kind=RKIND), intent(out) :: particleVelocityVert + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: aVertex, aTimeLevel + real (kind=RKIND), dimension(:,:), pointer :: uVertexVelocityArray, vVertexVelocityArray, wVertexVelocityArray, buoyancy + real (kind=RKIND) :: verticalVelocityInterp + real(kind=RKIND), dimension(:), allocatable :: areaB + real(kind=RKIND), dimension(:,:), allocatable :: vertCoords + real(kind=RKIND), dimension(:,:), allocatable :: uvCell + logical, pointer :: on_a_sphere, is_periodic + real(kind=RKIND), pointer :: x_period, y_period +#ifdef MPAS_DEBUG + call mpas_timer_start("velocity_time_interpolationLPT") +#endif + + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_config(meshPool, 'is_periodic', is_periodic) + call mpas_pool_get_config(meshPool, 'x_period', x_period) + call mpas_pool_get_config(meshPool, 'y_period', y_period) + + ! allocations for particular cell !{{{ + allocate(vertCoords(3,nCellVertices), uvCell(3,nCellVertices), areaB(nCellVertices)) + !}}} + + ! get horizontal vertex locations (noting that there may be a + ! bit of error because the particle could be at the top + ! of the cell or at the bottom of the cell) + do aVertex = 1, nCellVertices + if (on_a_sphere .or. .not. is_periodic) then + vertCoords(1,aVertex) = xVertex(verticesOnCell(aVertex,iCell)) + vertCoords(2,aVertex) = yVertex(verticesOnCell(aVertex,iCell)) + vertCoords(3,aVertex) = zVertex(verticesOnCell(aVertex,iCell)) + else + vertCoords(1,aVertex) = mpas_fix_periodicity(xVertex(verticesOnCell(aVertex,iCell)), xSubStep(1), x_period) + vertCoords(2,aVertex) = mpas_fix_periodicity(yVertex(verticesOnCell(aVertex,iCell)), xSubStep(2), y_period) + vertCoords(3,aVertex) = zVertex(verticesOnCell(aVertex,iCell)) + end if + areaB(aVertex) = areaBArray(iCell, aVertex) + end do + + ! initialize velocities to 0 + particleVelocity = 0.0_RKIND + particleVelocityVert = 0.0_RKIND + + ! general interpolation for the velocity field + do aTimeLevel = 1, timeInterpOrder + + ! define arrays!{{{ + call mpas_pool_get_array(lagrPartTrackFieldsPool, 'uVertexVelocity', uVertexVelocityArray, timeLevel=aTimeLevel) + call mpas_pool_get_array(lagrPartTrackFieldsPool, 'vVertexVelocity', vVertexVelocityArray, timeLevel=aTimeLevel) + call mpas_pool_get_array(lagrPartTrackFieldsPool, 'wVertexVelocity', wVertexVelocityArray, timeLevel=aTimeLevel) + call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', buoyancy) + !}}} + + ! get final, interpolated particle velocity at this point (collapse to point) + if (verticalTreatment == 4) then + ! buoyancy case (not using zSubStep for interpolation / iLevel) + ! use existing code noting we need to flip the order to get the right iLevel + + iLevel = mpas_get_vertical_id(maxLevelCell(iCell), buoyancyInterp, buoyancy(:,iCell)) + LIGHT_DEBUG_WRITE('iLevel=' COMMA iLevel) + ! note, if buoyancyInterp out of range this will try to reorient the particle to the top / bottom but there + ! will definitely be some error with this type of computation because the buoyancy is not available at this location + ! the time interpolation, as a consequency, can mix velocities from different buoyancy surfaces in order to advect + ! the particle + !call mpas_log_write( 'iLevel = $i',MPAS_LOG_OUT, intArgs=(/ iLevel /) ) + end if + + call particle_vertical_treatment(verticalTreatment, indexLevel, nCellVertices, verticesOnCell(:,iCell), & + uVertexVelocityArray, vVertexVelocityArray, wVertexVelocityArray, uvCell, boundaryVertex(iLevel,:), & + iLevel, maxLevelCell(iCell), zSubStep, zMid(:,iCell), zTop(:,iCell), buoyancyInterp, buoyancy(:,iCell), & + vertVelocityTop(:,iCell), verticalVelocityInterp) + + ! vertical + particleVelocityVert = particleVelocityVert + & + timeCoeff(aTimeLevel) * verticalVelocityInterp + + ! horizontal + ! timer commented out because it is used in more than just compute... +#ifdef MPAS_DEBUG + call mpas_timer_start("part_horiz_interpLPT") +#endif + LIGHT_DEBUG_WRITE('particleVelocityVert=' COMMA particleVelocityVert) + particleVelocity = particleVelocity + & + timeCoeff(aTimeLevel) * particle_horizontal_interpolation(nCellVertices, vertCoords, & + xSubStep, uvCell, meshPool, areaB) +#ifdef MPAS_DEBUG + call mpas_timer_stop("part_horiz_interpLPT") +#endif + LIGHT_DEBUG_WRITE('particleVelocity=' COMMA particleVelocity) + + + end do + + ! deallocations of temp memory + deallocate(vertCoords, uvCell, areaB) + +#ifdef MPAS_DEBUG + call mpas_timer_stop("velocity_time_interpolationLPT") +#endif + + end subroutine velocity_time_interpolation !}}} + + subroutine zero_autocorrelation_sums(domain) !{{{ + implicit none + + ! input/output variables + type (domain_type), intent(inout) :: domain + ! local + type (block_type), pointer :: block + type (mpas_particle_list_type), pointer :: particlelist + type (mpas_particle_type), pointer :: particle + ! output variables (per particle) + real (kind=RKIND), pointer :: sumU, sumV, sumUU, sumUV, sumVV + integer, pointer :: currentCell + + ! get the appropriate pools + block => domain % blocklist + do while (associated(block)) !{{{ + particlelist => block % particlelist + do while(associated(particlelist)) !{{{ + ! get pointers / option values + particle => particlelist % particle + + ! get values (may want a flag for reinitialization in the future) + !call mpas_pool_get_array(particle % haloDataPool, 'sumU', sumU) + !call mpas_pool_get_array(particle % haloDataPool, 'sumV', sumV) + !call mpas_pool_get_array(particle % haloDataPool, 'sumUU', sumUU) + !call mpas_pool_get_array(particle % haloDataPool, 'sumUV', sumUV) + !call mpas_pool_get_array(particle % haloDataPool, 'sumVV', sumVV) + call mpas_pool_get_array(particle % haloDataPool, 'currentCell', currentCell) + + ! initialize the values + !sumU = 0.0_RKIND + !sumV = 0.0_RKIND + !sumUU = 0.0_RKIND + !sumUV = 0.0_RKIND + !sumVV = 0.0_RKIND + currentCell = -1 + + ! get next particle to process on the list + particlelist => particlelist % next + end do !}}} + + ! get next block + block => block % next + end do !}}} + + end subroutine zero_autocorrelation_sums !}}} + +!*********************************************************************** +! +! routine particle_horizontal_interpolation +! +!> \brief Horizontal treatment to obtain correct velocity field at point +!> \author Phillip Wolfram +!> \date 03/31/2014 +!> \details +!> This routine returns the point values which will be used in the +!> particle interpolation time integration based on +!> vertex velocities uVertexVelocity, vVertexVelocity, wVertexVelocity +! +!----------------------------------------------------------------------- + function particle_horizontal_interpolation(nCellVertices, vertCoords, & !{{{ + pointInterp, uVertex, meshPool, areaB) + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, intent(in) :: nCellVertices + real (kind=RKIND), dimension(3, nCellVertices), intent(in) :: vertCoords + real (kind=RKIND), dimension(3), intent(in) :: pointInterp + real (kind=RKIND), dimension(3, nCellVertices), intent(in) :: uVertex + real (kind=RKIND), dimension(nCellVertices), intent(in) :: areaB + type (mpas_pool_type), pointer :: meshPool + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(nCellVertices) :: lambda + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(3) :: particle_horizontal_interpolation + + + ! get lambda coordinate for particle + lambda = mpas_wachspress_coordinates(nCellVertices, vertCoords , & + pointInterp, meshPool, areaB) + LIGHT_DEBUG_ALL_WRITE('lambda=' COMMA lambda) + LIGHT_DEBUG_ALL_WRITE('uVertex=' COMMA uVertex(1,:)) + LIGHT_DEBUG_ALL_WRITE('vVertex=' COMMA uVertex(2,:)) + LIGHT_DEBUG_ALL_WRITE('wVertex=' COMMA uVertex(3,:)) + + ! update particle velocities via horizontal interpolation + particle_horizontal_interpolation(1) = mpas_wachspress_interpolate(lambda, uVertex(1,:)) + particle_horizontal_interpolation(2) = mpas_wachspress_interpolate(lambda, uVertex(2,:)) + particle_horizontal_interpolation(3) = mpas_wachspress_interpolate(lambda, uVertex(3,:)) + + end function particle_horizontal_interpolation !}}} + +!*********************************************************************** +! +! routine particle_horizontal_movement +! +!> \brief Compute horizontal movement for particle so particle stays +!> in spherical shell +!> \author Phillip Wolfram +!> \date 05/20/2014 +!> \details +!> This routine returns the particle position pParticle corresponding +!> to an initial particle position pParticle for a Cartesian movemnt +!> dpParticle. If the calculation is onSphere, then the distance +!> |dpParticle| must be along the great circle route of pParticle +!> and the projection of pParticle + dpParticle on the spherical +!> shell corresponding to pParticle. +! +!----------------------------------------------------------------------- + subroutine particle_horizontal_movement(meshPool, pParticle, dpParticle) !{{{ + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(in) :: dpParticle + type (mpas_pool_type), intent(in), pointer :: meshPool + + !----------------------------------------------------------------- + ! input / output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(inout) :: pParticle + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND) :: lenPath, arcLen + real (kind=RKIND) :: radiusShell + real (kind=RKIND), dimension(size(pParticle)) :: pParticleTemp + real (kind=RKIND), dimension(size(pParticle)) :: pParticleInterp + real (kind=RKIND) :: alpha + real (kind=RKIND), parameter :: eps=1e-10_RKIND + logical, pointer :: onSphere, is_periodic + real(kind=RKIND), pointer :: x_period, y_period + character (len=StrKIND) :: message + ! choosen based on the parameters, note that we loose about 6 - 7 units of precision because R is so large! + ! therefore, eps = 1e-10 is conservative, if not too high! this just helps with numerical stability + !dpParticle = -4.2428037617887103E-011 4.3076544298828060E-011 5.0760704444480953E-011 + !pParticle = 4444887.2990309987 -891565.00525021972 4476665.3916420965 + !pParticleTemp = 4444887.2990309987 -891565.00525021972 4476665.3916420965 + !mpas_arc_length = 0.0000000000000000 lenPath = 7.8945399869363434E-011 + + call mpas_pool_get_config(meshPool, 'on_a_sphere', onSphere) + call mpas_pool_get_config(meshPool, 'is_periodic', is_periodic) + call mpas_pool_get_config(meshPool, 'x_period', x_period) + call mpas_pool_get_config(meshPool, 'y_period', y_period) + + ! may need a condition to determine if we need to project back to the sphere + if(onSphere) then + ! need to make sure new point is on the spherical shell + + ! get path length + LIGHT_DEBUG_ALL_WRITE('dpParticle = ' COMMA dpParticle) + lenPath = sqrt(sum(dpParticle*dpParticle)) + + ! consider case of particle not moving (need to have this code here in general) + !if (lenPath < eps) then + ! this is ok because this is only the case if the points are the same. If there is a + ! numerical instability it probably should be handled differently. + if (lenPath < eps) then + return + end if + + ! get radius of particle's horizontal shell + radiusShell = sqrt(sum(pParticle*pParticle)) + + ! project endpoint to spherical shell containing pParticle + pParticleTemp = pParticle + dpParticle + pParticleTemp = ( radiusShell / sqrt(sum(pParticleTemp*pParticleTemp)) ) * pParticleTemp + + ! compute alpha parameter for spherical interpolant / extrapolant + LIGHT_DEBUG_ALL_WRITE('dpParticle = ' COMMA dpParticle) + LIGHT_DEBUG_ALL_WRITE('pParticle = ' COMMA pParticle) + LIGHT_DEBUG_ALL_WRITE('pParticleTemp = ' COMMA pParticleTemp) + write(message, *) 'mpas_arc_length = ', mpas_arc_length( pParticle(1), pParticle(2), pParticle(3), & + pParticleTemp(1), pParticleTemp(2), pParticleTemp(3)), 'lenPath = ', lenPath + LIGHT_DEBUG_ALL_WRITE(message) + arcLen = mpas_arc_length(pParticle(1),pParticle(2),pParticle(3) , pParticleTemp(1),pParticleTemp(2),pParticleTemp(3)) + if (arcLen > eps) then + alpha = lenPath / arcLen + else + return + endif + + ! compute final position based on spherical interpolant + call mpas_spherical_linear_interp(pParticleInterp, pParticle, pParticleTemp, alpha) + pParticle = pParticleInterp + else + ! we are just on a plane so there is no need for spherical interpolation to keep + ! the new particle location on a spherical shell + pParticle = pParticle + dpParticle + + ! periodic fix to make sure particle advection stays in domain + if (is_periodic) then + pParticle(1) = mpas_fix_periodicity(pParticle(1), x_period/2.0_RKIND, x_period) + pParticle(2) = mpas_fix_periodicity(pParticle(2), y_period/2.0_RKIND, y_period) + !pParticle(3) = pParticle(3) + end if + endif + + end subroutine particle_horizontal_movement!}}} + +!*********************************************************************** +! +! routine interp_cell_scalars +! +!> \brief Interpolate cell scalar vector based on a criteria (z-level, buoyancy, etc) +!> \author Phillip Wolfram +!> \date 06/18/2014 +!> \details +!> This routine interpolates cell scalar vector to a particular scalar value +!> depending upon a criteria such as z-level, buoyancy, etc. +! +!----------------------------------------------------------------------- + subroutine interp_cell_scalars(iLevel, nVertLevels, zInterp, zVals, & !{{{ + phiVals, phiInterp) + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(in) :: zVals !< scalar values (x) on cell for interpolant + integer, intent(in) :: iLevel !< vertical level / cell of phiInterp + integer, intent(in) :: nVertLevels !< number of vertical levels + real (kind=RKIND), intent(in) :: zInterp !< location to interpolate + real (kind=RKIND), dimension(:), intent(in) :: phiVals !< values at elevation of cell middle (where vertex + !< velocities are defined) + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), intent(out) :: phiInterp !< interpolated cell scalar + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND) :: alpha + integer :: aVertex, theVertex, iHigh, iLow + real (kind=RKIND) :: eps=1e-14 + + if(iLevel < 1) then + ! top level + if (iLevel == 0) then + phiInterp = phiVals(nVertLevels) + ! bottom level + else if (iLevel == -1) then + phiInterp = phiVals(1) + end if + else + call get_bounding_indices(iLow, iHigh, zInterp, zVals, iLevel, nVertLevels) + + ! interpolate to vertical level now + if(abs(zVals(iHigh) - zVals(iLow)) < eps) then + ! we really can't distinguish between each of these points numerically, just take the + ! average of both + alpha = 0.5_RKIND + else + ! interpolate to vertical level now + alpha = (zInterp - zVals(iLow))/(zVals(iHigh) - zVals(iLow)) + end if + + ! interpolate to the vertical level + phiInterp = alpha * phiVals(iHigh) + (1.0_RKIND - alpha) * phiVals(iLow) + end if + + end subroutine interp_cell_scalars!}}} + +!*********************************************************************** +! +! routine interp_nodal_scalars +! +!> \brief Interpolate nodal scalar vector based on a criteria (z-level, buoyancy, etc) +!> \author Phillip Wolfram +!> \date 05/27/2014 +!> \details +!> This routine interpolates nodal scalar vector to a particular scalar value +!> depending upon a criteria such as z-level, buoyancy, etc. +! +!----------------------------------------------------------------------- + subroutine interp_nodal_scalars(nCellVertices, verticesOnCell, & !{{{ + iLevel, nVertLevels, phiInterp, phiVals, & + scalarVec, vertexScalar) + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(in) :: scalarVec !< vertex scalar + integer, dimension(:), intent(in) :: verticesOnCell !< list of vertex indices on cell + integer, intent(in) :: nCellVertices !< number of cell vertices + integer, intent(in) :: iLevel !< vertical level / cell of phiInterp + integer, intent(in) :: nVertLevels !< number of vertical levels + real (kind=RKIND), intent(in) :: phiInterp !< location to interpolate + real (kind=RKIND), dimension(:), intent(in) :: phiVals !< values at elevation of cell middle (where vertex + !< velocities are defined) + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(out) :: vertexScalar !< components of vertex scalar (interpolated) + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND) :: alpha + integer :: aVertex, theVertex, iHigh, iLow + + call get_bounding_indices(iLow, iHigh, phiInterp, phiVals, iLevel, nVertLevels) + + ! interpolate to vertical level now + alpha = (phiInterp - phiVals(iLow))/(phiVals(iHigh) - phiVals(iLow)) + + ! interpolate to the vertical level + do aVertex = 1, nCellVertices + theVertex = verticesOnCell(aVertex) + ! assume for now that we only care about the top level for a surface drifter + vertexScalar(aVertex) = alpha * scalarVec(iHigh, theVertex) + (1.0_RKIND - alpha) * scalarVec(iLow, theVertex) + end do + + end subroutine interp_nodal_scalars!}}} + +!*********************************************************************** +! +! routine interp_nodal_vectors +! +!> \brief Interpolate nodal vector to scalar based on a criteria (z-level, buoyancy, etc) +!> \author Phillip Wolfram +!> \date 05/27/2014 +!> \details +!> This routine interpolates nodal vectors to a particular scalar value +!> depending upon a criteria such as z-level, buoyancy, etc. +! +!----------------------------------------------------------------------- + subroutine interp_nodal_vectors(nCellVertices, verticesOnCell, & !{{{ + iLevel, nVertLevels, phiInterp, phiVals, & + uVertexVelocity, vVertexVelocity, wVertexVelocity, uvCell) + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(in) :: & + uVertexVelocity, vVertexVelocity, wVertexVelocity !< vertex velocities + integer, dimension(:), intent(in) :: verticesOnCell !< list of vertex indices on cell + integer, intent(in) :: nCellVertices !< number of cell vertices + integer, intent(in) :: iLevel !< vertical level / cell of phiInterp + integer, intent(in) :: nVertLevels !< number of vertical levels + real (kind=RKIND), intent(in) :: phiInterp !< location to interpolate + real (kind=RKIND), dimension(:), intent(in) :: phiVals !< values at elevation of cell middle (where vertex + !< velocities are defined) + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(out) :: uvCell !< components of vertex velocity (vertically selected) + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND) :: alpha + integer :: aVertex, theVertex, iHigh, iLow + real (kind=RKIND) :: eps=1e-14_RKIND + + uvCell = 0.0_RKIND + + !call mpas_log_write( 'interp' ) + if(iLevel < 1) then + if(iLevel == 0) then + !print *, 'nCellVertices = ', nCellVertices, 'buoyancyInterp= ', phiInterp + do aVertex = 1, nCellVertices + theVertex = verticesOnCell(aVertex) + !print *, maxloc(phiVals,1), phiVals(1:nVertLevels), uVertexVelocity(1:nVertLevels,theVertex) + uvCell(1,aVertex) = uVertexVelocity(maxloc(phiVals(1:nVertLevels),1), theVertex) + uvCell(2,aVertex) = vVertexVelocity(maxloc(phiVals(1:nVertLevels),1), theVertex) + uvCell(3,aVertex) = wVertexVelocity(maxloc(phiVals(1:nVertLevels),1), theVertex) + end do + else if(iLevel == -1) then + do aVertex = 1, nCellVertices + theVertex = verticesOnCell(aVertex) + uvCell(1,aVertex) = uVertexVelocity(minloc(phiVals(1:nVertLevels),1), theVertex) + uvCell(2,aVertex) = vVertexVelocity(minloc(phiVals(1:nVertLevels),1), theVertex) + uvCell(3,aVertex) = wVertexVelocity(minloc(phiVals(1:nVertLevels),1), theVertex) + end do + end if + else + call get_bounding_indices(iLow, iHigh, phiInterp, phiVals, iLevel, nVertLevels) + + ! interpolate to vertical level now + if(abs(phiVals(iHigh) - phiVals(iLow)) < eps) then + ! we really can't distinguish between each of these points numerically, just take the + ! average of both + alpha = 0.5_RKIND + else + alpha = (phiInterp - phiVals(iLow))/(phiVals(iHigh) - phiVals(iLow)) + end if + + ! interpolate to the vertical level + do aVertex = 1, nCellVertices + theVertex = verticesOnCell(aVertex) + ! assume for now that we only care about the top level for a surface drifter + uvCell(1,aVertex) = alpha * uVertexVelocity(iHigh, theVertex) + & + (1.0_RKIND - alpha) * uVertexVelocity(iLow, theVertex) + uvCell(2,aVertex) = alpha * vVertexVelocity(iHigh, theVertex) + & + (1.0_RKIND - alpha) * vVertexVelocity(iLow, theVertex) + uvCell(3,aVertex) = alpha * wVertexVelocity(iHigh, theVertex) + & + (1.0_RKIND - alpha) * wVertexVelocity(iLow, theVertex) + end do + end if + + end subroutine interp_nodal_vectors!}}} + +!*********************************************************************** +! +! routine zero_boundary_nodal_values +! +!> \brief Enfore boundary condition for nodal value, setting to 0 +!> \author Phillip Wolfram +!> \date 05/27/2014 +!> \details +!> This routine ensures zero Dirchilet boundary conditions +!> (commonly for the nodal velocity) +! +!----------------------------------------------------------------------- + subroutine zero_boundary_nodal_values(nCellVertices, verticesOnCell, & !{{{ + boundaryVertex, uvCell) + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, dimension(:), intent(in) :: verticesOnCell !< list of vertex indices on cell + integer, intent(in) :: nCellVertices !< number of cell vertices + integer, dimension(:), intent(in) :: boundaryVertex !< boundary vertices for particular level + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:), intent(out) :: uvCell !< components of vertex velocity (vertically selected) + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: aVertex, theVertex + + ! make sure to mask all boundary vertexes to be zero to enforce boundary conditions + do aVertex = 1, nCellVertices + theVertex = verticesOnCell(aVertex) + !print *, boundaryVertex(theVertex) + ! make all the boundary values be zero to prevent particle from horizontally leaving cell + uvCell(1,aVertex) = uvCell(1,aVertex) * (1-boundaryVertex(theVertex)) + uvCell(2,aVertex) = uvCell(2,aVertex) * (1-boundaryVertex(theVertex)) + uvCell(3,aVertex) = uvCell(3,aVertex) * (1-boundaryVertex(theVertex)) + end do + + end subroutine zero_boundary_nodal_values!}}} + +!*********************************************************************** +! +! routine get_bounding_indices +! +!> \brief Get indices for high and low values for interpolation +!> \author Phillip Wolfram +!> \date 05/28/2014 +!> \details +!> This routine returns the indices (iLow, iHigh) on either side of phiInterp +! +!----------------------------------------------------------------------- + subroutine get_bounding_indices(iLow, iHigh, phiInterp, phiVals, iLevel, nVertLevels) !{{{ + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, intent(in) :: iLevel !< vertical level / cell of phiInterp + integer, intent(in) :: nVertLevels !< number of vertical levels + real (kind=RKIND), intent(in) :: phiInterp !< location to interpolate + real (kind=RKIND), dimension(:), intent(in) :: phiVals !< values at elevation of cell middle (where vertex + !< velocities are defined) + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: iLow, iHigh !< interpolation indices + + ! assumes increasing index is in decreasing phi space, and phiInterp in range of phiVals + !print *, 'phiInterp = ', phiInterp, 'phiVals = ', phiVals, 'nVertLevels = ', nVertLevels, 'iLevel = ', iLevel + if(phiInterp > phiVals(iLevel)) then + iHigh = iLevel + 1 + iLow = iLevel + else + iHigh = iLevel + iLow = iLevel + 1 + end if + + ! check to make sure points are in range + ! optimization point: smarter algorithm won't have to call this ever + if(.not.((phiInterp <= phiVals(iHigh)) .and. (phiInterp >= phiVals(iLow)))) then + !call mpas_log_write( 'fast interpolation failed, trying general, brute force search for interpolation bounds') + !print *, 'iLow = ', iLow, ' iHigh = ', iHigh + !print *, 'phiInterp = ', phiInterp , ' phiLow =', phiVals(iLow), ' phiHigh = ', phiVals(iHigh) + call get_bounding_indices_brute_force(nVertLevels, phiInterp, phiVals, iLow, iHigh) + end if + + end subroutine get_bounding_indices !}}} + +!*********************************************************************** +! +! routine get_bounding_indices_brute_force +! +!> \brief Get the interpolation bounds via brute force +!> \author Phillip Wolfram +!> \date 05/27/2014 +!> \details +!> This routine finds the interpolation bounds directly (brute force). +! +!----------------------------------------------------------------------- + subroutine get_bounding_indices_brute_force(nVertLevels, phiInterp, phiVals, iLow, iHigh) !{{{ + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, intent(in) :: nVertLevels !< number of vertical levels + real (kind=RKIND), intent(in) :: phiInterp !< location to interpolate + real (kind=RKIND), dimension(:), intent(in) :: phiVals !< values at elevation of cell middle (where + !< vertex velocities are defined) + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: iLow, iHigh !< indexes for the high and low components for the interpolant + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + integer :: aLevel + character (len=StrKIND) :: message + + ! make no assumptions + do aLevel = 1, nVertLevels-1 + if(phiVals(aLevel) <= phiInterp .and. phiInterp <= phiVals(aLevel+1)) then + iLow = aLevel + iHigh = aLevel+1 + exit + else if (phiVals(aLevel+1) <= phiInterp .and. phiInterp <= phiVals(aLevel)) then + iLow = aLevel+1 + iHigh = aLevel + exit + end if + end do + +#ifdef MPAS_DEBUG + if(phiVals(iLow) <= phiInterp .and. phiInterp <= phiVals(iHigh)) then + ! we are ok + LIGHT_DEBUG_ALL_WRITE('brute force interpolation successful') + LIGHT_DEBUG_ALL_WRITE('iLow = ' COMMA iLow COMMA ' iHigh = ' COMMA iHigh) + write(message, *) 'phiInterp = ', phiInterp, ' phiLow =', phiVals(iLow), ' phiHigh = ', phiVals(iHigh) + LIGHT_DEBUG_ALL_WRITE(message) + else + print *, 'brute force interpolation failed with phiInterp = ', phiInterp, ' phiLow = ', phiVals(iLow), & + ' phiHigh = ', phiVals(iHigh) + LIGHT_DEBUG_ALL_WRITE(' phiVals = ' COMMA phiVals(1:nVertLevels)) + end if + + call mpas_log_write( 'Warning!: brute force interpolation used, boundary condition may be wrong!') +#endif + + end subroutine get_bounding_indices_brute_force!}}} + +!*********************************************************************** +! +! routine interp_vert_velocity_to_zlevel +! +!> \brief Interpolate the vertical velcity to a z level +!> \author Phillip Wolfram +!> \date 05/08/2014 +!> \details +!> This routine interpolates the vertical velocity to a particular +!> z-level. +! +!----------------------------------------------------------------------- + real (kind=RKIND) function interp_vert_velocity_to_zlevel( & !{{{ + iLevel, zSubStep, zTop, vertVelocityTop) + + implicit none + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + integer, intent(in) :: iLevel !< vertical level / cell of zSubStep + real (kind=RKIND), intent(in) :: zSubStep !< location to interpolate + real (kind=RKIND), dimension(:), intent(in) :: zTop !< elevation of cell top + real (kind=RKIND), dimension(:), intent(in) :: vertVelocityTop !< vertical velocity at top of cell + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + !real (kind=RKIND), intent(out) :: interp_vert_velocity_to_zlevel + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + real (kind=RKIND) :: alpha + + if(iLevel < 1) then + if(iLevel == 0) then + interp_vert_velocity_to_zlevel = vertVelocityTop(maxloc(zTop,1)) + else if(iLevel == -1) then + interp_vert_velocity_to_zlevel = vertVelocityTop(minloc(zTop,1)) + end if + else + ! interpolate the velocity [assumes zTop(iLevel+1) <= zSubStep <= zTop(iLevel)] + if (zTop(iLevel+1) <= zSubStep .and. zSubStep <= zTop(iLevel)) then + alpha = (zSubStep - zTop(iLevel+1))/(zTop(iLevel)- zTop(iLevel+1)) + else if (zTop(iLevel) <= zSubStep .and. zSubStep <= zTop(iLevel+1)) then + alpha = (zSubStep - zTop(iLevel))/(zTop(iLevel+1)- zTop(iLevel)) +#ifdef MPAS_DEBUG + else + call mpas_log_write( 'Error with vertical velocity interpolation!') +#endif + end if + interp_vert_velocity_to_zlevel = alpha * vertVelocityTop(iLevel)+ (1.0_RKIND - alpha) * vertVelocityTop(iLevel+1) + end if + + end function interp_vert_velocity_to_zlevel!}}} + +!*********************************************************************** +! +! routine time_interp_field +! +!> \brief Interpolate a field in time +!> \author Phillip Wolfram +!> \date 07/16/2014 +!> \details +!> This routine interpolates a field in time over multiple levels. +! +!----------------------------------------------------------------------- + subroutine time_interp_field(basePool, timeInterpOrder, timeCoeff, field, fieldname) !{{{ + implicit none + + type (mpas_pool_type), pointer, intent(in) :: basePool + integer, intent(in) :: timeInterpOrder + real (kind=RKIND), dimension(:), intent(in) :: timeCoeff + real (kind=RKIND), dimension(:,:), pointer, intent(out) :: field + character(len=*), intent(in) :: fieldname + + real (kind=RKIND), dimension(:,:), pointer :: tempfield + integer :: aTimeLevel + + field = 0.0_RKIND + do aTimeLevel = 1, timeInterpOrder + call mpas_pool_get_array(basePool, trim(fieldname), tempfield, timeLevel=aTimeLevel) + field = field + timeCoeff(aTimeLevel) * tempfield + end do + + end subroutine time_interp_field !}}} +!}}} + +end module ocn_lagrangian_particle_tracking + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking_interpolations.F b/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking_interpolations.F new file mode 100644 index 0000000000..ba6422d9a0 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking_interpolations.F @@ -0,0 +1,646 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!*********************************************************************** +! +! ocn_lagrangian_particle_tracking_interpolations +! +!> \brief LIGHT Vector reconstruction and filtering module +!> \author Phillip J. Wolfram +!> \date 07/21/2015 +!> \details +!> This module provides routines for performing vector interpolations +!> and spatial filtering. +! +!----------------------------------------------------------------------- +module ocn_lagrangian_particle_tracking_interpolations + + use mpas_derived_types + use mpas_constants + use mpas_rbf_interpolation + use mpas_geometry_utils + use mpas_vector_reconstruction + use mpas_dmpar + + implicit none + + contains + +!*********************************************************************** +! +! routine ocn_vertex_reconstruction +! +!> \brief Reconstruct vertex velocity driver / interface +!> \author Phillip Wolfram +!> \date 03/27/2014 +!> \details +!> Purpose: reconstruct vector field at vertex locations based on +!> particular choice of reconstruction method +!> Input: mesh meta data and vector component data residing at cell edges +!> initialize_weights logical is to determine if weights should be initialized +!> Output: reconstructed vector field (measured in X,Y,Z) located at vertices +!----------------------------------------------------------------------- + subroutine ocn_vertex_reconstruction(filterNum, meshPool, scratchPool, particleCellPool, layerThickness, u, & !{{{ + uvReconstructX, uvReconstructY, uvReconstructZ ) + + implicit none + + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh information + type (mpas_pool_type), pointer, intent(in) :: scratchPool !< Input: Scratch variables + type (mpas_pool_type), pointer, intent(in) :: particleCellPool !< Input: particlefield variables + integer, intent(in) :: filterNum ! filtering strength employed + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: layerThickness !< Input: layerThickness on cells + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: u !< Input: Velocity field on edges (normalVelocity) + type (field2DReal), pointer, intent(inout) :: uvReconstructX !< Output: X Component of velocity reconstructed to vertices + type (field2DReal), pointer, intent(inout) :: uvReconstructY !< Output: Y Component of velocity reconstructed to vertices + type (field2DReal), pointer, intent(inout) :: uvReconstructZ !< Output: Z Component of velocity reconstructed to vertices + + ! could add additional reconstruction techniques here with switch if desired + + ! assumption is made that mpas_init_reconstruct was previously called + call ocn_RBFvertex(meshPool, filterNum, layerThickness, u, uvReconstructX, uvReconstructY, uvReconstructZ, .false., & + scratchPool, particleCellPool) + + end subroutine ocn_vertex_reconstruction!}}} + +!*********************************************************************** +! +! routine ocn_RBFvertex +! +!> \brief Reconstruct vertex velocity using linear interpolation of +!> RBFs reconstruction at cell centers +!> \author Phillip Wolfram, Todd Ringler +!> \date 03/26/2014 +!> \details +!> Purpose: reconstruct vector field at vertex locations based on radial basis functions +!> Input: mesh meta data and vector component data residing at cell edges +!> initialize_weights logical is to determine if weights should be initialized +!> Output: reconstructed vector field (measured in X,Y,Z) located at vertices +!----------------------------------------------------------------------- + subroutine ocn_RBFvertex(meshPool, filterNum, layerThickness, u, uvReconstructX, uvReconstructY, uvReconstructZ, & !{{{ + initialize_weights, scratchPool, particleCellPool) + + implicit none + + ! inputs + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh information + type (mpas_pool_type), pointer, intent(in) :: scratchPool + type (mpas_pool_type), pointer, intent(in) :: particleCellPool !< Input: particlefield variables + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: u !< Input: Velocity field on edges + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: layerThickness !< Input: layerThickness on cells + integer, intent(in) :: filterNum !< number of times to filter + logical, intent(in) :: initialize_weights !< Input: Determine if weights for RBF should be pre-computed + + ! outputs + type (field2DReal), pointer, intent(inout) :: uvReconstructX !< Output: X Component of velocity reconstructed to vertices + type (field2DReal), pointer, intent(inout) :: uvReconstructY !< Output: Y Component of velocity reconstructed to vertices + type (field2DReal), pointer, intent(inout) :: uvReconstructZ !< Output: Z Component of velocity reconstructed to vertices + + ! local / temporary arrays needed in the compute procedure + type (field2DReal), pointer :: & + ucReconstructX, ucReconstructY, ucReconstructZ, ucReconstructZonal, ucReconstructMeridional ! cell center values + type (field2DReal), pointer :: ucStore, vcStore, wcStore + type (field2DInteger), pointer :: boundaryVertex, boundaryCell, boundaryCellGlobal, boundaryVertexGlobal + + ! get pointers + call mpas_pool_get_field(scratchPool, 'ucReconstructX', ucReconstructX) + call mpas_pool_get_field(scratchPool, 'ucReconstructY', ucReconstructY) + call mpas_pool_get_field(scratchPool, 'ucReconstructZ', ucReconstructZ) + call mpas_pool_get_field(scratchPool, 'ucReconstructZonal', ucReconstructZonal) + call mpas_pool_get_field(scratchPool, 'ucReconstructMeridional', ucReconstructMeridional) + call mpas_pool_get_field(scratchPool, 'boundaryVertexGlobal', boundaryVertexGlobal) + + ! allocate memory + call mpas_allocate_scratch_field(ucReconstructX, .True.) + call mpas_allocate_scratch_field(ucReconstructY, .True.) + call mpas_allocate_scratch_field(ucReconstructZ, .True.) + call mpas_allocate_scratch_field(ucReconstructZonal, .True.) + call mpas_allocate_scratch_field(ucReconstructMeridional, .True.) + call mpas_allocate_scratch_field(boundaryVertexGlobal, .True.) + + ucReconstructX % array = 0.0_RKIND + ucReconstructY % array = 0.0_RKIND + ucReconstructZ % array = 0.0_RKIND + + ! initialize weights (should be pre-initialized) + if (initialize_weights) then + call mpas_init_reconstruct(meshPool) + end if + + ! get cell center reconstructed RBF values + call mpas_reconstruct(meshPool, u, ucReconstructX % array, ucReconstructY % array, ucReconstructZ % array, & + ucReconstructZonal % array, ucReconstructMeridional % array) + + ! need to do exchange for uc components (we don't use Zonal / Meridional for this calculation) + call mpas_dmpar_exch_halo_field(ucReconstructX) + call mpas_dmpar_exch_halo_field(ucReconstructY) + call mpas_dmpar_exch_halo_field(ucReconstructZ) + + ! get boundaries + call mpas_pool_get_field(meshPool,'boundaryVertex', boundaryVertex) + call mpas_pool_get_field(meshPool,'boundaryCell', boundaryCell) + + + if (filternum > 0) then + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! filter the cell velocity field + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + call ocn_second_order_shapiro_filter_ops(filterNum, meshPool, scratchPool, boundaryVertex, boundaryCell, & + layerThickness, ucReconstructX, ucReconstructY, ucReconstructZ) + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! store filter data & + ! write data to file for output + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + call mpas_pool_get_field(particleCellPool, 'filteredVelocityU', ucStore) + call mpas_pool_get_field(particleCellPool, 'filteredVelocityV', vcStore) + call mpas_pool_get_field(particleCellPool, 'filteredVelocityW', wcStore) + ucStore % array = ucReconstructX % array + vcStore % array = ucReconstructY % array + wcStore % array = ucReconstructZ % array + + end if + + ! interpolate to vertex locations for use in Wachspress + call ocn_vector_cell_center_to_vertex(meshPool, boundaryVertex % array, boundaryCell % array, & + ucReconstructX % array, ucReconstructY % array, ucReconstructZ % array, & + uvReconstructX % array, uvReconstructY % array, uvReconstructZ % array) + + ! handle boundary vertices (should be zero). Can potentially remove if mpas_init_block initializes to 0 vs -1e34 + boundaryVertexGlobal % array = boundaryVertex % array + call mpas_dmpar_exch_halo_field(boundaryVertexGlobal) + ! definite change between these fields! + uvReconstructX % array = uvReconstructX % array * (1.0_RKIND - boundaryVertexGlobal % array) + uvReconstructY % array = uvReconstructY % array * (1.0_RKIND - boundaryVertexGlobal % array) + uvReconstructZ % array = uvReconstructZ % array * (1.0_RKIND - boundaryVertexGlobal % array) + + ! do halo exchanges + call mpas_dmpar_exch_halo_field(uvReconstructX) + call mpas_dmpar_exch_halo_field(uvReconstructY) + call mpas_dmpar_exch_halo_field(uvReconstructZ) + + ! deallocate memory + call mpas_deallocate_scratch_field(ucReconstructX, .True.) + call mpas_deallocate_scratch_field(ucReconstructY, .True.) + call mpas_deallocate_scratch_field(ucReconstructZ, .True.) + call mpas_deallocate_scratch_field(ucReconstructZonal, .True.) + call mpas_deallocate_scratch_field(ucReconstructMeridional, .True.) + call mpas_deallocate_scratch_field(boundaryVertexGlobal, .True.) + + end subroutine ocn_RBFvertex!}}} + +!*********************************************************************** +! +! routine ocn_vector_cell_center_to_vertex +! +!> \brief Interpolate cell center values to vertex values +!> \author Phillip Wolfram +!> \date 05/27/2014 +!> \details +!> Purpose: interpolate vector field at vertex locations from cell center values +!> using Barycentric (via Wachspress) interpolation +!> Input: cell center data and mesh information +!> Output: interpolated vertex values +!----------------------------------------------------------------------- + subroutine ocn_vector_cell_center_to_vertex(meshPool, boundaryVertex, boundaryCell, & !{{{ + ucReconstructX, ucReconstructY, ucReconstructZ, & + uvReconstructX, uvReconstructY, uvReconstructZ) + + implicit none + + ! input variables + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh information + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: ucReconstructX, & !< Input: X Cell center values + ucReconstructY, & !< Input: Y Cell center values + ucReconstructZ !< Input: z Cell center values + integer, dimension(:,:), pointer, intent(in) :: boundaryVertex, boundaryCell !< Input: Boundary flags + + ! output variables + real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: uvReconstructX !< Output: Vertex Reconstructed X Velocity Component + real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: uvReconstructY !< Output: Vertex Reconstructed Y Velocity Component + real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: uvReconstructZ !< Output: Vertex Reconstructed Z Velocity Component + + ! local variables + integer, pointer :: nVerticesSolve, nCells, vertexDegree, nVertLevels + integer :: aVertex, aCell, aLevel + integer, dimension(:,:), pointer :: cellsOnVertex + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell, xVertex, yVertex, zVertex + real (kind=RKIND), dimension(:,:), pointer :: kiteAreasOnVertex + ! temporary arrays needed in the (to be constructed) init procedure + ! note that lambda is going to be constant for this and could be cached + real (kind=RKIND), dimension(:), allocatable :: lambda + real (kind=RKIND), dimension(:,:), allocatable :: pointVertex + real (kind=RKIND), dimension(3) :: pointInterp + real (kind=RKIND) :: xp,yp,zp , sumArea, kiteArea + logical, pointer :: is_periodic + real(kind=RKIND), pointer :: x_period, y_period + + uvReconstructX = 0.0_RKIND + uvReconstructY = 0.0_RKIND + uvReconstructZ = 0.0_RKIND + + call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) + + allocate(lambda(vertexDegree), pointVertex(3,vertexDegree)) + + ! setup pointers + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + + call mpas_pool_get_array(meshPool, 'kiteAreasOnVertex', kiteAreasOnVertex) + + call mpas_pool_get_config(meshPool, 'is_periodic', is_periodic) + call mpas_pool_get_config(meshPool, 'x_period', x_period) + call mpas_pool_get_config(meshPool, 'y_period', y_period) + + ! loop over all vertices + do aVertex = 1, nVerticesSolve + ! could precompute the list as an optimization + ! really, condition is any boundaryVertex in column greater than 0 + if(any(boundaryVertex(:,aVertex) < 1)) then + ! get vertex location and cell center locations + do aCell = 1, vertexDegree + ! logical could be moved outside of code block as an optimization + ! (then essentially would have two nearly identical code blocks...) + if (is_periodic) then + ! fix periodicity with respect to pointInterp (xVertex) + pointVertex(1,aCell) = mpas_fix_periodicity(xCell(cellsOnVertex(aCell, aVertex)), xVertex(aVertex), x_period) + pointVertex(2,aCell) = mpas_fix_periodicity(yCell(cellsOnVertex(aCell, aVertex)), yVertex(aVertex), y_period) + pointVertex(3,aCell) = zCell(cellsOnVertex(aCell, aVertex)) + else + pointVertex(1,aCell) = xCell(cellsOnVertex(aCell, aVertex)) + pointVertex(2,aCell) = yCell(cellsOnVertex(aCell, aVertex)) + pointVertex(3,aCell) = zCell(cellsOnVertex(aCell, aVertex)) + end if + end do + ! vertex point for reconstruction + pointInterp(1) = xVertex(aVertex) + pointInterp(2) = yVertex(aVertex) + pointInterp(3) = zVertex(aVertex) + ! get interpolation constants (could be cached / optimized with areaBin) + lambda = mpas_wachspress_coordinates(vertexDegree, pointVertex , pointInterp, meshPool) + else + lambda = 0.0_RKIND + end if + + do aLevel = 1, nVertLevels + if(boundaryVertex(aLevel,aVertex) < 1) then + ! perform interpolation + uvReconstructX(aLevel,aVertex) = sum(ucReconstructX(aLevel,cellsOnVertex(:,aVertex)) * lambda) + uvReconstructY(aLevel,aVertex) = sum(ucReconstructY(aLevel,cellsOnVertex(:,aVertex)) * lambda) + uvReconstructZ(aLevel,aVertex) = sum(ucReconstructZ(aLevel,cellsOnVertex(:,aVertex)) * lambda) + end if + end do + + ! need to specify boundary conditions for the vertexes (outside this subroutine) + + end do + + deallocate(lambda, pointVertex) + + end subroutine ocn_vector_cell_center_to_vertex!}}} + +!*********************************************************************** +! +! routine ocn_vector_vertex_to_cell_center +! +!> \brief Interpolate vertex values to cell center +!> \author Phillip Wolfram +!> \date 08/01/2014 +!> \details +!> Purpose: interpolate vector field at cell center locations from vertex values +!> using Wachspress interpolation +!> Input: vertex vector data and mesh information +!> Output: interpolated cell values +!----------------------------------------------------------------------- + subroutine ocn_vector_vertex_to_cell_center(meshPool, & !{{{ + uvReconstructX, uvReconstructY, uvReconstructZ, & + ucReconstructX, ucReconstructY, ucReconstructZ) + + implicit none + + ! input variables + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh information + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: uvReconstructX, & !< Input: Vertex x values + uvReconstructY, & !< Input: Vertex y values + uvReconstructZ !< Input: Vertex z values + + ! output variables + real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: ucReconstructX !< Output: X Component of velocity + !< reconstructed to cells + real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: ucReconstructY !< Output: Y Component of velocity + !< reconstructed to cells + real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: ucReconstructZ !< Output: Z Component of velocity + !< reconstructed to cells + + ! local variables + integer, pointer :: nCellsSolve, nVertLevels + integer, dimension(:), pointer :: nEdgesOnCell + integer :: aVertex, aCell, aLevel, nLocalVertices + integer, dimension(:,:), pointer :: verticesOnCell + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell, xVertex, yVertex, zVertex + ! temporary arrays needed in the (to be constructed) init procedure + ! note that lambda is going to be constant for this and could be cached + real (kind=RKIND), dimension(:), allocatable :: lambda + real (kind=RKIND), dimension(3) :: pointInterp + real (kind=RKIND), dimension(:,:), allocatable :: pointVertex + real (kind=RKIND) :: xp,yp,zp + + ucReconstructX = 0.0_RKIND + ucReconstructY = 0.0_RKIND + ucReconstructZ = 0.0_RKIND + + ! setup pointers + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'verticesOnCell', verticesOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + + + ! loop over all vertices + do aCell = 1, nCellsSolve + ! could precompute the list as an optimization to + ! remove the following lines !{{{ + nLocalVertices = nEdgesOnCell(aCell) + ! really, condition is any boundaryVertex in column greater than 0 + allocate(lambda(nLocalVertices), pointVertex(3,nLocalVertices)) + ! get vertex location and cell center locations + do aVertex = 1, nLocalVertices + pointVertex(1,aVertex) = xVertex(verticesOnCell(aVertex, aCell)) + pointVertex(2,aVertex) = yVertex(verticesOnCell(aVertex, aCell)) + pointVertex(3,aVertex) = zVertex(verticesOnCell(aVertex, aCell)) + end do + ! vertex point for reconstruction + pointInterp(1) = xCell(aCell) + pointInterp(2) = yCell(aCell) + pointInterp(3) = zCell(aCell) + ! get interpolation constants (should be cached as an optimization!) + lambda = mpas_wachspress_coordinates(nLocalVertices, pointVertex , pointInterp, meshPool) + !}}} + + do aLevel = 1, nVertLevels + ! perform interpolation + ucReconstructX(aLevel,aCell) = sum(uvReconstructX(aLevel,verticesOnCell(1:nLocalVertices,aCell)) * lambda) + ucReconstructY(aLevel,aCell) = sum(uvReconstructY(aLevel,verticesOnCell(1:nLocalVertices,aCell)) * lambda) + ucReconstructZ(aLevel,aCell) = sum(uvReconstructZ(aLevel,verticesOnCell(1:nLocalVertices,aCell)) * lambda) + end do + + deallocate(lambda, pointVertex) + end do + + end subroutine ocn_vector_vertex_to_cell_center !}}} + +!*********************************************************************** +! +! routine ocn_second_order_shapiro_filter_ops +! +!> \brief Do Ntimes simple shapiro filtering operations, but make +!> higher order +!> \author Phillip Wolfram +!> \date 08/01/2014 +!> \details +!> Purpose: multiple applications of digital shapiro filter (discrete Laplacian) +!> Input: cell centered data and mesh information +!> Output: filtered cell values +!----------------------------------------------------------------------- + subroutine ocn_second_order_shapiro_filter_ops(Ntimes, meshPool, scratchPool, boundaryVertex, boundaryCell, & + layerThickness, ucReconstructX, ucReconstructY, ucReconstructZ) !{{{ + implicit none + + type (mpas_pool_type), pointer, intent(in) :: meshPool, scratchPool + type (field2DInteger), pointer, intent(in) :: boundaryVertex, boundaryCell + type (field2DReal), pointer, intent(inout) :: ucReconstructX, ucReconstructY, ucReconstructZ ! cell center values + integer, intent(in) :: Ntimes ! number of filter applications + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: layerThickness + + type (field2DReal), pointer :: ucStore, vcStore, wcStore + + call mpas_pool_get_field(scratchPool,'ucX',ucStore) + call mpas_pool_get_field(scratchPool,'ucY',vcStore) + call mpas_pool_get_field(scratchPool,'ucZ',wcStore) + call mpas_allocate_scratch_field(ucStore,.True.) + call mpas_allocate_scratch_field(vcStore,.True.) + call mpas_allocate_scratch_field(wcStore,.True.) + + + call ocn_multiple_vector_shapiro_filter_ops(Ntimes, meshPool, scratchPool, boundaryVertex, boundaryCell, & + layerThickness, ucReconstructX, ucReconstructY, ucReconstructZ) + ucStore % array = 2.0_RKIND*ucReconstructX % array + vcStore % array = 2.0_RKIND*ucReconstructY % array + wcStore % array = 2.0_RKIND*ucReconstructZ % array + call ocn_multiple_vector_shapiro_filter_ops(Ntimes, meshPool, scratchPool, boundaryVertex, boundaryCell, & + layerThickness, ucReconstructX, ucReconstructY, ucReconstructZ) + ucStore % array = ucStore % array - ucReconstructX % array + vcStore % array = vcStore % array - ucReconstructY % array + wcStore % array = wcStore % array - ucReconstructZ % array + + ! move temporary storage into final storage + ucReconstructX % array = ucStore % array + ucReconstructY % array = vcStore % array + ucReconstructZ % array = wcStore % array + + ! deallocate temporary memory + call mpas_deallocate_scratch_field(ucStore,.True.) + call mpas_deallocate_scratch_field(vcStore,.True.) + call mpas_deallocate_scratch_field(wcStore,.True.) + + end subroutine ocn_second_order_shapiro_filter_ops !}}} + +!*********************************************************************** +! +! routine ocn_multiple_vector_shapiro_filter_ops +! +!> \brief Do Ntimes simple shapiro filtering operations +!> \author Phillip Wolfram +!> \date 08/01/2014 +!> \details +!> Purpose: multiple applications of digital shapiro filter (discrete Laplacian) +!> Input: cell centered data and mesh information +!> Output: filtered cell values +!----------------------------------------------------------------------- + subroutine ocn_multiple_vector_shapiro_filter_ops(Ntimes, meshPool, scratchPool, boundaryVertex, boundaryCell, & + layerThickness, ucReconstructX, ucReconstructY, ucReconstructZ) !{{{ + implicit none + + type (mpas_pool_type), pointer, intent(in) :: meshPool, scratchPool + type (field2DInteger), pointer, intent(in) :: boundaryVertex, boundaryCell + type (field2DReal), pointer, intent(inout) :: ucReconstructX, ucReconstructY, ucReconstructZ ! cell center values + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: layerThickness + integer, intent(in) :: Ntimes ! number of filter applications + + ! local variables + integer atime + + do atime = 1,Ntimes + !call ocn_simple_vector_shapiro_filter(meshPool, scratchPool, boundaryVertex, boundaryCell, & + ! ucReconstructX, ucReconstructY, ucReconstructZ) + call ocn_simple_vector_laplacian_filter(meshPool, scratchPool, boundaryCell % array, layerThickness, & + ucReconstructX % array) + call ocn_simple_vector_laplacian_filter(meshPool, scratchPool, boundaryCell % array, layerThickness, & + ucReconstructY % array) + call ocn_simple_vector_laplacian_filter(meshPool, scratchPool, boundaryCell % array, layerThickness, & + ucReconstructZ % array) + + end do + + end subroutine ocn_multiple_vector_shapiro_filter_ops !}}} + +!*********************************************************************** +! +! routine ocn_simple_vector_laplacian_filter +! +!> \brief Do 1 pass of simple laplacian filter +!> \author Phillip Wolfram +!> \date 08/01/2014 +!> \details +!> Purpose: one pass of digital shapiro filter (discrete Laplacian) +!> Input: cell centered data and mesh information +!> Output: filtered cell values +!----------------------------------------------------------------------- + subroutine ocn_simple_vector_laplacian_filter(meshPool, scratchPool, boundaryCell, layerThickness, ucReconstruct) !{{{ + implicit none + + type (mpas_pool_type), pointer, intent(in) :: meshPool, scratchPool + integer, dimension(:,:), pointer, intent(in) :: boundaryCell + real (kind=RKIND), dimension(:,:), pointer, intent(inout) :: ucReconstruct + real (kind=RKIND), dimension(:,:), pointer, intent(in) :: layerThickness + + ! local variables + type (field2DReal), pointer :: ucTemp + integer :: aCell, aNeigh, aLevel + integer, pointer :: nCellsSolve, nVertLevels + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnCell + real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND) :: volSum, cellVol + + ! allocate scratch memory + call mpas_pool_get_field(scratchPool, 'ucTemp', ucTemp) + call mpas_allocate_scratch_field(ucTemp,.True.) + + ! get values from pools + call mpas_pool_get_dimension(meshPool,'nCellsSolve',nCellsSolve) + call mpas_pool_get_dimension(meshPool,'nVertLevels',nVertLevels) + call mpas_pool_get_array(meshPool,'nEdgesOnCell',nEdgesOnCell) + call mpas_pool_get_array(meshPool,'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool,'areaCell',areaCell) + + ucTemp % array = 0.0_RKIND + + ! perform laplacian filtering + do aCell = 1, nCellsSolve + do aLevel = 1, nVertLevels + volSum = nEdgesOnCell(aCell) * layerThickness(aLevel,aCell) * areaCell(aCell) * (1-boundaryCell(aLevel,aCell)) + ucTemp % array(aLevel, aCell) = ucReconstruct(aLevel, aCell) * volSum + if (volSum /= 0 ) then + ! loop over all neighbors + do aNeigh = 1, nEdgesOnCell(aCell) + cellVol = layerThickness(aLevel,cellsOnCell(aNeigh,aCell)) * areaCell(cellsOnCell(aNeigh,aCell)) & + * (1-boundaryCell(aLevel, cellsOnCell(aNeigh,aCell))) + volSum = volSum + cellVol + ucTemp % array(aLevel, aCell) = ucTemp % array(aLevel, aCell) + ucReconstruct(aLevel,cellsOnCell(aNeigh,aCell)) & + * cellVol + end do + ucTemp % array(aLevel, aCell) = ucTemp % array(aLevel, aCell) / volSum + end if + end do + end do + + ! exchange halo values + call mpas_dmpar_exch_halo_field(ucTemp) + + ! replace input values with filtered values + ucReconstruct = ucTemp % array + + ! deallocate scratch memory + call mpas_deallocate_scratch_field(ucTemp , .True.) + + end subroutine ocn_simple_vector_laplacian_filter !}}} + +!*********************************************************************** +! +! routine ocn_simple_vector_shapiro_filter +! +!> \brief Do 1 pass of simple shapiro filter +!> \author Phillip Wolfram +!> \date 08/01/2014 +!> \details +!> Purpose: one pass of digital shapiro filter to vertexes, back to cells +!> Input: cell centered data and mesh information +!> Output: filtered cell values +!----------------------------------------------------------------------- + subroutine ocn_simple_vector_shapiro_filter(meshPool, scratchPool, boundaryVertex, boundaryCell, & + ucReconstructX, ucReconstructY, ucReconstructZ) !{{{ + implicit none + + type (mpas_pool_type), pointer, intent(in) :: meshPool, scratchPool + type (field2DInteger), pointer, intent(in) :: boundaryVertex, boundaryCell + type (field2DReal), pointer, intent(inout) :: ucReconstructX, ucReconstructY, ucReconstructZ ! cell center values + + ! local variables + type (field2DReal), pointer :: uvX , uvY, uvZ ! cell center values + + ! allocate scratch memory + call mpas_pool_get_field(scratchPool, 'uvX', uvX) + call mpas_pool_get_field(scratchPool, 'uvY', uvY) + call mpas_pool_get_field(scratchPool, 'uvZ', uvZ) + call mpas_allocate_scratch_field(uvX,.True.) + call mpas_allocate_scratch_field(uvY,.True.) + call mpas_allocate_scratch_field(uvZ,.True.) + + uvX % array = 0.0_RKIND + uvY % array = 0.0_RKIND + uvZ % array = 0.0_RKIND + + ! perform filtering + + ! CC -> vertices + call ocn_vector_cell_center_to_vertex(meshPool, boundaryVertex % array, boundaryCell % array, & + ucReconstructX % array, ucReconstructY % array, ucReconstructZ % array, & + uvX % array, uvY % array, uvZ % array) + ! do halo exchanges + call mpas_dmpar_exch_halo_field(uvX) + call mpas_dmpar_exch_halo_field(uvY) + call mpas_dmpar_exch_halo_field(uvZ) + ! vertices -> CC + call ocn_vector_vertex_to_cell_center(meshPool, & + uvX % array, uvY % array, uvZ % array, & + ucReconstructX % array, ucReconstructY % array, ucReconstructZ % array) + ! do halo exchanges + call mpas_dmpar_exch_halo_field(ucReconstructX) + call mpas_dmpar_exch_halo_field(ucReconstructY) + call mpas_dmpar_exch_halo_field(ucReconstructZ) + + ! N.B., effect of forgetting halo exchange may be subtle for a single pass + + ! deallocate scratch memory + call mpas_deallocate_scratch_field(uvX , .True.) + call mpas_deallocate_scratch_field(uvY , .True.) + call mpas_deallocate_scratch_field(uvZ , .True.) + + end subroutine ocn_simple_vector_shapiro_filter !}}} + +end module ocn_lagrangian_particle_tracking_interpolations + diff --git a/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking_reset.F b/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking_reset.F new file mode 100644 index 0000000000..edb8442cda --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_lagrangian_particle_tracking_reset.F @@ -0,0 +1,393 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!*********************************************************************** +! +! ocn_lagrangian_particle_tracking_reset +! +!> \brief LIGHT reset functionality +!> \author Phillip J. Wolfram +!> \date 10/28/2015 +!> \details +!> This module provides routines for performing particle resets in LIGHT. +! +!----------------------------------------------------------------------- +module ocn_lagrangian_particle_tracking_reset + + use mpas_derived_types + use mpas_constants + use mpas_timekeeping + use mpas_stream_manager + use mpas_pool_routines + + use ocn_constants + + implicit none + private + + !----------------------------------------------------------------- + ! public routines and interfaces + !----------------------------------------------------------------- + ! define publically accessible subroutines, functions, interfaces + public :: ocn_setup_particle_reset_condition + public :: ocn_evaluate_particle_reset_condition + public :: ocn_finalize_particle_reset_condition + + contains + +!*********************************************************************** +! +! routine ocn_setup_particle_reset_condition +! +!> \brief Set up needed information for particle resets +!> \author Phillip Wolfram +!> \date 10/28/2015 +!> \details +!> Purpose: Perform set up for particle resets. +!> Input: domain +!----------------------------------------------------------------------- + subroutine ocn_setup_particle_reset_condition(domain, err) !{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackScalarPool, lagrPartTrackRegionsPool + real (kind=RKIND), pointer :: globalResetTimeValue + type (mpas_timeInterval_type) :: timeInterval + character (len=StrKIND), pointer :: config_AM_lagrPartTrack_reset_global_timestamp + character (len=StrKIND), pointer :: config_AM_lagrPartTrack_region_stream + character (len=StrKIND), pointer :: config_AM_lagrPartTrack_reset_criteria + type (field1DInteger), pointer :: resetInsideRegionMaskValue1Field, resetOutsideRegionMaskValue1Field + integer, dimension(:), pointer :: resetInsideRegionMaskValue1, resetOutsideRegionMaskValue1 + + err = 0 + + ! get the configuration options + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_reset_global_timestamp', & + config_AM_lagrPartTrack_reset_global_timestamp) + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_region_stream', & + config_AM_lagrPartTrack_region_stream) + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_reset_criteria', & + config_AM_lagrPartTrack_reset_criteria) + + ! load in region masks streams (masks stored in pool) + if (trim(config_AM_lagrPartTrack_reset_criteria) == 'region' .or. & + trim(config_AM_lagrPartTrack_reset_criteria) == 'all' & + ) then + call MPAS_stream_mgr_read(domain % streamManager, streamID=trim(config_AM_lagrPartTrack_region_stream), ierr=err) + end if + + ! convert input config_AM_lagrPartTrack_reset_global_timestamp into S for calculations + block => domain % blocklist + do while (associated(block)) + ! setup pointers / get block + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackScalars', lagrPartTrackScalarPool) + call mpas_pool_get_array(lagrPartTrackScalarPool, 'globalResetTimeValue', globalResetTimeValue) + + ! convert config_AM_lagrPartTrack_reset_global_timestamp into seconds and store in globalResetTimeValue + call mpas_set_timeInterval(timeInterval, timeString=trim(config_AM_lagrPartTrack_reset_global_timestamp)) + call mpas_get_timeInterval(timeInterval, dt=globalResetTimeValue) + + !print *, 'resetTimeValue = ', globalResetTimeValue + + block => block % next + end do + + end subroutine ocn_setup_particle_reset_condition!}}} + +!*********************************************************************** +! +! routine ocn_evaluate_particle_reset_condition +! +!> \brief Evaluate needed information for particle resets +!> \author Phillip Wolfram +!> \date 10/30/2015 +!> \details +!> Purpose: Evaluate if particle resets should occur for a particle +!> Input: domain, particle +!> Output: boolean specifying whether the particles should be reset. +!----------------------------------------------------------------------- + subroutine ocn_evaluate_particle_reset_condition(domain, block, particle, dt, iCell, resetParticle, err) !{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), intent(in) :: dt + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + type (block_type), intent(inout), pointer :: block + type (mpas_particle_type), pointer, intent(inout) :: particle + integer, intent(inout) :: iCell + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + logical, intent(out) :: resetParticle + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: lagrPartTrackScalarPool, lagrPartTrackRegionsPool + integer, pointer :: transfered, numTimesReset + integer, pointer :: currentBlock, currentBlockReset, currentCell, currentCellReset + real (kind=RKIND), pointer :: xParticleReset, yParticleReset, zParticleReset, zLevelParticleReset + real (kind=RKIND), pointer :: xParticle, yParticle, zParticle, zLevelParticle + real (kind=RKIND), pointer :: timeSinceReset + real (kind=RKIND), pointer :: sumU, sumV, sumUU, sumUV, sumVV + integer, pointer :: resetTime + real (kind=RKIND), pointer :: globalResetTimeValue + + character (len=StrKIND), pointer :: config_AM_lagrPartTrack_reset_criteria + logical, pointer :: config_AM_lagrPartTrack_reset_if_outside_region + logical, pointer :: config_AM_lagrPartTrack_reset_if_inside_region + integer, dimension(:), pointer :: resetInsideRegionMaskValue1, resetOutsideRegionMaskValue1 + + ! initialize outputs + err = 0 + resetParticle = .False. + + ! get config options + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_reset_criteria', & + config_AM_lagrPartTrack_reset_criteria) + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_reset_if_outside_region', & + config_AM_lagrPartTrack_reset_if_outside_region) + call mpas_pool_get_config(ocnConfigs, 'config_AM_lagrPartTrack_reset_if_inside_region', & + config_AM_lagrPartTrack_reset_if_inside_region) + + ! get variables + call mpas_pool_get_array(particle % haloDataPool, 'timeSinceReset', timeSinceReset) + call mpas_pool_get_array(particle % haloDataPool, 'resetTime', resetTime) + + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackScalars', lagrPartTrackScalarPool) + call mpas_pool_get_array(lagrPartTrackScalarPool, 'globalResetTimeValue', globalResetTimeValue) + + if (trim(config_AM_lagrPartTrack_reset_criteria) == 'region' .or. & + trim(config_AM_lagrPartTrack_reset_criteria) == 'all' & + ) then + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackRegions', lagrPartTrackRegionsPool) + call mpas_pool_get_array(lagrPartTrackRegionsPool, 'resetInsideRegionMaskValue1', resetInsideRegionMaskValue1) + call mpas_pool_get_array(lagrPartTrackRegionsPool, 'resetOutsideRegionMaskValue1', resetOutsideRegionMaskValue1) + end if + + ! advance particle time + timeSinceReset = timeSinceReset + dt + + ! determine whether reset should occur depending upon type of reset condition + select case (trim(config_AM_lagrPartTrack_reset_criteria)) + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! time based + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !{{{ + + case ('particle_time') + ! use particle's value for resetTime and timeSinceReset + if (timeSinceReset > resetTime) then + resetParticle = .True. + end if + + case ('global_time') + if (timeSinceReset > globalResetTimeValue) then + resetParticle = .True. + end if + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !}}} + ! region based + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !{{{ + + case ('region') + ! outside region + if (config_AM_lagrPartTrack_reset_if_outside_region .and. & + resetOutsideRegionMaskValue1(iCell) == 0) then + resetParticle = .True. + end if + ! inside region + if (config_AM_lagrPartTrack_reset_if_inside_region .and. & + resetInsideRegionMaskValue1(iCell) == 1) then + resetParticle = .True. + end if + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !}}} + ! all conditions + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !{{{ + + case ('all') + ! particle time + if ((timeSinceReset > resetTime) .or. & + ! global time + (timeSinceReset > globalResetTimeValue) .or. & + ! outside region + (config_AM_lagrPartTrack_reset_if_outside_region .and. & + resetOutsideRegionMaskValue1(iCell) == 0) .or. & + ! inside region + (config_AM_lagrPartTrack_reset_if_inside_region .and. & + resetInsideRegionMaskValue1(iCell) == 1)) then + resetParticle = .True. + end if + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !}}} + ! default + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + case default + call mpas_log_write( 'WARNING: reset criteria in config_AM_lagrPartTrack_reset_criteria=' // & + trim(config_AM_lagrPartTrack_reset_criteria) // ' unknown! Cannot restart.') + + end select + + ! reset particle block, cell, and position to reset values + if (resetParticle) then + + !call mpas_log_write( 'reseting particle') + + ! get data + call mpas_pool_get_array(particle % haloDataPool, 'currentBlock', currentBlock) + call mpas_pool_get_array(particle % haloDataPool, 'currentBlockReset', currentBlockReset) + call mpas_pool_get_array(particle % haloDataPool, 'currentCellReset', currentCellReset) + call mpas_pool_get_array(particle % haloDataPool, 'xParticleReset', xParticleReset) + call mpas_pool_get_array(particle % haloDataPool, 'yParticleReset', yParticleReset) + call mpas_pool_get_array(particle % haloDataPool, 'zParticleReset', zParticleReset) + call mpas_pool_get_array(particle % haloDataPool, 'zLevelParticleReset', zLevelParticleReset) + call mpas_pool_get_array(particle % haloDataPool, 'xParticle', xParticle) + call mpas_pool_get_array(particle % haloDataPool, 'yParticle', yParticle) + call mpas_pool_get_array(particle % haloDataPool, 'zParticle', zParticle) + call mpas_pool_get_array(particle % haloDataPool, 'zLevelParticle', zLevelParticle) + call mpas_pool_get_array(particle % haloDataPool, 'numTimesReset', numTimesReset) + call mpas_pool_get_array(particle % haloDataPool, 'transfered', transfered) + call mpas_pool_get_array(particle % haloDataPool, 'sumU', sumU) + call mpas_pool_get_array(particle % haloDataPool, 'sumV', sumV) + call mpas_pool_get_array(particle % haloDataPool, 'sumUU', sumUU) + call mpas_pool_get_array(particle % haloDataPool, 'sumUV', sumUV) + call mpas_pool_get_array(particle % haloDataPool, 'sumVV', sumVV) + + ! reset the time + timeSinceReset = 0.0_RKIND + + ! increment counters + if (currentBlock /= currentBlockReset) then + transfered = transfered + 1 + end if + numTimesReset = numTimesReset + 1 + + ! reset the block and the current cell + currentBlock = currentBlockReset + ! this should be a -1 in general but could precache based on an initial decomposition for performance + iCell = -1 + !iCell = currentCellReset + + ! reset positions + xParticle = xParticleReset + yParticle = yParticleReset + zParticle = zParticleReset + zLevelParticle = zLevelParticleReset + + ! reset velocity sums + sumU = 0.0_RKIND + sumV = 0.0_RKIND + sumUU = 0.0_RKIND + sumUV = 0.0_RKIND + sumVV = 0.0_RKIND + + ! more variables may need to be reset in the future + + end if + + end subroutine ocn_evaluate_particle_reset_condition!}}} + +!*********************************************************************** +! +! routine ocn_finalize_particle_reset_condition +! +!> \brief Finalize information for particle resets +!> \author Phillip Wolfram +!> \date 10/30/2015 +!> \details +!> Purpose: Finalize setup of particle resets +!> Input: domain +!----------------------------------------------------------------------- + subroutine ocn_finalize_particle_reset_condition(domain, err) !{{{ + + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + ! particle reset cleanup + + end subroutine ocn_finalize_particle_reset_condition!}}} + +end module ocn_lagrangian_particle_tracking_reset + diff --git a/src/core_ocean/analysis_members/mpas_ocn_layer_volume_weighted_averages.F b/src/core_ocean/analysis_members/mpas_ocn_layer_volume_weighted_averages.F index cf32b4b314..8c532b76a0 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_layer_volume_weighted_averages.F +++ b/src/core_ocean/analysis_members/mpas_ocn_layer_volume_weighted_averages.F @@ -56,6 +56,8 @@ module ocn_layer_volume_weighted_averages ! !-------------------------------------------------------------------- + logical :: computeActiveTracerBudgetsOn + !*********************************************************************** contains @@ -103,9 +105,15 @@ subroutine ocn_init_layer_volume_weighted_averages(domain, err)!{{{ ! !----------------------------------------------------------------- + logical, pointer :: config_compute_active_tracer_budgets + err = 0 - end subroutine ocn_init_layer_volume_weighted_averages!}}} + call mpas_pool_get_config(domain % configs,'config_compute_active_tracer_budgets', config_compute_active_tracer_budgets) + + computeActiveTracerBudgetsOn = config_compute_active_tracer_budgets + + end subroutine ocn_init_layer_volume_weighted_averages!}}} !*********************************************************************** ! @@ -160,6 +168,7 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ type (mpas_pool_type), pointer :: scratchPool type (mpas_pool_type), pointer :: diagnosticsPool type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: tracersPool real (kind=RKIND), dimension(:,:,:), pointer :: minValueWithinOceanLayerRegion real (kind=RKIND), dimension(:,:,:), pointer :: maxValueWithinOceanLayerRegion @@ -176,14 +185,20 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ real (kind=RKIND), dimension(:,:), pointer :: velocityZonal real (kind=RKIND), dimension(:,:), pointer :: velocityMeridional real (kind=RKIND), dimension(:,:), pointer :: vertVelocityTop - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + real (kind=RKIND),dimension(:,:,:),pointer::activeTracerVertMixTendency + real (kind=RKIND),dimension(:,:,:),pointer::activeTracerHorizontalAdvectionTendency + real (kind=RKIND),dimension(:,:,:),pointer::activeTracerVerticalAdvectionTendency + real (kind=RKIND),dimension(:,:,:),pointer::activeTracerSurfaceFluxTendency + real (kind=RKIND),dimension(:,:),pointer::temperatureShortWaveTendency + real (kind=RKIND),dimension(:,:,:),pointer::activeTracerNonLocalTendency real (kind=RKIND), dimension(:,:), pointer :: kineticEnergyCell real (kind=RKIND), dimension(:,:), pointer :: relativeVorticityCell real (kind=RKIND), dimension(:,:), pointer :: divergence ! pointers to data in mesh pool integer, pointer :: nVertLevels, nCells, nCellsSolve, nLayerVolWeightedAvgFields, nOceanRegionsTmp - integer, pointer :: indexTemperature, indexSalinity + integer, pointer :: index_temperature, index_salinity integer, dimension(:), pointer :: maxLevelCell real (kind=RKIND), dimension(:), pointer :: areaCell, lonCell, latCell @@ -197,9 +212,6 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ integer :: iDataField, nDefinedDataFields integer :: iCell, iLevel, iRegion, iTracer, err_tmp - ! package flag - logical, pointer :: layerVolumeWeightedAverageAMPKGActive - ! buffers data for message passaging integer :: kBuffer, kBufferLength real (kind=RKIND), dimension(:), allocatable :: workBufferSum, workBufferSumReduced @@ -238,9 +250,12 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'minValueWithinOceanLayerRegion', minValueWithinOceanLayerRegion) call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'maxValueWithinOceanLayerRegion', maxValueWithinOceanLayerRegion) call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'avgValueWithinOceanLayerRegion', avgValueWithinOceanLayerRegion) - call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'minValueWithinOceanVolumeRegion', minValueWithinOceanVolumeRegion) - call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'maxValueWithinOceanVolumeRegion', maxValueWithinOceanVolumeRegion) - call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'avgValueWithinOceanVolumeRegion', avgValueWithinOceanVolumeRegion) + call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'minValueWithinOceanVolumeRegion', & + minValueWithinOceanVolumeRegion) + call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'maxValueWithinOceanVolumeRegion', & + maxValueWithinOceanVolumeRegion) + call mpas_pool_get_array(layerVolumeWeightedAverageAMPool, 'avgValueWithinOceanVolumeRegion', & + avgValueWithinOceanVolumeRegion) ! loop over blocks ! NOTE: code is not valid for multiple blocks ! @@ -270,6 +285,7 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) ! get pointers to mesh call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) @@ -277,8 +293,8 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) call mpas_pool_get_dimension(block % dimensions, 'nLayerVolWeightedAvgFields', nLayerVolWeightedAvgFields) call mpas_pool_get_dimension(block % dimensions, 'nOceanRegionsTmp', nOceanRegionsTmp) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexSalinity) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'lonCell', lonCell) call mpas_pool_get_array(meshPool, 'latCell', latCell) @@ -300,10 +316,20 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ call mpas_pool_get_array(diagnosticsPool, 'velocityZonal', velocityZonal) call mpas_pool_get_array(diagnosticsPool, 'velocityMeridional', velocityMeridional) call mpas_pool_get_array(diagnosticsPool, 'vertVelocityTop', vertVelocityTop) - call mpas_pool_get_array(statePool, 'tracers', tracers, 1) call mpas_pool_get_array(diagnosticsPool, 'kineticEnergyCell', kineticEnergyCell) call mpas_pool_get_array(diagnosticsPool, 'relativeVorticityCell', relativeVorticityCell) call mpas_pool_get_array(diagnosticsPool, 'divergence', divergence) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + if ( computeActiveTracerBudgetsOn ) then + call mpas_pool_get_array(diagnosticsPool,'activeTracerHorizontalAdvectionTendency', & + activeTracerHorizontalAdvectionTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerVerticalAdvectionTendency', & + activeTracerVerticalAdvectionTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerVertMixTendency',activeTracerVertMixTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerSurfaceFluxTendency',activeTracerSurfaceFluxTendency) + call mpas_pool_get_array(diagnosticsPool,'temperatureShortWaveTendency',temperatureShortWaveTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerNonLocalTendency',activeTracerNonLocalTendency) + end if ! initialize buffers workBufferSum(:) = 0.0_RKIND @@ -320,7 +346,7 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ call compute_mask(iLevel, maxLevelCell, nCells, nCellsSolve, iRegion, lonCell, latCell, workMask) ! copy data into work array - workArray( :,:) = 0.0 + workArray( :,:) = 0.0_RKIND workArray( 1,:) = workMask(:) workArray( 2,:) = areaCell(:) workArray( 3,:) = layerThickness(iLevel,:) @@ -330,12 +356,37 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ workArray( 7,:) = velocityZonal(iLevel,:) workArray( 8,:) = velocityMeridional(iLevel,:) workArray( 9,:) = vertVelocityTop(iLevel,:) - workArray(10,:) = tracers(indexTemperature,iLevel,:) - workArray(11,:) = tracers(indexSalinity,iLevel,:) + if ( associated(activeTracers) ) workArray(10,:) = activeTracers(index_temperature,iLevel,:) + if ( associated(activeTracers) ) workArray(11,:) = activeTracers(index_salinity,iLevel,:) workArray(12,:) = kineticEnergyCell(iLevel,:) workArray(13,:) = relativeVorticityCell(iLevel,:) workArray(14,:) = divergence(iLevel,:) workArray(15,:) = relativeVorticityCell(iLevel,:)*relativeVorticityCell(iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(16,:) = & + activeTracerHorizontalAdvectionTendency(index_temperature,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(17,:) = & + activeTracerHorizontalAdvectionTendency(index_salinity,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(18,:) = & + activeTracerVerticalAdvectionTendency(index_temperature,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(19,:) = & + activeTracerVerticalAdvectionTendency(index_salinity,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(20,:) = & + activeTracerSurfaceFluxTendency(index_temperature,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(21,:) = & + activeTracerSurfaceFluxTendency(index_salinity,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(22,:) = & + temperatureShortWaveTendency(iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(23,:) = & + activeTracerNonLocalTendency(index_temperature,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(24,:) = & + activeTracerNonLocalTendency(index_salinity,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(25,:) = & + activeTracerVertMixTendency(index_temperature,iLevel,:) + if ( associated(activeTracers) .and. computeActiveTracerBudgetsOn ) workArray(26,:) = & + activeTracerVertMixTendency(index_salinity,iLevel,:) + + + call compute_statistics(nDefinedDataFields, nCellsSolve, workArray, workMask, workMin, workMax, workSum) @@ -387,16 +438,20 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ do iRegion=1,nOceanRegionsTmp do iDataField=1,nDefinedDataFields do iLevel=1,nVertLevels - avgValueWithinOceanVolumeRegion(iDataField, iRegion) = avgValueWithinOceanVolumeRegion(iDataField, iRegion) + avgValueWithinOceanLayerRegion(iDataField,iLevel,iRegion) + avgValueWithinOceanVolumeRegion(iDataField, iRegion) = avgValueWithinOceanVolumeRegion(iDataField, iRegion) & + + avgValueWithinOceanLayerRegion(iDataField,iLevel,iRegion) enddo enddo do iDataField=4,nDefinedDataFields - avgValueWithinOceanVolumeRegion(iDataField, iRegion) = avgValueWithinOceanVolumeRegion(iDataField, iRegion) / max(avgValueWithinOceanVolumeRegion(3,iRegion),1.0e-8_RKIND) + avgValueWithinOceanVolumeRegion(iDataField, iRegion) = avgValueWithinOceanVolumeRegion(iDataField, iRegion) & + / max(avgValueWithinOceanVolumeRegion(3,iRegion),1.0e-8_RKIND) enddo ! normalize total region volume by total volume cell area - avgValueWithinOceanVolumeRegion(3,iRegion) = avgValueWithinOceanVolumeRegion(3,iRegion) / max(avgValueWithinOceanVolumeRegion(2,iRegion),1.0e-8_RKIND) + avgValueWithinOceanVolumeRegion(3,iRegion) = avgValueWithinOceanVolumeRegion(3,iRegion) & + / max(avgValueWithinOceanVolumeRegion(2,iRegion),1.0e-8_RKIND) ! normalize total volume cell area by total number of cells - avgValueWithinOceanVolumeRegion(2,iRegion) = avgValueWithinOceanVolumeRegion(2,iRegion) / max(avgValueWithinOceanVolumeRegion(1,iRegion),1.0e-8_RKIND) + avgValueWithinOceanVolumeRegion(2,iRegion) = avgValueWithinOceanVolumeRegion(2,iRegion) & + / max(avgValueWithinOceanVolumeRegion(1,iRegion),1.0e-8_RKIND) enddo ! find min/max with region volume @@ -412,12 +467,15 @@ subroutine ocn_compute_layer_volume_weighted_averages(domain, timeLevel, err)!{{ do iLevel=1,nVertLevels ! normalize all field by total volume in each layer do iDataField=4,nDefinedDataFields - avgValueWithinOceanLayerRegion(iDataField,iLevel,iRegion) = avgValueWithinOceanLayerRegion(iDataField,iLevel,iRegion) / max(avgValueWithinOceanLayerRegion(3,iLevel,iRegion),1.0e-8_RKIND) + avgValueWithinOceanLayerRegion(iDataField,iLevel,iRegion) = avgValueWithinOceanLayerRegion(iDataField,iLevel,iRegion) & + / max(avgValueWithinOceanLayerRegion(3,iLevel,iRegion),1.0e-8_RKIND) enddo ! normalize total layer volume by layer area - avgValueWithinOceanLayerRegion(3,iLevel,iRegion) = avgValueWithinOceanLayerRegion(3,iLevel,iRegion) / max(avgValueWithinOceanLayerRegion(2,iLevel,iRegion),1.0e-8_RKIND) + avgValueWithinOceanLayerRegion(3,iLevel,iRegion) = avgValueWithinOceanLayerRegion(3,iLevel,iRegion) & + / max(avgValueWithinOceanLayerRegion(2,iLevel,iRegion),1.0e-8_RKIND) ! normalize total layer area by number of cells in region - avgValueWithinOceanLayerRegion(2,iLevel,iRegion) = avgValueWithinOceanLayerRegion(2,iLevel,iRegion) / max(avgValueWithinOceanLayerRegion(1,iLevel,iRegion),1.0e-8_RKIND) + avgValueWithinOceanLayerRegion(2,iLevel,iRegion) = avgValueWithinOceanLayerRegion(2,iLevel,iRegion) & + / max(avgValueWithinOceanLayerRegion(1,iLevel,iRegion),1.0e-8_RKIND) enddo enddo @@ -441,7 +499,7 @@ subroutine compute_mask(iLevel, maxLevelCell, nCells, nCellsSolve, iRegion, lonC integer :: iCell real(kind=RKIND) :: dtr - dtr = 4.0_RKIND*atan(1.0_RKIND) / 180.0_RKIND + dtr = 4.0_RKIND*atan(1.0_RKIND) / 180.0_RKIND workMask(:) = 0.0_RKIND do iCell=1,nCellsSolve if(iLevel.le.maxLevelCell(iCell)) workMask(iCell) = 1.0_RKIND @@ -505,7 +563,7 @@ subroutine compute_statistics(nDefinedDataFields, nCellsSolve, workArray, workMa integer :: iCell, iDataField real(kind=RKIND) :: cellMask, cellArea, cellVolume - workSum = 0.0 + workSum = 0.0_RKIND do iCell=1,nCellsSolve cellMask = workMask(iCell) ! mask cellArea = cellMask * workArray(2,iCell) ! area diff --git a/src/core_ocean/analysis_members/mpas_ocn_meridional_heat_transport.F b/src/core_ocean/analysis_members/mpas_ocn_meridional_heat_transport.F index 64bc16b483..a48925bd64 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_meridional_heat_transport.F +++ b/src/core_ocean/analysis_members/mpas_ocn_meridional_heat_transport.F @@ -116,13 +116,79 @@ subroutine ocn_init_meridional_heat_transport(domain, err)!{{{ real (kind=RKIND) :: binWidth ! These are array size 1 because mpas_dmpar_min_real_array calls require arrays. real (kind=RKIND), dimension(1) :: minBin, maxBin, minBinDomain, maxBinDomain + ! the variable used to discriminate cells into Bins (either the y-value or the latitude) real (kind=RKIND), dimension(:), pointer :: binBoundaryMerHeatTrans, binVariable + !number of latitude bins specified in the config integer, pointer :: config_AM_meridionalHeatTransport_num_bins + !smallest and highest latitude specified in the config real (kind=RKIND), pointer :: config_AM_meridionalHeatTransport_min_bin, config_AM_meridionalHeatTransport_max_bin + !determines if the simulation was run on a sphere or on a plane logical, pointer :: on_a_sphere + !!!! Region variables + !! region MHT calculation variables + real (kind=RKIND) :: maskFactor + integer :: curRegion, i, j, iCell + + !! region arrays/variables + character (len=STRKIND), dimension(:), pointer :: regionGroupNames + integer, dimension(:, :), pointer :: regionCellMasks, regionsInGroup + integer, dimension(:), pointer :: nRegionsInGroup + integer, pointer :: nRegions, nRegionGroups, maxRegionsInGroup, nCellsSolve + real (kind=RKIND), dimension(:), pointer :: minLatRegionLocal, maxLatRegionLocal, & + minLatRegionGlobal, maxLatRegionGlobal + character (len=STRKIND), pointer :: additionalRegion + + !! region preliminary variables + integer :: regionGroupNumber, regionsInAddGroup, regionGroupOffset + + !!region pool + type (mpas_pool_type), pointer :: regionPool + + !! region dimensions + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegions', nRegions) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegionGroups', nRegionGroups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'maxRegionsInGroup', maxRegionsInGroup) + + !! region config for MHT + call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_region_group', & + additionalRegion) + + !! get region values + call mpas_pool_get_subpool(domain % blocklist % structs, 'regions', regionPool) + call mpas_pool_get_array(regionPool, 'regionsInGroup', regionsInGroup) + call mpas_pool_get_array(regionPool, 'nRegionsInGroup', nRegionsInGroup) + call mpas_pool_get_array(regionPool, 'regionGroupNames', regionGroupNames) + + regionGroupOffset = 1 + regionGroupNumber = 0 + + if(additionalRegion /= '') then + !!! region preliminaries + !!! figure out the region group number that matches the configured additional region's name + do i = 1, nRegionGroups + if (regionGroupNames(i) .eq. additionalRegion) then + regionGroupNumber = i + !!! determine offset to compensate for several region groups in the + !!! regions file + do j = 1, i - 1 + regionGroupOffset = regionGroupOffset + nRegionsInGroup(j) + end do + end if + end do + end if + + if(additionalRegion /= '') then + regionsInAddGroup = nRegionsInGroup(regionGroupNumber) + allocate(minLatRegionLocal(maxRegionsInGroup)) + allocate(maxLatRegionLocal(maxRegionsInGroup)) + allocate(minLatRegionGlobal(maxRegionsInGroup)) + allocate(maxLatRegionGlobal(maxRegionsInGroup)) + end if + !!!! END Region variables + dminfo = domain % dminfo err = 0 @@ -133,9 +199,12 @@ subroutine ocn_init_meridional_heat_transport(domain, err)!{{{ call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nMerHeatTransBins', nMerHeatTransBins) call mpas_pool_get_subpool(domain % blocklist % structs, 'meridionalHeatTransportAM', meridionalHeatTransportAMPool) - call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_num_bins', config_AM_meridionalHeatTransport_num_bins) - call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_min_bin', config_AM_meridionalHeatTransport_min_bin) - call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_max_bin', config_AM_meridionalHeatTransport_max_bin) + call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_num_bins', & + config_AM_meridionalHeatTransport_num_bins) + call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_min_bin', & + config_AM_meridionalHeatTransport_min_bin) + call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_max_bin', & + config_AM_meridionalHeatTransport_max_bin) nMerHeatTransBinsUsed = config_AM_meridionalHeatTransport_num_bins @@ -147,6 +216,12 @@ subroutine ocn_init_meridional_heat_transport(domain, err)!{{{ call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + ! Get region-specific variables from pools + if(additionalRegion /= '') then + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(regionPool, 'regionCellMasks', regionCellMasks) + end if + ! Bin by latitude on a sphere, by yCell otherwise. if (on_a_sphere) then call mpas_pool_get_array(meshPool, 'latCell', binVariable) @@ -157,13 +232,25 @@ subroutine ocn_init_meridional_heat_transport(domain, err)!{{{ minBin = min(minBin, minval(binVariable) ) maxBin = max(maxBin, maxval(binVariable) ) + ! If using regions, iterate through groups and add as needed + if(additionalRegion /= '') then + do i = 1, regionsInAddGroup + curRegion = regionsInGroup(i, regionGroupNumber) + do iCell = 1, nCellsSolve + if (regionCellMasks(curRegion, iCell) .eq. 1) then + minLatRegionLocal(i) = min(minLatRegionLocal(i), binVariable(iCell)) + maxLatRegionLocal(i) = max(maxLatRegionLocal(i), binVariable(iCell)) + end if + end do + end do + end if block => block % next end do call mpas_dmpar_min_real_array(dminfo, 1, minBin, minBinDomain) call mpas_dmpar_max_real_array(dminfo, 1, maxBin, maxBinDomain) - ! Set up bins. + ! Set up bins. binBoundaryMerHeatTrans = -1.0e34_RKIND ! Change min and max bin bounds to configuration settings, if applicable. @@ -240,9 +327,12 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ type (block_type), pointer :: block type (mpas_pool_type), pointer :: meridionalHeatTransportAMPool type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: scratchPool type (mpas_pool_type), pointer :: diagnosticsPool + ! REGION POOL + type (mpas_pool_type), pointer :: regionPool integer :: iTracer, k, iCell, kMax, i, iEdge integer :: iBin, iField, nMerHeatTransVariables @@ -252,15 +342,31 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ real (kind=RKIND) :: div_huT real (kind=RKIND), dimension(:), pointer :: areaCell, binVariable, binBoundaryMerHeatTrans, dvEdge - real (kind=RKIND), dimension(:), pointer :: meridionalHeatTransportLat + real (kind=RKIND), dimension(:), pointer :: merHeatTransLat real (kind=RKIND), dimension(:,:), pointer :: layerThicknessEdge, normalTransportVelocity - real (kind=RKIND), dimension(:,:), pointer :: meridionalHeatTransportLatZ - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:), pointer :: merHeatTransLatZ + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers real (kind=RKIND), dimension(:,:), allocatable :: mht_meridional_integral real (kind=RKIND), dimension(:,:,:), allocatable :: sumMerHeatTrans, totalSumMerHeatTrans logical, pointer :: on_a_sphere + !!!! REGION VARIABLES + real (kind=RKIND) :: maskFactor + integer :: curRegion, regionGroupOffset, j + character (len=STRKIND) :: currentName + real (kind=RKIND), dimension(:,:,:,:), allocatable :: sumMerHeatTransRegion, totalSumMerHeatTransRegion + real (kind=RKIND), dimension(:,:,:), pointer :: merHeatTransLatZRegion + real (kind=RKIND), dimension(:,:), pointer :: merHeatTransLatRegion + character (len=STRKIND), dimension(:), pointer :: regionNames, regionGroupNames + integer, dimension(:, :), pointer :: regionCellMasks, regionVertexMasks, regionsInGroup + integer, dimension(:), pointer :: nRegionsInGroup + integer, pointer :: nRegions, nRegionGroups, maxRegionsInGroup + character (len=STRKIND), pointer :: additionalRegion + integer :: regionGroupNumber, regionsInAddGroup + real (kind=RKIND), dimension(:,:,:), allocatable :: mht_meridional_integral_region + !!!! END REGION VARIABLES + err = 0 dminfo = domain % dminfo @@ -275,11 +381,67 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ call mpas_pool_get_array(meridionalHeatTransportAMPool, 'binBoundaryMerHeatTrans', binBoundaryMerHeatTrans) + !!!! v + !! region dimensions + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegions', nRegions) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegionGroups', nRegionGroups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'maxRegionsInGroup', maxRegionsInGroup) + + !! region config for MHT + call mpas_pool_get_config(domain % configs, 'config_AM_meridionalHeatTransport_region_group', & + additionalRegion) + + !! get region values + call mpas_pool_get_subpool(domain % blocklist % structs, 'regions', regionPool) + call mpas_pool_get_array(regionPool, 'regionsInGroup', regionsInGroup) + call mpas_pool_get_array(regionPool, 'nRegionsInGroup', nRegionsInGroup) + call mpas_pool_get_array(regionPool, 'regionNames', regionNames) + call mpas_pool_get_array(regionPool, 'regionGroupNames', regionGroupNames) + call mpas_pool_get_array(meridionalHeatTransportAMPool, 'merHeatTransLatRegion', merHeatTransLatRegion) + call mpas_pool_get_array(meridionalHeatTransportAMPool, 'merHeatTransLatZRegion', merHeatTransLatZRegion) + + regionGroupOffset = 1 + regionGroupNumber = 0 + + !! if a region is selected, set up region group offsets etc + if(additionalRegion /= '') then + !!! region preliminaries + !!! figure out the region group number that matches the configured additional region's name + do i = 1, nRegionGroups + if (regionGroupNames(i) .eq. additionalRegion) then + regionGroupNumber = i + !!! determine offset to compensate for several region groups in the + !!! regions file + do j = 1, i - 1 + regionGroupOffset = regionGroupOffset + nRegionsInGroup(j) + end do + end if + end do + end if + + if(additionalRegion /= '') then + regionsInAddGroup = nRegionsInGroup(regionGroupNumber) + end if + !!!! end region initialization + + !! allocate MHT calculation arrays allocate(sumMerHeatTrans(nMerHeatTransVariables,nVertLevels,nMerHeatTransBinsUsed)) allocate(totalSumMerHeatTrans(nMerHeatTransVariables,nVertLevels,nMerHeatTransBinsUsed)) allocate(mht_meridional_integral(nVertLevels,nMerHeatTransBinsUsed)) + !! allocate region-specific arrays + if(additionalRegion /= '') then +! allocate(totalSumMerHeatTransLatZRegion(nVertLevels, nMerHeatTransBinsUsed + 1, maxRegionsInGroup)) + allocate(sumMerHeatTransRegion(nMerHeatTransVariables, nVertLevels, nMerHeatTransBinsUsed, maxRegionsInGroup)) + allocate(totalSumMerHeatTransRegion(nMerHeatTransVariables, nVertLevels, nMerHeatTransBinsUsed, maxRegionsInGroup)) + allocate(mht_meridional_integral_region(nVertLevels,nMerHeatTransBinsUsed, maxRegionsInGroup)) + end if + sumMerHeatTrans = 0.0_RKIND + if(additionalRegion /= '') then + sumMerHeatTransRegion = 0.0_RKIND + totalSumMerHeatTrans = 0.0_RKIND + end if block => domain % blocklist do while (associated(block)) @@ -287,9 +449,10 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) @@ -300,10 +463,14 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + if(additionalRegion /= '') then + call mpas_pool_get_array(regionPool, 'regionCellMasks', regionCellMasks) + end if + ! Bin by latitude on a sphere, by yCell otherwise. if (on_a_sphere) then call mpas_pool_get_array(meshPool, 'latCell', binVariable) @@ -317,11 +484,11 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ if (binVariable(iCell) .lt. binBoundaryMerHeatTrans(1)) cycle do iBin = 1, nMerHeatTransBinsUsed - if (binVariable(iCell) .lt. binBoundaryMerHeatTrans(iBin+1) ) then + if (binVariable(iCell) .lt. binBoundaryMerHeatTrans(iBin+1)) then do k = 1, kMax - ! Compute divergence of huT, i.e. layerThicknessEdge * normalVelocity * temperature, at an edge + ! Compute divergence of huT, i.e. layerThicknessEdge * normalTransportVelocity * temperature, at an edge ! for meridional heat transport. Here we use a centered difference to compute the temperature at ! the edge, which is an approximation to the actual edge temperature used in the horizontal ! advection scheme (for example, FCT). We expect that the error in this approximation is small. @@ -332,17 +499,26 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) div_huT = div_huT - layerThicknessEdge(k, iEdge) * normalTransportVelocity(k, iEdge) & - * 0.5_RKIND * (tracers(indexTemperature,k,cellsOnEdge(1,iEdge)) + tracers(indexTemperature,k,cellsOnEdge(2,iEdge))) & + * 0.5_RKIND * (activeTracers(indexTemperature,k,cellsOnEdge(1,iEdge)) & + + activeTracers(indexTemperature,k,cellsOnEdge(2,iEdge))) & * edgeSignOnCell(i, iCell) * dvEdge(iEdge) end do sumMerHeatTrans(iField,k,iBin) = sumMerHeatTrans(iField,k,iBin) + div_huT + !!!!! region-specific MHT + if(additionalRegion /= '') then + do i = 1, regionsInAddGroup + curRegion = regionsInGroup(i, regionGroupNumber) + sumMerHeatTransRegion(iField,k,iBin,curRegion) = sumMerHeatTrans(iField,k,iBin) * & + regionCellMasks(curRegion, iCell) + end do + !!!!! end region-specific MHT + end if + end do exit - endif end do - end do block => block % next @@ -351,7 +527,18 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ ! mpi summation over all processors ! Note the input and output arrays are of the same dimension, so summation is ! over the domain decompositon (by processor), not over an array index. - call mpas_dmpar_sum_real_array(dminfo, nVertLevels*nMerHeatTransBinsUsed*nMerHeatTransVariables, sumMerHeatTrans, totalSumMerHeatTrans) + call mpas_dmpar_sum_real_array(dminfo, nVertLevels*nMerHeatTransBinsUsed*nMerHeatTransVariables, & + sumMerHeatTrans, totalSumMerHeatTrans) + + !!!! Region version + if(additionalRegion /= '') then + do i = 1, regionsInAddGroup + curRegion = regionsInGroup(i, regionGroupNumber) + call mpas_dmpar_sum_real_array(dminfo, nVertLevels*nMerHeatTransBinsUsed*nMerHeatTransVariables, & + sumMerHeatTransRegion(:,:,:,curRegion), & + totalSumMerHeatTransRegion(:,:,:,curRegion)) + end do + end if ! Even though these variables do not include an index that is decomposed amongst ! domain partitions, we assign them within a block loop so that all blocks have the @@ -364,8 +551,13 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ call mpas_pool_get_subpool(block % structs, 'meridionalHeatTransportAM', meridionalHeatTransportAMPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_array(meridionalHeatTransportAMPool, 'meridionalHeatTransportLat', meridionalHeatTransportLat) - call mpas_pool_get_array(meridionalHeatTransportAMPool, 'meridionalHeatTransportLatZ', meridionalHeatTransportLatZ) + call mpas_pool_get_array(meridionalHeatTransportAMPool, 'meridionalHeatTransportLat', merHeatTransLat) + call mpas_pool_get_array(meridionalHeatTransportAMPool, 'meridionalHeatTransportLatZ', merHeatTransLatZ) + + if(additionalRegion /= '') then + call mpas_pool_get_array(meridionalHeatTransportAMPool, 'merHeatTransLatRegion', merHeatTransLatRegion) + call mpas_pool_get_array(meridionalHeatTransportAMPool, 'merHeatTransLatZRegion', merHeatTransLatZRegion) + end if do iBin = 1, nMerHeatTransBinsUsed do k = 1, nVertLevels @@ -375,33 +567,62 @@ subroutine ocn_compute_meridional_heat_transport(domain, timeLevel, err)!{{{ ! Here we simply multiply by (rho c_p) and convert to PW: iField = 1 mht_meridional_integral(k,iBin) = totalSumMerHeatTrans(iField,k,iBin)*rho_sw*cp_sw*1.0e-15_RKIND - + ! compute MHT per bin for regions + if(additionalRegion /= '') then + do i = 1, regionsInAddGroup + curRegion = regionsInGroup(i, regionGroupNumber) + mht_meridional_integral_region(k,iBin,curRegion) = totalSumMerHeatTransRegion(iField,k,iBin,curRegion) & + * rho_sw*cp_sw*1.0e-15_RKIND + end do + end if end do end do ! Compute integral of ( sum ( div(huT) A ) * rho c_p ) from southernmost latitude to bin boundary. ! Note that mht_meridional_integral is indexed by bin, spanning 1:nMerHeatTransBinsUsed, while - ! meridionalHeatTransportLatZ (second dimension) is indexed by bin boundary, spanning 1:nMerHeatTransBinsUsed+1 - meridionalHeatTransportLatZ(:,1) = 0.0_RKIND + ! merHeatTransLatZ (second dimension) is indexed by bin boundary, spanning 1:nMerHeatTransBinsUsed+1 + merHeatTransLatZ(:,1) = 0.0_RKIND do iBin = 2, nMerHeatTransBinsUsed+1 - meridionalHeatTransportLatZ(:,iBin) = meridionalHeatTransportLatZ(:,iBin-1) + mht_meridional_integral(:,iBin-1) + merHeatTransLatZ(:,iBin) = merHeatTransLatZ(:,iBin-1) + mht_meridional_integral(:,iBin-1) + ! integrate MHT for regions + if(additionalRegion /= '') then + do i = 1, regionsInAddGroup + curRegion = regionsInGroup(i, regionGroupNumber) + merHeatTransLatZRegion(:,iBin,curRegion) = merHeatTransLatZRegion(:,iBin-1,curRegion) +& + mht_meridional_integral_region(:,iBin-1,curRegion) + end do + end if end do - ! meridionalHeatTransportLatZ is a function of depth. Sum in vertical to get - ! meridionalHeatTransportLat, a single value for each latitude bin boundary. - ! meridionalHeatTransportLat is indexed by bin boundary, spanning 1:nMerHeatTransBinsUsed+1 + ! merHeatTransLatZ is a function of depth. Sum in vertical to get + ! merHeatTransLat, a single value for each latitude bin boundary. + ! merHeatTransLat is indexed by bin boundary, spanning 1:nMerHeatTransBinsUsed+1 do iBin = 1, nMerHeatTransBinsUsed+1 - meridionalHeatTransportLat(iBin) = sum(meridionalHeatTransportLatZ(:,iBin)) + merHeatTransLat(iBin) = sum(merHeatTransLatZ(:,iBin)) + ! sum up MHT for regions + if(additionalRegion /= '') then + do i = 1, regionsInAddGroup + curRegion = regionsInGroup(i, regionGroupNumber) + merHeatTransLatRegion(iBin,curRegion) = sum(merHeatTransLatZRegion(:,iBin,curRegion)) + end do + end if end do - block => block % next end do + call mpas_dmpar_sum_real_array(dminfo, nVertLevels*nMerHeatTransBinsUsed*nMerHeatTransVariables, & + sumMerHeatTrans, totalSumMerHeatTrans) + deallocate(sumMerHeatTrans) deallocate(totalSumMerHeatTrans) deallocate(mht_meridional_integral) + !!!! region clean-up + if(additionalRegion /= '') then + deallocate(totalSumMerHeatTransRegion) + deallocate(sumMerHeatTransRegion) + end if end subroutine ocn_compute_meridional_heat_transport!}}} !*********************************************************************** diff --git a/src/core_ocean/analysis_members/mpas_ocn_mixed_layer_depths.F b/src/core_ocean/analysis_members/mpas_ocn_mixed_layer_depths.F new file mode 100644 index 0000000000..11be7b7c46 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_mixed_layer_depths.F @@ -0,0 +1,657 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_mixed_layer_depths +! +!> \brief MPAS ocean analysis mode member: mixed_layer_depths +!> \author Luke Van Roekel +!> \date August 2015 +!> \details +!> MPAS ocean analysis mode member: mixed_layer_depths +!> +! Computes mixed layer depths via a gradient method and threshold method +! may add more methods from Holte and Talley (2009) at a future time +!----------------------------------------------------------------------- + +module ocn_mixed_layer_depths + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_mixed_layer_depths, & + ocn_compute_mixed_layer_depths, & + ocn_restart_mixed_layer_depths, & + ocn_finalize_mixed_layer_depths + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_mixed_layer_depths +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Luke Van Roekel +!> \date August 2015 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_mixed_layer_depths(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_init_mixed_layer_depths!}}} + +!*********************************************************************** +! +! routine ocn_compute_mixed_layer_depths +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Luke Van Roekel +!> \date August 2015 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_mixed_layer_depths(domain, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: mixedLayerDepthsAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: mixedLayerDepthsAM + + ! Here are some example variables which may be needed for your analysis member + integer, pointer :: nVertLevels, nCellsSolve + integer :: k, iCell, i, refIndex, refLevel(1) + integer, pointer :: index_temperature + integer, dimension(:), pointer :: maxLevelCell + + logical :: found_temp_mld, found_den_mld + logical,pointer :: tThresholdFlag, dThresholdFlag + logical,pointer :: tGradientFlag, dGradientFlag +! real (kind=RKIND), dimension(:), pointer :: areaCell + real (kind=RKIND), dimension(:), pointer :: tThresholdMLD, tGradientMLD + real (kind=RKIND), dimension(:), pointer :: dThresholdMLD, dGradientMLD + real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:), pointer :: zTop, zMid, pressure + real (kind=RKIND), dimension(:,:), pointer :: potentialDensity + real (kind=RKIND), pointer :: tempThresh + real (kind=RKIND), pointer :: tempGrad + real (kind=RKIND), pointer :: denThresh + real (kind=RKIND), pointer :: denGrad + integer, pointer :: interp_type + integer :: interp_local + real (kind=RKIND), pointer :: refPress + real (kind=RKIND), allocatable, dimension(:,:) :: densityGradient, temperatureGradient + real (kind=RKIND) :: mldTemp,dTempThres, dDenThres, dTempGrad, dDenGrad + real (kind=RKIND) :: dz,temp_ref_lev, den_ref_lev, dV, dVm1, dVp1, localVals(6) + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell + err = 0 + + dminfo = domain % dminfo + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mixedLayerDepthsAM', mixedLayerDepthsAMPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_Tthreshold', tThresholdFlag) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_Dthreshold', dThresholdFlag) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_Tgradient', tGradientFlag) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_Dgradient', dGradientFlag) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_crit_temp_threshold', tempThresh) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_crit_dens_threshold', denThresh) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_temp_gradient_threshold', tempGrad) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_den_gradient_threshold', denGrad) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_interp_method', interp_type) + call mpas_pool_get_config(domain % configs, 'config_AM_mixedLayerDepths_reference_pressure', refPress) + + if (interp_type == 1) interp_local = 1 + if (interp_type == 2) interp_local = 2 + if (interp_type == 3) interp_local = 3 + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'mixedLayerDepthsAM', mixedLayerDepthsAMPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(tracersPool, 'activeTracers', tracers, timeLevel) + call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', & + potentialDensity) + call mpas_pool_get_array(diagnosticsPool, 'pressure', pressure) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(diagnosticsPool, 'zTop', zTop) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + + + if(tThresholdFlag) then + call mpas_pool_get_array(mixedLayerDepthsAMPool, 'tThreshMLD',tThresholdMLD) + do iCell = 1,nCellsSolve + + !Initialize RefIndex for cases of very shallow columns + refIndex = maxLevelCell(iCell) + + found_temp_mld = .false. + + do k=1, maxLevelCell(iCell)-1 + if(pressure(k+1,iCell) > refPress) then + localvals(2:3)=tracers(index_temperature,k:k+1,iCell) + localvals(5:6)=pressure(k:k+1,iCell) + + call interp_bw_levels(localVals(2),localVals(3), & + localVals(5),localVals(6),refPress,interp_local, & + temp_ref_lev) + refIndex=k + exit + endif + enddo + + do k=refIndex,maxLevelCell(iCell)-1 + + if(.not. found_temp_mld .and. abs(tracers(index_temperature,k+1,iCell) - temp_ref_lev) .ge. tempThresh) then + dVp1 = abs(tracers(index_temperature,k+1,iCell) - temp_ref_lev) + dV = abs(tracers(index_temperature,k ,iCell) - temp_ref_lev) + + localVals(2:3)=zMid(k:k+1,iCell) + call interp_bw_levels(localVals(2),localVals(3), dV, dVp1, tempThresh, & + interp_local, mldTemp)!,dVm1, localVals(1)) + mldTemp=max(mldTemp,zMid(k+1,iCell)) !make sure MLD isn't deeper than zMid(k+1) + tThresholdMLD(iCell)=abs(min(mldTemp,zMid(k,iCell))) !MLD should be deeper than zMid(k) + found_temp_mld = .true. + exit + endif + enddo + +! if the mixed layer depth is not found, it is set to the depth of the bottom most level + if(.not. found_temp_mld) tThresholdMLD(iCell) = abs(zMid(maxLevelCell(iCell),iCell)) + enddo !iCell + endif !end tThresh MLD search + + if(dThresholdFlag) then + call mpas_pool_get_array(mixedLayerDepthsAMPool, 'dThreshMLD',dThresholdMLD) + + do iCell = 1,nCellsSolve + + !Initialize RefIndex for cases of very shallow columns + refIndex = maxLevelCell(iCell) + found_den_mld = .false. + + do k=1, maxLevelCell(iCell)-1 + if(pressure(k+1,iCell) > refPress) then + localvals(2:3)=potentialDensity(k:k+1,iCell) + localvals(5:6)=pressure(k:k+1,iCell) + + call interp_bw_levels(localVals(2),localVals(3), & + localVals(5),localVals(6),refPress,interp_local, & + den_ref_lev) + refIndex=k + exit + endif + enddo + + do k=refIndex,maxLevelCell(iCell)-1 + + if(.not. found_den_mld .and. abs(potentialDensity(k+1,iCell) - den_ref_lev) .ge. denThresh) then + dVp1 = abs(potentialDensity(k+1,iCell) - den_ref_lev) + dV = abs(potentialDensity(k ,iCell) - den_ref_lev) + localVals(2:3)=zMid(k:k+1,iCell) + call interp_bw_levels(localVals(2),localVals(3), dV, dVp1, denThresh, & + interp_local, mldTemp)!, dVm1, localVals(1)) + mldTemp=max(mldTemp,zMid(k+1,iCell)) !make sure MLD isn't deeper than zMid(k+1) + dThresholdMLD(iCell)=abs(min(mldTemp,zMid(k,iCell))) !MLD should be deeper than zMid(k) + found_den_mld = .true. + exit + endif + enddo + +! if the mixed layer depth is not found, it is set to the depth of the bottom most level + + if(.not. found_den_mld) dThresholdMLD(iCell) = abs(zMid(maxLevelCell(iCell),iCell)) + + enddo !iCell + + endif !end dThresh MLD search + + +! Compute the mixed layer depth based on a gradient threshold in temperature and density + if(tGradientFlag) then + call mpas_pool_get_array(mixedLayerDepthsAMPool, 'tGradMLD',tGradientMLD) + + allocate(temperatureGradient(nVertLevels,2)) + + do iCell = 1,nCellsSolve + + temperatureGradient(:,1) = 0.0_RKIND + temperatureGradient(1,2) = 1 + + found_temp_mld=.false. + + do k=2,maxLevelCell(iCell)-1 + dz=abs(pressure(k-1,iCell)-pressure(k,iCell)) + temperatureGradient(k,1) = abs(tracers(index_temperature,k-1,iCell) - tracers(index_temperature,k,iCell)) / dz + temperatureGradient(k,2) = k + enddo + +! smooth the gradients to eliminate reduce single point maxima + + do k=2,maxLevelCell(iCell)-1 + temperatureGradient(k,1) = (temperatureGradient(k-1,1) + temperatureGradient(k,1) & + + temperatureGradient(k+1,1)) / float(3) + enddo + + + do k=2, maxLevelCell(iCell)-1 + if(.not. found_temp_mld .and. temperatureGradient(k+1,1) .ge. tempGrad) then + call interp_bw_levels(zTop(k,iCell),zTop(k+1,iCell),temperatureGradient(k,1),temperatureGradient(k+1,1), & + tempGrad, interp_local,mldTemp,temperatureGradient(k-1,1),zTop(k-1,iCell)) + + mldTemp=max(mldTemp,zTop(k+1,iCell)) !make sure MLD isn't deeper than zMid(k+1) + tGradientMLD(iCell)=abs(min(mldTemp,zTop(k,iCell))) !MLD should be deeper than zMid(k) + + found_temp_mld=.true. + exit + endif + + enddo !maxLevelCell + + if(.not. found_temp_mld) then + refLevel=maxloc(temperatureGradient(:,1)) + tGradientMLD(iCell) = abs(zTop(refLevel(1),iCell)) + endif + + enddo !icell + + deallocate(temperatureGradient) + + endif !if(temperaturegradientflag) + + if(dGradientFlag) then + call mpas_pool_get_array(mixedLayerDepthsAMPool, 'dGradMLD',dGradientMLD) + + allocate(densityGradient(nVertLevels,2)) + + do iCell = 1,nCellsSolve + + densityGradient(:,:)=0.0_RKIND + densityGradient(1,2) = 1 + + found_den_mld=.false. + + do k=2,maxLevelCell(iCell)-1 + dz=abs(pressure(k-1,iCell)-pressure(k,iCell)) + densityGradient(k,1) = abs(potentialDensity(k-1,iCell)-potentialDensity(k,iCell)) / dz + densityGradient(k,2) = k + enddo + +! smooth the gradients to eliminate reduce single point maxima + + do k=2,maxLevelCell(iCell)-1 + densityGradient(k,1) = (densityGradient(k-1,1) + densityGradient(k,1) + densityGradient(k+1,1)) / float(3) + enddo + + + do k=2, maxLevelCell(iCell)-1 + if(.not. found_den_mld .and. densityGradient(k+1,1) .ge. denGrad) then + call interp_bw_levels(zTop(k,iCell),zTop(k+1,iCell),densityGradient(k,1),densityGradient(k+1,1), & + denGrad, interp_local,mldTemp,densityGradient(k-1,1),zTop(k-1,iCell)) + mldTemp=max(mldTemp,zTop(k+1,iCell)) !make sure MLD isn't deeper than zMid(k+1) + dGradientMLD(iCell)=abs(min(mldTemp,zTop(k,iCell))) !MLD should be deeper than zMid(k) + found_den_mld=.true. + exit + endif + + enddo !maxLevelCell + + + if(.not. found_den_mld) then + refLevel=maxloc(densityGradient(:,2)) + dGradientMLD(iCell) = abs(zTop(refLevel(1),iCell)) + endif + + enddo !icell + + deallocate(densityGradient) + endif !if(densitygradientflag) + + block => block % next + end do + + + ! Even though some variables do not include an index that is decomposed amongst + ! domain partitions, we assign them within a block loop so that all blocks have the + ! correct values for writing output. +! block => domain % blocklist +! do while (associated(block)) +! call mpas_pool_get_subpool(block % structs, 'temPlateAM', temPlateAMPool) +! +! ! assignment of final temPlateAM variables could occur here. +! +! block => block % next +! end do + + end subroutine ocn_compute_mixed_layer_depths!}}} + +!*********************************************************************** +! +! routine interp_bw_levels +! +!> \brief Interpolates between model layers +!> \author Luke Van Roekel +!> \date September 2015 +!> \details +!> This routine conducts computations to compute various field values +!> between model levels (in pressure or depth) or could interpolate +!> between temperature/salinity/density values. Interpolations are +!> of the form +!> y = coeffs(1)*x^3 + coeffs(2)*x^2 + coeffs(3)*x + coeffs(4) +! +!----------------------------------------------------------------------- + + subroutine interp_bw_levels(y0,y1,x0,x1,xT,interp_f,yT,xm1,ym1)!{{{ + + integer,intent(in) :: interp_f ! linear, quadratic, or spline + real(kind=RKIND),intent(in) :: y0,y1,x0,x1,xT + real(kind=RKIND),intent(inout) :: yT + real(kind=RKIND),optional,intent(in) :: xm1,ym1 + ! these values are to match the slope at a given point + +!------------------------------------------------------------------------ +! +! Local variables for the interpolations +! +!------------------------------------------------------------------------ + + real(kind=RKIND) :: coeffs(4) ! stores the coefficients for the interp + real(kind=RKIND) :: Minv(4,4) ! holds values for computing quad and spline + real(kind=RKIND) :: det + real(kind=RKIND) :: rhs(4) + integer :: k,k2 + + coeffs(:) = 0.0_RKIND + Minv(:,:) = 0.0_RKIND + rhs(:) = 0.0_RKIND + + + select case (interp_f) + + case (1) + + coeffs(2) = (y1-y0)/(x1-x0) + coeffs(1) = y0 - coeffs(2)*x0 + case (2) + + det = -(x1-x0)**2 + rhs(1) = y0 + rhs(2) = y1 + + if(present(xm1) .and. present(ym1)) then + rhs(3) = (y0-ym1)/(x0-xm1) + else + rhs(3) = 0.0_RKIND + endif + + Minv(1,1) = -1.0_RKIND/det + Minv(1,2) = 1.0_RKIND/det + Minv(1,3) = -1.0_RKIND/(x1-x0) + Minv(2,1) = 2.0_RKIND*x0/det + Minv(2,2) = -2.0_RKIND*x0/det + Minv(2,3) = (x1+x0)/(x1-x0) + Minv(3,1) = -(x0**2)/det + Minv(3,2) = x1*(2.0_RKIND*x0-x1)/det + Minv(3,3) = -x1*x0/(x1-x0) + + do k=1,3 + do k2=1,3 + coeffs(k2) = coeffs(k2) + Minv(4-k2,k)*rhs(k) + enddo + enddo + + case (3) + det = -(x1-x0)**3 + rhs(1) = y1 + rhs(2) = y0 + if(present(xm1) .and. present(ym1)) then + rhs(3) = (y0-ym1)/(x0-xm1) + else + rhs(3) = 0.0_RKIND + endif + + rhs(4) = (y1-y0)/(x1-x0) + + Minv(1,1) = 2.0_RKIND/det + Minv(1,2) = -2.0_RKIND/det + Minv(1,3) = (x0-x1)/det + Minv(1,4) = (x0-x1)/det + Minv(2,1) = -3.0_RKIND * (x1+x0)/det + Minv(2,2) = 3.0_RKIND*(x1+x0)/det + Minv(2,3) = (x1-x0)*(2.0_RKIND*x1+x0)/det + Minv(2,4) = (x1-x0)*(2.0_RKIND*x0+x1)/det + Minv(3,1) = 6.0_RKIND*x1*x0/det + Minv(3,2) = -6.0_RKIND*x1*x0/det + Minv(3,3) = -x1*(x1-x0)*(2.0_RKIND*x0+x1)/det + Minv(3,4) = -x0*(x1-x0)*(2.0_RKIND*x1+x0)/det + Minv(4,1) = -(x0**2)*(3.0_RKIND*x1-x0)/det + Minv(4,2) = -(x1**2)*(-3.0_RKIND*x0+x1)/det + Minv(4,3) = x0*(x1**2)*(x1-x0)/det + Minv(4,4) = x1*(x0**2)*(x1-x0)/det + + do k=1,4 + do k2=1,4 + coeffs(k2) = coeffs(k2) + Minv(5-k2,k)*rhs(k) + enddo + enddo + + end select + + yT = coeffs(4)*xT**3 + coeffs(3)*xT**2 + coeffs(2)*xT + coeffs(1) + end subroutine interp_bw_levels!}}} + +!*********************************************************************** +! +! routine ocn_restart_mixed_layer_depths +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Luke Van Roekel +!> \date September 2015 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_mixed_layer_depths(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_mixed_layer_depths!}}} + +!*********************************************************************** +! +! routine ocn_finalize_mixed_layer_depths +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Luke Van Roekel +!> \date August 2015 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_mixed_layer_depths(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_mixed_layer_depths!}}} + +end module ocn_mixed_layer_depths + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_moc_streamfunction.F b/src/core_ocean/analysis_members/mpas_ocn_moc_streamfunction.F new file mode 100644 index 0000000000..74dafc0f8c --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_moc_streamfunction.F @@ -0,0 +1,673 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_moc_streamfunction +! +!> \brief MPAS ocean analysis mode member: moc_streamfunction +!> \author Nils H. Feige, Mark R. Petersen +!> \date 2016-04-08 +!> \brief Computes Meridional Overturning Circulation streamfunction. +!> +!----------------------------------------------------------------------- + +module ocn_moc_streamfunction + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_moc_streamfunction, & + ocn_compute_moc_streamfunction, & + ocn_restart_moc_streamfunction, & + ocn_finalize_moc_streamfunction + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + integer :: nMocStreamfunctionBinsUsed + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_moc_streamfunction +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Nils H. Feige, Mark R. Petersen +!> \date 2016-04-08 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_moc_streamfunction(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: mocStreamfunctionAMPool + type (mpas_pool_type), pointer :: meshPool + + integer :: iBin + + real (kind=RKIND) :: binWidth + ! These are array size 1 because mpas_dmpar_min_real_array calls require arrays. + real (kind=RKIND), dimension(1) :: minBin, maxBin, minBinDomain, maxBinDomain + ! the variable used to discriminate cells into Bins (either the y-value or the latitude) + real (kind=RKIND), dimension(:), pointer :: binVariable, binBoundaryMocStreamfunction + + !number of latitude bins specified in the config + integer, pointer :: config_AM_mocStreamfunction_num_bins + !smallest and highest latitude specified in the config + real (kind=RKIND), pointer :: config_AM_mocStreamfunction_min_bin, config_AM_mocStreamfunction_max_bin + + !determines if the simulation was run on a sphere or on a plane + logical, pointer :: on_a_sphere + + !!!! REGION STUFF + !! region moc calculation variables + integer :: currentRegion, i, iCell + + !! region arrays/variables + character (len=STRKIND), dimension(:), pointer :: regionGroupNames + integer, dimension(:, :), pointer :: regionCellMasks, regionsInGroup + integer, dimension(:), pointer :: nRegionsInGroup + integer, pointer :: nRegions, nRegionGroups, maxRegionsInGroup, nCellsSolve + real (kind=RKIND), dimension(:,:), pointer :: minMaxLatRegion + real (kind=RKIND), dimension(:) , pointer :: minLatRegionLocal, maxLatRegionLocal, tminLatRegionLocal, tmaxLatRegionLocal + character (len=STRKIND), pointer :: additionalRegion + + !! region preliminary variables + integer :: regionGroupNumber, regionsInAddGroup + + !!region pool + type (mpas_pool_type), pointer :: regionPool + + !! region dimensions + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegions', nRegions) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegionGroups', nRegionGroups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'R3', maxRegionsInGroup) + + !! region config for moc + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_region_group', & + additionalRegion) + + !! get region values + call mpas_pool_get_subpool(domain % blocklist % structs, 'regions', regionPool) + call mpas_pool_get_array(regionPool, 'regionsInGroup', regionsInGroup) + call mpas_pool_get_array(regionPool, 'nRegionsInGroup', nRegionsInGroup) + call mpas_pool_get_array(regionPool, 'regionGroupNames', regionGroupNames) + + !!! region preliminaries + do i = 1, nRegionGroups + if (regionGroupNames(i) .eq. additionalRegion) then + regionGroupNumber = i + end if + end do + + regionsInAddGroup = MIN(nRegionsInGroup(regionGroupNumber), maxRegionsInGroup) + !!!! END REGION STUFF + + allocate(minLatRegionLocal(maxRegionsInGroup)) + allocate(maxLatRegionLocal(maxRegionsInGroup)) + allocate(tminLatRegionLocal(maxRegionsInGroup)) + allocate(tmaxLatRegionLocal(maxRegionsInGroup)) + + dminfo = domain % dminfo + + err = 0 + + minBin = 1.0e34_RKIND + maxBin = -1.0e34_RKIND + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mocStreamfunctionAM', mocStreamfunctionAMPool) + + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_num_bins', & + config_AM_mocStreamfunction_num_bins) + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_min_bin', & + config_AM_mocStreamfunction_min_bin) + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_max_bin', & + config_AM_mocStreamfunction_max_bin) + + call mpas_pool_get_array(mocStreamfunctionAMPool, 'minMaxLatRegion', minMaxLatRegion) + + minLatRegionLocal(:) = 4.0_RKIND + maxLatRegionLocal(:) = -4.0_RKIND + + nMocStreamfunctionBinsUsed = config_AM_mocStreamfunction_num_bins + + call mpas_pool_get_array(mocStreamfunctionAMPool, 'binBoundaryMocStreamfunction', binBoundaryMocStreamfunction) + + ! Find min and max values of binning variable. For the whole domain as well as for each region + ! in the current region group. + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(regionPool, 'regionCellMasks', regionCellMasks) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + ! Bin by latitude on a sphere, by yCell otherwise. + if (on_a_sphere) then + call mpas_pool_get_array(meshPool, 'latCell', binVariable) + else + call mpas_pool_get_array(meshPool, 'yCell', binVariable) + end if + + minBin = min(minBin, minval(binVariable) ) + maxBin = max(maxBin, maxval(binVariable) ) + + do i = 1, regionsInAddGroup + currentRegion = regionsInGroup(i, regionGroupNumber) + do iCell = 1, nCellsSolve + if (regionCellMasks(currentRegion, iCell) .eq. 1) then + minLatRegionLocal(i) = min(minLatRegionLocal(i), binVariable(iCell)) + maxLatRegionLocal(i) = max(maxLatRegionLocal(i), binVariable(iCell)) + end if + end do + end do + + block => block % next + end do + + call mpas_dmpar_min_real_array(dminfo, 1, minBin, minBinDomain) + call mpas_dmpar_max_real_array(dminfo, 1, maxBin, maxBinDomain) + + call mpas_dmpar_min_real_array(dminfo, maxRegionsInGroup, minLatRegionLocal(:), tminLatRegionLocal(:)) + call mpas_dmpar_max_real_array(dminfo, maxRegionsInGroup, maxLatRegionLocal(:), tmaxLatRegionLocal(:)) + minMaxLatRegion(1, :) = tminLatRegionLocal(:) + minMaxLatRegion(2, :) = tmaxLatRegionLocal(:) + + deallocate(minLatRegionLocal) + deallocate(maxLatRegionLocal) + deallocate(tminLatRegionLocal) + deallocate(tmaxLatRegionLocal) + + !print *, 'mins:', minMaxLatRegion(1,:) + !print *, 'maxs:', minMaxLatRegion(2,:) + + ! Set up bins. + binBoundaryMocStreamfunction = -1.0e34_RKIND + + ! Change min and max bin bounds to configuration settings, if applicable. + if (config_AM_mocStreamfunction_min_bin > -1.0e33_RKIND) then + minBinDomain(1) = config_AM_mocStreamfunction_min_bin + else + ! use measured min value, but decrease slightly to include least value. + minBinDomain(1) = minBinDomain(1) - 1.0e-10_RKIND * abs(minBinDomain(1)) + end if + + if (config_AM_mocStreamfunction_max_bin > -1.0e33_RKIND) then + maxBinDomain(1) = config_AM_mocStreamfunction_max_bin + else + ! use measured max value, but increase slightly to include max value. + maxBinDomain(1) = maxBinDomain(1) + 1.0e-10_RKIND * abs(maxBinDomain(1)) + end if + + binBoundaryMocStreamfunction(1) = minBinDomain(1) + binWidth = (maxBinDomain(1) - minBinDomain(1)) / nMocStreamfunctionBinsUsed + + ! Use the same bin boundaries for the regions and the global MOC. + do iBin = 2, nMocStreamfunctionBinsUsed + 1 + binBoundaryMocStreamfunction(iBin) = binBoundaryMocStreamfunction(iBin-1) + binWidth + end do + + end subroutine ocn_init_moc_streamfunction!}}} + +!*********************************************************************** +! +! routine ocn_compute_moc_streamfunction +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Nils H. Feige, Mark R. Petersen +!> \date 2016-04-08 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_moc_streamfunction(domain, timeLevel, err)!{{{ + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: mocStreamfunctionAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: regionPool + type (mpas_pool_type), pointer :: transectPool + + integer, pointer :: nVertLevels, nCellsSolve + integer :: iCell, iBin, genericCounter, k + real (kind=RKIND) :: binWidth + real (kind=RKIND), dimension(:,:), pointer :: mocStreamValLatAndDepthLocal + real (kind=RKIND), dimension(:), pointer :: latCell + real (kind=RKIND), dimension(:), pointer :: areaCell, binBoundaryMocStreamfunction + real (kind=RKIND), dimension(:,:), pointer :: mocStreamvalLatAndDepth, mocStreamValLatAndDepthTotal + real (kind=RKIND), dimension(:,:), pointer :: vertVelocityTop + real (kind=RKIND), dimension(:,:), pointer :: sumVertBinVelocity + character (len=STRKIND), pointer :: verticalVelocityArrayName, normalVelocityArrayName + + !!!! TRANSECT VARIABLES !!!! + integer, pointer :: nEdgesSolve, num_tracers + integer :: iEdge, iTransect, c1, c2, currentTransect + integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop + integer, dimension(:,:), pointer :: transectEdgeMasks, transectEdgeMaskSigns, cellsOnEdge, & + transectsInGroup + + real (kind=RKIND) :: m3ps_to_Sv + real (kind=RKIND), dimension(:), pointer :: dvEdge, refLayerThickness + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, normalVelocity + real (kind=RKIND), dimension(:,:), allocatable :: sumTransport, totalSumTransport + character (len=STRKIND), dimension(:), pointer :: transectNames, transectGroupNames + integer, dimension(:), pointer :: nTransectsInGroup + integer, pointer :: nTransects, nTransectGroups, maxTransectsInGroup + character (len=STRKIND), pointer :: additionalTransect + integer :: transectGroupNumber, transectsInAddGroup + !!!! END TRANSECT VARIABLES !!!! + + !!!! REGION VARIABLES + real (kind=RKIND) :: maskFactor + integer :: currentRegion, i + real (kind=RKIND), dimension(:,:,:), pointer :: mocStreamValLatAndDepthRegionLocal, & + mocStreamvalLatAndDepthRegion, mocStreamValLatAndDepthRegionTotal, & + sumVertBinVelocityRegion + integer, dimension(:, :), pointer :: regionCellMasks, regionVertexMasks, regionsInGroup + character (len=STRKIND), dimension(:), pointer :: regionNames, regionGroupNames + integer, dimension(:), pointer :: nRegionsInGroup + integer, pointer :: nRegions, nRegionGroups, maxRegionsInGroup + character (len=STRKIND), pointer :: additionalRegion + integer :: regionGroupNumber, regionsInAddGroup + + !!!! END REGION VARIABLES + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevels', nVertLevels) + + !!!! REGION INITIALIZATION + !! region dimensions + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegions', nRegions) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegionGroups', nRegionGroups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'R3', maxRegionsInGroup) + + !! region config for moc + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_region_group', & + additionalRegion) + + !! get region values + call mpas_pool_get_subpool(domain % blocklist % structs, 'regions', regionPool) + call mpas_pool_get_array(regionPool, 'regionsInGroup', regionsInGroup) + call mpas_pool_get_array(regionPool, 'nRegionsInGroup', nRegionsInGroup) + call mpas_pool_get_array(regionPool, 'regionNames', regionNames) + call mpas_pool_get_array(regionPool, 'regionGroupNames', regionGroupNames) + + do i = 1, nRegionGroups + if (regionGroupNames(i) .eq. additionalRegion) then + regionGroupNumber = i + !print *, 'found region with the same name:', regionGroupNames(i) + end if + end do + + regionsInAddGroup = MIN(nRegionsInGroup(regionGroupNumber), maxRegionsInGroup) + + !! allocate regional moc calculation arrays + allocate(mocStreamValLatAndDepthRegionLocal(nMocStreamfunctionBinsUsed + 1, nVertLevels, maxRegionsInGroup)) + allocate(sumVertBinVelocityRegion(nMocStreamfunctionBinsUsed + 1, nVertLevels, maxRegionsInGroup)) + allocate(mocStreamValLatAndDepthRegionTotal(nMocStreamfunctionBinsUsed + 1, nVertLevels, maxRegionsInGroup)) + + mocStreamValLatAndDepthRegionLocal = 0.0_RKIND + mocStreamValLatAndDepthRegionTotal = 0.0_RKIND + sumVertBinVelocityRegion = 0.0_RKIND + !!!! END REGION INITIALIZATION + + !!!! TRANSECT INITIALIZATION + !! transect dimensions + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nTransects', nTransects) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nTransectGroups', nTransectGroups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'maxTransectsInGroup', maxTransectsInGroup) + + !!!! Transect config for MOC + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_transect_group', & + additionalTransect) + !! get transect values + call mpas_pool_get_subpool(domain % blocklist % structs, 'transects', transectPool) + call mpas_pool_get_array(transectPool, 'transectsInGroup', transectsInGroup) + call mpas_pool_get_array(transectPool, 'nTransectsInGroup', nTransectsInGroup) + call mpas_pool_get_array(transectPool, 'transectNames', transectNames) + call mpas_pool_get_array(transectPool, 'transectGroupNames', transectGroupNames) + do i = 1, nTransectGroups + if (transectGroupNames(i) .eq. additionalTransect) then + transectGroupNumber = i + end if + end do + + transectsInAddGroup = nTransectsInGroup(transectGroupNumber) + + allocate(sumTransport(nVertLevels,maxTransectsInGroup)) + allocate(totalSumTransport(nVertLevels,maxTransectsInGroup)) + + m3ps_to_Sv = 1e-6 + !!!! END TRANSECT INITIALIZATION + + if (transectsInAddGroup .ne. regionsInAddGroup) then + call mpas_log_write ('ocn_moc_streamfunction AM: transectsInGroup count does not ' & + // 'match regionsInGroup count: $i, $i', intArgs = (/ transectsInAddGroup, regionsInAddGroup /) ) + i = min(transectsInAddGroup, regionsInAddGroup) + transectsInAddGroup = i + regionsInAddGroup = i + call mpas_log_write ('Setting both to min: $i, $i', intArgs = (/ transectsInAddGroup, regionsInAddGroup /) ) + end if + + err = 0 + + dminfo = domain % dminfo + + allocate(mocStreamValLatAndDepthLocal(nMocStreamfunctionBinsUsed + 1, nVertLevels)) + allocate(sumVertBinVelocity(nMocStreamfunctionBinsUsed + 1, nVertLevels)) + allocate(mocStreamValLatAndDepthTotal(nMocStreamfunctionBinsUsed + 1, nVertLevels)) + + mocStreamValLatAndDepthLocal = 0.0_RKIND + mocStreamValLatAndDepthTotal = 0.0_RKIND + sumVertBinVelocity = 0.0_RKIND + + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_vertical_velocity_value', & + verticalVelocityArrayName) + + call mpas_pool_get_config(domain % configs, 'config_AM_mocStreamfunction_normal_velocity_value', & + normalVelocityArrayName) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'mocStreamfunctionAM', mocStreamfunctionAMPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_array(mocStreamfunctionAMPool, 'binBoundaryMocStreamfunction', binBoundaryMocStreamfunction) + + binWidth = (binBoundaryMocStreamfunction(nMocStreamfunctionBinsUsed + 1) - binBoundaryMocStreamfunction(1)) & + / nMocStreamfunctionBinsUsed + + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(diagnosticsPool, verticalVelocityArrayName, vertVelocityTop) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + + call mpas_pool_get_array(regionPool, 'regionCellMasks', regionCellMasks) + + !!!! TRANSECT DOMAINSPLIT VARIABLES + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_array(transectPool,'transectEdgeMaskSigns',transectEdgeMaskSigns) + call mpas_pool_get_array(transectPool,'transectEdgeMasks',transectEdgeMasks) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_array(statePool, normalVelocityArrayName, normalVelocity, timeLevel) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) + + + !!!! TRANSECT CALCULATION + sumTransport = 0.0_RKIND + do iTransect = 1,transectsInAddGroup + currentTransect = transectsInGroup(iTransect, transectGroupNumber) + do iEdge = 1,nEdgesSolve + c1 = cellsOnEdge(1,iEdge) + c2 = cellsOnEdge(2,iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + sumTransport(k,iTransect) = sumTransport(k,iTransect) + & + transectEdgeMaskSigns(currentTransect,iEdge) & + * transectEdgeMasks(currentTransect, iEdge) & + * normalVelocity(k,iEdge)*dvEdge(iEdge) & + * 0.5_RKIND*(layerThickness(k,c1) + layerThickness(k,c2)) + end do + end do + do k = 2, nVertLevels + mocStreamValLatAndDepthRegionLocal(1, k, iTransect) = & + mocStreamValLatAndDepthRegionLocal(1, k - 1, iTransect) & + + sumTransport(k - 1, iTransect) + end do + end do + + !!!! END TRANSECT CALCULATION + + do iCell = 1,nCellsSolve + iBin = MAX(int((latCell(iCell) - binBoundaryMocStreamfunction(1)) / binWidth), 2) + do k = 1,maxLevelCell(iCell) + do i = 1, regionsInAddGroup + currentRegion = regionsInGroup(i, regionGroupNumber) + sumVertBinVelocityRegion(iBin, k, i) = sumVertBinVelocityRegion(iBin, k, i) + (vertVelocityTop(k, iCell) * & + areaCell(iCell) * regionCellMasks(currentRegion, iCell)) + end do + sumVertBinVelocity(iBin, k) = sumVertBinVelocity(iBin, k) + (vertVelocityTop(k, iCell) * areaCell(iCell)) + end do + end do + + do i = 1, regionsInAddGroup + do k = 1,nVertLevels + do iBin = 2, nMocStreamfunctionBinsUsed + 1 + mocStreamValLatAndDepthLocal(iBin, k) = mocStreamValLatAndDepthLocal(iBin-1, k) & + + sumVertBinVelocity(iBin, k) + mocStreamValLatAndDepthRegionLocal(iBin, k, i) = mocStreamValLatAndDepthRegionLocal(iBin-1, k, i) & + + sumVertBinVelocityRegion(iBin, k, i) + end do + end do + end do + + block => block % next + end do + + call mpas_dmpar_sum_real_array(dminfo, nVertLevels * (nMocStreamfunctionBinsUsed + 1), mocStreamValLatAndDepthLocal, & + mocStreamvalLatAndDepthTotal) + + call mpas_dmpar_sum_real_array(dminfo, nVertLevels * (nMocStreamfunctionBinsUsed + 1) * maxRegionsInGroup, & + mocStreamValLatAndDepthRegionLocal, mocStreamvalLatAndDepthRegionTotal) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mocStreamfunctionAM', mocStreamfunctionAMPool) + call mpas_pool_get_array(mocStreamfunctionAMPool, 'mocStreamvalLatAndDepth', mocStreamvalLatAndDepth) + mocStreamvalLatAndDepth = mocStreamvalLatAndDepthTotal * m3ps_to_Sv + + call mpas_pool_get_array(mocStreamfunctionAMPool, 'mocStreamvalLatAndDepthRegion', mocStreamvalLatAndDepthRegion) + mocStreamvalLatAndDepthRegion = mocStreamvalLatAndDepthRegionTotal * m3ps_to_Sv + + deallocate(mocStreamvalLatAndDepthTotal) + deallocate(mocStreamvalLatAndDepthLocal) + deallocate(sumVertBinVelocity) + + deallocate(mocStreamvalLatAndDepthRegionTotal) + deallocate(mocStreamvalLatAndDepthRegionLocal) + deallocate(sumVertBinVelocityRegion) + + !!!! TRANSECT CELANUP + deallocate(sumTransport) + deallocate(totalSumTransport) + + end subroutine ocn_compute_moc_streamfunction!}}} + +!*********************************************************************** +! +! routine ocn_restart_moc_streamfunction +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Nils H. Feige, Mark R. Petersen +!> \date 2016-04-08 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_moc_streamfunction(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_moc_streamfunction!}}} + +!*********************************************************************** +! +! routine ocn_finalize_moc_streamfunction +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Nils H. Feige, Mark R. Petersen +!> \date 2016-04-08 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_moc_streamfunction(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_moc_streamfunction!}}} + +end module ocn_moc_streamfunction diff --git a/src/core_ocean/analysis_members/mpas_ocn_okubo_weiss.F b/src/core_ocean/analysis_members/mpas_ocn_okubo_weiss.F index 2daa7c7dff..621401731b 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_okubo_weiss.F +++ b/src/core_ocean/analysis_members/mpas_ocn_okubo_weiss.F @@ -657,7 +657,7 @@ subroutine ocn_compute_OW_component_IDs(dminfo, block, meshPool, processorId, nV call mpas_timer_start("CC eddy stats") call ocn_compute_eddy_stats(dminfo, block, nVertLevels, nCells, nCellsSolve, nLocalCCs, & nEdgesOnCell, cellsOnCell, OW_cc_id, OW_thresh) - call mpas_timer_stop("CC local") + call mpas_timer_stop("CC eddy stats") end subroutine ocn_compute_OW_component_IDs!}}} @@ -881,8 +881,7 @@ subroutine ocn_compute_eddy_stats(dminfo, block, nVertLevels, nCells, nCellsSolv ! Found a new eddy. curCC = curCC + 1 if (curCC > nLocalCCs) then - call mpas_log_write("ERROR: curCC > nLocalCCs") !, curCC, nLocalCCs - call mpas_log_write("THIS IS A BUG!") + call mpas_log_write("curCC > nLocalCCs. THIS IS A BUG!", MPAS_LOG_CRIT) !, curCC, nLocalCCs end if ! Put this cell into the list of cells to be processed as ! seed for the flood fill of this eddy @@ -1041,7 +1040,7 @@ subroutine ocn_aggregate_eddy_stats(dmInfo, use_lat_lon_coords, min_cells, &!{{{ offset = 9 * maxCCsPerDomain * (dmInfo % my_proc_id) - aggregated = -1.0e34 + aggregated = -1.0e34_RKIND do i = 1, numCCs aggregated(9*(i-1) + 1 + offset) = origCCId(i) aggregated(9*(i-1) + 2 + offset) = sumVol(i) @@ -1129,12 +1128,12 @@ subroutine ocn_aggregate_eddy_stats(dmInfo, use_lat_lon_coords, min_cells, &!{{{ call mpas_timer_stop("sort/reduce") ! only output if IO node + call mpas_timer_start("output") if (dminfo % my_proc_id == IO_NODE) then ! Output aggregated - call mpas_timer_start("output") call ocn_output_eddy_stats(listIdx, aggregated, use_lat_lon_coords,xtime) - call mpas_timer_stop("output") end if + call mpas_timer_stop("output") end subroutine ocn_aggregate_eddy_stats!}}} @@ -1233,12 +1232,17 @@ subroutine ocn_output_eddy_stats(numEddies, aggData, use_lat_lon_coords,xtime)!{ fileID = mpas_get_free_unit() ! Create output file. - open(fileID, file=trim(config_AM_okuboWeiss_directory)//'/eddy_census_'//trim(xtime)//'.txt', STATUS='UNKNOWN', POSITION='rewind') + open(fileID, file=trim(config_AM_okuboWeiss_directory)//'/eddy_census_'//trim(xtime)//'.txt', STATUS='UNKNOWN', & + POSITION='rewind') if (use_lat_lon_coords) then - write (fileID, '(10A)') '"eddy ID", "number of cells", "volume sum, m^3", "average longitude, degrees", "average latitude, degrees", "average depth, m", "average zonal velocity, m/s", "average meridional velocity, m/s", "average vertical velocity, m/s"' + write (fileID, '(10A)') '"eddy ID", "number of cells", "volume sum, m^3", "average longitude, degrees", "average ' & + // 'latitude, degrees", "average depth, m", "average zonal velocity, m/s", "average ' & + // 'meridional velocity, m/s", "average vertical velocity, m/s"' else - write (fileID, '(10A)') '"eddy ID", "number of cells", "volume sum, m^3", "average x-position, m", "average y-position, m", "average depth, m", "average x-velocity, m/s", "average y-velocity, m/s", "average z-velocity, m/s"' + write (fileID, '(10A)') '"eddy ID", "number of cells", "volume sum, m^3", "average x-position, m", "average ' & + // 'y-position, m", "average depth, m", "average x-velocity, m/s", "average y-velocity, ' & + // 'm/s", "average z-velocity, m/s"' end if ! Output number of eddies and statistics for each eddy @@ -1247,8 +1251,10 @@ subroutine ocn_output_eddy_stats(numEddies, aggData, use_lat_lon_coords,xtime)!{ v = aggData(9*(i-1)+2) write (fileID, '(I10, A)', advance='no') int(aggData(9*(i-1)+9)), ', ' write (fileID, '(ES12.5, A)', advance='no') v, ', ' - write (fileID, '(ES12.5, A, ES12.5, A, ES12.5, A)', advance='no') aggData(9*(i-1)+3)/v, ', ', aggData(9*(i-1)+4)/v, ', ', aggData(9*(i-1)+5)/v, ', ' - write (fileID, '(ES12.5, A, ES12.5, A, ES12.5)') aggData(9*(i-1)+6)/v, ', ', aggData(9*(i-1)+7)/v, ', ', aggData(9*(i-1)+8)/v + write (fileID, '(ES12.5, A, ES12.5, A, ES12.5, A)', advance='no') aggData(9*(i-1)+3)/v, ', ', aggData(9*(i-1)+4)/v, & + ', ', aggData(9*(i-1)+5)/v, ', ' + write (fileID, '(ES12.5, A, ES12.5, A, ES12.5)') aggData(9*(i-1)+6)/v, ', ', aggData(9*(i-1)+7)/v, ', ', & + aggData(9*(i-1)+8)/v end do close(fileID) @@ -1380,9 +1386,9 @@ subroutine mpas_velocity_gradient_R3Cell(normalVelocity, tangentialVelocity, &!{ enddo enddo - velocityGradient = 0.0 + velocityGradient = 0.0_RKIND do iCell = 1, nCellsCompute - invAreaCell = 1.0 / areaCell(iCell) + invAreaCell = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) do k = 1, maxLevelCell(iCell) diff --git a/src/core_ocean/analysis_members/mpas_ocn_particle_list.F b/src/core_ocean/analysis_members/mpas_ocn_particle_list.F new file mode 100644 index 0000000000..43b9cc4b88 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_particle_list.F @@ -0,0 +1,4242 @@ +! Copyright (c) 2014, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_particle_list +! +!> \brief Particle framework +!> \author Phillip Wolfram +!> \date 04/10/2014 +!> \details +!> This module contains a general definition of particles which can be +!> used in implementation of Lagrangian Particles Tracking. +!----------------------------------------------------------------------- +#define COMMA , +#ifdef MPAS_DEBUG +#define LIGHT_DEBUG_WRITE(Z) print *, Z +#define LIGHT_DEBUG_ALL_WRITE(Z) print *, Z +#else +#define LIGHT_DEBUG_WRITE(Z) ! print *, Z +#define LIGHT_DEBUG_ALL_WRITE(Z) ! print *, Z +#endif + +#define LIGHT_ERROR_WRITE(Z) print *, Z +#define LIGHT_WARNING_WRITE(Z) print *, Z + +module ocn_particle_list + + ! declare general packages used +#ifdef _MPI + use mpi +#endif + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_dmpar + use mpas_block_decomp + use mpas_pool_routines + use mpas_timer + + ! blanket statments to restrict implicit module's scope + implicit none + private + + ! mpi defines +#ifdef _MPI + integer, parameter :: MPI_INTEGERKIND = MPI_INTEGER + integer, parameter :: MPI_2INTEGERKIND = MPI_2INTEGER + +#ifdef SINGLE_PRECISION + integer, parameter :: MPI_REALKIND = MPI_REAL + integer, parameter :: MPI_2REALKIND = MPI_2REAL +#else + integer, parameter :: MPI_REALKIND = MPI_DOUBLE_PRECISION + integer, parameter :: MPI_2REALKIND = MPI_2DOUBLE_PRECISION +#endif +#endif + + ! custom structures defined in mpas_grid_types + + ! define private interfaces + + ! add routines + interface add_halo_data_to_particle_list + !(particlelist, dataName, data) + module procedure add_halo_data_to_particle_list_1Dreal + module procedure add_halo_data_to_particle_list_1Dint + end interface + + interface add_halo_data_to_particle_list_array + module procedure add_halo_data_to_particle_list_1Dreal_array + module procedure add_halo_data_to_particle_list_1Dint_array + end interface + + interface add_nonhalo_data_to_particle_list + !(particlelist, dataName, data) + module procedure add_nonhalo_data_to_particle_list_1Dreal + module procedure add_nonhalo_data_to_particle_list_1Dint + end interface + + interface add_nonhalo_data_to_particle_list_array + module procedure add_nonhalo_data_to_particle_list_1Dreal_array + module procedure add_nonhalo_data_to_particle_list_1Dint_array + end interface + + ! get routines + interface get_halo_data_from_particle_list + !(particlelist, dataName, data) + module procedure get_halo_data_from_particle_list_1Dreal + module procedure get_halo_data_from_particle_list_1Dint + end interface + + interface get_halo_data_from_particle_list_array + module procedure get_halo_data_from_particle_list_1Dreal_array + module procedure get_halo_data_from_particle_list_1Dint_array + end interface + + interface get_nonhalo_data_from_particle_list + !(particlelist, dataName, data) + module procedure get_nonhalo_data_from_particle_list_1Dreal + module procedure get_nonhalo_data_from_particle_list_1Dint + end interface + + interface get_nonhalo_data_from_particle_list_array + !(particlelist, dataName, data) + module procedure get_nonhalo_data_from_particle_list_1Dreal_array + module procedure get_nonhalo_data_from_particle_list_1Dint_array + end interface + + !----------------------------------------------------------------- + ! public routines and interfaces + !----------------------------------------------------------------- + ! define publically accessible subroutines, functions, interfaces + public :: mpas_particle_list_build_and_assign_particle_list + public :: mpas_particle_list_destroy_particle_list, mpas_particle_list_remove_particles_not_on_current_block + public :: mpas_particle_list_build_computation_halos, mpas_particle_list_build_halos + public :: mpas_particle_list_update_particle_block + public :: mpas_particle_list_update_halos_start, mpas_particle_list_update_halos_end + public :: mpas_particle_list_transfer_particles_from_block_to_named_block + public :: mpas_particle_list_write_halo_data, mpas_particle_list_write_nonhalo_data + public :: mpas_particle_list_test_neighscalc, mpas_particle_list_test_numparticles_to_neighprocs + public :: mpas_particle_list_test_num_current_particlelist + public :: mpas_particle_list_self_union_halo_lists + + ! subroutine / function definition +contains + +!*********************************************************************** +! +! routine mpas_particle_list_build_and_assign_particle_list +! +!> \brief Allocates particles for initialization +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine builds and allocates particlces following initalization +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_build_and_assign_particle_list(domain,err) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + err = 0 + ! build / allocate the listPLSend, setting ioProc (currentBlock read in as haloData) + call build_block_particlelists(domain, err) +#ifdef MPAS_DEBUG + LIGHT_DEBUG_ALL_WRITE('finished build_block_particlelists 1') + call MPI_Barrier(domain % dminfo % comm, err) +#endif + + ! read in data from netCDF-injected data structures + ! nonhalo-data is diagnostic + call read_haloData(domain, err) +#ifdef MPAS_DEBUG + LIGHT_DEBUG_ALL_WRITE('finished read_haloData 1') + call MPI_Barrier(domain % dminfo % comm, err) +#endif + + ! note that nonhalo data is just initialized with 0 + ! values are not imported from netCDF input file + call read_nonhaloData(domain, err) +#ifdef MPAS_DEBUG + LIGHT_DEBUG_ALL_WRITE('finished read_nonhaloData 1') + call MPI_Barrier(domain % dminfo % comm, err) + + call mpas_particle_list_test_num_current_particlelist(domain) + LIGHT_DEBUG_ALL_WRITE('finished test in mpas_particle_list_build_and_assign_particle_list') + call MPI_Barrier(domain % dminfo % comm, err) +#endif + !! test to make sure deallocation is ok before transfer...!{{{ +#ifdef MPAS_DEBUG + LIGHT_DEBUG_ALL_WRITE(' Trying to clear particlelist memory on blocks') + call clear_block_particlelists(domain,err) + call build_block_particlelists(domain, err) + call read_haloData(domain, err) + call read_nonhaloData(domain, err) + call mpas_particle_list_test_num_current_particlelist(domain) + call test_currentBlock(domain) + LIGHT_DEBUG_ALL_WRITE(' Rebuilt data structures-- ok') + call MPI_Barrier(domain % dminfo % comm, err) +#endif + !}}} + end subroutine mpas_particle_list_build_and_assign_particle_list !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_particle_list_destroy_particle_list +! +!> \brief MPAS destroy particlelist +!> \author Phillip Wolfram +!> \date 06/27/2014 +!> \details +!> This routine destroys a particlelist, deallocating its memory +!> including that of all pointers it contains +! +!----------------------------------------------------------------------- +subroutine mpas_particle_list_destroy_particle_list(particlelist) !{{{ + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + type (mpas_particle_list_type), pointer :: pLCurr, pLCurrTemp + + if(associated(particlelist)) then + plCurr => particlelist + do while(associated(plCurr)) + pLCurrTemp => pLCurr + pLCurr => pLCurr % next + ! destroy the particle too + if(associated(pLCurrTemp % particle)) then + call destroy_particle(pLCurrTemp % particle) + end if + deallocate(pLCurrTemp) + end do + end if + +end subroutine mpas_particle_list_destroy_particle_list !}}} + +!*********************************************************************** +! +! routine mpas_particle_list_remove_particles_not_on_current_block +! +!> \brief Remove particles on block % particlelist that are not on currentBlock +!> \author Phillip Wolfram +!> \date 07/03/2014 +!> \details +!> This routine removes particles that were transfered strictly for IO. +!> If the particle's currentBlock is not the same as the current block, +!> the particle is removed. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_remove_particles_not_on_current_block(domain, err) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_particle_list_type), pointer :: particlelist, particlelisttemp, particlelisttemp2 + integer :: thisBlock + integer, pointer :: particleBlock + integer :: arrayIndex + !type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_particle_type), pointer :: particle + + err = 0 + + block => domain % blocklist + do while(associated(block)) + ! for each particle on each block + particlelist => block % particlelist + do while(associated(particlelist)) + particle => particlelist % particle + call mpas_pool_get_array(particle % haloDataPool, 'currentBlock', particleBlock) + + if(particleBlock /= block % blockID) then + ! REMOVE PARTICLE FROM EXISTING PARTICLELIST + ! remove particle and particle reference from existing particle list + ! 3 cases: head, middle, tail + + call destroy_particle(particle) + + if(associated(particlelist % prev)) then + particlelisttemp => particlelist % prev + if (associated(particlelist % next)) then + ! case of the middle + particlelisttemp % next => particlelist % next + particlelisttemp2 => particlelisttemp + ! want to keep particle memory intact because their pointers were passed previously, + ! so just empty the list, don't destroy it and its contents + particlelisttemp => particlelist % next + particlelisttemp % prev => particlelisttemp2 + ! just need to remove the single link, particle memory needs to stay intact + !deallocate(particlelist % particle) + deallocate(particlelist) + ! get next pointer + particlelist => particlelisttemp + else + ! case of tail + nullify(particlelisttemp % next) + !deallocate(particlelist % particle) + deallocate(particlelist) + ! set back to final + end if + else + if(associated(particlelist % next)) then + ! case of head, set new head (assumes more than one particle) + particlelisttemp => particlelist % next + nullify(particlelisttemp % prev) + block % particlelist => particlelisttemp + !deallocate(particlelist % particle) + deallocate(particlelist) + particlelist => particlelisttemp + else + ! case of single link / particle + !deallocate(particlelist % particle) + deallocate(particlelist) + nullify(block % particlelist) + end if + end if + else + particlelist => particlelist % next + end if + end do + + ! this is done for each block because we want processor - processor communication + block => block % next + end do + + end subroutine mpas_particle_list_remove_particles_not_on_current_block !}}} + +!*********************************************************************** +! +! routine mpas_particle_list_build_computation_halos +! +!> \brief Build up necessary info for communication of particle in +!> halo to neighboring cell during computation step. +!> \author Phillip Wolfram +!> \date 07/02/2014 +!> \details +!> This routine builds g_compProcNeighs which is the neighboring processor +!> list needed to process MPI communication, assuming a list of +!> particlelists is built up corresponding to the processors in this +!> array. The end result is that g_compProcNeighs is populated. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_build_computation_halos(domain, err, procNeighs) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + integer, dimension(:), pointer :: procNeighs + + err = 0 + + ! compute the cellOwnerBlock for cells + ! owning processor can be obtained from + ! mpas_get_owning_proc in mpas_block_decomp (src/framework) + call compute_cellOwnerBlock(domain, err) + + ! in order to mediate MPI exchanges, need to determine + ! 1. list of blockNeighs (global) + ! stored in block % blockNeighs + ! basically Neighs are processors who own the halo including itself + call compute_blockNeighs(domain, err) + + ! 2. list of procNeighs (extracted from blockNeighs, also global) + ! this information is necessary in order to know where to send data + ! (this is like a block exchange list for particles) + ! stored in block % procNeighs + ! this is just the list of processors who own blockNeighs + call compute_block_procNeighs(domain, err) + + ! need to aggregate procNeighs to be global for the processor (over each block on the + ! processor), total number of neighboring processors to a particular processor + call compute_procNeighs(domain, err, procNeighs) + + end subroutine mpas_particle_list_build_computation_halos !}}} + +!*********************************************************************** +! +! routine mpas_particle_list_build_halos +! +!> \brief Build the IO halo information to transmit particles from their +!> initial host IO processor to the appropriate currentBlock +!> processor. +!> \author Phillip Wolfram +!> \date 07/02/2014 +!> \details +!> This routine builds the IO halo information to transmit particles +!> from their initial host IO processor to the appropriate currentBlock +!> processor. The end result is that g_ioProcNeighs is populated. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_build_halos(domain, err, namedBlock, ioProcNeighs) !{{{ + !{{{ initialization + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + character(len=*), intent(in) :: namedBlock + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, dimension(:), pointer, intent(out) :: ioProcNeighs + integer, intent(out) :: err !< Output: error flag + + !}}} + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer, dimension(:), pointer :: currentBlocks, tempInt + integer :: i, nBlocks, nTotProcs, mpi_ierr + logical, dimension(:), pointer :: sendNeigh, recvNeigh + + err = 0 + + ! get unique list of currentBlock to determine all currentProcs + ! that the particles must be communicated to + ! determine complete set of ioProcs, which are assigned based on the + ! way that PIO decomposes the nParticle dimension. + ! at this point, processors owning the particle (at read-in) are + ! assumed to be the ioProcessor + call compute_all_particle_values_unique_int(domain, err, namedBlock, currentBlocks) + + nTotProcs = domain % dminfo % nprocs + allocate(sendNeigh(nTotProcs)) + allocate(recvNeigh(nTotProcs)) + sendNeigh = .false. + recvNeigh = .false. + + ! Get processors for currentBlocks. Note, however, this is one-sided because + ! only the IO processors know their send location, the receivers don't know + ! their sending processors + if(associated(currentBlocks)) then + nBlocks = size(currentBlocks) + !print *, 'nBlocks = ', nBlocks, ' currentBlocks = ', currentBlocks + allocate(ioProcNeighs(nBlocks)) + do i=1, nBlocks + call mpas_get_owning_proc(domain % dminfo, currentBlocks(i), ioProcNeighs(i)) + end do + + ! note, each ioProc knows where it is sending data. However, those processors + ! do not know they should receive data (their halos are empty). The halos + ! are not symmetric and the communication cannot occur unless this is fixed. + ! this is fixed via an all-to-all communication (only at initialization, otherwise + ! parallelism can be broken) + ! set counter for connectivity + do i=1,size(ioProcNeighs) + sendNeigh(ioProcNeighs(i)+1) = .true. + end do + deallocate(ioProcNeighs) + end if + + LIGHT_DEBUG_WRITE('sendNeigh = ' COMMA sendNeigh) + LIGHT_DEBUG_WRITE('recvNeigh= ' COMMA recvNeigh) +#ifdef _MPI + !call MPI_Barrier(domain % dminfo % comm, mpi_ierr) +#endif + ! send with MPI all to update in recvNeigh (should only have to be done once) +#ifdef _MPI + call MPI_Alltoall(sendNeigh, 1, MPI_LOGICAL, recvNeigh, 1, MPI_LOGICAL, domain % dminfo % comm, mpi_ierr) +#endif +#ifdef _MPI + !call MPI_Barrier(domain % dminfo % comm, err) +#endif + LIGHT_DEBUG_WRITE('Finished all to all with mpi_ierr = ' COMMA mpi_ierr) + LIGHT_DEBUG_WRITE('sendNeigh = ' COMMA sendNeigh) + LIGHT_DEBUG_WRITE('recvNeigh= ' COMMA recvNeigh) + LIGHT_DEBUG_WRITE('communicate = ' COMMA sendNeigh .or. recvNeigh) + + ! could possibly optimize here by keeping track of send / recv lists separately + ! however, if there is nothing to be sent the only message that is sent + ! is the number of particles to be transferred... + ! update ioProcNeighs from logical lists + ! "add" the lists + recvNeigh = recvNeigh .or. sendNeigh + allocate(tempInt(nTotProcs)) + ! this can artificially create a problem if there isn't a single block to a processor + tempInt = domain % dminfo % my_proc_id + do i=1,nTotProcs + if (recvNeigh(i)) tempInt(i) = i-1 + end do + deallocate(sendNeigh) + deallocate(recvNeigh) + + ! get a complete list of the processors (including itself) + call uniqueIntegerList(tempInt,ioProcNeighs) + call removeValueFromIntList(ioProcNeighs, domain % dminfo % my_proc_id) + LIGHT_DEBUG_WRITE('ioProcNeighs = ' COMMA ioProcNeighs) + deallocate(tempInt) + + end subroutine mpas_particle_list_build_halos !}}} + +!*********************************************************************** +! +! routine mpas_particle_list_update_halos_end +! +!> \brief Updates halo processors for communication, noting that +!> the receiving processors must be informed of changes +!> \author Phillip Wolfram +!> \date 07/08/2014 +!> \details +!> This routine transmits a logical list of processors that will +!> transmit data for each compProc or ioProc. On the compProcs or +!> ioProcs, these lists must be aggregated to build out the full list +!> of processors from which data will be received. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_update_halos_end(domain, err, destinationName, sendProcNeighs, sendProcSendList, & !{{{ + sendProcRecvList) + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + logical, dimension(:,:), intent(in) :: sendProcRecvList !< x: sendProcNeighs for send. y: each receiving + !< processors on x denoted by true + ! 'ioBlock' and 'currentBlock' are options for destinationName + character(len=*), intent(in) :: destinationName + logical, dimension(:), pointer, intent(inout) :: sendProcSendList !< location of true indicates processors to send data to + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + integer, dimension(:), pointer, intent(inout) :: sendProcNeighs !< list of io halo processors + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: i, nsendProcNeighs, nProcs + logical, dimension(:), pointer :: completeList + logical, dimension(:,:), pointer :: recvList + integer, dimension(:), pointer :: intArray + integer, dimension(:), pointer :: sendRequestID, recvRequestID + integer :: mpi_ierr + logical :: firstTime + + err = 0 + + ! compute send list now that all particles reside on correct block (processor) 'currentBlock' + ! didn't show up with serial IO because all computational processors sent data to proc 0 + ! note that this could be missing communication where before transfer particle on proc A + ! has sendProc of A and is sent to B (must have previously kept a record that B is in A's halo list + ! note, may be slightly redundant because we could update sendProcSendList once particles are transfered + call compute_additional_particle_send_list(domain, destinationName, sendProcSendList) + + ! need to update IO processors as to the change also so that they know where to get data from!!! + LIGHT_DEBUG_WRITE('destinationName= ' COMMA destinationName) + LIGHT_DEBUG_WRITE('sendProcSendList = ' COMMA sendProcSendList) + LIGHT_DEBUG_WRITE('sendProcRecvList = ' COMMA sendProcRecvList) + LIGHT_DEBUG_WRITE('sendProcNeighs before = ' COMMA sendProcNeighs) + + ! proceed to update the halo + nProcs = domain % dminfo % nprocs + nsendProcNeighs = size(sendProcNeighs) + allocate(completeList(nProcs), recvList(nProcs,nsendProcNeighs)) + allocate(sendRequestID(nsendProcNeighs), recvRequestID(nsendProcNeighs)) + + completeList = .False. + recvList = .False. + ! for each sendProc, send logical array information + do i = 1, nsendProcNeighs +#ifdef _MPI + call MPI_ISend(sendProcRecvList(:,i), nProcs, MPI_LOGICAL, sendProcNeighs(i), domain % dminfo % my_proc_id, & + domain % dminfo % comm, sendRequestID(i), mpi_ierr) +#endif + LIGHT_DEBUG_WRITE('sendProcNeigh= ' COMMA sendProcNeighs(i)) + LIGHT_DEBUG_WRITE('send data = ' COMMA sendProcRecvList(i,:)) +#ifdef _MPI + call MPI_IRecv(recvList(:,i), nProcs, MPI_LOGICAL, sendProcNeighs(i), sendProcNeighs(i), & + domain % dminfo % comm, recvRequestID(i), mpi_ierr) +#endif + end do + +#ifdef _MPI + ! wait until the data is in the buffer + call MPI_WaitAll(nsendProcNeighs,recvRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + + do i = 1, nsendProcNeighs + ! aggregate results after wait, making sure that we have the most + ! comprehensive list of sendProcs for receiving + LIGHT_DEBUG_WRITE('sendProcNeigh= ' COMMA sendProcNeighs(i)) + LIGHT_DEBUG_WRITE('recvList before = ' COMMA recvList(:,i)) + LIGHT_DEBUG_WRITE('completeList before = ' COMMA completeList) + completeList = completeList .or. recvList(:,i) + LIGHT_DEBUG_WRITE('recvList after = ' COMMA recvList(:,i)) + LIGHT_DEBUG_WRITE('completeList after = ' COMMA completeList) + end do + + ! wait to make sure (just in case) that all sends have completed +#ifdef _MPI + call MPI_WaitAll(nsendProcNeighs, sendRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + + ! "add" receiving and sending lists into a comple list + completeList = completeList .or. sendProcSendList + !print *, 'completeList = ', completeList + + ! convert complete list into a unique list of processor numbers + allocate(intArray(nProcs)) + firstTime = .True. + do i = 1, nProcs + if (completeList(i)) then + if (firstTime) then + intArray = i - 1 + firstTime = .False. + else + intArray(i) = i - 1 + end if + end if + end do + + ! now get the desired integer halo list + deallocate(sendProcNeighs) + call uniqueIntegerList(intArray, sendProcNeighs) + call removeValueFromIntList(sendProcNeighs, domain % dminfo % my_proc_id) + + deallocate(intArray, completeList, sendRequestID, recvRequestID) + ! need to update IO processors as to the change also so that they know where to get data from!!! + ! should uncomment for testing when multiple sendProcs are utilized (parallel IO) + LIGHT_DEBUG_WRITE('sendProcSendList after = ' COMMA sendProcSendList) + LIGHT_DEBUG_WRITE('sendProcRecvList after = ' COMMA sendProcRecvList) + LIGHT_DEBUG_WRITE('sendProcNeighs after = ' COMMA sendProcNeighs) + + end subroutine mpas_particle_list_update_halos_end !}}} + +!*********************************************************************** +! +! routine mpas_particle_list_transfer_particles_from_block_to_named_block +! +!> \brief Move particles to the appropriate currentBlock +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine uses MPI communication to ensure particles end up +!> on their appropriate currentBlock. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_transfer_particles_from_block_to_named_block(domain, err, & !{{{ + haloOnly, copyOnly, namedBlock, procNeighs) + implicit none + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + integer, dimension(:), pointer, intent(in) :: procNeighs + character(len=*), intent(in) :: namedBlock + logical, intent(in) :: copyOnly, haloOnly + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer, dimension(:), pointer :: nPartSend => NULL(), nPartRecv => NULL() + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_list_of_particle_list_type), dimension(:), pointer :: listPLSend => NULL(), listPLRecv => NULL() + + err = 0 + + ! allocate particle list in terms of its communication dimesion to other processors + allocate(listPLSend(size(procNeighs)),listPLRecv(size(procNeighs))) + allocate(nPartSend(size(procNeighs)),nPartRecv(size(procNeighs))) + + ! if statement inside each of the group's subroutines needs removed for parallel IO which + ! assumes a high-level nParticle decomposition which will allocate IO blocks via the + ! decomposition + + + ! this is the communication list and it needs to be a bi-directional graph so that + ! sending processors have their receiving processor listening, even if there is no + ! data transfer required + + ! presently don't distribute particles from each block to correct, owning blocks + !1. communicating from block to block (basically like make_proc_to_proc_particlelists but for blocks) + ! this is all done locally and all it will do is affect the particlelists on each block, forming temporary lists, + ! and then appending particles on these temporary lists back to block % particlelist + !this doesn't have to be tested unless there is more than one block per processor, + !which presently is not how things are done + ! this takes the block % particlelist on the first block and makes sure + ! it is appropriately distributed on other blocks on the same processor + ! call make_block_to_block_particlelists(domain) + + + ! 1. Make temporary particle lists for transfers based on currentBock of cells in halo. + ! These lists live on each block and correspond to other + ! blocks that the list must be moved to. The particle must end up on its currentBlock specified. + ! convention on particles lists is that g_compProcNeighs specifies the processor neighbor numbers + ! corresponding to each index in the particlelists pointer array. Should use linked-list + ! because processor neighbors are typically going to be somewhere less than 10 + ! (perfect partitioning of plane gives hexagons with 6 cell neighbors, for instance). + ! strategy is to make linked list corresponding to each gProcNeigh and then append + ! particles beloning to the list on the list + LIGHT_DEBUG_WRITE('before make_proc_to_proc_particlelists') +#ifdef MPAS_DEBUG + call MPI_Barrier(domain % dminfo % comm, err) + call mpas_particle_list_test_numparticles_to_neighprocs(domain % dminfo % my_proc_id, procNeighs, procNeighs) + call MPI_Barrier(domain % dminfo % comm, err) +#endif + call make_proc_to_proc_particlelists(domain, copyOnly, namedBlock, listPLSend, procNeighs, err) + LIGHT_DEBUG_WRITE('after make_proc_to_proc_particlelists') +#ifdef MPAS_DEBUG + call MPI_Barrier(domain % dminfo % comm, err) + call mpas_particle_list_test_numparticles_to_neighprocs(domain % dminfo % my_proc_id, procNeighs, procNeighs) + call MPI_Barrier(domain % dminfo % comm, err) +#endif + LIGHT_DEBUG_WRITE('finished make_proc_to_proc_particlelists') +#ifdef MPAS_DEBUG + call MPI_Barrier(domain % dminfo % comm, err) + call mpas_particle_list_test_num_current_particlelist(domain) + call MPI_Barrier(domain % dminfo % comm, err) + call test_num_particles_on_particlelist(listPLSend, size(procNeighs)) + call MPI_Barrier(domain % dminfo % comm, err) +#endif + + ! now move data from one proc to another, assuming that data is move to 1st block + ! on foreign processor + ! tell other processors how many particles are going to be communicated to them + call get_num_particlelists(listPLSend, size(procNeighs), nPartSend) +#ifdef MPAS_DEBUG + LIGHT_DEBUG_WRITE('finished get_num_particlelists') + LIGHT_DEBUG_WRITE('proc id=' COMMA domain % dminfo % my_proc_id COMMA ' comm=' COMMA domain % dminfo % comm) + LIGHT_DEBUG_WRITE('before 1st barrier communicate_num_particles_send_recv') + call MPI_Barrier(domain % dminfo % comm, err) + LIGHT_DEBUG_WRITE('after 1st barrier communicate_num_particles_send_recv') +#endif + call communicate_num_particles_send_recv(domain, procNeighs, nPartSend, nPartRecv) +#ifdef MPAS_DEBUG + ! Error is above of here + LIGHT_DEBUG_WRITE('before 2nd barrier communicate_num_particles_send_recv') + call MPI_Barrier(domain % dminfo % comm, err) + LIGHT_DEBUG_WRITE('after 2nd barrier communicate_num_particles_send_recv') + LIGHT_DEBUG_WRITE('finished communicate_num_particles_send_recv') +#endif + + !2. communicating from processor to processor + ! make appropriate list + call allocate_list_particlelists(nPartRecv, listPLRecv) + + ! communicate data (assumes that each processor has knowledge about construction of the pool lagrPartTrackPoolHalo, + ! from the registry. If this changes, this will break this member... It also assumes the code is deterministic + ! and that pools are built and computed the exact same way on each processor. + call mpas_timer_start("communicate_data_halo_LPT") + !call MPI_Barrier(domain % dminfo % comm, err) + call communicate_particle_halo_data(domain, procNeighs, nPartSend, nPartRecv, listPLSend, listPLRecv) + !call MPI_Barrier(domain % dminfo % comm, err) + call mpas_timer_stop("communicate_data_halo_LPT") + LIGHT_DEBUG_WRITE('finished communicate_particle_halo_data') + + call mpas_timer_start("communicate_data_nonhalo_LPT") + if(haloOnly) then + ! need to also allocate the nonHalo data somehow, it just needs initialized so that it can be "filled in" when + ! necessary for output + ! can utilize empty 0'd fields for the nonhalo data portion to initialize the field for all the processors + call allocate_list_nonHalo_data(domain, listPLRecv) + else + call communicate_particle_nonhalo_data(domain, procNeighs, nPartSend, nPartRecv, listPLSend, listPLRecv) + end if + call mpas_timer_stop("communicate_data_nonhalo_LPT") + + ! now there should be the complete particles on listPLRecv. These, however, need moved to the blocks of the processor + ! for use in calculations + !3. communicating from block to block + call distribute_particlelist_to_blocks(domain, namedBlock, listPLRecv) + + LIGHT_DEBUG_WRITE('finished distributing particlelist') + LIGHT_DEBUG_WRITE('halo procs = ' COMMA procNeighs) + LIGHT_DEBUG_WRITE('nSend = ' COMMA nPartSend COMMA ' nRecv = ' COMMA nPartRecv) + + ! deallocate listPLSend and listPLRecv + if (copyOnly) then + ! just empty the list without destroying the particle data + !call mpas_log_write( 'just emptying the particlelist') + call empty_list_particlelists(listPLSend) + else + ! remove list of particles as well as particle data because it was just sent to the other processors + call destory_list_particlelists(listPLSend) + endif + ! just empty the list without destroying the particle data (because we need particles that were just transferred!) + call empty_list_particlelists(listPLRecv) + + deallocate(nPartSend, nPartRecv) + + LIGHT_DEBUG_WRITE('finished mpas_particle_list_test_num_current_particlelist') + + end subroutine mpas_particle_list_transfer_particles_from_block_to_named_block !}}} + +!*********************************************************************** +! +! routine mpas_particle_list_write_halo_data +! +!> \brief Writes haloData to struct arrays for output +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine writes haloData output for this MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_write_halo_data(domain, err)!{{{ + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_particle_list_type), pointer :: particlelist + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_pool_iterator_type) :: dimItr + type (field1DReal), pointer :: field1DRealPointer + type (field1DInteger), pointer :: field1DIntPointer + real (kind=RKIND), dimension(:), pointer :: Array1DRealPointer => NULL() + integer, dimension(:), pointer :: Array1DIntPointer => NULL() + integer, dimension(:), pointer :: indexToParticleIDOriginal => NULL(), & + indexToParticleIDNew => NULL(), & + orderingVector => NULL() + character (len=StrKIND) :: message + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! particle related pointers + particlelist => block % particlelist + ! iterate over each member of the pool and make the relevant assignment + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackHalo', lagrPartTrackPool) + ! need to compute the ordering matrices + call mpas_pool_get_array(lagrPartTrackPool, 'indexToParticleID', indexToParticleIDOriginal) + call get_halo_data_from_particle_list_array(particlelist, 'indexToParticleID', indexToParticleIDNew) + ! note: orderingVector can be a subset of indexToParticleIDNew because this index can include compute as well + ! as IO particles however, it must be of the same size as indexToParticleIDOriginal + call compute_ordering_vector(indexToParticleIDOriginal, indexToParticleIDNew, orderingVector) + LIGHT_DEBUG_WRITE('write halo data') + LIGHT_DEBUG_WRITE('indexToParticleIDOriginal =' COMMA indexToParticleIDOriginal) + LIGHT_DEBUG_WRITE('indexToParticleIDNew =' COMMA indexToParticleIDNew) + LIGHT_DEBUG_WRITE('ordering vector=' COMMA orderingVector) + + allocate(Array1DRealPointer(count_particlelist(particlelist))) + allocate(Array1DIntPointer(count_particlelist(particlelist))) + + ! iterate over contents of pool and transfer + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + ! determine the type of data + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_REAL) then + ! get data and place it in appropriate array + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DRealPointer) + !{{{ + LIGHT_DEBUG_WRITE('write halo data') + LIGHT_DEBUG_WRITE('member name =' COMMA trim(dimItr % memberName)) + LIGHT_DEBUG_WRITE('particlelistSize= ' COMMA count_particlelist(particlelist)) + LIGHT_DEBUG_WRITE('memory arraysize= ' COMMA size(field1DRealPointer % array)) + LIGHT_DEBUG_WRITE(trim(dimItr % memberName) COMMA ' = ' COMMA field1DRealPointer % array) + !}}} + call get_halo_data_from_particle_list_array(particlelist, dimItr % memberName, Array1DRealPointer) + ! reorder + field1DRealPointer % array = Array1DRealPointer(orderingVector) + elseif (dimItr % dataType == MPAS_POOL_INTEGER) then + ! get data and place it in appropriate array + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DIntPointer) + call get_halo_data_from_particle_list_array(particlelist, dimItr % memberName, Array1DIntPointer) + ! reorder + field1DIntPointer % array = Array1DIntPointer(orderingVector) + else + LIGHT_DEBUG_ALL_WRITE("Different field type than implemented in nonHalo write!") + end if + elseif (dimItr % memberType == MPAS_POOL_DIMENSION) then + ! ignore dimensions for now and have this code so they aren't printed as an error message + else + write(message, *) "Different type expected in registry for key ", trim(dimItr % memberName), & + " in nonHalo data for write-- don't know what to do!" + LIGHT_DEBUG_ALL_WRITE(message) + end if + end do + + ! free memory for the next loop + deallocate(indexToParticleIDNew) + deallocate(orderingVector) + deallocate(Array1DRealPointer) + deallocate(Array1DIntPointer) + + block => block % next + end do + + end subroutine mpas_particle_list_write_halo_data!}}} + +!*********************************************************************** +! +! routine mpas_particle_list_write_nonhalo_data +! +!> \brief Writes nonhaloData to struct arrays for output +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine writes nonhaloData output for this MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_write_nonhalo_data(domain, err)!{{{ + + implicit none + + !----------------------------------------------------------------- + ! input variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_particle_list_type), pointer :: particlelist + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_pool_iterator_type) :: dimItr + type (field1DReal), pointer :: field1DRealPointer + type (field1DInteger), pointer :: field1DIntPointer + real (kind=RKIND), dimension(:), pointer :: Array1DRealPointer => NULL() + integer, dimension(:), pointer :: Array1DIntPointer => NULL() + integer, dimension(:), pointer :: indexToParticleIDOriginal => NULL(), & + indexToParticleIDNew => NULL(), & + orderingVector =>NULL() + character (len=StrKIND) :: message + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! particle related pointers + particlelist => block % particlelist + ! iterate over each member of the pool and make the relevant assignment + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackHalo', lagrPartTrackPool) + ! need to compute the ordering matrices + call mpas_pool_get_array(lagrPartTrackPool, 'indexToParticleID', indexToParticleIDOriginal) + call get_halo_data_from_particle_list_array(particlelist, 'indexToParticleID', indexToParticleIDNew) + ! note: orderingVector can be a subset of indexToParticleIDNew because this index can include compute as well as + ! IO particles however, it must be of the same size as indexToParticleIDOriginal + call compute_ordering_vector(indexToParticleIDOriginal, indexToParticleIDNew, orderingVector) + + ! iterate over each member of the pool and make the relevant assignment + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackNonHalo', lagrPartTrackPool) + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + ! determine the type of data + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_REAL) then + ! get data and place it in appropriate array + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DRealPointer) + !{{{ + LIGHT_DEBUG_WRITE('write nonhalo data') + LIGHT_DEBUG_WRITE('member name =' COMMA dimItr % memberName) + LIGHT_DEBUG_WRITE('particlelistSize= ' COMMA count_particlelist(particlelist)) + LIGHT_DEBUG_WRITE('memory arraysize= ' COMMA size(field1DRealPointer % array)) + !}}} + allocate(Array1DRealPointer(count_particlelist(particlelist))) + call get_nonhalo_data_from_particle_list_array(particlelist, dimItr % memberName, Array1DRealPointer) + ! reorder + field1DRealPointer % array = Array1DRealPointer(orderingVector) + deallocate(Array1DRealPointer) + elseif (dimItr % dataType == MPAS_POOL_INTEGER) then + write(message, *) "Integer type in registry for key ", dimItr % memberName, & + " in nonHalo data for write, not yet tested!" + LIGHT_DEBUG_ALL_WRITE(message) + ! get data and place it in appropriate array + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DIntPointer) + allocate(Array1DIntPointer(count_particlelist(particlelist))) + call get_nonhalo_data_from_particle_list_array(particlelist, dimItr % memberName, Array1DIntPointer) + ! reorder + field1DIntPointer % array = Array1DIntPointer(orderingVector) + deallocate(Array1DIntPointer) + else + LIGHT_DEBUG_ALL_WRITE("Different field type than implemented in nonHalo write!") + end if + elseif (dimItr % memberType == MPAS_POOL_DIMENSION) then + ! ignore dimensions for now and have this code so they aren't printed as an error message + else + write(message, *) "Different type expected in registry for key ", trim(dimItr % memberName), & + " in nonHalo data for write-- don't know what to do!" + LIGHT_DEBUG_ALL_WRITE(message) + end if + end do + + ! free memory for the next loop + deallocate(indexToParticleIDNew) + deallocate(orderingVector) + + block => block % next + end do + + end subroutine mpas_particle_list_write_nonhalo_data!}}} + +!----------------------------------------------------------------------- +! +! PRIVATE SUBROUTINES +! +!----------------------------------------------------------------------- +!{{{ + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine append_particle_to_particlelist +! +!> \brief MPAS add particle to particlelist, starting new list if +!> not allocated +!> \author Phillip Wolfram +!> \date 06/26/2014 +!> \details +!> This routine takes a particle and places it on an existing particlelist, +!> or in the event of no list, initializes the list with the particle +! +!----------------------------------------------------------------------- +subroutine append_particle_to_particlelist(particle, particlelist)!{{{ + implicit none + type (mpas_particle_type), pointer, intent(in) :: particle + type (mpas_particle_list_type), pointer, intent(inout) :: particlelist + type (mpas_particle_list_type), pointer :: headPL=>NULL(), tempPL=>NULL() + + ! case where particeList needs to be created and populated by particle + if(.not.associated(particlelist)) then + allocate(particlelist) + nullify(particlelist % next) + nullify(particlelist % prev) + particlelist % particle => particle + else + ! case where list exists get the head + headPL => particlelist + if (.not.associated(headPL % particle)) then + ! populate empty link + headPL % particle => particle + else + ! build new link + allocate(tempPL) + tempPL % particle => particle + nullify(tempPL % prev) + tempPL % next => headPL + ! connect to the list + headPL % prev => tempPL + particlelist => tempPL + end if + end if + +end subroutine append_particle_to_particlelist !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_halodata_to_particlelist_1Dreal_array +! +!> \brief MPAS get halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine reads 0D real arrays in each particle of the particlelist +!> and places the data into a 1D real field output. +! +!----------------------------------------------------------------------- +subroutine get_halo_data_from_particle_list_1Dreal_array & !{{{ + (particlelist, dataName, array1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + real (kind=RKIND), dimension(:), pointer, intent(out) :: array1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! allocate the array if it isn't allocated + if(.not.associated(array1DRealPointer)) then + allocate(array1DRealPointer(count_particlelist(particlelist))) + end if + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + + call mpas_pool_get_field(particlelistCurr % particle % haloDataPool, dataName, field0DRealPointer) + array1DRealPointer(dataNumber) = field0DRealPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_halo_data_from_particle_list_1Dreal_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_halodata_to_particlelist_1Dreal +! +!> \brief MPAS get halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine reads 0D real arrays in each particle of the particlelist +!> and places the data into a 1D real field output. +! +!----------------------------------------------------------------------- +subroutine get_halo_data_from_particle_list_1Dreal & !{{{ + (particlelist, dataName, field1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DReal), pointer, intent(out) :: field1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + + call mpas_pool_get_field(particlelistCurr % particle % haloDataPool, dataName, field0DRealPointer) + field1DRealPointer % array(dataNumber) = field0DRealPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_halo_data_from_particle_list_1Dreal !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_nonhalodata_to_particlelist_1Dint +! +!> \brief MPAS get nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/07/2014 +!> \details +! +!----------------------------------------------------------------------- +subroutine get_nonhalo_data_from_particle_list_1Dint& !{{{ + (particlelist, dataName, field1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DInteger), pointer, intent(out) :: field1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + call mpas_pool_get_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DIntPointer) + field1DIntPointer % array(dataNumber) = field0DIntPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_nonhalo_data_from_particle_list_1Dint !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_nonhalodata_to_particlelist_1Dint_array +! +!> \brief MPAS get nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/07/2014 +!> \details +! +!----------------------------------------------------------------------- +subroutine get_nonhalo_data_from_particle_list_1Dint_array & !{{{ + (particlelist, dataName, array1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + integer, dimension(:), pointer, intent(out) :: array1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! allocate the array if it isn't allocated + if(.not.associated(array1DIntPointer)) then + allocate(array1DIntPointer(count_particlelist(particlelist))) + end if + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + call mpas_pool_get_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DIntPointer) + array1DIntPointer(dataNumber) = field0DIntPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_nonhalo_data_from_particle_list_1Dint_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_nonhalodata_to_particlelist_1Dreal +! +!> \brief MPAS get nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 06/04/2014 +!> \details +! +!----------------------------------------------------------------------- +subroutine get_nonhalo_data_from_particle_list_1Dreal & !{{{ + (particlelist, dataName, field1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DReal), pointer, intent(out) :: field1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + call mpas_pool_get_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DRealPointer) + field1DRealPointer % array(dataNumber) = field0DRealPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_nonhalo_data_from_particle_list_1Dreal !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_nonhalodata_to_particlelist_1Dreal_array +! +!> \brief MPAS get nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/07/2014 +!> \details +! +!----------------------------------------------------------------------- +subroutine get_nonhalo_data_from_particle_list_1Dreal_array & !{{{ + (particlelist, dataName, array1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + real (kind=RKIND), dimension(:), pointer, intent(out) :: array1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! allocate the array if it isn't allocated + if(.not.associated(array1DRealPointer)) then + allocate(array1DRealPointer(count_particlelist(particlelist))) + end if + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + call mpas_pool_get_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DRealPointer) + array1DRealPointer(dataNumber) = field0DRealPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_nonhalo_data_from_particle_list_1Dreal_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_nonhalodata_to_particlelist_2Dreal +! +!> \brief MPAS get nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 04/10/2014 +!> \details +!> This routine takes a an array of 1D real arrays and places the data into +!> the nonhaloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine get_nonhalo_data_from_particle_list_2Dreal & !{{{ + (particlelist, dataName, field2DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field2DReal), pointer, intent(out) :: field2DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field1DReal), pointer :: field1DRealPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + call mpas_pool_get_field(particlelistCurr % particle % nonhaloDataPool, dataName, field1DRealPointer) + field2DRealPointer % array(dataNumber, :) = field1DRealPointer % array(:) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_nonhalo_data_from_particle_list_2Dreal !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_halodata_to_particlelist_1Dint_array +! +!> \brief MPAS get halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 04/10/2014 +!> \details +!> This routine takes a an array of 1D int arrays and places the data into +!> the haloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine get_halo_data_from_particle_list_1Dint_array & !{{{ + (particlelist, dataName, array1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + integer, dimension(:), pointer, intent(out) :: array1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! allocate the array if it isn't allocated + if(.not.associated(array1DIntPointer)) then + allocate(array1DIntPointer(count_particlelist(particlelist))) + end if + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a int link + do while(associated(particlelistCurr)) + call mpas_pool_get_field(particlelistCurr % particle % haloDataPool, dataName, field0DIntPointer) + array1DIntPointer(dataNumber) = field0DIntPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_halo_data_from_particle_list_1Dint_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_get_halodata_to_particlelist_1Dint +! +!> \brief MPAS get halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 04/10/2014 +!> \details +!> This routine takes a an array of 1D int arrays and places the data into +!> the haloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine get_halo_data_from_particle_list_1Dint & !{{{ + (particlelist, dataName, field1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DInteger), pointer, intent(out) :: field1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a int link + do while(associated(particlelistCurr)) + call mpas_pool_get_field(particlelistCurr % particle % haloDataPool, dataName, field0DIntPointer) + field1DIntPointer % array(dataNumber) = field0DIntPointer % scalar + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine get_halo_data_from_particle_list_1Dint !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_halodata_to_particlelist_1Dreal_array +! +!> \brief MPAS add halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine takes a an array of real scalars and places the data into +!> the haloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_halo_data_to_particle_list_1Dreal_array & !{{{ + (particlelist, dataName, array1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + real (kind=RKIND), dimension(:), intent(in) :: array1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + + allocate(field0DRealPointer) + field0DRealPointer % scalar = array1DRealPointer(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % haloDataPool, dataName, field0DRealPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_halo_data_to_particle_list_1Dreal_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_halodata_to_particlelist_1Dreal +! +!> \brief MPAS add halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine takes a an array of real scalars and places the data into +!> the haloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_halo_data_to_particle_list_1Dreal & !{{{ + (particlelist, dataName, field1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DReal), pointer, intent(in) :: field1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + + allocate(field0DRealPointer) + field0DRealPointer % scalar = field1DRealPointer % array(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % haloDataPool, dataName, field0DRealPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_halo_data_to_particle_list_1Dreal !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_nonhalodata_to_particlelist_1Dreal_array +! +!> \brief MPAS add nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine takes a an array of real scalars and places the data into +!> the nonhaloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_nonhalo_data_to_particle_list_1Dreal_array & !{{{ + (particlelist, dataName, array1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + real (kind=RKIND), dimension(:), pointer :: array1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + + allocate(field0DRealPointer) + field0DRealPointer % scalar = array1DRealPointer(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DRealPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_nonhalo_data_to_particle_list_1Dreal_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_nonhalodata_to_particlelist_1Dreal +! +!> \brief MPAS add nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine takes a an array of real scalars and places the data into +!> the nonhaloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_nonhalo_data_to_particle_list_1Dreal & !{{{ + (particlelist, dataName, field1DRealPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DReal), pointer :: field1DRealPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DReal), pointer :: field0DRealPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a real link + do while(associated(particlelistCurr)) + + allocate(field0DRealPointer) + field0DRealPointer % scalar = field1DRealPointer % array(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DRealPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_nonhalo_data_to_particle_list_1Dreal !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_halodata_to_particlelist_1Dint_array +! +!> \brief MPAS add halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine takes a an array of int scalars and places the data into +!> the haloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_halo_data_to_particle_list_1Dint_array & !{{{ + (particlelist, dataName, array1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + integer, dimension(:), intent(in) :: array1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a int link + do while(associated(particlelistCurr)) + + allocate(field0DIntPointer) + field0DIntPointer % scalar = array1DIntPointer(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % haloDataPool, dataName, field0DIntPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_halo_data_to_particle_list_1Dint_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_halodata_to_particlelist_1Dint +! +!> \brief MPAS add halodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine takes a an array of int scalars and places the data into +!> the haloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_halo_data_to_particle_list_1Dint & !{{{ + (particlelist, dataName, field1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DInteger), pointer, intent(in) :: field1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a int link + do while(associated(particlelistCurr)) + + allocate(field0DIntPointer) + field0DIntPointer % scalar = field1DIntPointer % array(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % haloDataPool, dataName, field0DIntPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_halo_data_to_particle_list_1Dint !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_nonhalodata_to_particlelist_1Dint_array +! +!> \brief MPAS add nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine takes a an array of int scalars and places the data into +!> the nonhaloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_nonhalo_data_to_particle_list_1Dint_array & !{{{ + (particlelist, dataName, array1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + integer, dimension(:), pointer, intent(in) :: array1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a int link + do while(associated(particlelistCurr)) + + allocate(field0DIntPointer) + field0DIntPointer % scalar = array1DIntPointer(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DIntPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_nonhalo_data_to_particle_list_1Dint_array !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine mpas_add_nonhalodata_to_particlelist_1Dint +! +!> \brief MPAS add nonhalodata to particle contained in a list +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine takes a an array of int scalars and places the data into +!> the nonhaloData pool of the particles in the list. It is assumed +!> that the dimensionality of the field is reduced in order by one +!> on each particle. +! +!----------------------------------------------------------------------- +subroutine add_nonhalo_data_to_particle_list_1Dint & !{{{ + (particlelist, dataName, field1DIntPointer) + ! input data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + character(len=*), intent(in) :: dataName + type (field1DInteger), pointer, intent(in) :: field1DIntPointer + + ! subroutine data + integer :: dataNumber + type (mpas_particle_list_type), pointer :: particlelistCurr + type (field0DInteger), pointer :: field0DIntPointer + + ! loop over all elements of the list and insert the data + dataNumber = 1 + particlelistCurr => particlelist + ! while we have a int link + do while(associated(particlelistCurr)) + + allocate(field0DIntPointer) + field0DIntPointer % scalar = field1DIntPointer % array(dataNumber) + call mpas_pool_add_field(particlelistCurr % particle % nonhaloDataPool, dataName, field0DIntPointer) + + ! increment for new dataNumber + dataNumber = dataNumber + 1 + ! get next link + particlelistCurr => particlelistCurr % next + end do + +end subroutine add_nonhalo_data_to_particle_list_1Dint !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine count_particlelist_particles +! +!> \brief MPAS count particles in particlelist (must be allocated) +!> \author Phillip Wolfram +!> \date 04/14/2014 +!> \details +!> This routine counts number of particles in particlelist. +! +!----------------------------------------------------------------------- +integer function count_particlelist_particles(particlelist) !{{{ + implicit none + ! input/output data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + + ! subroutine data + type (mpas_particle_list_type), pointer :: ParticleLinkCurr + + count_particlelist_particles = 0 + !call mpas_log_write( 'count_particlelist') + !if(.not.associated(particlelist)) then + ! call mpas_log_write( 'particleLinkCurr not associated') + ! return + !end if + + ! current link + particleLinkCurr => particlelist + + do while (associated(particleLinkCurr)) + ! increment the list + if (associated(particleLinkCurr % particle)) then + count_particlelist_particles = count_particlelist_particles + 1 + end if + ! get next item on the list + particleLinkCurr => particleLinkCurr % next + end do + + return + +end function count_particlelist_particles !}}} + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine build_new_particlelist +! +!> \brief MPAS build list of particles +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine builds up a list of empty particles ready to be populated. +!> This is just a raw constructor for a particle list. This assumes +!> that a list is more than 1 link, such that nParticles >= 2. +! +!----------------------------------------------------------------------- +subroutine build_new_particlelist(nParticles, particlelist, ioBlock) !{{{ + ! input/output data + ! number of particles + integer, intent(in) :: nParticles + integer, intent(in), optional :: ioBlock + type (field0dInteger), pointer :: ioBlockfield + type (mpas_particle_list_type), pointer, intent(inout) :: particlelist + ! subroutine data + integer aParticle + type (mpas_particle_type), pointer :: particle + type (mpas_particle_list_type), pointer :: newParticleLink + type (mpas_particle_list_type), pointer :: ParticleLinkCurr + + integer :: counter + + if(nParticles == 0) then + return + end if + + ! instantiate a list of empty particles of dimension nParticles + if(.not.associated(particlelist)) then + allocate(particlelist) + end if + + ! allocate memory for the new particle + allocate(particle) + call mpas_pool_create_pool(particle % haloDataPool) + call mpas_pool_create_pool(particle % nonhaloDataPool) + if (present(ioBlock)) then + allocate(ioBlockfield) + ioBlockfield % scalar = ioBlock + call mpas_pool_add_field(particle % haloDataPool, 'ioBlock', ioBlockfield) + end if + + !! allocate start of list link (this must have already been done! + ! assign allocated particle memory to link + particlelist % particle => particle + + ! current link + particleLinkCurr => particlelist + + ! add more links + do aParticle = 2, nParticles + ! allocate memory for the new particle + allocate(particle) + call mpas_pool_create_pool(particle % haloDataPool) + call mpas_pool_create_pool(particle % nonhaloDataPool) + if(present(ioBlock)) then + allocate(ioBlockfield) + ioBlockfield % scalar = ioBlock + call mpas_pool_add_field(particle % haloDataPool, 'ioBlock', ioBlockfield) + end if + ! we already have one link so make a new one + allocate(newParticleLink) + ! place the particle in the list link + newParticleLink % particle => particle + nullify(newParticleLink % next) + ! connect new link to current link + newParticleLink % prev => particleLinkCurr + ! next link is the new link + particleLinkCurr % next => newParticleLink + ! reset link to last link + particleLinkCurr => newParticleLink + end do + +end subroutine build_new_particlelist !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine count_particlelist +! +!> \brief MPAS count particles in particlelist +!> \author Phillip Wolfram +!> \date 04/14/2014 +!> \details +!> This routine counts number of particles in particlelist. +! +!----------------------------------------------------------------------- +integer function count_particlelist(particlelist) !{{{ + implicit none + ! input/output data + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + + ! subroutine data + type (mpas_particle_list_type), pointer :: ParticleLinkCurr + + count_particlelist = 0 + !call mpas_log_write( 'count_particlelist') + !if(.not.associated(particlelist)) then + ! call mpas_log_write( 'particleLinkCurr not associated') + ! return + !end if + + ! current link + particleLinkCurr => particlelist + + do while (associated(particleLinkCurr)) + ! increment the list + count_particlelist = count_particlelist + 1 + !print *, 'count_particlelist = ', count_particlelist + ! get next item on the list + particleLinkCurr => particleLinkCurr % next + end do + + return + +end function count_particlelist !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine destroy_particle +! +!> \brief MPAS destroy particle +!> \author Phillip Wolfram +!> \date 06/03/2014 +!> \details +!> This routine destroys a particle, deallocating its memory +! +!----------------------------------------------------------------------- +subroutine destroy_particle(particle) !{{{ + implicit none + + type (mpas_particle_type), pointer, intent(inout) :: particle + + if(associated(particle)) then + if(associated(particle % haloDataPool)) then + call mpas_pool_destroy_pool(particle % haloDataPool) + end if + if(associated(particle % nonhaloDataPool)) then + call mpas_pool_destroy_pool(particle % nonhaloDataPool) + end if + deallocate(particle) + end if + +end subroutine destroy_particle !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine empty_particlelist +! +!> \brief MPAS empty particlelist +!> \author Phillip Wolfram +!> \date 06/27/2014 +!> \details +!> This routine emptys a particlelist, deallocating its memory +!> but keeping memory of particles it contains intact +! +!----------------------------------------------------------------------- +subroutine empty_particlelist(particlelist) !{{{ + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + type (mpas_particle_list_type), pointer :: pLCurr, pLCurrTemp + + plCurr => particlelist + do while(associated(plCurr)) + pLCurrTemp => pLCurr + pLCurr => pLCurr % next + deallocate(pLCurrTemp) + ! N.B., particle contained in pLCurrTemp is not deallocated!!! + end do + +end subroutine empty_particlelist !}}} + +subroutine destory_list_particlelists(listParticleLists) !{{{ + type (mpas_list_of_particle_list_type), dimension(:), pointer, intent(inout) :: listParticleLists + integer :: i, sizeList + + if(associated(listParticleLists)) then + sizeList = size(listParticleLists) + do i=1,sizeList + call mpas_particle_list_destroy_particle_list(listParticleLists(i) % list) + end do + deallocate(listParticleLists) + end if + +end subroutine destory_list_particlelists !}}} + +subroutine empty_list_particlelists(listParticleLists) !{{{ + type (mpas_list_of_particle_list_type), dimension(:), pointer, intent(inout) :: listParticleLists + integer :: i, sizeList + + if(associated(listParticleLists)) then + sizeList = size(listParticleLists) + do i=1,sizeList + call empty_particlelist(listParticleLists(i) % list) + end do + deallocate(listParticleLists) + end if + +end subroutine empty_list_particlelists!}}} + +!*********************************************************************** +! +! routine compute_cellOwnerBlock(domain, err) +! +!> \brief Compute owner block arrays to specify halo ownership +!> \author Phillip Wolfram +!> \date 06/25/2014 +!> \details +!> This routine computes the cellOwnerBlock for all cells on a block, +!> diagnosing the block which owns each cell. Could potentially be +!> generalized and put in framework (probably need package variables +!> if particles are used). +! +!----------------------------------------------------------------------- + subroutine compute_cellOwnerBlock(domain, err) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (field1DInteger), pointer :: cellOwnerBlock + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! get pool to access cellOwnerBlock + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackCells', lagrPartTrackPool) + ! prepare cellOwnerBlock for use in determining blockID to + ! be passed from one processor to another + call mpas_pool_get_field(lagrPartTrackPool, 'cellOwnerBlock', cellOwnerBlock) + ! set cellOwnerBlock to be current block + cellOwnerBlock % array(:) = block % blockID + + block => block % next + end do + ! exchange halos + call mpas_dmpar_exch_halo_field(cellOwnerBlock) + + end subroutine compute_cellOwnerBlock !}}} + +!*********************************************************************** +! +! routine compute_blockNeighs(domain, err) +! +!> \brief Compute neighboring blocks from owner block, +!> parsing the halo to get unique values +!> \author Phillip Wolfram +!> \date 06/26/2014 +!> \details +!> This routine computes the neighboring blocks block % blockNeighs +!> for each block. Note that the size of this is not predetermined +!> and depends on the partitioning (typically in graph.info.part.#) +! +!----------------------------------------------------------------------- + subroutine compute_blockNeighs(domain, err) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (field1DInteger), pointer :: cellOwnerBlock + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! get pool to access cellOwnerBlock + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackCells', lagrPartTrackPool) + ! get cellOwnerBlock + call mpas_pool_get_field(lagrPartTrackPool, 'cellOwnerBlock', cellOwnerBlock) + + ! compute unique list obtained from cellOwnerBlock + call uniqueIntegerList(cellOwnerBlock % array, block % blockNeighs) + + block => block % next + end do + + end subroutine compute_blockNeighs !}}} + +!*********************************************************************** +! +! routine compute_block_procNeighs(domain, err) +! +!> \brief Compute neighboring processors from blockNeighs, +!> getting unique values on a block +!> \author Phillip Wolfram +!> \date 06/26/2014 +!> \details +!> This routine computes the neighboring processors corresponding to +!> block % blockNeighs for each block. +! +!----------------------------------------------------------------------- + subroutine compute_block_procNeighs(domain, err) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + !type (mpas_pool_type), pointer :: lagrPartTrackPool + integer, dimension(:), pointer :: array + integer :: numBlockNeighs, i + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! convert blockNeighs to procNeighs array + numBlockNeighs = size(block % blockNeighs) + allocate(array(numBlockNeighs)) + do i=1, numBlockNeighs + call mpas_get_owning_proc(domain % dminfo, block % blockNeighs(i), array(i)) + end do + ! compute unique list for processor neighbors + call uniqueIntegerList(array, block % procNeighs) + + ! free up temporary memory + deallocate(array) + + ! get the next block + block => block % next + end do + + end subroutine compute_block_procNeighs !}}} + +!*********************************************************************** +! +! routine compute_procNeighs(domain, err) +! +!> \brief Compute neighboring processors from blockNeighs, +!> getting unique values across all blocks +!> \author Phillip Wolfram +!> \date 06/26/2014 +!> \details +!> This routine computes the neighboring processors corresponding to +!> block % procNeighs for each block. +! +!----------------------------------------------------------------------- + subroutine compute_procNeighs(domain, err, procNeighs) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + integer, dimension(:), pointer, intent(out) :: procNeighs + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + integer, dimension(:), pointer :: tempIntegerArray + integer :: localSize, totalSize, iStart + + err = 0 + + totalSize = 0 + + block => domain % blocklist + do while(associated(block)) + totalSize = totalSize + size(block % procNeighs) + block => block % next + end do + + allocate(tempIntegerArray(totalSize)) + + block => domain % blocklist + iStart = 1 + do while(associated(block)) + localSize = size(block % procNeighs) + tempIntegerArray(iStart:iStart+localSize-1) = block % procNeighs + iStart = iStart + localSize + block => block % next + end do + + ! get unique array for complete list + call uniqueIntegerList(tempIntegerArray, procNeighs) + call removeValueFromIntList(procNeighs, domain % dminfo % my_proc_id) + + deallocate(tempIntegerArray) + + end subroutine compute_procNeighs !}}} + + +!*********************************************************************** +! +! routine removeValueFromIntList(list, removeval) +! +!> \brief Remove value removeval from list vector +!> \author Phillip Wolfram +!> \date 10/22/2015 +!> \details +!> This routine remotes the removeval value from the list in-place. +! +!----------------------------------------------------------------------- + subroutine removeValueFromIntList(list, removeval) !{{{ + implicit none + integer, dimension(:), pointer, intent(inout) :: list + integer, intent(in) :: removeval + + integer, dimension(:), pointer :: tmparray + integer :: nsize, i + + ! get size of new array + nsize = 0 + do i = 1, size(list) + if (list(i) /= removeval) then + nsize = nsize + 1 + end if + end do + + allocate(tmparray(nsize)) + nsize = 0 + do i = 1, size(list) + if (list(i) /= removeval) then + nsize = nsize + 1 + tmparray(nsize) = list(i) + end if + end do + + ! resize the list + deallocate(list) + allocate(list(nsize)) + + ! transfer contents back to list + do i = 1, nsize + list(i) = tmparray(i) + end do + + deallocate(tmparray) + + end subroutine removeValueFromIntList + +!*********************************************************************** +! +! routine mpas_particle_list_self_union_halo_lists(self, list) +! +!> \brief Taken union of self and list and store in self +!> \author Phillip J. Wolfram +!> \date 10/29/2015 +!> \details +!> This routine computes the unique entries in an array using a +!> linked list for dynamic memory storage +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_self_union_halo_lists(self, list, nprocs, procid) !{{{ + implicit none + integer, dimension(:), pointer, intent(inout) :: self + integer, dimension(:), pointer, intent(in) :: list + integer, intent(in) :: nprocs + integer, intent(in) :: procid + + logical, dimension(:), pointer :: theunion + integer :: i, thesum + + allocate(theunion(nprocs)) + theunion = .False. + + ! use Fortran integer indexing + theunion(self+1) = .True. + theunion(list+1) = .True. + + ! compute number of processors in halo + thesum = 0 + do i=1,nprocs + if (theunion(i)) then + thesum = thesum + 1 + end if + end do + + ! rebuild up self + deallocate(self) + allocate(self(thesum)) + + ! store processor number in new list + thesum = 0 + do i=1,nprocs + if (theunion(i)) then + thesum = thesum + 1 + self(thesum) = i-1 + end if + end do + + deallocate(theunion) + call removeValueFromIntList(self, procid) + + end subroutine mpas_particle_list_self_union_halo_lists !}}} + +!*********************************************************************** +! +! routine uniqueIntegerList(array, uniqueList) +! +!> \brief Compute unique entries in array with output in uniqueList +!> \author Phillip Wolfram +!> \date 06/26/2014 +!> \details +!> This routine computes the unique entries in an array using a +!> linked list for dynamic memory storage +! +!----------------------------------------------------------------------- + subroutine uniqueIntegerList(array, uniqueList) !{{{ + implicit none + integer, dimension(:), pointer, intent(out) :: uniqueList + integer, dimension(:), pointer, intent(in) :: array + + type simplelist + type (simplelist), pointer :: next => null() + integer :: num + end type simplelist + + type (simplelist), pointer :: listhead, templist, currlist + + integer :: nlist, i + + ! parse the simple list, looking for unique values + ! algorithm will scale like Nblocks*NCells + + if(.not.associated(array)) then + uniqueList => null() + return + end if + + ! first entry + allocate(listhead) + listhead % num = array(1) + nlist = 1 + + do i = 2, size(array) + currlist => listhead + ! check to see if value is on list + do while(associated(currlist)) + if(currlist % num == array(i)) then + exit + else + if(.not.associated(currlist % next)) then + ! we are on the end of the list, so we should add the entry + allocate(templist) + templist % num = array(i) + nlist = nlist + 1 + currlist % next => templist + end if + currlist => currlist % next + end if + end do + end do + + ! now we have a complete, unique list so store it + if(associated(uniqueList)) LIGHT_ERROR_WRITE('Trying to allocate uniqueList, which is already allocated!') + allocate(uniqueList(nlist)) + + currlist => listhead + i = 1 + do while(associated(currlist)) + uniqueList(i) = currlist % num + currlist => currlist % next + i = i + 1 + end do + + ! deallocate linked list + currlist => listhead + do while(associated(currlist)) + templist => currlist % next + deallocate(currlist) + currlist => templist + end do + + end subroutine uniqueIntegerList !}}} + +!*********************************************************************** +! +! routine compute_all_particle_values_unique_int +! +!> \brief Get a unique list of values across particlelists on all blocks +!> \author Phillip Wolfram +!> \date 07/02/2014 +!> \details +!> This routine computes a unique list of values for all the particles +!> on the processor. +! +!----------------------------------------------------------------------- + subroutine compute_all_particle_values_unique_int(domain, err, attrName, attrData) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + character(len=*) :: attrName + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + integer, dimension(:), pointer, intent(out) :: attrData + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + integer, dimension(:), pointer :: tempIntegerArray + integer :: localSize, totalSize, iStart, nPart + + err = 0 + + ! need to get full set of attrData from each processor and form unique list + + totalSize = 0 + + block => domain % blocklist + do while(associated(block)) + nPart = count_particlelist(block % particlelist) + if (nPart > 0) then + allocate(attrData(nPart)) + call get_halo_data_from_particle_list_array(block % particlelist, trim(attrName), attrData) + totalSize = totalSize + size(attrData) + deallocate(attrData) + end if + block => block % next + end do + + if (totalSize > 0) allocate(tempIntegerArray(totalSize)) + + block => domain % blocklist + iStart = 1 + do while(associated(block)) + nPart = count_particlelist(block % particlelist) + if (nPart > 0) then + allocate(attrData(nPart)) + call get_halo_data_from_particle_list_array(block % particlelist, trim(attrName), attrData) + localSize = size(attrData) + tempIntegerArray(iStart:iStart+localSize-1) = attrData + iStart = iStart + localSize + deallocate(attrData) + end if + block => block % next + end do + + ! get unique array for complete list + !if(associated(tempIntegerArray)) print *, 'tempIntegerArray = ', tempIntegerArray + call uniqueIntegerList(tempIntegerArray, attrData) + + if (associated(tempIntegerArray)) deallocate(tempIntegerArray) + + end subroutine compute_all_particle_values_unique_int !}}} + +!*********************************************************************** +! +! routine make_proc_to_proc_particlelist(domain, err) +! +!> \brief Compute neighboring processors from blockNeighs, +!> getting unique values across all blocks and forming the lists +!> \author Phillip Wolfram +!> \date 06/26/2014 +!> \details +!> This routine computes the lists of neighboring processors +! +!----------------------------------------------------------------------- + subroutine make_proc_to_proc_particlelists(domain, copyOnly, blockSendToName, interProcPLArray, procNeighs, err) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + character(len=*), intent(in) :: blockSendToName + logical, intent(in) :: copyOnly + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(in) :: domain + integer, dimension(:), pointer, intent(in) :: procNeighs + type (mpas_list_of_particle_list_type), dimension(:), & + pointer, intent(inout) :: interProcPLArray + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_particle_list_type), pointer :: particlelist, particlelisttemp, particlelisttemp2 + integer :: thisBlock + integer, pointer :: particleBlock + integer :: particleProc, arrayIndex + !type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_particle_type), pointer :: particle + character (len=StrKIND) :: message + + err = 0 + + LIGHT_DEBUG_WRITE('make_proc_to_proc_particlelists') + + block => domain % blocklist + do while(associated(block)) + ! for each particle on each block + particlelist => block % particlelist + do while(associated(particlelist)) + LIGHT_DEBUG_ALL_WRITE('link on particlelist ' COMMA loc(particlelist)) +#ifdef MPAS_DEBUG + if(.not.associated(particlelist % particle)) then + LIGHT_ERROR_WRITE('particle not associated!') + end if +#endif + particle => particlelist % particle +#ifdef MPAS_DEBUG + if(.not.associated(particle % haloDataPool)) then + LIGHT_ERROR_WRITE('particle haloDataPool not associated!') + end if +#endif + call mpas_pool_get_array(particle % haloDataPool, blockSendToName, particleBlock) + ! For example: + !call mpas_pool_get_array(particle % haloDataPool, 'currentBlock', particleBlock) + + LIGHT_DEBUG_ALL_WRITE('particleBlock ' COMMA particleBlock COMMA ' for ' COMMA trim(blockSendToName)) + LIGHT_DEBUG_ALL_WRITE('before mpas call') + call mpas_get_owning_proc(domain % dminfo, particleBlock, particleProc) +#ifdef MPAS_DEBUG + if(particleBlock /= particleProc) then + write(message, *) 'Error if one block per proc: currentBlock = ', particleBlock, ' currentProc = ', particleProc + LIGHT_ERROR_WRITE(message) + end if +#endif + LIGHT_DEBUG_WRITE('after mpas call') + ! determine whether it belongs on thisBlock + LIGHT_DEBUG_WRITE('myproc= ' COMMA domain % dminfo % my_proc_id COMMA ' particleProc= ' COMMA particleProc) + ! eventual support for multiple blocks + !if(particleBlock /= block % blockID) then + if(particleProc /= domain % dminfo % my_proc_id) then + ! we need to move the particle to a list for export to particleProc + ! get index for array and place particle in list at that index location + !call mpas_log_write( 'get array index') + arrayIndex = find_index(procNeighs, particleProc) + if(arrayIndex == -1) LIGHT_ERROR_WRITE('Found processor is not on list of "halo" processors!') + LIGHT_DEBUG_WRITE('ammending particle to processor index ' COMMA arrayIndex) + call append_particle_to_particlelist(particle, interProcPLArray(arrayIndex)%list) + write(message, *) 'added particle ', loc(particle), ' on link ', loc(particlelist), ' to list' + LIGHT_DEBUG_ALL_WRITE(message) + if (.not.copyOnly) then + ! REMOVE PARTICLE FROM EXISTING PARTICLELIST + ! remove particle reference from existing particle list to prevent bug / hitting null + ! particle if particle is deallocated when interProcPLArray particlelists are destroyed + ! next two lines mean (particlelist % prev) % next => particlelist % next, which + ! fortran doesn't allow even though syntactically this makes perfect sense. + ! 3 cases: head, middle, tail + if(associated(particlelist % prev)) then + LIGHT_DEBUG_ALL_WRITE('have previous particlelist link') + particlelisttemp => particlelist % prev + if (associated(particlelist % next)) then + LIGHT_DEBUG_ALL_WRITE('in middle of list') + ! case of the middle + particlelisttemp % next => particlelist % next + particlelisttemp2 => particlelisttemp + ! want to keep particle memory intact because their pointers were passed previously, + ! so just empty the list, don't destroy it and its contents + particlelisttemp => particlelist % next + particlelisttemp % prev => particlelisttemp2 + + particlelisttemp => particlelisttemp % prev + ! just need to remove the single link, particle memory needs to stay intact + !print *, 'deallocating link ', loc(particlelist) + ! deallocate link and get next link + deallocate(particlelist) + particlelist => particlelisttemp + else + LIGHT_DEBUG_ALL_WRITE('in tail of list') + ! case of tail + nullify(particlelisttemp % next) + !print *, 'deallocating link ', loc(particlelist) + ! deallocate link and get next link + deallocate(particlelist) + ! no other links to process + end if + else + if(associated(particlelist % next)) then + LIGHT_DEBUG_ALL_WRITE('in head of list') + ! case of head, set new head (assumes more than one particle) + particlelisttemp => particlelist % next + nullify(particlelisttemp % prev) + deallocate(particlelist) + block % particlelist => particlelisttemp + !print *, 'deallocating link ', loc(particlelist) + ! deallocate link and get next link + particlelist => particlelisttemp + else + LIGHT_DEBUG_ALL_WRITE('single link ' COMMA loc(particlelist)) + LIGHT_DEBUG_ALL_WRITE('block % particlelist' COMMA loc(block % particlelist)) + ! case of single link / particle + LIGHT_DEBUG_ALL_WRITE('deallocating link ' COMMA loc(particlelist)) + deallocate(particlelist) + nullify(block % particlelist) !deallocate(block % particlelist) + ! there is no other particle in the list (we now have 0!) + end if + end if + else + particlelist => particlelist % next + end if + else + LIGHT_DEBUG_ALL_WRITE('just go to the next particle') + particlelist => particlelist % next + end if + end do + + ! this is done for each block because we want processor - processor communication + block => block % next + end do + + end subroutine make_proc_to_proc_particlelists!}}} + + subroutine get_num_particlelists(particlelists, numLists, nPartList) !{{{ + implicit none + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_list_of_particle_list_type), dimension(:), & + pointer, intent(in) :: particlelists + integer, intent(in) :: numLists + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + integer, dimension(:), pointer, intent(inout) :: nPartList + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer :: i, allerr=0 + + ! now get number of particles in each list + ! for each entry in list of particlelist + nPartList = -1 + do i=1, numLists + nPartList(i) = count_particlelist(particlelists(i)%list) + LIGHT_DEBUG_WRITE('nPartList(i)=' COMMA nPartList(i)) + end do + + end subroutine get_num_particlelists !}}} + +!*********************************************************************** +! +! routine communicate_num_particles_send_recv +! +!> \brief Communicate the number of particles to be sent/recv'd +!> from adjacent processors +!> \author Phillip Wolfram +!> \date 06/27/2014 +!> \details +!> This routine transmitts nPartSend to be stored in nPartRecv of +!> adjacent processors in procNeighs. Not that matrix with +!> procNeighs in columns stored in rows for each processor +!> must be symmetric for this to work. +! +!----------------------------------------------------------------------- + subroutine communicate_num_particles_send_recv(domain, procNeighs, nPartSend, nPartRecv) !{{{ + implicit none + + type (domain_type), intent(in) :: domain + integer, dimension(:), pointer, intent(in) :: procNeighs + integer, dimension(:), pointer, intent(in) :: nPartSend + integer, dimension(:), pointer, intent(inout) :: nPartRecv + + integer :: i, j, numProcs + integer, dimension(:), pointer :: requestID + integer :: mpi_ierr + character (len=StrKIND) :: message + + numProcs = size(procNeighs) + + ! set to be -1 for error catching + nPartRecv = -1 + + ! want to send individual values in nPartSend to each entry in procNeighs (paired data) + ! values obtained after communication are to be stored in nPartRecv + +#ifdef MPAS_DEBUG + !i = 990 + domain % dminfo % my_proc_id + !write(message,*) 'proc_debug_message_', domain % dminfo % my_proc_id + !open(unit=i, file=message) + !write(i,*) 'procNeighs=', procNeighs + !write(i,*) '\n' + !write(i,*) 'nPartSend=', nPartSend + !close(unit=i) + +#ifdef _MPI + call MPI_Barrier(domain % dminfo % comm, mpi_ierr) +#endif +#endif + + allocate(requestID(2*numProcs)) +#ifdef MPAS_DEBUG + LIGHT_DEBUG_WRITE('before 1st barrier') +#ifdef _MPI + call MPI_Barrier(domain % dminfo % comm, mpi_ierr) +#endif + LIGHT_DEBUG_WRITE('after 1st barrier') + LIGHT_DEBUG_WRITE('receiving data') + LIGHT_DEBUG_WRITE('numProcs =' COMMA numProcs) +#endif + do i=1,numProcs + LIGHT_DEBUG_WRITE('receiving data i=' COMMA i COMMA ' procNeighs(i)=' COMMA procNeighs(i)) +#ifdef _MPI + call MPI_IRecv(nPartRecv(i), 1, MPI_INTEGERKIND, procNeighs(i), procNeighs(i), & + domain % dminfo % comm, requestID(i), mpi_ierr) +#endif + end do + do i=1,numProcs + LIGHT_DEBUG_WRITE('procNeighs=' COMMA procNeighs) + write(message, *) 'sending data i=', i, ' procNeighs(i)=', procNeighs(i), ' nPartSend(i)=', nPartSend(i) + LIGHT_DEBUG_WRITE(message) +#ifdef _MPI + call MPI_ISend(nPartSend(i), 1, MPI_INTEGERKIND, procNeighs(i), domain % dminfo % my_proc_id, & + domain % dminfo % comm, requestID(numProcs + i), mpi_ierr) +#endif + end do + LIGHT_DEBUG_WRITE('done with send receive calls') +#ifdef _MPI + call MPI_WaitAll(2*numProcs, requestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif +#ifdef MPAS_DEBUG + LIGHT_DEBUG_WRITE('before 2nd barrier') +#ifdef _MPI + call MPI_Barrier(domain % dminfo % comm, mpi_ierr) +#endif + LIGHT_DEBUG_WRITE('after 2nd barrier') + LIGHT_DEBUG_WRITE('done with wait') +#endif + deallocate(requestID) + + end subroutine communicate_num_particles_send_recv !}}} + +!*********************************************************************** +! +! routine compute_additional_particle_send_list +! +!> \brief Update send list +!> \author Phillip Wolfram +!> \date 06/24/2015 +!> \details +!> Compute send list for all particles residing on correct block 'currentBlock' +!----------------------------------------------------------------------- + subroutine compute_additional_particle_send_list(domain, destinationName, ioProcSendList) !{{{ + implicit none + type (domain_type), intent(in) :: domain + character(len=*), intent(in) :: destinationName + logical, dimension(:), pointer, intent(inout) :: ioProcSendList + + ! local variables + type (block_type), pointer :: block + type (mpas_particle_list_type), pointer :: particlelist + type (mpas_particle_type), pointer :: particle + integer, pointer :: sendBlock + integer :: ioProc + + block => domain % blocklist + do while (associated(block)) !{{{ + particlelist => block % particlelist + do while(associated(particlelist)) !{{{ + particle => particlelist % particle + call mpas_pool_get_array(particle % haloDataPool, destinationName, sendBlock) + call mpas_get_owning_proc(domain % dminfo, sendBlock, ioProc) + ioProcSendList(ioProc+1) = .True. + ! get next particle to process on the list + particlelist => particlelist % next + end do !}}} + ! get next block + block => block % next + end do !}}} + + end subroutine compute_additional_particle_send_list !}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine allocate_list_particlelists +! +!> \brief MPAS allocate list of particlelists +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine allocates a list of particlelists. Useful in preparation +!> to receive data from MPI communication +! +!----------------------------------------------------------------------- +subroutine allocate_list_particlelists(nPart, listPL) !{{{ + implicit none + + type (mpas_list_of_particle_list_type), dimension(:), intent(inout), pointer :: listPL + integer, dimension(:), pointer, intent(in) :: nPart + + integer :: nProcs, i + + nProcs = size(nPart) + + ! allocate size of particeLists + do i=1,nProcs + call build_new_particlelist(nPart(i), listPL(i)%list) + end do + +end subroutine allocate_list_particlelists !}}} + +!*********************************************************************** +! +! routine distribute_particlelist_to_blocks +! +!> \brief Take a list of particlelists and move them onto the appropriate +!> block on the processor +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine places particles in a list of particlelist on the appropriate +!> block (currentBlock) on the processor +! +!----------------------------------------------------------------------- + subroutine distribute_particlelist_to_blocks(domain, blockSendToName, listPL) !{{{ + implicit none + + type (domain_type), intent(in) :: domain + type (mpas_list_of_particle_list_type), dimension(:), pointer, intent(in) :: listPL + character(len=*), intent(in) :: blockSendToName + + integer :: i, numLists + type (block_type), pointer :: block + type (mpas_particle_list_type), pointer :: tempPL + type (mpas_particle_type), pointer :: particle + type (mpas_pool_type), pointer :: lagrPartTrackPool + integer, pointer :: blockNum + + ! need to copy pointers to particles from each entry of the listPL and move the + ! particle to the appropriate block + + ! loop over each part of the particle list + numLists = size(listPL) + do i=1,numLists + ! get a single list of particles + tempPL => listPL(i) % list + ! iterate through the list and assign particles + do while(associated(tempPL)) + ! get the particle blockNum + particle => tempPL % particle + call mpas_pool_get_array(particle % haloDataPool, blockSendToName, blockNum) + + ! determine the block the particle should be moved to + block => domain % blocklist + LIGHT_DEBUG_ALL_WRITE('blockNum = ' COMMA blockNum) + blocksearch: do while(associated(block)) + !print *, 'blockID = ', block % blockID, 'blockNum = ', blockNum + if (block % blockID == blockNum) then + ! we have the correct block + exit blocksearch + end if + block => block % next + end do blocksearch + + ! check to make sure block is correct + LIGHT_DEBUG_ALL_WRITE('blockNum = ' COMMA blockNum) + if (blockNum /= block % blockID) then + LIGHT_DEBUG_WRITE('block is not correct! for blockNum =' COMMA blockNum COMMA ' and blockID = ' COMMA block % blockID) + end if + + ! move the particle to the block + call append_particle_to_particlelist(particle, block % particlelist) + + ! get the next particle in the list + tempPL => tempPL % next + end do + + end do + + end subroutine distribute_particlelist_to_blocks !}}} + +!*********************************************************************** +! +! routine compute_ordering_vector +! +!> \brief Compute the orderingVector for restructuring of mixed up particles +!> into original order +!> \author Phillip Wolfram +!> \date 07/03/2014 +!> \details +!> This routine ensures that the particles are ordered consistently with +!> the ordering of the original data. +! +!----------------------------------------------------------------------- + subroutine compute_ordering_vector(arrayOrig, arrayNew, orderingVector) !{{{ + implicit none + + integer, dimension(:), pointer, intent(in) :: arrayOrig, arrayNew + integer, dimension(:), pointer, intent(out) :: orderingVector + + integer :: i, idx + + allocate(orderingVector(size(arrayOrig))) + ! allocate to -1 to that if a value isn't found it isn't random data, assuming arrays + ! are all indexed starting from 1 + orderingVector = -1 + ! loop over each element and figure out index + do i=1,size(arrayOrig) + !N.B. should in principle check to make sure index is found + idx = find_index(arrayNew, arrayOrig(i)) +#ifdef MPAS_DEBUG + if ( idx > 0 ) then +#endif + orderingVector(i) = idx +#ifdef MPAS_DEBUG + else + LIGHT_ERROR_WRITE("ERROR! Didn't find correct ordering index") + end if +#endif + end do + + end subroutine compute_ordering_vector !}}} + +!*********************************************************************** +! +! routine find_index +! +!> \brief find the index corresponding to num in array +!> \author Phillip Wolfram +!> \date 07/03/2014 +!> \details +!> This routine returns the index such that array(find_index) = num. +!> If the index doesn't exist it returns -1. +! +!----------------------------------------------------------------------- + integer function find_index(array, num) !{{{ + implicit none + + integer, dimension(:), pointer, intent(in) :: array + integer, intent(in) :: num + + integer :: i + + ! allocate to negative number to make sure it breaks if + ! an index is not found + + find_index = -1 + ! an error here could be caused by running with different + ! processors (blocks) specified in the input file + ! than run with MPI + do i=1,size(array) + if(num == array(i)) then + find_index = i + return + end if + end do + +#ifdef MPAS_DEBUG + if(find_index == -1) LIGHT_WARNING_WRITE('Error: Index number ' COMMA num COMMA ' not found in array!') +#endif + + end function find_index !}}} + +!*********************************************************************** +! +! routine communicate_particle_nonhalo_data +! +!> \brief Communicate particle nonhalo data to relevant processors +!> based on particlelists +!> \author Phillip Wolfram +!> \date 07/07/2014 +!> \details +!> This routine transmitts nonHaloData from particlelists +! +!----------------------------------------------------------------------- + subroutine communicate_particle_nonhalo_data(domain, procNeighs, nPartSend, nPartRecv, listSend, listRecv) !{{{ + implicit none + + type (domain_type), intent(in) :: domain + integer, dimension(:), pointer, intent(in) :: procNeighs + integer, dimension(:), pointer, intent(in) :: nPartSend + integer, dimension(:), pointer, intent(in) :: nPartRecv + type (mpas_list_of_particle_list_type), dimension(:), pointer :: listSend, listRecv + + integer :: i, j, numProcs, numFields, numRecv, numSends + integer, dimension(:), pointer :: recvRequestID, sendRequestID + integer :: mpi_ierr + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_pool_iterator_type) :: dimItr + type array1DReal_list + real (kind=RKIND), dimension(:), pointer :: val + end type + type array1DInt_list + integer, dimension(:), pointer :: val + end type + type (array1DInt_list), dimension(:), pointer :: array1DIntSend, array1DIntRecv + type (array1DReal_list), dimension(:), pointer :: array1DRealSend, array1DRealRecv + character (len=StrKIND) :: message + + ! for each entry in the halo pool, want to send and recv the data + +#ifdef _MPI + !call MPI_Barrier(domain % dminfo % comm) +#endif + + numProcs = size(procNeighs) + allocate(array1DRealSend(numProcs), array1DRealRecv(numProcs)) + allocate(array1DIntSend(numProcs), array1DIntRecv(numProcs)) + + numSends = 0 + do i = 1, numProcs + if (nPartSend(i) > 0) numSends = numSends + 1 + end do + allocate(sendRequestID(numSends)) + + numRecv = 0 + do i = 1, numProcs + if (nPartRecv(i) > 0) numRecv = numRecv + 1 + end do + allocate(recvRequestID(numRecv)) + + !Notes !{{{ + !! get number of items that need transfered from halo pool, numFields which is a constant + !call mpas_pool_get_subpool(domain % blocklist % structs, 'lagrPartTrackHalo', lagrPartTrackPool) + !call mpas_pool_begin_iteration(lagrPartTrackPool) + !numFields = 0 + !do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + ! ! only need to transfer pool + ! if (dimItr % memberType == MPAS_POOL_FIELD) then + ! numFields = numFields + 1 + ! end if + !end do + !! assume, for now, that this will be constant accross processors. If not, it would need to be sent to other + !! processors too. This also presumes that properties will be fixed accross the processesors. + !}}} + + ! on each list, transmit relevant fields to associated processors (note using the var struct since it has the names + ! required and this information is on each processor, even if the pool's fields are empty their names and types + ! are there from the registry). + call mpas_pool_get_subpool(domain % blocklist % structs, 'lagrPartTrackNonHalo', lagrPartTrackPool) + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + if (dimItr % memberType == MPAS_POOL_FIELD) then + !print *, 'transfering ', trim(dimItr % memberName) + if (dimItr % dataType == MPAS_POOL_REAL) then + ! recv + j = 1 + do i=1,numProcs + if(nPartRecv(i) > 0) then + allocate(array1DRealRecv(i)%val(nPartRecv(i))) + !print *, 'receiving real ', trim(dimItr % memberName), ' from ', procNeighs(i) + ! receive communicated data +#ifdef _MPI + call MPI_IRecv(array1DRealRecv(i)%val, nPartRecv(i), MPI_REALKIND, procNeighs(i), procNeighs(i), & + domain % dminfo % comm, recvRequestID(j), mpi_ierr) +#endif + j = j + 1 + end if + end do + ! send + j = 1 + do i=1,numProcs + if (nPartSend(i) > 0) then + allocate(array1DRealSend(i)%val(nPartSend(i))) + !print *, 'sending real ', trim(dimItr % memberName), ' to ', procNeighs(i) + call get_nonhalo_data_from_particle_list_array(listSend(i)%list, dimItr % memberName, & + array1DRealSend(i)%val) +#ifdef _MPI + call MPI_ISend(array1DRealSend(i)%val, nPartSend(i), MPI_REALKIND, procNeighs(i), & + domain % dminfo % my_proc_id, domain % dminfo % comm, sendRequestID(j), mpi_ierr) +#endif + j = j + 1 + end if + end do + +#ifdef _MPI + call MPI_WaitAll(numRecv, recvRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) call mpas_log_write('recv: mpi_ierr = ' COMMA mpi_ierr) +#ifdef _MPI + call MPI_WaitAll(numSends, sendRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) call mpas_log_write('send: mpi_ierr = ' COMMA mpi_ierr) + + ! store values + j = 1 + do i=1,numProcs + if(nPartRecv(i) > 0) then + ! place it in particle list + call add_nonhalo_data_to_particle_list_array(listRecv(i)%list, dimItr % memberName, & + array1DRealRecv(i)%val) + j = j + 1 + end if + end do + + do i=1,numProcs + if(nPartSend(i) > 0) deallocate(array1DRealSend(i)%val) + end do + do i=1,numProcs + if(nPartRecv(i) > 0) deallocate(array1DRealRecv(i)%val) + end do + !call mpas_log_write( 'finished' + + elseif (dimItr % dataType == MPAS_POOL_INTEGER) then + ! recv + j = 1 + do i=1,numProcs + if(nPartRecv(i) > 0) then + allocate(array1DIntRecv(i)%val(nPartRecv(i))) + ! receive communicated data + !print *, 'receiving int ', trim(dimItr % memberName), ' from ', procNeighs(i) +#ifdef _MPI + call MPI_IRecv(array1DIntRecv(i)%val, nPartRecv(i), MPI_INTEGERKIND, procNeighs(i), procNeighs(i), & + domain % dminfo % comm, recvRequestID(j), mpi_ierr) +#endif + !if( trim(dimItr % memberName) == 'currentBlock') print *, 'currentBlock received ', & + ! nPartRecv(i), ' from', procNeighs(i), ' = ', array1DIntRecv(i)%val + j = j + 1 + end if + end do + ! send + j = 1 + do i=1,numProcs + if(nPartSend(i) > 0) then + allocate(array1DIntSend(i)%val(nPartSend(i))) + !print *, 'sending int ', trim(dimItr % memberName), ' to ', procNeighs(i) + call get_nonhalo_data_from_particle_list_array(listSend(i)%list, dimItr % memberName, array1DIntSend(i)%val) + !if( trim(dimItr % memberName) == 'currentBlock') print *, 'currentBlock sent ',nPartSend(i), & + ! ' to ', procNeighs(i), ' = ', array1DIntSend(i)%val +#ifdef _MPI + call MPI_ISend(array1DIntSend(i)%val, nPartSend(i), MPI_INTEGERKIND, procNeighs(i), & + domain % dminfo % my_proc_id, domain % dminfo % comm, sendRequestID(j), mpi_ierr) +#endif + j = j + 1 + end if + end do + +#ifdef _MPI + call MPI_WaitAll(numRecv, recvRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) call mpas_log_write('mpi_ierr = ' COMMA mpi_ierr) +#ifdef _MPI + call MPI_WaitAll(numSends, sendRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) print *, 'mpi_ierr = ', mpi_ierr + + !do i=1,numProcs + ! if(nPartRecv(i) > 0) print *, 'Received ', trim(dimItr % memberName), ' = ', array1DIntRecv(i) % val + !end do + + ! store values + do i=1,numProcs + if(nPartRecv(i) > 0) then + ! place it in particle list + call add_nonhalo_data_to_particle_list_array(listRecv(i)%list, dimItr % memberName, array1DIntRecv(i)%val) + j = j + 1 + end if + end do + + do i=1,numProcs + if(nPartSend(i) > 0) deallocate(array1DIntSend(i)%val) + end do + do i=1,numProcs + if(nPartRecv(i) > 0) deallocate(array1DIntRecv(i)%val) + end do + !call mpas_log_write( 'finished') + else + !call mpas_log_write( "Different field type than implemented during nonHalo communication!") + end if + elseif (dimItr % memberType == MPAS_POOL_DIMENSION) then + ! ignore dimensions for now and have this code so they aren't printed as an error message + else + write(message, *) "Different type expected in registry for key ", trim(dimItr % memberName), & + " in nonHalo data for communication-- don't know what to do!" + LIGHT_DEBUG_ALL_WRITE(message) + end if + end do + + deallocate(array1DIntSend, array1DIntRecv, array1DRealSend, array1DRealRecv, recvRequestID, sendRequestID) + LIGHT_DEBUG_WRITE('Finished primary MPI communication for nonhalo') + + end subroutine communicate_particle_nonhalo_data!}}} + +!*********************************************************************** +! +! routine communicate_particle_halo_data +! +!> \brief Communicate particle halo data to relevant processors +!> based on particlelists +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine transmitts haloData from particlelists +! +!----------------------------------------------------------------------- + subroutine communicate_particle_halo_data(domain, procNeighs, nPartSend, nPartRecv, listSend, listRecv) !{{{ + implicit none + + type (domain_type), intent(in) :: domain + integer, dimension(:), pointer, intent(in) :: procNeighs + integer, dimension(:), pointer, intent(in) :: nPartSend + integer, dimension(:), pointer, intent(in) :: nPartRecv + integer :: MPI_TYPE + type (mpas_list_of_particle_list_type), dimension(:), pointer :: listSend, listRecv + + integer :: i, j, ii, numProcs, numFields, numRecv, numSends, numReals, numInts + integer, dimension(:), pointer :: recvRequestID, sendRequestID + integer :: mpi_ierr + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_pool_iterator_type) :: dimItr + type array1DReal_list + real (kind=RKIND), dimension(:), pointer :: val + end type + real (kind=RKIND), dimension(:), pointer :: realVal + type array1DInt_list + integer, dimension(:), pointer :: val + end type + integer, dimension(:), pointer :: intVal + type (array1DInt_list), dimension(:), pointer :: array1DIntSend, array1DIntRecv + type (array1DReal_list), dimension(:), pointer :: array1DRealSend, array1DRealRecv + character (len=StrKIND) :: message + + ! for each entry in the halo pool, want to send and recv the data + +#ifdef _MPI + !call MPI_Barrier(domain % dminfo % comm) +#endif + + numReals = 0 + numInts = 0 + call mpas_pool_get_subpool(domain % blocklist % structs, 'lagrPartTrackHalo', lagrPartTrackPool) + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + if (dimItr % memberType == MPAS_POOL_FIELD) then + !print *, 'transfering ', trim(dimItr % memberName) + if (dimItr % dataType == MPAS_POOL_REAL) then + numReals = numReals + 1 + elseif (dimItr % dataType == MPAS_POOL_INTEGER) then + numInts = numInts + 1 + end if + else + ! only need to print this warning once in this function to simplify if-else statements + write(message, *) "Different type expected in registry for key ", trim(dimItr % memberName), & + " of type", dimItr % memberType, "in halo data for communication-- don't know what to do!" + LIGHT_DEBUG_ALL_WRITE(message) + end if + end do + +! NOTE, code assumes that memberName is consistent between processors in terms of pool interation +!#ifdef mpas_debug +! ! verify that membername order is the same between processors +! call mpas_log_write('printing particle pool contents (verify that membername consistent between processors):') +! call mpas_pool_print_summary(lagrPartTrackPool, mpas_pool_field, .true.) +!#endif + + numProcs = size(procNeighs) + + numSends = 0 + do i = 1, numProcs + if (nPartSend(i) > 0) numSends = numSends + 1 + end do + allocate(sendRequestID(numSends)) + + numRecv = 0 + do i = 1, numProcs + if (nPartRecv(i) > 0) numRecv = numRecv + 1 + end do + allocate(recvRequestID(numRecv)) + + !!!!! begin REALS !!!! ! {{{ + allocate(array1DRealSend(numProcs), array1DRealRecv(numProcs)) + ! recv + j = 1 + do i=1,numProcs ! {{{ + if(nPartRecv(i) > 0) then + allocate(array1DRealRecv(i)%val(nPartRecv(i)*numReals)) +#ifdef _MPI + call MPI_IRecv(array1DRealRecv(i)%val, nPartRecv(i)*numReals, MPI_REALKIND, & + procNeighs(i), procNeighs(i), domain % dminfo % comm, recvRequestID(j), mpi_ierr) +#endif + j = j + 1 + end if + end do ! }}} + + ! aggregate data to send + do i=1,numProcs ! {{{ + if (nPartSend(i) > 0) then + allocate(array1DRealSend(i)%val(nPartSend(i)*numReals)) + allocate(realVal(nPartSend(i))) + j = 1 + ! perform the parallel communication + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_REAL) then ! {{{ + call get_halo_data_from_particle_list_array(listSend(i)%list, dimItr % memberName, & + realVal) + do ii=1,nPartSend(i) + array1DRealSend(i)%val((ii+(j-1)*nPartSend(i))) = realval(ii) + end do + j = j + 1 + end if ! }}} + end if + end do + deallocate(realVal) + end if + end do !}}} + + ! send + j = 1 + do i=1,numProcs ! {{{ + if (nPartSend(i) > 0) then +#ifdef _MPI + call MPI_ISend(array1DRealSend(i)%val, nPartSend(i)*numReals, MPI_REALKIND, procNeighs(i), & + domain % dminfo % my_proc_id, domain % dminfo % comm, sendRequestID(j), mpi_ierr) +#endif + j = j + 1 + end if + end do !}}} + +#ifdef _MPI + call MPI_WaitAll(numRecv, recvRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) call mpas_log_write('recv: mpi_ierr = %i', intArgs=(/ mpi_ierr /) ) +#ifdef _MPI + call MPI_WaitAll(numSends, sendRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) call mpas_log_write('send: mpi_ierr = %i', intArgs=(/ mpi_ierr /) ) + + ! store values + do i=1,numProcs ! {{{{ + if(nPartRecv(i) > 0) then + j = 1 + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_REAL) then ! {{{ + ! place it in particle list + call add_halo_data_to_particle_list_array(listRecv(i)%list, dimItr % memberName, & + array1DRealRecv(i)%val((1+(j-1)*nPartRecv(i)) : (j*nPartRecv(i)))) + j = j + 1 + end if ! }}} + end if + end do + end if + end do ! }}} + + do i=1,numProcs + if(nPartSend(i) > 0) deallocate(array1DRealSend(i)%val) + end do + do i=1,numProcs + if(nPartRecv(i) > 0) deallocate(array1DRealRecv(i)%val) + end do + + deallocate(array1DRealSend, array1DRealRecv) + !!!! end REALS !!!! ! }}} + + !!!!! begin INTS !!!! ! {{{ + allocate(array1DIntSend(numProcs), array1DIntRecv(numProcs)) + ! recv + j = 1 + do i=1,numProcs ! {{{ + if(nPartRecv(i) > 0) then + allocate(array1DIntRecv(i)%val(nPartRecv(i)*numInts)) +#ifdef _MPI + call MPI_IRecv(array1DIntRecv(i)%val, nPartRecv(i)*numInts, MPI_INTEGERKIND, & + procNeighs(i), procNeighs(i), domain % dminfo % comm, recvRequestID(j), mpi_ierr) +#endif + j = j + 1 + end if + end do ! }}} + + ! aggregate data to send + do i=1,numProcs ! {{{ + if (nPartSend(i) > 0) then + allocate(array1DIntSend(i)%val(nPartSend(i)*numInts)) + allocate(intVal(nPartSend(i))) + j = 1 + ! perform the parallel communication + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_INTEGER) then ! {{{ + call get_halo_data_from_particle_list_array(listSend(i)%list, dimItr % memberName, & + intVal) + do ii=1,nPartSend(i) + array1DIntSend(i)%val((ii+(j-1)*nPartSend(i))) = intval(ii) + end do + j = j + 1 + end if ! }}} + end if + end do + deallocate(intVal) + end if + end do !}}} + + ! send + j = 1 + do i=1,numProcs ! {{{ + if (nPartSend(i) > 0) then +#ifdef _MPI + call MPI_ISend(array1DIntSend(i)%val, nPartSend(i)*numInts, MPI_INTEGERKIND, procNeighs(i), & + domain % dminfo % my_proc_id, domain % dminfo % comm, sendRequestID(j), mpi_ierr) +#endif + j = j + 1 + end if + end do !}}} + +#ifdef _MPI + call MPI_WaitAll(numRecv, recvRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) call mpas_log_write('recv: mpi_ierr = %i', intArgs=(/ mpi_ierr /) ) +#ifdef _MPI + call MPI_WaitAll(numSends, sendRequestID, MPI_STATUSES_IGNORE, mpi_ierr) +#endif + if (mpi_ierr /= 0) call mpas_log_write('send: mpi_ierr = %i', intArgs=(/ mpi_ierr /) ) + + ! store values + do i=1,numProcs ! {{{{ + if(nPartRecv(i) > 0) then + j = 1 + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_INTEGER) then ! {{{ + ! place it in particle list + call add_halo_data_to_particle_list_array(listRecv(i)%list, dimItr % memberName, & + array1DIntRecv(i)%val((1+(j-1)*nPartRecv(i)) : (j*nPartRecv(i)))) + j = j + 1 + end if ! }}} + end if + end do + end if + end do ! }}} + + do i=1,numProcs + if(nPartSend(i) > 0) deallocate(array1DIntSend(i)%val) + end do + do i=1,numProcs + if(nPartRecv(i) > 0) deallocate(array1DIntRecv(i)%val) + end do + + deallocate(array1DIntSend, array1DIntRecv) + !!!! end INTS !!!! ! }}} + + deallocate(recvRequestID, sendRequestID) + LIGHT_DEBUG_WRITE('Finished primary MPI communication for halo') + + end subroutine communicate_particle_halo_data!}}} + +!*********************************************************************** +! +! routine allocate_nonHalo_data +! +!> \brief Allocate space for nonHalo data for diagnostic output +!> on particlelists +!> \author Phillip Wolfram +!> \date 07/01/2014 +!> \details +!> This routine allocates space for nonHaloData on the particlelist +! +!----------------------------------------------------------------------- + subroutine allocate_nonHalo_data(domain, particlelist) !{{{ + implicit none + + type (domain_type), intent(in) :: domain + type (mpas_particle_list_type), pointer, intent(in) :: particlelist + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_pool_iterator_type) :: dimItr + + integer :: i, nPart + real (kind=RKIND), dimension(:), pointer :: array1DRealPointer + integer, dimension(:), pointer :: array1DIntPointer + character (len=StrKIND) :: message + + + ! get number of particle on list + nPart = count_particlelist(particlelist) + + ! allocate zero arrays + allocate(array1DRealPointer(nPart)) + allocate(array1DIntPointer(nPart)) + array1DRealPointer = 0.0_RKIND + array1DIntPointer = 0 + + ! on each list, transmit relevant fields to associated processors + call mpas_pool_get_subpool(domain % blocklist % structs, 'lagrPartTrackNonHalo', lagrPartTrackPool) + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_REAL) then + call add_nonhalo_data_to_particle_list_array(particlelist, dimItr % memberName, array1DRealPointer) + elseif (dimItr % dataType == MPAS_POOL_INTEGER) then + call add_nonhalo_data_to_particle_list_array(particlelist, dimItr % memberName, array1DIntPointer) + else + LIGHT_DEBUG_ALL_WRITE("Different field type than implemented during halo communication!") + end if + elseif (dimItr % memberType == MPAS_POOL_DIMENSION) then + ! ignore dimensions for now and have this code so they aren't printed as an error message + else + write(message, *) "Different type expected in registry for key ", trim(dimItr % memberName), & + " in halo data for communication-- don't know what to do!" + LIGHT_DEBUG_ALL_WRITE(message) + end if + end do + + ! deallocate arrays + deallocate(array1DRealPointer) + deallocate(array1DIntPointer) + + end subroutine allocate_nonHalo_data !}}} + + subroutine allocate_list_nonHalo_data(domain, listPL) !{{{ + implicit none + + type (domain_type), intent(in) :: domain + type (mpas_list_of_particle_list_type), dimension(:), pointer, intent(inout) :: listPL + + integer :: i, numList + + numList = size(listPL) + do i=1, numList + call allocate_nonHalo_data(domain, listPL(i)%list) + end do + + end subroutine allocate_list_nonHalo_data !}}} + +!*********************************************************************** +! +! routine build_block_particlelists +! +!> \brief Allocates empty particlelist +!> \author Phillip Wolfram +!> \date 04/15/2014 +!> \details +!> This routine allocates empty particlelist data structures +! +!----------------------------------------------------------------------- + subroutine build_block_particlelists(domain, err)!{{{ + + implicit none + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + type (mpas_particle_list_type), pointer :: particlelist + integer, pointer :: nParticles + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! allocate pointers + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nParticles', nParticles) + + ! allocate the memory in its location for use + LIGHT_DEBUG_WRITE('in build_block_particlelists: nParticles = ' COMMA nParticles) + + if (nParticles > 0) then + allocate(block % particlelist) + particlelist => block % particlelist + + !----------------------------------------------------------------- + ! populate list of particles from input data structures + !----------------------------------------------------------------- + + ! initialize the particlelist for population + call build_new_particlelist(nParticles, particlelist, block % blockID) + end if + + block => block % next + end do + + end subroutine build_block_particlelists!}}} + + subroutine clear_block_particlelists(domain, err) !{{{ + implicit none + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + + err = 0 + + block => domain % blocklist + do while (associated(block)) + + call mpas_particle_list_destroy_particle_list(block % particlelist) + + block => block % next + end do + + end subroutine clear_block_particlelists !}}} + +!*********************************************************************** +! +! routine read_haloData +! +!> \brief Reads haloData from netCDF-injected struct arrays +!> \author Phillip Wolfram +!> \date 04/15/2014 +!> \details +!> This routine reads haloData input for this MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine read_haloData(domain, err)!{{{ + + implicit none + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_pool_iterator_type) :: dimItr + type (mpas_particle_list_type), pointer :: particlelist + type (field1DReal), pointer :: field1DRealPointer + !type (field2DReal), pointer :: field2DRealPointer + type (field1DInteger), pointer :: field1DIntPointer + integer, dimension(:), pointer :: array1DInt + character (len=StrKIND) :: message + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! allocate pointers + particlelist => block % particlelist + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackHalo', lagrPartTrackPool) + + ! iterate over each member of the pool and make the relevant assignment + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + ! determine the type of data + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_REAL) then + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DRealPointer) + LIGHT_DEBUG_WRITE(dimItr % memberName COMMA ' = ') + LIGHT_DEBUG_WRITE(field1DRealPointer % array) + call add_halo_data_to_particle_list(particlelist, dimItr % memberName, field1DRealPointer) + elseif (dimItr % dataType == MPAS_POOL_INTEGER) then + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DIntPointer) + ! assign ioBlock explicitly during initial read + if (dimItr % memberName == 'ioBlock') then + field1DIntPointer % array = domain % dminfo % my_proc_id + LIGHT_DEBUG_WRITE('ioBlock = ' COMMA field1DIntPointer % array) + end if + LIGHT_DEBUG_WRITE(dimItr % memberName COMMA ' = ') + LIGHT_DEBUG_WRITE(field1DIntPointer % array) + call add_halo_data_to_particle_list(particlelist, dimItr % memberName, field1DIntPointer) + else + LIGHT_DEBUG_WRITE("Different field type than implemented during halo read!") + end if + elseif (dimItr % memberType == MPAS_POOL_DIMENSION) then + ! ignore dimensions for now and have this code so they aren't printed as an error message + else + write(message, *) "Different type expected in registry for key ", trim(dimItr % memberName), & + " in halo data for read-- don't know what to do!" + LIGHT_DEBUG_WRITE(message) + ! false warning for + !Different type expected in registry for key on_a_sphere in nonHalo data for read, don't know what to do! + !Different type expected in registry for key sphere_radius in nonHalo data for read, don't know what to do! + !Different type expected in registry for key is_periodic in nonHalo data for read, don't know what to do! + !Different type expected in registry for key x_period in nonHalo data for read, don't know what to do! + !Different type expected in registry for key y_period in nonHalo data for read, don't know what to do! + end if + end do + + block => block % next + end do + LIGHT_DEBUG_WRITE('Finished reading halo data') + + end subroutine read_haloData!}}} + +!*********************************************************************** +! +! routine read_nonhaloData +! +!> \brief Reads nonhaloData from netCDF-injected struct arrays +!> \author Phillip Wolfram +!> \date 04/15/2014 +!> \details +!> This routine reads nonhaloData input for this MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + subroutine read_nonhaloData(domain, err)!{{{ + + implicit none + + !----------------------------------------------------------------- + ! input/output variables + !----------------------------------------------------------------- + type (domain_type), intent(in) :: domain + + !----------------------------------------------------------------- + ! output variables + !----------------------------------------------------------------- + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! local variables + !----------------------------------------------------------------- + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackPool + type (mpas_pool_iterator_type) :: dimItr + type (mpas_particle_list_type), pointer :: particlelist + type (field1DReal), pointer :: field1DRealPointer + type (field1DInteger), pointer :: field1DIntPointer + character (len=StrKIND) :: message + + err = 0 + + block => domain % blocklist + do while (associated(block)) + ! allocate pointers + particlelist => block % particlelist + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackNonHalo', lagrPartTrackPool) + + ! iterate over each member of the pool and make the relevant assignment + call mpas_pool_begin_iteration(lagrPartTrackPool) + do while(mpas_pool_get_next_member(lagrPartTrackPool, dimItr)) + ! determine the type of data + if (dimItr % memberType == MPAS_POOL_FIELD) then + if (dimItr % dataType == MPAS_POOL_REAL) then + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DRealPointer) + call add_nonhalo_data_to_particle_list(particlelist, dimItr % memberName, field1DRealPointer) + elseif (dimItr % dataType == MPAS_POOL_INTEGER) then + call mpas_pool_get_field(lagrPartTrackPool, dimItr % memberName, field1DIntPointer) + call add_nonhalo_data_to_particle_list(particlelist, dimItr % memberName, field1DIntPointer) + else + LIGHT_DEBUG_WRITE("Different field type than implemented in nonHalo read!") + end if + elseif (dimItr % memberType == MPAS_POOL_DIMENSION) then + ! ignore dimensions for now and have this code so they aren't printed as an error message + else + write(message, *) "Different type expected in registry for key ", trim(dimItr % memberName), & + " in nonHalo data for read-- don't know what to do!" + LIGHT_DEBUG_WRITE(message) + end if + end do + + ! alternatively, could initialize these fields or just make sure that they exist! + + block => block % next + end do + LIGHT_DEBUG_WRITE('Finished reading non-halo data') + + end subroutine read_nonhaloData!}}} + +!*********************************************************************** +! +! routine mpas_particle_list_update_particle_block +! +!> \brief Update particle block +!> \author Phillip Wolfram +!> \date 10/28/2015 +!> \details +!> This routine updates the currentBlock for particles within the +!> particlelist loop. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_update_particle_block(domain, block, particle, poolname, iCell) !{{{ + implicit none + type (domain_type), intent(inout) :: domain + type (block_type), intent(inout), pointer :: block + type (mpas_particle_type), intent(inout), pointer :: particle + character(len=*), intent(in) :: poolname + integer, intent(inout) :: iCell + + ! local variables + type (mpas_pool_type), pointer :: lagrPartTrackCellsPool + integer, dimension(:), pointer :: cellOwnerBlock + integer, pointer :: currentBlock, transfered + + call mpas_pool_get_subpool(block % structs, trim(poolname), lagrPartTrackCellsPool) + call mpas_pool_get_array(lagrPartTrackCellsPool, 'cellOwnerBlock', cellOwnerBlock) + call mpas_pool_get_array(particle % haloDataPool, 'currentBlock', currentBlock) + if(cellOwnerBlock(iCell) /= currentBlock) then + ! increment transfer counter + call mpas_pool_get_array(particle % haloDataPool, 'transfered', transfered) + transfered = transfered + 1 + ! set new current block + currentBlock = cellOwnerBlock(iCell) + ! reset cell_id to be brute force computed on new block after trasnfer + iCell = -1 + end if + + end subroutine mpas_particle_list_update_particle_block !}}} + +!*********************************************************************** +! +! routine mpas_particle_list_update_halos_start +! +!> \brief Update halo +!> \author Phillip Wolfram +!> \date 10/28/2015 +!> \details +!> This routine updates the halos for particles within the particlelist loop. +!> This facilitates a computational transfer of a particle from one domain +!> to another. +! +!----------------------------------------------------------------------- + subroutine mpas_particle_list_update_halos_start(domain, block, particle, poolname, iCell, & + arrayIndex, destinationName, sendProcRecvList, sendProcSendList, gsendProcNeighs ) !{{{ + implicit none + type (domain_type), intent(inout) :: domain + type (block_type), intent(inout), pointer :: block + type (mpas_particle_type), intent(inout), pointer :: particle + character(len=*), intent(in) :: poolname + ! 'ioBlock' and 'currentBlock' are options for destinationName + character(len=*), intent(in) :: destinationName + integer, intent(inout) :: iCell, arrayIndex + integer, dimension(:), pointer, intent(inout) :: gsendProcNeighs + logical, dimension(:,:), pointer, intent(inout) :: sendProcRecvList + logical, dimension(:), pointer, intent(inout) :: sendProcSendList + + ! local variables + integer :: currentProc, sendProc + type (mpas_pool_type), pointer :: lagrPartTrackCellsPool + integer, pointer :: currentBlock, sendBlock + + call mpas_pool_get_subpool(block % structs, trim(poolname), lagrPartTrackCellsPool) + call mpas_pool_get_array(particle % haloDataPool, 'currentBlock', currentBlock) + call mpas_pool_get_array(particle % haloDataPool, destinationName, sendBlock) + call mpas_get_owning_proc(domain % dminfo, currentBlock, currentProc) + call mpas_get_owning_proc(domain % dminfo, sendBlock, sendProc) + + ! increment data for receiving processors (sum should be total number of particles on processor) + LIGHT_DEBUG_WRITE('destinationName=' COMMA destinationName) + LIGHT_DEBUG_WRITE('g_sendProcNeighs=' COMMA gsendProcNeighs) + LIGHT_DEBUG_WRITE('sendProc=' COMMA sendProc) + ! do not need to transfer information for particles on-processor, this is computed from the send list + ! which is dependent upon current, on-processor particles + if (sendProc /= domain % dminfo % my_proc_id) then + arrayIndex = find_index(gsendProcNeighs, sendProc) + sendProcRecvList(currentProc+1, arrayIndex) = .True. + else + ! consider the case where a particle on A has an sendProc of A and is sent to B (need to have B in A's halo). + sendProcSendList(currentProc+1) = .True. + end if + ! must be computed after computational particles are transferred (this was a bug left-over from serial IO) + end subroutine mpas_particle_list_update_halos_start!}}} + +!}}} + +!----------------------------------------------------------------------- +! +! TESTING SUBROUTINES +! +!----------------------------------------------------------------------- +!{{{ + subroutine mpas_particle_list_test_neighscalc(domain, err) !{{{ + implicit none + + type (domain_type), intent(in) :: domain + integer, intent(out) :: err !< Output: error flag + type (block_type), pointer :: block + + err = 0 + block => domain % blocklist + do while (associated(block)) + + ! write out all blockNeighs and procNeighs + call mpas_log_write( 'blockID = $i', intArgs=(/ block % blockID /) ) + call mpas_log_write( 'blockNeighs = $i', intArgs=(/ block % blockNeighs /) ) + call mpas_log_write( 'procNeighs = $i', intArgs=(/ block % procNeighs /) ) + + block => block % next + end do + + end subroutine mpas_particle_list_test_neighscalc !}}} + + subroutine mpas_particle_list_test_numparticles_to_neighprocs(myproc, procNeighs, ioProcNeighs) !{{{ + implicit none + integer, intent(in) :: myproc + integer, dimension(:), pointer, intent(in) :: procNeighs, ioProcNeighs + + integer :: i, numNeighs + + numNeighs = size(procNeighs) + + LIGHT_DEBUG_WRITE( 'myproc, procNeighs') + do i=1,numNeighs + LIGHT_DEBUG_WRITE(myproc COMMA procNeighs(i)) + end do + if(associated(ioProcNeighs)) then + LIGHT_DEBUG_WRITE( 'myproc, ioProcNeighs') + numNeighs = size(ioProcNeighs) + do i=1,numNeighs + LIGHT_DEBUG_WRITE(myproc COMMA ioProcNeighs(i)) + end do + end if + + end subroutine mpas_particle_list_test_numparticles_to_neighprocs !}}} + + subroutine mpas_particle_list_test_num_current_particlelist(domain) !{{{ + implicit none + type (domain_type), intent(in) :: domain + type (block_type), pointer :: block + integer :: nPartList, nPartList_particles + + block => domain % blocklist + do while(associated(block)) + ! THIS LINE CAUSED A VERY, VERY, VERY NASTY BUG-- BE CAREFUL ABOUT GETTING THE LOCATION OF POTENTIALLY NULLS! + nPartList = count_particlelist(block % particlelist) + nPartList_particles = count_particlelist_particles(block % particlelist) + LIGHT_DEBUG_WRITE('block = ' COMMA block % blockID COMMA ' nPartList= ' COMMA nPartList COMMA ' nparticles = ' COMMA nPartList_particles) + if (nPartList > nPartList_particles) then + LIGHT_DEBUG_WRITE('Possible error! ' COMMA nPartList - nPartList_particles COMMA ' particles on particle list is not allocated!') + end if + + block => block % next + end do + + end subroutine mpas_particle_list_test_num_current_particlelist!}}} + + subroutine test_currentBlock(domain) !{{{ + implicit none + type (domain_type), intent(in) :: domain + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: lagrPartTrackPool + integer, dimension(:), pointer :: field1DInt + integer :: currentBlock, countOnProc, i + countOnProc = 0 + block => domain % blocklist + do while(associated(block)) + call mpas_pool_get_subpool(block % structs, 'lagrPartTrackHalo', lagrPartTrackPool) + call mpas_pool_get_array(lagrPartTrackPool, 'currentBlock', field1DInt) + do i=1,size(field1DInt(:)) + if (field1DInt(i) == block % blockID) countOnProc = countOnProc + 1 + end do + + block => block % next + end do + LIGHT_DEBUG_WRITE('number of particles on processor = ' COMMA countOnProc) + + end subroutine test_currentBlock !}}} + + subroutine test_num_particles_on_particlelist(listPL, nlist) !{{{ + implicit none + integer, intent(in) :: nlist + type (mpas_list_of_particle_list_type), dimension(:), pointer, intent(in) :: listPL + + integer :: i, sumtot, listparticles + + sumtot = 0 + do i = 1, nlist + listparticles = count_particlelist(listPL(i)%list) + LIGHT_DEBUG_WRITE('list i=' COMMA i COMMA ' nparticles=' COMMA listparticles) + sumtot = sumtot + listparticles + end do + LIGHT_DEBUG_WRITE('total_on_list=' COMMA sumtot) + end subroutine test_num_particles_on_particlelist !}}} +!}}} + + +end module ocn_particle_list +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_pointwise_stats.F b/src/core_ocean/analysis_members/mpas_ocn_pointwise_stats.F new file mode 100644 index 0000000000..e4dc8e374f --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_pointwise_stats.F @@ -0,0 +1,1320 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_pointwise_stats +! +!> \brief MPAS ocean analysis mode member: pointwise_stats +!> \author Mark Petersen +!> \date Jan 2016 +!> \details +!> MPAS ocean analysis mode member: pointwise_stats +!> +!----------------------------------------------------------------------- + +#define COMMA , +#define PWAM_DEBUG_WRITE(Z) ! call mpas_log_write( Z ) +#define PWAM_DEBUG_ALL_WRITE(Z) ! call mpas_log_write( Z ) +#define PWAM_WARNING_WRITE(Z) call mpas_log_write( Z , MPAS_LOG_WARN ) +#define PWAM_ERROR_WRITE(Z) call mpas_log_write( Z , MPAS_LOG_CRIT ) + +module ocn_pointwise_stats + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_bootstrap_pointwise_stats, & + ocn_init_pointwise_stats, & + ocn_compute_pointwise_stats, & + ocn_restart_pointwise_stats, & + ocn_finalize_pointwise_stats + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + character (len=*), parameter :: AMPoolSuffix = 'FieldMapping' + character (len=*), parameter :: AMFieldNameSuffix = 'PointStats' + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_bootstrap_pointwise_stats +! +!> \brief Bootstrap pointwise stats AM +!> \author Doug Jacobsen +!> \date 02/03/2016 +!> \details +!> This routine builds out the fields needed for the pointwise stats AM. +! +!----------------------------------------------------------------------- + + subroutine ocn_bootstrap_pointwise_stats(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + character (len=*), parameter :: AMName = 'pointwiseStats' + character (len=StrKIND), pointer :: config_AM_output_stream + + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: AMInputFields + + type (field0DInteger), pointer :: ptr0DInt, ptr0DIntNew + type (field1DInteger), pointer :: ptr1DInt, ptr1DIntNew + type (field2DInteger), pointer :: ptr2DInt, ptr2DIntNew + type (field3DInteger), pointer :: ptr3DInt, ptr3DIntNew + type (field0DReal), pointer :: ptr0DReal, ptr0DRealNew + type (field1DReal), pointer :: ptr1DReal, ptr1DRealNew + type (field2DReal), pointer :: ptr2DReal, ptr2DRealNew + type (field3DReal), pointer :: ptr3DReal, ptr3DRealNew + type (field4DReal), pointer :: ptr4DReal, ptr4DRealNew + type (field5DReal), pointer :: ptr5DReal, ptr5DRealNew + + type (mpas_pool_iterator_type) :: poolItr + type (mpas_pool_field_info_type) :: fieldInfo + character (len=StrKIND), pointer :: charPtr + character (len=StrKIND) :: fieldName + logical :: fieldActive + + logical :: keepField + integer :: iConst, iDim, iDim2 + + integer, pointer :: nPoints + + err = 0 + + call mpas_pool_get_config(domain % configs, 'config_AM_' // trim(AMName) // '_output_stream', config_AM_output_stream) + + call mpas_pool_create_pool(AMInputFields) + + if ( config_AM_output_stream /= 'none' ) then + call mpas_stream_mgr_begin_iteration(domain % streamManager, config_AM_output_stream) + do while ( mpas_stream_mgr_get_next_field(domain % streamManager, config_AM_output_stream, fieldNAme, fieldActive) ) + keepField = .false. + if ( fieldActive ) then + call mpas_pool_get_field_info(domain % blocklist % allFields, fieldName, fieldInfo) + + if ( fieldInfo % fieldType == MPAS_POOL_REAL ) then + if ( fieldInfo % nDims == 0 ) then + ! Can't compute stats for 0D real... + else if ( fieldInfo % nDims == 1 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr1DReal) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr1DReal % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr1DRealNew) + call mpas_duplicate_field(ptr1DReal, ptr1DRealNew) + + ptr1DRealNew % fieldName = trim(ptr1DReal % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr1DRealNew % fieldName)) + + ! Change constituent names, if it's a var array + if ( ptr1DRealNew % isVarArray ) then + do iConst = 1, size(ptr1DRealNew % constituentNames) + ptr1DRealNew % constituentNames(iConst) = trim(ptr1DReal % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr1DReal % fieldName), trim(ptr1DRealNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr1DRealNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr1DRealNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr1DRealNew % dimNames(iDim) = 'nPoints' + ptr1DRealNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr1DRealNew % array(ptr1DRealNew % dimSizes(1))) + ptr1DRealNew % array(:) = 0.0_RKIND + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr1DRealNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr1DRealNew % fieldName, ptr1DReal) + + ptr1DReal % next => ptr1DRealNew + ptr1DRealNew % prev => ptr1DReal + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr1DRealNew % fieldName, ptr1DRealNew) + end if + block => block % next + end do + else if ( fieldInfo % nDims == 2 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr2DReal) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr2DReal % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr2DRealNew) + call mpas_duplicate_field(ptr2DReal, ptr2DRealNew) + + ptr2DRealNew % fieldName = trim(ptr2DReal % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr2DRealNew % fieldName)) + + if ( ptr2DRealNew % isVarArray ) then + do iConst = 1, size(ptr2DRealNew % constituentNames) + ptr2DRealNew % constituentNames(iConst) = trim(ptr2DReal % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr2DReal % fieldName), trim(ptr2DRealNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr2DRealNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr2DRealNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr2DRealNew % dimNames(iDim) = 'nPoints' + ptr2DRealNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr2DRealNew % array(ptr2DRealNew % dimSizes(1), ptr2DRealNew % dimSizes(2))) + ptr2DRealNew % array(:, :) = 0.0_RKIND + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr2DRealNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr2DRealNew % fieldName, ptr2DReal) + + ptr2DReal % next => ptr2DRealNew + ptr2DRealNew % prev => ptr2DReal + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr2DRealNew % fieldName, ptr2DRealNew) + end if + block => block % next + end do + else if ( fieldInfo % nDims == 3 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr3DReal) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr3DReal % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr3DRealNew) + call mpas_duplicate_field(ptr3DReal, ptr3DRealNew) + + ptr3DRealNew % fieldName = trim(ptr3DReal % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr3DRealNew % fieldName)) + + if ( ptr3DRealNew % isVarArray ) then + do iConst = 1, size(ptr3DRealNew % constituentNames) + ptr3DRealNew % constituentNames(iConst) = trim(ptr3DReal % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr3DReal % fieldName), trim(ptr3DRealNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr3DRealNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr3DRealNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr3DRealNew % dimNames(iDim) = 'nPoints' + ptr3DRealNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr3DRealNew % array(ptr3DRealNew % dimSizes(1), ptr3DRealNew % dimSizes(2), & + ptr3DRealNew % dimSizes(3))) + ptr3DRealNew % array(:, :, :) = 0.0_RKIND + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr3DRealNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr3DRealNew % fieldName, ptr3DReal) + + ptr3DReal % next => ptr3DRealNew + ptr3DRealNew % prev => ptr3DReal + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr3DRealNew % fieldName, ptr3DRealNew) + end if + block => block % next + end do + else if ( fieldInfo % nDims == 4 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr4DReal) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr4DReal % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr4DRealNew) + call mpas_duplicate_field(ptr4DReal, ptr4DRealNew) + + ptr4DRealNew % fieldName = trim(ptr4DReal % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr4DRealNew % fieldName)) + + if ( ptr4DRealNew % isVarArray ) then + do iConst = 1, size(ptr4DRealNew % constituentNames) + ptr4DRealNew % constituentNames(iConst) = trim(ptr4DReal % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr4DReal % fieldName), trim(ptr4DRealNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr4DRealNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr4DRealNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr4DRealNew % dimNames(iDim) = 'nPoints' + ptr4DRealNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr4DRealNew % array(ptr4DRealNew % dimSizes(1), ptr4DRealNew % dimSizes(2), & + ptr4DRealNew % dimSizes(3), ptr4DRealNew % dimSizes(4))) + ptr4DRealNew % array(:, :, :, :) = 0.0_RKIND + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr4DRealNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr4DRealNew % fieldName, ptr4DReal) + + ptr4DReal % next => ptr4DRealNew + ptr4DRealNew % prev => ptr4DReal + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr4DRealNew % fieldName, ptr4DRealNew) + end if + block => block % next + end do + else if ( fieldInfo % nDims == 5 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr5DReal) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr5DReal % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr5DRealNew) + call mpas_duplicate_field(ptr5DReal, ptr5DRealNew) + + ptr5DRealNew % fieldName = trim(ptr5DReal % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr5DRealNew % fieldName)) + + if ( ptr5DRealNew % isVarArray ) then + do iConst = 1, size(ptr5DRealNew % constituentNames) + ptr5DRealNew % constituentNames(iConst) = trim(ptr5DReal % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr5DReal % fieldName), trim(ptr5DRealNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr5DRealNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr5DRealNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr5DRealNew % dimNames(iDim) = 'nPoints' + ptr5DRealNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr5DRealNew % array( ptr5DRealNew % dimSizes(1), ptr5DRealNew % dimSizes(2), & + ptr5DRealNew % dimSizes(3), ptr5DRealNew % dimSizes(4), & + ptr5DRealNew % dimSizes(5))) + ptr5DRealNew % array(:, :, :, :, :) = 0.0_RKIND + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr5DRealNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr5DRealNew % fieldName, ptr5DReal) + + ptr5DReal % next => ptr5DRealNew + ptr5DRealNew % prev => ptr5DReal + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr5DRealNew % fieldName, ptr5DRealNew) + end if + block => block % next + end do + end if + else if ( fieldInfo % fieldType == MPAS_POOL_INTEGER ) then + if ( fieldInfo % nDims == 0 ) then + ! Can't compute stats for 0D integer... + else if ( fieldInfo % nDims == 1 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr1DInt) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr1DInt % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr1DIntNew) + call mpas_duplicate_field(ptr1DInt, ptr1DIntNew) + + ptr1DIntNew % fieldName = trim(ptr1DInt % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr1DIntNew % fieldName)) + + if ( ptr1DIntNew % isVarArray ) then + do iConst = 1, size(ptr1DIntNew % constituentNames) + ptr1DIntNew % constituentNames(iConst) = trim(ptr1DInt % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr1DInt % fieldName), trim(ptr1DIntNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr1DIntNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr1DIntNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr1DIntNew % dimNames(iDim) = 'nPoints' + ptr1DIntNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr1DIntNew % array(ptr1DIntNew % dimSizes(1))) + ptr1DIntNew % array(:) = 0 + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr1DIntNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr1DIntNew % fieldName, ptr1DInt) + + ptr1DInt % next => ptr1DIntNew + ptr1DIntNew % prev => ptr1DInt + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr1DIntNew % fieldName, ptr1DIntNew) + end if + block => block % next + end do + else if ( fieldInfo % nDims == 2 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr2DInt) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr2DInt % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr2DIntNew) + call mpas_duplicate_field(ptr2DInt, ptr2DIntNew) + + ptr2DIntNew % fieldName = trim(ptr2DInt % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr2DIntNew % fieldName)) + + if ( ptr2DIntNew % isVarArray ) then + do iConst = 1, size(ptr2DIntNew % constituentNames) + ptr2DIntNew % constituentNames(iConst) = trim(ptr2DInt % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr2DInt % fieldName), trim(ptr2DIntNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr2DIntNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr2DIntNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr2DIntNew % dimNames(iDim) = 'nPoints' + ptr2DIntNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr2DIntNew % array(ptr2DIntNew % dimSizes(1), ptr2DIntNew % dimSizes(2))) + ptr2DIntNew % array(:, :) = 0 + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr2DIntNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr2DIntNew % fieldName, ptr2DInt) + + ptr2DInt % next => ptr2DIntNew + ptr2DIntNew % prev => ptr2DInt + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr2DIntNew % fieldName, ptr2DIntNew) + end if + block => block % next + end do + else if ( fieldInfo % nDims == 3 ) then + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_field(block % allFields, fieldName, ptr3DInt) + call mpas_pool_get_dimension(block % dimensions, 'nPoints', nPoints) + + ! Check for nCells as a dimension, since that's all we can use for now... + do iDim = 1, fieldInfo % nDims + if ( trim(ptr3DInt % dimNames(iDim)) == 'nCells' ) then + keepField = .true. + end if + end do + + if ( keepField ) then + ! Need to create a copy of the field, And change nCells with nPoints + allocate(ptr3DIntNew) + call mpas_duplicate_field(ptr3DInt, ptr3DIntNew) + + ptr3DIntNew % fieldName = trim(ptr3DInt % fieldName) // trim(AMFieldNameSuffix) + PWAM_DEBUG_WRITE(' -- New Name: ' // trim(ptr3DIntNew % fieldName)) + + if ( ptr3DIntNew % isVarArray ) then + do iConst = 1, size(ptr3DIntNew % constituentNames) + ptr3DIntNew % constituentNames(iConst) = trim(ptr3DInt % constituentNames(iConst)) & + // trim(AMFieldNameSuffix) + end do + end if + + ! Add input name to input name pool, with a value of the new name + call mpas_pool_add_config(AMInputFields, trim(ptr3DInt % fieldName), trim(ptr3DIntNew % fieldName)) + + ! Deallocate array, so we can resize it. + deallocate(ptr3DIntNew % array) + + ! Swap out nCells with nPoints + do iDim = 1, fieldInfo % nDims + if ( trim(ptr3DIntNew % dimNames(iDim)) == 'nCells' ) then + PWAM_DEBUG_WRITE(' -- Changing nCells to nPoints') + ptr3DIntNew % dimNames(iDim) = 'nPoints' + ptr3DIntNew % dimSizes(iDim) = nPoints + end if + end do + + ! Allocate new array size + allocate(ptr3DIntNew % array(ptr3DIntNew % dimSizes(1), ptr3DIntNew % dimSizes(2), & + ptr3DIntNew % dimSizes(3))) + ptr3DIntNew % array(:, :, :) = 0 + + ! Mark the field as non-decomposed, since nPoints is not a decomposed dimension + ptr3DIntNew % isDecomposed = .false. + + ! Link with previous block + if ( associated(block % prev) ) then + call mpas_pool_get_field(block % prev % allFields, ptr3DIntNew % fieldName, ptr3DInt) + + ptr3DInt % next => ptr3DIntNew + ptr3DIntNew % prev => ptr3DInt + end if + + ! Add field to allFields pool + call mpas_pool_add_field(block % allFields, ptr3DIntNew % fieldName, ptr3DIntNew) + end if + block => block % next + end do + end if + end if + end if + end do + end if + + ! Swap fields in the stream + call mpas_pool_begin_iteration(AMInputFields) + do while ( mpas_pool_get_next_member(AMInputFields, poolItr) ) + if ( poolItr % memberType == MPAS_POOL_CONFIG ) then + if ( poolItr % dataType == MPAS_POOL_CHARACTER ) then + call mpas_pool_get_config(AMInputFields, poolItr % memberName, charPtr) + + call mpas_stream_mgr_remove_field(domain % streamManager, config_AM_output_stream, poolItr % memberName) + call mpas_stream_mgr_add_field(domain % streamManager, config_AM_output_stream, charPtr) + end if + end if + end do + + call mpas_pool_add_subpool(domain % blocklist % structs, trim(AMName) // trim(AMPoolSuffix), AMInputFields) + nullify(AMInputFields) + + end subroutine ocn_bootstrap_pointwise_stats!}}} + +!*********************************************************************** +! +! routine ocn_init_pointwise_stats +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Jan 2016 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_pointwise_stats(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: pointPool + + integer, pointer :: nCells, nCellsSolve, nPoints, nVertLevels + integer :: iCell, iPoint, i + integer, dimension(:), pointer :: indexToCellID,pointCellGlobalID, pointCellLocalID, indexToPointCellLocalID + + err = 0 + + dminfo = domain % dminfo + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nPoints', nPoints) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevels', nVertLevels) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + call mpas_pool_get_subpool(block % structs, 'pointLocations', pointPool) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_array(meshPool, 'indexToCellID', indexToCellID) + call mpas_pool_get_array(pointPool, 'pointCellGlobalID', pointCellGlobalID) + call mpas_pool_get_array(pointPool, 'pointCellLocalID', pointCellLocalID) + call mpas_pool_get_array(pointPool, 'indexToPointCellLocalID', indexToPointCellLocalID) + + pointCellLocalID = nCells + 1 + indexToPointCellLocalID = 0 + + ! Initialize index arrays to record pointwise data. + i = 0 + do iCell = 1,nCellsSolve + do iPoint = 1,nPoints + if (indexToCellID(iCell).eq.pointCellGlobalID(iPoint)) then + i = i + 1 + indexToPointCellLocalID(i) = iPoint + pointCellLocalID(iPoint) = iCell + endif + end do + end do + + block => block % next + end do + + end subroutine ocn_init_pointwise_stats!}}} + +!*********************************************************************** +! +! routine ocn_compute_pointwise_stats +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Jan 2016 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_pointwise_stats(domain, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (dm_info) :: dminfo + type (block_type), pointer :: block + + integer, pointer :: nPoints, nCells + integer :: iPoint, i + integer, dimension(:), pointer :: pointCellGlobalID, pointCellLocalID, indexToPointCellLocalID + + character (len=*), parameter :: AMName = 'pointwiseStats' + + real (kind=RKIND), pointer :: ptr0DReal1, ptr0DReal2 + real (kind=RKIND), dimension(:), pointer :: ptr1DReal1, ptr1DReal2 + real (kind=RKIND), dimension(:, :), pointer :: ptr2DReal1, ptr2DReal2 + real (kind=RKIND), dimension(:, :, :), pointer :: ptr3DReal1, ptr3DReal2 + real (kind=RKIND), dimension(:, :, :, :), pointer :: ptr4DReal1, ptr4DReal2 + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: ptr5DReal1, ptr5DReal2 + + integer, pointer :: ptr0DInt1, ptr0DInt2 + integer, dimension(:), pointer :: ptr1DInt1, ptr1DInt2 + integer, dimension(:, :), pointer :: ptr2DInt1, ptr2DInt2 + integer, dimension(:, :, :), pointer :: ptr3DInt1, ptr3DInt2 + + character (len=StrKIND), pointer :: mappedName + type (mpas_pool_type), pointer :: AMFieldMapping, pointPool + type (mpas_pool_iterator_type) :: poolItr + type (mpas_pool_field_info_type) :: fieldInfo + integer :: nElements + integer, dimension(:), pointer :: arrShape + + real (kind=RKIND), dimension(:), pointer :: tempRealArrLocal, tempRealArrGlobal + integer, dimension(:), pointer :: tempIntArrLocal, tempIntArrGlobal + integer :: iElement, iDim1, iDim2, iDim3, iDim4, iDim5 + integer :: fieldTimeLevel + + err = 0 + + call mpas_pool_get_subpool(domain % blocklist % structs, trim(AMName) // trim(AMPoolSuffix), AMFieldMapping) + + call mpas_pool_begin_iteration(AMFieldMapping) + + do while ( mpas_pool_get_next_member(AMFieldMapping, poolItr) ) + if ( poolItr % memberType == MPAS_POOL_CONFIG ) then + call mpas_pool_get_config(AMFieldMapping, poolItr % memberName, mappedName) + call mpas_pool_get_field_info(domain % blocklist % allFields, poolItr % memberName, fieldInfo) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nPoints', nPoints) + nElements = 0 + + ! Set field time level, for retrieving the correct time level of the field later + if ( fieldInfo % nTimeLevels < timeLevel ) then + fieldTimeLevel = 1 + else + fieldTimeLevel = timeLevel + end if + + PWAM_DEBUG_WRITE(' -- Building pointer') + ! Get pointer to field in the first block, to store processor sum in + if ( fieldInfo % fieldType == MPAS_POOL_REAL ) then + if ( fieldInfo % nDims == 0 ) then + else if ( fieldInfo % nDims == 1 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr1DReal2) + ptr1DReal2(:) = 0.0_RKIND + nElements = size(ptr1DReal2) + else if ( fieldInfo % nDims == 2 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr2DReal2) + ptr2DReal2(:, :) = 0.0_RKIND + nElements = size(ptr2DReal2) + else if ( fieldInfo % nDims == 3 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr3DReal2) + ptr3DReal2(:, :, :) = 0.0_RKIND + nElements = size(ptr3DReal2) + else if ( fieldInfo % nDims == 4 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr4DReal2) + ptr4DReal2(:, :, :, :) = 0.0_RKIND + nElements = size(ptr4DReal2) + else if ( fieldInfo % nDims == 5 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr5DReal2) + ptr5DReal2(:, :, :, :, :) = 0.0_RKIND + nElements = size(ptr5DReal2) + end if + else if ( fieldInfo % fieldType == MPAS_POOL_INTEGER ) then + if ( fieldInfo % nDims == 0 ) then + else if ( fieldInfo % nDims == 1 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr1DInt2) + ptr1DInt2(:) = 0 + nElements = size(ptr1DInt2) + else if ( fieldInfo % nDims == 2 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr2DInt2) + ptr2DInt2(:, :) = 0 + nElements = size(ptr2DInt2) + else if ( fieldInfo % nDims == 3 ) then + call mpas_pool_get_array(domain % blocklist % allFields, mappedName, ptr3DInt2) + ptr3DInt2(:, :, :) = 0 + nElements = size(ptr3DInt2) + end if + end if + + ! Accumulate point data into the first block's field + PWAM_DEBUG_WRITE(' -- Accumulating field pointer ' // trim(poolItr % memberName)) + block => domain % blocklist + do while ( associated(block) ) + call mpas_pool_get_subpool(block % structs, 'pointLocations', pointPool) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_array(pointPool, 'pointCellLocalID', pointCellLocalID) + + if ( fieldInfo % fieldType == MPAS_POOL_REAL ) then + if ( fieldInfo % nDims == 0 ) then + ! Can't do 0D reals currently... + else if ( fieldInfo % nDims == 1 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr1DReal1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr1DReal2(iPoint) = ptr1DReal1( pointCellLocalID(iPoint) ) + end if + end do + else if ( fieldInfo % nDims == 2 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr2DReal1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr2DReal2(:, iPoint) = ptr2DReal1(:, pointCellLocalID(iPoint) ) + end if + end do + else if ( fieldInfo % nDims == 3 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr3DReal1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr3DReal2(:, :, iPoint) = ptr3DReal1(:, :, pointCellLocalID(iPoint) ) + end if + end do + else if ( fieldInfo % nDims == 4 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr4DReal1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr4DReal2(:, :, :, iPoint) = ptr4DReal1(:, :, :, pointCellLocalID(iPoint) ) + end if + end do + else if ( fieldInfo % nDims == 5 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr5DReal1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr5DReal2(:, :, :, :, iPoint) = ptr5DReal1(:, :, :, :, pointCellLocalID(iPoint) ) + end if + end do + end if + else if ( fieldInfo % fieldType == MPAS_POOL_INTEGER ) then + if ( fieldInfo % nDims == 0 ) then + ! Can't do 0D ints currently... + else if ( fieldInfo % nDims == 1 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr1DInt1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr1DInt2(iPoint) = ptr1DInt1( pointCellLocalID(iPoint) ) + end if + end do + else if ( fieldInfo % nDims == 2 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr2DInt1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr2DInt2(:, iPoint) = ptr2DInt1(:, pointCellLocalID(iPoint) ) + end if + end do + else if ( fieldInfo % nDims == 3 ) then + call mpas_pool_get_array(block % allFields, poolItr % memberName, ptr3DInt1, fieldTimeLevel) + + do iPoint = 1, nPoints + if ( pointCellLocalID(iPoint) < nCells + 1 ) then + ptr3DInt2(:, :, iPoint) = ptr3DInt1(:, :, pointCellLocalID(iPoint) ) + end if + end do + end if + end if + block => block % next + end do + + call mpas_log_write( ' -- Reducing field ' // trim(poolItr % memberName) // ' with nElements = $i', & + intArgs=(/ nElements /) ) + ! Need to sum field across processors + if ( fieldInfo % fieldType == MPAS_POOL_REAL ) then + allocate( tempRealArrLocal(nElements) ) + allocate( tempRealArrGlobal(nElements) ) + if ( fieldInfo % nDims == 0 ) then + else if ( fieldInfo % nDims == 1 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr1DReal2, dim=1) + tempRealArrLocal(iElement) = ptr1DReal2(iDim1) + + iElement = iElement + 1 + end do + + call mpas_dmpar_sum_real_array(domain % dminfo, nElements, tempRealArrLocal, tempRealArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr1DReal2, dim=1) + ptr1DReal2(iDim1) = tempRealArrGlobal(iElement) + iElement = iElement + 1 + end do + else if ( fieldInfo % nDims == 2 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr2DReal2, dim=2) + do iDim2 = 1, size(ptr2DReal2, dim=1) + tempRealArrLocal(iElement) = ptr2DReal2(iDim2, iDim1) + + iElement = iElement + 1 + end do + end do + + call mpas_dmpar_sum_real_array(domain % dminfo, nElements, tempRealArrLocal, tempRealArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr2DReal2, dim=2) + do iDim2 = 1, size(ptr2DReal2, dim=1) + ptr2DReal2(iDim2, iDim1) = tempRealArrGlobal(iElement) + iElement = iElement + 1 + end do + end do + else if ( fieldInfo % nDims == 3 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr3DReal2, dim=3) + do iDim2 = 1, size(ptr3DReal2, dim=2) + do iDim3 = 1, size(ptr3DReal2, dim=1) + tempRealArrLocal(iElement) = ptr3DReal2(iDim3, iDim2, iDim1) + + iElement = iElement + 1 + end do + end do + end do + + call mpas_dmpar_sum_real_array(domain % dminfo, nElements, tempRealArrLocal, tempRealArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr3DReal2, dim=3) + do iDim2 = 1, size(ptr3DReal2, dim=2) + do iDim3 = 1, size(ptr3DReal2, dim=1) + ptr3DReal2(iDim3, iDim2, iDim1) = tempRealArrGlobal(iElement) + iElement = iElement + 1 + end do + end do + end do + else if ( fieldInfo % nDims == 4 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr4DReal2, dim=4) + do iDim2 = 1, size(ptr4DReal2, dim=3) + do iDim3 = 1, size(ptr4DReal2, dim=2) + do iDim4 = 1, size(ptr4DReal2, dim=1) + tempRealArrLocal(iElement) = ptr4DReal2(iDim4, iDim3, iDim2, iDim1) + + iElement = iElement + 1 + end do + end do + end do + end do + + call mpas_dmpar_sum_real_array(domain % dminfo, nElements, tempRealArrLocal, tempRealArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr4DReal2, dim=4) + do iDim2 = 1, size(ptr4DReal2, dim=3) + do iDim3 = 1, size(ptr4DReal2, dim=2) + do iDim4 = 1, size(ptr4DReal2, dim=1) + ptr4DReal2(iDim4, iDim3, iDim2, iDim1) = tempRealArrGlobal(iElement) + iElement = iElement + 1 + end do + end do + end do + end do + else if ( fieldInfo % nDims == 5 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr5DReal2, dim=5) + do iDim2 = 1, size(ptr5DReal2, dim=4) + do iDim3 = 1, size(ptr5DReal2, dim=3) + do iDim4 = 1, size(ptr5DReal2, dim=2) + do iDim5 = 1, size(ptr5DReal2, dim=1) + tempRealArrLocal(iElement) = ptr5DReal2(iDim5, iDim4, iDim3, iDim2, iDim1) + + iElement = iElement + 1 + end do + end do + end do + end do + end do + + call mpas_dmpar_sum_real_array(domain % dminfo, nElements, tempRealArrLocal, tempRealArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr5DReal2, dim=5) + do iDim2 = 1, size(ptr5DReal2, dim=4) + do iDim3 = 1, size(ptr5DReal2, dim=3) + do iDim4 = 1, size(ptr5DReal2, dim=2) + do iDim5 = 1, size(ptr5DReal2, dim=1) + ptr5DReal2(iDim5, iDim4, iDim3, iDim2, iDim1) = tempRealArrGlobal(iElement) + iElement = iElement + 1 + end do + end do + end do + end do + end do + end if + deallocate( tempRealArrLocal ) + deallocate( tempRealArrGlobal ) + else if ( fieldInfo % fieldType == MPAS_POOL_INTEGER ) then + allocate( tempIntArrLocal(nElements) ) + allocate( tempIntArrGlobal(nElements) ) + if ( fieldInfo % nDims == 0 ) then + else if ( fieldInfo % nDims == 1 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr1DInt2, dim=1) + tempIntArrLocal(iElement) = ptr1DInt2(iDim1) + + iElement = iElement + 1 + end do + + call mpas_dmpar_sum_int_array(domain % dminfo, nElements, tempIntArrLocal, tempIntArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr1DInt2, dim=1) + ptr1DInt2(iDim1) = tempIntArrGlobal(iElement) + iElement = iElement + 1 + end do + else if ( fieldInfo % nDims == 2 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr2DInt2, dim=2) + do iDim2 = 1, size(ptr2DInt2, dim=1) + tempIntArrLocal(iElement) = ptr2DInt2(iDim2, iDim1) + + iElement = iElement + 1 + end do + end do + + call mpas_dmpar_sum_int_array(domain % dminfo, nElements, tempIntArrLocal, tempIntArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr2DInt2, dim=2) + do iDim2 = 1, size(ptr2DInt2, dim=1) + ptr2DInt2(iDim2, iDim1) = tempIntArrGlobal(iElement) + iElement = iElement + 1 + end do + end do + else if ( fieldInfo % nDims == 3 ) then + ! Pack local array + iElement = 1 + do iDim1 = 1, size(ptr3DInt2, dim=3) + do iDim2 = 1, size(ptr3DInt2, dim=2) + do iDim3 = 1, size(ptr3DInt2, dim=1) + tempIntArrLocal(iElement) = ptr3DInt2(iDim3, iDim2, iDim1) + + iElement = iElement + 1 + end do + end do + end do + + call mpas_dmpar_sum_int_array(domain % dminfo, nElements, tempIntArrLocal, tempIntArrGlobal) + + ! Unpack global array + iElement = 1 + do iDim1 = 1, size(ptr3DInt2, dim=3) + do iDim2 = 1, size(ptr3DInt2, dim=2) + do iDim3 = 1, size(ptr3DInt2, dim=1) + ptr3DInt2(iDim3, iDim2, iDim1) = tempIntArrGlobal(iElement) + iElement = iElement + 1 + end do + end do + end do + end if + deallocate( tempIntArrLocal ) + deallocate( tempIntArrGlobal ) + end if + PWAM_DEBUG_WRITE(' -- Completed field ' // trim(poolItr % memberName)) + end if + end do + + end subroutine ocn_compute_pointwise_stats!}}} + +!*********************************************************************** +! +! routine ocn_restart_pointwise_stats +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Jan 2016 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_pointwise_stats(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_pointwise_stats!}}} + +!*********************************************************************** +! +! routine ocn_finalize_pointwise_stats +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date Jan 2016 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_pointwise_stats(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_pointwise_stats!}}} + +end module ocn_pointwise_stats + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_regional_stats.F b/src/core_ocean/analysis_members/mpas_ocn_regional_stats.F new file mode 100644 index 0000000000..3f47beb184 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_regional_stats.F @@ -0,0 +1,3125 @@ +! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! ocn_regional_stats +! +!> \brief MPAS ocean analysis core member: regional_stats +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Flexible regional averaging, mins, and maxes of fields. +!----------------------------------------------------------------------- +module ocn_regional_stats + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + ! Public parameters + !-------------------------------------------------------------------- + + ! Public member functions + !-------------------------------------------------------------------- + public :: & + ocn_init_regional_stats, & + ocn_compute_regional_stats, & + ocn_restart_regional_stats, & + ocn_finalize_regional_stats + + ! Private module variables + !-------------------------------------------------------------------- + + type regional_variable_type + ! state per instance variable, stored in framework + character (len=StrKIND), pointer :: input_name + integer, pointer :: has_vertical + + ! generated on every instance call + character (len=StrKIND), dimension(:), allocatable :: output_names + end type regional_variable_type + + type regional_type + ! state per instance, stored in framework + integer, pointer :: number_of_variables + + integer, pointer :: operation + integer, pointer :: region_element + integer, pointer :: function_oned + integer, pointer :: function_twod + integer, pointer :: group_index + + ! looked up on every instance call + character (len=StrKIND), pointer :: weights_oned + character (len=StrKIND), pointer :: weights_twod + integer, dimension(:), pointer :: num_regions_per + integer, dimension(:, :), pointer :: groups + character (len=StrKIND), pointer :: vertical_mask, vertical_dim + + ! generated on every instance call + character (len=StrKIND) :: masking_field + + ! allocated on every instance call + type (regional_variable_type), dimension(:), allocatable :: variables + character (len=StrKIND), dimension(:), allocatable :: count_zerod_names + character (len=StrKIND), dimension(:), allocatable :: weight_zerod_names + character (len=StrKIND), dimension(:), allocatable :: count_oned_names + character (len=StrKIND), dimension(:), allocatable :: weight_oned_names + end type regional_type + + ! enum of ops and types + integer, parameter :: AVG_OP = 1 + integer, parameter :: MIN_OP = 2 + integer, parameter :: MAX_OP = 3 + integer, parameter :: SUM_OP = 4 + integer, parameter :: SOS_OP = 5 + + integer, parameter :: ID_FUNC = 11 + integer, parameter :: MUL_FUNC = 12 + + integer, parameter :: CELL_REGION = 101 + integer, parameter :: VERTEX_REGION = 102 + + ! namelist operators and identifiers for unique names + character (len=3), parameter :: AVG_TOKEN = 'avg' + character (len=3), parameter :: MIN_TOKEN = 'min' + character (len=3), parameter :: MAX_TOKEN = 'max' + character (len=3), parameter :: SUM_TOKEN = 'sum' + character (len=3), parameter :: SOS_TOKEN = 'sos' + + character (len=StrKIND), parameter :: COUNT_TOKEN = 'count' + character (len=StrKIND), parameter :: WEIGHT_TOKEN = 'weight' + character (len=StrKIND), parameter :: ZEROD_TOKEN = '0D' + character (len=StrKIND), parameter :: ONED_TOKEN = '1D' + + ! namelist operators + character (len=2), parameter :: ID_TOKEN = 'id' + character (len=3), parameter :: MUL_TOKEN = 'mul' + + ! namelist operators and identifiers for mask-struct data + character (len=5), parameter :: CELL_TOKEN = 'cell' + character (len=8), parameter :: VERTEX_TOKEN = 'vertex' + + ! camel case token for mask field names + character (len=5), parameter :: CELL_CAMEL = 'Cell' + character (len=8), parameter :: VERTEX_CAMEL = 'Vertex' + + ! canonical dimension names + character (len=6), parameter :: CELL_DIM = 'nCells' + character (len=9), parameter :: VERTEX_DIM = 'nVertices' + + ! canonical solve names + character (len=11), parameter :: CELL_SOLVE = 'nCellsSolve' + character (len=14), parameter :: VERTEX_SOLVE = 'nVerticesSolve' + + ! canonical mins and maxes + real (kind=RKIND), parameter :: DEFAULT_MPAS_MIN_VALUE = -1.0e34_RKIND + real (kind=RKIND), parameter :: DEFAULT_MPAS_MAX_VALUE = 1.0e34_RKIND + + ! none + character (len=4), parameter :: NONE_TOKEN = 'none' + + ! memory names for duplication + character (len=StrKIND), parameter :: REGIONAL_STATS_POOL = & + 'regionalStatsAM' + character (len=StrKIND), parameter :: ONE_INTEGER_MEMORY = & + 'regionalStatsOneInteger' + character (len=StrKIND), parameter :: ONE_STRING_MEMORY = & + 'regionalStatsOneString' + character (len=StrKIND), parameter :: ONE_REAL_MEMORY = & + 'regionalStatsOneReal' + + ! mask-struct array names and suffixes + character (len=StrKIND), parameter :: MASK_POOL_NAME = 'regions' + character (len=StrKIND), parameter :: MASK_DATA_PREFIX = 'region' + character (len=StrKIND), parameter :: MASK_DATA_SUFFIX = 'Masks' + character (len=StrKIND), parameter :: GROUP_DATA_NAME = & + 'regionsInGroup' + character (len=StrKIND), parameter :: REGIONS_PER_NAME = & + 'nRegionsInGroup' + character (len=StrKIND), parameter :: REGION_NAMES_NAME = & + 'regionNames' + character (len=StrKIND), parameter :: GROUP_NAMES_NAME = & + 'regionGroupNames' + + ! mask dimension names + character (len=StrKIND), parameter :: NUM_REGIONS_SUFFIX = 'nRegions' + character (len=StrKIND), parameter :: NUM_GROUPS_SUFFIX = 'nRegionGroups' + character (len=StrKIND), parameter :: MAX_REGIONS_SUFFIX = & + 'maxRegionsInGroup' + + ! prefixes + character (len=StrKIND), parameter :: CONFIG_PREFIX = & + 'config_AM_regionalStats' + character (len=StrKIND), parameter :: FRAMEWORK_PREFIX = 'regionalStats' + + ! namelist-only suffixes + character (len=StrKIND), parameter :: OUTPUT_STREAM_SUFFIX = '_output_stream' + character (len=StrKIND), parameter :: WEIGHTS_ONED_SUFFIX = & + '_1d_weighting_field' + character (len=StrKIND), parameter :: WEIGHTS_TWOD_SUFFIX = & + '_2d_weighting_field' + character (len=StrKIND), parameter :: REGION_GROUP_SUFFIX = '_region_group' + character (len=StrKIND), parameter :: INPUT_STREAM_SUFFIX = '_input_stream' + character (len=StrKIND), parameter :: RESTART_STREAM_SUFFIX = & + '_restart_stream' + character (len=StrKIND), parameter :: VERTICAL_MASK_SUFFIX = & + '_vertical_mask' + character (len=StrKIND), parameter :: VERTICAL_DIM_SUFFIX = & + '_vertical_dimension' + + ! namelist and instance suffixes + character (len=StrKIND), parameter :: OPERATION_SUFFIX = '_operation' + character (len=StrKIND), parameter :: REGION_TYPE_SUFFIX = '_region_type' + character (len=StrKIND), parameter :: FUNCTION_ONED_SUFFIX = & + '_1d_weighting_function' + character (len=StrKIND), parameter :: FUNCTION_TWOD_SUFFIX = & + '_2d_weighting_function' + + ! instance-only suffixes + character (len=StrKIND), parameter :: INPUT_NAME_SUFFIX = '_input_name' + character (len=StrKIND), parameter :: NUMBER_OF_VARIABLES_SUFFIX = & + '_number_of_variables' + character (len=StrKIND), parameter :: HAS_VERTICAL_SUFFIX = '_has_vertical' + + ! error message + character (len=StrKIND), parameter :: CURRENT_CORE_NAME = 'MPAS-Ocean' + +!*********************************************************************** +contains + + + +!*********************************************************************** +! routine ocn_init_regional_stats +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_init_regional_stats(domain, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: v + + ! start procedure + err = 0 + + ! create all of the state for this instance + call start_state(domain, instance, err) + +end subroutine ocn_init_regional_stats!}}} + + + +!*********************************************************************** +! routine ocn_compute_regional_stats +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_compute_regional_stats(domain, timeLevel, instance, err)!{{{ + ! input variables + integer, intent(in) :: timeLevel + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: v, last, m, b, i + integer, pointer :: levels, solve + type (regional_type) :: regions + integer, pointer :: count_sum_zerod + integer, dimension(:), pointer :: count_sum_oned + real (kind=RKIND), pointer :: weight_sum_zerod + real (kind=RKIND), dimension(:), pointer :: weight_sum_oned + type (block_type), pointer :: block + integer, dimension(:,:), pointer :: mask, vertical_mask + type (mpas_pool_type), pointer :: amPool, maskPool + real (kind=RKIND), dimension(:), pointer :: oned_weights + real (kind=RKIND), dimension(:,:), pointer :: twod_weights + logical :: active_vertical + real (kind=RKIND) :: real_copy + integer :: int_copy + real (kind=RKIND), dimension(:), allocatable :: real_vert_copy + integer, dimension(:), allocatable :: int_vert_copy + + ! start procedure + err = 0 + + ! get all of the state for this instance to be able to compute + call get_state(domain, instance, regions) + + ! + ! calculate all of the counts and weights at the beginning + call mpas_pool_get_subpool(domain % blocklist % structs, & + REGIONAL_STATS_POOL, amPool) + + active_vertical = (trim(regions % vertical_dim) /= trim(NONE_TOKEN)) + if (active_vertical) then + call mpas_pool_get_dimension(domain % blocklist % dimensions, & + regions % vertical_dim, levels) + allocate(real_vert_copy(levels)) + allocate(int_vert_copy(levels)) + end if + + last = regions % num_regions_per(regions % group_index) + do b = 1, last + m = regions % groups(b, regions % group_index) + + ! get the different target arrays + ! count 0d + call mpas_pool_get_array(amPool, & + regions % count_zerod_names(b), count_sum_zerod, 1) + count_sum_zerod = 0 + + ! weight 0d + if (regions % function_oned == MUL_FUNC) then + call mpas_pool_get_array(amPool, & + regions % weight_zerod_names(b), weight_sum_zerod, 1) + weight_sum_zerod = 0 + end if + + ! count 1d + if (active_vertical) then + call mpas_pool_get_array(amPool, & + regions % count_oned_names(b), count_sum_oned, 1) + count_sum_oned = 0 + + ! weight 1d + if (regions % function_twod == MUL_FUNC) then + call mpas_pool_get_array(amPool, & + regions % weight_oned_names(b), weight_sum_oned, 1) + weight_sum_oned = 0 + end if + end if + + ! iterate over all the blocks and sum mask counts and weights + block => domain % blocklist + do while (associated(block)) + ! get the mask + call mpas_pool_get_subpool(block % structs, MASK_POOL_NAME, maskPool) + call mpas_pool_get_array(maskPool, regions % masking_field, mask, 1) + + ! get the dimension + if (regions % region_element == CELL_REGION) then + call mpas_pool_get_dimension(block % dimensions, CELL_SOLVE, solve) + else + call mpas_pool_get_dimension(block % dimensions, VERTEX_SOLVE, solve) + end if + + ! create the counts and weights + do i = 1, solve + count_sum_zerod = count_sum_zerod + mask(m, i) + end do + + if (regions % function_oned == MUL_FUNC) then + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, oned_weights, 1) + + do i = 1, solve + weight_sum_zerod = weight_sum_zerod + mask(m, i) * oned_weights(i) + end do + end if + + if (active_vertical) then + call mpas_pool_get_array(block % allFields, & + regions % vertical_mask, vertical_mask, 1) + + do i = 1, solve + do v = 1, levels + count_sum_oned(v) = count_sum_oned(v) + & + vertical_mask(v, i) * mask(m, i) + end do + end do + + if (regions % function_twod == MUL_FUNC) then + call mpas_pool_get_array(block % allFields, & + regions % weights_twod, twod_weights, 1) + + do i = 1, solve + do v = 1, levels + weight_sum_oned(v) = weight_sum_oned(v) + & + vertical_mask(v, i) * mask(m, i) * twod_weights(v, i) + end do + end do + end if + end if + + block => block % next + end do + + ! reduce the weights and sums + call mpas_dmpar_sum_int(domain % dminfo, count_sum_zerod, int_copy) + count_sum_zerod = int_copy + + if (regions % function_oned == MUL_FUNC) then + call mpas_dmpar_sum_real(domain % dminfo, weight_sum_zerod, real_copy) + weight_sum_zerod = real_copy + end if + + if (active_vertical) then + call mpas_dmpar_sum_int_array(domain % dminfo, size(count_sum_oned), & + count_sum_oned, int_vert_copy) + count_sum_oned = int_vert_copy + + if (regions % function_twod == MUL_FUNC) then + call mpas_dmpar_sum_real_array( & + domain % dminfo, size(weight_sum_oned), & + weight_sum_oned, real_vert_copy) + weight_sum_oned = real_vert_copy + end if + end if + + end do + + ! free up memory + if (active_vertical) then + deallocate(int_vert_copy) + deallocate(real_vert_copy) + end if + + ! do all region reductions for each variable + do v = 1, regions % number_of_variables + call typed_operate(domain % dminfo, domain % blocklist, & + regions, regions % variables(v)) + end do + + ! clean up the instance memory + do v = 1, regions % number_of_variables + deallocate(regions % variables(v) % output_names) + end do + deallocate(regions % variables) + deallocate(regions % count_zerod_names) + deallocate(regions % weight_zerod_names) + deallocate(regions % count_oned_names) + deallocate(regions % weight_oned_names) +end subroutine ocn_compute_regional_stats!}}} + + + +!*********************************************************************** +! routine ocn_restart_regional_stats +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_restart_regional_stats(domain, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + + ! start procedure + err = 0 + +end subroutine ocn_restart_regional_stats!}}} + + + +!*********************************************************************** +! routine ocn_finalize_regional_stats +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_finalize_regional_stats(domain, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + + ! start procedure + err = 0 + +end subroutine ocn_finalize_regional_stats!}}} + +! +! local subroutines +! + +!*********************************************************************** +! routine add_new_string +! +!> \brief Allocate an string in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new integer in the AM pool and return a pointer to it. +!----------------------------------------------------------------------- +subroutine add_new_string(all_fields, inpool, outpool, field_name, target_ptr) + ! input variables + character (len=StrKIND) :: field_name + + ! input/output variables + type (mpas_pool_type), pointer, intent(inout) :: all_fields, inpool, outpool + + ! output variables + character (len=StrKIND), pointer, optional :: target_ptr + + ! local variables + type (field0DChar), pointer :: srcString, dstString + + call mpas_pool_get_field(inpool, ONE_STRING_MEMORY, srcString, 1) + call mpas_duplicate_field(srcString, dstString) + dstString % fieldName = field_name + call mpas_pool_add_field(outpool, dstString % fieldName, dstString) + call mpas_pool_add_field(all_fields, dstString % fieldName, dstString) + if (present(target_ptr)) then + call mpas_pool_get_array(outpool, dstString % fieldName, target_ptr, 1) + end if +end subroutine add_new_string + + + +!*********************************************************************** +! routine add_new_integer +! +!> \brief Allocate an integer in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new integer in the AM pool and return a pointer to it. +!----------------------------------------------------------------------- +subroutine add_new_integer(all_fields, inpool, outpool, field_name, target_ptr) + ! input variables + character (len=StrKIND) :: field_name + + ! input/output variables + type (mpas_pool_type), pointer, intent(inout) :: all_fields, inpool, outpool + + ! output variables + integer, pointer, optional :: target_ptr + + ! local variables + type (field0DInteger), pointer :: srcInteger, dstInteger + + call mpas_pool_get_field(inpool, ONE_INTEGER_MEMORY, srcInteger, 1) + call mpas_duplicate_field(srcInteger, dstInteger) + dstInteger % fieldName = field_name + call mpas_pool_add_field(outpool, dstInteger % fieldName, dstInteger) + call mpas_pool_add_field(all_fields, dstInteger % fieldName, dstInteger) + if (present(target_ptr)) then + call mpas_pool_get_array(outpool, dstInteger % fieldName, target_ptr, 1) + end if +end subroutine add_new_integer + + + +!*********************************************************************** +! routine add_new_real +! +!> \brief Allocate an real in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new real in the AM pool and return a pointer to it. +!----------------------------------------------------------------------- +subroutine add_new_real(all_fields, inpool, outpool, field_name, target_ptr) + ! input variables + character (len=StrKIND) :: field_name + + ! input/output variables + type (mpas_pool_type), pointer, intent(inout) :: all_fields, inpool, outpool + + ! output variables + real (kind=RKIND), pointer, optional :: target_ptr + + ! local variables + type (field0DReal), pointer :: srcReal, dstReal + + call mpas_pool_get_field(inpool, ONE_REAL_MEMORY, srcReal, 1) + call mpas_duplicate_field(srcReal, dstReal) + dstReal % fieldName = field_name + call mpas_pool_add_field(outpool, dstReal % fieldName, dstReal) + call mpas_pool_add_field(all_fields, dstReal % fieldName, dstReal) + if (present(target_ptr)) then + call mpas_pool_get_array(outpool, dstReal % fieldName, target_ptr, 1) + end if +end subroutine add_new_real + + + +!*********************************************************************** +! routine add_new_real_1d +! +!> \brief Allocate an 1d real in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new 1d real from a 2d integer (for creating vertical +!> weight arrays from the vertical mask, i.e., drops the element dimension) +!> has_vertical is integer so that the result can be stored in the framework. +!----------------------------------------------------------------------- +subroutine add_new_real_1d(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ +#include "regional_stats_inc/regional_field_nd_1.inc" + + type (field2DInteger), pointer :: src + type (field1DReal), pointer :: dst + integer, dimension(2) :: src_dims + type (mpas_pool_field_info_type) :: info + +#include "regional_stats_inc/regional_field_nd_2.inc" + allocate(dst % array(src_dims(1))) +#include "regional_stats_inc/regional_field_nd_3.inc" + + dst % hasTimeDimension = .true. + + call mpas_pool_get_field_info(outpool, outname, info) + info % nTimeLevels = 1 +end subroutine add_new_real_1d!}}} + + + +!*********************************************************************** +! routine add_new_integer_1d +! +!> \brief Allocate an 1d integer in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new 1d integer from a 2d integer (for creating vertical +!> count arrays from the vertical mask, i.e., drops the element dimension) +!> has_vertical is integer so that the result can be stored in the framework. +!----------------------------------------------------------------------- +subroutine add_new_integer_1d(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ +#include "regional_stats_inc/regional_field_nd_1.inc" + + type (field2DInteger), pointer :: src + type (field1DInteger), pointer :: dst + integer, dimension(2) :: src_dims + type (mpas_pool_field_info_type) :: info + +#include "regional_stats_inc/regional_field_nd_2.inc" + allocate(dst % array(src_dims(1))) +#include "regional_stats_inc/regional_field_nd_3.inc" + + dst % hasTimeDimension = .true. + + call mpas_pool_get_field_info(outpool, outname, info) + info % nTimeLevels = 1 +end subroutine add_new_integer_1d!}}} + + + +!*********************************************************************** +! function output_naming +! +!> \brief Given an input name, create a corresponding output name +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Code to create consistent output names from input names. +!----------------------------------------------------------------------- +character (len=StrKIND) function output_naming & +(op_name, input_name, region_identifier, instance) + character (len=StrKIND), intent(in) :: op_name, & + input_name, region_identifier, instance + + output_naming = trim(instance) // & + trim(region_identifier) // '_' // trim(op_name) // '_' // & + trim(input_name) +end function output_naming + + + +!*********************************************************************** +! function operator_naming +! +!> \brief Given an operator enum, create a corresponding operator name +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Code to create consistent operator names from operator enums. +!----------------------------------------------------------------------- +character (len=StrKIND) function operator_naming(enum) + integer, intent(in) :: enum + + if (enum == AVG_OP) then + operator_naming = AVG_TOKEN + else if (enum == MIN_OP) then + operator_naming = MIN_TOKEN + else if (enum == MAX_OP) then + operator_naming = MAX_TOKEN + else if (enum == SUM_OP) then + operator_naming = SUM_TOKEN + else if (enum == SOS_OP) then + operator_naming = SOS_TOKEN + else + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' The impossible happened. ' // & + 'Tried creating an operator of unknown kind in regional stats AM.', MPAS_LOG_CRIT) + endif +end function operator_naming + + + +!*********************************************************************** +! function element_naming +! +!> \brief Given an element enum, create a corresponding element name +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Code to create consistent operator names from operator enums. +!----------------------------------------------------------------------- +character (len=StrKIND) function element_naming(enum) + integer, intent(in) :: enum + + if (enum == CELL_REGION) then + element_naming = CELL_TOKEN + else if (enum == VERTEX_REGION) then + element_naming = VERTEX_TOKEN + else + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' The impossible happened. ' // & + 'Tried creating an element of unknown kind in regional stats AM.', MPAS_LOG_CRIT) + end if +end function element_naming + + + +!*********************************************************************** +! function dimension_naming +! +!> \brief Given a region type, return its canonical dimension string name +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Returns the canonical MPAS dimension name given a region element dimension. +!----------------------------------------------------------------------- +character (len=StrKIND) function dimension_naming(elem_type) + integer, intent(in) :: elem_type + + if (elem_type == CELL_REGION) then + dimension_naming = CELL_DIM + else if (elem_type == VERTEX_REGION) then + dimension_naming = VERTEX_DIM + else + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' The impossible happened. ' // & + 'Tried creating a element dimension name of unknown kind in ' // & + 'regional stats AM.', MPAS_LOG_CRIT) + end if +end function dimension_naming + + + +!*********************************************************************** +! function check_real_element_dim +! +!> \brief Return true if the last dimension matches the element and is real. +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Given an element dimension name and field info, return true +!> if the last dimension of the field is the element and the field is +!> real, otherwise return false. +!----------------------------------------------------------------------- +logical function check_real_element_dim(all_fields, field_name, elem_name) + type (mpas_pool_type), pointer, intent(in) :: all_fields + character (len=StrKIND), intent(in) :: field_name, elem_name + + logical :: has_elem + type (mpas_pool_field_info_type) :: info + type (field1DReal), pointer :: r1 + type (field2DReal), pointer :: r2 + type (field3DReal), pointer :: r3 + type (field4DReal), pointer :: r4 + type (field5DReal), pointer :: r5 + + call mpas_pool_get_field_info(all_fields, field_name, info) + + if (info % fieldType /= MPAS_POOL_REAL) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' WARNING: field "' // & + trim(field_name) // '" listed in the ' // & + 'output stream, for regional stats analysis member, ' // & + 'is not real. Regional stats will not be applied to ' // & + 'this field.') + + check_real_element_dim = .false. + else if (info % nDims < 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' WARNING: field "' // & + trim(field_name) // '" listed in the ' // & + 'output stream, for regional stats analysis member, ' // & + 'is 0D. Regional stats will not be applied to ' // & + 'this field.') + + check_real_element_dim = .false. + else + if (info % nDims == 1) then + call mpas_pool_get_field(all_fields, field_name, r1, 1) + has_elem = check_element_dim(r1 % dimNames, elem_name) + else if (info % nDims == 2) then + call mpas_pool_get_field(all_fields, field_name, r2, 1) + has_elem = check_element_dim(r2 % dimNames, elem_name) + else if (info % nDims == 3) then + call mpas_pool_get_field(all_fields, field_name, r3, 1) + has_elem = check_element_dim(r3 % dimNames, elem_name) + else if (info % nDims == 4) then + call mpas_pool_get_field(all_fields, field_name, r4, 1) + has_elem = check_element_dim(r4 % dimNames, elem_name) + else + call mpas_pool_get_field(all_fields, field_name, r5, 1) + has_elem = check_element_dim(r5 % dimNames, elem_name) + end if + + if (.not. has_elem) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' WARNING: field "' // & + trim(field_name) // '" listed in the output stream, ' // & + 'for regional stats analysis member, does not have ' // & + trim(elem_name) // ' as its last dimension. Regional stats ' // & + 'will not be applied to this field.') + end if + + check_real_element_dim = has_elem + end if + +end function check_real_element_dim + + + +!*********************************************************************** +! function check_element_dim +! +!> \brief Return true if the last dimension matches the element name. +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Given an element dimension name and list of dim names, return true +!> if the last dimension is the element, otherwise return false. +!----------------------------------------------------------------------- +logical function check_element_dim(dim_names, elem_name) + character (len=StrKIND), dimension(:), intent(in) :: dim_names + character (len=StrKIND), intent(in) :: elem_name + + integer :: last + + last = size(dim_names) + if (last > 0) then + check_element_dim = (trim(dim_names(last)) == trim(elem_name)) + else + check_element_dim = .false. + end if +end function check_element_dim + + + +!*********************************************************************** +! function check_vertical_dim +! +!> \brief Return not 0 if the last dimension matches the vertical name. +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Given an vertical dimension name and list of dim names, return not 0 +!> if the last dimension is the vertical, otherwise return 0. +!> It uses integers so the result can be stored in the framework. +!----------------------------------------------------------------------- +integer function check_vertical_dim(dim_names, vert_name) + character (len=StrKIND), dimension(:), intent(in) :: dim_names + character (len=StrKIND), intent(in) :: vert_name + + integer :: last + + last = size(dim_names) + if (last > 1) then + if (trim(dim_names(last - 1)) == trim(vert_name)) then + check_vertical_dim = 1 + else + check_vertical_dim = 0 + end if + else + check_vertical_dim = 0 + end if +end function check_vertical_dim + + + +!*********************************************************************** +! function mask_naming +! +!> \brief Consistent naming for mask field from the stream. +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details Creates the name of the mask field based on the element type. +!----------------------------------------------------------------------- +character (len=StrKIND) function mask_naming(elem_type) + integer, intent(in) :: elem_type + + if (elem_type == CELL_REGION) then + mask_naming = & + trim(MASK_DATA_PREFIX) // trim(CELL_CAMEL) // trim(MASK_DATA_SUFFIX) + else if (elem_type == VERTEX_REGION) then + mask_naming = & + trim(MASK_DATA_PREFIX) // trim(VERTEX_CAMEL) // trim(MASK_DATA_SUFFIX) + else + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' The impossible happened. ' // & + 'Tried to create a name for a mask field of unknown type.', MPAS_LOG_CRIT) + end if +end function mask_naming + + +!*********************************************************************** +! function fix_region_name +! +!> \brief Replaces non identifier characters with _ (underscore) +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details Given a character string, it will replace all of the Fortran +! non-identifier characters (anything but [a-zA-Z0-9_]) with underscore. +! This is primarily for renaming regions from the mask file, +! because NetCDF doesn't like non-identifier characters in the +! start position and Fortran identifiers are [a-zA-Z][a-zA-Z0-9_]+. +! It will also prepend 'reg' to the string to make sure it starts +! with [a-zA-Z]. +!----------------------------------------------------------------------- +character (len=StrKIND) function fix_region_name(string) + character (len=StrKIND), intent(in) :: string + + character (len=StrKIND) :: copy + integer :: tl, i, c + + copy = string + tl = len_trim(copy) + + if (tl < 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' A region name, from the ' // & + 'region mask input for the regional stats analysis member, the ' // & + 'is an empty string.', MPAS_LOG_CRIT) + end if + + tl = tl + 3 + copy = 'reg' // copy + + do i = 4, tl + c = iachar(copy(i:i)) + if ((c < 48) .or. ((c > 57) .and. (c < 65)) .or. (c > 122)) then + copy(i:i) = '_' + end if + end do + + fix_region_name = copy +end function fix_region_name + + +!*********************************************************************** +! routine debug_state +! +!> \brief Print all of the state for this instance. +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Given the current state in regions, print it all for debugging. +!----------------------------------------------------------------------- +subroutine debug_state(regions) + type (regional_type), intent(in) :: regions + integer :: v, b, last, m + + write(*,*) 'num vars: ', regions % number_of_variables + + write (*,*) 'op: ', regions % operation + write (*,*) 'elem: ', regions % region_element + write (*,*) '1d func: ', regions % function_oned + write (*,*) '1d weight: ', trim(regions % weights_oned) + write (*,*) '2d func: ', regions % function_twod + write (*,*) '2d weight: ', trim(regions % weights_twod) + + write (*,*) 'mask: ', trim(regions % masking_field) + write (*,*) 'vertical mask: ', trim(regions % vertical_mask) + + last = regions % num_regions_per(regions % group_index) + write(*,*) 'selected group: ', regions % group_index + write(*,*) 'num regions in: ', last + do b = 1, last + write(*,*) 'count 1d var: ', trim(regions % count_zerod_names(b)) + write(*,*) 'weight 1d var: ', trim(regions % weight_zerod_names(b)) + write(*,*) 'count 2d var: ', trim(regions % count_oned_names(b)) + write(*,*) 'weight 2d var: ', trim(regions % weight_oned_names(b)) + end do + do v = 1, regions % number_of_variables + write(*,*) 'var: ', trim(regions % variables(v) % input_name) + write(*,*) 'has vertical: ', regions % variables(v) % has_vertical + do b = 1, last + write(*,*) 'index: ', b + m = regions % groups(b, regions % group_index) + write(*,*) 'region index: ', m + write(*,*) 'out var: ', trim(regions % variables(v) % output_names(b)) + end do + end do +end subroutine debug_state + + + +!*********************************************************************** +! routine get_state +! +!> \brief Get all of the state for this instance. +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> This will allocate and fetch all of the state necessary for this +!> instance that is being run (prevalidated data from start_state). +!----------------------------------------------------------------------- +subroutine get_state(domain, instance, regions) + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + type (regional_type), intent(out) :: regions + + ! local variables + integer :: v, b, last, m + character (len=StrKIND) :: namelist_prefix, storage_prefix, field_name, & + var_identifier, op_name + type (mpas_pool_type), pointer :: maskPool, amPool + character (len=StrKIND), dimension(:), pointer :: names + logical :: active_vertical + + ! start procedure + namelist_prefix = trim(CONFIG_PREFIX) // trim(instance) + storage_prefix = trim(FRAMEWORK_PREFIX) // trim(instance) + + call mpas_pool_get_subpool(domain % blocklist % structs, & + REGIONAL_STATS_POOL, amPool) + + ! + ! get ones we stored in framework for this instance + ! + + ! number_of_variables + field_name = trim(storage_prefix) // trim(NUMBER_OF_VARIABLES_SUFFIX) + call mpas_pool_get_array(amPool, field_name, regions % number_of_variables, 1) + + ! operation + field_name = trim(storage_prefix) // trim(OPERATION_SUFFIX) + call mpas_pool_get_array(amPool, field_name, regions % operation, 1) + + op_name = operator_naming(regions % operation) + + ! mask type + field_name = trim(storage_prefix) // trim(REGION_TYPE_SUFFIX) + call mpas_pool_get_array(amPool, field_name, regions % region_element, 1) + + ! mask field & pool + regions % masking_field = mask_naming(regions % region_element) + call mpas_pool_get_subpool(domain % blocklist % structs, & + MASK_POOL_NAME, maskPool) + + ! weighting function + field_name = trim(storage_prefix) // trim(FUNCTION_ONED_SUFFIX) + call mpas_pool_get_array(amPool, field_name, regions % function_oned, 1) + + field_name = trim(storage_prefix) // trim(FUNCTION_TWOD_SUFFIX) + call mpas_pool_get_array(amPool, field_name, regions % function_twod, 1) + + ! get the input names and has vertical for variables + allocate(regions % variables(regions % number_of_variables)) + do v = 1, regions % number_of_variables + ! identifier + write(var_identifier, '(I0)') v + + ! input name + field_name = trim(storage_prefix) // '_' // trim(var_identifier) // & + trim(INPUT_NAME_SUFFIX) + call mpas_pool_get_array(amPool, field_name, & + regions % variables(v) % input_name, 1) + + ! has_vertical + field_name = trim(storage_prefix) // '_' // trim(var_identifier) // & + trim(HAS_VERTICAL_SUFFIX) + call mpas_pool_get_array(amPool, field_name, & + regions % variables(v) % has_vertical, 1) + end do + + ! group index + field_name = trim(storage_prefix) // trim(REGION_GROUP_SUFFIX) + call mpas_pool_get_array(amPool, field_name, regions % group_index, 1) + + ! + ! get ones that already exist from namelist/stream + ! + + ! weighting fields + field_name = trim(namelist_prefix) // trim(WEIGHTS_ONED_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % weights_oned) + + field_name = trim(namelist_prefix) // trim(WEIGHTS_TWOD_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % weights_twod) + + ! groups + call mpas_pool_get_array(maskPool, GROUP_DATA_NAME, regions % groups, 1) + + ! num_regions_per + call mpas_pool_get_array(maskPool, REGIONS_PER_NAME, & + regions % num_regions_per, 1) + + ! vertical mask + field_name = trim(namelist_prefix) // trim(VERTICAL_MASK_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % vertical_mask) + + field_name = trim(namelist_prefix) // trim(VERTICAL_DIM_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % vertical_dim) + + active_vertical = (trim(regions % vertical_mask) /= trim(NONE_TOKEN)) + + ! + ! generate output names + ! + + ! fetch region names and name via regions + call mpas_pool_get_array(maskPool, REGION_NAMES_NAME, names) + + last = regions % num_regions_per(regions % group_index) + ! allocate count and weight names + allocate(regions % count_zerod_names(last)) + allocate(regions % weight_zerod_names(last)) + allocate(regions % count_oned_names(last)) + allocate(regions % weight_oned_names(last)) + do b = 1, last + m = regions % groups(b, regions % group_index) + + ! 0d count & weight name + regions % count_zerod_names(b) = output_naming(COUNT_TOKEN, & + ZEROD_TOKEN, fix_region_name(names(m)), instance) + + if (regions % function_oned == MUL_FUNC) then + regions % weight_zerod_names(b) = & + output_naming(WEIGHT_TOKEN, & + ZEROD_TOKEN, fix_region_name(names(m)), instance) + end if + + ! 1d count & weight name + if (active_vertical) then + regions % count_oned_names(b) = & + output_naming(COUNT_TOKEN, & + ONED_TOKEN, fix_region_name(names(m)), instance) + + if (regions % function_twod == MUL_FUNC) then + regions % weight_oned_names(b) = & + output_naming(WEIGHT_TOKEN, & + ONED_TOKEN, fix_region_name(names(m)), instance) + end if + end if + + end do + + do v = 1, regions % number_of_variables + ! allocate output names + allocate(regions % variables(v) % output_names(last)) + + do b = 1, last + m = regions % groups(b, regions % group_index) + + ! region name + regions % variables(v) % output_names(b) = output_naming(op_name, & + regions % variables(v) % input_name, & + fix_region_name(names(m)), instance) + end do + end do + +end subroutine get_state + + +!*********************************************************************** +! routine start_state +! +!> \brief Begin the initialization of this analysis member +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> All the necessary details to initialize this analysis member +!> instance. +!----------------------------------------------------------------------- +subroutine start_state(domain, instance, err) + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + type (regional_type) :: regions + character (len=StrKIND), pointer :: config_results, output_stream_name, & + config_results_2 + character (len=StrKIND) :: namelist_prefix, storage_prefix, field_name, & + elem_name, op_name, var_identifier + integer :: v, b, last, m + integer, pointer :: number_of_regions, number_of_groups, max_regions_per + type (mpas_pool_type), pointer :: maskPool, amPool + type (field1DReal), pointer :: oned_field + type (field2DReal), pointer :: twod_field + type (field2DInteger), pointer :: mask + type (mpas_pool_field_info_type) :: info + character (len=StrKIND), dimension(:), pointer :: names + logical :: active_vertical + logical, dimension(:), allocatable :: valid_input + + ! start procedure + err = 0 + + namelist_prefix = trim(CONFIG_PREFIX) // trim(instance) + storage_prefix = trim(FRAMEWORK_PREFIX) // trim(instance) + + call mpas_pool_get_subpool(domain % blocklist % structs, & + REGIONAL_STATS_POOL, amPool) + + ! + ! get dimensions + ! + + call mpas_pool_get_dimension(domain % blocklist % dimensions, & + NUM_REGIONS_SUFFIX, number_of_regions) + call mpas_pool_get_dimension(domain % blocklist % dimensions, & + NUM_GROUPS_SUFFIX, number_of_groups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, & + MAX_REGIONS_SUFFIX, max_regions_per) + + if (number_of_regions < 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' number of regions dimension is ' // & + 'less than 1 for the regional stats analysis member.', MPAS_LOG_CRIT) + end if + + if (number_of_groups < 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' number of region groups ' // & + 'dimension is less than 1 for the regional stats analysis member.', MPAS_LOG_CRIT) + end if + + if (max_regions_per < 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' maximum number of regions per ' // & + 'group dimension is less than 1 for the regional stats analysis member.', MPAS_LOG_CRIT) + end if + + ! + ! allocate some framework memory for instance state, and assign values + ! + + ! number of variables done in modify stream + + ! operation + field_name = trim(storage_prefix) // trim(OPERATION_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % operation) + + ! get our operation + field_name = trim(namelist_prefix) // trim(OPERATION_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, config_results) + if (config_results == AVG_TOKEN) then + regions % operation = AVG_OP + else if (config_results == MIN_TOKEN) then + regions % operation = MIN_OP + else if (config_results == MAX_TOKEN) then + regions % operation = MAX_OP + else if (config_results == SUM_TOKEN) then + regions % operation = SUM_OP + else if (config_results == SOS_TOKEN) then + regions % operation = SOS_OP + else + ! error if unknown operation + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' unknown operation in ' // & + 'regional stats analysis member configuration.', MPAS_LOG_CRIT) + end if + + op_name = operator_naming(regions % operation) + + ! element type + field_name = trim(storage_prefix) // trim(REGION_TYPE_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % region_element) + + ! get the element type + field_name = trim(namelist_prefix) // trim(REGION_TYPE_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, config_results) + + if (config_results == CELL_TOKEN) then + regions % region_element = CELL_REGION + else if (config_results == VERTEX_TOKEN) then + regions % region_element = VERTEX_REGION + else + ! error if unknown element type + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' unknown region element type in ' // & + 'regional stats analysis member configuration.', MPAS_LOG_CRIT) + end if + + elem_name = dimension_naming(regions % region_element) + + ! get vertical dim + field_name = trim(namelist_prefix) // trim(VERTICAL_DIM_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % vertical_dim) + + active_vertical = (trim(regions % vertical_dim) /= trim(NONE_TOKEN)) + + ! mask field & pool + regions % masking_field = mask_naming(regions % region_element) + call mpas_pool_get_subpool(domain % blocklist % structs, & + MASK_POOL_NAME, maskPool) + + ! validate the mask field + ! + ! shouldn't actually need this because the naming convention, but + ! can't hurt to check, just in case + if (regions % masking_field /= NONE_TOKEN) then + call mpas_pool_get_field_info(maskPool, regions % masking_field, info) + + if (info % fieldType /= MPAS_POOL_INTEGER) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the mask ' // & + trim(regions % masking_field) // & + ' for the regional stats AM is not a integer field.', MPAS_LOG_CRIT) + end if + + if (info % nDims /= 2) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the mask ' // & + trim(regions % masking_field) // & + ' for the regional stats AM is not a 2D field.', MPAS_LOG_CRIT) + end if + + call mpas_pool_get_field(maskPool, regions % masking_field, mask) + + if (.not. check_element_dim(mask % dimNames, elem_name)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the mask ' // & + trim(regions % masking_field) // & + ' for the regional stats AM needs to have ' // trim(elem_name) // & + ' as its last dimension.', MPAS_LOG_CRIT) + end if + end if + + ! 1d weighting function + field_name = trim(storage_prefix) // trim(FUNCTION_ONED_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % function_oned) + + field_name = trim(namelist_prefix) // trim(FUNCTION_ONED_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, config_results) + + if (config_results == ID_TOKEN) then + regions % function_oned = ID_FUNC + else if (config_results == MUL_TOKEN) then + regions % function_oned = MUL_FUNC + else + ! error if unknown operation + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // & + ' unknown 1d weighting function in ' // & + 'regional stats analysis member configuration.', MPAS_LOG_CRIT) + end if + + if (regions % function_oned == MUL_FUNC .and. & + (regions % operation == MIN_OP .or. regions % operation == MAX_OP)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 1d weighting function in ' // & + 'regional stats analysis member can only "mul" if the' // & + 'if the operation is "avg", "sum", or "sos"', MPAS_LOG_CRIT) + end if + + ! 2d weighting function + field_name = trim(storage_prefix) // trim(FUNCTION_TWOD_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % function_twod) + + field_name = trim(namelist_prefix) // trim(FUNCTION_TWOD_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, config_results) + + if (config_results == ID_TOKEN) then + regions % function_twod = ID_FUNC + else if (config_results == MUL_TOKEN) then + regions % function_twod = MUL_FUNC + else + ! error if unknown operation + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // & + ' unknown 2d weighting function in ' // & + 'regional stats analysis member configuration.', MPAS_LOG_CRIT) + end if + + if (regions % function_twod == MUL_FUNC .and. & + (regions % operation == MIN_OP .or. regions % operation == MAX_OP)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 2d weighting function in ' // & + 'regional stats analysis member can only "mul" if the' // & + 'if the operation is "avg", "sum", or "sos"', MPAS_LOG_CRIT) + end if + + ! group index + field_name = trim(storage_prefix) // trim(REGION_GROUP_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % group_index) + + field_name = trim(namelist_prefix) // trim(REGION_GROUP_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + config_results) + + call mpas_pool_get_array(maskPool, GROUP_NAMES_NAME, names) + + ! error if we can't find it + regions % group_index = 0 + do v = 1, number_of_groups + if (trim(names(v)) == trim(config_results)) then + regions % group_index = v + end if + end do + + if (regions % group_index == 0) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' unable to find the region group ' // & + 'named "' // trim(config_results) // '" in the region mask input stream', MPAS_LOG_CRIT) + end if + + ! + ! verify the other data is OK before trying to use it later + ! + + ! validate input stream is not none + field_name = trim(namelist_prefix) // trim(INPUT_STREAM_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, config_results) + + if (config_results == NONE_TOKEN) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' input stream for regional ' // & + 'stats AM cannot be "none". It needs to point to the region/mask stream.', MPAS_LOG_CRIT) + end if + + ! validate the restart stream is not none + field_name = trim(namelist_prefix) // trim(RESTART_STREAM_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, config_results_2) + + if (config_results == NONE_TOKEN) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' restart stream for regional ' // & + 'stats AM cannot be "none". It needs to point to the region/mask stream.', MPAS_LOG_CRIT) + end if + + ! make sure input and restart are the same + if (config_results /= config_results_2) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the input and restart stream ' // & + 'for regional stats AM need to be the same to ensure that it ' // & + 'has the same behavior on start and restart.', MPAS_LOG_CRIT) + end if + + ! validate vertical mask + field_name = trim(namelist_prefix) // trim(VERTICAL_MASK_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % vertical_mask) + + active_vertical = .false. + if (regions % vertical_mask /= NONE_TOKEN) then + call mpas_pool_get_field_info(domain % blocklist % allFields, & + regions % vertical_mask, info) + + if (info % fieldType /= MPAS_POOL_INTEGER) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the vertical mask ' // & + trim(regions % vertical_mask) // & + 'for the regional stats AM is not an integer field.', MPAS_LOG_CRIT) + end if + + if (info % nDims /= 2) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the vertical mask ' // & + trim(regions % vertical_mask) // & + ' for the regional stats AM is not a 2D field.', MPAS_LOG_CRIT) + end if + + call mpas_pool_get_field(domain % blocklist % allFields, & + regions % vertical_mask, mask) + + if (.not. check_element_dim(mask % dimNames, elem_name)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the vertical mask ' // & + trim(regions % vertical_mask) // & + ' for the regional stats AM needs to have ' // trim(elem_name) // & + ' as its last dimension.', MPAS_LOG_CRIT) + end if + + if (check_vertical_dim(mask % dimNames, regions % vertical_dim) == 0) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the vertical mask ' // & + trim(regions % vertical_mask) // & + ' for the regional stats AM does not have ' // & + trim(regions % vertical_dim) // & + ' as its second to last dimension.', MPAS_LOG_CRIT) + end if + + active_vertical = .true. + end if + + ! validate 1d weighting field + field_name = trim(namelist_prefix) // trim(WEIGHTS_ONED_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % weights_oned) + + if (regions % function_oned == ID_FUNC) then + if (regions % weights_oned /= NONE_TOKEN) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 1d weighting field "' // & + trim(regions % weights_oned) // '" is not set to "none" ' // & + 'when the 1d weighting function is set to "id" in ' // & + 'regional stats AM.', MPAS_LOG_CRIT) + end if + else + ! weighting field & info + if (regions % weights_oned == NONE_TOKEN) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 1d weighting field "' // & + trim(regions % weights_oned) // '" is set to "none" ' // & + 'when the 1d weighting function is something other than "id" in ' // & + 'regional stats AM.', MPAS_LOG_CRIT) + else + call mpas_pool_get_field_info(domain % blocklist % allFields, & + regions % weights_oned, info) + + if (info % nDims /= 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // & + ' the listed 1d weighting field "' // & + trim(regions % weights_oned) // & + '" for the regional stats AM is not actually a 1D field.', MPAS_LOG_CRIT) + end if + + ! check if we can handle it + if (info % fieldType == MPAS_POOL_REAL) then + call mpas_pool_get_field(domain % blocklist % allFields, & + regions % weights_oned, oned_field) + + if (.not. check_element_dim(oned_field % dimNames, elem_name)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 1d weighting field "' // & + regions % weights_oned // & + '" in regional stats AM needs to have ' // & + elem_name // ' dimensioni as its last dimension.', MPAS_LOG_CRIT) + end if + else + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 1d weighting field "' // & + trim(regions % weights_oned) // '" listed in the ' // & + 'namelist, for regional stats analysis member ' // & + 'stream, is not real.', MPAS_LOG_CRIT) + end if + end if + end if + + ! validate 2d weighting field + field_name = trim(namelist_prefix) // trim(WEIGHTS_TWOD_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, & + regions % weights_twod) + + if (.not. active_vertical) then + if (regions % function_twod /= ID_FUNC) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' vertical dimension is not set ' // & + 'in regional stats AM when the 2d weight function is something ' // & + 'than "id"', MPAS_LOG_CRIT) + end if + else if (regions % function_twod == ID_FUNC) then + if (regions % weights_twod /= NONE_TOKEN) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 2d weighting field "' // & + trim(regions % weights_twod) // '" is not set to "none"' // & + 'when the 2d weighting function is set to "id", in ' // & + 'regional stats AM.', MPAS_LOG_CRIT) + end if + else + ! weighting field & info + if (regions % weights_twod == NONE_TOKEN) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 2d weighting field "' // & + trim(regions % weights_twod) // '" is set to "none"' // & + 'when the 2d weighting function is something other than "id" in ' // & + 'regional stats AM.', MPAS_LOG_CRIT) + else + call mpas_pool_get_field_info(domain % blocklist % allFields, & + regions % weights_twod, info) + + if (info % nDims /= 2) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // & + ' the listed 2d weighting field "' // & + trim(regions % weights_twod) // & + '" for the regional stats AM is not actually a 2D field.', MPAS_LOG_CRIT) + end if + + ! check if we can handle it + if (info % fieldType == MPAS_POOL_REAL) then + call mpas_pool_get_field(domain % blocklist % allFields, & + regions % weights_twod, twod_field) + + if (.not. check_element_dim(twod_field % dimNames, elem_name)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 2d weighting field "' // & + regions % weights_twod // & + '" in regional stats AM needs to have ' // & + elem_name // ' as its last dimension.', MPAS_LOG_CRIT) + end if + + if (check_vertical_dim(twod_field % dimNames, & + regions % vertical_dim) == 0) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 2d weighting field "' // & + regions % weights_twod // & + '" in regional stats AM needs to have ' // & + regions % vertical_dim // & + ' vertical dimension as its second to last dimension.', MPAS_LOG_CRIT) + end if + else + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' 2d weighting field "' // & + trim(regions % weights_twod) // '" listed in the ' // & + 'namelist, for regional stats analysis member ' // & + 'stream, is not real.', MPAS_LOG_CRIT) + end if + end if + end if + + ! groups + call mpas_pool_get_array(maskPool, GROUP_DATA_NAME, regions % groups, 1) + + ! num_regions + call mpas_pool_get_array(maskPool, REGIONS_PER_NAME, & + regions % num_regions_per, 1) + + ! verify selection is OK + last = regions % num_regions_per(regions % group_index) + if (last > max_regions_per) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the number of regions for ' // & + 'selected group, in the regional stats analysis member, ' // & + 'is greater than the maximum number of regions dimension.', MPAS_LOG_CRIT) + end if + if (last < 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the number of regions for ' // & + 'selection group, in the regional stats analysis member, ' // & + 'is less than 1.', MPAS_LOG_CRIT) + end if + + do b = 1, last + v = regions % groups(b, regions % group_index) + if ((v < 1) .or. (v > number_of_regions)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' a region index found ' // & + 'in the list of regions for the selected group in the ' // & + 'regional stats analysis member is out of bounds for the size ' // & + 'of the regions found in the masks input.', MPAS_LOG_CRIT) + end if + end do + + + ! + ! modify the stream to create destination output variables and remove inputs + ! + + ! get the output stream name + field_name = trim(namelist_prefix) // trim(OUTPUT_STREAM_SUFFIX) + call mpas_pool_get_config(domain % configs, field_name, output_stream_name) + + if (output_stream_name == NONE_TOKEN) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' output stream cannot be "none" ' // & + 'for regional stats.', MPAS_LOG_CRIT) + end if + + ! number_of_variables + field_name = trim(storage_prefix) // trim(NUMBER_OF_VARIABLES_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % number_of_variables) + + ! count total number of fields + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + b = 0 + do while (mpas_stream_mgr_get_next_field(domain % streamManager, & + output_stream_name, field_name)) + b = b + 1 + end do + + allocate(valid_input(b)) + + ! count the number of variables and mark if valid + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + b = 1 + regions % number_of_variables = 0 + do while (mpas_stream_mgr_get_next_field(domain % streamManager, & + output_stream_name, field_name)) + + ! check if we can handle it + valid_input(b) = check_real_element_dim(domain % blocklist % allFields, & + field_name, elem_name) + + if (valid_input(b)) then + regions % number_of_variables = regions % number_of_variables + 1 + end if + + b = b + 1 + end do + + if (regions % number_of_variables < 1) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' there are no fields ' // & + 'in the regional stats output stream "' // trim(output_stream_name) // & + '" that regional stats can be applied to.', MPAS_LOG_CRIT) + end if + + ! create the memory + allocate(regions % variables(regions % number_of_variables)) + + ! create input variable name space + do v = 1, regions % number_of_variables + ! identifier + write(var_identifier, '(I0)') v + + ! create input name space + field_name = trim(storage_prefix) // '_' // trim(var_identifier) // & + trim(INPUT_NAME_SUFFIX) + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % variables(v) % input_name) + + ! create has_vertical space + field_name = trim(storage_prefix) // '_' // trim(var_identifier) // & + trim(HAS_VERTICAL_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, regions % variables(v) % & + has_vertical) + end do + + ! get the old field names, assign to input name, and remove from stream + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + b = 1 + v = 1 + do while (mpas_stream_mgr_get_next_field(domain % streamManager, & + output_stream_name, field_name)) + + ! check if we can handle it + if (valid_input(b)) then + regions % variables(v) % input_name = field_name + + ! remove the old one from the stream + call mpas_stream_mgr_remove_field(domain % streamManager, & + output_stream_name, regions % variables(v) % input_name) + + v = v + 1 + end if + + b = b + 1 + end do + + deallocate(valid_input) + + ! + ! modify the stream + ! + + ! set up the variables and create output memory in stream + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + + ! fetch region names and name via regions + call mpas_pool_get_array(maskPool, REGION_NAMES_NAME, names) + + last = regions % num_regions_per(regions % group_index) + ! allocate count and weight names + allocate(regions % count_zerod_names(last)) + allocate(regions % weight_zerod_names(last)) + allocate(regions % count_oned_names(last)) + allocate(regions % weight_oned_names(last)) + do b = 1, last + m = regions % groups(b, regions % group_index) + + ! counts + regions % count_zerod_names(b) = output_naming(COUNT_TOKEN, & + ZEROD_TOKEN, fix_region_name(names(m)), instance) + + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + regions % count_zerod_names(b)) + + ! add the field to the output stream + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, regions % count_zerod_names(b), & + ierr=err) + + ! create the counts and add to pool + if (active_vertical) then + ! count name + regions % count_oned_names(b) = output_naming(COUNT_TOKEN, & + ONED_TOKEN, fix_region_name(names(m)), instance) + + call add_new_integer_1d(domain % blocklist % allFields, & + domain % blocklist % allFields, amPool, & + regions % vertical_mask, regions % count_oned_names(b), & + elem_name) + + ! add the field to the output stream + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, regions % count_oned_names(b), & + ierr=err) + end if + + ! weights + if (regions % function_oned == MUL_FUNC) then + ! weight name + regions % weight_zerod_names(b) = & + output_naming(WEIGHT_TOKEN, & + ZEROD_TOKEN, fix_region_name(names(m)), instance) + + call add_new_real(domain % blocklist % allFields, amPool, amPool, & + regions % weight_zerod_names(b)) + + ! add the field to the output stream + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, regions % weight_zerod_names(b), & + ierr=err) + end if + + if (regions % function_twod == MUL_FUNC) then + ! weight name + regions % weight_oned_names(b) = & + output_naming(WEIGHT_TOKEN, & + ONED_TOKEN, fix_region_name(names(m)), instance) + + call add_new_real_1d(domain % blocklist % allFields, & + domain % blocklist % allFields, amPool, & + regions % vertical_mask, regions % weight_oned_names(b), & + elem_name) + + ! add the field to the output stream + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, regions % weight_oned_names(b), & + ierr=err) + end if + end do + + do v = 1, regions % number_of_variables + ! allocate output names + allocate(regions % variables(v) % output_names(last)) + + ! get the info of the field + call mpas_pool_get_field_info(domain % blocklist % allFields, & + regions % variables(v) % input_name, info) + + ! allocate output fields if the mask is active for this group + do b = 1, last + m = regions % groups(b, regions % group_index) + + ! region name + regions % variables(v) % output_names(b) = output_naming(op_name, & + regions % variables(v) % input_name, & + fix_region_name(names(m)), instance) + + ! create the field and add to pool, + ! also, set has_vertical if necessary + if (b == 1) then + if (active_vertical) then + call add_new_field(info, domain % blocklist % allFields, & + domain % blocklist % allFields, amPool, & + regions % variables(v) % input_name, & + regions % variables(v) % output_names(b), elem_name, & + regions % variables(v) % has_vertical, regions % vertical_dim) + else + call add_new_field(info, domain % blocklist % allFields, & + domain % blocklist % allFields, amPool, & + regions % variables(v) % input_name, & + regions % variables(v) % output_names(b), elem_name) + + regions % variables(v) % has_vertical = 0 + end if + else + call add_new_field(info, domain % blocklist % allFields, & + domain % blocklist % allFields, amPool, & + regions % variables(v) % input_name, & + regions % variables(v) % output_names(b), elem_name) + end if + + ! add the field to the output stream + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, regions % variables(v) % output_names(b), & + ierr=err) + end do + end do ! number_of_variables + + ! clean up the instance memory + do v = 1, regions % number_of_variables + deallocate(regions % variables(v) % output_names) + end do + deallocate(regions % variables) + deallocate(regions % count_zerod_names) + deallocate(regions % weight_zerod_names) + deallocate(regions % count_oned_names) + deallocate(regions % weight_oned_names) +end subroutine start_state + + + +!*********************************************************************** +! routine add_new_field +! +!> \brief Function to create a new field from an existing field +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> This routine conducts all initializations required for +!> duplicating a field and adding it to a pool. +!----------------------------------------------------------------------- +subroutine add_new_field(info, all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ + ! input variables + type (mpas_pool_field_info_type), intent(in) :: info + type (mpas_pool_type), pointer, intent(inout) :: inpool, outpool, all_fields + character (len=StrKIND), intent(in) :: inname, outname, elem_name + integer, intent(out), optional :: has_vertical + character (len=StrKIND), intent(in), optional :: vertical_dim + + ! input/output variables + + ! output variables + + ! local variables + + ! duplicate field and add new field to inpool, outpool + if ((info % nDims == 0) .or. (info % fieldType /= MPAS_POOL_REAL)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' the impossible happened. ' // & + 'Tried to create an output field for an input field that ' // & + 'regional stats AM cannot support.', MPAS_LOG_CRIT) + end if + + if (info % nDims == 1) then + if (present(has_vertical)) then + call copy_field_1r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim) + else + call copy_field_1r(all_fields, inpool, outpool, & + inname, outname, elem_name) + end if + else if (info % nDims == 2) then + if (present(has_vertical)) then + call copy_field_2r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim) + else + call copy_field_2r(all_fields, inpool, outpool, & + inname, outname, elem_name) + end if + else if (info % nDims == 3) then + if (present(has_vertical)) then + call copy_field_3r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim) + else + call copy_field_3r(all_fields, inpool, outpool, & + inname, outname, elem_name) + end if + else if (info % nDims == 4) then + if (present(has_vertical)) then + call copy_field_4r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim) + else + call copy_field_4r(all_fields, inpool, outpool, & + inname, outname, elem_name) + end if + else + if (present(has_vertical)) then + call copy_field_5r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim) + else + call copy_field_5r(all_fields, inpool, outpool, & + inname, outname, elem_name) + end if + end if + +end subroutine add_new_field!}}} + + + +!*********************************************************************** +! routine typed_operate +! +!> \brief Do the operation, but switch on run-time type +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Since we don't know the type of the array, we need to do some +!> run-time type switching based on the type of the array. +!----------------------------------------------------------------------- +subroutine typed_operate(dminfo, blocklist, regions, variable) !{{{ + + ! input variables + type (dm_info), pointer, intent(in) :: dminfo + type (block_type), pointer, intent(in) :: blocklist + type (regional_type), intent(in) :: regions + type (regional_variable_type), intent(in) :: variable + + ! input/output variables + + ! output variables + + ! local variables + type (mpas_pool_field_info_type) :: info + integer, pointer :: levels + + ! get the info + call mpas_pool_get_field_info(blocklist % allFields, & + variable % input_name, info) + + if (variable % has_vertical /= 0) then + call mpas_pool_get_dimension(blocklist % dimensions, & + regions % vertical_dim, levels) + end if + + ! switch based on the type, dimensionality, operation, and vertical dim + if (info % nDims == 1) then + if (regions % operation == AVG_OP) then + call operate1r_avg(dminfo, blocklist, regions, variable) + else if (regions % operation == MIN_OP) then + call operate1r_min(dminfo, blocklist, regions, variable) + else if (regions % operation == MAX_OP) then + call operate1r_max(dminfo, blocklist, regions, variable) + else if (regions % operation == SUM_OP) then + call operate1r_sum(dminfo, blocklist, regions, variable) + else if (regions % operation == SOS_OP) then + call operate1r_sos(dminfo, blocklist, regions, variable) + else + call mpas_log_write(trim(CURRENT_CORE_NAME) // & + 'the impossible happened - tried to operate with an unknown ' // & + 'operator in the regional stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 2) then + if (regions % operation == AVG_OP) then + if (variable % has_vertical /= 0) then + call operatevert2r_avg(dminfo, blocklist, regions, variable, levels) + else + call operate2r_avg(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MIN_OP) then + if (variable % has_vertical /= 0) then + call operatevert2r_min(dminfo, blocklist, regions, variable, levels) + else + call operate2r_min(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MAX_OP) then + if (variable % has_vertical /= 0) then + call operatevert2r_max(dminfo, blocklist, regions, variable, levels) + else + call operate2r_max(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SUM_OP) then + if (variable % has_vertical /= 0) then + call operatevert2r_sum(dminfo, blocklist, regions, variable, levels) + else + call operate2r_sum(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SOS_OP) then + if (variable % has_vertical /= 0) then + call operatevert2r_sos(dminfo, blocklist, regions, variable, levels) + else + call operate2r_sos(dminfo, blocklist, regions, variable) + end if + else + call mpas_log_write(trim(CURRENT_CORE_NAME) // & + 'the impossible happened - tried to operate with an unknown ' // & + 'operator in the regional stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 3) then + if (regions % operation == AVG_OP) then + if (variable % has_vertical /= 0) then + call operatevert3r_avg(dminfo, blocklist, regions, variable, levels) + else + call operate3r_avg(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MIN_OP) then + if (variable % has_vertical /= 0) then + call operatevert3r_min(dminfo, blocklist, regions, variable, levels) + else + call operate3r_min(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MAX_OP) then + if (variable % has_vertical /= 0) then + call operatevert3r_max(dminfo, blocklist, regions, variable, levels) + else + call operate3r_max(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SUM_OP) then + if (variable % has_vertical /= 0) then + call operatevert3r_sum(dminfo, blocklist, regions, variable, levels) + else + call operate3r_sum(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SOS_OP) then + if (variable % has_vertical /= 0) then + call operatevert3r_sos(dminfo, blocklist, regions, variable, levels) + else + call operate3r_sos(dminfo, blocklist, regions, variable) + end if + else + call mpas_log_write(trim(CURRENT_CORE_NAME) // & + 'the impossible happened - tried to operate with an unknown ' // & + 'operator in the regional stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 4) then + if (regions % operation == AVG_OP) then + if (variable % has_vertical /= 0) then + call operatevert4r_avg(dminfo, blocklist, regions, variable, levels) + else + call operate4r_avg(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MIN_OP) then + if (variable % has_vertical /= 0) then + call operatevert4r_min(dminfo, blocklist, regions, variable, levels) + else + call operate4r_min(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MAX_OP) then + if (variable % has_vertical /= 0) then + call operatevert4r_max(dminfo, blocklist, regions, variable, levels) + else + call operate4r_max(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SUM_OP) then + if (variable % has_vertical /= 0) then + call operatevert4r_sum(dminfo, blocklist, regions, variable, levels) + else + call operate4r_sum(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SOS_OP) then + if (variable % has_vertical /= 0) then + call operatevert4r_sos(dminfo, blocklist, regions, variable, levels) + else + call operate4r_sos(dminfo, blocklist, regions, variable) + end if + else + call mpas_log_write(trim(CURRENT_CORE_NAME) // & + 'the impossible happened - tried to operate with an unknown ' // & + 'operator in the regional stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 5) then + if (regions % operation == AVG_OP) then + if (variable % has_vertical /= 0) then + call operatevert5r_avg(dminfo, blocklist, regions, variable, levels) + else + call operate5r_avg(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MIN_OP) then + if (variable % has_vertical /= 0) then + call operatevert5r_min(dminfo, blocklist, regions, variable, levels) + else + call operate5r_min(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == MAX_OP) then + if (variable % has_vertical /= 0) then + call operatevert5r_max(dminfo, blocklist, regions, variable, levels) + else + call operate5r_max(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SUM_OP) then + if (variable % has_vertical /= 0) then + call operatevert5r_sum(dminfo, blocklist, regions, variable, levels) + else + call operate5r_sum(dminfo, blocklist, regions, variable) + end if + else if (regions % operation == SOS_OP) then + if (variable % has_vertical /= 0) then + call operatevert5r_sos(dminfo, blocklist, regions, variable, levels) + else + call operate5r_sos(dminfo, blocklist, regions, variable) + end if + else + call mpas_log_write(trim(CURRENT_CORE_NAME) // & + 'the impossible happened - tried to operate with an unknown ' // & + 'operator in the regional stats AM', MPAS_LOG_CRIT) + end if + else + call mpas_log_write(trim(CURRENT_CORE_NAME) // & + 'the impossible happened - tried to operate on a real field "' // & + trim(variable % input_name) // '"that does not have 1-5 ' // & + 'dimensionality in the regional stats AM', MPAS_LOG_CRIT) + end if + +end subroutine typed_operate!}}} + + + +!*********************************************************************** +! routine copy_field_X +! +!> \brief Functions to create a new N-1 D field from an existing ND field +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> This routine conducts initializations required for +!> duplicating a ND field and adding a N-1D to the AM's pool. +!> This will only create an output in the first block of a blocklist. +!----------------------------------------------------------------------- +subroutine copy_field_1r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ +#include "regional_stats_inc/regional_field_1d_1.inc" + type (field1DReal), pointer :: src + type (field0DReal), pointer :: dst +#include "regional_stats_inc/regional_field_1d_2.inc" +end subroutine copy_field_1r!}}} + + +subroutine copy_field_2r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ +#include "regional_stats_inc/regional_field_nd_1.inc" + type (field2DReal), pointer :: src + type (field1DReal), pointer :: dst + integer, dimension(2) :: src_dims +#include "regional_stats_inc/regional_field_nd_2.inc" + allocate(dst % array(src_dims(1))) +#include "regional_stats_inc/regional_field_nd_3.inc" +end subroutine copy_field_2r!}}} + + +subroutine copy_field_3r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ +#include "regional_stats_inc/regional_field_nd_1.inc" + type (field3DReal), pointer :: src + type (field2DReal), pointer :: dst + integer, dimension(3) :: src_dims +#include "regional_stats_inc/regional_field_nd_2.inc" + allocate(dst % array(src_dims(1), src_dims(2))) +#include "regional_stats_inc/regional_field_nd_3.inc" +end subroutine copy_field_3r!}}} + + +subroutine copy_field_4r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ +#include "regional_stats_inc/regional_field_nd_1.inc" + type (field4DReal), pointer :: src + type (field3DReal), pointer :: dst + integer, dimension(4) :: src_dims +#include "regional_stats_inc/regional_field_nd_2.inc" + allocate(dst % array(src_dims(1), src_dims(2), src_dims(3))) +#include "regional_stats_inc/regional_field_nd_3.inc" +end subroutine copy_field_4r!}}} + + +subroutine copy_field_5r(all_fields, inpool, outpool, & + inname, outname, elem_name, & + has_vertical, vertical_dim)!{{{ +#include "regional_stats_inc/regional_field_nd_1.inc" + type (field5DReal), pointer :: src + type (field4DReal), pointer :: dst + integer, dimension(5) :: src_dims +#include "regional_stats_inc/regional_field_nd_2.inc" + allocate(dst % array(src_dims(1), src_dims(2), src_dims(3), src_dims(4))) +#include "regional_stats_inc/regional_field_nd_3.inc" +end subroutine copy_field_5r!}}} + + + +!*********************************************************************** +! routine operateX_Y +! +!> \brief Series of subroutines to support operations on run-time types +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> These subroutines encapsulate the different operations that can occur +!> based on the run-time types. (This would likely be +!> instantiated generics/templates in other languages.) +!> +!> We will do the reductions using slicing operations rather +!> than for loops, and then copy to and from a flattened array, +!> during distributed communication. +!> +!> This will be slower than for loops using flattened array output +!> indexing on the first copy, because we're going to have to copy twice +!> on the distributed communication. (Because the framework reduceAll +!> only works for rank-1 arrays.) The reason to do this is that +!> to preserve the dimensionality semantics for legibility and +!> for fewer bugs in the reduction. Basically, we flatten and unflatten, +!> and could remove the flatten, and reduce in a for loop with indexing +!> on a flattened array, and then reshape. While it could be possible to +!> do manual for loops without slicing, and increasing the code, +!> I doubt the performance gain for doing one less copy of a small +!> array will be that significant. +!----------------------------------------------------------------------- + +subroutine operate2r_avg (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_avg_1.inc" + out_array = out_array + in_array(:, i) * mask(m, i) +#include "regional_stats_inc/regional_op_avg_2.inc" + out_array = out_array + in_array(:, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_avg_3.inc" +end subroutine operate2r_avg + + +subroutine operate5r_avg (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_avg_1.inc" + out_array = out_array + in_array(:, :, :, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_avg_2.inc" + out_array = out_array + in_array(:, :, :, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_avg_3.inc" +end subroutine operate5r_avg + + +subroutine operate4r_avg (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_avg_1.inc" + out_array = out_array + in_array(:, :, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_avg_2.inc" + out_array = out_array + in_array(:, :, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_avg_3.inc" +end subroutine operate4r_avg + + +subroutine operate3r_avg (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_avg_1.inc" + out_array = out_array + in_array(:, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_avg_2.inc" + out_array = out_array + in_array(:, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_avg_3.inc" +end subroutine operate3r_avg + + +subroutine operate1r_avg (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array + real (kind=RKIND), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_avg1d_1.inc" + out_array = out_array + in_array(i) * mask(m, i) +#include "regional_stats_inc/regional_op_avg1d_2.inc" + out_array = out_array + in_array(i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_avg1d_3.inc" +end subroutine operate1r_avg + + +subroutine operate2r_min (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_min_1.inc" + out_array = min(out_array, & + in_array(:, i) * mask(m, i) + & + DEFAULT_MPAS_MAX_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_min_2.inc" +end subroutine operate2r_min + + +subroutine operate5r_min (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_min_1.inc" + out_array = min(out_array, & + in_array(:, :, :, :, i) * mask(m, i) + & + DEFAULT_MPAS_MAX_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_min_2.inc" +end subroutine operate5r_min + + +subroutine operate4r_min (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_min_1.inc" + out_array = min(out_array, & + in_array(:, :, :, i) * mask(m, i) + & + DEFAULT_MPAS_MAX_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_min_2.inc" +end subroutine operate4r_min + + +subroutine operate3r_min (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_min_1.inc" + out_array = min(out_array, & + in_array(:, :, i) * mask(m, i) + & + DEFAULT_MPAS_MAX_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_min_2.inc" +end subroutine operate3r_min + + +subroutine operate1r_min (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array + real (kind=RKIND), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_min1d_1.inc" + out_array = min(out_array, & + in_array(i) * mask(m, i) + & + DEFAULT_MPAS_MAX_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_min1d_2.inc" +end subroutine operate1r_min + + +subroutine operate2r_max (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_max_1.inc" + out_array = max(out_array, & + in_array(:, i) * mask(m, i) + & + DEFAULT_MPAS_MIN_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_max_2.inc" +end subroutine operate2r_max + + +subroutine operate5r_max (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_max_1.inc" + out_array = max(out_array, & + in_array(:, :, :, :, i) * mask(m, i) + & + DEFAULT_MPAS_MIN_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_max_2.inc" +end subroutine operate5r_max + + +subroutine operate4r_max (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_max_1.inc" + out_array = max(out_array, & + in_array(:, :, :, i) * mask(m, i) + & + DEFAULT_MPAS_MIN_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_max_2.inc" +end subroutine operate4r_max + + +subroutine operate3r_max (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_max_1.inc" + out_array = max(out_array, & + in_array(:, :, i) * mask(m, i) + & + DEFAULT_MPAS_MIN_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_max_2.inc" +end subroutine operate3r_max + + +subroutine operate1r_max (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array + real (kind=RKIND), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_max1d_1.inc" + out_array = max(out_array, & + in_array(i) * mask(m, i) + & + DEFAULT_MPAS_MIN_VALUE * (1 - mask(m, i))) +#include "regional_stats_inc/regional_op_max1d_2.inc" +end subroutine operate1r_max + + +subroutine operate2r_sum (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate2r_sum + + +subroutine operate5r_sum (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, :, :, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, :, :, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate5r_sum + + +subroutine operate4r_sum (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, :, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, :, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate4r_sum + + +subroutine operate3r_sum (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate3r_sum + + +subroutine operate1r_sum (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array + real (kind=RKIND), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos1d_1.inc" + out_array = out_array + in_array(i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos1d_2.inc" + out_array = out_array + in_array(i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos1d_3.inc" +end subroutine operate1r_sum + + +subroutine operate2r_sos (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, i) * in_array(:, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, i) * & + in_array(:, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate2r_sos + + +subroutine operate5r_sos (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, :, :, :, i) * & + in_array(:, :, :, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, :, :, :, i) * & + in_array(:, :, :, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate5r_sos + + +subroutine operate4r_sos (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, :, :, i) * & + in_array(:, :, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, :, :, i) * & + in_array(:, :, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate4r_sos + + +subroutine operate3r_sos (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos_1.inc" + out_array = out_array + in_array(:, :, i) * & + in_array(:, :, i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos_2.inc" + out_array = out_array + in_array(:, :, i) * & + in_array(:, :, i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos_3.inc" +end subroutine operate3r_sos + + +subroutine operate1r_sos (dminfo, start_block, regions, variable) +#include "regional_stats_inc/regional_op_start_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array + real (kind=RKIND), pointer :: out_array +#include "regional_stats_inc/regional_op_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_op_start_3.inc" + +#include "regional_stats_inc/regional_op_sumsos1d_1.inc" + out_array = out_array + in_array(i) * & + in_array(i) * mask(m, i) +#include "regional_stats_inc/regional_op_sumsos1d_2.inc" + out_array = out_array + in_array(i) * & + in_array(i) * (weights(i) * mask(m, i)) +#include "regional_stats_inc/regional_op_sumsos1d_3.inc" +end subroutine operate1r_sos + + +subroutine operatevert2r_avg (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_avg_1.inc" + out_array(v) = out_array(v) + in_array(v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_avg_2.inc" + out_array(v) = out_array(v) + in_array(v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_avg_3.inc" + do v = 1, levels + if (count_array(v) > 0) then + if (regions % function_twod == ID_FUNC) then + out_array(v) = out_array(v) / count_array(v) + else + out_array(v) = out_array(v) / weight_total(v) + end if + end if + end do +#include "regional_stats_inc/regional_opvert_avg_4.inc" +end subroutine operatevert2r_avg + + +subroutine operatevert5r_avg (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_avg_1.inc" + out_array(:, :, :, v) = out_array(:, :, :, v) + in_array(:, :, :, v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_avg_2.inc" + out_array(:, :, :, v) = out_array(:, :, :, v) + in_array(:, :, :, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_avg_3.inc" + do v = 1, levels + if (count_array(v) > 0) then + if (regions % function_twod == ID_FUNC) then + out_array(:, :, :, v) = out_array(:, :, :, v) / count_array(v) + else + out_array(:, :, :, v) = out_array(:, :, :, v) / weight_total(v) + end if + end if + end do +#include "regional_stats_inc/regional_opvert_avg_4.inc" +end subroutine operatevert5r_avg + + +subroutine operatevert4r_avg (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_avg_1.inc" + out_array(:, :, v) = out_array(:, :, v) + in_array(:, :, v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_avg_2.inc" + out_array(:, :, v) = out_array(:, :, v) + in_array(:, :, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_avg_3.inc" + do v = 1, levels + if (count_array(v) > 0) then + if (regions % function_twod == ID_FUNC) then + out_array(:, :, v) = out_array(:, :, v) / count_array(v) + else + out_array(:, :, v) = out_array(:, :, v) / weight_total(v) + end if + end if + end do +#include "regional_stats_inc/regional_opvert_avg_4.inc" +end subroutine operatevert4r_avg + + +subroutine operatevert3r_avg (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_avg_1.inc" + out_array(:, v) = out_array(:, v) + in_array(:, v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_avg_2.inc" + out_array(:, v) = out_array(:, v) + in_array(:, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_avg_3.inc" + do v = 1, levels + if (count_array(v) > 0) then + if (regions % function_twod == ID_FUNC) then + out_array(:, v) = out_array(:, v) / count_array(v) + else + out_array(:, v) = out_array(:, v) / weight_total(v) + end if + end if + end do +#include "regional_stats_inc/regional_opvert_avg_4.inc" +end subroutine operatevert3r_avg + + +subroutine operatevert2r_min (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_min_1.inc" + out_array(v) = min(out_array(v), & + in_array(v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MAX_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_min_2.inc" +end subroutine operatevert2r_min + + +subroutine operatevert5r_min (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_min_1.inc" + out_array(:, :, :, v) = min(out_array(:, :, :, v), & + in_array(:, :, :, v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MAX_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_min_2.inc" +end subroutine operatevert5r_min + + +subroutine operatevert4r_min (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_min_1.inc" + out_array(:, :, v) = min(out_array(:, :, v), & + in_array(:, :, v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MAX_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_min_2.inc" +end subroutine operatevert4r_min + + +subroutine operatevert3r_min (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MAX_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_min_1.inc" + out_array(:, v) = min(out_array(:, v), & + in_array(:, v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MAX_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_min_2.inc" +end subroutine operatevert3r_min + + +subroutine operatevert2r_max (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_max_1.inc" + out_array(v) = max(out_array(v), & + in_array(v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MIN_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_max_2.inc" +end subroutine operatevert2r_max + + +subroutine operatevert5r_max (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_max_1.inc" + out_array(:, :, :, v) = max(out_array(:, :, :, v), & + in_array(:, :, :, v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MIN_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_max_2.inc" +end subroutine operatevert5r_max + + +subroutine operatevert4r_max (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_max_1.inc" + out_array(:, :, v) = max(out_array(:, :, v), & + in_array(:, :, v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MIN_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_max_2.inc" +end subroutine operatevert4r_max + + +subroutine operatevert3r_max (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = DEFAULT_MPAS_MIN_VALUE +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_max_1.inc" + out_array(:, v) = max(out_array(:, v), & + in_array(:, v, i) * (mask(m, i) * vertical_mask(v, i)) + & + DEFAULT_MPAS_MIN_VALUE * (1 - (mask(m, i) * vertical_mask(v, i)))) +#include "regional_stats_inc/regional_opvert_max_2.inc" +end subroutine operatevert3r_max + + +subroutine operatevert2r_sum (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(v) = out_array(v) + in_array(v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(v) = out_array(v) + in_array(v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert2r_sum + + +subroutine operatevert5r_sum (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(:, :, :, v) = out_array(:, :, :, v) + in_array(:, :, :, v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(:, :, :, v) = out_array(:, :, :, v) + in_array(:, :, :, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert5r_sum + + +subroutine operatevert4r_sum (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(:, :, v) = out_array(:, :, v) + in_array(:, :, v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(:, :, v) = out_array(:, :, v) + in_array(:, :, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert4r_sum + + +subroutine operatevert3r_sum (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(:, v) = out_array(:, v) + in_array(:, v, i) * & + (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(:, v) = out_array(:, v) + in_array(:, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert3r_sum + + +subroutine operatevert2r_sos (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array + real (kind=RKIND), dimension(:), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(v) = out_array(v) + in_array(v, i) * & + in_array(v, i) * (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(v) = out_array(v) + in_array(v, i) * & + in_array(v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert2r_sos + + +subroutine operatevert5r_sos (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(:, :, :, v) = out_array(:, :, :, v) + in_array(:, :, :, v, i) * & + in_array(:, :, :, v, i) * (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(:, :, :, v) = out_array(:, :, :, v) + in_array(:, :, :, v, i) * & + in_array(:, :, :, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert5r_sos + + +subroutine operatevert4r_sos (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(:, :, v) = out_array(:, :, v) + in_array(:, :, v, i) * & + in_array(:, :, v, i) * (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(:, :, v) = out_array(:, :, v) + in_array(:, :, v, i) * & + in_array(:, :, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert4r_sos + + +subroutine operatevert3r_sos (dminfo, start_block, regions, variable, levels) +#include "regional_stats_inc/regional_opvert_start_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array + real (kind=RKIND), dimension(:, :), pointer :: out_array +#include "regional_stats_inc/regional_opvert_start_2.inc" + out_array = 0 +#include "regional_stats_inc/regional_opvert_start_3.inc" + +#include "regional_stats_inc/regional_opvert_sumsos_1.inc" + out_array(:, v) = out_array(:, v) + in_array(:, v, i) * & + in_array(:, v, i) * (mask(m, i) * vertical_mask(v, i)) +#include "regional_stats_inc/regional_opvert_sumsos_2.inc" + out_array(:, v) = out_array(:, v) + in_array(:, v, i) * & + in_array(:, v, i) * & + (weights(v, i) * (mask(m, i) * vertical_mask(v, i))) +#include "regional_stats_inc/regional_opvert_sumsos_3.inc" +end subroutine operatevert3r_sos + + +end module ocn_regional_stats +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_rpn_calculator.F b/src/core_ocean/analysis_members/mpas_ocn_rpn_calculator.F new file mode 100644 index 0000000000..0cf650b5f6 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_rpn_calculator.F @@ -0,0 +1,1240 @@ +! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! ocn_rpn_calculator +! +!> \brief MPAS ocean analysis core member: rpn_calculator +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details +!> Flexible vector RPN calculator of MPAS fields for up to 2D fields. +!----------------------------------------------------------------------- +module ocn_rpn_calculator + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + ! Public parameters + !-------------------------------------------------------------------- + + ! Public member functions + !-------------------------------------------------------------------- + public :: & + ocn_init_rpn_calculator, & + ocn_compute_rpn_calculator, & + ocn_restart_rpn_calculator, & + ocn_finalize_rpn_calculator + + ! Private module variables + !-------------------------------------------------------------------- + + type rpn_stack_value_type + integer :: symbol_type + integer :: number_of_dims + + type (field0DReal), pointer :: d0 + type (field1DReal), pointer :: d1 + type (field2DReal), pointer :: d2 + end type rpn_stack_value_type + + integer, parameter :: SYMBOL_NOT_FOUND = 0 + + integer, parameter :: IS_OPERATOR = 10 + integer, parameter :: IS_VARIABLE = 100 + integer, parameter :: IS_TEMPORARY = 1000 + + integer, parameter :: MAX_STACK_SIZE = StrKIND / 2 + + character (len=1), dimension(8), parameter :: variable_names = & + (/ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' /) + character (len=3), dimension(4) :: operator_names = & + (/ '* ' , '+ ', '- ', '/ ' /) + integer, parameter :: MUL_OP = 1 + integer, parameter :: PLUS_OP = 2 + integer, parameter :: MINUS_OP = 3 + integer, parameter :: DIV_OP = 4 + ! TODO FIXME + ! integer, parameter :: SUM_OP = 5 + + character (len=1), dimension(4) :: expression_names = & + (/ '1', '2', '3', '4' /) + + character (len=StrKIND), parameter :: VARIABLE_PREFIX = & + 'config_AM_rpnCalculator_variable_' + character (len=StrKIND), parameter :: EXPRESSION_PREFIX = & + 'config_AM_rpnCalculator_expression_' + character (len=StrKIND), parameter :: OUTPUT_PREFIX = & + 'config_AM_rpnCalculator_output_name_' + + character (len=StrKIND), parameter :: OUTPUT_STREAM_CONFIG = & + 'config_AM_rpnCalculator_output_stream' + + character (len=StrKIND), parameter :: NONE_TOKEN = 'none' + + character (len=StrKIND), parameter :: MPAS_CORE_NAME = 'MPAS-Ocean' + +!*********************************************************************** +contains + + + +!*********************************************************************** +! routine ocn_init_rpn_calculator +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_init_rpn_calculator(domain, err)!{{{ + ! input variables + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: i, last, stack_pointer + character (len=StrKIND) :: config, field_name + character (len=StrKIND), pointer :: config_result + type (rpn_stack_value_type) :: output_value + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE) :: stack + + ! start procedure + err = 0 + + ! typecheck all the expressions + last = size(expression_names) + do i = 1, last + config = trim(EXPRESSION_PREFIX) // trim(expression_names(i)) + call mpas_pool_get_config(domain % configs, config, config_result) + + if (trim(config_result) /= trim(NONE_TOKEN)) then + stack_pointer = -1 ! typecheck with an empty stack + call eval_expression(domain, config_result, i, stack, stack_pointer) + + ! check the stack size + if (stack_pointer /= 1) then + call mpas_log_write( & + 'expression #' // trim(expression_names(i)) // & + ' in the RPN calculator AM ' // & + 'resulted in the stack size not being equal to 1: ' // & + 'i.e., the return result of the expression should be the only ' // & + 'value on the stack after evaluation', MPAS_LOG_CRIT) + end if + + ! check that it's a new value + if (stack(stack_pointer) % symbol_type /= IS_TEMPORARY) then + call mpas_log_write( & + 'expression #' // trim(expression_names(i)) // & + ' in the RPN calculator AM did not calculate anything, ' // & + ' i.e., it only pushed a variable onto the stack', MPAS_LOG_CRIT) + end if + + ! rename the stack field and put in allFields pool + config = trim(OUTPUT_PREFIX) // trim(expression_names(i)) + call mpas_pool_get_config(domain % configs, config, config_result) + + if (trim(config_result) == (NONE_TOKEN)) then + call mpas_log_write( & + 'expression #' // trim(expression_names(i)) // & + ' in the RPN calculator AM was set, but the output field name ' // & + 'for that expression was set to "none"', MPAS_LOG_CRIT) + end if + + if (stack(1) % number_of_dims == 0) then + stack(1) % d0 % fieldName = config_result + call mpas_pool_add_field(domain % blocklist % allFields, & + config_result, stack(1) % d0) + else if (stack(1) % number_of_dims == 1) then + stack(1) % d1 % fieldName = config_result + call mpas_pool_add_field(domain % blocklist % allFields, & + config_result, stack(1) % d1) + else if (stack(1) % number_of_dims == 2) then + stack(1) % d2 % fieldName = config_result + call mpas_pool_add_field(domain % blocklist % allFields, & + config_result, stack(1) % d2) + else + call mpas_log_write( & + 'the impossible happened, the dimensions of the result on the ' // & + 'stack, for expression #' // trim(expression_names(i)) // & + ' was not between 0 and 2 in the RPN calculator AM', MPAS_LOG_CRIT) + end if + field_name = config_result + + ! put them in the stream if necessary + call mpas_pool_get_config(domain % configs, & + OUTPUT_STREAM_CONFIG, config_result) + + if (trim(config_result) /= trim(NONE_TOKEN)) then + call mpas_stream_mgr_add_field(domain % streamManager, & + config_result, field_name, ierr=err) + end if + end if + end do + +end subroutine ocn_init_rpn_calculator!}}} + + + +!*********************************************************************** +! routine ocn_compute_rpn_calculator +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_compute_rpn_calculator(domain, timeLevel, err)!{{{ + ! input variables + integer, intent(in) :: timeLevel + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: i, stack_pointer, last + character (len=StrKIND) :: config + character (len=StrKIND), pointer :: config_result + type (rpn_stack_value_type) :: output_value + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE) :: stack + type (field0DReal), pointer :: d0 + type (field1DReal), pointer :: d1, t1 + type (field2DReal), pointer :: d2, t2 + real (kind=RKIND), dimension(:), pointer :: s1 + real (kind=RKIND), dimension(:,:), pointer :: s2 + + ! start procedure + err = 0 + + ! do all the expressions + last = size(expression_names) + do i = 1, last + config = trim(EXPRESSION_PREFIX) // trim(expression_names(i)) + call mpas_pool_get_config(domain % configs, config, config_result) + + if (trim(config_result) /= trim(NONE_TOKEN)) then + stack_pointer = 0 ! evaluate with an empty stack + call eval_expression(domain, config_result, i, stack, stack_pointer) + + ! lookup the field and reassign pointers - then deallocate stack + config = trim(OUTPUT_PREFIX) // trim(expression_names(i)) + call mpas_pool_get_config(domain % configs, config, config_result) + if (stack(1) % number_of_dims == 0) then + call mpas_pool_get_field(domain % blocklist % allFields, & + config_result, d0, 1) + d0 % scalar = stack(1) % d0 % scalar + call mpas_deallocate_field(stack(1) % d0) + else if (stack(1) % number_of_dims == 1) then + call mpas_pool_get_field(domain % blocklist % allFields, & + config_result, d1, 1) + t1 => stack(1) % d1 + do while (associated(d1)) + s1 => d1 % array + d1 % array => t1 % array + t1 % array => s1 + + d1 => d1 % next + t1 => t1 % next + end do + call mpas_deallocate_field(stack(1) % d1) + else + call mpas_pool_get_field(domain % blocklist % allFields, & + config_result, d2, 1) + t2 => stack(1) % d2 + do while (associated(d2)) + s2 => d2 % array + d2 % array => t2 % array + t2 % array => s2 + + d2 => d2 % next + t2 => t2 % next + end do + call mpas_deallocate_field(stack(1) % d2) + end if + + end if + end do + +end subroutine ocn_compute_rpn_calculator!}}} + + + +!*********************************************************************** +! routine ocn_restart_rpn_calculator +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_restart_rpn_calculator(domain, err)!{{{ + ! input variables + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + + ! start procedure + err = 0 + +end subroutine ocn_restart_rpn_calculator!}}} + + + +!*********************************************************************** +! routine ocn_finalize_rpn_calculator +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_finalize_rpn_calculator(domain, err)!{{{ + ! input variables + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + + ! start procedure + err = 0 + +end subroutine ocn_finalize_rpn_calculator!}}} + +! +! local subroutines +! + +!*********************************************************************** +! routine eval_expression +! +!> \brief Given a character string, evaluate the stack expression +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a character string, evaluate the stack expression +!> and copy the bottom (top) of a 1-length stack into the target MPAS field. +!----------------------------------------------------------------------- +subroutine eval_expression (domain, expression, exp_number, & + stack, stack_pointer)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: expression + integer, intent(in) :: exp_number + + ! input/output variables + type (domain_type), intent(inout) :: domain + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + + ! output variables + + ! local variables + integer :: symbol_type + logical :: eol, typechecking + character (len=StrKIND) :: symbol, remainder + + ! start procedure + if(stack_pointer < 0) then + typechecking = .true. + stack_pointer = -1 - stack_pointer + else + typechecking = .false. + end if + + eol = .false. + remainder = expression + + ! get the first symbol + call stack_token(symbol, remainder, eol) + + ! iterate over symbols + do while(.not. eol) + symbol_type = symbol_table(symbol) + + ! operator + if ((symbol_type > IS_OPERATOR) .and. (symbol_type < IS_VARIABLE)) then + call eval_operator(exp_number, & + symbol_type - IS_OPERATOR, stack, stack_pointer, typechecking) + ! variable + else & + if ((symbol_type > IS_VARIABLE) .and. (symbol_type < IS_TEMPORARY)) then + call eval_variable(domain, exp_number, & + symbol_type - IS_VARIABLE, stack, stack_pointer, typechecking) + ! symbol not found + else + call mpas_log_write( & + trim(symbol) // '" found in expression #' // & + trim(expression_names(exp_number)) // & + ' in the RPN calculator AM was not found.', MPAS_LOG_CRIT) + end if + + ! get the next symbol + call stack_token(symbol, remainder, eol) + end do + +end subroutine eval_expression!}}} + + + +!*********************************************************************** +! routine stack_token +! +!> \brief Get the next stack token given a character string +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Parses a character string to get the next stack token to eval. +!----------------------------------------------------------------------- +subroutine stack_token(substr, next, eol)!{{{ + ! input variables + + ! input/output variables + character (len=StrKIND), intent(inout) :: next + + ! output variables + character (len=StrKIND), intent(out) :: substr + logical, intent(out) :: eol + + ! local variables + integer :: i + character (len=StrKIND) :: copy + + ! make a copy + copy = trim(next) + + ! if there's anything in it other than whitespace, pass through + i = verify(copy, ' ') + eol = i < 1 + if (eol) then + return + end if + copy = trim(next(i:)) + + ! find the first whitespace and split + i = scan(copy, ' ') + + ! return that substring and the remainder + if (i > 0) then + substr = trim(copy(1:i-1)) + next = trim(copy(i+1:)) + else + substr = trim(copy) + next = '' + end if + +end subroutine stack_token!}}} + + + +!*********************************************************************** +! function symbol_table +! +!> \brief Tries to find the symbol in the symbol table and its value +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Will attempt to find a symbol in the symbol table. The value +!> results are dependent on the return code of the symbol table lookup. +!----------------------------------------------------------------------- +integer function symbol_table (symbol)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: symbol + + ! input/output variables + + ! local variables + integer :: i, last + + ! start procedure + + ! check the operations + last = size(variable_names) + do i = 1, last + if (trim(symbol) == trim(variable_names(i))) then + symbol_table = IS_VARIABLE + i + return + end if + end do + + ! check the variables + last = size(operator_names) + do i = 1, last + if (trim(symbol) == trim(operator_names(i))) then + symbol_table = IS_OPERATOR + i + return + end if + end do + + ! else not found + symbol_table = SYMBOL_NOT_FOUND + +end function symbol_table!}}} + + + +!*********************************************************************** +! routine eval_operator +! +!> \brief Given a operator index number, put it on the stack +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a operator index number, put the result on the top of +!> the stack. It will combine whatever is on the stack to be able to +!> generate results and push them into the stack. +!----------------------------------------------------------------------- +subroutine eval_operator (exp_number, & + op_index, stack, stack_pointer, type_checking)!{{{ + ! input variables + integer, intent(in) :: exp_number, op_index + logical, intent(in) :: type_checking + + ! input/output variables + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + + ! output variables + + ! local variables + + ! start procedure + if (op_index == MUL_OP) then + call mul_operator(exp_number, stack, stack_pointer, type_checking) + else if (op_index == PLUS_OP) then + call plus_operator(exp_number, stack, stack_pointer, type_checking) + else if (op_index == MINUS_OP) then + call minus_operator(exp_number, stack, stack_pointer, type_checking) + else if (op_index == DIV_OP) then + call div_operator(exp_number, stack, stack_pointer, type_checking) + ! TODO FIXME + ! sum (and other reduces) needs to be fixed, + ! because it is using (:) over decomposed dimensions, which is wrong + ! + ! else if (op_index == SUM_OP) then + ! call sum_operator(exp_number, stack, stack_pointer, type_checking) + else + call mpas_log_write( & + 'the impossible happened, tried to apply an unknown operator ' // & + 'in expression #' // trim(expression_names(exp_number)) // & + ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if + +end subroutine eval_operator!}}} + + + +!*********************************************************************** +! routine eval_variable +! +!> \brief Given a variable index number, put it on the stack +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a variable index number, put the result on the top of +!> the stack. This will look up the field names in the variable and look +!> it up from the framework to push the pointer onto the stack. +!----------------------------------------------------------------------- +subroutine eval_variable (domain, exp_number, & + var_index, stack, stack_pointer, type_checking)!{{{ + ! input variables + integer, intent(in) :: exp_number, var_index + logical, intent(in) :: type_checking + + ! input/output variables + type (domain_type), intent(inout) :: domain + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + + ! output variables + + ! local variables + character (len=StrKIND) :: config + character (len=StrKIND), pointer :: config_result + type (mpas_pool_field_info_type) :: info + + ! start procedure + config = trim(VARIABLE_PREFIX) // trim(variable_names(var_index)) + call mpas_pool_get_config(domain % configs, config, config_result) + + if (type_checking) then + if (trim(config_result) == trim(NONE_TOKEN)) then + call mpas_log_write( & + 'the MPAS field assigned to variable ' // & + trim(variable_names(var_index)) // ' was evaluated, but it is ' // & + 'currently set to "none"', MPAS_LOG_CRIT) + end if + end if + + call mpas_pool_get_field_info & + (domain % blocklist % allFields, config_result, info) + + ! check if it's real + if (type_checking) then + if (info % fieldType /= MPAS_POOL_REAL) then + call mpas_log_write( & + 'the MPAS field "' // trim(config_result) // & + '"assigned to variable ' // & + trim(variable_names(var_index)) // ' in the RPN calculator AM is ' // & + 'not a real field', MPAS_LOG_CRIT) + end if + + ! check if it's 0D-2D + if (info % nDims > 2) then + call mpas_log_write( & + 'the MPAS field "' // trim(config_result) // & + '"assigned to variable ' // & + trim(variable_names(var_index)) // ' in the RPN calculator AM is ' // & + 'not a 0D, 1D, or 2D field', MPAS_LOG_CRIT) + end if + end if + + ! increment the stack and put it on the stack + stack_pointer = stack_pointer + 1 + stack(stack_pointer) % number_of_dims = info % nDims + stack(stack_pointer) % symbol_type = IS_VARIABLE + + ! get the dimension name if it is 1D + if (info % nDims == 0) then + call mpas_pool_get_field(domain % blocklist % allFields, & + config_result, stack(stack_pointer) % d0, 1) + else if (info % nDims == 1) then + call mpas_pool_get_field(domain % blocklist % allFields, & + config_result, stack(stack_pointer) % d1, 1) + else + call mpas_pool_get_field(domain % blocklist % allFields, & + config_result, stack(stack_pointer) % d2, 1) + end if +end subroutine eval_variable!}}} + + + +!*********************************************************************** +! routine create_2d_field_from_1ds +! +!> \brief Generates a new 2D field from 1D fields +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details This will take two 1D fields (second and top) and +!> generate a new 2D field (head) with second's dimension as its +!> first dimension, and top's as it's second dimension. If top is +!> decomposed, head will be decomposed as well. If second has +!> constituent names, head will have constituent names as well. +!> Both fields need to be active, otherwise head will be inactive. +!----------------------------------------------------------------------- +subroutine create_2d_field_from_1ds(second, top_head, head)!{{{ +#include "rpn_calc_inc/field_2d_from_1ds.inc" +end subroutine create_2d_field_from_1ds!}}} + + + +!*********************************************************************** +! routine create_1d_field_from_2d +! +!> \brief Generates a new 1D field from a 2D +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details This will take a 2D field (top_head) and +!> generate a new 1D field (head) with top's dimension as its +!> dimension. If top is decomposed, head will be decomposed as well. +!----------------------------------------------------------------------- +subroutine create_1d_field_from_2d(top_head, head)!{{{ +#include "rpn_calc_inc/field_1d_from_2d.inc" +end subroutine create_1d_field_from_2d!}}} + + + +!*********************************************************************** +! routine create_0d_field_from_1d +! +!> \brief Generates a new 1D field from a 2D +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details This will take a 1D field (top) and +!> generate a new 0D field (dst). +!----------------------------------------------------------------------- +subroutine create_0d_field_from_1d(top, dst)!{{{ +#include "rpn_calc_inc/field_0d_from_1d.inc" +end subroutine create_0d_field_from_1d!}}} + + + +!*********************************************************************** +! routine mul_operator +! +!> \brief Do mul on the stack +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a stack, take two arguments off the stack and +!> multiply them together, pushing the result back to the stack. +!----------------------------------------------------------------------- +subroutine mul_operator ( & + exp_number, stack, stack_pointer, type_checking)!{{{ +#include "rpn_calc_inc/binary_op_dispatch_start.inc" + op_name = '*' +#include "rpn_calc_inc/binary_op_dispatch_0d_0d.inc" + call mul_op_0d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_0d_1d.inc" + call mul_op_0d_1d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_0d_2d.inc" + call mul_op_0d_2d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_0d.inc" + call mul_op_1d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_same.inc" + call mul_op_1d_1d_same(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_diff.inc" + call mul_op_1d_1d_diff(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_first.inc" + call mul_op_1d_2d_first(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_second.inc" + call mul_op_1d_2d_second(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_0d.inc" + call mul_op_2d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_first.inc" + call mul_op_2d_1d_first(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_second.inc" + call mul_op_2d_1d_second(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_2d.inc" + call mul_op_2d_2d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_end.inc" +end subroutine mul_operator!}}} + +subroutine mul_op_0d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_0d_1.inc" + second * top +#include "rpn_calc_inc/binary_op_0d_0d_2.inc" +end subroutine mul_op_0d_0d!}}} + +subroutine mul_op_0d_1d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_1d_1.inc" + second * top +#include "rpn_calc_inc/binary_op_0d_1d_2.inc" +end subroutine mul_op_0d_1d!}}} + +subroutine mul_op_0d_2d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_2d_1.inc" + second * top +#include "rpn_calc_inc/binary_op_0d_2d_2.inc" +end subroutine mul_op_0d_2d!}}} + +subroutine mul_op_1d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_0d_1.inc" + second * top +#include "rpn_calc_inc/binary_op_1d_0d_2.inc" +end subroutine mul_op_1d_0d!}}} + +subroutine mul_op_1d_1d_same (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_same_1.inc" + second * top +#include "rpn_calc_inc/binary_op_1d_1d_same_2.inc" +end subroutine mul_op_1d_1d_same!}}} + +subroutine mul_op_1d_1d_diff (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_diff_1.inc" + second(i) * top(j) +#include "rpn_calc_inc/binary_op_1d_1d_diff_2.inc" +end subroutine mul_op_1d_1d_diff!}}} + +subroutine mul_op_1d_2d_first (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_2d_first_1.inc" + second * top(:,j) +#include "rpn_calc_inc/binary_op_1d_2d_first_2.inc" +end subroutine mul_op_1d_2d_first!}}} + +subroutine mul_op_1d_2d_second (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_2d_second_1.inc" + second * top(i,:) +#include "rpn_calc_inc/binary_op_1d_2d_second_2.inc" +end subroutine mul_op_1d_2d_second!}}} + +subroutine mul_op_2d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_0d_1.inc" + second * top +#include "rpn_calc_inc/binary_op_2d_0d_2.inc" +end subroutine mul_op_2d_0d!}}} + +subroutine mul_op_2d_1d_first (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_first_1.inc" + second(:,j) * top +#include "rpn_calc_inc/binary_op_2d_1d_first_2.inc" +end subroutine mul_op_2d_1d_first!}}} + +subroutine mul_op_2d_1d_second (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_second_1.inc" + second(i,:) * top +#include "rpn_calc_inc/binary_op_2d_1d_second_2.inc" +end subroutine mul_op_2d_1d_second!}}} + +subroutine mul_op_2d_2d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_2d_1.inc" + second * top +#include "rpn_calc_inc/binary_op_2d_2d_2.inc" +end subroutine mul_op_2d_2d!}}} + + + +!*********************************************************************** +! routine plus_operator +! +!> \brief Do plus on the stack +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a stack, take two arguments off the stack and +!> add them together, pushing the result back to the stack. +!----------------------------------------------------------------------- +subroutine plus_operator ( & + exp_number, stack, stack_pointer, type_checking)!{{{ +#include "rpn_calc_inc/binary_op_dispatch_start.inc" + op_name = '+' +#include "rpn_calc_inc/binary_op_dispatch_0d_0d.inc" + call plus_op_0d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_0d_1d.inc" + call plus_op_0d_1d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_0d_2d.inc" + call plus_op_0d_2d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_0d.inc" + call plus_op_1d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_same.inc" + call plus_op_1d_1d_same(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_diff.inc" + call plus_op_1d_1d_diff(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_first.inc" + call plus_op_1d_2d_first(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_second.inc" + call plus_op_1d_2d_second(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_0d.inc" + call plus_op_2d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_first.inc" + call plus_op_2d_1d_first(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_second.inc" + call plus_op_2d_1d_second(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_2d.inc" + call plus_op_2d_2d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_end.inc" +end subroutine plus_operator!}}} + +subroutine plus_op_0d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_0d_1.inc" + second + top +#include "rpn_calc_inc/binary_op_0d_0d_2.inc" +end subroutine plus_op_0d_0d!}}} + +subroutine plus_op_0d_1d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_1d_1.inc" + second + top +#include "rpn_calc_inc/binary_op_0d_1d_2.inc" +end subroutine plus_op_0d_1d!}}} + +subroutine plus_op_0d_2d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_2d_1.inc" + second + top +#include "rpn_calc_inc/binary_op_0d_2d_2.inc" +end subroutine plus_op_0d_2d!}}} + +subroutine plus_op_1d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_0d_1.inc" + second + top +#include "rpn_calc_inc/binary_op_1d_0d_2.inc" +end subroutine plus_op_1d_0d!}}} + +subroutine plus_op_1d_1d_same (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_same_1.inc" + second + top +#include "rpn_calc_inc/binary_op_1d_1d_same_2.inc" +end subroutine plus_op_1d_1d_same!}}} + +subroutine plus_op_1d_1d_diff (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_diff_1.inc" + second(i) + top(j) +#include "rpn_calc_inc/binary_op_1d_1d_diff_2.inc" +end subroutine plus_op_1d_1d_diff!}}} + +subroutine plus_op_1d_2d_first (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_2d_first_1.inc" + second + top(:,j) +#include "rpn_calc_inc/binary_op_1d_2d_first_2.inc" +end subroutine plus_op_1d_2d_first!}}} + +subroutine plus_op_1d_2d_second (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_2d_second_1.inc" + second + top(i,:) +#include "rpn_calc_inc/binary_op_1d_2d_second_2.inc" +end subroutine plus_op_1d_2d_second!}}} + +subroutine plus_op_2d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_0d_1.inc" + second + top +#include "rpn_calc_inc/binary_op_2d_0d_2.inc" +end subroutine plus_op_2d_0d!}}} + +subroutine plus_op_2d_1d_first (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_first_1.inc" + second(:,j) + top +#include "rpn_calc_inc/binary_op_2d_1d_first_2.inc" +end subroutine plus_op_2d_1d_first!}}} + +subroutine plus_op_2d_1d_second (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_second_1.inc" + second(i,:) + top +#include "rpn_calc_inc/binary_op_2d_1d_second_2.inc" +end subroutine plus_op_2d_1d_second!}}} + +subroutine plus_op_2d_2d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_2d_1.inc" + second + top +#include "rpn_calc_inc/binary_op_2d_2d_2.inc" +end subroutine plus_op_2d_2d!}}} + + + +!*********************************************************************** +! routine minus_operator +! +!> \brief Do minus on the stack +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a stack, take two arguments off the stack and +!> subtract top from second, pushing the result back to the stack. +!----------------------------------------------------------------------- +subroutine minus_operator ( & + exp_number, stack, stack_pointer, type_checking)!{{{ +#include "rpn_calc_inc/binary_op_dispatch_start.inc" + op_name = '-' +#include "rpn_calc_inc/binary_op_dispatch_0d_0d.inc" + call minus_op_0d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_0d_1d.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to subtract a 1d from a 0d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_0d_2d.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to subtract a 2d from a 0d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_1d_0d.inc" + call minus_op_1d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_same.inc" + call minus_op_1d_1d_same(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_diff.inc" + call minus_op_1d_1d_diff(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_first.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to subtract a 2d from a 1d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_second.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to subtract a 2d from a 1d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_2d_0d.inc" + call minus_op_2d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_first.inc" + call minus_op_2d_1d_first(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_second.inc" + call minus_op_2d_1d_second(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_2d.inc" + call minus_op_2d_2d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_end.inc" +end subroutine minus_operator!}}} + +subroutine minus_op_0d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_0d_1.inc" + second - top +#include "rpn_calc_inc/binary_op_0d_0d_2.inc" +end subroutine minus_op_0d_0d!}}} + +subroutine minus_op_1d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_0d_1.inc" + second - top +#include "rpn_calc_inc/binary_op_1d_0d_2.inc" +end subroutine minus_op_1d_0d!}}} + +subroutine minus_op_1d_1d_same (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_same_1.inc" + second - top +#include "rpn_calc_inc/binary_op_1d_1d_same_2.inc" +end subroutine minus_op_1d_1d_same!}}} + +subroutine minus_op_1d_1d_diff (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_diff_1.inc" + second(i) - top(j) +#include "rpn_calc_inc/binary_op_1d_1d_diff_2.inc" +end subroutine minus_op_1d_1d_diff!}}} + +subroutine minus_op_2d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_0d_1.inc" + second - top +#include "rpn_calc_inc/binary_op_2d_0d_2.inc" +end subroutine minus_op_2d_0d!}}} + +subroutine minus_op_2d_1d_first (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_first_1.inc" + second(:,j) - top +#include "rpn_calc_inc/binary_op_2d_1d_first_2.inc" +end subroutine minus_op_2d_1d_first!}}} + +subroutine minus_op_2d_1d_second (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_second_1.inc" + second(i,:) - top +#include "rpn_calc_inc/binary_op_2d_1d_second_2.inc" +end subroutine minus_op_2d_1d_second!}}} + +subroutine minus_op_2d_2d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_2d_1.inc" + second - top +#include "rpn_calc_inc/binary_op_2d_2d_2.inc" +end subroutine minus_op_2d_2d!}}} + + + +!*********************************************************************** +! routine div_operator +! +!> \brief Do div on the stack +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a stack, take two arguments off the stack and +!> divide the second by the top, pushing the result back to the stack. +!----------------------------------------------------------------------- +subroutine div_operator ( & + exp_number, stack, stack_pointer, type_checking)!{{{ +#include "rpn_calc_inc/binary_op_dispatch_start.inc" + op_name = '/' +#include "rpn_calc_inc/binary_op_dispatch_0d_0d.inc" + call div_op_0d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_0d_1d.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to divide a 0d by a 1d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_0d_2d.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to divide a 0d by a 2d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_1d_0d.inc" + call div_op_1d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_same.inc" + call div_op_1d_1d_same(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_1d_diff.inc" + call div_op_1d_1d_diff(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_first.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to divide a 1d by a 2d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_1d_2d_second.inc" + if (type_checking) then + call mpas_log_write( & + 'Unable to divide a 1d by a 2d in expression #' // & + trim(expression_names(exp_number)) // ' in the RPN calculator AM', MPAS_LOG_CRIT) + end if +#include "rpn_calc_inc/binary_op_dispatch_2d_0d.inc" + call div_op_2d_0d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_first.inc" + call div_op_2d_1d_first(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_1d_second.inc" + call div_op_2d_1d_second(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_2d_2d.inc" + call div_op_2d_2d(stack, stack_pointer) +#include "rpn_calc_inc/binary_op_dispatch_end.inc" +end subroutine div_operator!}}} + +function safe_divide_0d_0d(second, top) + implicit none + real (kind=RKIND), intent(in) :: second + real (kind=RKIND), intent(in) :: top + + real (kind=RKIND) :: safe_divide_0d_0d + + if (abs(top) > 0.0_RKIND) then + safe_divide_0d_0d = second / top + else + safe_divide_0d_0d = huge(second) + end if +end function safe_divide_0d_0d + +function safe_divide_1d_0d(second, top) + implicit none + real (kind=RKIND), dimension(:), intent(in) :: second + real (kind=RKIND), intent(in) :: top + + real (kind=RKIND), dimension(size(second)) :: safe_divide_1d_0d + + if (abs(top) > 0.0_RKIND) then + safe_divide_1d_0d = second / top + else + safe_divide_1d_0d = huge(second) + end if +end function safe_divide_1d_0d + +function safe_divide_2d_0d(second, top) + implicit none + real (kind=RKIND), dimension(:, :), intent(in) :: second + real (kind=RKIND), intent(in) :: top + + real (kind=RKIND), dimension(size(second, 1), size(second, 2)) :: & + safe_divide_2d_0d + + if (abs(top) > 0.0_RKIND) then + safe_divide_2d_0d = second / top + else + safe_divide_2d_0d = huge(second) + end if +end function safe_divide_2d_0d + +function safe_divide_1d_1d(second, top) + implicit none + real (kind=RKIND), dimension(:), intent(in) :: second + real (kind=RKIND), dimension(:), intent(in) :: top + + real (kind=RKIND), dimension(size(second)) :: safe_divide_1d_1d + + where (abs(top) > 0.0_RKIND) + safe_divide_1d_1d = second / top + elsewhere + safe_divide_1d_1d = huge(second) + end where +end function safe_divide_1d_1d + +function safe_divide_2d_2d(second, top) + implicit none + real (kind=RKIND), dimension(:, :), intent(in) :: second + real (kind=RKIND), dimension(:, :), intent(in) :: top + + real (kind=RKIND), dimension(size(second, 1), size(second, 2)) :: & + safe_divide_2d_2d + + where (abs(top) > 0.0_RKIND) + safe_divide_2d_2d = second / top + elsewhere + safe_divide_2d_2d = huge(second) + end where +end function safe_divide_2d_2d + +subroutine div_op_0d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_0d_0d_1.inc" + safe_divide_0d_0d(second, top) +#include "rpn_calc_inc/binary_op_0d_0d_2.inc" +end subroutine div_op_0d_0d!}}} + +subroutine div_op_1d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_0d_1.inc" + safe_divide_1d_0d(second, top) +#include "rpn_calc_inc/binary_op_1d_0d_2.inc" +end subroutine div_op_1d_0d!}}} + +subroutine div_op_1d_1d_same (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_same_1.inc" + safe_divide_1d_1d(second, top) +#include "rpn_calc_inc/binary_op_1d_1d_same_2.inc" +end subroutine div_op_1d_1d_same!}}} + +subroutine div_op_1d_1d_diff (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_1d_1d_diff_1.inc" + safe_divide_0d_0d(second(i), top(j)) +#include "rpn_calc_inc/binary_op_1d_1d_diff_2.inc" +end subroutine div_op_1d_1d_diff!}}} + +subroutine div_op_2d_0d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_0d_1.inc" + safe_divide_2d_0d(second, top) +#include "rpn_calc_inc/binary_op_2d_0d_2.inc" +end subroutine div_op_2d_0d!}}} + +subroutine div_op_2d_1d_first (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_first_1.inc" + safe_divide_1d_1d(second(:,j), top) +#include "rpn_calc_inc/binary_op_2d_1d_first_2.inc" +end subroutine div_op_2d_1d_first!}}} + +subroutine div_op_2d_1d_second (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_1d_second_1.inc" + safe_divide_1d_1d(second(i,:), top) +#include "rpn_calc_inc/binary_op_2d_1d_second_2.inc" +end subroutine div_op_2d_1d_second!}}} + +subroutine div_op_2d_2d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/binary_op_2d_2d_1.inc" + safe_divide_2d_2d(second, top) +#include "rpn_calc_inc/binary_op_2d_2d_2.inc" +end subroutine div_op_2d_2d!}}} + +!*********************************************************************** +! routine sum_operator +! +!> \brief Do sum on the stack +!> \author Jon Woodring +!> \date March 21, 2016 +!> \details Given a stack, take sum argument off the stack and +!> sum along the first dimension, pushing the result back to the stack. +!----------------------------------------------------------------------- +subroutine sum_operator ( & + exp_number, stack, stack_pointer, type_checking)!{{{ +#include "rpn_calc_inc/reduce_op_dispatch_start.inc" + op_name = 'sum' +#include "rpn_calc_inc/reduce_op_dispatch_1d.inc" + call sum_op_1d(stack, stack_pointer) +#include "rpn_calc_inc/reduce_op_dispatch_2d.inc" + call sum_op_2d(stack, stack_pointer) +#include "rpn_calc_inc/reduce_op_dispatch_end.inc" +end subroutine sum_operator + +subroutine sum_op_1d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/reduce_op_1d_1.inc" + 0 +#include "rpn_calc_inc/reduce_op_1d_2.inc" + reduced + sum(top) +#include "rpn_calc_inc/reduce_op_1d_3.inc" +end subroutine sum_op_1d!}}} + +subroutine sum_op_2d (stack, stack_pointer)!{{{ +#include "rpn_calc_inc/reduce_op_2d_1.inc" + 0 +#include "rpn_calc_inc/reduce_op_2d_2.inc" + reduced(j) + sum(top(:,j)) +#include "rpn_calc_inc/reduce_op_2d_3.inc" +end subroutine sum_op_2d!}}} + +end module ocn_rpn_calculator +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_surface_area_weighted_averages.F b/src/core_ocean/analysis_members/mpas_ocn_surface_area_weighted_averages.F index c173beb944..6993bebcf7 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_surface_area_weighted_averages.F +++ b/src/core_ocean/analysis_members/mpas_ocn_surface_area_weighted_averages.F @@ -157,10 +157,12 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ type (block_type), pointer :: block type (mpas_pool_type), pointer :: surfaceAreaWeightedAveragesAMPool type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: scratchPool type (mpas_pool_type), pointer :: diagnosticsPool type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: tracersSurfaceFluxPool real (kind=RKIND), dimension(:,:), pointer :: minValueWithinOceanRegion real (kind=RKIND), dimension(:,:), pointer :: maxValueWithinOceanRegion @@ -185,17 +187,17 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ real (kind=RKIND), dimension(:), pointer :: seaIceEnergy real (kind=RKIND), dimension(:), pointer :: surfaceThicknessFlux - real (kind=RKIND), dimension(:,:), pointer :: surfaceTracerFlux + real (kind=RKIND), dimension(:,:), pointer :: activeTracersSurfaceFlux real (kind=RKIND), dimension(:), pointer :: penetrativeTemperatureFlux real (kind=RKIND), dimension(:), pointer :: seaIceSalinityFlux - real (kind=RKIND), dimension(:), pointer :: surfaceWindStressMagnitude + real (kind=RKIND), dimension(:), pointer :: surfaceStressMagnitude real (kind=RKIND), dimension(:), pointer :: windStressZonal real (kind=RKIND), dimension(:), pointer :: windStressMeridional - real (kind=RKIND), dimension(:), pointer :: seaSurfacePressure + real (kind=RKIND), dimension(:), pointer :: atmosphericPressure real (kind=RKIND), dimension(:), pointer :: ssh real (kind=RKIND), dimension(:), pointer :: boundaryLayerDepth - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers ! pointers to data in mesh pool integer, pointer :: nCells, nCellsSolve, nSfcAreaWeightedAvgFields, nOceanRegions @@ -213,8 +215,7 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ integer :: iCell, iRegion, iTracer, err_tmp ! package flag - logical, pointer :: surfaceAreaWeightedAveragesAMPKGActive - logical, pointer :: bulkForcingPkgActive + logical, pointer :: activeTracersBulkRestoringPKG logical, pointer :: frazilIcePkgActive ! buffers data for message passaging @@ -227,7 +228,7 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ err = 0 ! get status of other packages - call mpas_pool_get_package(ocnPackages, 'bulkForcingActive', bulkForcingPkgActive) + call mpas_pool_get_package(ocnPackages, 'activeTracersBulkRestoringPKGActive', activeTracersBulkRestoringPKG) call mpas_pool_get_package(ocnPackages, 'frazilIceActive', frazilIcePkgActive) ! set highest level pointer @@ -248,12 +249,12 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ allocate(workBufferSumReduced(kBufferLength)) allocate(workBufferMinReduced(kBufferLength)) allocate(workBufferMaxReduced(kBufferLength)) - workBufferSum=0.0 - workBufferMin=0.0 - workBufferMax=0.0 - workBufferSumReduced=0.0 - workBufferMinReduced=0.0 - workBufferMaxReduced=0.0 + workBufferSum=0.0_RKIND + workBufferMin=0.0_RKIND + workBufferMax=0.0_RKIND + workBufferSumReduced=0.0_RKIND + workBufferMinReduced=0.0_RKIND + workBufferMaxReduced=0.0_RKIND ! loop over all ocean regions do iRegion=1,nOceanRegions @@ -288,17 +289,19 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ do while (associated(block)) ! get pointers to pools call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceFlux', tracersSurfaceFluxPool) ! get pointers to mesh call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) call mpas_pool_get_dimension(block % dimensions, 'nSfcAreaWeightedAvgFields', nSfcAreaWeightedAvgFields) call mpas_pool_get_dimension(block % dimensions, 'nOceanRegions', nOceanRegions) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexSalinity) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'lonCell', lonCell) call mpas_pool_get_array(meshPool, 'latCell', latCell) @@ -306,74 +309,76 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ ! test to make sure the arrays are big enough nDefinedDataFields = size(avgValueWithinOceanRegion,dim=1) if (nDefinedDataFields > nSfcAreaWeightedAvgFields) then - call mpas_log_write("Abort: nDefinedDataFields > nLayerVolWeightedAvgFields" // & + call mpas_log_write("nDefinedDataFields > nLayerVolWeightedAvgFields" // & " increase size of ocn_layer_volume_weighted_averages scratch space", MPAS_LOG_CRIT ) endif ! get pointers to data that will be analyzed ! listed in the order in which the fields appear in {avg,min,max}SurfaceStatistics - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'latentHeatFlux', latentHeatFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'sensibleHeatFlux', sensibleHeatFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxUp', longWaveHeatFluxUp) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxDown', longWaveHeatFluxDown) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'seaIceHeatFlux', seaIceHeatFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'shortWaveHeatFlux', shortWaveHeatFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'seaIceFreshWaterFlux', seaIceFreshWaterFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'riverRunoffFlux', riverRunoffFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'iceRunoffFlux', iceRunoffFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'rainFlux', rainFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'snowFlux', snowFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'latentHeatFlux', latentHeatFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'sensibleHeatFlux', sensibleHeatFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxUp', longWaveHeatFluxUp) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxDown', longWaveHeatFluxDown) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'seaIceHeatFlux', seaIceHeatFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'shortWaveHeatFlux', shortWaveHeatFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'seaIceFreshWaterFlux', seaIceFreshWaterFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'riverRunoffFlux', riverRunoffFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'iceRunoffFlux', iceRunoffFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'rainFlux', rainFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'snowFlux', snowFlux) if (frazilIcePkgActive) call mpas_pool_get_array(forcingPool, 'seaIceEnergy', seaIceEnergy) call mpas_pool_get_array(forcingPool, 'surfaceThicknessFlux', surfaceThicknessFlux) - call mpas_pool_get_array(forcingPool, 'surfaceTracerFlux', surfaceTracerFlux) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'seaIceSalinityFlux', seaIceSalinityFlux) - call mpas_pool_get_array(forcingPool, 'surfaceWindStressMagnitude', surfaceWindStressMagnitude) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) - if (bulkForcingPkgActive) call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional) - call mpas_pool_get_array(forcingPool, 'seaSurfacePressure', seaSurfacePressure) + call mpas_pool_get_array(tracersSurfaceFluxPool, 'activeTracersSurfaceFlux', activeTracersSurfaceFlux) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'seaIceSalinityFlux', seaIceSalinityFlux) + call mpas_pool_get_array(forcingPool, 'surfaceStressMagnitude', surfaceStressMagnitude) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) + if (activeTracersBulkRestoringPKG) call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional) + call mpas_pool_get_array(forcingPool, 'atmosphericPressure', atmosphericPressure) call mpas_pool_get_array(statePool, 'ssh', ssh, 1) - call mpas_pool_get_array(statePool, 'tracers', tracers, 1) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) call mpas_pool_get_array(diagnosticsPool, 'boundaryLayerDepth',boundaryLayerDepth) ! compute mask call compute_mask(nCells, nCellsSolve, iRegion, lonCell, latCell, workMask) ! copy data into work array - workArray( :,:) = 0.0 + workArray( :,:) = 0.0_RKIND workArray( 1,:) = workMask(:) workArray( 2,:) = areaCell(:) - if (bulkForcingPkgActive) workArray( 3,:) = latentHeatFlux(:) - if (bulkForcingPkgActive) workArray( 4,:) = sensibleHeatFlux(:) - if (bulkForcingPkgActive) workArray( 5,:) = longWaveHeatFluxUp(:) - if (bulkForcingPkgActive) workArray( 6,:) = longWaveHeatFluxDown(:) - if (bulkForcingPkgActive) workArray( 7,:) = seaIceHeatFlux(:) - if (bulkForcingPkgActive) workArray( 8,:) = shortWaveHeatFlux(:) - if (bulkForcingPkgActive) workArray( 9,:) = evaporationFlux(:) - if (bulkForcingPkgActive) workArray(10,:) = seaIceFreshWaterFlux(:) - if (bulkForcingPkgActive) workArray(11,:) = riverRunoffFlux(:) - if (bulkForcingPkgActive) workArray(12,:) = iceRunoffFlux(:) - if (bulkForcingPkgActive) workArray(13,:) = rainFlux(:) - if (bulkForcingPkgActive) workArray(14,:) = snowFlux(:) + if (activeTracersBulkRestoringPKG) workArray( 3,:) = latentHeatFlux(:) + if (activeTracersBulkRestoringPKG) workArray( 4,:) = sensibleHeatFlux(:) + if (activeTracersBulkRestoringPKG) workArray( 5,:) = longWaveHeatFluxUp(:) + if (activeTracersBulkRestoringPKG) workArray( 6,:) = longWaveHeatFluxDown(:) + if (activeTracersBulkRestoringPKG) workArray( 7,:) = seaIceHeatFlux(:) + if (activeTracersBulkRestoringPKG) workArray( 8,:) = shortWaveHeatFlux(:) + if (activeTracersBulkRestoringPKG) workArray( 9,:) = evaporationFlux(:) + if (activeTracersBulkRestoringPKG) workArray(10,:) = seaIceFreshWaterFlux(:) + if (activeTracersBulkRestoringPKG) workArray(11,:) = riverRunoffFlux(:) + if (activeTracersBulkRestoringPKG) workArray(12,:) = iceRunoffFlux(:) + if (activeTracersBulkRestoringPKG) workArray(13,:) = rainFlux(:) + if (activeTracersBulkRestoringPKG) workArray(14,:) = snowFlux(:) if (frazilIcePkgActive) workArray(15,:) = seaIceEnergy(:) workArray(16,:) = surfaceThicknessFlux(:) - workArray(17,:) = surfaceTracerFlux(indexTemperature,:) - workArray(18,:) = surfaceTracerFlux(indexSalinity,:) - if (bulkForcingPkgActive) workArray(19,:) = seaIceSalinityFlux(:) - workArray(20,:) = surfaceWindStressMagnitude(:) - if (bulkForcingPkgActive) workArray(21,:) = windStressZonal(:) - if (bulkForcingPkgActive) workArray(22,:) = windStressMeridional(:) - workArray(23,:) = seaSurfacePressure(:) + workArray(17,:) = activeTracersSurfaceFlux(indexTemperature,:) + workArray(18,:) = activeTracersSurfaceFlux(indexSalinity,:) + if (activeTracersBulkRestoringPKG) workArray(19,:) = seaIceSalinityFlux(:) + workArray(20,:) = surfaceStressMagnitude(:) + if (activeTracersBulkRestoringPKG) workArray(21,:) = windStressZonal(:) + if (activeTracersBulkRestoringPKG) workArray(22,:) = windStressMeridional(:) + workArray(23,:) = atmosphericPressure(:) workArray(24,:) = ssh(:) - workArray(25,:) = tracers(indexTemperature,1,:) - workArray(26,:) = tracers(indexSalinity,1,:) + workArray(25,:) = activeTracers(indexTemperature,1,:) + workArray(26,:) = activeTracers(indexSalinity,1,:) workArray(27,:) = boundaryLayerDepth(:) ! build net heat, salinity and fresh water budget - ! net heat into ocean = latentHeatFlux+sensibleHeatFlux+longWaveHeatFluxUp+longWaveHeatFluxDown+shortWaveHeatFlux+seaIceHeatFlux+(?seaIceEnergy?) + ! net heat into ocean = latentHeatFlux + sensibleHeatFlux + longWaveHeatFluxUp + longWaveHeatFluxDown + ! + shortWaveHeatFlux + seaIceHeatFlux + (?seaIceEnergy?) ! net salinity into ocean = seaIceSalinityFlux - ! net freshwater into ocean = evaporationFlux+seaIceFreshWaterFlux+riverRunoffFlux+iceRunoffFlux+rainFlux+snowFlux+(?seaIceEnergy?) - if (bulkForcingPkgActive) then + ! net freshwater into ocean = evaporationFlux + seaIceFreshWaterFlux + riverRunoffFlux + iceRunoffFlux + ! + rainFlux + snowFlux + (?seaIceEnergy?) + if (activeTracersBulkRestoringPKG) then workArray(28,:) = latentHeatFlux(:) & + sensibleHeatFlux(:) & + longWaveHeatFluxUp(:) & @@ -426,10 +431,12 @@ subroutine ocn_compute_surface_area_weighted_averages(domain, timeLevel, err)!{{ do iRegion=1,nOceanRegions ! normalize all field by total area do iDataField=3,nDefinedDataFields - avgValueWithinOceanRegion(iDataField,iRegion) = avgValueWithinOceanRegion(iDataField,iRegion) / max(avgValueWithinOceanRegion(2,iRegion),1.0e-8_RKIND) + avgValueWithinOceanRegion(iDataField,iRegion) = avgValueWithinOceanRegion(iDataField,iRegion) & + / max(avgValueWithinOceanRegion(2,iRegion),1.0e-8_RKIND) enddo ! normalize total area by number of cells in region - avgValueWithinOceanRegion(2,iRegion) = avgValueWithinOceanRegion(2,iRegion) / max(avgValueWithinOceanRegion(1,iRegion),1.0e-8_RKIND) + avgValueWithinOceanRegion(2,iRegion) = avgValueWithinOceanRegion(2,iRegion) & + / max(avgValueWithinOceanRegion(1,iRegion),1.0e-8_RKIND) enddo ! deallocate scratch fields @@ -458,7 +465,7 @@ subroutine compute_mask(nCells, nCellsSolve, iRegion, lonCell, latCell, workMask integer :: iCell real(kind=RKIND) :: dtr - dtr = 4.0*atan(1.0) / 180.0_RKIND + dtr = 4.0_RKIND*atan(1.0_RKIND) / 180.0_RKIND workMask(:) = 0.0_RKIND do iCell=1,nCellsSolve workMask(iCell) = 1.0_RKIND @@ -467,42 +474,42 @@ subroutine compute_mask(nCells, nCellsSolve, iRegion, lonCell, latCell, workMask if (iRegion.eq.1) then ! Arctic do iCell=1,nCellsSolve - if(latCell(iCell).lt. 60.0*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).lt. 60.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo elseif (iRegion.eq.2) then ! Equatorial do iCell=1,nCellsSolve - if(latCell(iCell).gt. 15.0*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt.-15.0*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).gt. 15.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).lt.-15.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo elseif (iRegion.eq.3) then ! Southern Ocean do iCell=1,nCellsSolve - if(latCell(iCell).gt.-50.0*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).gt.-50.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo elseif (iRegion.eq.4) then ! Nino 3 do iCell=1,nCellsSolve - if(latCell(iCell).gt. 5.0*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt. -5.0*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).lt.210.0*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).gt.270.0*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(lonCell(iCell).lt.210.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(lonCell(iCell).gt.270.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo elseif (iRegion.eq.5) then ! Nino 4 do iCell=1,nCellsSolve - if(latCell(iCell).gt. 5.0*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt. -5.0*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).lt.160.0*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).gt.210.0*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(lonCell(iCell).lt.160.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(lonCell(iCell).gt.210.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo elseif (iRegion.eq.6) then ! Nino 3.4 do iCell=1,nCellsSolve - if(latCell(iCell).gt. 5.0*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt. -5.0*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).lt.190.0*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).gt.240.0*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(lonCell(iCell).lt.190.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if(lonCell(iCell).gt.240.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo else ! global (do nothing!) @@ -521,7 +528,7 @@ subroutine compute_statistics(nDefinedDataFields, nCellsSolve, workArray, workMa real(kind=RKIND), dimension(:), intent(out) :: workMin, workMax, workSum integer :: iCell, iData - workSum = 0.0 + workSum = 0.0_RKIND do iCell=1,nCellsSolve workSum(1) = workSum(1) + workMask(iCell) workSum(2) = workSum(2) + workArray(2,iCell)*workMask(iCell) diff --git a/src/core_ocean/analysis_members/mpas_ocn_test_compute_interval.F b/src/core_ocean/analysis_members/mpas_ocn_test_compute_interval.F index 331b61a1e8..274229fb8c 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_test_compute_interval.F +++ b/src/core_ocean/analysis_members/mpas_ocn_test_compute_interval.F @@ -14,7 +14,7 @@ !> \date May 2015 !> \details !> MPAS ocean analysis core member: test_compute_interval -!> +!> ! !----------------------------------------------------------------------- @@ -67,8 +67,8 @@ module ocn_test_compute_interval !> \brief Initialize MPAS-Ocean analysis member !> \author Mark Petersen !> \date May 2015 -!> \details -!> This routine conducts all initializations required for the +!> \details +!> This routine conducts all initializations required for the !> MPAS-Ocean analysis member. ! !----------------------------------------------------------------------- @@ -128,7 +128,7 @@ end subroutine ocn_init_test_compute_interval!}}} !> \brief Compute MPAS-Ocean analysis member !> \author Mark Petersen !> \date May 2015 -!> \details +!> \details !> This routine conducts all computation required for this !> MPAS-Ocean analysis member. ! @@ -197,7 +197,7 @@ subroutine ocn_compute_test_compute_interval(domain, timeLevel, err)!{{{ block => block % next end do - !write(stderrUnit,'(3A,F10.1)') 'in ocn_compute_test_compute_interval, time = ', trim(xtime), & + !print *, 'in ocn_compute_test_compute_interval, time = ', trim(xtime), & ! ', testComputeIntervalCounter = ',testComputeIntervalCounter end subroutine ocn_compute_test_compute_interval!}}} @@ -209,7 +209,7 @@ end subroutine ocn_compute_test_compute_interval!}}} !> \brief Save restart for MPAS-Ocean analysis member !> \author Mark Petersen !> \date May 2015 -!> \details +!> \details !> This routine conducts computation required to save a restart state !> for the MPAS-Ocean analysis member. ! @@ -256,7 +256,7 @@ end subroutine ocn_restart_test_compute_interval!}}} !> \brief Finalize MPAS-Ocean analysis member !> \author Mark Petersen !> \date May 2015 -!> \details +!> \details !> This routine conducts all finalizations required for this !> MPAS-Ocean analysis member. ! diff --git a/src/core_ocean/analysis_members/mpas_ocn_time_filters.F b/src/core_ocean/analysis_members/mpas_ocn_time_filters.F new file mode 100644 index 0000000000..73ec02b399 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_time_filters.F @@ -0,0 +1,477 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_time_filters +! +!> \brief MPAS ocean analysis mode member: time_filters +!> \author Phillip J. Wolfram +!> \date 07/17/2015 +!> \details +!> Performs time high and low pass filtering. +!> +!----------------------------------------------------------------------- + +!#define TIME_FILTERS_DEBUG + +module ocn_time_filters + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + use mpas_vector_reconstruction + + use ocn_constants + use ocn_diagnostics_routines +#ifdef TIME_FILTERS_DEBUG + use mpas_constants +#endif + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_time_filters, & + ocn_compute_time_filters, & + ocn_restart_time_filters, & + ocn_finalize_time_filters + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- +#ifdef TIME_FILTERS_DEBUG + integer :: iEdgeOutput = 0, iBlockOutput = 0, iklevel = 1 + real (kind=RKIND) :: lonEdgePoint = 10.0_RKIND*pii/180.0_RKIND, latEdgePoint = 30.0_RKIND*pii/180.0_RKIND +#endif + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_time_filters +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 07/17/2015 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_time_filters(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (block_type), pointer :: block + logical, pointer :: initializeFilters + type (mpas_pool_type), pointer :: timeFiltersAMPool, statePool + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, normalVelocityLowPass, normalVelocityHighPass + +#ifdef TIME_FILTERS_DEBUG + real (kind=RKIND), dimension(:), pointer :: lonEdge, latEdge + real (kind=RKIND) :: dist, distmax = 1e9 + integer :: i, iBlock + integer, pointer :: nEdgesSolve +#endif + + err = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_AM_timeFilters_initialize_filters', initializeFilters) + if (initializeFilters) then +#ifdef TIME_FILTERS_DEBUG + call mpas_log_write( 'initializing time filters') +#endif + + ! loop over all blocks and make assignments + block => domain % blocklist + do while (associated(block)) + + ! get high and low pass velocity components + call mpas_pool_get_subpool(block % structs, 'timeFiltersAM', timeFiltersAMPool) + call mpas_pool_get_array(timeFiltersAMPool, 'normalVelocityLowPass', normalVelocityLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'normalVelocityHighPass', normalVelocityHighPass) + + ! get normal velocity + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=1) + + ! initialize normal velocities + normalVelocityLowPass(:,:) = normalVelocity(:,:) + normalVelocityHighPass(:,:) = normalVelocity(:,:) + + block => block % next + end do + + end if + +#ifdef TIME_FILTERS_DEBUG + ! get index for edge nearest to a location + block => domain % blocklist + iBlock = 0 + do while (associated(block)) + iBlock = iBlock + 1 + call mpas_pool_get_subpool(block % structs, 'mesh', statePool) + call mpas_pool_get_array(statePool, 'latEdge', latEdge) + call mpas_pool_get_array(statePool, 'lonEdge', lonEdge) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) + + do i=1,nEdgesSolve + dist = sqrt((latEdge(i) - latEdgePoint)**2 + (lonEdge(i) - lonEdgePoint)**2) + if (dist < distmax) then + distmax = dist + iEdgeOutput = i + iBlockOutput = iBlock + end if + end do + + block => block % next + end do + + block => domain % blocklist + ! get the right block number + do i=1,iBlockOutput-1 + block => block % next + end do + call mpas_pool_get_subpool(block % structs, 'mesh', statePool) + call mpas_pool_get_array(statePool, 'latEdge', latEdge) + call mpas_pool_get_array(statePool, 'lonEdge', lonEdge) + print *, 'lon = ', 180.0_RKIND/pii*lonEdge(iEdgeOutput), ' lat = ', 180.0_RKIND/pii*latEdge(iEdgeOutput), & + ' iklevel=',iklevel, ' iEdgeOutput=',iEdgeOutput, ' iBlockOutput = ', iBlockOutput +#endif + +#ifdef TIME_FILTERS_DEBUG + call mpas_log_write( 'finished initializing time filters') +#endif + + end subroutine ocn_init_time_filters!}}} + +!*********************************************************************** +! +! routine ocn_compute_time_filters +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 07/17/2015 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_time_filters(domain, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: timeFiltersAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: timeFiltersAM + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, normalVelocityLowPass, normalVelocityHighPass, & + normalVelocityTest + + type (field2DReal), pointer :: normalVelocityLowPassField, normalVelocityHighPassField + + real (kind=RKIND), dimension(:,:), pointer :: velocityZonalLowPass, velocityMeridionalLowPass, & + velocityXLowPass, velocityYLowPass, velocityZLowPass, & + velocityZonalHighPass, velocityMeridionalHighPass, & + velocityXHighPass, velocityYHighPass, velocityZHighPass + integer, pointer :: nVertLevels, nEdgesSolve + integer :: k, iEdge + integer, dimension(:), pointer :: maxLevelEdgeBot + + type (MPAS_timeInterval_type) :: timeStepESMF + character(len=StrKIND), pointer :: config_dt + logical, pointer :: computeCC + real (kind=RKIND) :: dt, tau +#ifdef TIME_FILTERS_DEBUG + integer :: iBlock +#endif + + err = 0 + + dminfo = domain % dminfo + +#ifdef TIME_FILTERS_DEBUG + call mpas_log_write( 'start computing time filters') +#endif + + ! get dt + call mpas_pool_get_config(domain % configs, 'config_dt', config_dt) + call mpas_set_timeInterval(timeStepESMF, timeString=config_dt, ierr=err) + call mpas_get_timeInterval(timeStepESMF, dt=dt) + ! get tau + call mpas_pool_get_config(domain % configs, 'config_AM_timeFilters_tau', config_dt) + call mpas_set_timeInterval(timeStepESMF, timeString=config_dt, ierr=err) + call mpas_get_timeInterval(timeStepESMF, dt=tau) + +#ifdef TIME_FILTERS_DEBUG + !print *, 'dt = ', dt, ' tau = ', tau +#endif + + block => domain % blocklist +#ifdef TIME_FILTERS_DEBUG + iBlock = 0 +#endif + do while (associated(block)) +#ifdef TIME_FILTERS_DEBUG + iBlock = iBlock + 1 +#endif + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'timeFiltersAM', timeFiltersAMPool) + + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeBot', maxLevelEdgeBot) + + ! get high and low pass velocity components + call mpas_pool_get_array(timeFiltersAMPool, 'normalVelocityLowPass', normalVelocityLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'normalVelocityHighPass', normalVelocityHighPass) + call mpas_pool_get_array(timeFiltersAMPool, 'normalVelocityFilterTest', normalVelocityTest) + ! get normal velocity + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=1) + + ! perform filter computations (in place) + do iEdge = 1,nEdgesSolve + do k = 1, maxLevelEdgeBot(iEdge) + normalVelocityLowPass(k,iEdge) = normalVelocityLowPass(k,iEdge)*(1.0_RKIND - dt/tau) + dt/tau*normalVelocity(k,iEdge) + normalVelocityHighPass(k,iEdge) = normalVelocity(k,iEdge) - normalVelocityLowPass(k,iEdge) + ! normalVelocityTest line can possibly be removed (needed for testing purposes) + normalVelocityTest(k,iEdge) = normalVelocity(k,iEdge) + end do +#ifdef TIME_FILTERS_DEBUG + if (iEdge == iEdgeOutput .and. iBlock == iBlockOutput) then + print *, 'vl=', normalVelocityLowPass(iklevel, iEdge), ' v=', normalVelocity(iklevel, iEdge) + end if +#endif + end do + ! exchange halo information in order to ensure that particles on halo are advected properly + call mpas_pool_get_field(timeFiltersAMPool, 'normalVelocityLowPass', normalVelocityLowPassField) + call mpas_pool_get_field(timeFiltersAMPool, 'normalVelocityHighPass', normalVelocityHighPassField) + call mpas_dmpar_exch_halo_field(normalVelocityLowPassField) + call mpas_dmpar_exch_halo_field(normalVelocityHighPassField) + + block => block % next + end do + + ! do IO communications if this is an output time step + call mpas_pool_get_config(domain % configs, 'config_AM_timeFilters_compute_cell_centered_values', computeCC) + if (mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID='timeFiltersOutput', & + direction=MPAS_STREAM_OUTPUT, ierr=err) .and. computeCC) then + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'timeFiltersAM', timeFiltersAMPool) + ! get variables for computations + call mpas_pool_get_array(timeFiltersAMPool, 'normalVelocityLowPass', normalVelocityLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityZonalLowPass', velocityZonalLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityMeridionalLowPass', velocityMeridionalLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityXLowPass', velocityXLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityYLowPass', velocityYLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityZLowPass', velocityZLowPass) + call mpas_pool_get_array(timeFiltersAMPool, 'normalVelocityHighPass', normalVelocityHighPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityZonalHighPass', velocityZonalHighPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityMeridionalHighPass', velocityMeridionalHighPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityXHighPass', velocityXHighPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityYHighPass', velocityYHighPass) + call mpas_pool_get_array(timeFiltersAMPool, 'velocityZHighPass', velocityZHighPass) + ! must perform reconstruction for cell centered values + call mpas_reconstruct(meshPool, normalVelocityLowPass, & + velocityXLowPass, velocityYLowPass, velocityZLowPass, & + velocityZonalLowPass, velocityMeridionalLowPass, & + includeHalos = .false.) + ! must perform reconstruction for cell centered values + call mpas_reconstruct(meshPool, normalVelocityHighPass, & + velocityXHighPass, velocityYHighPass, velocityZHighPass, & + velocityZonalHighPass, velocityMeridionalHighPass, & + includeHalos = .false.) + block => block % next + end do + end if + +#ifdef TIME_FILTERS_DEBUG + call mpas_log_write( 'finished computing time filters') +#endif + + end subroutine ocn_compute_time_filters!}}} + +!*********************************************************************** +! +! routine ocn_restart_time_filters +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 07/17/2015 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_time_filters(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_time_filters!}}} + +!*********************************************************************** +! +! routine ocn_finalize_time_filters +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Phillip J. Wolfram +!> \date 07/17/2015 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_time_filters(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_time_filters!}}} + +end module ocn_time_filters + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_time_series_stats.F b/src/core_ocean/analysis_members/mpas_ocn_time_series_stats.F new file mode 100644 index 0000000000..f50ff861d6 --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_time_series_stats.F @@ -0,0 +1,2320 @@ +! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! ocn_time_series_stats +! +!> \brief MPAS ocean analysis core member: time_series_stats +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Flexible time series averaging, mins, and maxes of fields. +!----------------------------------------------------------------------- +module ocn_time_series_stats + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + ! Public parameters + !-------------------------------------------------------------------- + + ! Public member functions + !-------------------------------------------------------------------- + public :: ocn_bootstrap_time_series_stats, & + ocn_init_time_series_stats, & + ocn_compute_time_series_stats, & + ocn_restart_time_series_stats, & + ocn_finalize_time_series_stats + + ! Private module variables + !-------------------------------------------------------------------- + + type time_series_alarms_type + type (mpas_time_type) :: start_time + type (mpas_timeinterval_type) :: duration_interval + type (mpas_timeinterval_type) :: repeat_interval + type (mpas_timeinterval_type) :: reset_interval + end type time_series_alarms_type + + type time_series_variable_type + ! state per variable, stored in framework + character (len=StrKIND), pointer :: input_name + character (len=StrKIND), dimension(:), allocatable :: output_names + end type time_series_variable_type + + type time_series_buffer_type + ! state per buffer, stored in framework + integer, pointer :: started_flag, accumulate_flag, reset_flag + + ! strings for looking up alarms and buffers per buffer + character (len=StrKIND), pointer :: start_alarm_ID, repeat_alarm_ID, & + duration_alarm_ID, reset_alarm_ID + + ! counter for accumulation + integer, pointer :: counter + character (len=StrKIND), pointer :: xtime_start + character (len=StrKIND), pointer :: xtime_end + end type time_series_buffer_type + + type time_series_type + ! state per instance, stored in framework + integer, pointer :: operation + integer, pointer :: number_of_variables + integer, pointer :: number_of_buffers + + ! allocated on every instance call + type (time_series_variable_type), dimension(:), allocatable :: variables + type (time_series_buffer_type), dimension(:), allocatable :: buffers + end type time_series_type + + ! enum of ops and types + integer, parameter :: AVG_OP = 1 + integer, parameter :: MIN_OP = 2 + integer, parameter :: MAX_OP = 3 + integer, parameter :: SUM_OP = 4 + integer, parameter :: SOS_OP = 5 + + integer, parameter :: START_TIMES = 11 + integer, parameter :: DURATION_INTERVALS = 12 + integer, parameter :: REPEAT_INTERVALS = 13 + integer, parameter :: RESET_INTERVALS = 14 + + character (len=3), parameter :: AVG_TOKEN = 'avg' + character (len=3), parameter :: MIN_TOKEN = 'min' + character (len=3), parameter :: MAX_TOKEN = 'max' + character (len=3), parameter :: SUM_TOKEN = 'sum' + character (len=3), parameter :: SOS_TOKEN = 'sos' + + character (len=11), parameter :: TIME_START_PREFIX = 'xtime_start' + character (len=9), parameter :: TIME_END_PREFIX = 'xtime_end' + + character (len=StrKIND), parameter :: TIME_SERIES_STATS_POOL = & + 'timeSeriesStatsAM' + character (len=StrKIND), parameter :: ONE_STRING_MEMORY = & + 'timeSeriesStatsOneString' + character (len=StrKIND), parameter :: ONE_INTEGER_MEMORY = & + 'timeSeriesStatsOneInteger' + character (len=StrKIND), parameter :: ONE_REAL_MEMORY = & + 'timeSeriesStatsOneReal' + + character (len=StrKIND), parameter :: CONFIG_PREFIX = & + 'config_AM_timeSeriesStats' + character (len=StrKIND), parameter :: FRAMEWORK_PREFIX = 'time' + + character (len=StrKIND), parameter :: OUTPUT_STREAM_SUFFIX = '_output_stream' + character (len=StrKIND), parameter :: RESTART_STREAM_SUFFIX = '_restart_stream' + character (len=StrKIND), parameter :: OPERATION_SUFFIX = '_operation' + + character (len=StrKIND), parameter :: NUMBER_OF_BUFFERS_SUFFIX = & + '_number_of_buffers' + character (len=StrKIND), parameter :: NUMBER_OF_VARIABLES_SUFFIX = & + '_number_of_variables' + + character (len=StrKIND), parameter :: INPUT_NAME_SUFFIX = '_input_name' + + character (len=StrKIND), parameter :: REFERENCE_TIMES_SUFFIX = & + '_reference_times' + character (len=StrKIND), parameter :: DURATION_INTERVALS_SUFFIX = & + '_duration_intervals' + character (len=StrKIND), parameter :: REPEAT_INTERVALS_SUFFIX = & + '_repeat_intervals' + character (len=StrKIND), parameter :: RESET_INTERVALS_SUFFIX = & + '_reset_intervals' + + character (len=StrKIND), parameter :: STARTED_FLAG_SUFFIX = & + '_started_flag' + character (len=StrKIND), parameter :: ACCUMULATE_FLAG_SUFFIX = & + '_accumulate_flag' + character (len=StrKIND), parameter :: RESET_FLAG_SUFFIX = & + '_reset_flag' + character (len=StrKIND), parameter :: START_ALARM_ID_SUFFIX = & + '_start_alarm_ID' + character (len=StrKIND), parameter :: REPEAT_ALARM_ID_SUFFIX = & + '_repeat_alarm_ID' + character (len=StrKIND), parameter :: DURATION_ALARM_ID_SUFFIX = & + '_duration_alarm_ID' + character (len=StrKIND), parameter :: RESET_ALARM_ID_SUFFIX = & + '_reset_alarm_ID' + character (len=StrKIND), parameter :: COUNTER_SUFFIX = & + '_counter' + + character (len=StrKIND), parameter :: START_ALARM_PREFIX = '_startAlarm_' + character (len=StrKIND), parameter :: REPEAT_ALARM_PREFIX = '_repeatAlarm_' + character (len=StrKIND), parameter :: DURATION_ALARM_PREFIX = & + '_durationAlarm_' + character (len=StrKIND), parameter :: RESET_ALARM_PREFIX = '_resetAlarm_' + + character (len=StrKIND), parameter :: INITIAL_TIME_TOKEN = 'initial_time' + character (len=StrKIND), parameter :: REPEAT_INTERVAL_TOKEN = & + 'repeat_interval' + character (len=StrKIND), parameter :: RESET_INTERVAL_TOKEN = 'reset_interval' + + character (len=StrKIND), parameter :: CURRENT_CORE_NAME = 'MPAS-Ocean' + character (len=4), parameter :: NONE_TOKEN = 'none' + +!*********************************************************************** +contains + +!*********************************************************************** +! routine ocn_bootstrap_time_series_stats +! +!> \brief Bootstrap time_series_stats analysis member +!> \author Doug Jacobsen +!> \date 10/08/2015 +!> \details +!> This routine performs pre-init configuration of the analysis member. +!> Specifically, it ensures the streams used for this instance are correctly +!> configured. +!----------------------------------------------------------------------- +subroutine ocn_bootstrap_time_series_stats(domain, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: v + type (time_series_type) :: series + logical, dimension(:), pointer :: valid_input + + ! start procedure + err = 0 + + ! initial allocation of instance state for this AM from the namelist + call start_state(domain, instance, series, valid_input, err) + + ! modify the output and restart streams for this AM instance + ! driver will do a restart read, after this, if necessary to fill values + call modify_stream(domain, instance, series, valid_input, err) + + ! clean up the instance memory + do v = 1, series % number_of_variables + deallocate(series % variables(v) % output_names) + end do + deallocate(series % variables) + deallocate(series % buffers) +end subroutine ocn_bootstrap_time_series_stats!}}} + +!*********************************************************************** +! routine ocn_init_time_series_stats +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_init_time_series_stats(domain, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: v, b + type (time_series_type) :: series + type (time_series_alarms_type), allocatable, dimension(:) :: alarms + type (MPAS_Time_type) :: start_intv + character (len=StrKIND) :: start_xtime + + ! start procedure + err = 0 + + ! coming back from a potential restart read + ! get all of the state for this instance + call get_state(domain, instance, series) + + ! get all of the timing configurations from namelist + allocate(alarms(series % number_of_buffers)) + call get_alarms(domain, instance, series, alarms, err) + + ! set the values of the alarms and current flag states based on timers + call set_alarms(domain, instance, series, alarms, err) + deallocate(alarms) + + ! set xtime start if it is still unset, for very first time step + ! (i.e., no restarts) + start_intv = mpas_get_clock_time(domain % clock, MPAS_NOW, err) + call mpas_get_time(start_intv, dateTimeString=start_xtime, ierr=err) + + do b = 1, series % number_of_buffers + if (trim(series % buffers(b) % xtime_start) == '') then + series % buffers(b) % xtime_start = start_xtime + end if + end do + + ! clean up the instance memory + do v = 1, series % number_of_variables + deallocate(series % variables(v) % output_names) + end do + deallocate(series % variables) + deallocate(series % buffers) +end subroutine ocn_init_time_series_stats!}}} + + +!*********************************************************************** +! routine ocn_compute_time_series_stats +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_compute_time_series_stats(domain, timeLevel, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + integer, intent(in) :: timeLevel + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: v, b + type (time_series_type) :: series + type (MPAS_TimeInterval_type) :: dt + type (MPAS_Time_type) :: start_intv, end_intv + character (len=StrKIND) :: start_xtime, end_xtime + logical :: unset_xtime + + ! start procedure + err = 0 + + ! get all of the state for this instance to be able to compute + call get_state(domain, instance, series) + + ! get the strings for the date + unset_xtime = .true. + + ! update the counter + do b = 1, series % number_of_buffers + if (series % buffers(b) % accumulate_flag == 1) then + if (unset_xtime) then + end_intv = mpas_get_clock_time(domain % clock, MPAS_NOW, err) + call mpas_get_time(end_intv, dateTimeString=end_xtime, ierr=err) + start_intv = end_intv - mpas_get_clock_timestep(domain % clock, err) + call mpas_get_time(start_intv, dateTimeString=start_xtime, ierr=err) + unset_xtime = .false. + end if + + if (series % buffers(b) % reset_flag == 1) then + series % buffers(b) % xtime_start = start_xtime + series % buffers(b) % counter = 1 + else + series % buffers(b) % xtime_end = end_xtime + series % buffers(b) % counter = series % buffers(b) % counter + 1 + end if + end if + end do + + ! do all of the operations + do v = 1, series % number_of_variables + call typed_operate(domain % blocklist, & + series % variables(v), & + series % buffers, & + series % operation) + end do + + ! do all of the time checking and flag setting + call timer_checking(series, domain % clock, err) + + ! clean up the instance memory + do v = 1, series % number_of_variables + deallocate(series % variables(v) % output_names) + end do + deallocate(series % variables) + deallocate(series % buffers) +end subroutine ocn_compute_time_series_stats!}}} + + + +!*********************************************************************** +! routine ocn_restart_time_series_stats +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_restart_time_series_stats(domain, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + + ! start procedure + err = 0 + +end subroutine ocn_restart_time_series_stats!}}} + + + +!*********************************************************************** +! routine ocn_finalize_time_series_stats +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +!----------------------------------------------------------------------- +subroutine ocn_finalize_time_series_stats(domain, instance, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + + ! start procedure + err = 0 + +end subroutine ocn_finalize_time_series_stats!}}} + +! +! local subroutines +! + +!*********************************************************************** +! routine debug_state +! +!> \brief Print the internal state of the time series analysis member +!> \author Jon Woodring +!> \date May 9, 2016 +!> \details +!> Print the state for the time series analysis member. Primarily for +!> internal debugging. +!----------------------------------------------------------------------- +subroutine debug_state(series) + ! input variables + type (time_series_type), intent(in) :: series + + ! input/output variables + + ! output variables + + ! local variables + integer :: v, b + + ! start procedure + write(*,*) 'operation: ', series % operation + write(*,*) '# variables: ', series % number_of_variables + write(*,*) '# buffers: ', series % number_of_buffers + + do b = 1, series % number_of_buffers + write(*,*) 'buffer #: ', b + write(*,*) 'started: ', series % buffers(b) % started_flag + write(*,*) 'accum: ', series % buffers(b) % accumulate_flag + write(*,*) 'reset: ', series % buffers(b) % reset_flag + write(*,*) 'start alarm: ', trim(series % buffers(b) % start_alarm_ID) + write(*,*) 'repeat alarm: ', trim(series % buffers(b) % repeat_alarm_ID) + write(*,*) 'duration alarm: ', trim(series % buffers(b) % duration_alarm_ID) + write(*,*) 'reset alarm: ', trim(series % buffers(b) % reset_alarm_ID) + write(*,*) 'counter: ', series % buffers(b) % counter + write(*,*) 'xtime start: ', trim(series % buffers(b) % xtime_start) + write(*,*) 'xtime end: ', trim(series % buffers(b) % xtime_end) + end do + + do v = 1, series % number_of_variables + write(*,*) 'variable #: ', v + write(*,*) 'input: ', trim(series % variables(v) % input_name) + do b = 1, series % number_of_buffers + write(*,*) 'buffer #: ', b + write(*,*) 'output: ', trim(series % variables(v) % output_names(b)) + end do + end do + +end subroutine debug_state + +!*********************************************************************** +! routine add_new_string +! +!> \brief Allocate a string in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new integer in the AM pool and return a pointer to it. +!----------------------------------------------------------------------- +subroutine add_new_string(all_fields, inpool, outpool, field_name, target_ptr) + ! input variables + character (len=StrKIND) :: field_name + + ! input/output variables + type (mpas_pool_type), pointer, intent(inout) :: all_fields, inpool, outpool + + ! output variables + character (len=StrKIND), pointer, optional :: target_ptr + + ! local variables + type (field0DChar), pointer :: srcString, dstString + + call mpas_pool_get_field(inpool, ONE_STRING_MEMORY, srcString, 1) + call mpas_duplicate_field(srcString, dstString) + dstString % fieldName = field_name + call mpas_pool_add_field(outpool, dstString % fieldName, dstString) + call mpas_pool_add_field(all_fields, dstString % fieldName, dstString) + if (present(target_ptr)) then + call mpas_pool_get_array(outpool, dstString % fieldName, target_ptr, 1) + end if +end subroutine add_new_string + + + +!*********************************************************************** +! routine add_new_integer +! +!> \brief Allocate an integer in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new integer in the AM pool and return a pointer to it. +!----------------------------------------------------------------------- +subroutine add_new_integer(all_fields, inpool, outpool, field_name, target_ptr) + ! input variables + character (len=StrKIND) :: field_name + + ! input/output variables + type (mpas_pool_type), pointer, intent(inout) :: all_fields, inpool, outpool + + ! output variables + integer, pointer, optional :: target_ptr + + ! local variables + type (field0DInteger), pointer :: srcInteger, dstInteger + + call mpas_pool_get_field(inpool, ONE_INTEGER_MEMORY, srcInteger, 1) + call mpas_duplicate_field(srcInteger, dstInteger) + dstInteger % fieldName = field_name + call mpas_pool_add_field(outpool, dstInteger % fieldName, dstInteger) + call mpas_pool_add_field(all_fields, dstInteger % fieldName, dstInteger) + if (present(target_ptr)) then + call mpas_pool_get_array(outpool, dstInteger % fieldName, target_ptr, 1) + end if +end subroutine add_new_integer + + + +!*********************************************************************** +! routine add_new_real +! +!> \brief Allocate a real in the MPAS framework for this AM +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Allocate a new real in the AM pool and return a pointer to it. +!----------------------------------------------------------------------- +subroutine add_new_real(all_fields, inpool, outpool, field_name, target_ptr) + ! input variables + character (len=StrKIND) :: field_name + + ! input/output variables + type (mpas_pool_type), pointer, intent(inout) :: all_fields, inpool, outpool + + ! output variables + real (kind=RKIND), pointer, optional :: target_ptr + + ! local variables + type (field0DReal), pointer :: srcReal, dstReal + + call mpas_pool_get_field(inpool, ONE_REAL_MEMORY, srcReal, 1) + call mpas_duplicate_field(srcReal, dstReal) + dstReal % fieldName = field_name + call mpas_pool_add_field(outpool, dstReal % fieldName, dstReal) + call mpas_pool_add_field(all_fields, dstReal % fieldName, dstReal) + if (present(target_ptr)) then + call mpas_pool_get_array(outpool, dstReal % fieldName, target_ptr, 1) + end if +end subroutine add_new_real + + + +!*********************************************************************** +! routine check_real_time +! +!> \brief Check to see if an MPAS field can have time series stats applied +!> \author Jon Woodring +!> \date December 17, 2015 +!> \details +!> Makes sure that time series stats can be applied to a field_name. +!> In particular, checks to see if it is real and has time levels. +!----------------------------------------------------------------------- +logical function check_real_time(all_fields, field_name) + type (mpas_pool_type), pointer, intent(in) :: all_fields + character (len=StrKIND), intent(in) :: field_name + + logical :: check + type (field0DReal), pointer :: r0 + type (field1DReal), pointer :: r1 + type (field2DReal), pointer :: r2 + type (field3DReal), pointer :: r3 + type (field4DReal), pointer :: r4 + type (field5DReal), pointer :: r5 + type(mpas_pool_field_info_type) :: info + + ! get the info of the field + call mpas_pool_get_field_info(all_fields, field_name, info) + check = info % fieldType == MPAS_POOL_REAL + + if(.not. check) then + call mpas_log_write( trim(CURRENT_CORE_NAME) // & + ' WARNING: field "' // trim(field_name) // '" listed in the ' // & + 'output stream, for time series stats analysis member ' // & + 'stream, is not real. Time series stats will not be applied to ' // & + 'this field.') + else + if(info % nDims == 0) then + call mpas_pool_get_field(all_fields, field_name, r0, 1) + check = r0 % hasTimeDimension + else if(info % nDims == 1) then + call mpas_pool_get_field(all_fields, field_name, r1, 1) + check = r1 % hasTimeDimension + else if(info % nDims == 2) then + call mpas_pool_get_field(all_fields, field_name, r2, 1) + check = r2 % hasTimeDimension + else if(info % nDims == 3) then + call mpas_pool_get_field(all_fields, field_name, r3, 1) + check = r3 % hasTimeDimension + else if(info % nDims == 4) then + call mpas_pool_get_field(all_fields, field_name, r4, 1) + check = r4 % hasTimeDimension + else if(info % nDims == 5) then + call mpas_pool_get_field(all_fields, field_name, r5, 1) + check = r5 % hasTimeDimension + end if + + if (.not. check) then + call mpas_log_write( trim(CURRENT_CORE_NAME) // & + ' WARNING: field "' // trim(field_name) // '" listed in the ' // & + 'output stream, for time series stats analysis member ' // & + 'stream, does not have a time dimension. Time series stats will ' // & + 'not be applied to this field.') + end if + end if + + check_real_time = check + +end function check_real_time + + +!*********************************************************************** +! routine get_state +! +!> \brief Get all of the state for this instance. +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This will allocate and fetch all of the state necessary for this +!> instance that is being run. +!----------------------------------------------------------------------- +subroutine get_state(domain, instance, series) + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + + ! output variables + type (time_series_type), intent(out) :: series + + ! local variables + integer :: v, b + character (len=StrKIND) :: storage_prefix, var_identifier, & + buf_identifier, var_prefix, buf_prefix, field_name, op_name + type (mpas_pool_type), pointer :: amPool + + ! start procedure + storage_prefix = trim(FRAMEWORK_PREFIX) // trim(instance) + call mpas_pool_get_subpool(domain % blocklist % structs, & + TIME_SERIES_STATS_POOL, amPool) + + ! + ! get the base + ! + + ! number_of_variables + field_name = trim(storage_prefix) // trim(NUMBER_OF_VARIABLES_SUFFIX) + call mpas_pool_get_array(amPool, field_name, series % number_of_variables, 1) + + ! number_of_buffers + field_name = trim(storage_prefix) // trim(NUMBER_OF_BUFFERS_SUFFIX) + call mpas_pool_get_array(amPool, field_name, series % number_of_buffers, 1) + + ! operation + field_name = trim(storage_prefix) // trim(OPERATION_SUFFIX) + call mpas_pool_get_array(amPool, field_name, series % operation, 1) + + op_name = operator_naming(series % operation) + + ! create the memory + allocate(series % variables(series % number_of_variables)) + allocate(series % buffers(series % number_of_buffers)) + do v = 1, series % number_of_variables + allocate(series % variables(v) % output_names(series % number_of_buffers)) + end do + + ! + ! get the instance values for variables + ! + + do v = 1, series % number_of_variables + ! identifier + write(var_identifier, '(I0)') v + var_prefix = trim(storage_prefix) // '_' // trim(var_identifier) + + ! input_name + field_name = trim(var_prefix) // trim(INPUT_NAME_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % variables(v) % input_name, 1) + + if (series % number_of_buffers > 1) then + do b = 1, series % number_of_buffers + write(buf_identifier, '(I0)') b + + ! create output names + series % variables(v) % output_names(b) = output_naming & + (storage_prefix, op_name, series % variables(v) % input_name, & + buf_identifier) + end do + else + series % variables(v) % output_names(1) = output_naming & + (storage_prefix, op_name, series % variables(v) % input_name) + end if + end do + + ! + ! get the instance values for buffers + ! + + do b = 1, series % number_of_buffers + ! identifier + write(buf_identifier, '(I0)') b + buf_prefix = trim(storage_prefix) // '_' // trim(buf_identifier) + + ! started_flag + field_name = trim(buf_prefix) // trim(STARTED_FLAG_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % started_flag, 1) + + ! accumulate_flag + field_name = trim(buf_prefix) // trim(ACCUMULATE_FLAG_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % accumulate_flag, 1) + + ! reset_flag + field_name = trim(buf_prefix) // trim(RESET_FLAG_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % reset_flag, 1) + + ! start_alarm_ID + field_name = trim(buf_prefix) // trim(START_ALARM_ID_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % start_alarm_ID, 1) + + ! repeat_alarm_ID + field_name = trim(buf_prefix) // trim(REPEAT_ALARM_ID_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % repeat_alarm_ID, 1) + + ! duration_alarm_ID + field_name = trim(buf_prefix) // trim(DURATION_ALARM_ID_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % duration_alarm_ID, 1) + + ! reset_alarm_ID + field_name = trim(buf_prefix) // trim(RESET_ALARM_ID_SUFFIX) + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % reset_alarm_ID, 1) + + ! counter + if (series % number_of_buffers > 1) then + field_name = counter_naming(storage_prefix, buf_identifier) + else + field_name = counter_naming(storage_prefix) + end if + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % counter, 1) + + ! xtime start + if (series % number_of_buffers > 1) then + field_name = trim(TIME_START_PREFIX) // trim(instance) // & + '_' // buf_identifier + else + field_name = trim(TIME_START_PREFIX) // trim(instance) + end if + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % xtime_start, 1) + + ! xtime end + if (series % number_of_buffers > 1) then + field_name = trim(TIME_END_PREFIX) // trim(instance) // & + '_' // buf_identifier + else + field_name = trim(TIME_END_PREFIX) // trim(instance) + end if + call mpas_pool_get_array(amPool, & + field_name, series % buffers(b) % xtime_end, 1) + + end do + +end subroutine get_state + +!*********************************************************************** +! routine start_state +! +!> \brief Begin the initialization of this analysis member +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This will count the number of variables, number of buffers, and +!> also get the stream name and operation strings. +!----------------------------------------------------------------------- +subroutine start_state(domain, instance, series, valid_input, err) + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + logical, dimension(:), pointer :: valid_input + + ! output variables + type (time_series_type), intent(out) :: series + integer, intent(out) :: err !< Output: error flag + + ! local variables + character (len=StrKIND), pointer :: config_results, output_stream_name + character (len=StrKIND) :: config, namelist_prefix, storage_prefix, & + var_identifier, buf_identifier, var_prefix, buf_prefix, field_name + integer :: b, v + type (mpas_pool_type), pointer :: amPool + + ! start procedure + err = 0 + + namelist_prefix = trim(CONFIG_PREFIX) // trim(instance) + storage_prefix = trim(FRAMEWORK_PREFIX) // trim(instance) + call mpas_pool_get_subpool(domain % blocklist % structs, & + TIME_SERIES_STATS_POOL, amPool) + + ! + ! allocate some framework memory for instance state + ! + + ! number_of_variables + field_name = trim(storage_prefix) // trim(NUMBER_OF_VARIABLES_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, series % number_of_variables) + + ! number_of_buffers + field_name = trim(storage_prefix) // trim(NUMBER_OF_BUFFERS_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, series % number_of_buffers) + + ! operation + field_name = trim(storage_prefix) // trim(OPERATION_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, series % operation) + + ! + ! assign some instance values + ! + + ! get the stream name + config = trim(namelist_prefix) // trim(OUTPUT_STREAM_SUFFIX) + call mpas_pool_get_config(domain % configs, config, output_stream_name) + + if (output_stream_name == NONE_TOKEN) then + call mpas_log_write( & + 'Stream cannot be "none" for time series stats.', MPAS_LOG_CRIT) + end if + + ! count the number of variables + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + b = 0 + do while (mpas_stream_mgr_get_next_field(domain % streamManager, & + output_stream_name, field_name)) + b = b + 1 + end do + + allocate(valid_input(b)) + + ! count the number of variables and mark if valid + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + b = 1 + series % number_of_variables = 0 + do while (mpas_stream_mgr_get_next_field(domain % streamManager, & + output_stream_name, field_name)) + + valid_input(b) = check_real_time(domain % blocklist % allFields, & + field_name) + + if (valid_input(b)) then + series % number_of_variables = series % number_of_variables + 1 + end if + + b = b + 1 + end do + + if (series % number_of_variables < 1) then + call mpas_log_write( & + 'In the time series stats output stream "' // trim(output_stream_name) & + // '" that time series stats can be applied to.', MPAS_LOG_CRIT) + end if + + ! count the number of buffers + config = trim(namelist_prefix) // trim(REFERENCE_TIMES_SUFFIX) + call mpas_pool_get_config(domain % configs, config, config_results) + config = config_results + series % number_of_buffers = 1 + b = scan(config, ';') + do while (b > 0) + series % number_of_buffers = series % number_of_buffers + 1 + config = config(b+1:) + b = scan(config, ';') + end do + + ! get our operation + config = trim(namelist_prefix) // trim(OPERATION_SUFFIX) + call mpas_pool_get_config(domain % configs, config, config_results) + + series % operation = operator_enum(config_results) + + ! create the memory + allocate(series % variables(series % number_of_variables)) + allocate(series % buffers(series % number_of_buffers)) + do v = 1, series % number_of_variables + allocate(series % variables(v) % output_names(series % number_of_buffers)) + end do + + ! + ! duplicate memory for storing AM instance state in the framework + ! + + ! create variable space + b = 1 + do v = 1, series % number_of_variables + ! identifier + write(var_identifier, '(I0)') v + var_prefix = trim(storage_prefix) // '_' // trim(var_identifier) + + ! input_name + field_name = trim(var_prefix) // trim(INPUT_NAME_SUFFIX) + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, series % variables(v) % input_name) + end do + + ! create buffer space + do b = 1, series % number_of_buffers + ! identifier + write(buf_identifier, '(I0)') b + buf_prefix = trim(storage_prefix) // '_' // trim(buf_identifier) + + ! started_flag + field_name = trim(buf_prefix) // trim(STARTED_FLAG_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % started_flag) + + ! accumulate_flag + field_name = trim(buf_prefix) // trim(ACCUMULATE_FLAG_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % accumulate_flag) + + ! reset_flag + field_name = trim(buf_prefix) // trim(RESET_FLAG_SUFFIX) + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % reset_flag) + + ! start_alarm_ID + field_name = trim(buf_prefix) // trim(START_ALARM_ID_SUFFIX) + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % start_alarm_ID) + + ! repeat_alarm_ID + field_name = trim(buf_prefix) // trim(REPEAT_ALARM_ID_SUFFIX) + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % repeat_alarm_ID) + + ! duration_alarm_ID + field_name = trim(buf_prefix) // trim(DURATION_ALARM_ID_SUFFIX) + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % duration_alarm_ID) + + ! reset_alarm_ID + field_name = trim(buf_prefix) // trim(RESET_ALARM_ID_SUFFIX) + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % reset_alarm_ID) + + ! + ! counter, xtime_start, and xtime_end is not allocated here, + ! because it is part of the restart stream and output_stream, + ! and not just the internal AM state + ! + ! it is allocated in modify_stream + ! + end do +end subroutine start_state + + + +!*********************************************************************** +! routine modify_stream +! +!> \brief Remove existing variables and replace them with new ones +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Given a stream name, this will remove the existing variables +!> in a stream and replace them with similiarly named ones for +!> their accumulation. It will also add xtime and optionally the mesh. +!----------------------------------------------------------------------- +subroutine modify_stream(domain, instance, series, valid_input, err)!{{{ + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + type (time_series_type), intent(inout) :: series + logical, dimension(:), pointer :: valid_input + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: v, b + logical :: emptyRestartStream, restartStreamEnabled + character (len=StrKIND), pointer :: output_stream_name, restart_stream_name + character (len=StrKIND) :: fieldName + character (len=StrKIND) :: field_name, config, op_name + character (len=StrKIND) :: namelist_prefix, & + storage_prefix, buf_identifier, buf_prefix + type (mpas_pool_field_info_type) :: info + type (mpas_pool_type), pointer :: amPool + + ! start procedure + err = 0 + + namelist_prefix = trim(CONFIG_PREFIX) // trim(instance) + storage_prefix = trim(FRAMEWORK_PREFIX) // trim(instance) + call mpas_pool_get_subpool(domain % blocklist % structs, & + TIME_SERIES_STATS_POOL, amPool) + + ! get the output stream name + config = trim(namelist_prefix) // trim(OUTPUT_STREAM_SUFFIX) + call mpas_pool_get_config(domain % configs, config, output_stream_name) + + ! get restart stream name + config = trim(namelist_prefix) // trim(RESTART_STREAM_SUFFIX) + call mpas_pool_get_config(domain % configs, config, restart_stream_name) + if ( trim(restart_stream_name) == 'none' ) then + restartStreamEnabled = .false. + else + restartStreamEnabled = .true. + endif + + op_name = operator_naming(series % operation) + + ! get the old field names, assign to input name, and remove from stream + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + b = 1 + v = 1 + do while (mpas_stream_mgr_get_next_field(domain % streamManager, & + output_stream_name, field_name)) + + ! check if we can handle it + if (valid_input(b)) then + series % variables(v) % input_name = field_name + + ! remove the old one + call mpas_stream_mgr_remove_field(domain % streamManager, & + output_stream_name, series % variables(v) % input_name) + + v = v + 1 + end if + + b = b + 1 + end do + + deallocate(valid_input) + + + ! + ! create memory and modify the stream + ! + + ! ensure restart stream is empty + if (restartStreamEnabled) then + emptyRestartStream = .true. + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + streamID=restart_stream_name, ierr=err) + do while (mpas_stream_mgr_get_next_field(domain % streamManager, & + streamID=restart_stream_name, fieldName=fieldName) .and. emptyRestartStream) + emptyRestartStream = .false. + end do + + if (.not. emptyRestartStream) then + call mpas_log_write( 'Stream named ''' & + // trim(restart_stream_name) // ''' is not empty, but is used in ' // & + 'an restart instance of the time series stats analysis member. ' // & + 'This restart stream will be built based on the contents of the ''' // & + trim(output_stream_name) // ''' stream. Please ensure it is empty ' // & + 'in the streams input file.') + call mpas_log_write( & + 'Misconfigured restart stream for time series stats ' // & + ' analysis member.', MPAS_LOG_CRIT) + end if + end if + + ! create and put the counter, xtime_start, xtime_end in the streams + do b = 1, series % number_of_buffers + write(buf_identifier, '(I0)') b + + ! allocate counter memory + if (series % number_of_buffers > 1) then + field_name = counter_naming(storage_prefix, buf_identifier) + else + field_name = counter_naming(storage_prefix) + end if + call add_new_integer(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % counter) + + ! put it in the output and restart + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, field_name, ierr=err) + if (restartStreamEnabled) then + call mpas_stream_mgr_add_field(domain % streamManager, & + restart_stream_name, field_name, ierr=err) + end if + + ! xtime start + if (series % number_of_buffers > 1) then + field_name = trim(TIME_START_PREFIX) // trim(instance) // & + '_' // buf_identifier + else + field_name = trim(TIME_START_PREFIX) // trim(instance) + end if + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % xtime_start) + + ! put it in the output and restart + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, field_name, ierr=err) + if (restartStreamEnabled) then + call mpas_stream_mgr_add_field(domain % streamManager, & + restart_stream_name, field_name, ierr=err) + end if + + ! xtime end + if (series % number_of_buffers > 1) then + field_name = trim(TIME_END_PREFIX) // trim(instance) // & + '_' // buf_identifier + else + field_name = trim(TIME_END_PREFIX) // trim(instance) + end if + call add_new_string(domain % blocklist % allFields, amPool, amPool, & + field_name, series % buffers(b) % xtime_end) + + ! put it in the output and restart + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, field_name, ierr=err) + if (restartStreamEnabled) then + call mpas_stream_mgr_add_field(domain % streamManager, & + restart_stream_name, field_name, ierr=err) + end if + end do + + ! set up the variables + call mpas_stream_mgr_begin_iteration(domain % streamManager, & + output_stream_name, err) + do v = 1, series % number_of_variables + ! get the info of the field + call mpas_pool_get_field_info(domain % blocklist % allFields, & + series % variables(v) % input_name, info) + + ! allocate a number of fields and add field + do b = 1, series % number_of_buffers + write(buf_identifier, '(I0)') b + + if (series % number_of_buffers > 1) then + field_name = output_naming(storage_prefix, op_name, & + series % variables(v) % input_name, buf_identifier) + else + field_name = output_naming(storage_prefix, op_name, & + series % variables(v) % input_name) + end if + + ! create the name of the output var + series % variables(v) % output_names(b) = field_name + + ! create the field and add to pool + call add_new_field(info, & + series % variables(v) % input_name, & + series % variables(v) % output_names(b), & + domain % blocklist % allFields, amPool) + + ! add the field to the output stream + call mpas_stream_mgr_add_field(domain % streamManager, & + output_stream_name, series % variables(v) % output_names(b), ierr=err) + + ! put it in the restart stream + if (restartStreamEnabled) then + call mpas_stream_mgr_add_field(domain % streamManager, & + restart_stream_name, series % variables(v) % output_names(b), ierr=err) + end if + end do + end do ! number_of_variables + +end subroutine modify_stream!}}} + + +!*********************************************************************** +! function output_naming +! +!> \brief Given an input name, create a cooresponding output name +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Code to create consistent output names from input names. +!----------------------------------------------------------------------- +character (len=StrKIND) function output_naming & +(storage_prefix, op_name, input_name, buf_identifier) + character (len=StrKIND), intent(in) :: storage_prefix, op_name, input_name + character (len=StrKIND), intent(in), optional :: buf_identifier + + if (present(buf_identifier)) then + output_naming = trim(storage_prefix) // '_' // trim(op_name) // '_' // & + trim(input_name) // '_' // trim(buf_identifier) + else + output_naming = trim(storage_prefix) // '_' // trim(op_name) // '_' // & + trim(input_name) + endif +end function output_naming + + +!*********************************************************************** +! function counter_naming +! +!> \brief Given an buffer number, create a cooresponding counter name +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Code to create consistent counter names from buffer numbers. +!----------------------------------------------------------------------- +character (len=StrKIND) function counter_naming & +(storage_prefix, buf_identifier) + character (len=StrKIND), intent(in) :: storage_prefix + character (len=StrKIND), intent(in), optional :: buf_identifier + + if (present(buf_identifier)) then + counter_naming = trim(storage_prefix) // trim(COUNTER_SUFFIX) // & + '_' // trim(buf_identifier) + else + counter_naming = trim(storage_prefix) // trim(COUNTER_SUFFIX) + end if +end function counter_naming + + +!*********************************************************************** +! function operator_naming +! +!> \brief Given an operator number, create a corresponding operator name +!> \author Jon Woodring +!> \date May 9, 2016 +!> \details +!> Code to create consistent operator names from operator enums. +!----------------------------------------------------------------------- +character (len=StrKIND) function operator_naming (operator_enum) + integer, intent(in) :: operator_enum + + ! operator + if (operator_enum == AVG_OP) then + operator_naming = AVG_TOKEN + else if (operator_enum == MIN_OP) then + operator_naming = MIN_TOKEN + else if (operator_enum == MAX_OP) then + operator_naming = MAX_TOKEN + else if (operator_enum == SUM_OP) then + operator_naming = SUM_TOKEN + else if (operator_enum == SOS_OP) then + operator_naming = SOS_TOKEN + else + call mpas_log_write( & + 'The impossible happened - ' // & + 'tried to create an operation in the time series stats ' // & + 'analysis member of unknown kind', MPAS_LOG_CRIT) + end if +end function operator_naming + + +!*********************************************************************** +! function operator_enum +! +!> \brief Given an operator token, create a corresponding operator enum +!> \author Jon Woodring +!> \date May 9, 2016 +!> \details +!> Code to create consistent operator enums from operator tokens. +!----------------------------------------------------------------------- +integer function operator_enum (token) + character (len=StrKIND), intent(in) :: token + + if (trim(token) == AVG_TOKEN) then + operator_enum = AVG_OP + else if (trim(token) == MIN_TOKEN) then + operator_enum = MIN_OP + else if (trim(token) == MAX_TOKEN) then + operator_enum = MAX_OP + else if (trim(token) == SUM_TOKEN) then + operator_enum = SUM_OP + else if (trim(token) == SOS_TOKEN) then + operator_enum = SOS_OP + else + call mpas_log_write( & + 'Unknown operation "' // & + trim(token) // '" requested in the time ' // & + 'series stats analysis member configuration', MPAS_LOG_CRIT) + end if +end function operator_enum + + +!*********************************************************************** +! routine get_alarms +! +!> \brief Read the namelist for timings +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This will read the namelist and get the strings and set the clocks +!> for the different timers to be used. The actual alarms are not set. +!----------------------------------------------------------------------- +subroutine get_alarms(domain, instance, series, alarms, err) + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + type (time_series_type), intent(inout) :: series + + ! output variables + integer, intent(out) :: err !< Output: error flag + type (time_series_alarms_type), dimension(:), intent(out) :: alarms + + ! local variables + character (len=StrKIND), pointer :: config_results + character (len=StrKIND) :: config, namelist_prefix + integer :: b + integer (kind=I8KIND) :: n + logical :: ok + type (mpas_timeinterval_type) :: rem, zero + + ! create prefix + namelist_prefix = trim(CONFIG_PREFIX) // trim(instance) + + ! configure start times - we don't have to check ok + ! because the timer count is based on reference_times tokens + config = trim(namelist_prefix) // trim(REFERENCE_TIMES_SUFFIX) + call mpas_pool_get_config(domain % configs, config, config_results) + call set_times(series, alarms, domain % clock, START_TIMES, & + config_results, ok, err) + + ! order matters, don't reorder these following ones! + ! it matters because times/intervals can be configured to be equal + ! to other ones + + ! configure reset intervals + config = trim(namelist_prefix) // trim(RESET_INTERVALS_SUFFIX) + call mpas_pool_get_config(domain % configs, config, config_results) + call set_times(series, alarms, domain % clock, RESET_INTERVALS, & + config_results, ok, err) + if (.not. ok) then + call mpas_log_write( & + 'Number of listed times in ' // & + 'reset_intervals is not consistent with number of listed times ' // & + 'in reference_times in time series stats analysis member ' // & + 'configuration.', MPAS_LOG_CRIT) + end if + + ! configure repeat intervals + config = trim(namelist_prefix) // trim(REPEAT_INTERVALS_SUFFIX) + call mpas_pool_get_config(domain % configs, config, config_results) + call set_times(series, alarms, domain % clock, REPEAT_INTERVALS, & + config_results, ok, err) + if (.not. ok) then + call mpas_log_write( & + 'Number of listed times in ' // & + 'repeat_intervals is not consistent with number of listed times ' // & + 'in reference_times in time series stats analysis member ' // & + 'configuration.', MPAS_LOG_CRIT) + end if + + ! configure duration intervals + config = trim(namelist_prefix) // trim(DURATION_INTERVALS_SUFFIX) + call mpas_pool_get_config(domain % configs, config, config_results) + call set_times(series, alarms, domain % clock, DURATION_INTERVALS, & + config_results, ok, err) + if (.not. ok) then + call mpas_log_write( & + 'Number of listed times in ' // & + 'duration_intervals is not consistent with number of listed times ' // & + 'in reference_times in time series stats analysis member ' // & + 'configuration.', MPAS_LOG_CRIT) + end if + + ! check if some of the time configuration is sensible + call mpas_set_timeInterval(zero, s=0) + + do b = 1, series % number_of_buffers + call mpas_interval_division(alarms(b) % start_time, & + alarms(b) % repeat_interval, & + alarms(b) % reset_interval, n, rem) + + if (n > 1 .or. (n == 1 .and. rem /= zero)) then + call mpas_log_write( trim(CURRENT_CORE_NAME) // & + 'WARNING: repeat_interval > ' // & + 'reset_interval in time series stats analysis member ' // & + 'configuration. Truncating repeat_interval.') + alarms(b) % repeat_interval = alarms(b) % reset_interval + end if + + call mpas_interval_division(alarms(b) % start_time, & + alarms(b) % duration_interval, & + alarms(b) % repeat_interval, n, rem) + + if (n > 1 .or. (n == 1 .and. rem /= zero)) then + call mpas_log_write( trim(CURRENT_CORE_NAME) // & + 'WARNING: duration_interval > ' // & + 'repeat_interval in time series stats analysis member ' // & + 'configuration. Truncating duration_interval.') + alarms(b) % repeat_interval = alarms(b) % reset_interval + end if + end do +end subroutine get_alarms + + + +!*********************************************************************** +! routine set_alarms +! +!> \brief Set the alarms based on the clocks +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Alarms for the different timers are set, such that temporal +!> window alarms are configured. +!----------------------------------------------------------------------- +subroutine set_alarms(domain, instance, series, alarms, err) + ! input variables + character (len=StrKIND), intent(in) :: instance + + ! input/output variables + type (domain_type), intent(inout) :: domain + type (time_series_type), intent(inout) :: series + type (time_series_alarms_type), dimension(:), intent(inout) :: alarms + + ! output variables + integer, intent(out) :: err !< Output: error flag + + ! local variables + integer :: b + integer (kind=I8KIND) :: repeat_n, duration_n, reset_n + character (len=StrKIND) :: buf_identifier, alarm_prefix + type (mpas_time_type) :: current_time, when, & + duration_time, repeat_time, reset_time + type (mpas_timeinterval_type) :: elapsed, zero, & + repeat_rem, duration_rem, reset_rem, zero_intv + + ! start procedure + alarm_prefix = trim(FRAMEWORK_PREFIX) // trim(instance) + + ! get current time + current_time = mpas_get_clock_time(domain % clock, MPAS_NOW, err) + call mpas_set_timeInterval(zero_intv, S=0) + + ! configure alarms + do b = 1, series % number_of_buffers + write(buf_identifier, '(I0)') b + + ! zero flags + series % buffers(b) % started_flag = 0 + series % buffers(b) % reset_flag = 0 + series % buffers(b) % accumulate_flag = 0 + + ! set start time and flag + if (current_time >= alarms(b) % start_time) then + series % buffers(b) % started_flag = 1 + + ! no start alarm + series % buffers(b) % start_alarm_ID = '' + else + ! set the start alarm + series % buffers(b) % start_alarm_ID = trim(alarm_prefix) // & + trim(START_ALARM_PREFIX) // trim(buf_identifier) + call mpas_add_clock_alarm(domain % clock, & + series % buffers(b) % start_alarm_ID, & + alarms(b) % start_time, ierr=err) + end if + + ! set next reset time and flag + when = alarms(b) % start_time + alarms(b) % reset_interval + if (current_time >= when) then + elapsed = current_time - when + call mpas_interval_division(when, elapsed, & + alarms(b) % reset_interval, reset_n, reset_rem) + + if (reset_rem == zero_intv) then + ! reset right now + reset_time = current_time + alarms(b) % reset_interval + series % buffers(b) % reset_flag = 1 + else + reset_rem = alarms(b) % reset_interval - reset_rem + reset_time = current_time + reset_rem + end if + else + reset_time = when + end if + + ! set next duration time and flag + when = alarms(b) % start_time + alarms(b) % duration_interval ! is offset + if (current_time >= when) then + elapsed = current_time - when + call mpas_interval_division(when, elapsed, & + alarms(b) % repeat_interval, & ! repeat is correct + duration_n, duration_rem) + + if (duration_rem == zero_intv) then + ! turn off accumulation + duration_time = current_time + alarms(b) % repeat_interval ! repeat + else + duration_rem = alarms(b) % repeat_interval - duration_rem ! repeat + duration_time = current_time + duration_rem ! remainder of repeat + end if + else + duration_time = when + duration_n = -1 + end if + + ! set next repeat time and flag + when = alarms(b) % start_time + alarms(b) % repeat_interval + if (current_time >= when) then + elapsed = current_time - when + call mpas_interval_division(when, elapsed, & + alarms(b) % repeat_interval, repeat_n, repeat_rem) + + if (repeat_rem == zero_intv) then + repeat_time = current_time + alarms(b) % repeat_interval + else + repeat_rem = alarms(b) % repeat_interval - repeat_rem + repeat_time = current_time + repeat_rem + end if + else + repeat_time = when + repeat_n = -1 + end if + + ! accumulate now if in a window (both duration & repeat are untriggered) + if ((duration_n == repeat_n) .and. & + (series % buffers(b) % started_flag == 1)) then + series % buffers(b) % accumulate_flag = 1 + end if + + ! + ! set the reoccurring timers + ! + series % buffers(b) % duration_alarm_ID = trim(alarm_prefix) // & + trim(DURATION_ALARM_PREFIX) // trim(buf_identifier) + call mpas_add_clock_alarm(domain % clock, & + series % buffers(b) % duration_alarm_ID, & + duration_time, & ! duration sets the offset + alarms(b) % repeat_interval, ierr=err) ! but repeat is interval + + series % buffers(b) % repeat_alarm_ID = trim(alarm_prefix) // & + trim(REPEAT_ALARM_PREFIX) // trim(buf_identifier) + call mpas_add_clock_alarm(domain % clock, & + series % buffers(b) % repeat_alarm_ID, & + repeat_time, & + alarms(b) % repeat_interval, ierr=err) + + series % buffers(b) % reset_alarm_ID = trim(alarm_prefix) // & + trim(RESET_ALARM_PREFIX) // trim(buf_identifier) + call mpas_add_clock_alarm(domain % clock, & + series % buffers(b) % reset_alarm_ID, & + reset_time, & + alarms(b) % reset_interval, ierr=err) + end do +end subroutine set_alarms + + + +!*********************************************************************** +! routine walk_string +! +!> \brief Walk a semicolon delimited string to find substrings +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Walk a string delimited by semicolons and return the first substring +!> from start index, and modify start to point at the next candidate. +!----------------------------------------------------------------------- +subroutine walk_string(next, substr, ok)!{{{ + ! input variables + + ! input/output variables + character (len=StrKIND), intent(inout) :: next + + ! output variables + character (len=StrKIND), intent(out) :: substr + logical, intent(out) :: ok + + ! local variables + integer :: i + character (len=StrKIND) :: copy + + ! make a copy + copy = trim(next) + + ! if there's anything in it other than whitespace, pass through + i = verify(copy, ' ') + ok = i > 0 + if (.not. ok) then + return + end if + copy = trim(next(i:)) + + ! find the first semicolon and split + i = scan(copy, ';') + + ! return that substring and the remainder + if (i > 0) then + substr = trim(copy(1:i-1)) + next = trim(copy(i+1:)) + else + substr = trim(copy) + next = '' + end if + +end subroutine walk_string!}}} + + + +!*********************************************************************** +! routine set_times +! +!> \brief Set a list of times +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Walk a list of times delimited by spaces and set the time info +!> for the buffer structure so that alarms can be set. +!----------------------------------------------------------------------- +subroutine set_times(series, alarms, clock, which, config, ok, err) + ! input variables + integer, intent(in) :: which + character (len=StrKIND), pointer, intent(in) :: config + + ! input/output variables + type (time_series_type), intent(inout) :: series + type (MPAS_Clock_type), intent(inout) :: clock + type (time_series_alarms_type), dimension(:), intent(inout) :: alarms + + ! output variables + logical, intent(out) :: ok + integer, intent(out) :: err + + ! local variables + character (len=StrKIND) :: next, time + integer :: b + + ! find the first time in the list + next = config + b = 0 + call walk_string(next, time, ok) + + ! while the time string is ok + do while (ok) + ! exit if we went over + b = b + 1 + if (b > series % number_of_buffers) then + exit + end if + + ! set the time + if (which == START_TIMES) then + if (time == INITIAL_TIME_TOKEN) then + alarms(b) % start_time = & + mpas_get_clock_time(clock, MPAS_START_TIME, err) + else + call mpas_set_time(alarms(b) % start_time, & + dateTimeString=time, ierr=err) + end if + else if (which == DURATION_INTERVALS) then + if (time == REPEAT_INTERVAL_TOKEN) then + alarms(b) % duration_interval = alarms(b) % repeat_interval + else + call mpas_set_timeInterval(alarms(b) % duration_interval, & + timeString=time, ierr=err) + end if + else if (which == REPEAT_INTERVALS) then + if (time == RESET_INTERVAL_TOKEN) then + alarms(b) % repeat_interval = alarms(b) % reset_interval + else + call mpas_set_timeInterval(alarms(b) % repeat_interval, & + timeString=time, ierr=err) + end if + else + call mpas_set_timeInterval(alarms(b) % reset_interval, & + timeString=time, ierr=err) + end if + + ! get the next time string + call walk_string(next, time, ok) + end do + + ! only ok if we parsed out as many as there are number of buffers + ok = series % number_of_buffers == b + end subroutine set_times + + + +!*********************************************************************** +! routine add_new_field +! +!> \brief Function to create a new field from an existing field +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This routine conducts all initializations required for +!> duplicating a field and adding it to the allFields pool. +!----------------------------------------------------------------------- +subroutine add_new_field(info, inname, outname, all_fields, amPool)!{{{ + ! input variables + type (mpas_pool_field_info_type), intent(in) :: info + character (len=StrKIND), intent(in) :: inname, outname + + ! input/output variables + type (mpas_pool_type), intent(inout) :: all_fields, amPool + + ! output variables + + ! local variables + + ! duplicate field and add new field to pool + if (info % nDims == 0) then + call copy_field_0r(inname, all_fields, amPool, outname) + else if (info % nDims == 1) then + call copy_field_1r(inname, all_fields, amPool, outname) + else if (info % nDims == 2) then + call copy_field_2r(inname, all_fields, amPool, outname) + else if (info % nDims == 3) then + call copy_field_3r(inname, all_fields, amPool, outname) + else if (info % nDims == 4) then + call copy_field_4r(inname, all_fields, amPool, outname) + else if (info % nDims == 5) then + call copy_field_5r(inname, all_fields, amPool, outname) + else + call mpas_log_write( & + 'the impossible happened - tried to copy a real field "' // & + trim(inname) // '" that does not have 0-5 dimensionality', MPAS_LOG_CRIT) + end if + +end subroutine add_new_field!}}} + + + +!*********************************************************************** +! routine timer_checking +! +!> \brief Timer functions to determine when to run +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This routine conducts timer checking to determine if it +!> needs to run at this particular time. +!----------------------------------------------------------------------- +subroutine timer_checking(series, clock, err)!{{{ + ! input variables + + ! input/output variables + type (time_series_type), intent(inout) :: series + type (mpas_clock_type), intent(inout) :: clock + + ! output variables + integer, intent(out) :: err + + ! local variables + integer :: b + + ! start procedure + err = 0 + + do b = 1, series % number_of_buffers + ! clear any resets + if (series % buffers(b) % reset_flag == 1) then + if (series % buffers(b) % accumulate_flag == 1) then + series % buffers(b) % reset_flag = 0 + end if + end if + + ! see if the started alarm is ringing + if (trim(series % buffers(b) % start_alarm_ID) /= '') then + if (mpas_is_alarm_ringing(clock, & + series % buffers(b) % start_alarm_ID, ierr=err)) then + call mpas_reset_clock_alarm(clock, & + series % buffers(b) % start_alarm_ID, ierr=err) + series % buffers(b) % started_flag = 1 + series % buffers(b) % reset_flag = 1 + series % buffers(b) % accumulate_flag = 1 + + series % buffers(b) % start_alarm_ID = '' + end if + end if + + ! if we aren't started, cycle to next buffer + if (series % buffers(b) % started_flag == 0) then + cycle + end if + + ! check various other alarms + ! see if we need to reset + if(mpas_is_alarm_ringing(clock, & + series % buffers(b) % reset_alarm_ID, ierr=err)) then + call mpas_reset_clock_alarm(clock, & + series % buffers(b) % reset_alarm_ID, ierr=err) + series % buffers(b) % reset_flag = 1 + end if + + ! turn off accumulation + ! + ! duration needs to be >= 2 * compute_interval + ! (a series can only be 2 or more) + if (mpas_is_alarm_ringing(clock, & + series % buffers(b) % duration_alarm_ID, ierr=err)) then + call mpas_reset_clock_alarm(clock, & + series % buffers(b) % duration_alarm_ID, ierr=err) + series % buffers(b) % accumulate_flag = 0 + end if + + ! turn on accumulation + ! (this is second, in case the duration and repeat + ! overlaps on the same timer) + if (mpas_is_alarm_ringing(clock, & + series % buffers(b) % repeat_alarm_ID, ierr=err)) then + call mpas_reset_clock_alarm(clock, & + series % buffers(b) % repeat_alarm_ID, ierr=err) + series % buffers(b) % accumulate_flag = 1 + end if + + end do +end subroutine timer_checking!}}} + + + +!*********************************************************************** +! routine typed_operate +! +!> \brief Do the operation, but switch on run-time type +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> Since we don't know the type of the array, we need to do some +!> run-time type switching based on the type of the array. +!----------------------------------------------------------------------- +subroutine typed_operate(block, variable, buffers, operation)!{{{ + ! input variables + type (block_type), pointer, intent(in) :: block + integer, intent(in) :: operation + type (time_series_variable_type), intent(in) :: variable + type (time_series_buffer_type), dimension(:), intent(in) :: buffers + + ! input/output variables + + ! output variables + + ! local variables + type (mpas_pool_field_info_type) :: info + + ! get the info + call mpas_pool_get_field_info(block % allFields, variable % input_name, info) + + ! switch based on the type, dimensionality, and operation + if (info % nDims == 0) then + if (operation == AVG_OP) then + call operate0r_avg(block, variable, buffers) + else if (operation == MIN_OP) then + call operate0r_min(block, variable, buffers) + else if (operation == MAX_OP) then + call operate0r_max(block, variable, buffers) + else if (operation == SUM_OP) then + call operate0r_sum(block, variable, buffers) + else if (operation == SOS_OP) then + call operate0r_sos(block, variable, buffers) + else + call mpas_log_write( & + 'the impossible happened - tried to operate with an ' // & + 'unknown operator in the time series stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 1) then + if (operation == AVG_OP) then + call operate1r_avg(block, variable, buffers) + else if (operation == MIN_OP) then + call operate1r_min(block, variable, buffers) + else if (operation == MAX_OP) then + call operate1r_max(block, variable, buffers) + else if (operation == SUM_OP) then + call operate1r_sum(block, variable, buffers) + else if (operation == SOS_OP) then + call operate1r_sos(block, variable, buffers) + else + call mpas_log_write( & + 'the impossible happened - tried to operate with an ' // & + 'unknown operator in the time series stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 2) then + if (operation == AVG_OP) then + call operate2r_avg(block, variable, buffers) + else if (operation == MIN_OP) then + call operate2r_min(block, variable, buffers) + else if (operation == MAX_OP) then + call operate2r_max(block, variable, buffers) + else if (operation == SUM_OP) then + call operate2r_sum(block, variable, buffers) + else if (operation == SOS_OP) then + call operate2r_sos(block, variable, buffers) + else + call mpas_log_write( & + 'the impossible happened - tried to operate with an ' // & + 'unknown operator in the time series stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 3) then + if (operation == AVG_OP) then + call operate3r_avg(block, variable, buffers) + else if (operation == MIN_OP) then + call operate3r_min(block, variable, buffers) + else if (operation == MAX_OP) then + call operate3r_max(block, variable, buffers) + else if (operation == SUM_OP) then + call operate3r_sum(block, variable, buffers) + else if (operation == SOS_OP) then + call operate3r_sos(block, variable, buffers) + else + call mpas_log_write( & + 'the impossible happened - tried to operate with an ' // & + 'unknown operator in the time series stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 4) then + if (operation == AVG_OP) then + call operate4r_avg(block, variable, buffers) + else if (operation == MIN_OP) then + call operate4r_min(block, variable, buffers) + else if (operation == MAX_OP) then + call operate4r_max(block, variable, buffers) + else if (operation == SUM_OP) then + call operate4r_sum(block, variable, buffers) + else if (operation == SOS_OP) then + call operate4r_sos(block, variable, buffers) + else + call mpas_log_write( & + 'The impossible happened - tried to operate with an ' // & + 'unknown operator in the time series stats AM', MPAS_LOG_CRIT) + end if + else if (info % nDims == 5) then + if (operation == AVG_OP) then + call operate5r_avg(block, variable, buffers) + else if (operation == MIN_OP) then + call operate5r_min(block, variable, buffers) + else if (operation == MAX_OP) then + call operate5r_max(block, variable, buffers) + else if (operation == SUM_OP) then + call operate5r_sum(block, variable, buffers) + else if (operation == SOS_OP) then + call operate5r_sos(block, variable, buffers) + else + call mpas_log_write( & + 'The impossible happened - tried to operate with an ' // & + 'unknown operator in the time series stats AM', MPAS_LOG_CRIT) + end if + else + call mpas_log_write( & + 'The impossible happened - tried to operate on a real field "' // & + trim(variable % input_name) // '" that does not have 0-5 ' // & + 'dimensionality in the time series stats AM', MPAS_LOG_CRIT) + end if + +end subroutine typed_operate!}}} + + + +!*********************************************************************** +! routine copy_field_X +! +!> \brief Functions to create a new field from an existing field +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> This routine conducts initializations required for +!> duplicating a field and adding it to the allFields pool based on type. +!----------------------------------------------------------------------- + +subroutine copy_field_0r(inname, all_fields, amPool, outname)!{{{ +#include "time_series_inc/copy_field_1.inc" + type (field0DReal), pointer :: src, dst +#include "time_series_inc/copy_field_2.inc" +end subroutine copy_field_0r!}}} + +subroutine copy_field_1r(inname, all_fields, amPool, outname)!{{{ +#include "time_series_inc/copy_field_1.inc" + type (field1DReal), pointer :: src, dst +#include "time_series_inc/copy_field_2.inc" +end subroutine copy_field_1r!}}} + +subroutine copy_field_2r(inname, all_fields, amPool, outname)!{{{ +#include "time_series_inc/copy_field_1.inc" + type (field2DReal), pointer :: src, dst +#include "time_series_inc/copy_field_2.inc" +end subroutine copy_field_2r!}}} + +subroutine copy_field_3r(inname, all_fields, amPool, outname)!{{{ +#include "time_series_inc/copy_field_1.inc" + type (field3DReal), pointer :: src, dst +#include "time_series_inc/copy_field_2.inc" +end subroutine copy_field_3r!}}} + +subroutine copy_field_4r(inname, all_fields, amPool, outname)!{{{ +#include "time_series_inc/copy_field_1.inc" + type (field4DReal), pointer :: src, dst +#include "time_series_inc/copy_field_2.inc" +end subroutine copy_field_4r!}}} + +subroutine copy_field_5r(inname, all_fields, amPool, outname)!{{{ +#include "time_series_inc/copy_field_1.inc" + type (field5DReal), pointer :: src, dst +#include "time_series_inc/copy_field_2.inc" +end subroutine copy_field_5r!}}} + + + +!*********************************************************************** +! routine operateX_Y +! +!> \brief Series of subroutines to support operations on run-time types +!> \author Jon Woodring +!> \date September 1, 2015 +!> \details +!> These subroutines encapsulate the different operations that can occur +!> based on the run-time types. (This would likely be +!> instantiated generics/templates in other languages.) +!> +!> Averaging is done by multiplying out and dividing such that +!> the average state is always in a normalized form -- while +!> this could (will) cause more error in the long run, it does +!> mean that other AMs will be able to use this data and it will +!> always be prenormalized (it also means that we don't have to +!> have a special case of normalizing the data before writing it +!> to disk). +!----------------------------------------------------------------------- +subroutine operate0r_avg (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = (out_array * & + (buffers(b) % counter - 1) + in_array) & + / buffers(b) % counter +#include "time_series_inc/operate_3.inc" +end subroutine operate0r_avg + +subroutine operate1r_avg (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = (out_array * & + (buffers(b) % counter - 1) + in_array) & + / buffers(b) % counter +#include "time_series_inc/operate_3.inc" +end subroutine operate1r_avg + +subroutine operate2r_avg (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = (out_array * & + (buffers(b) % counter - 1) + in_array) & + / buffers(b) % counter +#include "time_series_inc/operate_3.inc" +end subroutine operate2r_avg + +subroutine operate3r_avg (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = (out_array * & + (buffers(b) % counter - 1) + in_array) & + / buffers(b) % counter +#include "time_series_inc/operate_3.inc" +end subroutine operate3r_avg + +subroutine operate4r_avg (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = (out_array * & + (buffers(b) % counter - 1) + in_array) & + / buffers(b) % counter +#include "time_series_inc/operate_3.inc" +end subroutine operate4r_avg + +subroutine operate5r_avg (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = (out_array * & + (buffers(b) % counter - 1) + in_array) & + / buffers(b) % counter +#include "time_series_inc/operate_3.inc" +end subroutine operate5r_avg + +subroutine operate0r_min (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = min(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate0r_min + +subroutine operate1r_min (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = min(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate1r_min + +subroutine operate2r_min (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = min(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate2r_min + +subroutine operate3r_min (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = min(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate3r_min + +subroutine operate4r_min (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = min(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate4r_min + +subroutine operate5r_min (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = min(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate5r_min + +subroutine operate0r_max (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = max(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate0r_max + +subroutine operate1r_max (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = max(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate1r_max + +subroutine operate2r_max (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = max(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate2r_max + +subroutine operate3r_max (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = max(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate3r_max + +subroutine operate4r_max (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = max(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate4r_max + +subroutine operate5r_max (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = max(out_array, in_array) +#include "time_series_inc/operate_3.inc" +end subroutine operate5r_max + +subroutine operate0r_sum (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = out_array + in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate0r_sum + +subroutine operate1r_sum (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = out_array + in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate1r_sum + +subroutine operate2r_sum (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = out_array + in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate2r_sum + +subroutine operate3r_sum (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = out_array + in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate3r_sum + +subroutine operate4r_sum (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = out_array + in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate4r_sum + +subroutine operate5r_sum (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array, out_array +#include "time_series_inc/operate_2.inc" + out_array = out_array + in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate5r_sum + +subroutine operate0r_sos (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), pointer :: in_array, out_array +! note sum of squares has a different operate_2 inc +! because it has to square the input to initialize itself +#include "time_series_inc/operate_2_sos.inc" + out_array = out_array + in_array * in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate0r_sos + +subroutine operate1r_sos (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:), pointer :: in_array, out_array +! note sum of squares has a different operate_2 inc +! because it has to square the input to initialize itself +#include "time_series_inc/operate_2_sos.inc" + out_array = out_array + in_array * in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate1r_sos + +subroutine operate2r_sos (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :), pointer :: in_array, out_array +! note sum of squares has a different operate_2 inc +! because it has to square the input to initialize itself +#include "time_series_inc/operate_2_sos.inc" + out_array = out_array + in_array * in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate2r_sos + +subroutine operate3r_sos (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :), pointer :: in_array, out_array +! note sum of squares has a different operate_2 inc +! because it has to square the input to initialize itself +#include "time_series_inc/operate_2_sos.inc" + out_array = out_array + in_array * in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate3r_sos + +subroutine operate4r_sos (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :), pointer :: in_array, out_array +! note sum of squares has a different operate_2 inc +! because it has to square the input to initialize itself +#include "time_series_inc/operate_2_sos.inc" + out_array = out_array + in_array * in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate4r_sos + +subroutine operate5r_sos (start_block, variable, buffers) +#include "time_series_inc/operate_1.inc" + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: in_array, out_array +! note sum of squares has a different operate_2 inc +! because it has to square the input to initialize itself +#include "time_series_inc/operate_2_sos.inc" + out_array = out_array + in_array * in_array +#include "time_series_inc/operate_3.inc" +end subroutine operate5r_sos + +end module ocn_time_series_stats +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_transect_transport.F b/src/core_ocean/analysis_members/mpas_ocn_transect_transport.F new file mode 100644 index 0000000000..0d78fe79cf --- /dev/null +++ b/src/core_ocean/analysis_members/mpas_ocn_transect_transport.F @@ -0,0 +1,422 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_transect_transport +! +!> \brief MPAS ocean analysis mode member: transect_transport +!> \author Mark Petersen +!> \date April 2016 +!> \details +!> MPAS ocean analysis mode member: transect_transport +!> +!----------------------------------------------------------------------- + +module ocn_transect_transport + + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + use mpas_timekeeping + use mpas_stream_manager + + use ocn_constants + use ocn_diagnostics_routines + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_transect_transport, & + ocn_compute_transect_transport, & + ocn_restart_transect_transport, & + ocn_finalize_transect_transport + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_transect_transport +! +!> \brief Initialize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date April 2016 +!> \details +!> This routine conducts all initializations required for the +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_transect_transport(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: transectTransportAMPool + type (mpas_pool_type), pointer :: meshPool + + type (mpas_pool_type), pointer :: transectPool + + type (mpas_pool_type), pointer :: transectTransportAM + + integer, pointer :: nTransects + integer :: iTransect + + integer, dimension(:), pointer :: transectEdgeMasksMax + integer, dimension(:,:), pointer :: transectEdgeMasks + + err = 0 + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nTransects', nTransects) + call mpas_pool_get_subpool(domain % blocklist % structs, 'transectTransportAM', transectTransportAMPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'transects', transectPool) + + call mpas_pool_get_array(transectTransportAMPool,'transectEdgeMasksMax',transectEdgeMasksMax) + + transectEdgeMasksMax(:) = 0 + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'transectTransportAM', transectTransportAMPool) + + call mpas_pool_get_array(transectPool,'transectEdgeMasks',transectEdgeMasks) + do iTransect = 1,nTransects + !currentTransect = transectsInGroup(iTransect, transectGroupNumber) + transectEdgeMasksMax(iTransect) = max(transectEdgeMasksMax(iTransect),maxval(transectEdgeMasks(iTransect,:))) + end do + + block => block % next + end do + + end subroutine ocn_init_transect_transport!}}} + +!*********************************************************************** +! +! routine ocn_compute_transect_transport +! +!> \brief Compute MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date April 2016 +!> \details +!> This routine conducts all computation required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_transect_transport(domain, timeLevel, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: transectTransportAMPool + type (dm_info) :: dminfo + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: verticalMeshPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: transectTransportAM + + type (mpas_pool_type), pointer :: transectPool + + integer :: currentTransect, transectGroupNumber, transectsInAddGroup, i + integer, dimension(:,:), pointer :: transectsInGroup + character (len=STRKIND), dimension(:), pointer :: transectNames, transectGroupNames + integer, dimension(:), pointer :: nTransectsInGroup + integer, pointer :: nTransectGroups, maxTransectsInGroup + character (len=STRKIND), pointer :: additionalTransect + + ! Here are some example variables which may be needed for your analysis member + integer, pointer :: nVertLevels, nEdgesSolve, num_tracers, nTransects + integer :: k, iEdge, iTransect, nTransportVariables, c1,c2 + integer, dimension(:), pointer :: maxLevelEdgeTop, transectEdgeMasksMax + integer, dimension(:,:), pointer :: transectEdgeMasks, transectEdgeMaskSigns, cellsOnEdge + + real (kind=RKIND) :: m3ps_to_Sv + real (kind=RKIND), dimension(:), pointer :: dvEdge, transectVolumeTransport,refLayerThickness + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, normalVelocity, transectVolumeTransportZ + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + real (kind=RKIND), dimension(:,:,:), allocatable :: sumTransport, totalSumTransport + + err = 0 + + dminfo = domain % dminfo + + ! Only computing volume transport right now. Could add heat, tracer transport later. + nTransportVariables = 1 + + m3ps_to_Sv = 1e-6; ! m^3/sec flux to Sverdrups + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nTransects',nTransects) + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nTransectGroups', nTransectGroups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'maxTransectsInGroup', maxTransectsInGroup) + call mpas_pool_get_config(domain % configs, 'config_AM_transectTransport_transect_group', & + additionalTransect) + call mpas_pool_get_subpool(domain % blocklist % structs, 'transects', transectPool) + call mpas_pool_get_array(transectPool, 'transectsInGroup', transectsInGroup) + call mpas_pool_get_array(transectPool, 'nTransectsInGroup', nTransectsInGroup) + call mpas_pool_get_array(transectPool, 'transectNames', transectNames) + call mpas_pool_get_array(transectPool, 'transectGroupNames', transectGroupNames) + do i = 1, nTransectGroups + if (transectGroupNames(i) .eq. additionalTransect) then + transectGroupNumber = i + end if + end do + + transectsInAddGroup = nTransectsInGroup(transectGroupNumber) + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevels', nVertLevels) + + allocate(sumTransport(nVertLevels,nTransects,nTransportVariables)) + allocate(totalSumTransport(nVertLevels,nTransects,nTransportVariables)) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) + !call mpas_pool_get_subpool(block % structs, 'tracersPool', tracersPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'transectTransportAM', transectTransportAMPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'transects', transectPool) + + ! Here are some example variables which may be needed for your analysis member + call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) + + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(block % dimensions, 'nTransects', nTransects) + + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness',refLayerThickness); + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 1) + !call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + + call mpas_pool_get_array(transectPool,'transectEdgeMasks',transectEdgeMasks) + call mpas_pool_get_array(transectPool,'transectEdgeMaskSigns',transectEdgeMaskSigns) + + call mpas_pool_get_array(transectTransportAMPool,'transectEdgeMasksMax',transectEdgeMasksMax) + + sumTransport = 0.0_RKIND + + do iTransect = 1,transectsInAddGroup + currentTransect = transectsInGroup(iTransect, transectGroupNumber) + if (transectEdgeMasksMax(currentTransect)==0) cycle + do iEdge = 1,nEdgesSolve + if (transectEdgeMasks(currentTransect,iEdge)==0) cycle + c1 = cellsOnEdge(1,iEdge) + c2 = cellsOnEdge(2,iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + sumTransport(k,iTransect,1) = sumTransport(k,iTransect,1) + & + transectEdgeMaskSigns(currentTransect,iEdge) & + * normalVelocity(k,iEdge)*dvEdge(iEdge) & + * 0.5_RKIND*(layerThickness(k,c1) + layerThickness(k,c2))*m3ps_to_Sv + end do + end do + end do + + block => block % next + end do + + ! mpi summation over all processors + call mpas_dmpar_sum_real_array(dminfo, nVertLevels*nTransects*nTransportVariables, & + sumTransport, totalSumTransport) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'transectTransportAM', transectTransportAMPool) + call mpas_pool_get_array(transectTransportAMPool,'transectVolumeTransport',transectVolumeTransport) + call mpas_pool_get_array(transectTransportAMPool,'transectVolumeTransportZ',transectVolumeTransportZ) + + do iTransect = 1,nTransects + transectVolumeTransportZ(:,iTransect) = totalSumTransport(:,iTransect,1) + transectVolumeTransport(iTransect) = sum(transectVolumeTransportZ(:,iTransect)) + end do + + deallocate(sumTransport, totalSumTransport) + + end subroutine ocn_compute_transect_transport!}}} + +!*********************************************************************** +! +! routine ocn_restart_transect_transport +! +!> \brief Save restart for MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date April 2016 +!> \details +!> This routine conducts computation required to save a restart state +!> for the MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_restart_transect_transport(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_restart_transect_transport!}}} + +!*********************************************************************** +! +! routine ocn_finalize_transect_transport +! +!> \brief Finalize MPAS-Ocean analysis member +!> \author Mark Petersen +!> \date April 2016 +!> \details +!> This routine conducts all finalizations required for this +!> MPAS-Ocean analysis member. +! +!----------------------------------------------------------------------- + + subroutine ocn_finalize_transect_transport(domain, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + end subroutine ocn_finalize_transect_transport!}}} + +end module ocn_transect_transport + +! vim: foldmethod=marker diff --git a/src/core_ocean/analysis_members/mpas_ocn_water_mass_census.F b/src/core_ocean/analysis_members/mpas_ocn_water_mass_census.F index 423b4b8a8c..679a835ddd 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_water_mass_census.F +++ b/src/core_ocean/analysis_members/mpas_ocn_water_mass_census.F @@ -10,8 +10,8 @@ ! ocn_water_mass_census ! !> \brief MPAS ocean analysis member: water mass census -!> \author Todd Ringler -!> \date May 12, 2015 +!> \author Todd Ringler, Anne Berres +!> \date Mar 2, 2017 !> \details !> MPAS ocean analysis member: water_mass_census !> This analysis member sorts the ocean water volume based on it @@ -113,8 +113,8 @@ end subroutine ocn_init_water_mass_census!}}} ! routine ocn_compute_water_mass_census ! !> \brief Compute MPAS-Ocean analysis member -!> \author Todd Ringler -!> \date May 10, 2015 +!> \author Todd Ringler, Anne Berres +!> \date Mar 2, 2017 !> \details !> This routine conducts all computation required for this !> MPAS-Ocean analysis member. @@ -159,6 +159,8 @@ subroutine ocn_compute_water_mass_census(domain, timeLevel, err)!{{{ type (mpas_pool_type), pointer :: statePool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: regionPool real (kind=RKIND), dimension(:,:,:), pointer :: waterMassFractionalDistribution real (kind=RKIND), dimension(:,:,:), pointer :: potentialDensityOfTSDiagram @@ -166,11 +168,18 @@ subroutine ocn_compute_water_mass_census(domain, timeLevel, err)!{{{ real (kind=RKIND), dimension(:,:), pointer :: waterMassCensusTemperatureValues real (kind=RKIND), dimension(:,:), pointer :: waterMassCensusSalinityValues + ! region mask version of variables + real (kind=RKIND), dimension(:,:,:), pointer :: waterMassFractionalDistributionRegion + real (kind=RKIND), dimension(:,:,:), pointer :: potentialDensityOfTSDiagramRegion + real (kind=RKIND), dimension(:,:,:), pointer :: zPositionOfTSDiagramRegion + real (kind=RKIND), dimension(:,:), pointer :: waterMassCensusTemperatureValuesRegion + real (kind=RKIND), dimension(:,:), pointer :: waterMassCensusSalinityValuesRegion + ! pointers to data in pools required for T/S water mass census real (kind=RKIND), dimension(:,:), pointer :: layerThickness - real (kind=RKIND), dimension(:,:,:), pointer :: tracers real (kind=RKIND), dimension(:,:), pointer :: potentialDensity real (kind=RKIND), dimension(:,:), pointer :: zMid + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers ! pointers to data in mesh pool ! (note: nOceanRegionsTmpCensus, lonCell, latCell to be removed when region mask is intent(in)) @@ -180,6 +189,7 @@ subroutine ocn_compute_water_mass_census(domain, timeLevel, err)!{{{ integer, pointer :: nTemperatureBinsP1, nSalinityBinsP1 integer, dimension(:), pointer :: maxLevelCell real (kind=RKIND), dimension(:), pointer :: areaCell, lonCell, latCell + logical, pointer :: predefRegions ! local variables integer :: iCell, iRegion, iLevel, iTracer, iTemperatureBin, iSalinityBin, err_tmp @@ -187,9 +197,6 @@ subroutine ocn_compute_water_mass_census(domain, timeLevel, err)!{{{ real (kind=RKIND), pointer :: minSalinity, maxSalinity real (kind=RKIND) :: deltaTemperature, deltaSalinity, temperature, salinity, density, zPosition, volume - ! package flag - logical, pointer :: waterMassCensusAMPKGActive - ! buffers data for message passaging integer :: kBuffer, kBufferLength real (kind=RKIND), dimension(:), allocatable :: workBufferSum, workBufferSumReduced @@ -197,80 +204,181 @@ subroutine ocn_compute_water_mass_census(domain, timeLevel, err)!{{{ real (kind=RKIND), dimension(:), allocatable :: workMask real (kind=RKIND), dimension(:,:), allocatable :: regionMask + !!! region file variables + integer :: curRegion, regionGroupOffset, regionGroupNumber, regionsInAddGroup, i, j +! character (len=STRKIND) :: currentName + character (len=STRKIND), dimension(:), pointer :: regionNames, regionGroupNames + integer, dimension(:, :), pointer :: regionCellMasks, regionVertexMasks, regionsInGroup + integer, dimension(:), pointer :: nRegionsInGroup + integer, pointer :: nRegions, nRegionGroups, maxRegionsInGroup + character (len=STRKIND), pointer :: additionalRegion + + ! buffers data for message passaging + integer :: kBufferRegion, kBufferLengthRegion + real (kind=RKIND), dimension(:), allocatable :: workBufferSumRegion, workBufferSumReducedRegion + !!! end region file variables + + !!! general initialization ! assume no error err = 0 ! set highest level pointer dminfo = domain % dminfo + ! region config info for WMC + call mpas_pool_get_config(domain % configs, 'config_AM_waterMassCensus_compute_predefined_regions', & + predefRegions) + call mpas_pool_get_config(domain % configs, 'config_AM_waterMassCensus_region_group', & + additionalRegion) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'waterMassCensusAM', waterMassCensusAMPool) + ! find the number of regions, number of data fields and number of vertical levels - call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nOceanRegionsTmpCensus', nOceanRegionsTmpCensus) call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nTemperatureBins', nTemperatureBins) call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nSalinityBins', nSalinityBins) call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nTemperatureBinsP1', nTemperatureBinsP1) call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nSalinityBinsP1', nSalinityBinsP1) - ! allocate buffer for message passing - kBuffer=0 - kBufferLength=3*nOceanRegionsTmpCensus*nTemperatureBins*nSalinityBins - allocate(workBufferSum(kBufferLength)) - allocate(workBufferSumReduced(kBufferLength)) - workBufferSum=0.0_RKIND - workBufferSumReduced=0.0_RKIND - - ! all code below will go away when regionMask is intent(in) - ! allocate region mask and fill array - call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nCells', nCells) - allocate(workMask(nCells)) - allocate(regionMask(nOceanRegionsTmpCensus,nCells)) - block => domain % blocklist - do while (associated(block)) - ! get pointers to pools - call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_array(meshPool, 'lonCell', lonCell) - call mpas_pool_get_array(meshPool, 'latCell', latCell) - do iRegion=1,nOceanRegionsTmpCensus - call compute_mask(maxLevelCell, nCells, nCellsSolve, iRegion, lonCell, latCell, workMask) - regionMask(iRegion,:) = workMask(:) - enddo - block => block % next - enddo - ! all code above will go away when regionMask is intent(in) - - ! get pointers to analysis member arrays - call mpas_pool_get_subpool(domain % blocklist % structs, 'waterMassCensusAM', waterMassCensusAMPool) - call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassCensusTemperatureValues', waterMassCensusTemperatureValues) - call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassCensusSalinityValues', waterMassCensusSalinityValues) - call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassFractionalDistribution', waterMassFractionalDistribution) - call mpas_pool_get_array(waterMassCensusAMPool, 'potentialDensityOfTSDiagram', potentialDensityOfTSDiagram) - call mpas_pool_get_array(waterMassCensusAMPool, 'zPositionOfTSDiagram', zPositionOfTSDiagram) - ! get run-time configure variables call mpas_pool_get_config(domain % configs, 'config_AM_waterMassCensus_minTemperature', minTemperature) call mpas_pool_get_config(domain % configs, 'config_AM_waterMassCensus_maxTemperature', maxTemperature) call mpas_pool_get_config(domain % configs, 'config_AM_waterMassCensus_minSalinity', minSalinity) call mpas_pool_get_config(domain % configs, 'config_AM_waterMassCensus_maxSalinity', maxSalinity) - do iRegion=1,nOceanRegionsTmpCensus - ! compute temperature and salinity domains - ! (note: the ability to have different t/s domains for different regions is not yet built out) - deltaTemperature = (maxTemperature-minTemperature)/nTemperatureBins - do iTemperatureBin=1,nTemperatureBinsP1 - waterMassCensusTemperatureValues(iTemperatureBin,iRegion) = minTemperature + deltaTemperature*(iTemperatureBin-1) - enddo - deltaSalinity = (maxSalinity-minSalinity)/nSalinityBins - do iSalinityBin=1,nSalinityBinsP1 - waterMassCensusSalinityValues(iSalinityBin,iRegion) = minSalinity + deltaSalinity*(iSalinityBin-1) - enddo - enddo ! iRegion - - ! initialize intent(out) of this analysis member - waterMassFractionalDistribution(:,:,:)=0.0_RKIND - potentialDensityOfTSDiagram(:,:,:)=0.0_RKIND - zPositionOfTSDiagram(:,:,:)=0.0_RKIND + if (predefRegions .eqv. .true.) then + !!! hard-wired regions init + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nOceanRegionsTmpCensus', nOceanRegionsTmpCensus) + ! allocate buffer for message passing + kBuffer=0 + kBufferLength=3*nOceanRegionsTmpCensus*nTemperatureBins*nSalinityBins + allocate(workBufferSum(kBufferLength)) + allocate(workBufferSumReduced(kBufferLength)) + workBufferSum=0.0_RKIND + workBufferSumReduced=0.0_RKIND + + ! all code below will go away when regionMask is intent(in) + ! allocate region mask and fill array + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nCells', nCells) + allocate(workMask(nCells)) + allocate(regionMask(nOceanRegionsTmpCensus,nCells)) + block => domain % blocklist + do while (associated(block)) + ! get pointers to pools + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + do iRegion=1,nOceanRegionsTmpCensus + call compute_mask(maxLevelCell, nCells, nCellsSolve, iRegion, lonCell, latCell, workMask) + regionMask(iRegion,:) = workMask(:) + enddo + block => block % next + enddo + ! all code above will go away when regionMask is intent(in) + + ! get pointers to analysis member arrays + call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassCensusTemperatureValues', waterMassCensusTemperatureValues) + call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassCensusSalinityValues', waterMassCensusSalinityValues) + call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassFractionalDistribution', waterMassFractionalDistribution) + call mpas_pool_get_array(waterMassCensusAMPool, 'potentialDensityOfTSDiagram', potentialDensityOfTSDiagram) + call mpas_pool_get_array(waterMassCensusAMPool, 'zPositionOfTSDiagram', zPositionOfTSDiagram) + !!! end hard-wired regions init + + !!! hard-wired regions compute temperature and salinity + do iRegion=1,nOceanRegionsTmpCensus + ! compute temperature and salinity domains + ! (note: the ability to have different t/s domains for different regions is not yet built out) + deltaTemperature = (maxTemperature-minTemperature)/nTemperatureBins + do iTemperatureBin=1,nTemperatureBinsP1 + waterMassCensusTemperatureValues(iTemperatureBin,iRegion) = minTemperature + deltaTemperature*(iTemperatureBin-1) + enddo + deltaSalinity = (maxSalinity-minSalinity)/nSalinityBins + do iSalinityBin=1,nSalinityBinsP1 + waterMassCensusSalinityValues(iSalinityBin,iRegion) = minSalinity + deltaSalinity*(iSalinityBin-1) + enddo + enddo ! iRegion + + ! initialize intent(out) of this analysis member + waterMassFractionalDistribution(:,:,:)=0.0_RKIND + potentialDensityOfTSDiagram(:,:,:)=0.0_RKIND + zPositionOfTSDiagram(:,:,:)=0.0_RKIND + endif + + if (additionalRegion /= '') then + !!! region file init + ! if a region file is given and a region is selected, set up region file version + ! region file dimensions + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegions', nRegions) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nRegionGroups', nRegionGroups) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'maxRegionsInGroup', maxRegionsInGroup) + + ! get region file dimensions + call mpas_pool_get_subpool(domain % blocklist % structs, 'regions', regionPool) + call mpas_pool_get_array(regionPool, 'regionsInGroup', regionsInGroup) + call mpas_pool_get_array(regionPool, 'nRegionsInGroup', nRegionsInGroup) + call mpas_pool_get_array(regionPool, 'regionNames', regionNames) + call mpas_pool_get_array(regionPool, 'regionGroupNames', regionGroupNames) + + ! region file variables + call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassFractionalDistributionRegion', & + waterMassFractionalDistributionRegion) + call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassCensusTemperatureValuesRegion', & + waterMassCensusTemperatureValuesRegion) + call mpas_pool_get_array(waterMassCensusAMPool, 'waterMassCensusSalinityValuesRegion', & + waterMassCensusSalinityValuesRegion) + call mpas_pool_get_array(waterMassCensusAMPool, 'potentialDensityOfTSDiagramRegion', & + potentialDensityOfTSDiagramRegion) + call mpas_pool_get_array(waterMassCensusAMPool, 'zPositionOfTSDiagramRegion', & + zPositionOfTSDiagramRegion) + + regionGroupOffset = 1 + regionGroupNumber = 0 + + ! region preliminaries + ! figure out the region group number that matches the configured additional region's name + do i = 1, nRegionGroups + if (regionGroupNames(i) .eq. additionalRegion) then + regionGroupNumber = i + ! determine offset to compensate for several region groups in the + ! regions file + do j = 1, i - 1 + regionGroupOffset = regionGroupOffset + nRegionsInGroup(j) + enddo + endif + enddo + + regionsInAddGroup = nRegionsInGroup(regionGroupNumber) + + ! allocate buffer for message passing in region file + kBufferRegion=0 + kBufferLengthRegion = 3*regionsInAddGroup*nTemperatureBins*nSalinityBins + allocate(workBufferSumRegion(kBufferLengthRegion)) + allocate(workBufferSumReducedRegion(kBufferLengthRegion)) + workBufferSumRegion(:) = 0.0_RKIND + workBufferSumReducedRegion(:) = 0.0_RKIND + !!! end region init + + !!! region file compute temperature and salinity + do curRegion = 1, regionsInAddGroup + deltaTemperature = (maxTemperature-minTemperature)/nTemperatureBins + do iTemperatureBin=1,nTemperatureBinsP1 + waterMassCensusTemperatureValuesRegion(iTemperatureBin,curRegion) = minTemperature + & + deltaTemperature*(iTemperatureBin-1) + enddo + deltaSalinity = (maxSalinity-minSalinity)/nSalinityBins + do iSalinityBin=1,nSalinityBinsP1 + waterMassCensusSalinityValuesRegion(iSalinityBin,curRegion) = minSalinity + deltaSalinity*(iSalinityBin-1) + enddo + enddo + + ! initialize region file versions + waterMassFractionalDistributionRegion(:,:,:)=0.0_RKIND + potentialDensityOfTSDiagramRegion(:,:,:)=0.0_RKIND + zPositionOfTSDiagramRegion(:,:,:)=0.0_RKIND + endif + ! loop over blocks block => domain % blocklist @@ -279,13 +387,15 @@ subroutine ocn_compute_water_mass_census(domain, timeLevel, err)!{{{ call mpas_pool_get_subpool(block % structs, 'state', statePool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + ! get indices for T and S + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) ! get pointers to mesh call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_dimension(block % dimensions, 'nOceanRegionsTmpCensus', nOceanRegionsTmpCensus) - call mpas_pool_get_dimension(statePool, 'index_temperature', index_temperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', index_salinity) call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) @@ -293,115 +403,220 @@ subroutine ocn_compute_water_mass_census(domain, timeLevel, err)!{{{ call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', potentialDensity) call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + + if (additionalRegion == '') then + call mpas_pool_get_dimension(block % dimensions, 'nOceanRegionsTmpCensus', nOceanRegionsTmpCensus) + else + call mpas_pool_get_array(regionPool, 'regionCellMasks', regionCellMasks) + endif ! loop over and bin all data + if ( associated(activeTracers) ) then do iCell=1,nCellsSolve - do iLevel=1,maxLevelCell(iCell) - - ! make copies of data for convienence - temperature = tracers(index_temperature,iLevel,iCell) - salinity = tracers(index_salinity,iLevel,iCell) - density = potentialDensity(iLevel,iCell) - zPosition = zMid(iLevel,iCell) - volume = layerThickness(iLevel,iCell) * areaCell(iCell) - - ! find temperature bin, cycle if bin is out of range - iTemperatureBin = int((temperature-minTemperature)/deltaTemperature) + 1 - if (iTemperatureBin < 1) cycle - if (iTemperatureBin > nTemperatureBins) cycle - - ! find salinity bin, cycle if bin is out of range - iSalinityBin = int((salinity-minSalinity)/deltaSalinity) + 1 - if (iSalinityBin < 1) cycle - if (iSalinityBin > nSalinityBins) cycle - - do iRegion=1,nOceanRegionsTmpCensus - ! add volume into water mass census array for each region - waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) = & - waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) & - + volume * regionMask(iRegion,iCell) - potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & - potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) & - + density * volume * regionMask(iRegion,iCell) - zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & - zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) & - + zPosition * volume * regionMask(iRegion,iCell) - enddo + do iLevel=1,maxLevelCell(iCell) + ! make copies of data for convienence + temperature = activeTracers(index_temperature,iLevel,iCell) + salinity = activeTracers(index_salinity,iLevel,iCell) + density = potentialDensity(iLevel,iCell) + zPosition = zMid(iLevel,iCell) + volume = layerThickness(iLevel,iCell) * areaCell(iCell) + + ! find temperature bin, cycle if bin is out of range + iTemperatureBin = int((temperature-minTemperature)/deltaTemperature) + 1 + if (iTemperatureBin < 1) cycle + if (iTemperatureBin > nTemperatureBins) cycle + + ! find salinity bin, cycle if bin is out of range + iSalinityBin = int((salinity-minSalinity)/deltaSalinity) + 1 + if (iSalinityBin < 1) cycle + if (iSalinityBin > nSalinityBins) cycle + + if (predefRegions .eqv. .true.) then + !!! hard-wired regions compute + do iRegion=1,nOceanRegionsTmpCensus + ! add volume into water mass census array for each region + waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) = & + waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) & + + volume * regionMask(iRegion,iCell) + potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & + potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) & + + density * volume * regionMask(iRegion,iCell) + zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & + zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) & + + zPosition * volume * regionMask(iRegion,iCell) + enddo + endif + + !!! region file compute + if (additionalRegion /= '') then + do i=1,regionsInAddGroup + curRegion = regionsInGroup(i, regionGroupNumber) + ! add volume into water mass census array for each region + waterMassFractionalDistributionRegion(iTemperatureBin,iSalinityBin,curRegion) = & + waterMassFractionalDistributionRegion(iTemperatureBin,iSalinityBin,curRegion) & + + volume * regionCellMasks(curRegion, iCell) + potentialDensityOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) = & + potentialDensityOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) & + + density * volume * regionCellMasks(curRegion, iCell) + zPositionOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) = & + zPositionOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) & + + zPosition * volume * regionCellMasks(curRegion, iCell) + enddo + endif + enddo ! iLevel + enddo ! iCell + endif ! associated(activeTracers) + block => block % next + enddo ! block loop + + if (predefRegions .eqv. .true.) then + !!! hard-wired regions efficient computed + ! store data in buffer in order to allow only one dmpar calls + kBuffer=0 + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do iRegion=1,nOceanRegionsTmpCensus + kBuffer = kBuffer+1 + workBufferSum(kBuffer) = waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) + kBuffer = kBuffer+1 + workBufferSum(kBuffer) = potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) + kBuffer = kBuffer+1 + workBufferSum(kBuffer) = zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) + enddo + enddo + enddo - enddo ! iLevel - enddo ! iCell + ! communication + call mpas_dmpar_sum_real_array(dminfo, kBufferLength, workBufferSum, workBufferSumReduced ) + + ! unpack the buffer into intent(out) of this analysis member + kBuffer=0 + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do iRegion=1,nOceanRegionsTmpCensus + kBuffer = kBuffer+1 + waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) = workBufferSumReduced(kBuffer) + kBuffer = kBuffer+1 + potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = workBufferSumReduced(kBuffer) + kBuffer = kBuffer+1 + zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = workBufferSumReduced(kBuffer) + enddo + enddo + enddo - block => block % next - end do ! block loop + ! normalize potentialDensityOfTSDiagram by volume in each T,S bin + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do iRegion=1,nOceanRegionsTmpCensus + potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & + potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) / & + max(waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion), 1.0e-8_RKIND) + zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & + zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) / & + max(waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion), 1.0e-8_RKIND) + enddo + enddo + enddo + ! use workBufferSum as workspace to find total volume for each region + workBufferSum = 0.0_RKIND + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do iRegion=1,nOceanRegionsTmpCensus + workBufferSum(iRegion) = workBufferSum(iRegion) & + + waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) + enddo + enddo + enddo + + ! use this sum to convert waterMassFractionalDistribution from total volume to fractional volume + do iRegion=1,nOceanRegionsTmpCensus + waterMassFractionalDistribution(:,:,iRegion) = waterMassFractionalDistribution(:,:,iRegion) & + / max(workBufferSum(iRegion), 1.0e-8_RKIND) + enddo + !!! end hard-wired version + + ! deallocate buffers + deallocate(workBufferSum) + deallocate(workBufferSumReduced) + deallocate(regionMask) + deallocate(workMask) + endif + + !!!region file version efficient computed ! store data in buffer in order to allow only one dmpar calls - kBuffer=0 - do iTemperatureBin=1,nTemperatureBins - do iSalinityBin=1,nSalinityBins - do iRegion=1,nOceanRegionsTmpCensus - kBuffer = kBuffer+1 - workBufferSum(kBuffer) = waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) - kBuffer = kBuffer+1 - workBufferSum(kBuffer) = potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) - kBuffer = kBuffer+1 - workBufferSum(kBuffer) = zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) - enddo - enddo - enddo + if (additionalRegion /= '') then + kBufferRegion=0 + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do curRegion=1,regionsInAddGroup + kBufferRegion = kBufferRegion+1 + workBufferSumRegion(kBufferRegion) = & + waterMassFractionalDistributionRegion(iTemperatureBin,iSalinityBin,curRegion) + kBufferRegion = kBufferRegion+1 + workBufferSumRegion(kBufferRegion) = potentialDensityOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) + kBufferRegion = kBufferRegion+1 + workBufferSumRegion(kBufferRegion) = zPositionOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) + enddo + enddo + enddo - ! communication - call mpas_dmpar_sum_real_array(dminfo, kBufferLength, workBufferSum, workBufferSumReduced ) - - ! unpack the buffer into intent(out) of this analysis member - kBuffer=0 - do iTemperatureBin=1,nTemperatureBins - do iSalinityBin=1,nSalinityBins - do iRegion=1,nOceanRegionsTmpCensus - kBuffer = kBuffer+1 - waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) = workBufferSumReduced(kBuffer) - kBuffer = kBuffer+1 - potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = workBufferSumReduced(kBuffer) - kBuffer = kBuffer+1 - zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = workBufferSumReduced(kBuffer) - enddo - enddo - enddo + ! communication + call mpas_dmpar_sum_real_array(dminfo, kBufferLengthRegion, workBufferSumRegion, workBufferSumReducedRegion) + + ! unpack the buffer into intent(out) of this analysis member + kBufferRegion=0 + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do curRegion=1,regionsInAddGroup + kBufferRegion = kBufferRegion+1 + waterMassFractionalDistributionRegion(iTemperatureBin,iSalinityBin,curRegion) = & + workBufferSumReducedRegion(kBufferRegion) + kBufferRegion = kBufferRegion+1 + potentialDensityOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) = & + workBufferSumReducedRegion(kBufferRegion) + kBufferRegion = kBufferRegion+1 + zPositionOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) = & + workBufferSumReducedRegion(kBufferRegion) + enddo + enddo + enddo - ! normalize potentialDensityOfTSDiagram by volume in each T,S bin - do iTemperatureBin=1,nTemperatureBins - do iSalinityBin=1,nSalinityBins - do iRegion=1,nOceanRegionsTmpCensus - potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & - potentialDensityOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) / & - max(waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion), 1.0e-8_RKIND) - zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) = & - zPositionOfTSDiagram(iTemperatureBin,iSalinityBin,iRegion) / & - max(waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion), 1.0e-8_RKIND) - enddo - enddo - enddo + ! normalize potentialDensityOfTSDiagram by volume in each T,S bin + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do curRegion=1,regionsInAddGroup + potentialDensityOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) = & + potentialDensityOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) / & + max(waterMassFractionalDistributionRegion(iTemperatureBin,iSalinityBin,curRegion), 1.0e-8_RKIND) + zPositionOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) = & + zPositionOfTSDiagramRegion(iTemperatureBin,iSalinityBin,curRegion) / & + max(waterMassFractionalDistributionRegion(iTemperatureBin,iSalinityBin,curRegion), 1.0e-8_RKIND) + enddo + enddo + enddo - ! use workBufferSum as workspace to find total volume for each region - workBufferSum = 0.0_RKIND - do iTemperatureBin=1,nTemperatureBins - do iSalinityBin=1,nSalinityBins - do iRegion=1,nOceanRegionsTmpCensus - workBufferSum(iRegion) = workBufferSum(iRegion) + waterMassFractionalDistribution(iTemperatureBin,iSalinityBin,iRegion) - enddo - enddo - enddo + ! use workBufferSum as workspace to find total volume for each region + workBufferSumRegion = 0.0_RKIND + do iTemperatureBin=1,nTemperatureBins + do iSalinityBin=1,nSalinityBins + do curRegion=1,regionsInAddGroup + workBufferSumRegion(curRegion) = workBufferSumRegion(curRegion) & + + waterMassFractionalDistributionRegion(iTemperatureBin,iSalinityBin,curRegion) + enddo + enddo + enddo - ! use this sum to convert waterMassFractionalDistribution from total volume to fractional volume - do iRegion=1,nOceanRegionsTmpCensus - waterMassFractionalDistribution(:,:,iRegion) = waterMassFractionalDistribution(:,:,iRegion) / max(workBufferSum(iRegion),1.0e-8_RKIND) - enddo + do curRegion=1,regionsInAddGroup + waterMassFractionalDistributionRegion(:,:,curRegion) = waterMassFractionalDistributionRegion(:,:,curRegion) & + / max(workBufferSumRegion(curRegion), 1.0e-8_RKIND) + enddo - ! deallocate buffers - deallocate(workBufferSum) - deallocate(workBufferSumReduced) - deallocate(regionMask) - deallocate(workMask) + ! deallocate buffers + deallocate(workBufferSumRegion) + deallocate(workBufferSumReducedRegion) + endif contains @@ -418,7 +633,7 @@ subroutine compute_mask(maxLevelCell, nCells, nCellsSolve, iRegion, lonCell, lat integer :: iCell real(kind=RKIND) :: dtr - dtr = 4.0*atan(1.0) / 180.0_RKIND + dtr = 4.0_RKIND*atan(1.0_RKIND) / 180.0_RKIND workMask(:) = 0.0_RKIND do iCell=1,nCellsSolve workMask(iCell) = 1.0_RKIND @@ -427,52 +642,52 @@ subroutine compute_mask(maxLevelCell, nCells, nCellsSolve, iRegion, lonCell, lat if (iRegion.eq.1) then ! Arctic do iCell=1,nCellsSolve - if(latCell(iCell).lt. 60.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).lt. 60.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo - write(6,*) ' Arctic ', sum(workMask) +! print *, ' Arctic ', sum(workMask) elseif (iRegion.eq.2) then ! Equatorial do iCell=1,nCellsSolve - if(latCell(iCell).gt. 15.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt.-15.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).gt. 15.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).lt.-15.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo - write(6,*) ' Equatorial ', sum(workMask) +! print *, ' Equatorial ', sum(workMask) elseif (iRegion.eq.3) then ! Southern Ocean do iCell=1,nCellsSolve - if(latCell(iCell).gt.-50.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).gt.-50.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo - write(6,*) ' Southern Ocean ', sum(workMask) +! print *, ' Southern Ocean ', sum(workMask) elseif (iRegion.eq.4) then ! Nino 3 do iCell=1,nCellsSolve - if(latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).lt.210.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).gt.270.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (lonCell(iCell).lt.210.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (lonCell(iCell).gt.270.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo - write(6,*) ' Nino 3 ', sum(workMask) +! print *, ' Nino 3 ', sum(workMask) elseif (iRegion.eq.5) then ! Nino 4 do iCell=1,nCellsSolve - if(latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).lt.160.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).gt.210.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (lonCell(iCell).lt.160.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (lonCell(iCell).gt.210.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo - write(6,*) ' Nino 4 ', sum(workMask) +! print *, ' Nino 4 ', sum(workMask) elseif (iRegion.eq.6) then ! Nino 3.4 do iCell=1,nCellsSolve - if(latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).lt.190.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND - if(lonCell(iCell).gt.240.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).gt. 5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (latCell(iCell).lt. -5.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (lonCell(iCell).lt.190.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND + if (lonCell(iCell).gt.240.0_RKIND*dtr) workMask(iCell) = 0.0_RKIND enddo - write(6,*) ' Nino 3.4 ', sum(workMask) +! print *, ' Nino 3.4 ', sum(workMask) else ! global (do nothing!) - write(6,*) ' Global ', sum(workMask) +! print *, ' Global ', sum(workMask) endif end subroutine compute_mask diff --git a/src/core_ocean/analysis_members/mpas_ocn_zonal_mean.F b/src/core_ocean/analysis_members/mpas_ocn_zonal_mean.F index 47e101ee2f..2ad6f1dd86 100644 --- a/src/core_ocean/analysis_members/mpas_ocn_zonal_mean.F +++ b/src/core_ocean/analysis_members/mpas_ocn_zonal_mean.F @@ -60,59 +60,6 @@ module ocn_zonal_mean contains -!*********************************************************************** -! -! routine ocn_setup_packages_zonal_mean -! -!> \brief Set up packages for MPAS-Ocean analysis member -!> \author Mark Petersen -!> \date November 2013 -!> \details -!> This routine is intended to configure the packages for this MPAS -!> ocean analysis member -! -!----------------------------------------------------------------------- - - subroutine ocn_setup_packages_zonal_mean(configPool, packagePool, err)!{{{ - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - type (mpas_pool_type), intent(in) :: configPool - type (mpas_pool_type), intent(in) :: packagePool - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - logical, pointer :: zonalMeanAMActive - - err = 0 - - call mpas_pool_get_package(packagePool, 'zonalMeanAMActive', zonalMeanAMActive) - - ! turn on package for this analysis member - zonalMeanAMActive = .true. - - end subroutine ocn_setup_packages_zonal_mean!}}} - !*********************************************************************** ! ! routine ocn_init_zonal_mean @@ -211,7 +158,7 @@ subroutine ocn_init_zonal_mean(domain, err)!{{{ call mpas_dmpar_min_real_array(dminfo, 1, minBin, minBinDomain) call mpas_dmpar_max_real_array(dminfo, 1, maxBin, maxBinDomain) - ! Set up bins. + ! Set up bins. binBoundaryZonalMean = -1.0e34_RKIND binCenterZonalMean = -1.0e34_RKIND @@ -294,16 +241,17 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: scratchPool type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: tracersPool integer :: iTracer, k, iCell, kMax integer :: iBin, iField, nZonalMeanVariables - integer, pointer :: num_tracers, nCellsSolve, nVertLevels, nZonalMeanBins + integer, pointer :: num_activeTracers, nCellsSolve, nVertLevels, nZonalMeanBins integer, dimension(:), pointer :: maxLevelCell real (kind=RKIND), dimension(:), pointer :: areaCell, binVariable, binBoundaryZonalMean real (kind=RKIND), dimension(:,:), pointer :: velocityZonal, velocityMeridional real (kind=RKIND), dimension(:,:), pointer :: velocityZonalZonalMean, velocityMeridionalZonalMean - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers real (kind=RKIND), dimension(:,:,:), allocatable :: sumZonalMean, totalSumZonalMean, normZonalMean real (kind=RKIND), dimension(:,:,:), pointer :: tracersZonalMean @@ -316,8 +264,10 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) - call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) - nZonalMeanVariables = num_tracers + 3 + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(tracersPool, 'num_activeTracers', num_activeTracers) + nZonalMeanVariables = num_activeTracers + 3 call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nZonalMeanBins', nZonalMeanBins) call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevels', nVertLevels) @@ -336,6 +286,7 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) !state => block % state % time_levs(timeLevel) % state @@ -343,7 +294,7 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ !scratch => block % scratch !diagnostics => block % diagnostics - call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) + call mpas_pool_get_dimension(tracersPool, 'num_activeTracers', num_activeTracers) call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) @@ -351,7 +302,7 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) call mpas_pool_get_array(diagnosticsPool, 'velocityZonal', velocityZonal) call mpas_pool_get_array(diagnosticsPool, 'velocityMeridional', velocityMeridional) @@ -376,11 +327,12 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ ! Field 1 is the total area in this bin, which can vary by level due to land. sumZonalMean(1,k,iBin) = sumZonalMean(1,k,iBin) + areaCell(iCell) - do iField = 1,num_tracers - sumZonalMean(iField+1,k,iBin) = sumZonalMean(iField+1,k,iBin) + tracers(iField,k,iCell)*areaCell(iCell) + do iField = 1,num_activeTracers + sumZonalMean(iField+1,k,iBin) = sumZonalMean(iField+1,k,iBin) + activeTracers(iField,k,iCell) & + * areaCell(iCell) enddo - iField = num_tracers+2 + iField = num_activeTracers+2 sumZonalMean(iField,k,iBin) = sumZonalMean(iField,k,iBin) + velocityZonal(k,iCell)*areaCell(iCell) iField = iField+1 sumZonalMean(iField,k,iBin) = sumZonalMean(iField,k,iBin) + velocityMeridional(k,iCell)*areaCell(iCell) @@ -424,8 +376,9 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ call mpas_pool_get_subpool(block % structs, 'zonalMeanAM', zonalMeanAMPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) - call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) + call mpas_pool_get_dimension(tracersPool, 'num_activeTracers', num_activeTracers) call mpas_pool_get_array(zonalMeanAMPool, 'tracersZonalMean', tracersZonalMean) call mpas_pool_get_array(zonalMeanAMPool, 'velocityZonalZonalMean', velocityZonalZonalMean) @@ -434,11 +387,11 @@ subroutine ocn_compute_zonal_mean(domain, timeLevel, err)!{{{ do iBin = 1, nZonalMeanBins do k = 1, nVertLevels - do iField = 1, num_tracers + do iField = 1, num_activeTracers tracersZonalMean(iField,k,iBin) = normZonalMean(iField+1,k,iBin) enddo - iField = num_tracers + 2 + iField = num_activeTracers + 2 velocityZonalZonalMean(k,iBin) = normZonalMean(iField,k,iBin) iField = iField+1 velocityMeridionalZonalMean(k,iBin) = normZonalMean(iField,k,iBin) diff --git a/src/core_ocean/analysis_members/regional_stats_inc/README.md b/src/core_ocean/analysis_members/regional_stats_inc/README.md new file mode 100644 index 0000000000..e7fe6ed386 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/README.md @@ -0,0 +1 @@ +These are #include files used in mpas_ocn_regional_stats.F. diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d.F new file mode 100644 index 0000000000..43b23dd941 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d.F @@ -0,0 +1,56 @@ + character (len=StrKIND), intent(in) :: inname, outname, elem_name + type (mpas_pool_type), pointer, intent(inout) :: inpool, outpool, all_fields + integer, intent(out), optional :: has_vertical + character (len=StrKIND), intent(in), optional :: vertical_dim + integer :: i + +! 1 -> 2 + + if (mpas_threading_get_thread_num() == 0) then + ! get the field for this pool + call mpas_pool_get_field(inpool, inname, src, 1) + + ! check that elem_name is in the dim list + if (.not. check_element_dim(src % dimNames, elem_name)) then + call mpas_dmpar_global_abort( & + trim(CURRENT_CORE_NAME) // ' ERROR: element dimension "' // & + elem_name // '" was not found in field "' // src % fieldName // & + ', which is required for the current regional stats AM' // & + 'configuration.') + end if + + ! allocate the linked list for the field blocks + allocate(dst) + nullify(dst % prev) + nullify(dst % next) + + ! copy field info + dst % fieldName = outname ! name is different than src + dst % isDecomposed = .false. ! NOT decomposed! reducing across space + + dst % block => src % block + dst % isVarArray = src % isVarArray + dst % defaultValue = src % defaultValue + dst % isActive = src % isActive + dst % hasTimeDimension = src % hasTimeDimension + dst % sendList => src % sendList + dst % recvList => src % recvList + dst % copyList => src % copyList + + allocate(dst % attLists(1)) + + ! no constituent names because dst is a scalar + + ! no isPersistent, dimNames, dimSizes because dst is scalar + + ! no need to allocate memory or loop over blocks, + ! as 0D has it already in dst % scalar + + ! add field to this block's pool + call mpas_pool_add_field(outpool, dst % fieldName, dst) + call mpas_pool_add_field(all_fields, dst % fieldName, dst) + + if (present(has_vertical)) then + has_vertical = 0 + end if + end if diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d_1.inc new file mode 100644 index 0000000000..288e1dbe0a --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d_1.inc @@ -0,0 +1,7 @@ + character (len=StrKIND), intent(in) :: inname, outname, elem_name + type (mpas_pool_type), pointer, intent(inout) :: inpool, outpool, all_fields + integer, intent(out), optional :: has_vertical + character (len=StrKIND), intent(in), optional :: vertical_dim + integer :: i + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d_2.inc new file mode 100644 index 0000000000..f9a0d9755f --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_1d_2.inc @@ -0,0 +1,50 @@ +! 1 -> 2 + + if (mpas_threading_get_thread_num() == 0) then + ! get the field for this pool + call mpas_pool_get_field(inpool, inname, src, 1) + + ! check that elem_name is in the dim list + if (.not. check_element_dim(src % dimNames, elem_name)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' ERROR: element dimension "' // & + elem_name // '" was not found in field "' // src % fieldName // & + ', which is required for the current regional stats AM' // & + 'configuration.') + end if + + ! allocate the linked list for the field blocks + allocate(dst) + nullify(dst % prev) + nullify(dst % next) + + ! copy field info + dst % fieldName = outname ! name is different than src + dst % isDecomposed = .false. ! NOT decomposed! reducing across space + + dst % block => src % block + dst % isVarArray = src % isVarArray + dst % defaultValue = src % defaultValue + dst % isActive = src % isActive + dst % hasTimeDimension = src % hasTimeDimension + dst % sendList => src % sendList + dst % recvList => src % recvList + dst % copyList => src % copyList + + allocate(dst % attLists(1)) + + ! no constituent names because dst is a scalar + + ! no isPersistent, dimNames, dimSizes because dst is scalar + + ! no need to allocate memory or loop over blocks, + ! as 0D has it already in dst % scalar + + ! add field to this block's pool + call mpas_pool_add_field(outpool, dst % fieldName, dst) + call mpas_pool_add_field(all_fields, dst % fieldName, dst) + + if (present(has_vertical)) then + has_vertical = 0 + end if + end if diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd.F new file mode 100644 index 0000000000..cea497047f --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd.F @@ -0,0 +1,80 @@ + character (len=StrKIND), intent(in) :: inname, outname, elem_name + type (mpas_pool_type), pointer, intent(inout) :: inpool, outpool, all_fields + integer, intent(out), optional :: has_vertical + character (len=StrKIND), intent(in), optional :: vertical_dim + integer :: i + +! 1 -> 2 + + if (mpas_threading_get_thread_num() == 0 ) then + ! get the field for this pool + call mpas_pool_get_field(inpool, inname, src, 1) + + ! check that elem_name is in the dim list + if (.not. check_element_dim(src % dimNames, elem_name)) then + call mpas_dmpar_global_abort( & + trim(CURRENT_CORE_NAME) // ' ERROR: element dimension "' // & + elem_name // '" was not found in field "' // src % fieldName // & + ', which is required for the current regional stats AM' // & + 'configuration.') + end if + + ! allocate the linked list for the field blocks + allocate(dst) + nullify(dst % prev) + nullify(dst % next) + + ! copy field info + dst % fieldName = outname ! has a different name + dst % isDecomposed = .false. ! NOT decomposed! reducing across space + + dst % block => src % block + dst % isVarArray = src % isVarArray + dst % defaultValue = src % defaultValue + dst % isActive = src % isActive + dst % hasTimeDimension = src % hasTimeDimension + dst % sendList => src % sendList + dst % recvList => src % recvList + dst % copyList => src % copyList + + ! copy constitutent names + if (associated(src % constituentNames)) then + allocate(dst % constituentNames( & + size(src % constituentNames, dim=1))) + allocate(dst % attLists(size(src % constituentNames, dim=1))) + + do i = 1, size(dst % constituentNames, dim=1) + dst % constituentNames(i) = trim(outname) // '_' // & + trim(src % constituentNames(i)) + end do + else + nullify(dst % constituentNames) + allocate(dst % attLists(1)) + end if + + ! field values unique to non-scalars (rank-1+ arrays) + dst % isPersistent = src % isPersistent + + i = size(src % dimNames) - 1 + dst % dimNames(1:i) = src % dimNames(1:i) + dst % dimSizes(1:i) = src % dimSizes(1:i) + + ! allocate memory + if (src % isActive) then + ! create dimensions skipping the element dimension + src_dims = shape(src % array) + +! 2 -> 3 + + else + nullify(dst % array) + end if + + ! add field to this block's pool + call mpas_pool_add_field(outpool, dst % fieldName, dst) + call mpas_pool_add_field(all_fields, dst % fieldName, dst) + + if (present(has_vertical)) then + has_vertical = check_vertical_dim(src % dimNames, vertical_dim) + end if + end if diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_1.inc new file mode 100644 index 0000000000..288e1dbe0a --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_1.inc @@ -0,0 +1,7 @@ + character (len=StrKIND), intent(in) :: inname, outname, elem_name + type (mpas_pool_type), pointer, intent(inout) :: inpool, outpool, all_fields + integer, intent(out), optional :: has_vertical + character (len=StrKIND), intent(in), optional :: vertical_dim + integer :: i + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_2.inc new file mode 100644 index 0000000000..b8955f8861 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_2.inc @@ -0,0 +1,61 @@ +! 1 -> 2 + + if (mpas_threading_get_thread_num() == 0 ) then + ! get the field for this pool + call mpas_pool_get_field(inpool, inname, src, 1) + + ! check that elem_name is in the dim list + if (.not. check_element_dim(src % dimNames, elem_name)) then + call mpas_log_write( & + trim(CURRENT_CORE_NAME) // ' ERROR: element dimension "' // & + elem_name // '" was not found in field "' // src % fieldName // & + ', which is required for the current regional stats AM' // & + 'configuration.') + end if + + ! allocate the linked list for the field blocks + allocate(dst) + nullify(dst % prev) + nullify(dst % next) + + ! copy field info + dst % fieldName = outname ! has a different name + dst % isDecomposed = .false. ! NOT decomposed! reducing across space + + dst % block => src % block + dst % isVarArray = src % isVarArray + dst % defaultValue = src % defaultValue + dst % isActive = src % isActive + dst % hasTimeDimension = src % hasTimeDimension + dst % sendList => src % sendList + dst % recvList => src % recvList + dst % copyList => src % copyList + + ! copy constitutent names + if (associated(src % constituentNames)) then + allocate(dst % constituentNames( & + size(src % constituentNames, dim=1))) + allocate(dst % attLists(size(src % constituentNames, dim=1))) + + do i = 1, size(dst % constituentNames, dim=1) + dst % constituentNames(i) = trim(outname) // '_' // & + trim(src % constituentNames(i)) + end do + else + nullify(dst % constituentNames) + allocate(dst % attLists(1)) + end if + + ! field values unique to non-scalars (rank-1+ arrays) + dst % isPersistent = src % isPersistent + + i = size(src % dimNames) - 1 + dst % dimNames(1:i) = src % dimNames(1:i) + dst % dimSizes(1:i) = src % dimSizes(1:i) + + ! allocate memory + if (src % isActive) then + ! create dimensions skipping the element dimension + src_dims = shape(src % array) + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_3.inc new file mode 100644 index 0000000000..9eb350b29e --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_field_nd_3.inc @@ -0,0 +1,14 @@ +! 2 -> 3 + + else + nullify(dst % array) + end if + + ! add field to this block's pool + call mpas_pool_add_field(outpool, dst % fieldName, dst) + call mpas_pool_add_field(all_fields, dst % fieldName, dst) + + if (present(has_vertical)) then + has_vertical = check_vertical_dim(src % dimNames, vertical_dim) + end if + end if diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg.F new file mode 100644 index 0000000000..ebaec4f087 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg.F @@ -0,0 +1,41 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 + + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors and divide by total + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + if (count_array > 0) then + if (regions % function_oned == ID_FUNC) then + out_array = out_array / count_array + else + out_array = out_array / weight_total + end if + end if + + deallocate(reduced) + deallocate(flattened) + + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d.F new file mode 100644 index 0000000000..b4cdb0ee35 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d.F @@ -0,0 +1,33 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 + + end do + end if + + block => block % next + end do + + ! sum across processors and divide by total + call mpas_dmpar_sum_real(dminfo, out_array, flat_real) + out_array = flat_real + if (count_array > 0) then + if (regions % function_oned == ID_FUNC) then + out_array = out_array / count_array + else + out_array = out_array / weight_total + end if + end if + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_1.inc new file mode 100644 index 0000000000..e8e3785511 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_1.inc @@ -0,0 +1,5 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_2.inc new file mode 100644 index 0000000000..b9eb789bc1 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_2.inc @@ -0,0 +1,11 @@ +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_3.inc new file mode 100644 index 0000000000..e726ba9b20 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg1d_3.inc @@ -0,0 +1,19 @@ +! 2 -> 3 + + end do + end if + + block => block % next + end do + + ! sum across processors and divide by total + call mpas_dmpar_sum_real(dminfo, out_array, flat_real) + out_array = flat_real + if (count_array > 0) then + if (regions % function_oned == ID_FUNC) then + out_array = out_array / count_array + else + out_array = out_array / weight_total + end if + end if + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_1.inc new file mode 100644 index 0000000000..e8e3785511 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_1.inc @@ -0,0 +1,5 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_2.inc new file mode 100644 index 0000000000..b9eb789bc1 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_2.inc @@ -0,0 +1,11 @@ +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_3.inc new file mode 100644 index 0000000000..28d0b83cf4 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_avg_3.inc @@ -0,0 +1,27 @@ +! 2 -> 3 + + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors and divide by total + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + if (count_array > 0) then + if (regions % function_oned == ID_FUNC) then + out_array = out_array / count_array + else + out_array = out_array / weight_total + end if + end if + + deallocate(reduced) + deallocate(flattened) + + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max.F new file mode 100644 index 0000000000..b243e6ffb5 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max.F @@ -0,0 +1,20 @@ + do i = 1, solve + +! 1 -> 2 + + end do + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! max across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_max_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d.F new file mode 100644 index 0000000000..a899523216 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d.F @@ -0,0 +1,13 @@ + do i = 1, solve + +! 1 -> 2 + + end do + + block => block % next + end do + + ! max across processors + call mpas_dmpar_max_real(dminfo, out_array, flat_real) + out_array = flat_real + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d_1.inc new file mode 100644 index 0000000000..081ef3539b --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d_1.inc @@ -0,0 +1,3 @@ + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d_2.inc new file mode 100644 index 0000000000..6ca25029d6 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max1d_2.inc @@ -0,0 +1,11 @@ +! 1 -> 2 + + end do + + block => block % next + end do + + ! max across processors + call mpas_dmpar_max_real(dminfo, out_array, flat_real) + out_array = flat_real + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max_1.inc new file mode 100644 index 0000000000..081ef3539b --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max_1.inc @@ -0,0 +1,3 @@ + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max_2.inc new file mode 100644 index 0000000000..9d2f22400d --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_max_2.inc @@ -0,0 +1,18 @@ +! 1 -> 2 + + end do + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! max across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_max_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min.F new file mode 100644 index 0000000000..e837e0c811 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min.F @@ -0,0 +1,20 @@ + do i = 1, solve + +! 1 -> 2 + + end do + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! min across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_min_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d.F new file mode 100644 index 0000000000..4d1cbf6e20 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d.F @@ -0,0 +1,13 @@ + do i = 1, solve + +! 1 -> 2 + + end do + + block => block % next + end do + + ! min across processors + call mpas_dmpar_min_real(dminfo, out_array, flat_real) + out_array = flat_real + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d_1.inc new file mode 100644 index 0000000000..081ef3539b --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d_1.inc @@ -0,0 +1,3 @@ + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d_2.inc new file mode 100644 index 0000000000..328c0ab390 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min1d_2.inc @@ -0,0 +1,11 @@ +! 1 -> 2 + + end do + + block => block % next + end do + + ! min across processors + call mpas_dmpar_min_real(dminfo, out_array, flat_real) + out_array = flat_real + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min_1.inc new file mode 100644 index 0000000000..081ef3539b --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min_1.inc @@ -0,0 +1,3 @@ + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min_2.inc new file mode 100644 index 0000000000..ce65b5adf2 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_min_2.inc @@ -0,0 +1,18 @@ +! 1 -> 2 + + end do + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! min across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_min_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start.F new file mode 100644 index 0000000000..b72dc14ed9 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start.F @@ -0,0 +1,56 @@ + type (dm_info), pointer, intent(in) :: dminfo + type (block_type), pointer, intent(in) :: start_block + type (regional_type), intent(in) :: regions + type (regional_variable_type), intent(in) :: variable + + real (kind=RKIND), dimension(:), allocatable :: flattened, reduced + integer :: b, m, i, last + integer, pointer :: solve + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: amPool, maskPool + real (kind=RKIND), dimension(:), pointer :: weights + real (kind=RKIND), pointer :: weight_total + integer, dimension(:,:), pointer :: mask + integer :: flat_integer + real (kind=RKIND) :: flat_real + integer, pointer :: count_array + +! 1 -> 2 + + last = regions % num_regions_per(regions % group_index) + do b = 1, last + ! get the output array, which should be the first block + call mpas_pool_get_subpool(start_block % structs, & + REGIONAL_STATS_POOL, amPool) + call mpas_pool_get_array(amPool, variable % output_names(b), & + out_array, 1) + + m = regions % groups(b, regions % group_index) + + call mpas_pool_get_array(amPool, regions % count_zerod_names(b), & + count_array, 1) + if (regions % function_oned == MUL_FUNC) then + call mpas_pool_get_array(amPool, regions % weight_zerod_names(b), & + weight_total, 1) + end if + +! 2 -> 3 + + ! iterate over blocks + block => start_block + do while (associated(block)) + ! get the dimensions for this block + if (regions % region_element == CELL_REGION) then + call mpas_pool_get_dimension(block % dimensions, CELL_SOLVE, solve) + else + call mpas_pool_get_dimension(block % dimensions, VERTEX_SOLVE, solve) + end if + + ! get the subpools + call mpas_pool_get_subpool(block % structs, MASK_POOL_NAME, maskPool) + + ! get the arrays + call mpas_pool_get_array(block % allFields, & + variable % input_name, in_array, 1) + call mpas_pool_get_array(maskPool, regions % masking_field, mask, 1) + diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_1.inc new file mode 100644 index 0000000000..e4de1433d4 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_1.inc @@ -0,0 +1,18 @@ + type (dm_info), pointer, intent(in) :: dminfo + type (block_type), pointer, intent(in) :: start_block + type (regional_type), intent(in) :: regions + type (regional_variable_type), intent(in) :: variable + + real (kind=RKIND), dimension(:), allocatable :: flattened, reduced + integer :: b, m, i, last + integer, pointer :: solve + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: amPool, maskPool + real (kind=RKIND), dimension(:), pointer :: weights + real (kind=RKIND), pointer :: weight_total + integer, dimension(:,:), pointer :: mask + integer :: flat_integer + real (kind=RKIND) :: flat_real + integer, pointer :: count_array + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_2.inc new file mode 100644 index 0000000000..d440025d31 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_2.inc @@ -0,0 +1,20 @@ +! 1 -> 2 + + last = regions % num_regions_per(regions % group_index) + do b = 1, last + ! get the output array, which should be the first block + call mpas_pool_get_subpool(start_block % structs, & + REGIONAL_STATS_POOL, amPool) + call mpas_pool_get_array(amPool, variable % output_names(b), & + out_array, 1) + + m = regions % groups(b, regions % group_index) + + call mpas_pool_get_array(amPool, regions % count_zerod_names(b), & + count_array, 1) + if (regions % function_oned == MUL_FUNC) then + call mpas_pool_get_array(amPool, regions % weight_zerod_names(b), & + weight_total, 1) + end if + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_3.inc new file mode 100644 index 0000000000..f830eb5f5f --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_start_3.inc @@ -0,0 +1,20 @@ +! 2 -> 3 + + ! iterate over blocks + block => start_block + do while (associated(block)) + ! get the dimensions for this block + if (regions % region_element == CELL_REGION) then + call mpas_pool_get_dimension(block % dimensions, CELL_SOLVE, solve) + else + call mpas_pool_get_dimension(block % dimensions, VERTEX_SOLVE, solve) + end if + + ! get the subpools + call mpas_pool_get_subpool(block % structs, MASK_POOL_NAME, maskPool) + + ! get the arrays + call mpas_pool_get_array(block % allFields, & + variable % input_name, in_array, 1) + call mpas_pool_get_array(maskPool, regions % masking_field, mask, 1) + diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos.F new file mode 100644 index 0000000000..4ee4e32d0c --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos.F @@ -0,0 +1,34 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 + + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d.F new file mode 100644 index 0000000000..a7508b2677 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d.F @@ -0,0 +1,26 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 + + end do + end if + + block => block % next + end do + + ! sum across processors + call mpas_dmpar_sum_real(dminfo, out_array, flat_real) + out_array = flat_real + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_1.inc new file mode 100644 index 0000000000..e8e3785511 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_1.inc @@ -0,0 +1,5 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_2.inc new file mode 100644 index 0000000000..b9eb789bc1 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_2.inc @@ -0,0 +1,11 @@ +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_3.inc new file mode 100644 index 0000000000..8865d7ea72 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos1d_3.inc @@ -0,0 +1,12 @@ +! 2 -> 3 + + end do + end if + + block => block % next + end do + + ! sum across processors + call mpas_dmpar_sum_real(dminfo, out_array, flat_real) + out_array = flat_real + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_1.inc new file mode 100644 index 0000000000..e8e3785511 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_1.inc @@ -0,0 +1,5 @@ + ! no weights + if (regions % function_oned == ID_FUNC) then + do i = 1, solve + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_2.inc new file mode 100644 index 0000000000..b9eb789bc1 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_2.inc @@ -0,0 +1,11 @@ +! 1 -> 2 + + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_oned, weights, 1) + + do i = 1, solve + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_3.inc new file mode 100644 index 0000000000..3f247fc4ac --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_op_sumsos_3.inc @@ -0,0 +1,20 @@ +! 2 -> 3 + + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg.F new file mode 100644 index 0000000000..cc91f27894 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg.F @@ -0,0 +1,40 @@ + ! no weights + if (regions % function_twod == ID_FUNC) then + do i = 1, solve + do v = 1, levels + +! 1 -> 2 + + end do + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_twod, weights, 1) + + do i = 1, solve + do v = 1, levels + +! 2 -> 3 + + end do + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors (divide is outside) + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + +! 3 -> 4 + + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_1.inc new file mode 100644 index 0000000000..ad9dd707da --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_1.inc @@ -0,0 +1,6 @@ + ! no weights + if (regions % function_twod == ID_FUNC) then + do i = 1, solve + do v = 1, levels + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_2.inc new file mode 100644 index 0000000000..05562e3728 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_2.inc @@ -0,0 +1,13 @@ +! 1 -> 2 + + end do + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_twod, weights, 1) + + do i = 1, solve + do v = 1, levels + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_3.inc new file mode 100644 index 0000000000..a93822f9ee --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_3.inc @@ -0,0 +1,21 @@ +! 2 -> 3 + + end do + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors (divide is outside) + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + +! 3 -> 4 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_4.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_4.inc new file mode 100644 index 0000000000..781e2cc2a5 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_avg_4.inc @@ -0,0 +1,3 @@ +! 3 -> 4 + + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max.F new file mode 100644 index 0000000000..d226f8e2d8 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max.F @@ -0,0 +1,22 @@ + ! no weights + do i = 1, solve + do v = 1, levels + +! 1 -> 2 + + end do + end do + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! max across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_max_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max_1.inc new file mode 100644 index 0000000000..eae825b535 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max_1.inc @@ -0,0 +1,5 @@ + ! no weights + do i = 1, solve + do v = 1, levels + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max_2.inc new file mode 100644 index 0000000000..579d2393cc --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_max_2.inc @@ -0,0 +1,18 @@ +! 1 -> 2 + + end do + end do + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! max across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_max_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min.F new file mode 100644 index 0000000000..debc48741f --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min.F @@ -0,0 +1,22 @@ + ! no weights + do i = 1, solve + do v = 1, levels + +! 1 -> 2 + + end do + end do + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! min across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_min_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min_1.inc new file mode 100644 index 0000000000..eae825b535 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min_1.inc @@ -0,0 +1,5 @@ + ! no weights + do i = 1, solve + do v = 1, levels + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min_2.inc new file mode 100644 index 0000000000..af56f2979a --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_min_2.inc @@ -0,0 +1,18 @@ +! 1 -> 2 + + end do + end do + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! min across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_min_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start.F new file mode 100644 index 0000000000..f914991c65 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start.F @@ -0,0 +1,57 @@ + type (dm_info), pointer, intent(in) :: dminfo + type (block_type), pointer, intent(in) :: start_block + type (regional_type), intent(in) :: regions + type (regional_variable_type), intent(in) :: variable + integer, intent(in) :: levels + + real (kind=RKIND), dimension(:), allocatable :: flattened, reduced + integer :: b, m, i, last, v + integer, pointer :: solve + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: amPool, maskPool + real (kind=RKIND), dimension(:,:), pointer :: weights + real (kind=RKIND), dimension(:), pointer :: weight_total + integer, dimension(:,:), pointer :: mask, vertical_mask + integer, dimension(:), pointer :: count_array + integer, dimension(:), allocatable :: copy_count + +! 1 -> 2 + + last = regions % num_regions_per(regions % group_index) + do b = 1, last + ! get the output array, which should be the first block + call mpas_pool_get_subpool(start_block % structs, & + REGIONAL_STATS_POOL, amPool) + call mpas_pool_get_array(amPool, variable % output_names(b), & + out_array, 1) + + m = regions % groups(b, regions % group_index) + + call mpas_pool_get_array(amPool, regions % count_oned_names(b), & + count_array, 1) + if (regions % function_twod == MUL_FUNC) then + call mpas_pool_get_array(amPool, regions % weight_oned_names(b), & + weight_total, 1) + end if + +! 2 -> 3 + + ! iterate over blocks + block => start_block + do while (associated(block)) + ! get the dimensions for this block + if (regions % region_element == CELL_REGION) then + call mpas_pool_get_dimension(block % dimensions, CELL_SOLVE, solve) + else + call mpas_pool_get_dimension(block % dimensions, VERTEX_SOLVE, solve) + end if + + ! get the subpools + call mpas_pool_get_subpool(block % structs, MASK_POOL_NAME, maskPool) + + ! get the arrays + call mpas_pool_get_array(block % allFields, & + variable % input_name, in_array, 1) + call mpas_pool_get_array(maskPool, regions % masking_field, mask, 1) + call mpas_pool_get_array(block % allFields, regions % vertical_mask, & + vertical_mask, 1) diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_1.inc new file mode 100644 index 0000000000..57ab24b50f --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_1.inc @@ -0,0 +1,18 @@ + type (dm_info), pointer, intent(in) :: dminfo + type (block_type), pointer, intent(in) :: start_block + type (regional_type), intent(in) :: regions + type (regional_variable_type), intent(in) :: variable + integer, intent(in) :: levels + + real (kind=RKIND), dimension(:), allocatable :: flattened, reduced + integer :: b, m, i, last, v + integer, pointer :: solve + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: amPool, maskPool + real (kind=RKIND), dimension(:,:), pointer :: weights + real (kind=RKIND), dimension(:), pointer :: weight_total + integer, dimension(:,:), pointer :: mask, vertical_mask + integer, dimension(:), pointer :: count_array + integer, dimension(:), allocatable :: copy_count + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_2.inc new file mode 100644 index 0000000000..c524a53b6b --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_2.inc @@ -0,0 +1,20 @@ +! 1 -> 2 + + last = regions % num_regions_per(regions % group_index) + do b = 1, last + ! get the output array, which should be the first block + call mpas_pool_get_subpool(start_block % structs, & + REGIONAL_STATS_POOL, amPool) + call mpas_pool_get_array(amPool, variable % output_names(b), & + out_array, 1) + + m = regions % groups(b, regions % group_index) + + call mpas_pool_get_array(amPool, regions % count_oned_names(b), & + count_array, 1) + if (regions % function_twod == MUL_FUNC) then + call mpas_pool_get_array(amPool, regions % weight_oned_names(b), & + weight_total, 1) + end if + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_3.inc new file mode 100644 index 0000000000..995b5cff2f --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_start_3.inc @@ -0,0 +1,21 @@ +! 2 -> 3 + + ! iterate over blocks + block => start_block + do while (associated(block)) + ! get the dimensions for this block + if (regions % region_element == CELL_REGION) then + call mpas_pool_get_dimension(block % dimensions, CELL_SOLVE, solve) + else + call mpas_pool_get_dimension(block % dimensions, VERTEX_SOLVE, solve) + end if + + ! get the subpools + call mpas_pool_get_subpool(block % structs, MASK_POOL_NAME, maskPool) + + ! get the arrays + call mpas_pool_get_array(block % allFields, & + variable % input_name, in_array, 1) + call mpas_pool_get_array(maskPool, regions % masking_field, mask, 1) + call mpas_pool_get_array(block % allFields, regions % vertical_mask, & + vertical_mask, 1) diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos.F b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos.F new file mode 100644 index 0000000000..f3576c85cf --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos.F @@ -0,0 +1,37 @@ + ! no weights + if (regions % function_twod == ID_FUNC) then + do i = 1, solve + do v = 1, levels + +! 1 -> 2 + + end do + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_twod, weights, 1) + + do i = 1, solve + do v = 1, levels + +! 2 -> 3 + + end do + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_1.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_1.inc new file mode 100644 index 0000000000..ad9dd707da --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_1.inc @@ -0,0 +1,6 @@ + ! no weights + if (regions % function_twod == ID_FUNC) then + do i = 1, solve + do v = 1, levels + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_2.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_2.inc new file mode 100644 index 0000000000..05562e3728 --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_2.inc @@ -0,0 +1,13 @@ +! 1 -> 2 + + end do + end do + ! real weights + else + call mpas_pool_get_array(block % allFields, & + regions % weights_twod, weights, 1) + + do i = 1, solve + do v = 1, levels + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_3.inc b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_3.inc new file mode 100644 index 0000000000..8c7f53bebf --- /dev/null +++ b/src/core_ocean/analysis_members/regional_stats_inc/regional_opvert_sumsos_3.inc @@ -0,0 +1,20 @@ +! 2 -> 3 + + end do + end do + end if + + block => block % next + end do + + allocate(flattened(size(out_array))) + allocate(reduced(size(out_array))) + + ! sum across processors + flattened = reshape(out_array, shape(flattened)) + call mpas_dmpar_sum_real_array(dminfo, size(flattened), flattened, reduced) + out_array = reshape(reduced, shape(out_array)) + + deallocate(reduced) + deallocate(flattened) + end do diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d.F new file mode 100644 index 0000000000..b3e61bc8bf --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d.F @@ -0,0 +1,37 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field0DReal), pointer :: temp, temp_iter + type (field0DReal), pointer :: top_iter + type (field0DReal), pointer :: second_iter + real (kind=RKIND), pointer :: top + real (kind=RKIND), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d0, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d0 + second_iter => stack(stack_pointer - 1) % d0 + + ! do operation + top => top_iter % scalar + second => second_iter % scalar + temp_iter % scalar = & + +! 1-2 break + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d0) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d0) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d0 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 0 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d_1.inc new file mode 100644 index 0000000000..c8a587743d --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d_1.inc @@ -0,0 +1,22 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field0DReal), pointer :: temp, temp_iter + type (field0DReal), pointer :: top_iter + type (field0DReal), pointer :: second_iter + real (kind=RKIND), pointer :: top + real (kind=RKIND), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d0, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d0 + second_iter => stack(stack_pointer - 1) % d0 + + ! do operation + top => top_iter % scalar + second => second_iter % scalar + temp_iter % scalar = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d_2.inc new file mode 100644 index 0000000000..b476725f9d --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_0d_2.inc @@ -0,0 +1,16 @@ +! 1-2 break + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d0) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d0) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d0 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 0 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d.F new file mode 100644 index 0000000000..44d8df415f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d.F @@ -0,0 +1,43 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field0DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d0 + + second => second_iter % scalar + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + temp_iter % array = & + +! 1-2 break + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d0) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d_1.inc new file mode 100644 index 0000000000..c7e3cb4b99 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d_1.inc @@ -0,0 +1,24 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field0DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d0 + + second => second_iter % scalar + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + temp_iter % array = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d_2.inc new file mode 100644 index 0000000000..63835dcc17 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_1d_2.inc @@ -0,0 +1,20 @@ +! 1-2 break + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d0) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d.F new file mode 100644 index 0000000000..33b12c4132 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d.F @@ -0,0 +1,43 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field0DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d0 + + second => second_iter % scalar + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + temp_iter % array = & + +! 1-2 break + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d0) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d_1.inc new file mode 100644 index 0000000000..2d52175626 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d_1.inc @@ -0,0 +1,24 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field0DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d0 + + second => second_iter % scalar + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + temp_iter % array = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d_2.inc new file mode 100644 index 0000000000..9d9d8a0d51 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_0d_2d_2.inc @@ -0,0 +1,20 @@ +! 1-2 break + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d0) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d.F new file mode 100644 index 0000000000..02c6a95885 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d.F @@ -0,0 +1,43 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field0DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d0 + second_iter => stack(stack_pointer - 1) % d1 + + top => top_iter % scalar + + do while (associated(temp_iter)) + ! do operation + second => second_iter % array + temp_iter % array = & + +! 1-2 break + + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d0) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d_1.inc new file mode 100644 index 0000000000..2d57b6dd28 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d_1.inc @@ -0,0 +1,24 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field0DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d0 + second_iter => stack(stack_pointer - 1) % d1 + + top => top_iter % scalar + + do while (associated(temp_iter)) + ! do operation + second => second_iter % array + temp_iter % array = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d_2.inc new file mode 100644 index 0000000000..52c7acae19 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_0d_2.inc @@ -0,0 +1,20 @@ +! 1-2 break + + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d0) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff.F new file mode 100644 index 0000000000..078b626111 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff.F @@ -0,0 +1,53 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + integer :: i, j, iend, jend + + ! allocate a temp for result + call create_2d_field_from_1ds( & + stack(stack_pointer - 1) % d1, stack(stack_pointer) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d1 + + second => second_iter % array + iend = size(second) + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + jend = size(top) + + do j = 1, jend + do i = 1, iend + temp_iter % array(i,j) = & + +! 1-2 break + + end do + end do + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff_1.inc new file mode 100644 index 0000000000..eb0a6b6a78 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff_1.inc @@ -0,0 +1,31 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + integer :: i, j, iend, jend + + ! allocate a temp for result + call create_2d_field_from_1ds( & + stack(stack_pointer - 1) % d1, stack(stack_pointer) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d1 + + second => second_iter % array + iend = size(second) + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + jend = size(top) + + do j = 1, jend + do i = 1, iend + temp_iter % array(i,j) = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff_2.inc new file mode 100644 index 0000000000..9d3941314e --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_diff_2.inc @@ -0,0 +1,23 @@ +! 1-2 break + + end do + end do + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same.F new file mode 100644 index 0000000000..85821176ad --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same.F @@ -0,0 +1,43 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d1 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + temp_iter % array = & + +! 1-2 break + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same_1.inc new file mode 100644 index 0000000000..c69c217f96 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same_1.inc @@ -0,0 +1,23 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d1, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d1 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + temp_iter % array = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same_2.inc new file mode 100644 index 0000000000..03bd75e609 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_1d_same_2.inc @@ -0,0 +1,21 @@ +! 1-2 break + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first.F new file mode 100644 index 0000000000..a7a235733f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first.F @@ -0,0 +1,49 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + integer :: j, jend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d1 + + second => second_iter % array + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + jend = size(top, 2) + + do j = 1, jend + temp_iter % array(:,j) = & + +! 1-2 break + + end do + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first_1.inc new file mode 100644 index 0000000000..0eecac5114 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first_1.inc @@ -0,0 +1,28 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + integer :: j, jend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d1 + + second => second_iter % array + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + jend = size(top, 2) + + do j = 1, jend + temp_iter % array(:,j) = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first_2.inc new file mode 100644 index 0000000000..4700c0bac3 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_first_2.inc @@ -0,0 +1,22 @@ +! 1-2 break + + end do + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second.F new file mode 100644 index 0000000000..dd6dfb2ff9 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second.F @@ -0,0 +1,50 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + integer :: i, iend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d1 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + + iend = size(top, 1) + + do i = 1, iend + temp_iter % array(i,:) = & + +! 1-2 break + + end do + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second_1.inc new file mode 100644 index 0000000000..15b5a3fa21 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second_1.inc @@ -0,0 +1,28 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field1DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: second + integer :: i, iend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d1 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + + iend = size(top, 1) + + do i = 1, iend + temp_iter % array(i,:) = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second_2.inc new file mode 100644 index 0000000000..f016609cb5 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_1d_2d_second_2.inc @@ -0,0 +1,23 @@ +! 1-2 break + + end do + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d1) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d.F new file mode 100644 index 0000000000..5874874888 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d.F @@ -0,0 +1,44 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field0DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d0 + second_iter => stack(stack_pointer - 1) % d2 + + top => top_iter % scalar + + do while (associated(temp_iter)) + second => second_iter % array + + ! do operation + temp_iter % array = & + +! 1-2 break + + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d0) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d_1.inc new file mode 100644 index 0000000000..c6b7a48e53 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d_1.inc @@ -0,0 +1,25 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field0DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d0 + second_iter => stack(stack_pointer - 1) % d2 + + top => top_iter % scalar + + do while (associated(temp_iter)) + second => second_iter % array + + ! do operation + temp_iter % array = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d_2.inc new file mode 100644 index 0000000000..92114feb06 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_0d_2.inc @@ -0,0 +1,20 @@ +! 1-2 break + + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d0) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first.F new file mode 100644 index 0000000000..8b0a999f63 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first.F @@ -0,0 +1,49 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + integer :: j, jend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d2 + + top => top_iter % array + + do while (associated(temp_iter)) + ! do operation + second => second_iter % array + jend = size(second, 2) + + do j = 1, jend + temp_iter % array(:,j) = & + +! 1-2 break + + end do + + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first_1.inc new file mode 100644 index 0000000000..2e9f1a1872 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first_1.inc @@ -0,0 +1,28 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + integer :: j, jend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d2 + + top => top_iter % array + + do while (associated(temp_iter)) + ! do operation + second => second_iter % array + jend = size(second, 2) + + do j = 1, jend + temp_iter % array(:,j) = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first_2.inc new file mode 100644 index 0000000000..f331e24ea7 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_first_2.inc @@ -0,0 +1,22 @@ +! 1-2 break + + end do + + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second.F new file mode 100644 index 0000000000..7bbdaf8b3f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second.F @@ -0,0 +1,50 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + integer :: i, iend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d2 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + + iend = size(second, 1) + + do i = 1, iend + temp_iter % array(i,:) = & + +! 1-2 break + + end do + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second_1.inc new file mode 100644 index 0000000000..ffb441ed7f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second_1.inc @@ -0,0 +1,28 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field1DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + integer :: i, iend + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer - 1) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + second_iter => stack(stack_pointer - 1) % d2 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + + iend = size(second, 1) + + do i = 1, iend + temp_iter % array(i,:) = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second_2.inc new file mode 100644 index 0000000000..7821855c44 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_1d_second_2.inc @@ -0,0 +1,23 @@ +! 1-2 break + + end do + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d.F new file mode 100644 index 0000000000..48553ceeb1 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d.F @@ -0,0 +1,43 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d2 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + temp_iter % array = & + +! 1-2 break + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d_1.inc new file mode 100644 index 0000000000..ed2f5d755f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d_1.inc @@ -0,0 +1,23 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field2DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + type (field2DReal), pointer :: second_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:,:), pointer :: second + + ! allocate a temp for result + call mpas_duplicate_field(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + second_iter => stack(stack_pointer - 1) % d2 + + do while (associated(temp_iter)) + ! do operation + top => top_iter % array + second => second_iter % array + temp_iter % array = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d_2.inc new file mode 100644 index 0000000000..a3254d1d7e --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_2d_2d_2.inc @@ -0,0 +1,21 @@ +! 1-2 break + + top_iter => top_iter % next + second_iter => second_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + if (stack(stack_pointer - 1) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer - 1) % d2) + end if + + ! set stack + stack_pointer = stack_pointer - 1 + stack(stack_pointer) % d2 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 2 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch.F b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch.F new file mode 100644 index 0000000000..04b8562b19 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch.F @@ -0,0 +1,130 @@ + integer, intent(in) :: exp_number + logical, intent(in) :: type_checking + + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + + integer :: a_nd, b_nd + character (len=StrKIND) :: op_name + +! 0d 0d + + if (type_checking) then + ! check size of stack + if (stack_pointer < 2) then + call mpas_dmpar_global_abort(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + 'expression #' // trim(expression_names(exp_number)) // & + ' tried to ' // trim(op_name) // ' when there ' // & + 'were less than two operands on the stack, in the RPN calculator AM') + end if + end if + + a_nd = stack(stack_pointer - 1) % number_of_dims + b_nd = stack(stack_pointer) % number_of_dims + + ! call the right one + if (a_nd == 0) then + if (b_nd == 0) then + +! 0d 1d + + else if (b_nd == 1) then + +! 0d 2d + + else + +! 1d 0d + + end if + else if (a_nd == 1) then + if (b_nd == 0) then + +! 1d 1d same + + else if (b_nd == 1) then + if (trim(stack(stack_pointer - 1) % d1 % dimNames(1)) == & + trim(stack(stack_pointer) % d1 % dimNames(1))) then + +! 1d 1d diff + + else + if (type_checking) then + if (stack(stack_pointer - 1) % d1 % isDecomposed) then + call mpas_dmpar_global_abort(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'on two 1d arrays, with different dimensions, where the first ' // & + 'operand (1d array) is decomposed -- only the ' // & + 'second operand (the top of the stack) can be decomposed') + end if + end if + +! 1d 2d first + + end if + else + if (trim(stack(stack_pointer - 1) % d1 % dimNames(1)) == & + trim(stack(stack_pointer) % d2 % dimNames(1))) then + +! 1d 2d second + + else + if (type_checking) then + if (trim(stack(stack_pointer - 1) % d1 % dimNames(1)) /= & + trim(stack(stack_pointer) % d2 % dimNames(2))) then + call mpas_dmpar_global_abort(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'with a 1d array on a 2d array when none of the dimensions ' // & + 'match between the two arrays') + end if + end if + +! 2d 0d + + end if + end if + else + if (b_nd == 0) then + +! 2d 1d first + + else if (b_nd == 1) then + if (trim(stack(stack_pointer - 1) % d2 % dimNames(1)) == & + trim(stack(stack_pointer) % d1 % dimNames(1))) then + +! 2d 1d second + + else + if (type_checking) then + if (trim(stack(stack_pointer - 1) % d2 % dimNames(2)) /= & + trim(stack(stack_pointer) % d1 % dimNames(1))) then + call mpas_dmpar_global_abort(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'with a 1d array on a 2d array when none of the dimensions ' // & + 'match between the two arrays') + end if + end if + +! 2d 2d + + end if + else + if (type_checking) then + if ((trim(stack(stack_pointer - 1) % d2 % dimNames(1)) /= & + trim(stack(stack_pointer) % d2 % dimNames(1))) .or. & + (trim(stack(stack_pointer - 1) % d2 % dimNames(2)) /= & + trim(stack(stack_pointer) % d2 % dimNames(2)))) then + call mpas_dmpar_global_abort(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'on two 2d arrays when their dimension names do not match') + end if + end if + +! end + + end if + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_0d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_0d.inc new file mode 100644 index 0000000000..5eb6b99991 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_0d.inc @@ -0,0 +1,20 @@ +! 0d 0d + + if (type_checking) then + ! check size of stack + if (stack_pointer < 2) then + call mpas_log_write(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + 'expression #' // trim(expression_names(exp_number)) // & + ' tried to ' // trim(op_name) // ' when there ' // & + 'were less than two operands on the stack, in the RPN calculator AM') + end if + end if + + a_nd = stack(stack_pointer - 1) % number_of_dims + b_nd = stack(stack_pointer) % number_of_dims + + ! call the right one + if (a_nd == 0) then + if (b_nd == 0) then + +! 0d 1d diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_1d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_1d.inc new file mode 100644 index 0000000000..ec959cd54c --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_1d.inc @@ -0,0 +1,5 @@ +! 0d 1d + + else if (b_nd == 1) then + +! 0d 2d diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_2d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_2d.inc new file mode 100644 index 0000000000..b101960309 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_0d_2d.inc @@ -0,0 +1,5 @@ +! 0d 2d + + else + +! 1d 0d diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_0d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_0d.inc new file mode 100644 index 0000000000..03c620cbfc --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_0d.inc @@ -0,0 +1,7 @@ +! 1d 0d + + end if + else if (a_nd == 1) then + if (b_nd == 0) then + +! 1d 1d same diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_1d_diff.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_1d_diff.inc new file mode 100644 index 0000000000..827731af1e --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_1d_diff.inc @@ -0,0 +1,15 @@ +! 1d 1d diff + + else + if (type_checking) then + if (stack(stack_pointer - 1) % d1 % isDecomposed) then + call mpas_log_write(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'on two 1d arrays, with different dimensions, where the first ' // & + 'operand (1d array) is decomposed -- only the ' // & + 'second operand (the top of the stack) can be decomposed') + end if + end if + +! 1d 2d first diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_1d_same.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_1d_same.inc new file mode 100644 index 0000000000..07ef6e6044 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_1d_same.inc @@ -0,0 +1,7 @@ +! 1d 1d same + + else if (b_nd == 1) then + if (trim(stack(stack_pointer - 1) % d1 % dimNames(1)) == & + trim(stack(stack_pointer) % d1 % dimNames(1))) then + +! 1d 1d diff diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_2d_first.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_2d_first.inc new file mode 100644 index 0000000000..92340f8cb8 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_2d_first.inc @@ -0,0 +1,8 @@ +! 1d 2d first + + end if + else + if (trim(stack(stack_pointer - 1) % d1 % dimNames(1)) == & + trim(stack(stack_pointer) % d2 % dimNames(1))) then + +! 1d 2d second diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_2d_second.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_2d_second.inc new file mode 100644 index 0000000000..20f770fa22 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_1d_2d_second.inc @@ -0,0 +1,15 @@ +! 1d 2d second + + else + if (type_checking) then + if (trim(stack(stack_pointer - 1) % d1 % dimNames(1)) /= & + trim(stack(stack_pointer) % d2 % dimNames(2))) then + call mpas_log_write(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'with a 1d array on a 2d array when none of the dimensions ' // & + 'match between the two arrays') + end if + end if + +! 2d 0d diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_0d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_0d.inc new file mode 100644 index 0000000000..17137b57b6 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_0d.inc @@ -0,0 +1,8 @@ +! 2d 0d + + end if + end if + else + if (b_nd == 0) then + +! 2d 1d first diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_1d_first.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_1d_first.inc new file mode 100644 index 0000000000..43740e3247 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_1d_first.inc @@ -0,0 +1,7 @@ +! 2d 1d first + + else if (b_nd == 1) then + if (trim(stack(stack_pointer - 1) % d2 % dimNames(1)) == & + trim(stack(stack_pointer) % d1 % dimNames(1))) then + +! 2d 1d second diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_1d_second.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_1d_second.inc new file mode 100644 index 0000000000..54032f2425 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_1d_second.inc @@ -0,0 +1,15 @@ +! 2d 1d second + + else + if (type_checking) then + if (trim(stack(stack_pointer - 1) % d2 % dimNames(2)) /= & + trim(stack(stack_pointer) % d1 % dimNames(1))) then + call mpas_log_write(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'with a 1d array on a 2d array when none of the dimensions ' // & + 'match between the two arrays') + end if + end if + +! 2d 2d diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_2d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_2d.inc new file mode 100644 index 0000000000..040446c53f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_2d_2d.inc @@ -0,0 +1,17 @@ +! 2d 2d + + end if + else + if (type_checking) then + if ((trim(stack(stack_pointer - 1) % d2 % dimNames(1)) /= & + trim(stack(stack_pointer) % d2 % dimNames(1))) .or. & + (trim(stack(stack_pointer - 1) % d2 % dimNames(2)) /= & + trim(stack(stack_pointer) % d2 % dimNames(2)))) then + call mpas_log_write(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + trim(op_name) // ' in expression #' // & + trim(expression_names(exp_number)) // ' tried to operate ' // & + 'on two 2d arrays when their dimension names do not match') + end if + end if + +! end diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_end.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_end.inc new file mode 100644 index 0000000000..b829249a1b --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_end.inc @@ -0,0 +1,4 @@ +! end + + end if + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_start.inc b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_start.inc new file mode 100644 index 0000000000..aaeffe5d98 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/binary_op_dispatch_start.inc @@ -0,0 +1,10 @@ + integer, intent(in) :: exp_number + logical, intent(in) :: type_checking + + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + + integer :: a_nd, b_nd + character (len=StrKIND) :: op_name + +! 0d 0d diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/field_0d_from_1d.F b/src/core_ocean/analysis_members/rpn_calc_inc/field_0d_from_1d.F new file mode 100644 index 0000000000..5c5db26316 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/field_0d_from_1d.F @@ -0,0 +1,26 @@ + type (field0DReal), pointer, intent(out) :: dst + type (field1DReal), pointer, intent(inout) :: top + + if (mpas_threading_get_thread_num() == 0 ) then + ! allocate the linked list for the field blocks + allocate(dst) + nullify(dst % prev) + nullify(dst % next) + + ! copy field info + dst % fieldName = '_' // trim(top % fieldName) + dst % isDecomposed = .false. + + dst % block => top % block + dst % isVarArray = .false. + dst % defaultValue = top % defaultValue + dst % isActive = top % isActive + dst % hasTimeDimension = top % hasTimeDimension + dst % sendList => top % sendList + dst % recvList => top % recvList + dst % copyList => top % copyList + + allocate(dst % attLists(1)) + + nullify(dst % constituentNames) + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/field_0d_from_1d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/field_0d_from_1d.inc new file mode 100644 index 0000000000..5c5db26316 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/field_0d_from_1d.inc @@ -0,0 +1,26 @@ + type (field0DReal), pointer, intent(out) :: dst + type (field1DReal), pointer, intent(inout) :: top + + if (mpas_threading_get_thread_num() == 0 ) then + ! allocate the linked list for the field blocks + allocate(dst) + nullify(dst % prev) + nullify(dst % next) + + ! copy field info + dst % fieldName = '_' // trim(top % fieldName) + dst % isDecomposed = .false. + + dst % block => top % block + dst % isVarArray = .false. + dst % defaultValue = top % defaultValue + dst % isActive = top % isActive + dst % hasTimeDimension = top % hasTimeDimension + dst % sendList => top % sendList + dst % recvList => top % recvList + dst % copyList => top % copyList + + allocate(dst % attLists(1)) + + nullify(dst % constituentNames) + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/field_1d_from_2d.F b/src/core_ocean/analysis_members/rpn_calc_inc/field_1d_from_2d.F new file mode 100644 index 0000000000..971347840b --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/field_1d_from_2d.F @@ -0,0 +1,59 @@ + type (field2DReal), pointer, intent(in) :: top_head + type (field1DReal), pointer, intent(out) :: head + + type (field1DReal), pointer :: dst, prev + type (field2DReal), pointer :: top + + if (mpas_threading_get_thread_num() == 0 ) then + nullify(head) + nullify(prev) + + top => top_head + do while (associated(top)) + + ! allocate the linked list for the field blocks + allocate(dst) + if (.not. associated(head)) then + head => dst + end if + + if (.not. associated(prev)) then + nullify(dst % prev) + else + prev % next => dst + dst % prev => prev + end if + nullify(dst % next) + + ! copy field info + dst % fieldName = '_' // trim(top % fieldName) + dst % isDecomposed = top % isDecomposed + + dst % block => top % block + dst % isVarArray = .false. + dst % defaultValue = top % defaultValue + dst % isActive = top % isActive + dst % hasTimeDimension = top % hasTimeDimension + dst % sendList => top % sendList + dst % recvList => top % recvList + dst % copyList => top % copyList + dst % isPersistent = top % isPersistent + + allocate(dst % attLists(1)) + + nullify(dst % constituentNames) + + dst % dimNames(1) = top % dimNames(2) + dst % dimSizes(1) = top % dimSizes(2) + + ! allocate memory + if (top % isActive) then + allocate(dst % array(size(top % array, 2))) + else + nullify(dst % array) + end if + + top => top % next + prev => dst + end do + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/field_1d_from_2d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/field_1d_from_2d.inc new file mode 100644 index 0000000000..971347840b --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/field_1d_from_2d.inc @@ -0,0 +1,59 @@ + type (field2DReal), pointer, intent(in) :: top_head + type (field1DReal), pointer, intent(out) :: head + + type (field1DReal), pointer :: dst, prev + type (field2DReal), pointer :: top + + if (mpas_threading_get_thread_num() == 0 ) then + nullify(head) + nullify(prev) + + top => top_head + do while (associated(top)) + + ! allocate the linked list for the field blocks + allocate(dst) + if (.not. associated(head)) then + head => dst + end if + + if (.not. associated(prev)) then + nullify(dst % prev) + else + prev % next => dst + dst % prev => prev + end if + nullify(dst % next) + + ! copy field info + dst % fieldName = '_' // trim(top % fieldName) + dst % isDecomposed = top % isDecomposed + + dst % block => top % block + dst % isVarArray = .false. + dst % defaultValue = top % defaultValue + dst % isActive = top % isActive + dst % hasTimeDimension = top % hasTimeDimension + dst % sendList => top % sendList + dst % recvList => top % recvList + dst % copyList => top % copyList + dst % isPersistent = top % isPersistent + + allocate(dst % attLists(1)) + + nullify(dst % constituentNames) + + dst % dimNames(1) = top % dimNames(2) + dst % dimSizes(1) = top % dimSizes(2) + + ! allocate memory + if (top % isActive) then + allocate(dst % array(size(top % array, 2))) + else + nullify(dst % array) + end if + + top => top % next + prev => dst + end do + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/field_2d_from_1ds.F b/src/core_ocean/analysis_members/rpn_calc_inc/field_2d_from_1ds.F new file mode 100644 index 0000000000..ee545d328f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/field_2d_from_1ds.F @@ -0,0 +1,70 @@ + type (field1DReal), pointer, intent(in) :: second, top_head + type (field2DReal), pointer, intent(out) :: head + + type (field2DReal), pointer :: dst, prev + type (field1DReal), pointer :: top + + if (mpas_threading_get_thread_num() == 0 ) then + nullify(head) + nullify(prev) + + top => top_head + do while (associated(top)) + + ! allocate the linked list for the field blocks + allocate(dst) + if (.not. associated(head)) then + head => dst + end if + + if (.not. associated(prev)) then + nullify(dst % prev) + else + prev % next => dst + dst % prev => prev + end if + nullify(dst % next) + + ! copy field info + dst % fieldName = trim(second % fieldName) // '_' // trim(top % fieldName) + dst % isDecomposed = top % isDecomposed + + dst % block => top % block + dst % isVarArray = second % isVarArray + dst % defaultValue = second % defaultValue + dst % isActive = top % isActive .and. second % isActive + dst % hasTimeDimension = & + top % hasTimeDimension .or. second % hasTimeDimension + dst % sendList => top % sendList + dst % recvList => top % recvList + dst % copyList => top % copyList + dst % isPersistent = top % isPersistent .or. second % isPersistent + + + ! copy constitutent names if second has them + if (associated(second % constituentNames)) then + allocate(dst % constituentNames(size(second % constituentNames, dim=1))) + allocate(dst % attLists(size(second % constituentNames, dim=1))) + + dst % constituentNames(:) = second % constituentNames(:) + else + nullify(dst % constituentNames) + allocate(dst % attLists(1)) + end if + + dst % dimNames(1) = second % dimNames(1) + dst % dimNames(2) = top % dimNames(1) + dst % dimSizes(1) = second % dimSizes(1) + dst % dimSizes(2) = top % dimSizes(1) + + ! allocate memory + if (top % isActive .and. second % isActive) then + allocate(dst % array(size(second % array), size(top % array))) + else + nullify(dst % array) + end if + + top => top % next + prev => dst + end do + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/field_2d_from_1ds.inc b/src/core_ocean/analysis_members/rpn_calc_inc/field_2d_from_1ds.inc new file mode 100644 index 0000000000..ee545d328f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/field_2d_from_1ds.inc @@ -0,0 +1,70 @@ + type (field1DReal), pointer, intent(in) :: second, top_head + type (field2DReal), pointer, intent(out) :: head + + type (field2DReal), pointer :: dst, prev + type (field1DReal), pointer :: top + + if (mpas_threading_get_thread_num() == 0 ) then + nullify(head) + nullify(prev) + + top => top_head + do while (associated(top)) + + ! allocate the linked list for the field blocks + allocate(dst) + if (.not. associated(head)) then + head => dst + end if + + if (.not. associated(prev)) then + nullify(dst % prev) + else + prev % next => dst + dst % prev => prev + end if + nullify(dst % next) + + ! copy field info + dst % fieldName = trim(second % fieldName) // '_' // trim(top % fieldName) + dst % isDecomposed = top % isDecomposed + + dst % block => top % block + dst % isVarArray = second % isVarArray + dst % defaultValue = second % defaultValue + dst % isActive = top % isActive .and. second % isActive + dst % hasTimeDimension = & + top % hasTimeDimension .or. second % hasTimeDimension + dst % sendList => top % sendList + dst % recvList => top % recvList + dst % copyList => top % copyList + dst % isPersistent = top % isPersistent .or. second % isPersistent + + + ! copy constitutent names if second has them + if (associated(second % constituentNames)) then + allocate(dst % constituentNames(size(second % constituentNames, dim=1))) + allocate(dst % attLists(size(second % constituentNames, dim=1))) + + dst % constituentNames(:) = second % constituentNames(:) + else + nullify(dst % constituentNames) + allocate(dst % attLists(1)) + end if + + dst % dimNames(1) = second % dimNames(1) + dst % dimNames(2) = top % dimNames(1) + dst % dimSizes(1) = second % dimSizes(1) + dst % dimSizes(2) = top % dimSizes(1) + + ! allocate memory + if (top % isActive .and. second % isActive) then + allocate(dst % array(size(second % array), size(top % array))) + else + nullify(dst % array) + end if + + top => top % next + prev => dst + end do + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d.F b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d.F new file mode 100644 index 0000000000..3d48c2d103 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d.F @@ -0,0 +1,39 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field0DReal), pointer :: temp + type (field1DReal), pointer :: top_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), pointer :: reduced + + ! allocate a temp for result + call create_0d_field_from_1d(stack(stack_pointer) % d1, temp) + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + + ! initial value + reduced => temp % scalar + temp % scalar = & + +! 1-2 break + + do while (associated(top_iter)) + ! do operation + top => top_iter % array + + temp % scalar = & + +! 2-3 break + + top_iter => top_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + ! set stack + stack(stack_pointer) % d0 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 0 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_1.inc new file mode 100644 index 0000000000..65d50106fc --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_1.inc @@ -0,0 +1,18 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field0DReal), pointer :: temp + type (field1DReal), pointer :: top_iter + real (kind=RKIND), dimension(:), pointer :: top + real (kind=RKIND), pointer :: reduced + + ! allocate a temp for result + call create_0d_field_from_1d(stack(stack_pointer) % d1, temp) + + ! get pointers for computation + top_iter => stack(stack_pointer) % d1 + + ! initial value + reduced => temp % scalar + temp % scalar = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_2.inc new file mode 100644 index 0000000000..04276ea80f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_2.inc @@ -0,0 +1,9 @@ +! 1-2 break + + do while (associated(top_iter)) + ! do operation + top => top_iter % array + + temp % scalar = & + +! 2-3 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_3.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_3.inc new file mode 100644 index 0000000000..70f6657048 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_1d_3.inc @@ -0,0 +1,14 @@ +! 2-3 break + + top_iter => top_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d1) + end if + + ! set stack + stack(stack_pointer) % d0 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 0 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d.F b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d.F new file mode 100644 index 0000000000..a0d1e0b03f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d.F @@ -0,0 +1,48 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: reduced + integer :: j, jend + + ! allocate a temp for result + call create_1d_field_from_2d(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + + ! initial value + reduced => temp_iter % array + temp_iter % array = & + +! 1-2 break + + do while (associated(top_iter)) + ! do operation + top => top_iter % array + reduced => temp_iter % array + + jend = size(top, 2) + + do j = 1, jend + temp_iter % array(j) = & + +! 2-3 break + + end do + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + ! set stack + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_1.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_1.inc new file mode 100644 index 0000000000..027758d9e8 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_1.inc @@ -0,0 +1,20 @@ + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + type (field1DReal), pointer :: temp, temp_iter + type (field2DReal), pointer :: top_iter + real (kind=RKIND), dimension(:,:), pointer :: top + real (kind=RKIND), dimension(:), pointer :: reduced + integer :: j, jend + + ! allocate a temp for result + call create_1d_field_from_2d(stack(stack_pointer) % d2, temp) + temp_iter => temp + + ! get pointers for computation + top_iter => stack(stack_pointer) % d2 + + ! initial value + reduced => temp_iter % array + temp_iter % array = & + +! 1-2 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_2.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_2.inc new file mode 100644 index 0000000000..c5341bf85f --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_2.inc @@ -0,0 +1,13 @@ +! 1-2 break + + do while (associated(top_iter)) + ! do operation + top => top_iter % array + reduced => temp_iter % array + + jend = size(top, 2) + + do j = 1, jend + temp_iter % array(j) = & + +! 2-3 break diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_3.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_3.inc new file mode 100644 index 0000000000..d776eda7e4 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_2d_3.inc @@ -0,0 +1,17 @@ +! 2-3 break + + end do + + top_iter => top_iter % next + temp_iter => temp_iter % next + end do + + ! clean up old + if (stack(stack_pointer) % symbol_type == IS_TEMPORARY) then + call mpas_deallocate_field(stack(stack_pointer) % d2) + end if + + ! set stack + stack(stack_pointer) % d1 => temp + stack(stack_pointer) % symbol_type = IS_TEMPORARY + stack(stack_pointer) % number_of_dims = 1 diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch.F b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch.F new file mode 100644 index 0000000000..33a77816c0 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch.F @@ -0,0 +1,42 @@ + integer, intent(in) :: exp_number + logical, intent(in) :: type_checking + + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + + integer :: a_nd + character (len=StrKIND) :: op_name + +! start -> 1d + + if (type_checking) then + ! check size of stack + if (stack_pointer < 1) then + call mpas_dmpar_global_abort(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + 'expression #' // trim(expression_names(exp_number)) // & + ' tried to ' // trim(op_name) // ' when there ' // & + 'were no operands on the stack, in the RPN calculator AM') + end if + end if + + a_nd = stack(stack_pointer) % number_of_dims + + if (type_checking) then + if (a_nd < 1) then + call mpas_dmpar_global_abort(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + 'expression #' // trim(expression_names(exp_number)) // & + ' tried to ' // trim(op_name) // ' when the ' // & + 'operand on the stack is 0d, in the RPN calculator AM') + end if + end if + + ! call the right one + if (a_nd == 1) then + +! 1d -> 2d + + else + +! 2d -> end + + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_1d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_1d.inc new file mode 100644 index 0000000000..e564b24ab8 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_1d.inc @@ -0,0 +1,27 @@ +! start -> 1d + + if (type_checking) then + ! check size of stack + if (stack_pointer < 1) then + call mpas_log_write(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + 'expression #' // trim(expression_names(exp_number)) // & + ' tried to ' // trim(op_name) // ' when there ' // & + 'were no operands on the stack, in the RPN calculator AM') + end if + end if + + a_nd = stack(stack_pointer) % number_of_dims + + if (type_checking) then + if (a_nd < 1) then + call mpas_log_write(trim(MPAS_CORE_NAME) // ' ERROR: ' // & + 'expression #' // trim(expression_names(exp_number)) // & + ' tried to ' // trim(op_name) // ' when the ' // & + 'operand on the stack is 0d, in the RPN calculator AM') + end if + end if + + ! call the right one + if (a_nd == 1) then + +! 1d -> 2d diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_2d.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_2d.inc new file mode 100644 index 0000000000..8627dc85b9 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_2d.inc @@ -0,0 +1,5 @@ +! 1d -> 2d + + else + +! 2d -> end diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_end.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_end.inc new file mode 100644 index 0000000000..880e8f5b4a --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_end.inc @@ -0,0 +1,3 @@ +! 2d -> end + + end if diff --git a/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_start.inc b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_start.inc new file mode 100644 index 0000000000..6634eda3d5 --- /dev/null +++ b/src/core_ocean/analysis_members/rpn_calc_inc/reduce_op_dispatch_start.inc @@ -0,0 +1,10 @@ + integer, intent(in) :: exp_number + logical, intent(in) :: type_checking + + type (rpn_stack_value_type), dimension(MAX_STACK_SIZE), intent(inout) :: stack + integer, intent(inout) :: stack_pointer + + integer :: a_nd + character (len=StrKIND) :: op_name + +! start -> 1d diff --git a/src/core_ocean/analysis_members/time_series_inc/copy_field.F b/src/core_ocean/analysis_members/time_series_inc/copy_field.F new file mode 100644 index 0000000000..1750005aec --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/copy_field.F @@ -0,0 +1,20 @@ + character (len=StrKIND), intent(in) :: inname, outname + type (mpas_pool_type), intent(inout) :: all_fields, amPool + integer :: i + +! 1 -> 2 + + call mpas_pool_get_field(all_fields, inname, src, 1) + call mpas_duplicate_field(src, dst) + + dst % fieldName = outname + + if (associated(dst % constituentNames)) then + do i = 1, size(dst % constituentNames, dim=1) + dst % constituentNames(i) = trim(outname) // '_' // & + trim(dst % constituentNames(i)) + end do + end if + + call mpas_pool_add_field(all_fields, dst % fieldName, dst) + call mpas_pool_add_field(amPool, dst % fieldName, dst) diff --git a/src/core_ocean/analysis_members/time_series_inc/copy_field_1.inc b/src/core_ocean/analysis_members/time_series_inc/copy_field_1.inc new file mode 100644 index 0000000000..44e50c89f2 --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/copy_field_1.inc @@ -0,0 +1,5 @@ + character (len=StrKIND), intent(in) :: inname, outname + type (mpas_pool_type), intent(inout) :: all_fields, amPool + integer :: i + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/time_series_inc/copy_field_2.inc b/src/core_ocean/analysis_members/time_series_inc/copy_field_2.inc new file mode 100644 index 0000000000..715148baec --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/copy_field_2.inc @@ -0,0 +1,16 @@ +! 1 -> 2 + + call mpas_pool_get_field(all_fields, inname, src, 1) + call mpas_duplicate_field(src, dst) + + dst % fieldName = outname + + if (associated(dst % constituentNames)) then + do i = 1, size(dst % constituentNames, dim=1) + dst % constituentNames(i) = trim(outname) // '_' // & + trim(dst % constituentNames(i)) + end do + end if + + call mpas_pool_add_field(all_fields, dst % fieldName, dst) + call mpas_pool_add_field(amPool, dst % fieldName, dst) diff --git a/src/core_ocean/analysis_members/time_series_inc/operate.F b/src/core_ocean/analysis_members/time_series_inc/operate.F new file mode 100644 index 0000000000..54bb26dbac --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/operate.F @@ -0,0 +1,35 @@ + type (block_type), pointer, intent(in) :: start_block + type (time_series_buffer_type), dimension(:), intent(in) :: buffers + type (time_series_variable_type), intent(in) :: variable + + integer :: b + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: amPool + +! 1 -> 2 + + block => start_block + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, TIME_SERIES_STATS_POOL, amPool) + call mpas_pool_get_array(block % allFields, & + variable % input_name, in_array, 1) + + do b = 1, size(buffers) + if (buffers(b) % accumulate_flag == 0) then + cycle + end if + + call mpas_pool_get_array(amPool, variable % output_names(b), & + out_array, 1) + + if (buffers(b) % reset_flag == 1) then + out_array = in_array + else + +! 2 -> 3 + + end if + end do + + block => block % next + end do diff --git a/src/core_ocean/analysis_members/time_series_inc/operate_1.inc b/src/core_ocean/analysis_members/time_series_inc/operate_1.inc new file mode 100644 index 0000000000..21c28dde38 --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/operate_1.inc @@ -0,0 +1,9 @@ + type (block_type), pointer, intent(in) :: start_block + type (time_series_buffer_type), dimension(:), intent(in) :: buffers + type (time_series_variable_type), intent(in) :: variable + + integer :: b + type (block_type), pointer :: block + type (mpas_pool_type), pointer :: amPool + +! 1 -> 2 diff --git a/src/core_ocean/analysis_members/time_series_inc/operate_2.inc b/src/core_ocean/analysis_members/time_series_inc/operate_2.inc new file mode 100644 index 0000000000..519dbce3c7 --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/operate_2.inc @@ -0,0 +1,21 @@ +! 1 -> 2 + + block => start_block + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, TIME_SERIES_STATS_POOL, amPool) + call mpas_pool_get_array(block % allFields, & + variable % input_name, in_array, 1) + + do b = 1, size(buffers) + if (buffers(b) % accumulate_flag == 0) then + cycle + end if + + call mpas_pool_get_array(amPool, variable % output_names(b), & + out_array, 1) + + if (buffers(b) % reset_flag == 1) then + out_array = in_array + else + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/time_series_inc/operate_2_sos.inc b/src/core_ocean/analysis_members/time_series_inc/operate_2_sos.inc new file mode 100644 index 0000000000..2bf7614c8e --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/operate_2_sos.inc @@ -0,0 +1,21 @@ +! 1 -> 2 + + block => start_block + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, TIME_SERIES_STATS_POOL, amPool) + call mpas_pool_get_array(block % allFields, & + variable % input_name, in_array, 1) + + do b = 1, size(buffers) + if (buffers(b) % accumulate_flag == 0) then + cycle + end if + + call mpas_pool_get_array(amPool, variable % output_names(b), & + out_array, 1) + + if (buffers(b) % reset_flag == 1) then + out_array = in_array * in_array + else + +! 2 -> 3 diff --git a/src/core_ocean/analysis_members/time_series_inc/operate_3.inc b/src/core_ocean/analysis_members/time_series_inc/operate_3.inc new file mode 100644 index 0000000000..2c838ea4f4 --- /dev/null +++ b/src/core_ocean/analysis_members/time_series_inc/operate_3.inc @@ -0,0 +1,7 @@ +! 2 -> 3 + + end if + end do + + block => block % next + end do diff --git a/src/core_ocean/build_options.mk b/src/core_ocean/build_options.mk index 8c6c25a7c7..fd70ba46d8 100644 --- a/src/core_ocean/build_options.mk +++ b/src/core_ocean/build_options.mk @@ -3,7 +3,11 @@ ifeq "$(ROOT_DIR)" "" endif EXE_NAME=ocean_model NAMELIST_SUFFIX=ocean -FCINCLUDES += -I$(ROOT_DIR)/core_ocean/driver -I$(ROOT_DIR)/core_ocean/mode_forward -I$(ROOT_DIR)/core_ocean/mode_analysis -I$(ROOT_DIR)/core_ocean/shared -I$(ROOT_DIR)/core_ocean/analysis_members -I$(ROOT_DIR)/core_ocean/cvmix +FCINCLUDES += -I$(ROOT_DIR)/core_ocean/driver +FCINCLUDES += -I$(ROOT_DIR)/core_ocean/mode_forward -I$(ROOT_DIR)/core_ocean/mode_analysis -I$(ROOT_DIR)/core_ocean/mode_init +FCINCLUDES += -I$(ROOT_DIR)/core_ocean/shared -I$(ROOT_DIR)/core_ocean/analysis_members +FCINCLUDES += -I$(ROOT_DIR)/core_ocean/cvmix +FCINCLUDES += -I$(ROOT_DIR)/core_ocean/BGC override CPPFLAGS += -DCORE_OCEAN report_builds: diff --git a/src/core_ocean/driver/Makefile b/src/core_ocean/driver/Makefile index 4d5ad04589..a4a8c7381b 100644 --- a/src/core_ocean/driver/Makefile +++ b/src/core_ocean/driver/Makefile @@ -14,11 +14,17 @@ mpas_ocn_core_interface.o: mpas_ocn_core.o clean: $(RM) *.o *.mod *.f90 +ifneq (,$(findstring CPRIBM,$(CPPFLAGS))) +FFLAGS_noSMP := $(filter-out -qsmp%,$(FFLAGS)) +else +FFLAGS_noSMP := $(FFLAGS) +endif + .F.o: $(RM) $@ $*.mod ifeq "$(GEN_F90)" "true" $(CPP) $(CPPFLAGS) $(CPPINCLUDES) $< > $*.f90 - $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) + $(FC) $(FFLAGS_noSMP) -c $*.f90 $(FCINCLUDES) else - $(FC) $(CPPFLAGS) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) + $(FC) $(CPPFLAGS) $(FFLAGS_noSMP) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) endif diff --git a/src/core_ocean/driver/mpas_ocn_core.F b/src/core_ocean/driver/mpas_ocn_core.F index aae49e482a..af2a9f92fe 100644 --- a/src/core_ocean/driver/mpas_ocn_core.F +++ b/src/core_ocean/driver/mpas_ocn_core.F @@ -27,10 +27,11 @@ module ocn_core use mpas_timekeeping use mpas_dmpar use mpas_timer - use mpas_log + use mpas_threading use ocn_forward_mode use ocn_analysis_mode + use ocn_init_mode implicit none private @@ -59,15 +60,24 @@ function ocn_core_init(domain, startTimeStamp) result(ierr)!{{{ integer :: ierr character (len=StrKIND), pointer :: config_ocean_run_mode + integer :: numThreads ierr = 0 call mpas_pool_get_config(domain % configs, 'config_ocean_run_mode', config_ocean_run_mode) + numThreads = mpas_threading_get_max_threads() + + call mpas_log_write( ' *****************************************************************************') + call mpas_log_write( ' MPI Task $i has access to $i threads', intArgs=(/domain % dminfo % my_proc_id, numThreads/) ) + call mpas_log_write( ' *****************************************************************************') + if ( trim(config_ocean_run_mode) == 'forward' ) then ierr = ocn_forward_mode_init(domain, startTimeStamp) else if ( trim(config_ocean_run_mode) == 'analysis' ) then ierr = ocn_analysis_mode_init(domain, startTimeStamp) + else if ( trim(config_ocean_run_mode) == 'init' ) then + ierr = ocn_init_mode_init(domain, startTimeStamp) end if end function ocn_core_init!}}} @@ -101,6 +111,8 @@ function ocn_core_run(domain) result(iErr)!{{{ ierr = ocn_forward_mode_run(domain) else if ( trim(config_ocean_run_mode) == 'analysis' ) then ierr = ocn_analysis_mode_run(domain) + else if ( trim(config_ocean_run_mode) == 'init' ) then + ierr = ocn_init_mode_run(domain) end if end function ocn_core_run!}}} @@ -132,6 +144,8 @@ function ocn_core_finalize(domain) result(ierr)!{{{ ierr = ocn_forward_mode_finalize(domain) else if (trim(config_ocean_run_mode) == 'analysis' ) then ierr = ocn_analysis_mode_finalize(domain) + else if (trim(config_ocean_run_mode) == 'init' ) then + ierr = ocn_init_mode_finalize(domain) end if end function ocn_core_finalize!}}} diff --git a/src/core_ocean/driver/mpas_ocn_core_interface.F b/src/core_ocean/driver/mpas_ocn_core_interface.F index 618dada7d1..0906754b93 100644 --- a/src/core_ocean/driver/mpas_ocn_core_interface.F +++ b/src/core_ocean/driver/mpas_ocn_core_interface.F @@ -17,6 +17,7 @@ module ocn_core_interface use ocn_forward_mode use ocn_analysis_mode + use ocn_init_mode private @@ -30,7 +31,7 @@ module ocn_core_interface !> \brief Ocean core setup routine !> \author Doug Jacobsen !> \date 03/18/2015 - !> \details + !> \details !> This routine is intended to setup the necessary variables within a core_type !> for the ocean core. ! @@ -67,7 +68,7 @@ end subroutine ocn_setup_core!}}} !> \brief Ocean domain setup routine !> \author Doug Jacobsen !> \date 03/18/2015 - !> \details + !> \details !> This routine is intended to setup the necessary variables within a domain_type !> for the ocean core. ! @@ -87,7 +88,7 @@ end subroutine ocn_setup_domain!}}} !> \brief Pacakge setup routine !> \author Doug Jacobsen !> \date 03/12/2015 - !> \details + !> \details !> This function is intended to correctly configure the packages for this MPAS !> core. It can use any Fortran logic to properly configure packages, and it !> can also make use of any namelist options. All variables in the model are @@ -106,73 +107,285 @@ function ocn_setup_packages(configPool, packagePool, iocontext) result(ierr)!{{{ integer :: err_tmp - logical, pointer :: forwardModeActive, analysisModeActive + logical, pointer :: forwardModeActive + logical, pointer :: analysisModeActive + logical, pointer :: initModeActive logical, pointer :: thicknessFilterActive logical, pointer :: splitTimeIntegratorActive - logical, pointer :: surfaceRestoringActive - logical, pointer :: bulkForcingActive + logical, pointer :: windStressBulkPKGActive + logical, pointer :: tracerBudgetActive + logical, pointer :: landIcePressurePKGActive + logical, pointer :: landIceFluxesPKGActive + logical, pointer :: landIceCouplingPKGActive + logical, pointer :: thicknessBulkPKGActive logical, pointer :: frazilIceActive logical, pointer :: inSituEOSActive + logical, pointer :: variableShortwaveActive + + type (mpas_pool_iterator_type) :: pkgItr + logical, pointer :: packageActive + + logical, pointer :: tracerGroupPKGActive + logical, pointer :: tracerGroupBulkRestoringPKGActive + logical, pointer :: tracerGroupSurfaceRestoringPKGActive + logical, pointer :: tracerGroupInteriorRestoringPKGActive + logical, pointer :: tracerGroupExponentialDecayPKGActive + logical, pointer :: tracerGroupIdealAgePKGActive + logical, pointer :: tracerGroupTTDPKGActive + + logical, pointer :: config_use_tracerGroup + logical, pointer :: config_use_tracerGroup_surface_bulk_forcing + logical, pointer :: config_use_tracerGroup_surface_restoring + logical, pointer :: config_use_tracerGroup_interior_restoring + logical, pointer :: config_use_tracerGroup_exponential_decay + logical, pointer :: config_use_tracerGroup_idealAge_forcing + logical, pointer :: config_use_tracerGroup_ttd_forcing logical, pointer :: config_use_freq_filtered_thickness - logical, pointer :: config_frazil_ice_formation - character (len=StrKIND), pointer :: config_time_integrator, config_forcing_type - character (len=StrKIND), pointer :: config_ocean_run_mode, config_pressure_gradient_type + logical, pointer :: config_use_frazil_ice_formation + character (len=StrKIND), pointer :: config_time_integrator + character (len=StrKIND), pointer :: config_ocean_run_mode + character (len=StrKIND), pointer :: config_pressure_gradient_type + character (len=StrKIND), pointer :: config_sw_absorption_type - ! Get Packages - call mpas_pool_get_package(packagePool, 'forwardModeActive', forwardModeActive) - call mpas_pool_get_package(packagePool, 'analysisModeActive', analysisModeActive) - call mpas_pool_get_package(packagePool, 'thicknessFilterActive', thicknessFilterActive) - call mpas_pool_get_package(packagePool, 'splitTimeIntegratorActive', splitTimeIntegratorActive) - call mpas_pool_get_package(packagePool, 'surfaceRestoringActive', surfaceRestoringActive) - call mpas_pool_get_package(packagePool, 'bulkForcingActive', bulkForcingActive) - call mpas_pool_get_package(packagePool, 'frazilIceActive', frazilIceActive) - call mpas_pool_get_package(packagePool, 'inSituEOSActive', inSituEOSActive) + logical, pointer :: config_use_bulk_wind_stress + logical, pointer :: config_use_bulk_thickness_flux + logical, pointer :: config_compute_active_tracer_budgets + character (len=StrKIND), pointer :: config_land_ice_flux_mode - call mpas_pool_get_config(configPool, 'config_ocean_run_mode', config_ocean_run_mode) + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: tracerGroupName, configName, packageName + integer :: startIndex, strLen ierr = 0 + ! + ! determine the mode being used + ! + call mpas_pool_get_package(packagePool, 'forwardModeActive', forwardModeActive) + call mpas_pool_get_package(packagePool, 'analysisModeActive', analysisModeActive) + call mpas_pool_get_package(packagePool, 'initModeActive', initModeActive) + call mpas_pool_get_config(configPool, 'config_ocean_run_mode', config_ocean_run_mode) + if ( trim(config_ocean_run_mode) == 'forward' ) then forwardModeActive = .true. + endif + if ( trim(config_ocean_run_mode) == 'analysis') then + analysisModeActive = .true. + endif + if ( trim(config_ocean_run_mode) == 'init') then + initModeActive = .true. + endif + + ! + ! test for integration scheme + ! (TDR: this makes no sense, if split or unsplit then splitTimeIntegratorActive = .true.) + ! + call mpas_pool_get_package(packagePool, 'splitTimeIntegratorActive', splitTimeIntegratorActive) + call mpas_pool_get_config(configPool, 'config_time_integrator', config_time_integrator) + if ( forwardModeActive ) then + if ( config_time_integrator == trim('split_explicit') & + .or. config_time_integrator == trim('unsplit_explicit') ) then + splitTimeIntegratorActive = .true. + end if + endif - call mpas_pool_get_config(configPool, 'config_use_freq_filtered_thickness', config_use_freq_filtered_thickness) - call mpas_pool_get_config(configPool, 'config_time_integrator', config_time_integrator) - call mpas_pool_get_config(configPool, 'config_forcing_type', config_forcing_type) - call mpas_pool_get_config(configPool, 'config_frazil_ice_formation', config_frazil_ice_formation) - call mpas_pool_get_config(configPool, 'config_pressure_gradient_type', config_pressure_gradient_type) - + ! + ! test for time filtering scheme + ! + call mpas_pool_get_package(packagePool, 'thicknessFilterActive', thicknessFilterActive) + call mpas_pool_get_config(configPool, 'config_use_freq_filtered_thickness', config_use_freq_filtered_thickness) + if ( forwardModeActive ) then if (config_use_freq_filtered_thickness) then thicknessFilterActive = .true. end if + endif + + ! + ! test for bulk forcing of layer thickness, thicknessBulkPKG + ! + call mpas_pool_get_package(packagePool, 'thicknessBulkPKGActive', thicknessBulkPKGActive) + call mpas_pool_get_config(configPool, 'config_use_bulk_thickness_flux', config_use_bulk_thickness_flux) + if ( config_use_bulk_thickness_flux ) then + thicknessBulkPKGActive = .true. + end if - if (config_time_integrator == trim('split_explicit') & - .or. config_time_integrator == trim('unsplit_explicit') ) then + ! + ! test for bulk forcing of momentum by wind stress, windStressBulkPKG + ! + call mpas_pool_get_package(packagePool, 'windStressBulkPKGActive', windStressBulkPKGActive) + call mpas_pool_get_config(configPool, 'config_use_bulk_wind_stress', config_use_bulk_wind_stress) + if ( config_use_bulk_wind_stress ) then + windStressBulkPKGActive = .true. + end if - splitTimeIntegratorActive = .true. - end if + ! + ! test for tracer budget + ! + call mpas_pool_get_package(packagePool, 'tracerBudgetActive', tracerBudgetActive) + call mpas_pool_get_config(configPool, 'config_compute_active_tracer_budgets', config_compute_active_tracer_budgets) + if ( config_compute_active_tracer_budgets ) then + tracerBudgetActive = .true. + end if - if (config_forcing_type == trim('restoring')) then - surfaceRestoringActive = .true. - else if (config_forcing_type == trim('bulk')) then - bulkForcingActive = .true. - end if + ! + ! Test if chlorophyll, solar zenith angle, and clear sky radiation should be used + ! - if (config_frazil_ice_formation) then - frazilIceActive = .true. - end if + call mpas_pool_get_package(packagePool,'variableShortwaveActive',variableShortwaveActive) + call mpas_pool_get_config(configPool,'config_sw_absorption_type',config_sw_absorption_type) + if (trim (config_sw_absorption_type) == 'ohlmann00') then + variableShortwaveActive = .true. + end if - if (config_pressure_gradient_type.eq.'Jacobian_from_TS') then - inSituEOSActive = .true. - end if + ! + ! test for land ice pressure, landIcePressurePKG + ! test for land ice fluxes, landIceFluxesPKG + ! test for land ice coupling, landIceCouplingPKG + ! + call mpas_pool_get_package(packagePool, 'landIcePressurePKGActive', landIcePressurePKGActive) + call mpas_pool_get_package(packagePool, 'landIceFluxesPKGActive', landIceFluxesPKGActive) + call mpas_pool_get_package(packagePool, 'landIceCouplingPKGActive', landIceCouplingPKGActive) + call mpas_pool_get_config(configPool, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + if ( trim(config_land_ice_flux_mode) == 'pressure_only' ) then + landIcePressurePKGActive = .true. + else if ( trim(config_land_ice_flux_mode) == 'standalone' ) then + landIcePressurePKGActive = .true. + landIceFluxesPKGActive = .true. + else if ( trim(config_land_ice_flux_mode) == 'coupled' ) then + landIcePressurePKGActive = .true. + landIceFluxesPKGActive = .true. + landIceCouplingPKGActive = .true. + end if - call ocn_analysis_setup_packages(configPool, packagePool, err_tmp) - ierr = ior(ierr, err_tmp) - else if (trim(config_ocean_run_mode) == 'analysis' ) then - analysisModeActive = .true. - call ocn_analysis_setup_packages(configPool, packagePool, ierr) + ! + ! test for use of frazil ice formation, frazilIceActive + ! + call mpas_pool_get_package(packagePool, 'frazilIceActive', frazilIceActive) + call mpas_pool_get_config(configPool, 'config_use_frazil_ice_formation', config_use_frazil_ice_formation) + if (config_use_frazil_ice_formation) then + frazilIceActive = .true. + end if + + ! + ! test for form of pressure gradient computation + ! + ! TDR: need to add PKG + call mpas_pool_get_package(packagePool, 'inSituEOSActive', inSituEOSActive) + call mpas_pool_get_config(configPool, 'config_pressure_gradient_type', config_pressure_gradient_type) + if (config_pressure_gradient_type.eq.'Jacobian_from_TS') then + inSituEOSActive = .true. end if + ! + ! call into analysis member driver to set analysis member packages + ! + call ocn_analysis_setup_packages(configPool, packagePool, iocontext, err_tmp) + ierr = ior(ierr, err_tmp) + + + ! + ! if in init mode, validate configuration + ! + if ( initModeActive ) then + call ocn_init_mode_validate_configuration(configPool, packagePool, iocontext, ierr) + endif + + ! + ! iterate over tracer groups + ! each tracer group is toggled on/off using packages + ! test each package + ! + call mpas_pool_begin_iteration(packagePool) + do while ( mpas_pool_get_next_member(packagePool, groupItr) ) + startIndex = index(groupItr % memberName, 'TracersPKG') + if ( startIndex .ne. 0 ) then + strLen = len_trim(groupItr % memberName) + tracerGroupName = groupItr % memberName(1:strLen-9) + + configName = 'config_use_' // trim(tracerGroupName) + call mpas_pool_get_config(configPool, configName, config_use_tracerGroup) + if ( config_use_tracerGroup ) then + packageName = trim(tracerGroupName) // 'PKGActive' + call mpas_pool_get_package(packagePool, packageName, tracerGroupPKGActive) + tracerGroupPKGActive = .true. + + configName = 'config_use_' // trim(tracerGroupName) // '_surface_bulk_forcing' + call mpas_pool_get_config(configPool, configName, config_use_tracerGroup_surface_bulk_forcing) + + if ( config_use_tracerGroup_surface_bulk_forcing ) then + packageName = trim(tracerGroupName) // 'BulkRestoringPKGActive' + call mpas_pool_get_package(packagePool, packageName, tracerGroupBulkRestoringPKGActive) + tracerGroupBulkRestoringPKGActive = .true. + end if + + configName = 'config_use_' // trim(tracerGroupName) // '_surface_restoring' + call mpas_pool_get_config(configPool, configName, config_use_tracerGroup_surface_restoring) + + if ( config_use_tracerGroup_surface_restoring ) then + packageName = trim(tracerGroupName) // 'SurfaceRestoringPKGActive' + call mpas_pool_get_package(packagePool, packageName, tracerGroupSurfaceRestoringPKGActive) + tracerGroupSurfaceRestoringPKGActive = .true. + end if + + configName = 'config_use_' // trim(tracerGroupName) // '_interior_restoring' + call mpas_pool_get_config(configPool, configName, config_use_tracerGroup_interior_restoring) + if ( config_use_tracerGroup_interior_restoring ) then + packageName = trim(tracerGroupName) // 'InteriorRestoringPKGActive' + call mpas_pool_get_package(packagePool, packageName, tracerGroupInteriorRestoringPKGActive) + tracerGroupInteriorRestoringPKGActive = .true. + end if + + configName = 'config_use_' // trim(tracerGroupName) // '_exponential_decay' + call mpas_pool_get_config(configPool, configName, config_use_tracerGroup_exponential_decay) + if ( config_use_tracerGroup_exponential_decay ) then + packageName = trim(tracerGroupName) // 'ExponentialDecayPKGActive' + call mpas_pool_get_package(packagePool, packageName, tracerGroupExponentialDecayPKGActive) + tracerGroupExponentialDecayPKGActive = .true. + end if + + configName = 'config_use_' // trim(tracerGroupName) // '_idealAge_forcing' + call mpas_pool_get_config(configPool, configName, config_use_tracerGroup_idealAge_forcing) + if ( config_use_tracerGroup_idealAge_forcing ) then + packageName = trim(tracerGroupName) // 'IdealAgePKGActive' + call mpas_pool_get_package(packagePool, packageName, tracerGroupIdealAgePKGActive) + tracerGroupIdealAgePKGActive = .true. + end if + + configName = 'config_use_' // trim(tracerGroupName) // '_ttd_forcing' + call mpas_pool_get_config(configPool, configName, config_use_tracerGroup_ttd_forcing) + if ( config_use_tracerGroup_ttd_forcing ) then + packageName = trim(tracerGroupName) // 'TTDPKGActive' + call mpas_pool_get_package(packagePool, packageName, tracerGroupTTDPKGActive) + tracerGroupTTDPKGActive = .true. + end if + end if + end if + end do + + ! + ! test for conflicts, i.e. package settings that are inconsistent in combination + ! + + + + call mpas_log_write( '') + call mpas_log_write( ' **** Summary of ocean packages ****') + call mpas_pool_begin_iteration(packagePool) + do while ( mpas_pool_get_next_member(packagePool, pkgItr) ) + + if ( pkgItr % memberType == MPAS_POOL_PACKAGE ) then + call mpas_pool_get_package(packagePool, pkgItr % memberName, packageActive) + if ( packageActive ) then + call mpas_log_write( ' ' // trim(pkgItr % memberName) // ' = ON') + else + call mpas_log_write( ' ' // trim(pkgItr % memberName) // ' = OFF') + end if + end if + end do + call mpas_log_write( ' ***********************************') + call mpas_log_write( '') + end function ocn_setup_packages!}}} @@ -183,7 +396,7 @@ end function ocn_setup_packages!}}} !> \brief Decomposition setup routine !> \author Doug Jacobsen !> \date 04/08/2015 - !> \details + !> \details !> This routine is intended to create the decomposition list within a !> domain type, and register any decompositons the core wants within it. ! @@ -222,12 +435,12 @@ end function ocn_setup_decompositions!}}} !> \brief Pacakge setup routine !> \author Michael Duda !> \date 6 August 2014 - !> \details + !> \details !> The purpose of this function is to allow the core to set up a simulation !> clock that will be used by the I/O subsystem for timing reads and writes !> of I/O streams. - !> This function is called from the superstructure after the framework - !> has been initialized but before any fields have been allocated and + !> This function is called from the superstructure after the framework + !> has been initialized but before any fields have been allocated and !> initial fields have been read from input files. However, all namelist !> options are available. ! @@ -250,6 +463,8 @@ function ocn_setup_clock(core_clock, configs) result(ierr)!{{{ ierr = ocn_forward_mode_setup_clock(core_clock, configs) else if ( trim(config_ocean_run_mode) == 'analysis' ) then ierr = ocn_analysis_mode_setup_clock(core_clock, configs) + else if ( trim(config_ocean_run_mode) == 'init' ) then + ierr = ocn_init_mode_setup_clock(core_clock, configs) end if @@ -292,10 +507,6 @@ function ocn_setup_log(logInfo, domain) result(iErr)!{{{ ! (At present, there are not any. There could eventually be choices about the file naming conventions ! or other settings controlling behavior.) - ! ATTENTION: DISABLING LOG MANAGER FOR NOW - logInfo % outputLog % isActive = .false. - logInfo % errorLog % isActive = .false. - ! After core has had a chance to modify log defaults, open the output log call mpas_log_open(err=local_err) iErr = ior(iErr, local_err) @@ -310,10 +521,10 @@ end function ocn_setup_log!}}} !> \brief Returns the name of the stream containing mesh information !> \author Michael Duda !> \date 8 August 2014 - !> \details + !> \details !> This function returns the name of the I/O stream containing dimensions, - !> attributes, and mesh fields needed by the framework bootstrapping - !> routine. At the time this routine is called, only namelist options + !> attributes, and mesh fields needed by the framework bootstrapping + !> routine. At the time this routine is called, only namelist options !> are available. ! !----------------------------------------------------------------------- @@ -337,6 +548,8 @@ function ocn_get_mesh_stream(configs, stream) result(ierr)!{{{ if ( trim(config_ocean_run_mode) == 'forward' .or. trim(config_ocean_run_mode) == 'analysis' ) then write(stream,'(a)') 'mesh' + else if ( trim(config_ocean_run_mode) == 'init' ) then + write(stream,'(a)') 'input_init' end if end function ocn_get_mesh_stream!}}} @@ -349,7 +562,7 @@ end function ocn_get_mesh_stream!}}} !> \brief Ocean block setup function !> \author Doug Jacobsen !> \date 03/18/2015 - !> \details + !> \details !> This function is a wrapper function to properly setup a block to be a !> ocean core block. ! diff --git a/src/core_ocean/get_BGC.sh b/src/core_ocean/get_BGC.sh new file mode 100755 index 0000000000..237f3f13e4 --- /dev/null +++ b/src/core_ocean/get_BGC.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +## BGC Tag for build +#BGC_TAG=fa3d49a +BGC_TAG=9c31e70 + +## Subdirectory in BGC repo to use +BGC_SUBDIR=. + +## Available protocols for acquiring BGC source code +BGC_GIT_HTTP_ADDRESS=https://github.com/ACME-Climate/Ocean-BGC.git +BGC_GIT_SSH_ADDRESS=git@github.com:ACME-Climate/Ocean-BGC.git +BGC_SVN_ADDRESS=https://github.com/ACME-Climate/Ocean-BGC-src/tags +BGC_WEB_ADDRESS=https://github.com/ACME-Climate/Ocean-BGC-src/archive + +GIT=`which git` +SVN=`which svn` +PROTOCOL="" + +# BGC exists. Check to see if it is the correct version. +# Otherwise, flush the directory to ensure it's updated. +if [ -d BGC ]; then + + if [ -d .BGC_all/.git ]; then + cd .BGC_all + CURR_TAG=$(git rev-parse --short HEAD) + cd ../ + if [ "${CURR_TAG}" == "${BGC_TAG}" ]; then + echo "BGC version is current. Skip update" + else + unlink BGC + rm -rf .BGC_all + fi + else + unlink BGC + rm -rf .BGC_all + fi +fi + +# BGC Doesn't exist, need to acquire souce code +# If might have been flushed from the above if, in the case where it was svn or wget that acquired the source. +if [ ! -d BGC ]; then + if [ -d .BGC_all ]; then + rm -rf .BGC_all + fi + + if [ "${GIT}" != "" ]; then + echo " ** Using git to acquire BGC source. ** " + PROTOCOL="git ssh" + git clone ${BGC_GIT_SSH_ADDRESS} .BGC_all &> /dev/null + if [ -d .BGC_all ]; then + cd .BGC_all + git checkout ${BGC_TAG} &> /dev/null + cd ../ + ln -sf .BGC_all/${BGC_SUBDIR} BGC + else + git clone ${BGC_GIT_HTTP_ADDRESS} .BGC_all &> /dev/null + PROTOCOL="git http" + if [ -d .BGC_all ]; then + cd .BGC_all + git checkout ${BGC_TAG} &> /dev/null + cd ../ + ln -sf .BGC_all/${BGC_SUBDIR} BGC + fi + fi + elif [ "${SVN}" != "" ]; then + echo " ** Using svn to acquire BGC source. ** " + PROTOCOL="svn" + svn co ${BGC_SVN_ADDRESS}/${BGC_TAG} .BGC_all &> /dev/null + ln -sf .BGC_all/${BGC_SUBDIR} BGC + else + echo " ** Using wget to acquire BGC source. ** " + PROTOCOL="svn" + BGC_ZIP_DIR=`echo ${BGC_TAG} | sed 's/v//g'` + BGC_ZIP_DIR="BGC-src-${BGC_ZIP_DIR}" + if [ ! -e .${BGC_TAG}.zip ]; then + wget ${BGC_WEB_ADDRESS}/${BGC_TAG}.zip &> /dev/null + fi + unzip ${BGC_TAG}.zip &> /dev/null + mv ${BGC_TAG}.zip .${BGC_TAG}.zip + mv ${BGC_ZIP_DIR} .BGC_all + ln -sf .BGC_all/${BGC_SUBDIR} BGC + fi +fi + +if [ ! -d BGC ]; then + echo " ****************************************************** " + echo " ERROR: Build failed to acquire BGC source." + echo "" + echo " Please ensure your proxy information is setup properly for" + echo " the protocol you use to acquire BGC." + echo "" + echo " The automated script attempted to use: ${PROTOCOL}" + echo "" + if [ "${PROTOCOL}" == "git http" ]; then + echo " This protocol requires setting up the http.proxy git config option." + elif [ "${PROTOCOL}" == "git ssh" ]; then + echo " This protocol requires having ssh-keys setup, and ssh access to git@github.com." + echo " Please use 'ssh -vT git@github.com' to debug issues with ssh keys." + elif [ "${PROTOCOL}" == "svn" ]; then + echo " This protocol requires having svn proxys setup properly in ~/.subversion/servers." + elif [ "${PROTOCOL}" == "wget" ]; then + echo " This protocol requires having the http_proxy and https_proxy environment variables" + echo " setup properly for your shell." + fi + echo "" + echo " ****************************************************** " +fi diff --git a/src/core_ocean/get_cvmix.sh b/src/core_ocean/get_cvmix.sh index 736a43623c..76ca64f22c 100755 --- a/src/core_ocean/get_cvmix.sh +++ b/src/core_ocean/get_cvmix.sh @@ -1,8 +1,7 @@ #!/bin/bash ## CVMix Tag for build -CVMIX_TAG=v0.64-beta - +CVMIX_TAG=v0.84-beta ## Subdirectory in CVMix repo to use CVMIX_SUBDIR=src/shared @@ -16,22 +15,27 @@ GIT=`which git` SVN=`which svn` PROTOCOL="" -# CVMix exists. Need to make sure it's updated if it is git. +# CVMix exists. Check to see if it is the correct version. # Otherwise, flush the directory to ensure it's updated. if [ -d cvmix ]; then - unlink cvmix if [ -d .cvmix_all/.git ]; then cd .cvmix_all - git fetch origin &> /dev/null - git checkout ${CVMIX_TAG} &> /dev/null + CURR_TAG=$(git describe --tags) cd ../ - ln -sf .cvmix_all/${CVMIX_SUBDIR} cvmix + if [ "${CURR_TAG}" == "${CVMIX_TAG}" ]; then + echo "CVmix version is current. Skip update" + else + unlink cvmix + rm -rf .cvmix_all + fi else + unlink cvmix rm -rf .cvmix_all fi fi + # CVmix Doesn't exist, need to acquire souce code # If might have been flushed from the above if, in the case where it was svn or wget that acquired the source. if [ ! -d cvmix ]; then @@ -41,16 +45,16 @@ if [ ! -d cvmix ]; then if [ "${GIT}" != "" ]; then echo " ** Using git to acquire cvmix source. ** " - PROTOCOL="git https" - git clone ${CVMIX_GIT_HTTP_ADDRESS} .cvmix_all &> /dev/null + PROTOCOL="git ssh" + git clone ${CVMIX_GIT_SSH_ADDRESS} .cvmix_all &> /dev/null if [ -d .cvmix_all ]; then cd .cvmix_all git checkout ${CVMIX_TAG} &> /dev/null cd ../ ln -sf .cvmix_all/${CVMIX_SUBDIR} cvmix else - git clone ${CVMIX_GIT_SSH_ADDRESS} .cvmix_all &> /dev/null - PROTOCOL="git ssh" + git clone ${CVMIX_GIT_HTTP_ADDRESS} .cvmix_all &> /dev/null + PROTOCOL="git http" if [ -d .cvmix_all ]; then cd .cvmix_all git checkout ${CVMIX_TAG} &> /dev/null diff --git a/src/core_ocean/mode_analysis/mpas_ocn_analysis_mode.F b/src/core_ocean/mode_analysis/mpas_ocn_analysis_mode.F index b3558ada92..cf46d1cf90 100644 --- a/src/core_ocean/mode_analysis/mpas_ocn_analysis_mode.F +++ b/src/core_ocean/mode_analysis/mpas_ocn_analysis_mode.F @@ -37,7 +37,6 @@ module ocn_analysis_mode use ocn_diagnostics use ocn_equation_of_state use ocn_constants - use ocn_time_average private @@ -74,7 +73,9 @@ function ocn_analysis_mode_init(domain, startTimeStamp) result(ierr)!{{{ ! remove dt later real (kind=RKIND) :: dt - character (len=StrKIND), pointer :: xtime + character (len=StrKIND), pointer :: xtime, simulationStartTime + real (kind=RKIND), pointer :: daysSinceStartOfSim + type (MPAS_Time_type) :: xtime_timeType, simulationStartTime_timeType type (MPAS_Time_Type) :: startTime ierr = 0 @@ -92,11 +93,11 @@ function ocn_analysis_mode_init(domain, startTimeStamp) result(ierr)!{{{ ! ! Read input data for model ! - call mpas_timer_start('io_read', .false.) + call mpas_timer_start('io_read') call MPAS_stream_mgr_read(domain % streamManager, streamID='mesh', whence=MPAS_STREAM_NEAREST, ierr=err_tmp) call MPAS_stream_mgr_read(domain % streamManager, streamID='input', ierr=err_tmp) call mpas_timer_stop('io_read') - call mpas_timer_start('io_reset_alarms', .false.) + call mpas_timer_start('io_reset_alarms') call MPAS_stream_mgr_reset_alarms(domain % streamManager, streamID='input', ierr=err_tmp) call mpas_timer_stop('io_reset_alarms') @@ -121,10 +122,24 @@ function ocn_analysis_mode_init(domain, startTimeStamp) result(ierr)!{{{ call ocn_init_routines_block(block, dt, ierr) if(ierr.eq.1) then - call mpas_log_write('ERROR: An error was encountered in ocn_init_routines_block', MPAS_LOG_CRIT) + call mpas_log_write('An error was encountered in ocn_init_routines_block', MPAS_LOG_CRIT) endif xtime = startTimeStamp + + ! Set simulationStartTime only if that variable is not read from the restart file. + call mpas_pool_get_array(diagnosticsPool, 'simulationStartTime', simulationStartTime) + if (trim(simulationStartTime)=="no_date_available") then + simulationStartTime = startTimeStamp + end if + + ! compute time since start of simulation, in days + call mpas_pool_get_array(diagnosticsPool, 'daysSinceStartOfSim',daysSinceStartOfSim) + call mpas_set_time(xtime_timeType, dateTimeString=xtime) + call mpas_set_time(simulationStartTime_timeType, dateTimeString=simulationStartTime) + call mpas_get_timeInterval(xtime_timeType - simulationStartTime_timeType,dt=daysSinceStartOfSim) + daysSinceStartOfSim = daysSinceStartOfSim*days_per_second + block => block % next end do @@ -206,6 +221,7 @@ function ocn_analysis_mode_run(domain) result(ierr)!{{{ integer :: err, ierr type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool type (mpas_pool_type), pointer :: forcingPool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: diagnosticsPool @@ -228,23 +244,22 @@ function ocn_analysis_mode_run(domain) result(ierr)!{{{ call mpas_log_write('Initial time ' // trim(timeStamp)) ! fill in diagnostics variables - call mpas_timer_start("diagnostic solve") block_ptr => domain % blocklist do while(associated(block_ptr)) call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 1) + call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, 1) block_ptr => block_ptr % next end do - call mpas_timer_stop("diagnostic solve") if (config_write_output_on_startup) then - call ocn_analysis_compute_startup(domain, err) - call mpas_timer_start('io_write', .false.) + call ocn_analysis_compute_startup(domain, err) + call mpas_timer_start('io_write') call mpas_stream_mgr_write(domain % streamManager, ierr=ierr) call mpas_timer_stop('io_write') endif diff --git a/src/core_ocean/mode_forward/mpas_ocn_forward_mode.F b/src/core_ocean/mode_forward/mpas_ocn_forward_mode.F index 0c19fe1e8e..12b56f224b 100644 --- a/src/core_ocean/mode_forward/mpas_ocn_forward_mode.F +++ b/src/core_ocean/mode_forward/mpas_ocn_forward_mode.F @@ -50,12 +50,22 @@ module ocn_forward_mode use ocn_vel_hmix use ocn_vel_forcing use ocn_vel_coriolis + use ocn_vel_forcing_surface_stress + use ocn_surface_bulk_forcing + use ocn_surface_land_ice_fluxes + use ocn_frazil_forcing use ocn_tracer_hmix - use ocn_tracer_surface_flux + use ocn_tracer_hmix_redi + use ocn_tracer_surface_flux_to_tend use ocn_tracer_short_wave_absorption + use ocn_tracer_short_wave_absorption_variable use ocn_tracer_nonlocalflux use ocn_tracer_advection + use ocn_tracer_ecosys + use ocn_tracer_DMS + use ocn_tracer_MacroMolecules + use ocn_tracer_surface_restoring use ocn_gm use ocn_high_freq_thickness_hmix_del2 @@ -64,8 +74,6 @@ module ocn_forward_mode use ocn_vmix - use ocn_time_average - use ocn_forcing use ocn_sea_ice @@ -108,14 +116,19 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: diagnosticsPool - character (len=StrKIND), pointer :: xtime - type (MPAS_Time_Type) :: startTime + character (len=StrKIND), pointer :: xtime, simulationStartTime + real (kind=RKIND), pointer :: daysSinceStartOfSim + type (MPAS_Time_type) :: xtime_timeType, simulationStartTime_timeType + type (MPAS_Time_Type) :: startTime, alarmTime type (MPAS_TimeInterval_type) :: timeStep + type (MPAS_TimeInterval_type) :: alarmTimeStep - logical, pointer :: config_do_restart, config_filter_btr_mode, config_conduct_tests + logical, pointer :: config_do_restart, config_read_nearest_restart, config_filter_btr_mode, config_conduct_tests character (len=StrKIND), pointer :: config_vert_coord_movement, config_pressure_gradient_type - real (kind=RKIND), pointer :: config_maxMeshDensity + character (len=StrKIND), pointer :: config_surface_salinity_monthly_restoring_compute_interval + real (kind=RKIND), pointer :: config_maxMeshDensity + logical, pointer :: config_use_surface_salinity_monthly_restoring ierr = 0 ! @@ -129,22 +142,34 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ call ocn_constants_init(domain % configs, domain % packages) call mpas_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) + call mpas_pool_get_config(domain % configs, 'config_read_nearest_restart', config_read_nearest_restart) call mpas_pool_get_config(domain % configs, 'config_vert_coord_movement', config_vert_coord_movement) call mpas_pool_get_config(domain % configs, 'config_pressure_gradient_type', config_pressure_gradient_type) call mpas_pool_get_config(domain % configs, 'config_filter_btr_mode', config_filter_btr_mode) call mpas_pool_get_config(domain % configs, 'config_maxMeshDensity', config_maxMeshDensity) call mpas_pool_get_config(domain % configs, 'config_conduct_tests', config_conduct_tests) - + call mpas_pool_get_config(domain % configs, 'config_use_surface_salinity_monthly_restoring', & + config_use_surface_salinity_monthly_restoring) + call mpas_pool_get_config(domain % configs, 'config_surface_salinity_monthly_restoring_compute_interval', & + config_surface_salinity_monthly_restoring_compute_interval) ! ! Read input data for model ! call mpas_timer_start('io_read') call MPAS_stream_mgr_read(domain % streamManager, streamID='mesh', whence=MPAS_STREAM_NEAREST, ierr=err_tmp) + if ( config_do_restart ) then - call MPAS_stream_mgr_read(domain % streamManager, streamID='restart', ierr=err_tmp) + if ( config_read_nearest_restart ) then + call MPAS_stream_mgr_read(domain % streamManager, streamID='restart', whence=MPAS_STREAM_NEAREST, ierr=err_tmp) + else + call MPAS_stream_mgr_read(domain % streamManager, streamID='restart', ierr=err_tmp) + end if else call MPAS_stream_mgr_read(domain % streamManager, streamID='input', ierr=err_tmp) end if + + call ocn_analysis_bootstrap(domain, err=err_tmp) + call mpas_timer_stop('io_read') call mpas_timer_start('reset_io_alarms') call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID='input', ierr=err_tmp) @@ -152,6 +177,16 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) call mpas_timer_stop('reset_io_alarms') + ! Read the remaining input streams + call mpas_timer_start('io_read') + call mpas_stream_mgr_read(domain % streamManager, ierr=err_tmp) + ierr = ior(ierr, err_tmp) + call mpas_timer_stop('io_read') + call mpas_timer_start('reset_io_alarms') + call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_INPUT, ierr=err_tmp) + ierr = ior(ierr, err_tmp) + call mpas_timer_stop('reset_io_alarms') + ! Initialize submodules before initializing blocks. call ocn_timestep_init(ierr) @@ -174,19 +209,35 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ ierr = ior(ierr, err_tmp) call ocn_vel_forcing_init(err_tmp) ierr = ior(ierr, err_tmp) + call ocn_vel_forcing_surface_stress_init(err_tmp) + ierr = ior(ierr, err_tmp) + call ocn_surface_bulk_forcing_init(err_tmp) + ierr = ior(ierr, err_tmp) + call ocn_surface_land_ice_fluxes_init(err_tmp) + ierr = ior(ierr, err_tmp) + call ocn_frazil_forcing_init(err_tmp) + ierr = ior(ierr, err_tmp) call ocn_tracer_hmix_init(err_tmp) ierr = ior(ierr, err_tmp) + call ocn_tracer_hmix_redi_init(err_tmp) + ierr = ior(ierr, err_tmp) call ocn_tracer_surface_flux_init(err_tmp) ierr = ior(ierr, err_tmp) call ocn_tracer_advection_init(err_tmp) ierr = ior(ierr,err_tmp) - call ocn_tracer_short_wave_absorption_init(err_tmp) + call ocn_tracer_short_wave_absorption_init(domain,err_tmp) ierr = ior(ierr,err_tmp) call ocn_gm_init(err_tmp) ierr = ior(ierr,err_tmp) call ocn_tracer_nonlocalflux_init(err_tmp) ierr = ior(ierr,err_tmp) + call ocn_tracer_ecosys_init(domain, err_tmp) + ierr = ior(ierr,err_tmp) + call ocn_tracer_DMS_init(domain, err_tmp) + ierr = ior(ierr,err_tmp) + call ocn_tracer_MacroMolecules_init(domain, err_tmp) + ierr = ior(ierr,err_tmp) call ocn_vmix_init(domain, err_tmp) ierr = ior(ierr, err_tmp) @@ -209,18 +260,17 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ call ocn_sea_ice_init(nVertLevels, err_tmp) ierr = ior(ierr, err_tmp) - call ocn_analysis_init(domain, err_tmp) - ierr = ior(ierr, err_tmp) - if(ierr.eq.1) then - call mpas_log_write('ERROR: An error was encountered while initializing the MPAS-Ocean forward mode') + call mpas_log_write('An error was encountered while initializing the MPAS-Ocean forward mode', MPAS_LOG_CRIT) endif + call ocn_init_metadata(domain) + call ocn_init_routines_vert_coord(domain) call ocn_init_routines_compute_max_level(domain) - if (.not.config_do_restart) call ocn_time_integration_split_init(domain) + call ocn_time_integration_split_init(domain) call mpas_log_write(' Vertical coordinate movement is: ' // trim(config_vert_coord_movement)) @@ -229,21 +279,22 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ config_vert_coord_movement.ne.'impermeable_interfaces'.and. & config_vert_coord_movement.ne.'user_specified') then call mpas_log_write(' Incorrect choice of config_vert_coord_movement.') - call mpas_log_write('ERROR: Incorrect choice of config_vert_coord_movement.') + call mpas_log_write('Incorrect choice of config_vert_coord_movement.', MPAS_LOG_CRIT) endif - if(config_vert_coord_movement .ne. 'impermeable_interfaces' .and. config_pressure_gradient_type .eq. 'MontgomeryPotential') then - call mpas_log_write(' Incorrect combination of config_vert_coord_movement and config_pressure_gradient_type') - call mpas_log_write('ERROR: Incorrect combination of config_vert_coord_movement and config_pressure_gradient_type') + if (config_vert_coord_movement .ne. 'impermeable_interfaces' & + .and. config_pressure_gradient_type .eq. 'MontgomeryPotential') then + call mpas_log_write( & + 'Incorrect combination of config_vert_coord_movement and config_pressure_gradient_type', MPAS_LOG_CRIT) end if if (config_filter_btr_mode.and. & config_vert_coord_movement.ne.'fixed')then - call mpas_log_write('ERROR: filter_btr_mode has only been tested with config_vert_coord_movement=fixed.') + call mpas_log_write('filter_btr_mode has only been tested with config_vert_coord_movement=fixed.', MPAS_LOG_CRIT) endif ! find the maximum value of the meshDensity - if (config_maxMeshDensity < 0.0) then + if (config_maxMeshDensity < 0.0_RKIND) then maxDensity=-1 block => domain % blocklist do while (associated(block)) @@ -265,13 +316,28 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ block => domain % blocklist do while (associated(block)) call ocn_init_routines_block(block, dt, ierr) + if(ierr.eq.1) then - call mpas_log_write('ERROR: An error was encountered in ocn_init_routines_block') + call mpas_log_write('An error was encountered in ocn_init_routines_block', MPAS_LOG_CRIT) endif call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_array(diagnosticsPool, 'xtime', xtime) xtime = startTimeStamp + + ! Set simulationStartTime only if that variable is not read from the restart file. + call mpas_pool_get_array(diagnosticsPool, 'simulationStartTime', simulationStartTime) + if (trim(simulationStartTime)=="no_date_available") then + simulationStartTime = startTimeStamp + end if + + ! compute time since start of simulation, in days + call mpas_pool_get_array(diagnosticsPool, 'daysSinceStartOfSim',daysSinceStartOfSim) + call mpas_set_time(xtime_timeType, dateTimeString=xtime) + call mpas_set_time(simulationStartTime_timeType, dateTimeString=simulationStartTime) + call mpas_get_timeInterval(xtime_timeType - simulationStartTime_timeType,dt=daysSinceStartOfSim) + daysSinceStartOfSim = daysSinceStartOfSim*days_per_second + block => block % next end do @@ -281,6 +347,31 @@ function ocn_forward_mode_init(domain, startTimeStamp) result(ierr)!{{{ call mpas_timer_stop("test suite") endif + call ocn_analysis_init(domain, err_tmp) + ierr = ior(ierr, err_tmp) + if(ierr.eq.1) then + call mpas_log_write('An error was encountered while initializing ' & + // 'the analysis members in the MPAS-Ocean forward mode', MPAS_LOG_CRIT) + endif + + if (config_use_surface_salinity_monthly_restoring) then + !initialize the alarm for reading salinity data for restoring + if ( config_surface_salinity_monthly_restoring_compute_interval == 'dt') then + alarmTimeStep = mpas_get_clock_timestep(domain % clock, err_tmp) + call mpas_get_timeInterval(alarmTimeStep, timeString= & + config_surface_salinity_monthly_restoring_compute_interval, ierr=err_tmp) + endif + alarmTime = mpas_get_clock_time(domain % clock, MPAS_START_TIME, ierr=err_tmp) + + call mpas_set_timeInterval(alarmTimeStep, timeString= & + config_surface_salinity_monthly_restoring_compute_interval, ierr=err_tmp) + + call mpas_add_clock_alarm(domain % clock, 'salinityDataReadAlarm', alarmTime, & + alarmTimeInterval=alarmTimeStep, ierr=err_tmp) + call mpas_reset_clock_alarm(domain % clock, 'salinityDataReadAlarm', ierr=err_tmp) + + endif + end function ocn_forward_mode_init!}}} !*********************************************************************** @@ -371,21 +462,34 @@ function ocn_forward_mode_run(domain) result(ierr)!{{{ type (MPAS_Time_Type) :: currTime character(len=StrKIND) :: timeStamp - integer :: ierr + integer :: ierr, err_tmp type (mpas_pool_type), pointer :: averagePool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: statePool type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: scratchPool + type (MPAS_timeInterval_type) :: timeStep - character(len=StrKIND), pointer :: config_restart_timestamp_name + character(len=StrKIND), pointer :: config_restart_timestamp_name, config_sw_absorption_type + logical, pointer :: config_write_output_on_startup + logical, pointer :: config_use_ecosysTracers + logical, pointer :: config_use_activeTracers_surface_restoring + logical, pointer :: config_use_surface_salinity_monthly_restoring ierr = 0 call mpas_pool_get_config(domain % configs, 'config_write_output_on_startup', config_write_output_on_startup) call mpas_pool_get_config(domain % configs, 'config_restart_timestamp_name', config_restart_timestamp_name) + call mpas_pool_get_config(domain % configs, 'config_sw_absorption_type', config_sw_absorption_type) + call mpas_pool_get_config(domain % configs, 'config_use_ecosysTracers', config_use_ecosysTracers) + call mpas_pool_get_config(domain % configs, 'config_use_activeTracers_surface_restoring', & + config_use_activeTracers_surface_restoring) + call mpas_pool_get_config(domain % configs, 'config_use_surface_salinity_monthly_restoring', & + config_use_surface_salinity_monthly_restoring) ! Eventually, dt should be domain specific timeStep = mpas_get_clock_timestep(domain % clock, ierr=ierr) @@ -393,7 +497,9 @@ function ocn_forward_mode_run(domain) result(ierr)!{{{ currTime = mpas_get_clock_time(domain % clock, MPAS_NOW, ierr) call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=ierr) +#ifdef MPAS_DEBUG call mpas_log_write('Initial time ' // trim(timeStamp)) +#endif call ocn_analysis_compute_startup(domain, err) @@ -403,22 +509,41 @@ function ocn_forward_mode_run(domain) result(ierr)!{{{ call mpas_timer_stop('io_write') endif - block_ptr => domain % blocklist - do while(associated(block_ptr)) - call mpas_pool_get_subpool(block_ptr % structs, 'average', averagePool) - call ocn_time_average_init(averagePool) - block_ptr => block_ptr % next - end do + ! read initial data required for variable shortwave + call mpas_timer_start('io_shortwave',.false.) + call ocn_get_shortWaveData(domain % streamManager, domain, domain % clock, .true.) + call mpas_timer_stop('io_shortwave') + + ! read initial data required for ecosys forcing + if (config_use_ecosysTracers) then + call mpas_timer_start('io_ecosys',.false.) + call ocn_get_ecosysData(domain % streamManager, domain, domain % clock, .true.) + call mpas_timer_stop('io_ecosys') + endif + + ! read initial data required for monthly surface salinity restoring + ! always execute this call as initial data needed regardless of alarm + if (config_use_activeTracers_surface_restoring .and. config_use_surface_salinity_monthly_restoring) then + call mpas_timer_start('io_monthly_surface_salinity',.false.) + call ocn_get_surfaceSalinityData(domain % streamManager, domain, domain % clock, .true.) + call mpas_timer_stop('io_monthly_surface_salinity') + endif ! During integration, time level 1 stores the model state at the beginning of the ! time step, and time level 2 stores the state advanced dt in time by timestep(...) itimestep = 0 + do while (.not. mpas_is_clock_stop_time(domain % clock)) call mpas_timer_start('io_read') call mpas_stream_mgr_read(domain % streamManager, ierr=ierr) call mpas_timer_stop('io_read') call mpas_timer_start('reset_io_alarms') call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_INPUT, ierr=ierr) + + ! Also restart all block_* streams, if any are defined. + if ( mpas_stream_mgr_stream_exists(domain % streamManager, 'block_.*') ) then + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID='block_.*', ierr=ierr) + end if call mpas_timer_stop('reset_io_alarms') itimestep = itimestep + 1 @@ -428,31 +553,57 @@ function ocn_forward_mode_run(domain) result(ierr)!{{{ call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=ierr) call mpas_log_write('Doing timestep ' // trim(timeStamp)) +#ifdef MPAS_DEBUG + call mpas_log_write( ' Computing surface flux arrays') +#endif block_ptr => domain % blocklist do while(associated(block_ptr)) call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) - call ocn_forcing_build_arrays(meshPool, statePool, forcingPool, ierr, 1) - call ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, forcingpool, ierr, 1) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, diagnosticsPool, forcingPool, ierr, 1) + call mpas_timer_start("land_ice_build_arrays") + call ocn_surface_land_ice_fluxes_build_arrays(meshPool, diagnosticsPool, & + forcingPool, scratchPool, statePool, dt, err) + call mpas_timer_stop("land_ice_build_arrays") + + call ocn_frazil_forcing_build_arrays(domain, meshPool, forcingPool, diagnosticsPool, statePool, err) + block_ptr => block_ptr % next end do call mpas_timer_start("time integration") + +#ifdef MPAS_DEBUG + call mpas_log_write( ' Computing forward time step') +#endif + !$omp parallel default(firstprivate) shared(domain, dt, timeStamp) + call ocn_timestep(domain, dt, timeStamp) + + !$omp end parallel + call mpas_timer_stop("time integration") ! Move time level 2 fields back into time level 1 for next time step - block_ptr => domain % blocklist - do while(associated(block_ptr)) - call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) - call mpas_pool_shift_time_levels(statePool) - block_ptr => block_ptr % next - end do - - call ocn_analysis_compute(domain, err) +#ifdef MPAS_DEBUG + call mpas_log_write( ' Shifting time levels') +#endif + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_shift_time_levels(statePool) + +#ifdef MPAS_DEBUG + call mpas_log_write( ' Handling analysis members') +#endif + call ocn_analysis_compute(domain, err) + call ocn_analysis_restart(domain, err) call ocn_analysis_write(domain, err) +#ifdef MPAS_DEBUG + call mpas_log_write( ' Performing I/O') +#endif call mpas_timer_start('io_write') call mpas_stream_mgr_write(domain % streamManager, streamID='output', ierr=ierr) call mpas_timer_stop('io_write') @@ -460,15 +611,30 @@ function ocn_forward_mode_run(domain) result(ierr)!{{{ call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID='output', ierr=ierr) call mpas_timer_stop('reset_io_alarms') + if ( mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID='restart', direction=MPAS_STREAM_OUTPUT, & + ierr=ierr) ) then +#ifdef MPAS_DEBUG + call mpas_log_write( ' Writing restart timestamp file') +#endif + if ( domain % dminfo % my_proc_id == 0 ) then + open(22, file=config_restart_timestamp_name, form='formatted', status='replace') + write(22, *) trim(timeStamp) + close(22) + end if + + if(trim(config_sw_absorption_type)=='ohlmann00') call ocn_shortwave_forcing_write_restart(domain) + + if (config_use_ecosysTracers) call ocn_ecosys_forcing_write_restart(domain) + + if (config_use_activeTracers_surface_restoring .and. config_use_surface_salinity_monthly_restoring) & + call ocn_salinity_restoring_forcing_write_restart(domain) + + end if + call mpas_timer_start('io_write') call mpas_stream_mgr_write(domain % streamManager, streamID='restart', ierr=ierr) call mpas_timer_stop('io_write') - if ( mpas_stream_mgr_ringing_alarms(domain % streamManager, streamID='restart', direction=MPAS_STREAM_OUTPUT, ierr=ierr) ) then - open(22, file=config_restart_timestamp_name, form='formatted', status='replace') - write(22, *) trim(timeStamp) - close(22) - end if call mpas_timer_start('reset_io_alarms') call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID='restart', ierr=ierr) @@ -480,7 +646,34 @@ function ocn_forward_mode_run(domain) result(ierr)!{{{ call mpas_timer_start('reset_io_alarms') call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_OUTPUT, ierr=ierr) call mpas_timer_stop('reset_io_alarms') + + ! read next time level data required for variable shortwave + call mpas_timer_start('io_shortwave',.false.) + call ocn_get_shortWaveData(domain % streamManager, domain, domain % clock, .false.) + call mpas_timer_stop('io_shortwave') + + ! read next time level data required for ecosys forcing + if (config_use_ecosysTracers) then + call mpas_timer_start('io_ecosys',.false.) + call ocn_get_ecosysData(domain % streamManager, domain, domain % clock, .false.) + call mpas_timer_stop('io_ecosys') + endif + + ! read next time level data required for monthly surface salinity restoring + if (config_use_activeTracers_surface_restoring .and. config_use_surface_salinity_monthly_restoring) then + if ( mpas_is_alarm_ringing(domain % clock, 'salinityDataReadAlarm', ierr=err_tmp) ) then + call mpas_reset_clock_alarm(domain % clock, 'salinityDataReadAlarm', ierr=err_tmp) + call mpas_timer_start('io_monthly_surface_salinity',.false.) + call ocn_get_surfaceSalinityData(domain % streamManager, domain, domain % clock, .false.) + call mpas_timer_stop('io_monthly_surface_salinity') + endif + endif + + ! Validate that the state is OK to run with for the next timestep. + call ocn_validate_state(domain, timeLevel=1) + end do + end function ocn_forward_mode_run!}}} !*********************************************************************** diff --git a/src/core_ocean/mode_forward/mpas_ocn_time_integration.F b/src/core_ocean/mode_forward/mpas_ocn_time_integration.F index 0f0db8bf28..8a9618acba 100644 --- a/src/core_ocean/mode_forward/mpas_ocn_time_integration.F +++ b/src/core_ocean/mode_forward/mpas_ocn_time_integration.F @@ -23,6 +23,7 @@ module ocn_time_integration use mpas_derived_types use mpas_pool_routines use mpas_constants + use mpas_timekeeping use mpas_dmpar use mpas_vector_reconstruction use mpas_spline_interpolation @@ -80,9 +81,9 @@ subroutine ocn_timestep(domain, dt, timeStamp)!{{{ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Advance model state forward in time by the specified time step ! - ! Input: domain - current model state in time level 1 (e.g., time_levs(1)state%h(:,:)) + ! Input: domain - current model state in time level 1 (e.g., time_levs(1)state%h(:,:)) ! plus mesh meta-data - ! Output: domain - upon exit, time level 2 (e.g., time_levs(2)%state%h(:,:)) contains + ! Output: domain - upon exit, time level 2 (e.g., time_levs(2)%state%h(:,:)) contains ! model state advanced forward in time by dt seconds !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -92,15 +93,15 @@ subroutine ocn_timestep(domain, dt, timeStamp)!{{{ real (kind=RKIND), intent(in) :: dt character(len=*), intent(in) :: timeStamp - real (kind=RKIND) :: nanCheck - type (dm_info) :: dminfo type (block_type), pointer :: block - type (mpas_pool_type), pointer :: diagnosticsPool, statePool + type (mpas_pool_type), pointer :: diagnosticsPool, statePool, meshPool character (len=StrKIND), pointer :: xtime - real (kind=RKIND), dimension(:,:), pointer :: normalVelocity + real (kind=RKIND), pointer :: daysSinceStartOfSim + character (len=StrKIND), pointer :: simulationStartTime + type (MPAS_Time_type) :: xtime_timeType, simulationStartTime_timeType if (rk4On) then @@ -113,17 +114,22 @@ subroutine ocn_timestep(domain, dt, timeStamp)!{{{ do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'state', statePool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 2) call mpas_pool_get_array(diagnosticsPool, 'xtime', xtime) xtime = timeStamp - nanCheck = sum(normalVelocity) + ! compute time since start of simulation, in days + call mpas_pool_get_array(diagnosticsPool, 'simulationStartTime', simulationStartTime) + call mpas_pool_get_array(diagnosticsPool, 'daysSinceStartOfSim',daysSinceStartOfSim) + call mpas_set_time(xtime_timeType, dateTimeString=xtime) + call mpas_set_time(simulationStartTime_timeType, dateTimeString=simulationStartTime) + call mpas_get_timeInterval(xtime_timeType - simulationStartTime_timeType,dt=daysSinceStartOfSim) - if (nanCheck /= nanCheck) then - call mpas_log_write('Abort: NaN detected', MPAS_LOG_CRIT) - endif + !$omp single + daysSinceStartOfSim = daysSinceStartOfSim*days_per_second + !$omp end single block => block % next end do diff --git a/src/core_ocean/mode_forward/mpas_ocn_time_integration_rk4.F b/src/core_ocean/mode_forward/mpas_ocn_time_integration_rk4.F index 100d323b0a..f85688fd4a 100644 --- a/src/core_ocean/mode_forward/mpas_ocn_time_integration_rk4.F +++ b/src/core_ocean/mode_forward/mpas_ocn_time_integration_rk4.F @@ -23,6 +23,7 @@ module ocn_time_integration_rk4 use mpas_pool_routines use mpas_constants use mpas_dmpar + use mpas_threading use mpas_vector_reconstruction use mpas_spline_interpolation use mpas_timer @@ -34,9 +35,9 @@ module ocn_time_integration_rk4 use ocn_equation_of_state use ocn_vmix - use ocn_time_average use ocn_time_average_coupled - use ocn_sea_ice + + use ocn_effective_density_in_land_ice implicit none private @@ -72,12 +73,12 @@ module ocn_time_integration_rk4 subroutine ocn_time_integrator_rk4(domain, dt)!{{{ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! Advance model state forward in time by the specified time step using + ! Advance model state forward in time by the specified time step using ! 4th order Runge-Kutta ! - ! Input: domain - current model state in time level 1 (e.g., time_levs(1)state%h(:,:)) + ! Input: domain - current model state in time level 1 (e.g., time_levs(1)state%h(:,:)) ! plus mesh meta-data - ! Output: domain - upon exit, time level 2 (e.g., time_levs(2)%state%h(:,:)) contains + ! Output: domain - upon exit, time level 2 (e.g., time_levs(2)%state%h(:,:)) contains ! model state advanced forward in time by dt seconds !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -86,18 +87,21 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ type (domain_type), intent(inout) :: domain !< Input/Output: domain information real (kind=RKIND), intent(in) :: dt !< Input: timestep - integer :: iCell, k, i, err + integer :: iCell, iEdge, k, i, err type (block_type), pointer :: block type (mpas_pool_type), pointer :: tendPool + type (mpas_pool_type), pointer :: tracersTendPool type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: provisStatePool + type (mpas_pool_type), pointer :: provisTracersPool type (mpas_pool_type), pointer :: diagnosticsPool type (mpas_pool_type), pointer :: verticalMeshPool type (mpas_pool_type), pointer :: forcingPool type (mpas_pool_type), pointer :: scratchPool - type (mpas_pool_type), pointer :: averagePool + type (mpas_pool_type), pointer :: swForcingPool integer :: rk_step @@ -119,7 +123,9 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ logical, pointer :: config_filter_btr_mode, config_use_freq_filtered_thickness logical, pointer :: config_use_standardGM logical, pointer :: config_use_cvmix_kpp + logical, pointer :: config_use_tracerGroup real (kind=RKIND), pointer :: config_mom_del4 + character (len=StrKIND), pointer :: config_land_ice_flux_mode ! State indices integer, pointer :: indexTemperature @@ -136,11 +142,12 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ real (kind=RKIND), dimension(:,:), pointer :: normalVelocityProvis, layerThicknessProvis real (kind=RKIND), dimension(:,:), pointer :: highFreqThicknessProvis real (kind=RKIND), dimension(:,:), pointer :: lowFreqDivergenceProvis - real (kind=RKIND), dimension(:,:,:), pointer :: tracersProvis + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroupProvis ! Tend Array Pointers - real (kind=RKIND), dimension(:,:), pointer :: highFreqThicknessTend, lowFreqDivergenceTend, normalVelocityTend, layerThicknessTend - real (kind=RKIND), dimension(:,:,:), pointer :: tracersTend + real (kind=RKIND), dimension(:,:), pointer :: highFreqThicknessTend, lowFreqDivergenceTend, normalVelocityTend, & + layerThicknessTend + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroupTend ! Diagnostics Array Pointers real (kind=RKIND), dimension(:,:), pointer :: layerThicknessEdge @@ -148,9 +155,9 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ real (kind=RKIND), dimension(:,:), pointer :: normalTransportVelocity, normalGMBolusVelocity real (kind=RKIND), dimension(:,:), pointer :: velocityX, velocityY, velocityZ real (kind=RKIND), dimension(:,:), pointer :: velocityZonal, velocityMeridional - real (kind=RKIND), dimension(:,:), pointer :: gradSSH - real (kind=RKIND), dimension(:,:), pointer :: gradSSHX, gradSSHY, gradSSHZ - real (kind=RKIND), dimension(:,:), pointer :: gradSSHZonal, gradSSHMeridional + real (kind=RKIND), dimension(:), pointer :: gradSSH + real (kind=RKIND), dimension(:), pointer :: gradSSHX, gradSSHY, gradSSHZ + real (kind=RKIND), dimension(:), pointer :: gradSSHZonal, gradSSHMeridional real (kind=RKIND), dimension(:,:), pointer :: surfaceVelocity, sshGradient ! State Array Pointers @@ -160,14 +167,20 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ real (kind=RKIND), dimension(:,:), pointer :: lowFreqDivergenceCur, lowFreqDivergenceNew real (kind=RKIND), dimension(:), pointer :: sshCur, sshNew - real (kind=RKIND), dimension(:,:,:), pointer :: tracers, tracersCur, tracersNew + real (kind=RKIND), dimension(:,:,:), pointer :: tracerGroup, tracersCur, tracersNew - ! Forcing Array pointers - real (kind=RKIND), dimension(:), pointer :: seaIceEnergy + ! Diagnostics Field Pointers + type (field1DReal), pointer :: boundaryLayerDepthField, effectiveDensityField + type (field2DReal), pointer :: normalizedRelativeVorticityEdgeField, divergenceField, relativeVorticityField ! State/Tend Field Pointers type (field2DReal), pointer :: normalVelocityField, layerThicknessField - type (field3DReal), pointer :: tracersField + type (field3DReal), pointer :: tracersGroupField + + ! Tracer Group Iteartion + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: modifiedGroupName + character (len=StrKIND) :: configName ! Get config options call mpas_pool_get_config(domain % configs, 'config_mom_del4', config_mom_del4) @@ -177,6 +190,7 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ call mpas_pool_get_config(domain % configs, 'config_use_freq_filtered_thickness', config_use_freq_filtered_thickness) call mpas_pool_get_config(domain % configs, 'config_use_standardGM', config_use_standardGM) call mpas_pool_get_config(domain % configs, 'config_use_cvmix_kpp', config_use_cvmix_kpp) + call mpas_pool_get_config(domain % configs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) ! ! Initialize time_levs(2) with state at current time @@ -187,44 +201,81 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - - allocate(provisStatePool) + call mpas_pool_create_pool(provisStatePool) call mpas_pool_clone_pool(statePool, provisStatePool, 1) call mpas_pool_add_subpool(block % structs, 'provis_state', provisStatePool) + call mpas_threading_barrier() call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityNew, 2) call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) - call mpas_pool_get_array(statePool, 'tracers', tracersCur, 1) - call mpas_pool_get_array(statePool, 'tracers', tracersNew, 2) + call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessCur, 1) call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessNew, 2) call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceCur, 1) call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceNew, 2) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) - normalVelocityNew(:,:) = normalVelocityCur(:,:) - layerThicknessNew(:,:) = layerThicknessCur(:,:) + !$omp do schedule(runtime) private(k) + do iEdge = 1, nEdges + do k = 1, maxLevelEdgeTop(iEdge) + normalVelocityNew(k, iEdge) = normalVelocityCur(k, iEdge) + end do + end do + !$omp end do - do iCell = 1, nCells ! couple tracers to thickness + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells do k = 1, maxLevelCell(iCell) - tracersNew(:,k,iCell) = tracersCur(:,k,iCell) * layerThicknessCur(k,iCell) + layerThicknessNew(k, iCell) = layerThicknessCur(k, iCell) end do end do + !$omp end do + + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + + call mpas_pool_get_array(tracersPool, trim(groupItr % memberName), tracersCur, 1) + call mpas_pool_get_array(tracersPool, trim(groupItr % memberName), tracersNew, 2) + + if ( associated(tracersCur) .and. associated(tracersNew) ) then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells ! couple tracers to thickness + do k = 1, maxLevelCell(iCell) + tracersNew(:, k, iCell) = tracersCur(:, k, iCell) * layerThicknessCur(k, iCell) + end do + end do + !$omp end do + end if + end if + end do if (associated(highFreqThicknessCur)) then - highFreqThicknessNew(:,:) = highFreqThicknessCur(:,:) + !$omp do schedule(runtime) + do iCell = 1, nCells + highFreqThicknessNew(:, iCell) = highFreqThicknessCur(:, iCell) + end do + !$omp end do end if if (associated(lowFreqDivergenceCur)) then - lowFreqDivergenceNew(:,:) = lowFreqDivergenceCur(:,:) + !$omp do schedule(runtime) + do iCell = 1, nCells + lowFreqDivergenceNew(:, iCell) = lowFreqDivergenceCur(:, iCell) + end do + !$omp end do end if block => block % next @@ -261,18 +312,20 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ block => block % next end do + call mpas_threading_barrier() + ! Fourth-order Runge-Kutta, solving dy/dt = f(t,y) is typically written as follows - ! where h = delta t is the large time step. Here f(t,y) is the right hand side, + ! where h = delta t is the large time step. Here f(t,y) is the right hand side, ! called the tendencies in the code below. ! k_1 = h f(t_n , y_n) ! k_2 = h f(t_n + 1/2 h, y_n + 1/2 k_1) ! k_3 = h f(t_n + 1/2 h, y_n + 1/2 k_2) ! k_4 = h f(t_n + h, y_n + k_3) - ! y_{n+1} = y_n + 1/6 k_1 + 1/3 k_2 + 1/3 k_3 + 1/6 k_4 + ! y_{n+1} = y_n + 1/6 k_1 + 1/3 k_2 + 1/3 k_3 + 1/6 k_4 ! in index notation: ! k_{j+1} = h f(t_n + a_j h, y_n + a_j k_j) - ! y_{n+1} = y_n + sum ( b_j k_j ) + ! y_{n+1} = y_n + sum ( b_j k_j ) ! The coefficients of k_j are b_j = (1/6, 1/3, 1/3, 1/6) and are ! initialized here as delta t * b_j: @@ -295,58 +348,71 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ rk_substep_weights(4) = dt ! a_4 only used for ALE step, otherwise it is skipped. call mpas_timer_start("RK4-main loop") + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! BEGIN RK loop + ! BEGIN RK loop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! do rk_step = 1, 4 call mpas_pool_get_subpool(domain % blocklist % structs, 'diagnostics', diagnosticsPool) ! Update halos for diagnostic variables. - call mpas_timer_start("RK4-boundary layer depth halo update") if (config_use_cvmix_kpp) then + call mpas_timer_start("RK4-boundary layer depth halo update") call mpas_dmpar_field_halo_exch(domain, 'boundaryLayerDepth') + call mpas_timer_stop("RK4-boundary layer depth halo update") end if - call mpas_timer_stop("RK4-boundary layer depth halo update") + call mpas_timer_start("RK4-diagnostic halo update") + call mpas_dmpar_field_halo_exch(domain, 'normalizedRelativeVorticityEdge') - if (config_mom_del4 > 0.0) then + if (config_mom_del4 > 0.0_RKIND) then call mpas_dmpar_field_halo_exch(domain, 'divergence') call mpas_dmpar_field_halo_exch(domain, 'relativeVorticity') end if call mpas_timer_stop("RK4-diagnostic halo update") + call mpas_threading_barrier() + ! Compute tendencies for high frequency thickness - ! In RK4 notation, we are computing the right hand side f(t,y), + ! In RK4 notation, we are computing the right hand side f(t,y), ! which is the same as k_j / h. if (config_use_freq_filtered_thickness) then call mpas_timer_start("RK4-tendency computations") + block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) call ocn_tend_freq_filtered_thickness(tendPool, provisStatePool, diagnosticsPool, meshPool, 1) + call mpas_threading_barrier() block => block % next end do + call mpas_timer_stop("RK4-tendency computations") call mpas_timer_start("RK4-prognostic halo update") + call mpas_dmpar_field_halo_exch(domain, 'tendHighFreqThickness') call mpas_dmpar_field_halo_exch(domain, 'tendLowFreqDivergence') + call mpas_timer_stop("RK4-prognostic halo update") + call mpas_threading_barrier() + ! Compute next substep state for high frequency thickness. ! In RK4 notation, we are computing y_n + a_j k_j. - block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) @@ -354,230 +420,110 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ call mpas_pool_get_array(provisStatePool, 'highFreqThickness', highFreqThicknessProvis, 1) call mpas_pool_get_array(tendPool, 'highFreqThickness', highFreqThicknessTend) - highFreqThicknessProvis(:,:) = highFreqThicknessCur(:,:) + rk_substep_weights(rk_step) * highFreqThicknessTend(:,:) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + + !$omp do schedule(runtime) + do iCell = 1, nCells + highFreqThicknessProvis(:, iCell) = highFreqThicknessCur(:, iCell) + rk_substep_weights(rk_step) & + * highFreqThicknessTend(:, iCell) + end do + !$omp end do + call mpas_threading_barrier() block => block % next end do endif + ! Compute tendencies for velocity, thickness, and tracers. - ! In RK4 notation, we are computing the right hand side f(t,y), + ! In RK4 notation, we are computing the right hand side f(t,y), ! which is the same as k_j / h. - call mpas_timer_start("RK4-tendency computations") + call mpas_timer_start("RK4 vel/thick tendency computations") + block => domain % blocklist do while (associated(block)) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) - call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) - call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - call mpas_pool_get_subpool(block % structs, 'tend', tendPool) - call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) - - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) - call mpas_pool_get_array(statePool, 'ssh', sshCur, 1) - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) - - call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) - call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) - call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) - - call mpas_pool_get_array(provisStatePool, 'normalVelocity', normalVelocityProvis, 1) - call mpas_pool_get_array(provisStatePool, 'highFreqThickness', highFreqThicknessProvis, 1) - - ! advection of u uses u, while advection of layerThickness and tracers use normalTransportVelocity. - if (associated(highFreqThicknessProvis)) then - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & - layerThicknessCur,layerThicknessEdge, normalVelocityProvis, & - sshCur, rk_substep_weights(rk_step), & - vertAleTransportTop, err, highFreqThicknessProvis) - else - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & - layerThicknessCur,layerThicknessEdge, normalVelocityProvis, & - sshCur, rk_substep_weights(rk_step), & - vertAleTransportTop, err) - endif - - call ocn_tend_vel(tendPool, provisStatePool, forcingPool, diagnosticsPool, meshPool, scratchPool, 1) - - if (associated(highFreqThicknessProvis)) then - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & - layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & - sshCur, rk_substep_weights(rk_step), & - vertAleTransportTop, err, highFreqThicknessProvis) - else - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & - layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & - sshCur, rk_substep_weights(rk_step), & - vertAleTransportTop, err) - endif - - call ocn_tend_thick(tendPool, forcingPool, diagnosticsPool, meshPool) - - if (config_filter_btr_mode) then - call ocn_filter_btr_mode_tend_vel(tendPool, provisStatePool, diagnosticsPool, meshPool, 1) - endif - - call ocn_tend_tracer(tendPool, provisStatePool, forcingPool, diagnosticsPool, meshPool, scratchPool, dt, 1) + call ocn_time_integrator_rk4_compute_vel_tends( block, dt, rk_substep_weights(rk_step), err ) + + call ocn_time_integrator_rk4_compute_thick_tends( block, dt, rk_substep_weights(rk_step), err ) block => block % next end do - call mpas_timer_stop("RK4-tendency computations") + + call mpas_timer_stop("RK4 vel/thick tendency computations") ! Update halos for prognostic variables. - call mpas_timer_start("RK4-prognostic halo update") + call mpas_timer_start("RK4 vel/thick prognostic halo update") + call mpas_dmpar_field_halo_exch(domain, 'tendNormalVelocity') call mpas_dmpar_field_halo_exch(domain, 'tendLayerThickness') - call mpas_dmpar_field_halo_exch(domain, 'tendTracers') - call mpas_timer_stop("RK4-prognostic halo update") - - ! Compute next substep state for velocity, thickness, and tracers. - ! In RK4 notation, we are computing y_n + a_j k_j. - - call mpas_timer_start("RK4-update diagnostic variables") - if (rk_step < 4) then - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'tend', tendPool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) - call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) - call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) - - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) - call mpas_pool_get_array(statePool, 'tracers', tracersCur, 1) - call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceCur, 1) - - call mpas_pool_get_array(provisStatePool, 'normalVelocity', normalVelocityProvis, 1) - call mpas_pool_get_array(provisStatePool, 'layerThickness', layerThicknessProvis, 1) - call mpas_pool_get_array(provisStatePool, 'tracers', tracersProvis, 1) - call mpas_pool_get_array(provisStatePool, 'lowFreqDivergence', lowFreqDivergenceProvis, 1) + call mpas_timer_stop("RK4 vel/thick prognostic halo update") - call mpas_pool_get_array(tendPool, 'normalVelocity', normalVelocityTend) - call mpas_pool_get_array(tendPool, 'layerThickness', layerThicknessTend) - call mpas_pool_get_array(tendPool, 'tracers', tracersTend) - call mpas_pool_get_array(tendPool, 'lowFreqDivergence', lowFreqDivergenceTend) - - call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - - call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) - call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) - - normalVelocityProvis(:,:) = normalVelocityCur(:,:) + rk_substep_weights(rk_step) * normalVelocityTend(:,:) - - layerThicknessProvis(:,:) = layerThicknessCur(:,:) + rk_substep_weights(rk_step) * layerThicknessTend(:,:) - do iCell = 1, nCells - do k = 1, maxLevelCell(iCell) - tracersProvis(:,k,iCell) = ( layerThicknessCur(k,iCell) * tracersCur(:,k,iCell) & - + rk_substep_weights(rk_step) * tracersTend(:,k,iCell) & - ) / layerThicknessProvis(k,iCell) - end do + call mpas_timer_start("RK4 tracer tendency computations") - end do + block => domain % blocklist + do while (associated(block)) + call ocn_time_integrator_rk4_compute_tracer_tends( block, dt, rk_substep_weights(rk_step), err ) + block => block % next + end do - if (associated(lowFreqDivergenceCur)) then - lowFreqDivergenceProvis(:,:) = lowFreqDivergenceCur(:,:) + rk_substep_weights(rk_step) * lowFreqDivergenceTend(:,:) - end if + call mpas_timer_stop("RK4 tracer tendency computations") - if (config_prescribe_velocity) then - normalVelocityProvis(:,:) = normalVelocityCur(:,:) - end if + call mpas_timer_start("RK4 tracer prognostic halo update") - if (config_prescribe_thickness) then - layerThicknessProvis(:,:) = layerThicknessCur(:,:) - end if + call mpas_pool_get_subpool(domain % blocklist % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) - call ocn_diagnostic_solve(dt, provisStatePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 1) + call mpas_pool_begin_iteration(tracersTendPool) + do while ( mpas_pool_get_next_member(tracersTendPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + call mpas_dmpar_field_halo_exch(domain, trim(groupItr % memberName)) + end if + end do - ! ------------------------------------------------------------------ - ! Accumulating various parametrizations of the transport velocity - ! ------------------------------------------------------------------ - normalTransportVelocity(:,:) = normalVelocityProvis(:,:) + call mpas_timer_stop("RK4 tracer prognostic halo update") + call mpas_threading_barrier() - ! Compute normalGMBolusVelocity, relativeSlope and RediDiffVertCoef if respective flags are turned on - if (config_use_standardGM) then - call ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) - end if + ! Compute next substep state for velocity, thickness, and tracers. + ! In RK4 notation, we are computing y_n + a_j k_j. - if (config_use_standardGM) then - normalTransportVelocity(:,:) = normalTransportVelocity(:,:) + normalGMBolusVelocity(:,:) - end if - ! ------------------------------------------------------------------ - ! End: Accumulating various parametrizations of the transport velocity - ! ------------------------------------------------------------------ + call mpas_timer_start("RK4-update diagnostic variables") + if (rk_step < 4) then + block => domain % blocklist + do while (associated(block)) + call ocn_time_integrator_rk4_diagnostic_update(block, dt, rk_substep_weights(rk_step), err) block => block % next end do end if + call mpas_timer_stop("RK4-update diagnostic variables") + call mpas_threading_barrier() ! Accumulate update. ! In RK4 notation, we are computing b_j k_j and adding it to an accumulating sum so that we have - ! y_{n+1} = y_n + sum ( b_j k_j ) + ! y_{n+1} = y_n + sum ( b_j k_j ) ! after the fourth iteration. call mpas_timer_start("RK4-RK4 accumulate update") + block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'tend', tendPool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) - call mpas_pool_get_array(statePool, 'tracers', tracersCur, 1) - call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessCur, 1) - call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceCur, 1) - - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityNew, 2) - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) - call mpas_pool_get_array(statePool, 'tracers', tracersNew, 2) - call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessNew, 2) - call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceNew, 2) - - call mpas_pool_get_array(tendPool, 'normalVelocity', normalVelocityTend) - call mpas_pool_get_array(tendPool, 'layerThickness', layerThicknessTend) - call mpas_pool_get_array(tendPool, 'tracers', tracersTend) - call mpas_pool_get_array(tendPool, 'highFreqThickness', highFreqThicknessTend) - call mpas_pool_get_array(tendPool, 'lowFreqDivergence', lowFreqDivergenceTend) - - call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - - normalVelocityNew(:,:) = normalVelocityNew(:,:) + rk_weights(rk_step) * normalVelocityTend(:,:) - - layerThicknessNew(:,:) = layerThicknessNew(:,:) + rk_weights(rk_step) * layerThicknessTend(:,:) - - do iCell = 1, nCells - do k = 1, maxLevelCell(iCell) - tracersNew(:,k,iCell) = tracersNew(:,k,iCell) + rk_weights(rk_step) * tracersTend(:,k,iCell) - end do - end do - - if (associated(highFreqThicknessNew)) then - highFreqThicknessNew(:,:) = highFreqThicknessNew(:,:) + rk_weights(rk_step) * highFreqThicknessTend(:,:) - end if - - if (associated(lowFreqDivergenceNew)) then - lowFreqDivergenceNew(:,:) = lowFreqDivergenceNew(:,:) + rk_weights(rk_step) * lowFreqDivergenceTend(:,:) - end if + call ocn_time_integrator_rk4_accumulate_update(block, rk_weights(rk_step), err) block => block % next end do + call mpas_timer_stop("RK4-RK4 accumulate update") + call mpas_threading_barrier() end do !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! END RK loop + ! END RK loop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + call mpas_timer_stop("RK4-main loop") + call mpas_threading_barrier() ! ! A little clean up at the end: rescale tracer fields and compute diagnostics for new state @@ -587,97 +533,46 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ ! Rescale tracers block => domain % blocklist do while(associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) - call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) - call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - - call mpas_pool_get_array(statePool, 'tracers', tracersNew, 2) - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) + call ocn_time_integrator_rk4_cleanup(block, dt, err) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexSalinity) - - call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - - call mpas_pool_get_array(forcingPool, 'seaIceEnergy', seaIceEnergy) - - do iCell = 1, nCells - do k = 1, maxLevelCell(iCell) - tracersNew(:, k, iCell) = tracersNew(:, k, iCell) / layerThicknessNew(k, iCell) - end do - end do - - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 2) - call ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, layerThicknessNew, tracersNew, seaIceEnergy, err) block => block % next end do call mpas_timer_start("RK4-implicit vert mix") - block => domain % blocklist - do while(associated(block)) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) - call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityNew, 2) - call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + ! Update halo on u and tracers, which were just updated for implicit vertical mixing. If not done, + ! this leads to lack of volume conservation. It is required because halo updates in RK4 are only + ! conducted on tendencies, not on the velocity and tracer fields. So this update is required to + ! communicate the change due to implicit vertical mixing across the boundary. + call mpas_timer_start("RK4-implicit vert mix halos") - ! Call ocean diagnostic solve in preparation for vertical mixing. Note - ! it is called again after vertical mixing, because u and tracers change. - ! For Richardson vertical mixing, only density, layerThicknessEdge, and kineticEnergyCell need to - ! be computed. For kpp, more variables may be needed. Either way, this - ! could be made more efficient by only computing what is needed for the - ! implicit vmix routine that follows. - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 2) - - call ocn_vmix_implicit(dt, meshPool, diagnosticsPool, statePool, err, 2) - - ! ------------------------------------------------------------------ - ! Accumulating various parametrizations of the transport velocity - ! ------------------------------------------------------------------ - normalTransportVelocity(:,:) = normalVelocityNew(:,:) - - ! Compute normalGMBolusVelocity, slopeRelative and RediDiffVertCoef if respective flags are turned on - ! QC Note: this routine is called here to get updated k33. normalTransportVelocity probably does not need to be updated at all here. - if (config_use_standardGM) then - call ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) - end if + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) - if (config_use_standardGM) then - normalTransportVelocity(:,:) = normalTransportVelocity(:,:) + normalGMBolusVelocity(:,:) - end if - ! ------------------------------------------------------------------ - ! End: Accumulating various parametrizations of the transport velocity - ! ------------------------------------------------------------------ + call mpas_dmpar_field_halo_exch(domain, 'normalVelocity', timeLevel=2) - block => block % next + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + call mpas_dmpar_field_halo_exch(domain, groupItr % memberName, timeLevel=2) + end if end do - ! Update halo on u and tracers, which were just updated for implicit vertical mixing. If not done, - ! this leads to lack of volume conservation. It is required because halo updates in RK4 are only - ! conducted on tendencies, not on the velocity and tracer fields. So this update is required to - ! communicate the change due to implicit vertical mixing across the boundary. - call mpas_timer_start("RK4-implicit vert mix halos") - call mpas_dmpar_field_halo_exch(domain, 'normalVelocity', timeLevel=2) - call mpas_dmpar_field_halo_exch(domain, 'tracers', timeLevel=2) call mpas_timer_stop("RK4-implicit vert mix halos") call mpas_timer_stop("RK4-implicit vert mix") + call mpas_threading_barrier() block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - call mpas_pool_get_subpool(block % structs, 'average', averagePool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityNew, 2) @@ -705,60 +600,99 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ call mpas_pool_get_array(diagnosticsPool, 'surfaceVelocity', surfaceVelocity) call mpas_pool_get_array(diagnosticsPool, 'SSHGradient', SSHGradient) + if (config_prescribe_velocity) then - normalVelocityNew(:,:) = normalVelocityCur(:,:) + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalVelocityNew(:, iEdge) = normalVelocityCur(:, iEdge) + end do + !$omp end do end if if (config_prescribe_thickness) then - layerThicknessNew(:,:) = layerThicknessCur(:,:) + !$omp do schedule(runtime) + do iCell = 1, nCells + layerThicknessNew(:, iCell) = layerThicknessCur(:, iCell) + end do + !$omp end do end if - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 2) + call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, 2) + call mpas_threading_barrier() + + ! Update the effective desnity in land ice if we're coupling to land ice + call ocn_effective_density_in_land_ice_update(meshPool, forcingPool, statePool, scratchPool, err) ! ------------------------------------------------------------------ ! Accumulating various parameterizations of the transport velocity ! ------------------------------------------------------------------ - normalTransportVelocity(:,:) = normalVelocityNew(:,:) + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalTransportVelocity(:, iEdge) = normalVelocityNew(:, iEdge) + end do + !$omp end do + call mpas_threading_barrier() ! Compute normalGMBolusVelocity and the tracer transport velocity if (config_use_standardGM) then call ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) end if + call mpas_threading_barrier() if (config_use_standardGM) then - normalTransportVelocity(:,:) = normalTransportVelocity(:,:) + normalGMBolusVelocity(:,:) + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalTransportVelocity(:, iEdge) = normalTransportVelocity(:, iEdge) + normalGMBolusVelocity(:, iEdge) + end do + !$omp end do end if ! ------------------------------------------------------------------ ! End: Accumulating various parameterizations of the transport velocity ! ------------------------------------------------------------------ - call mpas_reconstruct(meshPool, normalVelocityNew, & - velocityX, velocityY, velocityZ, & - velocityZonal, velocityMeridional & - ) + call mpas_reconstruct(meshPool, normalVelocityNew, & + velocityX, velocityY, velocityZ, & + velocityZonal, velocityMeridional, & + includeHalos = .true.) - call mpas_reconstruct(meshPool, gradSSH, & - gradSSHX, gradSSHY, gradSSHZ, & - gradSSHZonal, gradSSHMeridional & - ) + call mpas_reconstruct(meshPool, gradSSH, & + gradSSHX, gradSSHY, gradSSHZ, & + gradSSHZonal, gradSSHMeridional, & + includeHalos = .true.) + call mpas_threading_barrier() - surfaceVelocity(indexSurfaceVelocityZonal, :) = velocityZonal(1, :) - surfaceVelocity(indexSurfaceVelocityMeridional, :) = velocityMeridional(1, :) + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceVelocity(indexSurfaceVelocityZonal, iCell) = velocityZonal(1, iCell) + surfaceVelocity(indexSurfaceVelocityMeridional, iCell) = velocityMeridional(1, iCell) - SSHGradient(indexSSHGradientZonal, :) = gradSSHZonal(1, :) - SSHGradient(indexSSHGradientMeridional, :) = gradSSHMeridional(1, :) + SSHGradient(indexSSHGradientZonal, iCell) = gradSSHZonal(iCell) + SSHGradient(indexSSHGradientMeridional, iCell) = gradSSHMeridional(iCell) + end do + !$omp end do - call ocn_time_average_accumulate(averagePool, statePool, diagnosticsPool, 2) - call ocn_time_average_coupled_accumulate(diagnosticsPool, forcingPool) + call ocn_time_average_coupled_accumulate(diagnosticsPool, statePool, forcingPool, 2) if (config_use_standardGM) then call ocn_reconstruct_gm_vectors(diagnosticsPool, meshPool) end if + call mpas_threading_barrier() block => block % next end do + + if (trim(config_land_ice_flux_mode) == 'coupled') then + call mpas_timer_start("RK4-effective density halo") + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_field(statePool, 'effectiveDensityInLandIce', effectiveDensityField, 2) + call mpas_dmpar_exch_halo_field(effectiveDensityField) + call mpas_timer_stop("RK4-effective density halo") + end if + call mpas_timer_stop("RK4-cleaup phase") + call mpas_threading_barrier() + block => domain % blocklist do while(associated(block)) call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) @@ -768,9 +702,663 @@ subroutine ocn_time_integrator_rk4(domain, dt)!{{{ call mpas_pool_remove_subpool(block % structs, 'provis_state') block => block % next end do + call mpas_threading_barrier() end subroutine ocn_time_integrator_rk4!}}} + subroutine ocn_time_integrator_rk4_compute_vel_tends(block, dt, rkWeight, err)!{{{ + type (block_type), intent(in) :: block + real (kind=RKIND), intent(in) :: dt + real (kind=RKIND), intent(in) :: rkWeight + integer, intent(out) :: err + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool + type (mpas_pool_type), pointer :: statePool, diagnosticsPool, forcingPool + type (mpas_pool_type), pointer :: scratchPool, tendPool, provisStatePool + type (mpas_pool_type), pointer :: tracersPool + + real (kind=RKIND), dimension(:), pointer :: sshCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessCur, normalVelocityCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessEdge, vertAleTransportTop + real (kind=RKIND), dimension(:, :), pointer :: normalTransportVelocity + real (kind=RKIND), dimension(:, :), pointer :: normalVelocityProvis, highFreqThicknessProvis + + logical, pointer :: config_filter_btr_mode + + err = 0 + + call mpas_pool_get_config(block % configs, 'config_filter_btr_mode', config_filter_btr_mode) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) + call mpas_pool_get_array(statePool, 'ssh', sshCur, 1) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) + + call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + + call mpas_pool_get_array(provisStatePool, 'normalVelocity', normalVelocityProvis, 1) + call mpas_pool_get_array(provisStatePool, 'highFreqThickness', highFreqThicknessProvis, 1) + + ! advection of u uses u, while advection of layerThickness and tracers use normalTransportVelocity. + if (associated(highFreqThicknessProvis)) then + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur,layerThicknessEdge, normalVelocityProvis, & + sshCur, rkWeight, & + vertAleTransportTop, err, highFreqThicknessProvis) + else + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur,layerThicknessEdge, normalVelocityProvis, & + sshCur, rkWeight, & + vertAleTransportTop, err) + endif + call mpas_threading_barrier() + + call ocn_tend_vel(tendPool, provisStatePool, forcingPool, diagnosticsPool, meshPool, scratchPool, 1) + call mpas_threading_barrier() + + end subroutine ocn_time_integrator_rk4_compute_vel_tends!}}} + + subroutine ocn_time_integrator_rk4_compute_thick_tends(block, dt, rkWeight, err)!{{{ + type (block_type), intent(in) :: block + real (kind=RKIND), intent(in) :: dt + real (kind=RKIND), intent(in) :: rkWeight + integer, intent(out) :: err + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool + type (mpas_pool_type), pointer :: statePool, diagnosticsPool, forcingPool + type (mpas_pool_type), pointer :: scratchPool, tendPool, provisStatePool + type (mpas_pool_type), pointer :: tracersPool + + real (kind=RKIND), dimension(:), pointer :: sshCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessCur, normalVelocityCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessEdge, vertAleTransportTop + real (kind=RKIND), dimension(:, :), pointer :: normalTransportVelocity + real (kind=RKIND), dimension(:, :), pointer :: normalVelocityProvis, highFreqThicknessProvis + + logical, pointer :: config_filter_btr_mode + + err = 0 + + call mpas_pool_get_config(block % configs, 'config_filter_btr_mode', config_filter_btr_mode) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) + call mpas_pool_get_array(statePool, 'ssh', sshCur, 1) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) + + call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + + call mpas_pool_get_array(provisStatePool, 'normalVelocity', normalVelocityProvis, 1) + call mpas_pool_get_array(provisStatePool, 'highFreqThickness', highFreqThicknessProvis, 1) + + ! advection of u uses u, while advection of layerThickness and tracers use normalTransportVelocity. + if (associated(highFreqThicknessProvis)) then + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & + sshCur, rkWeight, & + vertAleTransportTop, err, highFreqThicknessProvis) + else + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & + sshCur, rkWeight, & + vertAleTransportTop, err) + endif + call mpas_threading_barrier() + + call ocn_tend_thick(tendPool, forcingPool, diagnosticsPool, meshPool) + + call mpas_threading_barrier() + + end subroutine ocn_time_integrator_rk4_compute_thick_tends!}}} + + subroutine ocn_time_integrator_rk4_compute_tracer_tends(block, dt, rkWeight, err)!{{{ + type (block_type), intent(in) :: block + real (kind=RKIND), intent(in) :: dt + real (kind=RKIND), intent(in) :: rkWeight + integer, intent(out) :: err + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool + type (mpas_pool_type), pointer :: statePool, diagnosticsPool, forcingPool + type (mpas_pool_type), pointer :: scratchPool, tendPool, provisStatePool + type (mpas_pool_type), pointer :: swForcingPool, tracersPool + + real (kind=RKIND), dimension(:), pointer :: sshCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessCur, normalVelocityCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessEdge, vertAleTransportTop + real (kind=RKIND), dimension(:, :), pointer :: normalTransportVelocity + real (kind=RKIND), dimension(:, :), pointer :: normalVelocityProvis, highFreqThicknessProvis + + logical, pointer :: config_filter_btr_mode + + err = 0 + + call mpas_pool_get_config(block % configs, 'config_filter_btr_mode', config_filter_btr_mode) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) + call mpas_pool_get_subpool(block % structs, 'shortwave', swForcingPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) + call mpas_pool_get_array(statePool, 'ssh', sshCur, 1) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) + + call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + + call mpas_pool_get_array(provisStatePool, 'normalVelocity', normalVelocityProvis, 1) + call mpas_pool_get_array(provisStatePool, 'highFreqThickness', highFreqThicknessProvis, 1) + + ! advection of u uses u, while advection of layerThickness and tracers use normalTransportVelocity. + if (associated(highFreqThicknessProvis)) then + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & + sshCur, rkWeight, & + vertAleTransportTop, err, highFreqThicknessProvis) + else + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & + sshCur, rkWeight, & + vertAleTransportTop, err) + endif + call mpas_threading_barrier() + + if (config_filter_btr_mode) then + call ocn_filter_btr_mode_tend_vel(tendPool, provisStatePool, diagnosticsPool, meshPool, 1) + endif + call mpas_threading_barrier() + + call ocn_tend_tracer(tendPool, provisStatePool, forcingPool, diagnosticsPool, meshPool, swForcingPool, & + scratchPool, dt, activeTracersOnlyIn=.false., timeLevelIn=1) + call mpas_threading_barrier() + + end subroutine ocn_time_integrator_rk4_compute_tracer_tends!}}} + + subroutine ocn_time_integrator_rk4_compute_tends(block, dt, rkWeight, err)!{{{ + type (block_type), intent(in) :: block + real (kind=RKIND), intent(in) :: dt + real (kind=RKIND), intent(in) :: rkWeight + integer, intent(out) :: err + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool + type (mpas_pool_type), pointer :: statePool, diagnosticsPool, forcingPool + type (mpas_pool_type), pointer :: scratchPool, tendPool, provisStatePool + type (mpas_pool_type), pointer :: swForcingPool, tracersPool + + real (kind=RKIND), dimension(:), pointer :: sshCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessCur, normalVelocityCur + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessEdge, vertAleTransportTop + real (kind=RKIND), dimension(:, :), pointer :: normalTransportVelocity + real (kind=RKIND), dimension(:, :), pointer :: normalVelocityProvis, highFreqThicknessProvis + + logical, pointer :: config_filter_btr_mode + + err = 0 + + call mpas_pool_get_config(block % configs, 'config_filter_btr_mode', config_filter_btr_mode) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) + call mpas_pool_get_subpool(block % structs, 'shortwave', swForcingPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) + call mpas_pool_get_array(statePool, 'ssh', sshCur, 1) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) + + call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) + call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + + call mpas_pool_get_array(provisStatePool, 'normalVelocity', normalVelocityProvis, 1) + call mpas_pool_get_array(provisStatePool, 'highFreqThickness', highFreqThicknessProvis, 1) + + ! advection of u uses u, while advection of layerThickness and tracers use normalTransportVelocity. + if (associated(highFreqThicknessProvis)) then + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur,layerThicknessEdge, normalVelocityProvis, & + sshCur, rkWeight, & + vertAleTransportTop, err, highFreqThicknessProvis) + else + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur,layerThicknessEdge, normalVelocityProvis, & + sshCur, rkWeight, & + vertAleTransportTop, err) + endif + call mpas_threading_barrier() + + call ocn_tend_vel(tendPool, provisStatePool, forcingPool, diagnosticsPool, meshPool, scratchPool, 1) + call mpas_threading_barrier() + + if (associated(highFreqThicknessProvis)) then + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & + sshCur, rkWeight, & + vertAleTransportTop, err, highFreqThicknessProvis) + else + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & + layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & + sshCur, rkWeight, & + vertAleTransportTop, err) + endif + call mpas_threading_barrier() + + call ocn_tend_thick(tendPool, forcingPool, diagnosticsPool, meshPool) + + if (config_filter_btr_mode) then + call ocn_filter_btr_mode_tend_vel(tendPool, provisStatePool, diagnosticsPool, meshPool, 1) + endif + call mpas_threading_barrier() + + call ocn_tend_tracer(tendPool, provisStatePool, forcingPool, diagnosticsPool, meshPool, swForcingPool, & + scratchPool, dt, activeTracersOnlyIn=.false., timeLevelIn=1) + call mpas_threading_barrier() + + end subroutine ocn_time_integrator_rk4_compute_tends!}}} + + subroutine ocn_time_integrator_rk4_diagnostic_update(block, dt, rkWeight, err)!{{{ + type (block_type), intent(in) :: block + real (kind=RKIND), intent(in) :: dt + real (kind=RKIND), intent(in) :: rkWeight + integer, intent(out) :: err + + logical, pointer :: config_prescribe_velocity, config_prescribe_thickness, config_use_standardGM + + integer, pointer :: nCells, nEdges + integer :: iCell, iEdge, k + + type (mpas_pool_type), pointer :: statePool, tendPool, meshPool, scratchPool + type (mpas_pool_type), pointer :: diagnosticsPool, provisStatePool, forcingPool + type (mpas_pool_type), pointer :: tracersPool, tracersTendPool, provisTracersPool + + real (kind=RKIND), dimension(:, :), pointer :: normalVelocityCur, normalVelocityProvis, normalVelocityTend + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessCur, layerThicknessProvis, layerThicknessTend + real (kind=RKIND), dimension(:, :), pointer :: lowFreqDivergenceCur, lowFreqDivergenceProvis, lowFreqDivergenceTend + real (kind=RKIND), dimension(:, :), pointer :: normalTransportVelocity, normalGMBolusVelocity + + real (kind=RKIND), dimension(:, :, :), pointer :: tracersGroupCur, tracersGroupProvis, tracersGroupTend + + integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop + + logical, pointer :: config_use_tracerGroup + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: modifiedGroupName + character (len=StrKIND) :: configName + + err = 0 + + call mpas_pool_get_config(block % configs, 'config_prescribe_velocity', config_prescribe_velocity) + call mpas_pool_get_config(block % configs, 'config_prescribe_thickness', config_prescribe_thickness) + call mpas_pool_get_config(block % configs, 'config_use_standardGM', config_use_standardGM) + + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'provis_state', provisStatePool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) + + call mpas_pool_get_subpool(provisStatePool, 'tracers', provisTracersPool) + + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) + call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceCur, 1) + + call mpas_pool_get_array(provisStatePool, 'normalVelocity', normalVelocityProvis, 1) + call mpas_pool_get_array(provisStatePool, 'layerThickness', layerThicknessProvis, 1) + call mpas_pool_get_array(provisStatePool, 'lowFreqDivergence', lowFreqDivergenceProvis, 1) + + call mpas_pool_get_array(tendPool, 'normalVelocity', normalVelocityTend) + call mpas_pool_get_array(tendPool, 'layerThickness', layerThicknessTend) + call mpas_pool_get_array(tendPool, 'lowFreqDivergence', lowFreqDivergenceTend) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) + + call mpas_threading_barrier() + + !$omp do schedule(runtime) private(k) + do iEdge = 1, nEdges + do k = 1, maxLevelEdgeTop(iEdge) + normalVelocityProvis(k, iEdge) = normalVelocityCur(k, iEdge) + rkWeight & + * normalVelocityTend(k, iEdge) + end do + end do + !$omp end do + + + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + layerThicknessProvis(k, iCell) = layerThicknessCur(k, iCell) + rkWeight & + * layerThicknessTend(k, iCell) + end do + end do + !$omp end do + + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + configName = 'config_use_' // trim(groupItr % memberName) + call mpas_pool_get_config(block % configs, configName, config_use_tracerGroup) + + if ( config_use_tracerGroup ) then + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroupCur, 1) + call mpas_pool_get_array(provisTracersPool, groupItr % memberName, tracersGroupProvis, 1) + + modifiedGroupName = trim(groupItr % memberName) // 'Tend' + call mpas_pool_get_array(tracersTendPool, modifiedGroupName, tracersGroupTend) + if ( associated(tracersGroupProvis) .and. associated(tracersGroupCur) .and. associated(tracersGroupTend) ) then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + tracersGroupProvis(:, k, iCell) = ( layerThicknessCur(k, iCell) * tracersGroupCur(:, k, iCell) & + + rkWeight * tracersGroupTend(:, k, iCell) & + ) / layerThicknessProvis(k, iCell) + end do + + end do + !$omp end do + end if + end if + end if + end do + + if (associated(lowFreqDivergenceCur)) then + !$omp do schedule(runtime) + do iCell = 1, nCells + lowFreqDivergenceProvis(:, iCell) = lowFreqDivergenceCur(:, iCell) + rkWeight & + * lowFreqDivergenceTend(:, iCell) + end do + !$omp end do + end if + + if (config_prescribe_velocity) then + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalVelocityProvis(:, iEdge) = normalVelocityCur(:, iEdge) + end do + !$omp end do + end if + + if (config_prescribe_thickness) then + !$omp do schedule(runtime) + do iCell = 1, nCells + layerThicknessProvis(:, iCell) = layerThicknessCur(:, iCell) + end do + !$omp end do + end if + call mpas_threading_barrier() + + call ocn_diagnostic_solve(dt, provisStatePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, 1) + call mpas_threading_barrier() + + ! ------------------------------------------------------------------ + ! Accumulating various parametrizations of the transport velocity + ! ------------------------------------------------------------------ + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalTransportVelocity(:, iEdge) = normalVelocityProvis(:, iEdge) + end do + !$omp end do + call mpas_threading_barrier() + + ! Compute normalGMBolusVelocity, relativeSlope and RediDiffVertCoef if respective flags are turned on + if (config_use_standardGM) then + call ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) + end if + call mpas_threading_barrier() + + if (config_use_standardGM) then + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalTransportVelocity(:, iEdge) = normalTransportVelocity(:, iEdge) + normalGMBolusVelocity(:,iEdge) + end do + !$omp end do + end if + call mpas_threading_barrier() + ! ------------------------------------------------------------------ + ! End: Accumulating various parametrizations of the transport velocity + ! ------------------------------------------------------------------ + + end subroutine ocn_time_integrator_rk4_diagnostic_update!}}} + + subroutine ocn_time_integrator_rk4_accumulate_update(block, rkWeight, err)!{{{ + type (block_type), intent(in) :: block + real (kind=RKIND), intent(in) :: rkWeight + integer, intent(out) :: err + + integer, pointer :: nCells, nEdges + integer :: iCell, iEdge, k + + type (mpas_pool_type), pointer :: statePool, tendPool, meshPool + type (mpas_pool_type), pointer :: tracersPool, tracersTendPool + + real (kind=RKIND), dimension(:, :), pointer :: normalVelocityNew, normalVelocityTend + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessNew, layerThicknessTend + real (kind=RKIND), dimension(:, :), pointer :: highFreqThicknessNew, highFreqThicknessTend + real (kind=RKIND), dimension(:, :), pointer :: lowFreqDivergenceNew, lowFreqDivergenceTend + + real (kind=RKIND), dimension(:, :, :), pointer :: tracersGroupNew, tracersGroupTend + + integer, dimension(:), pointer :: maxLevelCell + + logical, pointer :: config_use_tracerGroup + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: modifiedGroupName + character (len=StrKIND) :: configName + + err = 0 + + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) + + !call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) + !call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) + !call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessCur, 1) + !call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceCur, 1) + + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityNew, 2) + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) + call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessNew, 2) + call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceNew, 2) + + call mpas_pool_get_array(tendPool, 'normalVelocity', normalVelocityTend) + call mpas_pool_get_array(tendPool, 'layerThickness', layerThicknessTend) + + call mpas_pool_get_array(tendPool, 'highFreqThickness', highFreqThicknessTend) + call mpas_pool_get_array(tendPool, 'lowFreqDivergence', lowFreqDivergenceTend) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + layerThicknessNew(k, iCell) = layerThicknessNew(k, iCell) + rkWeight * layerThicknessTend(k, iCell) + end do + end do + !$omp end do + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalVelocityNew(:, iEdge) = normalVelocityNew(:, iEdge) + rkWeight * normalVelocityTend(:, iEdge) + end do + !$omp end do + + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + configName = 'config_use_' // trim(groupItr % memberName) + call mpas_pool_get_config(block % configs, configName, config_use_tracerGroup) + + if ( config_use_tracerGroup ) then + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroupNew, 2) + + modifiedGroupName = trim(groupItr % memberName) // 'Tend' + call mpas_pool_get_array(tracersTendPool, modifiedGroupName, tracersGroupTend) + if ( associated(tracersGroupNew) .and. associated(tracersGroupTend) ) then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + tracersGroupNew(:, k, iCell) = tracersGroupNew(:, k, iCell) + rkWeight & + * tracersGroupTend(:, k, iCell) + end do + end do + !$omp end do + end if + end if + end if + end do + + if (associated(highFreqThicknessNew)) then + !$omp do schedule(runtime) + do iCell = 1, nCells + highFreqThicknessNew(:, iCell) = highFreqThicknessNew(:, iCell) + rkWeight * highFreqThicknessTend(:, iCell) + end do + !$omp end do + end if + + if (associated(lowFreqDivergenceNew)) then + !$omp do schedule(runtime) + do iCell = 1, nCells + lowFreqDivergenceNew(:, iCell) = lowFreqDivergenceNew(:, iCell) + rkWeight * lowFreqDivergenceTend(:, iCell) + end do + !$omp end do + end if + + end subroutine ocn_time_integrator_rk4_accumulate_update!}}} + + subroutine ocn_time_integrator_rk4_cleanup(block, dt, err)!{{{ + type (block_type), intent(in) :: block + real (kind=RKIND), intent(in) :: dt + integer, intent(out) :: err + + integer, pointer :: nCells, nEdges, indexTemperature, indexSalinity + integer :: iCell, iEdge, k + + type (mpas_pool_type), pointer :: statePool, meshPool, forcingPool + type (mpas_pool_type), pointer :: diagnosticsPool, scratchPool + type (mpas_pool_type), pointer :: tracersPool + + real (kind=RKIND), dimension(:, :), pointer :: layerThicknessNew, normalVelocityNew + real (kind=RKIND), dimension(:, :), pointer :: normalTransportVelocity, normalGMBolusVelocity + real (kind=RKIND), dimension(:, :, :), pointer :: tracersGroupNew + + integer, dimension(:), pointer :: maxLevelCell + + logical, pointer :: config_use_tracerGroup + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: modifiedGroupName + character (len=StrKIND) :: configName + + logical, pointer :: config_use_standardGM + + err = 0 + + call mpas_pool_get_config(block % configs, 'config_use_standardGM', config_use_standardGM) + + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityNew, 2) + + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroupNew, 2) + if ( associated(tracersGroupNew) ) then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + tracersGroupNew(:, k, iCell) = tracersGroupNew(:, k, iCell) / layerThicknessNew(k, iCell) + end do + end do + !$omp end do + end if + end if + end do + + call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, 2) + call mpas_threading_barrier() + + call ocn_vmix_implicit(dt, meshPool, diagnosticsPool, statePool, forcingPool, scratchPool, err, 2) + call mpas_threading_barrier() + + end subroutine ocn_time_integrator_rk4_cleanup!}}} + end module ocn_time_integration_rk4 ! vim: foldmethod=marker diff --git a/src/core_ocean/mode_forward/mpas_ocn_time_integration_split.F b/src/core_ocean/mode_forward/mpas_ocn_time_integration_split.F index f52a987b96..73df467ef0 100644 --- a/src/core_ocean/mode_forward/mpas_ocn_time_integration_split.F +++ b/src/core_ocean/mode_forward/mpas_ocn_time_integration_split.F @@ -28,6 +28,9 @@ module ocn_time_integration_split use mpas_vector_reconstruction use mpas_spline_interpolation use mpas_timer + use mpas_threading + use mpas_timekeeping + use mpas_log use ocn_tendency use ocn_diagnostics @@ -35,10 +38,9 @@ module ocn_time_integration_split use ocn_equation_of_state use ocn_vmix - use ocn_time_average use ocn_time_average_coupled - use ocn_sea_ice + use ocn_effective_density_in_land_ice implicit none private @@ -58,6 +60,10 @@ module ocn_time_integration_split public :: ocn_time_integrator_split, ocn_time_integration_split_init + character (len=*), parameter :: subcycleGroupName = 'subcycleFields' + character (len=*), parameter :: finalBtrGroupName = 'finalBtrFields' + integer :: nBtrSubcycles + contains !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -68,19 +74,19 @@ module ocn_time_integration_split !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 !> \details -!> This routine integrates a single time step (dt) using a +!> This routine integrates a master time step (dt) using a !> split explicit time integrator. ! !----------------------------------------------------------------------- subroutine ocn_time_integrator_split(domain, dt)!{{{ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! Advance model state forward in time by the specified time step using + ! Advance model state forward in time by the specified time step using ! Split_Explicit timestepping scheme ! - ! Input: domain - current model state in time level 1 (e.g., time_levs(1)state%h(:,:)) + ! Input: domain - current model state in time level 1 (e.g., time_levs(1)state%h(:,:)) ! plus mesh meta-data - ! Output: domain - upon exit, time level 2 (e.g., time_levs(2)%state%h(:,:)) contains + ! Output: domain - upon exit, time level 2 (e.g., time_levs(2)%state%h(:,:)) contains ! model state advanced forward in time by dt seconds !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -90,13 +96,15 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ real (kind=RKIND), intent(in) :: dt type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool type (mpas_pool_type), pointer :: meshPool type (mpas_pool_type), pointer :: verticalMeshPool type (mpas_pool_type), pointer :: diagnosticsPool type (mpas_pool_type), pointer :: tendPool + type (mpas_pool_type), pointer :: tracersTendPool type (mpas_pool_type), pointer :: forcingPool - type (mpas_pool_type), pointer :: averagePool type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: swForcingPool type (dm_info) :: dminfo integer :: iCell, i,k,j, iEdge, cell1, cell2, split_explicit_step, split, & @@ -109,31 +117,40 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ integer :: useVelocityCorrection, err real (kind=RKIND), dimension(:,:), pointer :: & vertViscTopOfEdge, vertDiffTopOfCell - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroup real (kind=RKIND), dimension(:), allocatable:: uTemp + real (kind=RKIND), dimension(:), pointer :: btrvel_temp + type (field1DReal), pointer :: btrvel_tempField real (kind=RKIND), dimension(:,:), allocatable:: tracersTemp - + logical :: activeTracersOnly ! if true only compute tendencies for active tracers integer :: tsIter + integer :: edgeHaloComputeCounter, cellHaloComputeCounter ! Config options character (len=StrKIND), pointer :: config_time_integrator integer, pointer :: config_n_bcl_iter_mid, config_n_bcl_iter_beg, config_n_bcl_iter_end - integer, pointer :: config_n_ts_iter, config_btr_subcycle_loop_factor, config_n_btr_subcycles - integer, pointer :: config_n_btr_cor_iter + integer, pointer :: config_n_ts_iter, config_btr_subcycle_loop_factor + integer, pointer :: config_n_btr_cor_iter, config_num_halos logical, pointer :: config_use_standardGM logical, pointer :: config_use_freq_filtered_thickness, config_btr_solve_SSH2, config_filter_btr_mode logical, pointer :: config_vel_correction, config_prescribe_velocity, config_prescribe_thickness logical, pointer :: config_use_cvmix_kpp + logical, pointer :: config_use_tracerGroup + logical, pointer :: config_compute_active_tracer_budgets + + character (len=StrKIND), pointer :: config_land_ice_flux_mode real (kind=RKIND), pointer :: config_mom_del4, config_btr_gam1_velWt1, config_btr_gam2_SSHWt1 real (kind=RKIND), pointer :: config_btr_gam3_velWt2 ! Dimensions - integer, pointer :: nCells, nEdges, nVertLevels, num_tracers, startIndex, endIndex + integer :: nCells, nEdges + integer, pointer :: nCellsPtr, nEdgesPtr, nVertLevels, num_tracersGroup, startIndex, endIndex integer, pointer :: indexTemperature, indexSalinity integer, pointer :: indexSurfaceVelocityZonal, indexSurfaceVelocityMeridional integer, pointer :: indexSSHGradientZonal, indexSSHGradientMeridional + integer, dimension(:), pointer :: nCellsArray, nEdgesArray ! Mesh array pointers integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop, nEdgesOnEdge, nEdgesOnCell @@ -154,14 +171,14 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ real (kind=RKIND), dimension(:,:), pointer :: layerThicknessCur, layerThicknessNew real (kind=RKIND), dimension(:,:), pointer :: highFreqThicknessCur, highFreqThicknessNew real (kind=RKIND), dimension(:,:), pointer :: lowFreqDivergenceCur, lowFreqDivergenceNew - real (kind=RKIND), dimension(:,:,:), pointer :: tracersCur, tracersNew + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroupCur, tracersGroupNew ! Tend Array Pointers real (kind=RKIND), dimension(:), pointer :: sshTend real (kind=RKIND), dimension(:,:), pointer :: highFreqThicknessTend real (kind=RKIND), dimension(:,:), pointer :: lowFreqDivergenceTend real (kind=RKIND), dimension(:,:), pointer :: normalVelocityTend, layerThicknessTend - real (kind=RKIND), dimension(:,:,:), pointer :: tracersTend + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroupTend, activeTracersTend ! Diagnostics Array Pointers real (kind=RKIND), dimension(:), pointer :: barotropicForcing, barotropicThicknessFlux @@ -169,13 +186,37 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ real (kind=RKIND), dimension(:,:), pointer :: vertAleTransportTop real (kind=RKIND), dimension(:,:), pointer :: velocityX, velocityY, velocityZ real (kind=RKIND), dimension(:,:), pointer :: velocityZonal, velocityMeridional - real (kind=RKIND), dimension(:,:), pointer :: gradSSH - real (kind=RKIND), dimension(:,:), pointer :: gradSSHX, gradSSHY, gradSSHZ - real (kind=RKIND), dimension(:,:), pointer :: gradSSHZonal, gradSSHMeridional + real (kind=RKIND), dimension(:), pointer :: gradSSH + real (kind=RKIND), dimension(:), pointer :: gradSSHX, gradSSHY, gradSSHZ + real (kind=RKIND), dimension(:), pointer :: gradSSHZonal, gradSSHMeridional real (kind=RKIND), dimension(:,:), pointer :: surfaceVelocity, SSHGradient - ! Forcing Array Pointer - real (kind=RKIND), dimension(:), pointer :: seaIceEnergy + ! Diagnostics Field Pointers + type (field2DReal), pointer :: normalizedRelativeVorticityEdgeField, divergenceField, relativeVorticityField + type (field1DReal), pointer :: barotropicThicknessFluxField, boundaryLayerDepthField, effectiveDensityField + ! tracer tendencies brought in here to normalize by new layer thickness + real (kind=RKIND), dimension(:,:,:), pointer :: & + activeTracerHorizontalAdvectionTendency, & + activeTracerVerticalAdvectionTendency, & + activeTracerSurfaceFluxTendency, & + activeTracerNonLocalTendency + + real (kind=RKIND), dimension(:,:), pointer :: & + temperatureShortWaveTendency + ! State/Tend Field Pointers + type (field1DReal), pointer :: normalBarotropicVelocitySubcycleField, sshSubcycleField + type (field2DReal), pointer :: highFreqThicknessField, lowFreqDivergenceField + type (field2DReal), pointer :: normalBaroclinicVelocityField, layerThicknessField + type (field2DReal), pointer :: normalVelocityField + type (field3DReal), pointer :: tracersGroupField + + ! tracer iterators + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: modifiedGroupName + character (len=StrKIND) :: configName + integer :: threadNum + + integer :: temp_mask call mpas_timer_start("se timestep") @@ -183,7 +224,6 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_config(domain % configs, 'config_n_bcl_iter_mid', config_n_bcl_iter_mid) call mpas_pool_get_config(domain % configs, 'config_n_bcl_iter_end', config_n_bcl_iter_end) call mpas_pool_get_config(domain % configs, 'config_n_ts_iter', config_n_ts_iter) - call mpas_pool_get_config(domain % configs, 'config_n_btr_subcycles', config_n_btr_subcycles) call mpas_pool_get_config(domain % configs, 'config_btr_subcycle_loop_factor', config_btr_subcycle_loop_factor) call mpas_pool_get_config(domain % configs, 'config_btr_gam1_velWt1', config_btr_gam1_velWt1) call mpas_pool_get_config(domain % configs, 'config_btr_gam3_velWt2', config_btr_gam3_velWt2) @@ -205,7 +245,11 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_config(domain % configs, 'config_use_standardGM', config_use_standardGM) call mpas_pool_get_config(domain % configs, 'config_use_cvmix_kpp', config_use_cvmix_kpp) + call mpas_pool_get_config(domain % configs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + call mpas_pool_get_config(domain % configs, 'config_num_halos', config_num_halos) + + call mpas_pool_get_config(domain % configs, 'config_compute_active_tracer_budgets', config_compute_active_tracer_budgets) allocate(n_bcl_iter(config_n_ts_iter)) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -216,12 +260,16 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_timer_start("se prep") block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCellsPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_array(statePool, 'normalBaroclinicVelocity', normalBaroclinicVelocityCur, 1) call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocityCur, 1) @@ -237,25 +285,30 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) - call mpas_pool_get_array(statePool, 'tracers', tracersCur, 1) - call mpas_pool_get_array(statePool, 'tracers', tracersNew, 2) - call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessCur, 1) call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessNew, 2) call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceCur, 1) call mpas_pool_get_array(statePool, 'lowFreqDivergence', lowFreqDivergenceNew, 2) + call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) + + nCells = nCellsPtr + nEdges = nEdgesPtr ! Initialize * variables that are used to compute baroclinic tendencies below. + + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges do k = 1, nVertLevels !maxLevelEdgeTop % array(iEdge) - ! The baroclinic velocity needs be recomputed at the beginning of a + ! The baroclinic velocity needs be recomputed at the beginning of a ! timestep because the implicit vertical mixing is conducted on the ! total u. We keep normalBarotropicVelocity from the previous timestep. - ! Note that normalBaroclinicVelocity may now include a barotropic component, because the + ! Note that normalBaroclinicVelocity may now include a barotropic component, because the ! weights layerThickness have changed. That is OK, because the barotropicForcing variable ! subtracts out the barotropic component from the baroclinic. normalBaroclinicVelocityCur(k,iEdge) = normalVelocityCur(k,iEdge) - normalBarotropicVelocityCur(iEdge) @@ -263,29 +316,54 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ normalVelocityNew(k,iEdge) = normalVelocityCur(k,iEdge) normalBaroclinicVelocityNew(k,iEdge) = normalBaroclinicVelocityCur(k,iEdge) + end do + end do + !$omp end do - ! DWJ-POOL What's this for? -! block % diagnostics % layerThicknessEdge % array(k,iEdge) & -! = block % diagnostics % layerThicknessEdge % array(k,iEdge) - end do - end do - - sshNew(:) = sshCur(:) - - do iCell = 1, nCells + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + sshNew(iCell) = sshCur(iCell) do k = 1, maxLevelCell(iCell) layerThicknessNew(k,iCell) = layerThicknessCur(k,iCell) - - tracersNew(:,k,iCell) = tracersCur(:,k,iCell) + ! set vertAleTransportTop to zero for stage 1 velocity tendency, first time through. + vertAleTransportTop(k,iCell) = 0.0_RKIND end do end do + !$omp end do + + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr)) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroupCur, 1) + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroupNew, 2) + + if ( associated(tracersGroupCur) .and. associated(tracersGroupNew) ) then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + tracersGroupNew(:,k,iCell) = tracersGroupCur(:,k,iCell) + end do + end do + !$omp end do + end if + end if + end do + if (associated(highFreqThicknessNew)) then - highFreqThicknessNew(:,:) = highFreqThicknessCur(:,:) + !$omp do schedule(runtime) + do iCell = 1, nCells + highFreqThicknessNew(:, iCell) = highFreqThicknessCur(:, iCell) + end do + !$omp end do end if if (associated(lowFreqDivergenceNew)) then - lowFreqDivergenceNew(:,:) = lowFreqDivergenceCur(:,:) + !$omp do schedule(runtime) + do iCell = 1, nCells + lowFreqDivergenceNew(:, iCell) = lowFreqDivergenceCur(:, iCell) + end do + !$omp end do endif block => block % next @@ -293,29 +371,32 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_timer_stop("se prep") !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! BEGIN large iteration loop + ! BEGIN large iteration loop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! n_bcl_iter = config_n_bcl_iter_mid n_bcl_iter(1) = config_n_bcl_iter_beg n_bcl_iter(config_n_ts_iter) = config_n_bcl_iter_end do split_explicit_step = 1, config_n_ts_iter + call mpas_timer_start('se loop') + stage1_tend_time = min(split_explicit_step,2) call mpas_pool_get_subpool(domain % blocklist % structs, 'diagnostics', diagnosticsPool) + call mpas_threading_barrier() ! --- update halos for diagnostic ocean boundayr layer depth - call mpas_timer_start("se halo diag obd") if (config_use_cvmix_kpp) then + call mpas_timer_start("se halo diag obd") call mpas_dmpar_field_halo_exch(domain, 'boundaryLayerDepth') + call mpas_timer_stop("se halo diag obd") end if - call mpas_timer_stop("se halo diag obd") ! --- update halos for diagnostic variables call mpas_timer_start("se halo diag") call mpas_dmpar_field_halo_exch(domain, 'normalizedRelativeVorticityEdge') - if (config_mom_del4 > 0.0) then + if (config_mom_del4 > 0.0_RKIND) then call mpas_dmpar_field_halo_exch(domain, 'divergence') call mpas_dmpar_field_halo_exch(domain, 'relativeVorticity') end if @@ -329,10 +410,13 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ if (config_use_freq_filtered_thickness) then call mpas_timer_start("se freq-filtered-thick computations") + block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'state', statepool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) @@ -341,18 +425,26 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ end do call mpas_timer_stop("se freq-filtered-thick computations") + call mpas_threading_barrier() + call mpas_timer_start("se freq-filtered-thick halo update") + call mpas_dmpar_field_halo_exch(domain, 'tendHighFreqThickness') call mpas_dmpar_field_halo_exch(domain, 'tendLowFreqDivergence') + call mpas_timer_stop("se freq-filtered-thick halo update") + call mpas_threading_barrier() block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCellsPtr) + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) @@ -361,27 +453,34 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(tendPool, 'highFreqThickness', highFreqThicknessTend) + nCells = nCellsPtr + + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) ! this is h^{hf}_{n+1} - highFreqThicknessNew(k,iCell) = highFreqThicknessCur(k,iCell) + dt * highFreqThicknessTend(k,iCell) + highFreqThicknessNew(k,iCell) = highFreqThicknessCur(k,iCell) + dt * highFreqThicknessTend(k,iCell) end do end do + !$omp end do + block => block % next end do endif - ! compute velocity tendencies, T(u*,w*,p*) call mpas_timer_start("se bcl vel") + call mpas_timer_start('se bcl vel tend') block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) @@ -393,24 +492,12 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(statePool, 'highFreqThickness', highFreqThicknessNew, 2) call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) - call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) - ! compute vertAleTransportTop. Use u (rather than normalTransportVelocity) for momentum advection. - ! Use the most recent time level available. - if (associated(highFreqThicknessNew)) then - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & - layerThicknessCur, layerThicknessEdge, normalVelocityCur, & - sshCur, dt, vertAleTransportTop, err, highFreqThicknessNew) - else - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & - layerThicknessCur, layerThicknessEdge, normalVelocityCur, & - sshCur, dt, vertAleTransportTop, err) - endif + call ocn_tend_vel(tendPool, statePool, forcingPool, diagnosticsPool, meshPool, scratchPool, stage1_tend_time) - call ocn_tend_vel(tendPool, statePool, forcingPool, diagnosticsPool, meshPool, scratchPool, stage1_tend_time) - - block => block % next + block => block % next end do + call mpas_timer_stop('se bcl vel tend') !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! BEGIN baroclinic iterations on linear Coriolis term @@ -424,14 +511,18 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ split = 1 endif + call mpas_timer_start('bcl iters on linear Coriolis') block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) @@ -448,19 +539,24 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) call mpas_pool_get_array(diagnosticsPool, 'barotropicForcing', barotropicForcing) - allocate(uTemp(nVertLevels)) + ! Only need to loop over the 1 halo, since there is a halo exchange immediately after this computation. + nEdges = nEdgesArray( 1 ) ! Put f*normalBaroclinicVelocity^{perp} in normalVelocityNew as a work variable call ocn_fuperp(statePool, meshPool, 2) + allocate(uTemp(nVertLevels)) + + !$omp do schedule(runtime) private(cell1, cell2, k, normalThicknessFluxSum, thicknessSum) do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - uTemp = 0.0 ! could put this after with uTemp(maxleveledgetop+1:nvertlevels)=0 + uTemp = 0.0_RKIND ! could put this after with uTemp(maxleveledgetop+1:nvertlevels)=0 do k = 1, maxLevelEdgeTop(iEdge) - ! normalBaroclinicVelocityNew = normalBaroclinicVelocityOld + dt*(-f*normalBaroclinicVelocityPerp + T(u*,w*,p*) + g*grad(SSH*) ) + ! normalBaroclinicVelocityNew = normalBaroclinicVelocityOld + dt*(-f*normalBaroclinicVelocityPerp + ! + T(u*,w*,p*) + g*grad(SSH*) ) ! Here uNew is a work variable containing -fEdge(iEdge)*normalBaroclinicVelocityPerp(k,iEdge) uTemp(k) = normalBaroclinicVelocityCur(k,iEdge) & + dt * (normalVelocityTend(k,iEdge) & @@ -469,8 +565,8 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ / dcEdge(iEdge) ) enddo - ! thicknessSum is initialized outside the loop because on land boundaries - ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a + ! thicknessSum is initialized outside the loop because on land boundaries + ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a ! nonzero value to avoid a NaN. normalThicknessFluxSum = layerThicknessEdge(1,iEdge) * uTemp(1) thicknessSum = layerThicknessEdge(1,iEdge) @@ -485,31 +581,39 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ do k = 1, maxLevelEdgeTop(iEdge) ! These two steps are together here: !{\bf u}'_{k,n+1} = {\bf u}'_{k,n} - \Delta t {\overline {\bf G}} - !{\bf u}'_{k,n+1/2} = \frac{1}{2}\left({\bf u}^{'}_{k,n} +{\bf u}'_{k,n+1}\right) + !{\bf u}'_{k,n+1/2} = \frac{1}{2}\left({\bf u}^{'}_{k,n} +{\bf u}'_{k,n+1}\right) ! so that normalBaroclinicVelocityNew is at time n+1/2 - normalBaroclinicVelocityNew(k,iEdge) = 0.5*( & + normalBaroclinicVelocityNew(k,iEdge) = 0.5_RKIND*( & normalBaroclinicVelocityCur(k,iEdge) + uTemp(k) - dt * barotropicForcing(iEdge)) enddo - + enddo ! iEdge + !$omp end do deallocate(uTemp) block => block % next end do + call mpas_threading_barrier() + call mpas_timer_start("se halo normalBaroclinicVelocity") call mpas_dmpar_field_halo_exch(domain, 'normalBaroclinicVelocity', timeLevel=2) call mpas_timer_stop("se halo normalBaroclinicVelocity") + call mpas_timer_stop('bcl iters on linear Coriolis') + end do ! do j=1,config_n_bcl_iter + call mpas_timer_start('se halo barotropicForcing') + call mpas_dmpar_field_halo_exch(domain, 'barotropicForcing') + call mpas_timer_stop('se halo barotropicForcing') + call mpas_timer_stop("se bcl vel") !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! END baroclinic iterations on linear Coriolis term !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! @@ -524,12 +628,15 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ if (trim(config_time_integrator) == 'unsplit_explicit') then + call mpas_timer_start('btr vel ue') block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) @@ -542,89 +649,122 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) - ! For Split_Explicit unsplit, simply set normalBarotropicVelocityNew=0, normalBarotropicVelocitySubcycle=0, and uNew=normalBaroclinicVelocityNew - normalBarotropicVelocityNew(:) = 0.0 + nEdges = nEdgesPtr - normalVelocityNew(:,:) = normalBaroclinicVelocityNew(:,:) + ! For Split_Explicit unsplit, simply set normalBarotropicVelocityNew=0, normalBarotropicVelocitySubcycle=0, and + ! uNew=normalBaroclinicVelocityNew + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges + normalBarotropicVelocityNew(iEdge) = 0.0_RKIND do k = 1, nVertLevels + normalVelocityNew(k, iEdge) = normalBaroclinicVelocityNew(k, iEdge) - ! normalTransportVelocity = normalBaroclinicVelocity + normalGMBolusVelocity - ! This is u used in advective terms for layerThickness and tracers + ! normalTransportVelocity = normalBaroclinicVelocity + normalGMBolusVelocity + ! This is u used in advective terms for layerThickness and tracers ! in tendency calls in stage 3. -!mrp note: in QC version, there is an if (config_use_standardGM) on adding normalGMBolusVelocity -! I think it is not needed because normalGMBolusVelocity=0 when GM not on. normalTransportVelocity(k,iEdge) = edgeMask(k,iEdge) & *( normalBaroclinicVelocityNew(k,iEdge) + normalGMBolusVelocity(k,iEdge) ) enddo end do ! iEdge - + !$omp end do + block => block % next end do ! block + call mpas_timer_stop('btr vel ue') elseif (trim(config_time_integrator) == 'split_explicit') then ! Initialize variables for barotropic subcycling + call mpas_timer_start('btr vel se init') block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCellsPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_array(diagnosticsPool, 'barotropicForcing', barotropicForcing) call mpas_pool_get_array(diagnosticsPool, 'barotropicThicknessFlux', barotropicThicknessFlux) call mpas_pool_get_array(statePool, 'ssh', sshCur, 1) call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleCur, oldBtrSubcycleTime) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, oldBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, & + oldBtrSubcycleTime) call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocityCur, 1) call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocityNew, 2) + nCells = nCellsPtr + nEdges = nEdgesPtr + if (config_filter_btr_mode) then - barotropicForcing(:) = 0.0 + !$omp do schedule(runtime) + do iEdge = 1, nEdges + barotropicForcing(iEdge) = 0.0_RKIND + end do + !$omp end do endif + !$omp do schedule(runtime) do iCell = 1, nCells - ! sshSubcycleOld = sshOld - sshSubcycleCur(iCell) = sshCur(iCell) + ! sshSubcycleOld = sshOld + sshSubcycleCur(iCell) = sshCur(iCell) end do + !$omp end do + !$omp do schedule(runtime) do iEdge = 1, nEdges - ! normalBarotropicVelocitySubcycleOld = normalBarotropicVelocityOld - normalBarotropicVelocitySubcycleCur(iEdge) = normalBarotropicVelocityCur(iEdge) + ! normalBarotropicVelocitySubcycleOld = normalBarotropicVelocityOld + normalBarotropicVelocitySubcycleCur(iEdge) = normalBarotropicVelocityCur(iEdge) ! normalBarotropicVelocityNew = BtrOld This is the first for the summation - normalBarotropicVelocityNew(iEdge) = normalBarotropicVelocityCur(iEdge) + normalBarotropicVelocityNew(iEdge) = normalBarotropicVelocityCur(iEdge) - ! barotropicThicknessFlux = 0 - barotropicThicknessFlux(iEdge) = 0.0 + ! barotropicThicknessFlux = 0 + barotropicThicknessFlux(iEdge) = 0.0_RKIND end do + !$omp end do block => block % next end do ! block + call mpas_timer_stop('btr vel se init') !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! BEGIN Barotropic subcycle loop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - do j = 1, config_n_btr_subcycles * config_btr_subcycle_loop_factor + + ! Allocate subcycled scratch fields before starting subcycle loop + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_field(scratchPool, 'btrvel_temp', btrvel_tempField) + call mpas_allocate_scratch_field(btrvel_tempField, .false.) + + call mpas_threading_barrier() + + call mpas_timer_start('btr se subcycle loop') + do j = 1, nBtrSubcycles * config_btr_subcycle_loop_factor + cellHaloComputeCounter = config_num_halos + edgeHaloComputeCounter = config_num_halos + 1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Barotropic subcycle: VELOCITY PREDICTOR STEP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - if (config_btr_gam1_velWt1 > 1.0e-12) then ! only do this part if it is needed in next SSH solve + if (config_btr_gam1_velWt1 > 1.0e-12_RKIND) then ! only do this part if it is needed in next SSH solve uPerpTime = oldBtrSubcycleTime block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) @@ -635,53 +775,65 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, uPerpTime) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, newBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, & + uPerpTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, & + newBtrSubcycleTime) call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleCur, oldBtrSubcycleTime) call mpas_pool_get_array(diagnosticsPool, 'barotropicForcing', barotropicForcing) + nEdges = nEdgesPtr + nEdges = nEdgesArray( edgeHaloComputeCounter ) + + !$omp do schedule(runtime) private(cell1, cell2, CoriolisTerm, i, eoe) do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) + temp_mask = edgeMask(1, iEdge) + + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + + ! Compute the barotropic Coriolis term, -f*uPerp + CoriolisTerm = 0.0_RKIND + do i = 1, nEdgesOnEdge(iEdge) + eoe = edgesOnEdge(i,iEdge) + CoriolisTerm = CoriolisTerm + weightsOnEdge(i,iEdge) & + * normalBarotropicVelocitySubcycleCur(eoe) * fEdge(eoe) + end do + + normalBarotropicVelocitySubcycleNew(iEdge) & + = temp_mask & + * (normalBarotropicVelocitySubcycleCur(iEdge) & + + dt / nBtrSubcycles * (CoriolisTerm - gravity & + * (sshSubcycleCur(cell2) - sshSubcycleCur(cell1) ) & + / dcEdge(iEdge) + barotropicForcing(iEdge))) - ! Compute the barotropic Coriolis term, -f*uPerp - CoriolisTerm = 0.0 - do i = 1, nEdgesOnEdge(iEdge) - eoe = edgesOnEdge(i,iEdge) - CoriolisTerm = CoriolisTerm + weightsOnEdge(i,iEdge) & - * normalBarotropicVelocitySubcycleCur(eoe) * fEdge(eoe) - end do - - ! normalBarotropicVelocityNew = normalBarotropicVelocityOld + dt/J*(-f*normalBarotropicVelocityoldPerp - g*grad(SSH) + G) - normalBarotropicVelocitySubcycleNew(iEdge) & - = (normalBarotropicVelocitySubcycleCur(iEdge) & - + dt / config_n_btr_subcycles * (CoriolisTerm - gravity & - * (sshSubcycleCur(cell2) - sshSubcycleCur(cell1) ) & - / dcEdge(iEdge) + barotropicForcing(iEdge))) * edgeMask(1, iEdge) end do + !$omp end do block => block % next end do ! block - - ! boundary update on normalBarotropicVelocityNew - call mpas_timer_start("se halo normalBarotropicVelocity") - call mpas_dmpar_field_halo_exch(domain, 'normalBarotropicVelocitySubcycle', timeLevel=newBtrSubcycleTime) - call mpas_timer_stop("se halo normalBarotropicVelocity") endif ! config_btr_gam1_velWt1>1.0e-12 + ! 1 Halo from edges is corrupted here, so reduce the edge halo layers by 1 + edgeHaloComputeCounter = edgeHaloComputeCounter - 1 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! Barotropic subcycle: SSH PREDICTOR STEP + ! Barotropic subcycle: SSH PREDICTOR STEP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCellsPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_array(tendPool, 'ssh', sshTend) @@ -698,39 +850,38 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleCur, oldBtrSubcycleTime) call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleNew, newBtrSubcycleTime) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, oldBtrSubcycleTime) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, newBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, & + oldBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, & + newBtrSubcycleTime) call mpas_pool_get_array(diagnosticsPool, 'barotropicThicknessFlux', barotropicThicknessFlux) - - sshTend(:) = 0.0 - - if (config_btr_solve_SSH2) then - ! If config_btr_solve_SSH2=.true., then do NOT accumulate barotropicThicknessFlux in this SSH predictor - ! section, because it will be accumulated in the SSH corrector section. - barotropicThicknessFlux_coeff = 0.0 - else - ! otherwise, DO accumulate barotropicThicknessFlux in this SSH predictor section - barotropicThicknessFlux_coeff = 1.0 - endif - + + nCells = nCellsPtr + nEdges = nEdgesPtr + + nCells = nCellsArray( cellHaloComputeCounter ) + nEdges = nEdgesArray( edgeHaloComputeCounter ) + ! config_btr_gam1_velWt1 sets the forward weighting of velocity in the SSH computation ! config_btr_gam1_velWt1= 1 flux = normalBarotropicVelocityNew*H ! config_btr_gam1_velWt1=0.5 flux = 1/2*(normalBarotropicVelocityNew+normalBarotropicVelocityOld)*H ! config_btr_gam1_velWt1= 0 flux = normalBarotropicVelocityOld*H + !$omp do schedule(runtime) private(i, iEdge, cell1, cell2, sshEdge, thicknessSum, flux) do iCell = 1, nCells + sshTend(iCell) = 0.0_RKIND do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) cell1 = cellsOnEdge(1, iEdge) cell2 = cellsOnEdge(2, iEdge) - sshEdge = 0.5 * (sshSubcycleCur(cell1) + sshSubcycleCur(cell2) ) + sshEdge = 0.5_RKIND * (sshSubcycleCur(cell1) + sshSubcycleCur(cell2) ) - ! method 0: orig, works only without pbc: + ! method 0: orig, works only without pbc: !thicknessSum = sshEdge + refBottomDepthTopOfCell(maxLevelEdgeTop(iEdge)+1) - + ! method 1, matches method 0 without pbcs, works with pbcs. thicknessSum = sshEdge + min(bottomDepth(cell1), bottomDepth(cell2)) @@ -741,68 +892,83 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ flux = ((1.0-config_btr_gam1_velWt1) * normalBarotropicVelocitySubcycleCur(iEdge) & + config_btr_gam1_velWt1 * normalBarotropicVelocitySubcycleNew(iEdge)) & - * thicknessSum + * thicknessSum - sshTend(iCell) = sshTend(iCell) + edgeSignOncell(i, iCell) * flux & - * dvEdge(iEdge) + sshTend(iCell) = sshTend(iCell) + edgeSignOncell(i, iCell) * flux * dvEdge(iEdge) end do + + ! SSHnew = SSHold + dt/J*(-div(Flux)) + sshSubcycleNew(iCell) = sshSubcycleCur(iCell) & + + dt / nBtrSubcycles * sshTend(iCell) / areaCell(iCell) end do + !$omp end do - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) + !! asarje: changed to avoid redundant computations when config_btr_solve_SSH2 is true - sshEdge = 0.5 * (sshSubcycleCur(cell1) & - + sshSubcycleCur(cell2) ) + if (config_btr_solve_SSH2) then - ! method 0: orig, works only without pbc: - !thicknessSum = sshEdge + refBottomDepthTopOfCell(maxLevelEdgeTop(iEdge)+1) - - ! method 1, matches method 0 without pbcs, works with pbcs. - thicknessSum = sshEdge + min(bottomDepth(cell1), bottomDepth(cell2)) + ! If config_btr_solve_SSH2=.true., + ! then do NOT accumulate barotropicThicknessFlux in this SSH predictor + ! section, because it will be accumulated in the SSH corrector section. + barotropicThicknessFlux_coeff = 0.0_RKIND - ! method 2: may be better than method 1. - ! take average of full thickness at two neighboring cells - !thicknessSum = sshEdge + 0.5 *( bottomDepth(cell1) & - ! + bottomDepth(cell2) ) + ! othing else to do - flux = ((1.0-config_btr_gam1_velWt1) * normalBarotropicVelocitySubcycleCur(iEdge) & - + config_btr_gam1_velWt1 * normalBarotropicVelocitySubcycleNew(iEdge)) & - * thicknessSum + else + + ! otherwise, DO accumulate barotropicThicknessFlux in this SSH predictor section + barotropicThicknessFlux_coeff = 1.0_RKIND + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + + sshEdge = 0.5_RKIND * (sshSubcycleCur(cell1) + sshSubcycleCur(cell2)) + + ! method 1, matches method 0 without pbcs, works with pbcs. + thicknessSum = sshEdge + min(bottomDepth(cell1), bottomDepth(cell2)) + + flux = ((1.0-config_btr_gam1_velWt1) * normalBarotropicVelocitySubcycleCur(iEdge) & + + config_btr_gam1_velWt1 * normalBarotropicVelocitySubcycleNew(iEdge)) & + * thicknessSum + + barotropicThicknessFlux(iEdge) = barotropicThicknessFlux(iEdge) + flux + end do + !$omp end do + + endif - barotropicThicknessFlux(iEdge) = barotropicThicknessFlux(iEdge) + barotropicThicknessFlux_coeff * flux - end do - - ! SSHnew = SSHold + dt/J*(-div(Flux)) - do iCell = 1, nCells - sshSubcycleNew(iCell) = sshSubcycleCur(iCell) + dt / config_n_btr_subcycles * sshTend(iCell) / areaCell(iCell) - end do - block => block % next end do ! block - - ! boundary update on SSHnew - call mpas_timer_start("se halo ssh") - call mpas_dmpar_field_halo_exch(domain, 'sshSubcycle', timeLevel=newBtrSubcycleTime) - call mpas_timer_stop("se halo ssh") - + + ! 1 cell halo layer is now corrupted, so remove one from computing on. + cellHaloComputeCounter = cellHaloComputeCounter - 1 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Barotropic subcycle: VELOCITY CORRECTOR STEP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! 1 edge halo is already corrupted from the predictor step. do BtrCorIter = 1, config_n_btr_cor_iter uPerpTime = newBtrSubcycleTime - + block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, oldBtrSubcycleTime) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, newBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, & + oldBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, & + newBtrSubcycleTime) call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleCur, oldBtrSubcycleTime) call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleNew, newBtrSubcycleTime) @@ -816,56 +982,92 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(diagnosticsPool, 'barotropicForcing', barotropicForcing) - allocate(utemp(nEdges+1)) + call mpas_pool_get_field(scratchPool, 'btrvel_temp', btrvel_tempField) + btrvel_temp => btrvel_tempField % array + + ! Need to initialize btr_vel_temp over the one more halo than we're computing over + nEdges = nEdgesPtr + + nEdges = nEdgesArray( min(edgeHaloComputeCounter + 1, config_num_halos + 1) ) + + !$omp do schedule(runtime) + do iEdge = 1, nEdges+1 + btrvel_temp(iEdge) = normalBarotropicVelocitySubcycleNew(iEdge) + end do + !$omp end do + + nEdges = nEdgesArray( edgeHaloComputeCounter ) + + !$omp do schedule(runtime) private(cell1, cell2, eoe, CoriolisTerm, i, sshCell1, sshCell2) + do iEdge = 1, nEdges + + ! asarje: added to avoid redundant computations based on mask + temp_mask = edgeMask(1,iEdge) + + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + + ! Compute the barotropic Coriolis term, -f*uPerp + CoriolisTerm = 0.0_RKIND + do i = 1, nEdgesOnEdge(iEdge) + eoe = edgesOnEdge(i,iEdge) + CoriolisTerm = CoriolisTerm & + + weightsOnEdge(i,iEdge) * btrvel_temp(eoe) * fEdge(eoe) + end do + + ! In this final solve for velocity, SSH is a linear + ! combination of SSHold and SSHnew. + sshCell1 = (1 - config_btr_gam2_SSHWt1) * sshSubcycleCur(cell1) & + + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell1) + sshCell2 = (1 - config_btr_gam2_SSHWt1) * sshSubcycleCur(cell2) & + + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell2) + + ! normalBarotropicVelocityNew = normalBarotropicVelocityOld + dt/J*(-f*normalBarotropicVelocityoldPerp + ! - g*grad(SSH) + G) + normalBarotropicVelocitySubcycleNew(iEdge) = temp_mask & + * (normalBarotropicVelocitySubcycleCur(iEdge) & + + dt / nBtrSubcycles & + * (CoriolisTerm - gravity * (sshCell2 - sshCell1) / dcEdge(iEdge) & + + barotropicForcing(iEdge))) - uTemp(:) = normalBarotropicVelocitySubcycleNew(:) - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - - ! Compute the barotropic Coriolis term, -f*uPerp - CoriolisTerm = 0.0 - do i = 1, nEdgesOnEdge(iEdge) - eoe = edgesOnEdge(i,iEdge) - CoriolisTerm = CoriolisTerm + weightsOnEdge(i,iEdge) & - !* normalBarotropicVelocitySubcycleNew(eoe) & - * uTemp(eoe) * fEdge(eoe) - end do - - ! In this final solve for velocity, SSH is a linear - ! combination of SSHold and SSHnew. - sshCell1 = (1-config_btr_gam2_SSHWt1) * sshSubcycleCur(cell1) + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell1) - sshCell2 = (1-config_btr_gam2_SSHWt1) * sshSubcycleCur(cell2) + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell2) - - ! normalBarotropicVelocityNew = normalBarotropicVelocityOld + dt/J*(-f*normalBarotropicVelocityoldPerp - g*grad(SSH) + G) - normalBarotropicVelocitySubcycleNew(iEdge) = (normalBarotropicVelocitySubcycleCur(iEdge) & - + dt / config_n_btr_subcycles *(CoriolisTerm - gravity *(sshCell2 - sshCell1) / dcEdge(iEdge) & - + barotropicForcing(iEdge))) * edgeMask(1,iEdge) end do - deallocate(uTemp) - + !$omp end do + block => block % next end do ! block - - ! boundary update on normalBarotropicVelocityNew - call mpas_timer_start("se halo normalBarotropicVelocity") - call mpas_dmpar_field_halo_exch(domain, 'normalBarotropicVelocitySubcycle', timeLevel=newBtrSubcycleTime) - call mpas_timer_stop("se halo normalBarotropicVelocity") + + ! mrp 170503 This is the original line. Go back to this once I + ! verify that halo regions are truly three cells wide. + ! if ( edgeHaloComputeCounter == 1 .and. BtrCorIter /= config_n_btr_cor_iter ) then + ! replaced with this, to force a halo update on the first iteration: + if ( edgeHaloComputeCounter == 1 .or. BtrCorIter == 1 ) then + edgeHaloComputeCounter = config_num_halos + 1 + call mpas_timer_start("se halo btr vel corr") + call mpas_dmpar_field_halo_exch(domain, 'normalBarotropicVelocitySubcycle', timeLevel=newBtrSubcycleTime) + call mpas_timer_stop("se halo btr vel corr") + else + edgeHaloComputeCounter = edgeHaloComputeCounter - 1 + end if + end do !do BtrCorIter=1,config_n_btr_cor_iter - + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Barotropic subcycle: SSH CORRECTOR STEP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (config_btr_solve_SSH2) then - + block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCellsPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_array(tendPool, 'ssh', sshTend) @@ -881,19 +1083,27 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleCur, oldBtrSubcycleTime) call mpas_pool_get_array(statePool, 'sshSubcycle', sshSubcycleNew, newBtrSubcycleTime) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, oldBtrSubcycleTime) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, newBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleCur, & + oldBtrSubcycleTime) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, & + newBtrSubcycleTime) call mpas_pool_get_array(diagnosticsPool, 'barotropicThicknessFlux', barotropicThicknessFlux) - - sshTend(:) = 0.0 - + + nCells = nCellsPtr + nEdges = nEdgesPtr + + nCells = nCellsArray( cellHaloComputeCounter ) + nEdges = nEdgesArray( edgeHaloComputeCounter ) + ! config_btr_gam3_velWt2 sets the forward weighting of velocity in the SSH computation ! config_btr_gam3_velWt2= 1 flux = normalBarotropicVelocityNew*H ! config_btr_gam3_velWt2=0.5 flux = 1/2*(normalBarotropicVelocityNew+normalBarotropicVelocityOld)*H ! config_btr_gam3_velWt2= 0 flux = normalBarotropicVelocityOld*H + !$omp do schedule(runtime) private(i, iEdge, cell1, cell2, sshCell1, sshCell2, sshEdge, thicknessSum, flux) do iCell = 1, nCells + sshTend(iCell) = 0.0_RKIND do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) @@ -905,19 +1115,19 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell1) sshCell2 = (1-config_btr_gam2_SSHWt1)* sshSubcycleCur(cell2) & + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell2) - - sshEdge = 0.5 * (sshCell1 + sshCell2) - ! method 0: orig, works only without pbc: + sshEdge = 0.5_RKIND * (sshCell1 + sshCell2) + + ! method 0: orig, works only without pbc: !thicknessSum = sshEdge + refBottomDepthTopOfCell(maxLevelEdgeTop(iEdge)+1) - + ! method 1, matches method 0 without pbcs, works with pbcs. thicknessSum = sshEdge + min(bottomDepth(cell1), bottomDepth(cell2)) ! method 2: may be better than method 1. ! take average of full thickness at two neighboring cells !thicknessSum = sshEdge + 0.5 *( bottomDepth(cell1) + bottomDepth (cell2) ) - + flux = ((1.0-config_btr_gam3_velWt2) * normalBarotropicVelocitySubcycleCur(iEdge) & + config_btr_gam3_velWt2 * normalBarotropicVelocitySubcycleNew(iEdge)) & * thicknessSum @@ -926,121 +1136,173 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ * dvEdge(iEdge) end do + + ! SSHnew = SSHold + dt/J*(-div(Flux)) + sshSubcycleNew(iCell) = sshSubcycleCur(iCell) & + + dt / nBtrSubcycles * sshTend(iCell) / areaCell(iCell) end do + !$omp end do + !$omp do schedule(runtime) private(cell1, cell2, sshCell1, sshCell2, sshEdge, thicknessSum, flux) do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - + ! SSH is a linear combination of SSHold and SSHnew. sshCell1 = (1-config_btr_gam2_SSHWt1)* sshSubcycleCur(cell1) + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell1) sshCell2 = (1-config_btr_gam2_SSHWt1)* sshSubcycleCur(cell2) + config_btr_gam2_SSHWt1 * sshSubcycleNew(cell2) - sshEdge = 0.5 * (sshCell1 + sshCell2) + sshEdge = 0.5_RKIND * (sshCell1 + sshCell2) - ! method 0: orig, works only without pbc: + ! method 0: orig, works only without pbc: !thicknessSum = sshEdge + refBottomDepthTopOfCell(maxLevelEdgeTop(iEdge)+1) - + ! method 1, matches method 0 without pbcs, works with pbcs. thicknessSum = sshEdge + min(bottomDepth(cell1), bottomDepth(cell2)) ! method 2, better, I think. ! take average of full thickness at two neighboring cells !thicknessSum = sshEdge + 0.5 *( bottomDepth(cell1) + bottomDepth(cell2) ) - + flux = ((1.0-config_btr_gam3_velWt2) * normalBarotropicVelocitySubcycleCur(iEdge) & + config_btr_gam3_velWt2 * normalBarotropicVelocitySubcycleNew(iEdge)) & * thicknessSum - + barotropicThicknessFlux(iEdge) = barotropicThicknessFlux(iEdge) + flux end do - - ! SSHnew = SSHold + dt/J*(-div(Flux)) - do iCell = 1, nCells - sshSubcycleNew(iCell) = sshSubcycleCur(iCell) & - + dt / config_n_btr_subcycles * sshTend(iCell) / areaCell(iCell) - end do - + !$omp end do + block => block % next end do ! block - - ! boundary update on SSHnew - call mpas_timer_start("se halo ssh") - call mpas_dmpar_field_halo_exch(domain, 'sshSubcycle') - call mpas_timer_stop("se halo ssh") endif ! config_btr_solve_SSH2 - + + ! boundary update on SSHnew + call mpas_timer_start("se halo subcycle") + call mpas_dmpar_exch_group_create(domain, subcycleGroupName) + call mpas_dmpar_exch_group_add_field(domain, subcycleGroupName, 'sshSubcycle', timeLevel=newBtrSubcycleTime) + call mpas_dmpar_exch_group_add_field(domain, subcycleGroupName, 'normalBarotropicVelocitySubcycle', & + timeLevel=newBtrSubcycleTime) + + call mpas_threading_barrier() + call mpas_dmpar_exch_group_full_halo_exch(domain, subcycleGroupName) + + call mpas_dmpar_exch_group_destroy(domain, subcycleGroupName) + call mpas_timer_stop("se halo subcycle") + + ! Reset the halo counters + edgeHaloComputeCounter = config_num_halos + 1 + cellHaloComputeCounter = config_num_halos + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Barotropic subcycle: Accumulate running sums, advance timestep pointers !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - + block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocityNew, 2) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, newBtrSubcycleTime) - + call mpas_pool_get_array(statePool, 'normalBarotropicVelocitySubcycle', normalBarotropicVelocitySubcycleNew, & + newBtrSubcycleTime) + ! normalBarotropicVelocityNew = normalBarotropicVelocityNew + normalBarotropicVelocitySubcycleNEW ! This accumulates the sum. - ! If the Barotropic Coriolis iteration is limited to one, this could + ! If the Barotropic Coriolis iteration is limited to one, this could ! be merged with the above code. - do iEdge = 1, nEdges - normalBarotropicVelocityNew(iEdge) = normalBarotropicVelocityNew(iEdge) + normalBarotropicVelocitySubcycleNew(iEdge) + + nEdges = nEdgesPtr + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalBarotropicVelocityNew(iEdge) = normalBarotropicVelocityNew(iEdge) & + + normalBarotropicVelocitySubcycleNew(iEdge) end do ! iEdge + !$omp end do + block => block % next end do ! block - + ! advance time pointers oldBtrSubcycleTime = mod(oldBtrSubcycleTime,2)+1 newBtrSubcycleTime = mod(newBtrSubcycleTime,2)+1 - - end do ! j=1,config_n_btr_subcycles + + end do ! j=1,nBtrSubcycles + call mpas_timer_stop('btr se subcycle loop') + + call mpas_threading_barrier() + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_field(scratchPool, 'btrvel_temp', btrvel_tempField) + call mpas_deallocate_scratch_field(btrvel_tempField, .false.) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! END Barotropic subcycle loop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Normalize Barotropic subcycle sums: ssh, normalBarotropicVelocity, and F + call mpas_timer_start('btr se norm') block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocityNew, 2) call mpas_pool_get_array(diagnosticsPool, 'barotropicThicknessFlux', barotropicThicknessFlux) - + + nEdges = nEdgesPtr + + nEdges = nEdgesArray(1) + + !$omp do schedule(runtime) do iEdge = 1, nEdges barotropicThicknessFlux(iEdge) = barotropicThicknessFlux(iEdge) & - / (config_n_btr_subcycles * config_btr_subcycle_loop_factor) - - normalBarotropicVelocityNew(iEdge) = normalBarotropicVelocityNew(iEdge) & - / (config_n_btr_subcycles * config_btr_subcycle_loop_factor + 1) + / (nBtrSubcycles * config_btr_subcycle_loop_factor) + + normalBarotropicVelocityNew(iEdge) = normalBarotropicVelocityNew(iEdge) & + / (nBtrSubcycles * config_btr_subcycle_loop_factor + 1) end do - + !$omp end do + block => block % next end do ! block - - + call mpas_timer_stop('btr se norm') + + call mpas_threading_barrier() + ! boundary update on F - call mpas_timer_start("se halo F") - call mpas_dmpar_field_halo_exch(domain, 'barotropicThicknessFlux') - call mpas_timer_stop("se halo F") + call mpas_timer_start("se halo F and btr vel") + call mpas_dmpar_exch_group_create(domain, finalBtrGroupName) + call mpas_dmpar_exch_group_add_field(domain, finalBtrGroupName, 'barotropicThicknessFlux') + call mpas_dmpar_exch_group_add_field(domain, finalBtrGroupName, 'normalBarotropicVelocity', timeLevel=2) + + call mpas_threading_barrier() + call mpas_dmpar_exch_group_full_halo_exch(domain, finalBtrGroupName) + + call mpas_dmpar_exch_group_destroy(domain, finalBtrGroupName) + call mpas_timer_stop("se halo F and btr vel") + + call mpas_threading_barrier() ! Check that you can compute SSH using the total sum or the individual increments ! over the barotropic subcycles. - ! efficiency: This next block of code is really a check for debugging, and can + ! efficiency: This next block of code is really a check for debugging, and can ! be removed later. + call mpas_timer_start('btr se ssh verif') block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) @@ -1055,13 +1317,17 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) + nEdges = nEdgesPtr + + nEdges = nEdgesArray( config_num_halos ) + allocate(uTemp(nVertLevels)) ! Correction velocity normalVelocityCorrection = (Flux - Sum(h u*))/H ! or, for the full latex version: - !{\bf u}^{corr} = \left( {\overline {\bf F}} + !{\bf u}^{corr} = \left( {\overline {\bf F}} ! - \sum_{k=1}^{N^{edge}} h_{k,*}^{edge} {\bf u}_k^{avg} \right) - ! \left/ \sum_{k=1}^{N^{edge}} h_{k,*}^{edge} \right. + ! \left/ \sum_{k=1}^{N^{edge}} h_{k,*}^{edge} \right. if (config_vel_correction) then useVelocityCorrection = 1 @@ -1069,15 +1335,15 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ useVelocityCorrection = 0 endif + !$omp do schedule(runtime) private(k, normalThicknessFluxSum, thicknessSum, normalVelocityCorrection) do iEdge = 1, nEdges ! velocity for normalVelocityCorrectionection is normalBarotropicVelocity + normalBaroclinicVelocity + uBolus -!mrp note: in QC version, there is an if (config_use_standardGM) on adding normalGMBolusVelocity -! I think it is not needed because normalGMBolusVelocity=0 when GM not on. - uTemp(:) = normalBarotropicVelocityNew(iEdge) + normalBaroclinicVelocityNew(:,iEdge) + normalGMBolusVelocity(:,iEdge) + uTemp(:) = normalBarotropicVelocityNew(iEdge) + normalBaroclinicVelocityNew(:,iEdge) & + + normalGMBolusVelocity(:,iEdge) - ! thicknessSum is initialized outside the loop because on land boundaries - ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a + ! thicknessSum is initialized outside the loop because on land boundaries + ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a ! nonzero value to avoid a NaN. normalThicknessFluxSum = layerThicknessEdge(1,iEdge) * uTemp(1) thicknessSum = layerThicknessEdge(1,iEdge) @@ -1087,12 +1353,14 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ thicknessSum = thicknessSum + layerThicknessEdge(k,iEdge) enddo - normalVelocityCorrection = useVelocityCorrection*(( barotropicThicknessFlux(iEdge) - normalThicknessFluxSum)/thicknessSum) + normalVelocityCorrection = useVelocityCorrection * (( barotropicThicknessFlux(iEdge) - normalThicknessFluxSum) & + / thicknessSum) do k = 1, nVertLevels - ! normalTransportVelocity = normalBarotropicVelocity + normalBaroclinicVelocity + normalGMBolusVelocity + normalVelocityCorrection - ! This is u used in advective terms for layerThickness and tracers + ! normalTransportVelocity = normalBarotropicVelocity + normalBaroclinicVelocity + normalGMBolusVelocity + ! + normalVelocityCorrection + ! This is u used in advective terms for layerThickness and tracers ! in tendency calls in stage 3. !mrp note: in QC version, there is an if (config_use_standardGM) on adding normalGMBolusVelocity ! I think it is not needed because normalGMBolusVelocity=0 when GM not on. @@ -1103,13 +1371,15 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ enddo end do ! iEdge + !$omp end do deallocate(uTemp) block => block % next end do ! block + call mpas_timer_stop('btr se ssh verif') - endif ! split_explicit + endif ! split_explicit call mpas_timer_stop("se btr vel") @@ -1119,15 +1389,26 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! Thickness tendency computations and thickness halo updates are completed before tracer + ! only compute tendencies for active tracers on last large iteration + if (split_explicit_step < config_n_ts_iter) then + activeTracersOnly = .true. + else + activeTracersOnly = .false. + endif + + ! Thickness tendency computations and thickness halo updates are completed before tracer ! tendency computations to allow monotonic advection. + call mpas_timer_start('se thick tend') block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) @@ -1139,66 +1420,100 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) ! compute vertAleTransportTop. Use normalTransportVelocity for advection of layerThickness and tracers. - ! Use time level 1 values of layerThickness and layerThicknessEdge because + ! Use time level 1 values of layerThickness and layerThicknessEdge because ! layerThickness has not yet been computed for time level 2. - if (associated(highFreqThicknessNew)) then - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & + call mpas_timer_start('thick vert trans vel top') + if (associated(highFreqThicknessNew)) then + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & sshCur, dt, vertAleTransportTop, err, highFreqThicknessNew) else - call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, & + call ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, & layerThicknessCur, layerThicknessEdge, normalTransportVelocity, & sshCur, dt, vertAleTransportTop, err) endif + call mpas_timer_stop('thick vert trans vel top') call ocn_tend_thick(tendPool, forcingPool, diagnosticsPool, meshPool) block => block % next end do + call mpas_timer_stop('se thick tend') + + call mpas_threading_barrier() ! update halo for thickness tendencies call mpas_timer_start("se halo thickness") + call mpas_dmpar_field_halo_exch(domain, 'tendLayerThickness') + call mpas_timer_stop("se halo thickness") + call mpas_threading_barrier() + + call mpas_timer_start('se tracer tend', .false.) block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call ocn_tend_tracer(tendPool, statePool, forcingPool, diagnosticsPool, meshPool, scratchPool, dt, 2) + call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block % structs, 'shortwave', swForcingPool) + call ocn_tend_tracer(tendPool, statePool, forcingPool, diagnosticsPool, meshPool, swForcingPool, scratchPool, & + dt, activeTracersOnly, 2) block => block % next end do + call mpas_timer_stop('se tracer tend') + + call mpas_threading_barrier() ! update halo for tracer tendencies call mpas_timer_start("se halo tracers") - call mpas_dmpar_field_halo_exch(domain, 'tendTracers') + call mpas_pool_get_subpool(domain % blocklist % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) + + call mpas_pool_begin_iteration(tracersTendPool) + do while ( mpas_pool_get_next_member(tracersTendPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + ! Only compute tendencies for active tracers if activeTracersOnly flag is true. + if ( .not.activeTracersOnly .or. trim(groupItr % memberName)=='activeTracersTend') then + call mpas_dmpar_field_halo_exch(domain, groupItr % memberName) + end if + end if + end do call mpas_timer_stop("se halo tracers") + call mpas_threading_barrier() + + call mpas_timer_start('se loop fini') block => domain % blocklist do while (associated(block)) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCellsPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'tend', tendPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) - call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) - call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) - call mpas_pool_get_array(statePool, 'tracers', tracersCur, 1) - call mpas_pool_get_array(statePool, 'tracers', tracersNew, 2) + call mpas_pool_get_array(tracersPool, 'activeTracers', tracersGroupCur, 1) + call mpas_pool_get_array(tracersPool, 'activeTracers', tracersGroupNew, 2) call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessCur, 1) call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) @@ -1212,12 +1527,16 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_array(statePool, 'normalBaroclinicVelocity', normalBaroclinicVelocityCur, 1) call mpas_pool_get_array(statePool, 'normalBaroclinicVelocity', normalBaroclinicVelocityNew, 2) - call mpas_pool_get_array(tendPool, 'tracers', tracersTend) call mpas_pool_get_array(tendPool, 'layerThickness', layerThicknessTend) call mpas_pool_get_array(tendPool, 'normalVelocity', normalVelocityTend) call mpas_pool_get_array(tendPool, 'highFreqThickness', highFreqThicknessTend) call mpas_pool_get_array(tendPool, 'lowFreqDivergence', lowFreqDivergenceTend) + call mpas_pool_get_array(tracersTendPool, 'activeTracersTend', activeTracersTend) + + nCells = nCellsPtr + nEdges = nEdgesPtr + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! If iterating, reset variables for next iteration @@ -1226,66 +1545,75 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ if (split_explicit_step < config_n_ts_iter) then ! Get indices for dynamic tracers (Includes T&S). - call mpas_pool_get_dimension(statePool, 'dynamics_start', startIndex) - call mpas_pool_get_dimension(statePool, 'dynamics_end', endIndex) + call mpas_pool_get_dimension(tracersPool, 'activeGRP_start', startIndex) + call mpas_pool_get_dimension(tracersPool, 'activeGRP_end', endIndex) ! Only need T & S for earlier iterations, ! then all the tracers needed the last time through. + + !$omp do schedule(runtime) private(k, temp_h, temp, i) do iCell = 1, nCells ! sshNew is a pointer, defined above. do k = 1, maxLevelCell(iCell) ! this is h_{n+1} - temp_h = layerThicknessCur(k,iCell) + dt * layerThicknessTend(k,iCell) + temp_h = layerThicknessCur(k,iCell) + dt * layerThicknessTend(k,iCell) ! this is h_{n+1/2} layerThicknessNew(k,iCell) = 0.5*( layerThicknessCur(k,iCell) + temp_h) do i = startIndex, endIndex ! This is Phi at n+1 - temp = ( tracersCur(i,k,iCell) * layerThicknessCur(k,iCell) + dt * tracersTend(i,k,iCell)) / temp_h - + temp = ( tracersGroupCur(i,k,iCell) * layerThicknessCur(k,iCell) + dt * activeTracersTend(i,k,iCell)) & + / temp_h + ! This is Phi at n+1/2 - tracersNew(i,k,iCell) = 0.5 * ( tracersCur(i,k,iCell) + temp ) + tracersGroupNew(i,k,iCell) = 0.5_RKIND * ( tracersGroupCur(i,k,iCell) + temp ) end do end do end do ! iCell + !$omp end do if (config_use_freq_filtered_thickness) then + !$omp do schedule(runtime) private(k, temp) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) ! h^{hf}_{n+1} was computed in Stage 1 ! this is h^{hf}_{n+1/2} - highFreqThicknessnew(k,iCell) = 0.5 * (highFreqThicknessCur(k,iCell) + highFreqThicknessNew(k,iCell)) + highFreqThicknessnew(k,iCell) = 0.5_RKIND * (highFreqThicknessCur(k,iCell) + highFreqThicknessNew(k,iCell)) ! this is D^{lf}_{n+1} temp = lowFreqDivergenceCur(k,iCell) & - + dt * lowFreqDivergenceTend(k,iCell) + + dt * lowFreqDivergenceTend(k,iCell) ! this is D^{lf}_{n+1/2} - lowFreqDivergenceNew(k,iCell) = 0.5 * (lowFreqDivergenceCur(k,iCell) + temp) + lowFreqDivergenceNew(k,iCell) = 0.5_RKIND * (lowFreqDivergenceCur(k,iCell) + temp) end do end do + !$omp end do end if + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges do k = 1, nVertLevels - ! u = normalBarotropicVelocity + normalBaroclinicVelocity + ! u = normalBarotropicVelocity + normalBaroclinicVelocity ! here normalBaroclinicVelocity is at time n+1/2 ! This is u used in next iteration or step - normalVelocityNew(k,iEdge) = edgeMask(k,iEdge) * ( normalBarotropicVelocityNew(iEdge) + normalBaroclinicVelocityNew(k,iEdge) ) + normalVelocityNew(k,iEdge) = edgeMask(k,iEdge) * ( normalBarotropicVelocityNew(iEdge) & + + normalBaroclinicVelocityNew(k,iEdge) ) enddo end do ! iEdge + !$omp end do - ! Efficiency note: We really only need this to compute layerThicknessEdge, density, pressure, and SSH + ! Efficiency note: We really only need this to compute layerThicknessEdge, density, pressure, and SSH ! in this diagnostics solve. - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 2) + call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, 2) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! @@ -1294,128 +1622,201 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! elseif (split_explicit_step == config_n_ts_iter) then + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) - ! this is h_{n+1} - layerThicknessNew(k,iCell) = layerThicknessCur(k,iCell) + dt * layerThicknessTend(k,iCell) - - ! This is Phi at n+1 - do i = 1, num_tracers - tracersNew(i,k,iCell) = (tracersCur(i,k,iCell) * layerThicknessCur(k,iCell) + dt * tracersTend(i,k,iCell) ) & - / layerThicknessNew(k,iCell) - - enddo + layerThicknessNew(k,iCell) = layerThicknessCur(k,iCell) + dt * layerThicknessTend(k,iCell) end do end do + !$omp end do + + if (config_compute_active_tracer_budgets) then + call mpas_pool_get_array(diagnosticsPool,'activeTracerHorizontalAdvectionTendency', & + activeTracerHorizontalAdvectionTendency) + call mpas_pool_get_array(diagnosticspool,'activeTracerVerticalAdvectionTendency', & + activeTracerVerticalAdvectionTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerSurfaceFluxTendency',activeTracerSurfaceFluxTendency) + call mpas_pool_get_array(diagnosticsPool,'temperatureShortWaveTendency',temperatureShortWaveTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerNonLocalTendency',activeTracerNonLocalTendency) + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k= 1, maxLevelCell(iCell) + activeTracerHorizontalAdvectionTendency(:,k,iCell) = & + activeTracerHorizontalAdvectionTendency(:,k,iCell) / & + layerThicknessNew(k,iCell) + + activeTracerVerticalAdvectionTendency(:,k,iCell) = & + activeTracerVerticalAdvectionTendency(:,k,iCell) / & + layerThicknessNew(k,iCell) + + activeTracerSurfaceFluxTendency(:,k,iCell) = & + activeTracerSurfaceFluxTendency(:,k,iCell) / & + layerThicknessNew(k,iCell) + + temperatureShortWaveTendency(k,iCell) = & + temperatureShortWaveTendency(k,iCell) / & + layerThicknessNew(k,iCell) + + activeTracerNonLocalTendency(:,k,iCell) = & + activeTracerNonLocalTendency(:,k,iCell) / & + layerThicknessNew(k,iCell) + end do + end do + !$omp end do + endif + + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + configName = 'config_use_' // trim(groupItr % memberName) + call mpas_pool_get_config(domain % configs, configName, config_use_tracerGroup) + + if ( config_use_tracerGroup ) then + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroupCur, 1) + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroupNew, 2) + + modifiedGroupName = trim(groupItr % memberName) // 'Tend' + call mpas_pool_get_array(tracersTendPool, modifiedGroupName, tracersGroupTend) + + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + tracersGroupNew(:,k,iCell) = (tracersGroupCur(:,k,iCell) * layerThicknessCur(k,iCell) + dt & + * tracersGroupTend(:,k,iCell) ) / layerThicknessNew(k,iCell) + end do + end do + !$omp end do + + ! limit salinity in separate loop + if ( trim(groupItr % memberName) == 'activeTracers' ) then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + tracersGroupNew(indexSalinity,k,iCell) = max(0.001_RKIND, tracersGroupNew(indexSalinity,k,iCell)) + end do + end do + !$omp end do + end if + + end if + end if + end do if (config_use_freq_filtered_thickness) then + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) ! h^{hf}_{n+1} was computed in Stage 1 ! this is D^{lf}_{n+1} - lowFreqDivergenceNew(k,iCell) = lowFreqDivergenceCur(k,iCell) + dt * lowFreqDivergenceTend(k,iCell) + lowFreqDivergenceNew(k,iCell) = lowFreqDivergenceCur(k,iCell) + dt * lowFreqDivergenceTend(k,iCell) end do end do + !$omp end do end if ! Recompute final u to go on to next step. - ! u_{n+1} = normalBarotropicVelocity_{n+1} + normalBaroclinicVelocity_{n+1} - ! Right now normalBaroclinicVelocityNew is at time n+1/2, so back compute to get normalBaroclinicVelocity at time n+1 - ! using normalBaroclinicVelocity_{n+1/2} = 1/2*(normalBaroclinicVelocity_n + u_Bcl_{n+1}) + ! u_{n+1} = normalBarotropicVelocity_{n+1} + normalBaroclinicVelocity_{n+1} + ! Right now normalBaroclinicVelocityNew is at time n+1/2, so back compute to get normalBaroclinicVelocity + ! at time n+1 using normalBaroclinicVelocity_{n+1/2} = 1/2*(normalBaroclinicVelocity_n + u_Bcl_{n+1}) ! so the following lines are ! u_{n+1} = normalBarotropicVelocity_{n+1} + 2*normalBaroclinicVelocity_{n+1/2} - normalBaroclinicVelocity_n ! note that normalBaroclinicVelocity is recomputed at the beginning of the next timestep due to Imp Vert mixing, ! so normalBaroclinicVelocity does not have to be recomputed here. - + + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges do k = 1, maxLevelEdgeTop(iEdge) - normalVelocityNew(k,iEdge) = normalBarotropicVelocityNew(iEdge) + 2 * normalBaroclinicVelocityNew(k,iEdge) - normalBaroclinicVelocityCur(k,iEdge) + normalVelocityNew(k,iEdge) = normalBarotropicVelocityNew(iEdge) + 2 * normalBaroclinicVelocityNew(k,iEdge) & + - normalBaroclinicVelocityCur(k,iEdge) end do end do ! iEdges + !$omp end do endif ! split_explicit_step block => block % next end do - + call mpas_timer_stop('se loop fini') + call mpas_timer_stop('se loop') end do ! split_explicit_step = 1, config_n_ts_iter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! END large iteration loop + ! END large iteration loop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! Perform Sea Ice Formation Adjustment - block => domain % blocklist - do while(associated(block)) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) - call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) - call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexSalinity) - - call mpas_pool_get_array(statePool, 'layerThickness', layerThicknessNew, 2) - call mpas_pool_get_array(statePool, 'tracers', tracersNew, 2) - - call mpas_pool_get_array(forcingPool, 'seaIceEnergy', seaIceEnergy) - - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 2) - call ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, layerThicknessNew, & - tracersNew, seaIceEnergy, err) - block => block % next - end do - call mpas_timer_start("se implicit vert mix") + block => domain % blocklist do while(associated(block)) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - ! Call ocean diagnostic solve in preparation for vertical mixing. Note + ! Call ocean diagnostic solve in preparation for vertical mixing. Note ! it is called again after vertical mixing, because u and tracers change. - ! For Richardson vertical mixing, only density, layerThicknessEdge, and kineticEnergyCell need to + ! For Richardson vertical mixing, only density, layerThicknessEdge, and kineticEnergyCell need to ! be computed. For kpp, more variables may be needed. Either way, this ! could be made more efficient by only computing what is needed for the ! implicit vmix routine that follows. - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 2) + call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, 2) ! Compute normalGMBolusVelocity; it will be added to the baroclinic modes in Stage 2 above. if (config_use_standardGM) then call ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) end if - call ocn_vmix_implicit(dt, meshPool, diagnosticsPool, statePool, err, 2) + call ocn_vmix_implicit(dt, meshPool, diagnosticsPool, statePool, forcingPool, scratchPool, err, 2) block => block % next end do - ! Update halo on u and tracers, which were just updated for implicit vertical mixing. If not done, + call mpas_threading_barrier() + + ! Update halo on u and tracers, which were just updated for implicit vertical mixing. If not done, ! this leads to lack of volume conservation. It is required because halo updates in stage 3 are only - ! conducted on tendencies, not on the velocity and tracer fields. So this update is required to + ! conducted on tendencies, not on the velocity and tracer fields. So this update is required to ! communicate the change due to implicit vertical mixing across the boundary. - call mpas_timer_start("se implicit vert mix halos") + call mpas_timer_start('se vmix halos') + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + + call mpas_timer_start('se vmix halos normalVelFld') call mpas_dmpar_field_halo_exch(domain, 'normalVelocity', timeLevel=2) - call mpas_dmpar_field_halo_exch(domain, 'tracers', timeLevel=2) - call mpas_timer_stop("se implicit vert mix halos") + call mpas_timer_stop('se vmix halos normalVelFld') + + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + call mpas_dmpar_field_halo_exch(domain, groupItr % memberName, timeLevel=2) + end if + end do + call mpas_timer_stop('se vmix halos') call mpas_timer_stop("se implicit vert mix") + call mpas_threading_barrier() + + call mpas_timer_start('se fini') block => domain % blocklist do while (associated(block)) call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - call mpas_pool_get_subpool(block % structs, 'average', averagePool) + + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCellsPtr) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdgesPtr) + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityCur, 1) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocityNew, 2) @@ -1444,39 +1845,61 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ call mpas_pool_get_dimension(diagnosticsPool, 'index_SSHGradientZonal', indexSSHGradientZonal) call mpas_pool_get_dimension(diagnosticsPool, 'index_SSHGradientMeridional', indexSSHGradientMeridional) + nCells = nCellsPtr + nEdges = nEdgesPtr + if (config_prescribe_velocity) then - normalVelocityNew(:,:) = normalVelocityCur(:,:) + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalVelocityNew(:, iEdge) = normalVelocityCur(:, iEdge) + end do + !$omp end do end if if (config_prescribe_thickness) then - layerThicknessNew(:,:) = layerThicknessCur(:,:) + !$omp do schedule(runtime) + do iCell = 1, nCells + layerThicknessNew(:, iCell) = layerThicknessCur(:, iCell) + end do + !$omp end do end if - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, 2) + call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, 2) - ! Compute normalGMBolusVelocity; it will be added to normalVelocity in Stage 2 of the next cycle. + ! Update the effective desnity in land ice if we're coupling to land ice + call ocn_effective_density_in_land_ice_update(meshPool, forcingPool, statePool, scratchPool, err) + + ! Compute normalGMBolusVelocity; it will be added to normalVelocity in Stage 2 of the next cycle. if (config_use_standardGM) then call ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) end if - call mpas_reconstruct(meshPool, normalVelocityNew, & - velocityX, velocityY, velocityZ, & - velocityZonal, velocityMeridional & - ) + call mpas_timer_start('se final mpas reconstruct', .false.) + + call mpas_reconstruct(meshPool, normalVelocityNew, & + velocityX, velocityY, velocityZ, & + velocityZonal, velocityMeridional, & + includeHalos = .true.) - call mpas_reconstruct(meshPool, gradSSH, & - gradSSHX, gradSSHY, gradSSHZ, & - gradSSHZonal, gradSSHMeridional & - ) + call mpas_reconstruct(meshPool, gradSSH, & + gradSSHX, gradSSHY, gradSSHZ, & + gradSSHZonal, gradSSHMeridional, & + includeHalos = .true.) - surfaceVelocity(indexSurfaceVelocityZonal, :) = velocityZonal(1, :) - surfaceVelocity(indexSurfaceVelocityMeridional, :) = velocityMeridional(1, :) + call mpas_timer_stop('se final mpas reconstruct') - SSHGradient(indexSSHGradientZonal, :) = gradSSHZonal(1, :) - SSHGradient(indexSSHGradientMeridional, :) = gradSSHMeridional(1, :) + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceVelocity(indexSurfaceVelocityZonal, iCell) = velocityZonal(1, iCell) + surfaceVelocity(indexSurfaceVelocityMeridional, iCell) = velocityMeridional(1, iCell) + + SSHGradient(indexSSHGradientZonal, iCell) = gradSSHZonal(iCell) + SSHGradient(indexSSHGradientMeridional, iCell) = gradSSHMeridional(iCell) + end do + !$omp end do - call ocn_time_average_accumulate(averagePool, statePool, diagnosticsPool, 2) - call ocn_time_average_coupled_accumulate(diagnosticsPool, forcingPool) + call ocn_time_average_coupled_accumulate(diagnosticsPool, statePool, forcingPool, 2) + call mpas_threading_barrier() if (config_use_standardGM) then call ocn_reconstruct_gm_vectors(diagnosticsPool, meshPool) @@ -1485,6 +1908,15 @@ subroutine ocn_time_integrator_split(domain, dt)!{{{ block => block % next end do + if (trim(config_land_ice_flux_mode) == 'coupled') then + call mpas_timer_start("se effective density halo") + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_field(statePool, 'effectiveDensityInLandIce', effectiveDensityField, 2) + call mpas_dmpar_exch_halo_field(effectiveDensityField) + call mpas_timer_stop("se effective density halo") + end if + + call mpas_timer_stop('se fini') call mpas_timer_stop("se timestep") deallocate(n_bcl_iter) @@ -1511,7 +1943,7 @@ subroutine ocn_time_integration_split_init(domain)!{{{ integer :: i, iCell, iEdge, iVertex, k type (block_type), pointer :: block - type (mpas_pool_type), pointer :: statePool, meshPool + type (mpas_pool_type), pointer :: statePool, meshPool, tracersPool integer :: iTracer, cell, cell1, cell2 integer, dimension(:), pointer :: maxLevelEdgeTop @@ -1522,96 +1954,142 @@ subroutine ocn_time_integration_split_init(domain)!{{{ real (kind=RKIND), dimension(:,:), pointer :: layerThickness real (kind=RKIND), dimension(:,:), pointer :: normalBaroclinicVelocity, normalVelocity integer, pointer :: nVertLevels, nCells, nEdges - character (len=StrKIND), pointer :: config_time_integrator - logical, pointer :: config_filter_btr_mode + character (len=StrKIND), pointer :: config_time_integrator, config_btr_dt, config_dt + logical, pointer :: config_filter_btr_mode, config_do_restart - ! Initialize z-level mesh variables from h, read in from input file. - block => domain % blocklist - do while (associated(block)) - call mpas_pool_get_config(block % configs, 'config_time_integrator', config_time_integrator) - call mpas_pool_get_config(block % configs, 'config_filter_btr_mode', config_filter_btr_mode) - call mpas_pool_get_subpool(block % structs, 'state', statePool) - call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + type (mpas_time_type) :: nowTime + type (mpas_timeInterval_type) :: fullTimeStep, barotropicTimeStep, remainder, zeroInterval - call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) - call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + integer :: iErr + integer (kind=I8KIND) :: nBtrSubcyclesI8 + + call mpas_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) - call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 1) - call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocity, 1) - call mpas_pool_get_array(statePool, 'normalBaroclinicVelocity', normalBaroclinicVelocity, 1) + ! Determine the number of barotropic subcycles based on the ratio of time steps + call mpas_pool_get_config(domain % configs, 'config_time_integrator', config_time_integrator) + call mpas_pool_get_config(domain % configs, 'config_btr_dt', config_btr_dt) + call mpas_pool_get_config(domain % configs, 'config_dt', config_dt) - call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) - call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + nowTime = mpas_get_clock_time(domain % clock, MPAS_NOW, ierr) + call mpas_set_timeInterval( zeroInterval, S=0 ) - ! Compute barotropic velocity at first timestep - ! This is only done upon start-up. - if (trim(config_time_integrator) == 'unsplit_explicit') then - call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocity) - normalBarotropicVelocity(:) = 0.0 + call mpas_set_timeInterval( fullTimeStep , timeString=config_dt ) + call mpas_set_timeInterval( barotropicTimeStep, timeString=config_btr_dt ) - normalBaroclinicVelocity(:,:) = normalVelocity(:,:) + ! transfer to I8 for division step + nBtrSubcyclesI8 = nBtrSubcycles + call mpas_interval_division( nowTime, fullTimeStep, barotropicTimeStep, nBtrSubcyclesI8, remainder ) + nBtrSubcycles = nBtrSubcyclesI8 - elseif (trim(config_time_integrator) == 'split_explicit') then + if ( remainder > zeroInterval ) then + nBtrSubcycles = nBtrSubcycles + 1 + end if - if (config_filter_btr_mode) then - do iCell = 1, nCells - layerThickness(1,iCell) = refBottomDepth(1) - enddo - endif + if (trim(config_time_integrator) == 'split_explicit') then + call mpas_log_write( '*******************************************************************************') + call mpas_log_write( 'The split explicit time integration is configured to use: $i barotropic subcycles', & + intArgs=(/ nBtrSubcycles /) ) + call mpas_log_write( '*******************************************************************************') + end if - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) + if ( .not. config_do_restart ) then + ! Initialize z-level mesh variables from h, read in from input file. + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_config(block % configs, 'config_time_integrator', config_time_integrator) + call mpas_pool_get_config(block % configs, 'config_filter_btr_mode', config_filter_btr_mode) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(block % dimensions, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 1) + call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocity, 1) + call mpas_pool_get_array(statePool, 'normalBaroclinicVelocity', normalBaroclinicVelocity, 1) + + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) - ! normalBarotropicVelocity = sum(u)/sum(h) on each column - ! ocn_diagnostic_solve has not yet been called, so compute hEdge - ! just for this edge. + ! Compute barotropic velocity at first timestep + ! This is only done upon start-up. + if (trim(config_time_integrator) == 'unsplit_explicit') then + call mpas_pool_get_array(statePool, 'normalBarotropicVelocity', normalBarotropicVelocity) - ! thicknessSum is initialized outside the loop because on land boundaries - ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a - ! nonzero value to avoid a NaN. - layerThicknessEdge1 = 0.5*( layerThickness(1,cell1) + layerThickness(1,cell2) ) - normalThicknessFluxSum = layerThicknessEdge1 * normalVelocity(1,iEdge) - layerThicknessSum = layerThicknessEdge1 + do iEdge = 1, nEdges + normalBarotropicVelocity(iEdge) = 0.0_RKIND + normalBaroclinicVelocity(:, iEdge) = normalVelocity(:, iEdge) + end do - do k=2, maxLevelEdgeTop(iEdge) + elseif (trim(config_time_integrator) == 'split_explicit') then + + call mpas_log_write( '*******************************************************************************') + call mpas_log_write( 'The split explicit time integration is configured to use: $i barotropic subcycles', & + intArgs=(/ nBtrSubcycles /) ) + call mpas_log_write( '*******************************************************************************') + + if (config_filter_btr_mode) then + do iCell = 1, nCells + layerThickness(1,iCell) = refBottomDepth(1) + enddo + endif + + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + + ! normalBarotropicVelocity = sum(h*u)/sum(h) on each edge ! ocn_diagnostic_solve has not yet been called, so compute hEdge ! just for this edge. - layerThicknessEdge1 = 0.5*( layerThickness(k,cell1) + layerThickness(k,cell2) ) - normalThicknessFluxSum = normalThicknessFluxSum & - + layerThicknessEdge1 * normalVelocity(k,iEdge) - layerThicknessSum = layerThicknessSum + layerThicknessEdge1 + ! thicknessSum is initialized outside the loop because on land boundaries + ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a + ! nonzero value to avoid a NaN. + layerThicknessEdge1 = 0.5_RKIND*( layerThickness(1,cell1) + layerThickness(1,cell2) ) + normalThicknessFluxSum = layerThicknessEdge1 * normalVelocity(1,iEdge) + layerThicknessSum = layerThicknessEdge1 + + do k=2, maxLevelEdgeTop(iEdge) + ! ocn_diagnostic_solve has not yet been called, so compute hEdge + ! just for this edge. + layerThicknessEdge1 = 0.5_RKIND*( layerThickness(k,cell1) + layerThickness(k,cell2) ) - enddo - normalBarotropicVelocity(iEdge) = normalThicknessFluxSum / layerThicknessSum + normalThicknessFluxSum = normalThicknessFluxSum & + + layerThicknessEdge1 * normalVelocity(k,iEdge) + layerThicknessSum = layerThicknessSum + layerThicknessEdge1 - ! normalBaroclinicVelocity(k,iEdge) = normalVelocity(k,iEdge) - normalBarotropicVelocity(iEdge) - do k = 1, maxLevelEdgeTop(iEdge) - normalBaroclinicVelocity(k,iEdge) = normalVelocity(k,iEdge) - normalBarotropicVelocity(iEdge) - enddo + enddo + normalBarotropicVelocity(iEdge) = normalThicknessFluxSum / layerThicknessSum - ! normalBaroclinicVelocity=0, normalVelocity=0 on land cells - do k = maxLevelEdgeTop(iEdge)+1, nVertLevels - normalBaroclinicVelocity(k,iEdge) = 0.0 - normalVelocity(k,iEdge) = 0.0 + ! normalBaroclinicVelocity(k,iEdge) = normalVelocity(k,iEdge) - normalBarotropicVelocity(iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + normalBaroclinicVelocity(k,iEdge) = normalVelocity(k,iEdge) - normalBarotropicVelocity(iEdge) + enddo + + ! normalBaroclinicVelocity=0, normalVelocity=0 on land cells + do k = maxLevelEdgeTop(iEdge)+1, nVertLevels + normalBaroclinicVelocity(k,iEdge) = 0.0_RKIND + normalVelocity(k,iEdge) = 0.0_RKIND + enddo enddo - enddo - if (config_filter_btr_mode) then - ! filter normalBarotropicVelocity out of initial condition - normalVelocity(:,:) = normalBaroclinicVelocity(:,:) + if (config_filter_btr_mode) then + ! filter normalBarotropicVelocity out of initial condition - normalBarotropicVelocity(:) = 0.0 - endif + normalVelocity(:,:) = normalBaroclinicVelocity(:,:) + normalBarotropicVelocity(:) = 0.0_RKIND - endif + endif - block => block % next - end do + endif + + block => block % next + end do + end if end subroutine ocn_time_integration_split_init!}}} diff --git a/src/core_ocean/mode_init/Makefile b/src/core_ocean/mode_init/Makefile new file mode 100644 index 0000000000..4f435e7ab5 --- /dev/null +++ b/src/core_ocean/mode_init/Makefile @@ -0,0 +1,88 @@ +.SUFFIXES: .F .o + +OBJS = mpas_ocn_init_mode.o + +UTILS = mpas_ocn_init_spherical_utils.o \ + mpas_ocn_init_vertical_grids.o \ + mpas_ocn_init_cell_markers.o \ + mpas_ocn_init_interpolation.o \ + mpas_ocn_init_ssh_and_landIcePressure.o + +TEST_CASES = mpas_ocn_init_baroclinic_channel.o \ + mpas_ocn_init_lock_exchange.o \ + mpas_ocn_init_internal_waves.o \ + mpas_ocn_init_overflow.o \ + mpas_ocn_init_cvmix_WSwSBF.o \ + mpas_ocn_init_iso.o \ + mpas_ocn_init_soma.o \ + mpas_ocn_init_ziso.o \ + mpas_ocn_init_sub_ice_shelf_2D.o \ + mpas_ocn_init_periodic_planar.o \ + mpas_ocn_init_ecosys_column.o \ + mpas_ocn_init_sea_mount.o \ + mpas_ocn_init_global_ocean.o \ + mpas_ocn_init_isomip.o \ + mpas_ocn_init_isomip_plus.o + #mpas_ocn_init_TEMPLATE.o + +all: init_mode + +init_mode: $(UTILS) $(TEST_CASES) $(OBJS) + +mpas_ocn_init_mode.o: $(UTILS) $(TEST_CASES) + +mpas_ocn_init_cell_markers.o: + +mpas_ocn_init_interpolation.o: + +mpas_ocn_init_ssh_and_landIcePressure.o: mpas_ocn_init_interpolation.o mpas_ocn_init_vertical_grids.o + +mpas_ocn_init_spherical_utils.o: + +mpas_ocn_init_vertical_grids.o: + +mpas_ocn_init_seaSurfaceHeightAndPressure.o: + +mpas_ocn_init_baroclinic_channel.o: $(UTILS) + +mpas_ocn_init_iso.o: $(UTILS) + +mpas_ocn_init_soma.o: $(UTILS) + +mpas_ocn_init_lock_exchange.o: $(UTILS) + +mpas_ocn_init_internal_waves.o: $(UTILS) + +mpas_ocn_init_overflow.o: $(UTILS) + +mpas_ocn_init_global_ocean.o: $(UTILS) + +mpas_ocn_init_sub_ice_shelf_2D.o: $(UTILS) + +mpas_ocn_init_cvmix_WSwSBF.o: $(UTILS) + +mpas_ocn_init_periodic_planar.o: $(UTILS) + +mpas_ocn_init_ecosys_column.o: $(UTILS) + +mpas_ocn_init_sea_mount.o: $(UTILS) + +mpas_ocn_init_isomip.o: $(UTILS) + +mpas_ocn_init_isomip_plus.o: $(UTILS) + +mpas_ocn_init_ziso.o: $(UTILS) + +#mpas_ocn_init_TEMPLATE.o: $(UTILS) + +clean: + $(RM) *.o *.mod *.f90 + +.F.o: + $(RM) $@ $*.mod +ifeq "$(GEN_F90)" "true" + $(CPP) $(CPPFLAGS) $(CPPINCLUDES) $< > $*.f90 + $(FC) $(FFLAGS) -c $*.f90 $(FCINCLUDES) +else + $(FC) $(CPPFLAGS) $(FFLAGS) -c $*.F $(CPPINCLUDES) $(FCINCLUDES) +endif diff --git a/src/core_ocean/mode_init/Registry.xml b/src/core_ocean/mode_init/Registry.xml new file mode 100644 index 0000000000..2df1a0ffe0 --- /dev/null +++ b/src/core_ocean/mode_init/Registry.xml @@ -0,0 +1,16 @@ +#include "Registry_baroclinic_channel.xml" +#include "Registry_lock_exchange.xml" +#include "Registry_internal_waves.xml" +#include "Registry_overflow.xml" +#include "Registry_global_ocean.xml" +#include "Registry_cvmix_WSwSBF.xml" +#include "Registry_iso.xml" +#include "Registry_soma.xml" +#include "Registry_ziso.xml" +#include "Registry_sub_ice_shelf_2D.xml" +#include "Registry_periodic_planar.xml" +#include "Registry_ecosys_column.xml" +#include "Registry_sea_mount.xml" +#include "Registry_isomip.xml" +#include "Registry_isomip_plus.xml" +// #include "Registry_TEMPLATE.xml" diff --git a/src/core_ocean/mode_init/Registry_TEMPLATE.xml b/src/core_ocean/mode_init/Registry_TEMPLATE.xml new file mode 100644 index 0000000000..7325bf0dc0 --- /dev/null +++ b/src/core_ocean/mode_init/Registry_TEMPLATE.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/src/core_ocean/mode_init/Registry_baroclinic_channel.xml b/src/core_ocean/mode_init/Registry_baroclinic_channel.xml new file mode 100644 index 0000000000..7cd265675a --- /dev/null +++ b/src/core_ocean/mode_init/Registry_baroclinic_channel.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_cvmix_WSwSBF.xml b/src/core_ocean/mode_init/Registry_cvmix_WSwSBF.xml new file mode 100644 index 0000000000..d5f78791b6 --- /dev/null +++ b/src/core_ocean/mode_init/Registry_cvmix_WSwSBF.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_ecosys_column.xml b/src/core_ocean/mode_init/Registry_ecosys_column.xml new file mode 100644 index 0000000000..5915257082 --- /dev/null +++ b/src/core_ocean/mode_init/Registry_ecosys_column.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_global_ocean.xml b/src/core_ocean/mode_init/Registry_global_ocean.xml new file mode 100644 index 0000000000..88c8026b7e --- /dev/null +++ b/src/core_ocean/mode_init/Registry_global_ocean.xml @@ -0,0 +1,412 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_internal_waves.xml b/src/core_ocean/mode_init/Registry_internal_waves.xml new file mode 100644 index 0000000000..6e32fef15c --- /dev/null +++ b/src/core_ocean/mode_init/Registry_internal_waves.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_iso.xml b/src/core_ocean/mode_init/Registry_iso.xml new file mode 100644 index 0000000000..2fb245a01e --- /dev/null +++ b/src/core_ocean/mode_init/Registry_iso.xml @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_isomip.xml b/src/core_ocean/mode_init/Registry_isomip.xml new file mode 100644 index 0000000000..8d9da70414 --- /dev/null +++ b/src/core_ocean/mode_init/Registry_isomip.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_isomip_plus.xml b/src/core_ocean/mode_init/Registry_isomip_plus.xml new file mode 100644 index 0000000000..5a85b2a69f --- /dev/null +++ b/src/core_ocean/mode_init/Registry_isomip_plus.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_lock_exchange.xml b/src/core_ocean/mode_init/Registry_lock_exchange.xml new file mode 100644 index 0000000000..c3a380cf30 --- /dev/null +++ b/src/core_ocean/mode_init/Registry_lock_exchange.xml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_overflow.xml b/src/core_ocean/mode_init/Registry_overflow.xml new file mode 100644 index 0000000000..c751a86da6 --- /dev/null +++ b/src/core_ocean/mode_init/Registry_overflow.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_periodic_planar.xml b/src/core_ocean/mode_init/Registry_periodic_planar.xml new file mode 100644 index 0000000000..35fc0d654e --- /dev/null +++ b/src/core_ocean/mode_init/Registry_periodic_planar.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_sea_mount.xml b/src/core_ocean/mode_init/Registry_sea_mount.xml new file mode 100644 index 0000000000..e1f5061c9d --- /dev/null +++ b/src/core_ocean/mode_init/Registry_sea_mount.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_soma.xml b/src/core_ocean/mode_init/Registry_soma.xml new file mode 100644 index 0000000000..5abad8efac --- /dev/null +++ b/src/core_ocean/mode_init/Registry_soma.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_sub_ice_shelf_2D.xml b/src/core_ocean/mode_init/Registry_sub_ice_shelf_2D.xml new file mode 100644 index 0000000000..50497ed1ec --- /dev/null +++ b/src/core_ocean/mode_init/Registry_sub_ice_shelf_2D.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/Registry_ziso.xml b/src/core_ocean/mode_init/Registry_ziso.xml new file mode 100644 index 0000000000..662a47a725 --- /dev/null +++ b/src/core_ocean/mode_init/Registry_ziso.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/mode_init/mpas_ocn_init_TEMPLATE.F b/src/core_ocean/mode_init/mpas_ocn_init_TEMPLATE.F new file mode 100644 index 0000000000..4c5f748a6e --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_TEMPLATE.F @@ -0,0 +1,336 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_TEMPLATE +! +!> \brief MPAS ocean initialize case -- TEMPLATE +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This module contains the routines for initializing the +!> TEMPLATE initial condition +!> +!> In order to add a new analysis member, do the following: +!> 1. In src/core_ocean/mode_init, copy these to your new analysis member name: +!> cp mpas_ocn_init_TEMPLATE.F mpas_ocn_init_your_new_name.F +!> cp Registry_TEMPLATE.xml Registry_ocn_your_new_name.xml +!> +!> 2. In those two new files, replace the following text: +!> TEMPLATE, FILL_IN_AUTHOR, FILL_IN_DATE +!> TEMPLATE uses underscores (subroutine names), like your_new_name. +!> +!> 3. Add a #include line for your registry to +!> src/core_ocean/mode_init/Registry.xml +!> +!> 4. Copy and change TEMPLATE lines in src/core_ocean/mode_init/mpas_ocn_init_mode.F +!> +!> 5. Add these dependency lines by following TEMPLATE examples in: +!> in src/core_ocean/mode_init/Makefile +! +!----------------------------------------------------------------------- + +module ocn_init_TEMPLATE + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_TEMPLATE, & + ocn_init_validate_TEMPLATE + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_TEMPLATE +! +!> \brief Setup for this initial condition +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This routine sets up the initial conditions for this case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_TEMPLATE(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: verticalMeshPool + + ! local variables + integer :: iCell, k, idx + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin, dcEdgeMinGlobal + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, yMidGlobal, xMinGlobal, xMaxGlobal + real (kind=RKIND) :: localVar1, localVar2 + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid + logical, pointer :: config_TEMPLATE_example_flag1 + real (kind=RKIND), pointer :: config_TEMPLATE_example_flag2 + + ! Define dimension pointers + integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels, nVertLevelsP1 + integer, pointer :: index_temperature, index_salinity + + ! Define variable pointers + logical, pointer :: on_a_sphere + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: xCell, yCell,refBottomDepth, refZMid, & + vertCoordMovementWeights, bottomDepth, & + fCell, fEdge, fVertex, dcEdge + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('TEMPLATE')) return + + ! Get config flag settings + + call mpas_pool_get_config(ocnConfigs, 'config_vertical_grid', config_vertical_grid) + + call mpas_pool_get_config(ocnConfigs, 'config_TEMPLATE_example_flag1', config_TEMPLATE_example_flag1) + call mpas_pool_get_config(ocnConfigs, 'config_TEMPLATE_example_flag2', config_TEMPLATE_example_flag2) + + ! Determine vertical grid for configuration + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + ! you may restrict your case geometry as follows: + ! if ( on_a_sphere ) call mpas_log_write('The TEMPLATE configuration can only be applied ' & + ! // 'to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + !-------------------------------------------------------------------- + ! Use this section to make boundaries non-periodic + !-------------------------------------------------------------------- + + ! Initalize min/max values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + yMin = min( yMin, minval(yCell(1:nCellsSolve))) + yMax = max( yMax, maxval(yCell(1:nCellsSolve))) + xMin = min( xMin, minval(xCell(1:nCellsSolve))) + xMax = max( xMax, maxval(xCell(1:nCellsSolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + + block_ptr => block_ptr % next + end do + + ! Determine global min and max values. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + + block_ptr => block_ptr % next + end do + + !-------------------------------------------------------------------- + ! Use this section to set initial values + !-------------------------------------------------------------------- + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + + ! ! Set refBottomDepth and refZMid + do k = 1, nVertLevels + refBottomDepth(k) = config_TEMPLATE_bottom_depth * interfaceLocations(k+1) + refZMid(k) = - 0.5_RKIND * (interfaceLocations(k+1) + interfaceLocations(k)) * config_TEMPLATE_bottom_depth + end do + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + do iCell = 1, nCellsSolve + + ! Set temperature + idx = index_temperature + do k = 1, nVertLevels + ! activeTracers(idx, k, iCell) = + end do + + ! Set salinity + idx = index_salinity + do k = 1, nVertLevels + ! activeTracers(idx, k, iCell) = + end do + + ! Set layerThickness and restingThickness + do k = 1, nVertLevels + ! layerThickness(k, iCell) = + ! restingThickness(k, iCell) = + end do + + ! Set bottomDepth + ! bottomDepth(iCell) = + + ! Set maxLevelCell + ! maxLevelCell(iCell) = + + ! Set Coriolis parameters, if other than zero + fCell(iCell) = config_TEMPLATE_coriolis_parameter + fEdge(iCell) = config_TEMPLATE_coriolis_parameter + fVertex(iCell) = config_TEMPLATE_coriolis_parameter + + end do + + block_ptr => block_ptr % next + end do + + deallocate(interfaceLocations) + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_TEMPLATE!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_TEMPLATE +! +!> \brief Validation for this initial condition +!> \author FILL_IN_AUTHOR +!> \date FILL_IN_DATE +!> \details +!> This routine validates the configuration options for this case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_TEMPLATE(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: configPool, packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_TEMPLATE_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('TEMPLATE')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_TEMPLATE_vert_levels', config_TEMPLATE_vert_levels) + + if(config_vert_levels <= 0 .and. config_TEMPLATE_vert_levels > 0) then + config_vert_levels = config_TEMPLATE_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for TEMPLATE. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_TEMPLATE!}}} + + +!*********************************************************************** + +end module ocn_init_TEMPLATE + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_baroclinic_channel.F b/src/core_ocean/mode_init/mpas_ocn_init_baroclinic_channel.F new file mode 100644 index 0000000000..2df9b44e62 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_baroclinic_channel.F @@ -0,0 +1,379 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_baroclinic_channel +! +!> \brief MPAS ocean initialize case -- Baroclinic Channel +!> \author Doug Jacobsen +!> \date 02/18/2014 +!> \details +!> This module contains the routines for initializing the +!> the baroclinic channel test case +! +!----------------------------------------------------------------------- + +module ocn_init_baroclinic_channel + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_baroclinic_channel, & + ocn_init_validate_baroclinic_channel + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_baroclinic_channel +! +!> \brief Setup for baroclinic channel test case +!> \author Doug Jacobsen +!> \date 02/19/2014 +!> \details +!> This routine sets up the initial conditions for the baroclinic channel test case. +!> It should also ensure the mesh that was input is valid for the configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_baroclinic_channel(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin, dcEdgeMinGlobal + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, yMidGlobal, xMinGlobal, xMaxGlobal + real (kind=RKIND) :: temperature, yOffset, xPerturbationMin, xPerturbationMax + real (kind=RKIND) :: perturbationWidth + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: verticalMeshPool + + integer :: iCell, k, idx + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid + logical, pointer :: config_baroclinic_channel_use_distances + real (kind=RKIND), pointer :: config_baroclinic_channel_gradient_width_dist, & + config_baroclinic_channel_gradient_width_frac, & + config_baroclinic_channel_bottom_depth, & + config_baroclinic_channel_surface_temperature, & + config_baroclinic_channel_bottom_temperature, & + config_baroclinic_channel_temperature_difference, & + config_baroclinic_channel_salinity, & + config_baroclinic_channel_coriolis_parameter + + ! Define dimension pointers + integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels, nVertLevelsP1 + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + ! Define variable pointers + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: xCell, yCell,refBottomDepth, refZMid, & + vertCoordMovementWeights, bottomDepth, & + fCell, fEdge, fVertex, dcEdge + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + + ! Define local interfaceLocations variable + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + logical, pointer :: on_a_sphere + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('baroclinic_channel')) return + + call mpas_pool_get_config(ocnConfigs, 'config_vertical_grid', config_vertical_grid) + + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_use_distances', config_baroclinic_channel_use_distances) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_gradient_width_dist', & + config_baroclinic_channel_gradient_width_dist) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_gradient_width_frac', & + config_baroclinic_channel_gradient_width_frac) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_bottom_depth', config_baroclinic_channel_bottom_depth) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_surface_temperature', & + config_baroclinic_channel_surface_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_bottom_temperature', & + config_baroclinic_channel_bottom_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_temperature_difference', & + config_baroclinic_channel_temperature_difference) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_salinity', config_baroclinic_channel_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_baroclinic_channel_coriolis_parameter', & + config_baroclinic_channel_coriolis_parameter) + + ! Determine vertical grid for configuration + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write('The baroclinic channel configuration ' & + // 'can only be applied to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + ! Initalize min/max values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + yMin = min( yMin, minval(yCell(1:nCellsSolve))) + yMax = max( yMax, maxval(yCell(1:nCellsSolve))) + xMin = min( xMin, minval(xCell(1:nCellsSolve))) + xMax = max( xMax, maxval(xCell(1:nCellsSolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + + block_ptr => block_ptr % next + end do + + ! Determine global min and max values. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + yMidGlobal = (yMinGlobal + yMaxGlobal) * 0.5_RKIND + xPerturbationMin = xMinGlobal + 4.0_RKIND * (xMaxGlobal - xMinGlobal) / 6.0_RKIND + xPerturbationMax = xMinGlobal + 5.0_RKIND * (xMaxGlobal - xMinGlobal) / 6.0_RKIND + if(config_baroclinic_channel_use_distances) then + perturbationWidth = config_baroclinic_channel_gradient_width_dist + else + perturbationWidth = (yMaxGlobal - yMinGlobal) * config_baroclinic_channel_gradient_width_frac + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + + ! Set refBottomDepth and refZMid + do k = 1, nVertLevels + refBottomDepth(k) = config_baroclinic_channel_bottom_depth * interfaceLocations(k+1) + refZMid(k) = - 0.5_RKIND * (interfaceLocations(k+1) + interfaceLocations(k)) * config_baroclinic_channel_bottom_depth + end do + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + do iCell = 1, nCellsSolve + ! Determine cutoff location for large sin wave + yOffset = perturbationWidth * sin (6.0_RKIND * pii * (xCell(iCell) - xMinGlobal) / (xMaxGlobal - xMinGlobal)) + + ! Set debug tracer + if ( associated(debugTracers) ) then + idx = index_tracer1 + do k = 1, nVertLevels + debugTracers(idx, k, iCell) = 1.0_RKIND + enddo + end if + + ! Set stratification based on northern half of domain temperature + if ( associated(activeTracers) ) then + idx = index_temperature + do k = nVertLevels, 1, -1 + temperature = config_baroclinic_channel_bottom_temperature & + + (config_baroclinic_channel_surface_temperature - config_baroclinic_channel_bottom_temperature) & + * ( (refZMid(k) + refBottomDepth(nVertLevels)) / refBottomDepth(nVertLevels) ) + activeTracers(idx, k, iCell) = temperature + end do + + if(yCell(iCell) < yMidGlobal - yOffset) then + ! If cell is in the southern half, outside the sin width, subtract temperature difference + activeTracers(idx, :, iCell) = activeTracers(idx, :, iCell) - config_baroclinic_channel_temperature_difference + else if(yCell(iCell) >= yMidGlobal - yOffset .and. & + yCell(iCell) < yMidGlobal - yOffset + perturbationWidth) then + activeTracers(idx, :, iCell) = activeTracers(idx, :, iCell) - config_baroclinic_channel_temperature_difference & + * ( 1.0_RKIND - ( yCell(iCell) - ((yMaxGlobal + yMinGlobal) * 0.5_RKIND & + - yOffset)) / perturbationWidth) + end if + + ! Determine yOffset for 3rd crest in sin wave. + yOffset = 0.5_RKIND * perturbationWidth * sin(pii * (xCell(iCell) - xPerturbationMin) & + / (xPerturbationMax - xPerturbationMin)) + + if ( yCell(iCell) >= yMidGlobal - yOffset - 0.5_RKIND * perturbationWidth .and. & + yCell(iCell) <= yMidGlobal - yOffset + 0.5_RKIND * perturbationWidth .and. & + xCell(iCell) >= xPerturbationMin .and. & + xCell(iCell) <= xPerturbationMax) then + + + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = activeTracers(idx, k, iCell) & + + 0.3_RKIND * ( 1.0_RKIND - ( ( yCell(iCell) - (yMidGlobal - yOffset)) & + / (0.5_RKIND * perturbationWidth))) + end do + end if + + ! Set salinity + idx = index_salinity + activeTracers(idx, :, iCell) = config_baroclinic_channel_salinity + end if + + ! Set layerThickness and restingThickness + do k = 1, nVertLevels + layerThickness(k, iCell) = config_baroclinic_channel_bottom_depth * ( interfaceLocations(k+1) & + - interfaceLocations(k) ) + restingThickness(k, iCell) = config_baroclinic_channel_bottom_depth * ( interfaceLocations(k+1) & + - interfaceLocations(k) ) + + end do + + ! Set bottomDepth + bottomDepth(iCell) = config_baroclinic_channel_bottom_depth + + ! Set maxLevelCell + maxLevelCell(iCell) = nVertLevels + end do + + ! Set Coriolis parameters + fCell(:) = config_baroclinic_channel_coriolis_parameter + fEdge(:) = config_baroclinic_channel_coriolis_parameter + fVertex(:) = config_baroclinic_channel_coriolis_parameter + + block_ptr => block_ptr % next + end do + + deallocate(interfaceLocations) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_baroclinic_channel!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_baroclinic_channel +! +!> \brief Validation for baroclinic channel test case +!> \author Doug Jacobsen +!> \date 02/20/2014 +!> \details +!> This routine validates the configuration options for the baroclinic channel test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_baroclinic_channel(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: configPool, packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_baroclinic_channel_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('baroclinic_channel')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_baroclinic_channel_vert_levels', config_baroclinic_channel_vert_levels) + + if(config_vert_levels <= 0 .and. config_baroclinic_channel_vert_levels > 0) then + config_vert_levels = config_baroclinic_channel_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for baroclinic channel. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_baroclinic_channel!}}} + +!*********************************************************************** + +end module ocn_init_baroclinic_channel + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_cell_markers.F b/src/core_ocean/mode_init/mpas_ocn_init_cell_markers.F new file mode 100644 index 0000000000..4f8d8bb7fe --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_cell_markers.F @@ -0,0 +1,301 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_cell_markers +! +!> \brief MPAS ocean cell marker +!> \author Doug Jacobsen +!> \date 03/20/2015 +!> \details +!> This module contains the routines for marking +!> cells for removing +! +!----------------------------------------------------------------------- +module ocn_init_cell_markers + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_timer + + use ocn_constants + + implicit none + private + + public :: ocn_mark_north_boundary, ocn_mark_south_boundary + public :: ocn_mark_east_boundary, ocn_mark_west_boundary + public :: ocn_mark_maxlevelcell + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + + !*********************************************************************** + ! + ! routine ocn_mark_north_boundary + ! + !> \brief North boundary marker + !> \author Doug Jacobsen + !> \date 03/30/2015 + !> \details + !> This routine marks cells along the north boundary of a domain for removal. + !> It can only be applied to a planar mesh. North-south is defined as the y direction. + ! + !----------------------------------------------------------------------- + subroutine ocn_mark_north_boundary(meshPool, yMax, edgeMin, iErr)!{{{ + implicit none + + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), intent(in) :: yMax + real (kind=RKIND), intent(in) :: edgeMin + integer, intent(out) :: iErr + + real (kind=RKIND), dimension(:), pointer :: yCell + integer, dimension(:), pointer :: cullCell + + logical, pointer :: on_a_sphere + integer, pointer :: nCells + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write( 'WARNING: Can only mark north boundaries of planar meshes. ' & + // 'Skipping marking of cells...') + + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'cullCell', cullCell) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + if ( associated(cullCell) ) then + do iCell = 1, nCells + if ( yCell(iCell) > yMax - 0.8_RKIND * edgeMin ) then + cullCell(iCell) = 1 + end if + end do + end if + + end subroutine ocn_mark_north_boundary!}}} + + !*********************************************************************** + ! + ! routine ocn_mark_south_boundary + ! + !> \brief south boundary marker + !> \author Doug Jacobsen + !> \date 03/30/2015 + !> \details + !> This routine marks cells along the south boundary of a domain for removal. + !> It can only be applied to a planar mesh. north-south is defined as the y direction. + ! + !----------------------------------------------------------------------- + subroutine ocn_mark_south_boundary(meshPool, yMin, edgeMin, iErr)!{{{ + implicit none + + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), intent(in) :: yMin + real (kind=RKIND), intent(in) :: edgeMin + integer, intent(out) :: iErr + + real (kind=RKIND), dimension(:), pointer :: yCell + integer, dimension(:), pointer :: cullCell + + logical, pointer :: on_a_sphere + integer, pointer :: nCells + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write( 'WARNING: Can only mark north boundaries of planar meshes. ' & + // 'Skipping marking of cells...') + + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'cullCell', cullCell) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + if ( associated(cullCell) ) then + do iCell = 1, nCells + if ( yCell(iCell) < yMin + 0.8_RKIND * edgeMin ) then + cullCell(iCell) = 1 + end if + end do + end if + + end subroutine ocn_mark_south_boundary!}}} + + !*********************************************************************** + ! + ! routine ocn_mark_east_boundary + ! + !> \brief East boundary marker + !> \author Doug Jacobsen + !> \date 03/30/2015 + !> \details + !> This routine marks cells along the east boundary of a domain for removal. + !> It can only be applied to a planar mesh. west-east is defined as the x direction. + ! + !----------------------------------------------------------------------- + subroutine ocn_mark_east_boundary(meshPool, xMax, edgeMin, iErr)!{{{ + implicit none + + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), intent(in) :: xMax + real (kind=RKIND), intent(in) :: edgeMin + integer, intent(out) :: iErr + + real (kind=RKIND), dimension(:), pointer :: xCell + integer, dimension(:), pointer :: cullCell + + logical, pointer :: on_a_sphere + integer, pointer :: nCells + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write( 'WARNING: Can only mark north boundaries of planar meshes. ' & + // 'Skipping marking of cells...') + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'cullCell', cullCell) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + if ( associated(cullCell) ) then + do iCell = 1, nCells + if ( xCell(iCell) > xMax - 0.8_RKIND * edgeMin ) then + cullCell(iCell) = 1 + end if + end do + end if + + end subroutine ocn_mark_east_boundary!}}} + + !*********************************************************************** + ! + ! routine ocn_mark_west_boundary + ! + !> \brief West boundary marker + !> \author Doug Jacobsen + !> \date 03/30/2015 + !> \details + !> This routine marks cells along the west boundary of a domain for removal. + !> It can only be applied to a planar mesh. west-east is defined as the x direction. + ! + !----------------------------------------------------------------------- + subroutine ocn_mark_west_boundary(meshPool, xMin, edgeMin, iErr)!{{{ + implicit none + + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), intent(in) :: xMin + real (kind=RKIND), intent(in) :: edgeMin + integer, intent(out) :: iErr + + real (kind=RKIND), dimension(:), pointer :: xCell + integer, dimension(:), pointer :: cullCell + + logical, pointer :: on_a_sphere + integer, pointer :: nCells + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write( 'WARNING: Can only mark north boundaries of planar meshes. ' & + // 'Skipping marking of cells...') + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'cullCell', cullCell) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + if ( associated(cullCell) ) then + do iCell = 1, nCells + if ( xCell(iCell) < xMin + 0.8_RKIND * edgeMin ) then + cullCell(iCell) = 1 + end if + end do + end if + + end subroutine ocn_mark_west_boundary!}}} + + !*********************************************************************** + ! + ! routine ocn_mark_maxlevelcell + ! + !> \brief MaxLevelCell cell marker + !> \author Doug Jacobsen + !> \date 03/31/2015 + !> \details + !> This routine marks cells for removal that have maxLevelCell <= 0. + ! + !----------------------------------------------------------------------- + subroutine ocn_mark_maxlevelcell(meshPool, iErr)!{{{ + implicit none + + type (mpas_pool_type), intent(in) :: meshPool + integer, intent(out) :: iErr + + integer, dimension(:), pointer :: cullCell, maxLevelCell + + logical, pointer :: on_a_sphere + + integer, pointer :: nCells + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_array(meshPool, 'cullCell', cullCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + if ( associated(cullCell) ) then + do iCell = 1, nCells + if ( maxLevelCell(iCell) <= 0 ) then + cullCell(iCell) = 1 + end if + end do + end if + + end subroutine ocn_mark_maxlevelcell!}}} + +!*********************************************************************** + +end module ocn_init_cell_markers + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_cvmix_WSwSBF.F b/src/core_ocean/mode_init/mpas_ocn_init_cvmix_WSwSBF.F new file mode 100644 index 0000000000..37d19604cd --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_cvmix_WSwSBF.F @@ -0,0 +1,478 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_cvmix_WSwSBF +! +!> \brief MPAS ocean initialize case -- CVMix Unit Test +!> WSwSBF means Wind Stress with Surface Buoyancy Forcing +!> \author Todd Ringler +!> \date 04/23/2015 +!> \details +!> This module contains the routines for initializing the +!> the cvmix WSwSBF unit test configuration. This in a +!> single column configuration +! +!----------------------------------------------------------------------- + +module ocn_init_cvmix_WSwSBF + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + + use ocn_init_cell_markers + use ocn_init_vertical_grids + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_cvmix_WSwSBF, & + ocn_init_validate_cvmix_WSwSBF + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_cvmix_WSwSBF +! +!> \brief Setup for cvmix WSwSBF unit test configuration +!> \author Todd Ringler +!> \date 04/23/2015 +!> \details +!> This routine sets up the initial conditions for the cvmix WSwSBF unit test configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_cvmix_WSwSBF(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + real (kind=RKIND) :: temperature, salinity + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, statePool + type (mpas_pool_type), pointer :: diagnosticsPool, forcingPool + + type (mpas_pool_type), pointer :: tracersPool, & + tracersSurfaceRestoringFieldsPool, & + tracersInteriorRestoringFieldsPool + + integer, pointer :: nVertLevels, nVertLevelsP1, nCellsSolve, nEdgesSolve, nVerticesSolve + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: refBottomDepth, refZMid, vertCoordMovementWeights + real (kind=RKIND), dimension(:), pointer :: windStressZonal, windStressMeridional + real (kind=RKIND), dimension(:), pointer :: latentHeatFlux, sensibleHeatFlux, shortWaveHeatFlux + real (kind=RKIND), dimension(:), pointer :: evaporationFlux, rainFlux + real (kind=RKIND), dimension(:), pointer :: salinityRestore, bottomDepth, angleEdge + real (kind=RKIND), dimension(:), pointer :: fCell, fEdge, fVertex + real (kind=RKIND), dimension(:, :), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracers, debugTracers + real (kind=RKIND), dimension(:, :), pointer :: activeTracersPistonVelocity, activeTracersSurfaceRestoringValue + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracersInteriorRestoringValue, activeTracersInteriorRestoringRate + + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + integer :: iCell, iEdge, iVertex, k, kML + + real (kind=RKIND) :: BLdepth + + character (len=StrKIND), pointer :: config_init_configuration, & + config_cvmix_WSwSBF_vertical_grid + + integer, pointer :: config_cvmix_WSwSBF_vert_levels + + real (kind=RKIND), pointer :: config_cvmix_WSwSBF_surface_temperature, & + config_cvmix_WSwSBF_surface_salinity, & + config_cvmix_WSwSBF_surface_restoring_temperature, & + config_cvmix_WSwSBF_surface_restoring_salinity, & + config_cvmix_WSwSBF_temperature_piston_velocity, & + config_cvmix_WSwSBF_salinity_piston_velocity, & + config_cvmix_WSwSBF_sensible_heat_flux, & + config_cvmix_WSwSBF_latent_heat_flux, & + config_cvmix_WSwSBF_shortwave_heat_flux, & + config_cvmix_WSwSBF_rain_flux, & + config_cvmix_WSwSBF_evaporation_flux, & + config_cvmix_WSwSBF_interior_temperature_restoring_rate, & + config_cvmix_WSwSBF_interior_salinity_restoring_rate, & + config_cvmix_WSwSBF_temperature_gradient, & + config_cvmix_WSwSBF_salinity_gradient, & + config_cvmix_WSwSBF_bottom_depth, & + config_cvmix_WSwSBF_max_windstress, & + config_cvmix_WSwSBF_coriolis_parameter, & + config_cvmix_WSwSBF_temperature_gradient_mixed_layer, & + config_cvmix_WSwSBF_salinity_gradient_mixed_layer, & + config_cvmix_WSwSBF_mixed_layer_depth_temperature, & + config_cvmix_WSwSBF_mixed_layer_depth_salinity, & + config_cvmix_WSwSBF_mixed_layer_temperature_change, & + config_cvmix_WSwSBF_mixed_layer_salinity_change + ! assume no error + iErr = 0 + + ! get and test if this is the configuration specified + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('cvmix_WSwSBF')) return + + ! build the vertical grid + ! intent(out) is interfaceLocations. An array ranging from 0 to 1 + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_vertical_grid', config_cvmix_WSwSBF_vertical_grid) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevelsP1', nVertLevelsP1) + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid(config_cvmix_WSwSBF_vertical_grid, interfaceLocations) + + ! load the remaining configuration parameters + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_surface_temperature', & + config_cvmix_WSwSBF_surface_temperature) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_surface_salinity', config_cvmix_WSwSBF_surface_salinity) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_surface_restoring_temperature', & + config_cvmix_WSwSBF_surface_restoring_temperature) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_surface_restoring_salinity', & + config_cvmix_WSwSBF_surface_restoring_salinity) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_temperature_piston_velocity', & + config_cvmix_WSwSBF_temperature_piston_velocity) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_salinity_piston_velocity', & + config_cvmix_WSwSBF_salinity_piston_velocity) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_sensible_heat_flux', config_cvmix_WSwSBF_sensible_heat_flux) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_latent_heat_flux', config_cvmix_WSwSBF_latent_heat_flux) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_shortwave_heat_flux', & + config_cvmix_WSwSBF_shortwave_heat_flux) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_rain_flux', config_cvmix_WSwSBF_rain_flux) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_evaporation_flux', config_cvmix_WSwSBF_evaporation_flux) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_interior_temperature_restoring_rate', & + config_cvmix_WSwSBF_interior_temperature_restoring_rate) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_interior_salinity_restoring_rate', & + config_cvmix_WSwSBF_interior_salinity_restoring_rate) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_temperature_gradient', & + config_cvmix_WSwSBF_temperature_gradient) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_salinity_gradient', config_cvmix_WSwSBF_salinity_gradient) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_bottom_depth', config_cvmix_WSwSBF_bottom_depth) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_max_windstress', config_cvmix_WSwSBF_max_windstress) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_coriolis_parameter', config_cvmix_WSwSBF_coriolis_parameter) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_temperature_gradient_mixed_layer', & + config_cvmix_WSwSBF_temperature_gradient_mixed_layer) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_salinity_gradient_mixed_layer', & + config_cvmix_WSwSBF_salinity_gradient_mixed_layer) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_mixed_layer_depth_temperature', & + config_cvmix_WSwSBF_mixed_layer_depth_temperature) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_mixed_layer_depth_salinity', & + config_cvmix_WSwSBF_mixed_layer_depth_salinity) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_mixed_layer_temperature_change', & + config_cvmix_WSwSBF_mixed_layer_temperature_change) + call mpas_pool_get_config(domain % configs, 'config_cvmix_WSwSBF_mixed_layer_salinity_change', & + config_cvmix_WSwSBF_mixed_layer_salinity_change) + + ! load data that required to initialize the ocean simulation + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + call mpas_pool_get_subpool(forcingPool, 'tracersInteriorRestoringFields', tracersInteriorRestoringFieldsPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'angleEdge', angleEdge) + + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal, 1) + call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional, 1) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', activeTracersPistonVelocity, 1) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersSurfaceRestoringValue', & + activeTracersSurfaceRestoringValue, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringRate', & + activeTracersInteriorRestoringRate, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringValue', & + activeTracersInteriorRestoringValue, 1) + call mpas_pool_get_array(forcingPool, 'latentHeatFlux', latentHeatFlux) + call mpas_pool_get_array(forcingPool, 'sensibleHeatFlux', sensibleHeatFlux) + call mpas_pool_get_array(forcingPool, 'shortWaveHeatFlux', shortWaveHeatFlux) + call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) + call mpas_pool_get_array(forcingPool, 'rainFlux', rainFlux) + + + ! Set refBottomDepth and refBottomDepthTopOfCell + do k = 1, nVertLevels + refBottomDepth(k) = config_cvmix_WSwSBF_bottom_depth * interfaceLocations(k+1) + refZMid(k) = - 0.5_RKIND * config_cvmix_WSwSBF_bottom_depth * (interfaceLocations(k) + interfaceLocations(k+1)) + end do + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + do iCell = 1, nCellsSolve + if(associated(activeTracers) ) then + + ! Loop from surface through surface layer depth + k=1 + + do while (k .le. nVertLevels .and. refZMid(k) > - config_cvmix_WSwSBF_mixed_layer_depth_temperature) + temperature = config_cvmix_WSwSBF_surface_temperature + refZMid(k) * & + config_cvmix_WSwSBF_temperature_gradient_mixed_layer + activeTracers(index_temperature, k, iCell) = temperature + k = k + 1 + enddo + + ! the value of k is now the first layer below the surface layer + if ( k > 1 ) then + temperature = activeTracers(index_temperature, k-1, iCell) + config_cvmix_WSwSBF_mixed_layer_temperature_change + activeTracers(index_temperature, k, iCell) = temperature + BLdepth = refZMid(k) + else + activeTracers(index_temperature, k, iCell) = config_cvmix_WSwSBF_surface_temperature + BLdepth = 0.0_RKIND + endif + + ! find the first level below the mixed layer + kML = k + 1 + + ! now loop from the bottom of the mixed layer thru to the bottom of the domain + do k = kML, nVertLevels + temperature = activeTracers(index_temperature, kML-1, iCell) + (refZMid(k) - BLdepth) * & + config_cvmix_WSwSBF_temperature_gradient + activeTracers(index_temperature, k, iCell) = temperature + enddo + + ! + ! next compute the salinity profile + ! + + ! Loop from surface through surface layer depth + k=1 + do while (k .le. nVertLevels .and. refZMid(k) > - config_cvmix_WSwSBF_mixed_layer_depth_salinity) + salinity = config_cvmix_WSwSBF_surface_salinity + refZMid(k) * config_cvmix_WSwSBF_salinity_gradient_mixed_layer + activeTracers(index_salinity, k, iCell) = salinity + k = k + 1 + enddo + + ! the value of k is now the first layer below the surface layer + if ( k > 1 ) then + salinity = activeTracers(index_salinity, k-1, iCell) + config_cvmix_WSwSBF_mixed_layer_salinity_change + activeTracers(index_salinity, k, iCell) = salinity + BLdepth = refZMid(k) + else + activeTracers(index_salinity, k, iCell) = config_cvmix_WSwSBF_surface_salinity + BLdepth = 0.0_RKIND + endif + + ! find the first level below the mixed layer + kML = k + 1 + + ! now loop from the bottom of the mixed layer thru to the bottom of the domain + do k = kML, nVertLevels + salinity = activeTracers(index_salinity, kML-1, iCell) + (refZMid(k) - BLdepth) * & + config_cvmix_WSwSBF_salinity_gradient + activeTracers(index_salinity, k, iCell) = salinity + enddo + + endif ! if (associated(activeTracer)) + + ! as a place holder, have some debug tracer in the top few layers and zero below + if ( associated(debugTracers) ) then + debugTracers(index_tracer1, k, iCell) = 0.0_RKIND + do k=1,min(4,nVertLevels) + debugTracers(index_tracer1, k, iCell) = 1.0_RKIND + enddo + endif + + ! Set layerThickness + do k = 1, nVertLevels + layerThickness(k, iCell) = config_cvmix_WSwSBF_bottom_depth * (interfaceLocations(k+1) - interfaceLocations(k)) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + + ! Set surface temperature restoring value and rate + ! Value in units of C, piston velocity in units of m/s + if ( associated(activeTracersSurfaceRestoringValue) ) then + activeTracersSurfaceRestoringValue(index_temperature, iCell) = config_cvmix_WSwSBF_surface_restoring_temperature + end if + if ( associated(activeTracersPistonVelocity) ) then + activeTracersPistonVelocity(index_temperature, iCell) = config_cvmix_WSwSBF_temperature_piston_velocity + end if + + ! Set surface salinity restoring value and rate + ! Value in units of PSU, piston velocity in units of m/s + if ( associated(activeTracersSurfaceRestoringValue) ) then + activeTracersSurfaceRestoringValue(index_salinity, iCell) = config_cvmix_WSwSBF_surface_restoring_salinity + end if + if ( associated(activeTracersPistonVelocity) ) then + activeTracersPistonVelocity(index_salinity, iCell) = config_cvmix_WSwSBF_salinity_piston_velocity + end if + + ! Set sensible heat flux + sensibleHeatFlux(iCell) = config_cvmix_WSwSBF_sensible_heat_flux + + ! Set latent heat flux + latentHeatFlux(iCell) = config_cvmix_WSwSBF_latent_heat_flux + + ! Set shortwave heat flux + shortWaveHeatFlux(iCell) = config_cvmix_WSwSBF_shortwave_heat_flux + + ! Set precipation and evaporation + rainFlux(iCell) = config_cvmix_WSwSBF_rain_flux + evaporationFlux(iCell) = config_cvmix_WSwSBF_evaporation_flux + + ! Set interior temperature restoring value and rate + do k = 1, nVertLevels + if ( associated(activeTracersInteriorRestoringValue) ) then + activeTracersInteriorRestoringValue(index_temperature, k, iCell) = activeTracers(index_temperature, k, iCell) + end if + if ( associated(activeTracersInteriorRestoringRate) ) then + activeTracersInteriorRestoringRate(index_temperature, k, iCell) = & + config_cvmix_WSwSBF_interior_temperature_restoring_rate + end if + enddo + + ! Set interior salinity restoring value and rate + do k = 1, nVertLevels + if ( associated(activeTracersInteriorRestoringValue) ) then + activeTracersInteriorRestoringValue(index_salinity, k, iCell) = activeTracers(index_salinity, k, iCell) + end if + if ( associated(activeTracersInteriorRestoringRate) ) then + activeTracersInteriorRestoringRate(index_salinity, k, iCell) = config_cvmix_WSwSBF_interior_salinity_restoring_rate + end if + enddo + + ! Set Coriolis parameter + fCell(iCell) = config_cvmix_WSwSBF_coriolis_parameter + + ! Set bottomDepth + bottomDepth(iCell) = config_cvmix_WSwSBF_bottom_depth + + ! Set maxLevelCell + maxLevelCell(iCell) = nVertLevels + + end do ! do iCell + + do iCell = 1, nCellsSolve + windStressZonal(iCell) = config_cvmix_WSwSBF_max_windstress + windStressMeridional(iCell) = 0.0_RKIND + enddo + + do iEdge = 1, nEdgesSolve + fEdge(iEdge) = config_cvmix_WSwSBF_coriolis_parameter + end do + + do iVertex=1, nVerticesSolve + fVertex(iVertex) = config_cvmix_WSwSBF_coriolis_parameter + end do + + block_ptr => block_ptr % next + end do + + deallocate(interfaceLocations) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_cvmix_WSwSBF!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_cvmix_WSwSBF +! +!> \brief Validation for CVMix WSwSBF mixing unit test case +!> \author Doug Jacobsen +!> \date 04/01/2015 +!> \details +!> This routine validates the configuration options for the CVMix WSwSBF mixing unit test configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_cvmix_WSwSBF(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool + type (mpas_pool_type), intent(inout) :: packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_cvmix_WSwSBF_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('cvmix_WSwSBF')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_cvmix_WSwSBF_vert_levels', config_cvmix_WSwSBF_vert_levels) + + if(config_vert_levels <= 0 .and. config_cvmix_WSwSBF_vert_levels > 0) then + config_vert_levels = config_cvmix_WSwSBF_vert_levels + else if(config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for CVMix WSwSBF unit test case. Not given a usable value for ' & + // 'vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_cvmix_WSwSBF!}}} + +!*********************************************************************** + +end module ocn_init_cvmix_WSwSBF + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_ecosys_column.F b/src/core_ocean/mode_init/mpas_ocn_init_ecosys_column.F new file mode 100644 index 0000000000..db3052d4f8 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_ecosys_column.F @@ -0,0 +1,561 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_ecosys_column +! +!> \brief MPAS ocean initialize case -- BGC (ecosys + DMS + MacroMolecules) +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This module contains the routines for initializing the +!> the ecosys column test configuration. This in a +!> single column configuration. +! +!----------------------------------------------------------------------- + +module ocn_init_ecosys_column + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_io_streams + + use ocn_init_cell_markers + use ocn_init_vertical_grids + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_ecosys_column, & + ocn_init_setup_ecosys_read_column, & + ocn_init_validate_ecosys_column + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + type (field2DReal) :: columnIC + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_ecosys_column +! +!> \brief Setup for ecosys column test configuration +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine sets up the initial conditions for the ecosys column test configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_ecosys_column(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, statePool + type (mpas_pool_type), pointer :: diagnosticsPool, forcingPool + type (mpas_pool_type), pointer :: ecosysAuxiliary ! additional forcing fields + + type (mpas_pool_type), pointer :: tracersPool + + integer, pointer :: nVertLevels, nVertLevelsP1, nCellsSolve, index_dummy + + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: refBottomDepth, refZMid, vertCoordMovementWeights + real (kind=RKIND), dimension(:), pointer :: bottomDepth + real (kind=RKIND), dimension(:, :), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:), pointer :: PH_PREV, PH_PREV_ALT_CO2 + real (kind=RKIND), dimension(:, :), pointer :: PH_PREV_3D, PH_PREV_ALT_CO2_3D + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracers, ecosysTracers, DMSTracers, & + MacroMoleculesTracers + + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + real (kind=RKIND), allocatable, dimension(:,:) :: ecoFieldColumn + + integer :: iCell, iEdge, iVertex, iField, k, numTracersTotal, nVertLevelsInputColumn + + integer, allocatable, dimension(:) :: indexField + + character (len=StrKIND) :: fieldName + + character (len=StrKIND), pointer :: config_init_configuration, & + config_ecosys_column_TS_filename, & + config_ecosys_column_ecosys_filename, & + config_ecosys_column_vertical_grid + + integer, pointer :: config_ecosys_column_vert_levels + + real (kind=RKIND), pointer :: config_ecosys_column_bottom_depth + + ! assume no error + iErr = 0 + + ! get and test if this is the configuration specified + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('ecosys_column')) return + + ! build the vertical grid + ! intent(out) is interfaceLocations. An array ranging from 0 to 1 + call mpas_pool_get_config(domain % configs, 'config_ecosys_column_vertical_grid', config_ecosys_column_vertical_grid) + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevelsP1', nVertLevelsP1) + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid(config_ecosys_column_vertical_grid, interfaceLocations) + + ! load the remaining configuration parameters + call mpas_pool_get_config(domain % configs, 'config_ecosys_column_bottom_depth', config_ecosys_column_bottom_depth) + call mpas_pool_get_config(domain % configs, 'config_ecosys_column_TS_filename', config_ecosys_column_TS_filename) + call mpas_pool_get_config(domain % configs, 'config_ecosys_column_ecosys_filename', config_ecosys_column_ecosys_filename) + call mpas_pool_get_config(domain % configs, 'config_ecosys_column_vert_levels', config_ecosys_column_vert_levels) + + nVertLevelsInputColumn = config_ecosys_column_vert_levels + + ! load data that required to initialize the ocean simulation + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'ecosysTracers', ecosysTracers, 1) + call mpas_pool_get_array(tracersPool, 'DMSTracers', DMSTracers, 1) + call mpas_pool_get_array(tracersPool, 'MacroMoleculesTracers', MacroMoleculesTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV', PH_PREV) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_ALT_CO2', PH_PREV_ALT_CO2) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_3D', PH_PREV_3D) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_ALT_CO2_3D', PH_PREV_ALT_CO2_3D) + + ! Set refBottomDepth and refBottomDepthTopOfCell + do k = 1, nVertLevels + refBottomDepth(k) = config_ecosys_column_bottom_depth * interfaceLocations(k+1) + refZMid(k) = - 0.5_RKIND * config_ecosys_column_bottom_depth * (interfaceLocations(k) + interfaceLocations(k+1)) + end do + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + if (nVertLevelsInputColumn /= nVertLevels) return + + numTracersTotal = 32 ! T,S + 30 eco + allocate(ecoFieldColumn(nVertLevelsInputColumn, numTracersTotal)) + allocate(indexField(numTracersTotal)) + + if ( associated(activeTracers) ) then + fieldName = 'temperature' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_TS_filename, & + nVertLevelsInputColumn, 1, ecoFieldColumn, iErr) + + fieldName = 'salinity' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_TS_filename, & + nVertLevelsInputColumn, 2, ecoFieldColumn, iErr) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_dummy) + indexField(1) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_dummy) + indexField(2) = index_dummy + do iCell = 1, nCellsSolve + do k = 1, nVertLevels + activeTracers(indexField(1), k, iCell) = ecoFieldColumn(k,1) + activeTracers(indexField(2), k, iCell) = ecoFieldColumn(k,2) + end do + end do + end if + + if ( associated(ecosysTracers) ) then + + fieldName = 'PO4' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 3, ecoFieldColumn, iErr) + fieldName = 'NO3' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 4, ecoFieldColumn, iErr) + fieldName = 'SiO3' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 5, ecoFieldColumn, iErr) + fieldName = 'NH4' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 6, ecoFieldColumn, iErr) + fieldName = 'Fe' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 7, ecoFieldColumn, iErr) + fieldName = 'O2' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 8, ecoFieldColumn, iErr) + fieldName = 'DIC' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 9, ecoFieldColumn, iErr) + fieldName = 'DIC_ALT_CO2' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 10, ecoFieldColumn, iErr) + fieldName = 'ALK' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 11, ecoFieldColumn, iErr) + fieldName = 'DOC' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 12, ecoFieldColumn, iErr) + fieldName = 'DON' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 13, ecoFieldColumn, iErr) + fieldName = 'DOFe' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 14, ecoFieldColumn, iErr) + fieldName = 'DOP' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 15, ecoFieldColumn, iErr) + fieldName = 'DOPr' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 16, ecoFieldColumn, iErr) + fieldName = 'DONr' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 17, ecoFieldColumn, iErr) + fieldName = 'zooC' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 18, ecoFieldColumn, iErr) + fieldName = 'spChl' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 19, ecoFieldColumn, iErr) + fieldName = 'spC' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 20, ecoFieldColumn, iErr) + fieldName = 'spFe' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 21, ecoFieldColumn, iErr) + fieldName = 'spCaCO3' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 22, ecoFieldColumn, iErr) + fieldName = 'diatChl' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 23, ecoFieldColumn, iErr) + fieldName = 'diatC' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 24, ecoFieldColumn, iErr) + fieldName = 'diatFe' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 25, ecoFieldColumn, iErr) + fieldName = 'diatSi' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 26, ecoFieldColumn, iErr) + fieldName = 'diazChl' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 27, ecoFieldColumn, iErr) + fieldName = 'diazC' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 28, ecoFieldColumn, iErr) + fieldName = 'diazFe' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 29, ecoFieldColumn, iErr) + fieldName = 'phaeoChl' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 30, ecoFieldColumn, iErr) + fieldName = 'phaeoC' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 31, ecoFieldColumn, iErr) + fieldName = 'phaeoFe' + call ocn_init_setup_ecosys_read_column(domain, fieldName, config_ecosys_column_ecosys_filename, & + nVertLevelsInputColumn, 32, ecoFieldColumn, iErr) + + call mpas_pool_get_dimension(tracersPool, 'index_PO4', index_dummy) + indexField(3) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_NO3', index_dummy) + indexField(4) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_SiO3', index_dummy) + indexField(5) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_NH4', index_dummy) + indexField(6) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_Fe', index_dummy) + indexField(7) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_O2', index_dummy) + indexField(8) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DIC', index_dummy) + indexField(9) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DIC_ALT_CO2', index_dummy) + indexField(10) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_ALK', index_dummy) + indexField(11) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOC', index_dummy) + indexField(12) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DON', index_dummy) + indexField(13) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOFe', index_dummy) + indexField(14) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOP', index_dummy) + indexField(15) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOPr', index_dummy) + indexField(16) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DONr', index_dummy) + indexField(17) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_zooC', index_dummy) + indexField(18) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spChl', index_dummy) + indexField(19) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spC', index_dummy) + indexField(20) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spFe', index_dummy) + indexField(21) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spCaCO3', index_dummy) + indexField(22) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatChl', index_dummy) + indexField(23) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatC', index_dummy) + indexField(24) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatFe', index_dummy) + indexField(25) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatSi', index_dummy) + indexField(26) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazChl', index_dummy) + indexField(27) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazC', index_dummy) + indexField(28) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazFe', index_dummy) + indexField(29) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoChl', index_dummy) + indexField(30) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoC', index_dummy) + indexField(31) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoFe', index_dummy) + indexField(32) = index_dummy + + do iField = 3, numTracersTotal + do iCell = 1, nCellsSolve + do k = 1, nVertLevels + ecosysTracers(indexField(iField), k, iCell) = ecoFieldColumn(k,iField) + end do + end do + end do + + do iCell = 1, nCellsSolve + PH_PREV(iCell) = 8.0_RKIND + PH_PREV_ALT_CO2(iCell) = 8.0_RKIND + do k = 1, nVertLevels + PH_PREV_3D(k, iCell) = 8.0_RKIND + PH_PREV_ALT_CO2_3D(k, iCell) = 8.0_RKIND + end do + end do + + end if ! associated(ecosysTracers) + + if ( associated(DMSTracers) ) then + call mpas_pool_get_dimension(tracersPool, 'index_DMS', index_dummy) + indexField(1) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DMSP', index_dummy) + indexField(2) = index_dummy + do iCell = 1, nCellsSolve + do k = 1, nVertLevels + DMSTracers(indexField(1), k, iCell) = 0.0_RKIND + DMSTracers(indexField(2), k, iCell) = 0.0_RKIND + end do + end do + end if ! associated(DMSTracers) + + if ( associated(MacroMoleculesTracers) ) then + call mpas_pool_get_dimension(tracersPool, 'index_PROT', index_dummy) + indexField(1) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_POLY', index_dummy) + indexField(2) = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_LIP', index_dummy) + indexField(3) = index_dummy + do iCell = 1, nCellsSolve + do k = 1, nVertLevels + MacroMoleculesTracers(indexField(1), k, iCell) = 0.0_RKIND + MacroMoleculesTracers(indexField(2), k, iCell) = 0.0_RKIND + MacroMoleculesTracers(indexField(3), k, iCell) = 0.0_RKIND + end do + end do + end if ! associated(MacroMoleculesTracers) + + do iCell = 1, nCellsSolve + ! Set layerThickness + do k = 1, nVertLevels + layerThickness(k, iCell) = config_ecosys_column_bottom_depth * (interfaceLocations(k+1) - interfaceLocations(k)) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + + ! Set bottomDepth + bottomDepth(iCell) = config_ecosys_column_bottom_depth + + ! Set maxLevelCell + maxLevelCell(iCell) = nVertLevels + end do + + block_ptr => block_ptr % next + end do + + deallocate(interfaceLocations) + deallocate(ecoFieldColumn, indexField) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_ecosys_column!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_ecosys_column +! +!> \brief Validation for ecosys column test case +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine validates the configuration options for the ecosys column test configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_ecosys_column(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool + type (mpas_pool_type), intent(inout) :: packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_ecosys_column_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('ecosys_column')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_ecosys_column_vert_levels', config_ecosys_column_vert_levels) + + if(config_vert_levels <= 0 .and. config_ecosys_column_vert_levels > 0) then + config_vert_levels = config_ecosys_column_vert_levels + else if(config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for ecosys column test case. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_ecosys_column!}}} + +!*********************************************************************** + +! +! routine ocn_init_setup_ecosys_read_column +! +!> \brief Read a column of a specified field from a given file +!> \author Mathew Maltrud +!> \date 11/01/2014 +!> \details +!> This routine reads a column of a specified field from a given file +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_ecosys_read_column(domain, fieldName, fileName, & + nVertLevelsInputColumn, iField, ecoFieldColumn, iErr)!{{{ + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + integer, intent(in) :: nVertLevelsInputColumn, iField + character (len=StrKIND), intent(in) :: fieldName, fileName + real (kind=RKIND), dimension(:,:), intent(inout) :: ecoFieldColumn + + type (block_type), pointer :: block_ptr + + type (MPAS_Stream_type) :: columnStream + + character (len=StrKIND), pointer :: config_global_ocean_temperature_file, config_global_ocean_temperature_varname, & + config_global_ocean_tracer_nlon_dimname, config_global_ocean_tracer_nlat_dimname, & + config_global_ocean_depth_dimname + + integer :: k + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_nlon_dimname', & + config_global_ocean_tracer_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_dimname', config_global_ocean_depth_dimname) + + ! Define stream for reading a column +! call MPAS_createStream(columnStream, domain % iocontext, fileName, MPAS_IO_NETCDF, MPAS_IO_READ, ierr=iErr) + call MPAS_createStream(columnStream, domain % iocontext, fileName, MPAS_IO_NETCDF, MPAS_IO_READ) + + ! Setup field for stream to be read in + columnIC % fieldName = trim(fieldName) + columnIC % dimSizes(1) = nVertLevelsInputColumn + columnIC % dimSizes(2) = 1 + columnIC % dimNames(1) = 'nVertLevels' + columnIC % dimNames(2) = 'nCells' + columnIC % isVarArray = .false. + columnIC % isPersistent = .true. + columnIC % isActive = .true. + columnIC % hasTimeDimension = .false. + columnIC % block => domain % blocklist + allocate(columnIC % array(nVertLevelsInputColumn, 1)) + + ! Add column field to stream + call MPAS_streamAddField(columnStream, columnIC, iErr) + + ! Read stream + call MPAS_readStream(columnStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(columnStream) + + do k = 1, nVertLevelsInputColumn + ecoFieldColumn(k,iField) = columnIC % array(k,1) + end do + + end subroutine ocn_init_setup_ecosys_read_column + +!*********************************************************************** + +end module ocn_init_ecosys_column + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_global_ocean.F b/src/core_ocean/mode_init/mpas_ocn_init_global_ocean.F new file mode 100644 index 0000000000..33ead6b7e0 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_global_ocean.F @@ -0,0 +1,3338 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_global_ocean +! +!> \brief MPAS ocean initialize case -- Global Ocean +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This module contains the routines for initializing the +!> the global ocean test case +! +!----------------------------------------------------------------------- + +module ocn_init_global_ocean + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_io + use mpas_io_streams + use mpas_stream_manager + use mpas_timekeeping + use mpas_dmpar + + use ocn_constants + use ocn_equation_of_state + use ocn_init_cell_markers + use ocn_init_vertical_grids + use ocn_init_interpolation + use ocn_init_ssh_and_landIcePressure + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_global_ocean, & + ocn_init_validate_global_ocean + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + ! 2D input variables. + ! Note Tracer and ecosys variables may have different original grids. + integer :: nLatTracer, nLonTracer, nDepthTracer + integer :: nLonEcosys, nLatEcosys, nDepthEcosys + integer :: nDepthOutput, nTimes + + ! 3D input variables + integer :: nLatWind, nLonWind + integer :: nLatTopo, nLonTopo + integer :: nLonSW, nLatSW + integer :: nLatLandIceThk, nLonLandIceThk + + type (field1DReal) :: depthOutput + type (field1DReal) :: tracerLat, tracerLon, tracerDepth + type (field1DReal) :: windLat, windLon + type (field1DReal) :: topoLat, topoLon + type (field1DReal) :: swDataLat, swDataLon + type (field1DReal) :: landIceThkLat, landIceThkLon + + type (field2DReal) :: topoIC, zonalWindIC, meridionalWindIC, chlorophyllIC, zenithAngleIC, clearSkyIC + type (field2DReal) :: landIceThkIC, landIceDraftIC + type (field2DReal) :: oceanFracIC, landIceFracIC, groundedFracIC + + type (field3DReal) :: tracerIC, ecosysForcingIC + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean +! +!> \brief Setup for global ocean test case +!> \author Mark Petersen, Doug Jacobsen, Xylar Asay-Davis +!> \date 12/29/2016 +!> \details +!> This routine sets up the initial conditions for the global ocean test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + type (mpas_pool_type), pointer :: meshPool, forcingPool, statePool, tracersPool, scratchPool + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration, config_sw_absorption_type, & + config_global_ocean_ecosys_file, config_global_ocean_ecosys_forcing_file + + character (len=StrKIND) :: interpTracerName + + type (block_type), pointer :: block_ptr + + logical, pointer :: config_global_ocean_cull_inland_seas + logical, pointer :: config_global_ocean_depress_by_land_ice + + logical, pointer :: on_a_sphere + + type (mpas_pool_type), pointer :: ecosysAuxiliary ! additional ecosys fields + + real (kind=RKIND), dimension(:), pointer :: PH_PREV, PH_PREV_ALT_CO2, pocToSed + real (kind=RKIND), dimension(:, :), pointer :: PH_PREV_3D, PH_PREV_ALT_CO2_3D + real (kind=RKIND), dimension(:, :), pointer :: FESEDFLUX + + integer, dimension(:), pointer :: maxLevelCell + + real (kind=RKIND), dimension(:, :, :), pointer :: ecosysTracers, activeTracers, debugTracers, & + DMSTracers, MacroMoleculesTracers + integer, pointer :: nVertLevels, nCellsSolve, tracerIndex + integer :: iCell, k, iTracer + integer, dimension(3) :: indexField + + type (field2DReal), pointer :: interpActiveTracerField, interpEcosysTracerField, & + interpActiveTracerSmoothField, interpEcosysTracerSmoothField + + type (field3DReal), pointer :: ecosysTracersField + + character (len=StrKIND) :: fieldName, poolName + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + call mpas_pool_get_config(domain % configs, 'config_sw_absorption_type', config_sw_absorption_type) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_file', config_global_ocean_ecosys_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_forcing_file', & + config_global_ocean_ecosys_forcing_file) + + if (trim(config_init_configuration) /= "global_ocean") return + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + ! the following are to test if each is associated before calling init routines + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_array(tracersPool, 'ecosysTracers', ecosysTracers, 1) + call mpas_pool_get_array(tracersPool, 'DMSTracers', DMSTracers, 1) + call mpas_pool_get_array(tracersPool, 'MacroMoleculesTracers', MacroMoleculesTracers, 1) + + if ( .not. on_a_sphere ) call mpas_log_write('The global ocean configuration can ' & + // 'only be applied to a spherical mesh. Exiting...', MPAS_LOG_CRIT) + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_cull_inland_seas', & + config_global_ocean_cull_inland_seas) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depress_by_land_ice', & + config_global_ocean_depress_by_land_ice) + + + !*********************************************************************** + ! + ! Topography + ! + !*********************************************************************** + + call mpas_log_write( 'Reading depth levels.') + call ocn_init_setup_global_ocean_read_depth_levels(domain, iErr) + + call mpas_log_write( 'Reading topography data.') + call ocn_init_setup_global_ocean_read_topo(domain, iErr) + call mpas_log_write( 'Interpolating topography data.') + call ocn_init_setup_global_ocean_create_model_topo(domain, iErr) + call mpas_log_write( 'Cleaning up topography IC fields') + call ocn_init_global_ocean_destroy_topo_fields() + + !*********************************************************************** + ! + ! Land ice depression + ! + !*********************************************************************** + + if (config_global_ocean_depress_by_land_ice) then + call mpas_log_write( 'Reading land ice topography data.') + call ocn_init_setup_global_ocean_read_land_ice_topography(domain, iErr) + call mpas_log_write( 'Interpolating land ice topography data.') + call ocn_init_setup_global_ocean_interpolate_land_ice_topography(domain, iErr) + end if + + call mpas_log_write( 'Initializing vertical coordinate with ssh = 0.') + ! compute the vertical grid (layerThickness, restingThickness, maxLevelCell, zMid) + ! based on bottomDepth and refBottomDepth and apply PBCS if requested + call ocn_init_ssh_and_landIcePressure_vertical_grid(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_vertical_grid failed.', MPAS_LOG_CRIT) + call mpas_dmpar_finalize(domain % dminfo) + end if + + !*********************************************************************** + ! + ! Shortwave data + ! + !*********************************************************************** + + if(trim(config_sw_absorption_type) == 'ohlmann00') then + + call mpas_log_write( 'Reading penetrating shortwave lat/lon data') + call ocn_init_setup_global_ocean_read_swData_lat_lon(domain,iErr) + call mpas_log_write( 'Interpolating penetrating shortwave data') + call ocn_init_setup_global_ocean_interpolate_swData(domain,iErr) + call mpas_log_write( 'Cleaning penetrating shortwave data') + call ocn_init_global_ocean_destroy_swData_fields() + + endif + + !*********************************************************************** + ! + ! Active tracers (temperature and salinity) + ! + !*********************************************************************** + + call mpas_log_write( 'Reading tracer Lat/Lon coordinates') + call ocn_init_setup_global_ocean_read_tracer_lat_lon(domain, iErr) + + allocate(tracerIC % attLists(1)) + allocate(tracerIC % array(nLonTracer, nLatTracer, nDepthTracer)) + + call mpas_pool_get_field(scratchPool, 'interpActiveTracer', interpActiveTracerField) + call mpas_allocate_scratch_field(interpActiveTracerField, .false.) + call mpas_pool_get_field(scratchPool, 'interpActiveTracerSmooth', interpActiveTracerSmoothField) + call mpas_allocate_scratch_field(interpActiveTracerSmoothField, .false.) + + interpTracerName = 'interpActiveTracer' + + fieldName = 'temperature' + call mpas_pool_get_dimension(tracersPool, 'index_temperature', tracerIndex) + call ocn_init_setup_global_ocean_read_temperature(domain, iErr) + call ocn_init_setup_global_ocean_interpolate_tracers(domain, activeTracers, tracerIndex, interpTracerName, iErr) + + fieldName = 'salinity' + call mpas_pool_get_dimension(tracersPool, 'index_salinity', tracerIndex) + call ocn_init_setup_global_ocean_read_salinity(domain, iErr) + call ocn_init_setup_global_ocean_interpolate_tracers(domain, activeTracers, tracerIndex, interpTracerName, iErr) + + deallocate(tracerIC % array) + deallocate(tracerIC % attLists) + call mpas_deallocate_scratch_field(interpActiveTracerField, .false.) + call mpas_deallocate_scratch_field(interpActiveTracerSmoothField, .false.) + call ocn_init_global_ocean_destroy_tracer_fields() + + !*********************************************************************** + ! + ! Debug tracers + ! + !*********************************************************************** + + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', tracerIndex) + if(associated(debugTracers)) then + debugTracers(tracerIndex,:,:) = 1.0_RKIND + end if + + !*********************************************************************** + ! + ! Ecosystem tracers + ! + !*********************************************************************** + + if ( associated(ecosysTracers) ) then + + call mpas_log_write( 'Reading ecosys lat/lon data') + call ocn_init_setup_global_ocean_read_ecosys_lat_lon(domain,iErr) + + call mpas_log_write( 'Reading ecosys IC.') + allocate(tracerIC % attLists(1)) + allocate(tracerIC % array(nLonEcosys, nLatEcosys, nDepthEcosys)) + + call mpas_pool_get_field(scratchPool, 'interpEcosysTracer', interpEcosysTracerField) + call mpas_allocate_scratch_field(interpEcosysTracerField, .false.) + call mpas_pool_get_field(scratchPool, 'interpEcosysTracerSmooth', interpEcosysTracerSmoothField) + call mpas_allocate_scratch_field(interpEcosysTracerSmoothField, .false.) + + interpTracerName = 'interpEcosysTracer' + + call mpas_pool_get_field(tracersPool, 'ecosysTracers', ecosysTracersField, 1) + do iTracer = 1, size( ecosysTracersField % constituentNames) + call ocn_init_setup_global_ocean_read_ecosys(domain, ecosysTracersField % constituentNames(iTracer), & + config_global_ocean_ecosys_file, iErr) + call ocn_init_setup_global_ocean_interpolate_tracers(domain, ecosysTracers, iTracer, interpTracerName, iErr) + end do + + deallocate(tracerIC % array) + call mpas_deallocate_scratch_field(interpEcosysTracerField, .false.) + call mpas_deallocate_scratch_field(interpEcosysTracerSmoothField, .false.) + + !*********************************************************************** + ! + ! Ecosystem forcing tracers + ! + !*********************************************************************** + + call mpas_log_write( 'Reading ecosys forcing.') + allocate(ecosysForcingIC % attLists(1)) + allocate(ecosysForcingIC % array(nLonEcosys, nLatEcosys, 1)) + + fieldName = 'dust_FLUX_IN'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'IRON_FLUX_IN'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'depositionFluxNO3'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'depositionFluxNH4'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'pocToSed'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxNO3'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxPO4'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxSiO3'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxFe'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxDOC'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxDON'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxDOP'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxDIC'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'riverFluxALK'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'atmosphericCO2'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'atmosphericCO2_ALT_CO2'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'windSpeedSquared10m'; poolName = 'ecosysAuxiliary' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + + fieldName = 'iceFraction'; poolName = 'forcing' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'atmosphericPressure'; poolName = 'forcing' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + fieldName = 'shortWaveHeatFlux'; poolName = 'forcing' + call ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, & + config_global_ocean_ecosys_forcing_file, iErr) + call ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr) + + call mpas_log_write( 'Cleaning up ecosys IC fields') + deallocate(ecosysForcingIC % array) + call ocn_init_global_ocean_destroy_ecosys_fields() + + !*********************************************************************** + ! + ! Ecosystem pH arrays + ! + !*********************************************************************** + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV', PH_PREV) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_ALT_CO2', PH_PREV_ALT_CO2) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_3D', PH_PREV_3D) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_ALT_CO2_3D', PH_PREV_ALT_CO2_3D) + call mpas_pool_get_array(ecosysAuxiliary, 'FESEDFLUX', FESEDFLUX) + call mpas_pool_get_array(ecosysAuxiliary, 'pocToSed', pocToSed) + + do iCell = 1, nCellsSolve + PH_PREV(iCell) = 8.0_RKIND + PH_PREV_ALT_CO2(iCell) = 8.0_RKIND + do k = 1, nVertLevels + PH_PREV_3D(k, iCell) = 8.0_RKIND + PH_PREV_ALT_CO2_3D(k, iCell) = 8.0_RKIND + + if(maxLevelCell(iCell) == k) then + FESEDFLUX(k, iCell) = pocToSed(iCell)*6.8e-4_RKIND + else + FESEDFLUX(k, iCell) = 0.0_RKIND + end if + end do + end do + + block_ptr => block_ptr % next + end do + + end if + + if ( associated(DMSTracers) ) then + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(tracersPool, 'index_DMS', tracerIndex) + indexField(1) = tracerIndex + call mpas_pool_get_dimension(tracersPool, 'index_DMSP', tracerIndex) + indexField(2) = tracerIndex + do iCell = 1, nCellsSolve + do k = 1, nVertLevels + DMSTracers(indexField(1), k, iCell) = 0.0_RKIND + DMSTracers(indexField(2), k, iCell) = 0.0_RKIND + end do + end do + block_ptr => block_ptr % next + end do + end if + + if ( associated(MacroMoleculesTracers) ) then + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(tracersPool, 'index_PROT', tracerIndex) + indexField(1) = tracerIndex + call mpas_pool_get_dimension(tracersPool, 'index_POLY', tracerIndex) + indexField(2) = tracerIndex + call mpas_pool_get_dimension(tracersPool, 'index_LIP', tracerIndex) + indexField(3) = tracerIndex + do iCell = 1, nCellsSolve + do k = 1, nVertLevels + MacroMoleculesTracers(indexField(1), k, iCell) = 0.0_RKIND + MacroMoleculesTracers(indexField(2), k, iCell) = 0.0_RKIND + MacroMoleculesTracers(indexField(3), k, iCell) = 0.0_RKIND + end do + end do + block_ptr => block_ptr % next + end do + end if + + call mpas_log_write( 'Reading windstress IC.') + call ocn_init_setup_global_ocean_read_windstress(domain, iErr) + call mpas_log_write( 'Interpolating windstress.') + call ocn_init_setup_global_ocean_interpolate_windstress(domain, iErr) + call mpas_log_write( 'Destroying windstress fields') + call ocn_init_global_ocean_destroy_windstress_fields() + + if (config_global_ocean_depress_by_land_ice) then + call mpas_log_write('Modifying temperature and surface restoring under land ice.') + call ocn_init_setup_global_ocean_modify_temp_under_land_ice(domain, iErr) + + call mpas_log_write( 'Recalculating ocean layer topography due to land ice depression') + ! compute or update the land-ice pressure (or possibly SSH), also computing density along the way + ! If this is the initial guess, the vertical grid and activeTracers may also be recomputed based on SSH + call ocn_init_ssh_and_landIcePressure_balance(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_balance failed.', MPAS_LOG_CRIT) + call mpas_dmpar_finalize(domain % dminfo) + end if + + call mpas_log_write( 'Cleaning up land ice topography IC fields') + call ocn_init_global_ocean_destroy_land_ice_topography_fields() + end if + + call mpas_log_write( 'Copying restoring fields') + ! this occurs after ocn_init_ssh_and_landIcePressure_balance because activeTracers may have been remapped + ! to a new vertical coordinate + call ocn_init_setup_global_ocean_interpolate_restoring(domain, iErr) + + call mpas_log_write( 'Compute Haney number') + call ocn_compute_Haney_number(domain, iErr) + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_compute_Haney_number failed.', MPAS_LOG_CRIT) + call mpas_dmpar_finalize(domain % dminfo) + end if + + if (config_global_ocean_cull_inland_seas) then + call mpas_log_write( 'Removing inland seas.') + call ocn_init_setup_global_ocean_cull_inland_seas(domain, iErr) + end if + + block_ptr => domain % blocklist + do while (associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call ocn_mark_maxlevelcell(meshPool, iErr) + block_ptr => block_ptr % next + end do + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_global_ocean!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_topo +! +!> \brief Read the topography IC file +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine reads the topography IC file, including latitude and longitude +!> information for topography data. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_topo(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: topographyStream + + character (len=StrKIND), pointer :: config_global_ocean_topography_file, & + config_global_ocean_topography_lat_varname, & + config_global_ocean_topography_nlat_dimname, & + config_global_ocean_topography_lon_varname, & + config_global_ocean_topography_nlon_dimname, & + config_global_ocean_topography_varname, & + config_global_ocean_topography_ocean_frac_varname + + logical, pointer :: config_global_ocean_topography_latlon_degrees, config_global_ocean_topography_has_ocean_frac + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_file', & + config_global_ocean_topography_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_lat_varname', & + config_global_ocean_topography_lat_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_nlat_dimname', & + config_global_ocean_topography_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_lon_varname', & + config_global_ocean_topography_lon_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_nlon_dimname', & + config_global_ocean_topography_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_varname', & + config_global_ocean_topography_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_latlon_degrees', & + config_global_ocean_topography_latlon_degrees) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_ocean_frac_varname', & + config_global_ocean_topography_ocean_frac_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_has_ocean_frac', & + config_global_ocean_topography_has_ocean_frac) + + ! Define stream for depth levels + call MPAS_createStream(topographyStream, domain % iocontext, config_global_ocean_topography_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup topoLat, topoLon, and topoIC fields for stream to be read in + topoLat % fieldName = trim(config_global_ocean_topography_lat_varname) + topoLat % dimSizes(1) = nLatTopo + topoLat % dimNames(1) = trim(config_global_ocean_topography_nlat_dimname) + topoLat % isVarArray = .false. + topoLat % isPersistent = .true. + topoLat % isActive = .true. + topoLat % hasTimeDimension = .false. + topoLat % block => domain % blocklist + allocate(topoLat % attLists(1)) + allocate(topoLat % array(nLatTopo)) + + topoLon % fieldName = trim(config_global_ocean_topography_lon_varname) + topoLon % dimSizes(1) = nLonTopo + topoLon % dimNames(1) = trim(config_global_ocean_topography_nlon_dimname) + topoLon % isVarArray = .false. + topoLon % isPersistent = .true. + topoLon % isActive = .true. + topoLon % hasTimeDimension = .false. + topoLon % block => domain % blocklist + allocate(topoLon % attLists(1)) + allocate(topoLon % array(nLonTopo)) + + topoIC % fieldName = trim(config_global_ocean_topography_varname) + topoIC % dimSizes(1) = nLonTopo + topoIC % dimSizes(2) = nLatTopo + topoIC % dimNames(1) = trim(config_global_ocean_topography_nlon_dimname) + topoIC % dimNames(2) = trim(config_global_ocean_topography_nlat_dimname) + topoIC % isVarArray = .false. + topoIC % isPersistent = .true. + topoIC % isActive = .true. + topoIC % hasTimeDimension = .false. + topoIC % block => domain % blocklist + allocate(topoIC % attLists(1)) + allocate(topoIC % array(nLonTopo, nLatTopo)) + + ! Add topoLat, topoLon, and topoIC fields to stream + call MPAS_streamAddField(topographyStream, topoLat, iErr) + call MPAS_streamAddField(topographyStream, topoLon, iErr) + call MPAS_streamAddField(topographyStream, topoIC, iErr) + + if(config_global_ocean_topography_has_ocean_frac) then + oceanFracIC % fieldName = trim(config_global_ocean_topography_ocean_frac_varname) + oceanFracIC % dimSizes(1) = nLonTopo + oceanFracIC % dimSizes(2) = nLatTopo + oceanFracIC % dimNames(1) = trim(config_global_ocean_topography_nlon_dimname) + oceanFracIC % dimNames(2) = trim(config_global_ocean_topography_nlat_dimname) + oceanFracIC % isVarArray = .false. + oceanFracIC % isPersistent = .true. + oceanFracIC % isActive = .true. + oceanFracIC % hasTimeDimension = .false. + oceanFracIC % block => domain % blocklist + allocate(oceanFracIC % attLists(1)) + allocate(oceanFracIC % array(nLonTopo, nLatTopo)) + + call MPAS_streamAddField(topographyStream, oceanFracIC, iErr) + end if + + ! Read stream + call MPAS_readStream(topographyStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(topographyStream) + + if (config_global_ocean_topography_latlon_degrees) then + topoLat % array(:) = topoLat % array(:) * pii / 180.0_RKIND + topoLon % array(:) = topoLon % array(:) * pii / 180.0_RKIND + end if + + end subroutine ocn_init_setup_global_ocean_read_topo!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_land_ice_topography +! +!> \brief Read the ice sheet thickness IC file +!> \author Jeremy Fyke, Xylar Asay-Davis, Mark Petersen (modified from Doug Jacobsen code) +!> \date 06/15/2015 +!> \details +!> This routine reads the ice sheet topography IC file, including latitude and longitude +!> information for ice sheet topography data. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_land_ice_topography(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: landIceThicknessStream + + character (len=StrKIND), pointer :: config_global_ocean_land_ice_topo_file, config_global_ocean_land_ice_topo_lat_varname, & + config_global_ocean_land_ice_topo_draft_varname, & + config_global_ocean_land_ice_topo_ice_frac_varname, & + config_global_ocean_land_ice_topo_grounded_frac_varname, & + config_global_ocean_land_ice_topo_nlat_dimname, config_global_ocean_land_ice_topo_lon_varname, & + config_global_ocean_land_ice_topo_nlon_dimname, config_global_ocean_land_ice_topo_thickness_varname + + logical, pointer :: config_global_ocean_land_ice_topo_latlon_degrees + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_file', & + config_global_ocean_land_ice_topo_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_lat_varname', & + config_global_ocean_land_ice_topo_lat_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_nlat_dimname', & + config_global_ocean_land_ice_topo_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_lon_varname', & + config_global_ocean_land_ice_topo_lon_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_nlon_dimname', & + config_global_ocean_land_ice_topo_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_thickness_varname', & + config_global_ocean_land_ice_topo_thickness_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_draft_varname', & + config_global_ocean_land_ice_topo_draft_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_ice_frac_varname', & + config_global_ocean_land_ice_topo_ice_frac_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_grounded_frac_varname', & + config_global_ocean_land_ice_topo_grounded_frac_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_land_ice_topo_latlon_degrees', & + config_global_ocean_land_ice_topo_latlon_degrees) + + ! Define stream for depth levels + call MPAS_createStream(landIceThicknessStream, domain % iocontext, config_global_ocean_land_ice_topo_file, & + MPAS_IO_NETCDF, MPAS_IO_READ, ierr=iErr) + + ! Setup landIceThkLat, landIceThkLon, and landIceThkIC fields for stream to be read in + landIceThkLat % fieldName = trim(config_global_ocean_land_ice_topo_lat_varname) + landIceThkLat % dimSizes(1) = nLatLandIceThk + landIceThkLat % dimNames(1) = trim(config_global_ocean_land_ice_topo_nlat_dimname) + landIceThkLat % isVarArray = .false. + landIceThkLat % isPersistent = .true. + landIceThkLat % isActive = .true. + landIceThkLat % hasTimeDimension = .false. + landIceThkLat % block => domain % blocklist + allocate(landIceThkLat % attLists(1)) + allocate(landIceThkLat % array(nLatLandIceThk)) + + landIceThkLon % fieldName = trim(config_global_ocean_land_ice_topo_lon_varname) + landIceThkLon % dimSizes(1) = nLonLandIceThk + landIceThkLon % dimNames(1) = trim(config_global_ocean_land_ice_topo_nlon_dimname) + landIceThkLon % isVarArray = .false. + landIceThkLon % isPersistent = .true. + landIceThkLon % isActive = .true. + landIceThkLon % hasTimeDimension = .false. + landIceThkLon % block => domain % blocklist + allocate(landIceThkLon % attLists(1)) + allocate(landIceThkLon % array(nLonLandIceThk)) + + landIceThkIC % fieldName = trim(config_global_ocean_land_ice_topo_thickness_varname) + landIceThkIC % dimSizes(1) = nLonLandIceThk + landIceThkIC % dimSizes(2) = nLatLandIceThk + landIceThkIC % dimNames(1) = trim(config_global_ocean_land_ice_topo_nlon_dimname) + landIceThkIC % dimNames(2) = trim(config_global_ocean_land_ice_topo_nlat_dimname) + landIceThkIC % isVarArray = .false. + landIceThkIC % isPersistent = .true. + landIceThkIC % isActive = .true. + landIceThkIC % hasTimeDimension = .false. + landIceThkIC % block => domain % blocklist + allocate(landIceThkIC % attLists(1)) + allocate(landIceThkIC % array(nLonLandIceThk, nLatLandIceThk)) + + landIceDraftIC % fieldName = trim(config_global_ocean_land_ice_topo_draft_varname) + landIceDraftIC % dimSizes(1) = nLonLandIceThk + landIceDraftIC % dimSizes(2) = nLatLandIceThk + landIceDraftIC % dimNames(1) = trim(config_global_ocean_land_ice_topo_nlon_dimname) + landIceDraftIC % dimNames(2) = trim(config_global_ocean_land_ice_topo_nlat_dimname) + landIceDraftIC % isVarArray = .false. + landIceDraftIC % isPersistent = .true. + landIceDraftIC % isActive = .true. + landIceDraftIC % hasTimeDimension = .false. + landIceDraftIC % block => domain % blocklist + allocate(landIceDraftIC % attLists(1)) + allocate(landIceDraftIC % array(nLonLandIceThk, nLatLandIceThk)) + + landIceFracIC % fieldName = trim(config_global_ocean_land_ice_topo_ice_frac_varname) + landIceFracIC % dimSizes(1) = nLonLandIceThk + landIceFracIC % dimSizes(2) = nLatLandIceThk + landIceFracIC % dimNames(1) = trim(config_global_ocean_land_ice_topo_nlon_dimname) + landIceFracIC % dimNames(2) = trim(config_global_ocean_land_ice_topo_nlat_dimname) + landIceFracIC % isVarArray = .false. + landIceFracIC % isPersistent = .true. + landIceFracIC % isActive = .true. + landIceFracIC % hasTimeDimension = .false. + landIceFracIC % block => domain % blocklist + allocate(landIceFracIC % attLists(1)) + allocate(landIceFracIC % array(nLonLandIceThk, nLatLandIceThk)) + + groundedFracIC % fieldName = trim(config_global_ocean_land_ice_topo_grounded_frac_varname) + groundedFracIC % dimSizes(1) = nLonLandIceThk + groundedFracIC % dimSizes(2) = nLatLandIceThk + groundedFracIC % dimNames(1) = trim(config_global_ocean_land_ice_topo_nlon_dimname) + groundedFracIC % dimNames(2) = trim(config_global_ocean_land_ice_topo_nlat_dimname) + groundedFracIC % isVarArray = .false. + groundedFracIC % isPersistent = .true. + groundedFracIC % isActive = .true. + groundedFracIC % hasTimeDimension = .false. + groundedFracIC % block => domain % blocklist + allocate(groundedFracIC % attLists(1)) + allocate(groundedFracIC % array(nLonLandIceThk, nLatLandIceThk)) + + ! Add landIceThkLat, landIceThkLon, and landIceThkIC fields to stream + call MPAS_streamAddField(landIceThicknessStream, landIceThkLat, iErr) + call MPAS_streamAddField(landIceThicknessStream, landIceThkLon, iErr) + call MPAS_streamAddField(landIceThicknessStream, landIceThkIC, iErr) + call MPAS_streamAddField(landIceThicknessStream, landIceDraftIC, iErr) + call MPAS_streamAddField(landIceThicknessStream, landIceFracIC, iErr) + call MPAS_streamAddField(landIceThicknessStream, groundedFracIC, iErr) + + ! Read stream + call MPAS_readStream(landIceThicknessStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(landIceThicknessStream) + + if (config_global_ocean_land_ice_topo_latlon_degrees) then + landIceThkLat % array(:) = landIceThkLat % array(:) * pii / 180.0_RKIND + landIceThkLon % array(:) = landIceThkLon % array(:) * pii / 180.0_RKIND + end if + + end subroutine ocn_init_setup_global_ocean_read_land_ice_topography!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_create_model_topo +! +!> \brief Interpolate the topography IC to MPAS mesh +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine interpolates topography data to the MPAS mesh. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_create_model_topo(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, scratchPool, verticalMeshPool, criticalPassagesPool + + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell, bottomDepth, bottomDepthObserved, & + refBottomDepth, refLayerThickness, refZMid, oceanFracObserved + + integer, pointer :: nCells, nCellsSolve, nVertLevels + + type (field1DInteger), pointer :: maxLevelCellField, smoothedLevelsField + integer, dimension(:), pointer :: maxLevelCell, nEdgesOnCell + integer, dimension(:, :), pointer :: cellsOnCell + + integer :: iCell, coc, j, k, maxLevel + integer :: minimum_levels + + character (len=StrKIND), pointer :: config_global_ocean_topography_method + logical, pointer :: config_global_ocean_smooth_topography, config_global_ocean_topography_has_ocean_frac, & + config_global_ocean_deepen_critical_passages + real (kind=RKIND), pointer :: config_global_ocean_minimum_depth + + logical :: isOcean + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_method', config_global_ocean_topography_method) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_minimum_depth', config_global_ocean_minimum_depth) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_smooth_topography', config_global_ocean_smooth_topography) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_deepen_critical_passages', & + config_global_ocean_deepen_critical_passages) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_has_ocean_frac', & + config_global_ocean_topography_has_ocean_frac) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'bottomDepthObserved', bottomDepthObserved) + call mpas_pool_get_array(meshPool, 'oceanFracObserved', oceanFracObserved) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + + do k = 1, nVertLevels + if (refBottomDepth(k).gt.config_global_ocean_minimum_depth) then + minimum_levels = k + print '(a,f8.2,2a,i5,a,f8.2,a)', 'config_global_ocean_minimum_depth=', & + config_global_ocean_minimum_depth,' m. ', 'Setting minimum layer index to ', & + minimum_levels, ' with a bottom depth of ', refBottomDepth(k), ' m.' + exit + end if + end do + + ! Record depth of the bottom of the ocean, before any alterations for modeling purposes. + if (config_global_ocean_topography_method .eq. "nearest_neighbor") then + + call ocn_init_interpolation_nearest_horiz(topoLon % array, topoLat % array, & + topoIC % array, nLonTopo, nLatTopo, & + lonCell, latCell, bottomDepthObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + if (config_global_ocean_topography_has_ocean_frac) then + call ocn_init_interpolation_nearest_horiz(topoLon % array, topoLat % array, & + oceanFracIC % array, nLonTopo, nLatTopo, & + lonCell, latCell, oceanFracObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + end if + + elseif (config_global_ocean_topography_method .eq. "bilinear_interpolation") then + call ocn_init_interpolation_bilinear_horiz(topoLon % array, topoLat % array, & + topoIC % array, nLonTopo, nLatTopo, & + lonCell, latCell, bottomDepthObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + if (config_global_ocean_topography_has_ocean_frac) then + call ocn_init_interpolation_bilinear_horiz(topoLon % array, topoLat % array, & + oceanFracIC % array, nLonTopo, nLatTopo, & + lonCell, latCell, oceanFracObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + end if + + else + call mpas_log_write( 'Invalid choice of config_global_ocean_topography_method.', MPAS_LOG_CRIT) + iErr = 1 + call mpas_dmpar_finalize(domain % dminfo) + endif + + do iCell = 1, nCells + ! Record depth of the bottom of the ocean, before any alterations for modeling purposes. + ! Flip the sign to positive down. + bottomDepthObserved(iCell) = -bottomDepthObserved(iCell) + !isOcean = bottomDepthObserved(iCell) > 0.0_RKIND + isOcean = .true. + if (config_global_ocean_topography_has_ocean_frac) then + ! if there is an ocean-fraction field, mark cells that are < 50% ocean as land + isOcean = isOcean .and. (oceanFracObserved(iCell) >= 0.5_RKIND) + end if + if (isOcean) then + ! Enforce minimum depth + bottomDepth(iCell) = max(bottomDepthObserved(iCell), refBottomDepth(minimum_levels)) + + maxLevelCell(iCell) = -1 + do k = 1, nVertLevels + if (refBottomDepth(k) >= bottomDepth(iCell)) then + maxLevelCell(iCell) = k + exit + end if + end do + + if (maxLevelCell(iCell) == -1) then + maxLevelCell(iCell) = nVertLevels + bottomDepth(iCell) = refBottomDepth( nVertLevels ) + end if + + else + bottomDepth(iCell) = 0.0_RKIND + maxLevelCell(iCell) = -1 + end if + end do + + if (config_global_ocean_deepen_critical_passages) then + call mpas_pool_get_subpool(block_ptr % structs, 'criticalPassages', criticalPassagesPool) + call ocn_init_setup_global_ocean_deepen_critical_passages(meshPool, criticalPassagesPool, iErr) + end if + + ! Smooth depth levels. Enforce different in maxLevelCell to only be a maximum + ! of 1 vertical level between two neighboring cells. + if (config_global_ocean_smooth_topography) then + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_field(scratchPool, 'smoothedLevels', smoothedLevelsField) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + + call mpas_allocate_scratch_field(smoothedLevelsField, .true.) + + maxLevelCell(nCells+1) = -1 + smoothedLevelsField % array = maxLevelCell + + do iCell = 1, nCellsSolve + maxLevel = 0 + do j = 1, nEdgesOnCell(iCell) + coc = cellsOnCell(j, iCell) + maxLevel = max(maxLevel, maxLevelCell(coc)) + end do + + if (maxLevel < maxLevelCell(iCell) ) then + smoothedLevelsField % array(iCell) = maxLevel + 1 + bottomDepth(iCell) = refBottomDepth(maxLevel + 1) + end if + end do + + maxLevelCell(:) = smoothedLevelsField % array(:) + + call mpas_deallocate_scratch_field(smoothedLevelsField, .true.) + end if + + block_ptr => block_ptr % next + end do + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_field(meshPool, 'maxLevelCell', maxLevelCellField) + call mpas_dmpar_exch_halo_field(maxLevelCellField) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + + ! Compute refLayerThickness and refZMid + call ocn_compute_layerThickness_zMid_from_bottomDepth(refLayerThickness,refZMid, & + refBottomDepth,refBottomDepth(nVertLevels), & + nVertLevels,nVertLevels,iErr) + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_global_ocean_create_model_topo!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_deepen_critical_passages +! +!> \brief Deepen critical passages in model grid so that relevant seas are included. +!> \author Xylar Asay-Davis +!> \date 5 April 2016 +!> \details +!> Deepen cirtical passages in model grid so that relevant seas are included. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_deepen_critical_passages(meshPool, criticalPassagesPool, iErr)!{{{ + type (mpas_pool_type), intent(inout) :: meshPool, criticalPassagesPool + integer, intent(out) :: iErr + + integer, pointer :: nCells, nTransects, nVertLevels + + real(kind=RKIND), dimension(:), pointer :: bottomDepth, criticalPassageDepths, & + refBottomDepth + integer, dimension(:,:), pointer :: criticalPassageMasks + integer, dimension(:), pointer :: maxLevelCell + + integer, dimension(:), pointer :: criticalPassageLevel + + integer :: iCell, iTransect, k + + iErr = 0 + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + + call mpas_pool_get_dimension(criticalPassagesPool, 'nTransects', nTransects) + + call mpas_pool_get_array(criticalPassagesPool, 'transectCellMasks', criticalPassageMasks) + call mpas_pool_get_array(criticalPassagesPool, 'depthTransects', criticalPassageDepths) + call mpas_pool_get_array(criticalPassagesPool, 'criticalPassageLevel', criticalPassageLevel) + + do iTransect = 1,nTransects + do k=1,nVertLevels + criticalPassageLevel(iTransect) = nVertLevels + if(refBottomDepth(k) > criticalPassageDepths(iTransect)) then + criticalPassageLevel(iTransect) = k + exit + end if + end do + end do + + do iCell = 1,nCells + do iTransect = 1,nTransects + if(criticalPassageMasks(iTransect, iCell) == 0) cycle + k = criticalPassageLevel(iTransect) + if(bottomDepth(iCell) < refBottomDepth(k)) then + bottomDepth(iCell) = refBottomDepth(k) + maxLevelCell(iCell) = k + end if + end do + end do + + end subroutine ocn_init_setup_global_ocean_deepen_critical_passages!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_interpolate_land_ice_topography +! +!> \brief Interpolate the topography IC to MPAS mesh +!> \author Jeremy Fyke, Xylar Asay-Davis, Mark Petersen +!> \date 06/25/2014 +!> \details +!> This routine interpolates ice sheet thickness data to the MPAS mesh. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_interpolate_land_ice_topography(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, forcingPool, landIceInitPool, diagnosticsPool, & + statePool + + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell + real (kind=RKIND), dimension(:), pointer :: landIceThkObserved, landIceDraftObserved, & + landIceFracObserved, landIceGroundedFracObserved + + real (kind=RKIND), dimension(:), pointer :: landIcePressure, landIceFraction, ssh, & + bottomDepth + + integer, pointer :: nCells + integer, dimension(:), pointer :: maxLevelCell, landIceMask, modifySSHMask + real (kind=RKIND), pointer :: config_land_ice_flux_rho_ice + character (len=StrKIND), pointer :: config_global_ocean_topography_method, config_iterative_init_variable + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_topography_method', & + config_global_ocean_topography_method) + call mpas_pool_get_config(domain % configs, 'config_land_ice_flux_rho_ice', config_land_ice_flux_rho_ice) + call mpas_pool_get_config(domain % configs, 'config_iterative_init_variable', config_iterative_init_variable) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'landIceInit', landIceInitPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(landIceInitPool, 'landIceDraftObserved', landIceDraftObserved) + call mpas_pool_get_array(landIceInitPool, 'landIceThkObserved', landIceThkObserved) + call mpas_pool_get_array(landIceInitPool, 'landIceFracObserved', landIceFracObserved) + call mpas_pool_get_array(landIceInitPool, 'landIceGroundedFracObserved', landIceGroundedFracObserved) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(diagnosticsPool, 'modifySSHMask', modifySSHMask) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + + if (config_global_ocean_topography_method .eq. "nearest_neighbor") then + + call ocn_init_interpolation_nearest_horiz(landIceThkLon % array, landIceThkLat % array, & + landIceThkIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceThkObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_nearest_horiz(landIceThkLon % array, landIceThkLat % array, & + landIceDraftIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceDraftObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_nearest_horiz(landIceThkLon % array, landIceThkLat % array, & + landIceFracIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceFracObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_nearest_horiz(landIceThkLon % array, landIceThkLat % array, & + groundedFracIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceGroundedFracObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + elseif (config_global_ocean_topography_method .eq. "bilinear_interpolation") then + call ocn_init_interpolation_bilinear_horiz(landIceThkLon % array, landIceThkLat % array, & + landIceThkIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceThkObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_bilinear_horiz(landIceThkLon % array, landIceThkLat % array, & + landIceDraftIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceDraftObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_bilinear_horiz(landIceThkLon % array, landIceThkLat % array, & + landIceFracIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceFracObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_bilinear_horiz(landIceThkLon % array, landIceThkLat % array, & + groundedFracIC % array, nLonLandIceThk, nLatLandIceThk, & + lonCell, latCell, landIceGroundedFracObserved, nCells, & + inXPeriod = 2.0_RKIND * pii) + + else + call mpas_log_write( 'Invalid choice of config_global_ocean_topography_method.', MPAS_LOG_CRIT) + iErr = 1 + call mpas_dmpar_finalize(domain % dminfo) + endif + + ssh(:) = 0.0_RKIND + landIceFraction(:) = 0.0_RKIND + landIceMask(:) = 0 + modifySSHMask(:) = 0 + landIcePressure(:) = 0.0_RKIND + do iCell = 1, nCells + + if(landIceFracObserved(iCell) > 0.5_RKIND) then + landIceFraction(iCell) = landIceFracObserved(iCell) + landIceMask(iCell) = 1 + end if + + ! nothing to do here if the cell is land + if (maxLevelCell(iCell) <= 0) cycle + + if(config_iterative_init_variable == 'ssh') then + ! we compute the land-ice pressure first and find out the SSH + landIcePressure(iCell) = max(0.0_RKIND, config_land_ice_flux_rho_ice & + * gravity * landIceThkObserved(iCell)) + if(landIcePressure(iCell) > 0.0_RKIND) then + modifySSHMask(iCell) = 1 + end if + else if(config_iterative_init_variable == 'landIcePressure' & + .or. config_iterative_init_variable == 'landIcePressure_from_top_density') then + ! we compute the SSH first and find out the land-ice pressure + ssh(iCell) = min(0.0_RKIND,landIceDraftObserved(iCell)) + if(ssh(iCell) < 0.0_RKIND) then + modifySSHMask(iCell) = 1 + end if + else + call mpas_log_write( 'Invalid choice of config_iterative_init_variable.', MPAS_LOG_CRIT) + iErr = 1 + call mpas_dmpar_finalize(domain % dminfo) + end if + end do + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_global_ocean_interpolate_land_ice_topography!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_modify_temp_under_land_ice +! +!> \brief Modify temperature and restoring under land ice +!> \author Xylar Asay-Davis +!> \date 12/29/2016 +!> \details +!> This routine will set the temperature under land ice to a constant value if the +!> appropriate flag (config_global_ocean_use_constant_land_ice_cavity_temperature) is +!> set. The routine also turns off surface restoring under land ice by modifying +!> the piston velocities. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_modify_temp_under_land_ice(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + logical, pointer :: config_global_ocean_use_constant_land_ice_cavity_temperature + real (kind=RKIND), pointer :: config_global_ocean_constant_land_ice_cavity_temperature + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, forcingPool, tracersPool, & + statePool, tracersSurfaceRestoringFieldsPool + + integer, pointer :: nCells, tracerIndex + integer, dimension(:), pointer :: maxLevelCell, landIceMask + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + real (kind=RKIND), dimension(:, :), pointer :: activeTracersPistonVelocity + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_use_constant_land_ice_cavity_temperature', & + config_global_ocean_use_constant_land_ice_cavity_temperature) + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_constant_land_ice_cavity_temperature', & + config_global_ocean_constant_land_ice_cavity_temperature) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', tracerIndex) + + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', & + activeTracersPistonVelocity, 1) + + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + + if (config_global_ocean_use_constant_land_ice_cavity_temperature & + .and. associated(activeTracers) .and. associated(landIceMask)) then + do iCell = 1, nCells + if((maxLevelCell(iCell) < 1) .or. (landIceMask(iCell) == 0)) cycle ! nothing to modify + + activeTracers(tracerIndex, 1:maxLevelCell(iCell), iCell) = & + config_global_ocean_constant_land_ice_cavity_temperature + + end do + end if + + if ( associated(activeTracersPistonVelocity) .and. associated(landIceMask) ) then + do iCell = 1, nCells + if(landIceMask(iCell) == 1) then + activeTracersPistonVelocity(:, iCell) = 0.0_RKIND + end if + end do + end if + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_global_ocean_modify_temp_under_land_ice!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_cull_inland_seas +! +!> \brief Read the topography IC file +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine removes all inland seas. These are defined as isolated ocean cells. +!> It uses a parallel version of an advancing front algorithm which might not be +!> optimal for this purpose. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_cull_inland_seas(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: scratchPool, meshPool + + type (field1DInteger), pointer :: cullStackField, touchedCellField, oceanCellField + + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell, bottomDepth + integer, dimension(:), pointer :: stack, oceanMask, touchMask + integer, pointer :: stackSize + + integer :: iCell + integer :: localStackSize, globalStackSize + integer :: j, coc + integer :: touched + + integer, pointer :: nCells, nCellsSolve, nVertLevels + integer, dimension(:), pointer :: maxLevelCell, nEdgesOnCell + integer, dimension(:, :), pointer :: cellsOnCell + + iErr = 0 + + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + + call mpas_pool_get_field(scratchPool, 'cullStack', cullStackField) + call mpas_pool_get_field(scratchPool, 'touchedCell', touchedCellField) + call mpas_pool_get_field(scratchPool, 'oceanCell', oceanCellField) + + call mpas_allocate_scratch_field(cullStackField, .false.) + call mpas_allocate_scratch_field(touchedCellField, .false.) + call mpas_allocate_scratch_field(oceanCellField, .false.) + + ! Seed all deepest points for advancing front algorithm + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(scratchPool, 'cullStack', stack) + call mpas_pool_get_array(scratchPool, 'oceanCell', oceanMask) + call mpas_pool_get_array(scratchPool, 'touchedCell', touchMask) + call mpas_pool_get_array(scratchPool, 'cullStackSize', stackSize) + + stack(:) = 0 + oceanMask(:) = 0 + touchMask(:) = 0 + stackSize = 0 + + ! Add all cells that have maxLevelCell == nVertLevels to stack + do iCell = 1, nCellsSolve + if (maxLevelCell(iCell) == nVertLevels) then + stackSize = stackSize + 1 + stack(stackSize) = iCell + touchMask(iCell) = 1 + oceanMask(iCell) = 1 + end if + end do + + block_ptr => block_ptr % next + end do + + ! Advancing front algorithm continues until all stacks on all processes are empty. + globalStackSize = 1 + do while(globalStackSize /= 0) + ! Advance front on each block with a non-zero stack until stack is empty. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + + call mpas_pool_get_array(scratchPool, 'cullStack', stack) + call mpas_pool_get_array(scratchPool, 'oceanCell', oceanMask) + call mpas_pool_get_array(scratchPool, 'touchedCell', touchMask) + call mpas_pool_get_array(scratchPool, 'cullStackSize', stackSize) + + touched = 0 + do while(stackSize > 0) + iCell = stack(stackSize) + stackSize = stackSize - 1 + do j = 1, nEdgesOnCell(iCell) + coc = cellsOnCell(j, iCell) + if (touchMask(coc) == 0 .and. bottomDepth(coc) > 0.0_RKIND) then + oceanMask(coc) = 1 + stackSize = stackSize + 1 + stack(stackSize) = coc + end if + touchMask(coc) = 1 + touched = touched + 1 + end do + end do + + block_ptr => block_ptr % next + end do + + ! Perform a halo exchange on oceanMask + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_field(scratchPool, 'oceanCell', oceanCellField) + call mpas_dmpar_exch_halo_field(oceanCellField) + + ! Check to see if any cells have been masked as ocean in the halo that have not been touched. + ! If there are any, add them to the stack. Also, compute globalStackSize + localStackSize = 0 + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(scratchPool, 'cullStack', stack) + call mpas_pool_get_array(scratchPool, 'oceanCell', oceanMask) + call mpas_pool_get_array(scratchPool, 'touchedCell', touchMask) + call mpas_pool_get_array(scratchPool, 'cullStackSize', stackSize) + + do iCell = nCellsSolve, nCells + if (oceanMask(iCell) == 1 .and. touchMask(iCell) == 0) then + stackSize = stackSize + 1 + stack(stackSize) = iCell + touchMask(iCell) = 1 + end if + end do + + localStackSize = localStackSize + stackSize + block_ptr => block_ptr % next + end do + + call mpas_dmpar_sum_int(domain % dminfo, localStackSize, globalStackSize) + end do + + ! Mark all cells that aren't ocean cells for removal + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(scratchPool, 'oceanCell', oceanMask) + + do iCell = 1, nCellsSolve + if (oceanMask(iCell) == 0) then + maxLevelCell(iCell) = -1 + end if + end do + block_ptr => block_ptr % next + end do + + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + + call mpas_pool_get_field(scratchPool, 'cullStack', cullStackField) + call mpas_pool_get_field(scratchPool, 'touchedCell', touchedCellField) + call mpas_pool_get_field(scratchPool, 'oceanCell', oceanCellField) + + call mpas_deallocate_scratch_field(cullStackField, .false.) + call mpas_deallocate_scratch_field(touchedCellField, .false.) + call mpas_deallocate_scratch_field(oceanCellField, .false.) + + end subroutine ocn_init_setup_global_ocean_cull_inland_seas!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_depth_levels +! +!> \brief Read depth levels for global ocean test case +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine reads the depth levels from the temperature IC file and sets +!> refBottomDepth accordingly +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_depth_levels(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (MPAS_Stream_type) :: depthStream + + type (mpas_pool_type), pointer :: meshPool + + character (len=StrKIND), pointer :: config_global_ocean_depth_file, config_global_ocean_depth_varname, & + config_global_ocean_depth_dimname + + integer :: k + + real (kind=RKIND), pointer :: config_global_ocean_depth_conversion_factor + + real (kind=RKIND), dimension(:), pointer :: refBottomDepth + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_file', config_global_ocean_depth_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_varname', config_global_ocean_depth_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_dimname', config_global_ocean_depth_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_conversion_factor', & + config_global_ocean_depth_conversion_factor) + + ! Define stream for depth levels + call MPAS_createStream(depthStream, domain % iocontext, config_global_ocean_depth_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup depth field for stream to be read in + depthOutput % fieldName = trim(config_global_ocean_depth_varname) + depthOutput % dimSizes(1) = nDepthOutput + depthOutput % dimNames(1) = trim(config_global_ocean_depth_dimname) + depthOutput % isVarArray = .false. + depthOutput % isPersistent = .true. + depthOutput % isActive = .true. + depthOutput % hasTimeDimension = .false. + depthOutput % block => domain % blocklist + allocate(depthOutput % attLists(1)) + allocate(depthOutput % array(nDepthOutput)) + + ! Add depth field to stream + call MPAS_streamAddField(depthStream, depthOutput, iErr) + + ! Read stream + call MPAS_readStream(depthStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(depthStream) + depthOutput % array(:) = depthOutput % array(:) * config_global_ocean_depth_conversion_factor + + ! Set refBottomDepth depending on depth levels. And convert appropriately + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + ! depthOutput is the mid-depth of each layer. Convert to bottom depth. + refBottomDepth(1) = 2.0_RKIND * depthOutput % array(1) + do k=2,nDepthOutput + refBottomDepth(k) = refBottomDepth(k-1) + 2*(depthOutput % array(k) - refBottomDepth(k-1)) + enddo + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_global_ocean_read_depth_levels!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_tracer_lat_lon +! +!> \brief Read Lat/Lon for tracers in global ocean test case +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine reads the latitude and longitude coordinats for tracers from the temperature IC file. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_tracer_lat_lon(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: tracerStream + + character (len=StrKIND), pointer :: config_global_ocean_temperature_file, & + config_global_ocean_tracer_lat_varname, config_global_ocean_tracer_nlat_dimname, & + config_global_ocean_tracer_lon_varname, config_global_ocean_tracer_nlon_dimname, & + config_global_ocean_tracer_depth_varname, config_global_ocean_tracer_ndepth_dimname + + real (kind=RKIND), pointer :: config_global_ocean_tracer_depth_conversion_factor + + logical, pointer :: config_global_ocean_tracer_latlon_degrees + + integer :: iLat, iLon + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_temperature_file', config_global_ocean_temperature_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_lat_varname', & + config_global_ocean_tracer_lat_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_nlat_dimname', & + config_global_ocean_tracer_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_lon_varname', & + config_global_ocean_tracer_lon_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_nlon_dimname', & + config_global_ocean_tracer_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_depth_varname', & + config_global_ocean_tracer_depth_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_ndepth_dimname', & + config_global_ocean_tracer_ndepth_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_latlon_degrees', & + config_global_ocean_tracer_latlon_degrees) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_depth_conversion_factor', & + config_global_ocean_tracer_depth_conversion_factor) + + ! Define stream for depth levels + call MPAS_createStream(tracerStream, domain % iocontext, config_global_ocean_temperature_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup tracerLat and tracerLon fields for stream to be read in + tracerLat % fieldName = trim(config_global_ocean_tracer_lat_varname) + tracerLat % dimSizes(1) = nLatTracer + tracerLat % dimNames(1) = trim(config_global_ocean_tracer_nlat_dimname) + tracerLat % isVarArray = .false. + tracerLat % isPersistent = .true. + tracerLat % isActive = .true. + tracerLat % hasTimeDimension = .false. + tracerLat % block => domain % blocklist + allocate(tracerLat % attLists(1)) + allocate(tracerLat % array(nLatTracer)) + + tracerLon % fieldName = trim(config_global_ocean_tracer_lon_varname) + tracerLon % dimSizes(1) = nLonTracer + tracerLon % dimNames(1) = trim(config_global_ocean_tracer_nlon_dimname) + tracerLon % isVarArray = .false. + tracerLon % isPersistent = .true. + tracerLon % isActive = .true. + tracerLon % hasTimeDimension = .false. + tracerLon % block => domain % blocklist + allocate(tracerLon % attLists(1)) + allocate(tracerLon % array(nLonTracer)) + + tracerDepth % fieldName = trim(config_global_ocean_tracer_depth_varname) + tracerDepth % dimSizes(1) = nDepthTracer + tracerDepth % dimNames(1) = trim(config_global_ocean_tracer_ndepth_dimname) + tracerDepth % isVarArray = .false. + tracerDepth % isPersistent = .true. + tracerDepth % isActive = .true. + tracerDepth % hasTimeDimension = .false. + tracerDepth % block => domain % blocklist + allocate(tracerDepth % attLists(1)) + allocate(tracerDepth % array(nDepthTracer)) + + ! Add tracerLat and tracerLon fields to stream + call MPAS_streamAddField(tracerStream, tracerLat, iErr) + call MPAS_streamAddField(tracerStream, tracerLon, iErr) + call MPAS_streamAddField(tracerStream, tracerDepth, iErr) + + ! Read stream + call MPAS_readStream(tracerStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(tracerStream) + + ! note IC tracer depth is in z coordinates, so negative + tracerDepth % array(:) = - tracerDepth % array(:) * config_global_ocean_tracer_depth_conversion_factor + + if (config_global_ocean_tracer_latlon_degrees) then + do iLat = 1, nLatTracer + tracerLat % array(iLat) = tracerLat % array(iLat) * pii / 180.0_RKIND + end do + + do iLon = 1, nLonTracer + tracerLon % array(iLon) = tracerLon % array(iLon) * pii / 180.0_RKIND + end do + end if + + end subroutine ocn_init_setup_global_ocean_read_tracer_lat_lon!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_swData_lat_lon +! +!> \brief Read Lat/Lon for swData in global ocean test case +!> \author Luke Van Roekel +!> \date 11/16/2015 +!> \details +!> This routine reads the latitude and longitude coordinats for swData (chlorophyll, +! clearSkyRadiation, zenithangle) from the swData IC file. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_swData_lat_lon(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: SWStream + + character (len=StrKIND), pointer :: config_global_ocean_swData_file, config_global_ocean_swData_lat_varname, & + config_global_ocean_swData_nlat_dimname, config_global_ocean_swData_lon_varname, & + config_global_ocean_swData_nlon_dimname + + logical, pointer :: config_global_ocean_swData_latlon_degrees + + integer :: iLat, iLon + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_file', config_global_ocean_swData_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_lat_varname', & + config_global_ocean_swData_lat_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_nlat_dimname', & + config_global_ocean_swData_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_lon_varname', & + config_global_ocean_swData_lon_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_nlon_dimname', & + config_global_ocean_swData_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_latlon_degrees', & + config_global_ocean_swData_latlon_degrees) + + ! Define stream for depth levels + call MPAS_createStream(SWStream, domain % iocontext, config_global_ocean_swData_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup tracerLat and tracerLon fields for stream to be read in + swDataLat % fieldName = trim(config_global_ocean_swData_lat_varname) + swDataLat % dimSizes(1) = nLatSW + swDataLat % dimNames(1) = trim(config_global_ocean_swData_nlat_dimname) + swDataLat % isVarArray = .false. + swDataLat % isPersistent = .true. + swDataLat % isActive = .true. + swDataLat % hasTimeDimension = .false. + swDataLat % block => domain % blocklist + allocate(swDataLat % attLists(1)) + allocate(swDataLat % array(nLatSW)) + + swDataLon % fieldName = trim(config_global_ocean_swData_lon_varname) + swDataLon % dimSizes(1) = nLonSW + swDataLon % dimNames(1) = trim(config_global_ocean_swData_nlon_dimname) + swDataLon % isVarArray = .false. + swDataLon % isPersistent = .true. + swDataLon % isActive = .true. + swDataLon % hasTimeDimension = .false. + swDataLon % block => domain % blocklist + allocate(swDataLon % attLists(1)) + allocate(swDataLon % array(nLonSW)) + + ! Add tracerLat and tracerLon fields to stream + call MPAS_streamAddField(SWStream, swDataLat, iErr) + call MPAS_streamAddField(SWStream, swDataLon, iErr) + + ! Read stream + call MPAS_readStream(SWStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(SWStream) + + if (config_global_ocean_swData_latlon_degrees) then + do iLat = 1, nLatSW + swDataLat % array(iLat) = swDataLat % array(iLat) * pii / 180.0_RKIND + end do + + do iLon = 1, nLonSW + swDataLon % array(iLon) = swDataLon % array(iLon) * pii / 180.0_RKIND + end do + end if + + end subroutine ocn_init_setup_global_ocean_read_swData_lat_lon!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_ecosys_lat_lon +! +!> \brief Read Lat/Lon for ecosys in global ocean test case +!> \author Mark Petersen +!> \date Aug 19 2016 +!> \details +!> This routine reads the latitude and longitude coordinats for ecosys +!> from the ecosys IC file. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_ecosys_lat_lon(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: tracerStream + + character (len=StrKIND), pointer :: config_global_ocean_ecosys_file, & + config_global_ocean_ecosys_lat_varname, config_global_ocean_ecosys_nlat_dimname, & + config_global_ocean_ecosys_lon_varname, config_global_ocean_ecosys_nlon_dimname, & + config_global_ocean_ecosys_depth_varname, config_global_ocean_ecosys_ndepth_dimname + + real (kind=RKIND), pointer :: config_global_ocean_ecosys_depth_conversion_factor + + logical, pointer :: config_global_ocean_ecosys_latlon_degrees + + integer :: iLat, iLon + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_file', config_global_ocean_ecosys_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_lat_varname', & + config_global_ocean_ecosys_lat_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_nlat_dimname', & + config_global_ocean_ecosys_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_lon_varname', & + config_global_ocean_ecosys_lon_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_nlon_dimname', & + config_global_ocean_ecosys_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_depth_varname', & + config_global_ocean_ecosys_depth_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_ndepth_dimname', & + config_global_ocean_ecosys_ndepth_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_latlon_degrees', & + config_global_ocean_ecosys_latlon_degrees) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_depth_conversion_factor', & + config_global_ocean_ecosys_depth_conversion_factor) + + ! Define stream for depth levels + call MPAS_createStream(tracerStream, domain % iocontext, config_global_ocean_ecosys_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup tracerLat and tracerLon fields for stream to be read in + tracerLat % fieldName = trim(config_global_ocean_ecosys_lat_varname) + tracerLat % dimSizes(1) = nLatEcosys + tracerLat % dimNames(1) = trim(config_global_ocean_ecosys_nlat_dimname) + tracerLat % isVarArray = .false. + tracerLat % isPersistent = .true. + tracerLat % isActive = .true. + tracerLat % hasTimeDimension = .false. + tracerLat % block => domain % blocklist + allocate(tracerLat % attLists(1)) + allocate(tracerLat % array(nLatEcosys)) + + tracerLon % fieldName = trim(config_global_ocean_ecosys_lon_varname) + tracerLon % dimSizes(1) = nLonEcosys + tracerLon % dimNames(1) = trim(config_global_ocean_ecosys_nlon_dimname) + tracerLon % isVarArray = .false. + tracerLon % isPersistent = .true. + tracerLon % isActive = .true. + tracerLon % hasTimeDimension = .false. + tracerLon % block => domain % blocklist + allocate(tracerLon % attLists(1)) + allocate(tracerLon % array(nLonEcosys)) + + tracerDepth % fieldName = trim(config_global_ocean_ecosys_depth_varname) + tracerDepth % dimSizes(1) = nDepthEcosys + tracerDepth % dimNames(1) = trim(config_global_ocean_ecosys_ndepth_dimname) + tracerDepth % isVarArray = .false. + tracerDepth % isPersistent = .true. + tracerDepth % isActive = .true. + tracerDepth % hasTimeDimension = .false. + tracerDepth % block => domain % blocklist + allocate(tracerDepth % attLists(1)) + allocate(tracerDepth % array(nDepthEcosys)) + + ! Add ecosys Lat, Lon and Depth fields to stream + call MPAS_streamAddField(tracerStream, tracerLat, iErr) + call MPAS_streamAddField(tracerStream, tracerLon, iErr) + call MPAS_streamAddField(tracerStream, tracerDepth, iErr) + + ! Read stream + call MPAS_readStream(tracerStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(tracerStream) + + ! note IC tracer depth is in z coordinates, so negative + tracerDepth % array(:) = - tracerDepth % array(:) * config_global_ocean_ecosys_depth_conversion_factor + + if (config_global_ocean_ecosys_latlon_degrees) then + do iLat = 1, nLatEcosys + tracerLat % array(iLat) = tracerLat % array(iLat) * pii / 180.0_RKIND + end do + + do iLon = 1, nLonEcosys + tracerLon % array(iLon) = tracerLon % array(iLon) * pii / 180.0_RKIND + end do + end if + + end subroutine ocn_init_setup_global_ocean_read_ecosys_lat_lon!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_temperature +! +!> \brief Read temperature ICs for global ocean test case +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine reads the temperature field from the temperature IC file. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_temperature(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: tracerStream + + character (len=StrKIND), pointer :: config_global_ocean_temperature_file, config_global_ocean_temperature_varname, & + config_global_ocean_tracer_nlon_dimname, config_global_ocean_tracer_nlat_dimname, & + config_global_ocean_depth_dimname + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_temperature_file', config_global_ocean_temperature_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_temperature_varname', & + config_global_ocean_temperature_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_nlon_dimname', & + config_global_ocean_tracer_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_nlat_dimname', & + config_global_ocean_tracer_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_dimname', config_global_ocean_depth_dimname) + + ! Define stream for temperature IC + call MPAS_createStream(tracerStream, domain % iocontext, config_global_ocean_temperature_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup temperature field for stream to be read in + tracerIC % fieldName = trim(config_global_ocean_temperature_varname) + tracerIC % dimSizes(1) = nLonTracer + tracerIC % dimSizes(2) = nLatTracer + tracerIC % dimSizes(3) = nDepthTracer + tracerIC % dimNames(1) = trim(config_global_ocean_tracer_nlon_dimname) + tracerIC % dimNames(2) = trim(config_global_ocean_tracer_nlat_dimname) + tracerIC % dimNames(3) = trim(config_global_ocean_depth_dimname) + tracerIC % isVarArray = .false. + tracerIC % isPersistent = .true. + tracerIC % isActive = .true. + tracerIC % hasTimeDimension = .false. + tracerIC % block => domain % blocklist + + ! Add temperature field to stream + call MPAS_streamAddField(tracerStream, tracerIC, iErr) + + ! Read stream + call MPAS_readStream(tracerStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(tracerStream) + + end subroutine ocn_init_setup_global_ocean_read_temperature!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_salinity +! +!> \brief Read salinity ICs for global ocean test case +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine reads the salinity field from the salinity IC file. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_salinity(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: tracerStream + + character (len=StrKIND), pointer :: config_global_ocean_salinity_file, config_global_ocean_salinity_varname, & + config_global_ocean_tracer_nlon_dimname, config_global_ocean_tracer_nlat_dimname, & + config_global_ocean_depth_dimname + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_salinity_file', config_global_ocean_salinity_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_salinity_varname', config_global_ocean_salinity_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_nlon_dimname', & + config_global_ocean_tracer_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_nlat_dimname', & + config_global_ocean_tracer_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_dimname', config_global_ocean_depth_dimname) + + ! Define stream for salinity IC + call MPAS_createStream(tracerStream, domain % iocontext, config_global_ocean_salinity_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup salinity field for stream to be read in + tracerIC % fieldName = trim(config_global_ocean_salinity_varname) + tracerIC % dimSizes(1) = nLonTracer + tracerIC % dimSizes(2) = nLatTracer + tracerIC % dimSizes(3) = nDepthTracer + tracerIC % dimNames(1) = trim(config_global_ocean_tracer_nlon_dimname) + tracerIC % dimNames(2) = trim(config_global_ocean_tracer_nlat_dimname) + tracerIC % dimNames(3) = trim(config_global_ocean_depth_dimname) + tracerIC % isVarArray = .false. + tracerIC % isPersistent = .true. + tracerIC % isActive = .true. + tracerIC % hasTimeDimension = .false. + tracerIC % block => domain % blocklist + allocate(tracerIC % attLists(1)) + allocate(tracerIC % array(nLonTracer, nLatTracer, nDepthTracer)) + + ! Add salinity field to stream + call MPAS_streamAddField(tracerStream, tracerIC, iErr) + + ! Read stream + call MPAS_readStream(tracerStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(tracerStream) + + end subroutine ocn_init_setup_global_ocean_read_salinity!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_ecosys +! +!> \brief Read ecosys ICs for global ocean test case +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine reads the ecosys fields from the ecosys IC file. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_ecosys(domain, fieldName, fileName, iErr)!{{{ + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + character (len=StrKIND), intent(in) :: fieldName, fileName + + type (MPAS_Stream_type) :: tracerStream + + character (len=StrKIND), pointer :: config_global_ocean_ecosys_nlon_dimname, & + config_global_ocean_ecosys_nlat_dimname, & + config_global_ocean_depth_dimname + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_nlon_dimname', & + config_global_ocean_ecosys_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_nlat_dimname', & + config_global_ocean_ecosys_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_depth_dimname', & + config_global_ocean_depth_dimname) + + ! Define stream for ecosys IC + call MPAS_createStream(tracerStream, domain % iocontext, fileName, MPAS_IO_NETCDF, MPAS_IO_READ, ierr=iErr) + + ! Setup ecosys field for stream to be read in + tracerIC % fieldName = trim(fieldName) + tracerIC % dimSizes(1) = nLonEcosys + tracerIC % dimSizes(2) = nLatEcosys + tracerIC % dimSizes(3) = nDepthEcosys + tracerIC % dimNames(1) = trim(config_global_ocean_ecosys_nlon_dimname) + tracerIC % dimNames(2) = trim(config_global_ocean_ecosys_nlat_dimname) + tracerIC % dimNames(3) = trim(config_global_ocean_depth_dimname) + tracerIC % isVarArray = .false. + tracerIC % isPersistent = .true. + tracerIC % isActive = .true. + tracerIC % hasTimeDimension = .false. + tracerIC % block => domain % blocklist + + ! Add ecosys field to stream + call MPAS_streamAddField(tracerStream, tracerIC, iErr) + + ! Read stream + call MPAS_readStream(tracerStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(tracerStream) + + end subroutine ocn_init_setup_global_ocean_read_ecosys!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_ecosys_forcing +! +!> \brief Read ecosys forcing for global ocean test case +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine reads the ecosys forcing fields from the ecosys forcing file. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_ecosys_forcing(domain, fieldName, fileName, iErr)!{{{ + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + character (len=StrKIND), intent(in) :: fieldName, fileName + + type (MPAS_Stream_type) :: ecosysForcingStream + + character (len=StrKIND), pointer :: config_global_ocean_ecosys_nlon_dimname, & + config_global_ocean_ecosys_nlat_dimname, & + config_global_ocean_ecosys_forcing_time_dimname + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_nlon_dimname', & + config_global_ocean_ecosys_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_nlat_dimname', & + config_global_ocean_ecosys_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_forcing_time_dimname', & + config_global_ocean_ecosys_forcing_time_dimname) + + ! Define stream for ecosys forcing + call MPAS_createStream(ecosysForcingStream, domain % iocontext, fileName, MPAS_IO_NETCDF, MPAS_IO_READ, ierr=iErr) + + ! Setup ecosys field for stream to be read in + ecosysForcingIC % fieldName = trim(fieldName) + ecosysForcingIC % dimSizes(1) = nLonEcosys + ecosysForcingIC % dimSizes(2) = nLatEcosys + nTimes = 1 + ecosysForcingIC % dimSizes(3) = nTimes + ecosysForcingIC % dimNames(1) = trim(config_global_ocean_ecosys_nlon_dimname) + ecosysForcingIC % dimNames(2) = trim(config_global_ocean_ecosys_nlat_dimname) + ecosysForcingIC % dimNames(3) = trim(config_global_ocean_ecosys_forcing_time_dimname) + ecosysForcingIC % isVarArray = .false. + ecosysForcingIC % isPersistent = .true. + ecosysForcingIC % isActive = .true. + ecosysForcingIC % hasTimeDimension = .false. + ecosysForcingIC % block => domain % blocklist + + ! Add ecosys field to stream + call MPAS_streamAddField(ecosysForcingStream, ecosysForcingIC, iErr) + + ! Read stream + call MPAS_readStream(ecosysForcingStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(ecosysForcingStream) + + end subroutine ocn_init_setup_global_ocean_read_ecosys_forcing!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_interpolate_tracers +! +!> \brief Interpolate tracer quantities to MPAS grid +!> \author Mark Petersen, Doug Jacobsen, Xylar Asay-Davis +!> \date 08/23/2016 +!> \details +!> This routine interpolates the temperature/salinity data read in from the +!> initial condition file to the MPAS grid. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_interpolate_tracers(domain, tracerArray, tracerIndex, interpTracerName, iErr)!{{{ + + type (domain_type), intent(inout) :: domain + real (kind=RKIND), dimension(:, :, :), intent(inout) :: tracerArray + integer, intent(in) :: tracerIndex + integer, intent(out) :: iErr + character (len=StrKIND), intent(in) :: interpTracerName + + type (block_type), pointer :: block_ptr + type (mpas_pool_type), pointer :: meshPool, scratchPool, diagnosticsPool + + real (kind=RKIND) :: counter + integer :: iSmooth, j, coc, iCell, k, nDepth + integer, pointer :: nCells, nVertLevels + + integer, dimension(:), pointer :: maxLevelCell, nEdgesOnCell + integer, dimension(:, :), pointer :: cellsOnCell + + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell + real (kind=RKIND), dimension(:, :), pointer :: smoothedTracer + + character (len=StrKIND), pointer :: config_global_ocean_tracer_method + integer, pointer :: config_global_ocean_smooth_TS_iterations + + real (kind=RKIND), dimension(:), pointer :: outTracerColumn + real (kind=RKIND), dimension(:,:), pointer :: zMid + integer :: inKMax, outKMax + + type (field2DReal), pointer :: interpTracerField + real (kind=RKIND), dimension(:,:), pointer :: interpTracer + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_tracer_method', & + config_global_ocean_tracer_method) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_smooth_TS_iterations', & + config_global_ocean_smooth_TS_iterations) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + + call mpas_pool_get_array(scratchPool, trim(interpTracerName), interpTracer) + + if (config_global_ocean_tracer_method .eq. "nearest_neighbor") then + call ocn_init_interpolation_nearest_horiz(tracerLon % array, tracerLat % array, & + tracerIC % array, nLonTracer, nLatTracer, & + lonCell, latCell, interpTracer, nCells, & + inXPeriod = 2.0_RKIND * pii) + + elseif (config_global_ocean_tracer_method .eq. "bilinear_interpolation") then + + call ocn_init_interpolation_bilinear_horiz(tracerLon % array, tracerLat % array, & + tracerIC % array, nLonTracer, nLatTracer, & + lonCell, latCell, interpTracer, nCells, & + inXPeriod = 2.0_RKIND * pii) + + else + call mpas_log_write( 'Invalid choice of config_global_ocean_tracer_method.', MPAS_LOG_CRIT) + iErr = 1 + call mpas_dmpar_finalize(domain % dminfo) + endif + + block_ptr => block_ptr % next + end do + + ! Smooth the tracer + if (config_global_ocean_smooth_TS_iterations .gt. 0) then + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + + do iSmooth = 1,config_global_ocean_smooth_TS_iterations + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + + call mpas_pool_get_array(scratchPool, trim(interpTracerName)//'Smooth', smoothedTracer) + call mpas_pool_get_array(scratchPool, trim(interpTracerName), interpTracer) + + nDepth = size(smoothedTracer, 1) + + ! initialize smoothed tracer with original values + smoothedTracer = interpTracer + + do iCell = 1, nCells + + do k = 1, nDepth + ! Initialize counter to 1 because of central cell in stencil. + counter = 1 + + do j = 1, nEdgesOnCell(iCell) + coc = cellsOnCell(j, iCell) + if (coc block_ptr % next + end do + + call mpas_pool_get_field(scratchPool, trim(interpTracerName), interpTracerField) + call mpas_dmpar_exch_halo_field(interpTracerField) + + end do ! iSmooth + + endif + + ! reinterpolate tracers from original depths to zMid for PBCs or other modified vertical coordinates + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(scratchPool, trim(interpTracerName), interpTracer) + + allocate(outTracerColumn(nVertLevels)) + + inKMax = size(interpTracer, 1) + do iCell = 1, nCells + outKMax = maxLevelCell(iCell) + if(outKMax < 1) cycle ! nothing to interpolate + + outTracerColumn(:) = 9.969209968386869e+36_RKIND + call ocn_init_interpolation_linear_vert(tracerDepth % array(1:inKMax), & + interpTracer(1:inKMax,iCell), & + inKMax, & + zMid(1:outKMax,iCell), & + outTracerColumn(1:outKMax), & + outKMax, & + extrapolate=.false.) + tracerArray(tracerIndex,1:outKMax,iCell) = outTracerColumn(1:outKMax) + + end do + + deallocate(outTracerColumn) + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_global_ocean_interpolate_tracers!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_interpolate_ecosys_forcing +! +!> \brief Interpolate ecosys forcing quantities to MPAS grid +!> \author Doug Jacobsen +!> \date 03/05/2014 +!> \details +!> This routine interpolates the ecosys forcing data read in from the +!> forcing file to the MPAS grid. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_interpolate_ecosys_forcing(domain, fieldName, poolName, iErr)!{{{ + + type (domain_type), intent(inout) :: domain + character (len=StrKIND), intent(in) :: fieldName, poolName + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + type (mpas_pool_type), pointer :: meshPool, forcingPool, ecosysAuxiliary + + integer :: timeCounter + integer, pointer :: nCells, nCellsSolve + + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell + real (kind=RKIND), dimension(:), pointer :: ecosysForcingField + + character (len=StrKIND), pointer :: config_global_ocean_ecosys_method + integer, pointer :: config_global_ocean_smooth_ecosys_iterations + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_ecosys_method', & + config_global_ocean_ecosys_method) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_smooth_ecosys_iterations', & + config_global_ocean_smooth_ecosys_iterations) + + timeCounter = 1 + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + + if (trim(poolName) == 'ecosysAuxiliary') then + call mpas_pool_get_array(ecosysAuxiliary, trim(fieldName), ecosysForcingField, 1) + else if (trim(poolName) == 'forcing') then + call mpas_pool_get_array(forcingPool, trim(fieldName), ecosysForcingField, 1) + end if + + if (config_global_ocean_ecosys_method .eq. "nearest_neighbor") then + call ocn_init_interpolation_nearest_horiz(tracerLon % array, tracerLat % array, & + ecosysForcingIC % array(:,:,timeCounter), nLonEcosys, nLatEcosys, & + lonCell, latCell, ecosysForcingField, nCells, & + inXPeriod = 2.0_RKIND * pii) + + elseif (config_global_ocean_ecosys_method .eq. "bilinear_interpolation") then + call ocn_init_interpolation_bilinear_horiz(tracerLon % array, tracerLat % array, & + ecosysForcingIC % array(:,:,timeCounter), nLonEcosys, nLatEcosys, & + lonCell, latCell, ecosysForcingField, nCells, & + inXPeriod = 2.0_RKIND * pii) + else + call mpas_log_write( 'Invalid choice of config_global_ocean_ecosys_method.', MPAS_LOG_CRIT) + iErr = 1 + call mpas_dmpar_finalize(domain % dminfo) + endif + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_global_ocean_interpolate_ecosys_forcing!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_interpolate_restoring +! +!> \brief Copy tracer quantities for restoring +!> \author Doug Jacobsen, Xylar Asay-Davis +!> \date 03/05/2014 +!> \details +!> This routine copies temperature/salinity into surface and interior restoring fields. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_interpolate_restoring(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + type (mpas_pool_type), pointer :: meshPool, statePool, tracersPool, forcingPool + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool, tracersInteriorRestoringFieldsPool + + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + real (kind=RKIND), dimension(:, :), pointer :: activeTracersPistonVelocity, activeTracersSurfaceRestoringValue + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracersInteriorRestoringValue, activeTracersInteriorRestoringRate + + real (kind=RKIND), pointer :: config_global_ocean_piston_velocity + real (kind=RKIND), pointer :: config_global_ocean_interior_restore_rate + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_piston_velocity', config_global_ocean_piston_velocity) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_interior_restore_rate', & + config_global_ocean_interior_restore_rate) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + call mpas_pool_get_subpool(forcingPool, 'tracersInteriorRestoringFields', tracersInteriorRestoringFieldsPool) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersSurfaceRestoringValue', & + activeTracersSurfaceRestoringValue, 1) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', & + activeTracersPistonVelocity, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringValue', & + activeTracersInteriorRestoringValue, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringRate', & + activeTracersInteriorRestoringRate, 1) + + ! set interior restoring values and rate + if ( associated(activeTracersInteriorRestoringValue) .and. associated(activeTracers) ) then + activeTracersInteriorRestoringValue(:, :, :) = activeTracers(:, :, :) + end if + + if ( associated(activeTracersInteriorRestoringRate) ) then + activeTracersInteriorRestoringRate(:, :, :) = config_global_ocean_interior_restore_rate + end if + + ! set surface restoring values and rate + if ( associated(activeTracersSurfaceRestoringValue) .and. associated(activeTracers) ) then + activeTracersSurfaceRestoringValue(:, :) = activeTracers(:, 1, :) + end if + + if ( associated(activeTracersPistonVelocity) ) then + activeTracersPistonVelocity(:, :) = config_global_ocean_piston_velocity + end if + + block_ptr => block_ptr % next + end do + + + end subroutine ocn_init_setup_global_ocean_interpolate_restoring!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_interpolate_swData +! +!> \brief Interpolate penetrating shortwave radiation quantities to MPAS grid +!> \author Luke Van Roekel, Xylar Asay-Davis +!> \date 11/11/2015 +!> \details +!> This routine interpolates the penetrating swData data read in from the +!> initial condition file to the MPAS grid. +! +!----------------------------------------------------------------------- + +subroutine ocn_init_setup_global_ocean_interpolate_swData(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + type (MPAS_Stream_type) :: zenithStream, chlorophyllStream, clearSkyStream + type (mpas_pool_type), pointer :: meshPool, statePool, shortwavePool, diagnosticsPool + + type(MPAS_TimeInterval_type) :: timeStep ! time step interval + type(MPAS_Time_Type) :: currentTime + character(len=STRKIND) :: currentTimeStamp + + integer :: monIndex + integer, pointer :: nCells, nCellsSolve, maxLevelCell + + integer, dimension(12), parameter :: daysInMonth = (/31,28,31,30,31,30,31,31,30,31,30,31/) + + real (kind=RKIND), dimension(:), pointer :: chlorophyllData, zenithAngle, clearSkyRadiation + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell + + character (len=StrKIND), pointer :: config_global_ocean_swData_method, xtime + character (len=StrKIND), pointer :: config_global_ocean_swData_file, config_global_ocean_zenithAngle_varname, & + config_global_ocean_swData_nlon_dimname, config_global_ocean_swData_nlat_dimname, & + config_global_ocean_chlorophyll_varname, config_global_ocean_clearSky_varname + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_method', config_global_ocean_swData_method) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_file', config_global_ocean_swData_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_zenithAngle_varname', config_global_ocean_zenithAngle_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_chlorophyll_varname', config_global_ocean_chlorophyll_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_clearSky_varname', config_global_ocean_clearSky_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_nlon_dimname', config_global_ocean_swData_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_nlat_dimname', config_global_ocean_swData_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_swData_method', config_global_ocean_swData_method) + ! Define stream for zenithAngle IC + call MPAS_createStream(zenithStream, domain % iocontext, config_global_ocean_swData_file, MPAS_IO_NETCDF, MPAS_IO_READ, & + ierr=iErr) + call MPAS_createStream(chlorophyllStream, domain % iocontext, config_global_ocean_swData_file, MPAS_IO_NETCDF, MPAS_IO_READ, & + ierr=iErr) + call MPAS_createStream(clearSkyStream, domain % iocontext, config_global_ocean_swData_file, MPAS_IO_NETCDF, MPAS_IO_READ, & + ierr=iErr) + + + ! Setup zenithAngle field for stream to be read in + + zenithAngleIC % fieldName = trim(config_global_ocean_zenithAngle_varname) + zenithAngleIC % dimSizes(1) = nLonSW + zenithAngleIC % dimSizes(2) = nLatSW + zenithAngleIC % dimNames(1) = trim(config_global_ocean_swData_nlon_dimname) + zenithAngleIC % dimNames(2) = trim(config_global_ocean_swData_nlat_dimname) + + zenithAngleIC % isVarArray = .false. + zenithAngleIC % isPersistent = .true. + zenithAngleIC % isActive = .true. + zenithAngleIC % hasTimeDimension = .false. + zenithAngleIC % block => domain % blocklist + allocate(zenithAngleIC % attLists(1)) + allocate(zenithAngleIC % array(nLonSW, nLatSW)) + + ! Setup zenithAngle field for stream to be read in + chlorophyllIC % fieldName = trim(config_global_ocean_chlorophyll_varname) + chlorophyllIC % dimSizes(1) = nLonSW + chlorophyllIC % dimSizes(2) = nLatSW + chlorophyllIC % dimNames(1) = trim(config_global_ocean_swData_nlon_dimname) + chlorophyllIC % dimNames(2) = trim(config_global_ocean_swData_nlat_dimname) + chlorophyllIC % isVarArray = .false. + chlorophyllIC % isPersistent = .true. + chlorophyllIC % isActive = .true. + chlorophyllIC % hasTimeDimension = .false. + chlorophyllIC % block => domain % blocklist + allocate(chlorophyllIC % attLists(1)) + allocate(chlorophyllIC % array(nLonSW, nLatSW)) + + ! Setup zenithAngle field for stream to be read in + clearSKYIC % fieldName = trim(config_global_ocean_clearSky_varname) + clearSKYIC % dimSizes(1) = nLonSW + clearSKYIC % dimSizes(2) = nLatSW + clearSKYIC % dimNames(1) = trim(config_global_ocean_swData_nlon_dimname) + clearSKYIC % dimNames(2) = trim(config_global_ocean_swData_nlat_dimname) + clearSKYIC % isVarArray = .false. + clearSKYIC % isPersistent = .true. + clearSKYIC % isActive = .true. + clearSKYIC % hasTimeDimension = .false. + clearSKYIC % block => domain % blocklist + allocate(clearSKYIC % attLists(1)) + allocate(clearSKYIC % array(nLonSW, nLatSW)) + ! Add chlorophyll field to stream + + call MPAS_streamAddField(zenithStream, zenithAngleIC, iErr) + call MPAS_streamAddField(chlorophyllStream, chlorophyllIC, iErr) + call MPAS_streamAddField(clearSkyStream, clearSKYIC, iErr) + + do monIndex=1,12 + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'shortwave', shortwavePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(diagnosticsPool, 'xtime', xtime) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(shortwavePool, 'chlorophyllData', chlorophyllData) + call mpas_pool_get_array(shortWavePool, 'zenithAngle', zenithAngle) + call mpas_pool_get_array(shortWavePool, 'clearSkyRadiation', clearSkyRadiation) + + ! Read stream + + call MPAS_readStream(zenithStream, monIndex, iErr) + call MPAS_readStream(chlorophyllStream, monIndex, iErr) + call MPAS_readStream(clearSkyStream, monIndex, iErr) + + + if (config_global_ocean_swData_method .eq. "nearest_neighbor") then + call ocn_init_interpolation_nearest_horiz(swDataLon % array, swDataLat % array, & + chlorophyllIC % array, nLonSW, nLatSW, & + lonCell, latCell, chlorophyllData, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_nearest_horiz(swDataLon % array, swDataLat % array, & + zenithAngleIC % array, nLonSW, nLatSW, & + lonCell, latCell, zenithAngle, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_nearest_horiz(swDataLon % array, swDataLat % array, & + clearSKYIC % array, nLonSW, nLatSW, & + lonCell, latCell, clearSkyRadiation, nCells, & + inXPeriod = 2.0_RKIND * pii) + + elseif (config_global_ocean_swData_method .eq. "bilinear_interpolation") then + call ocn_init_interpolation_bilinear_horiz(swDataLon % array, swDataLat % array, & + chlorophyllIC % array, nLonSW, nLatSW, & + lonCell, latCell, chlorophyllData, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_bilinear_horiz(swDataLon % array, swDataLat % array, & + zenithAngleIC % array, nLonSW, nLatSW, & + lonCell, latCell, zenithAngle, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_bilinear_horiz(swDataLon % array, swDataLat % array, & + clearSKYIC % array, nLonSW, nLatSW, & + lonCell, latCell, clearSkyRadiation, nCells, & + inXPeriod = 2.0_RKIND * pii) + else + call mpas_log_write( 'Invalid choice of config_global_ocean_swData_method.', MPAS_LOG_CRIT) + iErr = 1 + call mpas_dmpar_finalize(domain % dminfo) + endif + + block_ptr => block_ptr % next + end do !loop on blocks + + ! increment clock with month string + + currentTime = mpas_get_clock_time(domain % clock, MPAS_NOW, iErr) + call mpas_get_time(currentTime, dateTimeString=currentTimeStamp) + + xtime=currentTimeStamp + call mpas_stream_mgr_write(domain % streamManager, streamID='shortwave_forcing_data_init', & + forceWriteNow=.true., ierr=ierr) + call mpas_set_timeInterval(timeStep, dt=real(daysInMonth(monIndex),RKIND)*86400.0_RKIND) + call mpas_advance_clock(domain % clock, timeStep) + + enddo !ends loop over months + + ! Close stream + call MPAS_closeStream(zenithStream) + call MPAS_closeStream(chlorophyllStream) + call MPAS_closeStream(clearSkyStream) + + ! reset mpas clock for other streams and final write + + currentTime = mpas_get_clock_time(domain % clock, MPAS_START_TIME, iErr) + call mpas_set_clock_time(domain%clock, currentTime , MPAS_NOW,iErr) + currentTime = mpas_get_clock_time(domain % clock, MPAS_NOW, iErr) + call mpas_get_time(currentTime, dateTimeString=currentTimeStamp) + + xtime=currentTimeStamp + call mpas_stream_mgr_reset_alarms(domain%streamManager, streamID='shortwave_forcing_data_init', & + direction=MPAS_STREAM_OUTPUT, ierr=ierr) + +end subroutine ocn_init_setup_global_ocean_interpolate_swData!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_read_windstress +! +!> \brief Read the windstress IC file +!> \author Doug Jacobsen +!> \date 03/07/2014 +!> \details +!> This routine reads the windstress IC file, including latitude and longitude +!> information for windstress data. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_read_windstress(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (MPAS_Stream_type) :: windstressStream + + character (len=StrKIND), pointer :: config_global_ocean_windstress_file, config_global_ocean_windstress_lat_varname, & + config_global_ocean_windstress_nlat_dimname, & + config_global_ocean_windstress_lon_varname, & + config_global_ocean_windstress_nlon_dimname, & + config_global_ocean_windstress_zonal_varname, & + config_global_ocean_windstress_meridional_varname + + logical, pointer :: config_global_ocean_windstress_latlon_degrees + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_file', config_global_ocean_windstress_file) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_lat_varname', & + config_global_ocean_windstress_lat_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_nlat_dimname', & + config_global_ocean_windstress_nlat_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_lon_varname', & + config_global_ocean_windstress_lon_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_nlon_dimname', & + config_global_ocean_windstress_nlon_dimname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_zonal_varname', & + config_global_ocean_windstress_zonal_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_meridional_varname', & + config_global_ocean_windstress_meridional_varname) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_latlon_degrees', & + config_global_ocean_windstress_latlon_degrees) + + ! Define stream for depth levels + call MPAS_createStream(windstressStream, domain % iocontext, config_global_ocean_windstress_file, MPAS_IO_NETCDF, & + MPAS_IO_READ, ierr=iErr) + + ! Setup windLat, windLon, and windIC fields for stream to be read in + windLat % fieldName = trim(config_global_ocean_windstress_lat_varname) + windLat % dimSizes(1) = nLatWind + windLat % dimNames(1) = trim(config_global_ocean_windstress_nlat_dimname) + windLat % isVarArray = .false. + windLat % isPersistent = .true. + windLat % isActive = .true. + windLat % hasTimeDimension = .false. + windLat % block => domain % blocklist + allocate(windLat % attLists(1)) + allocate(windLat % array(nLatWind)) + + windLon % fieldName = trim(config_global_ocean_windstress_lon_varname) + windLon % dimSizes(1) = nLonWind + windLon % dimNames(1) = trim(config_global_ocean_windstress_nlon_dimname) + windLon % isVarArray = .false. + windLon % isPersistent = .true. + windLon % isActive = .true. + windLon % hasTimeDimension = .false. + windLon % block => domain % blocklist + allocate(windLon % attLists(1)) + allocate(windLon % array(nLonWind)) + + zonalWindIC % fieldName = trim(config_global_ocean_windstress_zonal_varname) + zonalWindIC % dimSizes(1) = nLonWind + zonalWindIC % dimSizes(2) = nLatWind + zonalWindIC % dimNames(1) = trim(config_global_ocean_windstress_nlon_dimname) + zonalWindIC % dimNames(2) = trim(config_global_ocean_windstress_nlat_dimname) + zonalWindIC % isVarArray = .false. + zonalWindIC % isPersistent = .true. + zonalWindIC % isActive = .true. + zonalWindIC % hasTimeDimension = .false. + zonalWindIC % block => domain % blocklist + allocate(zonalWindIC % attLists(1)) + allocate(zonalWindIC % array(nLonWind, nLatWind)) + + meridionalWindIC % fieldName = trim(config_global_ocean_windstress_meridional_varname) + meridionalWindIC % dimSizes(1) = nLonWind + meridionalWindIC % dimSizes(2) = nLatWind + meridionalWindIC % dimNames(1) = trim(config_global_ocean_windstress_nlon_dimname) + meridionalWindIC % dimNames(2) = trim(config_global_ocean_windstress_nlat_dimname) + meridionalWindIC % isVarArray = .false. + meridionalWindIC % isPersistent = .true. + meridionalWindIC % isActive = .true. + meridionalWindIC % hasTimeDimension = .false. + meridionalWindIC % block => domain % blocklist + allocate(meridionalWindIC % attLists(1)) + allocate(meridionalWindIC % array(nLonWind, nLatWind)) + + ! Add windLat, windLon, and windIC fields to stream + call MPAS_streamAddField(windstressStream, windLat, iErr) + call MPAS_streamAddField(windstressStream, windLon, iErr) + call MPAS_streamAddField(windstressStream, zonalWindIC, iErr) + call MPAS_streamAddField(windstressStream, meridionalWindIC, iErr) + + ! Read stream + call MPAS_readStream(windstressStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(windstressStream) + + if (config_global_ocean_windstress_latlon_degrees) then + windLat % array(:) = windLat % array(:) * pii / 180.0_RKIND + windLon % array(:) = windLon % array(:) * pii / 180.0_RKIND + end if + + end subroutine ocn_init_setup_global_ocean_read_windstress!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_global_ocean_interpolate_windstress +! +!> \brief Interpolate the windstress IC to MPAS mesh +!> \author Doug Jacobsen +!> \date 03/07/2014 +!> \details +!> This routine interpolates windstress data to the MPAS mesh. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_global_ocean_interpolate_windstress(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, forcingPool + + real (kind=RKIND), dimension(:), pointer :: latCell, lonCell, windStressZonal, windStressMeridional + + integer, pointer :: nCells + + logical, pointer :: config_use_bulk_wind_stress + character (len=StrKIND), pointer :: config_global_ocean_windstress_method + real (kind=RKIND), pointer :: config_global_ocean_windstress_conversion_factor + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_use_bulk_wind_stress', config_use_bulk_wind_stress) + if (.not.config_use_bulk_wind_stress) then + call mpas_log_write( ' WARNING: wind stress not initialized because config_use_bulk_wind_stress = .false.') + return + endif + + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_method', config_global_ocean_windstress_method) + call mpas_pool_get_config(domain % configs, 'config_global_ocean_windstress_conversion_factor', & + config_global_ocean_windstress_conversion_factor) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + + call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) + call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional) + + if (config_global_ocean_windstress_method .eq. "nearest_neighbor") then + call ocn_init_interpolation_nearest_horiz(windLon % array, windLat % array, & + zonalWindIC % array, nLonWind, nLatWind, & + lonCell, latCell, windStressZonal, nCells, & + inXPeriod = 2.0_RKIND * pii) + + call ocn_init_interpolation_nearest_horiz(windLon % array, windLat % array, & + meridionalWindIC % array, nLonWind, nLatWind, & + lonCell, latCell, windStressMeridional, nCells, & + inXPeriod = 2.0_RKIND * pii) + + elseif (config_global_ocean_windstress_method .eq. "bilinear_interpolation") then + call ocn_init_interpolation_bilinear_horiz(windLon % array, windLat % array, & + zonalWindIC % array, nLonWind, nLatWind, & + lonCell, latCell, windStressZonal, nCells, & + inXPeriod = 2.0_RKIND*pii) + + call ocn_init_interpolation_bilinear_horiz(windLon % array, windLat % array, & + meridionalWindIC % array, nLonWind, nLatWind, & + lonCell, latCell, windStressMeridional, nCells, & + inXPeriod = 2.0_RKIND*pii) + + else + call mpas_log_write( 'Invalid choice of config_global_ocean_windstress_method.', MPAS_LOG_CRIT) + iErr = 1 + call mpas_dmpar_finalize(domain % dminfo) + endif + + windStressZonal(:) = windStressZonal(:) * config_global_ocean_windstress_conversion_factor + windStressMeridional(:) = windStressMeridional(:) * config_global_ocean_windstress_conversion_factor + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_global_ocean_interpolate_windstress!}}} + +!*********************************************************************** +! +! routine ocn_init_global_ocean_destroy_tracer_fields +! +!> \brief Tracer field cleanup routine +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine destroys the fields that were created to hold tracer +!> initial condition information +! +!----------------------------------------------------------------------- + + subroutine ocn_init_global_ocean_destroy_tracer_fields()!{{{ + deallocate(tracerLat % array) + deallocate(tracerLon % array) + end subroutine ocn_init_global_ocean_destroy_tracer_fields!}}} + +!*********************************************************************** +! +! routine ocn_init_global_ocean_destroy_topo_fields +! +!> \brief Topography field cleanup routine +!> \author Doug Jacobsen +!> \date 03/07/2014 +!> \details +!> This routine destroys the fields that were created to hold topography +!> initial condition information +! +!----------------------------------------------------------------------- + + subroutine ocn_init_global_ocean_destroy_topo_fields()!{{{ + deallocate(topoIC % array) + if(associated(oceanFracIC % array)) deallocate(oceanFracIC % array) + deallocate(topoLat % array) + deallocate(topoLon % array) + end subroutine ocn_init_global_ocean_destroy_topo_fields!}}} + +!*********************************************************************** +! +! routine ocn_init_global_ocean_destroy_land_ice_topography_fields +! +!> \brief Topography field cleanup routine +!> \author Jeremy Fyke, Xylar Asay-Davis, Mark Petersen +!> \date 06/23/2015 +!> \details +!> This routine destroys the fields created to hold land ice topography +!> initial condition information +! +!----------------------------------------------------------------------- + + subroutine ocn_init_global_ocean_destroy_land_ice_topography_fields()!{{{ + deallocate(landIceThkIC % array) + deallocate(landIceDraftIC % array) + deallocate(landIceThkLat % array) + deallocate(landIceThkLon % array) + end subroutine ocn_init_global_ocean_destroy_land_ice_topography_fields!}}} + +!*********************************************************************** +! +! routine ocn_init_global_ocean_destroy_windstress_fields +! +!> \brief Windstress field cleanup routine +!> \author Doug Jacobsen +!> \date 03/07/2014 +!> \details +!> This routine destroys the fields that were created to hold windstress +!> initial condition information +! +!----------------------------------------------------------------------- + + subroutine ocn_init_global_ocean_destroy_windstress_fields()!{{{ + deallocate(zonalWindIC % array) + deallocate(meridionalWindIC % array) + deallocate(windLat % array) + deallocate(windLon % array) + end subroutine ocn_init_global_ocean_destroy_windstress_fields!}}} + +!*********************************************************************** +! +! routine ocn_init_global_ocean_destroy_swData_fields +! +!> \brief penetrating shortwave data fields cleanup routine +!> \author Luke Van Roekel +!> \date 11/11/2015 +!> \details +!> This routine destroys the fields that were created to hold penetrating sw radiation data +!> initial condition information +! +!----------------------------------------------------------------------- + + subroutine ocn_init_global_ocean_destroy_swData_fields()!{{{ + deallocate(chlorophyllIC % array) + deallocate(zenithAngleIC % array) + deallocate(clearSKYIC % array) + end subroutine ocn_init_global_ocean_destroy_swData_fields!}}} + +!*********************************************************************** +! +! routine ocn_init_global_ocean_destroy_ecosys_fields +! +!> \brief Ecosys field cleanup routine +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine destroys the fields that were created to hold ecosys +!> initial condition information +! +!----------------------------------------------------------------------- + + subroutine ocn_init_global_ocean_destroy_ecosys_fields()!{{{ + deallocate(tracerLat % array) + deallocate(tracerLon % array) + end subroutine ocn_init_global_ocean_destroy_ecosys_fields!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_global_ocean +! +!> \brief Validation for global ocean test case +!> \author Doug Jacobsen +!> \date 03/04/2014 +!> \details +!> This routine validates the configuration options for the global ocean test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_global_ocean(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool, packagePool + type (mpas_io_context_type), intent(inout), target :: iocontext + integer, intent(out) :: iErr + + type (mpas_io_context_type), pointer :: iocontext_ptr + type (MPAS_IO_Handle_type) :: inputFile + character (len=StrKIND), pointer :: config_init_configuration, & + config_global_ocean_depth_file, & + config_global_ocean_depth_dimname, & + config_global_ocean_temperature_file, & + config_global_ocean_salinity_file, & + config_global_ocean_tracer_nlat_dimname, & + config_global_ocean_tracer_nlon_dimname, & + config_global_ocean_tracer_ndepth_dimname, & + config_global_ocean_topography_file, & + config_global_ocean_topography_nlat_dimname, & + config_global_ocean_topography_nlon_dimname, & + config_global_ocean_windstress_file, & + config_global_ocean_windstress_nlat_dimname, & + config_global_ocean_windstress_nlon_dimname, & + config_global_ocean_land_ice_topo_file, & + config_global_ocean_land_ice_topo_nlat_dimname, & + config_global_ocean_land_ice_topo_nlon_dimname, & + config_global_ocean_swData_file, & + config_global_ocean_swData_nlon_dimname, & + config_global_ocean_swData_nlat_dimname, & + config_global_ocean_ecosys_file, & + config_global_ocean_ecosys_nlat_dimname, & + config_global_ocean_ecosys_nlon_dimname, & + config_global_ocean_ecosys_ndepth_dimname + + integer, pointer :: config_vert_levels, config_global_ocean_tracer_vert_levels, & + config_global_ocean_ecosys_vert_levels + logical, pointer :: config_use_ecosysTracers + logical, pointer :: landIceInitActive, config_global_ocean_depress_by_land_ice + logical, pointer :: criticalPassagesActive, config_global_ocean_deepen_critical_passages + + iocontext_ptr => iocontext + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('global_ocean')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + + call mpas_pool_get_config(configPool, 'config_global_ocean_depth_file', & + config_global_ocean_depth_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_depth_dimname', & + config_global_ocean_depth_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_temperature_file', & + config_global_ocean_temperature_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_salinity_file', & + config_global_ocean_salinity_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_tracer_nlat_dimname', & + config_global_ocean_tracer_nlat_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_tracer_nlon_dimname', & + config_global_ocean_tracer_nlon_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_tracer_ndepth_dimname', & + config_global_ocean_tracer_ndepth_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_tracer_vert_levels', & + config_global_ocean_tracer_vert_levels) + call mpas_pool_get_config(configPool, 'config_global_ocean_topography_file', & + config_global_ocean_topography_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_topography_nlat_dimname', & + config_global_ocean_topography_nlat_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_topography_nlon_dimname', & + config_global_ocean_topography_nlon_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_depress_by_land_ice', & + config_global_ocean_depress_by_land_ice) + call mpas_pool_get_config(configPool, 'config_use_ecosysTracers', & + config_use_ecosysTracers) + call mpas_pool_get_config(configPool, 'config_global_ocean_land_ice_topo_file', & + config_global_ocean_land_ice_topo_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_land_ice_topo_nlat_dimname', & + config_global_ocean_land_ice_topo_nlat_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_land_ice_topo_nlon_dimname', & + config_global_ocean_land_ice_topo_nlon_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_deepen_critical_passages', & + config_global_ocean_deepen_critical_passages) + call mpas_pool_get_config(configPool, 'config_global_ocean_windstress_file', & + config_global_ocean_windstress_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_windstress_nlat_dimname', & + config_global_ocean_windstress_nlat_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_windstress_nlon_dimname', & + config_global_ocean_windstress_nlon_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_swData_file', & + config_global_ocean_swData_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_swData_nlat_dimname', & + config_global_ocean_swData_nlat_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_swData_nlon_dimname', & + config_global_ocean_swData_nlon_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_ecosys_file', & + config_global_ocean_ecosys_file) + call mpas_pool_get_config(configPool, 'config_global_ocean_ecosys_nlat_dimname', & + config_global_ocean_ecosys_nlat_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_ecosys_nlon_dimname', & + config_global_ocean_ecosys_nlon_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_ecosys_ndepth_dimname', & + config_global_ocean_ecosys_ndepth_dimname) + call mpas_pool_get_config(configPool, 'config_global_ocean_ecosys_vert_levels', & + config_global_ocean_ecosys_vert_levels) + + call mpas_pool_get_package(packagePool, 'landIceInitActive', landIceInitActive) + if ( config_global_ocean_depress_by_land_ice) then + landIceInitActive = .true. + end if + + call mpas_pool_get_package(packagePool, 'criticalPassagesActive', criticalPassagesActive) + if ( config_global_ocean_deepen_critical_passages) then + criticalPassagesActive = .true. + end if + + if (trim(config_global_ocean_depth_file) == 'none') then + call mpas_log_write( 'Validation failed for global ocean. ' & + // 'Invalid filename for config_global_ocean_depth_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + inputFile = MPAS_io_open(config_global_ocean_depth_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + if (iErr .ne. 0) then + call mpas_log_write( 'could not open file '// trim(config_global_ocean_depth_file), MPAS_LOG_CRIT) + return + end if + + call MPAS_io_inq_dim(inputFile, config_global_ocean_depth_dimname, nDepthOutput, iErr) + + call MPAS_io_close(inputFile, iErr) + + if (trim(config_global_ocean_temperature_file) == 'none') then + call mpas_log_write( 'Validation failed for global ocean. ' & + // 'Invalid filename for config_global_ocean_temperature_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + if (trim(config_global_ocean_salinity_file) == 'none') then + call mpas_log_write( 'Validation failed for global ocean. ' & + // 'Invalid filename for config_global_ocean_salinity_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + inputFile = MPAS_io_open(config_global_ocean_temperature_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + if (iErr .ne. 0) then + call mpas_log_write( 'could not open file '// trim(config_global_ocean_temperature_file), MPAS_LOG_CRIT) + return + end if + + call MPAS_io_inq_dim(inputFile, config_global_ocean_tracer_nlat_dimname, nLatTracer, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_tracer_nlon_dimname, nLonTracer, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_tracer_ndepth_dimname, nDepthTracer, iErr) + + call MPAS_io_close(inputFile, iErr) + + if (config_global_ocean_tracer_vert_levels <= 0 .and. nDepthTracer > 0) then + config_global_ocean_tracer_vert_levels = nDepthTracer + else if(config_global_ocean_tracer_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for global ocean. ' & + // 'Value of config_global_ocean_tracer_vert_levels=-1 ' & + // 'but nDepthTracer was not correctly read from input file.', MPAS_LOG_CRIT) + iErr = 1 + end if + + if (trim(config_global_ocean_windstress_file) == 'none') then + call mpas_log_write( 'Validation failed for global ocean. ' & + // 'Invalid filename for config_global_ocean_windstress_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + inputFile = MPAS_io_open(config_global_ocean_swData_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + + call MPAS_io_inq_dim(inputFile, config_global_ocean_swData_nlat_dimname, nLatSW, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_swData_nlon_dimname, nLonSW, iErr) + + call MPAS_io_close(inputFile, iErr) + + if (trim(config_global_ocean_topography_file) == 'none') then + call mpas_log_write( 'Validation failed for global ocean. ' & + // 'Invalid filename for config_global_ocean_topography_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + inputFile = MPAS_io_open(config_global_ocean_topography_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + if (iErr .ne. 0) then + call mpas_log_write( 'could not open file '// trim(config_global_ocean_topography_file), MPAS_LOG_CRIT) + return + end if + + call MPAS_io_inq_dim(inputFile, config_global_ocean_topography_nlat_dimname, nLatTopo, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_topography_nlon_dimname, nLonTopo, iErr) + + call MPAS_io_close(inputFile, iErr) + + inputFile = MPAS_io_open(config_global_ocean_windstress_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + if (iErr .ne. 0) then + call mpas_log_write( 'could not open file '// trim(config_global_ocean_windstress_file), MPAS_LOG_CRIT) + return + end if + + call MPAS_io_inq_dim(inputFile, config_global_ocean_windstress_nlat_dimname, nLatWind, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_windstress_nlon_dimname, nLonWind, iErr) + + call MPAS_io_close(inputFile, iErr) + + if (config_vert_levels <= 0 .and. nDepthOutput > 0) then + config_vert_levels = nDepthOutput + else if(config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for global ocean. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + if ( config_use_ecosysTracers ) then + if (trim(config_global_ocean_ecosys_file) == 'none') then + call mpas_log_write( & + 'Validation failed for global ocean. Invalid filename for config_global_ocean_windstress_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + inputFile = MPAS_io_open(config_global_ocean_ecosys_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + + call MPAS_io_inq_dim(inputFile, config_global_ocean_ecosys_nlat_dimname, nLatEcosys, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_ecosys_nlon_dimname, nLonEcosys, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_ecosys_ndepth_dimname, nDepthEcosys, iErr) + + call MPAS_io_close(inputFile, iErr) + + if (config_global_ocean_ecosys_vert_levels <= 0 .and. nDepthEcosys > 0) then + config_global_ocean_ecosys_vert_levels = nDepthEcosys + else if(config_global_ocean_ecosys_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for global ocean. ' & + // 'Value of config_global_ocean_ecosys_vert_levels=-1, ' & + // 'but nDepthEcosys was not correctly read from input file.', MPAS_LOG_CRIT) + iErr = 1 + end if + + end if + + if ( config_global_ocean_depress_by_land_ice) then + if (trim(config_global_ocean_land_ice_topo_file) == 'none') then + call mpas_log_write( 'Validation failed for global ocean. '// & + 'Invalid filename for config_global_ocean_land_ice_topo_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + inputFile = MPAS_io_open(config_global_ocean_land_ice_topo_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + if (iErr .ne. 0) then + call mpas_log_write( 'could not open file '// trim(config_global_ocean_land_ice_topo_file), MPAS_LOG_CRIT) + return + end if + + call MPAS_io_inq_dim(inputFile, config_global_ocean_land_ice_topo_nlat_dimname, nLatLandIceThk, iErr) + call MPAS_io_inq_dim(inputFile, config_global_ocean_land_ice_topo_nlon_dimname, nLonLandIceThk, iErr) + + call MPAS_io_close(inputFile, iErr) + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_global_ocean!}}} + +!*********************************************************************** + +end module ocn_init_global_ocean + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_internal_waves.F b/src/core_ocean/mode_init/mpas_ocn_init_internal_waves.F new file mode 100644 index 0000000000..77a49551f7 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_internal_waves.F @@ -0,0 +1,389 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_internal_waves +! +!> \brief MPAS ocean initialize case -- Internal waves +!> \author Doug Jacobsen +!> \date 02/18/2014 +!> \details +!> This module contains the routines for initializing the +!> the internal waves test case +! +!----------------------------------------------------------------------- + +module ocn_init_internal_waves + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_dmpar + + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_internal_waves, & + ocn_init_validate_internal_waves + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_internal_waves +! +!> \brief Setup for internal waves test case +!> \author Doug Jacobsen +!> \date 02/19/2014 +!> \details +!> This routine sets up the initial conditions for the internal waves test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_internal_waves(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + ! Define pool pointers + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, statePool, tracersPool + + ! Define dimension pointers + integer, pointer :: nVertLevels, nVertLevelsP1, nCells, nEdges, nVertices + integer, pointer :: nCellsSolve, nEdgesSolve, index_temperature, index_salinity, index_tracer1 + + ! Define array pointers + integer, dimension(:), pointer :: maxLevelCell + + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, bottomDepth, dcEdge + real (kind=RKIND), dimension(:), pointer :: refBottomDepth, refZMid, vertCoordMovementWeights + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, yMidGlobal, xMinGlobal, xMaxGlobal, dcEdgeMinGlobal + real (kind=RKIND) :: temperature, yOffset, perturbationWidth + + ! Define config pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid, config_internal_waves_layer_type + logical, pointer :: config_internal_waves_use_distances + real (kind=RKIND), pointer :: config_internal_waves_amplitude_width_frac, config_internal_waves_amplitude_width_dist + real (kind=RKIND), pointer :: config_internal_waves_bottom_depth, config_internal_waves_bottom_temperature + real (kind=RKIND), pointer :: config_internal_waves_surface_temperature, config_internal_waves_temperature_difference + real (kind=RKIND), pointer :: config_internal_waves_salinity, config_internal_waves_isopycnal_displacement + + type (block_type), pointer :: block_ptr + + integer :: iCell, k + + real (kind=RKIND) :: deltaTemperature + real (kind=RKIND), dimension(:), pointer :: zTop, refTemperature, refTemperatureTop, refZTop + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('internal_waves')) return + + ! Initalize min/max values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgEMin = 1.0E10_RKIND + + ! Define locations of layer interfaces + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(domain % configs, 'config_vertical_grid', config_vertical_grid) + allocate( interfaceLocations( nVertLevelsP1 ) ) + + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + call mpas_pool_get_config(domain % configs, 'config_internal_waves_use_distances', config_internal_waves_use_distances) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_amplitude_width_frac', & + config_internal_waves_amplitude_width_frac) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_amplitude_width_dist', & + config_internal_waves_amplitude_width_dist) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_bottom_depth', config_internal_waves_bottom_depth) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_bottom_temperature', & + config_internal_waves_bottom_temperature) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_surface_temperature', & + config_internal_waves_surface_temperature) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_temperature_difference', & + config_internal_waves_temperature_difference) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_salinity', config_internal_waves_salinity) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_isopycnal_displacement', & + config_internal_waves_isopycnal_displacement) + call mpas_pool_get_config(domain % configs, 'config_internal_waves_layer_type', config_internal_waves_layer_type) + + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + xMin = min( xMin, minval(xCell(1:nCellsSolve))) + xMax = max( xMax, maxval(xCell(1:nCellsSolve))) + yMin = min( yMin, minval(yCell(1:nCellsSolve))) + yMax = max( yMax, maxval(yCell(1:nCellsSolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + + block_ptr => block_ptr % next + end do + + ! Determine global min and max values. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + yMidGlobal = (yMinGlobal + yMaxGlobal) * 0.5_RKIND + if(config_internal_waves_use_distances) then + perturbationWidth = config_internal_waves_amplitude_width_dist + else + perturbationWidth = (yMaxGlobal - yMinGlobal) * config_internal_waves_amplitude_width_frac + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal,iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal,iErr) + + allocate(zTop(nVertLevels+1), refTemperature(nVertLevels), refTemperatureTop(nVertLevels+1), refZTop(nVertLevels+1)) + + ! Set refBottomDepth and refBottomDepthTopOfCell + do k = 1, nVertLevels + refBottomDepth(k) = config_internal_waves_bottom_depth * interfaceLocations(k+1) + refZMid(k) = -0.5_RKIND * config_internal_waves_bottom_depth * (interfaceLocations(k) + interfaceLocations(k+1)) + end do + + if ( trim(config_internal_waves_layer_type) == 'isopycnal' ) then + + refTemperatureTop(1) = config_internal_waves_surface_temperature + refTemperatureTop(nVertLevels+1) = config_internal_waves_bottom_temperature + deltaTemperature = (config_internal_waves_surface_temperature - config_internal_waves_bottom_temperature)/nVertLevels + refTemperature(1) = config_internal_waves_surface_temperature - deltaTemperature/2.0_RKIND + refZTop(1) = 0.0_RKIND + do k = 2, nVertLevels + refTemperatureTop(k) = refTemperatureTop(1) - (k-1)*deltaTemperature + refTemperature(k) = refTemperature(1) - (k-1)*deltaTemperature + refZTop(k) = refZTop(k-1) - config_internal_waves_bottom_depth / nVertLevels + end do + + endif + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + do iCell = 1, nCellsSolve + + ! Set debug tracer + if ( associated(debugTracers) ) then + do k = 1, nVertLevels + debugTracers(index_tracer1, k, iCell) = 1.0_RKIND + enddo + end if + + if ( trim(config_internal_waves_layer_type) == 'z-level' ) then + + ! Set stratified temperature + if ( associated(activeTracers) ) then + do k = nVertLevels, 1, -1 + temperature = config_internal_waves_bottom_temperature & + + (config_internal_waves_surface_temperature - config_internal_waves_bottom_temperature) & + * ( (refZMid(k) - refZMid(nVertLevels)) / (-refZMid(nVertLevels) )) + activeTracers(index_temperature, k, iCell) = temperature + end do + + if ( abs(yCell(iCell) - yMidGlobal) < perturbationWidth ) then + ! If cell is in the southern half, outside the sin width, subtract temperature difference + do k = 2, nVertLevels + temperature = -config_internal_waves_temperature_difference * cos(0.5_RKIND * pii * (yCell(iCell) & + - yMidGlobal) / perturbationWidth) * sin ( pii * refBottomDepth(k-1) & + / refBottomDepth(nVertLevels-1) ) + + activeTracers(index_temperature, k, iCell) = activeTracers(index_temperature, k, iCell) + temperature + end do + end if + end if + + ! Set layerThickness and restingThickness + do k = 1, nVertLevels + layerThickness(k, iCell) = config_internal_waves_bottom_depth * ( interfaceLocations(k+1) & + - interfaceLocations(k) ) + restingThickness(k, iCell) = config_internal_waves_bottom_depth * ( interfaceLocations(k+1) & + - interfaceLocations(k) ) + end do + + else if ( trim(config_internal_waves_layer_type) == 'isopycnal' ) then + + ! Set stratified temperature + if ( associated(activeTracers) ) then + activeTracers(index_temperature, :, iCell) = refTemperature(:) + end if + + ! Set layerThickness + if ( abs(yCell(iCell) - yMidGlobal) < perturbationWidth) then + ! If cell is in the southern half, outside the sin width, subtract temperature difference + zTop(1) = 0.0_RKIND + do k = 2, nVertLevels + zTop(k) = refZTop(k) + & + config_internal_waves_isopycnal_displacement * sin(pii * (k-1) / (nVertLevels+4)) & + * cos(0.5_RKIND * pii * (yCell(iCell) - yMidGlobal) / perturbationWidth) + end do + zTop(nVertLevels+1) = -config_internal_waves_bottom_depth + + do k = 1, nVertLevels + layerThickness(k, iCell) = zTop(k) - zTop(k+1) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + else + layerThickness(:, iCell) = config_internal_waves_bottom_depth / nVertLevels + restingThickness(:, iCell) = layerThickness(:, iCell) + end if + else + call mpas_log_write('MPAS-ocean: Error: wrong choice of config_internal_waves_layer_type') + endif + + ! Set salinity + if ( associated(activeTracers) ) then + activeTracers(index_salinity, :, iCell) = config_internal_waves_salinity + end if + + ! Set bottomDepth + bottomDepth(iCell) = config_internal_waves_bottom_depth + + ! Set maxLevelCell + maxLevelCell(iCell) = nVertLevels + end do + + deallocate(zTop, refTemperature, refTemperatureTop, refZTop) + + block_ptr => block_ptr % next + end do + + + + deallocate(interfaceLocations) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_internal_waves!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_internal_waves +! +!> \brief Validation for internal waves test case +!> \author Doug Jacobsen +!> \date 02/20/2014 +!> \details +!> This routine validates the configuration options for the internal waves test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_internal_waves(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool + type (mpas_pool_type), intent(inout) :: packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_internal_waves_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('internal_waves')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_internal_waves_vert_levels', config_internal_waves_vert_levels) + + if(config_vert_levels <= 0 .and. config_internal_waves_vert_levels > 0) then + config_vert_levels = config_internal_waves_vert_levels + else if(config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for internal waves. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_internal_waves!}}} + +!*********************************************************************** + +end module ocn_init_internal_waves + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_interpolation.F b/src/core_ocean/mode_init/mpas_ocn_init_interpolation.F new file mode 100644 index 0000000000..1b8b4a5f97 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_interpolation.F @@ -0,0 +1,534 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_vertical_grids +! +!> \brief MPAS ocean vertical grid generator +!> \author Xylar Asay-Davis +!> \date 10/30/2015 +!> \details +!> This module contains the routines for generating +!> vertical grids. +! +!----------------------------------------------------------------------- +module ocn_init_interpolation + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_timer + + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_interpolation_linear_vert, & + ocn_init_interpolation_nearest_horiz, & + ocn_init_interpolation_bilinear_horiz + + interface ocn_init_interpolation_nearest_horiz + module procedure ocn_init_interpolation_nearest_horiz_2D + module procedure ocn_init_interpolation_nearest_horiz_3D + end interface + + interface ocn_init_interpolation_bilinear_horiz + module procedure ocn_init_interpolation_bilinear_horiz_2D + module procedure ocn_init_interpolation_bilinear_horiz_3D + end interface + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_interpolation_linear_vert +! +!> \brief linearly interpolate a field in the vertical +!> \author Xylar Asay-Davis +!> \date 10/12/2015 +!> \details +!> Perform vertical linear interpolation of a field from a reference field +!> with data located at inZ to new locations outZ. Bu default, out-of-range values of +!> outZ are clamped to the nearest in-range value. That is, wherever +!> outZ > inZ(1), outField = inField(1); wherever outZ < inZ(inNVertLevels), +!> outFiled = inField(inNVertLevels). If the optional extrapolate argument +!> is present and set to .true., linear extrapolation is perfored outside the +!> bounds of inZ. + +!----------------------------------------------------------------------- + + subroutine ocn_init_interpolation_linear_vert(inZ, inField, inNVertLevels, outZ, outField, outNVertLevels, extrapolate)!{{{ + + !-------------------------------------------------------------------- + + integer, intent(in) :: inNVertLevels, outNVertLevels + real (kind=RKIND), dimension(inNVertLevels), intent(in) :: inZ, inField + real (kind=RKIND), dimension(outNVertLevels), intent(in) :: outZ + real (kind=RKIND), dimension(outNVertLevels), intent(out) :: outField + logical, optional, intent(in) :: extrapolate + + ! Define variable pointers + integer :: outK, inK + real (kind=RKIND) :: z, frac + + logical :: doExtrapolate + + if(outNVertLevels <= 0) return + + ! handle single level of input data as a special case + if(inNVertLevels == 1) then + outField(1:outNVertLevels) = inField(1) + return + end if + + doExtrapolate = .false. + if(present(extrapolate)) then + doExtrapolate = extrapolate + end if + + if(doExtrapolate) then + do outK=1,outNVertLevels + z = outZ(outK) + if(z >= inZ(1)) then + inK = 1 + else if(z <= inZ(inNVertLevels)) then + inK = inNVertLevels-1 + else + do inK = 1, inNVertLevels-1 + if(z >= inZ(inK+1)) exit + end do + end if + ! frac can be outside [0,1) if we're extrapolating + frac = (z - inZ(inK))/(inZ(inK+1) - inZ(inK)) + outField(outK) = (1.0_RKIND - frac)*inField(inK) + frac*inField(inK+1) + end do + else ! not extrapolating + do outK=1,outNVertLevels + z = outZ(outK) + if(z >= inZ(1)) then + inK = 1 + frac = 0.0_RKIND + else if(z <= inZ(inNVertLevels)) then + inK = inNVertLevels-1 + frac = 1.0_RKIND + else + do inK = 1, inNVertLevels-1 + if(z >= inZ(inK+1)) exit + end do + ! frac should always be inside [0,1) + frac = (z - inZ(inK))/(inZ(inK+1) - inZ(inK)) + end if + outField(outK) = (1.0_RKIND - frac)*inField(inK) + frac*inField(inK+1) + end do + end if + + + !-------------------------------------------------------------------- + + end subroutine ocn_init_interpolation_linear_vert!}}} + +!*********************************************************************** +! +! routine ocn_init_interpolation_nearest_horiz_2D +! +!> \brief nearest-neighbor interpolation in horiz. +!> \author Xylar Asay-Davis +!> \date 10/30/2015 +!> \details +!> Perform horizontal nearest-neighbor interpolation of a field from +!> values on a logically rectangular grid. + +!----------------------------------------------------------------------- + + subroutine ocn_init_interpolation_nearest_horiz_2D(inX, inY, inField, inNx, inNy, & + outX, outY, outField, outN, & + inXPeriod, inYPeriod)!{{{ + + !-------------------------------------------------------------------- + + integer, intent(in) :: inNx, inNy, outN + real (kind=RKIND), dimension(inNx), intent(in) :: inX + real (kind=RKIND), dimension(inNy), intent(in) :: inY + real (kind=RKIND), dimension(inNx,inNy), intent(in) :: inField + real (kind=RKIND), dimension(outN), intent(in) :: outX, outY + real (kind=RKIND), dimension(outN), intent(out) :: outField + real (kind=RKIND), intent(in), optional :: inXPeriod, inYPeriod + + ! Define variable pointers + integer :: outIndex, xSearch, ySearch, searchIdx + real (kind=RKIND) :: currentX, currentY, minDist, dist + + do outIndex = 1, outN + currentX = outX(outIndex) + if(present(inXPeriod)) then + ! put currentX in the range of [inX(1),inX(1)+inXPeriod) + currentX = mod(currentX-inX(1),inXPeriod) + inX(1) + end if + currentY = outY(outIndex) + if(present(inYPeriod)) then + ! put currentY in the range of [inY(1),inY(1)+inYPeriod) + currentY = mod(currentY-inY(1),inYPeriod) + inY(1) + end if + + xSearch = 1 + minDist = 1e34 + do searchIdx = 1, inNx + dist = abs(currentX - inX(searchIdx)) + if (dist < minDist) then + minDist = dist + xSearch = searchIdx + end if + end do + + ySearch = 1 + minDist = 1e34 + do searchIdx = 1, inNy + dist = abs(currentY - inY(searchIdx)) + if (dist < minDist) then + minDist = dist + ySearch = searchIdx + end if + end do + + outField(outIndex) = inField(xSearch, ySearch) + + end do + + !-------------------------------------------------------------------- + + end subroutine ocn_init_interpolation_nearest_horiz_2D!}}} + +!*********************************************************************** +! +! routine ocn_init_interpolation_nearest_horiz_3D +! +!> \brief nearest-neighbor interpolation in horiz. +!> \author Xylar Asay-Davis +!> \date 10/30/2015 +!> \details +!> Perform horizontal nearest-neighbor interpolation of a field from +!> values on a logically rectangular grid. + +!----------------------------------------------------------------------- + + subroutine ocn_init_interpolation_nearest_horiz_3D(inX, inY, inField, inNx, inNy, & + outX, outY, outField, outN, & + inXPeriod, inYPeriod)!{{{ + + !-------------------------------------------------------------------- + + integer, intent(in) :: inNx, inNy, outN + real (kind=RKIND), dimension(inNx), intent(in) :: inX + real (kind=RKIND), dimension(inNy), intent(in) :: inY + real (kind=RKIND), dimension(:,:,:), intent(in) :: inField + real (kind=RKIND), dimension(outN), intent(in) :: outX, outY + real (kind=RKIND), dimension(:,:), intent(out) :: outField + real (kind=RKIND), intent(in), optional :: inXPeriod, inYPeriod + + ! Define variable pointers + integer :: outIndex, xSearch, ySearch, searchIdx + real (kind=RKIND) :: currentX, currentY, minDist, dist + + do outIndex = 1, outN + currentX = outX(outIndex) + if(present(inXPeriod)) then + ! put currentX in the range of [inX(1),inX(1)+inXPeriod) + currentX = mod(currentX-inX(1),inXPeriod) + inX(1) + end if + currentY = outY(outIndex) + if(present(inYPeriod)) then + ! put currentY in the range of [inY(1),inY(1)+inYPeriod) + currentY = mod(currentY-inY(1),inYPeriod) + inY(1) + end if + + xSearch = 1 + minDist = 1e34 + do searchIdx = 1, inNx + dist = abs(currentX - inX(searchIdx)) + if (dist < minDist) then + minDist = dist + xSearch = searchIdx + end if + end do + + ySearch = 1 + minDist = 1e34 + do searchIdx = 1, inNy + dist = abs(currentY - inY(searchIdx)) + if (dist < minDist) then + minDist = dist + ySearch = searchIdx + end if + end do + + outField(:,outIndex) = inField(xSearch, ySearch,:) + + end do + + !-------------------------------------------------------------------- + + end subroutine ocn_init_interpolation_nearest_horiz_3D!}}} + +!*********************************************************************** +! +! routine ocn_init_interpolation_bilinear_horiz_2D +! +!> \brief bilinear interpolation in horiz. +!> \author Xylar Asay-Davis +!> \date 10/30/2015 +!> \details +!> Perform horizontal bilinear interpolation of a field from +!> values on a logically rectangular grid. Optional parameters +!> inXPeriod and inYPeriod are used to specify the period of the +!> input grid. If either or both are omitted, the grid is not +!> treated as being periodic in either or both dimensions. + +!----------------------------------------------------------------------- + + subroutine ocn_init_interpolation_bilinear_horiz_2D(inX, inY, inField, inNx, inNy, & + outX, outY, outField, outN, & + inXPeriod, inYPeriod, extrapX, extrapY)!{{{ + + !-------------------------------------------------------------------- + + integer, intent(in) :: inNx, inNy, outN + real (kind=RKIND), dimension(inNx), intent(in) :: inX + real (kind=RKIND), dimension(inNy), intent(in) :: inY + real (kind=RKIND), dimension(inNx,inNy), intent(in) :: inField + real (kind=RKIND), dimension(outN), intent(in) :: outX, outY + real (kind=RKIND), dimension(outN), intent(out) :: outField + real (kind=RKIND), intent(in), optional :: inXPeriod, inYPeriod + logical, intent(in), optional :: extrapX, extrapY + + ! Define variable pointers + integer :: outIndex, xInd1, xInd2, yInd1, yInd2, k + real (kind=RKIND) :: x, y, xFrac, yFrac + + do outIndex = 1, outN + x = outX(outIndex) + y = outY(outIndex) + + if(present(inXPeriod)) then + call getLinearCoeffs(x, inX, inNx, xInd1, xInd2, xFrac, inXPeriod) + else + call getLinearCoeffs(x, inX, inNx, xInd1, xInd2, xFrac) + end if + + ! if we're not extrapolating, limit xFrac + if(present(extrapX)) then + if(.not. extrapX) then + xFrac = min(1.0_RKIND,max(0.0_RKIND,xFrac)) + end if + else + ! by default, we don't extrapolate + xFrac = min(1.0_RKIND,max(0.0_RKIND,xFrac)) + end if + + if(present(inYPeriod)) then + call getLinearCoeffs(y, inY, inNy, yInd1, yInd2, yFrac, inYPeriod) + else + call getLinearCoeffs(y, inY, inNy, yInd1, yInd2, yFrac) + end if + + ! if we're not extrapolating, limit yFrac + if(present(extrapY)) then + if(.not. extrapY) then + yFrac = min(1.0_RKIND,max(0.0_RKIND,yFrac)) + end if + else + ! by default, we don't extrapolate + yFrac = min(1.0_RKIND,max(0.0_RKIND,yFrac)) + end if + + outField(outIndex) = & + (1.0_RKIND-xFrac)*(1.0_RKIND-yFrac)*inField(xInd1,yInd1) & + + xFrac*(1.0_RKIND-yFrac)*inField(xInd2,yInd1) & + + (1.0_RKIND-xFrac)*yFrac*inField(xInd1,yInd2) & + + xFrac*yFrac*inField(xInd2,yInd2) + end do + + !-------------------------------------------------------------------- + + end subroutine ocn_init_interpolation_bilinear_horiz_2D!}}} + +!*********************************************************************** +! +! routine ocn_init_interpolation_bilinear_horiz_3D +! +!> \brief bilinear interpolation in horiz. +!> \author Xylar Asay-Davis +!> \date 10/30/2015 +!> \details +!> Perform horizontal bilinear interpolation of a field from +!> values on a logically rectangular grid. Optional parameters +!> inXPeriod and inYPeriod are used to specify the period of the +!> input grid. If either or both are omitted, the grid is not +!> treated as being periodic in either or both dimensions. + +!----------------------------------------------------------------------- + + subroutine ocn_init_interpolation_bilinear_horiz_3D(inX, inY, inField, inNx, inNy, & + outX, outY, outField, outN, & + inXPeriod, inYPeriod, extrapX, extrapY)!{{{ + + !-------------------------------------------------------------------- + + integer, intent(in) :: inNx, inNy, outN + real (kind=RKIND), dimension(inNx), intent(in) :: inX + real (kind=RKIND), dimension(inNy), intent(in) :: inY + real (kind=RKIND), dimension(:,:,:), intent(in) :: inField + real (kind=RKIND), dimension(outN), intent(in) :: outX, outY + real (kind=RKIND), dimension(:,:), intent(out) :: outField + real (kind=RKIND), intent(in), optional :: inXPeriod, inYPeriod + logical, intent(in), optional :: extrapX, extrapY + + ! Define variable pointers + integer :: outIndex, xInd1, xInd2, yInd1, yInd2, k + real (kind=RKIND) :: x, y, xFrac, yFrac + + do outIndex = 1, outN + x = outX(outIndex) + y = outY(outIndex) + + if(present(inXPeriod)) then + call getLinearCoeffs(x, inX, inNx, xInd1, xInd2, xFrac, inXPeriod) + else + call getLinearCoeffs(x, inX, inNx, xInd1, xInd2, xFrac) + end if + + ! if we're not extrapolating, limit xFrac + if(present(extrapX)) then + if(.not. extrapX) then + xFrac = min(1.0_RKIND,max(0.0_RKIND,xFrac)) + end if + else + ! by default, we don't extrapolate + xFrac = min(1.0_RKIND,max(0.0_RKIND,xFrac)) + end if + + if(present(inYPeriod)) then + call getLinearCoeffs(y, inY, inNy, yInd1, yInd2, yFrac, inYPeriod) + else + call getLinearCoeffs(y, inY, inNy, yInd1, yInd2, yFrac) + end if + + ! if we're not extrapolating, limit yFrac + if(present(extrapY)) then + if(.not. extrapY) then + yFrac = min(1.0_RKIND,max(0.0_RKIND,yFrac)) + end if + else + ! by default, we don't extrapolate + yFrac = min(1.0_RKIND,max(0.0_RKIND,yFrac)) + end if + + outField(:,outIndex) = & + (1.0_RKIND-xFrac)*(1.0_RKIND-yFrac)*inField(xInd1,yInd1,:) & + + xFrac*(1.0_RKIND-yFrac)*inField(xInd2,yInd1,:) & + + (1.0_RKIND-xFrac)*yFrac*inField(xInd1,yInd2,:) & + + xFrac*yFrac*inField(xInd2,yInd2,:) + end do + + !-------------------------------------------------------------------- + + end subroutine ocn_init_interpolation_bilinear_horiz_3D!}}} + +!*********************************************************************** +! +! routine getLinearCoeffs +! +!> \brief compute coefficients for linear interpolation +!> \author Xylar Asay-Davis +!> \date 10/30/2015 +!> \details +!> Given a point and an array of locations, returns the indices +!> the point is bounded by, as a fraction, where the point lies +!> between the nearest two points in array of locations. +!> Optional parameter period is used to indicate that the array +!> of locations is periodic with the given period. + +!----------------------------------------------------------------------- + + subroutine getLinearCoeffs(xValue, xArray, nx, index1, index2, frac, period)!{{{ + + integer, intent(in) :: nx + real (kind=RKIND), intent(in) :: xValue + real (kind=RKIND), dimension(nx), intent(in) :: xArray + integer, intent(out) :: index1, index2 + real (kind=RKIND), intent(out) :: frac + real (kind=RKIND), intent(in), optional :: period + + integer :: xIndex + real (kind=RKIND) :: x + + x = xValue + + if(present(period)) then + ! Set up bilinear interpolation indices in x, watching for periodic boundary + ! shift x to be within the range of [xArray(1),xArray(1)+period) + x = modulo(x-xArray(1),period) + xArray(1) + if (x >= xArray(nx)) then + ! at the periodic boundary so treat as special case + index1 = nx + index2 = 1 + frac = (x-xArray(index1))/(xArray(index2)+period-xArray(index1)) + else + do xIndex = 1, nx-1 + if (x .le. xArray(xIndex+1)) then + index1 = xIndex + index2 = xIndex+1 + frac = (x-xArray(index1))/(xArray(index2)-xArray(index1)) + exit + end if + end do + end if + else + ! not periodic + index1 = nx-1 + do xIndex = 1, nx-2 + if (x .le. xArray(xIndex+1)) then + index1 = xIndex + exit + end if + end do + index2 = index1+1 + frac = (x-xArray(index1))/(xArray(index2)-xArray(index1)) + end if + + end subroutine getLinearCoeffs!}}} + + +!*********************************************************************** + +end module ocn_init_interpolation + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_iso.F b/src/core_ocean/mode_init/mpas_ocn_init_iso.F new file mode 100644 index 0000000000..e71de36460 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_iso.F @@ -0,0 +1,905 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_iso +! +!> \brief MPAS ocean initialize case -- Idealized Southern Ocean (ISO) +!> \author Juan A. Saenz, based on idealized_acc and others +!> \date 12/08/2014 +!> \details +!> This module contains the routines for initializing the +!> the idealized Southern Ocean (ISO) test case +! +!----------------------------------------------------------------------- + +module ocn_init_iso + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_iso, & + ocn_init_validate_iso + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_iso +! +!> \brief Setup for ISO test case +!> \author Juan A. Saenz +!> \date 02/26/2014 +!> \details +!> This routine sets up the initial conditions for the +!> Idealized Southern Ocean configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_iso(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + ! local work variables + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, statePool, forcingPool, tracersPool + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool, tracersInteriorRestoringFieldsPool + + integer :: iCell, k, idx + + real (kind=RKIND) :: distance, xDistance, yDistance, zMid, sphereRadius + real (kind=RKIND) :: currentLon, currentLat + real (kind=RKIND) :: location, amplitude + real (kind=RKIND) :: Tbottom, Tmin, TminGlobal + real (kind=RKIND) :: depth, contSlopeWidthRad, widthWindASFRad, windStress + real (kind=RKIND) :: widthQSouth, widthQMiddle, widthQNorth, heatFluxZonal, heatFlux1, heatFlux2 + real (kind=RKIND) :: temperature + real (kind=RKIND), dimension(30) :: featureDepth + + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid + + integer, pointer :: config_iso_vert_levels + real (kind=RKIND), pointer :: config_iso_main_channel_depth + real (kind=RKIND), pointer :: config_iso_north_wall_lat + real (kind=RKIND), pointer :: config_iso_south_wall_lat + logical, pointer :: config_iso_ridge_flag + real (kind=RKIND), pointer :: config_iso_ridge_center_lon + real (kind=RKIND), pointer :: config_iso_ridge_height + real (kind=RKIND), pointer :: config_iso_ridge_width + logical, pointer :: config_iso_plateau_flag + real (kind=RKIND), pointer :: config_iso_plateau_center_lon + real (kind=RKIND), pointer :: config_iso_plateau_center_lat + real (kind=RKIND), pointer :: config_iso_plateau_height + real (kind=RKIND), pointer :: config_iso_plateau_radius + real (kind=RKIND), pointer :: config_iso_plateau_slope_width + logical, pointer :: config_iso_shelf_flag + real (kind=RKIND), pointer :: config_iso_shelf_depth + real (kind=RKIND), pointer :: config_iso_shelf_width + logical, pointer :: config_iso_cont_slope_flag + real (kind=RKIND), pointer :: config_iso_max_cont_slope + logical, pointer :: config_iso_embayment_flag + real (kind=RKIND), pointer :: config_iso_embayment_radius + real (kind=RKIND), pointer :: config_iso_embayment_depth + real (kind=RKIND), pointer :: config_iso_embayment_center_lon + real (kind=RKIND), pointer :: config_iso_embayment_center_lat + logical, pointer :: config_iso_depression_flag + real (kind=RKIND), pointer :: config_iso_depression_width + real (kind=RKIND), pointer :: config_iso_depression_depth + real (kind=RKIND), pointer :: config_iso_depression_center_lon + real (kind=RKIND), pointer :: config_iso_depression_south_lat + real (kind=RKIND), pointer :: config_iso_depression_north_lat + real (kind=RKIND), pointer :: config_iso_salinity + real (kind=RKIND), pointer :: config_iso_wind_stress_max + real (kind=RKIND), pointer :: config_iso_asf_wind + real (kind=RKIND), pointer :: config_iso_acc_wind + real (kind=RKIND), pointer :: config_iso_wind_trans + real (kind=RKIND), pointer :: config_iso_heat_flux_south + real (kind=RKIND), pointer :: config_iso_heat_flux_middle + real (kind=RKIND), pointer :: config_iso_heat_flux_north + real (kind=RKIND), pointer :: config_iso_heat_flux_lat_ss + real (kind=RKIND), pointer :: config_iso_heat_flux_lat_sm + real (kind=RKIND), pointer :: config_iso_heat_flux_lat_mn + real (kind=RKIND), pointer :: config_iso_surface_temperature_piston_velocity + real (kind=RKIND), pointer :: config_iso_initial_temp_t1 + real (kind=RKIND), pointer :: config_iso_initial_temp_t2 + real (kind=RKIND), pointer :: config_iso_initial_temp_h0 + real (kind=RKIND), pointer :: config_iso_initial_temp_h1 + real (kind=RKIND), pointer :: config_iso_initial_temp_mt + real (kind=RKIND), pointer :: config_iso_initial_temp_latS + real (kind=RKIND), pointer :: config_iso_initial_temp_latN + real (kind=RKIND), pointer :: config_iso_region1_center_lon + real (kind=RKIND), pointer :: config_iso_region1_center_lat + real (kind=RKIND), pointer :: config_iso_region2_center_lon + real (kind=RKIND), pointer :: config_iso_region2_center_lat + real (kind=RKIND), pointer :: config_iso_region3_center_lon + real (kind=RKIND), pointer :: config_iso_region3_center_lat + real (kind=RKIND), pointer :: config_iso_region4_center_lon + real (kind=RKIND), pointer :: config_iso_region4_center_lat + logical, pointer :: config_iso_heat_flux_region1_flag + real (kind=RKIND), pointer :: config_iso_heat_flux_region1 + real (kind=RKIND), pointer :: config_iso_heat_flux_region1_radius + logical, pointer :: config_iso_heat_flux_region2_flag + real (kind=RKIND), pointer :: config_iso_heat_flux_region2 + real (kind=RKIND), pointer :: config_iso_heat_flux_region2_radius + real (kind=RKIND), pointer :: config_iso_temperature_sponge_t1 + real (kind=RKIND), pointer :: config_iso_temperature_sponge_h1 + real (kind=RKIND), pointer :: config_iso_temperature_sponge_l1 + real (kind=RKIND), pointer :: config_iso_temperature_sponge_tau1 + + logical, pointer :: config_iso_temperature_restore_region1_flag + real (kind=RKIND), pointer :: config_iso_temperature_restore_t1 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcx1 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcy1 + logical, pointer :: config_iso_temperature_restore_region2_flag + real (kind=RKIND), pointer :: config_iso_temperature_restore_t2 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcx2 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcy2 + logical, pointer :: config_iso_temperature_restore_region3_flag + real (kind=RKIND), pointer :: config_iso_temperature_restore_t3 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcx3 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcy3 + logical, pointer :: config_iso_temperature_restore_region4_flag + real (kind=RKIND), pointer :: config_iso_temperature_restore_t4 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcx4 + real (kind=RKIND), pointer :: config_iso_temperature_restore_lcy4 + + ! Define dimension pointers + integer, pointer :: nVertLevels, nCells, nVertLevelsP1 + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + ! Define variable pointers + logical, pointer :: on_a_sphere + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: refBottomDepth, bottomCell, refZMid + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), pointer :: sphere_radius + real (kind=RKIND), dimension(:), pointer :: lonCell, latCell, bottomDepth + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + real (kind=RKIND), dimension(:), pointer :: sensibleHeatFlux + real (kind=RKIND), dimension(:), pointer :: windStressZonal, windStressMeridional + real (kind=RKIND), dimension(:, :), pointer :: activeTracersPistonVelocity, activeTracersSurfaceRestoringValue + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracersInteriorRestoringValue, activeTracersInteriorRestoringRate + + ! Define variables for the config_iso_ variables + real (kind=RKIND) :: mainChannelDepth, northWallLat, southWallLat + logical :: ridgeFlag + real (kind=RKIND) :: ridgeCenterLon, ridgeHeight, ridgeWidth + logical :: plateauFlag + real (kind=RKIND) :: plateauCenterLon, plateauCenterLat + real (kind=RKIND) :: plateauHeight, plateauRadius, plateauSlopeWidth + logical :: shelfFlag + real (kind=RKIND) :: shelfDepth, shelfWidth + logical :: contSlopeFlag + real (kind=RKIND) :: maxContSlope + logical :: embaymentFlag + real (kind=RKIND) :: embaymentRadius, embaymentDepth, embaymentCenterLon, embaymentCenterLat + logical :: depressionFlag + real (kind=RKIND) :: depressionWidth, depressionDepth + real (kind=RKIND) :: depressionCenterLon, depressionSouthLat, depressionNorthLat + real (kind=RKIND) :: salinity0 + real (kind=RKIND) :: windStressMax, windASF, windACC, latWindTrans + real (kind=RKIND) :: QSouth, QNorth, QMiddle, transSS, transSM, transMN + real (kind=RKIND) :: tempPistonVel, tempT1, tempT2, temph1, tempmT, temph0, tempLatS, tempLatN + real (kind=RKIND) :: regionCenterLat1, regionCenterLon1, regionCenterLat2, regionCenterLon2 + real (kind=RKIND) :: regionCenterLat3, regionCenterLon3, regionCenterLat4, regionCenterLon4 + logical :: heatRegionFlag1 + real (kind=RKIND) :: heatRegion1flux, heatRegion1Radius + logical :: heatRegionFlag2 + real (kind=RKIND) :: heatRegion2flux, heatRegion2Radius + real (kind=RKIND) :: tempSpongeT1, tempSpongeh1, tempSpongeWeightL1, tempSpongeTau1 + logical :: tempRestoreFlag1 + real (kind=RKIND) :: tempRestoreT1, tempRestoreLcx1, tempRestoreLcy1 + logical :: tempRestoreFlag2 + real (kind=RKIND) :: tempRestoreT2, tempRestoreLcx2, tempRestoreLcy2 + logical :: tempRestoreFlag3 + real (kind=RKIND) :: tempRestoreT3, tempRestoreLcx3, tempRestoreLcy3 + logical :: tempRestoreFlag4 + real (kind=RKIND) :: tempRestoreT4, tempRestoreLcx4, tempRestoreLcy4 + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('iso')) return + + ! get config variables + call mpas_pool_get_config(domain % configs, 'config_vertical_grid', config_vertical_grid) + call mpas_pool_get_config(domain % configs, 'config_iso_vert_levels', config_iso_vert_levels) + call mpas_pool_get_config(domain % configs, 'config_iso_main_channel_depth', config_iso_main_channel_depth) + call mpas_pool_get_config(domain % configs, 'config_iso_north_wall_lat', config_iso_north_wall_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_south_wall_lat', config_iso_south_wall_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_ridge_flag', config_iso_ridge_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_ridge_center_lon', config_iso_ridge_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_ridge_height', config_iso_ridge_height) + call mpas_pool_get_config(domain % configs, 'config_iso_ridge_width', config_iso_ridge_width) + call mpas_pool_get_config(domain % configs, 'config_iso_plateau_flag', config_iso_plateau_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_plateau_center_lon', config_iso_plateau_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_plateau_center_lat', config_iso_plateau_center_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_plateau_height', config_iso_plateau_height) + call mpas_pool_get_config(domain % configs, 'config_iso_plateau_radius', config_iso_plateau_radius) + call mpas_pool_get_config(domain % configs, 'config_iso_plateau_slope_width', config_iso_plateau_slope_width) + call mpas_pool_get_config(domain % configs, 'config_iso_shelf_flag', config_iso_shelf_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_shelf_depth', config_iso_shelf_depth) + call mpas_pool_get_config(domain % configs, 'config_iso_shelf_width', config_iso_shelf_width) + call mpas_pool_get_config(domain % configs, 'config_iso_cont_slope_flag', config_iso_cont_slope_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_max_cont_slope', config_iso_max_cont_slope) + call mpas_pool_get_config(domain % configs, 'config_iso_embayment_flag', config_iso_embayment_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_embayment_radius', config_iso_embayment_radius) + call mpas_pool_get_config(domain % configs, 'config_iso_embayment_depth', config_iso_embayment_depth) + call mpas_pool_get_config(domain % configs, 'config_iso_embayment_center_lon', config_iso_embayment_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_embayment_center_lat', config_iso_embayment_center_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_depression_flag', config_iso_depression_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_depression_width', config_iso_depression_width) + call mpas_pool_get_config(domain % configs, 'config_iso_depression_depth', config_iso_depression_depth) + call mpas_pool_get_config(domain % configs, 'config_iso_depression_center_lon', config_iso_depression_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_depression_south_lat', config_iso_depression_south_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_depression_north_lat', config_iso_depression_north_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_salinity', config_iso_salinity) + call mpas_pool_get_config(domain % configs, 'config_iso_wind_stress_max', config_iso_wind_stress_max) + call mpas_pool_get_config(domain % configs, 'config_iso_asf_wind', config_iso_asf_wind) + call mpas_pool_get_config(domain % configs, 'config_iso_acc_wind', config_iso_acc_wind) + call mpas_pool_get_config(domain % configs, 'config_iso_wind_trans', config_iso_wind_trans) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_south', config_iso_heat_flux_south) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_middle', config_iso_heat_flux_middle) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_north', config_iso_heat_flux_north) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_lat_ss', config_iso_heat_flux_lat_ss) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_lat_sm', config_iso_heat_flux_lat_sm) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_lat_mn', config_iso_heat_flux_lat_mn) + call mpas_pool_get_config(domain % configs, 'config_iso_surface_temperature_piston_velocity', & + config_iso_surface_temperature_piston_velocity) + call mpas_pool_get_config(domain % configs, 'config_iso_initial_temp_t1', config_iso_initial_temp_t1) + call mpas_pool_get_config(domain % configs, 'config_iso_initial_temp_t2', config_iso_initial_temp_t2) + call mpas_pool_get_config(domain % configs, 'config_iso_initial_temp_h0', config_iso_initial_temp_h0) + call mpas_pool_get_config(domain % configs, 'config_iso_initial_temp_h1', config_iso_initial_temp_h1) + call mpas_pool_get_config(domain % configs, 'config_iso_initial_temp_mt', config_iso_initial_temp_mt) + call mpas_pool_get_config(domain % configs, 'config_iso_initial_temp_latS', config_iso_initial_temp_latS) + call mpas_pool_get_config(domain % configs, 'config_iso_initial_temp_latN', config_iso_initial_temp_latN) + call mpas_pool_get_config(domain % configs, 'config_iso_region1_center_lon', config_iso_region1_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_region1_center_lat', config_iso_region1_center_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_region2_center_lon', config_iso_region2_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_region2_center_lat', config_iso_region2_center_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_region3_center_lon', config_iso_region3_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_region3_center_lat', config_iso_region3_center_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_region4_center_lon', config_iso_region4_center_lon) + call mpas_pool_get_config(domain % configs, 'config_iso_region4_center_lat', config_iso_region4_center_lat) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_region1_flag', config_iso_heat_flux_region1_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_region1', config_iso_heat_flux_region1) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_region1_radius', config_iso_heat_flux_region1_radius) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_region2_flag', config_iso_heat_flux_region2_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_region2', config_iso_heat_flux_region2) + call mpas_pool_get_config(domain % configs, 'config_iso_heat_flux_region2_radius', config_iso_heat_flux_region2_radius) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_sponge_t1', config_iso_temperature_sponge_t1) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_sponge_h1', config_iso_temperature_sponge_h1) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_sponge_l1', config_iso_temperature_sponge_l1) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_sponge_tau1', config_iso_temperature_sponge_tau1) + + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_region1_flag', & + config_iso_temperature_restore_region1_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_t1', config_iso_temperature_restore_t1) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcx1', config_iso_temperature_restore_lcx1) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcy1', config_iso_temperature_restore_lcy1) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_region2_flag', & + config_iso_temperature_restore_region2_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_t2', config_iso_temperature_restore_t2) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcx2', config_iso_temperature_restore_lcx2) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcy2', config_iso_temperature_restore_lcy2) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_region3_flag', & + config_iso_temperature_restore_region3_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_t3', config_iso_temperature_restore_t3) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcx3', config_iso_temperature_restore_lcx3) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcy3', config_iso_temperature_restore_lcy3) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_region4_flag', & + config_iso_temperature_restore_region4_flag) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_t4', config_iso_temperature_restore_t4) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcx4', config_iso_temperature_restore_lcx4) + call mpas_pool_get_config(domain % configs, 'config_iso_temperature_restore_lcy4', config_iso_temperature_restore_lcy4) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_config(meshPool, 'sphere_radius', sphere_radius) + sphereRadius = sphere_radius + + + if(.not. on_a_sphere) then + call mpas_log_write( 'ISO test case can only be defined on a spherical mesh.', MPAS_LOG_CRIT) + iErr = 1 + return + else + call mpas_log_write( 'ISO test case using spherical radius of size: $f', realArgs=(/ sphereRadius /) ) + end if + + ! Define interface locations + allocate( interfaceLocations( nVertLevelsP1 ) ) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations, domain % configs ) + + ! assign config variables + nVertLevels = config_iso_vert_levels + mainChannelDepth = config_iso_main_channel_depth + northWallLat = config_iso_north_wall_lat * pii/180.0_RKIND + southWallLat = config_iso_south_wall_lat * pii/180.0_RKIND + ridgeFlag = config_iso_ridge_flag + ridgeCenterLon = config_iso_ridge_center_lon * pii/180.0_RKIND + ridgeHeight = config_iso_ridge_height + ridgeWidth = config_iso_ridge_width + plateauFlag = config_iso_plateau_flag + plateauCenterLon = config_iso_plateau_center_lon * pii/180.0_RKIND + plateauCenterLat = config_iso_plateau_center_lat * pii/180.0_RKIND + plateauHeight = config_iso_plateau_height + plateauRadius = config_iso_plateau_radius + plateauSlopeWidth = config_iso_plateau_slope_width + shelfFlag = config_iso_shelf_flag + shelfDepth = config_iso_shelf_depth + shelfWidth = config_iso_shelf_width + contSlopeFlag = config_iso_cont_slope_flag + maxContSlope = config_iso_max_cont_slope + embaymentFlag = config_iso_embayment_flag + embaymentRadius = config_iso_embayment_radius + embaymentDepth = config_iso_embayment_depth + embaymentCenterLon = config_iso_embayment_center_lon * pii/180.0_RKIND + embaymentCenterLat = config_iso_embayment_center_lat * pii/180.0_RKIND + depressionFlag = config_iso_depression_flag + depressionWidth = config_iso_depression_width + depressionDepth = config_iso_depression_depth + depressionCenterLon = config_iso_depression_center_lon * pii/180.0_RKIND + depressionSouthLat = config_iso_depression_south_lat * pii/180.0_RKIND + depressionNorthLat = config_iso_depression_north_lat * pii/180.0_RKIND + salinity0 = config_iso_salinity + windStressMax = config_iso_wind_stress_max + windASF = config_iso_asf_wind + windACC = config_iso_acc_wind + latWindTrans = config_iso_wind_trans * pii/180.0_RKIND + QSouth = config_iso_heat_flux_south + QMiddle = config_iso_heat_flux_middle + QNorth = config_iso_heat_flux_north + transSS = config_iso_heat_flux_lat_ss * pii/180.0_RKIND + transSM = config_iso_heat_flux_lat_sm * pii/180.0_RKIND + transMN = config_iso_heat_flux_lat_mn * pii/180.0_RKIND + tempPistonVel = config_iso_surface_temperature_piston_velocity + tempT1 = config_iso_initial_temp_t1 + tempT2 = config_iso_initial_temp_t2 + temph0 = config_iso_initial_temp_h0 + temph1 = config_iso_initial_temp_h1 + tempmT = config_iso_initial_temp_mt + tempLatS = config_iso_initial_temp_latS * pii/180.0_RKIND + tempLatN = config_iso_initial_temp_latN * pii/180.0_RKIND + regionCenterLon1 = config_iso_region1_center_lon * pii/180.0_RKIND + regionCenterLat1 = config_iso_region1_center_lat * pii/180.0_RKIND + regionCenterLon2 = config_iso_region2_center_lon * pii/180.0_RKIND + regionCenterLat2 = config_iso_region2_center_lat * pii/180.0_RKIND + regionCenterLon3 = config_iso_region3_center_lon * pii/180.0_RKIND + regionCenterLat3 = config_iso_region3_center_lat * pii/180.0_RKIND + regionCenterLon4 = config_iso_region4_center_lon * pii/180.0_RKIND + regionCenterLat4 = config_iso_region4_center_lat * pii/180.0_RKIND + heatRegionFlag1 = config_iso_heat_flux_region1_flag + heatRegion1flux = config_iso_heat_flux_region1 + heatRegion1Radius = config_iso_heat_flux_region1_radius + heatRegionFlag2 = config_iso_heat_flux_region2_flag + heatRegion2flux = config_iso_heat_flux_region2 + heatRegion2Radius = config_iso_heat_flux_region2_radius + tempSpongeT1 = config_iso_temperature_sponge_t1 + tempSpongeh1 = config_iso_temperature_sponge_h1 + tempSpongeWeightL1 = config_iso_temperature_sponge_l1 + tempSpongeTau1 = config_iso_temperature_sponge_tau1 + + tempRestoreFlag1 = config_iso_temperature_restore_region1_flag + tempRestoreT1 = config_iso_temperature_restore_t1 + tempRestoreLcx1 = config_iso_temperature_restore_lcx1 + tempRestoreLcy1 = config_iso_temperature_restore_lcy1 + tempRestoreFlag2 = config_iso_temperature_restore_region2_flag + tempRestoreT2 = config_iso_temperature_restore_t2 + tempRestoreLcx2 = config_iso_temperature_restore_lcx2 + tempRestoreLcy2 = config_iso_temperature_restore_lcy2 + tempRestoreFlag3 = config_iso_temperature_restore_region3_flag + tempRestoreT3 = config_iso_temperature_restore_t3 + tempRestoreLcx3 = config_iso_temperature_restore_lcx3 + tempRestoreLcy3 = config_iso_temperature_restore_lcy3 + tempRestoreFlag4 = config_iso_temperature_restore_region4_flag + tempRestoreT4 = config_iso_temperature_restore_t4 + tempRestoreLcx4 = config_iso_temperature_restore_lcx4 + tempRestoreLcy4 = config_iso_temperature_restore_lcy4 + + + !!!!!!!!!!!!!!!!!!!!!!!!! + ! Setup the vertical grid + !!!!!!!!!!!!!!!!!!!!!!!!! + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + + ! Set layerThickness and restingThickness + do k = 1, nVertLevels + layerThickness(k, :) = config_iso_main_channel_depth * ( interfaceLocations(k+1) - interfaceLocations(k) ) + restingThickness(k, :) = layerThickness(k, :) + end do + + ! Set refBottomDepth + do k = 1, nVertLevels + refBottomDepth(k) = config_iso_main_channel_depth * interfaceLocations(k+1) + refZMid(k) = -config_iso_main_channel_depth * (interfaceLocations(k)+interfaceLocations(k+1))/2.0_RKIND + end do + + block_ptr => block_ptr % next + + end do + + !!!!!!!!!!!!!!!!!!!!!!!!! + ! Set Topography + !!!!!!!!!!!!!!!!!!!!!!!!! + write(*,*) 'setting up topography' + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + + ! calculate the width of the continental slope, + ! based on the specified max value of the slope of the continental slope, maxContSlope + contSlopeWidthRad = & + pii * 0.5_RKIND * (-shelfDepth + mainChannelDepth) / maxContSlope / sphereRadius + + do iCell = 1, nCells + currentLon = lonCell(iCell) + currentLat = latCell(iCell) + + bottomDepth(iCell) = 0.0_RKIND + + !!!!!!!!!!!!!!!!!!!!!!!!! + ! Main channel + if (currentLat <= northWallLat .and. currentLat >= southWallLat) then + bottomDepth(iCell) = mainChannelDepth + endif + + !!!!!!!!!!!!!!!!!!!!!!!!! + ! set up fill-in features + featureDepth = 1.0E6_RKIND + + ! feature 1: Add Ridge + if (ridgeFlag) then + distance = (currentLon - ridgeCenterLon) & + *sphereRadius*cos(currentLat) + if ( abs(distance) <= 0.6_RKIND * ridgeWidth ) then + featureDepth(1) = mainChannelDepth - & + ridgeHeight * exp(-2.0_RKIND*(distance / ridgeWidth / 0.4_RKIND)**2) + endif + endif + + ! feature 2: Add Plateau + if (plateauFlag) then + distance = sqrt( & + ( (currentLon - plateauCenterLon) * sphereRadius*cos(currentLat) )**2 & + + ( (currentLat - plateauCenterLat) * sphereRadius )**2 & + ) + if (abs(distance) <= plateauRadius) then + featureDepth(2) = mainChannelDepth - plateauHeight + else if (abs(distance) > plateauRadius .and. abs(distance) < plateauSlopeWidth) then + featureDepth(2) = mainChannelDepth - plateauHeight * & + exp( -2 * ( (abs(distance)-plateauRadius) / plateauSlopeWidth / 0.4_RKIND ) **2 ) + endif + endif + + ! feature 3: Add continental slope, or continental shelf break + if (contSlopeFlag) then + zMid = 0.5_RKIND*(mainChannelDepth+shelfDepth) + amplitude = 0.5_RKIND*(-shelfDepth+mainChannelDepth) + if (currentLat <= southWallLat + contSlopeWidthRad& + .and. currentLat > southWallLat) then + featureDepth(3) = zMid - amplitude * sin( 0.5_RKIND*pii + pii/contSlopeWidthRad & + *(currentLat-southWallLat) ) + endif + endif + + ! choose the shallowest + bottomDepth(iCell) = min(minval(featureDepth), bottomDepth(iCell)) + + + !!!!!!!!!!!!!!!!!!!!!!!!! + ! Set up dig-out features + featureDepth = 0.0_RKIND + + ! feature 1: Continental shelf + if (shelfFlag) then + if (currentLat <= southWallLat .and. currentLat >= southWallLat-shelfWidth/sphereRadius) then + featureDepth(1) = shelfDepth + endif + endif + + ! feature 2: Embayment + if (embaymentFlag) then + distance = sqrt( & + ( (currentLon - embaymentCenterLon) * sphereRadius*cos(currentLat) )**2 & + + ( (currentLat - embaymentCenterLat) * sphereRadius )**2 & + ) + if(distance <= embaymentRadius .and. currentLat < embaymentCenterLat) then + featureDepth(2) = embaymentDepth + endif + endif + + ! feature 3: depression + if (depressionFlag) then + distance = (currentLon - depressionCenterLon) * sphereRadius*cos(currentLat) + if( abs(distance) <= 0.5_RKIND*depressionWidth & + .and. currentLat >= depressionSouthLat .and. currentLat <= depressionNorthLat ) & + then + featureDepth(3) = depressionDepth + endif + endif + + ! choose the deepest one + bottomDepth(iCell) = max(maxval(featureDepth), bottomDepth(iCell)) + + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! Set maxLevelCell to -1 for cells to be culled + if (bottomDepth(iCell) > 0.0_RKIND) then + maxLevelCell(iCell) = 1 + else + maxLevelCell(iCell) = -1 + endif + + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! Determine maxLevelCell based on bottomDepth and refBottomDepth + ! Also set botomDepth based on refBottomDepth, since + ! above bottomDepth was set with continuous analytical functions, + ! and needs to be discrete + if (maxLevelCell(iCell) > 0) then + maxLevelCell(iCell) = nVertLevels + if (nVertLevels .gt. 1) then + do k = 1, nVertLevels + if (bottomDepth(iCell) < refBottomDepth(k) ) then + maxLevelCell(iCell) = k-1 + bottomDepth(iCell) = refBottomDepth(k-1) + exit + end if + end do + end if + end if + + enddo ! Looping through with iCell + + block_ptr => block_ptr % next + enddo ! done setting topography + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! mark cells for culling + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + block_ptr => domain % blocklist + do while (associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call ocn_mark_maxlevelcell(meshPool, iErr) + block_ptr => block_ptr % next + end do + + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! Set forcing boundary conditions and initial conditions + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + write(*,*) 'setting up forcing and boundary conditions' + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_array(forcingPool, 'sensibleHeatFlux', sensibleHeatFlux) + call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) + call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional, 1) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + call mpas_pool_get_subpool(forcingPool, 'tracersInteriorRestoringFields', tracersInteriorRestoringFieldsPool) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', activeTracersPistonVelocity, 1) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersSurfaceRestoringValue', & + activeTracersSurfaceRestoringValue, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringRate', & + activeTracersInteriorRestoringRate, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringValue', & + activeTracersInteriorRestoringValue, 1) + + activeTracersInteriorRestoringRate(:,:,:) = 0.0_RKIND + activeTracersInteriorRestoringValue(:,:,:) = 0.0_RKIND + activeTracersPistonVelocity(:,:) = 0.0_RKIND + activeTracersSurfaceRestoringValue(:,:) = 0.0_RKIND + + do iCell = 1, nCells + currentLon = lonCell(iCell) + currentLat = latCell(iCell) + + ! Set initial temperature + idx = index_temperature + do k = 1, nVertLevels + zMid = refZMid(k) + !temperature = tempT1 + tempT2*tanh(zMid/temph1) + tempmT * zMid + temperature = (tempT1 + tempT2*tanh((zMid+temph0)/temph1) + tempmT * zMid) & + * (-tempLatS+currentLat)*( 1.0_RKIND/(-tempLatS+tempLatN) ) + activeTracers(idx, k, iCell) = temperature + enddo + + ! Set initial salinity + idx = index_salinity + activeTracers(idx, :, iCell) = salinity0 + + ! Set up debugging tracers + idx = index_tracer1 + if ( associated(debugTracers) ) then + debugTracers(idx, :, iCell) = 1.0_RKIND + end if + + ! Heat fluxes + heatFluxZonal = 0.0_RKIND + heatFlux1 = 0.0_RKIND + heatFlux2 = 0.0_RKIND + + ! Setup zonally constant surface heat fluxes + widthQSouth = transSM - transSS + widthQMiddle = transMN - transSM + widthQNorth = northWallLat - transMN + if (currentLat > transSS .and. currentLat < transSM) then + heatFluxZonal = QSouth*sin(pii*(currentLat-transSM)/widthQSouth)**2 + elseif (currentLat > transSM .and. currentLat < transMN) then + heatFluxZonal = QMiddle*sin(pii*(currentLat-transMN)/widthQMiddle)**2 + elseif (currentLat > transMN .and. currentLat < northWallLat) then + heatFluxZonal = QNorth*sin(pii*(currentLat-northWallLat)/widthQNorth)**2 + endif + + ! Setup heat flux over localized region 1 + if (heatRegionFlag1) then + distance = sqrt( & + ( (currentLon - regionCenterLon1) * sphereRadius*cos(currentLat) )**2 & + + ( (currentLat - regionCenterLat1) * sphereRadius )**2 & + ) + if (abs(distance) <= heatRegion1Radius) then + heatFlux1 = heatRegion1flux * exp(-2.0_RKIND*(distance / 2.0_RKIND / heatRegion1Radius / 0.4_RKIND)**2) + endif + endif + ! Setup heat flux over localized region 2 + if (heatRegionFlag2) then + distance = sqrt( & + ( (currentLon - regionCenterLon2) * sphereRadius*cos(currentLat) )**2 & + + ( (currentLat - regionCenterLat2) * sphereRadius )**2 & + ) + if (abs(distance) <= heatRegion2Radius) then + heatFlux2 = heatRegion2flux * exp(-2.0_RKIND*(distance / 2.0_RKIND / heatRegion2Radius / 0.4_RKIND)**2) + endif + endif + + if (currentLat < transSM) then + sensibleHeatFlux(iCell) = min(heatFluxZonal, heatFlux1, heatFlux2) + else + sensibleHeatFlux(iCell) = heatFluxZonal + endif + + ! Set interior restoring + do k = 1, nVertLevels + zMid = refZMid(k) + + !Temperature + !Interior restoring along northern wall + distance = sphereRadius * ( currentLat - northWallLat) + if(abs(distance) <= 3.0_RKIND*tempSpongeWeightL1) then + idx = index_temperature + temperature = tempSpongeT1 * exp(zMid/tempSpongeh1) + activeTracersInteriorRestoringValue(idx, k, iCell) = temperature + + idx = index_temperature + activeTracersInteriorRestoringRate(idx, k, iCell) = exp(-abs(distance)/tempSpongeWeightL1) * ( 1.0_RKIND & + / (tempSpongeTau1*86400.0_RKIND)) + endif + + ! Interior restoring at localized region 1 + if (tempRestoreFlag1) then + xDistance = (currentLon - regionCenterLon1) * sphereRadius*cos(currentLat) + yDistance = (currentLat - regionCenterLat1) * sphereRadius + if (abs(yDistance) <= tempRestoreLcy1 .and. abs(xDistance) <= tempRestoreLcx1) then + idx = index_temperature + activeTracersInteriorRestoringValue(idx, k, iCell) = TempRestoreT1 + + idx = index_temperature + activeTracersInteriorRestoringRate(idx, k, iCell) = ( 1.0_RKIND / (tempSpongeTau1*86400.0_RKIND)) * & + exp(-(2.0_RKIND*xDistance/tempRestoreLcx1)**2 - (2.0_RKIND*yDistance/tempRestoreLcy1)**2 ) + endif + endif + + ! Interior restoring at localized region 2 + if (tempRestoreFlag2) then + xDistance = (currentLon - regionCenterLon2) * sphereRadius*cos(currentLat) + yDistance = (currentLat - regionCenterLat2) * sphereRadius + if (abs(yDistance) <= tempRestoreLcy2 .and. abs(xDistance) <= tempRestoreLcx2) then + idx = index_temperature + activeTracersInteriorRestoringValue(idx, k, iCell) = TempRestoreT2 + + idx = index_temperature + activeTracersInteriorRestoringRate(idx, k, iCell) = ( 1.0_RKIND / (tempSpongeTau1*86400.0_RKIND)) * & + exp(-(2.0_RKIND*xDistance/tempRestoreLcx2)**2 - (2.0_RKIND*yDistance/tempRestoreLcy2)**2 ) + endif + endif + + ! Interior restoring at localized region 3 + if (tempRestoreFlag3) then + xDistance = (currentLon - regionCenterLon3) * sphereRadius*cos(currentLat) + yDistance = (currentLat - regionCenterLat3) * sphereRadius + if (abs(yDistance) <= tempRestoreLcy3 .and. abs(xDistance) <= tempRestoreLcx3) then + idx = index_temperature + activeTracersInteriorRestoringValue(idx, k, iCell) = TempRestoreT3 + + idx = index_temperature + activeTracersInteriorRestoringRate(idx, k, iCell) = ( 1.0_RKIND / (tempSpongeTau1*86400.0_RKIND)) * & + exp(-(2.0_RKIND*xDistance/tempRestoreLcx3)**2 - (2.0_RKIND*yDistance/tempRestoreLcy3)**2 ) + endif + endif + + ! Interior restoring at localized region 4 + if (tempRestoreFlag4) then + xDistance = (currentLon - regionCenterLon4) * sphereRadius*cos(currentLat) + yDistance = (currentLat - regionCenterLat4) * sphereRadius + if (abs(yDistance) <= tempRestoreLcy4 .and. abs(xDistance) <= tempRestoreLcx4) then + idx = index_temperature + activeTracersInteriorRestoringValue(idx, k, iCell) = TempRestoreT4 + + idx = index_temperature + activeTracersInteriorRestoringRate(idx, k, iCell) = ( 1.0_RKIND / (tempSpongeTau1*86400.0_RKIND)) * & + exp(-(2.0_RKIND*xDistance/tempRestoreLcx4)**2 - (2.0_RKIND*yDistance/tempRestoreLcy4)**2 ) + endif + endif + + ! Salinity + idx = index_salinity + activeTracersInteriorRestoringValue(idx, k, iCell) = salinity0 + idx = index_salinity + activeTracersInteriorRestoringRate(idx, k, iCell) = 0.0_RKIND + + enddo ! k = 1, nVertLevels, interior restoring loop + + + end do ! iCell = 1, nCells + + ! fill activeTracersSurfaceRestoringValue surface restoring values + ! fill activeTracersPistonVelocity with surface restoring rate + activeTracersSurfaceRestoringValue(index_temperature,:) = activeTracers(index_temperature, 1, :) + activeTracersPistonVelocity(index_temperature,:) = tempPistonVel + activeTracersSurfaceRestoringValue(index_salinity,:) = activeTracers(index_salinity, 1, :) + activeTracersPistonVelocity(index_salinity,:) = 0.0_RKIND + + ! Set wind stress + widthWindASFRad = 1.1_RKIND*contSlopeWidthRad + do iCell = 1, nCells + currentLon = lonCell(iCell) + currentLat = latCell(iCell) + windStress = 0.0_RKIND + + ! Set wind stress over the ACC, or main channel + if (currentLat > latWindTrans) then + windStress = windACC * & + sin( pii * (currentLat - latWindTrans) & + / (northWallLat-latWindTrans) )**2 + ! Set the wind over the continental slope front, over continental slope region + else if (currentLat > latWindTrans - widthWindASFRad .and. currentLat < latWindTrans) then + windStress = windASF * sin( pii * (latWindTrans-currentLat) / widthWindASFRad )**2 + endif + windStressZonal(iCell) = windStress + windStressMeridional(iCell) = 0.0_RKIND + end do + + + block_ptr => block_ptr % next + end do + + write(*,*) 'exiting ocn_init_setup_iso' + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_iso!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_iso +! +!> \brief Validation for ISO test case +!> \author Juan A. Saenz +!> \date 02/26/2014 +!> \details +!> This routine validates the configuration options for the ISO test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_iso(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool, packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_iso_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('iso')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_iso_vert_levels', config_iso_vert_levels) + + if(config_vert_levels <= 0 .and. config_iso_vert_levels > 0) then + config_vert_levels = config_iso_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for ISO. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_iso!}}} + +end module ocn_init_iso + + + + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_isomip.F b/src/core_ocean/mode_init/mpas_ocn_init_isomip.F new file mode 100644 index 0000000000..b6385544db --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_isomip.F @@ -0,0 +1,498 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_isomip +! +!> \brief MPAS ocean initialize case -- isomip +!> \author Xylar Asay-Davis +!> \date 06/01/2015 +!> \details +!> This module contains the routines for initializing the +!> the Ice Shelf/Ocean Model Intercomparision Project (ISOMIP) test cases +! +!----------------------------------------------------------------------- + +module ocn_init_isomip + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + use ocn_init_ssh_and_landIcePressure + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_isomip, & + ocn_init_validate_isomip + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_isomip +! +!> \brief Setup for ISoMIP test cases +!> \author Xylar Asay-Davis +!> \date 06/01/2015 +!> \details +!> This routine sets up the initial conditions for the ISOMIP test cases. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_isomip(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, & + config_isomip_vertical_level_distribution + + real (kind=RKIND), pointer :: config_isomip_bottom_depth, & + config_isomip_temperature, & + config_isomip_salinity, & + config_isomip_restoring_temperature, & + config_isomip_restoring_salinity, & + config_isomip_temperature_piston_velocity, & + config_isomip_salinity_piston_velocity, & + config_isomip_coriolis_parameter, & + config_isomip_southern_boundary, & + config_isomip_northern_boundary, & + config_isomip_western_boundary, & + config_isomip_eastern_boundary, & + config_isomip_angle, & + config_isomip_y1, & + config_isomip_z1, & + config_isomip_ice_fraction1, & + config_isomip_y2, & + config_isomip_z2, & + config_isomip_ice_fraction2, & + config_isomip_y3, & + config_isomip_z3, & + config_isomip_ice_fraction3, & + config_isomip_effective_density + + logical, pointer :: on_a_sphere + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: verticalMeshPool + type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: scratchPool + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool + + type (block_type), pointer :: block_ptr + + ! Define dimension pointers + integer, pointer :: nCells, nEdgesSolve, nVertLevels + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + ! Define variable pointers + integer, dimension(:), pointer :: maxLevelCell, modifySSHMask, landIceMask + real (kind=RKIND), dimension(:), pointer :: xCell, yCell,refBottomDepth, & + vertCoordMovementWeights, bottomDepth, & + fCell, fEdge, fVertex, dcEdge, & + landIceFraction, landIceSurfaceTemperature, & + refLayerThickness, refZMid, & + ssh + !real (kind=RKIND), dimension(:), pointer :: temperatureRestore, salinityRestore, maskRestore + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness, zMid + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + real (kind=RKIND), dimension(:, :), pointer :: activeTracersPistonVelocity, activeTracersSurfaceRestoringValue + + integer :: iCell, k, iFit + + real(kind=RKIND) :: x, y, ySouth, yNorth, xWest, xEast, & + pressure, dcEdgeMinGlobal, dcEdgeMin + + real(kind=RKIND), parameter :: eps = 1e-3_RKIND + + real(kind=RKIND), dimension(5) :: yFit, zFit, fracFit + + real(kind=RKIND), dimension(:), pointer :: columnThicknessFraction + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + + if(trim(config_init_configuration) .ne. trim('isomip')) return + + ! Setup configuration + call mpas_pool_get_config(ocnConfigs, 'config_isomip_vertical_level_distribution', config_isomip_vertical_level_distribution) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_bottom_depth', config_isomip_bottom_depth) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_temperature', config_isomip_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_salinity', config_isomip_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_restoring_temperature', config_isomip_restoring_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_restoring_salinity', config_isomip_restoring_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_temperature_piston_velocity', config_isomip_temperature_piston_velocity) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_salinity_piston_velocity', config_isomip_salinity_piston_velocity) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_coriolis_parameter', config_isomip_coriolis_parameter) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_southern_boundary', config_isomip_southern_boundary) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_northern_boundary', config_isomip_northern_boundary) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_western_boundary', config_isomip_western_boundary) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_eastern_boundary', config_isomip_eastern_boundary) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_y1', config_isomip_y1) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_z1', config_isomip_z1) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_ice_fraction1', config_isomip_ice_fraction1) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_y2', config_isomip_y2) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_z2', config_isomip_z2) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_ice_fraction2', config_isomip_ice_fraction2) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_y3', config_isomip_y3) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_z3', config_isomip_z3) + call mpas_pool_get_config(ocnConfigs, 'config_isomip_ice_fraction3', config_isomip_ice_fraction3) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + if ( on_a_sphere ) call mpas_log_write(& + 'The ISOMIP configuration can only be applied to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + ySouth = config_isomip_southern_boundary + yNorth = config_isomip_northern_boundary + xWest = config_isomip_western_boundary + xEast = config_isomip_eastern_boundary + + dcEdgeMin = 1.0E10_RKIND + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + block_ptr => block_ptr % next + end do + + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + yFit(2) = config_isomip_y1 + yFit(3) = config_isomip_y2 + yFit(4) = config_isomip_y3 + zFit(2) = config_isomip_z1 + zFit(3) = config_isomip_z2 + zFit(4) = config_isomip_z3 + + yFit(1) = min(yFit(2),ySouth)-eps + zFit(1) = zFit(2) + yFit(5) = max(yFit(4),yNorth)+eps + zFit(5) = zFit(4) + + fracFit(2) = config_isomip_ice_fraction1 + fracFit(3) = config_isomip_ice_fraction2 + fracFit(4) = config_isomip_ice_fraction3 + fracFit(1) = fracFit(2) + fracFit(5) = fracFit(4) + + allocate(columnThicknessFraction(nVertLevels)) + if(trim(config_isomip_vertical_level_distribution) == "constant") then + columnThicknessFraction(:) = 1.0_RKIND/nVertLevels + else if(trim(config_isomip_vertical_level_distribution) == "boundary_layer") then + if(mod(nVertLevels,2) == 0) then + columnThicknessFraction(nVertLevels/2) = 0.25_RKIND + columnThicknessFraction(nVertLevels/2+1) = 0.25_RKIND + else + columnThicknessFraction(nVertLevels/2) = 0.125_RKIND + columnThicknessFraction(nVertLevels/2+1) = 0.5_RKIND + columnThicknessFraction(nVertLevels/2+2) = 0.125_RKIND + end if + do k = nVertLevels/2-1, 2, -1 + columnThicknessFraction(k) = 0.5_RKIND*columnThicknessFraction(k+1) + columnThicknessFraction(nVertLevels-k+1) = 0.5_RKIND*columnThicknessFraction(nvertLevels-k) + end do + columnThicknessFraction(1) = columnThicknessFraction(2) + columnThicknessFraction(nVertLevels) = columnThicknessFraction(nVertLevels-1) + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + call mpas_pool_get_array(forcingPool, 'landIceSurfaceTemperature', landIceSurfaceTemperature) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(diagnosticsPool, 'modifySSHMask', modifySSHMask) + + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + + + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', & + activeTracersPistonVelocity, 1) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersSurfaceRestoringValue', & + activeTracersSurfaceRestoringValue, 1) + + + ! flat bottom + maxLevelCell(:) = nVertLevels + maxLevelCell(nCells+1) = -1 + bottomDepth(:) = abs(config_isomip_bottom_depth) + do iCell = 1, nCells + do k = 1, nVertLevels + restingThickness(k, iCell) = columnThicknessFraction(k)*bottomDepth(iCell) + end do + end do + + call ocn_mark_north_boundary(meshPool, yNorth, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, ySouth, dcEdgeMinGlobal, iErr) + call ocn_mark_east_boundary(meshPool, xEast, dcEdgeMinGlobal, iErr) + call ocn_mark_west_boundary(meshPool, xWest, dcEdgeMinGlobal, iErr) + + ! Set refBottomDepth + refBottomDepth(1) = columnThicknessFraction(1)*abs(config_isomip_bottom_depth) + do k = 2, nVertLevels + refBottomDepth(k) = refBottomDepth(k-1) + columnThicknessFraction(k)*abs(config_isomip_bottom_depth) + end do + + ! Compute refLayerThickness and refZMid + call ocn_compute_layerThickness_zMid_from_bottomDepth(refLayerThickness,refZMid, & + refBottomDepth,refBottomDepth(nVertLevels), & + nVertLevels,nVertLevels,iErr) + + fCell(:) = config_isomip_coriolis_parameter + fEdge(:) = config_isomip_coriolis_parameter + fVertex(:) = config_isomip_coriolis_parameter + + landIceFraction(:) = 0.0_RKIND + landIceSurfaceTemperature(:) = -25.0_RKIND !doesn't matter because ice is insulating + modifySSHMask(:) = 0 + landIceMask(:) = 0 + + + do iCell = 1, nCells + ! tracers computed using restingThickness with no ice shelf + x = xCell(iCell) + y = yCell(iCell) + + ! Compute iceDraft by linear fit + do iFit = 1, 4 + if((y >= yFit(iFit)) .and. (y <= yFit(iFit+1))) then + ssh(iCell) = (zFit(iFit+1) - zFit(iFit))*(y - yFit(iFit)) & + / (yFit(iFit+1) - yFit(iFit)) + zFit(iFit) + landIceFraction(iCell) = (fracFit(iFit+1) - fracFit(iFit))*(y - yFit(iFit)) & + / (yFit(iFit+1) - yFit(iFit)) + fracFit(iFit) + exit + end if + end do + if(landIceFraction(iCell) > 0.5_RKIND) then + landIceMask(iCell) = 1 + else + landIceFraction(iCell) = 0.0_RKIND + end if + + if(ssh(iCell) < 0.0_RKIND) then + modifySSHMask(iCell) = 1 + end if + + if(.not. associated(activeTracers)) then + call mpas_log_write( 'isomip test case needs activeTracers package to be active.', MPAS_LOG_CRIT) + return + end if + + activeTracers(index_temperature, :, iCell) = config_isomip_temperature + activeTracers(index_salinity, :, iCell) = config_isomip_salinity + if(associated(debugTracers)) then + debugTracers(index_tracer1, :, iCell) = 1.0_RKIND + end if + + ! Set surface temperature restoring value and rate + ! Value in units of C, piston velocity in units of m/s + if ( associated(activeTracersSurfaceRestoringValue) ) then + activeTracersSurfaceRestoringValue(index_temperature, iCell) = config_isomip_restoring_temperature + end if + if ( associated(activeTracersPistonVelocity) ) then + ! only restore where there *isn't* land ice + activeTracersPistonVelocity(index_temperature, iCell) = (1.0_RKIND - landIceFraction(iCell)) & + * config_isomip_temperature_piston_velocity + end if + + ! Set surface salinity restoring value and rate + ! Value in units of PSU, piston velocity in units of m/s + if ( associated(activeTracersSurfaceRestoringValue) ) then + activeTracersSurfaceRestoringValue(index_salinity, iCell) = config_isomip_restoring_salinity + end if + if ( associated(activeTracersPistonVelocity) ) then + ! only restore where there *isn't* land ice + activeTracersPistonVelocity(index_salinity, iCell) = (1.0_RKIND - landIceFraction(iCell)) & + * config_isomip_salinity_piston_velocity + end if + + end do + + block_ptr => block_ptr % next + end do + + deallocate(columnThicknessFraction) + + + ! compute the vertical grid (layerThickness, restingThickness, maxLevelCell, zMid) + ! based on ssh, bottomDepth and refBottomDepth + call ocn_init_ssh_and_landIcePressure_vertical_grid(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_vertical_grid failed.', MPAS_LOG_CRIT) + return + end if + + ! compute or update the land-ice pressure (or possibly SSH), also computing density along the way + ! If this is the initial guess, the vertical grid and activeTracers may also be recomputed based on SSH + call ocn_init_ssh_and_landIcePressure_balance(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_balance failed.', MPAS_LOG_CRIT) + return + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call ocn_compute_Haney_number(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_compute_Haney_number failed.', MPAS_LOG_CRIT) + return + end if + + block_ptr => block_ptr % next + end do + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_isomip!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_isomip +! +!> \brief Validation for ISOMIP test cases +!> \author Xylar Asay-Davis +!> \date 06/01/2015 +!> \details +!> This routine validates the configuration options for the ISOMIP test cases. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_isomip(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: configPool, packagePool + + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration, & + config_isomip_vertical_level_distribution + + integer, pointer :: config_vert_levels, config_isomip_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(trim(config_init_configuration) .ne. trim('isomip')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_isomip_vert_levels', config_isomip_vert_levels) + + if(config_vert_levels <= 0 .and. config_isomip_vert_levels > 0) then + config_vert_levels = config_isomip_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for isomip. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + call mpas_pool_get_config(configPool, 'config_isomip_vertical_level_distribution', config_isomip_vertical_level_distribution) + + if((trim(config_isomip_vertical_level_distribution) .ne. "constant") & + .and. (trim(config_isomip_vertical_level_distribution) .ne. "boundary_layer")) then + call mpas_log_write( 'Validation failed for isomip. Bad vertical level distribution.', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_isomip!}}} + + +!*********************************************************************** + +end module ocn_init_isomip + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_isomip_plus.F b/src/core_ocean/mode_init/mpas_ocn_init_isomip_plus.F new file mode 100644 index 0000000000..285f17acc5 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_isomip_plus.F @@ -0,0 +1,809 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_isomip_plus +! +!> \brief MPAS ocean initialize case -- ISOMIP+ +!> \author Xylar Asay-Davis +!> \date 07/21/2015 +!> \details +!> This module contains the routines for initializing the +!> the ISOMIP+ experiments +! +!----------------------------------------------------------------------- + +module ocn_init_isomip_plus + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_io + use mpas_io_streams + use mpas_dmpar + + use ocn_constants + + use ocn_init_cell_markers + + use ocn_init_vertical_grids + use ocn_init_ssh_and_landIcePressure + use ocn_init_interpolation + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_isomip_plus, & + ocn_init_validate_isomip_plus + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + integer :: nyTopo, nxTopo + type (field1DReal) :: topoY, topoX + type (field2DReal) :: iceTopoIC, bedTopoIC, oceanFracTopoIC, & + floatingFracTopoIC, smoothedDraftMaskTopoIC + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_isomip_plus +! +!> \brief Setup for ISOMIP+ experiments +!> \author Xylar Asay-Davis +!> \date 07/21/2015 +!> \details +!> This routine sets up the initial conditions for the ISOMIP+ experiments +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_isomip_plus(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + type (mpas_pool_type), pointer :: meshPool, statePool, diagnosticsPool, & + verticalMeshPool, forcingPool, & + tracersPool + type (mpas_pool_type), pointer :: tracersInteriorRestoringFieldsPool + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration, & + config_isomip_plus_vertical_level_distribution, & + config_land_ice_flux_mode + + real(kind=RKIND), pointer :: config_isomip_plus_max_bottom_depth, & + config_isomip_plus_init_top_temp, & + config_isomip_plus_init_bot_temp, & + config_isomip_plus_init_top_sal, & + config_isomip_plus_init_bot_sal, & + config_isomip_plus_restore_top_temp, & + config_isomip_plus_restore_bot_temp, & + config_isomip_plus_restore_top_sal, & + config_isomip_plus_restore_bot_sal, & + config_isomip_plus_restore_rate, & + config_isomip_plus_restore_evap_rate, & + config_isomip_plus_restore_xMin, & + config_isomip_plus_restore_xMax, & + config_isomip_plus_coriolis_parameter, & + config_isomip_plus_effective_density + + type (block_type), pointer :: block_ptr + + logical, pointer :: on_a_sphere + + integer, pointer :: nCells, nVertLevels + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + integer, dimension(:), pointer :: maxLevelCell + + real(kind=RKIND), dimension(:), pointer :: refBottomDepth, & + fCell, fEdge, fVertex, effectiveDensityInLandIce, xCell, & + refZMid, refLayerThickness + + real(kind=RKIND), dimension(:,:), pointer :: zMid + real(kind=RKIND), dimension(:,:), pointer :: layerThickness + real(kind=RKIND), dimension(:,:,:), pointer :: activeTracers + real(kind=RKIND), dimension(:,:,:), pointer :: debugTracers + + real(kind=RKIND), dimension(:,:,:), pointer :: activeTracersInteriorRestoringValue, activeTracersInteriorRestoringRate + real (kind=RKIND), dimension(:), pointer :: evaporationFlux, seaIceSalinityFlux, seaIceHeatFlux + + + real(kind=RKIND), dimension(:), pointer :: columnThicknessFraction + + integer :: iCell, k + real(kind=RKIND) :: frac, evapRate + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + + if (trim(config_init_configuration) /= "isomip_plus") return + + call mpas_pool_get_config(domain % configs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + if ( on_a_sphere ) call mpas_log_write('ISOMIP+ can only be applied to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_vertical_level_distribution', & + config_isomip_plus_vertical_level_distribution) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_max_bottom_depth', config_isomip_plus_max_bottom_depth) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_init_top_temp', config_isomip_plus_init_top_temp) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_init_bot_temp', config_isomip_plus_init_bot_temp) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_init_top_sal', config_isomip_plus_init_top_sal) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_init_bot_sal', config_isomip_plus_init_bot_sal) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_top_temp', config_isomip_plus_restore_top_temp) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_bot_temp', config_isomip_plus_restore_bot_temp) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_top_sal', config_isomip_plus_restore_top_sal) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_bot_sal', config_isomip_plus_restore_bot_sal) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_rate', config_isomip_plus_restore_rate) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_evap_rate', config_isomip_plus_restore_evap_rate) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_xMin', config_isomip_plus_restore_xMin) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_restore_xMax', config_isomip_plus_restore_xMax) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_coriolis_parameter', config_isomip_plus_coriolis_parameter) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_effective_density', config_isomip_plus_effective_density) + + + allocate(columnThicknessFraction(nVertLevels)) + if(trim(config_isomip_plus_vertical_level_distribution) == "constant") then + columnThicknessFraction(:) = 1.0_RKIND/nVertLevels + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + + ! Set refBottomDepth + refBottomDepth(1) = columnThicknessFraction(1)*abs(config_isomip_plus_max_bottom_depth) + do k = 2, nVertLevels + refBottomDepth(k) = refBottomDepth(k-1) + columnThicknessFraction(k)*abs(config_isomip_plus_max_bottom_depth) + end do + + ! Compute refLayerThickness and refZMid + call ocn_compute_layerThickness_zMid_from_bottomDepth(refLayerThickness,refZMid, & + refBottomDepth,refBottomDepth(nVertLevels), & + nVertLevels,nVertLevels,iErr) + + block_ptr => block_ptr % next + end do + + call mpas_log_write( 'Reading topography data.') + call ocn_init_setup_isomip_plus_read_topo(domain, iErr) + call mpas_log_write( 'Interpolating topography data.') + call ocn_init_setup_isomip_plus_interpolate_topo(domain, iErr) + call mpas_log_write( 'Cleaning up topography IC fields') + call ocn_init_isomip_plus_destroy_topo_fields() + + ! compute the vertical grid (layerThickness, restingThickness, maxLevelCell, zMid) bottomDepth and refBottomDepth + call ocn_init_ssh_and_landIcePressure_vertical_grid(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_vertical_grid failed.', MPAS_LOG_CRIT) + return + end if + + block_ptr => domain % blocklist + do while (associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + frac = (0.0_RKIND-zMid(k, iCell))/(0.0_RKIND-config_isomip_plus_max_bottom_depth) + activeTracers(index_temperature, k, iCell) = (1.0_RKIND - frac)*config_isomip_plus_init_top_temp & + + frac*config_isomip_plus_init_bot_temp + activeTracers(index_salinity, k, iCell) = (1.0_RKIND - frac)*config_isomip_plus_init_top_sal & + + frac*config_isomip_plus_init_bot_sal + end do + end do + + block_ptr => block_ptr % next + end do + + + ! compute or update the land-ice pressure (or possibly SSH), also computing density and bottomPressure along the way + ! If this is the initial guess, the vertical grid and activeTracers may also be recomputed based on SSH + call ocn_init_ssh_and_landIcePressure_balance(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_balance failed.', MPAS_LOG_CRIT) + return + end if + + + block_ptr => domain % blocklist + do while (associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + + call mpas_pool_get_subpool(forcingPool, 'tracersInteriorRestoringFields', tracersInteriorRestoringFieldsPool) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringRate', & + activeTracersInteriorRestoringRate, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, 'activeTracersInteriorRestoringValue', & + activeTracersInteriorRestoringValue, 1) + + call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) + call mpas_pool_get_array(forcingPool, 'seaIceSalinityFlux', seaIceSalinityFlux) + call mpas_pool_get_array(forcingPool, 'seaIceHeatFlux', seaIceHeatFlux) + + if(associated(activeTracersInteriorRestoringValue)) then + activeTracersInteriorRestoringRate(:,:,:) = 0.0_RKIND + activeTracersInteriorRestoringValue(:,:,:) = 0.0_RKIND + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + frac = (0.0_RKIND-zMid(k, iCell))/(0.0_RKIND-config_isomip_plus_max_bottom_depth) + activeTracersInteriorRestoringValue(index_temperature, k, iCell) & + = (1.0_RKIND - frac)*config_isomip_plus_restore_top_temp & + + frac*config_isomip_plus_restore_bot_temp + activeTracersInteriorRestoringValue(index_salinity, k, iCell) & + = (1.0_RKIND - frac)*config_isomip_plus_restore_top_sal & + + frac*config_isomip_plus_restore_bot_sal + end do + ! fraction of the way between southern and northern boundaries of restoring region + frac = max((xCell(iCell)-config_isomip_plus_restore_xMin) & + /(config_isomip_plus_restore_xMax-config_isomip_plus_restore_xMin), 0.0_RKIND) + ! restoring rate converted from 1/days to 1/s + activeTracersInteriorRestoringRate(:,:,iCell) = frac*config_isomip_plus_restore_rate/86400.0_RKIND + end do + end if + + if(associated(evaporationFlux) .and. associated(seaIceSalinityFlux) & + .and. associated(seaIceHeatFlux)) then + do iCell = 1, nCells + if((xCell(iCell) >= config_isomip_plus_restore_xMin) & + .and. (xCell(iCell) <= config_isomip_plus_restore_xMax)) then + ! convert to m/s, negative for evaporation rather than precipitation + evapRate = -config_isomip_plus_restore_evap_rate/31536000.0_RKIND + evaporationFlux(iCell) = rho_sw*evapRate + ! associated salt flux in kg/m^2/s of salt + seaIceSalinityFlux(iCell) = evapRate*config_isomip_plus_restore_top_sal/sflux_factor + ! associated heat flux in W/m^2 + seaIceHeatFlux(iCell) = evapRate*config_isomip_plus_restore_top_temp/hflux_factor + else + evaporationFlux(iCell) = 0.0_RKIND + seaIceSalinityFlux(iCell) = 0.0_RKIND + seaIceHeatFlux(iCell) = 0.0_RKIND + end if + end do + end if + + if(associated(debugTracers)) then + debugTracers(index_tracer1,:,:) = 1.0_RKIND + end if + + call ocn_mark_maxlevelcell(meshPool, iErr) + + ! Set Coriolis parameters + fCell(:) = config_isomip_plus_coriolis_parameter + fEdge(:) = config_isomip_plus_coriolis_parameter + fVertex(:) = config_isomip_plus_coriolis_parameter + + if(config_land_ice_flux_mode == 'coupled') then + call mpas_pool_get_array(statePool, 'effectiveDensityInLandIce', effectiveDensityInLandIce, 1) + effectiveDensityInLandIce(:) = config_isomip_plus_effective_density + end if + + block_ptr => block_ptr % next + end do + + call ocn_compute_Haney_number(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_compute_Haney_number failed.', MPAS_LOG_CRIT) + return + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_isomip_plus!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_isomip_plus_read_topo +! +!> \brief Read the topography IC file +!> \author Xylar Asay-Davis +!> \date 07/21/2015 +!> \details +!> This routine reads the topography IC file, including x and y coordinates. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_isomip_plus_read_topo(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (MPAS_Stream_type) :: topographyStream + + character (len=StrKIND), pointer :: config_isomip_plus_topography_file + + integer :: iY, iX + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_topography_file', config_isomip_plus_topography_file) + + ! Define stream for depth levels + call MPAS_createStream(topographyStream, domain % iocontext, config_isomip_plus_topography_file, & + MPAS_IO_NETCDF, MPAS_IO_READ, ierr=iErr) + + ! Setup topoY, topoX, bedTopoIC and iceTopoIC fields for stream to be read in + topoY % fieldName = 'y' + topoY % dimSizes(1) = nyTopo + topoY % dimNames(1) = 'y' + topoY % isVarArray = .false. + topoY % isPersistent = .true. + topoY % isActive = .true. + topoY % hasTimeDimension = .false. + topoY % block => domain % blocklist + allocate(topoY % attLists(1)) + allocate(topoY % array(nyTopo)) + + topoX % fieldName = 'x' + topoX % dimSizes(1) = nxTopo + topoX % dimNames(1) = 'x' + topoX % isVarArray = .false. + topoX % isPersistent = .true. + topoX % isActive = .true. + topoX % hasTimeDimension = .false. + topoX % block => domain % blocklist + allocate(topoX % attLists(1)) + allocate(topoX % array(nxTopo)) + + bedTopoIC % fieldName = 'Z_bed' + bedTopoIC % dimSizes(1) = nxTopo + bedTopoIC % dimSizes(2) = nyTopo + bedTopoIC % dimNames(1) = 'x' + bedTopoIC % dimNames(2) = 'y' + bedTopoIC % isVarArray = .false. + bedTopoIC % isPersistent = .true. + bedTopoIC % isActive = .true. + bedTopoIC % hasTimeDimension = .false. + bedTopoIC % block => domain % blocklist + allocate(bedTopoIC % attLists(1)) + allocate(bedTopoIC % array(nxTopo, nyTopo)) + + iceTopoIC % fieldName = 'Z_ice_draft' + iceTopoIC % dimSizes(1) = nxTopo + iceTopoIC % dimSizes(2) = nyTopo + iceTopoIC % dimNames(1) = 'x' + iceTopoIC % dimNames(2) = 'y' + iceTopoIC % isVarArray = .false. + iceTopoIC % isPersistent = .true. + iceTopoIC % isActive = .true. + iceTopoIC % hasTimeDimension = .false. + iceTopoIC % block => domain % blocklist + allocate(iceTopoIC % attLists(1)) + allocate(iceTopoIC % array(nxTopo, nyTopo)) + + ! ocean fraction is 1.0 - landFraction + oceanFracTopoIC % fieldName = 'landFraction' + oceanFracTopoIC % dimSizes(1) = nxTopo + oceanFracTopoIC % dimSizes(2) = nyTopo + oceanFracTopoIC % dimNames(1) = 'x' + oceanFracTopoIC % dimNames(2) = 'y' + oceanFracTopoIC % isVarArray = .false. + oceanFracTopoIC % isPersistent = .true. + oceanFracTopoIC % isActive = .true. + oceanFracTopoIC % hasTimeDimension = .false. + oceanFracTopoIC % block => domain % blocklist + allocate(oceanFracTopoIC % attLists(1)) + allocate(oceanFracTopoIC % array(nxTopo, nyTopo)) + + floatingFracTopoIC % fieldName = 'floatingIceFraction' + floatingFracTopoIC % dimSizes(1) = nxTopo + floatingFracTopoIC % dimSizes(2) = nyTopo + floatingFracTopoIC % dimNames(1) = 'x' + floatingFracTopoIC % dimNames(2) = 'y' + floatingFracTopoIC % isVarArray = .false. + floatingFracTopoIC % isPersistent = .true. + floatingFracTopoIC % isActive = .true. + floatingFracTopoIC % hasTimeDimension = .false. + floatingFracTopoIC % block => domain % blocklist + allocate(floatingFracTopoIC % attLists(1)) + allocate(floatingFracTopoIC % array(nxTopo, nyTopo)) + + smoothedDraftMaskTopoIC % fieldName = 'smoothedDraftMask' + smoothedDraftMaskTopoIC % dimSizes(1) = nxTopo + smoothedDraftMaskTopoIC % dimSizes(2) = nyTopo + smoothedDraftMaskTopoIC % dimNames(1) = 'x' + smoothedDraftMaskTopoIC % dimNames(2) = 'y' + smoothedDraftMaskTopoIC % isVarArray = .false. + smoothedDraftMaskTopoIC % isPersistent = .true. + smoothedDraftMaskTopoIC % isActive = .true. + smoothedDraftMaskTopoIC % hasTimeDimension = .false. + smoothedDraftMaskTopoIC % block => domain % blocklist + allocate(smoothedDraftMaskTopoIC % attLists(1)) + allocate(smoothedDraftMaskTopoIC % array(nxTopo, nyTopo)) + + ! Add topoY, topoX, bedTopoIC and iceTopoIC fields to stream + call MPAS_streamAddField(topographyStream, topoY, iErr) + call MPAS_streamAddField(topographyStream, topoX, iErr) + call MPAS_streamAddField(topographyStream, bedTopoIC, iErr) + call MPAS_streamAddField(topographyStream, iceTopoIC, iErr) + call MPAS_streamAddField(topographyStream, oceanFracTopoIC, iErr) + call MPAS_streamAddField(topographyStream, floatingFracTopoIC, iErr) + call MPAS_streamAddField(topographyStream, smoothedDraftMaskTopoIC, iErr) + + ! Read stream + call MPAS_readStream(topographyStream, 1, iErr) + + ! Close stream + call MPAS_closeStream(topographyStream) + + ! we read in the land fraction, and oceanFrac = 1.0 - landFraction + oceanFracTopoIC % array(:,:) = 1.0_RKIND - oceanFracTopoIC % array(:,:) + + end subroutine ocn_init_setup_isomip_plus_read_topo!}}} + +!*********************************************************************** +! +! routine ocn_init_setup_isomip_plus_interpolate_topo +! +!> \brief Interpolate the topography IC to MPAS mesh +!> \author Xylar Asay-Davis +!> \date 07/21/2015 +!> \details +!> This routine interpolates topography data to the MPAS mesh. Currently it +!> uses a bilinear interpolation +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_isomip_plus_interpolate_topo(domain, iErr)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, & + forcingPool, diagnosticsPool, & + statePool + + real (kind=RKIND), dimension(:), pointer :: yCell, xCell, bottomDepth, refBottomDepth, & + ssh, landIceFraction, oceanFracObserved, & + bottomDepthObserved + + integer, pointer :: nCells, nVertLevels + + integer, dimension(:), pointer :: maxLevelCell, modifySSHMask + + integer :: iCell, k, maxLevel + + integer, pointer :: config_isomip_plus_minimum_levels + real(kind=RKIND), pointer :: config_isomip_plus_min_column_thickness, config_isomip_plus_min_ocean_fraction + + real (kind=RKIND) :: depth + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_minimum_levels', & + config_isomip_plus_minimum_levels) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_min_column_thickness', & + config_isomip_plus_min_column_thickness) + call mpas_pool_get_config(domain % configs, 'config_isomip_plus_min_ocean_fraction', config_isomip_plus_min_ocean_fraction) + + ! weight bed topo and ice draft by the fraction of ocean before interpolating + bedTopoIC % array(:,:) = bedTopoIC % array(:,:) * oceanFracTopoIC % array(:,:) + iceTopoIC % array(:,:) = iceTopoIC % array(:,:) * oceanFracTopoIC % array(:,:) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepthObserved', bottomDepthObserved) + call mpas_pool_get_array(meshPool, 'oceanFracObserved', oceanFracObserved) + + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(diagnosticsPool, 'modifySSHMask', modifySSHMask) + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + + + ! use landIceFraction as temporary storage here + call ocn_init_interpolation_bilinear_horiz(topoX % array, topoY % array, & + smoothedDraftMaskTopoIC % array, nxTopo, nyTopo, & + xCell, yCell, landIceFraction, nCells) + + do iCell = 1,nCells + if(landIceFraction(iCell) > 0.01_RKIND) then + modifySSHMask(iCell) = 1 + else + modifySSHMask(iCell) = 0 + end if + end do + + call ocn_init_interpolation_bilinear_horiz(topoX % array, topoY % array, & + oceanFracTopoIC % array, nxTopo, nyTopo, & + xCell, yCell, oceanFracObserved, nCells) + + call ocn_init_interpolation_bilinear_horiz(topoX % array, topoY % array, & + floatingFracTopoIC % array, nxTopo, nyTopo, & + xCell, yCell, landIceFraction, nCells) + + call ocn_init_interpolation_bilinear_horiz(topoX % array, topoY % array, & + bedTopoIC % array, nxTopo, nyTopo, & + xCell, yCell, bottomDepthObserved, nCells) + + bottomDepthObserved(:) = -bottomDepthObserved(:) + + call ocn_init_interpolation_bilinear_horiz(topoX % array, topoY % array, & + iceTopoIC % array, nxTopo, nyTopo, & + xCell, yCell, ssh, nCells) + + do iCell = 1,nCells + if(oceanFracObserved(iCell) > config_isomip_plus_min_ocean_fraction) then + ! we multiplied by the fraction of ocean before; divide by the interpolated ocean fraction + bottomDepthObserved(iCell) = bottomDepthObserved(iCell)/oceanFracObserved(iCell) + else + bottomDepthObserved(iCell) = 0.0_RKIND + end if + + if(bottomDepthObserved(iCell) > 0.0_RKIND) then + ! we multiplied by the fraction of ocean before; divide by the interpolated ocean fraction + ssh(iCell) = ssh(iCell)/oceanFracObserved(iCell) + ! bottomDepth is lowered (increased) if necessary to accommodate the minimum column thickness + bottomDepth(iCell) = max(bottomDepthObserved(iCell),-ssh(iCell)+config_isomip_plus_min_column_thickness) + ! landIceFraction is the fraction of the *ocean* that is covered by land ice, so divide + ! by the fraction of the cell that is ocean + landIceFraction(iCell) = landIceFraction(iCell)/oceanFracObserved(iCell) + + if(landIceFraction(iCell) < 0.5_RKIND) then + landIceFraction(iCell) = 0.0_RKIND + end if + + maxLevelCell(iCell) = -1 + do k = 1, nVertLevels + depth = refBottomDepth(k) + + if (depth >= bottomDepth(iCell)) then + maxLevelCell(iCell) = k + exit + end if + end do + + if (maxLevelCell(iCell) == -1) then + maxLevelCell(iCell) = nVertLevels + bottomDepth(iCell) = refBottomDepth( nVertLevels ) + ! this time we may need to raise the ssh if the column is too thin because we can't go any deeper + ssh(iCell) = max(ssh(iCell), -bottomDepth(iCell)+config_isomip_plus_min_column_thickness) + else if (maxLevelCell(iCell) <= config_isomip_plus_minimum_levels) then + maxLevelCell(iCell) = config_isomip_plus_minimum_levels + bottomDepth(iCell) = refBottomDepth( config_isomip_plus_minimum_levels ) + end if + + else ! not an ocean cell + bottomDepth(iCell) = 0.0_RKIND + maxLevelCell(iCell) = -1 + ssh(iCell) = 0.0_RKIND + landIceFraction(iCell) = 0.0_RKIND + modifySSHMask(iCell) = 0 + end if + end do !iCell + + ! Enforce minimum number of layers in ocean cells. + do iCell = 1, nCells + if (maxLevelCell(iCell) > 0 .and. maxLevelCell(iCell) < config_isomip_plus_minimum_levels) then + maxLevelCell(iCell) = config_isomip_plus_minimum_levels + bottomDepth(iCell) = refBottomDepth(config_isomip_plus_minimum_levels) + end if + end do + + block_ptr => block_ptr % next + end do + + end subroutine ocn_init_setup_isomip_plus_interpolate_topo!}}} + +!*********************************************************************** +! +! routine ocn_init_isomip_plus_destroy_topo_fields +! +!> \brief Topography field cleanup routine +!> \author Xylar Asay-Davis +!> \date 07/21/2015 +!> \details +!> This routine destroys the fields that were created to hold topography +!> initial condition information +! +!----------------------------------------------------------------------- + + subroutine ocn_init_isomip_plus_destroy_topo_fields()!{{{ + deallocate(bedTopoIC % array) + deallocate(iceTopoIC % array) + deallocate(oceanFracTopoIC % array) + deallocate(floatingFracTopoIC % array) + deallocate(topoY % array) + deallocate(topoX % array) + end subroutine ocn_init_isomip_plus_destroy_topo_fields!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_isomip_plus +! +!> \brief Validation for ISOMIP+ experiments +!> \author Xylar Asay-Davis +!> \date 07/21/2015 +!> \details +!> This routine validates the configuration options for the ISOMIP+ experiments. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_isomip_plus(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: configPool, packagePool + type (mpas_io_context_type), intent(inout), target :: iocontext + integer, intent(out) :: iErr + + type (mpas_io_context_type), pointer :: iocontext_ptr + + type (MPAS_IO_Handle_type) :: inputFile + + character (len=StrKIND), pointer :: config_init_configuration, & + config_isomip_plus_topography_file, & + config_isomip_plus_vertical_level_distribution, & + config_land_ice_flux_mode + + integer, pointer :: config_vert_levels, config_isomip_plus_vert_levels + + iErr = 0 + + iocontext_ptr => iocontext + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('isomip_plus')) return + + + call mpas_pool_get_config(configPool, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + if(config_land_ice_flux_mode .ne. 'standalone' .and. config_land_ice_flux_mode .ne. 'coupled') then + call mpas_log_write( 'Validation failed for isomip_plus. config_land_ice_flux_mode must either'// & + ' be standalone or coupled.', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_isomip_plus_vert_levels', config_isomip_plus_vert_levels) + + if(config_vert_levels <= 0 .and. config_isomip_plus_vert_levels > 0) then + config_vert_levels = config_isomip_plus_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for isomip_plus. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + call mpas_pool_get_config(configPool, 'config_isomip_plus_vertical_level_distribution', & + config_isomip_plus_vertical_level_distribution) + + if(trim(config_isomip_plus_vertical_level_distribution) .ne. "constant") then + call mpas_log_write( 'Validation failed for isomip_plus. Bad vertical level distribution.', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + + call mpas_pool_get_config(configPool, 'config_isomip_plus_topography_file', config_isomip_plus_topography_file) + + if (trim(config_isomip_plus_topography_file) == 'none') then + call mpas_log_write( 'Validation failed for ISOMIP+. Invalid filename for config_isomip_plus_topography_file', MPAS_LOG_CRIT) + iErr = 1 + return + end if + + inputFile = MPAS_io_open(config_isomip_plus_topography_file, MPAS_IO_READ, MPAS_IO_NETCDF, iocontext_ptr, ierr=iErr) + if(iErr > 0) then + call mpas_log_write( 'Validation failed for isomip_plus. Could not open file:'// & + trim(config_isomip_plus_topography_file) , MPAS_LOG_CRIT) + return + end if + + call MPAS_io_inq_dim(inputFile, 'x', nxTopo, iErr) + if(iErr /= 0) then + call mpas_log_write( 'Validation failed for isomip_plus. Could not find dim x in file:'// & + trim(config_isomip_plus_topography_file) , MPAS_LOG_CRIT) + return + end if + + call MPAS_io_inq_dim(inputFile, 'y', nyTopo, iErr) + if(iErr /= 0) then + call mpas_log_write( 'Validation failed for isomip_plus. Could not find dim y in file:'// & + trim(config_isomip_plus_topography_file) , MPAS_LOG_CRIT) + return + end if + + call MPAS_io_close(inputFile, iErr) + if(iErr /= 0) then + call mpas_log_write( 'Validation failed for isomip_plus. Could not close file:'// & + trim(config_isomip_plus_topography_file), MPAS_LOG_CRIT) + return + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_isomip_plus!}}} + +!*********************************************************************** + +end module ocn_init_isomip_plus + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_lock_exchange.F b/src/core_ocean/mode_init/mpas_ocn_init_lock_exchange.F new file mode 100644 index 0000000000..b46f508bc2 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_lock_exchange.F @@ -0,0 +1,352 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_lock_exchange +! +!> \brief MPAS ocean initialize case -- Lock Exchange +!> \author Doug Jacobsen +!> \date 02/18/2014 +!> \details +!> This module contains the routines for initializing the +!> the lock exchange test case +! +!----------------------------------------------------------------------- + +module ocn_init_lock_exchange + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_lock_exchange, & + ocn_init_validate_lock_exchange + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_lock_exchange +! +!> \brief Setup for lock exchange test case +!> \author Doug Jacobsen +!> \date 02/18/2014 +!> \details +!> This routine sets up the initial conditions for the lock exchange test case. +!> It is setup in the y direction, such that everything in the southern half of +!> the domain has a temperature of 5.0C and the northern half has a value of +!> 30.0C. Salinity is setup as a constant 35PSU. +!> No windstress is specified, and layerThickness is constant depending on the input parameter +!> config_lock_exchange_bottom_depth. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_lock_exchange(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, xMinGlobal, xMaxGlobal, dcEdgeMinGlobal + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, statePool, verticalMeshPool, tracersPool + + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid, config_lock_exchange_layer_type, & + config_lock_exchange_direction + + integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels, nVertLevelsP1, index_temperature, index_salinity, index_tracer1 + + integer, dimension(:), pointer :: maxLevelCell + + real (kind=RKIND), pointer :: config_lock_exchange_cold_temperature, config_lock_exchange_warm_temperature, & + config_lock_exchange_salinity, config_lock_exchange_bottom_depth, & + config_lock_exchange_isopycnal_min_thickness + + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, bottomDepth, refBottomDepthTopOfCell, refBottomDepth, & + vertCoordMovementWeights, dcEdge + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + logical, pointer :: on_a_sphere + + integer :: iCell, k + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + + if (config_init_configuration .ne. trim('lock_exchange')) return + + call mpas_pool_get_config(ocnConfigs, 'config_vertical_grid', config_vertical_grid) + + call mpas_pool_get_config(ocnConfigs, 'config_lock_exchange_cold_temperature', config_lock_exchange_cold_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_lock_exchange_warm_temperature', config_lock_exchange_warm_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_lock_exchange_salinity', config_lock_exchange_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_lock_exchange_bottom_depth', config_lock_exchange_bottom_depth) + call mpas_pool_get_config(ocnConfigs, 'config_lock_exchange_direction', config_lock_exchange_direction) + call mpas_pool_get_config(ocnConfigs, 'config_lock_exchange_isopycnal_min_thickness', & + config_lock_exchange_isopycnal_min_thickness) + call mpas_pool_get_config(ocnConfigs, 'config_lock_exchange_layer_type', config_lock_exchange_layer_type) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write('The lock exchange configuration can not be ' & + // 'applied to spherical meshes', MPAS_LOG_CRIT) + + ! Define interface locations + allocate( interfaceLocations( nVertLevelsP1 ) ) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + ! Initalize y values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max y value. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + xMin = min( xMin, minval(xCell(1:nCellssolve))) + xMax = max( xMax, maxval(xCell(1:nCellssolve))) + yMin = min( yMin, minval(yCell(1:nCellssolve))) + yMax = max( yMax, maxval(yCell(1:nCellssolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgessolve))) + + block_ptr => block_ptr % next + end do + + ! Determine global min and max y value. This is so the domain + ! can be split into north and south. + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'refBottomDepthTopOfCell', refBottomDepthTopOfCell) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + + do iCell = 1, nCellsSolve + ! Set layerThickness, and restingThickness + + if ( trim(config_lock_exchange_layer_type) == 'z-level' ) then + ! Set layerThickness and restingThickness + do k = 1, nVertLevels + layerThickness(k, iCell) = config_lock_exchange_bottom_depth * ( interfaceLocations(k+1) & + - interfaceLocations(k) ) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + + ! Set temperature + if ( associated(activeTracers) ) then + if ( trim(config_lock_exchange_direction) == 'x' ) then + if(xCell(iCell) < xMinGlobal + (xMaxGlobal - xMinGlobal) * 0.5_RKIND) then + activeTracers(index_temperature, :, iCell) = config_lock_exchange_cold_temperature + else + activeTracers(index_temperature, :, iCell) = config_lock_exchange_warm_temperature + end if + + elseif ( trim(config_lock_exchange_direction) == 'y' ) then + if(yCell(iCell) < yMinGlobal + (yMaxGlobal - yMinGlobal) * 0.5_RKIND) then + activeTracers(index_temperature, :, iCell) = config_lock_exchange_cold_temperature + else + activeTracers(index_temperature, :, iCell) = config_lock_exchange_warm_temperature + end if + + elseif ( trim(config_lock_exchange_direction) == 'z' ) then + activeTracers(index_temperature, 1:nVertLevels/2, iCell) = config_lock_exchange_warm_temperature + activeTracers(index_temperature, nVertLevels/2+1:nVertLevels, iCell) = & + config_lock_exchange_cold_temperature + else + call mpas_log_write('MPAS-ocean: Error: wrong choice of config_lock_exchange_direction') + end if + end if + + else if ( trim(config_lock_exchange_layer_type) == 'isopycnal' ) then + if ( associated(activeTracers) ) then + activeTracers(index_temperature, 1, iCell) = config_lock_exchange_warm_temperature + activeTracers(index_temperature, 2:nVertLevels, iCell) = config_lock_exchange_cold_temperature + end if + + if(yCell(iCell) < (yMaxGlobal - yMinGlobal) * 0.5_RKIND) then + layerThickness(1, iCell) = config_lock_exchange_isopycnal_min_thickness + layerThickness(2:nVertLevels, iCell) = config_lock_exchange_bottom_depth & + - config_lock_exchange_isopycnal_min_thickness + else + layerThickness(1, iCell) = config_lock_exchange_bottom_depth - config_lock_exchange_isopycnal_min_thickness + layerThickness(2:nVertLevels, iCell) = config_lock_exchange_isopycnal_min_thickness + end if + else + call mpas_log_write('MPAS-ocean: Error: wrong choice of config_lock_exchange_layer_type') + end if + + ! Set salinity + if ( associated(activeTracers) ) then + activeTracers(index_salinity, :, iCell) = config_lock_exchange_salinity + end if + + ! Set debugging tracer + if ( associated(debugTracers) ) then + do k = 1, nVertLevels + debugTracers(index_tracer1, k, iCell) = 1.0_RKIND + enddo + end if + + ! Set bottomDepth + bottomDepth(iCell) = config_lock_exchange_bottom_depth + + ! Set maxLevelCell + maxLevelCell(iCell) = nVertLevels + end do + + ! Set refBottomDepth and refBottomDepthTopOfCell + do k = 1, nVertLevels + refBottomDepth(k) = config_lock_exchange_bottom_depth * interfaceLocations(k+1) + refBottomDepthTopOfCell(k) = config_lock_exchange_bottom_depth * interfaceLocations(k) + end do + + refBottomDepthTopOfCell(nVertLevels+1) = interfaceLocations(nVertLevels+1) * config_lock_exchange_bottom_depth + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + block_ptr => block_ptr % next + end do + + deallocate(interfaceLocations) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_lock_exchange!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_lock_exchange +! +!> \brief Validation for lock exchange test case +!> \author Doug Jacobsen +!> \date 02/20/2014 +!> \details +!> This routine validates the configuration options for the lock exchange test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_lock_exchange(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool + type (mpas_pool_type), intent(inout) :: packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + integer, pointer :: config_vert_levels, config_lock_exchange_vert_levels + character (len=StrKIND), pointer :: config_init_configuration + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('lock_exchange')) return + + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_lock_exchange_vert_levels', config_lock_exchange_vert_levels) + + if(config_vert_levels <= 0 .and. config_lock_exchange_vert_levels > 0) then + config_vert_levels = config_lock_exchange_vert_levels + else if(config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for lock exchange test case. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_lock_exchange!}}} + +!*********************************************************************** + +end module ocn_init_lock_exchange + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_mode.F b/src/core_ocean/mode_init/mpas_ocn_init_mode.F new file mode 100644 index 0000000000..129ea41a13 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_mode.F @@ -0,0 +1,392 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_mode +! +!> \brief Main driver for MPAS ocean core +!> \author Doug Jacobsen, Mark Petersen, Todd Ringler +!> \date September 2011 +!> \details +!> This module contains initialization and timestep drivers for +!> the MPAS ocean core. +! +!----------------------------------------------------------------------- + +module ocn_init_mode + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_stream_manager + use mpas_timekeeping + use mpas_dmpar + use mpas_timer + use mpas_io_units + use mpas_constants + use mpas_decomp + + use ocn_init_routines + + use ocn_equation_of_state + + use ocn_constants + + use ocn_init_spherical_utils + + !use ocn_init_TEMPLATE + use ocn_init_baroclinic_channel + use ocn_init_lock_exchange + use ocn_init_internal_waves + use ocn_init_overflow + use ocn_init_global_ocean + use ocn_init_cvmix_WSwSBF + use ocn_init_iso + use ocn_init_soma + use ocn_init_ziso + use ocn_init_sub_ice_shelf_2D + use ocn_init_periodic_planar + use ocn_init_ecosys_column + use ocn_init_sea_mount + use ocn_init_isomip + use ocn_init_isomip_plus + + implicit none + private + + public :: ocn_init_mode_init, ocn_init_mode_run, ocn_init_mode_finalize + public :: ocn_init_mode_setup_clock, ocn_init_mode_validate_configuration + + contains + +!*********************************************************************** +! +! function ocn_init_mode_init +! +!> \brief Initialize MPAS-Ocean core in init mode +!> \author Doug Jacobsen +!> \date 06/15/2015 +!> \details +!> This function calls all initializations required to start MPAS-Ocean in +!> init mode. +! +!----------------------------------------------------------------------- + + function ocn_init_mode_init(domain, startTimeStamp) result(ierr)!{{{ + + type (domain_type), intent(inout) :: domain + character(len=*), intent(out) :: startTimeStamp + integer :: ierr + + real (kind=RKIND) :: dt + type (block_type), pointer :: block + + integer :: err_tmp + integer, pointer :: nVertLevels + real (kind=RKIND) :: maxDensity, maxDensity_global + real (kind=RKIND), dimension(:), pointer :: meshDensity + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: diagnosticsPool + + character (len=StrKIND), pointer :: xtime + type (MPAS_Time_Type) :: startTime + type (MPAS_TimeInterval_type) :: timeStep + + logical, pointer :: config_do_restart, config_filter_btr_mode, config_conduct_tests + logical, pointer :: config_write_stats_on_startup + character (len=StrKIND), pointer :: config_vert_coord_movement, config_pressure_gradient_type + real (kind=RKIND), pointer :: config_maxMeshDensity + + ierr = 0 + + ! + ! Set startTimeStamp based on the start time of the simulation clock + ! + startTime = mpas_get_clock_time(domain % clock, MPAS_START_TIME, err_tmp) + call mpas_get_time(startTime, dateTimeString=startTimeStamp) + ierr = ior(ierr, err_tmp) + + ! Setup ocean config pool + call ocn_constants_init(domain % configs, domain % packages) + + ! + ! Read input data for model + ! + call mpas_timer_start('io_read', .false.) + call MPAS_stream_mgr_read(domain % streamManager, streamID='input_init', ierr=err_tmp) + call mpas_timer_stop('io_read') + + call mpas_timer_start('reset_io_alarms', .false.) + call mpas_stream_mgr_reset_alarms(domain % streamManager, streamID='input_init', ierr=err_tmp) + ! call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_OUTPUT, ierr=err_tmp) + call mpas_timer_stop('reset_io_alarms') + + ! Read the remaining input streams + call mpas_timer_start('io_read', .false.) + call mpas_stream_mgr_read(domain % streamManager, ierr=err_tmp) + ierr = ior(ierr, err_tmp) + call mpas_timer_stop('io_read') + call mpas_timer_start('reset_io_alarms', .false.) + call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_INPUT, ierr=err_tmp) + ierr = ior(ierr, err_tmp) + call mpas_timer_stop('reset_io_alarms') + + ! Initialize submodules before initializing blocks. + call ocn_equation_of_state_init(err_tmp) + ierr = ior(ierr, err_tmp) + if(ierr.eq.1) then + call mpas_log_write('An error was encountered while initializing the MPAS-Ocean init mode', MPAS_LOG_CRIT) + endif + + ! + ! Initialize core + ! + timeStep = mpas_get_clock_timestep(domain % clock, ierr=err_tmp) + call mpas_get_timeInterval(timeStep, dt=dt) + + block => domain % blocklist + do while (associated(block)) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_array(diagnosticsPool, 'xtime', xtime) + xtime = startTimeStamp + block => block % next + end do + + ! Expand sphere if it needs to be expanded + call ocn_init_expand_sphere(domain, domain % streamManager, a, ierr) + + end function ocn_init_mode_init!}}} + +!*********************************************************************** +! +! function ocn_init_mode_setup_clock +! +!> \brief Setup MPAS-Ocean clock +!> \author Doug Jacobsen +!> \date 06/15/2015 +!> \details +!> This function initializes the MPAS-Ocean clock for the init mode. +! +!----------------------------------------------------------------------- + function ocn_init_mode_setup_clock(core_clock, configs) result(ierr)!{{{ + + implicit none + + type (MPAS_Clock_type), intent(inout) :: core_clock + type (mpas_pool_type), intent(inout) :: configs + integer :: ierr + + type (MPAS_Time_Type) :: startTime, stopTime, alarmStartTime + type (MPAS_TimeInterval_type) :: runDuration, timeStep, alarmTimeStep + character(len=StrKIND) :: restartTimeStamp + character(len=StrKIND), pointer :: config_start_time, config_stop_time, config_run_duration + character(len=StrKIND), pointer :: config_dt, config_restart_timestamp_name + integer :: err_tmp + + ierr = 0 + + call mpas_pool_get_config(configs, 'config_dt', config_dt) + call mpas_pool_get_config(configs, 'config_start_time', config_start_time) + call mpas_pool_get_config(configs, 'config_stop_time', config_stop_time) + call mpas_pool_get_config(configs, 'config_run_duration', config_run_duration) + call mpas_pool_get_config(configs, 'config_restart_timestamp_name', config_restart_timestamp_name) + + call mpas_set_time(startTime, dateTimeString=config_start_time, ierr=err_tmp) + call mpas_set_timeInterval(timeStep, timeString=config_dt, ierr=err_tmp) + if (trim(config_run_duration) /= "none") then + call mpas_set_timeInterval(runDuration, timeString=config_run_duration, ierr=err_tmp) + call mpas_create_clock(core_clock, startTime=startTime, timeStep=timeStep, runDuration=runDuration, ierr=err_tmp) + + if (trim(config_stop_time) /= "none") then + call mpas_set_time(curr_time=stopTime, dateTimeString=config_stop_time, ierr=err_tmp) + if(startTime + runduration /= stopTime) then + call mpas_log_write( 'Warning: config_run_duration and config_stop_time are inconsitent: using config_run_duration.') + end if + end if + else if (trim(config_stop_time) /= "none") then + call mpas_set_time(curr_time=stopTime, dateTimeString=config_stop_time, ierr=err_tmp) + call mpas_create_clock(core_clock, startTime=startTime, timeStep=timeStep, stopTime=stopTime, ierr=err_tmp) + else + call mpas_log_write( ' Warning: config_run_duration and config_start_time were "none", setting run duration to 1 second.') + call mpas_set_timeInterval(runDuration, timeString="0000_00:00:01", ierr=err_tmp) + call mpas_create_clock(core_clock, startTime=startTime, timeStep=timeStep, runDuration=runDuration, ierr=err_tmp) + end if + + end function ocn_init_mode_setup_clock!}}} + +!*********************************************************************** +! +! function ocn_init_mode_run +! +!> \brief MPAS-Ocean init mode run step +!> \author Doug Jacobsen +!> \date 06/15/2015 +!> \details +!> This function sets up the initial configuration using the MPAS-Ocean init +!> mode. +! +!----------------------------------------------------------------------- + + function ocn_init_mode_run(domain) result(iErr)!{{{ + + type (domain_type), intent(inout) :: domain + integer :: iErr + + integer :: itimestep + real (kind=RKIND) :: dt + type (block_type), pointer :: block_ptr + + type (MPAS_Time_Type) :: currTime + character(len=StrKIND) :: timeStamp + + type (mpas_pool_type), pointer :: averagePool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: forcingPool + + type (MPAS_timeInterval_type) :: timeStep + + character (len=StrKIND), pointer :: config_init_configuration + + ierr = 0 + + ! Eventually, dt should be domain specific + timeStep = mpas_get_clock_timestep(domain % clock, ierr=ierr) + call mpas_get_timeInterval(timeStep, dt=dt) + + currTime = mpas_get_clock_time(domain % clock, MPAS_NOW, ierr) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=ierr) + + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + call mpas_log_write( ' Generating configuration: ' // trim(config_init_configuration)) + + call ocn_init_setup_baroclinic_channel(domain, ierr) + call ocn_init_setup_lock_exchange(domain, ierr) + call ocn_init_setup_internal_waves(domain, ierr) + call ocn_init_setup_overflow(domain, ierr) + call ocn_init_setup_global_ocean(domain, ierr) + call ocn_init_setup_cvmix_WSwSBF(domain, ierr) + call ocn_init_setup_iso(domain, ierr) + call ocn_init_setup_soma(domain, ierr) + call ocn_init_setup_ziso(domain, ierr) + call ocn_init_setup_sub_ice_shelf_2D(domain, ierr) + call ocn_init_setup_periodic_planar(domain, ierr) + call ocn_init_setup_ecosys_column(domain, ierr) + call ocn_init_setup_sea_mount(domain, ierr) + call ocn_init_setup_isomip(domain, ierr) + call ocn_init_setup_isomip_plus(domain, ierr) + !call ocn_init_setup_TEMPLATE(domain, ierr) + + call mpas_log_write( ' Completed setup of: ' // trim(config_init_configuration)) + call mpas_timer_start('io_write', .false.) + call mpas_stream_mgr_write(domain % streamManager, ierr=ierr) + call mpas_timer_stop('io_write') + call mpas_timer_start('reset_io_alarms', .false.) + call mpas_stream_mgr_reset_alarms(domain % streamManager, direction=MPAS_STREAM_OUTPUT, ierr=ierr) + call mpas_timer_stop('reset_io_alarms') + + end function ocn_init_mode_run!}}} + +!*********************************************************************** +! +! function ocn_init_mode_finalize +! +!> \brief MPAS-Ocean init mode run step +!> \author Doug Jacobsen +!> \date 06/15/2015 +!> \details +!> This function sets up the initial configuration using the MPAS-Ocean init +!> mode. +! +!----------------------------------------------------------------------- + + function ocn_init_mode_finalize(domain) result(iErr)!{{{ + + type (domain_type), intent(inout) :: domain + integer :: ierr + + iErr = 0 + + call mpas_destroy_clock(domain % clock, ierr) + + call mpas_decomp_destroy_decomp_list(domain % decompositions) + + end function ocn_init_mode_finalize!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_configuration +! +!> \brief Configuration validation routine +!> \author Doug Jacobsen +!> \date 03/20/2015 +!> \details +!> This routine is used to validate the namelist options against the +!> configuration definition. It will call the validate routines for each of the +!> configurations to ensure namelist options are set in a valid way. +! +!----------------------------------------------------------------------- + subroutine ocn_init_mode_validate_configuration(configPool, packagePool, iocontext, iErr)!{{{ + + type (mpas_pool_type), intent(inout) :: configPool !< Input: Pool with namelist options + type (mpas_pool_type), intent(inout) :: packagePool !< Input: Pool with packages + type (mpas_io_context_type), intent(inout) :: iocontext + integer, intent(out) :: iErr !< Output: Error core + + logical, pointer :: cullCellsActive + + logical, pointer :: config_write_cull_cell_mask + + integer :: err_tmp + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_write_cull_cell_mask', config_write_cull_cell_mask) + call mpas_pool_get_package(packagePool, 'cullCellsActive', cullCellsActive) + + if ( config_write_cull_cell_mask ) then + cullCellsActive = .true. + end if + + call ocn_init_validate_baroclinic_channel(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_lock_exchange(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_internal_waves(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_overflow(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_global_ocean(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_cvmix_WSwSBF(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_iso(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_soma(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_ziso(configPool, packagePool, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_sub_ice_shelf_2D(configPool, packagePool,iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_periodic_planar(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_ecosys_column(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_sea_mount(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_isomip(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + call ocn_init_validate_isomip_plus(configPool, packagePool, iocontext, iErr=err_tmp) + iErr = ior(iErr, err_tmp) + ! call ocn_init_validate_TEMPLATE(configPool, packagePool, iocontext, iErr=err_tmp) + ! iErr = ior(iErr, err_tmp) + end subroutine ocn_init_mode_validate_configuration!}}} + +end module ocn_init_mode + +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_overflow.F b/src/core_ocean/mode_init/mpas_ocn_init_overflow.F new file mode 100644 index 0000000000..846018551d --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_overflow.F @@ -0,0 +1,358 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_overflow +! +!> \brief MPAS ocean initialize case -- Overflow +!> \author Doug Jacobsen +!> \date 02/18/2014 +!> \details +!> This module contains the routines for initializing the +!> the overflow test case +! +!----------------------------------------------------------------------- + +module ocn_init_overflow + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_overflow, & + ocn_init_validate_overflow + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_overflow +! +!> \brief Setup for overflow test case +!> \author Doug Jacobsen +!> \date 02/18/2014 +!> \details +!> This routine sets up the initial conditions for the overflow test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_overflow(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + real (kind=RKIND) :: yMin, yMax, dcEdgeMin + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, dcEdgeMinGlobal + real (kind=RKIND) :: plugWidth + real (kind=RKIND) :: slopeCenter, slopeWidth + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: verticalMeshPool + type (mpas_pool_type), pointer :: tracersPool + + integer :: iCell, k + + ! Define dimensions + integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels, nVertLevelsP1 + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + ! Define arrays + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: yCell, refBottomDepth, bottomDepth, vertCoordMovementWeights, dcEdge + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + + ! Define configs + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid, config_overflow_layer_type + logical, pointer :: config_overflow_use_distances + real (kind=RKIND), pointer :: config_overflow_plug_width_dist, config_overflow_slope_center_dist, & + config_overflow_slope_width_dist, config_overflow_plug_width_frac, & + config_overflow_slope_center_frac, config_overflow_slope_width_frac, & + config_overflow_bottom_depth, config_overflow_ridge_depth, & + config_overflow_plug_temperature, config_overflow_domain_temperature, & + config_overflow_salinity, config_overflow_isopycnal_min_thickness + + + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + iErr = 0 + + + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('overflow')) return + + call mpas_pool_get_config(ocnConfigs, 'config_vertical_grid', config_vertical_grid) + + call mpas_pool_get_config(ocnConfigs, 'config_overflow_use_distances', config_overflow_use_distances) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_plug_width_dist', config_overflow_plug_width_dist) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_slope_center_dist', config_overflow_slope_center_dist) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_slope_width_dist', config_overflow_slope_width_dist) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_plug_width_frac', config_overflow_plug_width_frac) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_slope_center_frac', config_overflow_slope_center_frac) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_slope_width_frac', config_overflow_slope_width_frac) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_bottom_depth', config_overflow_bottom_depth) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_ridge_depth', config_overflow_ridge_depth) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_plug_temperature', config_overflow_plug_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_domain_temperature', config_overflow_domain_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_salinity', config_overflow_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_layer_type', config_overflow_layer_type) + call mpas_pool_get_config(ocnConfigs, 'config_overflow_isopycnal_min_thickness', config_overflow_isopycnal_min_thickness) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid(config_vertical_grid, interfaceLocations) + + ! Initalize y values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max y value. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + yMin = min( yMin, minval(yCell(1:nCellssolve))) + yMax = max( yMax, maxval(yCell(1:nCellssolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgessolve))) + + block_ptr => block_ptr % next + end do + + ! Determine global min and max y value. This is so the domain + ! can be split into north and south. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + if ( config_overflow_use_distances ) then + plugWidth = config_overflow_plug_width_dist + slopeCenter = yMinGlobal + config_overflow_slope_center_dist + slopeWidth = config_overflow_slope_width_dist + else + plugWidth = (yMaxGlobal - yMinGlobal) * config_overflow_plug_width_frac + slopeCenter = yMinGlobal + (yMaxGlobal - yMinGlobal) * config_overflow_slope_center_frac + slopeWidth = (yMaxGlobal - yMinGlobal) * config_overflow_slope_width_frac + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + + ! Set refBottomDepth, bottomDepth, and maxLevelCell + do k = 1, nVertLevels + refBottomDepth(k) = config_overflow_bottom_depth * interfaceLocations(k+1) + end do + + do iCell = 1, nCellsSolve + ! From Mehmet Ilicak: + ! depth=2000 + ! val1 = 500 is top of ridge + ! h(i,j) = val1 + 0.5*(depth-val1) * (1.0+TANH((lon(i,j)-40000.0)/7000.0)) + bottomDepth(iCell) = config_overflow_ridge_depth & + + 0.5_RKIND*(config_overflow_bottom_depth - config_overflow_ridge_depth) & + * (1.0_RKIND+tanh((yCell(iCell) - slopeCenter)/slopeWidth)) + + if ( trim(config_overflow_layer_type) == 'sigma' .or. trim(config_overflow_layer_type) == 'isopycnal' ) then + maxLevelCell(iCell) = nVertLevels + else if ( trim(config_overflow_layer_type) == 'z-level' ) then + maxLevelCell(iCell) = -1 + do k = 1, nVertLevels + if (bottomDepth(iCell) .le. refBottomDepth(k) .and. & + maxLevelCell(iCell) == -1) then + + maxLevelCell(iCell) = k + end if + end do + end if + end do + + do iCell = 1, nCellsSolve + ! Set temperature + if ( associated(activeTracers) ) then + if ( trim(config_overflow_layer_type) == 'sigma' .or. trim(config_overflow_layer_type) == 'z-level' ) then + do k = 1, maxLevelCell(iCell) + if(yCell(iCell) < yMinGlobal + plugWidth) then + activeTracers(index_temperature, k, iCell) = config_overflow_plug_temperature + else + activeTracers(index_temperature, k, iCell) = config_overflow_domain_temperature + end if + end do + else if ( trim(config_overflow_layer_type) == 'isopycnal' ) then + activeTracers(index_temperature, 1, :) = config_overflow_domain_temperature + activeTracers(index_temperature, 2:nVertLevels, :) = config_overflow_plug_temperature + end if + end if + + ! Set layerThickness and restingThickness + if ( trim(config_overflow_layer_type) == 'z-level' ) then + do k = 1, maxLevelCell(iCell) + layerThickness(k, iCell) = config_overflow_bottom_depth * (interfaceLocations(k+1) - interfaceLocations(k)) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + else if ( trim(config_overflow_layer_type) == 'sigma' ) then + do k = 1, nVertLevels + layerThickness(k, iCell) = bottomDepth(iCell) / nVertLevels + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + else if ( trim(config_overflow_layer_type) == 'isopycnal' ) then + ! Set layerThickness. Normally isopycnal overflow has only two layers. + if ( yCell(iCell) < yMinGlobal + plugWidth) then + layerThickness(1, iCell) = config_overflow_isopycnal_min_thickness + layerThickness(2:nVertLevels, iCell) = bottomDepth(iCell) - config_overflow_isopycnal_min_thickness + restingThickness(:, iCell) = layerThickness(:, iCell) + else + layerThickness(1, iCell) = bottomDepth(iCell) - config_overflow_isopycnal_min_thickness + layerThickness(2:nVertLevels, iCell) = config_overflow_isopycnal_min_thickness + restingThickness(:, iCell) = layerThickness(:, iCell) + end if + end if + + ! Set salinity + if ( associated(activeTracers) ) then + activeTracers(index_salinity, :, iCell) = config_overflow_salinity + end if + + ! Set debug tracer + if ( associated(debugTracers) ) then + do k = 1, nVertLevels + debugTracers(index_tracer1, k, iCell) = 1.0_RKIND + end do + end if + + end do + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + block_ptr => block_ptr % next + end do + + deallocate(interfaceLocations) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_overflow!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_overflow +! +!> \brief Validation for overflow test case +!> \author Doug Jacobsen +!> \date 02/20/2014 +!> \details +!> This routine validates the configuration options for the overflow test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_overflow(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool, packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_overflow_vert_levels, config_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('overflow')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_overflow_vert_levels', config_overflow_vert_levels) + + if(config_vert_levels <= 0 .and. config_overflow_vert_levels > 0) then + config_vert_levels = config_overflow_vert_levels + else if(config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for overflow test case. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_overflow!}}} + +!*********************************************************************** + +end module ocn_init_overflow + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_periodic_planar.F b/src/core_ocean/mode_init/mpas_ocn_init_periodic_planar.F new file mode 100644 index 0000000000..099f44215b --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_periodic_planar.F @@ -0,0 +1,411 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_periodic_planar +! +!> \brief MPAS ocean initialize case -- periodic_planar +!> \author Phillip J. Wolfram +!> \date 10/14/2015 +!> \details +!> This module contains the routines for initializing the +!> periodic_planar initial condition, which is a constant +!> velocity in a periodic domain. +! +!----------------------------------------------------------------------- + +module ocn_init_periodic_planar + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_stream_manager + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_periodic_planar, & + ocn_init_validate_periodic_planar + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_periodic_planar +! +!> \brief Setup for this initial condition +!> \author Phillip J. Wolfram +!> \date 10/14/2015 +!> \details +!> This routine sets up the initial conditions for this case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_periodic_planar(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + ! local work variables + type (block_type), pointer :: block_ptr + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, statePool, forcingPool, tracersPool, scratchPool + + integer :: iCell, iEdge, iVertex, k, idx + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid + logical, pointer :: config_write_cull_cell_mask + + ! periodic_planar test case run-time configuration parameters + real (kind=RKIND), pointer :: config_periodic_planar_bottom_depth, config_periodic_planar_velocity_strength + + integer, pointer :: config_periodic_planar_vert_levels + + + ! Define dimension pointers + integer, pointer :: nVertLevels, nCellsSolve, nEdgesSolve, nVerticesSolve, nVertLevelsP1 + integer, pointer :: index_temperature, index_salinity + + ! Define variable pointers + logical, pointer :: on_a_sphere + integer, dimension(:), pointer :: maxLevelCell + integer, dimension(:,:), pointer :: verticesOnEdge + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, xEdge, yEdge, xVertex, yVertex, refBottomDepth, refZMid, & + vertCoordMovementWeights, bottomDepth, & + fCell, fEdge, fVertex, dcEdge, dvEdge + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness, normalVelocity + real (kind=RKIND), dimension(:), pointer :: psiVertex + type (field1DReal), pointer :: psiVertexField + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin, dcEdgeMinGlobal + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, yMidGlobal, xMinGlobal, xMaxGlobal + real(kind=RKIND), pointer :: y_period + character (len=StrKIND) :: streamID + integer :: directionProperty + + ! assume no error + iErr = 0 + + + ! test if periodic_planar is the desired configuration + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('periodic_planar')) return + + call mpas_log_write( 'Starting initialization of planar periodic grid') + + ! get config variables !{{{ + call mpas_pool_get_config(domain % configs, 'config_write_cull_cell_mask', config_write_cull_cell_mask) + call mpas_pool_get_config(domain % configs, 'config_periodic_planar_bottom_depth', config_periodic_planar_bottom_depth) + call mpas_pool_get_config(domain % configs, 'config_periodic_planar_vert_levels', config_periodic_planar_vert_levels) + call mpas_pool_get_config(domain % configs, 'config_periodic_planar_velocity_strength', & + config_periodic_planar_velocity_strength) + call mpas_pool_get_config(domain % configs, 'config_vertical_grid', config_vertical_grid) + !}}} + + ! Determine vertical grid for configuration + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + ! test if configure settings are invalid + if ( on_a_sphere ) call mpas_log_write('The planar periodic configuration can ' & + // 'only be applied to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + ! Define interface locations + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + ! assign config variables + nVertLevels = config_periodic_planar_vert_levels + nVertLevelsP1 = nVertLevels + 1 + + ! keep all cells on planar, periodic mesh (no culling) + + !-------------------------------------------------------------------- + ! Use this section to make boundaries non-periodic + !-------------------------------------------------------------------- + + ! Initalize min/max values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + yMin = min( yMin, minval(yCell(1:nCellsSolve))) + yMax = max( yMax, maxval(yCell(1:nCellsSolve))) + xMin = min( xMin, minval(xCell(1:nCellsSolve))) + xMax = max( xMax, maxval(xCell(1:nCellsSolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + + block_ptr => block_ptr % next + end do ! do while(associated(block_ptr)) + + + !-------------------------------------------------------------------- + ! Use this section to set initial values + !-------------------------------------------------------------------- + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_field(scratchPool, 'psiVertex', psiVertexField) + call mpas_allocate_scratch_field(psiVertexField, .false.) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'xEdge', xEdge) + call mpas_pool_get_array(meshPool, 'yEdge', yEdge) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(scratchPool, 'psiVertex', psiVertex) + call mpas_pool_get_array(meshPool, 'verticesOnEdge', verticesOnEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=1) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + ! Determine global min and max values. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + ! mark north / south boundaries + if(config_write_cull_cell_mask) then + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + call mpas_pool_get_config(meshPool, 'y_period', y_period) + y_period = 0.0_RKIND + endif + call mpas_stream_mgr_begin_iteration(domain % streamManager) + do while (mpas_stream_mgr_get_next_stream(domain % streamManager, streamID, directionProperty)) + if ( directionProperty == MPAS_STREAM_OUTPUT .or. directionProperty == MPAS_STREAM_INPUT_OUTPUT ) then + call mpas_stream_mgr_add_att(domain % streamManager, 'y_period', 0.0_RKIND, streamID) + end if + end do + + ! Set refBottomDepth and refZMid + do k = 1, nVertLevels + refBottomDepth(k) = config_periodic_planar_bottom_depth * interfaceLocations(k+1) + refZMid(k) = - 0.5_RKIND * (interfaceLocations(k+1) + interfaceLocations(k)) * config_periodic_planar_bottom_depth + end do + + ! set bottomDepth and maxLevelCell !{{{{ + bottomDepth(:) = 0.0_RKIND + do iCell = 1, nCellsSolve + + bottomDepth(iCell) = config_periodic_planar_bottom_depth + + ! Determine maxLevelCell based on bottomDepth and refBottomDepth + ! Also set botomDepth based on refBottomDepth, since + ! above bottomDepth was set with continuous analytical functions, + ! and needs to be discrete + maxLevelCell(iCell) = nVertLevels + if (nVertLevels > 1) then + do k = 1, nVertLevels + if (bottomDepth(iCell) < refBottomDepth(k)) then + maxLevelCell(iCell) = k-1 + bottomDepth(iCell) = refBottomDepth(k-1) + exit + end if + end do + end if + + enddo ! Looping through with iCell !}}} + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + do iCell = 1, nCellsSolve + + ! Set initial temperature + idx = index_temperature + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = 0.0_RKIND + end do + + ! Set initial salinity + idx = index_salinity + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = 0.0_RKIND + end do + + ! Set layerThickness and restingThickness + ! Uniform layer thickness + do k = 1, nVertLevels + layerThickness(k, iCell) = config_periodic_planar_bottom_depth * ( interfaceLocations(k+1) - interfaceLocations(k) ) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + + ! Set bottomDepth (above) + + ! Set maxLevelCell (above) + + end do ! do iCell + + ! Set Coriolis parameters, if other than zero + do iCell = 1, nCellsSolve + fCell(iCell) = 0.0_RKIND + end do + do iEdge = 1, nEdgesSolve + fEdge(iEdge) = 0.0_RKIND + end do + do iVertex = 1, nVerticesSolve + fVertex(iVertex) = 0.0_RKIND + end do + + ! Setup stream function for velocity + do iVertex = 1, nVerticesSolve ! need to loop over all vertices to ensure correct value for edges + psiVertex(iVertex) = yVertex(iVertex)*config_periodic_planar_velocity_strength + end do + + !boundaryVertex => block_ptr % mesh % boundaryVertex % array(1,:) + !block_ptr % scratch % psiVertex % array = & + ! boundaryVertex * & + ! sum(boundaryVertex * block_ptr % scratch % psiVertex % array) & + ! /sum(boundaryVertex) & + ! + (1-boundaryVertex) * block_ptr % scratch % psiVertex % array + + ! Define normalVelocity as (grad psiVertex) + do iEdge = 1, nEdgesSolve + normalVelocity(:,iEdge) = -1.0_RKIND * (psiVertex(verticesOnEdge(1, iEdge)) & + - psiVertex(verticesOnEdge(2, iEdge)))/dvEdge(iEdge) + end do + + block_ptr => block_ptr % next + end do ! do while(associated(block_ptr)) + call mpas_deallocate_scratch_field(psiVertexField, .false.) + + call mpas_log_write( 'Finishing initialization of periodic_planar') + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_periodic_planar!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_periodic_planar +! +!> \brief Validation for this initial condition +!> \author Phillip J. Wolfram +!> \date 10/14/2015 +!> \details +!> This routine validates the configuration options for this case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_periodic_planar(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool, packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_periodic_planar_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('periodic_planar')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_periodic_planar_vert_levels', config_periodic_planar_vert_levels) + + if(config_vert_levels <= 0 .and. config_periodic_planar_vert_levels > 0) then + config_vert_levels = config_periodic_planar_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for periodic_planar. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_periodic_planar!}}} + + +!*********************************************************************** + +end module ocn_init_periodic_planar + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_sea_mount.F b/src/core_ocean/mode_init/mpas_ocn_init_sea_mount.F new file mode 100644 index 0000000000..72e3e00b02 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_sea_mount.F @@ -0,0 +1,376 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_sea_mount +! +!> \brief MPAS ocean initialize case -- Sea Mount +!> \author Mark Petersen +!> \date 08/10/15 +!> \details +!> This module contains the routines for initializing the +!> the sea mount test case +! +!----------------------------------------------------------------------- + +module ocn_init_sea_mount + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_sea_mount, & + ocn_init_validate_sea_mount + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_sea_mount +! +!> \brief Setup for sea mount test case +!> \author Mark Petersen +!> \date 08/10/15 +!> \details +!> This routine sets up the initial conditions for the sea mount test case. +!> It should also ensure the mesh that was input is valid for the configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_sea_mount(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin, dcEdgeMinGlobal + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, yMidGlobal, xMinGlobal, xMaxGlobal, xMidGlobal + real (kind=RKIND) :: densityCell, z, radius + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: verticalMeshPool + type (mpas_pool_type), pointer :: tracersPool + + integer :: iCell, k, idx + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid, config_sea_mount_layer_type, & + config_sea_mount_stratification_type + real (kind=RKIND), pointer :: config_sea_mount_width, & + config_sea_mount_bottom_depth, config_sea_mount_height,config_sea_mount_radius, & + config_sea_mount_density_coef_linear, config_sea_mount_density_coef_exp, & + config_sea_mount_density_gradient_linear, config_sea_mount_density_gradient_exp, & + config_sea_mount_density_depth_linear, config_sea_mount_density_depth_exp, & + config_sea_mount_density_ref, config_sea_mount_density_alpha, config_sea_mount_density_Tref, & + config_sea_mount_salinity, config_sea_mount_coriolis_parameter + + ! Define dimension pointers + integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels, nVertLevelsP1 + integer, pointer :: index_temperature, index_salinity + + ! Define variable pointers + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: xCell, yCell,refBottomDepth, refZMid, refLayerThickness, & + vertCoordMovementWeights, bottomDepth, & + fCell, fEdge, fVertex, dcEdge + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + ! Define local interfaceLocations variable + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + logical, pointer :: on_a_sphere + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('sea_mount')) return + + call mpas_pool_get_config(ocnConfigs, 'config_vertical_grid', config_vertical_grid) + + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_layer_type', config_sea_mount_layer_type) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_stratification_type', config_sea_mount_stratification_type) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_width', config_sea_mount_width) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_bottom_depth', config_sea_mount_bottom_depth) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_height', config_sea_mount_height) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_radius', config_sea_mount_radius) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_coef_linear', config_sea_mount_density_coef_linear) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_gradient_linear', config_sea_mount_density_gradient_linear) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_depth_linear', config_sea_mount_density_depth_linear) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_coef_exp', config_sea_mount_density_coef_exp) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_gradient_exp', config_sea_mount_density_gradient_exp) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_depth_exp', config_sea_mount_density_depth_exp) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_ref', config_sea_mount_density_ref) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_alpha', config_sea_mount_density_alpha) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_density_Tref', config_sea_mount_density_Tref) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_salinity', config_sea_mount_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_sea_mount_coriolis_parameter', config_sea_mount_coriolis_parameter) + + ! Determine vertical grid for configuration + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write('The sea mount configuration can ' & + // 'only be applied to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + ! Initalize min/max values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + yMin = min( yMin, minval(yCell(1:nCellsSolve))) + yMax = max( yMax, maxval(yCell(1:nCellsSolve))) + xMin = min( xMin, minval(xCell(1:nCellsSolve))) + xMax = max( xMax, maxval(xCell(1:nCellsSolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + + block_ptr => block_ptr % next + end do + + ! Determine global min and max values. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + xMidGlobal = (xMinGlobal + xMaxGlobal) * 0.5_RKIND + yMidGlobal = (yMinGlobal + yMaxGlobal) * 0.5_RKIND + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + + ! mrp note: doubly non-periodic. Can delete these later. + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_east_boundary(meshPool, xMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_west_boundary(meshPool, xMinGlobal, dcEdgeMinGlobal, iErr) + + ! Set refBottomDepth and refZMid + do k = 1, nVertLevels + refBottomDepth(k) = config_sea_mount_bottom_depth * interfaceLocations(k+1) + refZMid(k) = - 0.5_RKIND * (interfaceLocations(k+1) + interfaceLocations(k)) * config_sea_mount_bottom_depth + end do + + refLayerThickness(1) = refBottomDepth(1) + do k = 2, nVertLevels + refLayerThickness(k) = refBottomDepth(k) - refBottomDepth(k-1) + end do + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + do iCell = 1, nCellsSolve + + radius = sqrt( (xCell(iCell)-xMidGlobal)**2 + (yCell(iCell)-yMidGlobal)**2 ) + + ! Set bottomDepth. See Beckmann and Haidvogel 1993 eqn 12, Shchepetkin 2003 eqn 4.2 + bottomDepth(iCell) = config_sea_mount_bottom_depth - config_sea_mount_height & + * exp(-(max(radius-config_sea_mount_radius, 0.0_RKIND))**2 / config_sea_mount_width**2) + + ! Set maxLevelCell and layerThickness + if ( trim(config_sea_mount_layer_type) == 'z-level' ) then + maxLevelCell(iCell) = -1 + do k = 1, nVertLevels + if (bottomDepth(iCell) .le. refBottomDepth(k)) then + maxLevelCell(iCell) = k + ! make full cell only: + bottomDepth(iCell) = refBottomDepth(k) + exit + end if + end do + do k = 1, maxLevelCell(iCell) + layerThickness(k, iCell) = refLayerThickness(k) + end do + else if ( trim(config_sea_mount_layer_type) == 'sigma') then + maxLevelCell(iCell) = nVertLevels + do k = 1, nVertLevels + layerThickness(k, iCell) = bottomDepth(iCell) / nVertLevels + end do + end if + + ! Set restingThickness + do k = 1, maxLevelCell(iCell) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + + ! Set stratification using temperature. See Beckmann and Haidvogel 1993 eqn 15-16. + idx = index_temperature + z = 0.0_RKIND + do k = 1, maxLevelCell(iCell) + + z = z - 0.5_RKIND * layerThickness(k, iCell) + + if ( trim(config_sea_mount_stratification_type) == 'linear' ) then + densityCell = config_sea_mount_density_coef_linear - config_sea_mount_density_gradient_linear * z & + / config_sea_mount_density_depth_linear + elseif ( trim(config_sea_mount_stratification_type) == 'exponential' ) then + densityCell = config_sea_mount_density_coef_exp - config_sea_mount_density_gradient_exp * exp( z & + / config_sea_mount_density_depth_exp) + else + call mpas_log_write('MPAS-ocean: Error: Incorrect config_sea_mount_stratification_type: ' & + // config_sea_mount_stratification_type) + end if + + ! Back-solve linear EOS for temperature, with S=S_ref + ! T = T_ref - (rho - rho_ref)/alpha + activeTracers(idx, k, iCell) = config_sea_mount_density_Tref - (densityCell - config_sea_mount_density_ref) & + / config_sea_mount_density_alpha + z = z - 0.5_RKIND * layerThickness(k, iCell) + + end do + + ! Set salinity + idx = index_salinity + activeTracers(idx, :, iCell) = config_sea_mount_salinity + + end do + + ! Set Coriolis parameters + fCell(:) = config_sea_mount_coriolis_parameter + fEdge(:) = config_sea_mount_coriolis_parameter + fVertex(:) = config_sea_mount_coriolis_parameter + + block_ptr => block_ptr % next + end do + + deallocate(interfaceLocations) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_sea_mount!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_sea_mount +! +!> \brief Validation for sea mount test case +!> \author Mark Petersen +!> \date 08/10/15 +!> \details +!> This routine validates the configuration options for the sea mount test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_sea_mount(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: configPool, packagePool + type (mpas_io_context_type), intent(inout), target :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_sea_mount_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('sea_mount')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_sea_mount_vert_levels', config_sea_mount_vert_levels) + + if(config_vert_levels <= 0 .and. config_sea_mount_vert_levels > 0) then + config_vert_levels = config_sea_mount_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for sea mount. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_sea_mount!}}} + +!*********************************************************************** + +end module ocn_init_sea_mount + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_soma.F b/src/core_ocean/mode_init/mpas_ocn_init_soma.F new file mode 100644 index 0000000000..cbeef5441a --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_soma.F @@ -0,0 +1,454 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_soma +! +!> \brief MPAS ocean initialize case -- Simulating Ocean Mesoscale Activity (SOMA) +!> \author Todd Ringler +!> \date 10/08/2013 +!> \details +!> This module contains the routines for initializing the +!> the idealized SOMA test case +! +!----------------------------------------------------------------------- + +module ocn_init_soma + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_soma, & + ocn_init_validate_soma + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_soma +! +!> \brief Setup for soma test case +!> \author Todd Ringler +!> \date 02/26/2014 +!> \details +!> This routine sets up the initial conditions for the +!> SOMA configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_soma(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + ! local work variables + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, statePool, forcingPool, tracersPool + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool, tracersInteriorRestoringFieldsPool + + integer :: iCell, iEdge, iVertex, k, idx + real (kind=RKIND) :: distance, deltaLon, deltaLat, xDistance, yDistance, zMid, sphereRadius + real (kind=RKIND) :: lonCurrent, latCurrent + real (kind=RKIND) :: deltay, depth, factor, latCenter, lonCenter, windStress + real (kind=RKIND) :: temperature, salinity + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid + + ! SOMA test case run-time configuration parameters + integer, pointer :: config_soma_vert_levels + real (kind=RKIND), pointer :: config_eos_linear_alpha + real (kind=RKIND), pointer :: config_soma_surface_salinity + real (kind=RKIND), pointer :: config_soma_surface_temperature + real (kind=RKIND), pointer :: config_soma_density_difference_linear + real (kind=RKIND), pointer :: config_soma_thermocline_depth + real (kind=RKIND), pointer :: config_soma_center_latitude + real (kind=RKIND), pointer :: config_soma_center_longitude + real (kind=RKIND), pointer :: config_soma_domain_width + real (kind=RKIND), pointer :: config_soma_shelf_width + real (kind=RKIND), pointer :: config_soma_shelf_depth + real (kind=RKIND), pointer :: config_soma_bottom_depth + real (kind=RKIND), pointer :: config_soma_phi + real (kind=RKIND), pointer :: config_soma_ref_density + real (kind=RKIND), pointer :: config_soma_density_difference + real (kind=RKIND), pointer :: config_soma_surface_temp_restoring_at_center_latitude + real (kind=RKIND), pointer :: config_soma_surface_temp_restoring_latitude_gradient + real (kind=RKIND), pointer :: config_soma_restoring_temp_piston_vel + + ! Define dimension pointers + integer, pointer :: nVertLevels, nCells, nVertLevelsP1, nCellsSolve, nEdgesSolve, nVerticesSolve + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + ! Define variable pointers + logical, pointer :: on_a_sphere, config_soma_use_surface_temp_restoring + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: refBottomDepth, bottomCell, refZMid, fCell, fEdge, fVertex + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), pointer :: sphere_radius + real (kind=RKIND), dimension(:), pointer :: lonCell, latCell, latEdge, latVertex, bottomDepth + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + real (kind=RKIND), dimension(:, :), pointer :: activeTracersPistonVelocity, activeTracersSurfaceRestoringValue + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracersInteriorRestoringValue, activeTracersInteriorRestoringRate + real (kind=RKIND), dimension(:), pointer :: windStressZonal, windStressMeridional + + ! assume no error + iErr = 0 + + ! test if SOMA is the desired configuration + call mpas_pool_get_config(domain % configs, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('soma')) return + + ! get config variables + call mpas_pool_get_config(domain % configs, 'config_eos_linear_alpha', config_eos_linear_alpha) + call mpas_pool_get_config(domain % configs, 'config_soma_density_difference_linear', config_soma_density_difference_linear) + call mpas_pool_get_config(domain % configs, 'config_soma_thermocline_depth', config_soma_thermocline_depth) + call mpas_pool_get_config(domain % configs, 'config_soma_surface_temperature', config_soma_surface_temperature) + call mpas_pool_get_config(domain % configs, 'config_soma_surface_salinity', config_soma_surface_salinity) + call mpas_pool_get_config(domain % configs, 'config_vertical_grid', config_vertical_grid) + call mpas_pool_get_config(domain % configs, 'config_soma_vert_levels', config_soma_vert_levels) + call mpas_pool_get_config(domain % configs, 'config_soma_center_latitude', config_soma_center_latitude) + call mpas_pool_get_config(domain % configs, 'config_soma_center_longitude', config_soma_center_longitude) + call mpas_pool_get_config(domain % configs, 'config_soma_domain_width', config_soma_domain_width) + call mpas_pool_get_config(domain % configs, 'config_soma_shelf_width', config_soma_shelf_width) + call mpas_pool_get_config(domain % configs, 'config_soma_shelf_depth', config_soma_shelf_depth) + call mpas_pool_get_config(domain % configs, 'config_soma_bottom_depth', config_soma_bottom_depth) + call mpas_pool_get_config(domain % configs, 'config_soma_phi', config_soma_phi) + call mpas_pool_get_config(domain % configs, 'config_soma_ref_density', config_soma_ref_density) + call mpas_pool_get_config(domain % configs, 'config_soma_density_difference', config_soma_density_difference) + call mpas_pool_get_config(domain % configs, 'config_soma_surface_temp_restoring_at_center_latitude', & + config_soma_surface_temp_restoring_at_center_latitude) + call mpas_pool_get_config(domain % configs, 'config_soma_surface_temp_restoring_latitude_gradient', & + config_soma_surface_temp_restoring_latitude_gradient) + call mpas_pool_get_config(domain % configs, 'config_soma_restoring_temp_piston_vel', config_soma_restoring_temp_piston_vel) + call mpas_pool_get_config(domain % configs, 'config_soma_use_surface_temp_restoring', config_soma_use_surface_temp_restoring) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_config(meshPool, 'sphere_radius', sphere_radius) + sphereRadius = sphere_radius + + ! error checking + if(.not. on_a_sphere) then + call mpas_log_write( 'SOMA test case can only be defined on a spherical mesh.', MPAS_LOG_CRIT) + iErr = 1 + return + else + call mpas_log_write( 'SOMA test case using spherical radius of size: $f ', realArgs=(/ sphereRadius /) ) + end if + + ! assign config variables + nVertLevels = config_soma_vert_levels + nVertLevelsP1 = nVertLevels + 1 + + ! Define interface locations + allocate( interfaceLocations( nVertLevelsP1 ) ) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + ! set center of SOMA domain + ! Convert center locations to radians from degrees + latCenter = config_soma_center_latitude * pii / 180.0_RKIND + lonCenter = config_soma_center_longitude * pii / 180.0_RKIND + + ! Setup the vertical grid and layerThickness initial condition + call mpas_log_write( 'setting up vertical grid and layer thickness') + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + + ! Set layerThickness and restingThickness + ! Uniform layer thickness across lat/lon + do k = 1, nVertLevels + layerThickness(k, :) = config_soma_bottom_depth * ( interfaceLocations(k+1) - interfaceLocations(k) ) + restingThickness(k, :) = layerThickness(k, :) + end do + + ! Set refBottomDepth + do k = 1, nVertLevels + refBottomDepth(k) = config_soma_bottom_depth * interfaceLocations(k+1) + refZMid(k) = -config_soma_bottom_depth * (interfaceLocations(k)+interfaceLocations(k+1))/2.0_RKIND + end do + + block_ptr => block_ptr % next + + end do + + ! Set bathymetry + call mpas_log_write( 'setting up bathymetry') + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + + ! set bottomDepth + bottomDepth(:) = 0.0_RKIND + do iCell = 1, nCells + lonCurrent = lonCell(iCell) + latCurrent = latCell(iCell) + + deltaLon = abs(lonCurrent - lonCenter) + if (deltaLon .gt. pii) deltaLon = deltaLon - 2.0_RKIND*pii + deltaLat = latCurrent - latCenter + xDistance = deltaLon * sphereRadius * cos(latCurrent) + yDistance = deltaLat * sphereRadius + distance = sqrt( xDistance**2 + yDistance**2 ) + factor = 1.0 - distance**2 / config_soma_domain_width**2 + + if(factor > config_soma_shelf_width) then + bottomDepth(iCell) = config_soma_shelf_depth + (config_soma_bottom_depth-config_soma_shelf_depth)/2.0_RKIND & + * (1.0 + tanh(factor/config_soma_phi)) + else + bottomDepth(iCell) = -1.0_RKIND + endif + + ! Set maxLevelCell to -1 for cells to be culled + if (bottomDepth(iCell) > 0.0_RKIND) then + maxLevelCell(iCell) = 1 + else + maxLevelCell(iCell) = -1 + endif + + ! Determine maxLevelCell based on bottomDepth and refBottomDepth + ! Also set botomDepth based on refBottomDepth, since + ! above bottomDepth was set with continuous analytical functions, + ! and needs to be discrete + if (maxLevelCell(iCell) > 0) then + maxLevelCell(iCell) = nVertLevels + if (nVertLevels .gt. 1) then + do k = 1, nVertLevels + if (bottomDepth(iCell) < refBottomDepth(k) ) then + maxLevelCell(iCell) = k-1 + bottomDepth(iCell) = refBottomDepth(k-1) + exit + end if + end do + end if + end if + + enddo ! Looping through with iCell + + block_ptr => block_ptr % next + + enddo ! done setting bathymetry + + ! mark cells for culling + block_ptr => domain % blocklist + do while (associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call ocn_mark_maxlevelcell(meshPool, iErr) + block_ptr => block_ptr % next + end do + + ! Set forcing boundary conditions and initial conditions for temperature and salinity + call mpas_log_write( 'setting up forcing and initial T/S conditions') + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'latEdge', latEdge) + call mpas_pool_get_array(meshPool, 'latVertex', latVertex) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) + call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional, 1) + + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', activeTracersPistonVelocity, 1) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, & + 'activeTracersSurfaceRestoringValue', activeTracersSurfaceRestoringValue, 1) + + do iCell = 1, nCells + lonCurrent = lonCell(iCell) + latCurrent = latCell(iCell) + + ! Set initial temperature and salinity + do k = 1, nVertLevels + zMid = refZMid(k) + + distance = config_soma_ref_density & + - (1.0_RKIND - config_soma_density_difference_linear) * config_soma_density_difference & + * tanh(zMid / config_soma_thermocline_depth) & + - config_soma_density_difference_linear * config_soma_density_difference & + * zMid / config_soma_bottom_depth + factor = (config_soma_ref_density - distance) / config_eos_linear_alpha + temperature = config_soma_surface_temperature + factor + + factor = - zMid / config_soma_bottom_depth + salinity = config_soma_surface_salinity + 2.0_RKIND * factor + + if ( associated(activeTracers) ) then + activeTracers(index_temperature, k, iCell) = temperature + activeTracers(index_salinity, k, iCell) = salinity + end if + enddo + + if (config_soma_use_surface_temp_restoring) then + ! surface restoring + idx = index_temperature + activeTracersSurfaceRestoringValue(idx,iCell) = & + config_soma_surface_temp_restoring_at_center_latitude & + + config_soma_surface_temp_restoring_latitude_gradient & + *(latCurrent*180.0_RKIND/pii - config_soma_center_latitude) + activeTracersPistonVelocity(idx,iCell) = config_soma_restoring_temp_piston_vel + + idx = index_salinity + activeTracersSurfaceRestoringValue(idx,iCell) = 34.0_RKIND + activeTracersPistonVelocity(idx,iCell) = 0.0_RKIND + end if + + + ! Set up debugging tracers + if ( associated(debugTracers) ) then + debugTracers(index_tracer1, :, iCell) = 1.0_RKIND + end if + + end do ! iCell = 1, nCells + + ! Set wind stress + do iCell = 1, nCells + lonCurrent = lonCell(iCell) + latCurrent = latCell(iCell) + + deltay = sphereRadius * ( latCurrent - latCenter ) + factor = 1.0_RKIND - 0.5_RKIND * deltay / config_soma_domain_width + windstress = factor * 0.1_RKIND * exp( -(deltay / config_soma_domain_width)**2 ) & + * cos(pii * deltay / config_soma_domain_width) + + windStressZonal(iCell) = windStress + windStressMeridional(iCell) = 0.0_RKIND + + end do + + ! Set Coriolis parameters + do iCell = 1, nCellsSolve + fCell(iCell) = 2.0_RKIND * omega * sin(latCell(iCell)) + end do + do iEdge = 1, nEdgesSolve + fEdge(iEdge) = 2.0_RKIND * omega * sin(latEdge(iEdge)) + end do + do iVertex = 1, nVerticesSolve + fVertex(iVertex) = 2.0_RKIND * omega * sin(latVertex(iVertex)) + end do + + block_ptr => block_ptr % next + end do + + call mpas_log_write( 'exiting ocn_init_setup_soma') + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_soma!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_soma +! +!> \brief Validation for SOMA test case +!> \author Todd Ringler +!> \date 02/26/2014 +!> \details +!> This routine validates the configuration options for the SOMA test case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_soma(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(inout) :: configPool, packagePool + type (mpas_io_context_type), intent(inout) :: iocontext + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_soma_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('soma')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_soma_vert_levels', config_soma_vert_levels) + + if(config_vert_levels <= 0 .and. config_soma_vert_levels > 0) then + config_vert_levels = config_soma_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for SOMA. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_soma!}}} + +end module ocn_init_soma + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_spherical_utils.F b/src/core_ocean/mode_init/mpas_ocn_init_spherical_utils.F new file mode 100644 index 0000000000..aa4451d702 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_spherical_utils.F @@ -0,0 +1,622 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_spherical_utils +! +!> \brief MPAS ocean spherical utilities +!> \author Doug Jacobsen +!> \date 03/20/2015 +!> \details +!> This module contains the routines for updating mesh quantities based on a spherical radius +! +!----------------------------------------------------------------------- + +module ocn_init_spherical_utils + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_stream_manager + + implicit none + private + + public :: ocn_init_expand_sphere, ocn_transform_from_lonlat_to_xyz + public :: transform_from_xyz_to_lonlat, ocn_unit_vector_in_3space + public :: ocn_vector_on_tangent_plane, ocn_cross_product_in_3space + public :: ocn_init_set_pools_sphere_radius + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_expand_sphere +! +!> \brief MPAS-Ocean Spherical Expansion Routine +!> \author Doug Jacobsen +!> \date 03/20/2015 +!> \details +!> This routine expands mesh quantities to sphere of radius newRadius. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_expand_sphere(domain, stream_manager, newRadius, err)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + type (mpas_streamManager_type), intent(inout) :: stream_manager + real (kind=RKIND), intent(in) :: newRadius + integer, intent(out) :: err + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool + + character (len=StrKIND) :: streamID + integer :: directionProperty + + logical, pointer :: config_expand_sphere, config_realistic_coriolis_parameter + logical, pointer :: on_a_sphere + real (kind=RKIND), pointer :: sphere_radius + + integer, pointer :: nCells, nCellsSolve, nEdgesSolve, nVerticesSolve, vertexDegree + + integer, dimension(:, :), pointer :: cellsOnVertex + + real (kind=RKIND), dimension(:), pointer :: areaCell, areaTriangle + real (kind=RKIND), dimension(:), pointer :: dvEdge, dcEdge + real (kind=RKIND), dimension(:), pointer :: fCell, fEdge, fVertex + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, zCell, latCell, lonCell + real (kind=RKIND), dimension(:), pointer :: xEdge, yEdge, zEdge, latEdge, lonEdge + real (kind=RKIND), dimension(:), pointer :: xVertex, yVertex, zVertex, latVertex, lonVertex + real (kind=RKIND), dimension(:, :), pointer :: kiteAreasOnVertex + + real (kind=RKIND) :: oldRadius, ratio + real (kind=RKIND) :: norm + real (kind=RKIND) :: oldX, oldY, oldZ + integer :: iCell, iEdge, iVertex, i + + err = 0 + + call mpas_pool_get_config(domain % configs, 'config_expand_sphere', config_expand_sphere) + + if ( .not. config_expand_sphere ) return + + call mpas_pool_get_config(domain % configs, 'config_realistic_coriolis_parameter', config_realistic_coriolis_parameter) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + call mpas_pool_get_config(meshPool, 'sphere_radius', sphere_radius) + + if ( .not. on_a_sphere ) then + call mpas_log_write( 'Warning: Only spherical meshes can been expanded.') + call mpas_log_write( 'Skipping expansion') + return + end if + + if ( sphere_radius == 0.0_RKIND ) then + call mpas_log_write( 'Sphere radius is 0.0', MPAS_LOG_CRIT) + err = 1 + return + end if + + call mpas_log_write( 'Expanding mesh to a radius of size: $f m.', realArgs=(/ newRadius /) ) + + call mpas_stream_mgr_begin_iteration(stream_manager) + do while (mpas_stream_mgr_get_next_stream(stream_manager, streamID, directionProperty)) + if ( directionProperty == MPAS_STREAM_OUTPUT .or. directionProperty == MPAS_STREAM_INPUT_OUTPUT ) then + call mpas_stream_mgr_add_att(stream_manager, 'sphere_radius', newRadius, streamID) + end if + end do + + oldRadius = sphere_radius + ratio = newRadius / oldRadius + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call ocn_init_set_pools_sphere_radius(block_ptr % structs, newRadius) + + ! Expand cell quantities + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'zCell', zCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) + call mpas_pool_get_array(meshPool, 'lonCell', lonCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + + call mpas_pool_get_array(meshPool, 'xEdge', xEdge) + call mpas_pool_get_array(meshPool, 'yEdge', yEdge) + call mpas_pool_get_array(meshPool, 'zEdge', zEdge) + call mpas_pool_get_array(meshPool, 'latEdge', latEdge) + call mpas_pool_get_array(meshPool, 'lonEdge', lonEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'zVertex', zVertex) + call mpas_pool_get_array(meshPool, 'latVertex', latVertex) + call mpas_pool_get_array(meshPool, 'lonVertex', lonVertex) + call mpas_pool_get_array(meshPool, 'areaTriangle', areaTriangle) + call mpas_pool_get_array(meshPool, 'kiteAreasOnVertex', kiteAreasOnVertex) + call mpas_pool_get_array(meshPool, 'cellsOnVertex', cellsOnVertex) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + do iCell = 1, nCellsSolve + oldX = xCell(iCell) + oldY = yCell(iCell) + oldZ = zCell(iCell) + + norm = sqrt(oldX**2 + oldY**2 + oldZ**2) + + xCell(iCell) = (oldX / norm) * newRadius + yCell(iCell) = (oldY / norm) * newRadius + zCell(iCell) = (oldZ / norm) * newRadius + areaCell(iCell) = (areaCell(iCell) / (oldRadius**2) )* newRadius**2 + + if(config_realistic_coriolis_parameter) then + fCell(iCell) = 2.0_RKIND * omega * sin(latCell(iCell)) + end if + end do + + ! Expand vertex quantities + do iVertex = 1, nVerticesSolve + oldX = xVertex(iVertex) + oldY = yVertex(iVertex) + oldZ = zVertex(iVertex) + + norm = sqrt(oldX**2 + oldY**2 + oldZ**2) + + xVertex(iVertex) = (oldX / norm) * newRadius + yVertex(iVertex) = (oldY / norm) * newRadius + zVertex(iVertex) = (oldZ / norm) * newRadius + areaTriangle(iVertex) = 0.0_RKIND + + do i = 1, vertexDegree + if (cellsOnVertex(i, iVertex) < nCells+1) then + kiteAreasOnVertex(i, iVertex) = ( kiteAreasOnVertex(i, iVertex) / oldRadius**2) * newRadius**2 + else + kiteAreasOnVertex(i, iVertex) = 0.0_RKIND + end if + areaTriangle(iVertex) = areaTriangle(iVertex) + kiteAreasOnVertex(i, iVertex) + end do + + if(config_realistic_coriolis_parameter) then + fVertex(iVertex) = 2.0_RKIND * omega * sin( latVertex(iVertex) ) + end if + end do + + ! Expand edge quantities + do iEdge = 1, nEdgesSolve + oldX = xEdge(iEdge) + oldY = yEdge(iEdge) + oldZ = zEdge(iEdge) + + norm = sqrt(oldX**2 + oldY**2 + oldZ**2) + + xEdge(iEdge) = (oldX / norm) * newRadius + yEdge(iEdge) = (oldY / norm) * newRadius + zEdge(iEdge) = (oldZ / norm) * newRadius + dvEdge(iEdge) = (dvEdge(iEdge) / oldRadius) * newRadius + dcEdge(iEdge) = (dcEdge(iEdge) / oldRadius) * newRadius + + if(config_realistic_coriolis_parameter) then + fEdge(iEdge) = 2.0_RKIND * omega * sin( latEdge(iEdge) ) + end if + end do + + block_ptr % domain % sphere_radius = newRadius + sphere_radius = newRadius + block_ptr => block_ptr % next + + end do + + !-------------------------------------------------------------------- + + end subroutine ocn_init_expand_sphere!}}} + +!*********************************************************************** +! +! recursive routine ocn_init_set_pools_sphere_radius +! +!> \brief MPAS-Ocean Sphere radius update routine +!> \author Doug Jacobsen +!> \date 09/09/2015 +!> \details +!> This routine updates the value of sphere_radius in all pools that contain +!> it. +! +!----------------------------------------------------------------------- + recursive subroutine ocn_init_set_pools_sphere_radius(inPool, newRadius)!{{{ + type (mpas_pool_type), intent(inout) :: inPool + real (kind=RKIND), intent(in) :: newRadius + + type (mpas_pool_type), pointer :: subPool + type (mpas_pool_iterator_type) :: poolItr + real (kind=RKIND), pointer :: sphere_radius + + call mpas_pool_begin_iteration(inPool) + + do while ( mpas_pool_get_next_member(inPool, poolItr) ) + if ( poolItr % memberType == MPAS_POOL_SUBPOOL ) then + call mpas_pool_get_subpool(inPool, poolItr % memberName, subPool) + call ocn_init_set_pools_sphere_radius(subPool, newRadius) + else if ( poolItr % memberType == MPAS_POOL_CONFIG ) then + + if ( poolItr % memberName == 'sphere_radius' ) then + call mpas_pool_get_config(inPool, poolItr % memberName, sphere_radius) + sphere_radius = newRadius + end if + + end if + end do + + end subroutine ocn_init_set_pools_sphere_radius!}}} + +!*********************************************************************** +! +! routine ocn_transform_from_lonlat_to_xyz +! +!> \brief MPAS-Ocean Tranform LatLon to XYZ +!> \author Todd Ringler +!> \date 02/19/2014 +!> \details +!> This routine converts a (lat, lon) coordinate into an (x, y, z) coordinate +!> INTENT(IN) +!> xin = x position +!> yin = y position +!> zin = z position +!> ulon = east component of vector +!> ulat = north component of vector +!> +!> INTENT(OUT) +!> ux = x component of vector +!> uy = y component of vector +!> uz = z component of vector +! +!----------------------------------------------------------------------- + subroutine ocn_transform_from_lonlat_to_xyz(xin, yin, zin, ulon, ulat, ux, uy, uz)!{{{ + implicit none + real, intent(in) :: xin, yin, zin, ulon, ulat + real, intent(out) :: ux, uy, uz + real :: h(3,3), p(3), q(3), g(3), X1(3,3), X2(3,3), trans_X2_to_X1(3,3), r + integer :: i,j,k + logical :: l_Pole + real, parameter :: epsvt = 1.0e-10_RKIND + + !----------------------------------------------------------------------- + ! define the e1, e2, and e3 directions + !----------------------------------------------------------------------- + X1(1,1) = 1.0_RKIND; X1(1,2) = 0.0_RKIND; X1(1,3) = 0.0_RKIND + X1(2,1) = 0.0_RKIND; X1(2,2) = 1.0_RKIND; X1(2,3) = 0.0_RKIND + X1(3,1) = 0.0_RKIND; X1(3,2) = 0.0_RKIND; X1(3,3) = 1.0_RKIND + + !----------------------------------------------------------------------- + ! find the vectors (measured in X1) that point in the local + ! east (h(1,:)), north (h(2,:)), and vertical (h(3,:)) direction + !----------------------------------------------------------------------- + h(3,1) = xin; h(3,2) = yin; h(3,3) = zin + call ocn_unit_vector_in_3space(h(3,:)) + + !----------------------------------------------------------------------- + ! g(:) is a work array and holds the vector pointing to the North Pole. + ! measured in X1 + !----------------------------------------------------------------------- + g(:) = X1(3,:) + + !----------------------------------------------------------------------- + ! determine if the local vertical hits a pole + !----------------------------------------------------------------------- + l_Pole = .false. + r = g(1)*h(3,1) + g(2)*h(3,2) + g(3)*h(3,3) + r = abs(r) + epsvt + if(r.gt.1.0_RKIND) then + l_Pole = .true. + h(3,:) = h(3,:) + epsvt + call ocn_unit_vector_in_3space(h(3,:)) + endif + + !----------------------------------------------------------------------- + ! find the vector that is perpendicular to the local vertical vector + ! and points in the direction of of the North pole, this defines the local + ! north direction. measured in X1 + !----------------------------------------------------------------------- + call ocn_vector_on_tangent_plane ( h(3,:), g(:), h(2,:) ) + + !----------------------------------------------------------------------- + ! take the cross product of the local North direction and the local vertical + ! to find the local east vector. still in X1 + !----------------------------------------------------------------------- + call ocn_cross_product_in_3space ( h(2,:), h(3,:), h(1,:) ) + + !----------------------------------------------------------------------- + ! put these 3 vectors into a matrix X2 + !----------------------------------------------------------------------- + X2(1,:) = h(1,:) ! local east (measured in X1) + X2(2,:) = h(2,:) ! local north (measured in X1) + X2(3,:) = h(3,:) ! local vertical (measured in X1) + + !----------------------------------------------------------------------- + ! compute the transformation matrix + !----------------------------------------------------------------------- + trans_X2_to_X1(:,:) = matmul(X1,transpose(X2)) + + !----------------------------------------------------------------------- + ! transform (ulon, ulat) into (x,y,z) + !----------------------------------------------------------------------- + p(1) = ulon; p(2) = ulat; p(3) = 0 + g(:) = matmul(trans_X2_to_X1(:, :), p(:)) + ux = g(1); uy = g(2); uz = g(3) + + end subroutine ocn_transform_from_lonlat_to_xyz!}}} + +!*********************************************************************** +! +! routine ocn_transform_from_xyz_to_lonlat +! +!> \brief MPAS-Ocean transform XYZ to LatLon +!> \author Todd Ringler +!> \date 02/19/2014 +!> \details +!> This routine converts an (x, y, z) coordinate into a (lat, lon) coordinate +!> INTENT(IN) +!> xin = x position +!> yin = y position +!> zin = z position +!> ux = x component of vector +!> uy = y component of vector +!> uz = z component of vector +!> +!> INTENT(OUT) +!> ulon = east component of vector +!> ulat = north component of vector +! +!----------------------------------------------------------------------- + subroutine transform_from_xyz_to_lonlat(xin, yin, zin, ux, uy, uz, ulon, ulat)!{{{ + implicit none + real, intent(in) :: xin, yin, zin, ux, uy, uz + real, intent(out) :: ulon, ulat + real :: h(3,3), p(3), q(3), g(3), X1(3,3), X2(3,3), trans_X1_to_X2(3,3), r + integer :: i,j,k + logical :: l_Pole + real, parameter :: epsvt = 1.0e-10_RKIND + + !----------------------------------------------------------------------- + ! define the e1, e2, and e3 directions + !----------------------------------------------------------------------- + X1(1,1) = 1.0_RKIND; X1(1,2) = 0.0_RKIND; X1(1,3) = 0.0_RKIND + X1(2,1) = 0.0_RKIND; X1(2,2) = 1.0_RKIND; X1(2,3) = 0.0_RKIND + X1(3,1) = 0.0_RKIND; X1(3,2) = 0.0_RKIND; X1(3,3) = 1.0_RKIND + + !----------------------------------------------------------------------- + ! find the vectors (measured in X1) that point in the local + ! east (h(1,:)), north (h(2,:)), and vertical (h(3,:)) direction + !----------------------------------------------------------------------- + h(3,1) = xin; h(3,2) = yin; h(3,3) = zin + call ocn_unit_vector_in_3space(h(3,:)) + + !----------------------------------------------------------------------- + ! g(:) is a work array and holds the vector pointing to the North Pole. + ! measured in X1 + !----------------------------------------------------------------------- + g(:) = X1(3,:) + + !----------------------------------------------------------------------- + ! determine if the local vertical hits a pole + !----------------------------------------------------------------------- + l_Pole = .false. + r = g(1)*h(3,1) + g(2)*h(3,2) + g(3)*h(3,3) + r = abs(r) + epsvt + if(r.gt.1.0_RKIND) then + l_Pole = .true. + h(3,:) = h(3,:) + epsvt + call ocn_unit_vector_in_3space(h(3,:)) + endif + + !----------------------------------------------------------------------- + ! find the vector that is perpendicular to the local vertical vector + ! and points in the direction of of the North pole, this defines the local + ! north direction. measured in X1 + !----------------------------------------------------------------------- + call ocn_vector_on_tangent_plane ( h(3,:), g(:), h(2,:) ) + + !----------------------------------------------------------------------- + ! take the cross product of the local North direction and the local vertical + ! to find the local east vector. still in X1 + !----------------------------------------------------------------------- + call ocn_cross_product_in_3space ( h(2,:), h(3,:), h(1,:) ) + + !----------------------------------------------------------------------- + ! put these 3 vectors into a matrix X2 + !----------------------------------------------------------------------- + X2(1,:) = h(1,:) ! local east (measured in X1) + X2(2,:) = h(2,:) ! local north (measured in X1) + X2(3,:) = h(3,:) ! local vertical (measured in X1) + + !----------------------------------------------------------------------- + ! compute the transformation matrix + !----------------------------------------------------------------------- + trans_X1_to_X2(:,:) = matmul(X2,transpose(X1)) + + !----------------------------------------------------------------------- + ! transform (ulon, ulat) into (x,y,z) + !----------------------------------------------------------------------- + p(1) = ux; p(2) = uy; p(3) = uz + g(:) = matmul(trans_X1_to_X2(:, :), p(:)) + ulon = g(1); ulat= g(2); + + end subroutine transform_from_xyz_to_lonlat!}}} + +!*********************************************************************** +! +! routine ocn_unit_vector_in_3space +! +!> \brief MPAS-Ocean 3D unit vector +!> \author Todd Ringler +!> \date 02/19/2014 +!> \details +!> This routine normalizes a vector in 3space. +! +!----------------------------------------------------------------------- + subroutine ocn_unit_vector_in_3space (p_1)!{{{ + + !----------------------------------------------------------------------- + ! PURPOSE : normalize p_1 to unit length and overwrite p_1 + !----------------------------------------------------------------------- + + !----------------------------------------------------------------------- + ! intent(inout) + !----------------------------------------------------------------------- + real , intent(inout) :: & + p_1 (:) + + !----------------------------------------------------------------------- + ! local + !----------------------------------------------------------------------- + real :: length + + length = SQRT (p_1(1)**2 + p_1(2)**2 + p_1(3)**2 ) + length = 1.0_RKIND/length + p_1(1) = p_1(1)*length + p_1(2) = p_1(2)*length + p_1(3) = p_1(3)*length + + end subroutine ocn_unit_vector_in_3space!}}} + +!*********************************************************************** +! +! routine ocn_vector_on_tangent_plane +! +!> \brief MPAS-Ocean Vector on a tangent plane +!> \author Todd Ringler +!> \date 02/19/2014 +!> \details +!> Given two points measured in (x,y,z) and lying on +!> the unit sphere, find the vector (p_out) that lies on the plane +!> perpendicular to the p_1 vector and points in the direction of +!> the projection of p_2 onto the tangent plane. +!> +!> NOTE : p_1 and p_2 are assumed to be of unit length +!> NOTE : p_out is normalized to unit length +! +!----------------------------------------------------------------------- + subroutine ocn_vector_on_tangent_plane(p_1, p_2, p_out)!{{{ +!----------------------------------------------------------------------- +! intent(in) +!----------------------------------------------------------------------- + real , intent(in) :: & + p_1 (:), & + p_2 (:) + +!----------------------------------------------------------------------- +! intent(out) +!----------------------------------------------------------------------- + real , intent(out) :: & + p_out (:) + +!----------------------------------------------------------------------- +! local +!----------------------------------------------------------------------- + real :: & + work (3), t1(3), t2(3) + +! work (1) = - p_1(2) * ( -p_1(2) * p_2(1) + p_1(1) * p_2(2) ) & +! + p_1(3) * ( p_1(3) * p_2(1) - p_1(1) * p_2(3) ) + +! work (2) = + p_1(1) * ( -p_1(2) * p_2(1) + p_1(1) * p_2(2) ) & +! - p_1(3) * ( -p_1(3) * p_2(2) + p_1(2) * p_2(3) ) + +! work (3) = - p_1(1) * ( p_1(3) * p_2(1) - p_1(1) * p_2(3) ) & +! + p_1(2) * ( -p_1(3) * p_2(2) + p_1(2) * p_2(3) ) + + + t1(:) = p_2(:) - p_1(:) + t2(:) = p_1 + + call ocn_unit_vector_in_3space (t1) + call ocn_unit_vector_in_3space (t2) + + call ocn_cross_product_in_3space(t1(:), t2(:), work(:)) + call ocn_unit_vector_in_3space (work) + call ocn_cross_product_in_3space(t2(:),work(:),p_out(:)) + call ocn_unit_vector_in_3space (p_out) + + end subroutine ocn_vector_on_tangent_plane!}}} + +!*********************************************************************** +! +! routine ocn_cross_product_in_3space +! +!> \brief MPAS-Ocean Cross product in 3D +!> \author Todd Ringler +!> \date 02/19/2014 +!> \details +!> compute p_1 cross p_2 and place in p_out +! +!----------------------------------------------------------------------- + subroutine ocn_cross_product_in_3space(p_1,p_2,p_out)!{{{ +!----------------------------------------------------------------------- +! intent(in) +!----------------------------------------------------------------------- + real , intent(in) :: & + p_1 (:), & + p_2 (:) + +!----------------------------------------------------------------------- +! intent(out) +!----------------------------------------------------------------------- + real , intent(out) :: & + p_out (:) + + p_out(1) = p_1(2)*p_2(3)-p_1(3)*p_2(2) + p_out(2) = p_1(3)*p_2(1)-p_1(1)*p_2(3) + p_out(3) = p_1(1)*p_2(2)-p_1(2)*p_2(1) + + end subroutine ocn_cross_product_in_3space!}}} + +!*********************************************************************** + +end module ocn_init_spherical_utils + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_ssh_and_landIcePressure.F b/src/core_ocean/mode_init/mpas_ocn_init_ssh_and_landIcePressure.F new file mode 100644 index 0000000000..2f5fdadb13 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_ssh_and_landIcePressure.F @@ -0,0 +1,714 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_ssh_and_landIcePressure +! +!> \brief MPAS ocean initialize matching SSH and land-ice pressure +!> \author Xylar Asay-Davis +!> \date 06/05/2015 +!> \details +!> This module contains the routines for aiding in initializing the +!> land-ice pressure based on the sea-surface height (SSH) +!> so that the barotropic pressure-gradient force (PGF) is initially small +! +!----------------------------------------------------------------------- + +module ocn_init_ssh_and_landIcePressure + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + + use ocn_constants + use ocn_init_interpolation + use ocn_init_vertical_grids + + use ocn_equation_of_state + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_ssh_and_landIcePressure_vertical_grid, & + ocn_init_ssh_and_landIcePressure_balance + + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + + +!*********************************************************************** +! +! routine ocn_init_ssh_and_landIcePressure_vertical_grid +! +!> \brief Initialize z* vertical grid based on SSH +!> \author Xylar Asay-Davis +!> \date 10/21/2015 +!> \details +!> This routine sets up the vertical grid (layerThickness, +!> zMid and restingThickness) needed for computing SSH from +!> land-ice pressure or visa versa. bottomDepth, refBottomDepth, maxLevelCell +!> and modifySSHMask must have been computed by the test case +!> before calling this routine. If +!> config_iterative_init_variable = 'landIcePressure' or 'landIcePressure_from_top_density', the test +!> case must compute ssh before calling this routine. +!> modifySSHMask should be set to 1 wherever the ssh or landIcePressure +!> should be modified for consistency (e.g. under land ice). This +!> routine will take care of setting up partial bottom cells +!> by calling ocn_alter_bottomDepth_for_pbcs (except for the +!> Haney-number-constrained coordinate, which handle thin bottom +!> cells via the Haney-number constraint. + +!----------------------------------------------------------------------- + + subroutine ocn_init_ssh_and_landIcePressure_vertical_grid(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + !-------------------------------------------------------------------- + + iErr = 0 + call ocn_init_vertical_grid(domain, updateWithSSH=.false., iErr=iErr) + + end subroutine ocn_init_ssh_and_landIcePressure_vertical_grid + +!*********************************************************************** +! +! routine ocn_init_ssh_and_landIcePressure_balance +! +!> \brief Compute the balance land-ice pressure given the SSH or visa versa +!> \author Xylar Asay-Davis +!> \date 8/8/2016 +!> \details +!> This routine either updates SSH based on land-ice pressure (if config_iterative_init_variable = 'ssh') +!> or visa versa (if config_iterative_init_variable = 'landIcePressure' or 'landIcePressure_from_top_density'). +!> The routine produces an initial guess at land-ice pressure or SSH using either the density of the topmost layer +!> ('landIcePressure_from_top_density') or of all layers above the SSH ('ssh' or 'landIcePressure') +!> to determine the effective density of seawater within the land ice. +!> The resulting land-ice pressure and SSH are approximately consistent with one another +!> in the sense that the horizontal pressure-gradient force (HPGF) +!> should be small at the ocean surface. +!> ocn_init_ssh_and_landIcePressure_vertical_grid should be called to produce +!> the appropriate vertical grid before calling this subroutine. +!> activeTracers should be initialized based on this vertical grid. +!> Upon completion, the vertical grid will have been updated +!> to be consistent with the SSH and the activeTracers will have been +!> interpolated to the new grid. + +!----------------------------------------------------------------------- + + subroutine ocn_init_ssh_and_landIcePressure_balance(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, forcingPool, statePool, diagnosticsPool, & + verticalMeshPool, scratchPool + + type (mpas_pool_type), pointer :: tracersPool + + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: ssh + + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, zMid + + real (kind=RKIND), dimension(:,:), pointer :: density + real (kind=RKIND), dimension(:), pointer :: landIcePressure, landIceDraft, & + effectiveDensityInLandIce + + + real(kind=RKIND), dimension(:,:), pointer :: origZMid + integer, dimension(:), pointer :: origMaxLevelCell, modifySSHMask + type (field2DReal), pointer :: origZMidField + type (field1DInteger), pointer :: origMaxLevelCellField + integer, pointer :: nCells, nVertLevels + + character (len=StrKIND), pointer :: config_iterative_init_variable + + integer :: iCell + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_iterative_init_variable', config_iterative_init_variable) + + ! compute density (needed regardless of config_iterative_init_variable) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(diagnosticsPool, 'density', density) + + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, & + scratchPool, nCells, 0, 'relative', density, iErr, & + timeLevelIn=1) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_equation_of_state_density failed.', MPAS_LOG_CRIT) + return + end if + + block_ptr => block_ptr % next + end do !block_ptr + + ! first, handle the simple case where we're going to compute landIcePressure from the density at the top. + ! In this case, we already computed the correct vertical grid with the ssh, so all we have to do is + ! landIcePressure from the denisty we just got. + if(config_iterative_init_variable == 'landIcePressure_from_top_density') then + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) + + call mpas_pool_get_array(forcingPool, 'landIceDraft', landIceDraft) + + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(statePool, 'effectiveDensityInLandIce', effectiveDensityInLandIce, 1) + call mpas_pool_get_array(diagnosticsPool, 'density', density) + call mpas_pool_get_array(diagnosticsPool, 'modifySSHMask', modifySSHMask) + + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, & + scratchPool, nCells, 0, 'relative', density, iErr, & + timeLevelIn=1) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_equation_of_state_density failed.', MPAS_LOG_CRIT) + return + end if + + do iCell = 1, nCells + if(modifySSHMask(iCell) == 0) then + ssh(iCell) = 0.0_RKIND + landIcePressure(iCell) = 0.0_RKIND + + if (associated(effectiveDensityInLandIce)) & + ! effective density cannot be determined + effectiveDensityInLandIce(iCell) = 0.0_RKIND + cycle + end if + + landIcePressure(iCell) = max(0.0_RKIND, -density(1,iCell)*gravity*ssh(iCell)) + if (associated(effectiveDensityInLandIce)) & + effectiveDensityInLandIce(iCell) = density(1,iCell) + end do + + ! copy the SSH into the landIceDraft so we can use it later to remove it when + ! computing sea-surface tilt + landIceDraft(:) = ssh(:) + + block_ptr => block_ptr % next + end do !block_ptr + + return + end if + + ! The other cases are more complicated and require interpolating the activeTracers + ! once the ssh or landIcePressure is determined + + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + + call mpas_pool_get_field(scratchPool, 'scratchZMid', origZMidField) + call mpas_allocate_scratch_field(origZMidField, .false.) + call mpas_pool_get_field(scratchPool, 'scratchMaxLevelCell', origMaxLevelCellField) + call mpas_allocate_scratch_field(origMaxLevelCellField, .false.) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) + + call mpas_pool_get_array(forcingPool, 'landIceDraft', landIceDraft) + + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(statePool, 'effectiveDensityInLandIce', effectiveDensityInLandIce, 1) + call mpas_pool_get_array(diagnosticsPool, 'density', density) + call mpas_pool_get_array(diagnosticsPool, 'modifySSHMask', modifySSHMask) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(scratchPool, 'scratchZMid', origZMid) + call mpas_pool_get_array(scratchPool, 'scratchMaxLevelCell', origMaxLevelCell) + + + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, & + scratchPool, nCells, 0, 'relative', density, iErr, & + timeLevelIn=1) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_equation_of_state_density failed.', MPAS_LOG_CRIT) + return + end if + + do iCell = 1, nCells + if(modifySSHMask(iCell) == 0) then + ssh(iCell) = 0.0_RKIND + landIcePressure(iCell) = 0.0_RKIND + + if (associated(effectiveDensityInLandIce)) & + ! effective density cannot be determined + effectiveDensityInLandIce(iCell) = 0.0_RKIND + + cycle + end if + + if(config_iterative_init_variable == 'ssh') then + ! compute ssh where pressure equals landIcePressure + ssh(iCell) = find_z_given_pressure(landIcePressure(iCell), density(:,iCell), & + layerThickness(:,iCell), nVertLevels, maxLevelCell(iCell)) + else + ! compute landIcePressure based on hydrostatic pressure at SSH + landIcePressure(iCell) = max(0.0_RKIND, find_pressure_given_z(ssh(iCell), density(:,iCell), & + layerThickness(:,iCell), nVertLevels, maxLevelCell(iCell))) + end if + + if (associated(effectiveDensityInLandIce)) then + ! the effective density of ocean water in land ice is determined from the land-ice pressure and SSH + effectiveDensityInLandIce(iCell) = -landIcePressure(iCell)/(gravity*ssh(iCell)) + end if + + end do + + ! save the old zMid for use in tracer inerpolation + origZMid(:,:) = zMid(:,:) + origMaxLevelCell(:) = maxLevelCell(:) + + ! copy the SSH into the landIceDraft so we can use it later to remove it when + ! computing sea-surface tilt + landIceDraft(:) = ssh(:) + + block_ptr => block_ptr % next + end do !block_ptr + + ! update the vertical grid based on the new ssh + call ocn_init_vertical_grid(domain, updateWithSSH=.true., iErr=iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_vertical_grid failed.', MPAS_LOG_CRIT) + return + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + + call mpas_pool_get_array(scratchPool, 'scratchZMid', origZMid) + call mpas_pool_get_array(scratchPool, 'scratchMaxLevelCell', origMaxLevelCell) + + ! interpolate active tracers to the new zMid + call interpolate_activeTracers(meshPool, origZMid, zMid, & + origMaxLevelCell, maxLevelCell, & + activeTracers, iErr) + if(iErr .ne. 0) then + call mpas_log_write( 'interpolate_activeTracers failed.', MPAS_LOG_CRIT) + return + end if + + block_ptr => block_ptr % next + end do !block_ptr + + call mpas_deallocate_scratch_field(origZMidField, .false.) + call mpas_deallocate_scratch_field(origMaxLevelCellField, .false.) + + !-------------------------------------------------------------------- + + end subroutine ocn_init_ssh_and_landIcePressure_balance + +!*********************************************************************** +! +! PRIVATE SUBROUTINES +! +!*********************************************************************** + +!*********************************************************************** +! +! routine ocn_init_vertical_grid +! +!> \brief Initialize z* vertical grid based on SSH +!> \author Xylar Asay-Davis +!> \date 8/8/2016 +!> \details +!> This routine sets up the vertical grid (layerThickness, +!> zMid and restingThickness) needed for computing SSH from +!> land-ice pressure or visa versa. bottomDepth, refBottomDepth and maxLevelCell +!> must have been computed by the test case before calling this +!> routine. If config_iterative_init_variable = 'landIcePressure' or 'landIcePressure_from_top_density', the test +!> case must compute ssh before calling this routine. This +!> routine will take care of setting up partial bottom cells +!> by calling ocn_alter_bottomDepth_for_pbcs (except for the +!> Haney-number-constrained coordinate, which handle thin bottom +!> cells via the Haney-number constraint. +!----------------------------------------------------------------------- + + subroutine ocn_init_vertical_grid(domain, updateWithSSH, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + logical, intent(in) :: updateWithSSH + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, statePool, diagnosticsPool, verticalMeshPool + + logical, pointer :: config_use_rx1_constraint + + character (len=StrKIND), pointer :: config_iterative_init_variable + + ! Define dimension pointers + integer, pointer :: nCells, nVertLevels + + ! Define variable pointers + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: refBottomDepth, bottomDepth, ssh + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness, zMid + + integer :: iCell + + logical :: initWithSSH, initRx1WithSSH + + !-------------------------------------------------------------------- + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_iterative_init_variable', config_iterative_init_variable) + + if(config_iterative_init_variable .ne. 'ssh' & + .and. config_iterative_init_variable .ne. 'landIcePressure_from_top_density' & + .and. config_iterative_init_variable .ne. 'landIcePressure') then + iErr = 1 + call mpas_log_write( 'invalid value for config_iterative_init_variable'// trim(config_iterative_init_variable), MPAS_LOG_CRIT) + return + end if + call mpas_pool_get_config(ocnConfigs, 'config_use_rx1_constraint', config_use_rx1_constraint) + + ! one reason for using config_iterative_init_variable == 'landIcePressure_from_top_density' is that we can immediately compute + ! the vertical grid displaced by the ssh + initWithSSH = updateWithSSH .or. (config_iterative_init_variable == 'landIcePressure_from_top_density') + initRx1WithSSH = initWithSSH .and. config_use_rx1_constraint + + if(.not. config_use_rx1_constraint .and. .not. updateWithSSH) then + ! only alter pbcs if we're initializing for the first time (updateWithSSH == .false.) so we haven't done it already + ! and we're not going to handle bottomDepth another way (with the Haney-number-constrained coordinate) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + do iCell = 1, nCells + call ocn_alter_bottomDepth_for_pbcs(bottomDepth(iCell), refBottomDepth, maxLevelCell(iCell), iErr) + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_alter_bottomDepth_for_pbcs failed.', MPAS_LOG_CRIT) + return + end if + end do + block_ptr => block_ptr % next + end do !block_ptr + end if + + if(initRx1WithSSH) then + ! We already know the ssh and landIcePressure we want to use. + ! Compute the layer thicknesses and zMid based on topography and ssh. + ! Use rx1 constraint to recompute the vertical grid. + call ocn_init_vertical_grid_with_max_rx1(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_vertical_grid_with_max_rx1 failed.', MPAS_LOG_CRIT) + return + end if + + else + ! we're initializing to z-star + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + + do iCell = 1, nCells + if(initWithSSH) then + ! we already know the ssh and landIcePressure we want to use. + ! compute the layer thicknesses and zMid based on topography and ssh + call ocn_compute_layerThickness_zMid_from_bottomDepth(layerThickness(:,iCell),zMid(:,iCell), & + refBottomDepth,bottomDepth(iCell), & + maxLevelCell(iCell),nVertLevels,iErr, & + restingThickness=restingThickness(:,iCell), & + ssh=ssh(iCell)) + else + ! We don't know the ssh or landIcePressure yet, and we need tracers on a reference grid to figure it out. + ! compute restingThickness and reference layerThickness and zMid based on topography with ssh=0 + ! (omitting ssh argument) + call ocn_compute_layerThickness_zMid_from_bottomDepth(layerThickness(:,iCell),zMid(:,iCell), & + refBottomDepth,bottomDepth(iCell), & + maxLevelCell(iCell),nVertLevels,iErr, & + restingThickness=restingThickness(:,iCell)) + end if + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_compute_layerThickness_zMid_from_bottomDepth failed.', MPAS_LOG_CRIT) + return + end if + end do !iCell + + block_ptr => block_ptr % next + end do !block_ptr + end if + + end subroutine ocn_init_vertical_grid + +!*********************************************************************** +! +! routine interpolate_activeTracers +! +!> \brief interpolate the active tracers from reference fields +!> \author Xylar Asay-Davis +!> \date 10/12/2015 +!> \details +!> Perform linear interpolation of T and S from reference fields without +!> the sea-surface height (SSH) displacement at refZMid to new locations +!> zMid that take the SSH into account. + +!----------------------------------------------------------------------- + + subroutine interpolate_activeTracers(meshPool, inZMid, outZMid, & + inMaxLevelCell, outMaxLevelCell, & + activeTracers, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: meshPool + real (kind=RKIND), dimension(:,:), intent(in) :: inZMid, outZMid + integer, dimension(:), intent(in) :: inMaxLevelCell, outMaxLevelCell + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: activeTracers + integer, intent(out) :: iErr + + ! Define dimension pointers + integer, pointer :: nCells, nVertLevels + + ! Define variable pointers + integer :: iCell, inKMax, outKMax + + real (kind=RKIND), dimension(:), allocatable :: inTracerColumn, outTracerColumn + + integer :: nTracers, iTracer + + iErr = 0 + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + nTracers = size(activeTracers, dim=1) + allocate(inTracerColumn(nVertLevels),outTracerColumn(nVertLevels)) + + do iCell = 1, nCells + inKMax = inMaxLevelCell(iCell) + outKMax = outMaxLevelCell(iCell) + if((inKMax <= 0) .or. (outKMax <= 0)) cycle + + do iTracer = 1, nTracers + inTracerColumn(:) = activeTracers(iTracer,:,iCell) + outTracerColumn(:) = -9.969209968386869e+36_RKIND + call ocn_init_interpolation_linear_vert(inZMid(1:inKMax,iCell), & + inTracerColumn(1:inKMax), & + inKMax, & + outZMid(1:outKMax,iCell), & + outTracerColumn(1:outKMax), & + outKMax, & + extrapolate=.true.) + activeTracers(iTracer,:,iCell) = outTracerColumn(:) + end do + end do + + deallocate(inTracerColumn, outTracerColumn) + + !-------------------------------------------------------------------- + + end subroutine interpolate_activeTracers!}}} + +!*********************************************************************** +! +! funciton find_pressure_given_z +! +!> \brief Determine the pressure at a given depth +!> \author Xylar Asay-Davis +!> \date 8/8/2016 +!> \details +!> In a column, find the hydrostatic pressure at a given depth with +!> the given density profile. + +!----------------------------------------------------------------------- + + function find_pressure_given_z(z, density, layerThickness, nVertLevels, maxLevelCell) result(pressure) + real (kind=RKIND), intent(in) :: z + real (kind=RKIND), intent(in), dimension(nVertLevels) :: density, layerThickness + integer, intent(in) :: nVertLevels, maxLevelCell + real (kind=RKIND) :: pressure + + integer :: k + real (kind=RKIND) :: pressureTop, pressureBot, zTop, zBot + + pressure = 0.0_RKIND + zTop = 0.0_RKIND + + if(maxLevelCell <= 0) return + + do k = 1, maxLevelCell + zBot = zTop - layerThickness(k) + if(z > zBot) then + ! note: this will simply extrapolate if z is positive for some reason + pressure = pressure + density(k)*gravity*(zTop - z) + return + end if + pressure = pressure + density(k)*gravity*layerThickness(k) + zTop = zBot + end do + + end function find_pressure_given_z + +!*********************************************************************** +! +! funciton find_z_given_pressure +! +!> \brief Find the depth at which pressure has a given value +!> \author Xylar Asay-Davis +!> \date 10/13/2015 +!> \details +!> In a column, find the depth at which the hydrostatic pressure reaches a given +!> value provided a density profile. + +!----------------------------------------------------------------------- + + function find_z_given_pressure(pressure, density, layerThickness, nVertLevels, maxLevelCell) result(z) + real (kind=RKIND), intent(in) :: pressure + real (kind=RKIND), intent(in), dimension(nVertLevels) :: density, layerThickness + integer, intent(in) :: nVertLevels, maxLevelCell + real (kind=RKIND) :: z + + integer :: k + real (kind=RKIND) :: pressureTop, pressureBot + + pressureTop = 0.0_RKIND + z = 0.0_RKIND + + if(maxLevelCell <= 0) return + + do k = 1, maxLevelCell + pressureBot = pressureTop + density(k)*gravity*layerThickness(k) + if(pressure < pressureBot) then + ! note: this will simply extrapolate if presssure is negative for some reason + z = z - (pressure - pressureTop)/(pressureBot - pressureTop)*layerThickness(k) + return + end if + z = z - layerThickness(k) + pressureTop = pressureBot + end do + + end function find_z_given_pressure + +!*********************************************************************** + +end module ocn_init_ssh_and_landIcePressure + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_sub_ice_shelf_2D.F b/src/core_ocean/mode_init/mpas_ocn_init_sub_ice_shelf_2D.F new file mode 100644 index 0000000000..7b3811ff61 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_sub_ice_shelf_2D.F @@ -0,0 +1,400 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_sub_ice_shelf_2D +! +!> \brief MPAS ocean initialize case -- sub_ice_shelf_2D +!> \author Mark Petersen +!> \date 9/2/2015 +! +!----------------------------------------------------------------------- + +module ocn_init_sub_ice_shelf_2D + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + use ocn_init_ssh_and_landIcePressure + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_sub_ice_shelf_2D, & + ocn_init_validate_sub_ice_shelf_2D + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + + contains + + !*********************************************************************** + ! + ! routine ocn_init_setup_sub_ice_shelf_2D + ! + !> \brief Setup for this initial condition + !> \author Mark Petersen + !> \date 9/2/2015 + !> \details + !> This routine sets up the initial conditions for this case. + ! + !----------------------------------------------------------------------- + + subroutine ocn_init_setup_sub_ice_shelf_2D(domain, iErr)!{{{ + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin, dcEdgeMinGlobal, maxDepth + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, yMidGlobal, xMinGlobal, xMaxGlobal + real (kind=RKIND) :: totalSubIceThickness, y1,y2,y3, d1,d2,d3, surfaceDepression, surfaceSalinity, bottomSalinity + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: verticalMeshPool + + integer :: iCell, k, idx + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid + real (kind=RKIND), pointer :: config_sub_ice_shelf_2D_bottom_depth, & + config_sub_ice_shelf_2D_cavity_thickness, config_sub_ice_shelf_2D_edge_width, config_sub_ice_shelf_2D_temperature, & + config_sub_ice_shelf_2D_surface_salinity, config_sub_ice_shelf_2D_bottom_salinity, & + config_sub_ice_shelf_2D_y1, config_sub_ice_shelf_2D_y2,config_sub_ice_shelf_2D_slope_height + + ! Define dimension pointers + integer, pointer :: nCellsSolve, nEdgesSolve, nVertLevels, nVertLevelsP1, nCells + integer, pointer :: index_temperature, index_salinity + + ! Define variable pointers + integer, dimension(:), pointer :: maxLevelCell, modifySSHMask, landIceMask + real (kind=RKIND), dimension(:), pointer :: xCell, yCell,refBottomDepth, refZMid, & + vertCoordMovementWeights, bottomDepth, & + fCell, fEdge, fVertex, dcEdge, refLayerThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + ! Define local interfaceLocations variable + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + logical, pointer :: on_a_sphere + + type (mpas_pool_type), pointer :: diagnosticsPool + real(kind=RKIND), dimension(:), pointer :: landIceFraction, ssh + real (kind=RKIND), dimension(:,:), pointer :: zMid + + iErr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('sub_ice_shelf_2D')) return + + call mpas_pool_get_config(ocnConfigs, 'config_vertical_grid', config_vertical_grid) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_bottom_depth',config_sub_ice_shelf_2D_bottom_depth) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_cavity_thickness', config_sub_ice_shelf_2D_cavity_thickness) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_edge_width', config_sub_ice_shelf_2D_edge_width) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_temperature', config_sub_ice_shelf_2D_temperature) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_surface_salinity', config_sub_ice_shelf_2D_surface_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_bottom_salinity', config_sub_ice_shelf_2D_bottom_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_y1', config_sub_ice_shelf_2D_y1) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_y2', config_sub_ice_shelf_2D_y2) + call mpas_pool_get_config(ocnConfigs, 'config_sub_ice_shelf_2D_slope_height', config_sub_ice_shelf_2D_slope_height) + + ! points 1 and 2 are where angles on ice shelf are located. + ! point 3 is at the surface. + ! d variables are total water thickness below ice shelf. + y1=config_sub_ice_shelf_2D_y1 + y2=config_sub_ice_shelf_2D_y2 + y3=config_sub_ice_shelf_2D_y2 + config_sub_ice_shelf_2D_edge_width + d1=config_sub_ice_shelf_2D_cavity_thickness + d2=config_sub_ice_shelf_2D_cavity_thickness+config_sub_ice_shelf_2D_slope_height + d3=config_sub_ice_shelf_2D_bottom_depth + + bottomSalinity = config_sub_ice_shelf_2D_bottom_salinity + surfaceSalinity = config_sub_ice_shelf_2D_surface_salinity + + ! Determine vertical grid for configuration + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + if ( on_a_sphere ) call mpas_log_write('The sub_ice_shelf_2D configuration can ' & + // 'only be applied to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + ! Initalize min/max values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + yMin = min( yMin, minval(yCell(1:nCellsSolve))) + yMax = max( yMax, maxval(yCell(1:nCellsSolve))) + xMin = min( xMin, minval(xCell(1:nCellsSolve))) + xMax = max( xMax, maxval(xCell(1:nCellsSolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + + block_ptr => block_ptr % next + end do + + ! Determine global min and max values. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(diagnosticsPool, 'modifySSHMask', modifySSHMask) + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + + ! Set refBottomDepth and refZMid + do k = 1, nVertLevels + refBottomDepth(k) = config_sub_ice_shelf_2D_bottom_depth * interfaceLocations(k+1) + end do + + ! Compute refLayerThickness and refZMid + call ocn_compute_layerThickness_zMid_from_bottomDepth(refLayerThickness,refZMid, & + refBottomDepth,refBottomDepth(nVertLevels), & + nVertLevels,nVertLevels,iErr) + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + maxDepth = refBottomDepth(nVertLevels) + + if(associated(landIceFraction)) & + landIceFraction(:) = 0.0_RKIND + modifySSHMask(:) = 0 + if(associated(landIceMask)) & + landIceMask(:) = 0 + + do iCell = 1, nCells + ! set up sub ice shelf thicknesses + if (yCell(iCell) < y1 ) then + totalSubIceThickness = d1 + elseif (yCell(iCell) < y2 ) then + totalSubIceThickness = d1 + (d2-d1)*(yCell(iCell)-y1)/(y2-y1) + elseif (yCell(iCell) < y3 ) then + totalSubIceThickness = d2 + (d3-d2)*(yCell(iCell)-y2)/(y3-y2) + else + totalSubIceThickness = d3 + endif + ssh(iCell) = -config_sub_ice_shelf_2D_bottom_depth + totalSubIceThickness + + if(ssh(iCell) < 0.0_RKIND) then + modifySSHMask(iCell) = 1 + end if + end do + + do iCell = 1, nCells + if (yCell(iCell) < y3 ) then + if(associated(landIceFraction)) & + landIceFraction(iCell) = 1.0_RKIND + if(associated(landIceMask)) & + landIceMask(iCell) = 1 + end if + + ! Set bottomDepth + bottomDepth(iCell) = refBottomDepth(nVertLevels) + + ! Set maxLevelCell + maxLevelCell(iCell) = nVertLevels + end do + + block_ptr => block_ptr % next + end do + + ! compute the vertical grid (layerThickness, restingThickness, maxLevelCell, zMid) based on ssh, + ! bottomDepth and refBottomDepth + call ocn_init_ssh_and_landIcePressure_vertical_grid(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_vertical_grid failed.', MPAS_LOG_CRIT) + call mpas_dmpar_finalize(domain % dminfo) + end if + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + + ! compute active tracer fields + ! If we are constructing an initial guess (rather than reading ssh in from a stream), these are reference activeTracers + ! on a vertical grid that has not been displaced by the ssh + do iCell = 1, nCells + ! Set temperature + idx = index_temperature + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = config_sub_ice_shelf_2D_temperature + end do + + ! Set up salinity stratification + idx = index_salinity + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = surfaceSalinity + (bottomSalinity - surfaceSalinity) & + * (zMid(k,iCell)/(-config_sub_ice_shelf_2D_bottom_depth)) + end do + end do + + block_ptr => block_ptr % next + end do + + ! compute or update the land-ice pressure (or possibly SSH), also computing density along the way + ! If this is the initial guess, the vertical grid and activeTracers may also be recomputed based on SSH + call ocn_init_ssh_and_landIcePressure_balance(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_init_ssh_and_landIcePressure_balance failed.', MPAS_LOG_CRIT) + call mpas_dmpar_finalize(domain % dminfo) + end if + + call ocn_compute_Haney_number(domain, iErr) + + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_compute_Haney_number failed.', MPAS_LOG_CRIT) + call mpas_dmpar_finalize(domain % dminfo) + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_sub_ice_shelf_2D!}}} + + !*********************************************************************** + ! + ! routine ocn_init_validate_sub_ice_shelf_2D + ! + !> \brief Validation for this initial condition + !> \author Mark Petersen + !> \date 9/2/2015 + !> \details + !> This routine validates the configuration options for this case. + ! + !----------------------------------------------------------------------- + + subroutine ocn_init_validate_sub_ice_shelf_2D(configPool, packagePool, iocontext, iErr)!{{{ + + !-------------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: configPool, packagePool + type (mpas_io_context_type), intent(inout), target :: iocontext + + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_sub_ice_shelf_2D_vert_levels + + type (mpas_io_context_type), pointer :: iocontext_ptr + + iocontext_ptr => iocontext + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + + if(config_init_configuration .ne. trim('sub_ice_shelf_2D')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_sub_ice_shelf_2D_vert_levels', config_sub_ice_shelf_2D_vert_levels) + + if(config_vert_levels <= 0 .and. config_sub_ice_shelf_2D_vert_levels > 0) then + config_vert_levels = config_sub_ice_shelf_2D_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for sub_ice_shelf_2D. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_sub_ice_shelf_2D!}}} + + + !*********************************************************************** + + end module ocn_init_sub_ice_shelf_2D + + !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ! vim: foldmethod=marker diff --git a/src/core_ocean/mode_init/mpas_ocn_init_vertical_grids.F b/src/core_ocean/mode_init/mpas_ocn_init_vertical_grids.F new file mode 100644 index 0000000000..c3a855ec56 --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_vertical_grids.F @@ -0,0 +1,1486 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_vertical_grids +! +!> \brief MPAS ocean vertical grid generator +!> \author Doug Jacobsen +!> \date 03/20/2015 +!> \details +!> This module contains the routines for generating +!> vertical grids. +! +!----------------------------------------------------------------------- +module ocn_init_vertical_grids + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_timer + + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_generate_vertical_grid, & + ocn_compute_layerThickness_zMid_from_bottomDepth, & + ocn_alter_bottomDepth_for_pbcs, & + ocn_compute_Haney_number, & + ocn_init_vertical_grid_with_max_rx1 + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + + !*********************************************************************** + ! + ! routine ocn_generate_vertical_grid + ! + !> \brief Vertical grid generator driver + !> \author Doug Jacobsen + !> \date 03/20/2015 + !> \details + !> This routine is a driver for generating vertical grids. It calls a private + !> module routine based on the value of the input argument gridType. + !> The output array interfaceLocations will contain values between + !> 0 being the top of top layer and 1 being the bottom of bottom layer + ! + !----------------------------------------------------------------------- + subroutine ocn_generate_vertical_grid(gridType, interfaceLocations, configPool)!{{{ + implicit none + + character (len=*), intent(in) :: gridType + real (kind=RKIND), dimension(:), intent(out) :: interfaceLocations + type (mpas_pool_type), optional, intent(in) :: configPool !< Input: Pool with namelist options + + if ( trim(gridType) == 'uniform' ) then + call ocn_generate_uniform_vertical_grid(interfaceLocations) + else if ( trim(gridType) == '60layerPHC' ) then + call ocn_generate_60layerPHC_vertical_grid(interfaceLocations) + else if ( trim(gridType) == '42layerWOCE' ) then + call ocn_generate_42layerWOCE_vertical_grid(interfaceLocations) + else if ( trim(gridType) == '100layerACMEv1' ) then + call ocn_generate_100layerACMEv1_vertical_grid(interfaceLocations) + else if ( trim(gridType) == '1dCVTgenerator' ) then + if (.not. present(configPool)) then + call mpas_log_write("MPAS-ocean: requesting a 1d CVT vertical grid generation " & + // "without passing the corresponding parameters. Exiting...", MPAS_LOG_CRIT) + else + call ocn_generate_1dCVT_vertical_grid(configPool, interfaceLocations) + end if + else + call mpas_log_write( ' WARNING: '//trim(gridType)//' is an invalid vertical grid choice. No vertical ' & + // 'grid will be generated.') + end if + + end subroutine ocn_generate_vertical_grid!}}} + + !*********************************************************************** + ! + ! routine ocn_generate_uniform_vertical_grid + ! + !> \brief Uniform Vertical grid generator + !> \author Doug Jacobsen + !> \date 03/20/2015 + !> \details + !> This routine generates a uniform vertical grid. + ! + !----------------------------------------------------------------------- + subroutine ocn_generate_uniform_vertical_grid(interfaceLocations)!{{{ + implicit none + + real (kind=RKIND), dimension(:), intent(out) :: interfaceLocations + + real (kind=RKIND) :: layerSpacing + integer :: nInterfaces, iInterface + + call mpas_log_write( ' ---- Generating uniform vertical grid ---- ') + + nInterfaces = size(interfaceLocations, dim=1) + layerSpacing = 1.0_RKIND / (nInterfaces - 1) + + interfaceLocations(1) = 0.0_RKIND + + do iInterface = 2, nInterfaces + interfaceLocations(iInterface) = interfaceLocations(iInterface-1) + layerSpacing + end do + + end subroutine ocn_generate_uniform_vertical_grid!}}} + + !*********************************************************************** + ! + ! routine ocn_generate_60layerPHC_vertical_grid + ! + !> \brief 60 layer PHC vertical grid generator + !> \author Doug Jacobsen + !> \date 03/20/2015 + !> \details + !> This routine generates a 60 layer vertical grid based on the PHC data set. + ! + !----------------------------------------------------------------------- + subroutine ocn_generate_60layerPHC_vertical_grid(interfaceLocations)!{{{ + implicit none + + real (kind=RKIND), dimension(:), intent(out) :: interfaceLocations + + real (kind=RKIND) :: maxInterfaceLocation + integer :: nInterfaces, iInterface + + nInterfaces = size(interfaceLocations, dim=1) + + if ( nInterfaces /= 61 ) then + call mpas_log_write("MPAS-ocean: Vertical grid must have 60 layers to "// & + "apply 60 Layer PHC grid. Exiting...", MPAS_LOG_CRIT) + end if + + interfaceLocations(1) = 0.0_RKIND + interfaceLocations(2) = 500_RKIND + interfaceLocations(3) = 1500_RKIND + interfaceLocations(4) = 2500_RKIND + interfaceLocations(5) = 3500_RKIND + interfaceLocations(6) = 4500_RKIND + interfaceLocations(7) = 5500_RKIND + interfaceLocations(8) = 6500_RKIND + interfaceLocations(9) = 7500_RKIND + interfaceLocations(10) = 8500_RKIND + interfaceLocations(11) = 9500_RKIND + interfaceLocations(12) = 10500_RKIND + interfaceLocations(13) = 11500_RKIND + interfaceLocations(14) = 12500_RKIND + interfaceLocations(15) = 13500_RKIND + interfaceLocations(16) = 14500_RKIND + interfaceLocations(17) = 15500_RKIND + interfaceLocations(18) = 16509.83984375_RKIND + interfaceLocations(19) = 17547.904296875_RKIND + interfaceLocations(20) = 18629.125_RKIND + interfaceLocations(21) = 19766.025390625_RKIND + interfaceLocations(22) = 20971.134765625_RKIND + interfaceLocations(23) = 22257.826171875_RKIND + interfaceLocations(24) = 23640.880859375_RKIND + interfaceLocations(25) = 25137.013671875_RKIND + interfaceLocations(26) = 26765.416015625_RKIND + interfaceLocations(27) = 28548.361328125_RKIND + interfaceLocations(28) = 30511.91796875_RKIND + interfaceLocations(29) = 32686.794921875_RKIND + interfaceLocations(30) = 35109.34375_RKIND + interfaceLocations(31) = 37822.75390625_RKIND + interfaceLocations(32) = 40878.4609375_RKIND + interfaceLocations(33) = 44337.765625_RKIND + interfaceLocations(34) = 48273.66796875_RKIND + interfaceLocations(35) = 52772.796875_RKIND + interfaceLocations(36) = 57937.28515625_RKIND + interfaceLocations(37) = 63886.2578125_RKIND + interfaceLocations(38) = 70756.328125_RKIND + interfaceLocations(39) = 78700.25_RKIND + interfaceLocations(40) = 87882.5234375_RKIND + interfaceLocations(41) = 98470.5859375_RKIND + interfaceLocations(42) = 110620.421875_RKIND + interfaceLocations(43) = 124456.6953125_RKIND + interfaceLocations(44) = 140049.71875_RKIND + interfaceLocations(45) = 157394.640625_RKIND + interfaceLocations(46) = 176400.328125_RKIND + interfaceLocations(47) = 196894.421875_RKIND + interfaceLocations(48) = 218645.65625_RKIND + interfaceLocations(49) = 241397.15625_RKIND + interfaceLocations(50) = 264900.125_RKIND + interfaceLocations(51) = 288938.46875_RKIND + interfaceLocations(52) = 313340.46875_RKIND + interfaceLocations(53) = 337979.375_RKIND + interfaceLocations(54) = 362767.0625_RKIND + interfaceLocations(55) = 387645.21875_RKIND + interfaceLocations(56) = 412576.84375_RKIND + interfaceLocations(57) = 437539.28125_RKIND + interfaceLocations(58) = 462519.0625_RKIND + interfaceLocations(59) = 487508.375_RKIND + interfaceLocations(60) = 512502.84375_RKIND + interfaceLocations(61) = 537500_RKIND + + maxInterfaceLocation = maxval(interfaceLocations) + + interfaceLocations(:) = interfaceLocations(:) / maxInterfaceLocation + + end subroutine ocn_generate_60layerPHC_vertical_grid!}}} + + !*********************************************************************** + ! + ! routine ocn_generate_42layerWOCE_vertical_grid + ! + !> \brief 42 layer WOCE vertical grid generator + !> \author Doug Jacobsen + !> \date 03/20/2015 + !> \details + !> This routine generates a 42 layer vertical grid based on the WOCE data set. + ! + !----------------------------------------------------------------------- + subroutine ocn_generate_42layerWOCE_vertical_grid(interfaceLocations)!{{{ + implicit none + + real (kind=RKIND), dimension(:), intent(out) :: interfaceLocations + + real (kind=RKIND) :: maxInterfaceLocation + integer :: nInterfaces, iInterface + + nInterfaces = size(interfaceLocations, dim=1) + + if ( nInterfaces /= 43 ) then + call mpas_log_write("MPAS-ocean: Vertical grid must have 42 layers "// & + "to apply 42 Layer WOCE grid. Exiting...", MPAS_LOG_CRIT) + end if + + interfaceLocations(1) = 0.0_RKIND + interfaceLocations(2) = 5.00622_RKIND + interfaceLocations(3) = 15.06873_RKIND + interfaceLocations(4) = 25.28343_RKIND + interfaceLocations(5) = 35.75849_RKIND + interfaceLocations(6) = 46.61269_RKIND + interfaceLocations(7) = 57.98099_RKIND + interfaceLocations(8) = 70.02139_RKIND + interfaceLocations(9) = 82.92409_RKIND + interfaceLocations(10) = 96.92413_RKIND + interfaceLocations(11) = 112.3189_RKIND + interfaceLocations(12) = 129.4936_RKIND + interfaceLocations(13) = 148.9582_RKIND + interfaceLocations(14) = 171.4044_RKIND + interfaceLocations(15) = 197.7919_RKIND + interfaceLocations(16) = 229.4842_RKIND + interfaceLocations(17) = 268.4617_RKIND + interfaceLocations(18) = 317.6501_RKIND + interfaceLocations(19) = 381.3864_RKIND + interfaceLocations(20) = 465.9132_RKIND + interfaceLocations(21) = 579.3073_RKIND + interfaceLocations(22) = 729.3513_RKIND + interfaceLocations(23) = 918.3723_RKIND + interfaceLocations(24) = 1139.153_RKIND + interfaceLocations(25) = 1378.574_RKIND + interfaceLocations(26) = 1625.7_RKIND + interfaceLocations(27) = 1875.106_RKIND + interfaceLocations(28) = 2125.011_RKIND + interfaceLocations(29) = 2375_RKIND + interfaceLocations(30) = 2624.999_RKIND + interfaceLocations(31) = 2874.999_RKIND + interfaceLocations(32) = 3124.999_RKIND + interfaceLocations(33) = 3374.999_RKIND + interfaceLocations(34) = 3624.999_RKIND + interfaceLocations(35) = 3874.999_RKIND + interfaceLocations(36) = 4124.999_RKIND + interfaceLocations(37) = 4374.999_RKIND + interfaceLocations(38) = 4624.999_RKIND + interfaceLocations(39) = 4874.999_RKIND + interfaceLocations(40) = 5124.999_RKIND + interfaceLocations(41) = 5374.999_RKIND + interfaceLocations(42) = 5624.999_RKIND + interfaceLocations(43) = 5874.999_RKIND + + maxInterfaceLocation = maxval(interfaceLocations) + + interfaceLocations(:) = interfaceLocations(:) / maxInterfaceLocation + + end subroutine ocn_generate_42layerWOCE_vertical_grid!}}} + + + !*********************************************************************** + ! + ! routine ocn_generate_100layerACMEv1_vertical_grid + ! + !> \brief 100 vertical layer vertical grid generator for ACME v1 + !> \author Todd Ringler + !> \date 04/23/2015 + !> \details + !> This routine generates a 100 layer grid + ! + !----------------------------------------------------------------------- + subroutine ocn_generate_100layerACMEv1_vertical_grid(interfaceLocations)!{{{ + implicit none + + real (kind=RKIND), dimension(:), intent(out) :: interfaceLocations + + real (kind=RKIND) :: maxInterfaceLocation + integer :: nInterfaces, iInterface + + nInterfaces = size(interfaceLocations, dim=1) + + if ( nInterfaces /= 101 ) then + call mpas_log_write("MPAS-ocean: Vertical grid must have 100 layers to "// & + "apply 100 Layer PHC grid. Exiting...", MPAS_LOG_CRIT) + end if + + interfaceLocations( 1) = 0.0000E+00_RKIND + interfaceLocations( 2) = 0.1510E+01_RKIND + interfaceLocations( 3) = 0.3135E+01_RKIND + interfaceLocations( 4) = 0.4882E+01_RKIND + interfaceLocations( 5) = 0.6761E+01_RKIND + interfaceLocations( 6) = 0.8779E+01_RKIND + interfaceLocations( 7) = 0.1095E+02_RKIND + interfaceLocations( 8) = 0.1327E+02_RKIND + interfaceLocations( 9) = 0.1577E+02_RKIND + interfaceLocations( 10) = 0.1845E+02_RKIND + interfaceLocations( 11) = 0.2132E+02_RKIND + interfaceLocations( 12) = 0.2440E+02_RKIND + interfaceLocations( 13) = 0.2769E+02_RKIND + interfaceLocations( 14) = 0.3122E+02_RKIND + interfaceLocations( 15) = 0.3500E+02_RKIND + interfaceLocations( 16) = 0.3904E+02_RKIND + interfaceLocations( 17) = 0.4335E+02_RKIND + interfaceLocations( 18) = 0.4797E+02_RKIND + interfaceLocations( 19) = 0.5289E+02_RKIND + interfaceLocations( 20) = 0.5815E+02_RKIND + interfaceLocations( 21) = 0.6377E+02_RKIND + interfaceLocations( 22) = 0.6975E+02_RKIND + interfaceLocations( 23) = 0.7614E+02_RKIND + interfaceLocations( 24) = 0.8294E+02_RKIND + interfaceLocations( 25) = 0.9018E+02_RKIND + interfaceLocations( 26) = 0.9790E+02_RKIND + interfaceLocations( 27) = 0.1061E+03_RKIND + interfaceLocations( 28) = 0.1148E+03_RKIND + interfaceLocations( 29) = 0.1241E+03_RKIND + interfaceLocations( 30) = 0.1340E+03_RKIND + interfaceLocations( 31) = 0.1445E+03_RKIND + interfaceLocations( 32) = 0.1556E+03_RKIND + interfaceLocations( 33) = 0.1674E+03_RKIND + interfaceLocations( 34) = 0.1799E+03_RKIND + interfaceLocations( 35) = 0.1932E+03_RKIND + interfaceLocations( 36) = 0.2072E+03_RKIND + interfaceLocations( 37) = 0.2221E+03_RKIND + interfaceLocations( 38) = 0.2379E+03_RKIND + interfaceLocations( 39) = 0.2546E+03_RKIND + interfaceLocations( 40) = 0.2722E+03_RKIND + interfaceLocations( 41) = 0.2909E+03_RKIND + interfaceLocations( 42) = 0.3106E+03_RKIND + interfaceLocations( 43) = 0.3314E+03_RKIND + interfaceLocations( 44) = 0.3534E+03_RKIND + interfaceLocations( 45) = 0.3766E+03_RKIND + interfaceLocations( 46) = 0.4011E+03_RKIND + interfaceLocations( 47) = 0.4269E+03_RKIND + interfaceLocations( 48) = 0.4541E+03_RKIND + interfaceLocations( 49) = 0.4827E+03_RKIND + interfaceLocations( 50) = 0.5128E+03_RKIND + interfaceLocations( 51) = 0.5445E+03_RKIND + interfaceLocations( 52) = 0.5779E+03_RKIND + interfaceLocations( 53) = 0.6130E+03_RKIND + interfaceLocations( 54) = 0.6498E+03_RKIND + interfaceLocations( 55) = 0.6885E+03_RKIND + interfaceLocations( 56) = 0.7291E+03_RKIND + interfaceLocations( 57) = 0.7717E+03_RKIND + interfaceLocations( 58) = 0.8164E+03_RKIND + interfaceLocations( 59) = 0.8633E+03_RKIND + interfaceLocations( 60) = 0.9124E+03_RKIND + interfaceLocations( 61) = 0.9638E+03_RKIND + interfaceLocations( 62) = 0.1018E+04_RKIND + interfaceLocations( 63) = 0.1074E+04_RKIND + interfaceLocations( 64) = 0.1133E+04_RKIND + interfaceLocations( 65) = 0.1194E+04_RKIND + interfaceLocations( 66) = 0.1259E+04_RKIND + interfaceLocations( 67) = 0.1326E+04_RKIND + interfaceLocations( 68) = 0.1396E+04_RKIND + interfaceLocations( 69) = 0.1469E+04_RKIND + interfaceLocations( 70) = 0.1546E+04_RKIND + interfaceLocations( 71) = 0.1625E+04_RKIND + interfaceLocations( 72) = 0.1708E+04_RKIND + interfaceLocations( 73) = 0.1794E+04_RKIND + interfaceLocations( 74) = 0.1884E+04_RKIND + interfaceLocations( 75) = 0.1978E+04_RKIND + interfaceLocations( 76) = 0.2075E+04_RKIND + interfaceLocations( 77) = 0.2176E+04_RKIND + interfaceLocations( 78) = 0.2281E+04_RKIND + interfaceLocations( 79) = 0.2390E+04_RKIND + interfaceLocations( 80) = 0.2503E+04_RKIND + interfaceLocations( 81) = 0.2620E+04_RKIND + interfaceLocations( 82) = 0.2742E+04_RKIND + interfaceLocations( 83) = 0.2868E+04_RKIND + interfaceLocations( 84) = 0.2998E+04_RKIND + interfaceLocations( 85) = 0.3134E+04_RKIND + interfaceLocations( 86) = 0.3274E+04_RKIND + interfaceLocations( 87) = 0.3418E+04_RKIND + interfaceLocations( 88) = 0.3568E+04_RKIND + interfaceLocations( 89) = 0.3723E+04_RKIND + interfaceLocations( 90) = 0.3882E+04_RKIND + interfaceLocations( 91) = 0.4047E+04_RKIND + interfaceLocations( 92) = 0.4218E+04_RKIND + interfaceLocations( 93) = 0.4393E+04_RKIND + interfaceLocations( 94) = 0.4574E+04_RKIND + interfaceLocations( 95) = 0.4761E+04_RKIND + interfaceLocations( 96) = 0.4953E+04_RKIND + interfaceLocations( 97) = 0.5151E+04_RKIND + interfaceLocations( 98) = 0.5354E+04_RKIND + interfaceLocations( 99) = 0.5564E+04_RKIND + interfaceLocations(100) = 0.5779E+04_RKIND + interfaceLocations(101) = 0.6000E+04_RKIND + + maxInterfaceLocation = maxval(interfaceLocations) + + interfaceLocations(:) = interfaceLocations(:) / maxInterfaceLocation + + end subroutine ocn_generate_100layerACMEv1_vertical_grid!}}} + + +!*********************************************************************** +! +! routine ocn_generate_1dCVT_vertical_grid +! +!> \brief 1D CVT vertical grid generator +!> \author Juan A. Saenz +!> \date 09/10/2015 +!> \details +!> This routine generates a vertical grid with total depth = 1. +!> This code is adapted from Todd's cvt_1d code. +! +!----------------------------------------------------------------------- + + subroutine ocn_generate_1dCVT_vertical_grid(configPool, interfaceLocations)!{{{ + + type (mpas_pool_type), intent(in) :: configPool + real (kind=RKIND), dimension(:), intent(out) :: interfaceLocations + + integer :: k + integer :: nInterfaces, nVertLevels + real (kind=RKIND) :: stretch1 + real (kind=RKIND) :: stretch2 + real (kind=RKIND) :: dzSeed + + real (kind=RKIND) :: stretch + real (kind=RKIND) :: dz + real (kind=RKIND) :: maxInterfaceLocation + + real (kind=RKIND), pointer :: config_1dCVTgenerator_stretch1 + real (kind=RKIND), pointer :: config_1dCVTgenerator_stretch2 + real (kind=RKIND), pointer :: config_1dCVTgenerator_dzSeed + + call mpas_pool_get_config(configPool, 'config_1dCVTgenerator_stretch1', config_1dCVTgenerator_stretch1) + call mpas_pool_get_config(configPool, 'config_1dCVTgenerator_stretch2', config_1dCVTgenerator_stretch2) + call mpas_pool_get_config(configPool, 'config_1dCVTgenerator_dzSeed', config_1dCVTgenerator_dzSeed) + + stretch1 = config_1dCVTgenerator_stretch1 + stretch2 = config_1dCVTgenerator_stretch2 + dzSeed = config_1dCVTgenerator_dzSeed + + nInterfaces = size(interfaceLocations, dim=1) + nVertLevels = nInterfaces - 1 + + ! compute profile starting at top and stretch dz as we move down + dz = dzSeed + interfaceLocations(1) = 0.0_RKIND + interfaceLocations(2) = dz + do k=2,nVertLevels + stretch = stretch1 + (stretch2-stretch1)*k/nVertLevels + dz = stretch*dz + interfaceLocations(k+1) = interfaceLocations(k) + dz + enddo + + ! normalize so that positions span 0 to 1 + maxInterfaceLocation = maxval(interfaceLocations) + interfaceLocations(:) = interfaceLocations(:) / maxInterfaceLocation + + end subroutine ocn_generate_1dCVT_vertical_grid!}}} + + +!*********************************************************************** +! +! routine ocn_compute_layerThickness_zMid_from_bottomDepth +! +!> \brief Compute auxiliary z-variables from bottomDepth +!> \author Mark Petersen +!> \date 10/17/2015 +!> \details +!> This routine computes auxiliary z-variables from bottomDepth +! +!----------------------------------------------------------------------- + + subroutine ocn_compute_layerThickness_zMid_from_bottomDepth(layerThickness,zMid,refBottomDepth,bottomDepth, & + maxLevelCell,nVertLevels,iErr,restingThickness,ssh)!{{{ + real (kind=RKIND), dimension(nVertLevels), intent(out) :: layerThickness, zMid + real (kind=RKIND), dimension(nVertLevels), intent(in) :: refBottomDepth + real (kind=RKIND), intent(in) :: bottomDepth + integer, intent(in) :: maxLevelCell, nVertLevels + integer, intent(out) :: iErr + real (kind=RKIND), dimension(nVertLevels), intent(out), optional :: restingThickness + real (kind=RKIND), intent(in), optional :: ssh + + integer :: k + real (kind=RKIND) :: layerStretch, zTop + + iErr = 0 + + layerThickness(:) = 0.0_RKIND + zMid(:) = 0.0_RKIND + + if(present(ssh) .and. .not. present(restingThickness)) then + call mpas_log_write( ' Error: ssh present but restingThickness not present ' & + // 'in ocn_compute_layerThickness_zMid_from_bottomDepth') + iErr = 1 + return + end if + + if (maxLevelCell<=0) return + + ! first, compute the resting layer thickness (same as layer thickness if ssh not present) + if (maxLevelCell==1) then + layerThickness(1) = bottomDepth + else + layerThickness(1) = refBottomDepth(1) + + do k = 2, maxLevelCell-1 + layerThickness(k) = refBottomDepth(k) - refBottomDepth(k-1) + end do + + k = maxLevelCell + layerThickness(k) = bottomDepth - refBottomDepth(k-1) + + endif + + zTop = 0.0_RKIND + ! copy to layerThickness to restingThickness + if (present(restingThickness)) then + restingThickness(:) = layerThickness(:) + ! stretch layers if ssh is present + if(present(ssh)) then + layerStretch = (ssh + bottomDepth)/bottomDepth + zTop = ssh + do k=1,maxLevelCell + layerThickness(k) = layerStretch*restingThickness(k) + end do + end if + end if + + ! compute zMid based on the layer thickness + do k = 1, maxLevelCell + zMid(k) = zTop - 0.5_RKIND*layerThickness(k) + zTop = zTop - layerThickness(k) + end do + + end subroutine ocn_compute_layerThickness_zMid_from_bottomDepth !}}} + + +!*********************************************************************** +! +! routine ocn_alter_bottomDepth_for_pbcs +! +!> \brief Alter bottom depth for partial bottom cells +!> \author Mark Petersen +!> \date 10/19/2015 +!> \details +!> This routine alters the bottom depth in a single column based on pbc settings +! +!----------------------------------------------------------------------- + subroutine ocn_alter_bottomDepth_for_pbcs(bottomDepth, refBottomDepth, maxLevelCell, iErr) + + real (kind=RKIND), intent(inout) :: bottomDepth + integer, intent(inout) :: maxLevelCell + real (kind=RKIND), dimension(maxLevelCell), intent(in) :: refBottomDepth + integer, intent(out) :: iErr + integer :: k + + logical, pointer :: config_alter_ICs_for_pbcs + real (kind=RKIND) :: minBottomDepth, minBottomDepthMid + real (kind=RKIND), pointer :: config_min_pbc_fraction + character (len=StrKIND), pointer :: config_pbc_alteration_type + call mpas_pool_get_config(ocnConfigs, 'config_alter_ICs_for_pbcs', config_alter_ICs_for_pbcs) + call mpas_pool_get_config(ocnConfigs, 'config_pbc_alteration_type', config_pbc_alteration_type) + call mpas_pool_get_config(ocnConfigs, 'config_min_pbc_fraction', config_min_pbc_fraction) + + iErr = 0 + + if (maxLevelCell > 1) then + if (config_alter_ICs_for_pbcs) then + + if (config_pbc_alteration_type .eq. 'partial_cell') then + ! Change value of maxLevelCell for partial bottom cells + k = maxLevelCell + minBottomDepth = refBottomDepth(k) - (1.0-config_min_pbc_fraction)*(refBottomDepth(k) - refBottomDepth(k-1)) + minBottomDepthMid = 0.5_RKIND*(minBottomDepth + refBottomDepth(k-1)) + if (bottomDepth .lt. minBottomDepthMid) then + ! Round up to cell above + maxLevelCell = maxLevelCell - 1 + bottomDepth = refBottomDepth(maxLevelCell) + else if (bottomDepth .lt. minBottomDepth) then + ! Round down cell to the min_pbc_fraction. + bottomDepth = minBottomDepth + end if + elseif (config_pbc_alteration_type .eq. 'full_cell') then + bottomDepth = refBottomDepth(maxLevelCell) + else + call mpas_log_write( ' Error: Incorrect choice of config_pbc_alteration_type: '// config_pbc_alteration_type) + iErr = 1 + endif + endif + endif + + end subroutine ocn_alter_bottomDepth_for_pbcs + +!*********************************************************************** +! +! routine ocn_compute_Haney_number +! +!> \brief computes the Haney number (rx1) +!> \author Xylar Asay-Davis +!> \date 11/20/2015 +!> \details +!> This routine computes the Haney number (rx1), which is a measure of +!> hydrostatic consistency +! +!----------------------------------------------------------------------- + subroutine ocn_compute_Haney_number(domain, iErr) + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, diagnosticsPool, statePool + real (kind=RKIND), dimension(:,:), pointer :: zMid + real (kind=RKIND), dimension(:,:), pointer :: rx1Edge, rx1Cell + real (kind=RKIND), dimension(:), pointer :: rx1MaxEdge, rx1MaxCell, ssh, rx1MaxLevel + real (kind=RKIND), pointer :: globalRx1Max + + integer, pointer :: nCells, nVertLevels, nEdges + integer, dimension(:), pointer :: maxLevelCell + integer, dimension(:,:), pointer :: cellsOnEdge + + integer :: iEdge, c1, c2, k, maxLevelEdge + + real (kind=RKIND) :: dzVert1, dzVert2, dzEdgeK, dzEdgeKp1, rx1, localMaxRx1Edge + + iErr = 0 + + localMaxRx1Edge = 0.0_RKIND + + block_ptr => domain % blocklist + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + allocate(rx1MaxLevel(nVertLevels)) + rx1MaxLevel(:) = 0.0_RKIND + + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid, 1) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + + call mpas_pool_get_array(diagnosticsPool, 'rx1Edge', rx1Edge, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1Cell', rx1Cell, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1MaxEdge', rx1MaxEdge, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1MaxCell', rx1MaxCell, 1) + + rx1Edge(:,:) = 0.0_RKIND + rx1Cell(:,:) = 0.0_RKIND + rx1MaxEdge(:) = 0.0_RKIND + rx1MaxCell(:) = 0.0_RKIND + do iEdge = 1,nEdges + c1 = cellsOnEdge(1,iEdge) + c2 = cellsOnEdge(2,iEdge) + ! not a valid edge + if((c1 > nCells) .or. (c2 > nCells)) cycle + maxLevelEdge = min(maxLevelCell(c1), maxLevelCell(c2)) + do k = 1,maxLevelEdge + if(k == 1) then + dzVert1 = 2.0_RKIND*(ssh(c1)-zMid(k,c1)) + dzVert2 = 2.0_RKIND*(ssh(c2)-zMid(k,c2)) + dzEdgeK = ssh(c2)-ssh(c1) + else + dzVert1 = zMid(k-1,c1)-zMid(k,c1) + dzVert2 = zMid(k-1,c2)-zMid(k,c2) + dzEdgeK = zMid(k-1,c2)-zMid(k-1,c1) + end if + dzEdgeKp1 = zMid(k,c2)-zMid(k,c1) + + rx1 = abs(dzEdgeK+dzEdgeKp1)/(dzVert1+dzVert2) + + rx1Edge(k,iEdge) = rx1 + rx1MaxLevel(k) = max(rx1MaxLevel(k),rx1) + rx1Cell(k,c1) = max(rx1Cell(k,c1),rx1) + rx1Cell(k,c2) = max(rx1Cell(k,c2),rx1) + + rx1MaxEdge(iEdge) = max(rx1MaxEdge(iEdge),rx1) + rx1MaxCell(c2) = max(rx1MaxCell(c2),rx1) + rx1MaxCell(c1) = max(rx1MaxCell(c1),rx1) + end do + end do + + localMaxRx1Edge = max(localMaxRx1Edge,maxval(rx1MaxEdge)) + + block_ptr => block_ptr % next + end do + call mpas_pool_get_subpool(domain % blocklist % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_array(diagnosticsPool, 'globalRx1Max', globalRx1Max, 1) + do k = 1,nVertLevels + call mpas_dmpar_max_real(domain % dminfo, rx1MaxLevel(k), globalRx1Max) + call mpas_log_write (' max of rx1 in level $i : $r', intArgs=(/ k /), realArgs=(/ globalRx1Max /)) + end do + + call mpas_dmpar_max_real(domain % dminfo, localMaxRx1Edge, globalRx1Max) + call mpas_log_write ('global max of rx1: $r', realArgs=(/ globalRx1Max /)) + + deallocate(rx1MaxLevel) + + end subroutine ocn_compute_Haney_number + +!*********************************************************************** +! +! routine ocn_init_vertical_grid_with_max_rx1 +! +!> \brief re-initializes the vertical grid so rx1 < rx1Max +!> \author Xylar Asay-Davis +!> \date 11/23/2015 +!> \details +!> This routine re-initializes the vertical grid (layerThickness, +!> restingThickness maxLevelCell, zMid) so that the Haney number is +!> less than a maximum value (rx1 < rx1Max). ssh and bottomDepth should +!> have been initialized before calling this routine. bottomDepth will +!> be modified for full or partial bottom cells in this routine, so +!> this step should not be performed before calling this routine +! +!----------------------------------------------------------------------- + subroutine ocn_init_vertical_grid_with_max_rx1(domain, iErr) + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, statePool, diagnosticsPool, verticalMeshPool, scratchPool, forcingPool + + integer, pointer :: config_rx1_outer_iter_count, config_rx1_inner_iter_count, & + config_rx1_horiz_smooth_open_ocean_cells, config_rx1_min_levels + real (kind=RKIND), pointer :: config_rx1_max, config_rx1_horiz_smooth_weight, & + config_rx1_vert_smooth_weight, config_rx1_slope_weight, & + config_rx1_zstar_weight, config_rx1_init_inner_weight, & + config_rx1_min_layer_thickness + + type (field2DReal), pointer :: zInterfaceField, goalStretchField, goalWeightField, & + verticalStretchField + type (field1DReal), pointer :: zTopField, zBotField, zBotNewField + type (field1DInteger), pointer :: smoothingMaskField, smoothingMaskNewField + + real (kind=RKIND), dimension(:,:), pointer :: zMid, layerThickness, restingThickness, zInterface, & + verticalStretch, goalStretch, goalWeight, rx1Edge + real (kind=RKIND), dimension(:), pointer :: ssh, bottomDepth, refBottomDepth, zTop, zBot, zBotNew, landIceFraction, & + refLayerThickness + + real (kind=RKIND), pointer :: globalRx1Max, globalVerticalStretchMax, globalVerticalStretchMin + + integer, pointer :: nCells, nVertLevels, nEdges + integer, dimension(:), pointer :: maxLevelCell, cullCell, nEdgesOnCell, smoothingMask, smoothingMaskNew + integer, dimension(:,:), pointer :: cellsOnEdge, cellsOnCell, edgesOnCell + + integer :: iCell, iEdge, coc, c1, c2, k, maxLevelEdge, iSmooth, iterIndex + + real (kind=RKIND) :: dzEdgeK, dzEdgeKp1, dzEdgeMean, dzVertGoal, & + zMean, weight, rx1Goal, dzVertMean, & + zMidNext, frac, stretch, localMaxRx1Edge, & + localStretchMax, localStretchMin + + logical :: moveInterface + + real (kind=RKIND), parameter :: eps=1e-6_RKIND + + iErr = 0 + + call mpas_pool_get_config(domain % configs, 'config_rx1_outer_iter_count', config_rx1_outer_iter_count) + call mpas_pool_get_config(domain % configs, 'config_rx1_inner_iter_count', config_rx1_inner_iter_count) + call mpas_pool_get_config(domain % configs, 'config_rx1_init_inner_weight', config_rx1_init_inner_weight) + call mpas_pool_get_config(domain % configs, 'config_rx1_max', config_rx1_max) + call mpas_pool_get_config(domain % configs, 'config_rx1_horiz_smooth_weight', config_rx1_horiz_smooth_weight) + call mpas_pool_get_config(domain % configs, 'config_rx1_vert_smooth_weight', config_rx1_vert_smooth_weight) + call mpas_pool_get_config(domain % configs, 'config_rx1_slope_weight', config_rx1_slope_weight) + call mpas_pool_get_config(domain % configs, 'config_rx1_zstar_weight', config_rx1_zstar_weight) + call mpas_pool_get_config(domain % configs, 'config_rx1_horiz_smooth_open_ocean_cells', & + config_rx1_horiz_smooth_open_ocean_cells) + call mpas_pool_get_config(domain % configs, 'config_rx1_min_levels', config_rx1_min_levels) + call mpas_pool_get_config(domain % configs, 'config_rx1_min_layer_thickness', config_rx1_min_layer_thickness) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_field(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMaskField, 1) + call mpas_pool_get_field(diagnosticsPool, 'verticalStretch', verticalStretchField, 1) + call mpas_pool_get_array(diagnosticsPool, 'globalRx1Max', globalRx1Max, 1) + call mpas_pool_get_array(diagnosticsPool, 'globalVerticalStretchMax', globalVerticalStretchMax, 1) + call mpas_pool_get_array(diagnosticsPool, 'globalVerticalStretchMin', globalVerticalStretchMin, 1) + + ! allocate scratch variables that persist across blocks + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_field(scratchPool, 'zInterfaceScratch', zInterfaceField) + call mpas_pool_get_field(scratchPool, 'zTopScratch', zTopField) + call mpas_pool_get_field(scratchPool, 'zBotScratch', zBotField) + call mpas_allocate_scratch_field(zInterfaceField, .false.) + call mpas_allocate_scratch_field(zTopField, .false.) + call mpas_allocate_scratch_field(zBotField, .false.) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'cullCell', cullCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + + maxLevelCell(nCells+1) = -1 + do iCell = 1, nCells + if(associated(cullCell)) then + if(cullCell(iCell) == 1) then + ! we need to know to ignore this cell later + maxLevelCell(iCell) = -1 + end if + end if + end do + + ! initialize the smoothing mask to valid cells under land ice + smoothingMask(:) = 0 + where((maxLevelCell(:) > 0) .and. (landIceFraction(:) > eps)) + smoothingMask(:) = 1 + end where + + block_ptr => block_ptr % next + end do !block_ptr + + ! expand the smoothing mask to neighbors of land-ice cells + do iSmooth = 1, config_rx1_horiz_smooth_open_ocean_cells + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + + call mpas_pool_get_field(scratchPool, 'smoothingMaskNewScratch', smoothingMaskNewField) + call mpas_allocate_scratch_field(smoothingMaskNewField, .true.) + call mpas_pool_get_array(scratchPool, 'smoothingMaskNewScratch', smoothingMaskNew) + + smoothingMaskNew(:) = smoothingMask(:) + + ! expand the mask to neighbors + do iCell = 1, nCells + if(smoothingMask(iCell) == 0) cycle + + do iEdge = 1, nEdgesOnCell(iCell) + coc = cellsOnCell(iEdge,iCell) + if(coc > 0) then + if((maxLevelCell(coc) > 0) .and. (smoothingMaskNew(coc) == 0)) then + ! we have a neighbor of a cell being smoothed so this one should also be smoothed + smoothingMaskNew(coc) = 1 + end if + end if + end do !iEdge + end do !iCell + + smoothingMask(:) = smoothingMaskNew(:) + call mpas_deallocate_scratch_field(smoothingMaskNewField, .true.) + + block_ptr => block_ptr % next + end do !block_ptr + + ! do halo update on smoothingMask + call mpas_dmpar_exch_halo_field(smoothingMaskField) + end do !iSmooth + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(scratchPool, 'zInterfaceScratch', zInterface) + + call mpas_pool_get_array(diagnosticsPool, 'verticalStretch', verticalStretch, 1) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + + do iCell = 1, nCells + if(maxLevelCell(iCell) <= 0) then + ! this is land + zInterface(:,iCell) = 0.0_RKIND + verticalStretch(:,iCell) = 1.0_RKIND + cycle + end if + + ! initialize zInterface to z* without PBCs and extended to nVertLevels+1 (beyond bottomDepth) + + ! don't let bottomDepth go below the z-level grid + bottomDepth(iCell) = min(bottomDepth(iCell), refBottomDepth(nVertLevels)) + + ! lower bottomDepth if the whole column is thinner than the minimum + !bottomDepth(iCell) = max(bottomDepth(iCell), -ssh(iCell) + config_rx1_min_layer_thickness*config_rx1_min_levels) + + verticalStretch(:,iCell) = (ssh(iCell) + bottomDepth(iCell))/bottomDepth(iCell) + + zInterface(1,iCell) = ssh(iCell) + do k = 1, nVertLevels + zInterface(k+1,iCell) = zInterface(k,iCell) - verticalStretch(k,iCell)*refLayerThickness(k) + end do + + end do !iCell + + block_ptr => block_ptr % next + end do !block_ptr + + + do iterIndex = 1, config_rx1_outer_iter_count + + ! smooth/nudge twice so changes propagate further per halo update + do iSmooth = 1, 2 + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + + call mpas_pool_get_array(diagnosticsPool, 'verticalStretch', verticalStretch, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1Edge', rx1Edge, 1) + + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + + call mpas_pool_get_field(scratchPool, 'goalStretchScratch', goalStretchField) + call mpas_allocate_scratch_field(goalStretchField, .true.) + call mpas_pool_get_array(scratchPool, 'goalStretchScratch', goalStretch) + call mpas_pool_get_field(scratchPool, 'goalWeightScratch', goalWeightField) + call mpas_allocate_scratch_field(goalWeightField, .true.) + call mpas_pool_get_array(scratchPool, 'goalWeightScratch', goalWeight) + + goalStretch(:,:) = 0.0_RKIND + goalWeight(:,:) = 0.0_RKIND + + do iCell = 1, nCells + if(smoothingMask(iCell) == 0) cycle + + do iEdge = 1, nEdgesOnCell(iCell) + coc = cellsOnCell(iEdge,iCell) + if(maxLevelCell(coc) <= 0) cycle + + ! average horiz neighbors + goalStretch(:, iCell) = goalStretch(:, iCell) + config_rx1_horiz_smooth_weight*verticalStretch(:, coc) + goalWeight(:, iCell) = goalWeight(:, iCell) + config_rx1_horiz_smooth_weight + + ! change stretch toward flatter level interfaces + do k = 1, nVertLevels + ! we want to try to move the bottom interface to zMean so it's more level + zMean = 0.5_RKIND*(zInterface(k+1,iCell) + zInterface(k+1,coc)) + ! frac is the factor by which we want to modify the stretch above (and including) this cell + frac = (ssh(iCell)-zMean)/(ssh(iCell)-zInterface(k+1,iCell)) + goalStretch(1:k, iCell) = goalStretch(1:k, iCell) & + + config_rx1_slope_weight*frac*verticalStretch(1:k, iCell) + goalWeight(1:k, iCell) = goalWeight(1:k, iCell) + config_rx1_slope_weight + end do + end do !iEdge + + ! include this cell in average + goalStretch(:, iCell) = goalStretch(:, iCell) + verticalStretch(:, iCell) + goalWeight(:, iCell) = goalWeight(:, iCell) + 1.0_RKIND + + ! average vert neighbors + goalStretch(1:nVertLevels-1, iCell) = goalStretch(1:nVertLevels-1, iCell) & + + config_rx1_vert_smooth_weight*verticalStretch(2:nVertLevels, iCell) + goalWeight(1:nVertLevels-1, iCell) = goalWeight(1:nVertLevels-1, iCell) & + + config_rx1_vert_smooth_weight + goalStretch(2:nVertLevels, iCell) = goalStretch(2:nVertLevels, iCell) & + + config_rx1_vert_smooth_weight*verticalStretch(1:nVertLevels-1, iCell) + goalWeight(2:nVertLevels, iCell) = goalWeight(2:nVertLevels, iCell) & + + config_rx1_vert_smooth_weight + + ! nudge toward z-star + stretch = (ssh(iCell) + bottomDepth(iCell))/bottomDepth(iCell) + goalStretch(:, iCell) = goalStretch(:, iCell) + config_rx1_zstar_weight*stretch + goalWeight(:, iCell) = goalWeight(:, iCell) + config_rx1_zstar_weight + + end do !iCell + + do iCell = 1, nCells + if(smoothingMask(iCell) == 1) then + do k = 1, nVertLevels + ! minimum allowed stretch + stretch = config_rx1_min_layer_thickness/refLayerThickness(k) + verticalStretch(k,iCell) = max(stretch, goalStretch(k,iCell)/goalWeight(k,iCell)) + end do + end if + end do + + call mpas_deallocate_scratch_field(goalStretchField, .true.) + call mpas_deallocate_scratch_field(goalWeightField, .true.) + + block_ptr => block_ptr % next + end do !block_ptr + end do !iSmooth + + ! do a halo exchange on verticalStretch + call mpas_dmpar_exch_halo_field(verticalStretchField) + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(scratchPool, 'zTopScratch', zTop) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + + zTop(:) = ssh(:) + where(smoothingMask == 1) + ! start with maxLevelCell == nVertLevels+1; we will update it when we encounter the bottom + maxLevelCell(:) = nVertLevels+1 + end where + + block_ptr => block_ptr % next + end do + + do k = 1, nVertLevels + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(scratchPool, 'zInterfaceScratch', zInterface) + call mpas_pool_get_array(scratchPool, 'zBotScratch', zBot) + call mpas_pool_get_array(diagnosticsPool, 'verticalStretch', verticalStretch, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + + zInterface(k+1,:) = zInterface(k,:) - verticalStretch(k,:)*refLayerThickness(k) + + ! match interfaces to bottomDepth + if(k >= config_rx1_min_levels) then + moveInterface = .false. + if(config_rx1_outer_iter_count == 1) then + weight = 1.0_RKIND + else + weight = (iterIndex - 1.0_RKIND)/(config_rx1_outer_iter_count - 1.0_RKIND) + end if + + do iCell = 1, nCells + if((smoothingMask(iCell) == 0) .or. (maxLevelCell(iCell) .ne. nVertLevels+1)) cycle + + if(-bottomDepth(iCell) > zBot(iCell)) then + ! we missed maxLevelCell, either because k < config_rx1_min_levels or + ! because our guess at the layer depth wasn't accurate. We can't move + ! the layer interface to match bottomDepth but we can at least set + ! maxLevelCell to the appropriate value + maxLevelCell(iCell) = k + cycle + end if + + if(k == nVertLevels) then + ! match the bottom of the layer to bottomDepth + moveInterface = .true. + else + zInterface(k+1,iCell) = zInterface(k,iCell) - verticalStretch(k,iCell)*refLayerThickness(k) + ! our current best guess at the mid depth of the next layer + zMidNext = zInterface(k+1,iCell) - 0.5_RKIND*verticalStretch(k+1,iCell)*refLayerThickness(k+1) + moveInterface = -bottomDepth(iCell) >= zMidNext + end if + ! relax toward bottomDepth with increasing strength with each iteration + if(moveInterface) then + zInterface(k+1,iCell) = min((1.0_RKIND - weight)*zInterface(k+1,iCell) + weight*(-bottomDepth(iCell)), & + zInterface(k,iCell) - config_rx1_min_layer_thickness) + maxLevelCell(iCell) = k + end if + end do + end if + zBot(:) = 0.5_RKIND*(zInterface(k,:) + zInterface(k+1,:)) + + block_ptr => block_ptr % next + end do !block_ptr + + if(k == 1) then + ! rx1 is allowed to get twice as big in the top layer because we're only looking at half a layer + rx1Goal = 2.0_RKIND*config_rx1_max + else + rx1Goal = config_rx1_max + end if + call constrain_rx1_layer(domain, config_rx1_inner_iter_count, config_rx1_init_inner_weight, & + rx1Goal, k > config_rx1_min_levels, iErr) + + ! update zInterface, zTop + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_array(scratchPool, 'zInterfaceScratch', zInterface) + call mpas_pool_get_array(scratchPool, 'zTopScratch', zTop) + call mpas_pool_get_array(scratchPool, 'zBotScratch', zBot) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid, 1) + call mpas_pool_get_array(diagnosticsPool, 'verticalStretch', verticalStretch, 1) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + + where (smoothingMask(:) == 1) + verticalStretch(k,:) = 2.0_RKIND*(zInterface(k,:) - zBot(:))/refLayerThickness(k) + zInterface(k+1,:) = 2.0_RKIND*zBot(:) - zInterface(k,:) + end where + + zTop(:) = zBot(:) + zMid(k,:) = zBot(:) + + block_ptr => block_ptr % next + end do !block_ptr + + end do !k + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + + where((smoothingMask(:) == 1) .and. (maxLevelCell(:) == nVertLevels+1)) + ! we never found the bottom, so it must be the last level + maxLevelCell(:) = nVertLevels + end where + + block_ptr => block_ptr % next + end do + + localMaxRx1Edge = -1e30_RKIND + localStretchMin = 1e30_RKIND + localStretchMax = -1e30_RKIND + + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(diagnosticsPool, 'verticalStretch', verticalStretch, 1) + call mpas_pool_get_array(diagnosticsPool, 'rx1Edge', rx1Edge, 1) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid, 1) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + + ! compute rx1Edge so we can determine which cells need to be thickened + rx1Edge(:,:) = 0.0_RKIND + do iEdge = 1,nEdges + c1 = cellsOnEdge(1,iEdge) + c2 = cellsOnEdge(2,iEdge) + if((c1 <= 0) .or. (c2 <= 0) .or. (c1 > nCells) .or. (c2 > nCells)) cycle + if((maxLevelCell(c1) <= 0) .or. (maxLevelCell(c2) <= 0)) cycle + + ! at the top level, use ssh instead of zMid + dzVertMean = 0.5_RKIND*(ssh(c1)-zMid(1,c1)+ssh(c2)-zMid(1,c2)) + dzEdgeMean = 0.5_RKIND*abs(ssh(c2)-ssh(c1)+zMid(1,c2)-zMid(1,c1)) + ! a factor of 0.5 because ssh is at top interface, not middle of the previous layer + rx1Edge(1,iEdge) = 0.5_RKIND*dzEdgeMean/dzVertMean + maxLevelEdge = min(maxLevelCell(c1), maxLevelCell(c2)) + do k = 1, maxLevelEdge-1 + dzVertMean = 0.5_RKIND*(zMid(k,c1)-zMid(k+1,c1)+zMid(k,c2)-zMid(k+1,c2)) + dzEdgeMean = 0.5_RKIND*abs(zMid(k,c2)-zMid(k,c1)+zMid(k+1,c2)-zMid(k+1,c1)) + rx1Edge(k+1,iEdge) = dzEdgeMean/dzVertMean + end do + end do + + localMaxRx1Edge = max(localMaxRx1Edge,maxval(rx1Edge)) + + do iCell = 1, nCells + if(maxLevelCell(iCell) <= 0) cycle + do k = 1, maxLevelCell(iCell) + localStretchMax = max(localStretchMax, verticalStretch(k,iCell)) + localStretchMin = min(localStretchMin, verticalStretch(k,iCell)) + end do + end do + + block_ptr => block_ptr % next + end do + call mpas_dmpar_max_real(domain % dminfo, localMaxRx1Edge, globalRx1Max) + call mpas_log_write (' iter: $i global max of rx1 $r', intArgs=(/ iterIndex /), realArgs=(/ globalRx1Max /)) + call mpas_dmpar_min_real(domain % dminfo, localStretchMin, globalVerticalStretchMin) + call mpas_log_write (' global min of verticalStretch: $r', realArgs=(/ globalVerticalStretchMin /)) + call mpas_dmpar_max_real(domain % dminfo, localStretchMax, globalVerticalStretchMax) + call mpas_log_write (' global max of verticalStretch: $r', realArgs=(/ globalVerticalStretchMax /)) + + end do !iterIndex + + ! compute maxLevelCell, zMid and restingThickness; update bottomDepth and layerThickness + ! for full or partial bottom cells (if requested) + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid, 1) + call mpas_pool_get_array(scratchPool, 'zInterfaceScratch', zInterface) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + call mpas_pool_get_array(diagnosticsPool, 'verticalStretch', verticalStretch, 1) + call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) + + ! compute zMid, layerThickness and restingThickness + do iCell = 1, nCells + if(maxLevelCell(iCell) == -1) then + bottomDepth(iCell) = 0.0_RKIND + zMid(:,iCell) = 0.0_RKIND + layerThickness(:,iCell) = 0.0_RKIND + restingThickness(:,iCell) = 0.0_RKIND + cycle + end if + + + if(smoothingMask(iCell) == 0) then + ! initialize with PBCs + call ocn_alter_bottomDepth_for_pbcs(bottomDepth(iCell), refBottomDepth, maxLevelCell(iCell), iErr) + if(iErr .ne. 0) then + call mpas_log_write( 'ocn_alter_bottomDepth_for_pbcs failed.', MPAS_LOG_CRIT) + return + end if + else + ! we may not have been able to put the layer interface exactly at bottomDepth, + ! either because bottomDepth was too shallow for the minimum number of layers + ! or because contracting the layer would have led to rx1 > rx1Max + bottomDepth(iCell) = -zInterface(maxLevelCell(iCell)+1,iCell) + end if + + ! don't allow layers to go below -bottomDepth + zInterface(:,iCell) = max(zInterface(:,iCell),-bottomDepth(iCell)) + + zMid(:,iCell) = 0.5_RKIND*(zInterface(1:nVertLevels,iCell) + zInterface(2:nVertLevels+1,iCell)) + layerThickness(:,iCell) = zInterface(1:nVertLevels,iCell) - zInterface(2:nVertLevels+1,iCell) + verticalStretch(:,iCell) = layerThickness(:,iCell)/refLayerThickness(:) + + !restingThickness can be computed by "undoing" the z* stretch + stretch = (ssh(iCell) + bottomDepth(iCell))/bottomDepth(iCell) + zInterface(:,iCell) = (zInterface(:,iCell) + bottomDepth(iCell))/stretch - bottomDepth(iCell) + restingThickness(:,iCell) = zInterface(1:nVertLevels,iCell) - zInterface(2:nVertLevels+1,iCell) + end do + + block_ptr => block_ptr % next + end do !block_ptr + + call mpas_deallocate_scratch_field(zInterfaceField, .false.) + call mpas_deallocate_scratch_field(zTopField, .false.) + call mpas_deallocate_scratch_field(zBotField, .false.) + + end subroutine ocn_init_vertical_grid_with_max_rx1 + +!*********************************************************************** +! +! routine constrain_rx1_layer +! +!> \brief modify zBot in a layer so rx1 <= rx1Goal +!> \author Xylar Asay-Davis +!> \date 05/09/2015 +!> \details +!> This routine is used to iteratively constrains zBot such that +!> rx1 <= rx1Goal. zBot is nudged toward a goal field initially +!> with weight initIterWeight and finally with a weight of 1.0. +!> +! +!----------------------------------------------------------------------- + + subroutine constrain_rx1_layer(domain, iterCount, initIterWeight, rx1Goal, checkBelowBottom, iErr) + + type (domain_type), intent(inout) :: domain + integer, intent(in) :: iterCount + real (kind=RKIND), intent(in) :: initIterWeight, rx1Goal + logical :: checkBelowBottom + integer, intent(out) :: iErr + + type (block_type), pointer :: block_ptr + + type (mpas_pool_type), pointer :: meshPool, diagnosticsPool, scratchPool + + type (field1DReal), pointer :: zBotField, zBotNewField + + real (kind=RKIND), dimension(:), pointer :: zTop, zBot, zBotNew, bottomDepth + + integer, pointer :: nCells, nVertLevels, nEdges + integer, dimension(:), pointer :: maxLevelCell, smoothingMask + integer, dimension(:,:), pointer :: cellsOnEdge + + integer :: iCell, iEdge, c1, c2, iterIndex + + real (kind=RKIND) :: dzEdgeK, dzEdgeKp1, dzEdgeMean, dzVertGoal, weight, zBotEdge, & + deltaZBot + + iErr = 0 + + call mpas_pool_get_subpool(domain % blocklist % structs, 'scratch', scratchPool) + call mpas_pool_get_field(scratchPool, 'zBotScratch', zBotField) + + do iterIndex = 1, iterCount + ! next, adjust zBot toward zBotNew (with rx1 < rx1Max) + if(iterCount == 1) then + weight = 1.0_RKIND + else + weight = (iterIndex - 1.0_RKIND)/(iterCount - 1.0_RKIND) + end if + ! the weight goes from initIterWeight for the first iteration to 1.0 for the last + weight = (1.0_RKIND - weight)*initIterWeight + weight + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'scratch', scratchPool) + call mpas_pool_get_subpool(block_ptr % structs, 'diagnostics', diagnosticsPool) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + + call mpas_pool_get_array(scratchPool, 'zTopScratch', zTop) + call mpas_pool_get_array(scratchPool, 'zBotScratch', zBot) + call mpas_pool_get_array(diagnosticsPool, 'rx1InitSmoothingMask', smoothingMask, 1) + + call mpas_pool_get_field(scratchPool, 'zBotNewScratch', zBotNewField) + call mpas_allocate_scratch_field(zBotNewField, .true.) + call mpas_pool_get_array(scratchPool, 'zBotNewScratch', zBotNew) + + zBotNew(:) = zBot(:) + do iEdge = 1,nEdges + c1 = cellsOnEdge(1,iEdge) + c2 = cellsOnEdge(2,iEdge) + if((c1 <= 0) .or. (c1 > nCells) .or. (c2 <= 0) .or. (c2 > nCells)) cycle + if((maxLevelCell(c1) <= 0) .or. (maxLevelCell(c2) <= 0)) cycle + if((smoothingMask(c1) == 0) .and. (smoothingMask(c2) == 0)) cycle + + if(checkBelowBottom) then + ! if both cells are definitely below the bathymetry, no need to constrain rx1 + if((zTop(c1) < -bottomDepth(c1)) .and. (zTop(c2) < -bottomDepth(c2))) cycle + end if + + dzEdgeK = zTop(c2)-zTop(c1) + dzEdgeKp1 = zBot(c2)-zBot(c1) + dzEdgeMean = 0.5_RKIND*abs(dzEdgeK+dzEdgeKp1) + dzVertGoal = dzEdgeMean/rx1Goal + zBotEdge = 0.5_RKIND*(zTop(c1)+zTop(c2)) - dzVertGoal + + ! Once iteration has converged, we want 0.5_RKIND*(zBot(c1) + zBot(c2)) <= zBotEdge + deltaZBot = (2.0_RKIND*zBotEdge - zBot(c2) - zBotNew(c1)) + if(deltaZBot < 0.0_RKIND) then + zBotNew(c1) = zBotNew(c1) + deltaZBot + end if + deltaZBot = (2.0_RKIND*zBotEdge - zBot(c1) - zBotNew(c2)) + if(deltaZBot < 0.0_RKIND) then + zBotNew(c2) = zBotNew(c2) + deltaZBot + end if + end do !iEdge + + where(smoothingMask(:) == 1) + zBot(:) = (1.0_RKIND - weight)*zBot(:) + weight*zBotNew(:) + end where + + call mpas_deallocate_scratch_field(zBotNewField, .true.) + + block_ptr => block_ptr % next + end do !block_ptr + + ! do halo update on zBot + call mpas_dmpar_exch_halo_field(zBotField) + + end do !iterIndex + + end subroutine constrain_rx1_layer + +!*********************************************************************** + +end module ocn_init_vertical_grids + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker et ts=3 tw=132 diff --git a/src/core_ocean/mode_init/mpas_ocn_init_ziso.F b/src/core_ocean/mode_init/mpas_ocn_init_ziso.F new file mode 100644 index 0000000000..cb90682a5f --- /dev/null +++ b/src/core_ocean/mode_init/mpas_ocn_init_ziso.F @@ -0,0 +1,590 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_init_ziso +! +!> \brief MPAS ocean initialize case -- Zonally periodic Idealized Southern Ocean (ZISO) +!> \author Phillip J. Wolfram, Luke Van Roekel, Todd Ringler +!> \date 09/14/2015 +!> \details +!> This module contains the routines for initializing the +!> ZISO initial condition. +! +!----------------------------------------------------------------------- + +module ocn_init_ziso + + use mpas_kind_types + use mpas_io_units + use mpas_derived_types + use mpas_pool_routines + use mpas_constants + use mpas_stream_manager + use mpas_dmpar + + use ocn_constants + use ocn_init_vertical_grids + use ocn_init_cell_markers + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_init_setup_ziso, & + ocn_init_validate_ziso + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_init_setup_ziso +! +!> \brief Setup for this initial condition +!> \author Phillip J. Wolfram, Luke Van Roekel, Todd Ringler +!> \date 09/14/2015 +!> \details +!> This routine sets up the initial conditions for the ZISO configuration. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_setup_ziso(domain, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (domain_type), intent(inout) :: domain + integer, intent(out) :: iErr + + ! local work variables + type (block_type), pointer :: block_ptr + type (mpas_pool_type), pointer :: meshPool, verticalMeshPool, statePool, forcingPool, tracersPool + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool, tracersInteriorRestoringFieldsPool + + integer :: iCell, iEdge, iVertex, k, idx + real (kind=RKIND), dimension(:), pointer :: interfaceLocations + + ! Define config variable pointers + character (len=StrKIND), pointer :: config_init_configuration, config_vertical_grid + logical, pointer :: config_write_cull_cell_mask, config_use_debugTracers + + ! ZISO test case run-time configuration parameters + logical, pointer :: config_ziso_use_slopping_bathymetry + real (kind=RKIND), pointer :: config_ziso_meridional_extent + real (kind=RKIND), pointer :: config_ziso_bottom_depth + real (kind=RKIND), pointer :: config_ziso_wind_stress_max + real (kind=RKIND), pointer :: config_ziso_reference_coriolis + real (kind=RKIND), pointer :: config_ziso_coriolis_gradient + real (kind=RKIND), pointer :: config_ziso_shelf_depth + real (kind=RKIND), pointer :: config_ziso_slope_center_position + real (kind=RKIND), pointer :: config_ziso_slope_half_width + real (kind=RKIND), pointer :: config_ziso_initial_temp_t1 + real (kind=RKIND), pointer :: config_ziso_initial_temp_t2 + real (kind=RKIND), pointer :: config_ziso_initial_temp_h1 + real (kind=RKIND), pointer :: config_ziso_initial_temp_mt + real (kind=RKIND), pointer :: config_ziso_mean_restoring_temp + real (kind=RKIND), pointer :: config_ziso_restoring_temp_dev_ta + real (kind=RKIND), pointer :: config_ziso_restoring_temp_dev_tb + real (kind=RKIND), pointer :: config_ziso_restoring_temp_piston_vel + real (kind=RKIND), pointer :: config_ziso_restoring_sponge_l + real (kind=RKIND), pointer :: config_ziso_restoring_temp_tau + real (kind=RKIND), pointer :: config_ziso_restoring_temp_ts + real (kind=RKIND), pointer :: config_ziso_restoring_temp_ze + real (kind=RKIND), pointer :: config_ziso_wind_transition_position + real (kind=RKIND), pointer :: config_ziso_antarctic_shelf_front_width + real (kind=RKIND), pointer :: config_ziso_wind_stress_shelf_front_max + logical, pointer :: config_ziso_add_easterly_wind_stress_ASF + + ! configure settings related to frazil + logical, pointer :: config_ziso_frazil_enable + real (kind=RKIND), pointer :: config_ziso_frazil_temperature_anomaly + + integer, pointer :: config_ziso_vert_levels + + ! Define dimension pointers + integer, pointer :: nVertLevels, nCellsSolve, nEdgesSolve, nVerticesSolve, nVertLevelsP1 + integer, pointer :: index_temperature, index_salinity, index_tracer1 + + ! Define variable pointers + logical, pointer :: on_a_sphere + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:), pointer :: xCell, yCell, xEdge, yEdge, xVertex, yVertex, refBottomDepth, refZMid, & + vertCoordMovementWeights, bottomDepth, & + fCell, fEdge, fVertex, dcEdge + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, debugTracers + real (kind=RKIND), dimension(:, :), pointer :: activeTracersPistonVelocity, activeTracersSurfaceRestoringValue + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracersInteriorRestoringValue, activeTracersInteriorRestoringRate + real (kind=RKIND), dimension(:), pointer :: windStressZonal, windStressMeridional + + real (kind=RKIND) :: yMin, yMax, xMin, xMax, dcEdgeMin, dcEdgeMinGlobal + real (kind=RKIND) :: yMinGlobal, yMaxGlobal, yMidGlobal, xMinGlobal, xMaxGlobal + real(kind=RKIND), pointer :: y_period + character (len=StrKIND) :: streamID + integer :: directionProperty + + ! Local variable related to frazil + real (kind=RKIND) :: distanceX, distanceY, distance, frazil_temperature, scaleFactor + + ! assume no error + iErr = 0 + + ! test if ZISO is the desired configuration + call mpas_pool_get_config(ocnConfigs, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('ziso')) return + + call mpas_log_write( 'Starting initialization of Zonally periodic Idealized Southern Ocean (ZISO)') + + ! get config variables !{{{ + call mpas_pool_get_config(domain % configs, 'config_use_debugTracers', config_use_debugTracers) + call mpas_pool_get_config(domain % configs, 'config_write_cull_cell_mask', config_write_cull_cell_mask) + call mpas_pool_get_config(domain % configs, 'config_ziso_use_slopping_bathymetry', config_ziso_use_slopping_bathymetry) + call mpas_pool_get_config(domain % configs, 'config_ziso_bottom_depth', config_ziso_bottom_depth) + call mpas_pool_get_config(domain % configs, 'config_ziso_meridional_extent', config_ziso_meridional_extent) + call mpas_pool_get_config(domain % configs, 'config_ziso_reference_coriolis', config_ziso_reference_coriolis) + call mpas_pool_get_config(domain % configs, 'config_ziso_coriolis_gradient', config_ziso_coriolis_gradient) + call mpas_pool_get_config(domain % configs, 'config_ziso_vert_levels', config_ziso_vert_levels) + call mpas_pool_get_config(domain % configs, 'config_ziso_wind_stress_max', config_ziso_wind_stress_max) + call mpas_pool_get_config(domain % configs, 'config_ziso_slope_half_width', config_ziso_slope_half_width) + call mpas_pool_get_config(domain % configs, 'config_ziso_shelf_depth', config_ziso_shelf_depth) + call mpas_pool_get_config(domain % configs, 'config_ziso_slope_center_position', config_ziso_slope_center_position) + call mpas_pool_get_config(domain % configs, 'config_ziso_initial_temp_t1', config_ziso_initial_temp_t1) + call mpas_pool_get_config(domain % configs, 'config_ziso_initial_temp_t2', config_ziso_initial_temp_t2) + call mpas_pool_get_config(domain % configs, 'config_ziso_initial_temp_h1', config_ziso_initial_temp_h1) + call mpas_pool_get_config(domain % configs, 'config_ziso_initial_temp_mt', config_ziso_initial_temp_mt) + call mpas_pool_get_config(domain % configs, 'config_ziso_mean_restoring_temp', config_ziso_mean_restoring_temp) + call mpas_pool_get_config(domain % configs, 'config_ziso_restoring_temp_dev_ta', config_ziso_restoring_temp_dev_ta) + call mpas_pool_get_config(domain % configs, 'config_ziso_restoring_temp_dev_tb', config_ziso_restoring_temp_dev_tb) + call mpas_pool_get_config(domain % configs, 'config_ziso_restoring_temp_piston_vel', config_ziso_restoring_temp_piston_vel) + call mpas_pool_get_config(domain % configs, 'config_ziso_restoring_sponge_l', config_ziso_restoring_sponge_l) + call mpas_pool_get_config(domain % configs, 'config_ziso_restoring_temp_tau', config_ziso_restoring_temp_tau) + call mpas_pool_get_config(domain % configs, 'config_ziso_restoring_temp_ts', config_ziso_restoring_temp_ts) + call mpas_pool_get_config(domain % configs, 'config_ziso_restoring_temp_ze', config_ziso_restoring_temp_ze) + call mpas_pool_get_config(domain % configs, 'config_vertical_grid', config_vertical_grid) + call mpas_pool_get_config(domain % configs, 'config_ziso_add_easterly_wind_stress_ASF', & + config_ziso_add_easterly_wind_stress_ASF) + call mpas_pool_get_config(domain % configs, 'config_ziso_wind_transition_position', config_ziso_wind_transition_position) + call mpas_pool_get_config(domain % configs, 'config_ziso_antarctic_shelf_front_width', config_ziso_antarctic_shelf_front_width) + call mpas_pool_get_config(domain % configs, 'config_ziso_wind_stress_shelf_front_max', config_ziso_wind_stress_shelf_front_max) + + ! frazil configures + call mpas_pool_get_config(domain % configs, 'config_ziso_frazil_enable', config_ziso_frazil_enable) + call mpas_pool_get_config(domain % configs, 'config_ziso_frazil_temperature_anomaly', config_ziso_frazil_temperature_anomaly) + !}}} + + ! Determine vertical grid for configuration + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_config(meshPool, 'on_a_sphere', on_a_sphere) + + ! test if configure settings are invalid + if ( on_a_sphere ) call mpas_log_write('The ZISO configuration can ' & + // 'only be applied to a planar mesh. Exiting...', MPAS_LOG_CRIT) + + ! Define interface locations + allocate(interfaceLocations(nVertLevelsP1)) + call ocn_generate_vertical_grid( config_vertical_grid, interfaceLocations ) + + ! assign config variables + nVertLevels = config_ziso_vert_levels + nVertLevelsP1 = nVertLevels + 1 + + ! keep all cells on planar, periodic mesh (no culling) + + !-------------------------------------------------------------------- + ! Use this section to find min/max of grid to allow culling + !-------------------------------------------------------------------- + + ! Initalize min/max values to large positive and negative values + yMin = 1.0E10_RKIND + yMax = -1.0E10_RKIND + xMin = 1.0E10_RKIND + xMax = -1.0E10_RKIND + dcEdgeMin = 1.0E10_RKIND + + ! Determine local min and max values. + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + yMin = min( yMin, minval(yCell(1:nCellsSolve))) + yMax = max( yMax, maxval(yCell(1:nCellsSolve))) + xMin = min( xMin, minval(xCell(1:nCellsSolve))) + xMax = max( xMax, maxval(xCell(1:nCellsSolve))) + dcEdgeMin = min( dcEdgeMin, minval(dcEdge(1:nEdgesSolve))) + + block_ptr => block_ptr % next + end do ! do while(associated(block_ptr)) + + + !-------------------------------------------------------------------- + ! Use this section to set initial values + !-------------------------------------------------------------------- + + block_ptr => domain % blocklist + do while(associated(block_ptr)) + call mpas_pool_get_subpool(block_ptr % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block_ptr % structs, 'state', statePool) + call mpas_pool_get_subpool(block_ptr % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(block_ptr % structs, 'forcing', forcingPool) + + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + + call mpas_pool_get_dimension(tracersPool, 'index_temperature', index_temperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', index_salinity) + call mpas_pool_get_dimension(tracersPool, 'index_tracer1', index_tracer1) + + call mpas_pool_get_array(meshPool, 'xCell', xCell) + call mpas_pool_get_array(meshPool, 'yCell', yCell) + call mpas_pool_get_array(meshPool, 'xEdge', xEdge) + call mpas_pool_get_array(meshPool, 'yEdge', yEdge) + call mpas_pool_get_array(meshPool, 'xVertex', xVertex) + call mpas_pool_get_array(meshPool, 'yVertex', yVertex) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) + call mpas_pool_get_array(meshPool, 'bottomDepth', bottomDepth) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'fCell', fCell) + call mpas_pool_get_array(meshPool, 'fEdge', fEdge) + call mpas_pool_get_array(meshPool, 'fVertex', fVertex) + + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + if(config_use_debugTracers) call mpas_pool_get_array(tracersPool, 'debugTracers', debugTracers, 1) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + + call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) + call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) + + call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) + call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional) + ! tests to make sure these are allocated + if (.not. associated(windStressZonal) .or. .not. associated(windStressMeridional)) then + call mpas_log_write("MPAS-ocean: windStressZonal and / or windStressMeridional are not allocated") + end if + + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + if (.not. associated(tracersSurfaceRestoringFieldsPool)) then + call mpas_log_write("MPAS-ocean: tracersSurfaceRestoringFieldsPool not allocated.") + end if + call mpas_pool_get_subpool(forcingPool, 'tracersInteriorRestoringFields', tracersInteriorRestoringFieldsPool) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersPistonVelocity', activeTracersPistonVelocity, 1) + if (.not. associated(activeTracersPistonVelocity)) then + call mpas_log_write("MPAS-ocean: activeTracersPistonVelocity not allocated.") + end if + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, & + 'activeTracersSurfaceRestoringValue', activeTracersSurfaceRestoringValue, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, & + 'activeTracersInteriorRestoringRate', activeTracersInteriorRestoringRate, 1) + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, & + 'activeTracersInteriorRestoringValue', activeTracersInteriorRestoringValue, 1) + + ! Determine global min and max values. + call mpas_dmpar_min_real(domain % dminfo, yMin, yMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, yMax, yMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, xMin, xMinGlobal) + call mpas_dmpar_max_real(domain % dminfo, xMax, xMaxGlobal) + call mpas_dmpar_min_real(domain % dminfo, dcEdgeMin, dcEdgeMinGlobal) + + ! mark north / south boundaries + if(config_write_cull_cell_mask) then + call ocn_mark_north_boundary(meshPool, yMaxGlobal, dcEdgeMinGlobal, iErr) + call ocn_mark_south_boundary(meshPool, yMinGlobal, dcEdgeMinGlobal, iErr) + call mpas_pool_get_config(meshPool, 'y_period', y_period) + y_period = 0.0_RKIND + endif + call mpas_stream_mgr_begin_iteration(domain % streamManager) + do while (mpas_stream_mgr_get_next_stream(domain % streamManager, streamID, directionProperty)) + if ( directionProperty == MPAS_STREAM_OUTPUT .or. directionProperty == MPAS_STREAM_INPUT_OUTPUT ) then + call mpas_stream_mgr_add_att(domain % streamManager, 'y_period', 0.0_RKIND, streamID) + end if + end do + + activeTracersInteriorRestoringRate(:,:,:) = 0.0_RKIND + activeTracersInteriorRestoringValue(:,:,:) = 0.0_RKIND + activeTracersPistonVelocity(:,:) = 0.0_RKIND + activeTracersSurfaceRestoringValue(:,:) = 0.0_RKIND + + ! Set refBottomDepth and refZMid + do k = 1, nVertLevels + refBottomDepth(k) = config_ziso_bottom_depth * interfaceLocations(k+1) + refZMid(k) = - 0.5_RKIND * (interfaceLocations(k+1) + interfaceLocations(k)) * config_ziso_bottom_depth + end do + + ! set bottomDepth and maxLevelCell !{{{{ + bottomDepth(:) = 0.0_RKIND + do iCell = 1, nCellsSolve + + if (config_ziso_use_slopping_bathymetry) then + ! bottom depth function to be applied + bottomDepth(iCell) = config_ziso_shelf_depth + & + 0.5_RKIND*(config_ziso_bottom_depth - config_ziso_shelf_depth) * & + (1.0_RKIND + tanh((yCell(iCell) - config_ziso_slope_center_position) / & + config_ziso_slope_half_width)) + else + bottomDepth(iCell) = config_ziso_bottom_depth + end if + + ! Determine maxLevelCell based on bottomDepth and refBottomDepth + ! Also set botomDepth based on refBottomDepth, since + ! above bottomDepth was set with continuous analytical functions, + ! and needs to be discrete + maxLevelCell(iCell) = nVertLevels + if (nVertLevels > 1) then + do k = 1, nVertLevels + if (bottomDepth(iCell) < refBottomDepth(k)) then + maxLevelCell(iCell) = k-1 + bottomDepth(iCell) = refBottomDepth(k-1) + exit + end if + end do + end if + + enddo ! Looping through with iCell !}}} + + ! Set vertCoordMovementWeights + vertCoordMovementWeights(:) = 1.0_RKIND + + do iCell = 1, nCellsSolve + + ! Set initial temperature + idx = index_temperature + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = config_ziso_initial_temp_t1 + & + config_ziso_initial_temp_t2*tanh(refZMid(k)/config_ziso_initial_temp_h1) + config_ziso_initial_temp_mt*refZMid(k) + end do + + ! Set initial salinity + idx = index_salinity + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = 34.0_RKIND + end do + + ! Set layerThickness and restingThickness + ! Uniform layer thickness + do k = 1, nVertLevels + layerThickness(k, iCell) = config_ziso_bottom_depth * ( interfaceLocations(k+1) - interfaceLocations(k) ) + restingThickness(k, iCell) = layerThickness(k, iCell) + end do + + ! set a passive tracer + if(config_use_debugTracers) then + idx = index_tracer1 + do k = 1, nVertLevels + debugTracers(idx, k, iCell) = 1.0_RKIND + & + 100000.0_RKIND*exp(-(refZMid(k)+1250.0_RKIND)**2/100.0_RKIND**2) * & + exp(-(yCell(iCell)-1250.0_RKIND*1000.0_RKIND)**2/(50.0_RKIND*1000.0_RKIND)**2) + enddo + endif + + ! set windstress + if (config_ziso_add_easterly_wind_stress_ASF) then + if(yCell(iCell) .ge. config_ziso_wind_transition_position) then + windStressZonal(iCell) = config_ziso_wind_stress_max*sin((pii*(yCell(iCell) - & + config_ziso_wind_transition_position) / & + (config_ziso_meridional_extent - config_ziso_wind_transition_position)))**2 + elseif(yCell(iCell) .ge. config_ziso_wind_transition_position - config_ziso_antarctic_shelf_front_width) then + windStressZonal(iCell) = 0.0_RKIND + if(yCell(iCell) .lt. config_ziso_wind_transition_position) then + windStressZonal(iCell) = config_ziso_wind_stress_shelf_front_max * & + sin((pii*(config_ziso_wind_transition_position & + - yCell(iCell)))/config_ziso_antarctic_shelf_front_width)**2 + endif + endif + else + windStressZonal(iCell) = config_ziso_wind_stress_max * exp(-((yCell(iCell) - & + config_ziso_meridional_extent/2.0_RKIND) / & + (config_ziso_meridional_extent/2.0_RKIND))**2.0_RKIND) * cos(pii/2.0_RKIND*(yCell(iCell) - & + config_ziso_meridional_extent/2.0_RKIND)/(config_ziso_meridional_extent/2.0_RKIND)) + endif + windStressMeridional(iCell) = 0.0_RKIND + + ! surface restoring + idx = index_temperature + activeTracersSurfaceRestoringValue(idx,iCell) = config_ziso_mean_restoring_temp & + + config_ziso_restoring_temp_dev_ta * & + tanh(2.0_RKIND*(yCell(iCell)-config_ziso_meridional_extent/2.0_RKIND)/(config_ziso_meridional_extent/2.0_RKIND)) & + + config_ziso_restoring_temp_dev_tb * & + (yCell(iCell)-config_ziso_meridional_extent/2.0_RKIND)/(config_ziso_meridional_extent/2.0_RKIND) + activeTracersPistonVelocity(idx,iCell) = config_ziso_restoring_temp_piston_vel + idx = index_salinity + activeTracersSurfaceRestoringValue(idx,iCell) = 34.0_RKIND + activeTracersPistonVelocity(idx,iCell) = 0.0_RKIND + + ! set restoring at equatorward (north) boundary + do k = 1, nVertLevels + !Interior restoring along northern wall + if(config_ziso_meridional_extent-yCell(iCell) <= 1.5_RKIND*config_ziso_restoring_sponge_l) then + idx = index_temperature + activeTracersInteriorRestoringValue(idx, k, iCell) = activeTracersSurfaceRestoringValue(idx,iCell) & + * exp(refZMid(k)/config_ziso_restoring_temp_ze) + activeTracersInteriorRestoringRate(idx, k, iCell) = & + exp(-(config_ziso_meridional_extent-yCell(iCell))/config_ziso_restoring_sponge_l) & + * ( 1.0_RKIND / (config_ziso_restoring_temp_tau*86400.0_RKIND)) + idx = index_salinity + activeTracersInteriorRestoringValue(idx, k, iCell) = 34.0_RKIND + activeTracersInteriorRestoringRate(idx, k, iCell) = 0.0_RKIND + end if + end do + + + ! set restoring at poleward (south) boundary + do k = 1, nVertLevels + !Interior restoring along southern wall + if(yCell(iCell) <= 2.0_RKIND*config_ziso_restoring_sponge_l) then + idx = index_temperature + activeTracersInteriorRestoringValue(idx, k, iCell) = activeTracersSurfaceRestoringValue(idx,iCell) + activeTracersInteriorRestoringRate(idx, k, iCell) = exp(-yCell(iCell)/config_ziso_restoring_sponge_l) & + * ( 1.0_RKIND / (config_ziso_restoring_temp_tau*86400.0_RKIND)) + idx = index_salinity + activeTracersInteriorRestoringValue(idx, k, iCell) = 34.0_RKIND + activeTracersInteriorRestoringRate(idx, k, iCell) = 0.0_RKIND + end if + enddo + +!************************************************************************************************************************ +! this test case is overloaded with the ability to evaluate the frazil algorithm +! if config_ziso_enable_frazil is true, some of the configure options are over written to make the test useful for frazil +!************************************************************************************************************************ + + if(config_ziso_frazil_enable) then + config_ziso_initial_temp_t1 = 0.0_RKIND + config_ziso_initial_temp_t2 = -1.0_RKIND + config_ziso_initial_temp_h1 = 300.0_RKIND + config_ziso_initial_temp_mt = 0.0_RKIND + + ! recompute initial temperature with altered parameters + idx = index_temperature + do k = 1, nVertLevels + activeTracers(idx, k, iCell) = config_ziso_initial_temp_t1 + & + config_ziso_initial_temp_t2*tanh(refZMid(k)/config_ziso_initial_temp_h1) + config_ziso_initial_temp_mt*refZMid(k) + end do + + distanceX = config_ziso_meridional_extent/4.0_RKIND-xCell(iCell) + distanceY = config_ziso_meridional_extent/2.0_RKIND-yCell(iCell) + distance = sqrt(distanceY**2+distanceX**2) + scaleFactor = exp(-distance/config_ziso_meridional_extent*20.0_RKIND) + if (scaleFactor.gt.0.9_RKIND) call mpas_log_write( ' frazil production likely at this cell: $i', intArgs=(/ iCell /) ) + do k = 1, nVertLevels + frazil_temperature = config_ziso_frazil_temperature_anomaly & + + config_ziso_initial_temp_t2 * tanh(refZMid(k) / config_ziso_initial_temp_h1) & + + config_ziso_initial_temp_mt * refZMid(k) + if (refZMid(k).gt.-50.0) frazil_temperature = frazil_temperature + 1.0_RKIND*cos( refZMid(k) / 50.0_RKIND & + * pii / 2.0_RKIND) + activeTracers(idx, k, iCell) = (1.0_RKIND-scaleFactor)* activeTracers(idx, k, iCell) + scaleFactor & + * frazil_temperature + end do + end if + +!********************************************************************************* +! end frazil overload +!********************************************************************************* + + end do ! do iCell + + if(config_ziso_frazil_enable) then + call mpas_log_write( ' This test case is configured for the testing of the frazil algorithm') + endif + + ! Set Coriolis parameters, if other than zero + do iCell = 1, nCellsSolve + fCell(iCell) = config_ziso_reference_coriolis + yCell(iCell) * config_ziso_coriolis_gradient + end do + do iEdge = 1, nEdgesSolve + fEdge(iEdge) = config_ziso_reference_coriolis + yEdge(iEdge) * config_ziso_coriolis_gradient + end do + do iVertex = 1, nVerticesSolve + fVertex(iVertex) = config_ziso_reference_coriolis + yVertex(iVertex) * config_ziso_reference_coriolis + end do + + block_ptr => block_ptr % next + end do ! do while(associated(block_ptr)) + + call mpas_log_write( 'Finishing initialization of Zonally periodic Idealized Southern Ocean (ZISO)') + !-------------------------------------------------------------------- + + end subroutine ocn_init_setup_ziso!}}} + +!*********************************************************************** +! +! routine ocn_init_validate_ziso +! +!> \brief Validation for this initial condition +!> \author Phillip J. Wolfram, Luke Van Roekel, Todd Ringler +!> \date 09/14/2015 +!> \details +!> This routine validates the configuration options for this case. +! +!----------------------------------------------------------------------- + + subroutine ocn_init_validate_ziso(configPool, packagePool, iErr)!{{{ + + !-------------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: configPool, packagePool + integer, intent(out) :: iErr + + character (len=StrKIND), pointer :: config_init_configuration + integer, pointer :: config_vert_levels, config_ziso_vert_levels + + iErr = 0 + + call mpas_pool_get_config(configPool, 'config_init_configuration', config_init_configuration) + if(config_init_configuration .ne. trim('ziso')) return + + call mpas_pool_get_config(configPool, 'config_vert_levels', config_vert_levels) + call mpas_pool_get_config(configPool, 'config_ziso_vert_levels', config_ziso_vert_levels) + + if(config_vert_levels <= 0 .and. config_ziso_vert_levels > 0) then + config_vert_levels = config_ziso_vert_levels + else if (config_vert_levels <= 0) then + call mpas_log_write( 'Validation failed for ziso. Not given a usable value for vertical levels.', MPAS_LOG_CRIT) + iErr = 1 + end if + + !-------------------------------------------------------------------- + + end subroutine ocn_init_validate_ziso!}}} + + +!*********************************************************************** + +end module ocn_init_ziso + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/Makefile b/src/core_ocean/shared/Makefile index bfcb3e7fe5..10a47b6f75 100644 --- a/src/core_ocean/shared/Makefile +++ b/src/core_ocean/shared/Makefile @@ -1,12 +1,17 @@ .SUFFIXES: .F .o + +#Missing In Objs? +#mpas_ocn_tracer_surface_flux.o + + OBJS = mpas_ocn_init_routines.o \ - mpas_ocn_gm.o \ - mpas_ocn_diagnostics.o \ - mpas_ocn_diagnostics_routines.o \ - mpas_ocn_thick_ale.o \ - mpas_ocn_equation_of_state.o \ - mpas_ocn_equation_of_state_jm.o \ - mpas_ocn_equation_of_state_linear.o \ + mpas_ocn_gm.o \ + mpas_ocn_diagnostics.o \ + mpas_ocn_diagnostics_routines.o \ + mpas_ocn_thick_ale.o \ + mpas_ocn_equation_of_state.o \ + mpas_ocn_equation_of_state_jm.o \ + mpas_ocn_equation_of_state_linear.o \ mpas_ocn_thick_hadv.o \ mpas_ocn_thick_vadv.o \ mpas_ocn_thick_surface_flux.o \ @@ -17,39 +22,55 @@ OBJS = mpas_ocn_init_routines.o \ mpas_ocn_vel_hmix_leith.o \ mpas_ocn_vel_hmix_del4.o \ mpas_ocn_vel_forcing.o \ - mpas_ocn_vel_forcing_windstress.o \ + mpas_ocn_vel_forcing_surface_stress.o \ + mpas_ocn_vel_forcing_explicit_bottom_drag.o \ mpas_ocn_vel_forcing_rayleigh.o \ mpas_ocn_vel_pressure_grad.o \ mpas_ocn_vmix.o \ mpas_ocn_vmix_coefs_const.o \ mpas_ocn_vmix_coefs_rich.o \ mpas_ocn_vmix_coefs_tanh.o \ - mpas_ocn_vmix_coefs_redi.o \ + mpas_ocn_vmix_coefs_redi.o \ mpas_ocn_vmix_cvmix.o \ mpas_ocn_tendency.o \ mpas_ocn_tracer_hmix.o \ mpas_ocn_tracer_hmix_del2.o \ mpas_ocn_tracer_hmix_del4.o \ + mpas_ocn_tracer_hmix_redi.o \ mpas_ocn_tracer_advection.o \ + mpas_ocn_tracer_advection_mono.o \ + mpas_ocn_tracer_advection_std.o \ mpas_ocn_tracer_nonlocalflux.o \ mpas_ocn_tracer_short_wave_absorption.o \ mpas_ocn_tracer_short_wave_absorption_jerlov.o \ + mpas_ocn_tracer_short_wave_absorption_variable.o \ + mpas_ocn_tracer_surface_restoring.o \ + mpas_ocn_tracer_interior_restoring.o \ + mpas_ocn_tracer_exponential_decay.o \ + mpas_ocn_tracer_ideal_age.o \ + mpas_ocn_tracer_TTD.o \ + mpas_ocn_tracer_ecosys.o \ + mpas_ocn_tracer_DMS.o \ + mpas_ocn_tracer_MacroMolecules.o \ mpas_ocn_high_freq_thickness_hmix_del2.o \ - mpas_ocn_tracer_surface_flux.o \ + mpas_ocn_tracer_surface_flux_to_tend.o \ mpas_ocn_test.o \ mpas_ocn_constants.o \ mpas_ocn_forcing.o \ - mpas_ocn_forcing_bulk.o \ + mpas_ocn_surface_bulk_forcing.o \ + mpas_ocn_surface_land_ice_fluxes.o \ + mpas_ocn_effective_density_in_land_ice.o \ + mpas_ocn_frazil_forcing.o \ mpas_ocn_forcing_restoring.o \ - mpas_ocn_time_average.o \ mpas_ocn_time_average_coupled.o \ - mpas_ocn_sea_ice.o + mpas_ocn_sea_ice.o \ + mpas_ocn_framework_forcing.o all: $(OBJS) -mpas_ocn_init_routines.o: mpas_ocn_constants.o mpas_ocn_time_average.o mpas_ocn_diagnostics.o mpas_ocn_gm.o +mpas_ocn_init_routines.o: mpas_ocn_constants.o mpas_ocn_diagnostics.o mpas_ocn_gm.o mpas_ocn_forcing.o mpas_ocn_surface_land_ice_fluxes.o -mpas_ocn_tendency.o: mpas_ocn_time_average.o mpas_ocn_high_freq_thickness_hmix_del2.o mpas_ocn_tracer_surface_flux.o mpas_ocn_thick_surface_flux.o mpas_ocn_tracer_short_wave_absorption.o mpas_ocn_tracer_advection.o mpas_ocn_tracer_hmix.o mpas_ocn_tracer_nonlocalflux.o mpas_ocn_vmix.o mpas_ocn_constants.o +mpas_ocn_tendency.o: mpas_ocn_high_freq_thickness_hmix_del2.o mpas_ocn_tracer_surface_restoring.o mpas_ocn_thick_surface_flux.o mpas_ocn_tracer_short_wave_absorption.o mpas_ocn_tracer_advection.o mpas_ocn_tracer_hmix.o mpas_ocn_tracer_nonlocalflux.o mpas_ocn_surface_bulk_forcing.o mpas_ocn_surface_land_ice_fluxes.o mpas_ocn_tracer_surface_flux_to_tend.o mpas_ocn_tracer_interior_restoring.o mpas_ocn_tracer_exponential_decay.o mpas_ocn_tracer_ideal_age.o mpas_ocn_tracer_TTD.o mpas_ocn_vmix.o mpas_ocn_constants.o mpas_ocn_frazil_forcing.o mpas_ocn_tracer_ecosys.o mpas_ocn_tracer_DMS.o mpas_ocn_tracer_MacroMolecules.o mpas_ocn_diagnostics.o mpas_ocn_diagnostics_routines.o: mpas_ocn_constants.o @@ -57,8 +78,6 @@ mpas_ocn_diagnostics.o: mpas_ocn_thick_ale.o mpas_ocn_diagnostics_routines.o mpa mpas_ocn_thick_ale.o: mpas_ocn_constants.o -mpas_ocn_time_average.o: - mpas_ocn_time_average_coupled.o: mpas_ocn_constants.o mpas_ocn_thick_hadv.o: mpas_ocn_constants.o @@ -81,21 +100,29 @@ mpas_ocn_vel_hmix_leith.o: mpas_ocn_constants.o mpas_ocn_vel_hmix_del4.o: mpas_ocn_constants.o -mpas_ocn_vel_forcing.o: mpas_ocn_vel_forcing_windstress.o mpas_ocn_vel_forcing_rayleigh.o mpas_ocn_forcing.o mpas_ocn_constants.o +mpas_ocn_vel_forcing.o: mpas_ocn_vel_forcing_surface_stress.o mpas_ocn_vel_forcing_rayleigh.o mpas_ocn_vel_forcing_explicit_bottom_drag.o mpas_ocn_forcing.o mpas_ocn_constants.o + +mpas_ocn_vel_forcing_surface_stress.o: mpas_ocn_forcing.o mpas_ocn_constants.o -mpas_ocn_vel_forcing_windstress.o: mpas_ocn_forcing.o mpas_ocn_constants.o +mpas_ocn_vel_forcing_explicit_bottom_drag.o: mpas_ocn_constants.o mpas_ocn_vel_forcing_rayleigh.o: mpas_ocn_constants.o mpas_ocn_vel_coriolis.o: mpas_ocn_constants.o -mpas_ocn_tracer_hmix.o: mpas_ocn_tracer_hmix_del2.o mpas_ocn_tracer_hmix_del4.o +mpas_ocn_tracer_hmix.o: mpas_ocn_tracer_hmix_del2.o mpas_ocn_tracer_hmix_del4.o mpas_ocn_tracer_hmix_redi.o mpas_ocn_tracer_hmix_del2.o: mpas_ocn_constants.o mpas_ocn_tracer_hmix_del4.o: mpas_ocn_constants.o -mpas_ocn_tracer_advection.o: mpas_ocn_constants.o +mpas_ocn_tracer_advection.o: mpas_ocn_constants.o mpas_ocn_tracer_advection_mono.o mpas_ocn_tracer_advection_std.o + +mpas_ocn_tracer_advection_mono.o: mpas_ocn_constants.o + +mpas_ocn_tracer_advection_std.o: mpas_ocn_constants.o + +mpas_ocn_tracer_hmix_redi.o: mpas_ocn_constants.o mpas_ocn_high_freq_thickness_hmix_del2.o: mpas_ocn_constants.o @@ -103,7 +130,9 @@ mpas_ocn_tracer_nonlocalflux.o: mpas_ocn_constants.o mpas_ocn_tracer_surface_flux.o: mpas_ocn_forcing.o mpas_ocn_constants.o -mpas_ocn_tracer_short_wave_absorption.o: mpas_ocn_tracer_short_wave_absorption_jerlov.o mpas_ocn_constants.o +mpas_ocn_tracer_short_wave_absorption.o: mpas_ocn_tracer_short_wave_absorption_jerlov.o mpas_ocn_tracer_short_wave_absorption_variable.o mpas_ocn_constants.o + +mpas_ocn_tracer_short_wave_absorption_variable.o: mpas_ocn_constants.o mpas_ocn_framework_forcing.o mpas_ocn_tracer_short_wave_absorption_jerlov.o: mpas_ocn_constants.o @@ -129,14 +158,41 @@ mpas_ocn_test.o: mpas_ocn_constants.o mpas_ocn_constants.o: -mpas_ocn_forcing.o: mpas_ocn_constants.o mpas_ocn_forcing_bulk.o mpas_ocn_forcing_restoring.o +mpas_ocn_forcing.o: mpas_ocn_constants.o mpas_ocn_forcing_restoring.o + +mpas_ocn_surface_bulk_forcing.o: mpas_ocn_constants.o mpas_ocn_equation_of_state.o + +mpas_ocn_surface_land_ice_fluxes.o: mpas_ocn_constants.o mpas_ocn_equation_of_state.o + +mpas_ocn_frazil_forcing.o: mpas_ocn_constants.o mpas_ocn_equation_of_state.o -mpas_ocn_forcing_bulk.o: mpas_ocn_constants.o +mpas_ocn_effective_density_in_land_ice.o: mpas_ocn_constants.o mpas_ocn_forcing_restoring.o: mpas_ocn_constants.o -mpas_ocn_sea_ice.o: mpas_ocn_constants.o +mpas_ocn_sea_ice.o: mpas_ocn_constants.o mpas_ocn_equation_of_state.o + +mpas_ocn_tracer_surface_restoring.o: mpas_ocn_constants.o mpas_ocn_framework_forcing.o + +mpas_ocn_tracer_interior_restoring.o: mpas_ocn_constants.o + +mpas_ocn_tracer_exponential_decay.o: mpas_ocn_constants.o + +mpas_ocn_tracer_ideal_age.o: mpas_ocn_constants.o + +mpas_ocn_tracer_TTD.o: mpas_ocn_constants.o + +mpas_ocn_tracer_ecosys.o: mpas_ocn_constants.o mpas_ocn_framework_forcing.o + +mpas_ocn_tracer_DMS.o: mpas_ocn_constants.o + +mpas_ocn_tracer_MacroMolecules.o: mpas_ocn_constants.o + +mpas_ocn_tracer_surface_flux_to_tend.o: mpas_ocn_constants.o + +mpas_ocn_time_average_coupled.o: mpas_ocn_constants.o +mpas_ocn_framework_forcing.o: clean: $(RM) *.o *.i *.mod *.f90 diff --git a/src/core_ocean/shared/mpas_ocn_constants.F b/src/core_ocean/shared/mpas_ocn_constants.F index ad5d1f4501..671dc0dc23 100644 --- a/src/core_ocean/shared/mpas_ocn_constants.F +++ b/src/core_ocean/shared/mpas_ocn_constants.F @@ -64,6 +64,7 @@ module ocn_constants T0_Kelvin ,&! zero point for Celsius mpercm ,&! meters per m cmperm ,&! m per meter + days_per_second ,&! days per second salt_to_ppt ,&! salt (kg/kg) to ppt ppt_to_salt ,&! salt ppt to kg/kg mass_to_Sv ,&! mass flux to Sverdrups @@ -87,7 +88,7 @@ module ocn_constants !> \brief Initializes the ocean constants !> \author Doug Jacobsen !> \date 04/25/12 -!> \details +!> \details !> This routine sets up constants for use in the ocean model. ! !----------------------------------------------------------------------- @@ -96,9 +97,13 @@ subroutine ocn_constants_init(configPool, packagePool)!{{{ type (mpas_pool_type), pointer :: packagePool integer :: n + real (kind=RKIND), pointer :: config_density0 + ocnConfigs => configPool ocnPackages => packagePool + call mpas_pool_get_config(configPool, 'config_density0', config_density0) + !----------------------------------------------------------------------- ! ! physical constants @@ -109,7 +114,7 @@ subroutine ocn_constants_init(configPool, packagePool)!{{{ T0_Kelvin = 273.16_RKIND ! zero point for Celsius rho_air = 1.2_RKIND ! ambient air density (kg/m^3) - rho_sw = 1.026e3_RKIND ! density of salt water (kg/m^3) + rho_sw = config_density0 ! density of salt water (kg/m^3) rho_fw = 1.0e3_RKIND ! avg. water density (kg/m^3) rho_ice = 0.917e3_RKIND ! density of ice (kg/m^3) cp_sw = 3.996e3_RKIND ! specific heat salt water @@ -131,6 +136,7 @@ subroutine ocn_constants_init(configPool, packagePool)!{{{ ! !----------------------------------------------------------------------- + days_per_second = 1._RKIND/86400._RKIND ! days per second salt_to_ppt = 1000._RKIND ! salt (kg/kg) to ppt ppt_to_salt = 1.e-3_RKIND ! salt ppt to kg/kg mass_to_Sv = 1.0e-12_RKIND ! mass flux to Sverdrups @@ -155,7 +161,7 @@ subroutine ocn_constants_init(configPool, packagePool)!{{{ vonkar = SHR_CONST_KARMAN stefan_boltzmann = SHR_CONST_STEBOL ! W/m^2/K^4 latent_heat_vapor_mks = SHR_CONST_LATVAP ! J/kg - latent_heat_fusion_mks = SHR_CONST_LATICE ! J/kg + latent_heat_fusion_mks = SHR_CONST_LATICE ! J/kg ocn_ref_salinity = SHR_CONST_OCN_REF_SAL ! psu sea_ice_salinity = SHR_CONST_ICE_REF_SAL ! psu #endif diff --git a/src/core_ocean/shared/mpas_ocn_diagnostics.F b/src/core_ocean/shared/mpas_ocn_diagnostics.F index 6bd850a9d7..87350c9a79 100644 --- a/src/core_ocean/shared/mpas_ocn_diagnostics.F +++ b/src/core_ocean/shared/mpas_ocn_diagnostics.F @@ -20,11 +20,14 @@ module ocn_diagnostics + use mpas_timer use mpas_derived_types use mpas_pool_routines use mpas_constants - use mpas_timer + use mpas_threading use mpas_vector_reconstruction + use mpas_stream_manager + use mpas_io_units use ocn_constants use ocn_gm @@ -54,7 +57,9 @@ module ocn_diagnostics ocn_filter_btr_mode_vel, & ocn_filter_btr_mode_tend_vel, & ocn_reconstruct_gm_vectors, & - ocn_diagnostics_init + ocn_diagnostics_init, & + ocn_compute_kpp_input_fields, & + ocn_validate_state !-------------------------------------------------------------------- ! @@ -77,12 +82,13 @@ module ocn_diagnostics !> \brief Computes diagnostic variables !> \author Mark Petersen !> \date 23 September 2011 -!> \details +!> \details !> This routine computes the diagnostic variables for the ocean ! !----------------------------------------------------------------------- - subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, timeLevelIn)!{{{ + subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool, &!{{{ + timeLevelIn) real (kind=RKIND), intent(in) :: dt !< Input: Time step type (mpas_pool_type), intent(in) :: statePool !< Input: State information @@ -90,18 +96,20 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information type (mpas_pool_type), intent(inout) :: diagnosticsPool !< Input: diagnostic fields derived from State type (mpas_pool_type), intent(in) :: scratchPool !< Input: scratch variables + type (mpas_pool_type), intent(in) :: tracersPool !< Input: tracer fields integer, intent(in), optional :: timeLevelIn !< Input: Time level in state integer :: iEdge, iCell, iVertex, k, cell1, cell2, vertex1, vertex2, eoe, i, j - integer :: boundaryMask, velMask, err - integer, pointer :: nEdgesSolve, nCells, nEdges, nVertices, nVertLevels, vertexDegree + integer :: boundaryMask, velMask, err, nCells, nEdges, nVertices + integer, pointer :: nVertLevels, vertexDegree + integer, dimension(:), pointer :: nCellsArray, nEdgesArray, nVerticesArray integer, dimension(:), pointer :: nEdgesOnCell, nEdgesOnEdge, & maxLevelCell, maxLevelEdgeTop, maxLevelEdgeBot, & maxLevelVertexBot integer, dimension(:,:), pointer :: cellsOnEdge, cellsOnVertex, & verticesOnEdge, edgesOnEdge, edgesOnVertex,boundaryCell, kiteIndexOnCell, & - verticesOnCell, edgeSignOnVertex, edgeSignOnCell, edgesOnCell + verticesOnCell, edgeSignOnVertex, edgeSignOnCell, edgesOnCell, edgeMask real (kind=RKIND) :: d2fdx2_cell1, d2fdx2_cell2, coef_3rd_order, r_tmp, & invAreaCell1, invAreaCell2, invAreaTri1, invAreaTri2, invLength, layerThicknessVertex, coef, & @@ -110,19 +118,19 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic real (kind=RKIND), dimension(:), allocatable:: pTop, div_hu,div_huTransport,div_huGMBolus real (kind=RKIND), dimension(:), pointer :: & - bottomDepth, fVertex, dvEdge, dcEdge, areaCell, areaTriangle, ssh, seaSurfacePressure + bottomDepth, fVertex, dvEdge, dcEdge, areaCell, areaTriangle, ssh, seaIcePressure, atmosphericPressure, frazilSurfacePressure, & + pressureAdjustedSSH, gradSSH, landIcePressure, landIceDraft real (kind=RKIND), dimension(:,:), pointer :: & - weightsOnEdge, kiteAreasOnVertex, layerThicknessEdge, layerThickness, normalVelocity, normalTransportVelocity, normalGMBolusVelocity, tangentialVelocity, pressure,& - circulation, kineticEnergyCell, montgomeryPotential, vertAleTransportTop, zMid, zTop, divergence, & - relativeVorticity, relativeVorticityCell, & - normalizedPlanetaryVorticityEdge, normalizedPlanetaryVorticityVertex, & - normalizedRelativeVorticityEdge, normalizedRelativeVorticityVertex, normalizedRelativeVorticityCell, & - density, displacedDensity, potentialDensity, temperature, salinity, kineticEnergyVertex, kineticEnergyVertexOnCells, & - vertVelocityTop, vertTransportVelocityTop, vertGMBolusVelocityTop, BruntVaisalaFreqTop, & - vorticityGradientNormalComponent, vorticityGradientTangentialComponent, gradSSH, RiTopOfCell, & - inSituThermalExpansionCoeff, inSituSalineContractionCoeff - - real (kind=RKIND), dimension(:,:,:), pointer :: tracers, derivTwo + weightsOnEdge, kiteAreasOnVertex, layerThicknessEdge, layerThickness, normalVelocity, normalTransportVelocity, & + normalGMBolusVelocity, tangentialVelocity, pressure, circulation, kineticEnergyCell, montgomeryPotential, & + vertAleTransportTop, zMid, zTop, divergence, relativeVorticity, relativeVorticityCell, & + normalizedPlanetaryVorticityEdge, normalizedPlanetaryVorticityVertex, normalizedRelativeVorticityEdge, & + normalizedRelativeVorticityVertex, normalizedRelativeVorticityCell, density, displacedDensity, potentialDensity, & + temperature, salinity, kineticEnergyVertex, kineticEnergyVertexOnCells, vertVelocityTop, vertTransportVelocityTop, & + vertGMBolusVelocityTop, BruntVaisalaFreqTop, vorticityGradientNormalComponent, vorticityGradientTangentialComponent, & + RiTopOfCell, inSituThermalExpansionCoeff, inSituSalineContractionCoeff + + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers, derivTwo character :: c1*6 real (kind=RKIND), dimension(:,:), pointer :: tracersSurfaceValue @@ -137,28 +145,41 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic integer :: timeLevel integer, pointer :: indexTemperature, indexSalinity logical, pointer :: config_use_cvmix_kpp - real (kind=RKIND), pointer :: config_density0, config_apvm_scale_factor, config_coef_3rd_order, config_cvmix_kpp_surface_layer_averaging + real (kind=RKIND), pointer :: config_apvm_scale_factor, config_coef_3rd_order, config_cvmix_kpp_surface_layer_averaging character (len=StrKIND), pointer :: config_pressure_gradient_type + real (kind=RKIND), pointer :: config_flux_attenuation_coefficient + real (kind=RKIND), pointer :: config_flux_attenuation_coefficient_runoff + real (kind=RKIND), dimension(:), pointer :: surfaceFluxAttenuationCoefficient + real (kind=RKIND), dimension(:), pointer :: surfaceFluxAttenuationCoefficientRunoff + + real (kind=RKIND) :: areaTri1, layerThicknessVertexInv, tempRiVal, dcEdge_temp, dvEdge_temp, weightsOnEdge_temp + real (kind=RKIND), dimension(:), allocatable:: layerThicknessVertexVec + integer :: edgeSignOnCell_temp + + call mpas_timer_start('diagnostic solve') + if (present(timeLevelIn)) then timeLevel = timeLevelIn else timeLevel = 1 end if - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) call mpas_pool_get_config(ocnConfigs, 'config_apvm_scale_factor', config_apvm_scale_factor) call mpas_pool_get_config(ocnConfigs, 'config_pressure_gradient_type', config_pressure_gradient_type) call mpas_pool_get_config(ocnConfigs, 'config_coef_3rd_order', config_coef_3rd_order) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_surface_layer_averaging', config_cvmix_kpp_surface_layer_averaging) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_kpp', config_use_cvmix_kpp) + call mpas_pool_get_config(ocnConfigs, 'config_flux_attenuation_coefficient', config_flux_attenuation_coefficient) + call mpas_pool_get_config(ocnConfigs, 'config_flux_attenuation_coefficient_runoff', & + config_flux_attenuation_coefficient_runoff) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexSalinity) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) call mpas_pool_get_array(statePool, 'ssh', ssh, timeLevel) call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) @@ -186,6 +207,7 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) call mpas_pool_get_array(diagnosticsPool, 'gradSSH', gradSSH) call mpas_pool_get_array(diagnosticsPool, 'RiTopOfCell', RiTopOfCell) + call mpas_pool_get_array(diagnosticsPool, 'pressureAdjustedSSH', pressureAdjustedSSH) call mpas_pool_get_array(meshPool, 'weightsOnEdge', weightsOnEdge) call mpas_pool_get_array(meshPool, 'kiteAreasOnVertex', kiteAreasOnVertex) @@ -213,13 +235,17 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic call mpas_pool_get_array(meshPool, 'boundaryCell', boundaryCell) call mpas_pool_get_array(meshPool, 'edgeSignOnVertex', edgeSignOnVertex) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) - call mpas_pool_get_array(forcingPool, 'seaSurfacePressure', seaSurfacePressure) - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) - call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + call mpas_pool_get_array(forcingPool, 'seaIcePressure', seaIcePressure) + call mpas_pool_get_array(forcingPool, 'atmosphericPressure', atmosphericPressure) + call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) + call mpas_pool_get_array(forcingPool, 'landIceDraft', landIceDraft) + call mpas_pool_get_array(forcingPool, 'frazilSurfacePressure', frazilSurfacePressure) + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(meshPool, 'nVerticesArray', nVerticesArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) @@ -228,154 +254,213 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic call mpas_pool_get_array(diagnosticsPool, 'normalVelocitySurfaceLayer', normalVelocitySurfaceLayer) call mpas_pool_get_array(diagnosticsPool, 'indexSurfaceLayerDepth', indexSurfaceLayerDepth) + call mpas_pool_get_array(diagnosticsPool, 'surfaceFluxAttenuationCoefficient', surfaceFluxAttenuationCoefficient) + call mpas_pool_get_array(diagnosticsPool, 'surfaceFluxAttenuationCoefficientRunoff', surfaceFluxAttenuationCoefficientRunoff) ! ! Compute height on cell edges at velocity locations ! Namelist options control the order of accuracy of the reconstructed layerThicknessEdge value ! - ! initialize layerThicknessEdge to avoid divide by zero and NaN problems. - layerThicknessEdge = -1.0e34 - coef_3rd_order = config_coef_3rd_order + nEdges = nEdgesArray( size(nEdgesArray) ) + nCells = nCellsArray( size(nCellsArray) ) + nVertices = nVerticesArray( size(nVerticesArray) ) + + !$omp do schedule(runtime) private(cell1, cell2, k) do iEdge = 1, nEdges + ! initialize layerThicknessEdge to avoid divide by zero and NaN problems. + layerThicknessEdge(:, iEdge) = -1.0e34_RKIND cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) do k = 1, maxLevelEdgeTop(iEdge) - layerThicknessEdge(k,iEdge) = 0.5 * (layerThickness(k,cell1) + layerThickness(k,cell2)) + layerThicknessEdge(k,iEdge) = 0.5_RKIND * (layerThickness(k,cell1) + layerThickness(k,cell2)) end do end do + !$omp end do + + coef_3rd_order = config_coef_3rd_order ! ! set the velocity and height at dummy address ! used -1e34 so error clearly occurs if these values are used. ! + + !$omp single normalVelocity(:,nEdges+1) = -1e34 layerThickness(:,nCells+1) = -1e34 - tracers(indexTemperature,:,nCells+1) = -1e34 - tracers(indexSalinity,:,nCells+1) = -1e34 - - divergence(:,:) = 0.0 - vertVelocityTop(:,:)=0.0 - kineticEnergyCell(:,:) = 0.0 - tangentialVelocity(:,:) = 0.0 + activeTracers(indexTemperature,:,nCells+1) = -1e34 + activeTracers(indexSalinity,:,nCells+1) = -1e34 + !$omp end single call ocn_relativeVorticity_circulation(relativeVorticity, circulation, meshPool, normalVelocity, err) - relativeVorticityCell(:,:) = 0.0 + ! Need owned cells for relativeVorticityCell + nCells = nCellsArray( 1 ) + + !$omp do schedule(runtime) private(invAreaCell1, i, j, k, iVertex) do iCell = 1, nCells - invAreaCell1 = 1.0 / areaCell(iCell) + relativeVorticityCell(:,iCell) = 0.0_RKIND + invAreaCell1 = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) j = kiteIndexOnCell(i, iCell) iVertex = verticesOnCell(i, iCell) do k = 1, maxLevelCell(iCell) - relativeVorticityCell(k, iCell) = relativeVorticityCell(k, iCell) + kiteAreasOnVertex(j, iVertex) * relativeVorticity(k, iVertex) * invAreaCell1 + relativeVorticityCell(k, iCell) = relativeVorticityCell(k, iCell) + kiteAreasOnVertex(j, iVertex) & + * relativeVorticity(k, iVertex) * invAreaCell1 end do end do end do + !$omp end do + ! Need 0 and 1 halo cells + nCells = nCellsArray( 2 ) ! ! Compute divergence, kinetic energy, and vertical velocity ! allocate(div_hu(nVertLevels),div_huTransport(nVertLevels),div_huGMBolus(nVertLevels)) + + !$omp do schedule(runtime) private(invAreaCell1, iEdge, r_tmp, i, k) do iCell = 1, nCells - div_hu(:) = 0.0 - div_huTransport(:) = 0.0 - div_huGMBolus(:) = 0.0 - invAreaCell1 = 1.0 / areaCell(iCell) + divergence(:, iCell) = 0.0_RKIND + kineticEnergyCell(:, iCell) = 0.0_RKIND + div_hu(:) = 0.0_RKIND + div_huTransport(:) = 0.0_RKIND + div_huGMBolus(:) = 0.0_RKIND + invAreaCell1 = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) + edgeSignOnCell_temp = edgeSignOnCell(i, iCell) + dcEdge_temp = dcEdge(iEdge) + dvEdge_temp = dvEdge(iEdge) do k = 1, maxLevelCell(iCell) - r_tmp = dvEdge(iEdge) * normalVelocity(k, iEdge) * invAreaCell1 - - divergence(k, iCell) = divergence(k, iCell) - edgeSignOnCell(i, iCell) * r_tmp - div_hu(k) = div_hu(k) - layerThicknessEdge(k, iEdge) * edgeSignOnCell(i, iCell) * r_tmp - kineticEnergyCell(k, iCell) = kineticEnergyCell(k, iCell) + 0.25 * r_tmp * dcEdge(iEdge) * normalVelocity(k,iEdge) + r_tmp = dvEdge_temp * normalVelocity(k, iEdge) * invAreaCell1 + divergence(k, iCell) = divergence(k, iCell) - edgeSignOnCell_temp * r_tmp + div_hu(k) = div_hu(k) - layerThicknessEdge(k, iEdge) * edgeSignOnCell_temp * r_tmp + kineticEnergyCell(k, iCell) = kineticEnergyCell(k, iCell) & + + 0.25 * r_tmp * dcEdge_temp * normalVelocity(k,iEdge) ! Compute vertical velocity from the horizontal total transport - div_huTransport(k) = div_huTransport(k) - layerThicknessEdge(k, iEdge) * edgeSignOnCell(i, iCell) * dvEdge(iEdge) * normalTransportVelocity(k, iEdge) * invAreaCell1 + div_huTransport(k) = div_huTransport(k) & + - layerThicknessEdge(k, iEdge) * edgeSignOnCell_temp & + * dvEdge_temp * normalTransportVelocity(k, iEdge) * invAreaCell1 ! Compute vertical velocity from the horizontal GM Bolus velocity - div_huGMBolus(k) = div_huGMBolus(k) - layerThicknessEdge(k, iEdge) * edgeSignOnCell(i, iCell) * dvEdge(iEdge) * normalGMBolusVelocity(k, iEdge) * invAreaCell1 + div_huGMBolus(k) = div_huGMBolus(k) & + - layerThicknessEdge(k, iEdge) * edgeSignOnCell_temp * dvEdge_temp & + * normalGMBolusVelocity(k, iEdge) * invAreaCell1 end do end do ! Vertical velocity at bottom (maxLevelCell(iCell)+1) is zero, initialized above. + vertVelocityTop(maxLevelCell(iCell)+1, iCell) = 0.0_RKIND do k=maxLevelCell(iCell),1,-1 vertVelocityTop(k,iCell) = vertVelocityTop(k+1,iCell) - div_hu(k) vertTransportVelocityTop(k,iCell) = vertTransportVelocityTop(k+1,iCell) - div_huTransport(k) vertGMBolusVelocityTop(k,iCell) = vertGMBolusVelocityTop(k+1,iCell) - div_huGMBolus(k) - end do + end do end do + !$omp end do + deallocate(div_hu,div_huTransport,div_huGMBolus) + nEdges = nEdgesArray( 2 ) + + !$omp do schedule(runtime) private(eoe, i, k) do iEdge = 1, nEdges + tangentialVelocity(:, iEdge) = 0.0_RKIND ! Compute v (tangential) velocities do i = 1, nEdgesOnEdge(iEdge) eoe = edgesOnEdge(i,iEdge) - do k = 1, maxLevelEdgeTop(iEdge) - tangentialVelocity(k,iEdge) = tangentialVelocity(k,iEdge) + weightsOnEdge(i,iEdge) * normalVelocity(k, eoe) + weightsOnEdge_temp = weightsOnEdge(i, iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + tangentialVelocity(k,iEdge) = tangentialVelocity(k,iEdge) & + + weightsOnEdge_temp * normalVelocity(k, eoe) end do end do end do + !$omp end do + + nCells = nCellsArray( size(nCellsArray) ) ! ! Compute kinetic energy ! - call mpas_pool_get_field(scratchPool, 'kineticEnergyVertex', kineticEnergyVertexField) - call mpas_pool_get_field(scratchPool, 'kineticEnergyVertexOnCells', kineticEnergyVertexOnCellsField) - call mpas_allocate_scratch_field(kineticEnergyVertexField, .true.) - call mpas_allocate_scratch_field(kineticEnergyVertexOnCellsField, .true.) - kineticEnergyVertex => kineticEnergyVertexField % array - kineticEnergyVertexOnCells => kineticEnergyVertexOnCellsField % array - kineticEnergyVertex(:,:) = 0.0; - kineticEnergyVertexOnCells(:,:) = 0.0 - do iVertex = 1, nVertices*ke_vertex_flag - do i = 1, vertexDegree - iEdge = edgesOnVertex(i, iVertex) - r_tmp = dcEdge(iEdge) * dvEdge(iEdge) * 0.25 / areaTriangle(iVertex) - do k = 1, nVertLevels - kineticEnergyVertex(k, iVertex) = kineticEnergyVertex(k, iVertex) + r_tmp * normalVelocity(k, iEdge)**2 - end do - end do - end do + if ( ke_vertex_flag == 1 ) then + call mpas_pool_get_field(scratchPool, 'kineticEnergyVertex', kineticEnergyVertexField) + call mpas_pool_get_field(scratchPool, 'kineticEnergyVertexOnCells', kineticEnergyVertexOnCellsField) + call mpas_allocate_scratch_field(kineticEnergyVertexField, .true.) + call mpas_allocate_scratch_field(kineticEnergyVertexOnCellsField, .true.) + call mpas_threading_barrier() + + kineticEnergyVertex => kineticEnergyVertexField % array + kineticEnergyVertexOnCells => kineticEnergyVertexOnCellsField % array + + !$omp do schedule(runtime) private(i, iEdge, r_tmp, k) + do iVertex = 1, nVertices + kineticEnergyVertex(:, iVertex) = 0.0_RKIND + do i = 1, vertexDegree + iEdge = edgesOnVertex(i, iVertex) + r_tmp = dcEdge(iEdge) * dvEdge(iEdge) * 0.25_RKIND / areaTriangle(iVertex) + do k = 1, nVertLevels + kineticEnergyVertex(k, iVertex) = kineticEnergyVertex(k, iVertex) + r_tmp * normalVelocity(k, iEdge)**2 + end do + end do + end do + !$omp end do - do iCell = 1, nCells*ke_vertex_flag - invAreaCell1 = 1.0 / areaCell(iCell) - do i = 1, nEdgesOnCell(iCell) - j = kiteIndexOnCell(i, iCell) - iVertex = verticesOnCell(i, iCell) - do k = 1, nVertLevels - kineticEnergyVertexOnCells(k, iCell) = kineticEnergyVertexOnCells(k, iCell) + kiteAreasOnVertex(j, iVertex) * kineticEnergyVertex(k, iVertex) * invAreaCell1 - end do - end do - end do + !$omp do schedule(runtime) private(invAreaCell1, i, j, iVertex, k) + do iCell = 1, nCells + kineticEnergyVertexOnCells(:, iCell) = 0.0_RKIND + invAreaCell1 = 1.0_RKIND / areaCell(iCell) + do i = 1, nEdgesOnCell(iCell) + j = kiteIndexOnCell(i, iCell) + iVertex = verticesOnCell(i, iCell) + do k = 1, nVertLevels + kineticEnergyVertexOnCells(k, iCell) = kineticEnergyVertexOnCells(k, iCell) + kiteAreasOnVertex(j, iVertex) & + * kineticEnergyVertex(k, iVertex) * invAreaCell1 + end do + end do + end do + !$omp end do - ! - ! Compute kinetic energy in each cell by blending kineticEnergyCell and kineticEnergyVertexOnCells - ! - do iCell = 1, nCells * ke_vertex_flag - do k = 1, nVertLevels - kineticEnergyCell(k,iCell) = 5.0 / 8.0 * kineticEnergyCell(k,iCell) + 3.0 / 8.0 * kineticEnergyVertexOnCells(k,iCell) + ! + ! Compute kinetic energy in each cell by blending kineticEnergyCell and kineticEnergyVertexOnCells + ! + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, nVertLevels + kineticEnergyCell(k,iCell) = 5.0_RKIND / 8.0_RKIND * kineticEnergyCell(k,iCell) + 3.0_RKIND / 8.0_RKIND & + * kineticEnergyVertexOnCells(k,iCell) + end do end do - end do + !$omp end do - call mpas_deallocate_scratch_field(kineticEnergyVertexField, .true.) - call mpas_deallocate_scratch_field(kineticEnergyVertexOnCellsField, .true.) + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(kineticEnergyVertexField, .true.) + call mpas_deallocate_scratch_field(kineticEnergyVertexOnCellsField, .true.) + end if ! ! Compute normalized relative and planetary vorticity ! call mpas_pool_get_field(scratchPool, 'normalizedRelativeVorticityVertex', normalizedRelativeVorticityVertexField) call mpas_pool_get_field(scratchPool, 'normalizedPlanetaryVorticityVertex', normalizedPlanetaryVorticityVertexField) - call mpas_allocate_scratch_field(normalizedRelativeVorticityVertexField, .true.) - call mpas_allocate_scratch_field(normalizedPlanetaryVorticityVertexField, .true.) + call mpas_allocate_scratch_field(normalizedRelativeVorticityVertexField, .true., .false.) + call mpas_allocate_scratch_field(normalizedPlanetaryVorticityVertexField, .true., .false.) + call mpas_threading_barrier() + normalizedPlanetaryVorticityVertex => normalizedPlanetaryVorticityVertexField % array normalizedRelativeVorticityVertex => normalizedRelativeVorticityVertexField % array + + nVertices = nVerticesArray( 3 ) + + !$omp do schedule(runtime) private(invAreaTri1, k, layerThicknessVertex, i) do iVertex = 1, nVertices - invAreaTri1 = 1.0 / areaTriangle(iVertex) + invAreaTri1 = 1.0_RKIND / areaTriangle(iVertex) do k = 1, maxLevelVertexBot(iVertex) - layerThicknessVertex = 0.0 + layerThicknessVertex = 0.0_RKIND do i = 1, vertexDegree - layerThicknessVertex = layerThicknessVertex + layerThickness(k,cellsOnVertex(i,iVertex)) * kiteAreasOnVertex(i,iVertex) + layerThicknessVertex = layerThicknessVertex + layerThickness(k,cellsOnVertex(i,iVertex)) & + * kiteAreasOnVertex(i,iVertex) end do layerThicknessVertex = layerThicknessVertex * invAreaTri1 @@ -383,21 +468,31 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic normalizedPlanetaryVorticityVertex(k,iVertex) = fVertex(iVertex) / layerThicknessVertex end do end do + !$omp end do - normalizedRelativeVorticityEdge(:,:) = 0.0 - normalizedPlanetaryVorticityEdge(:,:) = 0.0 + nEdges = nEdgesArray( 3 ) + + !$omp do schedule(runtime) private(vertex1, vertex2, k) do iEdge = 1, nEdges + normalizedRelativeVorticityEdge(:, iEdge) = 0.0_RKIND + normalizedPlanetaryVorticityEdge(:, iEdge) = 0.0_RKIND vertex1 = verticesOnEdge(1, iEdge) vertex2 = verticesOnEdge(2, iEdge) do k = 1, maxLevelEdgeBot(iEdge) - normalizedRelativeVorticityEdge(k, iEdge) = 0.5 * (normalizedRelativeVorticityVertex(k, vertex1) + normalizedRelativeVorticityVertex(k, vertex2)) - normalizedPlanetaryVorticityEdge(k, iEdge) = 0.5 * (normalizedPlanetaryVorticityVertex(k, vertex1) + normalizedPlanetaryVorticityVertex(k, vertex2)) + normalizedRelativeVorticityEdge(k, iEdge) = 0.5_RKIND * (normalizedRelativeVorticityVertex(k, vertex1) & + + normalizedRelativeVorticityVertex(k, vertex2)) + normalizedPlanetaryVorticityEdge(k, iEdge) = 0.5_RKIND * (normalizedPlanetaryVorticityVertex(k, vertex1) & + + normalizedPlanetaryVorticityVertex(k, vertex2)) end do end do + !$omp end do + + nCells = nCellsArray( 2 ) - normalizedRelativeVorticityCell(:,:) = 0.0 + !$omp do schedule(runtime) private(invAreaCell1, i, j, iVertex, k) do iCell = 1, nCells - invAreaCell1 = 1.0 / areaCell(iCell) + normalizedRelativeVorticityCell(:, iCell) = 0.0_RKIND + invAreaCell1 = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) j = kiteIndexOnCell(i, iCell) @@ -408,6 +503,11 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic end do end do end do + !$omp end do + + nCells = nCellsArray( size(nCellsArray) ) + nVertices = nVerticesArray( size(nVerticesArray) ) + nEdges = nCellsArray( size(nCellsArray) ) ! Diagnostics required for the Anticipated Potential Vorticity Method (apvm). if (config_apvm_scale_factor>1e-10) then @@ -416,16 +516,21 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic call mpas_pool_get_field(scratchPool, 'vorticityGradientTangentialComponent', vorticityGradientTangentialComponentField) call mpas_allocate_scratch_field(vorticityGradientNormalComponentField, .true.) call mpas_allocate_scratch_field(vorticityGradientTangentialComponentField, .true.) + call mpas_threading_barrier() + vorticityGradientNormalComponent => vorticityGradientNormalComponentField % array vorticityGradientTangentialComponent => vorticityGradientTangentialComponentField % array + nEdges = nEdgesArray( 2 ) + + !$omp do schedule(runtime) private(cell1, cell2, vertex1, vertex2, invLength, k) do iEdge = 1,nEdges cell1 = cellsOnEdge(1, iEdge) cell2 = cellsOnEdge(2, iEdge) vertex1 = verticesOnedge(1, iEdge) vertex2 = verticesOnedge(2, iEdge) - invLength = 1.0 / dcEdge(iEdge) + invLength = 1.0_RKIND / dcEdge(iEdge) ! Compute gradient of PV in normal direction ! ( this computes the gradient for all edges bounding real cells ) do k=1,maxLevelEdgeTop(iEdge) @@ -433,7 +538,7 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic (normalizedRelativeVorticityCell(k,cell2) - normalizedRelativeVorticityCell(k,cell1)) * invLength enddo - invLength = 1.0 / dvEdge(iEdge) + invLength = 1.0_RKIND / dvEdge(iEdge) ! Compute gradient of PV in the tangent direction ! ( this computes the gradient at all edges bounding real cells and distance-1 ghost cells ) do k = 1,maxLevelEdgeBot(iEdge) @@ -442,10 +547,12 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic enddo enddo + !$omp end do ! ! Modify PV edge with upstream bias. ! + !$omp do schedule(runtime) private(k) do iEdge = 1,nEdges do k = 1,maxLevelEdgeBot(iEdge) normalizedRelativeVorticityEdge(k,iEdge) = normalizedRelativeVorticityEdge(k,iEdge) & @@ -454,54 +561,72 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic + tangentialVelocity(k,iEdge) * vorticityGradientTangentialComponent(k,iEdge) ) enddo enddo + !$omp end do + + call mpas_threading_barrier() call mpas_deallocate_scratch_field(vorticityGradientNormalComponentField, .true.) call mpas_deallocate_scratch_field(vorticityGradientTangentialComponentField, .true.) endif + + call mpas_threading_barrier() call mpas_deallocate_scratch_field(normalizedRelativeVorticityVertexField, .true.) call mpas_deallocate_scratch_field(normalizedPlanetaryVorticityVertexField, .true.) ! ! equation of state ! - call mpas_timer_start("equation of state") + + ! Only need densities on 0, 1, and 2 halo cells + nCells = nCellsArray( 3 ) ! compute in-place density if (config_pressure_gradient_type.eq.'Jacobian_from_TS') then ! only compute EOS derivatives if needed. call mpas_pool_get_array(diagnosticsPool, 'inSituThermalExpansionCoeff',inSituThermalExpansionCoeff) call mpas_pool_get_array(diagnosticsPool, 'inSituSalineContractionCoeff', inSituSalineContractionCoeff) - call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, 0, 'relative', density, err, & - inSituThermalExpansionCoeff, inSituSalineContractionCoeff, timeLevelIn=timeLevel) + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, & + nCells, 0, 'relative', density, err, & + inSituThermalExpansionCoeff, inSituSalineContractionCoeff, & + timeLevelIn=timeLevel) else - call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, 0, 'relative', density, err, & - timeLevelIn=timeLevel) + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, & + nCells, 0, 'relative', density, err, & + timeLevelIn=timeLevel) endif + call mpas_threading_barrier() ! compute potentialDensity, the density displaced adiabatically to the mid-depth of top layer. - call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, 1, 'absolute', potentialDensity, err, timeLevelIn=timeLevel) + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, & + nCells, 1, 'absolute', potentialDensity, & + err, timeLevelIn=timeLevel) - ! compute displacedDensity, density displaced adiabatically to the mid-depth one layer deeper. + ! compute displacedDensity, density displaced adiabatically to the mid-depth one layer deeper. ! That is, layer k has been displaced to the depth of layer k+1. - call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, 1, 'relative', displacedDensity, err, timeLevelIn=timeLevel) - - call mpas_timer_stop("equation of state") + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, & + nCells, 1, 'relative', displacedDensity, & + err, timeLevelIn=timeLevel) + call mpas_threading_barrier() ! ! Pressure ! This section must be placed in the code after computing the density. ! + nCells = nCellsArray( size(nCellsArray) ) if (config_pressure_gradient_type.eq.'MontgomeryPotential') then ! use Montgomery Potential when layers are isopycnal. ! However, one may use 'pressure_and_zmid' when layers are isopycnal as well. ! Compute pressure at top of each layer, and then Montgomery Potential. + allocate(pTop(nVertLevels)) + + !$omp do schedule(runtime) private(k) do iCell = 1, nCells ! assume atmospheric pressure at the surface is zero for now. - pTop(1) = 0.0 - ! At top layer it is g*SSH, where SSH may be off by a + pTop(1) = 0.0_RKIND + ! At top layer it is g*SSH, where SSH may be off by a ! constant (ie, bottomDepth can be relative to top or bottom) montgomeryPotential(1,iCell) = gravity & * (bottomDepth(iCell) + sum(layerThickness(1:nVertLevels,iCell))) @@ -511,24 +636,29 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic ! from delta M = p delta / density montgomeryPotential(k,iCell) = montgomeryPotential(k-1,iCell) & - + pTop(k)*(1.0/density(k,iCell) - 1.0/density(k-1,iCell)) + + pTop(k)*(1.0_RKIND/density(k,iCell) - 1.0_RKIND/density(k-1,iCell)) end do end do + !$omp end do + deallocate(pTop) else + !$omp do schedule(runtime) private(k) do iCell = 1, nCells ! Pressure for generalized coordinates. ! Pressure at top surface may be due to atmospheric pressure - ! or an ice-shelf depression. - pressure(1,iCell) = seaSurfacePressure(iCell) + density(1,iCell)*gravity & - * 0.5*layerThickness(1,iCell) + ! or an ice-shelf depression. + pressure(1,iCell) = atmosphericPressure(iCell) + seaIcePressure(iCell) + if ( associated(frazilSurfacePressure) ) pressure(1,iCell) = pressure(1,iCell) + frazilSurfacePressure(iCell) + if ( associated(landIcePressure) ) pressure(1,iCell) = pressure(1,iCell) + landIcePressure(iCell) + pressure(1,iCell) = pressure(1,iCell) + density(1,iCell)*gravity*0.5_RKIND*layerThickness(1,iCell) do k = 2, maxLevelCell(iCell) pressure(k,iCell) = pressure(k-1,iCell) & - + 0.5*gravity*( density(k-1,iCell)*layerThickness(k-1,iCell) & + + 0.5_RKIND*gravity*( density(k-1,iCell)*layerThickness(k-1,iCell) & + density(k ,iCell)*layerThickness(k ,iCell)) end do @@ -537,12 +667,12 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic ! Note the negative sign, since bottomDepth is positive ! and z-coordinates are negative below the surface. k = maxLevelCell(iCell) - zMid(k:nVertLevels,iCell) = -bottomDepth(iCell) + 0.5*layerThickness(k,iCell) + zMid(k:nVertLevels,iCell) = -bottomDepth(iCell) + 0.5_RKIND*layerThickness(k,iCell) zTop(k:nVertLevels,iCell) = -bottomDepth(iCell) + layerThickness(k,iCell) do k = maxLevelCell(iCell)-1, 1, -1 zMid(k,iCell) = zMid(k+1,iCell) & - + 0.5*( layerThickness(k+1,iCell) & + + 0.5_RKIND*( layerThickness(k+1,iCell) & + layerThickness(k ,iCell)) zTop(k,iCell) = zTop(k+1,iCell) & + layerThickness(k ,iCell) @@ -552,61 +682,85 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic ssh(iCell) = zTop(1,iCell) end do + !$omp end do endif + nCells = nCellsArray( 3 ) + ! ! Brunt-Vaisala frequency (this has units of s^{-2}) ! - coef = -gravity / config_density0 + coef = -gravity / rho_sw + !$omp do schedule(runtime) private(k) do iCell = 1, nCells - BruntVaisalaFreqTop(1,iCell) = 0.0 + BruntVaisalaFreqTop(1,iCell) = 0.0_RKIND do k = 2, maxLevelCell(iCell) - BruntVaisalaFreqTop(k,iCell) = coef * (displacedDensity(k-1,iCell) - density(k,iCell)) & + BruntVaisalaFreqTop(k,iCell) = coef * (displacedDensity(k-1,iCell) - density(k,iCell)) & / (zMid(k-1,iCell) - zMid(k,iCell)) end do end do + !$omp end do ! ! Gradient Richardson number ! - RiTopOfCell = 100.0 + + !$omp do schedule(runtime) private(invAreaCell1, k, shearSquared, i, iEdge, factor, delU2, shearMean) do iCell=1,nCells - invAreaCell1 = 1.0 / areaCell(iCell) + RiTopOfCell(:,iCell) = 100.0_RKIND + invAreaCell1 = 1.0_RKIND / areaCell(iCell) do k=2,maxLevelCell(iCell) - shearSquared = 0.0 + shearSquared = 0.0_RKIND do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) - factor = 0.5 * dcEdge(iEdge) * dvEdge(iEdge) * invAreaCell1 + factor = 0.5_RKIND * dcEdge(iEdge) * dvEdge(iEdge) * invAreaCell1 delU2 = (normalVelocity(k-1,iEdge) - normalVelocity(k,iEdge))**2 shearSquared = shearSquared + factor * delU2 - enddo + enddo shearMean = sqrt(shearSquared) shearMean = shearMean / (zMid(k-1,iCell) - zMid(k,iCell)) - RiTopOfCell(k,iCell) = BruntVaisalaFreqTop(k,iCell) / (shearMean**2 + 1.0e-10) + RiTopOfCell(k,iCell) = BruntVaisalaFreqTop(k,iCell) / (shearMean**2 + 1.0e-10_RKIND) end do RiTopOfCell(1,iCell) = RiTopOfCell(2,iCell) end do + !$omp end do ! ! extrapolate tracer values to ocean surface ! this eventually be a modelled process ! at present, just copy k=1 tracer values onto surface values ! field will be updated below is better approximations are available - tracersSurfaceValue(:,:) = tracers(:,1,:) - normalVelocitySurfaceLayer(:) = normalVelocity(1,:) + +!TDR need to consider how to handle tracersSurfaceValues + !$omp do schedule(runtime) + do iCell = 1, nCells + tracersSurfaceValue(:, iCell) = activeTracers(:,1, iCell) + end do + !$omp end do + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + normalVelocitySurfaceLayer(iEdge) = normalVelocity(1, iEdge) + end do + !$omp end do ! ! average tracer values over the ocean surface layer ! the ocean surface layer is generally assumed to be about 0.1 of the boundary layer depth if(config_use_cvmix_kpp) then - tracersSurfaceLayerValue(:,:) = 0.0 - indexSurfaceLayerDepth(:) = -9.e30 + + nCells = nCellsArray( 1 ) + + !$omp do schedule(runtime) private(surfaceLayerDepth, sumSurfaceLayer, k, rSurfaceLayer) do iCell=1,nCells surfaceLayerDepth = config_cvmix_kpp_surface_layer_averaging - sumSurfaceLayer=0.0 + sumSurfaceLayer=0.0_RKIND + tracersSurfaceLayerValue(:,iCell) = 0.0_RKIND + indexSurfaceLayerDepth(iCell) = -9.e30 do k=1,maxLevelCell(iCell) sumSurfaceLayer = sumSurfaceLayer + layerThickness(k,iCell) + rSurfaceLayer = maxLevelCell(iCell) if(sumSurfaceLayer.gt.surfaceLayerDepth) then sumSurfaceLayer = sumSurfaceLayer - layerThickness(k,iCell) rSurfaceLayer = int(k-1) + (surfaceLayerDepth-sumSurfaceLayer)/layerThickness(k,iCell) @@ -614,24 +768,32 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic exit endif end do + tracersSurfaceLayerValue(:, iCell) = 0.0_RKIND do k=1,int(rSurfaceLayer) - tracersSurfaceLayerValue(:,iCell) = tracersSurfaceLayerValue(:,iCell) + tracers(:,k,iCell)*layerThickness(k,iCell) + tracersSurfaceLayerValue(:,iCell) = tracersSurfaceLayerValue(:,iCell) + activeTracers(:,k,iCell) & + * layerThickness(k,iCell) enddo - k=int(rSurfaceLayer)+1 - tracersSurfaceLayerValue(:,iCell) = tracersSurfaceLayerValue(:,iCell) + fraction(rSurfaceLayer)*tracers(:,k,iCell)*layerThickness(k,iCell) - tracersSurfaceLayerValue(:,iCell) = tracersSurfaceLayerValue(:,iCell) / surfaceLayerDepth + k=min( int(rSurfaceLayer)+1, maxLevelCell(iCell) ) + tracersSurfaceLayerValue(:,iCell) = (tracersSurfaceLayerValue(:,iCell) + fraction(rSurfaceLayer) & + * activeTracers(:,k,iCell) * layerThickness(k,iCell)) / surfaceLayerDepth enddo + !$omp end do + + nEdges = nEdgesArray( 1 ) ! ! average normal velocity values over the ocean surface layer ! the ocean surface layer is generally assumed to be about 0.1 of the boundary layer depth ! - normalVelocitySurfaceLayer(:) = 0.0_RKIND + + !$omp do schedule(runtime) private(cell1, cell2, surfaceLayerDepth, sumSurfaceLayer, k, rSurfaceLayer) do iEdge=1,nEdges + normalVelocitySurfaceLayer(iEdge) = 0.0_RKIND cell1=cellsOnEdge(1,iEdge) cell2=cellsOnEdge(2,iEdge) surfaceLayerDepth = config_cvmix_kpp_surface_layer_averaging - sumSurfaceLayer=0.0 + sumSurfaceLayer=0.0_RKIND + rSurfaceLayer = min(1, maxLevelEdgeTop(iEdge)) do k=1,maxLevelEdgeTop(iEdge) rSurfaceLayer = k sumSurfaceLayer = sumSurfaceLayer + layerThicknessEdge(k,iEdge) @@ -641,27 +803,69 @@ subroutine ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnostic exit endif end do + normalVelocitySurfaceLayer(iEdge) = 0.0_RKIND do k=1,int(rSurfaceLayer) - normalVelocitySurfaceLayer(iEdge) = normalVelocitySurfaceLayer(iEdge) + normalVelocity(k,iEdge)*layerThicknessEdge(k,iEdge) + normalVelocitySurfaceLayer(iEdge) = normalVelocitySurfaceLayer(iEdge) + normalVelocity(k,iEdge) & + * layerThicknessEdge(k,iEdge) enddo k=int(rSurfaceLayer)+1 if(k.le.maxLevelEdgeTop(iEdge)) then - normalVelocitySurfaceLayer(iEdge) = normalVelocitySurfaceLayer(iEdge) + fraction(rSurfaceLayer)*normalVelocity(k,iEdge)*layerThicknessEdge(k,iEdge) - normalVelocitySurfaceLayer(iEdge) = normalVelocitySurfaceLayer(iEdge) / surfaceLayerDepth + normalVelocitySurfaceLayer(iEdge) = (normalVelocitySurfaceLayer(iEdge) + fraction(rSurfaceLayer) & + * normalVelocity(k,iEdge) * layerThicknessEdge(k,iEdge)) / surfaceLayerDepth end if enddo + !$omp end do - ! - ! compute fields used as intent(in) to CVMix/KPP - call computeKPPInputFields(statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, timeLevel) endif - do iEdge = 1, nEdgesSolve + nCells = nCellsArray( 2 ) + + ! compute the attenuation coefficient for surface fluxes + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceFluxAttenuationCoefficient(iCell) = config_flux_attenuation_coefficient + surfaceFluxAttenuationCoefficientRunoff(iCell) = config_flux_attenuation_coefficient_runoff + end do + !$omp end do + + ! + ! compute fields needed to compute land-ice fluxes, either in the ocean model or in the coupler + call ocn_compute_land_ice_flux_input_fields(meshPool, statePool, forcingPool, scratchPool, & + diagnosticsPool, timeLevel) + + nCells = nCellsArray( 2 ) + !$omp do schedule(runtime) + do iCell = 1, nCells + pressureAdjustedSSH(iCell) = ssh(iCell) + ( seaIcePressure(iCell) / ( gravity * rho_sw ) ) + end do + !$omp end do + + if(associated(landIceDraft)) then + !$omp do schedule(runtime) + do iCell = 1, nCells + ! subtract the land ice draft from the SSH so sea ice doesn't experience tilt + ! toward land ice + pressureAdjustedSSH(iCell) = pressureAdjustedSSH(iCell) - landIceDraft(iCell) + end do + !$omp end do + end if + + nEdges = nEdgesArray( 2 ) + + !$omp do schedule(runtime) private(cell1, cell2) + do iEdge = 1, nEdges cell1 = cellsOnEdge(1, iEdge) cell2 = cellsOnEdge(2, iEdge) - gradSSH(1, iEdge) = (ssh(cell2) - ssh(cell1)) / dcEdge(iEdge) + gradSSH(iEdge) = edgeMask(1, iEdge) * ( pressureAdjustedSSH(cell2) - pressureAdjustedSSH(cell1) ) / dcEdge(iEdge) end do + !$omp end do + + call mpas_threading_barrier() + + call mpas_deallocate_scratch_field(normalizedRelativeVorticityVertexField, .true.) + call mpas_deallocate_scratch_field(normalizedPlanetaryVorticityVertexField, .true.) + call mpas_timer_stop('diagnostic solve') end subroutine ocn_diagnostic_solve!}}} @@ -672,12 +876,12 @@ end subroutine ocn_diagnostic_solve!}}} !> \brief Computes vertical transport !> \author Mark Petersen !> \date August 2013 -!> \details -!> This routine computes the vertical transport through the top of each -!> cell. +!> \details +!> This routine computes the vertical transport through the top of each +!> cell. ! !----------------------------------------------------------------------- - subroutine ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, oldLayerThickness, layerThicknessEdge, & + subroutine ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, scratchPool, oldLayerThickness, layerThicknessEdge, & normalVelocity, oldSSH, dt, vertAleTransportTop, err, newHighFreqThickness)!{{{ !----------------------------------------------------------------- @@ -692,6 +896,8 @@ subroutine ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, oldLayerT type (mpas_pool_type), intent(in) :: & verticalMeshPool !< Input: vertical mesh information + type (mpas_pool_type), intent(in) :: scratchPool !< Input: scratch variables + real (kind=RKIND), dimension(:,:), intent(in) :: & oldLayerThickness !< Input: layer thickness at old time @@ -727,19 +933,22 @@ subroutine ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, oldLayerT ! !----------------------------------------------------------------- - integer :: iEdge, iCell, k, i - integer, pointer :: nCells, nVertLevels + integer :: iEdge, iCell, k, i, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: nEdgesOnCell, nEdgesOnEdge, & maxLevelCell, maxLevelEdgeBot integer, dimension(:,:), pointer :: edgesOnCell, edgeSignOnCell - real (kind=RKIND) :: flux, invAreaCell + real (kind=RKIND) :: flux, invAreaCell, div_hu_btr real (kind=RKIND), dimension(:), pointer :: dvEdge, areaCell - real (kind=RKIND), dimension(:), allocatable :: & - div_hu_btr !> barotropic divergence of (thickness*velocity) - real (kind=RKIND), dimension(:,:), allocatable :: & + real (kind=RKIND), dimension(:), pointer :: & + projectedSSH !> projected SSH at new time + type (field1DReal), pointer :: projectedSSHField + real (kind=RKIND), dimension(:,:), pointer :: & ALE_Thickness, & !> ALE thickness at new time div_hu !> divergence of (thickness*velocity) + type (field2DReal), pointer :: ALE_ThicknessField, div_huField character (len=StrKIND), pointer :: config_vert_coord_movement @@ -755,45 +964,64 @@ subroutine ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, oldLayerT call mpas_pool_get_array(meshPool, 'maxLevelEdgeBot', maxLevelEdgeBot) call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) if (config_vert_coord_movement.eq.'impermeable_interfaces') then - vertAleTransportTop=0.0 + vertAleTransportTop=0.0_RKIND return end if - allocate(div_hu(nVertLevels,nCells), div_hu_btr(nCells), ALE_Thickness(nVertLevels,nCells)) + ! Only need to compute over 0 and 1 halos + nCells = nCellsArray( 2 ) + + call mpas_pool_get_field(scratchPool, 'div_hu', div_huField) + call mpas_pool_get_field(scratchPool, 'projectedSSH', projectedSSHField) + call mpas_pool_get_field(scratchPool, 'ALE_Thickness', ALE_ThicknessField) + call mpas_allocate_scratch_field(div_huField, .true.) + call mpas_allocate_scratch_field(projectedSSHField, .true.) + call mpas_allocate_scratch_field(ALE_ThicknessField, .true.) + + call mpas_threading_barrier() + + div_hu => div_huField % array + projectedSSH => projectedSSHField % array + ALE_Thickness => ALE_ThicknessField % array ! ! thickness-weighted divergence and barotropic divergence ! ! See Ringler et al. (2010) jcp paper, eqn 19, 21, and fig. 3. + !$omp do schedule(runtime) private(invAreaCell, i, iEdge, k, flux, div_hu_btr) do iCell = 1, nCells - div_hu(:,iCell) = 0.0 - div_hu_btr(iCell) = 0.0 - invAreaCell = 1.0 / areaCell(iCell) + div_hu(:,iCell) = 0.0_RKIND + div_hu_btr = 0.0_RKIND + invAreaCell = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) do k = 1, maxLevelEdgeBot(iEdge) - flux = layerThicknessEdge(k, iEdge) * normalVelocity(k, iEdge) * dvEdge(iEdge) * edgeSignOnCell(i, iCell) * invAreaCell + flux = layerThicknessEdge(k, iEdge) * normalVelocity(k, iEdge) * dvEdge(iEdge) * edgeSignOnCell(i, iCell) & + * invAreaCell div_hu(k,iCell) = div_hu(k,iCell) - flux - div_hu_btr(iCell) = div_hu_btr(iCell) - flux + div_hu_btr = div_hu_btr - flux end do end do - - enddo + projectedSSH(iCell) = oldSSH(iCell) - dt*div_hu_btr + end do + !$omp end do ! ! Compute desired thickness at new time ! if (present(newHighFreqThickness)) then - call ocn_ALE_thickness(meshPool, verticalMeshPool, oldSSH, div_hu_btr, dt, ALE_thickness, err, newHighFreqThickness) + call ocn_ALE_thickness(meshPool, verticalMeshPool, projectedSSH, ALE_thickness, err, newHighFreqThickness) else - call ocn_ALE_thickness(meshPool, verticalMeshPool, oldSSH, div_hu_btr, dt, ALE_thickness, err) + call ocn_ALE_thickness(meshPool, verticalMeshPool, projectedSSH, ALE_thickness, err) endif + call mpas_threading_barrier() + ! ! Vertical transport through layer interfaces ! @@ -801,16 +1029,21 @@ subroutine ocn_vert_transport_velocity_top(meshPool, verticalMeshPool, oldLayerT ! Here we are using solving the continuity equation for vertAleTransportTop ($w^t$), ! and using ALE_Thickness for thickness at the new time. + !$omp do schedule(runtime) private(k) do iCell = 1,nCells - vertAleTransportTop(1,iCell) = 0.0 - vertAleTransportTop(maxLevelCell(iCell)+1,iCell) = 0.0 + vertAleTransportTop(1,iCell) = 0.0_RKIND + vertAleTransportTop(maxLevelCell(iCell)+1,iCell) = 0.0_RKIND do k = maxLevelCell(iCell),2,-1 vertAleTransportTop(k,iCell) = vertAleTransportTop(k+1,iCell) - div_hu(k,iCell) & - (ALE_Thickness(k,iCell) - oldLayerThickness(k,iCell))/dt end do end do + !$omp end do - deallocate(div_hu, div_hu_btr, ALE_Thickness) + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(div_huField, .true.) + call mpas_deallocate_scratch_field(projectedSSHField, .true.) + call mpas_deallocate_scratch_field(ALE_ThicknessField, .true.) end subroutine ocn_vert_transport_velocity_top!}}} @@ -821,7 +1054,7 @@ end subroutine ocn_vert_transport_velocity_top!}}} !> \brief Computes f u_perp !> \author Mark Petersen !> \date 23 September 2011 -!> \details +!> \details !> This routine computes f u_perp for the ocean ! !----------------------------------------------------------------------- @@ -850,6 +1083,7 @@ subroutine ocn_fuperp(statePool, meshPool, timeLevelIn)!{{{ end if call mpas_timer_start("ocn_fuperp") + call mpas_threading_barrier() call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) call mpas_pool_get_array(statePool, 'normalBaroclinicVelocity', normalBaroclinicVelocity, timeLevel) @@ -865,23 +1099,29 @@ subroutine ocn_fuperp(statePool, meshPool, timeLevelIn)!{{{ call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + !DWJ: ADD OMP (Only needed for split explicit) + ! ! Put f*normalBaroclinicVelocity^{perp} in u as a work variable ! + !$omp do schedule(runtime) private(cell1, cell2, k, eoe) do iEdge = 1, nEdgesSolve cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) do k = 1, maxLevelEdgeTop(iEdge) - normalVelocity(k,iEdge) = 0.0 + normalVelocity(k,iEdge) = 0.0_RKIND do j = 1,nEdgesOnEdge(iEdge) eoe = edgesOnEdge(j,iEdge) - normalVelocity(k,iEdge) = normalVelocity(k,iEdge) + weightsOnEdge(j,iEdge) * normalBaroclinicVelocity(k,eoe) * fEdge(eoe) + normalVelocity(k,iEdge) = normalVelocity(k,iEdge) + weightsOnEdge(j,iEdge) * normalBaroclinicVelocity(k,eoe) & + * fEdge(eoe) end do end do end do + !$omp end do + call mpas_threading_barrier() call mpas_timer_stop("ocn_fuperp") end subroutine ocn_fuperp!}}} @@ -893,7 +1133,7 @@ end subroutine ocn_fuperp!}}} !> \brief filters barotropic mode out of the velocity variable. !> \author Mark Petersen !> \date 23 September 2011 -!> \details +!> \details !> This routine filters barotropic mode out of the velocity variable. ! !----------------------------------------------------------------------- @@ -914,7 +1154,6 @@ subroutine ocn_filter_btr_mode_vel(statePool, diagnosticsPool, meshPool, timeLev call mpas_timer_start("ocn_filter_btr_mode_vel") - if (present(timeLevelIn)) then timeLevel = timeLevelIn else @@ -929,10 +1168,11 @@ subroutine ocn_filter_btr_mode_vel(statePool, diagnosticsPool, meshPool, timeLev call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + !$omp do schedule(runtime) private(normalThicknessFluxSum, thicknessSum, k, vertSum) do iEdge = 1, nEdges - ! thicknessSum is initialized outside the loop because on land boundaries - ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a + ! thicknessSum is initialized outside the loop because on land boundaries + ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a ! nonzero value to avoid a NaN. normalThicknessFluxSum = layerThicknessEdge(1,iEdge) * normalVelocity(1,iEdge) thicknessSum = layerThicknessEdge(1,iEdge) @@ -947,6 +1187,9 @@ subroutine ocn_filter_btr_mode_vel(statePool, diagnosticsPool, meshPool, timeLev normalVelocity(k,iEdge) = normalVelocity(k,iEdge) - vertSum enddo enddo ! iEdge + !$omp end do + + call mpas_threading_barrier() call mpas_timer_stop("ocn_filter_btr_mode_vel") @@ -959,7 +1202,7 @@ end subroutine ocn_filter_btr_mode_vel!}}} !> \brief ocn_filters barotropic mode out of the velocity tendency !> \author Mark Petersen !> \date 23 September 2011 -!> \details +!> \details !> This routine filters barotropic mode out of the velocity tendency. ! !----------------------------------------------------------------------- @@ -996,10 +1239,11 @@ subroutine ocn_filter_btr_mode_tend_vel(tendPool, statePool, diagnosticsPool, me call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + !$omp do schedule(runtime) private(normalThicknessFluxSum, thicknessSum, vertSum, k) do iEdge = 1, nEdges - ! thicknessSum is initialized outside the loop because on land boundaries - ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a + ! thicknessSum is initialized outside the loop because on land boundaries + ! maxLevelEdgeTop=0, but I want to initialize thicknessSum with a ! nonzero value to avoid a NaN. normalThicknessFluxSum = layerThicknessEdge(1,iEdge) * tend_normalVelocity(1,iEdge) thicknessSum = layerThicknessEdge(1,iEdge) @@ -1014,6 +1258,7 @@ subroutine ocn_filter_btr_mode_tend_vel(tendPool, statePool, diagnosticsPool, me tend_normalVelocity(k,iEdge) = tend_normalVelocity(k,iEdge) - vertSum enddo enddo ! iEdge + !$omp end do call mpas_timer_stop("ocn_filter_btr_mode_tend_vel") @@ -1026,7 +1271,7 @@ end subroutine ocn_filter_btr_mode_tend_vel!}}} !> \brief Initializes flags used within diagnostics routines. !> \author Mark Petersen !> \date 4 November 2011 -!> \details +!> \details !> This routine initializes flags related to quantities computed within !> other diagnostics routines. ! @@ -1055,7 +1300,7 @@ subroutine ocn_diagnostics_init(err)!{{{ fCoef = 1 elseif (trim(config_time_integrator) == 'split_explicit' & .or.trim(config_time_integrator) == 'unsplit_explicit') then - ! For split explicit, PV is eta/h because the Coriolis term + ! For split explicit, PV is eta/h because the Coriolis term ! is added separately to the momentum tendencies. fCoef = 0 end if @@ -1064,9 +1309,9 @@ end subroutine ocn_diagnostics_init!}}} !*********************************************************************** ! -! routine computeKPPInputFields +! routine ocn_compute_KPP_input_fields ! -!> \brief +!> \brief !> Compute fields necessary to drive the CVMix KPP module !> \author Todd Ringler !> \date 20 August 2013 @@ -1080,7 +1325,7 @@ end subroutine ocn_diagnostics_init!}}} ! !----------------------------------------------------------------------- - subroutine computeKPPInputFields(statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, timeLevelIn)!{{{ + subroutine ocn_compute_KPP_input_fields(statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, timeLevelIn)!{{{ type (mpas_pool_type), intent(in) :: statePool !< Input/Output: State information type (mpas_pool_type), intent(in) :: forcingPool !< Input: Forcing information @@ -1089,8 +1334,14 @@ subroutine computeKPPInputFields(statePool, forcingPool, meshPool, diagnosticsPo type (mpas_pool_type), intent(in) :: scratchPool !< Input: scratch variables integer, intent(in), optional :: timeLevelIn + ! pool pointers + type (mpas_pool_type), pointer :: tracersSurfaceFluxPool + type (mpas_pool_type), pointer :: tracersPool + ! scalars - integer, pointer :: nCells, nVertLevels + integer :: nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray ! integer pointers integer, dimension(:), pointer :: maxLevelCell, nEdgesOnCell @@ -1100,27 +1351,33 @@ subroutine computeKPPInputFields(statePool, forcingPool, meshPool, diagnosticsPo real (kind=RKIND), dimension(:), pointer :: dcEdge, dvEdge, areaCell real (kind=RKIND), dimension(:), pointer :: penetrativeTemperatureFlux, surfaceThicknessFlux, & surfaceBuoyancyForcing, surfaceFrictionVelocity, penetrativeTemperatureFluxOBL, & - normalVelocitySurfaceLayer - real (kind=RKIND), dimension(:), pointer :: surfaceWindStress, surfaceWindStressMagnitude + normalVelocitySurfaceLayer, surfaceThicknessFluxRunoff + real (kind=RKIND), pointer :: config_flux_attenuation_coefficient, config_flux_attenuation_coefficient_runoff + + real (kind=RKIND), dimension(:), pointer :: surfaceStress, surfaceStressMagnitude real (kind=RKIND), dimension(:,:), pointer :: & - layerThickness, zMid, zTop, tracersSurfaceValues, densitySurfaceDisplaced, density, & - normalVelocity, surfaceTracerFlux, thermalExpansionCoeff, salineContractionCoeff + layerThickness, zMid, zTop, densitySurfaceDisplaced, density, & + normalVelocity, activeTracersSurfaceFlux, thermalExpansionCoeff, salineContractionCoeff, & + activeTracersSurfaceFluxRunoff, nonLocalSurfaceTracerFlux real (kind=RKIND), dimension(:), pointer :: & indexSurfaceLayerDepth - real (kind=RKIND), dimension(:,:), pointer :: & + real (kind=RKIND), dimension(:,:,:), pointer :: & + activeTracers + + real (kind=RKIND), dimension(:,:), pointer :: & bulkRichardsonNumberBuoy, bulkRichardsonNumberShear ! local integer :: iCell, iEdge, i, k, err, timeLevel integer, pointer :: indexTempFlux, indexSaltFlux - real (kind=RKIND) :: numerator, denominator, turbulentVelocitySquared - real (kind=RKIND) :: buoyContribution, shearContribution, factor, deltaVelocitySquared, delU2, invAreaCell - real (kind=RKIND), dimension(:), allocatable :: buoySmoothed, shearSmoothed + real (kind=RKIND) :: numerator, denominator, turbulentVelocitySquared, fracAbsorbed, fracAbsorbedRunoff + real (kind=RKIND) :: factor, deltaVelocitySquared, delU2, invAreaCell type (field2DReal), pointer :: densitySurfaceDisplacedField, thermalExpansionCoeffField, salineContractionCoeffField - real (kind=RKIND), pointer :: config_density0 + + call mpas_timer_start('KPP input fields') if (present(timeLevelIn)) then timeLevel = timeLevelIn @@ -1128,16 +1385,19 @@ subroutine computeKPPInputFields(statePool, forcingPool, meshPool, diagnosticsPo timeLevel = 1 end if - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) - + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceFlux', tracersSurfaceFluxPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_config(ocnConfigs, 'config_flux_attenuation_coefficient', config_flux_attenuation_coefficient) + call mpas_pool_get_config(ocnConfigs, 'config_flux_attenuation_coefficient_runoff', & + config_flux_attenuation_coefficient_runoff) ! set the parameter turbulentVelocitySquared turbulentVelocitySquared = 0.001_RKIND ! set scalar values - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(forcingPool, 'index_surfaceTemperatureFlux', indexTempFlux) - call mpas_pool_get_dimension(forcingPool, 'index_surfaceSalinityFlux', indexSaltFlux) + call mpas_pool_get_dimension(tracersSurfaceFluxPool, 'index_temperatureSurfaceFlux', indexTempFlux) + call mpas_pool_get_dimension(tracersSurfaceFluxPool, 'index_salinitySurfaceFlux', indexSaltFlux) ! set pointers into state, mesh, diagnostics and scratch call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) @@ -1153,123 +1413,421 @@ subroutine computeKPPInputFields(statePool, forcingPool, meshPool, diagnosticsPo call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) call mpas_pool_get_array(diagnosticsPool, 'zTop', zTop) call mpas_pool_get_array(diagnosticsPool, 'density', density) - call mpas_pool_get_array(diagnosticsPool, 'tracersSurfaceValue ', tracersSurfaceValues) call mpas_pool_get_array(diagnosticsPool, 'surfaceFrictionVelocity', surfaceFrictionVelocity) call mpas_pool_get_array(diagnosticsPool, 'penetrativeTemperatureFluxOBL', penetrativeTemperatureFluxOBL) - call mpas_pool_get_array(diagnosticsPool, 'bulkRichardsonNumberBuoy', bulkRichardsonNumberBuoy) - call mpas_pool_get_array(diagnosticsPool, 'bulkRichardsonNumberShear', bulkRichardsonNumberShear) call mpas_pool_get_array(diagnosticsPool, 'indexSurfaceLayerDepth', indexSurfaceLayerDepth) call mpas_pool_get_array(diagnosticsPool, 'surfaceBuoyancyForcing', surfaceBuoyancyForcing) call mpas_pool_get_array(diagnosticsPool, 'normalVelocitySurfaceLayer', normalVelocitySurfaceLayer) + call mpas_pool_get_array(tracersSurfaceFluxPool, 'nonLocalSurfaceTracerFlux', nonLocalSurfaceTracerFlux) call mpas_pool_get_array(forcingPool, 'surfaceThicknessFlux', surfaceThicknessFlux) - call mpas_pool_get_array(forcingPool, 'surfaceTracerFlux', surfaceTracerFlux) + call mpas_pool_get_array(forcingPool, 'surfaceThicknessFluxRunoff', surfaceThicknessFluxRunoff) call mpas_pool_get_array(forcingPool, 'penetrativeTemperatureFlux', penetrativeTemperatureFlux) - call mpas_pool_get_array(forcingPool, 'surfaceWindStress', surfaceWindStress) - call mpas_pool_get_array(forcingPool, 'surfaceWindStressMagnitude', surfaceWindStressMagnitude) + call mpas_pool_get_array(forcingPool, 'surfaceStress', surfaceStress) + call mpas_pool_get_array(forcingPool, 'surfaceStressMagnitude', surfaceStressMagnitude) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(tracersSurfaceFluxPool, 'activeTracersSurfaceFlux', activeTracersSurfaceFlux) + call mpas_pool_get_array(tracersSurfaceFluxPool, 'activeTracersSurfaceFluxRunoff', activeTracersSurfaceFluxRunoff) ! allocate scratch space displaced density computation call mpas_pool_get_field(scratchPool, 'densitySurfaceDisplaced', densitySurfaceDisplacedField) call mpas_pool_get_field(scratchPool, 'thermalExpansionCoeff', thermalExpansionCoeffField) call mpas_pool_get_field(scratchPool, 'salineContractionCoeff', salineContractionCoeffField) - call mpas_allocate_scratch_field(densitySurfaceDisplacedField, .true.) - call mpas_allocate_scratch_field(thermalExpansionCoeffField, .true.) - call mpas_allocate_scratch_field(salineContractionCoeffField, .true.) + call mpas_allocate_scratch_field(densitySurfaceDisplacedField, .true., .false.) + call mpas_allocate_scratch_field(thermalExpansionCoeffField, .true., .false.) + call mpas_allocate_scratch_field(salineContractionCoeffField, .true., .false.) + call mpas_threading_barrier() + densitySurfaceDisplaced => densitySurfaceDisplacedField % array thermalExpansionCoeff => thermalExpansionCoeffField % array salineContractionCoeff => salineContractionCoeffField % array - ! allocate local work space - allocate(buoySmoothed(nVertLevels)) - allocate(shearSmoothed(nVertLevels)) + ! Only need to compute over the 0 and 1 halos + nCells = nCellsArray( 3 ) ! compute EOS by displacing SST/SSS to every vertical layer in column - call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, 0, 'surfaceDisplaced', densitySurfaceDisplaced, err, & - thermalExpansionCoeff, salineContractionCoeff, timeLevel) + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, nCells, 0, 'surfaceDisplaced', & + densitySurfaceDisplaced, err, thermalExpansionCoeff, salineContractionCoeff, & + timeLevel) + !$omp do schedule(runtime) private(invAreaCell, deltaVelocitySquared, i, iEdge, factor, delU2, fracAbsorbed, & + !$omp fracAbsorbedRunoff) do iCell = 1, nCells - invAreaCell = 1.0 / areaCell(iCell) + invAreaCell = 1.0_RKIND / areaCell(iCell) - ! compute surface buoyancy forcing based on surface fluxes of mass, temperature, salinity and frazil (frazil to be added later) + ! compute surface buoyancy forcing based on surface fluxes of mass, temperature, salinity and frazil + ! (frazil to be added later) ! since this computation is confusing, variables, units and sign convention is repeated here ! everything below should be consistent with that specified in Registry - ! everything below should be consistent with the CVMix/KPP documentation: https://www.dropbox.com/s/6hqgc0rsoa828nf/cvmix_20aug2013.pdf + ! everything below should be consistent with the CVMix/KPP documentation: + ! https://www.dropbox.com/s/6hqgc0rsoa828nf/cvmix_20aug2013.pdf ! ! surfaceThicknessFlux: surface mass flux, m/s, positive into ocean - ! surfaceTracerFlux(indexTempFlux): non-penetrative temperature flux, C m/s, positive into ocean + ! activeTracersSurfaceFlux(indexTempFlux): non-penetrative temperature flux, C m/s, positive into ocean ! penetrativeTemperatureFlux: penetrative surface temperature flux at ocean surface, positive into ocean - ! surfaceTracerFlux(indexSaltFlux): salinity flux, PSU m/s, positive into ocean + ! activeTracersSurfaceFlux(indexSaltFlux): salinity flux, PSU m/s, positive into ocean ! penetrativeTemperatureFluxOBL: penetrative temperature flux computed at z=OBL, positive down ! ! note: the following fields used the CVMix/KPP computation of buoyancy forcing are not included here ! 1. Tm: temperature associated with surfaceThicknessFlux, C (here we assume Tm == temperatureSurfaceValue) - ! 2. Sm: salinity associated with surfaceThicknessFlux, PSU (here we assume Sm == salinitySurfaceValue and account for salinity flux in surfaceTracerFlux array) + ! 2. Sm: salinity associated with surfaceThicknessFlux, PSU (here we assume Sm == salinitySurfaceValue and account for + ! salinity flux in activeTracersSurfaceFlux array) ! - surfaceBuoyancyForcing(iCell) = thermalExpansionCoeff (1,iCell) * & - (surfaceTracerFlux(indexTempFlux,iCell) + penetrativeTemperatureFlux(iCell) - penetrativeTemperatureFluxOBL(iCell)) & - - salineContractionCoeff(1,iCell) * surfaceTracerFlux(indexSaltFlux,iCell) - - ! at this point, surfaceBuoyancyForcing has units of m/s + + ! Compute fraction of thickness flux that is in the top model layer + fracAbsorbed = 1.0_RKIND - exp( max(-layerThickness(1, iCell) / config_flux_attenuation_coefficient, -100.0_RKIND) ) + fracAbsorbedRunoff = 1.0_RKIND - exp( max(-layerThickness(1, iCell) / config_flux_attenuation_coefficient_runoff, & + -100.0_RKIND) ) + ! Store the total tracer flux below in nonLocalSurfaceTemperatureFlux for use in the CVMix nonlocal + ! transport code. This includes tracer forcing due to thickness + + nonLocalSurfaceTracerFlux(indexTempFlux, iCell) = activeTracersSurfaceFlux(indexTempFlux,iCell) & + + penetrativeTemperatureFlux(iCell) - penetrativeTemperatureFluxOBL(iCell) - fracAbsorbed * & + surfaceThicknessFlux(iCell) * activeTracers(indexTempFlux,1,iCell) + & + activeTracersSurfaceFluxRunoff(indexTempFlux,iCell) * fracAbsorbedRunoff + + nonLocalSurfaceTracerFlux(indexSaltFlux,iCell) = activeTracersSurfaceFlux(indexSaltFlux,iCell) & + - fracAbsorbed * surfaceThicknessFlux(iCell) * activeTracers(indexSaltFlux,1,iCell) + + surfaceBuoyancyForcing(iCell) = thermalExpansionCoeff (1,iCell) & + * nonLocalSurfaceTracerFlux(indexTempFlux,iCell) & + - salineContractionCoeff(1,iCell) * nonLocalSurfaceTracerFlux(indexSaltFlux,iCell) + + ! at this point, surfaceBuoyancyForcing has units of m/s ! change into units of m^2/s^3 (which can be thought of as the flux of buoyancy, units of buoyancy * velocity ) surfaceBuoyancyForcing(iCell) = surfaceBuoyancyForcing(iCell) * gravity - ! compute magnitude of surface windstress - deltaVelocitySquared = 0.0 + ! compute magnitude of surface stress + deltaVelocitySquared = 0.0_RKIND do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) - factor = 0.5 * dcEdge(iEdge) * dvEdge(iEdge) * invAreaCell - delU2 = (surfaceWindStress(iEdge))**2 + factor = 0.5_RKIND * dcEdge(iEdge) * dvEdge(iEdge) * invAreaCell + delU2 = (surfaceStress(iEdge))**2 deltaVelocitySquared = deltaVelocitySquared + factor * delU2 enddo - surfacewindStressMagnitude(iCell) = sqrt(deltaVelocitySquared) + surfaceStressMagnitude(iCell) = sqrt(deltaVelocitySquared) ! compute surface friction velocity - surfaceFrictionVelocity(iCell) = sqrt(surfacewindStressMagnitude(iCell) / config_density0) + surfaceFrictionVelocity(iCell) = sqrt(surfaceStressMagnitude(iCell) / rho_sw) - ! zero the bulk Richardson number within the ocean surface layer - ! this prevent CVMix/KPP from mis-diagnosing the OBL to be within the surface layer - bulkRichardsonNumberBuoy (:,iCell) = 1.0e8_RKIND - bulkRichardsonNumberShear(:,iCell) = 1.0_RKIND - ! loop over vertical to compute bulk Richardson number - do k=1,maxLevelCell(iCell) + enddo + !$omp end do - ! find deltaVelocitySquared defined at cell centers based on velocity at levels 1 and k - deltaVelocitySquared = 0.0_RKIND - do i = 1, nEdgesOnCell(iCell) - iEdge = edgesOnCell(i, iCell) - factor = 0.5 * dcEdge(iEdge) * dvEdge(iEdge) * invAreaCell - delU2 = (normalVelocitySurfaceLayer(iEdge) - normalVelocity(k,iEdge))**2 - deltaVelocitySquared = deltaVelocitySquared + factor * delU2 - enddo + call mpas_threading_barrier() - buoyContribution = gravity * (density(k,iCell) - densitySurfaceDisplaced(k,iCell)) / config_density0 - shearContribution = max(deltaVelocitySquared,1.0e-15_RKIND) + ! deallocate scratch space + call mpas_deallocate_scratch_field(thermalExpansionCoeffField, .true.) + call mpas_deallocate_scratch_field(salineContractionCoeffField, .true.) + call mpas_deallocate_scratch_field(densitySurfaceDisplacedField, .true.) - ! store the buoyancy and resolved shear contributions to bulk Richardson number - bulkRichardsonNumberBuoy(k,iCell) = buoyContribution - bulkRichardsonNumberShear(k,iCell) = shearContribution + call mpas_timer_stop('KPP input fields') - enddo ! do k=1,maxLevelCell(iCell) + end subroutine ocn_compute_KPP_input_fields!}}} - ! set bulkRichardsonNumberBuoy to a negative value within surface layer to prevent CVMix/KPP from - ! incorrectly diagnosing OBL to be within surface layer - ! require boundary layer to be below the top layer - k=max(int(indexSurfaceLayerDepth(iCell)),1) - bulkRichardsonNumberBuoy(1:k,iCell) = 0.0_RKIND - enddo +!*********************************************************************** +! +! routine ocn_compute_land_ice_flux_input_fields +! +!> \brief Builds the forcing array for land-ice forcing +!> \author Xylar Asay-Davis +!> \date 09/14/2015 +!> \details +!> This routine builds surface flux arrays related to land-ice forcing. +! +!----------------------------------------------------------------------- - ! deallocate scratch space - call mpas_deallocate_scratch_field(densitySurfaceDisplacedField, .true.) - call mpas_deallocate_scratch_field(thermalExpansionCoeffField, .true.) - call mpas_deallocate_scratch_field(salineContractionCoeffField, .true.) + subroutine ocn_compute_land_ice_flux_input_fields(meshPool, statePool, & + forcingPool, scratchPool, diagnosticsPool, timeLevel)!{{{ + + type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information + type (mpas_pool_type), intent(in) :: statePool !< Input: State information + type (mpas_pool_type), intent(in) :: forcingPool !< Input: Forcing information + type (mpas_pool_type), intent(in) :: scratchPool !< Input/Output: scratch variables + type (mpas_pool_type), intent(inout) :: diagnosticsPool !< Input/Output: Diagnostics information + + integer, intent(in) :: timeLevel + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: tracersPool + + integer :: iCell, iEdge, cell1, cell2, iLevel, i + integer, pointer :: nCells, nEdges + + integer, dimension(:,:), pointer :: cellsOnCell, cellsOnEdge, cellMask + + integer, dimension(:), pointer :: maxLevelCell, nEdgesOnCell + + integer, pointer :: indexT, indexS, indexBLT, indexBLS, indexHeatTrans, indexSaltTrans + + character (len=StrKIND), pointer :: config_land_ice_flux_formulation, config_land_ice_flux_mode + + real (kind=RKIND), pointer :: config_land_ice_flux_boundaryLayerThickness, & + config_land_ice_flux_boundaryLayerNeighborWeight, & + config_land_ice_flux_topDragCoeff, & + config_land_ice_flux_rms_tidal_velocity, & + config_land_ice_flux_jenkins_heat_transfer_coefficient, & + config_land_ice_flux_jenkins_salt_transfer_coefficient, & + config_land_ice_flux_attenuation_coefficient + + real (kind=RKIND) :: blThickness, dz, weightSum, h_nu, Gamma_turb, landIceEdgeFraction, velocityMagnitude + + real (kind=RKIND), dimension(:), pointer :: landIceFraction, & + landIceFrictionVelocity, & + topDrag, & + topDragMagnitude, & + fCell, & + blTempScratch, blSaltScratch, & + surfaceFluxAttenuationCoefficient + + integer, dimension(:), pointer :: landIceMask + + real (kind=RKIND), dimension(:,:), pointer :: kineticEnergyCell, layerThickness, normalVelocity, & + landIceBoundaryLayerTracers, landIceTracerTransferVelocities + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + type (field1DReal), pointer :: boundaryLayerTemperatureField, boundaryLayerSalinityField + + logical :: jenkinsOn, hollandJenkinsOn + + ! constants for Holland and Jenkins 1999 parameterization of the boundary layer + real (kind=RKIND), parameter :: & + Pr = 13.8_RKIND, & ! the Prandtl number + Sc = 2432.0_RKIND, & ! the Schmidt number + nuSaltWater = 1.95e-6_RKIND, & ! molecular viscosity of sea water (m^2/s) + kVonKarman = 0.4_RKIND, & ! the von Karman constant + xiN = 0.052_RKIND ! dimensionless planetary boundary layer constant + + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + if ( trim(config_land_ice_flux_mode) .ne. 'off' ) then + + ! we need to compute the landiceMask regardless of which mode we're in so it can be + ! added to the restart file + + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + ! compute landIceMask from landIceFraction + !$omp do schedule(runtime) + do iCell = 1, nCells + if (landIceFraction(iCell) >= 0.5_RKIND) then + landIceMask(iCell) = 1 + else + landIceMask(iCell) = 0 + ! don't allow land-ice fluxes if land ice is masked out + landIceFraction(iCell) = 0.0_RKIND + end if + end do + !$omp end do + + end if + + if ( trim(config_land_ice_flux_mode) .ne. 'standalone' .and. trim(config_land_ice_flux_mode) .ne. 'coupled' ) then + return + end if + + call mpas_timer_start("land_ice_diagnostic_fields", .false.) + + jenkinsOn = .false. + hollandJenkinsOn = .false. + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_formulation', config_land_ice_flux_formulation) + if ( trim(config_land_ice_flux_formulation) == 'Jenkins' ) then + jenkinsOn = .true. + else if ( trim(config_land_ice_flux_formulation) == 'HollandJenkins' ) then + hollandJenkinsOn = .true. + end if + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_topDragCoeff', config_land_ice_flux_topDragCoeff) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_boundaryLayerThickness', & + config_land_ice_flux_boundaryLayerThickness) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_boundaryLayerNeighborWeight', & + config_land_ice_flux_boundaryLayerNeighborWeight) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_rms_tidal_velocity', config_land_ice_flux_rms_tidal_velocity) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_attenuation_coefficient', & + config_land_ice_flux_attenuation_coefficient) + + if(jenkinsOn) then + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_jenkins_heat_transfer_coefficient', & + config_land_ice_flux_jenkins_heat_transfer_coefficient) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_jenkins_salt_transfer_coefficient', & + config_land_ice_flux_jenkins_salt_transfer_coefficient) + end if + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellMask', cellMask) + + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexT) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexS) - ! deallocate local work space - deallocate(buoySmoothed) - deallocate(shearSmoothed) + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + + call mpas_pool_get_array(diagnosticsPool, 'kineticEnergyCell', kineticEnergyCell) + + call mpas_pool_get_array(diagnosticsPool, 'landIceFrictionVelocity', landIceFrictionVelocity) + call mpas_pool_get_array(diagnosticsPool, 'topDrag', topDrag) + call mpas_pool_get_array(diagnosticsPool, 'topDragMagnitude', topDragMagnitude) + + call mpas_pool_get_array(diagnosticsPool, 'landIceBoundaryLayerTracers', landIceBoundaryLayerTracers) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceBoundaryLayerTemperature', indexBLT) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceBoundaryLayerSalinity', indexBLS) + + if(jenkinsOn .or. hollandJenkinsOn) then + call mpas_pool_get_array(diagnosticsPool, 'landIceTracerTransferVelocities', landIceTracerTransferVelocities) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceHeatTransferVelocity', indexHeatTrans) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceSaltTransferVelocity', indexSaltTrans) + end if + call mpas_pool_get_array(diagnosticsPool, 'surfaceFluxAttenuationCoefficient', surfaceFluxAttenuationCoefficient) + + call mpas_pool_get_field(scratchPool, 'boundaryLayerTemperatureScratch', boundaryLayerTemperatureField) + call mpas_pool_get_field(scratchPool, 'boundaryLayerSalinityScratch', boundaryLayerSalinityField) + call mpas_allocate_scratch_field(boundaryLayerTemperatureField, .true.) + call mpas_allocate_scratch_field(boundaryLayerSalinityField, .true.) + call mpas_threading_barrier() + blTempScratch => boundaryLayerTemperatureField % array + blSaltScratch => boundaryLayerSalinityField % array + + if(hollandJenkinsOn) then + call mpas_pool_get_array(meshPool, 'fCell', fCell) + end if + + ! Compute top drag + !$omp do schedule(runtime) private(cell1, cell2, velocityMagnitude, landIceEdgeFraction) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + + ! top drag tau = - CD*|u|*u, where |u| = sqrt(2*KE) = sqrt(KE1 + KE2) from the neighboring cells + velocityMagnitude = sqrt(kineticEnergyCell(1,cell1) + kineticEnergyCell(1,cell2)) + landIceEdgeFraction = 0.5_RKIND*(landIceFraction(cell1)+landIceFraction(cell2)) + + topDrag(iEdge) = - rho_sw * landIceEdgeFraction * config_land_ice_flux_topDragCoeff & + * velocityMagnitude * normalVelocity(1,iEdge) + + end do + !$omp end do + + ! compute top drag magnitude and friction velocity at cell centers + !$omp do schedule(runtime) + do iCell = 1, nCells + ! the magnitude of the top drag is CD*u**2 = CD*(2*KE) + topDragMagnitude(iCell) = rho_sw * landIceFraction(iCell) & + * 2.0_RKIND * config_land_ice_flux_topDragCoeff * kineticEnergyCell(1,iCell) + + ! the friction velocity is the square root of the top drag + variance of tidal velocity + ! (computed regardless of land-ice coverage) + landIceFrictionVelocity(iCell) = sqrt(config_land_ice_flux_topDragCoeff * (2.0_RKIND * kineticEnergyCell(1,iCell) & + + config_land_ice_flux_rms_tidal_velocity**2)) + end do + !$omp end do + + + + ! average temperature and salinity over horizontal neighbors and the sub-ice-shelf boundary layer + !$omp do schedule(runtime) private(blThickness, iLevel, dz) + do iCell = 1, nCells + blThickness = 0.0_RKIND + blTempScratch(iCell) = 0.0_RKIND + blSaltScratch(iCell) = 0.0_RKIND + do iLevel = 1, maxLevelCell(iCell) + dz = min(layerThickness(iLevel,iCell),config_land_ice_flux_boundaryLayerThickness-blThickness) + if(dz <= 0.0_RKIND) exit + blTempScratch(iCell) = blTempScratch(iCell) + activeTracers(indexT, iLevel, iCell)*dz + blSaltScratch(iCell) = blSaltScratch(iCell) + activeTracers(indexS, iLevel, iCell)*dz + blThickness = blThickness + dz + end do + if(blThickness > 0.0_RKIND) then + blTempScratch(iCell) = blTempScratch(iCell)/blThickness + blSaltScratch(iCell) = blSaltScratch(iCell)/blThickness + end if + end do + !$omp end do + + !$omp do schedule(runtime) private(weightSum, i, cell2) + do iCell = 1, nCells + landIceBoundaryLayerTracers(indexBLT, iCell) = blTempScratch(iCell) + landIceBoundaryLayerTracers(indexBLS, iCell) = blSaltScratch(iCell) + if(config_land_ice_flux_boundaryLayerNeighborWeight > 0.0_RKIND) then + weightSum = 1.0_RKIND + do i = 1, nEdgesOnCell(iCell) + cell2 = cellsOnCell(i,iCell) + + landIceBoundaryLayerTracers(indexBLT, iCell) = landIceBoundaryLayerTracers(indexBLT, iCell) & + + cellMask(1,cell2)*config_land_ice_flux_boundaryLayerNeighborWeight*blTempScratch(cell2) + landIceBoundaryLayerTracers(indexBLS, iCell) = landIceBoundaryLayerTracers(indexBLS, iCell) & + + cellMask(1,cell2)*config_land_ice_flux_boundaryLayerNeighborWeight*blSaltScratch(cell2) + weightSum = weightSum + cellMask(1,cell2)*config_land_ice_flux_boundaryLayerNeighborWeight + end do + landIceBoundaryLayerTracers(:, iCell) = landIceBoundaryLayerTracers(:, iCell)/weightSum + end if + end do + !$omp end do + + if(jenkinsOn) then + !$omp do schedule(runtime) + do iCell = 1, nCells + ! transfer coefficients from namelist + landIceTracerTransferVelocities(indexHeatTrans, iCell) = landIceFrictionVelocity(iCell) & + * config_land_ice_flux_jenkins_heat_transfer_coefficient + landIceTracerTransferVelocities(indexSaltTrans, iCell) = landIceFrictionVelocity(iCell) & + * config_land_ice_flux_jenkins_salt_transfer_coefficient + end do + !$omp end do + else if(hollandJenkinsOn) then + !$omp do schedule(runtime) private(h_nu, Gamma_turb) + do iCell = 1, nCells + ! friction-velocity dependent non-dimensional transfer coefficients from + ! Holland and Jenkins 1999, (14)-(16) with eta_* = 1 + h_nu = 5.0_RKIND*nuSaltWater/landIceFrictionVelocity(iCell) ! uStar should never be zero because of tidal term + + Gamma_turb = 1.0_RKIND/(2.0_RKIND*xiN) - 1.0_RKIND/kVonKarman + if(abs(fCell(iCell)) > 0.0_RKIND) then + Gamma_turb = Gamma_turb + 1.0_RKIND/kVonKarman*log(landIceFrictionVelocity(iCell) & + *xiN/(abs(fCell(iCell))*h_nu)) + end if + + landIceTracerTransferVelocities(indexHeatTrans, iCell) = 1.0_RKIND/(Gamma_turb + 12.5_RKIND & + * Pr**(2.0_RKIND/3.0_RKIND) - 6.0_RKIND) + landIceTracerTransferVelocities(indexSaltTrans, iCell) = 1.0_RKIND/(Gamma_turb + 12.5_RKIND & + * Sc**(2.0_RKIND/3.0_RKIND) - 6.0_RKIND) + end do + !$omp end do + end if + + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(boundaryLayerTemperatureField, .true.) + call mpas_deallocate_scratch_field(boundaryLayerSalinityField, .true.) + + ! modify the spatially-varying attenuation coefficient where there is land ice + !$omp do schedule(runtime) + do iCell = 1, nCells + if(landIceMask(iCell) == 1) then + surfaceFluxAttenuationCoefficient(iCell) = config_land_ice_flux_attenuation_coefficient + end if + end do + !$omp end do - end subroutine computeKPPInputFields!}}} + call mpas_timer_stop("land_ice_diagnostic_fields") + + !-------------------------------------------------------------------- + + end subroutine ocn_compute_land_ice_flux_input_fields!}}} !*********************************************************************** ! @@ -1278,84 +1836,715 @@ end subroutine computeKPPInputFields!}}} !> \brief Computes cell-centered vector diagnostics !> \author Mark Petersen !> \date May 2014 -!> \details +!> \details !> This routine computes cell-centered vector diagnostics ! !----------------------------------------------------------------------- - subroutine ocn_reconstruct_gm_vectors(diagnosticsPool, meshPool) + subroutine ocn_reconstruct_gm_vectors(diagnosticsPool, meshPool) !{{{ type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information type (mpas_pool_type), intent(in) :: diagnosticsPool !< Input: Diagnostic information real (kind=RKIND), dimension(:,:), pointer :: & - normalTransportVelocity, transportVelocityX, transportVelocityY, transportVelocityZ, transportVelocityZonal, transportVelocityMeridional, & - normalGMBolusVelocity, GMBolusVelocityX, GMBolusVelocityY, GMBolusVelocityZ, GMBolusVelocityZonal, GMBolusVelocityMeridional, & - relativeSlopeTopOfEdge, relativeSlopeTopOfCellX, relativeSlopeTopOfCellY, relativeSlopeTopOfCellZ, relativeSlopeTopOfCellZonal, relativeSlopeTopOfCellMeridional, & + normalTransportVelocity, transportVelocityX, transportVelocityY, transportVelocityZ, transportVelocityZonal, & + transportVelocityMeridional, normalGMBolusVelocity, GMBolusVelocityX, GMBolusVelocityY, GMBolusVelocityZ, & + GMBolusVelocityZonal, GMBolusVelocityMeridional, relativeSlopeTopOfEdge, relativeSlopeTopOfCellX, & + relativeSlopeTopOfCellY, relativeSlopeTopOfCellZ, relativeSlopeTopOfCellZonal, relativeSlopeTopOfCellMeridional, & gmStreamFuncTopOfEdge, GMStreamFuncX, GMStreamFuncY, GMStreamFuncZ, GMStreamFuncZonal, GMStreamFuncMeridional - call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) - call mpas_pool_get_array(diagnosticsPool, 'transportVelocityX', transportVelocityX) - call mpas_pool_get_array(diagnosticsPool, 'transportVelocityY', transportVelocityY) - call mpas_pool_get_array(diagnosticsPool, 'transportVelocityZ', transportVelocityZ) - call mpas_pool_get_array(diagnosticsPool, 'transportVelocityZonal', transportVelocityZonal) - call mpas_pool_get_array(diagnosticsPool, 'transportVelocityMeridional', transportVelocityMeridional) - - call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) - call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityX', GMBolusVelocityX) - call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityY', GMBolusVelocityY) - call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityZ', GMBolusVelocityZ) - call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityZonal', GMBolusVelocityZonal) - call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityMeridional', GMBolusVelocityMeridional) - - call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfEdge', relativeSlopeTopOfEdge) - call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellX', relativeSlopeTopOfCellX) - call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellY', relativeSlopeTopOfCellY) - call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellZ', relativeSlopeTopOfCellZ) - call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellZonal', relativeSlopeTopOfCellZonal) - call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellMeridional', relativeSlopeTopOfCellMeridional) - - call mpas_pool_get_array(diagnosticsPool, 'gmStreamFuncTopOfEdge', gmStreamFuncTopOfEdge) - call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncX', GMStreamFuncX) - call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncY', GMStreamFuncY) - call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncZ', GMStreamFuncZ) - call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncZonal', GMStreamFuncZonal) - call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncMeridional', GMStreamFuncMeridional) - - call mpas_reconstruct(meshPool, normalTransportVelocity, & - transportVelocityX, & - transportVelocityY, & - transportVelocityZ, & - transportVelocityZonal, & - transportVelocityMeridional & - ) - - call mpas_reconstruct(meshPool, normalGMBolusVelocity, & - GMBolusVelocityX, & - GMBolusVelocityY, & - GMBolusVelocityZ, & - GMBolusVelocityZonal, & - GMBolusVelocityMeridional & - ) - - call mpas_reconstruct(meshPool, relativeSlopeTopOfEdge, & - relativeSlopeTopOfCellX, & - relativeSlopeTopOfCellY, & - relativeSlopeTopOfCellZ, & - relativeSlopeTopOfCellZonal, & - relativeSlopeTopOfCellMeridional & - ) - - call mpas_reconstruct(meshPool, gmStreamFuncTopOfEdge, & - GMStreamFuncX, & - GMStreamFuncY, & - GMStreamFuncZ, & - GMStreamFuncZonal, & - GMStreamFuncMeridional & - ) + call mpas_timer_start('reconstruct gm vecs') + + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) + call mpas_pool_get_array(diagnosticsPool, 'transportVelocityX', transportVelocityX) + call mpas_pool_get_array(diagnosticsPool, 'transportVelocityY', transportVelocityY) + call mpas_pool_get_array(diagnosticsPool, 'transportVelocityZ', transportVelocityZ) + call mpas_pool_get_array(diagnosticsPool, 'transportVelocityZonal', transportVelocityZonal) + call mpas_pool_get_array(diagnosticsPool, 'transportVelocityMeridional', transportVelocityMeridional) + + call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) + call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityX', GMBolusVelocityX) + call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityY', GMBolusVelocityY) + call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityZ', GMBolusVelocityZ) + call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityZonal', GMBolusVelocityZonal) + call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityMeridional', GMBolusVelocityMeridional) + + call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfEdge', relativeSlopeTopOfEdge) + call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellX', relativeSlopeTopOfCellX) + call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellY', relativeSlopeTopOfCellY) + call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellZ', relativeSlopeTopOfCellZ) + call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellZonal', relativeSlopeTopOfCellZonal) + call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCellMeridional', relativeSlopeTopOfCellMeridional) + + call mpas_pool_get_array(diagnosticsPool, 'gmStreamFuncTopOfEdge', gmStreamFuncTopOfEdge) + call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncX', GMStreamFuncX) + call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncY', GMStreamFuncY) + call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncZ', GMStreamFuncZ) + call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncZonal', GMStreamFuncZonal) + call mpas_pool_get_array(diagnosticsPool, 'GMStreamFuncMeridional', GMStreamFuncMeridional) + + call mpas_reconstruct(meshPool, normalTransportVelocity, & + transportVelocityX, & + transportVelocityY, & + transportVelocityZ, & + transportVelocityZonal, & + transportVelocityMeridional & + ) + + call mpas_reconstruct(meshPool, normalGMBolusVelocity, & + GMBolusVelocityX, & + GMBolusVelocityY, & + GMBolusVelocityZ, & + GMBolusVelocityZonal, & + GMBolusVelocityMeridional & + ) + + call mpas_reconstruct(meshPool, relativeSlopeTopOfEdge, & + relativeSlopeTopOfCellX, & + relativeSlopeTopOfCellY, & + relativeSlopeTopOfCellZ, & + relativeSlopeTopOfCellZonal, & + relativeSlopeTopOfCellMeridional & + ) + + call mpas_reconstruct(meshPool, gmStreamFuncTopOfEdge, & + GMStreamFuncX, & + GMStreamFuncY, & + GMStreamFuncZ, & + GMStreamFuncZonal, & + GMStreamFuncMeridional & + ) + + call mpas_timer_stop('reconstruct gm vecs') end subroutine ocn_reconstruct_gm_vectors!}}} + +!*********************************************************************** +! +! routine ocn_validate_state +! +!> \brief Ocean state validation routine +!> \author Doug Jacobsen +!> \date 08/11/2016 +!> \details +!> This routine validates that the ocean state is able to continue running +!> with for the next time step. +!> If a processor detects that it is unable to continue running, some +!> diagnostic information is written out about it. +!> This routine relies on the definition that a NaN does not equal itself +!> for detecting issues with the state. +! +!----------------------------------------------------------------------- + subroutine ocn_validate_state(domain, timeLevel)!{{{ + type (domain_type), intent(inout) :: domain + integer, intent(in), optional :: timeLevel + + integer :: timeLevelLocal + + type (block_type), pointer :: block + + integer, pointer :: nCellsSolve, nEdgesSolve, nVerticesSolve + + type (mpas_pool_type), pointer :: meshPool, statePool, tracersPool + type (mpas_pool_type), pointer :: forcingPool + + real (kind=RKIND), dimension(:, :), pointer :: layerThickness, normalVelocity + real (kind=RKIND), dimension(:, :, :), pointer :: activeTracers + + integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeBot, maxLevelVertexBot + + real (kind=RKIND), dimension(:), pointer :: real1DArr + real (kind=RKIND), dimension(:, :), pointer :: real2DArr + real (kind=RKIND), dimension(:, :, :), pointer :: real3DArr + real (kind=RKIND), dimension(:, :, :, :), pointer :: real4DArr + real (kind=RKIND), dimension(:, :, :, :, :), pointer :: real5DArr + integer, dimension(:), pointer :: int1DArr + integer, dimension(:, :), pointer :: int2DArr + integer, dimension(:, :, :), pointer :: int3DArr + + integer :: iCell, iEdge, iVertex, iTracer, k + + logical :: fatalErrorDetected + + logical :: thickNanFound, velNanFound, tracersNanFound + + real (kind=RKIND) :: minValue, maxValue + + integer :: debugUnit + character (len=StrKIND) :: debugFilename, fieldName + + if ( present(timeLevel) ) then + timeLevelLocal = timeLevel + else + timeLevelLocal = 1 + end if + + fatalErrorDetected = .false. + + call mpas_new_unit(debugUnit) + + block => domain % blocklist + do while ( associated(block) ) + thickNanFound = .false. + velNanFound = .false. + tracersNanFound = .false. + + debugFilename = ocn_build_log_filename('mpas_ocean_block_stats_', block % blockID) + + ! Get standard fields, for checking errors + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nVerticesSolve', nVerticesSolve) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeBot', maxLevelEdgeBot) + call mpas_pool_get_array(meshPool, 'maxLevelVertexBot', maxLevelVertexBot) + + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel=timeLevelLocal) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel=timeLevelLocal) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel=timeLevelLocal) + + ! Check for errors in the state fields + do iCell = 1, nCellsSolve + do k = 1, maxLevelCell(iCell) + thickNanFound = thickNanFound .or. ( .not. layerThickness(k, iCell) == layerThickness(k, iCell) ) + do iTracer = 1, size(activeTracers, dim=1) + tracersNanFound = tracersNanFound .or. & + ( .not. activeTracers(iTracer, k, iCell) == activeTracers(iTracer, k, iCell) ) + end do + end do + end do + + do iEdge = 1, nEdgesSolve + do k = 1, maxLevelEdgeBot(iEdge) + velNanFound = velNanFound .or. ( .not. normalVelocity(k, iEdge) == normalVelocity(k, iEdge) ) + end do + end do + + ! If an error was found, we need to open the file to write data out. + if ( thickNanFound .or. tracersNanFound .or. velNanFound ) then!{{{ + open(unit=debugUnit, file=debugFilename, form='formatted', status='unknown') + + write(debugUnit, *) 'ERROR: NaN Detected in state see below for which field contained a NaN.' + write(debugUnit, *) ' -- Statistics information for block fields' + + if ( mpas_stream_mgr_stream_exists(domain % streamManager, 'block_.*') ) then + call mpas_stream_mgr_block_Write(domain % streamManager, writeBlock=block, streamID='block_.*', & + forceWriteNow=.true.) + end if + + ! Also, write general block information, like lat/lon bounds + + ! Test latCell + fieldName = 'latCell' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(meshPool, fieldName, real1DArr) + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + + ! Test lonCell + fieldName = 'lonCell' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(meshPool, fieldName, real1DArr) + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + + ! Test xCell + fieldName = 'xCell' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(meshPool, fieldName, real1DArr) + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + + ! Test yCell + fieldName = 'yCell' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(meshPool, fieldName, real1DArr) + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + + ! Test zCell + fieldName = 'zCell' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(meshPool, fieldName, real1DArr) + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + + ! Test areaCell + fieldName = 'areaCell' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(meshPool, fieldName, real1DArr) + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if!}}} + + ! If there was a thickness NaN found, write out information about fields that affect thickness + if ( thickNanFound ) then!{{{ + write(debugUnit, *) '' + write(debugUnit, *) '' + write(debugUnit, *) 'ERROR: NaN Detected in layerThickness.' + write(debugUnit, *) ' -- Statistics information for layerThickness fields' + + ! Test seaIcePressure + fieldName = 'seaIcePressure' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test atmosphericPressure + fieldName = 'atmosphericPressure' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test rainFlux + fieldName = 'rainFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test evaporationFlux + fieldName = 'evaporationFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test snowFlow + fieldName = 'snowFlow' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test seaIceFreshWaterFlux + fieldName = 'seaIceFreshWaterFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test iceRunoffFlux + fieldName = 'iceRunoffFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test riverRunoffFlux + fieldName = 'riverRunoffFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + end if!}}} + + ! If there was a velocity NaN found, write out information about fields that affect velocity + if ( velNanFound ) then!{{{ + write(debugUnit, *) '' + write(debugUnit, *) '' + write(debugUnit, *) 'ERROR: NaN Detected in normalVelocity.' + write(debugUnit, *) ' -- Statistics information for normalVelocity fields' + + ! Test seaIcePressure + fieldName = 'seaIcePressure' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test atmosphericPressure + fieldName = 'atmosphericPressure' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test windStressZonal + fieldName = 'windStressZonal' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test windStressMeridional + fieldName = 'windStressMeridional' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test surfaceStressMagnitude + fieldName = 'surfaceStressMagnitude' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test surfaceStress + fieldName = 'surfaceStress' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iEdge = 1, nEdgesSolve + minValue = min( minValue, real1DArr(iEdge) ) + maxValue = max( maxValue, real1DArr(iEdge) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test angleEdge + fieldName = 'angleEdge' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(meshPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iEdge = 1, nEdgesSolve + minValue = min( minValue, real1DArr(iEdge) ) + maxValue = max( maxValue, real1DArr(iEdge) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + end if!}}} + + ! If there was a tracers NaN found, write out information about fields that affect tracers + if ( tracersNanFound ) then!{{{ + write(debugUnit, *) '' + write(debugUnit, *) '' + write(debugUnit, *) 'ERROR: NaN Detected in activeTracers.' + write(debugUnit, *) ' -- Statistics information for activeTracers fields' + + ! Test latentHeatFlux + fieldName = 'latentHeatFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test sensibleHeatFlux + fieldName = 'sensibleHeatFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test longWaveHeatFluxUp + fieldName = 'longWaveHeatFluxUp' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test longWaveHeatFluxDown + fieldName = 'longWaveHeatFluxDown' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test seaIceHeatFlux + fieldName = 'seaIceHeatFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test seaIceFreshWaterFlux + fieldName = 'snowFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + + ! Test rainFlux + fieldName = 'rainFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test snowFlux + fieldName = 'snowFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test iceRunoffFlux + fieldName = 'iceRunoffFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test riverRunoffFlux + fieldName = 'riverRunoffFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test seaIceSalinityFlux + fieldName = 'seaIceSalinityFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + + ! Test shortWaveHeatFlux + fieldName = 'shortWaveHeatFlux' + minValue = HUGE(minValue) + maxValue = -HUGE(maxValue) + call mpas_pool_get_array(forcingPool, fieldName, real1DArr) + if ( associated(real1DArr) ) then + do iCell = 1, nCellsSolve + minValue = min( minValue, real1DArr(iCell) ) + maxValue = max( maxValue, real1DArr(iCell) ) + end do + call ocn_write_field_statistics(debugUnit, fieldName, minValue, maxValue) + end if + end if!}}} + + ! If an error was found, we need to close the file now. + if ( thickNanFound .or. tracersNanFound .or. velNanFound ) then!{{{ + flush(debugUnit) + close(debugUnit) + fatalErrorDetected = .true. + end if!}}} + + block => block % next + end do + + call mpas_release_unit(debugUnit) + + if ( fatalErrorDetected ) then + call mpas_log_write( & + 'ERROR: State validation failed. See block stats files for more information.', & + MPAS_LOG_CRIT) + end if + + end subroutine ocn_validate_state!}}} + + function ocn_build_log_filename(prefix, identifier) result(filename)!{{{ + character (len=*), intent(in) :: prefix + integer, intent(in) :: identifier + + character (len=StrKIND) :: filename + + character (len=StrKIND) :: identifierString + + if ( identifier .lt. 10 ) then + write(identifierString, '(I1)') identifier + else if ( identifier .lt. 100 ) then + write(identifierString, '(I2)') identifier + else if ( identifier .lt. 1000 ) then + write(identifierString, '(I3)') identifier + else if ( identifier .lt. 10000 ) then + write(identifierString, '(I4)') identifier + else if ( identifier .lt. 100000 ) then + write(identifierString, '(I5)') identifier + else if ( identifier .lt. 1000000 ) then + write(identifierString, '(I6)') identifier + else if ( identifier .lt. 10000000 ) then + write(identifierString, '(I7)') identifier + else + write(identifierString, '(I99)') identifier + end if + + filename = trim(prefix) // trim(identifierString) + + end function ocn_build_log_filename!}}} + + subroutine ocn_write_field_statistics(unitNumber, fieldName, minValue, maxValue)!{{{ + integer, intent(in) :: unitNumber + character (len=*), intent(in) :: fieldName + real (kind=RKIND), intent(in) :: minValue, maxValue + + write(unitNumber, *) ' Field: ', trim(fieldName) + write(unitNumber, *) ' Min: ', minValue + write(unitNumber, *) ' Max: ', maxValue + + end subroutine ocn_write_field_statistics!}}} + !*********************************************************************** end module ocn_diagnostics diff --git a/src/core_ocean/shared/mpas_ocn_diagnostics_routines.F b/src/core_ocean/shared/mpas_ocn_diagnostics_routines.F index 02bc956bcf..bbf502c935 100644 --- a/src/core_ocean/shared/mpas_ocn_diagnostics_routines.F +++ b/src/core_ocean/shared/mpas_ocn_diagnostics_routines.F @@ -59,7 +59,7 @@ module ocn_diagnostics_routines !> \brief Computes relative vorticity and circulation !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date November 2013 -!> \details +!> \details !> Computes relative vorticity and circulation ! !----------------------------------------------------------------------- @@ -85,10 +85,10 @@ subroutine ocn_relativeVorticity_circulation(relativeVorticity, circulation, mes !----------------------------------------------------------------- real (kind=RKIND), dimension(:,:), intent(out) :: & - relativeVorticity + relativeVorticity real (kind=RKIND), dimension(:,:), intent(out) :: & - circulation + circulation integer, intent(out) :: err !< Output: error flag @@ -120,21 +120,22 @@ subroutine ocn_relativeVorticity_circulation(relativeVorticity, circulation, mes err = 0 - circulation(:,:) = 0.0 - relativeVorticity(:,:) = 0.0 + !$omp do schedule(runtime) private(invAreaTri1, i, iEdge, k, r_tmp) do iVertex = 1, nVertices - invAreaTri1 = 1.0 / areaTriangle(iVertex) + circulation(:, iVertex) = 0.0_RKIND + relativeVorticity(:, iVertex) = 0.0_RKIND + invAreaTri1 = 1.0_RKIND / areaTriangle(iVertex) do i = 1, vertexDegree iEdge = edgesOnVertex(i, iVertex) do k = 1, maxLevelVertexBot(iVertex) r_tmp = dcEdge(iEdge) * normalVelocity(k, iEdge) - circulation(k, iVertex) = circulation(k, iVertex) + edgeSignOnVertex(i, iVertex) * r_tmp + circulation(k, iVertex) = circulation(k, iVertex) + edgeSignOnVertex(i, iVertex) * r_tmp relativeVorticity(k, iVertex) = relativeVorticity(k, iVertex) + edgeSignOnVertex(i, iVertex) * r_tmp * invAreaTri1 end do end do end do - + !$omp end do !-------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_effective_density_in_land_ice.F b/src/core_ocean/shared/mpas_ocn_effective_density_in_land_ice.F new file mode 100644 index 0000000000..6bdef68a5a --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_effective_density_in_land_ice.F @@ -0,0 +1,188 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_effective_density_in_land_ice +! +!> \brief MPAS ocean effective density in land ice +!> \author Xylar Asay-Davis +!> \date 10/03/2015 +!> \details +!> This module contains routines for computing the effective seawater +!> density in land ice using Arhimedes' principle. +! +!----------------------------------------------------------------------- + +module ocn_effective_density_in_land_ice + + use mpas_constants + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_effective_density_in_land_ice_update + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_effective_density_in_land_ice_update +! +!> \brief updates effective density in land ice +!> \author Xylar Asay-Davis +!> \date 10/03/2015 +!> \details +!> This routine updates the value of the effective seawater density +!> displaced by land ice, based on Archimedes' principle. The effective +!> density is smoothed and extrapolated by averaging with nearest neighbors +!> (cellsOnCell). +! +!----------------------------------------------------------------------- + + subroutine ocn_effective_density_in_land_ice_update(meshPool, forcingPool, statePool, scratchPool, ierr)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + type (mpas_pool_type), intent(in) :: forcingPool !< Input: Forcing information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: statePool !< Input/Output: state information + type (mpas_pool_type), intent(inout) :: scratchPool !< Input/Output: scratch information + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: ierr !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + character (len=StrKIND), pointer :: config_land_ice_flux_mode + + real (kind=RKIND), dimension(:), pointer :: landIcePressure, & + ssh, & + effectiveDensityCur, & + effectiveDensityNew, & + effectiveDensityScratch + + integer, dimension(:), pointer :: landIceMask + + type (field1DReal), pointer :: effectiveDensityField + + real (kind=RKIND) :: weightSum + + integer :: iCell, cell2, i + integer, pointer :: nCells, nEdges + + integer, dimension(:,:), pointer :: cellsOnCell, cellMask + + integer, dimension(:), pointer :: nEdgesOnCell + + ierr = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + if ( (trim(config_land_ice_flux_mode) .ne. 'coupled') ) then + return + end if + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'cellMask', cellMask) + + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) + call mpas_pool_get_array(statePool, 'ssh', ssh, 2) + call mpas_pool_get_array(statePool, 'effectiveDensityInLandIce', effectiveDensityCur, 1) + call mpas_pool_get_array(statePool, 'effectiveDensityInLandIce', effectiveDensityNew, 2) + + call mpas_pool_get_field(scratchPool, 'effectiveDensityScratch', effectiveDensityField) + call mpas_allocate_scratch_field(effectiveDensityField, .true.) + effectiveDensityScratch => effectiveDensityField % array + + !$omp do schedule(runtime) + do iCell = 1, nCells + ! TODO: should only apply to floating land ice, once wetting/drying is supported + if(landIceMask(iCell) == 1) then + ! land ice is present to update the effective density + effectiveDensityScratch(iCell) = -landIcePressure(iCell)/(ssh(iCell)*gravity) + else + ! we copy the previous effective density + effectiveDensityScratch(iCell) = effectiveDensityCur(iCell) + end if + end do + !$omp end do + + !$omp do schedule(runtime) private(weightSum, i, cell2) + do iCell = 1, nCells + ! smooth/extrapolate by averaging with nearest neighbors + weightSum = 1.0_RKIND + effectiveDensityNew(iCell) = effectiveDensityScratch(iCell) + do i = 1, nEdgesOnCell(iCell) + cell2 = cellsOnCell(i,iCell) + effectiveDensityNew(iCell) = effectiveDensityNew(iCell) & + + cellMask(1,cell2)*effectiveDensityScratch(cell2) + weightSum = weightSum + cellMask(1,cell2) + end do + effectiveDensityNew(iCell) = effectiveDensityNew(iCell)/weightSum + end do + !$omp end do + call mpas_deallocate_scratch_field(effectiveDensityField, .true.) + + !-------------------------------------------------------------------- + + end subroutine ocn_effective_density_in_land_ice_update !}}} + +!*********************************************************************** + +end module ocn_effective_density_in_land_ice + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_equation_of_state.F b/src/core_ocean/shared/mpas_ocn_equation_of_state.F index 0cb59c787d..1a422b95d7 100644 --- a/src/core_ocean/shared/mpas_ocn_equation_of_state.F +++ b/src/core_ocean/shared/mpas_ocn_equation_of_state.F @@ -20,6 +20,7 @@ module ocn_equation_of_state + use mpas_timer use mpas_kind_types use mpas_derived_types use mpas_pool_routines @@ -45,7 +46,9 @@ module ocn_equation_of_state !-------------------------------------------------------------------- public :: ocn_equation_of_state_density, & - ocn_equation_of_state_init + ocn_equation_of_state_init, & + ocn_freezing_temperature, & + ocn_freezing_temperature_salinity_deriv !-------------------------------------------------------------------- ! @@ -67,26 +70,27 @@ module ocn_equation_of_state !> \brief Calls equation of state !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine calls the equation of state to update the density ! !----------------------------------------------------------------------- - subroutine ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, k_displaced, displacement_type, density, err, & - thermalExpansionCoeff, salineContractionCoeff, timeLevelIn)!{{{ + subroutine ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, nCells, k_displaced, & !{{{ + displacement_type, density, err, thermalExpansionCoeff, & + salineContractionCoeff, timeLevelIn) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! This module contains routines necessary for computing the density ! from model temperature and salinity using an equation of state. ! ! Input: mesh - mesh metadata - ! s - state: tracers - ! k_displaced + ! s - state: activeTracers + ! k_displaced ! - ! If k_displaced==0, density is returned with no displacement + ! If k_displaced==0, density is returned with no displacement ! ! If k_displaced~=0, density is returned, and is for - ! a parcel adiabatically displaced from its original level to level - ! k_displaced. When using the linear EOS, state % displacedDensity is + ! a parcel adiabatically displaced from its original level to level + ! k_displaced. When using the linear EOS, state % displacedDensity is ! still filled, but depth (i.e. pressure) does not modify the output. ! ! Output: s - state: computed density @@ -96,7 +100,10 @@ subroutine ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, k type (mpas_pool_type), intent(in) :: statePool type (mpas_pool_type), intent(inout) :: diagnosticsPool type (mpas_pool_type), intent(in) :: meshPool + type (mpas_pool_type), intent(in) :: scratchPool !< Input/Output: Scratch structure + integer, intent(in) :: nCells integer, intent(in), optional :: timeLevelIn + type (mpas_pool_type), pointer :: tracersPool integer :: k_displaced character(len=*), intent(in) :: displacement_type real (kind=RKIND), dimension(:,:), intent(out) :: density @@ -107,7 +114,7 @@ subroutine ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, k integer, dimension(:), pointer :: maxLevelCell real (kind=RKIND), dimension(:,:), pointer :: tracersSurfaceValue - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers integer :: iCell, k integer, pointer :: indexT, indexS type (dm_info) :: dminfo @@ -115,6 +122,8 @@ subroutine ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, k err = 0 + call mpas_timer_start("equation of state") + if (present(timeLevelIn)) then timeLevel = timeLevelIn else @@ -122,22 +131,27 @@ subroutine ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, k end if call mpas_pool_get_array(diagnosticsPool, 'tracersSurfaceValue', tracersSurfaceValue) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexT) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexS) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexT) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexS) if (linearEos) then - call ocn_equation_of_state_linear_density(meshPool, k_displaced, displacement_type, indexT, indexS, tracers, density, err, & - tracersSurfaceValue, thermalExpansionCoeff, salineContractionCoeff) + call ocn_equation_of_state_linear_density(meshPool, nCells, k_displaced, displacement_type, indexT, indexS, & + activeTracers, density, err, tracersSurfaceValue, & + thermalExpansionCoeff, salineContractionCoeff) elseif (jmEos) then - call ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_type, indexT, indexS, tracers, density, err, & - tracersSurfaceValue, thermalExpansionCoeff, salineContractionCoeff) + call ocn_equation_of_state_jm_density(meshPool, scratchPool, nCells, k_displaced, displacement_type, indexT, indexS, & + activeTracers, density, err, tracersSurfaceValue, thermalExpansionCoeff, & + salineContractionCoeff) endif + call mpas_timer_stop("equation of state") + end subroutine ocn_equation_of_state_density!}}} !*********************************************************************** @@ -147,11 +161,11 @@ end subroutine ocn_equation_of_state_density!}}} !> \brief Initializes ocean momentum horizontal mixing quantities !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> horizontal velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> horizontal velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !---------------------------------------------------------------------- @@ -191,6 +205,114 @@ subroutine ocn_equation_of_state_init(err)!{{{ end subroutine ocn_equation_of_state_init!}}} + +!*********************************************************************** +! +! function ocn_freezing_temperature +! +!> \brief Computes the freezing temperature of the ocean. +!> \author Xylar Asay-Davis +!> \date 11/16/2016 +!> \details +!> This routine computes the freezing temperature of the ocean at a given +!> salinity and pressure. Different coefficients are used in the open ocean +!> (and under sea ice) than in land-ice cavities. +! +!----------------------------------------------------------------------- + real (kind=RKIND) function ocn_freezing_temperature(salinity, pressure, inLandIceCavity)!{{{ + real (kind=RKIND), intent(in) :: salinity !< Input: Salinity value of water for freezing temperature + real (kind=RKIND), intent(in) :: pressure !< Input: Pressure value for freezing temperature + logical, intent(in) :: inLandIceCavity !< Input: flag indicating if the freezing temperature is computed + ! in land ice cavities or in open ocean + + real (kind=RKIND), pointer :: coeff_0 + real (kind=RKIND), pointer :: coeff_S + real (kind=RKIND), pointer :: coeff_p + real (kind=RKIND), pointer :: coeff_pS + real (kind=RKIND), pointer :: reference_pressure + real (kind=RKIND) :: pressureOffset + + if(inLandIceCavity) then + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_coeff_0', & + coeff_0) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_coeff_S', & + coeff_S) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_coeff_p', & + coeff_p) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_coeff_pS', & + coeff_pS) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_reference_pressure', & + reference_pressure) + else + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_coeff_0', & + coeff_0) + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_coeff_S', & + coeff_S) + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_coeff_p', & + coeff_p) + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_coeff_pS', & + coeff_pS) + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_reference_pressure', & + reference_pressure) + end if + + + pressureOffset = max(pressure - reference_pressure, 0.0_RKIND) + + ocn_freezing_temperature = coeff_0 & + + coeff_S * salinity & + + coeff_p * pressureOffset & + + coeff_pS * pressureOffset * salinity + + end function ocn_freezing_temperature!}}} + +!*********************************************************************** +! +! function ocn_freezing_temperature_salinity_deriv +! +!> \brief Computes the freezing-temperature salinity derivative +!> \author Xylar Asay-Davis +!> \date 11/16/2016 +!> \details +!> This routine computes the derivative of the freezing temperature of the ocean with +!> respect to salinity at a given salinity and pressure. Different coefficients are +!> used in the open ocean (and under sea ice) than in land-ice cavities. +! +!----------------------------------------------------------------------- + real (kind=RKIND) function ocn_freezing_temperature_salinity_deriv(salinity, pressure, inLandIceCavity)!{{{ + real (kind=RKIND), intent(in) :: salinity !< Input: Salinity value of water for freezing temperature + real (kind=RKIND), intent(in) :: pressure !< Input: Pressure value for freezing temperature + logical, intent(in) :: inLandIceCavity !< Input: flag indicating if the freezing temperature is computed + ! in land ice cavities or in open ocean + + real (kind=RKIND), pointer :: coeff_S + real (kind=RKIND), pointer :: coeff_pS + real (kind=RKIND), pointer :: reference_pressure + real (kind=RKIND) :: pressureOffset + + if(inLandIceCavity) then + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_coeff_S', & + coeff_S) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_coeff_pS', & + coeff_pS) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_cavity_freezing_temperature_reference_pressure', & + reference_pressure) + else + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_coeff_S', & + coeff_S) + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_coeff_pS', & + coeff_pS) + call mpas_pool_get_config(ocnConfigs, 'config_open_ocean_freezing_temperature_reference_pressure', & + reference_pressure) + end if + + + pressureOffset = max(pressure - reference_pressure, 0.0_RKIND) + + ocn_freezing_temperature_salinity_deriv = coeff_S + coeff_pS * pressureOffset + + end function ocn_freezing_temperature_salinity_deriv!}}} + !*********************************************************************** end module ocn_equation_of_state diff --git a/src/core_ocean/shared/mpas_ocn_equation_of_state_jm.F b/src/core_ocean/shared/mpas_ocn_equation_of_state_jm.F index 26d64f688f..ae0cfc134f 100644 --- a/src/core_ocean/shared/mpas_ocn_equation_of_state_jm.F +++ b/src/core_ocean/shared/mpas_ocn_equation_of_state_jm.F @@ -63,24 +63,24 @@ module ocn_equation_of_state_jm !> \brief Calls JM equation of state !> \author Mark Petersen and Todd Ringler !> \date September 2011, updated August 2013 -!> \details -!> This routine uses a JM equation of state to update the density. +!> \details +!> This routine uses a JM equation of state to update the density. !> -!> Density can be computed in-situ using k_displaced=0 and +!> Density can be computed in-situ using k_displaced=0 and !> displacement_type = 'relative'. !> -!> Potential density (referenced to top layer) can be computed +!> Potential density (referenced to top layer) can be computed !> using k_displaced=1 and displacement_type = 'absolute'. !> -!> The density of SST/SSS after adiabatic displacement to each layer +!> The density of SST/SSS after adiabatic displacement to each layer !> can be computed using displacement_type = 'surfaceDisplaced'. !> -!> When using displacement_type = 'surfaceDisplaced', k_displaced is +!> When using displacement_type = 'surfaceDisplaced', k_displaced is !> ignored and tracersSurfaceLayerValue must be present. ! !----------------------------------------------------------------------- - subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_type, & + subroutine ocn_equation_of_state_jm_density(meshPool, scratchPool, nCells, k_displaced, displacement_type, & indexT, indexS, tracers, density, err, & tracersSurfaceLayerValue, thermalExpansionCoeff, salineContractionCoeff)!{{{ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -93,11 +93,11 @@ subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_ ! ! Input: mesh - mesh metadata ! s - state: tracers - ! k_displaced + ! k_displaced ! If k_displaced=0, density is returned with no displacement ! If k_displaced>0,the density returned is that for a parcel - ! adiabatically displaced from its original level to level + ! adiabatically displaced from its original level to level ! k_displaced. ! @@ -107,6 +107,8 @@ subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_ implicit none type (mpas_pool_type), intent(in) :: meshPool + type (mpas_pool_type), intent(in) :: scratchPool !< Input/Output: Scratch structure + integer, intent(in) :: nCells integer, intent(in) :: k_displaced, indexT, indexS character(len=*), intent(in) :: displacement_type real (kind=RKIND), dimension(:,:,:), intent(in) :: tracers @@ -117,13 +119,12 @@ subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_ thermalExpansionCoeff, &! Thermal expansion coefficient (alpha), defined as $-1/\rho d\rho/dT$ (note negative sign) salineContractionCoeff ! Saline contraction coefficient (beta), defined as $1/\rho d\rho/dS$ - type (dm_info) :: dminfo integer :: iEdge, iCell, iVertex, k, k_displaced_local - integer, pointer :: nCells, nEdges, nVertices, nVertLevels + integer, pointer :: nVertices, nVertLevels integer, dimension(:), pointer :: maxLevelCell character(len=60) :: displacement_type_local - real (kind=RKIND) :: & + real (kind=RKIND) :: & depth, & DRDT0, &! d(density)/d(temperature), for surface DRDS0, &! d(density)/d(salinity ), for surface @@ -137,14 +138,14 @@ subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_ refBottomDepth, pRefEOS real (kind=RKIND), dimension(:), allocatable :: & p, p2 ! temporary pressure scalars - real (kind=RKIND), dimension(:,:), allocatable :: & + real (kind=RKIND), dimension(:), pointer :: & TQ,SQ, &! adjusted T,S BULK_MOD, &! Bulk modulus SQR,DENOMK, &! work arrays RHO_S, &! density at the surface WORK1, WORK2, WORK3, WORK4, T2 - real (kind=RKIND), dimension(:,:,:), allocatable :: & - tracerTS + real (kind=RKIND), dimension(:), allocatable :: & + tracerTemp, tracerSalt !----------------------------------------------------------------------- ! @@ -155,125 +156,106 @@ subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_ !*** for density of fresh water (standard UNESCO) real (kind=RKIND), parameter :: & - unt0 = 999.842594, & - unt1 = 6.793952e-2, & - unt2 = -9.095290e-3, & - unt3 = 1.001685e-4, & - unt4 = -1.120083e-6, & - unt5 = 6.536332e-9 - + unt0 = 999.842594_RKIND, & + unt1 = 6.793952e-2_RKIND, & + unt2 = -9.095290e-3_RKIND, & + unt3 = 1.001685e-4_RKIND, & + unt4 = -1.120083e-6_RKIND, & + unt5 = 6.536332e-9_RKIND + !*** for dependence of surface density on salinity (UNESCO) real (kind=RKIND), parameter :: & - uns1t0 = 0.824493 , & - uns1t1 = -4.0899e-3, & - uns1t2 = 7.6438e-5, & - uns1t3 = -8.2467e-7, & - uns1t4 = 5.3875e-9, & - unsqt0 = -5.72466e-3, & - unsqt1 = 1.0227e-4, & - unsqt2 = -1.6546e-6, & - uns2t0 = 4.8314e-4 - + uns1t0 = 0.824493_RKIND , & + uns1t1 = -4.0899e-3_RKIND, & + uns1t2 = 7.6438e-5_RKIND, & + uns1t3 = -8.2467e-7_RKIND, & + uns1t4 = 5.3875e-9_RKIND, & + unsqt0 = -5.72466e-3_RKIND, & + unsqt1 = 1.0227e-4_RKIND, & + unsqt2 = -1.6546e-6_RKIND, & + uns2t0 = 4.8314e-4_RKIND + !*** from Table A1 of Jackett and McDougall - + real (kind=RKIND), parameter :: & - bup0s0t0 = 1.965933e+4, & - bup0s0t1 = 1.444304e+2, & - bup0s0t2 = -1.706103 , & - bup0s0t3 = 9.648704e-3, & - bup0s0t4 = -4.190253e-5 - + bup0s0t0 = 1.965933e+4_RKIND, & + bup0s0t1 = 1.444304e+2_RKIND, & + bup0s0t2 = -1.706103_RKIND , & + bup0s0t3 = 9.648704e-3_RKIND, & + bup0s0t4 = -4.190253e-5_RKIND + real (kind=RKIND), parameter :: & - bup0s1t0 = 5.284855e+1, & - bup0s1t1 = -3.101089e-1, & - bup0s1t2 = 6.283263e-3, & - bup0s1t3 = -5.084188e-5 - + bup0s1t0 = 5.284855e+1_RKIND, & + bup0s1t1 = -3.101089e-1_RKIND, & + bup0s1t2 = 6.283263e-3_RKIND, & + bup0s1t3 = -5.084188e-5_RKIND + real (kind=RKIND), parameter :: & - bup0sqt0 = 3.886640e-1, & - bup0sqt1 = 9.085835e-3, & - bup0sqt2 = -4.619924e-4 - + bup0sqt0 = 3.886640e-1_RKIND, & + bup0sqt1 = 9.085835e-3_RKIND, & + bup0sqt2 = -4.619924e-4_RKIND + real (kind=RKIND), parameter :: & - bup1s0t0 = 3.186519 , & - bup1s0t1 = 2.212276e-2, & - bup1s0t2 = -2.984642e-4, & - bup1s0t3 = 1.956415e-6 - + bup1s0t0 = 3.186519_RKIND , & + bup1s0t1 = 2.212276e-2_RKIND, & + bup1s0t2 = -2.984642e-4_RKIND, & + bup1s0t3 = 1.956415e-6_RKIND + real (kind=RKIND), parameter :: & - bup1s1t0 = 6.704388e-3, & - bup1s1t1 = -1.847318e-4, & - bup1s1t2 = 2.059331e-7, & - bup1sqt0 = 1.480266e-4 - + bup1s1t0 = 6.704388e-3_RKIND, & + bup1s1t1 = -1.847318e-4_RKIND, & + bup1s1t2 = 2.059331e-7_RKIND, & + bup1sqt0 = 1.480266e-4_RKIND + real (kind=RKIND), parameter :: & - bup2s0t0 = 2.102898e-4, & - bup2s0t1 = -1.202016e-5, & - bup2s0t2 = 1.394680e-7, & - bup2s1t0 = -2.040237e-6, & - bup2s1t1 = 6.128773e-8, & - bup2s1t2 = 6.207323e-10 - + bup2s0t0 = 2.102898e-4_RKIND, & + bup2s0t1 = -1.202016e-5_RKIND, & + bup2s0t2 = 1.394680e-7_RKIND, & + bup2s1t0 = -2.040237e-6_RKIND, & + bup2s1t1 = 6.128773e-8_RKIND, & + bup2s1t2 = 6.207323e-10_RKIND + integer :: k_test, k_ref - + err = 0 - + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - -! allocate local T,S tracer field - allocate(tracerTS(2,nVertLevels,nCells+1)) -! fill tracerTS - if (displacement_type == 'surfaceDisplaced') then - if(present(tracersSurfaceLayerValue)) then - do k=1,nVertLevels - tracerTS(1,k,:) = tracersSurfaceLayerValue(indexT,:) - tracerTS(2,k,:) = tracersSurfaceLayerValue(indexS,:) - enddo - displacement_type_local = 'relative' - k_displaced_local = 0 - else - call mpas_log_write('Abort: tracersSurfaceLayerValue must be present', MPAS_LOG_CRIT) - endif - else - tracerTS(1,:,:) = tracers(indexT,:,:) - tracerTS(2,:,:) = tracers(indexS,:,:) - displacement_type_local = trim(displacement_type) - k_displaced_local = k_displaced - endif + allocate(tracerTemp(nVertLevels)) + allocate(tracerSalt(nVertLevels)) ! Jackett and McDougall - tmin = -2.0 ! valid pot. temp. range - tmax = 40.0 - smin = 0.0 ! valid salinity, in psu - smax = 42.0 + tmin = -2.0_RKIND ! valid pot. temp. range + tmax = 40.0_RKIND + smin = 0.0_RKIND ! valid salinity, in psu + smax = 42.0_RKIND ! This function computes pressure in bars from depth in meters -! using a mean density derived from depth-dependent global -! average temperatures and salinities from Levitus 1994, and +! using a mean density derived from depth-dependent global +! average temperatures and salinities from Levitus 1994, and ! integrating using hydrostatic balance. allocate(pRefEOS(nVertLevels),p(nVertLevels),p2(nVertLevels)) - allocate(TQ(nVertLevels,nCells),SQ(nVertLevels,nCells),BULK_MOD(nVertLevels,nCells),SQR(nVertLevels,nCells),DENOMK(nVertLevels,nCells), RHO_S(nVertLevels,nCells), & - WORK1(nVertLevels,nCells), WORK2(nVertLevels,nCells), WORK3(nVertLevels,nCells), WORK4(nVertLevels,nCells), T2(nVertLevels,nCells)) + allocate(SQ(nVertLevels), TQ(nVertLevels), SQR(nVertLevels), T2(nVertLevels), WORK1(nVertLevels), & + WORK2(nVertLevels), RHO_S(nVertLevels), WORK3(nVertLevels), WORK4(nVertLevels), & + BULK_MOD(nVertLevels), DENOMK(nVertLevels)) ! This could be put in the init routine. ! Note I am using refBottomDepth, so pressure on top level does ! not include SSH contribution. I am not sure if that matters, but ! POP does it the same way. - depth = 0.5*refBottomDepth(1) - pRefEOS(1) = 0.059808*(exp(-0.025*depth) - 1.0) & - + 0.100766*depth + 2.28405e-7*depth**2 + depth = 0.5_RKIND*refBottomDepth(1) + pRefEOS(1) = 0.059808_RKIND*(exp(-0.025_RKIND*depth) - 1.0_RKIND) & + + 0.100766_RKIND*depth + 2.28405e-7_RKIND*depth**2 do k = 2,nVertLevels - depth = 0.5*(refBottomDepth(k)+refBottomDepth(k-1)) - pRefEOS(k) = 0.059808*(exp(-0.025*depth) - 1.0) & - + 0.100766*depth + 2.28405e-7*depth**2 + depth = 0.5_RKIND*(refBottomDepth(k)+refBottomDepth(k-1)) + pRefEOS(k) = 0.059808_RKIND*(exp(-0.025_RKIND*depth) - 1.0_RKIND) & + + 0.100766_RKIND*depth + 2.28405e-7_RKIND*depth**2 enddo ! If k_displaced=0, in-situ density is returned (no displacement) @@ -285,6 +267,20 @@ subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_ ! referenced to level k_displaced for all k ! NOTE: k_displaced = 0 or > nVertLevels is incompatible with 'absolute' ! so abort if necessary + if (displacement_type == 'surfaceDisplaced') then + if(present(tracersSurfaceLayerValue)) then + displacement_type_local = 'relative' + k_displaced_local = 0 + else + call mpas_log_write( & + 'tracersSurfaceLayerValue must be present when displacement_type is ' & + // '''surfaceDisplaced'' in JM EOS', & + MPAS_LOG_CRIT) + endif + else + displacement_type_local = trim(displacement_type) + k_displaced_local = k_displaced + endif if (displacement_type_local == 'absolute' .and. & (k_displaced_local <= 0 .or. k_displaced_local > nVertLevels) ) then @@ -313,97 +309,124 @@ subroutine ocn_equation_of_state_jm_density(meshPool, k_displaced, displacement_ enddo endif + !$omp do schedule(runtime) private(k, DRDT0, DKDT, DRHODT, DRDS0, DKDS, DRHODS) do iCell=1,nCells + if (displacement_type == 'surfaceDisplaced') then + if(present(tracersSurfaceLayerValue)) then + do k=1,nVertLevels + tracerTemp(k) = tracersSurfaceLayerValue(indexT,iCell) + tracerSalt(k) = tracersSurfaceLayerValue(indexS,iCell) + enddo + else + call mpas_log_write( & + 'tracersSurfaceLayerValue must be present in JM EOS call', & + MPAS_LOG_CRIT) + endif + else + do k = 1, nVertLevels + tracerTemp(k) = tracers(indexT, k, iCell) + tracerSalt(k) = tracers(indexS, k, iCell) + end do + endif + do k=1,maxLevelCell(iCell) - SQ(k,iCell) = max(min(tracerTS(2,k,iCell),smax),smin) - TQ(k,iCell) = max(min(tracerTS(1,k,iCell),tmax),tmin) - - SQR(k,iCell) = sqrt(SQ(k,iCell)) - T2(k,iCell) = TQ(k,iCell)*TQ(k,iCell) + SQ(k) = max(min(tracerSalt(k),smax),smin) + TQ(k) = max(min(tracerTemp(k),tmax),tmin) + + SQR(k) = sqrt(SQ(k)) + T2(k) = TQ(k)*TQ(k) !*** !*** first calculate surface (p=0) values from UNESCO eqns. !*** - WORK1(k,iCell) = uns1t0 + uns1t1*TQ(k,iCell) + & - (uns1t2 + uns1t3*TQ(k,iCell) + uns1t4*T2(k,iCell))*T2(k,iCell) - WORK2(k,iCell) = SQR(k,iCell)*(unsqt0 + unsqt1*TQ(k,iCell) + unsqt2*T2(k,iCell)) + WORK1(k) = uns1t0 + uns1t1*TQ(k) + & + (uns1t2 + uns1t3*TQ(k) + uns1t4*T2(k))*T2(k) + WORK2(k) = SQR(k)*(unsqt0 + unsqt1*TQ(k) + unsqt2*T2(k)) - RHO_S(k,iCell) = unt1*TQ(k,iCell) + (unt2 + unt3*TQ(k,iCell) + (unt4 + unt5*TQ(k,iCell))*T2(k,iCell))*T2(k,iCell) & - + (uns2t0*SQ(k,iCell) + WORK1(k,iCell) + WORK2(k,iCell))*SQ(k,iCell) + RHO_S(k) = unt1*TQ(k) + (unt2 + unt3*TQ(k) + (unt4 + unt5*TQ(k))*T2(k))*T2(k) & + + (uns2t0*SQ(k) + WORK1(k) + WORK2(k))*SQ(k) !*** - !*** now calculate bulk modulus at pressure p from + !*** now calculate bulk modulus at pressure p from !*** Jackett and McDougall formula !*** - WORK3(k,iCell) = bup0s1t0 + bup0s1t1*TQ(k,iCell) + & - (bup0s1t2 + bup0s1t3*TQ(k,iCell))*T2(k,iCell) + & - p(k) *(bup1s1t0 + bup1s1t1*TQ(k,iCell) + bup1s1t2*T2(k,iCell)) + & - p2(k)*(bup2s1t0 + bup2s1t1*TQ(k,iCell) + bup2s1t2*T2(k,iCell)) - WORK4(k,iCell) = SQR(k,iCell)*(bup0sqt0 + bup0sqt1*TQ(k,iCell) + bup0sqt2*T2(k,iCell) + & + WORK3(k) = bup0s1t0 + bup0s1t1*TQ(k) + & + (bup0s1t2 + bup0s1t3*TQ(k))*T2(k) + & + p(k) *(bup1s1t0 + bup1s1t1*TQ(k) + bup1s1t2*T2(k)) + & + p2(k)*(bup2s1t0 + bup2s1t1*TQ(k) + bup2s1t2*T2(k)) + WORK4(k) = SQR(k)*(bup0sqt0 + bup0sqt1*TQ(k) + bup0sqt2*T2(k) + & bup1sqt0*p(k)) - - BULK_MOD(k,iCell) = bup0s0t0 + bup0s0t1*TQ(k,iCell) + & - (bup0s0t2 + bup0s0t3*TQ(k,iCell) + bup0s0t4*T2(k,iCell))*T2(k,iCell) + & - p(k) *(bup1s0t0 + bup1s0t1*TQ(k,iCell) + & - (bup1s0t2 + bup1s0t3*TQ(k,iCell))*T2(k,iCell)) + & - p2(k)*(bup2s0t0 + bup2s0t1*TQ(k,iCell) + bup2s0t2*T2(k,iCell)) + & - SQ(k,iCell)*(WORK3(k,iCell) + WORK4(k,iCell)) - - DENOMK(k,iCell) = 1.0/(BULK_MOD(k,iCell) - p(k)) - - density(k,iCell) = (unt0 + RHO_S(k,iCell))*BULK_MOD(k,iCell)*DENOMK(k,iCell) - end do - end do + BULK_MOD(k) = bup0s0t0 + bup0s0t1*TQ(k) + & + (bup0s0t2 + bup0s0t3*TQ(k) + bup0s0t4*T2(k))*T2(k) + & + p(k) *(bup1s0t0 + bup1s0t1*TQ(k) + & + (bup1s0t2 + bup1s0t3*TQ(k))*T2(k)) + & + p2(k)*(bup2s0t0 + bup2s0t1*TQ(k) + bup2s0t2*T2(k)) + & + SQ(k)*(WORK3(k) + WORK4(k)) - if (present(thermalExpansionCoeff)) then - do iCell=1,nCells - do k=1,maxLevelCell(iCell) - DRDT0 = unt1 + 2.0*unt2*TQ(k,iCell) + & - (3.0*unt3 + 4.0*unt4*TQ(k,iCell) + 5.0*unt5*T2(k,iCell))*T2(k,iCell) + & - (uns1t1 + 2.0*uns1t2*TQ(k,iCell) + & - (3.0*uns1t3 + 4.0*uns1t4*TQ(k,iCell))*T2(k,iCell) + & - (unsqt1 + 2.0*unsqt2*TQ(k,iCell))*SQR(k,iCell) )*SQ(k,iCell) + DENOMK(k) = 1.0/(BULK_MOD(k) - p(k)) - DKDT = bup0s0t1 + 2.0*bup0s0t2*TQ(k,iCell) + & - (3.0*bup0s0t3 + 4.0*bup0s0t4*TQ(k,iCell))*T2(k,iCell) + & - p(k) *(bup1s0t1 + 2.0*bup1s0t2*TQ(k,iCell) + 3.0*bup1s0t3*T2(k,iCell)) + & - p2(k)*(bup2s0t1 + 2.0*bup2s0t2*TQ(k,iCell)) + & - SQ(k,iCell)*(bup0s1t1 + 2.0*bup0s1t2*TQ(k,iCell) + 3.0*bup0s1t3*T2(k,iCell) + & - p(k) *(bup1s1t1 + 2.0*bup1s1t2*TQ(k,iCell)) + & - p2(k) *(bup2s1t1 + 2.0*bup2s1t2*TQ(k,iCell)) + & - SQR(k,iCell)*(bup0sqt1 + 2.0*bup0sqt2*TQ(k,iCell))) + density(k, iCell) = (unt0 + RHO_S(k))*BULK_MOD(k)*DENOMK(k) - DRHODT = (DENOMK(k,iCell)*(DRDT0*BULK_MOD(k,iCell) - & - p(k)*(unt0+RHO_S(k,iCell))*DKDT*DENOMK(k,iCell))) + end do + if (present(thermalExpansionCoeff)) then + do k=1,maxLevelCell(iCell) + DRDT0 = unt1 + 2.0_RKIND*unt2*TQ(k) + & + (3.0_RKIND*unt3 + 4.0_RKIND*unt4*TQ(k) + 5.0_RKIND*unt5*T2(k))*T2(k) + & + (uns1t1 + 2.0_RKIND*uns1t2*TQ(k) + & + (3.0_RKIND*uns1t3 + 4.0_RKIND*uns1t4*TQ(k))*T2(k) + & + (unsqt1 + 2.0_RKIND*unsqt2*TQ(k))*SQR(k) )*SQ(k) + + DKDT = bup0s0t1 + 2.0_RKIND*bup0s0t2*TQ(k) + & + (3.0_RKIND*bup0s0t3 + 4.0_RKIND*bup0s0t4*TQ(k))*T2(k) + & + p(k) *(bup1s0t1 + 2.0_RKIND*bup1s0t2*TQ(k) + 3.0_RKIND*bup1s0t3*T2(k)) + & + p2(k)*(bup2s0t1 + 2.0_RKIND*bup2s0t2*TQ(k)) + & + SQ(k)*(bup0s1t1 + 2.0_RKIND*bup0s1t2*TQ(k) + 3.0_RKIND*bup0s1t3*T2(k) + & + p(k) *(bup1s1t1 + 2.0_RKIND*bup1s1t2*TQ(k)) + & + p2(k) *(bup2s1t1 + 2.0_RKIND*bup2s1t2*TQ(k)) + & + SQR(k)*(bup0sqt1 + 2.0_RKIND*bup0sqt2*TQ(k))) + + DRHODT = (DENOMK(k)*(DRDT0*BULK_MOD(k) - & + p(k)*(unt0+RHO_S(k))*DKDT*DENOMK(k))) thermalExpansionCoeff(k,iCell) = -DRHODT/density(k,iCell) - end do - end do - endif + end if - if (present(salineContractionCoeff)) then - do iCell=1,nCells + if (present(salineContractionCoeff)) then do k=1,maxLevelCell(iCell) - DRDS0 = 2.0*uns2t0*SQ(k,iCell) + WORK1(k,iCell) + 1.5*WORK2(k,iCell) - DKDS = WORK3(k,iCell) + 1.5*WORK4(k,iCell) + DRDS0 = 2.0_RKIND*uns2t0*SQ(k) + WORK1(k) + 1.5_RKIND*WORK2(k) + DKDS = WORK3(k) + 1.5_RKIND*WORK4(k) - DRHODS = DENOMK(k,iCell)*(DRDS0*BULK_MOD(k,iCell) - & - p(k)*(unt0+RHO_S(k,iCell))*DKDS*DENOMK(k,iCell)) + DRHODS = DENOMK(k)*(DRDS0*BULK_MOD(k) - & + p(k)*(unt0+RHO_S(k))*DKDS*DENOMK(k)) salineContractionCoeff(k,iCell) = DRHODS/density(k,iCell) end do - end do - endif + + end if + end do + !$omp end do deallocate(pRefEOS,p,p2) - deallocate(tracerTS) - deallocate(TQ,SQ,T2,BULK_MOD,SQR,DENOMK,RHO_S, WORK1, WORK2, WORK3, WORK4) + deallocate(tracerTemp) + deallocate(tracerSalt) + + deallocate(SQ) + deallocate(TQ) + deallocate(SQR) + deallocate(T2) + deallocate(WORK1) + deallocate(WORK2) + deallocate(RHO_S) + deallocate(WORK3) + deallocate(WORK4) + deallocate(BULK_MOD) + deallocate(DENOMK) end subroutine ocn_equation_of_state_jm_density!}}} @@ -414,11 +437,11 @@ end subroutine ocn_equation_of_state_jm_density!}}} !> \brief Initializes ocean momentum horizontal mixing quantities !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> horizontal velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> horizontal velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_equation_of_state_linear.F b/src/core_ocean/shared/mpas_ocn_equation_of_state_linear.F index f762a4941b..ffad329e9a 100644 --- a/src/core_ocean/shared/mpas_ocn_equation_of_state_linear.F +++ b/src/core_ocean/shared/mpas_ocn_equation_of_state_linear.F @@ -70,13 +70,14 @@ module ocn_equation_of_state_linear !> \brief Calls equation of state !> \author Mark Petersen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine uses a linear equation of state to update the density ! !----------------------------------------------------------------------- - subroutine ocn_equation_of_state_linear_density(meshPool, k_displaced, displacement_type, indexT, indexS, tracers, density, err, & - tracersSurfaceLayerValue, thermalExpansionCoeff, salineContractionCoeff)!{{{ + subroutine ocn_equation_of_state_linear_density(meshPool, nCells, k_displaced, displacement_type, & !{{{ + indexT, indexS, tracers, density, err, tracersSurfaceLayerValue, thermalExpansionCoeff, & + salineContractionCoeff) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! This module contains routines necessary for computing the density ! from model temperature and salinity using an equation of state. @@ -105,6 +106,7 @@ subroutine ocn_equation_of_state_linear_density(meshPool, k_displaced, displacem implicit none type (mpas_pool_type), intent(in) :: meshPool + integer, intent(in) :: nCells character(len=*), intent(in) :: displacement_type integer, intent(in) :: k_displaced, indexT, indexS real (kind=RKIND), dimension(:,:,:), intent(in) :: tracers @@ -117,12 +119,11 @@ subroutine ocn_equation_of_state_linear_density(meshPool, k_displaced, displacem integer, dimension(:), pointer :: maxLevelCell integer :: iCell, k, k_displaced_local, k_ref - integer, pointer :: nCells, nVertLevels + integer, pointer :: nVertLevels character(len=60) :: displacement_type_local type (dm_info) :: dminfo call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) err = 0 @@ -141,6 +142,7 @@ subroutine ocn_equation_of_state_linear_density(meshPool, k_displaced, displacem ! if surfaceDisplaced, then compute density at all levels based on surface values if (displacement_type_local == 'surfaceDisplaced') then + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) ! Linear equation of state @@ -149,12 +151,14 @@ subroutine ocn_equation_of_state_linear_density(meshPool, k_displaced, displacem + config_eos_linear_beta * (tracersSurfaceLayerValue(indexS,iCell) - config_eos_linear_Sref) end do end do + !$omp end do endif ! if absolute, then compute density at all levels based on pressure of k_displaced value ! but since linear EOS does not (at present) have a pressure dependency, this just returns density if (displacement_type_local == 'absolute') then + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) ! Linear equation of state @@ -163,11 +167,13 @@ subroutine ocn_equation_of_state_linear_density(meshPool, k_displaced, displacem + config_eos_linear_beta * (tracers(indexS,k,iCell) - config_eos_linear_Sref) end do end do + !$omp end do endif ! if relative, then compute density at all levels based on k+k_displaced pressure value ! but since (at present) linear EOS has not dependence on pressure, it returns density if (displacement_type_local == 'relative') then + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) ! Linear equation of state @@ -176,22 +182,27 @@ subroutine ocn_equation_of_state_linear_density(meshPool, k_displaced, displacem + config_eos_linear_beta * (tracers(indexS,k,iCell) - config_eos_linear_Sref) end do end do + !$omp end do endif if (present(thermalExpansionCoeff)) then + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) thermalExpansionCoeff(k,iCell) = config_eos_linear_alpha / density(k,iCell) end do end do + !$omp end do endif if (present(salineContractionCoeff)) then + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) salineContractionCoeff(k,iCell) = config_eos_linear_beta / density(k,iCell) end do end do + !$omp end do endif end subroutine ocn_equation_of_state_linear_density!}}} @@ -203,11 +214,11 @@ end subroutine ocn_equation_of_state_linear_density!}}} !> \brief Initializes ocean momentum horizontal mixing quantities !> \author Mark Petersen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> horizontal velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> horizontal velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_forcing.F b/src/core_ocean/shared/mpas_ocn_forcing.F index 179e1b3a15..bbed313398 100644 --- a/src/core_ocean/shared/mpas_ocn_forcing.F +++ b/src/core_ocean/shared/mpas_ocn_forcing.F @@ -25,14 +25,15 @@ module ocn_forcing use mpas_timekeeping use mpas_log use mpas_dmpar - use ocn_forcing_bulk - use ocn_forcing_restoring use ocn_constants implicit none private save + ! TRACER-CLEAN-UP + ! Need to figure out what to do with absorption coefficient computation. + !-------------------------------------------------------------------- ! ! Public parameters @@ -45,8 +46,7 @@ module ocn_forcing ! !-------------------------------------------------------------------- - public :: ocn_forcing_build_arrays, & - ocn_forcing_init, & + public :: ocn_forcing_init, & ocn_forcing_build_fraction_absorbed_array, & ocn_forcing_transmission @@ -56,103 +56,11 @@ module ocn_forcing ! !-------------------------------------------------------------------- - real (kind=RKIND) :: attenuationCoefficient - - logical :: restoringOn, bulkOn - !*********************************************************************** contains !*********************************************************************** -! -! routine ocn_forcing_build_arrays -! -!> \brief Determines the forcing arrays. -!> \author Doug Jacobsen -!> \date 12/13/12 -!> \details -!> This routine computes the forcing arrays used later in MPAS. -! -!----------------------------------------------------------------------- - - subroutine ocn_forcing_build_arrays(meshPool, statePool, forcingPool, err, timeLevelIn)!{{{ - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - statePool, & !< Input: State information - meshPool !< Input: mesh information - - integer, intent(in), optional :: timeLevelIn - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: Error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - integer :: timeLevel - integer, pointer :: indexTemperature, indexSalinity - integer, pointer :: indexSurfaceTemperatureFlux, indexSurfaceSalinityFlux - - real (kind=RKIND), dimension(:), pointer :: temperatureRestore, salinityRestore - real (kind=RKIND), dimension(:,:), pointer :: surfaceTracerFlux - real (kind=RKIND), dimension(:,:,:), pointer :: tracers - - if (present(timeLevelIn)) then - timeLevel = timeLevelIn - else - timeLevel = 1 - end if - - if ( bulkOn ) then - call ocn_forcing_bulk_build_arrays(meshPool, forcingPool, err) - end if - - if ( restoringOn ) then - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexSalinity) - - call mpas_pool_get_dimension(forcingPool, 'index_surfaceTemperatureFlux', indexSurfaceTemperatureFlux) - call mpas_pool_get_dimension(forcingPool, 'index_surfaceSalinityFlux', indexSurfaceSalinityFlux) - - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) - - call mpas_pool_get_array(meshPool, 'temperatureRestore', temperatureRestore) - call mpas_pool_get_array(meshPool, 'salinityRestore', salinityRestore) - - call mpas_pool_get_array(forcingPool, 'surfaceTracerFlux', surfaceTracerFlux) - - call ocn_forcing_restoring_build_arrays(meshPool, indexTemperature, indexSalinity, & - indexSurfaceTemperatureFlux, indexSurfaceSalinityFlux, & - tracers, temperatureRestore, salinityRestore, & - surfaceTracerFlux, err) - end if - - !-------------------------------------------------------------------- - - end subroutine ocn_forcing_build_arrays!}}} !*********************************************************************** ! @@ -161,7 +69,7 @@ end subroutine ocn_forcing_build_arrays!}}} !> \brief Initializes forcing module !> \author Doug Jacobsen !> \date 12/13/12 -!> \details +!> \details !> This routine initializes the forcing modules. ! !----------------------------------------------------------------------- @@ -170,36 +78,7 @@ subroutine ocn_forcing_init(err)!{{{ integer, intent(out) :: err !< Output: error flag - integer :: err1 - - character (len=StrKIND), pointer :: config_forcing_type - real (kind=RKIND), pointer :: config_flux_attenuation_coefficient - err = 0 - err1 = 0 - - call mpas_pool_get_config(ocnConfigs, 'config_flux_attenuation_coefficient', config_flux_attenuation_coefficient) - call mpas_pool_get_config(ocnConfigs, 'config_forcing_type', config_forcing_type) - - attenuationCoefficient = config_flux_attenuation_coefficient - - if ( config_forcing_type == trim('bulk') ) then - call ocn_forcing_bulk_init(err1) - bulkOn = .true. - restoringOn = .false. - else if ( config_forcing_type == trim('restoring') ) then - call ocn_forcing_restoring_init(err1) - restoringOn = .true. - bulkOn = .false. - else if ( config_forcing_type == trim('off') ) then - restoringOn = .false. - bulkOn = .false. - else - err = 1 - call mpas_log_write("ERROR: config_forcing_type not one of 'bulk', 'restoring', or 'off'.") - end if - - err = ior(err,err1) end subroutine ocn_forcing_init!}}} @@ -210,15 +89,16 @@ end subroutine ocn_forcing_init!}}} !> \brief fraction absorbed coefficient array for surface forcing. !> \author Doug Jacobsen !> \date 10/03/2013 -!> \details +!> \details !> This subroutine builds the fractionAbsorbed coefficient array for use in !> applying surface fluxes deeper than the surface layer. ! !----------------------------------------------------------------------- - subroutine ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, forcingPool, err, timeLevelIn)!{{{ + subroutine ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, diagnosticsPool, forcingPool, err, timeLevelIn)!{{{ type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information type (mpas_pool_type), intent(in) :: statePool !< Input: State information + type (mpas_pool_type), intent(in) :: diagnosticsPool !< Input: Diagnostics information type (mpas_pool_type), intent(inout) :: forcingPool !< Input/Output: Forcing information integer, intent(out) :: err !< Output: Error code integer, intent(in), optional :: timeLevelIn @@ -231,12 +111,13 @@ subroutine ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, forcin real (kind=RKIND) :: zTop, zBot, transmissionCoeffTop, transmissionCoeffBot - real (kind=RKIND), dimension(:,:), pointer :: layerThickness, fractionAbsorbed + real (kind=RKIND), dimension(:), pointer :: surfaceFluxAttenuationCoefficient + real (kind=RKIND), dimension(:), pointer :: surfaceFluxAttenuationCoefficientRunoff + real (kind=RKIND), dimension(:,:), pointer :: layerThickness, fractionAbsorbed, fractionAbsorbedRunoff - integer :: iCell, k, timeLevel - integer, pointer :: nCells + integer :: iCell, k, timeLevel, nCells - integer, dimension(:), pointer :: maxLevelCell + integer, dimension(:), pointer :: maxLevelCell, nCellsArray err = 0 @@ -246,20 +127,27 @@ subroutine ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, forcin timeLevel = 1 end if - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) + call mpas_pool_get_array(diagnosticsPool, 'surfaceFluxAttenuationCoefficient', surfaceFluxAttenuationCoefficient) + call mpas_pool_get_array(diagnosticsPool, 'surfaceFluxAttenuationCoefficientRunoff', & + surfaceFluxAttenuationCoefficientRunoff) + call mpas_pool_get_array(forcingPool, 'fractionAbsorbed', fractionAbsorbed) + call mpas_pool_get_array(forcingPool, 'fractionAbsorbedRunoff', fractionAbsorbedRunoff) + + nCells = nCellsArray( 2 ) do iCell = 1, nCells zTop = 0.0_RKIND - transmissionCoeffTop = ocn_forcing_transmission(zTop) + transmissionCoeffTop = ocn_forcing_transmission(zTop, surfaceFluxAttenuationCoefficient(iCell)) do k = 1, maxLevelCell(iCell) zBot = zTop - layerThickness(k,iCell) - transmissionCoeffBot = ocn_forcing_transmission(zBot) + transmissionCoeffBot = ocn_forcing_transmission(zBot, surfaceFluxAttenuationCoefficient(iCell)) fractionAbsorbed(k, iCell) = transmissionCoeffTop - transmissionCoeffBot @@ -268,6 +156,22 @@ subroutine ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, forcin end do end do +! now do river runoff separately + + do iCell = 1, nCells + zTop = 0.0_RKIND + transmissionCoeffTop = ocn_forcing_transmission(zTop, surfaceFluxAttenuationCoefficientRunoff(iCell)) + do k = 1, maxLevelCell(iCell) + zBot = zTop - layerThickness(k,iCell) + transmissionCoeffBot = ocn_forcing_transmission(zBot, surfaceFluxAttenuationCoefficientRunoff(iCell)) + + fractionAbsorbedRunoff(k, iCell) = transmissionCoeffTop - transmissionCoeffBot + + zTop = zBot + transmissionCoeffTop = transmissionCoeffBot + end do + end do + end subroutine ocn_forcing_build_fraction_absorbed_array!}}} !*********************************************************************** @@ -277,17 +181,17 @@ end subroutine ocn_forcing_build_fraction_absorbed_array!}}} !> \brief Transmission coefficient for surface forcing. !> \author Doug Jacobsen !> \date 05/03/2013 -!> \details +!> \details !> This function computes and returns the transmission coefficient for surface !> forcing based on depth. It uses an exponential decay function to determine the !> coefficients. ! !----------------------------------------------------------------------- - real (kind=RKIND) function ocn_forcing_transmission(z)!{{{ - real (kind=RKIND), intent(in) :: z + real (kind=RKIND) function ocn_forcing_transmission(z, attenuationCoefficient)!{{{ + real (kind=RKIND), intent(in) :: z, attenuationCoefficient - ocn_forcing_transmission = exp( z / attenuationCoefficient ) + ocn_forcing_transmission = exp( max(z / attenuationCoefficient, -100.0_RKIND) ) end function ocn_forcing_transmission!}}} diff --git a/src/core_ocean/shared/mpas_ocn_forcing_bulk.F b/src/core_ocean/shared/mpas_ocn_forcing_bulk.F deleted file mode 100644 index bce3cb0794..0000000000 --- a/src/core_ocean/shared/mpas_ocn_forcing_bulk.F +++ /dev/null @@ -1,220 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! ocn_forcing_bulk -! -!> \brief MPAS ocean bulk forcing -!> \author Doug Jacobsen -!> \date 04/25/12 -!> \details -!> This module contains routines for building the forcing arrays, -!> if bulk forcing is used. -! -!----------------------------------------------------------------------- - -module ocn_forcing_bulk - - use mpas_kind_types - use mpas_derived_types - use mpas_pool_routines - use mpas_timekeeping - use ocn_constants - - implicit none - private - save - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - - public :: ocn_forcing_bulk_build_arrays, & - ocn_forcing_bulk_init - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - real (kind=RKIND) :: refDensity - -!*********************************************************************** - -contains - -!*********************************************************************** -! -! routine ocn_forcing_bulk_build_arrays -! -!> \brief Determines the forcing array used for the bulk forcing. -!> \author Doug Jacobsen -!> \date 04/25/12 -!> \details -!> This routine computes the forcing arrays used later in MPAS. -! -!----------------------------------------------------------------------- - - subroutine ocn_forcing_bulk_build_arrays(meshPool, forcingPool, err)!{{{ - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: Error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - integer :: iEdge, cell1, cell2 - integer :: iCell, k - integer, pointer :: index_temperature_flux, index_salinity_flux - integer, pointer :: nCells, nEdges - - integer, dimension(:,:), pointer :: cellsOnEdge - - real (kind=RKIND) :: meridionalAverage, zonalAverage - real (kind=RKIND), dimension(:), pointer :: angleEdge - real (kind=RKIND), dimension(:), pointer :: windStressZonal, windStressMeridional - real (kind=RKIND), dimension(:), pointer :: latentHeatFlux, sensibleHeatFlux, longWaveHeatFluxUp, longWaveHeatFluxDown, evaporationFlux, seaIceHeatFlux, snowFlux - real (kind=RKIND), dimension(:), pointer :: seaIceFreshWaterFlux, seaIceSalinityFlux, riverRunoffFlux, iceRunoffFlux - real (kind=RKIND), dimension(:), pointer :: shortWaveHeatFlux, penetrativeTemperatureFlux - - real (kind=RKIND), dimension(:), pointer :: rainFlux - real (kind=RKIND), dimension(:), pointer :: seaSurfacePressure, iceFraction - - real (kind=RKIND), dimension(:), pointer :: surfaceThicknessFlux, surfaceWindStress, surfaceWindStressMagnitude - real (kind=RKIND), dimension(:,:), pointer :: surfaceTracerFlux - - err = 0 - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - - call mpas_pool_get_array(meshPool, 'angleEdge', angleEdge) - call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - - call mpas_pool_get_dimension(forcingPool, 'index_surfaceTemperatureFlux', index_temperature_flux) - call mpas_pool_get_dimension(forcingPool, 'index_surfaceSalinityFlux', index_salinity_flux) - - call mpas_pool_get_array(forcingPool, 'surfaceWindStress', surfaceWindStress) - call mpas_pool_get_array(forcingPool, 'surfaceWindStressMagnitude', surfaceWindStressMagnitude) - call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) - call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional) - call mpas_pool_get_array(forcingPool, 'latentHeatFlux', latentHeatFlux) - call mpas_pool_get_array(forcingPool, 'sensibleHeatFlux', sensibleHeatFlux) - call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxUp', longWaveHeatFluxUp) - call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxDown', longWaveHeatFluxDown) - call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) - call mpas_pool_get_array(forcingPool, 'seaIceHeatFlux', seaIceHeatFlux) - call mpas_pool_get_array(forcingPool, 'snowFlux', snowFlux) - call mpas_pool_get_array(forcingPool, 'shortWaveHeatFlux', shortWaveHeatFlux) - - call mpas_pool_get_array(forcingPool, 'seaIceFreshWaterFlux', seaIceFreshWaterFlux) - call mpas_pool_get_array(forcingPool, 'seaIceSalinityFlux', seaIceSalinityFlux) - call mpas_pool_get_array(forcingPool, 'riverRunoffFlux', riverRunoffFlux) - call mpas_pool_get_array(forcingPool, 'iceRunoffFlux', iceRunoffFlux) - - call mpas_pool_get_array(forcingPool, 'rainFlux', rainFlux) - - call mpas_pool_get_array(forcingPool, 'seaSurfacePressure', seaSurfacePressure) - call mpas_pool_get_array(forcingPool, 'iceFraction', iceFraction) - - call mpas_pool_get_array(forcingPool, 'surfaceThicknessFlux', surfaceThicknessFlux) - call mpas_pool_get_array(forcingPool, 'surfaceTracerFlux', surfaceTracerFlux) - call mpas_pool_get_array(forcingPool, 'penetrativeTemperatureFlux', penetrativeTemperatureFlux) - - ! Convert CESM wind stress to MPAS-O windstress - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1, iEdge) - cell2 = cellsOnEdge(2, iEdge) - - zonalAverage = 0.5 * (windStressZonal(cell1) + windStressZonal(cell2)) - meridionalAverage = 0.5 * (windStressMeridional(cell1) + windStressMeridional(cell2)) - - surfaceWindStress(iEdge) = cos(angleEdge(iEdge)) * zonalAverage + sin(angleEdge(iEdge)) * meridionalAverage - end do - - - ! Build surface fluxes at cell centers - do iCell = 1, nCells - surfaceWindStressMagnitude(iCell) = sqrt(windStressZonal(iCell)**2 + windStressMeridional(iCell)**2) - surfaceTracerFlux(index_temperature_flux, iCell) = (latentHeatFlux(iCell) + sensibleHeatFlux(iCell) + longWaveHeatFluxUp(iCell) + longWaveHeatFluxDown(iCell) & - + seaIceHeatFlux(iCell) - (snowFlux(iCell) + iceRunoffFlux(iCell)) * latent_heat_fusion_mks) * hflux_factor - - surfaceTracerFlux(index_salinity_flux, iCell) = seaIceSalinityFlux(iCell) * sflux_factor - - surfaceThicknessFlux(iCell) = ( snowFlux(iCell) + rainFlux(iCell) + evaporationFlux(iCell) + seaIceFreshWaterFlux(iCell) + iceRunoffFlux(iCell) + riverRunoffFlux(iCell) ) / refDensity - end do - - penetrativeTemperatureFlux = shortWaveHeatFlux * hflux_factor - - end subroutine ocn_forcing_bulk_build_arrays!}}} - -!*********************************************************************** -! -! routine ocn_forcing_bulk_init -! -!> \brief Initializes bulk forcing module -!> \author Doug Jacobsen -!> \date 04/25/12 -!> \details -!> This routine initializes the bulk forcing module. -! -!----------------------------------------------------------------------- - - subroutine ocn_forcing_bulk_init(err)!{{{ - - integer, intent(out) :: err !< Output: error flag - - real (kind=RKIND), pointer :: config_density0 - - err = 0 - - - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) - - refDensity = config_density0 - - end subroutine ocn_forcing_bulk_init!}}} - -!*********************************************************************** - -end module ocn_forcing_bulk - - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_forcing_restoring.F b/src/core_ocean/shared/mpas_ocn_forcing_restoring.F deleted file mode 100644 index b7377d105d..0000000000 --- a/src/core_ocean/shared/mpas_ocn_forcing_restoring.F +++ /dev/null @@ -1,180 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! ocn_forcing_restoring -! -!> \brief MPAS ocean restoring -!> \author Doug Jacobsen -!> \date 10/28/2013 -!> \details -!> This module contains routines for building surface flux arrays based on restoring. -! -!----------------------------------------------------------------------- - -module ocn_forcing_restoring - - use mpas_derived_types - use mpas_pool_routines - use ocn_constants - - implicit none - private - save - - !-------------------------------------------------------------------- - ! - ! Public parameters - ! - !-------------------------------------------------------------------- - - !-------------------------------------------------------------------- - ! - ! Public member functions - ! - !-------------------------------------------------------------------- - - public :: ocn_forcing_restoring_build_arrays, & - ocn_forcing_restoring_init - - !-------------------------------------------------------------------- - ! - ! Private module variables - ! - !-------------------------------------------------------------------- - - real (kind=RKIND) :: temperatureTimeScale, salinityTimeScale !< restoring timescales - real (kind=RKIND) :: temperatureLengthScale, salinityLengthScale !< restoring timescales - - -!*********************************************************************** - -contains - -!*********************************************************************** -! -! routine ocn_forcing_restoring_build_arrays -! -!> \brief Builds the forcing array for restoring -!> \author Doug Jacobsen -!> \date 10/29/2013 -!> \details -!> This routine builds the forcing array based on surface restoring. -! -!----------------------------------------------------------------------- - - subroutine ocn_forcing_restoring_build_arrays(meshPool, indexT, indexS, indexTFlux, indexSFlux, tracers, temperatureRestoring, salinityRestoring, surfaceTracerFluxes, err)!{{{ - - !----------------------------------------------------------------- - ! - ! input variables - ! - !----------------------------------------------------------------- - - type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information - - real (kind=RKIND), dimension(:,:,:), intent(in) :: & - tracers !< Input: tracer quantities - - real (kind=RKIND), dimension(:), intent(in) :: & - temperatureRestoring, & !< Input: Restoring values for temperature - salinityRestoring !< Input: Restoring values for salinity - - integer, intent(in) :: indexT !< Input: index for temperature - integer, intent(in) :: indexS !< Input: index for salinity - integer, intent(in) :: indexTFlux !< Input: index for temperature flux - integer, intent(in) :: indexSFlux !< Input: index for salinity flux - - !----------------------------------------------------------------- - ! - ! input/output variables - ! - !----------------------------------------------------------------- - - real (kind=RKIND), dimension(:,:), intent(out) :: & - surfaceTracerFluxes !< Input: tracer quantities - - !----------------------------------------------------------------- - ! - ! output variables - ! - !----------------------------------------------------------------- - - integer, intent(out) :: err !< Output: Error flag - - !----------------------------------------------------------------- - ! - ! local variables - ! - !----------------------------------------------------------------- - - integer :: iCell, k - integer, pointer :: nCells - - real (kind=RKIND) :: invTemp, invSalinity - - err = 0 - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - - invTemp = 1.0 / (temperatureTimeScale * 86400.0) - invSalinity = 1.0 / (salinityTimeScale * 86400.0) - - k = 1 ! restoring only in top layer - do iCell=1,nCells - surfaceTracerFluxes(indexTFlux, iCell) = - temperatureLengthScale * (tracers(indexT, k, iCell) - temperatureRestoring(iCell)) * invTemp - surfaceTracerFluxes(indexSFlux, iCell) = - salinityLengthScale * (tracers(indexS, k, iCell) - salinityRestoring(iCell)) * invSalinity - enddo - - !-------------------------------------------------------------------- - - end subroutine ocn_forcing_restoring_build_arrays!}}} - -!*********************************************************************** -! -! routine ocn_forcing_restoring_init -! -!> \brief Initializes ocean surface restoring -!> \author Doug Jacobsen -!> \date 10/29/2013 -!> \details -!> This routine initializes a variety of quantities related to -!> restoring in the ocean. -! -!----------------------------------------------------------------------- - - subroutine ocn_forcing_restoring_init(err)!{{{ - - integer, intent(out) :: err !< Output: error flag - - real (kind=RKIND), pointer :: config_restoreT_timescale, config_restoreT_lengthscale - real (kind=RKIND), pointer :: config_restoreS_timescale, config_restoreS_lengthscale - - err = 0 - - call mpas_pool_get_config(ocnConfigs, 'config_restoreT_timescale', config_restoreT_timescale) - call mpas_pool_get_config(ocnConfigs, 'config_restoreT_lengthscale', config_restoreT_lengthscale) - call mpas_pool_get_config(ocnConfigs, 'config_restoreS_timescale', config_restoreS_timescale) - call mpas_pool_get_config(ocnConfigs, 'config_restoreS_lengthscale', config_restoreS_lengthscale) - - temperatureTimeScale = config_restoreT_timescale - salinityTimeScale = config_restoreS_timescale - temperatureLengthScale = config_restoreT_lengthscale - salinityLengthScale = config_restoreS_lengthscale - - !-------------------------------------------------------------------- - - end subroutine ocn_forcing_restoring_init!}}} - -!*********************************************************************** - -end module ocn_forcing_restoring - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_framework_forcing.F b/src/core_ocean/shared/mpas_ocn_framework_forcing.F new file mode 100644 index 0000000000..c0378d1316 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_framework_forcing.F @@ -0,0 +1,31 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_framework_forcing +! +!> \brief Declare the forcing group linked list head +!> \author Adrian K. Turner +!> \date 19th May 2017 +!> \details +!> Declare the forcing group linked list head +! +!----------------------------------------------------------------------- + +module ocn_framework_forcing + + use mpas_derived_types + use mpas_forcing + + implicit none + private + + type(MPAS_forcing_group_type), pointer, public :: & + forcingGroupHead + +end module ocn_framework_forcing diff --git a/src/core_ocean/shared/mpas_ocn_frazil_forcing.F b/src/core_ocean/shared/mpas_ocn_frazil_forcing.F new file mode 100644 index 0000000000..b7c43e266f --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_frazil_forcing.F @@ -0,0 +1,659 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_frazil_forcing +! +!> \brief MPAS ocean frazil formation module +!> \author Todd Ringler +!> \date 10/19/2015 +!> \details +!> This module contains routines for the formation of frazil ice. +! +!----------------------------------------------------------------------- + +module ocn_frazil_forcing + + use mpas_kind_types + use mpas_constants + use mpas_derived_types + use mpas_pool_routines + use mpas_timekeeping + use mpas_timer + use ocn_constants + use ocn_equation_of_state + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_frazil_forcing_build_arrays, & + ocn_frazil_forcing_tracers, & + ocn_frazil_forcing_layer_thickness, & + ocn_frazil_forcing_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + logical :: frazilFormationOn + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_frazil_forcing_tracers +! +!> \brief Determines the tracer tendency due to frazil +!> \author Todd Ringler +!> \date 18 October 2015 +!> \details +!> This routine adds to the tracer tendency arrays +!> used to compute tracer at n+1. +! +!----------------------------------------------------------------------- + + subroutine ocn_frazil_forcing_tracers(meshPool, tracersPool, groupName, forcingPool, tracersTend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + character (len=*) :: groupName !< Input: Name of tracer group + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: meshPool !< Input/Output: mesh information + type (mpas_pool_type), intent(inout) :: tracersPool !< Input/Output: tracer tendency pool + type (mpas_pool_type), intent(inout) :: forcingPool !< Input/Output: forcing pool holding frazil-induced tendencies + real (kind=RKIND), dimension(:,:,:), intent(inout) :: tracersTend + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + if ( .not. frazilFormationOn ) return + + call mpas_timer_start("tracer frazil") + + if ( trim(groupName) == 'activeTracers' ) then + call ocn_frazil_forcing_active_tracers(meshPool, tracersPool, forcingPool, tracersTend, err) + end if + + call mpas_timer_stop("tracer frazil") + + end subroutine ocn_frazil_forcing_tracers!}}} + +!*********************************************************************** +! +! routine ocn_frazil_forcing_layer_thickness +! +!> \brief Add tendency due to frazil processes +!> \author Todd Ringler +!> \date 18 October 2015 +!> \details +!> This routine adds a tendency to layer thickness due to frazil formation +! +!----------------------------------------------------------------------- + + subroutine ocn_frazil_forcing_layer_thickness(meshPool, forcingPool, layerThicknessTend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + type (mpas_pool_type), intent(in) :: forcingPool !< Input: Forcing information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + real (kind=RKIND), intent(inout), dimension(:,:) :: layerThicknessTend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, k, nCells + integer, dimension(:), pointer :: nCellsArray + integer, dimension(:), pointer :: maxLevelCell + real (kind=RKIND), dimension(:,:), pointer :: frazilLayerThicknessTendency + + err = 0 + + if ( .not. frazilFormationOn ) return + + call mpas_timer_start("frazil thickness tendency") + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(forcingPool, 'frazilLayerThicknessTendency', frazilLayerThicknessTendency) + + ! frazil fields are needed only over 0 and 1 halos + nCells = nCellsArray( 2 ) + + ! Build surface fluxes at cell centers + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + layerThicknessTend(k,iCell) = layerThicknessTend(k,iCell) + frazilLayerThicknessTendency(k,iCell) + end do + end do + !$omp end do + + call mpas_timer_stop("frazil thickness tendency") + + end subroutine ocn_frazil_forcing_layer_thickness!}}} + + +!*********************************************************************** +! +! routine ocn_frazil_forcing_active_tracers +! +!> \brief Adds the active tracers forcing due to frazil +!> \author Todd Ringler +!> \date 18 October 2015 +!> \details +!> This routine adds the active tracers forcing due to frazil +!> from which tracer tendencies are computed later. +! +!----------------------------------------------------------------------- + + subroutine ocn_frazil_forcing_active_tracers(meshPool, tracersPool, forcingPool, activeTracersTend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + type (mpas_pool_type), intent(inout) :: tracersPool !< Input: tracer tendency pool + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:,:,:), intent(inout) :: activeTracersTend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, k, nCells + integer, pointer :: indexTemperature + integer, pointer :: indexSalinity + integer, pointer, dimension(:) :: nCellsArray + integer, pointer, dimension(:) :: maxLevelCell + + real (kind=RKIND), dimension(:,:), pointer :: frazilTemperatureTendency + real (kind=RKIND), dimension(:,:), pointer :: frazilSalinityTendency + + err = 0 + + if ( .not. frazilFormationOn ) return + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(forcingPool, 'frazilTemperatureTendency', frazilTemperatureTendency) + call mpas_pool_get_array(forcingPool, 'frazilSalinityTendency', frazilSalinityTendency) + + ! frazil fields are needed only over 0 and 1 halos + nCells = nCellsArray( 2 ) + + ! add to surface fluxes at cell centers + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + activeTracersTend(indexTemperature,k,iCell) = activeTracersTend(indexTemperature,k,iCell) & + + frazilTemperatureTendency(k,iCell) + activeTracersTend(indexSalinity,k,iCell) = activeTracersTend(indexSalinity,k,iCell) + frazilSalinityTendency(k,iCell) + end do + end do + !$omp end do + + end subroutine ocn_frazil_forcing_active_tracers!}}} + +!*********************************************************************** +! +! routine ocn_frazil_forcing_build_arrays +! +!> \brief Performs the formation of frazil within the ocean. +!> \author Todd Ringler +!> \date 10/19/2015 +!> \details +!> ocn_frazil_forcing_build_arrays computes the tendencies to layer thickness, temperature and salinity +!> due to the creation and possible melting of frazil ice +!> +!> these tendencies can be retrieved at any point by calling into ocn_frazil_forcing_{tracers, thickness} routines +!> +!> the pressure exerted by the frazil on the ocean "surface" is added to the pressure computation in diagnostics +!> +!> this routine should be call at the beginning of whatever time stepping method is utilized +!> and the tendencies should be retieved when building up the RHS of the thickess, temperature +!> and salinity equations. +!> +!> this routine is only applicable to the surface pressure, thickness and active tracer fields +! +!----------------------------------------------------------------------- + + subroutine ocn_frazil_forcing_build_arrays(domain, meshPool, forcingPool, diagnosticsPool, statePool, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer, intent(in) :: meshPool !< Input: Mesh information + type (mpas_pool_type), pointer, intent(in) :: diagnosticsPool !< Input: Diagnostic information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (domain_type), intent(inout) :: domain + type (mpas_pool_type), pointer, intent(inout) :: statePool !< Input: State information + type (mpas_pool_type), pointer, intent(inout) :: forcingPool !< Input: Forcing information + integer, intent(inout) :: err !< Error flag + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), pointer :: tracersPool + + real (kind=RKIND), dimension(:,:), pointer :: frazilLayerThicknessTendency + real (kind=RKIND), dimension(:,:), pointer :: frazilTemperatureTendency + real (kind=RKIND), dimension(:,:), pointer :: frazilSalinityTendency + real (kind=RKIND), dimension(:), pointer :: frazilSurfacePressure + integer, dimension(:), pointer :: landIceMask + + integer :: iCell, k, nCells + integer, dimension(:), pointer :: nCellsArray + integer, pointer :: nVertLevels + real (kind=RKIND) :: dt, columnTemperatureMin + + type (MPAS_timeInterval_type) :: timeStep + real (kind=RKIND), pointer :: config_frazil_heat_of_fusion + real (kind=RKIND), pointer :: config_frazil_ice_density + real (kind=RKIND), pointer :: config_frazil_fractional_thickness_limit + real (kind=RKIND), pointer :: config_frazil_maximum_depth + real (kind=RKIND), pointer :: config_specific_heat_sea_water + real (kind=RKIND), pointer :: config_frazil_sea_ice_reference_salinity + real (kind=RKIND), pointer :: config_frazil_land_ice_reference_salinity + real (kind=RKIND), pointer :: config_frazil_maximum_freezing_temperature + logical, pointer :: config_frazil_use_surface_pressure + logical, pointer :: config_frazil_in_open_ocean + logical, pointer :: config_frazil_under_land_ice + + real (kind=RKIND) :: newFrazilIceThickness, newThicknessWeightedSaltContent + real (kind=RKIND) :: sumNewFrazilIceThickness, sumNewThicknessWeightedSaltContent + real (kind=RKIND) :: meltedFrazilIceThickness, meltedThicknessWeightedSaltContent + real (kind=RKIND) :: oceanFreezingTemperature, frazilSalinity + + real (kind=RKIND), pointer, dimension(:) :: accumulatedFrazilIceMassNew + real (kind=RKIND), pointer, dimension(:) :: accumulatedFrazilIceMassOld + real (kind=RKIND), pointer, dimension(:) :: accumulatedFrazilIceSalinityNew + real (kind=RKIND), pointer, dimension(:) :: accumulatedFrazilIceSalinityOld + real (kind=RKIND), pointer, dimension(:) :: accumulatedLandIceFrazilMassNew + real (kind=RKIND), pointer, dimension(:) :: accumulatedLandIceFrazilMassOld + real (kind=RKIND), pointer, dimension(:) :: ssh + real (kind=RKIND), pointer, dimension(:,:) :: zMid + real (kind=RKIND), pointer, dimension(:,:) :: layerThickness + real (kind=RKIND), pointer, dimension(:,:) :: density + real (kind=RKIND), pointer, dimension(:,:) :: pressure + real (kind=RKIND), pointer, dimension(:,:,:) :: activeTracers + + integer, dimension(:), pointer :: maxLevelCell + integer, pointer :: indexTemperature !< index in tracers array for temperature + integer, pointer :: indexSalinity !< index in tracers array for salinity + integer :: kBottomFrazil ! k index where testing for frazil begins + + logical :: underLandIce ! indicates if we are under land ice + + real (kind=RKIND) :: potential ! scalar holding freezing/melt potential + real (kind=RKIND) :: freezingEnergy ! energy available for freezing, positive definite + real (kind=RKIND) :: meltingEnergy ! energy available for melting, positive definite + + ! if frazil is not enabled, return + if(.not. frazilFormationOn) return + + call mpas_timer_start("frazil") + + ! get pool pointers + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + + ! get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) + + ! get mesh information + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + + ! get arrays + ! note: state information is used to produce tendencies, so always grab "new" time level + call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) + call mpas_pool_get_array(statePool, 'accumulatedFrazilIceMass', accumulatedFrazilIceMassNew, 2) + call mpas_pool_get_array(statePool, 'accumulatedFrazilIceMass', accumulatedFrazilIceMassOld, 1) + call mpas_pool_get_array(statePool, 'accumulatedFrazilIceSalinity', accumulatedFrazilIceSalinityNew, 2) + call mpas_pool_get_array(statePool, 'accumulatedFrazilIceSalinity', accumulatedFrazilIceSalinityOld, 1) + call mpas_pool_get_array(statePool, 'accumulatedLandIceFrazilMass', accumulatedLandIceFrazilMassNew, 2) + call mpas_pool_get_array(statePool, 'accumulatedLandIceFrazilMass', accumulatedLandIceFrazilMassOld, 1) + call mpas_pool_get_array(statePool, 'ssh', ssh, 1) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, 1) + call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) + call mpas_pool_get_array(diagnosticsPool, 'density', density) + call mpas_pool_get_array(diagnosticsPool, 'pressure', pressure) + call mpas_pool_get_array(forcingPool, 'frazilTemperatureTendency', frazilTemperatureTendency) + call mpas_pool_get_array(forcingPool, 'frazilSalinityTendency', frazilSalinityTendency) + call mpas_pool_get_array(forcingPool, 'frazilLayerThicknessTendency', frazilLayerThicknessTendency) + call mpas_pool_get_array(forcingPool, 'frazilSurfacePressure', frazilSurfacePressure) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + + ! get configure parameters + call mpas_pool_get_config(ocnConfigs, 'config_frazil_in_open_ocean', config_frazil_in_open_ocean) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_under_land_ice', config_frazil_under_land_ice) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_maximum_depth', config_frazil_maximum_depth) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_fractional_thickness_limit', config_frazil_fractional_thickness_limit) + call mpas_pool_get_config(ocnConfigs, 'config_specific_heat_sea_water', config_specific_heat_sea_water) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_heat_of_fusion', config_frazil_heat_of_fusion) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_ice_density', config_frazil_ice_density) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_sea_ice_reference_salinity', config_frazil_sea_ice_reference_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_land_ice_reference_salinity', config_frazil_land_ice_reference_salinity) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_use_surface_pressure', config_frazil_use_surface_pressure) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_maximum_freezing_temperature', & + config_frazil_maximum_freezing_temperature) + + ! get time step in units of seconds + timeStep = mpas_get_clock_timestep(domain % clock, ierr=err) + call mpas_get_timeInterval(timeStep, dt=dt) + + ! initialize frazil tendency fields + frazilTemperatureTendency = 0.0_RKIND + frazilSalinityTendency = 0.0_RKIND + frazilLayerThicknessTendency = 0.0_RKIND + + ! frazil fields are needed only over 0 and 1 halos + nCells = nCellsArray( 2 ) + + ! loop over all columns + !$omp do schedule(runtime) private(kBottomFrazil, k, columnTemperatureMin, sumNewFrazilIceThickness, & + !$omp oceanFreezingTemperature, potential, freezingEnergy, meltingEnergy, & + !$omp newFrazilIceThickness, meltedFrazilIceThickness) + do iCell=1,nCells + + underLandIce = .false. + if ( associated(landIceMask) ) then + underLandIce = (landIceMask(iCell) == 1) + end if + + if (underLandIce .and. .not. config_frazil_under_land_ice) then + ! skip this cell because we're not doing frazil under land ice + cycle + end if + + if (.not. underLandIce .and. .not. config_frazil_in_open_ocean) then + ! skip this cell because we're not doing frazil in the open ocean + cycle + end if + + ! find deepest level where frazil can be created + kBottomFrazil=maxLevelCell(iCell) + do k=maxLevelCell(iCell), 1, -1 + ! add the ssh so frazil can form below land ice, where the ssh is depressed + if (-zMid(k,iCell) < -ssh(iCell) + config_frazil_maximum_depth) then + kBottomFrazil=k + exit + endif + enddo + + ! find minimum temperature between 1:kBottomFrazil + columnTemperatureMin = 1.0e30_RKIND + do k=1,kBottomFrazil + if ( activeTracers(indexTemperature,k,iCell) < columnTemperatureMin ) then + columnTemperatureMin = activeTracers(indexTemperature,k,iCell) + end if + enddo + + ! test min temperature agains max freezing temperature to see if we should even consider creating frazil + if (columnTemperatureMin > config_frazil_maximum_freezing_temperature) cycle + + ! initialize the sum of new frazil ice created + sumNewFrazilIceThickness = 0.0_RKIND + sumNewThicknessWeightedSaltContent = 0.0_RKIND + + ! loop from maximum depth of frazil creation to surface + do k = kBottomFrazil, 1, -1 + + ! get freezing temperature + oceanFreezingTemperature = ocn_freezing_temperature(salinity=activeTracers(indexSalinity,k,iCell), & + pressure=pressure(k,iCell), & + inLandIceCavity=underLandIce) + + potential = layerThickness(k,iCell) * config_specific_heat_sea_water & + * rho_sw * (activeTracers(indexTemperature,k,iCell) - oceanFreezingTemperature) + + freezingEnergy = max(0.0_RKIND, -potential) + meltingEnergy = max(0.0_RKIND, potential) + + if (freezingEnergy > 0) then + + ! get frazil salinity + if (underLandIce) then + frazilSalinity = config_frazil_land_ice_reference_salinity + else + frazilSalinity = config_frazil_sea_ice_reference_salinity + end if + frazilSalinity = min( frazilSalinity, activeTracers(indexSalinity, k, iCell) ) + + ! new frazil ice formation measured in meters + newFrazilIceThickness = freezingEnergy / (config_frazil_heat_of_fusion * config_frazil_ice_density) + + ! limit the frazil formed appropriately + newFrazilIceThickness = min(newFrazilIceThickness, layerThickness(k,iCell) * config_frazil_fractional_thickness_limit) + + + ! Determine new salt in frazil + newThicknessWeightedSaltContent = newFrazilIceThickness * frazilSalinity + + ! compute tendency to thickness, temperature and salinity + ! layerTendency is scaled so that mass of ice created == mass of ocean water removed + + ! layer thickness decreased due to creation of frazil + ! note: -- this has to be density (not rho_sw) to keep buoyancy equal + frazilLayerThicknessTendency(k,iCell) = - newFrazilIceThickness * config_frazil_ice_density / density(k,iCell) / dt + + ! salt is extracted with the frazil + frazilSalinityTendency(k,iCell) = - newThicknessWeightedSaltContent / dt + + ! ocean fluid temperature is warmed due to creation of frazil + frazilTemperatureTendency(k,iCell) = + ( newFrazilIceThickness * config_frazil_heat_of_fusion & + * config_frazil_ice_density ) / (config_specific_heat_sea_water * rho_sw) / dt + + ! keep track of sum of frazil ice + sumNewFrazilIceThickness = sumNewFrazilIceThickness + newFrazilIceThickness + sumNewThicknessWeightedSaltContent = sumNewThicknessWeightedSaltContent + newThicknessWeightedSaltContent + + else + + ! ocean water is warm enough to melt frazil + + ! test to see if there is frazil to be melted + if (sumNewFrazilIceThickness > 0.0_RKIND) then + + ! Frazil melting + meltedFrazilIceThickness = meltingEnergy / (config_frazil_heat_of_fusion * config_frazil_ice_density) + + ! limit melting by what there is to melt + meltedFrazilIceThickness = min(meltedFrazilIceThickness, sumNewFrazilIceThickness) + + ! limit melting by fraction of layer thickness + meltedFrazilIceThickness = min(meltedFrazilIceThickness, layerThickness(k,iCell) & + * config_frazil_fractional_thickness_limit) + + ! assign some salt to the melted ice + meltedThicknessWeightedSaltContent = meltedFrazilIceThickness & + * ( sumNewThicknessWeightedSaltContent / sumNewFrazilIceThickness ) + + ! compute tendency to thickness, temperature and salinity + + ! layer thickness increases due to melting of frazil + ! note -- scaling by local ocean density to mimimize surface pressure forcing errors + frazilLayerThicknessTendency(k,iCell) = + meltedFrazilIceThickness * config_frazil_ice_density & + / density(k,iCell) / dt + + ! salt is released into ocean with the melting frazil + frazilSalinityTendency(k,iCell) = + meltedThicknessWeightedSaltContent/ dt + + ! ocean fluid temperature is cooled due to melting of frazil + frazilTemperatureTendency(k,iCell) = - ( meltedFrazilIceThickness * config_frazil_heat_of_fusion & + * config_frazil_ice_density ) / (config_specific_heat_sea_water * rho_sw) & + / dt + + ! keep track of new frazil ice + sumNewThicknessWeightedSaltContent = sumNewThicknessWeightedSaltContent - meltedThicknessWeightedSaltContent + sumNewFrazilIceThickness = sumNewFrazilIceThickness - meltedFrazilIceThickness + + endif ! if (sumNewFrazilIceThickness > 0.0_RKIND) + + endif ! if (freezingEnergy < 0) + + enddo ! do k=kBottom,1-1 + + ! accumulate frazil mass to column total + ! note: the accumulatedFrazilIceMass (at both time levels) is reset to zero after being sent to the coupler + accumulatedFrazilIceMassNew(iCell) = accumulatedFrazilIceMassOld(iCell) + sumNewFrazilIceThickness & + * config_frazil_ice_density + accumulatedFrazilIceSalinityNew(iCell) = accumulatedFrazilIceSalinityOld(iCell) + sumNewThicknessWeightedSaltContent + + if ( underLandIce ) then + ! accumulate frazil formed under land ice in case we're not coupling and we need to keep track of it + ! for freshwater budgets + accumulatedLandIceFrazilMassNew(iCell) = accumulatedLandIceFrazilMassOld(iCell) & + + sumNewFrazilIceThickness * config_frazil_ice_density + end if + + enddo ! do iCell = 1, nCells + !$omp end do + + if ( config_frazil_use_surface_pressure ) then + !$omp do schedule(runtime) + do iCell = 1, nCells + frazilSurfacePressure(iCell) = accumulatedFrazilIceMassNew(iCell) * gravity + end do + !$omp end do + end if + + call mpas_timer_stop("frazil") + + end subroutine ocn_frazil_forcing_build_arrays!}}} + +!*********************************************************************** +! +! routine ocn_frazil_forcing_init +! +!> \brief Initializes ocean frazil ice module. +!> \author Todd Ringler +!> \date 10/19/2015 +!> \details +!> This routine initializes the ocean frazil ice module and variables.. +! +!----------------------------------------------------------------------- + + subroutine ocn_frazil_forcing_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + logical, pointer :: config_use_frazil_ice_formation + + err = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_use_frazil_ice_formation', config_use_frazil_ice_formation) + + frazilFormationOn = .false. + + if(config_use_frazil_ice_formation) then + frazilFormationOn = .true. + end if + + end subroutine ocn_frazil_forcing_init!}}} + +!*********************************************************************** + +end module ocn_frazil_forcing + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_gm.F b/src/core_ocean/shared/mpas_ocn_gm.F index c06c9f1ef6..3c4fc87f34 100644 --- a/src/core_ocean/shared/mpas_ocn_gm.F +++ b/src/core_ocean/shared/mpas_ocn_gm.F @@ -11,11 +11,12 @@ module ocn_gm use mpas_pool_routines use mpas_timer use mpas_constants - + use mpas_threading + use ocn_constants implicit none - private + private save !-------------------------------------------------------------------- @@ -41,12 +42,17 @@ module ocn_gm private :: tridiagonal_solve ! Config options - real (kind=RKIND), pointer :: config_gravWaveSpeed_trunc, config_standardGM_tracer_kappa, config_density0, & - config_max_relative_slope, config_Redi_kappa - logical, pointer :: config_use_standardGM + real (kind=RKIND), pointer :: config_gravWaveSpeed_trunc + real (kind=RKIND), pointer :: config_standardGM_tracer_kappa + real (kind=RKIND), pointer :: config_max_relative_slope + real (kind=RKIND), pointer :: config_Redi_kappa logical, pointer :: config_disable_redi_k33 + logical, pointer :: config_use_Redi_surface_layer_tapering + logical, pointer :: config_use_Redi_bottom_layer_tapering + real (kind=RKIND), pointer :: config_Redi_surface_layer_tapering_extent + real (kind=RKIND), pointer :: config_Redi_bottom_layer_tapering_depth - real (kind=RKIND), parameter :: epsGM = 1.0e-12 + real (kind=RKIND), parameter :: epsGM = 1.0e-12_RKIND !*********************************************************************** @@ -59,7 +65,7 @@ module ocn_gm !> \brief Computes GM Bolus velocity !> \author Qingshan Chen, Mark Petersen, Todd Ringler !> \date January 2013 -!> \details +!> \details !> This routine is the main driver for the Gent-McWilliams (GM) parameterization. !> It computes horizontal and vertical density gradients, the slope !> of isopycnal surfaces, and solves a boundary value problem in each column @@ -92,29 +98,33 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) ! !----------------------------------------------------------------- - real(kind=RKIND), dimension(:,:), pointer :: density, displacedDensity, zMid, normalGMBolusVelocity, hEddyFlux, layerThicknessEdge, & - gradDensityEdge, gradDensityTopOfEdge, gradDensityConstZTopOfEdge, gradZMidEdge, & - gradZMidTopOfEdge, relativeSlopeTopOfEdge, relativeSlopeTopOfCell, k33, gmStreamFuncTopOfEdge, BruntVaisalaFreqTop, gmStreamFuncTopOfCell, & - dDensityDzTopOfEdge, dDensityDzTopOfCell, relativeSlopeTapering, relativeSlopeTaperingCell, areaCellSum + real(kind=RKIND), dimension(:,:), pointer :: density, displacedDensity, zMid, normalGMBolusVelocity, hEddyFlux, & + layerThicknessEdge, gradDensityEdge, gradDensityTopOfEdge, gradDensityConstZTopOfEdge, gradZMidEdge, & + gradZMidTopOfEdge, relativeSlopeTopOfEdge, relativeSlopeTopOfCell, k33, gmStreamFuncTopOfEdge, BruntVaisalaFreqTop, & + gmStreamFuncTopOfCell, dDensityDzTopOfEdge, dDensityDzTopOfCell, relativeSlopeTapering, relativeSlopeTaperingCell, & + areaCellSum + real(kind=RKIND), dimension(:), pointer :: boundaryLayerDepth real(kind=RKIND), dimension(:), pointer :: areaCell, dcEdge, dvEdge, tridiagA, tridiagB, tridiagC, rightHandSide - integer, dimension(:), pointer :: maxLevelEdgeTop, maxLevelCell - integer, dimension(:,:), pointer :: cellsOnEdge - integer :: k, iEdge, cell1, cell2, iCell, N - real(kind=RKIND) :: h1, h2, areaEdge, c, BruntVaisalaFreqTopEdge, rtmp, maxSlopeK33 + integer, dimension(:), pointer :: maxLevelEdgeTop, maxLevelCell, nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnEdge, edgesOnCell + integer :: i, k, iEdge, cell1, cell2, iCell, N, iter + real(kind=RKIND) :: h1, h2, areaEdge, c, BruntVaisalaFreqTopEdge, rtmp, stmp, maxSlopeK33 ! Dimensions - integer, pointer :: nCells, nEdges + integer :: nCells, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray, nEdgesArray type (field2DReal), pointer :: gradDensityEdgeField, gradDensityTopOfEdgeField, gradDensityConstZTopOfEdgeField, & gradZMidEdgeField, gradZMidTopOfEdgeField, dDensityDzTopOfCellField, dDensityDzTopOfEdgeField,areaCellSumField - type (field1DReal), pointer :: rightHandSideField, tridiagAField, tridiagBField, tridiagCField + call mpas_timer_start('gm bolus velocity') call mpas_pool_get_array(diagnosticsPool, 'density', density) call mpas_pool_get_array(diagnosticsPool, 'displacedDensity', displacedDensity) call mpas_pool_get_array(diagnosticsPool, 'zMid', zMid) - call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) + call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfEdge', relativeSlopeTopOfEdge) call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTopOfCell', relativeSlopeTopOfCell) call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTapering', relativeSlopeTapering) @@ -127,15 +137,21 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) call mpas_pool_get_array(diagnosticsPool, 'gmStreamFuncTopOfEdge', gmStreamFuncTopOfEdge) call mpas_pool_get_array(diagnosticsPool, 'gmStreamFuncTopOfCell', gmStreamFuncTopOfCell) + if (config_use_Redi_surface_layer_tapering) call mpas_pool_get_array(diagnosticsPool, 'boundaryLayerDepth', & + boundaryLayerDepth) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_field(scratchPool, 'gradDensityEdge', gradDensityEdgeField) call mpas_pool_get_field(scratchPool, 'gradDensityTopOfEdge', gradDensityTopOfEdgeField) @@ -144,24 +160,18 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) call mpas_pool_get_field(scratchPool, 'dDensityDzTopOfEdge', dDensityDzTopOfEdgeField) call mpas_pool_get_field(scratchPool, 'gradZMidEdge', gradZMidEdgeField) call mpas_pool_get_field(scratchPool, 'gradZMidTopOfEdge', gradZMidTopOfEdgeField) - call mpas_pool_get_field(scratchPool, 'rightHandSide', rightHandSideField) - call mpas_pool_get_field(scratchPool, 'tridiagA', tridiagAField) - call mpas_pool_get_field(scratchPool, 'tridiagB', tridiagBField) - call mpas_pool_get_field(scratchPool, 'tridiagC', tridiagCField) call mpas_pool_get_field(scratchPool, 'areaCellSum', areaCellSumField) - call mpas_allocate_scratch_field(gradDensityEdgeField, .True.) - call mpas_allocate_scratch_field(gradDensityTopOfEdgeField, .True.) - call mpas_allocate_scratch_field(gradDensityConstZTopOfEdgeField, .True.) - call mpas_allocate_scratch_field(dDensityDzTopOfCellField, .True.) - call mpas_allocate_scratch_field(dDensityDzTopOfEdgeField, .True.) - call mpas_allocate_scratch_field(gradZMidEdgeField, .True.) - call mpas_allocate_scratch_field(gradZMidTopOfEdgeField, .True.) - call mpas_allocate_scratch_field(rightHandSideField, .True.) - call mpas_allocate_scratch_field(tridiagAField, .True.) - call mpas_allocate_scratch_field(tridiagBField, .True.) - call mpas_allocate_scratch_field(tridiagCField, .True.) - call mpas_allocate_scratch_field(areaCellSumField, .True.) + call mpas_allocate_scratch_field(gradDensityEdgeField, .True., .false.) + call mpas_allocate_scratch_field(gradDensityTopOfEdgeField, .True., .false.) + call mpas_allocate_scratch_field(gradDensityConstZTopOfEdgeField, .True., .false.) + call mpas_allocate_scratch_field(dDensityDzTopOfCellField, .True., .false.) + call mpas_allocate_scratch_field(dDensityDzTopOfEdgeField, .True., .false.) + call mpas_allocate_scratch_field(gradZMidEdgeField, .True., .false.) + call mpas_allocate_scratch_field(gradZMidTopOfEdgeField, .True., .false.) + call mpas_allocate_scratch_field(areaCellSumField, .True., .false.) + + call mpas_threading_barrier() gradDensityEdge => gradDensityEdgeField % array gradDensityTopOfEdge => gradDensityTopOfEdgeField % array @@ -170,38 +180,56 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) dDensityDzTopOfEdge => dDensityDzTopOfEdgeField % array gradZMidEdge => gradZMidEdgeField % array gradZMidTopOfEdge => gradZMidTopOfEdgeField % array - rightHandSide => rightHandSideField % array - tridiagA => tridiagAField % array - tridiagB => tridiagBField % array - tridiagC => tridiagCField % array areaCellSum => areaCellSumField % array + allocate(rightHandSide(nVertLevels)) + allocate(tridiagA(nVertLevels)) + allocate(tridiagB(nVertLevels)) + allocate(tridiagC(nVertLevels)) + + nCells = nCellsArray( size(nCellsArray) ) + nEdges = nEdgesArray( size(nEdgesArray) ) + ! Assign a huge value to the scratch variables which may manifest itself when ! there is a bug. - gradDensityEdge(:,:) = huge(0D0) - gradDensityTopOfEdge(:,:) = huge(0D0) - dDensityDzTopOfCell(:,:) = huge(0D0) - dDensityDzTopOfEdge(:,:) = huge(0D0) - gradZMidEdge(:,:) = huge(0D0) - gradZMidTopOfEdge(:,:) = huge(0D0) - - relativeSlopeTopOfEdge(:,:) = 0.0_RKIND - relativeSlopeTopOfCell(:,:) = 0.0_RKIND - relativeSlopeTapering(:,:) = 0.0_RKIND - relativeSlopeTaperingCell(:,:) = 0.0_RKIND - k33(:,:) = 0.0_RKIND - normalGMBolusVelocity(:,:) = 0.0_RKIND - + !$omp do schedule(runtime) + do iEdge = 1, nEdges + 1 + do k = 1, nVertLevels + gradDensityEdge(k, iEdge) = huge(0D0) + gradDensityTopOfEdge(k, iEdge) = huge(0D0) + dDensityDzTopOfEdge(k, iEdge) = huge(0D0) + gradZMidEdge(k, iEdge) = huge(0D0) + gradZMidTopOfEdge(k, iEdge) = huge(0D0) + relativeSlopeTopOfEdge(k, iEdge) = 0.0_RKIND + relativeSlopeTapering(k, iEdge) = 0.0_RKIND + normalGMBolusVelocity(k, iEdge) = 0.0_RKIND + end do + end do + !$omp end do + + !$omp do schedule(runtime) + do iCell = 1, nCells + 1 + do k = 1, nVertLevels + dDensityDzTopOfCell(k, iCell) = huge(0D0) + k33(k, iCell) = 0.0_RKIND + relativeSlopeTopOfCell(k, iCell) = 0.0_RKIND + relativeSlopeTaperingCell(k, iCell) = 0.0_RKIND + end do + end do + !$omp end do + !-------------------------------------------------------------------- ! ! Compute vertical derivative of density at top of cell, interpolate to top of edge ! This is required for Redi and Bolus parts. ! !-------------------------------------------------------------------- - + + nCells = nCellsArray( 3 ) ! Compute vertical derivative of density (dDensityDzTopOfCell) at cell center and layer interface - ! Note that displacedDensity is used from the upper cell, so that the EOS reference level for + ! Note that displacedDensity is used from the upper cell, so that the EOS reference level for ! pressure is the same for both displacedDensity(k-1,iCell) and density(k,iCell). + !$omp do schedule(runtime) private(k, rtmp) do iCell = 1, nCells do k = 2, maxLevelCell(iCell) rtmp = (displacedDensity(k-1,iCell) - density(k,iCell)) / (zMid(k-1,iCell) - zMid(k,iCell)) @@ -214,8 +242,12 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) dDensityDzTopOfCell(1,iCell) = 0.0_RKIND dDensityDzTopOfCell(maxLevelCell(iCell)+1,iCell) = 0.0_RKIND end do + !$omp end do + + nEdges = nEdgesArray( 3 ) ! Interpolate dDensityDzTopOfCell to edge and layer interface + !$omp do schedule(runtime) private(k, cell1, cell2) do iEdge = 1, nEdges do k = 1, maxLevelEdgeTop(iEdge)+1 cell1 = cellsOnEdge(1,iEdge) @@ -223,6 +255,7 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) dDensityDzTopOfEdge(k,iEdge) = 0.5_RKIND * (dDensityDzTopOfCell(k,cell1) + dDensityDzTopOfCell(k,cell2)) end do end do + !$omp end do !-------------------------------------------------------------------- ! @@ -231,9 +264,12 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) ! !-------------------------------------------------------------------- - ! Compute density gradient (gradDensityEdge) and gradient of zMid (gradZMidEdge) + nEdges = nEdgesArray( 3 ) + + ! Compute density gradient (gradDensityEdge) and gradient of zMid (gradZMidEdge) ! along the constant coordinate surface. ! The computed variables lives at edge and mid-layer depth + !$omp do schedule(runtime) private(cell1, cell2, k) do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) @@ -243,11 +279,15 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) gradZMidEdge(k,iEdge) = (zMid(k,cell2) - zMid(k,cell1)) / dcEdge(iEdge) end do end do + !$omp end do + + nEdges = nEdgesArray( 3 ) ! Interpolate gradDensityEdge and gradZMidEdge to layer interface + !$omp do schedule(runtime) private(k, h1, h2) do iEdge = 1, nEdges ! The interpolation can only be carried out on non-boundary edges - if (maxLevelEdgeTop(iEdge) .GE. 1) then + if (maxLevelEdgeTop(iEdge) .GE. 1) then do k = 2, maxLevelEdgeTop(iEdge) h1 = layerThicknessEdge(k-1,iEdge) h2 = layerThicknessEdge(k,iEdge) @@ -265,6 +305,7 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) gradZMidTopOfEdge(maxLevelEdgeTop(iEdge)+1,iEdge) = gradZMidEdge(maxLevelEdgeTop(iEdge),iEdge) end if end do + !$omp end do !-------------------------------------------------------------------- ! @@ -272,13 +313,18 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) ! !-------------------------------------------------------------------- + nEdges = nEdgesArray( 3 ) + + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges if (maxLevelEdgeTop(iEdge) .GE. 1) then do k = 1, maxLevelEdgeTop(iEdge)+1 - gradDensityConstZTopOfEdge(k,iEdge) = gradDensityTopOfEdge(k,iEdge) - dDensityDzTopOfEdge(k,iEdge) * gradZMidTopOfEdge(k,iEdge) + gradDensityConstZTopOfEdge(k,iEdge) = gradDensityTopOfEdge(k,iEdge) - dDensityDzTopOfEdge(k,iEdge) & + * gradZMidTopOfEdge(k,iEdge) end do end if end do + !$omp end do !-------------------------------------------------------------------- ! @@ -287,82 +333,184 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) ! !-------------------------------------------------------------------- + nEdges = nEdgesArray( 3 ) + ! Compute relativeSlopeTopOfEdge at edge and layer interface ! set relativeSlopeTopOfEdge to zero for horizontal land/water edges. - relativeSlopeTopOfEdge = 0.0_RKIND + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges + relativeSlopeTopOfEdge(:, iEdge) = 0.0_RKIND ! Beside a full land cell (e.g. missing cell) maxLevelEdgeTop=0, so relativeSlopeTopOfEdge at that edge will remain zero. do k = 2, maxLevelEdgeTop(iEdge) relativeSlopeTopOfEdge(k,iEdge) = - gradDensityTopOfEdge(k,iEdge) / min(dDensityDzTopOfEdge(k,iEdge),-epsGM) end do - ! Since dDensityDzTopOfEdge is guaranteed to be zero on the top surface, relativeSlopeTopOfEdge on the top surface is identified with its value on the second interface. + ! Since dDensityDzTopOfEdge is guaranteed to be zero on the top surface, relativeSlopeTopOfEdge on the top + ! surface is identified with its value on the second interface. relativeSlopeTopOfEdge(1,iEdge) = relativeSlopeTopOfEdge(2,iEdge) - ! dDensityDzTopOfEdge may or may not equal zero on the bottom surface, depending on whether maxLevelEdgeTop(iEdge) = maxLevelEdgeBottom(iEdge). But here we - ! take a simplistic approach and identify relativeSlopeTopOfEdge on the bottom surface with its value on the interface just above. + ! dDensityDzTopOfEdge may or may not equal zero on the bottom surface, depending on whether + ! maxLevelEdgeTop(iEdge) = maxLevelEdgeBottom(iEdge). But here we + ! take a simplistic approach and identify relativeSlopeTopOfEdge on the bottom surface with its value on + ! the interface just above. relativeSlopeTopOfEdge( maxLevelEdgeTop(iEdge)+1, iEdge ) = relativeSlopeTopOfEdge( max(1,maxLevelEdgeTop(iEdge)), iEdge ) end do + !$omp end do + + nEdges = nEdgesArray( 3 ) ! slope can be unbounded in regions of neutral stability, reset to the large, but bounded, value ! values is hardwrite to 1.0, this is equivalent to a slope of 45 degrees - where(relativeSlopeTopOfEdge < -1.0_RKIND) relativeSlopeTopOfEdge = -1.0_RKIND - where(relativeSlopeTopOfEdge > 1.0_RKIND) relativeSlopeTopOfEdge = 1.0_RKIND + !$omp do schedule(runtime) private(k) + do iEdge = 1, nEdges + do k = 1, nVertLevels + relativeSlopeTopOfEdge(k, iEdge) = max( min( relativeSlopeTopOfEdge(k, iEdge), 1.0_RKIND), -1.0_RKIND) + end do + end do + !$omp end do ! average relative slope to cell centers ! do this by computing (relative slope)^2, then taking sqrt - areaCellSum = 1.0e-34_RKIND - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - ! contribution of cell area from this edge: - areaEdge = 0.25_RKIND * dcEdge(iEdge) * dvEdge(iEdge) - do k = 1, maxLevelEdgeTop(iEdge) + nCells = nCellsArray( 2 ) - ! only one component is summed (thus the weighting by a factor of 2.0) - rtmp = 2.0_RKIND * areaEdge * relativeSlopeTopOfEdge(k,iEdge)**2 - relativeSlopeTopOfCell(k,cell1) = relativeSlopeTopOfCell(k,cell1) + rtmp - relativeSlopeTopOfCell(k,cell2) = relativeSlopeTopOfCell(k,cell2) + rtmp + !$omp do schedule(runtime) private(i, iEdge, areaEdge, rtmp, k) + do iCell = 1, nCells + areaCellSum(:, iCell) = 1.0e-34_RKIND + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + + !contribution of cell area from this edge * 2.0 + areaEdge = 0.5_RKIND * dcEdge(iEdge) * dvEdge(iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + rtmp = areaEdge * relativeSlopeTopOfEdge(k, iEdge)**2 + relativeSlopeTopOfCell(k, iCell) = relativeSlopeTopOfCell(k, iCell) + rtmp + areaCellSum(k, iCell) = areaCellSum(k, iCell) + areaEdge + end do + end do + end do + !$omp end do - areaCellSum(k,cell1) = areaCellSum(k,cell1) + areaEdge - areaCellSum(k,cell2) = areaCellSum(k,cell2) + areaEdge + nCells = nCellsArray( 2 ) - end do - end do + !$omp do schedule(runtime) private(k) do iCell=1,nCells do k = 1, maxLevelCell(iCell) - relativeSlopeTopOfCell(k,iCell) = sqrt(relativeSlopeTopOfCell(k,iCell)/areaCellSum(k,iCell)) + relativeSlopeTopOfCell(k,iCell) = sqrt( relativeSlopeTopOfCell(k,iCell)/areaCellSum(k,iCell) ) end do end do + !$omp end do ! Compute tapering function ! Compute k33 at cell center and layer interface - k33(:,:) = 0.0_RKIND + + nCells = nCellsArray( size(nCellsArray) ) + + !$omp do schedule(runtime) + do iCell = 1, nCells + k33(:, iCell) = 0.0_RKIND + end do + !$omp end do + + ! use relativeSlopeTaperingCell as a temporary space for smoothing of relativeSlopeTopOfCell + relativeSlopeTaperingCell = relativeSlopeTopOfCell + do iter = 1, 5 + !$omp barrier + + nCells = nCellsArray( 2 ) + + !$omp do schedule(runtime) private(k, rtmp) + do iCell=1,nCells + relativeSlopeTaperingCell(1, iCell) = 0.0_RKIND + relativeSlopeTaperingCell(maxLevelCell(iCell):nVertLevels, iCell) = 0.0_RKIND + do k = 2, maxLevelCell(iCell)-1 + rtmp = relativeSlopeTopOfCell(k-1,iCell) + relativeSlopeTopOfCell(k+1,iCell) + stmp = 2.0_RKIND*relativeSlopeTopOfCell(k,iCell) + relativeSlopeTaperingCell(k,iCell) = (rtmp+stmp)/4.0_RKIND + end do + relativeSlopeTopOfCell(:, iCell) = relativeSlopeTaperingCell(:, iCell) + end do + !$omp end do + end do ! iter + + nCells = nCellsArray ( 2 ) + ! first, compute tapering across full domain based on a maximum allowable slope + !$omp do schedule(runtime) private(k) + do iCell=1,nCells + do k = 1, maxLevelCell(iCell) + relativeSlopeTaperingCell(k,iCell) = min(1.0_RKIND, config_max_relative_slope / (relativeSlopeTopOfCell(k,iCell)+epsGM)) + end do + end do + !$omp end do + + ! now further taper in the boundary layer + ! vertical (k33) tapering starts at 2*OBL, increases linearly to OBL and is held uniform across OBL + ! rtmp = 1 @ zMid = -2.0*OBL, rtmp = 0 @ zMid = -OBL + if(config_use_Redi_surface_layer_tapering) then + nCells = nCellsArray ( 2 ) + !$omp do schedule(runtime) private(k, rtmp) + do iCell=1,nCells + do k = 1, maxLevelCell(iCell) + rtmp = -zMid(k,iCell)/max(config_Redi_surface_layer_tapering_extent,boundaryLayerDepth(iCell)+epsGM) + rtmp = max(0.0_RKIND,rtmp) + rtmp = min(1.0_RKIND,rtmp) + relativeSlopeTaperingCell(k,iCell) = rtmp*relativeSlopeTaperingCell(k,iCell) + end do + end do + !$omp end do + endif ! config_use_Redi_surface_layer_tapering + + ! now further taper in the boundary layer + ! vertical (k33) tapering starts at 2*OBL, increases linearly to OBL and is held uniform across OBL + ! rtmp = 1 @ zMid = zMid(maxLevelCell) + config_Redi_bottom_layer_tapering_depth, rtmp = 0 @ zMid = zMid(maxLevelCell) + if(config_use_Redi_bottom_layer_tapering) then + nCells = nCellsArray ( 2 ) + !$omp do schedule(runtime) private(k, rtmp) + do iCell=1,nCells + do k = 1, maxLevelCell(iCell) + rtmp = (zMid(k,iCell)-zMid(maxLevelCell(iCell),iCell))/(config_Redi_bottom_layer_tapering_depth+epsGM) + rtmp = max(0.0_RKIND,rtmp) + rtmp = min(1.0_RKIND,rtmp) + relativeSlopeTaperingCell(k,iCell) = rtmp*relativeSlopeTaperingCell(k,iCell) + end do + end do + !$omp end do + endif ! config_use_Redi_bottom_layer_tapering + + nCells = nCellsArray( 2 ) + !$omp do schedule(runtime) private(k) do iCell=1,nCells + k33(:, iCell) = 0.0_RKIND do k = 2, maxLevelCell(iCell) - relativeSlopeTaperingCell(k,iCell) = min(1.0_RKIND, config_max_relative_slope**2 / (relativeSlopeTopOfCell(k,iCell)**2+epsGM)) - k33(k,iCell) = relativeSlopeTaperingCell(k,iCell) * (relativeSlopeTopOfCell(k,iCell))**2 + k33(k,iCell) = ( relativeSlopeTaperingCell(k,iCell) * relativeSlopeTopOfCell(k,iCell) )**2 end do end do + !$omp end do + + nEdges = nEdgesArray( 3 ) ! average tapering function to layer edges + !$omp do schedule(runtime) private(cell1, cell2, k) do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - do k = 2, maxLevelEdgeTop(iEdge) + do k = 1, maxLevelEdgeTop(iEdge) relativeSlopeTapering(k,iEdge) = 0.5_RKIND * (relativeSlopeTaperingCell(k,cell1) + relativeSlopeTaperingCell(k,cell2)) enddo enddo - - ! k33 is still non-dimensional measuring the limited (relative slope)^2 of neutral surfaces. - ! scale k33 by config_Redi_kappa so it has units of diffusivity - k33 = config_Redi_kappa * k33 + !$omp end do ! allow disabling of K33 for testing - if(config_disable_redi_k33) k33=0.0_RKIND + if(config_disable_redi_k33) then + nCells = nCellsArray( size(nCellsArray) ) + !$omp do schedule(runtime) + do iCell = 1, nCells + k33(:, iCell) = 0.0_RKIND + end do + !$omp end do + end if !-------------------------------------------------------------------- ! @@ -370,74 +518,104 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) ! !-------------------------------------------------------------------- - gmStreamFuncTopOfEdge(:,:) = 0.0_RKIND c = config_gravWaveSpeed_trunc**2 - do iEdge = 1, nEdges + nEdges = nEdgesArray( 3 ) + + !$omp do schedule(runtime) private(cell1, cell2, k, BruntVaisalaFreqTopEdge, N) + do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) + gmStreamFuncTopOfEdge(:, iEdge) = 0.0_RKIND + ! Construct the tridiagonal matrix if (maxLevelEdgeTop(iEdge) .GE. 3) then ! First row - k = 2 + k = 2 BruntVaisalaFreqTopEdge = 0.5_RKIND * (BruntVaisalaFreqTop(k,cell1) + BruntVaisalaFreqTop(k,cell2)) BruntVaisalaFreqTopEdge = max(BruntVaisalaFreqTopEdge, 0.0_RKIND) - tridiagB(k-1) = - 2.0_RKIND * config_gravWaveSpeed_trunc**2/(layerThicknessEdge(k-1,iEdge)*layerThicknessEdge(k,iEdge)) - BruntVaisalaFreqTopEdge - tridiagC(k-1) = 2.0_RKIND * config_gravWaveSpeed_trunc**2/layerThicknessEdge(k,iEdge)/(layerThicknessEdge(k-1,iEdge)+layerThicknessEdge(k,iEdge)) - rightHandSide(k-1) = config_standardGM_tracer_kappa * gravity / config_density0 * gradDensityConstZTopOfEdge(k,iEdge) + tridiagB(k-1) = - 2.0_RKIND * config_gravWaveSpeed_trunc**2 / (layerThicknessEdge(k-1,iEdge) & + * layerThicknessEdge(k,iEdge)) - BruntVaisalaFreqTopEdge + tridiagC(k-1) = 2.0_RKIND * config_gravWaveSpeed_trunc**2 / layerThicknessEdge(k, iEdge) & + / (layerThicknessEdge(k-1, iEdge) + layerThicknessEdge(k, iEdge)) + rightHandSide(k-1) = config_standardGM_tracer_kappa * gravity / rho_sw * gradDensityConstZTopOfEdge(k,iEdge) ! Second to next to the last rows do k = 3, maxLevelEdgeTop(iEdge)-1 BruntVaisalaFreqTopEdge = 0.5_RKIND * (BruntVaisalaFreqTop(k,cell1) + BruntVaisalaFreqTop(k,cell2)) BruntVaisalaFreqTopEdge = max(BruntVaisalaFreqTopEdge, 0.0_RKIND) - tridiagA(k-2) = 2.0_RKIND * config_gravWaveSpeed_trunc**2/layerThicknessEdge(k-1,iEdge)/(layerThicknessEdge(k-1,iEdge)+layerThicknessEdge(k,iEdge)) - tridiagB(k-1) = - 2.0_RKIND * config_gravWaveSpeed_trunc**2/(layerThicknessEdge(k-1,iEdge)*layerThicknessEdge(k,iEdge)) - BruntVaisalaFreqTopEdge - tridiagC(k-1) = 2.0_RKIND * config_gravWaveSpeed_trunc**2/layerThicknessEdge(k,iEdge)/(layerThicknessEdge(k-1,iEdge)+layerThicknessEdge(k,iEdge)) - rightHandSide(k-1) = config_standardGM_tracer_kappa * gravity / config_density0 * gradDensityConstZTopOfEdge(k,iEdge) + tridiagA(k-2) = 2.0_RKIND * config_gravWaveSpeed_trunc**2 / layerThicknessEdge(k-1, iEdge) & + / (layerThicknessEdge(k-1, iEdge) + layerThicknessEdge(k, iEdge)) + tridiagB(k-1) = - 2.0_RKIND * config_gravWaveSpeed_trunc**2 / (layerThicknessEdge(k-1, iEdge) & + * layerThicknessEdge(k, iEdge) ) - BruntVaisalaFreqTopEdge + tridiagC(k-1) = 2.0_RKIND * config_gravWaveSpeed_trunc**2 / layerThicknessEdge(k, iEdge) & + / (layerThicknessEdge(k-1, iEdge) + layerThicknessEdge(k, iEdge)) + rightHandSide(k-1) = config_standardGM_tracer_kappa * gravity / rho_sw * gradDensityConstZTopOfEdge(k,iEdge) end do ! Last row - k = maxLevelEdgeTop(iEdge) + k = maxLevelEdgeTop(iEdge) BruntVaisalaFreqTopEdge = 0.5_RKIND * (BruntVaisalaFreqTop(k,cell1) + BruntVaisalaFreqTop(k,cell2)) BruntVaisalaFreqTopEdge = max(BruntVaisalaFreqTopEdge, 0.0_RKIND) - tridiagA(k-2) = 2.0_RKIND * config_gravWaveSpeed_trunc**2/layerThicknessEdge(k-1,iEdge)/(layerThicknessEdge(k-1,iEdge)+layerThicknessEdge(k,iEdge)) - tridiagB(k-1) = - 2.0_RKIND * config_gravWaveSpeed_trunc**2/(layerThicknessEdge(k-1,iEdge)*layerThicknessEdge(k,iEdge)) - BruntVaisalaFreqTopEdge - rightHandSide(k-1) = config_standardGM_tracer_kappa * gravity / config_density0 * gradDensityConstZTopOfEdge(k,iEdge) + tridiagA(k-2) = 2.0_RKIND * config_gravWaveSpeed_trunc**2 / layerThicknessEdge(k-1,iEdge) & + / (layerThicknessEdge(k-1,iEdge) + layerThicknessEdge(k,iEdge)) + tridiagB(k-1) = - 2.0_RKIND * config_gravWaveSpeed_trunc**2 / (layerThicknessEdge(k-1, iEdge) & + * layerThicknessEdge(k, iEdge)) - BruntVaisalaFreqTopEdge + rightHandSide(k-1) = config_standardGM_tracer_kappa * gravity / rho_sw * gradDensityConstZTopOfEdge(k,iEdge) ! Total number of rows N = maxLevelEdgeTop(iEdge) - 1 ! Call the tridiagonal solver - call tridiagonal_solve(tridiagA, tridiagB, tridiagC, rightHandSide, gmStreamFuncTopOfEdge(2:maxLevelEdgeTop(iEdge),iEdge), N) + call tridiagonal_solve(tridiagA, tridiagB, tridiagC, rightHandSide, & + gmStreamFuncTopOfEdge(2:maxLevelEdgeTop(iEdge), iEdge), N) end if - end do + !$omp end do + nEdges = nEdgesArray( 3 ) ! Compute normalGMBolusVelocity from the stream function + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges do k = 1, maxLevelEdgeTop(iEdge) - normalGMBolusVelocity(k,iEdge) = (gmStreamFuncTopOfEdge(k,iEdge) - gmStreamFuncTopOfEdge(k+1,iEdge)) / layerThicknessEdge(k,iEdge) + normalGMBolusVelocity(k,iEdge) = (gmStreamFuncTopOfEdge(k,iEdge) - gmStreamFuncTopOfEdge(k+1,iEdge)) & + / layerThicknessEdge(k,iEdge) end do end do + !$omp end do + + nCells = nCellsArray( 1 ) ! Interpolate gmStreamFuncTopOfEdge to cell centers for visualization - gmStreamFuncTopOfCell(:,:) = 0.0_RKIND - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - areaEdge = 0.25_RKIND * dcEdge(iEdge) * dvEdge(iEdge) + !$omp do schedule(runtime) private(i, iEdge, areaEdge, k, rtmp) + do iCell = 1, nCells + gmStreamFuncTopOfCell(:, iCell) = 0.0_RKIND + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) - do k = 1, maxLevelEdgeTop(iEdge) - rtmp = 0.5_RKIND * ( gmStreamFuncTopOfEdge(k,iEdge) + gmStreamFuncTopOfEdge(k+1,iEdge) ) * areaEdge - gmStreamFuncTopOfCell(k,cell1) = gmStreamFuncTopOfCell(k,cell1) + rtmp - gmStreamFuncTopOfCell(k,cell2) = gmStreamFuncTopOfCell(k,cell2) + rtmp - end do + areaEdge = 0.25_RKIND * dcEdge(iEdge) * dvEdge(iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + rtmp = 0.5_RKIND * ( gmStreamFuncTopOfEdge(k, iEdge) + gmStreamFuncTopOfEdge(k+1, iEdge) ) * areaEdge + gmStreamFuncTopOfCell(k, iCell) = gmStreamFuncTopOfCell(k, iCell) + rtmp + end do + end do end do + !$omp end do + + !$omp do schedule(runtime) do iCell = 1, nCells gmStreamFuncTopOfCell(:, iCell) = gmStreamFuncTopOfCell(:,iCell) / areaCell(iCell) end do + !$omp end do + + deallocate(rightHandSide) + deallocate(tridiagA) + deallocate(tridiagB) + deallocate(tridiagC) + + call mpas_threading_barrier() ! Deallocate scratch variables call mpas_deallocate_scratch_field(gradDensityEdgeField, .true.) @@ -447,10 +625,9 @@ subroutine ocn_gm_compute_Bolus_velocity(diagnosticsPool, meshPool, scratchPool) call mpas_deallocate_scratch_field(dDensityDzTopOfEdgeField, .true.) call mpas_deallocate_scratch_field(gradZMidEdgeField, .true.) call mpas_deallocate_scratch_field(gradZMidTopOfEdgeField, .true.) - call mpas_deallocate_scratch_field(rightHandSideField, .true.) - call mpas_deallocate_scratch_field(tridiagAField, .true.) - call mpas_deallocate_scratch_field(tridiagBField, .true.) - call mpas_deallocate_scratch_field(tridiagCField, .true.) + call mpas_deallocate_scratch_field(areaCellSumField, .true.) + + call mpas_timer_stop('gm bolus velocity') end subroutine ocn_gm_compute_Bolus_velocity!}}} @@ -461,7 +638,7 @@ end subroutine ocn_gm_compute_Bolus_velocity!}}} !> \brief Solve the matrix equation Ax=r for x, where A is tridiagonal. !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> Solve the matrix equation Ax=r for x, where A is tridiagonal. !> A is an nxn matrix, with: !> a sub-diagonal, filled from 1:n-1 (a(1) appears on row 2) @@ -471,7 +648,7 @@ end subroutine ocn_gm_compute_Bolus_velocity!}}} !----------------------------------------------------------------------- ! mrp note: This subroutine also appears in vmix and should really be put in the framework. subroutine tridiagonal_solve(a,b,c,r,x,n) !{{{ - + !----------------------------------------------------------------- ! ! input variables @@ -499,27 +676,23 @@ subroutine tridiagonal_solve(a,b,c,r,x,n) !{{{ real (KIND=RKIND) :: m integer i - call mpas_timer_start("tridiagonal_solve") - ! Use work variables for b and r bTemp(1) = b(1) rTemp(1) = r(1) - + ! First pass: set the coefficients do i = 2,n m = a(i-1)/bTemp(i-1) bTemp(i) = b(i) - m*c(i-1) rTemp(i) = r(i) - m*rTemp(i-1) - end do - + end do + x(n) = rTemp(n)/bTemp(n) ! Second pass: back-substition do i = n-1, 1, -1 x(i) = (rTemp(i) - c(i)*x(i+1))/bTemp(i) end do - call mpas_timer_stop("tridiagonal_solve") - end subroutine tridiagonal_solve !}}} !*********************************************************************** @@ -529,7 +702,7 @@ end subroutine tridiagonal_solve !}}} !> \brief Initializes ocean momentum horizontal pressure gradient !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine initializes parameters required for the computation of the !> horizontal pressure gradient. ! @@ -550,10 +723,12 @@ subroutine ocn_gm_init(err)!{{{ call mpas_pool_get_config(ocnConfigs, 'config_gravWaveSpeed_trunc',config_gravWaveSpeed_trunc) call mpas_pool_get_config(ocnConfigs, 'config_standardGM_tracer_kappa',config_standardGM_tracer_kappa) call mpas_pool_get_config(ocnConfigs, 'config_max_relative_slope',config_max_relative_slope) - call mpas_pool_get_config(ocnConfigs, 'config_density0',config_density0) call mpas_pool_get_config(ocnConfigs, 'config_Redi_kappa', config_Redi_kappa) - call mpas_pool_get_config(ocnConfigs, 'config_use_standardGM',config_use_standardGM) call mpas_pool_get_config(ocnConfigs, 'config_disable_redi_k33',config_disable_redi_k33) + call mpas_pool_get_config(ocnConfigs, 'config_use_Redi_surface_layer_tapering',config_use_Redi_surface_layer_tapering) + call mpas_pool_get_config(ocnConfigs, 'config_use_Redi_bottom_layer_tapering',config_use_Redi_bottom_layer_tapering) + call mpas_pool_get_config(ocnConfigs, 'config_Redi_surface_layer_tapering_extent',config_Redi_surface_layer_tapering_extent) + call mpas_pool_get_config(ocnConfigs, 'config_Redi_bottom_layer_tapering_depth',config_Redi_bottom_layer_tapering_depth) end subroutine ocn_gm_init!}}} diff --git a/src/core_ocean/shared/mpas_ocn_high_freq_thickness_hmix_del2.F b/src/core_ocean/shared/mpas_ocn_high_freq_thickness_hmix_del2.F index 61a75aae2d..c4528a6275 100644 --- a/src/core_ocean/shared/mpas_ocn_high_freq_thickness_hmix_del2.F +++ b/src/core_ocean/shared/mpas_ocn_high_freq_thickness_hmix_del2.F @@ -13,13 +13,14 @@ !> \author Mark Petersen !> \date July 2013 !> \details -!> This module contains the main driver routine for computing +!> This module contains the main driver routine for computing !> horizontal mixing tendencies for high frequency thickness mixing ! !----------------------------------------------------------------------- module ocn_high_freq_thickness_hmix_del2 + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants @@ -64,8 +65,8 @@ module ocn_high_freq_thickness_hmix_del2 !> \brief Computes Laplacian tendency term for horizontal highFreqThickness mixing !> \author Mark Petersen !> \date July 2013 -!> \details -!> This routine computes the horizontal mixing tendency for +!> \details +!> This routine computes the horizontal mixing tendency for !> high frequency thickness !> based on current state using a Laplacian parameterization. ! @@ -122,6 +123,8 @@ subroutine ocn_high_freq_thickness_hmix_del2_tend(meshPool, highFreqThickness, t if(.not.config_use_highFreqThick_del2) return + call mpas_timer_start("thick hmix del2") + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) @@ -137,15 +140,16 @@ subroutine ocn_high_freq_thickness_hmix_del2_tend(meshPool, highFreqThickness, t call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + !$omp do schedule(runtime) private(invAreaCell, i, iEdge, cell1, cell2, r_tmp, k, hhf_turb_flux, flux) do iCell = 1, nCells - invAreaCell = 1.0 / areaCell(iCell) + invAreaCell = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOncell(iCell) iEdge = edgesOnCell(i, iCell) cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) r_tmp = meshScalingDel2(iEdge) * config_highFreqThick_del2 * dvEdge(iEdge) / dcEdge(iEdge) - + do k = 1, maxLevelEdgeTop(iEdge) ! \nabla h^{hf} on edge hhf_turb_flux = highFreqThickness(k,cell2) - highFreqThickness(k,cell1) @@ -158,6 +162,9 @@ subroutine ocn_high_freq_thickness_hmix_del2_tend(meshPool, highFreqThickness, t end do end do + !$omp end do + + call mpas_timer_stop("thick hmix del2") end subroutine ocn_high_freq_thickness_hmix_del2_tend!}}} @@ -168,10 +175,10 @@ end subroutine ocn_high_freq_thickness_hmix_del2_tend!}}} !> \brief Initializes horizontal highFreqThickness mixing !> \author Mark Petersen !> \date July 2013 -!> \details -!> This routine initializes the module for horizontal mixing of +!> \details +!> This routine initializes the module for horizontal mixing of !> high frequency thickness -!> +!> ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_init_routines.F b/src/core_ocean/shared/mpas_ocn_init_routines.F index 78c86e7f37..e501299925 100644 --- a/src/core_ocean/shared/mpas_ocn_init_routines.F +++ b/src/core_ocean/shared/mpas_ocn_init_routines.F @@ -33,11 +33,20 @@ module ocn_init_routines use mpas_vector_reconstruction use mpas_tracer_advection_helpers - use ocn_time_average use ocn_diagnostics use ocn_gm use ocn_constants + use ocn_surface_land_ice_fluxes + use ocn_forcing + + interface ocn_init_add_tau_metadata + module procedure ocn_init_add_tau_metadata_real + module procedure ocn_init_add_tau_metadata_int + module procedure ocn_init_add_tau_metadata_logical + module procedure ocn_init_add_tau_metadata_character + end interface + private !-------------------------------------------------------------------- @@ -57,7 +66,8 @@ module ocn_init_routines ocn_init_routines_compute_mesh_scaling, & ocn_init_routines_setup_sign_and_index_fields, & ocn_init_routines_vert_coord, & - ocn_init_routines_block + ocn_init_routines_block, & + ocn_init_metadata !-------------------------------------------------------------------- ! @@ -130,6 +140,7 @@ subroutine ocn_init_routines_compute_max_level(domain)!{{{ min( maxLevelCell(cellsOnEdge(1,iEdge)), & maxLevelCell(cellsOnEdge(2,iEdge)) ) end do + maxLevelEdgeTop(nEdges+1) = 0 ! maxLevelEdgeBot is the maximum (deepest) of the surrounding cells @@ -138,6 +149,7 @@ subroutine ocn_init_routines_compute_max_level(domain)!{{{ max( maxLevelCell(cellsOnEdge(1,iEdge)), & maxLevelCell(cellsOnEdge(2,iEdge)) ) end do + maxLevelEdgeBot(nEdges+1) = 0 ! maxLevelVertexBot is the maximum (deepest) of the surrounding cells @@ -149,6 +161,7 @@ subroutine ocn_init_routines_compute_max_level(domain)!{{{ maxLevelCell(cellsOnVertex(i,iVertex))) end do end do + maxLevelVertexBot(nVertices+1) = 0 ! maxLevelVertexTop is the minimum (shallowest) of the surrounding cells @@ -160,11 +173,14 @@ subroutine ocn_init_routines_compute_max_level(domain)!{{{ maxLevelCell(cellsOnVertex(i,iVertex))) end do end do + maxLevelVertexTop(nVertices+1) = 0 ! set boundary edge boundaryEdge(:,1:nEdges+1)=1 edgeMask(:,1:nEdges+1)=0 + + do iEdge = 1, nEdges boundaryEdge(1:maxLevelEdgeTop(iEdge),iEdge)=0 edgeMask(1:maxLevelEdgeTop(iEdge),iEdge)=1 @@ -177,6 +193,8 @@ subroutine ocn_init_routines_compute_max_level(domain)!{{{ cellMask(:,1:nCells+1) = 0 boundaryVertex(:,1:nVertices+1) = 0 vertexMask(:,1:nVertices+1) = 0 + + do iEdge = 1, nEdges do k = 1, nVertLevels if (boundaryEdge(k,iEdge).eq.1) then @@ -324,16 +342,23 @@ subroutine ocn_init_routines_compute_mesh_scaling(meshPool, scaleHmixWithMesh, m ! ! Compute the scaling factors to be used in the del2 and del4 dissipation ! - meshScalingDel2(:) = 1.0 - meshScalingDel4(:) = 1.0 - meshScaling(:) = 1.0 + ! Typical use cases have the minval(meshScaling)==1. + ! meshScaling values of approximately 1 indicate the highest resolution of the domain. + + meshScalingDel2(:) = 1.0_RKIND + meshScalingDel4(:) = 1.0_RKIND + meshScaling(:) = 1.0_RKIND + if (scaleHmixWithMesh) then do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - meshScalingDel2(iEdge) = 1.0 / ( ((meshDensity(cell1) + meshDensity(cell2) ) / 2.0) / maxMeshDensity)**(3.0 / 4.0) ! goes as dc**3 - meshScalingDel4(iEdge) = 1.0 / ( ((meshDensity(cell1) + meshDensity(cell2) ) / 2.0) / maxMeshDensity)**(3.0 / 4.0) ! goes as dc**3 - meshScaling(iEdge) = 1.0 / ( ((meshDensity(cell1) + meshDensity(cell2) ) / 2.0) / maxMeshDensity)**(1.0 / 4.0) + meshScalingDel2(iEdge) = 1.0_RKIND / ( ((meshDensity(cell1) + meshDensity(cell2) ) / 2.0_RKIND) & + / maxMeshDensity)**(1.0_RKIND / 4.0_RKIND) ! goes as dc**1 + meshScalingDel4(iEdge) = 1.0_RKIND / ( ((meshDensity(cell1) + meshDensity(cell2) ) / 2.0_RKIND) & + / maxMeshDensity)**(3.0_RKIND / 4.0_RKIND) ! goes as dc**3 + meshScaling(iEdge) = 1.0_RKIND / ( ((meshDensity(cell1) + meshDensity(cell2) ) / 2.0_RKIND) & + / maxMeshDensity)**(1.0_RKIND / 4.0_RKIND) end do end if @@ -358,6 +383,8 @@ subroutine ocn_init_routines_vert_coord(domain)!{{{ type (mpas_pool_type), pointer :: statePool type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: forcingPool type (mpas_pool_type), pointer :: verticalMeshPool type (dm_info) :: dminfo @@ -367,29 +394,26 @@ subroutine ocn_init_routines_vert_coord(domain)!{{{ integer :: iTracer, cell, cell1, cell2 real (kind=RKIND) :: normalThicknessFluxSum, thicknessSum, hEdge1, zMidPBC - integer, dimension(:), pointer :: maxLevelCell + integer, dimension(:), pointer :: maxLevelCell, landIceMask real (kind=RKIND), dimension(:), pointer :: refBottomDepth, & refBottomDepthTopOfCell, vertCoordMovementWeights, bottomDepth, refZMid, refLayerThickness real (kind=RKIND), dimension(:), allocatable :: minBottomDepth, minBottomDepthMid, zMidZLevel - real (kind=RKIND), dimension(:,:), pointer :: layerThickness, restingThickness - real (kind=RKIND), dimension(:,:,:), pointer :: tracers - integer, pointer :: nVertLevels, nCells, num_tracers + real (kind=RKIND), dimension(:,:), pointer :: layerThickness + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroup + integer, pointer :: nVertLevels, nCells logical :: consistentSSH - real (kind=RKIND), pointer :: config_min_pbc_fraction - logical, pointer :: config_do_restart, config_alter_ICs_for_pbcs, config_check_ssh_consistency - logical, pointer :: config_check_zlevel_consistency, config_set_restingThickness_to_IC - character (len=StrKIND), pointer :: config_vert_coord_movement, config_pbc_alteration_type + logical, pointer :: config_do_restart, config_check_ssh_consistency + logical, pointer :: config_check_zlevel_consistency + character (len=StrKIND), pointer :: config_vert_coord_movement + + type (mpas_pool_iterator_type) :: groupItr call mpas_pool_get_config(domain % configs, 'config_vert_coord_movement', config_vert_coord_movement) call mpas_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) - call mpas_pool_get_config(domain % configs, 'config_alter_ICs_for_pbcs', config_alter_ICs_for_pbcs) - call mpas_pool_get_config(domain % configs, 'config_pbc_alteration_type', config_pbc_alteration_type) call mpas_pool_get_config(domain % configs, 'config_check_ssh_consistency', config_check_ssh_consistency) call mpas_pool_get_config(domain % configs, 'config_check_zlevel_consistency', config_check_zlevel_consistency) - call mpas_pool_get_config(domain % configs, 'config_min_pbc_fraction', config_min_pbc_fraction) - call mpas_pool_get_config(domain % configs, 'config_set_restingThickness_to_IC', config_set_restingThickness_to_IC) ! Initialize z-level mesh variables from h, read in from input file. block => domain % blocklist @@ -397,9 +421,10 @@ subroutine ocn_init_routines_vert_coord(domain)!{{{ call mpas_pool_get_subpool(block % structs, 'state', statePool) call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) call mpas_pool_get_subpool(block % structs, 'verticalMesh', verticalMeshPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) - call mpas_pool_get_array(statePool, 'tracers', tracers, 1) call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) call mpas_pool_get_array(meshPool, 'refBottomDepthTopOfCell', refBottomDepthTopOfCell) @@ -407,140 +432,62 @@ subroutine ocn_init_routines_vert_coord(domain)!{{{ call mpas_pool_get_array(meshPool, 'vertCoordMovementWeights', vertCoordMovementWeights) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_array(verticalMeshPool, 'restingThickness', restingThickness) call mpas_pool_get_array(verticalMeshPool, 'refZMid', refZMid) call mpas_pool_get_array(verticalMeshPool, 'refLayerThickness', refLayerThickness) call mpas_pool_get_dimension(meshPool, 'nCells', nCells) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(statePool, 'num_tracers', num_tracers) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) ! TopOfCell needed where zero depth for the very top may be referenced. - refBottomDepthTopOfCell(1) = 0.0 + refBottomDepthTopOfCell(1) = 0.0_RKIND do k = 1, nVertLevels refBottomDepthTopOfCell(k+1) = refBottomDepth(k) refLayerThickness(k) = refBottomDepth(k) - refBottomDepthTopOfCell(k) - refZMid(k) = - refBottomDepthTopOfCell(k) - refLayerThickness(k)/2.0 + refZMid(k) = - refBottomDepthTopOfCell(k) - refLayerThickness(k)/2.0_RKIND end do ! Initialization of vertCoordMovementWeights. This determines how SSH perturbations ! are distributed throughout the column. if (config_vert_coord_movement.eq.'fixed') then - vertCoordMovementWeights = 0.0 - vertCoordMovementWeights(1) = 1.0 + vertCoordMovementWeights = 0.0_RKIND + vertCoordMovementWeights(1) = 1.0_RKIND elseif (config_vert_coord_movement.eq.'uniform_stretching') then - vertCoordMovementWeights = 1.0 + vertCoordMovementWeights = 1.0_RKIND endif - ! Initial condition files (ocean.nc, produced by basin) include a realistic - ! bottomDepth variable and h,T,S variables for full thickness cells. - ! If running with pbcs, set config_alter_ICs_for_pbc='zlevel_pbcs_on'. Then thin pbc cells - ! will be changed, and h,T,S will be altered to match the pbcs. - ! If running without pbcs, set config_alter_ICs_for_pbc='zlevel_pbcs_off'. Then - ! bottomDepth will be altered so it is full cells everywhere. - ! If your input file does not include bottomDepth, the false option will - ! initialize bottomDepth correctly for a non-pbc run. - - if (.not. config_do_restart .and. config_alter_ICs_for_pbcs) then - - if (config_pbc_alteration_type .eq. 'partial_cell') then - - call mpas_log_write(' Altering bottomDepth to avoid very thin cells.') - call mpas_log_write(' Altering layerThickness and tracer initial conditions to conform with partial bottom cells.') - - allocate(minBottomDepth(nVertLevels),minBottomDepthMid(nVertLevels),zMidZLevel(nVertLevels)) - - ! min_pbc_fraction restricts pbcs from being too small. - ! A typical value is 10%, so pbcs must occupy at least 10% of the cell thickness. - ! If min_pbc_fraction = 0.0, bottomDepth gives the actual depth for that cell. - ! If min_pbc_fraction = 1.0, bottomDepth reverts to discrete z-level depths, same - ! as partial_bottom_cells = .false. - - minBottomDepth(1) = (1.0-config_min_pbc_fraction)*refBottomDepth(1) - minBottomDepthMid(1) = 0.5*(minBottomDepth(1) + refBottomDepthTopOfCell(1)) - zMidZLevel(1) = - 0.5*(refBottomDepth(1) + refBottomDepthTopOfCell(1)) - do k = 2, nVertLevels - minBottomDepth(k) = refBottomDepth(k) - (1.0-config_min_pbc_fraction)*(refBottomDepth(k) - refBottomDepth(k-1)) - minBottomDepthMid(k) = 0.5*(minBottomDepth(k) + refBottomDepthTopOfCell(k)) - zMidZLevel(k) = - 0.5*(refBottomDepth(k) + refBottomDepthTopOfCell(k)) - enddo - - do iCell = 1, nCells - - ! Change value of maxLevelCell for partial bottom cells - k = maxLevelCell(iCell) - if (bottomDepth(iCell) .lt. minBottomDepthMid(k)) then - ! Round up to cell above - maxLevelCell(iCell) = maxLevelCell(iCell) - 1 - bottomDepth(iCell) = refBottomDepth(maxLevelCell(iCell)) - elseif (bottomDepth(iCell) .lt. minBottomDepth(k)) then - ! Round down cell to the min_pbc_fraction. - bottomDepth(iCell) = minBottomDepth(k) - endif - ! reset k to new value of maxLevelCell - k = maxLevelCell(iCell) - - ! Alter thickness of bottom level to account for PBC - layerThickness(k,iCell) = bottomDepth(iCell) - refBottomDepthTopOfCell(k) - - ! Linearly interpolate the initial T&S for new location of bottom cell for PBCs - zMidPBC = -0.5*(bottomDepth(iCell) + refBottomDepthTopOfCell(k)) - km1 = max(k-1,1) - do iTracer = 1, num_tracers - tracers(iTracer,k,iCell) = tracers(iTracer,k,iCell) & - + (tracers(iTracer,km1,iCell) - tracers(iTracer,k,iCell)) & - /(zMidZLevel(km1)-zMidZLevel(k)+1.0e-16) & - *(zMidPBC - zMidZLevel(k)) - enddo - + if (config_check_ssh_consistency) then + ! Check if abs(ssh)>20m. If so, print warning. + consistentSSH = .true. + if ( associated(landIceMask) ) then + do iCell = 1,nCells + if (landIceMask(iCell)==0.and.abs(sum(layerThickness(1:maxLevelCell(iCell),iCell))-bottomDepth(iCell))>20.0_RKIND) then + consistentSSH = .false. + call mpas_log_write(' Warning: Sea surface height is outside of acceptable physical range, i.e. abs(sum(h)-bottomDepth)>20m.', & + MPAS_LOG_ERR) + call mpas_log_write(' iCell: $i, maxLevelCell(iCell): $i, bottomDepth(iCell): $r, sum(h): $r', & + intArgs=(/iCell, maxLevelCell(iCell) /), & + realArgs=(/ bottomDepth(iCell),sum(layerThickness(1:maxLevelCell(iCell),iCell)) /) ) + endif enddo - - deallocate(minBottomDepth,zMidZLevel) - - elseif (config_pbc_alteration_type .eq. 'full_cell') then - - do iCell = 1,nCells - bottomDepth(iCell) = refBottomDepth(maxLevelCell(iCell)) + else ! landIceMask not associated, so no ice shelves + do iCell = 1,nCells + if (abs(sum(layerThickness(1:maxLevelCell(iCell),iCell))-bottomDepth(iCell))>20.0_RKIND) then + consistentSSH = .false. + call mpas_log_write(' Warning: Sea surface height is outside of acceptable physical range, i.e. abs(sum(h)-bottomDepth)>20m.', & + MPAS_LOG_ERR) + call mpas_log_write(' iCell: $i, maxLevelCell(iCell): $i, bottomDepth(iCell): $r, sum(h): $r', & + intArgs=(/iCell, maxLevelCell(iCell) /), & + realArgs=(/ bottomDepth(iCell),sum(layerThickness(1:maxLevelCell(iCell),iCell)) /) ) + endif enddo - - else - - call mpas_log_write(' Incorrect choice of config_pbc_alteration_type.', MPAS_LOG_CRIT) - endif - endif ! .not.config_do_restart - - if (.not. config_do_restart) then - - ! Layer thickness when the ocean is at rest, i.e. without SSH or internal perturbations. - ! This is applied only from the initial condition - if (config_set_restingThickness_to_IC) then - restingThickness = layerThickness - endif - - endif ! .not.config_do_restart.and.config_alter_ICs_for_pbcs - - if (config_check_ssh_consistency) then - consistentSSH = .true. - do iCell = 1,nCells - ! Check if abs(ssh)>2m. If so, print warning. - if (abs(sum(layerThickness(1:maxLevelCell(iCell),iCell))-bottomDepth(iCell))>2.0) then - consistentSSH = .false. - call mpas_log_write(' Warning: abs(sum(h)-bottomDepth)>2m. Most likely, initial layerThickness does not match bottomDepth.', & - MPAS_LOG_ERR) -! mrp log this later - !call mpas_log_write(' iCell, K=maxLevelCell(iCell), bottomDepth(iCell),sum(h),bottomDepth: ', & - ! iCell, maxLevelCell(iCell), bottomDepth(iCell),sum(layerThickness(1:maxLevelCell(iCell),iCell)),bottomDepth(iCell), & - ! layerThickness(maxLevelCell(iCell),iCell) - endif - enddo - if (.not. consistentSSH) then call mpas_log_write('Warning: SSH is not consistent. Most likely, initial layerThickness does not match bottomDepth.') end if @@ -553,11 +500,9 @@ subroutine ocn_init_routines_vert_coord(domain)!{{{ if (bottomDepth(iCell) > refBottomDepth(maxLevelCell(iCell)).or. & bottomDepth(iCell) < refBottomDepthTopOfCell(maxLevelCell(iCell))) then call mpas_log_write(' fatal error: bottomDepth and maxLevelCell do not match:', MPAS_LOG_ERR) -! mrp add this text back in later -! call mpas_log_write(' iCell, maxLevelCell(iCell), bottomDepth(iCell): ', & -! iCell, maxLevelCell(iCell), bottomDepth(iCell) -! call mpas_log_write(' refBottomDepth(maxLevelCell(iCell)), refBottomDepthTopOfCell(maxLevelCell(iCell)): ', & -! refBottomDepth(maxLevelCell(iCell)), refBottomDepthTopOfCell(maxLevelCell(iCell)) + call mpas_log_write(' iCell: $i, maxLevelCell(iCell): $i, bottomDepth(iCell): $r', & + intArgs=(/iCell, maxLevelCell(iCell) /), & + realArgs=(/ bottomDepth(iCell) /) ) endif enddo @@ -587,7 +532,7 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ real (kind=RKIND), intent(in) :: dt integer, intent(out) :: err - type (mpas_pool_type), pointer :: meshPool, averagePool, statePool + type (mpas_pool_type), pointer :: meshPool, statePool, tracersPool type (mpas_pool_type), pointer :: forcingPool, diagnosticsPool, scratchPool integer :: i, iEdge, iCell, k integer :: err1 @@ -603,7 +548,7 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ real (kind=RKIND), dimension(:,:), pointer :: velocityZonal, velocityMeridional real (kind=RKIND), dimension(:,:,:), pointer :: derivTwo - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroup integer, pointer :: nCells, nEdges, nVertices, nVertLevels integer, pointer :: config_horiz_tracer_adv_order @@ -611,6 +556,8 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ logical, pointer :: config_use_standardGM real (kind=RKIND), pointer :: config_maxMeshDensity + type (mpas_pool_iterator_type) :: groupItr + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) call mpas_pool_get_dimension(block % dimensions, 'nEdges', nEdges) call mpas_pool_get_dimension(block % dimensions, 'nVertices', nVertices) @@ -621,7 +568,8 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) call mpas_pool_get_subpool(block % structs, 'scratch', scratchPool) - call mpas_pool_get_subpool(block % structs, 'average', averagePool) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) call mpas_pool_get_array(meshPool, 'derivTwo', derivTwo) call mpas_pool_get_array(meshPool, 'advCoefs', advCoefs) @@ -648,7 +596,6 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, 1) call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, 1) - call mpas_pool_get_array(statePool, 'tracers', tracers, 1) call mpas_pool_get_config(block % configs, 'config_horiz_tracer_adv_order', config_horiz_tracer_adv_order) call mpas_pool_get_config(block % configs, 'config_hmix_scaleWithMesh', config_hmix_scaleWithMesh) @@ -665,37 +612,45 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ boundaryCell) err = ior(err, err1) - call ocn_time_average_init(averagePool) - if (.not. config_do_restart) then do iCell=1,nCells - boundaryLayerDepth(iCell) = layerThickness(1, iCell) * 0.5 + boundaryLayerDepth(iCell) = layerThickness(1, iCell) * 0.5_RKIND end do end if - call mpas_timer_start("diagnostic solve") - call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool) - call mpas_timer_stop("diagnostic solve") + call ocn_diagnostic_solve(dt, statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, tracersPool) - ! initialize velocities and tracers on land to be zero. - areaCell(nCells+1) = -1.0e34 + ! initialize velocities and active tracers on land to be zero. + areaCell(nCells+1) = -1.0e34_RKIND + + layerThickness(:, nCells+1) = 0.0_RKIND - layerThickness(:, nCells+1) = 0.0 do iEdge=1, nEdges - normalVelocity(maxLevelEdgeTop(iEdge)+1:maxLevelEdgeBot(iEdge), iEdge) = 0.0 + normalVelocity(maxLevelEdgeTop(iEdge)+1:maxLevelEdgeBot(iEdge), iEdge) = 0.0_RKIND - normalVelocity(maxLevelEdgeBot(iEdge)+1:nVertLevels,iEdge) = -1.0e34 + normalVelocity(maxLevelEdgeBot(iEdge)+1:nVertLevels,iEdge) = -1.0e34_RKIND end do - do iCell=1,nCells - tracers(:, maxLevelCell(iCell)+1:nVertLevels,iCell) = -1.0e34 + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroup, 1) + if ( associated(tracersGroup) ) then + do iCell=1,nCells + tracersGroup(:, maxLevelCell(iCell)+1:nVertLevels,iCell) = -1.0e34_RKIND + end do + end if + end if end do ! ------------------------------------------------------------------ ! Accumulating various parametrizations of the transport velocity ! ------------------------------------------------------------------ - normalTransportVelocity(:,:) = normalVelocity(:,:) + do iEdge = 1, nEdges + normalTransportVelocity(:, iEdge) = normalVelocity(:, iEdge) + end do + ! Compute normalGMBolusVelocity, relativeSlope and RediDiffVertCoef if respective flags are turned on if (config_use_standardGM) then @@ -703,8 +658,11 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ end if if (config_use_standardGM) then - normalTransportVelocity(:,:) = normalTransportVelocity(:,:) + normalGMBolusVelocity(:,:) + do iEdge = 1, nEdges + normalTransportVelocity(:, iEdge) = normalTransportVelocity(:, iEdge) + normalGMBolusVelocity(:, iEdge) + end do end if + ! ------------------------------------------------------------------ ! End: Accumulating various parametrizations of the transport velocity ! ------------------------------------------------------------------ @@ -714,7 +672,7 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ call mpas_rbf_interp_initialize(meshPool) call mpas_initialize_tangent_vectors(meshPool, edgeTangentVectors) - call mpas_init_reconstruct(meshPool) + call mpas_init_reconstruct(meshPool, includeHalos=.true.) call mpas_reconstruct(meshPool, normalVelocity, & velocityX, & @@ -730,8 +688,177 @@ subroutine ocn_init_routines_block(block, dt, err)!{{{ call mpas_pool_initialize_time_levels(statePool) + ! compute land-ice fluxes for potential output at startup + call ocn_forcing_build_fraction_absorbed_array(meshPool, statePool, diagnosticsPool, forcingPool, err1, 1) + err = ior(err, err1) + call ocn_surface_land_ice_fluxes_build_arrays(meshPool, diagnosticsPool, & + forcingPool, scratchPool, statePool, dt, err1) + err = ior(err, err1) + + + end subroutine ocn_init_routines_block!}}} + +!*********************************************************************** +! +! routine ocn_init_metadata +! +!> \brief Initialize any metadata for this processor +!> \author Doug Jacobsen +!> \date 08/05/2016 +!> \details +!> This routine sets up any metadata for this MPI task and it's associated threads. +!> The meta data could be related to performance data, or information about +!> all the blocks on this processor. +! +!----------------------------------------------------------------------- + subroutine ocn_init_metadata(domain)!{{{ + type (domain_type), intent(inout) :: domain + + type (block_type), pointer :: block + + character (len=StrKIND) :: metaDataName + + integer :: iHalo + integer :: numBlocks + integer, dimension(:), pointer :: nCellsArray, nEdgesArray, nVerticesArray + + numBlocks = 0 + + block => domain % blocklist + do while ( associated(block) ) + numBlocks = numBlocks + 1 + + call mpas_pool_get_dimension(block % dimensions, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(block % dimensions, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(block % dimensions, 'nVerticesArray', nVerticesArray) + + call ocn_init_add_tau_metadata( 'numCellHalos', size(nCellsArray) ) + call ocn_init_add_tau_metadata( 'numEdgeHalos', size(nEdgesArray) ) + call ocn_init_add_tau_metadata( 'numVertexHalos', size(nVerticesArray) ) + + do iHalo = 1, size(nCellsArray) + write(metaDataName, '(a8, i2)' ) 'cellHalo', iHalo + call ocn_init_add_tau_metadata( metaDataName, nCellsArray(iHalo) ) + end do + + do iHalo = 1, size(nEdgesArray) + write(metaDataName, '(a8, i2)' ) 'edgeHalo', iHalo + call ocn_init_add_tau_metadata( metaDataName, nEdgesArray(iHalo) ) + end do + + do iHalo = 1, size(nVerticesArray) + write(metaDataName, '(a10, i2)' ) 'vertexHalo', iHalo + call ocn_init_add_tau_metadata( metaDataName, nVerticesArray(iHalo) ) + end do + + block => block % next + end do + + call ocn_init_add_tau_metadata( 'numBlocks', numBlocks ) + + end subroutine ocn_init_metadata!}}} + + +!*********************************************************************** +! +! routine ocn_init_add_tau_metadata_real +! +!> \brief Add tau real metadata +!> \author Doug Jacobsen +!> \date 08/05/2016 +!> \details +!> This routine adds a real value metadata for TAU to this task. +! +!----------------------------------------------------------------------- + subroutine ocn_init_add_tau_metadata_real( dataName, dataValue )!{{{ + character (len=*), intent(in) :: dataName + real (kind=RKIND), intent(in) :: dataValue + + character (len=StrKIND) :: dataString + +#ifdef MPAS_TAU + dataString = '' + write( dataString, * ) dataValue + call tau_metadata( trim(dataName), trim(dataString) ) +#endif + + end subroutine ocn_init_add_tau_metadata_real!}}} + +!*********************************************************************** +! +! routine ocn_init_add_tau_metadata_int +! +!> \brief Add tau integer metadata +!> \author Doug Jacobsen +!> \date 08/05/2016 +!> \details +!> This routine adds an integer value metadata for TAU to this task. +! +!----------------------------------------------------------------------- + subroutine ocn_init_add_tau_metadata_int( dataName, dataValue )!{{{ + character (len=*), intent(in) :: dataName + integer, intent(in) :: dataValue + + character (len=StrKIND) :: dataString + +#ifdef MPAS_TAU + dataString = '' + write( dataString, * ) dataValue + call tau_metadata( trim(dataName), trim(dataString) ) +#endif + + end subroutine ocn_init_add_tau_metadata_int!}}} + +!*********************************************************************** +! +! routine ocn_init_add_tau_metadata_logical +! +!> \brief Add tau logical metadata +!> \author Doug Jacobsen +!> \date 08/05/2016 +!> \details +!> This routine adds a logical value metadata for TAU to this task. +! +!----------------------------------------------------------------------- + subroutine ocn_init_add_tau_metadata_logical( dataName, dataValue )!{{{ + character (len=*), intent(in) :: dataName + logical, intent(in) :: dataValue + + character (len=StrKIND) :: dataString + +#ifdef MPAS_TAU + dataString = '' + write( dataString, * ) dataValue + call tau_metadata( trim(dataName), trim(dataString) ) +#endif + + end subroutine ocn_init_add_tau_metadata_logical!}}} + +!*********************************************************************** +! +! routine ocn_init_add_tau_metadata_character +! +!> \brief Add tau character metadata +!> \author Doug Jacobsen +!> \date 08/05/2016 +!> \details +!> This routine adds a character value metadata for TAU to this task. +! +!----------------------------------------------------------------------- + subroutine ocn_init_add_tau_metadata_character( dataName, dataValue )!{{{ + character (len=*), intent(in) :: dataName + character (len=*), intent(in) :: dataValue + +#ifdef MPAS_TAU + call tau_metadata( trim(dataName), trim(dataValue) ) +#endif + + end subroutine ocn_init_add_tau_metadata_character!}}} + + + end module ocn_init_routines ! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_sea_ice.F b/src/core_ocean/shared/mpas_ocn_sea_ice.F index 2b8b077c28..2b759e5be1 100644 --- a/src/core_ocean/shared/mpas_ocn_sea_ice.F +++ b/src/core_ocean/shared/mpas_ocn_sea_ice.F @@ -24,6 +24,7 @@ module ocn_sea_ice use mpas_pool_routines use mpas_timekeeping use ocn_constants + use ocn_equation_of_state implicit none private @@ -64,7 +65,7 @@ module ocn_sea_ice !> \brief Performs the formation of Sea Ice within the ocean. !> \author Doug Jacobsen !> \date 08/19/2013 -!> \details +!> \details !> ocn_sea_ice_formation performs the adjustment of tracer values !> and layerThickness based on the formation of frazil ice within the ocean. ! @@ -117,7 +118,6 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye real (kind=RKIND) :: referenceSalinity, iceSalinity real (kind=RKIND) :: freezingTemp, density_ice real (kind=RKIND), dimension(:), allocatable :: iceTracer - real (kind=RKIND), pointer :: config_density0 if(.not. frazilFormationOn) return @@ -126,15 +126,17 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) nTracers = size(tracers, dim=1) - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) allocate(iceTracer(nTracers)) iceTracer = 0.0_RKIND iceTracer(indexSalinity) = sea_ice_salinity * ppt_to_salt + density_ice = rho_ice + !$omp do schedule(runtime) private(maxLevel, netEnergyChange, k, freezingTemp, availableEnergyChange, energyChange, & + !$omp temperatureChange, thicknessChange, iceThicknessChange, iTracer) do iCell = 1, nCellsSolve ! Check performance of these two loop definitions ! do iCell = nCellsSolve, 1, -1 maxLevel = min(maxLevelCell(iCell), verticalLevelCap) @@ -142,11 +144,13 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye ! Loop over vertical levels, starting from the bottom of a column do k = maxLevel, 1, -1 - freezingTemp = ocn_freezing_temperature(tracers(indexSalinity, k, iCell)) + freezingTemp = ocn_freezing_temperature(tracers(indexSalinity, k, iCell), & + pressure=0.0_RKIND, & + inLandIceCavity=.false.) ! availableEnergyChange is: ! positive when frazil ice is formed ! negative when frazil ice can be melted - availableEnergyChange = config_density0 * cp_sw * layerThickness(k, iCell) & + availableEnergyChange = rho_sw * cp_sw * layerThickness(k, iCell) & * (freezingTemp - tracers(indexTemperature, k, iCell)) ! energyChange is capped when negative. @@ -155,9 +159,9 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye energyChange = max(availableEnergyChange, -netEnergyChange) ! Compute temperature change in ocean cell due to energy change - temperatureChange = energyChange / ( config_density0 * cp_sw * layerThickness(k, iCell) ) + temperatureChange = energyChange / ( rho_sw * cp_sw * layerThickness(k, iCell) ) ! Compute thickness change in ocean cell due to energy change - thicknessChange = energyChange / ( config_density0 * latent_heat_fusion_mks ) + thicknessChange = energyChange / ( rho_sw * latent_heat_fusion_mks ) ! Compute thickness change in sea ice due to energy change iceThicknessChange = energyChange / ( density_ice * latent_heat_fusion_mks ) @@ -165,11 +169,11 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye do iTracer = 1, nTracers if(iTracer /= indexTemperature) then ! computed as: - ! \rho_{ocn} h_{ocn}^{pre} \theta_{ocn}^{pre} = + ! \rho_{ocn} h_{ocn}^{pre} \theta_{ocn}^{pre} = ! \rho_{ocn}^{new} h_{ocn}^{new} \theta_{ocn}^{new} = \rho_{si} h_{si} \theta_{si} - tracers(iTracer, k, iCell) = ( config_density0 * layerThickness(k,iCell) * tracers(iTracer, k, iCell) & + tracers(iTracer, k, iCell) = ( rho_sw * layerThickness(k,iCell) * tracers(iTracer, k, iCell) & - density_ice * iceThicknessChange * iceTracer(iTracer)) / & - (config_density0 * (layerThickness(k,iCell) + thicknessChange)) + (rho_sw * (layerThickness(k,iCell) + thicknessChange)) end if end do @@ -193,11 +197,13 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye k = 1 netEnergychange = 0.0_RKIND - freezingTemp = ocn_freezing_temperature(tracers(indexSalinity, k, iCell)) + freezingTemp = ocn_freezing_temperature(tracers(indexSalinity, k, iCell), & + pressure=0.0_RKIND, & + inLandIceCavity=.false.) ! availableEnergyChange is: ! positive when frazil ice is formed ! negative when frazil ice can be melted - availableEnergyChange = config_density0 * cp_sw * layerThickness(k, iCell) & + availableEnergyChange = rho_sw * cp_sw * layerThickness(k, iCell) & * (freezingTemp - tracers(indexTemperature, k, iCell)) ! energyChange is capped when negative. @@ -207,9 +213,9 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye energyChange = max(availableEnergyChange, -seaIceEnergy(iCell)) ! Compute temperature change in ocean cell due to energy change - temperatureChange = energyChange / ( config_density0 * cp_sw * layerThickness(k, iCell) ) + temperatureChange = energyChange / ( rho_sw * cp_sw * layerThickness(k, iCell) ) ! Compute thickness change in ocean cell due to energy change - thicknessChange = energyChange / ( config_density0 * latent_heat_fusion_mks ) + thicknessChange = energyChange / ( rho_sw * latent_heat_fusion_mks ) ! Compute thickness change in sea ice due to energy change iceThicknessChange = energyChange / ( density_ice * latent_heat_fusion_mks ) @@ -217,11 +223,11 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye do iTracer = 1, nTracers if(iTracer /= indexTemperature) then ! computed as: - ! \rho_{ocn} h_{ocn}^{pre} \theta_{ocn}^{pre} = + ! \rho_{ocn} h_{ocn}^{pre} \theta_{ocn}^{pre} = ! \rho_{ocn}^{new} h_{ocn}^{new} \theta_{ocn}^{new} = \rho_{si} h_{si} \theta_{si} - tracers(iTracer, k, iCell) = ( config_density0 * layerThickness(k,iCell) * tracers(iTracer, k, iCell) & + tracers(iTracer, k, iCell) = ( rho_sw * layerThickness(k,iCell) * tracers(iTracer, k, iCell) & - density_ice * iceThicknessChange * iceTracer(iTracer)) / & - (config_density0 * (layerThickness(k,iCell) + thicknessChange)) + (rho_sw * (layerThickness(k,iCell) + thicknessChange)) end if end do @@ -235,30 +241,12 @@ subroutine ocn_sea_ice_formation(meshPool, indexTemperature, indexSalinity, laye seaIceEnergy(iCell) = seaIceEnergy(iCell) + energyChange end if end do + !$omp end do deallocate(iceTracer) end subroutine ocn_sea_ice_formation!}}} -!*********************************************************************** -! -! function ocn_freezing_temperature -! -!> \brief Computes the freezing temperature of the ocean. -!> \author Doug Jacobsen -!> \date 08/29/2013 -!> \details -!> This routine computes the freezing temperature of the ocean at a given -!> salinity value. -! -!----------------------------------------------------------------------- - real (kind=RKIND) function ocn_freezing_temperature(salinity)!{{{ - real (kind=RKIND) :: salinity !< Input: Salinity value of water for freezing temperature - - ocn_freezing_temperature = -1.8 - end function ocn_freezing_temperature!}}} - - !*********************************************************************** ! ! routine ocn_sea_ice_init @@ -266,7 +254,7 @@ end function ocn_freezing_temperature!}}} !> \brief Initializes ocean sea ice module. !> \author Doug Jacobsen !> \date 08/19/2013 -!> \details +!> \details !> This routine initializes the ocean sea ice module and variables.. ! !----------------------------------------------------------------------- @@ -276,16 +264,17 @@ subroutine ocn_sea_ice_init(nVertLevels, err)!{{{ integer, intent(in) :: nVertLevels !< Input: Number of vertical levels suggested for level cap integer, intent(out) :: err !< Output: error flag - logical, pointer :: config_frazil_ice_formation, config_monotonic + logical, pointer :: config_use_frazil_ice_formation, config_monotonic, config_frazil_in_open_ocean err = 0 - call mpas_pool_get_config(ocnConfigs, 'config_frazil_ice_formation', config_frazil_ice_formation) + call mpas_pool_get_config(ocnConfigs, 'config_use_frazil_ice_formation', config_use_frazil_ice_formation) + call mpas_pool_get_config(ocnConfigs, 'config_frazil_in_open_ocean', config_frazil_in_open_ocean) call mpas_pool_get_config(ocnConfigs, 'config_monotonic', config_monotonic) frazilFormationOn = .false. - if(config_frazil_ice_formation) then + if(config_use_frazil_ice_formation .and. config_frazil_in_open_ocean) then frazilFormationOn = .true. end if diff --git a/src/core_ocean/shared/mpas_ocn_surface_bulk_forcing.F b/src/core_ocean/shared/mpas_ocn_surface_bulk_forcing.F new file mode 100644 index 0000000000..3348729164 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_surface_bulk_forcing.F @@ -0,0 +1,506 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_surface_bulk_forcing +! +!> \brief MPAS ocean bulk forcing +!> \author Doug Jacobsen +!> \date 04/25/12 +!> \details +!> This module contains routines for building the forcing arrays, +!> if bulk forcing is used. +! +!----------------------------------------------------------------------- + +module ocn_surface_bulk_forcing + + use mpas_timer + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_timekeeping + use ocn_constants + use ocn_equation_of_state + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_surface_bulk_forcing_tracers, & + ocn_surface_bulk_forcing_vel, & + ocn_surface_bulk_forcing_thick, & + ocn_surface_bulk_forcing_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + logical :: bulkWindStressOn, bulkThicknessFluxOn + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_surface_bulk_forcing_tracers +! +!> \brief Determines the tracers forcing array used for the bulk forcing. +!> \author Doug Jacobsen +!> \date 04/25/12 +!> \details +!> This routine computes the tracers forcing arrays used later in MPAS. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_bulk_forcing_tracers(meshPool, groupName, forcingPool, tracerGroup, & + tracersSurfaceFlux, tracersSurfaceFluxRunoff, tracersSurfaceFluxRemoved, dt, layerThickness, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + character (len=*) :: groupName !< Input: Name of tracer group + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + real (kind=RKIND), dimension(:,:,:), intent(inout) :: tracerGroup + real (kind=RKIND), dimension(:,:), intent(inout) :: tracersSurfaceFlux !< Input/Output: Surface flux for tracer group + real (kind=RKIND), dimension(:,:), intent(inout) :: & + tracersSurfaceFluxRunoff !< Input/Output: Surface flux for tracer group due to river runoff + real (kind=RKIND), dimension(:,:), intent(inout) :: & + tracersSurfaceFluxRemoved !< Input/Output: Accumulator for ignored Surface flux for tracer group + real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness + real (kind=RKIND), intent(in) :: dt + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + call mpas_timer_start("bulk_" // trim(groupName)) + if ( trim(groupName) == 'activeTracers' ) then + call ocn_surface_bulk_forcing_active_tracers(meshPool, forcingPool, tracerGroup, & + tracersSurfaceFlux, tracersSurfaceFluxRunoff, tracersSurfaceFluxRemoved, layerThickness, dt, err) + end if + call mpas_timer_stop("bulk_" // trim(groupName)) + + end subroutine ocn_surface_bulk_forcing_tracers!}}} + +!*********************************************************************** +! +! routine ocn_surface_bulk_forcing_vel +! +!> \brief Determines the velocity forcing array used for the bulk forcing. +!> \author Doug Jacobsen +!> \date 04/25/12 +!> \details +!> This routine computes the velocity forcing arrays used later in MPAS. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_bulk_forcing_vel(meshPool, forcingPool, surfaceStress, surfaceStressMagnitude, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + type (mpas_pool_type), intent(in) :: forcingPool !< Input: Forcing information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(inout) :: surfaceStress, & !< Input/Output: Array for surface stress + surfaceStressMagnitude !< Input/Output: Array for magnitude of surface stress + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iEdge, cell1, cell2, iCell, nCells, nEdges + integer, dimension(:), pointer :: nCellsArray, nEdgesArray + + integer, dimension(:,:), pointer :: cellsOnEdge + + real (kind=RKIND) :: meridionalAverage, zonalAverage + real (kind=RKIND), dimension(:), pointer :: angleEdge + real (kind=RKIND), dimension(:), pointer :: windStressZonal, windStressMeridional + + err = 0 + + if ( .not. bulkWindStressOn ) return + + call mpas_timer_start("bulk_ws", .false.) + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + + call mpas_pool_get_array(meshPool, 'angleEdge', angleEdge) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + + call mpas_pool_get_array(forcingPool, 'windStressZonal', windStressZonal) + call mpas_pool_get_array(forcingPool, 'windStressMeridional', windStressMeridional) + + nEdges = nEdgesArray( 4 ) + nCells = nCellsArray( 3 ) + + ! Convert CESM wind stress to MPAS-O wind stress + !$omp do schedule(runtime) private(cell1, cell2, zonalAverage, meridionalAverage) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + + zonalAverage = 0.5_RKIND * (windStressZonal(cell1) + windStressZonal(cell2)) + meridionalAverage = 0.5_RKIND * (windStressMeridional(cell1) + windStressMeridional(cell2)) + + surfaceStress(iEdge) = surfaceStress(iEdge) + cos(angleEdge(iEdge)) * zonalAverage + sin(angleEdge(iEdge)) & + * meridionalAverage + end do + !$omp end do + + ! Build surface fluxes at cell centers + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceStressMagnitude(iCell) = surfaceStressMagnitude(iCell) + sqrt( windStressZonal(iCell)**2 & + + windStressMeridional(iCell)**2 ) + end do + !$omp end do + + call mpas_timer_stop("bulk_ws") + + end subroutine ocn_surface_bulk_forcing_vel!}}} + +!*********************************************************************** +! +! routine ocn_surface_bulk_forcing_thick +! +!> \brief Determines the thickness forcing array used for the bulk forcing. +!> \author Doug Jacobsen +!> \date 04/25/12 +!> \details +!> This routine computes the thickness forcing arrays used later in MPAS. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_bulk_forcing_thick(meshPool, forcingPool, surfaceThicknessFlux, surfaceThicknessFluxRunoff, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + real (kind=RKIND), dimension(:), intent(inout) :: surfaceThicknessFlux !< Input/Output: Array for surface thickness flux + real (kind=RKIND), dimension(:), intent(inout) :: & + surfaceThicknessFluxRunoff !< Input/Output: Array for surface thickness flux due to river runoff + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, nCells, nEdges + integer, dimension(:), pointer :: nCellsArray + + integer, dimension(:,:), pointer :: cellsOnEdge + + real (kind=RKIND), dimension(:), pointer :: evaporationFlux, snowFlux + real (kind=RKIND), dimension(:), pointer :: seaIceFreshWaterFlux, riverRunoffFlux, iceRunoffFlux + real (kind=RKIND), dimension(:), pointer :: rainFlux + + err = 0 + + if ( .not. bulkThicknessFluxOn ) return + + call mpas_timer_start("bulk_thick", .false.) + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + + call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) + call mpas_pool_get_array(forcingPool, 'snowFlux', snowFlux) + call mpas_pool_get_array(forcingPool, 'seaIceFreshWaterFlux', seaIceFreshWaterFlux) + call mpas_pool_get_array(forcingPool, 'riverRunoffFlux', riverRunoffFlux) + call mpas_pool_get_array(forcingPool, 'iceRunoffFlux', iceRunoffFlux) + call mpas_pool_get_array(forcingPool, 'rainFlux', rainFlux) + + nCells = nCellsArray( 3 ) + + ! Build surface fluxes at cell centers + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceThicknessFlux(iCell) = surfaceThicknessFlux(iCell) + ( snowFlux(iCell) + rainFlux(iCell) + evaporationFlux(iCell) & + + seaIceFreshWaterFlux(iCell) + iceRunoffFlux(iCell) ) / rho_sw + surfaceThicknessFluxRunoff(iCell) = riverRunoffFlux(iCell) / rho_sw + end do + !$omp end do + + call mpas_timer_stop("bulk_thick") + + end subroutine ocn_surface_bulk_forcing_thick!}}} + +!*********************************************************************** +! +! routine ocn_surface_bulk_forcing_init +! +!> \brief Initializes bulk forcing module +!> \author Doug Jacobsen +!> \date 04/25/12 +!> \details +!> This routine initializes the bulk forcing module. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_bulk_forcing_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + + logical, pointer :: config_use_bulk_wind_stress, config_use_bulk_thickness_flux + + err = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_use_bulk_wind_stress', config_use_bulk_wind_stress) + call mpas_pool_get_config(ocnConfigs, 'config_use_bulk_thickness_flux', config_use_bulk_thickness_flux) + + bulkWindStressOn = config_use_bulk_wind_stress + bulkThicknessFluxOn = config_use_bulk_thickness_flux + + end subroutine ocn_surface_bulk_forcing_init!}}} + +!*********************************************************************** +! +! Private module subroutines +! +!*********************************************************************** + + +!*********************************************************************** +! +! routine ocn_surface_bulk_forcing_active_tracers +! +!> \brief Determines the active tracers forcing array used for the bulk forcing. +!> \author Doug Jacobsen +!> \date 04/25/12 +!> \details +!> This routine computes the active tracers forcing arrays used later in MPAS. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_bulk_forcing_active_tracers(meshPool, forcingPool, tracerGroup, & + tracersSurfaceFlux, tracersSurfaceFluxRunoff, tracersSurfaceFluxRemoved, layerThickness, dt, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + real (kind=RKIND), dimension(:,:), intent(inout) :: tracersSurfaceFlux + real (kind=RKIND), dimension(:,:), intent(inout) :: tracersSurfaceFluxRunoff + real (kind=RKIND), dimension(:,:), intent(inout) :: tracersSurfaceFluxRemoved + real (kind=RKIND), dimension(:,:,:), intent(inout) :: tracerGroup + real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness + real (kind=RKIND), intent(in) :: dt + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, nCells + integer, pointer :: index_temperature_flux, index_salinity_flux + integer, dimension(:), pointer :: nCellsArray + + type(mpas_pool_type),pointer :: tracersSurfaceFluxPool + + real (kind=RKIND), dimension(:), pointer :: latentHeatFlux, sensibleHeatFlux, longWaveHeatFluxUp, longWaveHeatFluxDown, & + seaIceHeatFlux, evaporationFlux, riverRunoffFlux + real (kind=RKIND), dimension(:), pointer :: seaIceFreshWaterFlux, seaIceSalinityFlux, iceRunoffFlux + real (kind=RKIND), dimension(:), pointer :: shortWaveHeatFlux, penetrativeTemperatureFlux + real (kind=RKIND), dimension(:), pointer :: snowFlux, rainFlux + real (kind=RKIND) :: requiredSalt, allowedSalt + + err = 0 + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceFlux',tracersSurfaceFluxPool) + + call mpas_pool_get_dimension(tracersSurfaceFluxPool, 'index_temperatureSurfaceFlux', index_temperature_flux) + call mpas_pool_get_dimension(tracersSurfaceFluxPool, 'index_salinitySurfaceFlux', index_salinity_flux) + + call mpas_pool_get_array(forcingPool, 'latentHeatFlux', latentHeatFlux) + call mpas_pool_get_array(forcingPool, 'sensibleHeatFlux', sensibleHeatFlux) + call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxUp', longWaveHeatFluxUp) + call mpas_pool_get_array(forcingPool, 'longWaveHeatFluxDown', longWaveHeatFluxDown) + call mpas_pool_get_array(forcingPool, 'seaIceHeatFlux', seaIceHeatFlux) + call mpas_pool_get_array(forcingPool, 'rainFlux', rainFlux) + call mpas_pool_get_array(forcingPool, 'snowFlux', snowFlux) + call mpas_pool_get_array(forcingPool, 'shortWaveHeatFlux', shortWaveHeatFlux) + call mpas_pool_get_array(forcingPool, 'evaporationFlux', evaporationFlux) + + call mpas_pool_get_array(forcingPool, 'seaIceFreshWaterFlux', seaIceFreshWaterFlux) + call mpas_pool_get_array(forcingPool, 'seaIceSalinityFlux', seaIceSalinityFlux) + call mpas_pool_get_array(forcingPool, 'iceRunoffFlux', iceRunoffFlux) + call mpas_pool_get_array(forcingPool, 'riverRunoffFlux', riverRunoffFlux) + call mpas_pool_get_array(forcingPool, 'penetrativeTemperatureFlux', penetrativeTemperatureFlux) + + nCells = nCellsArray( 3 ) + + ! Build surface fluxes at cell centers + !$omp do schedule(runtime) private(allowedSalt, requiredSalt) + do iCell = 1, nCells + tracersSurfaceFlux(index_temperature_flux, iCell) = tracersSurfaceFlux(index_temperature_flux, iCell) & + + (latentHeatFlux(iCell) + sensibleHeatFlux(iCell) & + + longWaveHeatFluxUp(iCell) + longWaveHeatFluxDown(iCell) & + + seaIceHeatFlux(iCell) - (snowFlux(iCell) + iceRunoffFlux(iCell)) & + * latent_heat_fusion_mks) * hflux_factor + + ! Negative seaIceSalinityFlux is an extraction of salt from the ocean + ! So, we negate seaIceSalinityFlux when determining how much salt this flux needs. + requiredSalt = - seaIceSalinityFlux(iCell) * sflux_factor * dt / layerThickness(1, iCell) + allowedSalt = min( 4.0_RKIND, tracerGroup(index_salinity_flux, 1, iCell) ) + + if ( allowedSalt < requiredSalt ) then + tracersSurfaceFluxRemoved(index_salinity_flux, iCell) = tracersSurfaceFluxRemoved(index_salinity_flux, iCell) & + + ( 1 - allowedSalt / requiredSalt ) * seaIceSalinityFlux(iCell) & + * sflux_factor + + tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & + + ( allowedSalt / requiredSalt ) * seaIceSalinityFlux(iCell) & + * sflux_factor + else + tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & + + seaIceSalinityFlux(iCell) * sflux_factor + end if + end do + !$omp end do + ! assume that snow comes in at 0 C + + ! Surface fluxes of water have an associated heat content, but the coupled system does not account for this + ! Assume surface fluxes of water have a temperature dependent on the incoming mass flux. + ! Assume surface fluxes of water have zero salinity. So the RHS forcing is zero for salinity. + ! Only include this heat forcing when bulk thickness is turned on + ! indices on tracerGroup are (iTracer, iLevel, iCell) + if (bulkThicknessFluxOn) then + !$omp do schedule(runtime) + do iCell = 1, nCells + + ! Accumulate fluxes that use the surface temperature + tracersSurfaceFlux(index_temperature_flux, iCell) = tracersSurfaceFlux(index_temperature_flux, iCell) & + + (rainFlux(iCell) + evaporationFlux(iCell)) * tracerGroup(index_temperature_flux,1,iCell) / rho_sw + + ! Runoff can only have a minimum temperature of 0.0C, since it is fresh water. + tracersSurfaceFluxRunoff(index_temperature_flux,iCell) = riverRunoffFlux(iCell) & + * max(tracerGroup(index_temperature_flux,1,iCell), 0.0_RKIND) / rho_sw + + ! Accumulate fluxes that use the freezing point + tracersSurfaceFlux(index_temperature_flux, iCell) = tracersSurfaceFlux(index_temperature_flux, iCell) & + + seaIceFreshWaterFlux(iCell) * ocn_freezing_temperature( tracerGroup(index_salinity_flux, 1, iCell) , & + pressure=0.0_RKIND, & + inLandIceCavity=.false.) / rho_sw + + ! Fields with zero temperature are not accumulated. These include: + ! snowFlux + ! iceRunoffFlux + + end do + !$omp end do + endif ! bulkThicknessFluxOn + + ! convert short wave heat flux to a temperature flux + !$omp do schedule(runtime) + do iCell = 1, nCells + penetrativeTemperatureFlux(iCell) = shortWaveHeatFlux(iCell) * hflux_factor + end do + !$omp end do + + end subroutine ocn_surface_bulk_forcing_active_tracers!}}} + +end module ocn_surface_bulk_forcing + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_surface_land_ice_fluxes.F b/src/core_ocean/shared/mpas_ocn_surface_land_ice_fluxes.F new file mode 100644 index 0000000000..9a7c7d0080 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_surface_land_ice_fluxes.F @@ -0,0 +1,1009 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_surface_land_ice_fluxes +! +!> \brief MPAS ocean surface land-ice fluxes +!> \author Xylar Asay-Davis +!> \date 10/02/2014 +!> \details +!> This module contains routines for computing surface flux related +!> melting under land-ice. +! +!----------------------------------------------------------------------- + +module ocn_surface_land_ice_fluxes + + use mpas_timer + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + + use ocn_constants + use ocn_equation_of_state + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_surface_land_ice_fluxes_tracers, & + ocn_surface_land_ice_fluxes_vel, & + ocn_surface_land_ice_fluxes_thick, & + ocn_surface_land_ice_fluxes_build_arrays, & + ocn_surface_land_ice_fluxes_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + logical :: landIceFluxesOn, standaloneOn, isomipOn, jenkinsOn, hollandJenkinsOn + + real (kind=RKIND) :: cp_land_ice, rho_land_ice + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_surface_land_ice_fluxes_tracers +! +!> \brief Determines the tracers melt fluxes under land ice +!> \author Xylar Asay-Davis +!> \date 9 September 2015 +!> \details +!> This routine adds land-ice tracer fluxes to the surface flux array +!> used to compute tracer tendencies later in MPAS. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_land_ice_fluxes_tracers(meshPool, groupName, forcingPool, tracersSurfaceFlux, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + character (len=*) :: groupName !< Input: Name of tracer group + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + real (kind=RKIND), dimension(:,:), intent(inout) :: tracersSurfaceFlux !< Input/Output: Surface flux for tracer group + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + err = 0 + + if ( .not. landIceFluxesOn ) return + + call mpas_timer_start("land_ice_" // trim(groupName)) + + if ( trim(groupName) == 'activeTracers' ) then + call ocn_surface_land_ice_fluxes_active_tracers(meshPool, forcingPool, tracersSurfaceFlux, err) + end if + + call mpas_timer_stop("land_ice_" // trim(groupName)) + + end subroutine ocn_surface_land_ice_fluxes_tracers!}}} + +!*********************************************************************** +! +! routine ocn_surface_land_ice_fluxes_vel +! +!> \brief Computes tendency term for top drag +!> \author Xylar Asay-Davis +!> \date 9 September 2015 +!> \details +!> This routine computes the top-drag tendency for momentum +!> based on current state. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_land_ice_fluxes_vel(meshPool, diagnosticsPool, surfaceStress, surfaceStressMagnitude, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + type (mpas_pool_type), intent(in) :: diagnosticsPool !< Input: Diagnostics information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + real (kind=RKIND), dimension(:), intent(inout) :: surfaceStress, & !< Input/Output: Array for total surface stress + surfaceStressMagnitude !< Input/Output: Array for magnitude of surface stress + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + integer :: iEdge, iCell + integer, pointer :: nCells, nEdges + + real (kind=RKIND), dimension(:), pointer :: topDrag, topDragMagnitude + + err = 0 + + if ( .not. landIceFluxesOn ) return + + call mpas_timer_start("top_drag", .false.) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + + call mpas_pool_get_array(diagnosticsPool, 'topDrag', topDrag) + call mpas_pool_get_array(diagnosticsPool, 'topDragMagnitude', topDragMagnitude) + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + surfaceStress(iEdge) = surfaceStress(iEdge) + topDrag(iEdge) + end do + !$omp end do + + ! Build surface stress magnitude at cell centers + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceStressMagnitude(iCell) = surfaceStressMagnitude(iCell) + topDragMagnitude(iCell) + end do + !$omp end do + + call mpas_timer_stop("top_drag") + + !-------------------------------------------------------------------- + + end subroutine ocn_surface_land_ice_fluxes_vel!}}} + +!*********************************************************************** +! +! routine ocn_surface_land_ice_fluxes_thick +! +!> \brief Add land-ice fluxes to surfaceThicknessFlux. +!> \author Xylar Asay-Davis +!> \date 11 September 2015 +!> \details +!> This routine adds land-ice freshwater fluxes to the surface thickness flux +!> to be converted into a thickness tendency later. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_land_ice_fluxes_thick(meshPool, forcingPool, surfaceThicknessFlux, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + real (kind=RKIND), dimension(:), intent(inout) :: surfaceThicknessFlux !< Input/Output: Array for surface thickness flux + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell + integer, pointer :: nCells + + real (kind=RKIND), dimension(:), pointer :: landIceFreshwaterFlux + + err = 0 + + if ( .not. landIceFluxesOn ) return + + call mpas_timer_start("land_ice_thick") + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(forcingPool, 'landIceFreshwaterFlux', landIceFreshwaterFlux) + + ! Build surface fluxes at cell centers + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceThicknessFlux(iCell) = surfaceThicknessFlux(iCell) + landIceFreshwaterFlux(iCell) / rho_sw + end do + !$omp end do + + call mpas_timer_stop("land_ice_thick") + + end subroutine ocn_surface_land_ice_fluxes_thick!}}} + +!*********************************************************************** +! +! routine ocn_surface_land_ice_fluxes_active_tracers +! +!> \brief Adds the active tracers fluxes from land-ice melting. +!> \author Xylar Asay-Davis +!> \date 11 September 2015 +!> \details +!> This routine adds the active tracers fluxes to surface fluxes +!> from which tracer tendencies are computed later. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_land_ice_fluxes_active_tracers(meshPool, forcingPool, tracersSurfaceFlux, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + real (kind=RKIND), dimension(:,:), intent(inout) :: tracersSurfaceFlux + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell + integer, pointer :: nCells + + real (kind=RKIND), dimension(:), pointer :: landIceHeatFlux + + err = 0 + + if ( .not. landIceFluxesOn ) return + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(forcingPool, 'landIceHeatFlux', landIceHeatFlux) + + ! add to surface fluxes at cell centers + !$omp do schedule(runtime) + do iCell = 1, nCells + tracersSurfaceFlux(1, iCell) = tracersSurfaceFlux(1, iCell) + landIceHeatFlux(iCell)/(rho_sw*cp_sw) + end do + !$omp end do + + end subroutine ocn_surface_land_ice_fluxes_active_tracers!}}} + + +!*********************************************************************** +! +! routine ocn_surface_land_ice_fluxes_build_arrays +! +!> \brief Builds the forcing array for land-ice forcing +!> \author Xylar Asay-Davis +!> \date 10/02/2014 +!> \details +!> This routine computes surface fluxes related to land-ice forcing based +!> on diagnostics from the previous time step. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_land_ice_fluxes_build_arrays(meshPool, diagnosticsPool, & + forcingPool, scratchPool, statePool, dt, err) !{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: & + meshPool, & !< Input: mesh information + diagnosticsPool !< Input: diagnostics information + real(kind=RKIND), intent(in) :: dt ! the time step over which to accumulate fluxes + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + type (mpas_pool_type), intent(inout) :: & + forcingPool, & !< Input: Forcing information + scratchPool, & !< Input: scratch field information + statePool !< Input: state field information + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: tracersPool + + integer :: iCell + integer, pointer :: nCellsSolve + + real (kind=RKIND), pointer :: config_land_ice_flux_ISOMIP_gammaT + + logical, pointer :: config_land_ice_flux_useHollandJenkinsAdvDiff + + + real (kind=RKIND) :: freshwaterFlux, heatFlux + + real (kind=RKIND), dimension(:), pointer :: landIcePressure, landIceFraction, & + landIceSurfaceTemperature, & + landIceFrictionVelocity, & + landIceFreshwaterFlux, & + landIceHeatFlux, heatFluxToLandIce, & + freezeInterfaceSalinity, freezeInterfaceTemperature, & + freezeFreshwaterFlux, freezeHeatFlux, & + freezeIceHeatFlux + real (kind=RKIND), dimension(:), pointer :: accumulatedLandIceMassOld, & + accumulatedLandIceMassNew, & + accumulatedLandIceHeatOld, & + accumulatedLandIceHeatNew + + integer, dimension(:), pointer :: landIceMask + + real (kind=RKIND), dimension(:,:), pointer :: landIceBoundaryLayerTracers, & + landIceInterfaceTracers, & + landIceTracerTransferVelocities + integer, pointer :: indexBLT, indexBLS, indexIT, indexIS, indexHeatTrans, indexSaltTrans + + type (field1DReal), pointer :: boundaryLayerTemperatureField, boundaryLayerSalinityField, & + freezeInterfaceSalinityField, freezeInterfaceTemperatureField, & + freezeFreshwaterFluxField, freezeHeatFluxField, & + freezeIceHeatFluxField + + err = 0 + + if ( .not. standaloneOn ) return + + call mpas_timer_start("land_ice_build_arrays") + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_ISOMIP_gammaT', config_land_ice_flux_ISOMIP_gammaT) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_useHollandJenkinsAdvDiff', & + config_land_ice_flux_useHollandJenkinsAdvDiff) + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + call mpas_pool_get_array(diagnosticsPool, 'landIceFrictionVelocity', landIceFrictionVelocity) + + call mpas_pool_get_array(forcingPool, 'landIcePressure', landIcePressure) + call mpas_pool_get_array(diagnosticsPool, 'landIceBoundaryLayerTracers', landIceBoundaryLayerTracers) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceBoundaryLayerTemperature', indexBLT) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceBoundaryLayerSalinity', indexBLS) + + if(jenkinsOn .or. hollandJenkinsOn) then + call mpas_pool_get_array(diagnosticsPool, 'landIceTracerTransferVelocities', landIceTracerTransferVelocities) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceHeatTransferVelocity', indexHeatTrans) + call mpas_pool_get_dimension(diagnosticsPool, 'index_landIceSaltTransferVelocity', indexSaltTrans) + end if + + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + + call mpas_pool_get_array(forcingPool, 'landIceFreshwaterFlux', landIceFreshwaterFlux) + call mpas_pool_get_array(forcingPool, 'landIceHeatFlux', landIceHeatFlux) + call mpas_pool_get_array(forcingPool, 'heatFluxToLandIce', heatFluxToLandIce) + + call mpas_pool_get_array(forcingPool, 'landIceInterfaceTracers', landIceInterfaceTracers) + call mpas_pool_get_dimension(forcingPool, 'index_landIceInterfaceTemperature', indexIT) + call mpas_pool_get_dimension(forcingPool, 'index_landIceInterfaceSalinity', indexIS) + call mpas_pool_get_array(statePool, 'accumulatedLandIceMass', accumulatedLandIceMassNew, 2) + call mpas_pool_get_array(statePool, 'accumulatedLandIceMass', accumulatedLandIceMassOld, 1) + call mpas_pool_get_array(statePool, 'accumulatedLandIceHeat', accumulatedLandIceHeatNew, 2) + call mpas_pool_get_array(statePool, 'accumulatedLandIceHeat', accumulatedLandIceHeatOld, 1) + + if(config_land_ice_flux_useHollandJenkinsAdvDiff) then + call mpas_pool_get_array(forcingPool, 'landIceSurfaceTemperature', landIceSurfaceTemperature) + + call mpas_pool_get_field(scratchPool, 'freezeInterfaceSalinityScratch', freezeInterfaceSalinityField) + call mpas_pool_get_field(scratchPool, 'freezeInterfaceTemperatureScratch', freezeInterfaceTemperatureField) + call mpas_pool_get_field(scratchPool, 'freezeFreshwaterFluxScratch', freezeFreshwaterFluxField) + call mpas_pool_get_field(scratchPool, 'freezeHeatFluxScratch', freezeHeatFluxField) + call mpas_pool_get_field(scratchPool, 'freezeIceHeatFluxScratch', freezeIceHeatFluxField) + call mpas_allocate_scratch_field(freezeInterfaceSalinityField, .true.) + call mpas_allocate_scratch_field(freezeInterfaceTemperatureField, .true.) + call mpas_allocate_scratch_field(freezeFreshwaterFluxField, .true.) + call mpas_allocate_scratch_field(freezeHeatFluxField, .true.) + call mpas_allocate_scratch_field(freezeIceHeatFluxField, .true.) + freezeInterfaceSalinity => freezeInterfaceSalinityField % array + freezeInterfaceTemperature => freezeInterfaceTemperatureField % array + freezeFreshwaterFlux => freezeFreshwaterFluxField % array + freezeHeatFlux => freezeHeatFluxField % array + freezeIceHeatFlux => freezeIceHeatFluxField % array + end if + + if(isomipOn) then + !$omp do schedule(runtime) private(heatFlux) + do iCell = 1, nCellsSolve + if (landIceMask(iCell) == 0) cycle + + ! linearized equaiton for the S and p dependent potential freezing temperature + landIceInterfaceTracers(indexIT,iCell) = ocn_freezing_temperature( & + salinity=landIceBoundaryLayerTracers(indexBLT,iCell), & + pressure=landIcePressure(iCell), & + inLandIceCavity=.true.) + + ! using (3) and (4) from Hunter (2006) + ! or (7) from Jenkins et al. (2001) if gamma constant + ! and no heat flux into ice + ! freshwater flux = density * melt rate is in kg/m^2/s + freshwaterFlux = -rho_sw * config_land_ice_flux_ISOMIP_gammaT * (cp_sw/latent_heat_fusion_mks) & + * (landIceInterfaceTracers(indexIT,iCell)-landIceBoundaryLayerTracers(indexBLT,iCell)) + + landIceFreshwaterFlux(iCell) = landIceFraction(iCell)*freshwaterFlux + + ! Using (13) from Jenkins et al. (2001) + ! heat flux is in W/s + heatFlux = cp_sw*(freshwaterFlux*landIceInterfaceTracers(indexIT,iCell) & + + rho_sw*config_land_ice_flux_ISOMIP_gammaT & + * (landIceInterfaceTracers(indexIT,iCell)-landIceBoundaryLayerTracers(indexBLT,iCell))) + landIceHeatFlux(iCell) = landIceFraction(iCell)*heatFlux + + heatFluxToLandIce(iCell) = 0.0_RKIND + + end do + !$omp end do + end if + + if(jenkinsOn .or. hollandJenkinsOn) then + if(config_land_ice_flux_useHollandJenkinsAdvDiff) then + ! melting solution + call compute_HJ99_melt_fluxes( & + landIceMask, & + landIceBoundaryLayerTracers(indexBLT,:), & + landIceBoundaryLayerTracers(indexBLS,:), & + landIceTracerTransferVelocities(indexHeatTrans,:), & + landIceTracerTransferVelocities(indexSaltTrans,:), & + landIceSurfaceTemperature, & + landIcePressure, & + landIceInterfaceTracers(indexIT,:), & + landIceInterfaceTracers(indexIS,:), & + landIceFreshwaterFlux, & + landIceHeatFlux, & + heatFluxToLandIce, & + nCellsSolve, & + err) + if(err .ne. 0) then + call mpas_log_write( & + 'compute_HJ99_melt_fluxes failed.', & + MPAS_LOG_CRIT) + end if + + ! freezing solution + call compute_melt_fluxes( & + landIceMask, & + landIceBoundaryLayerTracers(indexBLT,:), & + landIceBoundaryLayerTracers(indexBLS,:), & + landIceTracerTransferVelocities(indexHeatTrans,:), & + landIceTracerTransferVelocities(indexSaltTrans,:), & + landIcePressure, & + freezeInterfaceTemperature, & + freezeInterfaceSalinity, & + freezeFreshwaterFlux, & + freezeHeatFlux, & + freezeIceHeatFlux, & + nCellsSolve, & + err) + if(err .ne. 0) then + call mpas_log_write( & + 'compute_melt_fluxes failed.', & + MPAS_LOG_CRIT) + end if + + do iCell = 1, nCellsSolve + if ((landIceMask(iCell) == 0) .or. (landIceFreshwaterFlux(iCell) >= 0.0_RKIND)) cycle + + landIceInterfaceTracers(indexIS,iCell) = freezeInterfaceSalinity(iCell) + landIceInterfaceTracers(indexIT,iCell) = freezeInterfaceTemperature(iCell) + landIceFreshwaterFlux(iCell) = freezeFreshwaterFlux(iCell) + landIceHeatFlux(iCell) = freezeHeatFlux(iCell) + heatFluxToLandIce(iCell) = freezeIceHeatFlux(iCell) + end do + else ! not using Holland and Jenkins advection/diffusion + call compute_melt_fluxes( & + landIceMask, & + landIceBoundaryLayerTracers(indexBLT,:), & + landIceBoundaryLayerTracers(indexBLS,:), & + landIceTracerTransferVelocities(indexHeatTrans,:), & + landIceTracerTransferVelocities(indexSaltTrans,:), & + landIcePressure, & + landIceInterfaceTracers(indexIT,:), & + landIceInterfaceTracers(indexIS,:), & + landIceFreshwaterFlux, & + landIceHeatFlux, & + heatFluxToLandIce, & + nCellsSolve, & + err) + if(err .ne. 0) then + call mpas_log_write( & + 'compute_melt_fluxes failed.', & + MPAS_LOG_CRIT) + end if + end if + + ! modulate the fluxes by the landIceFraction + do iCell = 1, nCellsSolve + if (landIceMask(iCell) == 0) cycle + + landIceFreshwaterFlux(iCell) = landIceFraction(iCell)*landIceFreshwaterFlux(iCell) + landIceHeatFlux(iCell) = landIceFraction(iCell)*landIceHeatFlux(iCell) + heatFluxToLandIce(iCell) = landIceFraction(iCell)*heatFluxToLandIce(iCell) + end do + + end if + + if(config_land_ice_flux_useHollandJenkinsAdvDiff) then + call mpas_deallocate_scratch_field(freezeInterfaceSalinityField, .true.) + call mpas_deallocate_scratch_field(freezeInterfaceTemperatureField, .true.) + call mpas_deallocate_scratch_field(freezeFreshwaterFluxField, .true.) + call mpas_deallocate_scratch_field(freezeHeatFluxField, .true.) + call mpas_deallocate_scratch_field(freezeIceHeatFluxField, .true.) + end if + + ! accumulate land-ice mass and heat + do iCell = 1, nCellsSolve + accumulatedLandIceMassNew(iCell) = accumulatedLandIceMassOld(iCell) - dt*landIceFreshwaterFlux(iCell) + accumulatedLandIceHeatNew(iCell) = accumulatedLandIceHeatOld(iCell) + dt*heatFluxToLandIce(iCell) + end do + + call mpas_timer_stop("land_ice_build_arrays") + + !-------------------------------------------------------------------- + + end subroutine ocn_surface_land_ice_fluxes_build_arrays!}}} + +!*********************************************************************** +! +! routine ocn_surface_land_ice_fluxes_init +! +!> \brief Initializes land-ice forcing +!> \author Xylar Asay-Davis +!> \date 10/02/2014 +!> \details +!> This routine initializes a variety of quantities related to +!> land-ice forcing. +! +!----------------------------------------------------------------------- + + subroutine ocn_surface_land_ice_fluxes_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + + character (len=StrKIND), pointer :: config_land_ice_flux_formulation, config_land_ice_flux_mode + + real (kind=RKIND), pointer :: config_land_ice_flux_cp_ice, & + config_land_ice_flux_rho_ice + + + err = 0 + isomipOn = .false. + jenkinsOn = .false. + hollandJenkinsOn = .false. + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + landIceFluxesOn = (trim(config_land_ice_flux_mode) == 'standalone') & + .or. (trim(config_land_ice_flux_mode) == 'coupled') + if(.not. landIceFluxesOn) return + + standaloneOn = trim(config_land_ice_flux_mode) == 'standalone' + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_formulation', config_land_ice_flux_formulation) + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_cp_ice', config_land_ice_flux_cp_ice) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_rho_ice', config_land_ice_flux_rho_ice) + + if ( trim(config_land_ice_flux_formulation) == 'ISOMIP' ) then + isomipOn = .true. + else if ( trim(config_land_ice_flux_formulation) == 'Jenkins' ) then + jenkinsOn = .true. + else if ( trim(config_land_ice_flux_formulation) == 'HollandJenkins' ) then + hollandJenkinsOn = .true. + else + call mpas_log_write( & + "config_land_ice_flux_formulation not one of 'ISOMIP', 'Jenkins', " & + // "or 'HollandJenkins'.", & + MPAS_LOG_CRIT) + err = 1 + end if + + cp_land_ice = config_land_ice_flux_cp_ice + rho_land_ice = config_land_ice_flux_rho_ice + + !-------------------------------------------------------------------- + + end subroutine ocn_surface_land_ice_fluxes_init!}}} + +!*********************************************************************** +! +! routine ocn_forcing_compute_melt_fluxes +! +!> \brief Computes ocean and ice melt fluxes, etc. +!> \author Xylar Asay-Davis +!> \date 3/27/2015 +!> This routine computes melt fluxes (melt rate, temperature fluxes +!> into the ice and the ocean, and salt flux) as well as the interface +!> temperature and salinity. This routine expects an ice temperature +!> in the bottom layer of ice and ocean temperature and salinity in +!> the top ocean layer as well as the pressure at the ice/ocean interface. +!> +!> The ocean heat and salt transfer velocities are determined based on +!> observations of turbulent mixing rates in the under-ice boundary layer. +!> They should be the product of the friction velocity and a (possibly +!> spatially variable) non-dimenional transfer coefficient. +!> +!> The iceTemperatureDistance is the distance between the location +!> where the iceTemperature is supplied and the ice-ocean interface, +!> used to compute a temperature gradient. The ice thermal conductivity, +!> kappa_land_ice, is zero for the freezing solution from Holland and Jenkins +!> (1999) in which the ice is purely insulating. +! +!----------------------------------------------------------------------- + + + subroutine compute_melt_fluxes( & + mask, & + oceanTemperature, & + oceanSalinity, & + oceanHeatTransferVelocity, & + oceanSaltTransferVelocity, & + interfacePressure, & + outInterfaceTemperature, & + outInterfaceSalinity, & + outFreshwaterFlux, & + outOceanHeatFlux, & + outIceHeatFlux, & + nCells, & + err, & + iceTemperature, & + iceTemperatureDistance, & + kappa_land_ice) !{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, dimension(:), intent(in) :: & + mask !< Input: mask for land-ice fluxes + + real (kind=RKIND), dimension(:), intent(in) :: & + oceanTemperature, & !< Input: ocean temperature in top layer + oceanSalinity, & !< Input: ocean salinity in top layer + oceanHeatTransferVelocity, & !< Input: ocean heat transfer velocity + oceanSaltTransferVelocity, & !< Input: ocean salt transfer velocity + interfacePressure !< Input: pressure at the ice-ocean interface + + integer, intent(in) :: nCells !< Input: number of cells in each array + + real (kind=RKIND), dimension(:), intent(in), optional:: & + iceTemperature, & !< Input: ice temperature in bottom layer + iceTemperatureDistance !< Input: distance to ice temperature from ice-ocean interface + + real (kind=RKIND), intent(in), optional:: & + kappa_land_ice !< Input: the diffusivity of heat in land ice + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(out) :: & + outInterfaceTemperature, & !< Output: ice/ocean temperature at the interface + outInterfaceSalinity, & !< Output: ocean salinity at the interface + outFreshwaterFlux, & !< Output: ocean thickness flux (melt rate) + outOceanHeatFlux, & !< Output: the temperature flux into the ocean + outIceHeatFlux !< Output: the temperature flux into the ice + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND) :: T0, transferVelocityRatio, Tlatent, nu, a, b, c, eta, & + iceHeatFluxCoeff, iceDeltaT, dTf_dS + integer :: iCell + + logical :: coupled + + real (kind=RKIND), parameter :: minInterfaceSalinity = 0.001_RKIND + + err = 0 + coupled = present(iceTemperature) .and. present(iceTemperatureDistance) & + .and. present(kappa_land_ice) + Tlatent = latent_heat_fusion_mks/cp_sw + + !$omp do schedule(runtime) private(iceHeatFluxCoeff, nu, iceDeltaT, T0, transferVelocityRatio, a, b, c) + do iCell = 1, nCells + if (mask(iCell) == 0) cycle + + if(coupled) then + iceHeatFluxCoeff = rho_land_ice*cp_land_ice*kappa_land_ice/iceTemperatureDistance(iCell) + nu = iceHeatFluxCoeff/(rho_sw*cp_sw*oceanHeatTransferVelocity(iCell)) + iceDeltaT = T0 - iceTemperature(iCell) + else + nu = 0.0_RKIND + iceDeltaT = 0.0_RKIND + end if + T0 = ocn_freezing_temperature(salinity=0.0_RKIND, pressure=interfacePressure(iCell), & + inLandIceCavity=.true.) + dTf_dS = ocn_freezing_temperature_salinity_deriv(salinity=0.0_RKIND, pressure=interfacePressure(iCell), & + inLandIceCavity=.true.) + + transferVelocityRatio = oceanSaltTransferVelocity(iCell)/oceanHeatTransferVelocity(iCell) + + a = -dTf_dS*(1.0_RKIND + nu) + b = transferVelocityRatio*Tlatent - nu*iceDeltaT + oceanTemperature(iCell) - T0 + c = -transferVelocityRatio*Tlatent*max(oceanSalinity(iCell), 0.0_RKIND) + + ! a is non-negative; c is strictly non-positive so we never get imaginary roots. + ! Since a can be zero, we need a solution of the quadratic equation for 1/Si instead of Si. + ! Following: https://people.csail.mit.edu/bkph/articles/Quadratics.pdf + ! Since a and -c are are non-negative, the term in the square root is also always >= |b|. + ! In all reasonable cases, b will be strictly positive, since transferVelocityRatio*Tlatent ~ 2 C, + ! T0 ~ -1.8 C and oceanTemperature should never be able to get below about -3 C + ! As long as either b or both a and c are greater than zero, the strictly non-negative root is + outInterfaceSalinity(iCell) = max(-(2.0_RKIND*c)/(b + sqrt(b**2 - 4.0_RKIND*a*c)), minInterfaceSalinity) + + outInterfaceTemperature(iCell) = dTf_dS*outInterfaceSalinity(iCell)+T0 + + outFreshwaterFlux(iCell) = rho_sw*oceanSaltTransferVelocity(iCell) & + * (oceanSalinity(iCell)/outInterfaceSalinity(iCell) - 1.0_RKIND) + + ! According to Jenkins et al. (2001), the temperature fluxes into the ocean are: + ! 1. the advection of meltwater into the top layer (or removal for freezing) + ! 2. the turbulent transfer of heat across the boundary layer, based on the termal driving + outOceanHeatFlux(iCell) = cp_sw*(outFreshwaterFlux(iCell)*outInterfaceTemperature(iCell) & + - rho_sw*oceanHeatTransferVelocity(iCell)*(oceanTemperature(iCell)-outInterfaceTemperature(iCell))) + + ! the temperature fluxes into the ice are: + ! 1. the advection of ice at the interface temperature out of the domain due to melting + ! (or in due to freezing) + ! 2. the diffusion (if any) of heat into the ice, based on temperature difference between + ! the reference point in the ice (either the surface or the middle of the bottom layer) + ! and the interface + outIceHeatFlux(iCell) = -cp_land_ice*outFreshwaterFlux(iCell)*outInterfaceTemperature(iCell) + if(coupled) then + outIceHeatFlux(iCell) = outIceHeatFlux(iCell) & + - iceHeatFluxCoeff*(iceTemperature(iCell) - outInterfaceTemperature(iCell)) + end if + end do + !$omp end do + + !-------------------------------------------------------------------- + + end subroutine compute_melt_fluxes !}}} + + +!*********************************************************************** +! +! routine compute_HJ99_melt_fluxes +! +!> \brief Computes melt fluxes, etc. according to HJ99 +!> \author Xylar Asay-Davis +!> \date 3/28/2015 +!> \details +!> This routine computes melt fluxes (melt rate, temperature fluxes +!> into the ice and the ocean, and salt flux) as well as the interface +!> temperature and salinity. Following Holland and Jenkins (1999), +!> temperature is assumed to be vertically advected and diffused in +!> the ice at a rate determined by the melt rate, so that no +!> heat transfer velocity for the ice need be supplied. Except for +!> very small melt rates, the Holland and Jenkins advection/diffusion +!> solution produces an ice temperature profile that is approximately +!> constant with depth except near the ice-ocean interface. The ice +!> temperature supplied to this routine should be the far-field value, +!> equal to the time-averaged surface temperature. +!> +!> The solution is only appropriate for melting (positive ocean +!> thickness flux). For freezing, the fluxes should be computed using +!> ocn_forcing_compute_melt_fluxes with ``insulating'' ice where +!> the iceHeatTransferVelocity is set to zero. +! +!----------------------------------------------------------------------- + + subroutine compute_HJ99_melt_fluxes( & + mask, & + oceanTemperature, & + oceanSalinity, & + oceanHeatTransferVelocity, & + oceanSaltTransferVelocity, & + iceTemperature, & + interfacePressure, & + outInterfaceTemperature, & + outInterfaceSalinity, & + outFreshwaterFlux, & + outOceanHeatFlux, & + outIceHeatFlux, & + nCells, & + err) !{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + integer, dimension(:), intent(in) :: & + mask !< Input: mask for land-ice fluxes + + real (kind=RKIND), dimension(:), intent(in) :: & + oceanTemperature, & !< Input: ocean temperature in top layer + oceanSalinity, & !< Input: ocean salinity in top layer + oceanHeatTransferVelocity, & !< Input: ocean heat transfer velocity + oceanSaltTransferVelocity, & !< Input: ocean salt transfer velocity + iceTemperature, & !< Input: ice temperature in bottom layer + interfacePressure !< Input: pressure at the ice-ocean interface + + integer, intent(in) :: nCells !< Input: number of cells in each array + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:), intent(out) :: & + outInterfaceTemperature, & !< Output: ice/ocean temperature at the interface + outInterfaceSalinity, & !< Output: ocean salinity at the interface + outFreshwaterFlux, & !< Output: ocean thickness flux (melt rate) + outOceanHeatFlux, & !< Output: the temperature flux into the ocean + outIceHeatFlux !< Output: the temperature flux into the ice + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND) :: T0, cpRatio, transferVelocityRatio, Tlatent, a, b, c, eta, TlatentStar, dTf_dS + + integer :: iCell + + err = 0 + cpRatio = cp_land_ice/cp_sw + !$omp do schedule(runtime) private(T0, transferVelocityRatio, Tlatent, eta, TlatentStar, a, b, c) + do iCell = 1, nCells + if (mask(iCell) == 0) cycle + + T0 = ocn_freezing_temperature(salinity=0.0_RKIND, pressure=interfacePressure(iCell), & + inLandIceCavity=.true.) + dTf_dS = ocn_freezing_temperature_salinity_deriv(salinity=0.0_RKIND, pressure=interfacePressure(iCell), & + inLandIceCavity=.true.) + transferVelocityRatio = (rho_fw/rho_sw)*oceanSaltTransferVelocity(iCell)/oceanHeatTransferVelocity(iCell) + Tlatent = latent_heat_fusion_mks/cp_sw + + eta = cpRatio * transferVelocityRatio + TlatentStar = Tlatent + cpRatio*(T0-iceTemperature(iCell)) + a = -dTf_dS*(1.0_RKIND - eta) + b = (transferVelocityRatio*TlatentStar - eta*dTf_dS*oceanSalinity(iCell) & + + oceanTemperature(iCell) - T0) + c = -transferVelocityRatio*TlatentStar + + ! a is strictly positive; c is strictly negative so we never get imaginary roots + ! The positive root is the one we want (salinity is strictly positive) + outInterfaceSalinity(iCell) = (-b + sqrt(b**2 - 4.0_RKIND*a*c*oceanSalinity(iCell)))/(2.0_RKIND*a) + if (outInterfaceSalinity(iCell) .le. 0.0_RKIND) then + err = 1 + call mpas_log_write( & + 'interfaceSalinity is negative.', & + MPAS_LOG_CRIT) + end if + outInterfaceTemperature(iCell) = dTf_dS*outInterfaceSalinity(iCell)+T0 + + outFreshwaterFlux(iCell) = rho_sw*oceanSaltTransferVelocity(iCell) & + * (oceanSalinity(iCell)/outInterfaceSalinity(iCell) - 1.0_RKIND) + + ! According to Jenkins et al. (2001), the temperature fluxes into the ocean are: + ! 1. the advection of meltwater into the top layer (or removal for freezing) + ! 2. the turbulent transfer of heat across the boundary layer, based on the termal driving + outOceanHeatFlux(iCell) = cp_sw*(outFreshwaterFlux(iCell)*outInterfaceTemperature(iCell) & + - rho_sw*oceanHeatTransferVelocity(iCell)*(oceanTemperature(iCell)-outInterfaceTemperature(iCell))) + + ! Since we're considering only melting and ignoring diffusion, + ! the ice loses heat simply by the loss of ice mass at the prescribed + ! (surface?) ice temperature + outIceHeatFlux(iCell) = -cp_land_ice*outFreshwaterFlux(iCell)*iceTemperature(iCell) + end do + !$omp end do + + !-------------------------------------------------------------------- + + end subroutine compute_HJ99_melt_fluxes !}}} + + +!*********************************************************************** + +end module ocn_surface_land_ice_fluxes + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tendency.F b/src/core_ocean/shared/mpas_ocn_tendency.F index 1d0168a50e..04546cd837 100644 --- a/src/core_ocean/shared/mpas_ocn_tendency.F +++ b/src/core_ocean/shared/mpas_ocn_tendency.F @@ -24,12 +24,28 @@ module ocn_tendency use mpas_pool_routines use mpas_constants use mpas_timer - + use mpas_threading + use ocn_diagnostics use ocn_constants + use ocn_surface_bulk_forcing + use ocn_surface_land_ice_fluxes + use ocn_frazil_forcing + + use ocn_tracer_hmix + use ocn_high_freq_thickness_hmix_del2 use ocn_tracer_advection use ocn_tracer_short_wave_absorption use ocn_tracer_nonlocalflux + use ocn_tracer_surface_restoring + use ocn_tracer_interior_restoring + use ocn_tracer_exponential_decay + use ocn_tracer_ideal_age + use ocn_tracer_TTD + use ocn_tracer_surface_flux_to_tend + use ocn_tracer_ecosys + use ocn_tracer_DMS + use ocn_tracer_MacroMolecules use ocn_thick_hadv use ocn_thick_vadv @@ -42,10 +58,6 @@ module ocn_tendency use ocn_vel_forcing use ocn_vmix - use ocn_tracer_hmix - use ocn_high_freq_thickness_hmix_del2 - use ocn_tracer_surface_flux - implicit none private save @@ -87,7 +99,7 @@ module ocn_tendency !> \brief Computes thickness tendency !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the thickness tendency for the ocean ! !----------------------------------------------------------------------- @@ -96,22 +108,24 @@ subroutine ocn_tend_thick(tendPool, forcingPool, diagnosticsPool, meshPool)!{{{ implicit none type (mpas_pool_type), intent(inout) :: tendPool !< Input/Output: Tendency structure - type (mpas_pool_type), intent(in) :: forcingPool !< Input: Forcing information + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information type (mpas_pool_type), intent(in) :: diagnosticsPool !< Input: Diagnostics information type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information real (kind=RKIND), dimension(:), pointer :: surfaceThicknessFlux + real (kind=RKIND), dimension(:), pointer :: surfaceThicknessFluxRunoff real (kind=RKIND), dimension(:,:), pointer :: layerThickness, layerThicknessEdge, & - vertAleTransportTop, tend_layerThickness, normalTransportVelocity, fractionAbsorbed + vertAleTransportTop, tend_layerThickness, normalTransportVelocity, fractionAbsorbed, fractionAbsorbedRunoff - integer :: err + integer, pointer :: nCells + integer :: err, iCell logical, pointer :: config_disable_thick_all_tend - call mpas_timer_start("ocn_tend_thick") - call mpas_pool_get_config(ocnConfigs, 'config_disable_thick_all_tend', config_disable_thick_all_tend) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) call mpas_pool_get_array(diagnosticsPool, 'vertAleTransportTop', vertAleTransportTop) @@ -119,44 +133,60 @@ subroutine ocn_tend_thick(tendPool, forcingPool, diagnosticsPool, meshPool)!{{{ call mpas_pool_get_array(tendPool, 'layerThickness', tend_layerThickness) call mpas_pool_get_array(forcingPool, 'surfaceThicknessFlux', surfaceThicknessFlux) + call mpas_pool_get_array(forcingPool, 'surfaceThicknessFluxRunoff', surfaceThicknessFluxRunoff) call mpas_pool_get_array(forcingPool, 'fractionAbsorbed', fractionAbsorbed) - + call mpas_pool_get_array(forcingPool, 'fractionAbsorbedRunoff', fractionAbsorbedRunoff) + ! ! height tendency: start accumulating tendency terms ! - tend_layerThickness = 0.0 + !$omp do schedule(runtime) + do iCell = 1, nCells + tend_layerThickness(:, iCell) = 0.0_RKIND + surfaceThicknessFlux(iCell) = 0.0_RKIND + surfaceThicknessFluxRunoff(iCell) = 0.0_RKIND + end do + !$omp end do if(config_disable_thick_all_tend) return + call mpas_timer_start("ocn_tend_thick") + + ! Build suface mass flux array from bulk + call ocn_surface_bulk_forcing_thick(meshPool, forcingPool, surfaceThicknessFlux, surfaceThicknessFluxRunoff, err) + + ! Build suface thickness flux array from land ice + call ocn_surface_land_ice_fluxes_thick(meshPool, forcingPool, surfaceThicknessFlux, err) + ! ! height tendency: horizontal advection term -\nabla\cdot ( hu) ! - ! See Ringler et al. (2010) jcp paper, eqn 19, 21, and fig. 3. + ! See Ringler et al. (2010) jcp paper, eqn 19, 21, and fig. 3. ! for explanation of divergence operator. ! ! QC Comment (3/15/12): need to make sure that uTranport is the right ! transport velocity here. - call mpas_timer_start("hadv") call ocn_thick_hadv_tend(meshPool, normalTransportVelocity, layerThicknessEdge, tend_layerThickness, err) - call mpas_timer_stop("hadv") ! ! height tendency: vertical advection term -d/dz(hw) ! - call mpas_timer_start("vadv") call ocn_thick_vadv_tend(meshPool, vertAleTransportTop, tend_layerThickness, err) - call mpas_timer_stop("vadv") ! ! surface flux tendency ! - call mpas_timer_start("surface flux") - call ocn_thick_surface_flux_tend(meshPool, fractionAbsorbed, layerThickness, surfaceThicknessFlux, tend_layerThickness, err) - call mpas_timer_stop("surface flux") + call ocn_thick_surface_flux_tend(meshPool, fractionAbsorbed, fractionAbsorbedRunoff, layerThickness, & + surfaceThicknessFlux, surfaceThicknessFluxRunoff, tend_layerThickness, err) + + ! + ! surface flux tendency + ! + call ocn_frazil_forcing_layer_thickness(meshPool, forcingPool, tend_layerThickness, err) call mpas_timer_stop("ocn_tend_thick") - + end subroutine ocn_tend_thick!}}} !*********************************************************************** @@ -166,7 +196,7 @@ end subroutine ocn_tend_thick!}}} !> \brief Computes velocity tendency !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the velocity tendency for the ocean ! !----------------------------------------------------------------------- @@ -182,25 +212,25 @@ subroutine ocn_tend_vel(tendPool, statePool, forcingPool, diagnosticsPool, meshP type (mpas_pool_type), intent(inout) :: scratchPool !< Input: Scratch structure integer, intent(in), optional :: timeLevelIn !< Input: Time level for state fields - real (kind=RKIND), dimension(:), pointer :: surfaceWindStress + type (mpas_pool_type), pointer :: tracersPool + + real (kind=RKIND), dimension(:), pointer :: surfaceStress, surfaceStressMagnitude, surfaceFluxAttenuationCoefficient real (kind=RKIND), dimension(:,:), pointer :: & layerThicknessEdge, normalVelocity, tangentialVelocity, density, potentialDensity, zMid, pressure, & tend_normalVelocity, circulation, relativeVorticity, viscosity, kineticEnergyCell, & normalizedRelativeVorticityEdge, normalizedPlanetaryVorticityEdge, & montgomeryPotential, vertAleTransportTop, divergence, vertViscTopOfEdge, & inSituThermalExpansionCoeff, inSituSalineContractionCoeff - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers integer :: timeLevel - integer :: err - integer, pointer :: indexTemperature, indexSalinity + integer :: err, iEdge, iCell + integer, pointer :: indexTemperature, indexSalinity, nEdges, nCells logical, pointer :: config_disable_vel_all_tend character (len=StrKIND), pointer :: config_pressure_gradient_type - call mpas_timer_start("ocn_tend_vel") - if (present(timeLevelIn)) then timeLevel = timeLevelIn else @@ -210,10 +240,15 @@ subroutine ocn_tend_vel(tendPool, statePool, forcingPool, diagnosticsPool, meshP call mpas_pool_get_config(ocnConfigs, 'config_disable_vel_all_tend', config_disable_vel_all_tend) call mpas_pool_get_config(ocnConfigs, 'config_pressure_gradient_type', config_pressure_gradient_type) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexSalinity) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) call mpas_pool_get_array(diagnosticsPool, 'kineticEnergyCell', kineticEnergyCell) call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) @@ -230,74 +265,87 @@ subroutine ocn_tend_vel(tendPool, statePool, forcingPool, diagnosticsPool, meshP call mpas_pool_get_array(diagnosticsPool, 'density', density) call mpas_pool_get_array(diagnosticsPool, 'potentialDensity', potentialDensity) call mpas_pool_get_array(diagnosticsPool, 'tangentialVelocity', tangentialVelocity) + call mpas_pool_get_array(diagnosticsPool, 'surfaceFluxAttenuationCoefficient', surfaceFluxAttenuationCoefficient) call mpas_pool_get_array(tendPool, 'normalVelocity', tend_normalVelocity) - - call mpas_pool_get_array(forcingPool, 'surfaceWindStress', surfaceWindStress) + + call mpas_pool_get_array(forcingPool, 'surfaceStress', surfaceStress) + call mpas_pool_get_array(forcingPool, 'surfaceStressMagnitude', surfaceStressMagnitude) ! ! velocity tendency: start accumulating tendency terms ! - tend_normalVelocity(:,:) = 0.0 + !$omp do schedule(runtime) + do iEdge = 1, nEdges + tend_normalVelocity(:, iEdge) = 0.0_RKIND + surfaceStress(iEdge) = 0.0_RKIND + end do + !$omp end do + + !$omp do schedule(runtime) + do iCell = 1, nCells + surfaceStressMagnitude(iCell) = 0.0_RKIND + end do + !$omp end do if(config_disable_vel_all_tend) return + call mpas_timer_start("ocn_tend_vel") + + ! Build bulk forcing suface stress + call ocn_surface_bulk_forcing_vel(meshPool, forcingPool, surfaceStress, surfaceStressMagnitude, err) + + ! Add top drag to suface stress + call ocn_surface_land_ice_fluxes_vel(meshPool, diagnosticsPool, surfaceStress, surfaceStressMagnitude, err) + ! ! velocity tendency: nonlinear Coriolis term and grad of kinetic energy ! - - call mpas_timer_start("coriolis") call ocn_vel_coriolis_tend(meshPool, normalizedRelativeVorticityEdge, normalizedPlanetaryVorticityEdge, layerThicknessEdge, & normalVelocity, kineticEnergyCell, tend_normalVelocity, err) - call mpas_timer_stop("coriolis") ! ! velocity tendency: vertical advection term -w du/dz ! - call mpas_timer_start("vadv") call ocn_vel_vadv_tend(meshPool, normalVelocity, layerThicknessEdge, vertAleTransportTop, tend_normalVelocity, err) - call mpas_timer_stop("vadv") ! ! velocity tendency: pressure gradient ! - call mpas_timer_start("pressure grad") if (config_pressure_gradient_type.eq.'Jacobian_from_TS') then ! only pass EOS derivatives if needed. call mpas_pool_get_array(diagnosticsPool, 'inSituThermalExpansionCoeff',inSituThermalExpansionCoeff) call mpas_pool_get_array(diagnosticsPool, 'inSituSalineContractionCoeff', inSituSalineContractionCoeff) call ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, zMid, density, potentialDensity, & - indexTemperature, indexSalinity, tracers, tend_normalVelocity, err, & + indexTemperature, indexSalinity, activeTracers, tend_normalVelocity, err, & inSituThermalExpansionCoeff,inSituSalineContractionCoeff) else call ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, zMid, density, potentialDensity, & - indexTemperature, indexSalinity, tracers, tend_normalVelocity, err, & + indexTemperature, indexSalinity, activeTracers, tend_normalVelocity, err, & inSituThermalExpansionCoeff,inSituSalineContractionCoeff) endif - call mpas_timer_stop("pressure grad") ! ! velocity tendency: del2 dissipation, \nu_2 \nabla^2 u ! computed as \nu( \nabla divergence + k \times \nabla relativeVorticity ) ! strictly only valid for config_mom_del2 == constant ! - call mpas_timer_start("hmix") - call ocn_vel_hmix_tend(meshPool, divergence, relativeVorticity, normalVelocity, tangentialVelocity, viscosity, & - tend_normalVelocity, scratchPool, err) - call mpas_timer_stop("hmix") + call ocn_vel_hmix_tend(meshPool, scratchPool, divergence, relativeVorticity, normalVelocity, tangentialVelocity, viscosity, & + tend_normalVelocity, err) ! ! velocity tendency: forcing and bottom drag ! + call ocn_vel_forcing_tend(meshPool, normalVelocity, surfaceFluxAttenuationCoefficient, & + surfaceStress, kineticEnergyCell, layerThicknessEdge, & + tend_normalVelocity, err) - call mpas_timer_start("forcings") - call ocn_vel_forcing_tend(meshPool, normalVelocity, surfaceWindStress, layerThicknessEdge, tend_normalVelocity, err) - call mpas_timer_stop("forcings") ! ! velocity tendency: vertical mixing d/dz( nu_v du/dz)) ! call mpas_timer_stop("ocn_tend_vel") + call mpas_threading_barrier() end subroutine ocn_tend_vel!}}} @@ -308,50 +356,149 @@ end subroutine ocn_tend_vel!}}} !> \brief Computes tracer tendency !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes tracer tendencies for the ocean ! !----------------------------------------------------------------------- - subroutine ocn_tend_tracer(tendPool, statePool, forcingPool, diagnosticsPool, meshPool, scratchPool, dt, timeLevelIn)!{{{ + subroutine ocn_tend_tracer(tendPool, statePool, forcingPool, diagnosticsPool, meshPool, swForcingPool, scratchPool, & !{{{ + dt, activeTracersOnlyIn, timeLevelIn ) implicit none - type (mpas_pool_type), intent(inout) :: tendPool !< Input/Output: Tendency structure - type (mpas_pool_type), intent(in) :: statePool !< Input: State information - type (mpas_pool_type), intent(in) :: forcingPool !< Input: Forcing information - type (mpas_pool_type), intent(in) :: diagnosticsPool !< Input: Diagnostic information - type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information - type (mpas_pool_type), intent(in) :: scratchPool !< Input: Scratch information - real (kind=RKIND), intent(in) :: dt !< Input: Time step - integer, intent(in), optional :: timeLevelIn + ! + ! intent in/out + ! + type (mpas_pool_type), intent(inout) :: tendPool !< Input/Output: Tendency structure + type (mpas_pool_type), intent(in) :: statePool !< Input: State information + type (mpas_pool_type), intent(inout) :: forcingPool !< Input: Forcing information + type (mpas_pool_type), intent(inout) :: diagnosticsPool !< Input: Diagnostic information + type (mpas_pool_type), intent(inout) :: meshPool !< Input: Mesh information + type (mpas_pool_type), intent(in) :: swForcingPool !< Input: sw data input info + type (mpas_pool_type), intent(in) :: scratchPool !< Input: Scratch information + real (kind=RKIND), intent(in) :: dt !< Input: Time step + logical, intent(in), optional :: activeTracersOnlyIn !< Input: if true, only compute for active tracers + integer, intent(in), optional :: timeLevelIn !< Input/Optional: Time Level Indes + + ! + ! additional pools + ! + type (mpas_pool_type), pointer :: tracersPool, tracersTendPool ! tracers and their tendency + type (mpas_pool_type), pointer :: tracersSurfaceFluxPool ! surface fluxes + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool ! surface restoring + type (mpas_pool_type), pointer :: tracersInteriorRestoringFieldsPool ! interior restoring + type (mpas_pool_type), pointer :: tracersExponentialDecayFieldsPool ! exponential decay + type (mpas_pool_type), pointer :: tracersIdealAgeFieldsPool ! ideal age + type (mpas_pool_type), pointer :: tracersTTDFieldsPool ! transit time distribution + + ! scalar pointers + integer :: nTracerGroup + integer, pointer :: nVertLevels, nEdges, nCells, nCellsSolve, indexTemperature, indexSalinity + logical, pointer :: config_disable_tr_all_tend, config_use_cvmix_kpp + logical, pointer :: config_use_tracerGroup, config_use_tracerGroup_surface_bulk_forcing, & + config_use_tracerGroup_surface_restoring, config_use_tracerGroup_interior_restoring, & + config_use_tracerGroup_exponential_decay, config_use_tracerGroup_idealAge_forcing, & + config_use_tracerGroup_ttd_forcing, config_use_surface_salinity_monthly_restoring + + logical, pointer :: config_compute_active_tracer_budgets, & + config_cvmix_kpp_nonlocal_with_implicit_mix + real (kind=RKIND), pointer :: salinity_restoring_constant_piston_velocity + + ! iterator for tracer categories + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: modifiedGroupName + character (len=StrKIND) :: modifiedConfigName + ! + ! one dimensional pointers + ! + real (kind=RKIND), dimension(:), pointer :: penetrativeTemperatureFlux, penetrativeTemperatureFluxOBL + real (kind=RKIND), dimension(:), pointer :: tracerGroupExponentialDecayRate, latCell + integer, dimension(:), pointer :: maxLevelCell + + ! + ! two dimensional pointers + ! + real (kind=RKIND), dimension(:,:), pointer :: tracerGroupPistonVelocity, tracerGroupSurfaceRestoringValue, & + tracerGroupIdealAgeMask, tracerGroupTTDMask - real (kind=RKIND), dimension(:), pointer :: penetrativeTemperatureFlux real (kind=RKIND), dimension(:,:), pointer :: & normalTransportVelocity, layerThickness,vertAleTransportTop, layerThicknessEdge, vertDiffTopOfCell, & - tend_layerThickness, normalThicknessFlux, surfaceTracerFlux, fractionAbsorbed, zMid, relativeSlopeTopOfEdge, & - relativeSlopeTapering, relativeSlopeTaperingCell + normalThicknessFlux, tracerGroupSurfaceFlux, fractionAbsorbed, zMid, relativeSlopeTopOfEdge, & + relativeSlopeTapering, relativeSlopeTaperingCell, fractionAbsorbedRunoff, tracerGroupSurfaceFluxRunoff, & + tracerGroupSurfaceFluxRemoved, nonLocalSurfaceTracerFlux + + ! + ! three dimensional pointers + ! real (kind=RKIND), dimension(:,:,:), pointer :: & - tracers, tend_tr, vertNonLocalFlux + tracerGroup, tracerGroupTend, vertNonLocalFlux - integer :: err, iEdge, k - integer, pointer :: nVertLevels, nEdges, indexTemperature - integer :: timeLevel + real (kind=RKIND), dimension(:,:,:), pointer :: & + activeTracers, & ! need T, S for ecosys + ecosysTracers ! need ecosys for DMS and MacroMolecules - logical, pointer :: config_disable_tr_all_tend, config_use_cvmix_kpp + real (kind=RKIND), dimension(:,:,:), pointer :: tracerGroupInteriorRestoringRate, tracerGroupInteriorRestoringValue - call mpas_timer_start("ocn_tend_tracer") + real (kind=RKIND), dimension(:,:,:), pointer :: & + activeTracerSurfaceFluxTendency, & + activeTracerNonLocalTendency + + real (kind=RKIND), dimension(:,:), pointer :: & + temperatureShortWaveTendency + ! + ! Field pointers + ! + type (field2DReal), pointer :: normalThicknessFluxField + + ! + ! local integers/reals/logicals + ! + integer :: err, iCell, iEdge, k, timeLevel, nTracersEcosys + logical :: activeTracersOnly ! if true, only compute for active tracers + + ! + ! set time level of optional argument is present + ! if (present(timeLevelIn)) then timeLevel = timeLevelIn else timeLevel = 1 end if + if (present(activeTracersOnlyIn)) then + activeTracersOnly = activeTracersOnlyIn + else + activeTracersOnly = .false. + end if + + ! + ! get tracers pools + ! + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(tendPool, 'tracersTend', tracersTendPool) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceFlux', tracersSurfaceFluxPool) + + ! + ! get dimensions + ! + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) + + ! + ! get configure options + ! + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_nonlocal_with_implicit_mix', & + config_cvmix_kpp_nonlocal_with_implicit_mix) call mpas_pool_get_config(ocnConfigs, 'config_disable_tr_all_tend', config_disable_tr_all_tend) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_kpp', config_use_cvmix_kpp) - + call mpas_pool_get_config(ocnConfigs, 'config_compute_active_tracer_budgets', config_compute_active_tracer_budgets) + ! + ! get arrays + ! call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) - call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity', normalTransportVelocity) call mpas_pool_get_array(diagnosticsPool, 'layerThicknessEdge', layerThicknessEdge) call mpas_pool_get_array(diagnosticsPool, 'vertDiffTopOfCell', vertDiffTopOfCell) @@ -361,79 +508,388 @@ subroutine ocn_tend_tracer(tendPool, statePool, forcingPool, diagnosticsPool, me call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTapering', relativeSlopeTapering) call mpas_pool_get_array(diagnosticsPool, 'relativeSlopeTaperingCell', relativeSlopeTaperingCell) call mpas_pool_get_array(diagnosticsPool, 'vertNonLocalFlux', vertNonLocalFlux) - call mpas_pool_get_array(forcingPool, 'penetrativeTemperatureFlux', penetrativeTemperatureFlux) - call mpas_pool_get_array(forcingPool, 'surfaceTracerFlux', surfaceTracerFlux) + call mpas_pool_get_array(diagnosticsPool, 'penetrativeTemperatureFluxOBL', penetrativeTemperatureFluxOBL) call mpas_pool_get_array(forcingPool, 'fractionAbsorbed', fractionAbsorbed) - - call mpas_pool_get_array(tendPool, 'tracers', tend_tr) - call mpas_pool_get_array(tendPool, 'layerThickness', tend_layerThickness) + call mpas_pool_get_array(forcingPool, 'fractionAbsorbedRunoff', fractionAbsorbedRunoff) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'latCell', latCell) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(statePool, 'index_temperature', indexTemperature) - ! - ! initialize tracer tendency (RHS of tracer equation) to zero. + ! get diagnostic arrays for temperature/salinity budget ! - tend_tr(:,:,:) = 0.0 + if (config_compute_active_tracer_budgets) then + call mpas_pool_get_array(diagnosticsPool,'activeTracerSurfaceFluxTendency',activeTracerSurfaceFluxTendency) + call mpas_pool_get_array(diagnosticsPool,'temperatureShortWaveTendency',temperatureShortWaveTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerNonLocalTendency',activeTracerNonLocalTendency) + endif if(config_disable_tr_all_tend) return - allocate(normalThicknessFlux(nVertLevels, nEdges+1)) + call mpas_timer_start("ocn_tend_tracer") + + !allocate(normalThicknessFlux(nVertLevels, nEdges+1)) + call mpas_pool_get_field(scratchPool, 'normalThicknessFlux', normalThicknessFluxField) + call mpas_allocate_scratch_field(normalThicknessFluxField, .true.) + call mpas_threading_barrier() + + normalThicknessFlux => normalThicknessFluxField % array + ! ! transport velocity for the tracer. + ! + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges do k = 1, nVertLevels normalThicknessFlux(k, iEdge) = normalTransportVelocity(k, iEdge) * layerThicknessEdge(k, iEdge) end do end do + !$omp end do ! - ! tracer tendency: horizontal advection term -div( layerThickness \phi u) + ! begin iterate over tracer categories ! + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + ! Only compute tendencies for active tracers if activeTracersOnly flag is true. + if ( .not.activeTracersOnly .or. trim(groupItr % memberName)=='activeTracers') then + ! load configure setting for this category + ! + modifiedConfigName = 'config_use_' // trim(groupItr % memberName) + call mpas_pool_get_config(ocnConfigs, modifiedConfigName, config_use_tracerGroup) + + if ( config_use_tracerGroup ) then + modifiedConfigName = 'config_use_' // trim(groupItr % memberName) // '_surface_bulk_forcing' + call mpas_pool_get_config(ocnConfigs, modifiedConfigName, config_use_tracerGroup_surface_bulk_forcing) + modifiedConfigName = 'config_use_' // trim(groupItr % memberName) // '_surface_restoring' + call mpas_pool_get_config(ocnConfigs, modifiedConfigName, config_use_tracerGroup_surface_restoring) + modifiedConfigName = 'config_use_' // trim(groupItr % memberName) // '_interior_restoring' + call mpas_pool_get_config(ocnConfigs, modifiedConfigName, config_use_tracerGroup_interior_restoring) + modifiedConfigName = 'config_use_' // trim(groupItr % memberName) // '_exponential_decay' + call mpas_pool_get_config(ocnConfigs, modifiedConfigName, config_use_tracerGroup_exponential_decay) + modifiedConfigName = 'config_use_' // trim(groupItr % memberName) // '_idealAge_forcing' + call mpas_pool_get_config(ocnConfigs, modifiedConfigName, config_use_tracerGroup_idealAge_forcing) + modifiedConfigName = 'config_use_' // trim(groupItr % memberName) // '_ttd_forcing' + call mpas_pool_get_config(ocnConfigs, modifiedConfigName, config_use_tracerGroup_ttd_forcing) + + + ! Get tracer group, and other groups (tendencies, etc.) + call mpas_pool_get_array(tracersPool, trim(groupItr % memberName), tracerGroup, timeLevel) + nTracerGroup = size(tracerGroup, dim=1) + + ! Get Tendency array + modifiedGroupName = trim(groupItr % memberName) // "Tend" + call mpas_pool_get_array(tracersTendPool, trim(modifiedGroupName), tracerGroupTend) + + ! Get surface flux array + modifiedGroupName = trim(groupItr % memberName) // "SurfaceFlux" + call mpas_pool_get_array(tracersSurfaceFluxPool, trim(modifiedGroupName), tracerGroupSurfaceFlux) + + ! Get Array of total surface temp/salt flux (includes thickness + ! tendencies + call mpas_pool_get_array(tracersSurfaceFluxPool, 'nonLocalSurfaceTracerFlux', nonLocalSurfaceTracerFlux) + + ! Get surface flux due to river runoff array +!maltrud only active tracers have runoff flux for now, but we still need to associate for ALL tracers + modifiedGroupName = trim(groupItr % memberName) // "SurfaceFluxRunoff" + call mpas_pool_get_array(tracersSurfaceFluxPool, trim(modifiedGroupName), tracerGroupSurfaceFluxRunoff) + + ! Get surface flux removed array to keep track of how much flux is ignored + modifiedGroupName = trim(groupItr % memberName) // "SurfaceFluxRemoved" + call mpas_pool_get_array(tracersSurfaceFluxPool, trim(modifiedGroupName), tracerGroupSurfaceFluxRemoved) + + ! + ! initialize tracer surface flux and tendency to zero. + ! + !$omp do schedule(runtime) + do iCell = 1, nCells + tracerGroupTend(:,:, iCell) = 0.0_RKIND + tracerGroupSurfaceFlux(:, iCell) = 0.0_RKIND + end do + !$omp end do + + ! + ! fill components of surface tracer flux + ! + if (config_use_tracerGroup_surface_bulk_forcing) then + !$omp do schedule(runtime) + do iCell = 1, nCells + tracerGroupSurfaceFluxRunoff(:, iCell) = 0.0_RKIND + tracerGroupSurfaceFluxRemoved(:, iCell) = 0.0_RKIND + end do + !$omp end do + + call ocn_surface_bulk_forcing_tracers(meshPool, groupItr % memberName, forcingPool, tracerGroup, & + tracerGroupSurfaceFlux, tracerGroupSurfaceFluxRunoff, & + tracerGroupSurfaceFluxRemoved, dt, layerThickness, err) + end if + + ! + ! compute ecosystem source-sink tendencies and net surface fluxes + ! NOTE: must be called before ocn_tracer_surface_flux_tend + ! + if ( trim(groupItr % memberName) == 'ecosysTracers' ) then + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) + call ocn_tracer_ecosys_compute(activeTracers, tracerGroup, forcingPool, nTracerGroup, & + nCellsSolve, latCell, maxLevelCell, nVertLevels, layerThickness, zMid, indexTemperature, & + indexSalinity, tracerGroupTend, err) + + call ocn_tracer_ecosys_surface_flux_compute(activeTracers, tracerGroup, forcingPool, & + nTracerGroup, nCellsSolve, zMid, indexTemperature, indexSalinity, tracerGroupSurfaceFlux, err) + endif + + ! + ! compute DMS source-sink tendencies and net surface fluxes + ! NOTE: must be called before ocn_tracer_surface_flux_tend + ! + if ( trim(groupItr % memberName) == 'DMSTracers' ) then + call mpas_pool_get_array(tracersPool, 'ecosysTracers', ecosysTracers, timeLevel) + nTracersEcosys = size(ecosysTracers, dim=1) + call ocn_tracer_DMS_compute(activeTracers, tracerGroup, nTracerGroup, ecosysTracers, & + nTracersEcosys, forcingPool, nCellsSolve, maxLevelCell, & + nVertLevels, layerThickness, indexTemperature, indexSalinity, tracerGroupTend, err) + + call ocn_tracer_DMS_surface_flux_compute(activeTracers, tracerGroup, forcingPool, & + nTracerGroup, nCellsSolve, zMid, indexTemperature, indexSalinity, tracerGroupSurfaceFlux, & + tracerGroupSurfaceFluxRemoved, err) + endif + + ! + ! compute MacroMolecules source-sink tendencies and net surface fluxes + ! NOTE: must be called before ocn_tracer_surface_flux_tend + ! + if ( trim(groupItr % memberName) == 'MacroMoleculesTracers' ) then + call mpas_pool_get_array(tracersPool, 'ecosysTracers', ecosysTracers, timeLevel) + nTracersEcosys = size(ecosysTracers, dim=1) + call ocn_tracer_MacroMolecules_compute(tracerGroup, nTracerGroup, ecosysTracers, nTracersEcosys, forcingPool, & + nCellsSolve, maxLevelCell, nVertLevels, layerThickness, & + tracerGroupTend, err) + + call ocn_tracer_MacroMolecules_surface_flux_compute(activeTracers, tracerGroup, forcingPool, & + nTracerGroup, nCellsSolve, zMid, indexTemperature, indexSalinity, tracerGroupSurfaceFlux, err) + endif + + ! + ! ocean surface restoring + ! + if (config_use_tracerGroup_surface_restoring) then + call mpas_timer_start("surface_restoring_" // trim(groupItr % memberName)) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields', tracersSurfaceRestoringFieldsPool) + modifiedGroupName = trim(groupItr % memberName) // "PistonVelocity" + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, trim(modifiedGroupName), & + tracerGroupPistonVelocity) + modifiedGroupName = trim(groupItr % memberName) // "SurfaceRestoringValue" + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, trim(modifiedGroupName), & + tracerGroupSurfaceRestoringValue) + ! Note: monthly surface salinity restoring is a special case for tracer restoring + call mpas_pool_get_config(ocnConfigs, 'config_use_surface_salinity_monthly_restoring', & + config_use_surface_salinity_monthly_restoring) + call MPAS_pool_get_config(ocnConfigs, 'config_salinity_restoring_constant_piston_velocity', & + salinity_restoring_constant_piston_velocity) + call ocn_tracer_surface_restoring_compute(groupItr % memberName, nTracerGroup, nCells, tracerGroup, & + tracerGroupPistonVelocity, tracerGroupSurfaceRestoringValue, tracerGroupSurfaceFlux, indexSalinity, & + config_use_surface_salinity_monthly_restoring, & + salinity_restoring_constant_piston_velocity, err) + call mpas_timer_stop("surface_restoring_" // trim(groupItr % memberName)) + endif + + ! tracer fluxes at the land-ice / ocean interface + ! this is a flux at the top ocean surface -- so these fluxes are added into tracerGroupSurfaceFlux + call ocn_surface_land_ice_fluxes_tracers(meshPool, groupItr % memberName, forcingPool, tracerGroupSurfaceFlux, err) + + ! + ! other additions to tracerGroupSurfaceFlux should be added here + ! + + ! + ! now begin to accumulate the RHS tracer tendencies. + ! + + ! + ! interior restoring forcing tendency + ! + if (config_use_tracerGroup_interior_restoring) then + call mpas_timer_start("interior_restoring_" // trim(groupItr % memberName), .false.) + call mpas_pool_get_subpool(forcingPool, 'tracersInteriorRestoringFields', tracersInteriorRestoringFieldsPool) + modifiedGroupName = trim(groupItr % memberName) // "InteriorRestoringRate" + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, trim(modifiedGroupName), & + tracerGroupInteriorRestoringRate) + modifiedGroupName = trim(groupItr % memberName) // "InteriorRestoringValue" + call mpas_pool_get_array(tracersInteriorRestoringFieldsPool, trim(modifiedGroupName), & + tracerGroupInteriorRestoringValue) + call ocn_tracer_interior_restoring_compute(nTracerGroup, nCells, maxLevelCell, layerThickness, & + tracerGroup, tracerGroupInteriorRestoringRate, tracerGroupInteriorRestoringValue, tracerGroupTend, err) + call mpas_timer_stop("interior_restoring_" // trim(groupItr % memberName)) + endif + + ! + ! exponential decay tendency + ! + if (config_use_tracerGroup_exponential_decay) then + call mpas_log_write( & + "WARNING: exponential decay not fully tested", & + MPAS_LOG_WARN) + call mpas_timer_start("exponential decay " // trim(groupItr % memberName)) + call mpas_pool_get_subpool(forcingPool, 'tracersExponentialDecayFields', tracersExponentialDecayFieldsPool) + modifiedGroupName = trim(groupItr % memberName) // "ExponentialDecayRate" + call mpas_pool_get_array(tracersExponentialDecayFieldsPool, trim(modifiedGroupName), & + tracerGroupExponentialDecayRate) + call ocn_tracer_exponential_decay_compute(nTracerGroup, nCells, maxLevelCell, layerThickness, & + tracerGroup, tracerGroupExponentialDecayRate, tracerGroupTend, err) + + call mpas_timer_stop("exponential decay " // trim(groupItr % memberName)) + endif + + ! + ! ideal age forcing tendency + ! note: ocn_tracer_ideal_age_compute resets tracers in top layer to zero + ! + if (config_use_tracerGroup_idealAge_forcing) then + call mpas_timer_start("ideal age " // trim(groupItr % memberName)) + call mpas_log_write( & + "WARNING: ideal age not fully tested", & + MPAS_LOG_WARN) + call mpas_pool_get_subpool(forcingPool, 'tracersIdealAgeFields', tracersIdealAgeFieldsPool) + modifiedGroupName = trim(groupItr % memberName) // "IdealAgeMask" + call mpas_pool_get_array(tracersIdealAgeFieldsPool, trim(modifiedGroupName), tracerGroupIdealAgeMask) + call ocn_tracer_ideal_age_compute(nTracerGroup, nCells, maxLevelCell, layerThickness, & + tracerGroupIdealAgeMask, tracerGroup, tracerGroupTend, err) + + call mpas_timer_stop("ideal age " // trim(groupItr % memberName)) + endif + + ! + ! transit-time distribution (TTD) forcing tendency + ! note: no tendency is actually computed in ocn_tracer_TTD_compute + ! note: rather, tracerGroup is reset to tracerGroupTTDMask in top-most layer + ! + if (config_use_tracerGroup_ttd_forcing) then + call mpas_timer_start("TTD " // trim(groupItr % memberName)) + call mpas_log_write( & + "WARNING: TTD not fully tested", & + MPAS_LOG_WARN) + call mpas_pool_get_subpool(forcingPool, 'tracersTTDFields', tracersTTDFieldsPool) + modifiedGroupName = trim(groupItr % memberName) // "TTDMask" + call mpas_pool_get_array(tracersTTDFieldsPool, trim(modifiedGroupName), tracerGroupTTDMask) + call ocn_tracer_TTD_compute(nTracerGroup, nCells, maxLevelCell, layerThickness, & + tracerGroupTTDMask, tracerGroup, err) + + call mpas_timer_stop("TTD " // trim(groupItr % memberName)) + endif + + ! + ! tracer tendency: horizontal advection term -div( layerThickness \phi u) + ! + + ! Monotonoic Advection, or standard advection + ! Tendency for tracer budget is stored within the tracer adv + ! routine + + call ocn_tracer_advection_tend(tracerGroup, normalThicknessFlux, vertAleTransportTop, layerThickness, & + dt, meshPool, scratchPool, diagnosticsPool, tracerGroupTend, & + trim(groupItr % memberName)) + + ! + ! tracer tendency: del2 horizontal tracer diffusion, div(h \kappa_2 \nabla \phi) + ! + call ocn_tracer_hmix_tend(meshPool, scratchPool, layerThicknessEdge, zMid, tracerGroup, & + relativeSlopeTopOfEdge, relativeSlopeTapering, relativeSlopeTaperingCell, & + tracerGroupTend, err) + + ! + ! convert the surface tracer flux into a tracer tendency by distributing the flux across some number + ! of surface layers + ! + if (config_compute_active_tracer_budgets) then + if ( trim(groupItr % memberName) == 'activeTracers' ) then + !$omp do schedule(runtime) + do iCell = 1, nCells + activeTracerSurfaceFluxTendency(:,:,iCell) = tracerGroupTend(:,:,iCell) + end do + !$omp end do + endif + endif + + call ocn_tracer_surface_flux_tend(meshPool, fractionAbsorbed, fractionAbsorbedRunoff, layerThickness, & + tracerGroupSurfaceFlux, tracerGroupSurfaceFluxRunoff, & + tracerGroupTend, err) + + ! + ! Performing shortwave absorption + ! + if ( trim(groupItr % memberName) == 'activeTracers' ) then + + if (config_compute_active_tracer_budgets) then + !$omp do schedule(runtime) + do iCell = 1, nCells + activeTracerSurfaceFluxTendency(:,:,iCell) = tracerGroupTend(:,:,iCell) & + - activeTracerSurfaceFluxTendency(:,:,iCell) + temperatureShortWaveTendency(:,iCell) = tracerGroupTend(indexTemperature,:,iCell) + end do + !$omp end do + endif + + call ocn_tracer_short_wave_absorption_tend(meshPool, swForcingPool, forcingPool, indexTemperature, & + layerThickness, penetrativeTemperatureFlux, penetrativeTemperatureFluxOBL, tracerGroupTend, err) + + if (config_compute_active_tracer_budgets) then + !$omp do schedule(runtime) + do iCell = 1, nCells + temperatureShortWaveTendency(:,iCell) = tracerGroupTend(indexTemperature,:,iCell) - & + temperatureShortWaveTendency(:,iCell) + end do + !$omp end do + endif + endif + + ! + ! Compute tracer tendency due to non-local flux computed in KPP + ! + if (config_use_cvmix_kpp) then + call mpas_timer_start("non-local flux from KPP") + call ocn_compute_KPP_input_fields(statePool, forcingPool, meshPool, diagnosticsPool, scratchPool, timeLevel) + if (.not. config_cvmix_kpp_nonlocal_with_implicit_mix) then + if( trim(groupItr % memberName) == 'activeTracers' ) then + if (config_compute_active_tracer_budgets) then + !$omp do schedule(runtime) + do iCell = 1, nCells + activeTracerNonLocalTendency(:,:,iCell) = tracerGroupTend(:,:,iCell) + end do + !$omp end do + endif + call ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, nonLocalSurfaceTracerFlux, tracerGroupTend, err) + if (config_compute_active_tracer_budgets) then + !$omp do schedule(runtime) + do iCell = 1, nCells + activeTracerNonLocalTendency(:,:,iCell) = tracerGroupTend(:,:,iCell)-activeTracerNonLocalTendency(:,:,iCell) + end do + !$omp end do + endif + else + call ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, tracerGroupSurfaceFlux, tracerGroupTend, err) + endif + end if + call mpas_timer_stop("non-local flux from KPP") + end if + + ! + ! Compute tracer tendency due to production/destruction of frazil ice + ! + call ocn_frazil_forcing_tracers(meshPool, tracersPool, groupItr%memberName, forcingPool, tracerGroupTend, err) + end if + end if ! active only + end if + end do - ! Monotonoic Advection, or standard advection - call mpas_timer_start("adv") - call ocn_tracer_advection_tend(tracers, normalThicknessFlux, vertAleTransportTop, layerThickness, layerThickness, dt, meshPool, tend_layerThickness, tend_tr) - call mpas_timer_stop("adv") - - ! - ! tracer tendency: del2 horizontal tracer diffusion, div(h \kappa_2 \nabla \phi) - ! - call mpas_timer_start("hmix") - call ocn_tracer_hmix_tend(meshPool, scratchPool, layerThickness, layerThicknessEdge, zMid, tracers, & - relativeSlopeTopOfEdge, relativeSlopeTapering, relativeSlopeTaperingCell, tend_tr, err) - call mpas_timer_stop("hmix") - - ! - ! Perform forcing from surface fluxes - ! - call mpas_timer_start("surface_flux") - call ocn_tracer_surface_flux_tend(meshPool, fractionAbsorbed, layerThickness, surfaceTracerFlux, tend_tr, err) - call mpas_timer_stop("surface_flux") - - ! - ! Performing shortwave absorption - ! - call mpas_timer_start("short wave") - call ocn_tracer_short_wave_absorption_tend(meshPool, indexTemperature, layerThickness, penetrativeTemperatureFlux, tend_tr, err) - call mpas_timer_stop("short wave") - - ! - ! Compute tracer tendency due to non-local flux computed in KPP - ! - if (config_use_cvmix_kpp) then - call mpas_timer_start("non-local flux from KPP") - call ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, surfaceTracerFlux, tend_tr, err) - call mpas_timer_stop("non-local flux from KPP") - endif + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(normalThicknessFluxField, .true.) call mpas_timer_stop("ocn_tend_tracer") - deallocate(normalThicknessFlux) - end subroutine ocn_tend_tracer!}}} !*********************************************************************** @@ -443,8 +899,8 @@ end subroutine ocn_tend_tracer!}}} !> \brief Compute tendencies needed for frequency filtered thickness !> \author Mark Petersen !> \date July 2013 -!> \details -!> This routine compute high frequency thickness tendency and the +!> \details +!> This routine compute high frequency thickness tendency and the !> low freqency divergence. It is only called when !> config_freq_filtered_thickness is true (z-tilde) ! @@ -475,6 +931,7 @@ subroutine ocn_tend_freq_filtered_thickness(tendPool, statePool, diagnosticsPool real (kind=RKIND), pointer :: config_thickness_filter_timescale, config_highFreqThick_restore_time call mpas_timer_start("ocn_tend_freq_filtered_thickness") + err = 0 if (present(timeLevelIn)) then @@ -507,21 +964,23 @@ subroutine ocn_tend_freq_filtered_thickness(tendPool, statePool, diagnosticsPool call mpas_pool_get_array(tendPool, 'lowFreqDivergence', tend_lowFreqDivergence) call mpas_pool_get_array(tendPool, 'highFreqThickness', tend_highFreqThickness) - allocate(div_hu(nVertLevels)) - ! ! Low Frequency Divergence and high frequency thickness Tendency ! - tend_lowFreqDivergence = 0.0 - tend_highFreqThickness = 0.0 ! Convert restore time from days to seconds - thickness_filter_timescale_sec = config_thickness_filter_timescale*86400.0 - highFreqThick_restore_time_sec = config_highFreqThick_restore_time*86400.0 + thickness_filter_timescale_sec = config_thickness_filter_timescale*86400.0_RKIND + highFreqThick_restore_time_sec = config_highFreqThick_restore_time*86400.0_RKIND + + allocate(div_hu(nVertLevels)) + + !$omp do schedule(runtime) private(div_hu_btr, invAreaCell, i, iEdge, k, totalThickness, flux) do iCell = 1, nCells - div_hu(:) = 0.0 - div_hu_btr = 0.0 - invAreaCell = 1.0 / areaCell(iCell) + tend_lowFreqDivergence(:, iCell) = 0.0_RKIND + tend_highFreqThickness(:, iCell) = 0.0_RKIND + div_hu(:) = 0.0_RKIND + div_hu_btr = 0.0_RKIND + invAreaCell = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) @@ -546,17 +1005,15 @@ subroutine ocn_tend_freq_filtered_thickness(tendPool, statePool, diagnosticsPool + use_highFreqThick_restore*( -2.0 * pii / highFreqThick_restore_time_sec * highFreqThickness(k,iCell) ) end do - end do + !$omp end do deallocate(div_hu) ! ! high frequency thickness tendency: del2 horizontal hhf diffusion, div(\kappa_{hf} \nabla h^{hf}) ! - call mpas_timer_start("hmix") call ocn_high_freq_thickness_hmix_del2_tend(meshPool, highFreqThickness, tend_highFreqThickness, err) - call mpas_timer_stop("hmix") call mpas_timer_stop("ocn_tend_freq_filtered_thickness") @@ -569,7 +1026,7 @@ end subroutine ocn_tend_freq_filtered_thickness!}}} !> \brief Initializes flags used within tendency routines. !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date 4 November 2011 -!> \details +!> \details !> This routine initializes flags related to quantities computed within !> other tendency routines. ! diff --git a/src/core_ocean/shared/mpas_ocn_test.F b/src/core_ocean/shared/mpas_ocn_test.F index 0a1aae428e..366c01da3a 100644 --- a/src/core_ocean/shared/mpas_ocn_test.F +++ b/src/core_ocean/shared/mpas_ocn_test.F @@ -69,7 +69,7 @@ module ocn_test !> \brief Call all internal start-up tests !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date October 2013 -!> \details +!> \details !> Call all routines to test various MPAS-Ocean components. ! !----------------------------------------------------------------------- @@ -114,7 +114,7 @@ end subroutine ocn_test_suite!}}} !> \brief set up scratch variables to test strain rate and tensor divergence operators !> \author Mark Petersen !> \date May 2013 -!> \details +!> \details !> This routine sets up scratch variables to test strain rate and tensor divergence operators. ! !----------------------------------------------------------------------- @@ -197,6 +197,7 @@ subroutine ocn_prep_test_tensor(domain,err)!{{{ call mpas_allocate_scratch_field(divTensorLonLatRCellSolutionField, .false.) call mpas_allocate_scratch_field(outerProductEdgeField, .false.) + call mpas_test_tensor(domain, config_tensor_test_function, & edgeSignOnCellField, & edgeTangentVectorsField, & @@ -214,6 +215,7 @@ subroutine ocn_prep_test_tensor(domain,err)!{{{ divTensorLonLatRCellSolutionField, & outerProductEdgeField ) + call mpas_deallocate_scratch_field(normalVelocityTestField, .false.) call mpas_deallocate_scratch_field(tangentialVelocityTestField, .false.) call mpas_deallocate_scratch_field(strainRateR3CellField, .false.) @@ -239,7 +241,7 @@ end subroutine ocn_prep_test_tensor!}}} !> \brief Initialize Gent-McWilliams test functions !> \author Mark Petersen !> \date May 2014 -!> \details +!> \details !> For the initial temperature distribution !> T = T_1 + T_2*y/y_{max} + T_3*z/z_{max} !> and linear EOS with T coefficient alpha, this subroutine computes @@ -281,14 +283,13 @@ subroutine ocn_init_gm_test_functions(diagnosticsPool, meshPool, scratchPool)!{{ real(kind=RKIND) :: zTop, config_gm_analytic_temperature2, config_gm_analytic_temperature3, config_gm_analytic_ymax, & config_gm_analytic_bottom_depth, L, R, c1, c2, zMax, zBot - real (kind=RKIND), pointer :: config_gravWaveSpeed_trunc, config_density0, config_standardGM_tracer_kappa, config_eos_linear_alpha + real (kind=RKIND), pointer :: config_gravWaveSpeed_trunc, config_standardGM_tracer_kappa, config_eos_linear_alpha real(kind=RKIND), dimension(:), pointer :: bottomDepth, refBottomDepthTopOfCell, yCell, yEdge real(kind=RKIND), dimension(:,:), pointer :: yRelativeSlopeSolution, yGMStreamFuncSolution, yGMBolusVelocitySolution, zMid type (field2DReal), pointer :: yRelativeSlopeSolutionField, yGMStreamFuncSolutionField, yGMBolusVelocitySolutionField - call mpas_pool_get_config(ocnConfigs, 'config_density0',config_density0) call mpas_pool_get_config(ocnConfigs, 'config_eos_linear_alpha', config_eos_linear_alpha) call mpas_pool_get_config(ocnConfigs, 'config_gravWaveSpeed_trunc',config_gravWaveSpeed_trunc) call mpas_pool_get_config(ocnConfigs, 'config_standardGM_tracer_kappa',config_standardGM_tracer_kappa) @@ -309,24 +310,26 @@ subroutine ocn_init_gm_test_functions(diagnosticsPool, meshPool, scratchPool)!{{ yRelativeSlopeSolution => yRelativeSlopeSolutionField % array yGMStreamFuncSolution => yGMStreamFuncSolutionField % array - yGMBolusVelocitySolution => yGMBolusVelocitySolutionField % array + yGMBolusVelocitySolution => yGMBolusVelocitySolutionField % array ! These are flags that must match your initial conditions settings. See gm_analytic initial condition in mode_init. - config_gm_analytic_temperature2 = 10; - config_gm_analytic_temperature3 = -10; - config_gm_analytic_ymax = 500000; - config_gm_analytic_bottom_depth = 1000; + config_gm_analytic_temperature2 = 10 + config_gm_analytic_temperature3 = -10 + config_gm_analytic_ymax = 500000 + config_gm_analytic_bottom_depth = 1000 ! zMax is associated with linear temperature profile in z - zMax = -config_gm_analytic_bottom_depth; - ! zBot is location we apply boundary conditions on the ODE for stream function. - zBot = zMax; + zMax = -config_gm_analytic_bottom_depth + ! zBot is location we apply boundary conditions on the ODE for stream function. + zBot = zMax - L = config_gravWaveSpeed_trunc * sqrt(config_density0*zMax/gravity/config_eos_linear_alpha/config_gm_analytic_temperature3); - R = - config_standardGM_tracer_kappa * config_gm_analytic_temperature2 * zMax / config_gm_analytic_temperature3 / config_gm_analytic_ymax; - c1 = R*(1-exp(-zBot/L))/(exp(zBot/L) - exp(-zBot/L)); - c2 = R-c1; + L = config_gravWaveSpeed_trunc * sqrt(rho_sw * zMax / gravity / config_eos_linear_alpha / config_gm_analytic_temperature3) + R = - config_standardGM_tracer_kappa * config_gm_analytic_temperature2 * zMax / config_gm_analytic_temperature3 & + / config_gm_analytic_ymax + c1 = R*(1-exp(-zBot/L))/(exp(zBot/L) - exp(-zBot/L)) + c2 = R-c1 + !$omp do schedule(runtime) private(k, zTop) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) @@ -342,11 +345,12 @@ subroutine ocn_init_gm_test_functions(diagnosticsPool, meshPool, scratchPool)!{{ end do k = maxLevelCell(iCell)+1 - ! placed at top interface, cell center. - zTop = zBot - yGMStreamFuncSolution(k,iCell) = c1*exp(zTop/L) + c2*exp(-zTop/L) - R; + ! placed at top interface, cell center. + zTop = zBot + yGMStreamFuncSolution(k,iCell) = c1*exp(zTop/L) + c2*exp(-zTop/L) - R; end do + !$omp end do end subroutine ocn_init_gm_test_functions!}}} diff --git a/src/core_ocean/shared/mpas_ocn_thick_ale.F b/src/core_ocean/shared/mpas_ocn_thick_ale.F index 02f4960631..7b10d3e841 100644 --- a/src/core_ocean/shared/mpas_ocn_thick_ale.F +++ b/src/core_ocean/shared/mpas_ocn_thick_ale.F @@ -62,14 +62,14 @@ module ocn_thick_ale !> \brief Computes desired ALE thickness at new time !> \author Mark Petersen !> \date August 2013 -!> \details +!> \details !> This routine computes the desired Arbitrary Lagrangian-Eulerian (ALE) -!> thickness at the new time. It uses the ALE formulation, and includes +!> thickness at the new time. It uses the ALE formulation, and includes !> contributions from SSH variations (z-star), high-frequency divergence !> (z-tilde), and imposes a minimum layer thickness. ! !----------------------------------------------------------------------- - subroutine ocn_ALE_thickness(meshPool, verticalMeshPool, oldSSH, div_hu_btr, dt, ALE_thickness, err, newHighFreqThickness)!{{{ + subroutine ocn_ALE_thickness(meshPool, verticalMeshPool, SSH, ALE_thickness, err, newHighFreqThickness)!{{{ !----------------------------------------------------------------- ! @@ -84,15 +84,11 @@ subroutine ocn_ALE_thickness(meshPool, verticalMeshPool, oldSSH, div_hu_btr, dt, verticalMeshPool !< Input: vertical mesh information real (kind=RKIND), dimension(:), intent(in) :: & - oldSSH, &!< Input: sea surface height at old time - div_hu_btr !< Input: thickness-weighted barotropic divergence + SSH !< Input: sea surface height real (kind=RKIND), dimension(:,:), intent(in), optional :: & newHighFreqThickness !< Input: high frequency thickness. Alters ALE thickness. - real (kind=RKIND), intent(in) :: & - dt !< Input: time step - !----------------------------------------------------------------- ! ! output variables @@ -114,7 +110,7 @@ subroutine ocn_ALE_thickness(meshPool, verticalMeshPool, oldSSH, div_hu_btr, dt, integer, pointer :: nCells, nVertLevels integer, dimension(:), pointer :: maxLevelCell - real (kind=RKIND) :: thicknessSum, newSSH, remainder, newThickness, thicknessWithRemainder + real (kind=RKIND) :: thicknessSum, remainder, newThickness, thicknessWithRemainder real (kind=RKIND), dimension(:), pointer :: vertCoordMovementWeights real (kind=RKIND), dimension(:), allocatable :: & SSH_ALE_thickness, & !> ALE thickness alteration due to SSH (z-star) @@ -151,31 +147,34 @@ subroutine ocn_ALE_thickness(meshPool, verticalMeshPool, oldSSH, div_hu_btr, dt, ! ! ALE thickness alteration due to SSH (z-star) ! + !$omp do schedule(runtime) private(kMax, thicknessSum, k) do iCell = 1, nCells kMax = maxLevelCell(iCell) - newSSH = oldSSH(iCell) - dt*div_hu_btr(iCell) - thicknessSum = 1e-14 + thicknessSum = 1e-14_RKIND do k = 1, kMax - SSH_ALE_Thickness(k) = newSSH * vertCoordMovementWeights(k) * restingThickness(k, iCell) + SSH_ALE_Thickness(k) = SSH(iCell) * vertCoordMovementWeights(k) * restingThickness(k, iCell) thicknessSum = thicknessSum + vertCoordMovementWeights(k) * restingThickness(k, iCell) end do - SSH_ALE_Thickness = SSH_ALE_Thickness / thicknessSum ! Note that restingThickness is nonzero, and remaining terms are perturbations about zero. - ALE_Thickness(1:kMax, iCell) = & - restingThickness(1:kMax,iCell) & - + SSH_ALE_Thickness(1:kMax) + do k = 1, kMax + SSH_ALE_Thickness(k) = SSH_ALE_Thickness(k) / thicknessSum + ALE_Thickness(k, iCell) = restingThickness(k, iCell) + SSH_ALE_Thickness(k) + end do enddo + !$omp end do if (thicknessFilterActive) then + !$omp do schedule(runtime) private(kMax) do iCell = 1, nCells kMax = maxLevelCell(iCell) - + ALE_Thickness(1:kMax, iCell) = & ALE_Thickness(1:kMax, iCell) & + newHighFreqThickness(1:kMax,iCell) enddo + !$omp end do end if ! @@ -183,31 +182,38 @@ subroutine ocn_ALE_thickness(meshPool, verticalMeshPool, oldSSH, div_hu_btr, dt, ! if (config_use_min_max_thickness) then + !$omp do schedule(runtime) private(kMax, remainder, k, newThickness) do iCell = 1, nCells kMax = maxLevelCell(iCell) ! go down the column: prelim_ALE_Thickness(1:kMax) = ALE_Thickness(1:kMax, iCell) - remainder = 0.0 + remainder = 0.0_RKIND do k = 1, kMax - newThickness = max(min(prelim_ALE_Thickness(k) + remainder, config_max_thickness_factor * restingThickness(k,iCell)), config_min_thickness) - min_ALE_thickness_down(k) = newThickness - prelim_ALE_Thickness(k) + newThickness = max( min(prelim_ALE_Thickness(k) + remainder, & + config_max_thickness_factor * restingThickness(k,iCell) ), & + config_min_thickness) + min_ALE_thickness_down(k) = newThickness - prelim_ALE_Thickness(k) remainder = remainder - min_ALE_thickness_down(k) end do ! go back up the column: - min_ALE_thickness_up(kMax) = 0.0 + min_ALE_thickness_up(kMax) = 0.0_RKIND prelim_ALE_Thickness(1:kMax) = prelim_ALE_Thickness(1:kMax) + min_ALE_thickness_down(1:kMax) do k = kMax-1, 1, -1 - newThickness = max(min(prelim_ALE_Thickness(k) + remainder, config_max_thickness_factor * restingThickness(k,iCell)), config_min_thickness) - min_ALE_thickness_up(k) = newThickness - prelim_ALE_Thickness(k) + newThickness = max( min(prelim_ALE_Thickness(k) + remainder, & + config_max_thickness_factor * restingThickness(k,iCell) ), & + config_min_thickness) + min_ALE_thickness_up(k) = newThickness - prelim_ALE_Thickness(k) remainder = remainder - min_ALE_thickness_up(k) end do min_ALE_thickness_up(1) = min_ALE_thickness_up(1) + remainder - ALE_Thickness(1:kMax, iCell) = ALE_Thickness(1:kMax, iCell) + min_ALE_thickness_down(1:kMax) + min_ALE_thickness_up(1:kMax) + ALE_Thickness(1:kMax, iCell) = ALE_Thickness(1:kMax, iCell) + min_ALE_thickness_down(1:kMax) & + + min_ALE_thickness_up(1:kMax) enddo + !$omp end do endif ! config_use_min_max_thickness @@ -222,7 +228,7 @@ end subroutine ocn_ALE_thickness!}}} !> \brief Initializes flags used within diagnostics routines. !> \author Mark Petersen !> \date August 2013 -!> \details +!> \details !> This routine initializes flags related to quantities computed within !> other diagnostics routines. ! diff --git a/src/core_ocean/shared/mpas_ocn_thick_hadv.F b/src/core_ocean/shared/mpas_ocn_thick_hadv.F index 9b6433133b..cf938762d0 100644 --- a/src/core_ocean/shared/mpas_ocn_thick_hadv.F +++ b/src/core_ocean/shared/mpas_ocn_thick_hadv.F @@ -13,13 +13,14 @@ !> \author Doug Jacobsen !> \date 16 September 2011 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> tendencies for thickness from horizontal advection ! !----------------------------------------------------------------------- module ocn_thick_hadv + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants @@ -48,7 +49,7 @@ module ocn_thick_hadv ! Private module variables ! !-------------------------------------------------------------------- - + logical :: thickHadvOn !*********************************************************************** @@ -62,7 +63,7 @@ module ocn_thick_hadv !> \brief Computes tendency term from horizontal advection of thickness !> \author Doug Jacobsen !> \date 15 September 2011 -!> \details +!> \details !> This routine computes the horizontal advection tendency for !> thicknes based on current state and user choices of forcings. ! @@ -108,8 +109,9 @@ subroutine ocn_thick_hadv_tend(meshPool, normalVelocity, layerThicknessEdge, ten ! !----------------------------------------------------------------- - integer :: iEdge, cell1, cell2, k, i, iCell - integer, pointer :: nCells, nEdges, nVertLevels + integer :: iEdge, cell1, cell2, k, i, iCell, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: maxLevelEdgeBot, MaxLevelCell, nEdgesOnCell integer, dimension(:,:), pointer :: cellsOnEdge, edgesOnCell, edgeSignOnCell @@ -120,7 +122,7 @@ subroutine ocn_thick_hadv_tend(meshPool, normalVelocity, layerThicknessEdge, ten !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- @@ -129,8 +131,9 @@ subroutine ocn_thick_hadv_tend(meshPool, normalVelocity, layerThicknessEdge, ten if(.not.thickHadvOn) return - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_timer_start("thick hadv") + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) @@ -143,8 +146,11 @@ subroutine ocn_thick_hadv_tend(meshPool, normalVelocity, layerThicknessEdge, ten call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + nCells = nCellsArray( 1 ) + + !$omp do schedule(runtime) private(invAreaCell, i, iEdge, k, flux) do iCell = 1, nCells - invAreaCell = 1.0 / areaCell(iCell) + invAreaCell = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) do k = 1, maxLevelEdgeBot(iEdge) @@ -153,6 +159,9 @@ subroutine ocn_thick_hadv_tend(meshPool, normalVelocity, layerThicknessEdge, ten end do end do end do + !$omp end do + + call mpas_timer_stop("thick hadv") !-------------------------------------------------------------------- @@ -165,9 +174,9 @@ end subroutine ocn_thick_hadv_tend!}}} !> \brief Initializes ocean horizontal thickness advection !> \author Doug Jacobsen !> \date 16 September 2011 -!> \details -!> This routine initializes quantities related to horizontal thickness -!> advection in the ocean. +!> \details +!> This routine initializes quantities related to horizontal thickness +!> advection in the ocean. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_thick_surface_flux.F b/src/core_ocean/shared/mpas_ocn_thick_surface_flux.F index 8593715cd2..d9caec3625 100644 --- a/src/core_ocean/shared/mpas_ocn_thick_surface_flux.F +++ b/src/core_ocean/shared/mpas_ocn_thick_surface_flux.F @@ -13,13 +13,14 @@ !> \author Doug Jacobsen !> \date 12/17/12 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> tendencies for thickness from surface fluxes ! !----------------------------------------------------------------------- module ocn_thick_surface_flux + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants @@ -64,13 +65,14 @@ module ocn_thick_surface_flux !> \brief Computes tendency term from horizontal advection of thickness !> \author Doug Jacobsen !> \date 15 September 2011 -!> \details +!> \details !> This routine computes the horizontal advection tendency for !> thicknes based on current state and user choices of forcings. ! !----------------------------------------------------------------------- - subroutine ocn_thick_surface_flux_tend(meshPool, transmissionCoefficients, layerThickness, surfaceThicknessFlux, tend, err)!{{{ + subroutine ocn_thick_surface_flux_tend(meshPool, transmissionCoefficients, transmissionCoefficientsRunoff, & + layerThickness, surfaceThicknessFlux, surfaceThicknessFluxRunoff, tend, err)!{{{ !----------------------------------------------------------------- ! ! input variables @@ -81,13 +83,15 @@ subroutine ocn_thick_surface_flux_tend(meshPool, transmissionCoefficients, layer meshPool !< Input: mesh information real (kind=RKIND), dimension(:,:), intent(in) :: & - transmissionCoefficients !< Input: Coefficients for the transmission of surface fluxes + transmissionCoefficients, &!< Input: Coefficients for the transmission of surface fluxes + transmissionCoefficientsRunoff !< Input: Coefficients for the transmission of surface fluxes due to river runoff real (kind=RKIND), dimension(:,:), intent(in) :: & layerThickness !< Input: Layer thickness real (kind=RKIND), dimension(:), intent(in) :: & - surfaceThicknessFlux !< Input: surface flux of thickness + surfaceThicknessFlux, &!< Input: surface flux of thickness + surfaceThicknessFluxRunoff !< Input: surface flux of thickness due to river runoff !----------------------------------------------------------------- @@ -113,34 +117,49 @@ subroutine ocn_thick_surface_flux_tend(meshPool, transmissionCoefficients, layer ! !----------------------------------------------------------------- - integer :: iCell, k - integer, pointer :: nCells, nVertLevels + integer :: iCell, k, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: maxLevelCell - integer, dimension(:,:), pointer :: cellMask - real (kind=RKIND) :: remainingFlux + real (kind=RKIND) :: remainingFlux, remainingFluxRunoff err = 0 if (.not. surfaceThicknessFluxOn) return + call mpas_timer_start("thick surface flux") + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_array(meshPool, 'cellMask', cellMask) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + + nCells = nCellsArray( 1 ) + !$omp do schedule(runtime) private(remainingFlux, remainingFluxRunoff, k) do iCell = 1, nCells remainingFlux = 1.0_RKIND + remainingFluxRunoff = 1.0_RKIND do k = 1, maxLevelCell(iCell) remainingFlux = remainingFlux - transmissionCoefficients(k, iCell) + remainingFluxRunoff = remainingFluxRunoff - transmissionCoefficientsRunoff(k, iCell) - tend(k, iCell) = tend(k, iCell) + cellMask(k, iCell) * surfaceThicknessFlux(iCell) * transmissionCoefficients(k, iCell) + tend(k, iCell) = tend(k, iCell) + surfaceThicknessFlux(iCell) * transmissionCoefficients(k, iCell) & + + surfaceThicknessFluxRunoff(iCell) * transmissionCoefficientsRunoff(k, iCell) end do if(maxLevelCell(iCell) > 0 .and. remainingFlux > 0.0_RKIND) then - tend(maxLevelCell(iCell), iCell) = tend(maxLevelCell(iCell), iCell) + cellMask(maxLevelCell(iCell), iCell) * remainingFlux * surfaceThicknessFlux(iCell) + tend(maxLevelCell(iCell), iCell) = tend(maxLevelCell(iCell), iCell) + remainingFlux * surfaceThicknessFlux(iCell) + end if + + if(maxLevelCell(iCell) > 0 .and. remainingFluxRunoff > 0.0_RKIND) then + tend(maxLevelCell(iCell), iCell) = tend(maxLevelCell(iCell), iCell) & + + remainingFluxRunoff * surfaceThicknessFluxRunoff(iCell) end if end do + !$omp end do + + call mpas_timer_stop("thick surface flux") !-------------------------------------------------------------------- @@ -153,9 +172,9 @@ end subroutine ocn_thick_surface_flux_tend!}}} !> \brief Initializes ocean horizontal thickness surface fluxes !> \author Doug Jacobsen !> \date 12/17/12 -!> \details -!> This routine initializes quantities related to thickness -!> surface fluxes in the ocean. +!> \details +!> This routine initializes quantities related to thickness +!> surface fluxes in the ocean. ! !----------------------------------------------------------------------- @@ -172,12 +191,10 @@ subroutine ocn_thick_surface_flux_init(err)!{{{ integer, intent(out) :: err !< Output: error flag logical, pointer :: config_disable_thick_sflux - character (len=StrKIND), pointer :: config_forcing_type err = 0 call mpas_pool_get_config(ocnConfigs, 'config_disable_thick_sflux', config_disable_thick_sflux) - call mpas_pool_get_config(ocnConfigs, 'config_forcing_type', config_forcing_type) surfaceThicknessFluxOn = .true. @@ -185,11 +202,6 @@ subroutine ocn_thick_surface_flux_init(err)!{{{ surfaceThicknessFluxOn = .false. end if - if (config_forcing_type == trim('off')) then - surfaceThicknessFluxOn = .false. - end if - - !-------------------------------------------------------------------- end subroutine ocn_thick_surface_flux_init!}}} diff --git a/src/core_ocean/shared/mpas_ocn_thick_vadv.F b/src/core_ocean/shared/mpas_ocn_thick_vadv.F index ba1f5a096d..baec62a6d6 100644 --- a/src/core_ocean/shared/mpas_ocn_thick_vadv.F +++ b/src/core_ocean/shared/mpas_ocn_thick_vadv.F @@ -13,13 +13,14 @@ !> \author Doug Jacobsen !> \date 16 September 2011 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> tendencies for thickness from vertical advection ! !----------------------------------------------------------------------- module ocn_thick_vadv + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants @@ -62,7 +63,7 @@ module ocn_thick_vadv !> \brief Computes tendency term from vertical advection of thickness !> \author Doug Jacobsen !> \date 15 September 2011 -!> \details +!> \details !> This routine computes the vertical advection tendency for !> thicknes based on current state and user choices of forcings. ! @@ -105,14 +106,15 @@ subroutine ocn_thick_vadv_tend(meshPool, vertAleTransportTop, tend, err)!{{{ ! !----------------------------------------------------------------- - integer :: iCell, k - integer, pointer :: nCells, nVertLevels + integer :: iCell, k, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: MaxLevelCell !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- @@ -121,16 +123,24 @@ subroutine ocn_thick_vadv_tend(meshPool, vertAleTransportTop, tend, err)!{{{ if(.not.thickVadvOn) return + call mpas_timer_start("thick vadv") + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + nCells = nCellsArray( 1 ) + + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 1, maxLevelCell(iCell) tend(k,iCell) = tend(k,iCell) + vertAleTransportTop(k+1,iCell) - vertAleTransportTop(k,iCell) end do end do + !$omp end do + + call mpas_timer_stop("thick vadv") !-------------------------------------------------------------------- @@ -143,9 +153,9 @@ end subroutine ocn_thick_vadv_tend!}}} !> \brief Initializes ocean thickness vertical advection !> \author Doug Jacobsen !> \date 16 September 2011 -!> \details -!> This routine initializes quantities related to vertical advection of -!> thickness in the ocean. +!> \details +!> This routine initializes quantities related to vertical advection of +!> thickness in the ocean. ! !----------------------------------------------------------------------- @@ -168,7 +178,7 @@ subroutine ocn_thick_vadv_init(err)!{{{ thickVadvOn = .true. if(config_disable_thick_vadv) thickVadvOn = .false. - + err = 0 !-------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_time_average.F b/src/core_ocean/shared/mpas_ocn_time_average.F deleted file mode 100644 index 82aa016364..0000000000 --- a/src/core_ocean/shared/mpas_ocn_time_average.F +++ /dev/null @@ -1,215 +0,0 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! -module ocn_time_average - - use mpas_derived_types - use mpas_pool_routines - - implicit none - save - public - - contains - - subroutine ocn_time_average_init(averagePool)!{{{ - type (mpas_pool_type), intent(inout) :: averagePool - - real (kind=RKIND), pointer :: nAverage - - real (kind=RKIND), dimension(:), pointer :: avgSSH, varSSH - real (kind=RKIND), dimension(:,:), pointer :: & - avgNormalVelocity, avgVelocityZonal, avgVelocityMeridional, avgVertVelocityTop, & - varNormalVelocity, varVelocityZonal, varVelocityMeridional, & - avgNormalTransportVelocity, avgTransportVelocityZonal, avgTransportVelocityMeridional, avgVertTransportVelocityTop, & - avgNormalGMBolusVelocity, avgGMBolusVelocityZonal, avgGMBolusVelocityMeridional, avgVertGMBolusVelocityTop - - call mpas_pool_get_array(averagePool, 'nAverage', nAverage) - call mpas_pool_get_array(averagePool, 'avgSSH', avgSSH) - call mpas_pool_get_array(averagePool, 'varSSH', varSSH) - call mpas_pool_get_array(averagePool, 'avgNormalVelocity', avgNormalVelocity) - call mpas_pool_get_array(averagePool, 'avgVelocityZonal', avgVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgVelocityMeridional', avgVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertVelocityTop', avgVertVelocityTop) - call mpas_pool_get_array(averagePool, 'varNormalVelocity', varNormalVelocity) - call mpas_pool_get_array(averagePool, 'varVelocityZonal', varVelocityZonal) - call mpas_pool_get_array(averagePool, 'varVelocityMeridional', varVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgNormalTransportVelocity', avgNormalTransportVelocity) - call mpas_pool_get_array(averagePool, 'avgTransportVelocityZonal', avgTransportVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgTransportVelocityMeridional', avgTransportVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertTransportVelocityTop', avgVertTransportVelocityTop) - call mpas_pool_get_array(averagePool, 'avgNormalGMBolusVelocity', avgNormalGMBolusVelocity) - call mpas_pool_get_array(averagePool, 'avgGMBolusVelocityZonal', avgGMBolusVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgGMBolusVelocityMeridional', avgGMBolusVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertGMBolusVelocityTop', avgVertGMBolusVelocityTop) - - nAverage = 0 - - avgSSH = 0.0 - varSSH = 0.0 - avgNormalVelocity = 0.0 - avgVelocityZonal = 0.0 - avgVelocityMeridional = 0.0 - avgVertVelocityTop = 0.0 - varNormalVelocity = 0.0 - varVelocityZonal = 0.0 - varVelocityMeridional = 0.0 - avgNormalTransportVelocity = 0.0 - avgTransportVelocityZonal = 0.0 - avgTransportVelocityMeridional = 0.0 - avgVertTransportVelocityTop = 0.0 - avgNormalGMBolusVelocity = 0.0 - avgGMBolusVelocityZonal = 0.0 - avgGMBolusVelocityMeridional = 0.0 - avgVertGMBolusVelocityTop = 0.0 - - end subroutine ocn_time_average_init!}}} - - subroutine ocn_time_average_accumulate(averagePool, statePool, diagnosticsPool, timeLevelIn)!{{{ - type (mpas_pool_type), intent(inout) :: averagePool - type (mpas_pool_type), intent(in) :: statePool - type (mpas_pool_type), intent(in) :: diagnosticsPool - integer, intent(in), optional :: timeLevelIn - - real (kind=RKIND), pointer :: nAverage, old_nAverage - - real (kind=RKIND), dimension(:), pointer :: ssh - real (kind=RKIND), dimension(:,:), pointer :: & - velocityZonal, velocityMeridional, normalVelocity, vertVelocityTop, & - transportVelocityZonal, transportVelocityMeridional, normalTransportVelocity, vertTransportVelocityTop, & - GMBolusVelocityZonal, GMBolusVelocityMeridional, normalGMBolusVelocity, vertGMBolusVelocityTop - - real (kind=RKIND), dimension(:), pointer :: avgSSH, varSSH - real (kind=RKIND), dimension(:,:), pointer :: & - avgNormalVelocity, avgVelocityZonal, avgVelocityMeridional, avgVertVelocityTop, & - varNormalVelocity, varVelocityZonal, varVelocityMeridional, & - avgNormalTransportVelocity, avgTransportVelocityZonal, avgTransportVelocityMeridional, avgVertTransportVelocityTop, & - avgNormalGMBolusVelocity, avgGMBolusVelocityZonal, avgGMBolusVelocityMeridional, avgVertGMBolusVelocityTop - - real (kind=RKIND), dimension(:), pointer :: old_avgSSH, old_varSSH - real (kind=RKIND), dimension(:,:), pointer :: & - old_avgNormalVelocity, old_avgVelocityZonal, old_avgVelocityMeridional, old_avgVertVelocityTop, & - old_varNormalVelocity, old_varVelocityZonal, old_varVelocityMeridional, & - old_avgNormalTransportVelocity, old_avgTransportVelocityZonal, old_avgTransportVelocityMeridional, old_avgVertTransportVelocityTop, & - old_avgNormalGMBolusVelocity, old_avgGMBolusVelocityZonal, old_avgGMBolusVelocityMeridional, old_avgVertGMBolusVelocityTop - - integer :: timeLevel - - if (present(timeLevelIn)) then - timeLevel = timeLevelIn - else - timeLevel = 1 - end if - - call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) - call mpas_pool_get_array(statePool, 'ssh', ssh, timeLevel) - - call mpas_pool_get_array(diagnosticsPool, 'velocityZonal', velocityZonal) - call mpas_pool_get_array(diagnosticsPool, 'velocityMeridional', velocityMeridional) - call mpas_pool_get_array(diagnosticsPool, 'vertVelocityTop', vertVelocityTop) - call mpas_pool_get_array(diagnosticsPool, 'normalTransportVelocity ', normalTransportVelocity) - call mpas_pool_get_array(diagnosticsPool, 'transportVelocityZonal', transportVelocityZonal) - call mpas_pool_get_array(diagnosticsPool, 'transportVelocityMeridional', transportVelocityMeridional) - call mpas_pool_get_array(diagnosticsPool, 'vertTransportVelocityTop', vertTransportVelocityTop) - call mpas_pool_get_array(diagnosticsPool, 'normalGMBolusVelocity', normalGMBolusVelocity) - call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityZonal', GMBolusVelocityZonal) - call mpas_pool_get_array(diagnosticsPool, 'GMBolusVelocityMeridional', GMBolusVelocityMeridional) - call mpas_pool_get_array(diagnosticsPool, 'vertGMBolusVelocityTop', vertGMBolusVelocityTop) - - call mpas_pool_get_array(averagePool, 'nAverage', nAverage) - call mpas_pool_get_array(averagePool, 'avgSSH', avgSSH) - call mpas_pool_get_array(averagePool, 'varSSH', varSSH) - call mpas_pool_get_array(averagePool, 'avgNormalVelocity', avgNormalVelocity) - call mpas_pool_get_array(averagePool, 'avgVelocityZonal', avgVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgVelocityMeridional', avgVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertVelocityTop', avgVertVelocityTop) - call mpas_pool_get_array(averagePool, 'varNormalVelocity', varNormalVelocity) - call mpas_pool_get_array(averagePool, 'varVelocityZonal', varVelocityZonal) - call mpas_pool_get_array(averagePool, 'varVelocityMeridional', varVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgNormalTransportVelocity', avgNormalTransportVelocity) - call mpas_pool_get_array(averagePool, 'avgTransportVelocityZonal', avgTransportVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgTransportVelocityMeridional', avgTransportVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertTransportVelocityTop', avgVertTransportVelocityTop) - call mpas_pool_get_array(averagePool, 'avgNormalGMBolusVelocity', avgNormalGMBolusVelocity) - call mpas_pool_get_array(averagePool, 'avgGMBolusVelocityZonal', avgGMBolusVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgGMBolusVelocityMeridional', avgGMBolusVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertGMBolusVelocityTop', avgVertGMBolusVelocityTop) - - avgSSH = avgSSH + ssh - varSSH = varSSH + ssh**2 - avgNormalVelocity = avgNormalVelocity + normalVelocity - avgVelocityZonal = avgVelocityZonal + velocityZonal - avgVelocityMeridional = avgVelocityMeridional + velocityMeridional - avgVertVelocityTop = avgVertVelocityTop + vertVelocityTop - varNormalVelocity = varNormalVelocity + normalVelocity**2 - varVelocityZonal = varVelocityZonal + velocityZonal**2 - varVelocityMeridional = varVelocityMeridional + velocityMeridional**2 - avgNormalTransportVelocity = avgNormalTransportVelocity + normalTransportVelocity - avgTransportVelocityZonal = avgTransportVelocityZonal + transportVelocityZonal - avgTransportVelocityMeridional = avgTransportVelocityMeridional + transportVelocityMeridional - avgVertTransportVelocityTop = avgVertTransportVelocityTop + vertTransportVelocityTop - avgNormalGMBolusVelocity = avgNormalGMBolusVelocity + normalGMBolusVelocity - avgGMBolusVelocityZonal = avgGMBolusVelocityZonal + GMBolusVelocityZonal - avgGMBolusVelocityMeridional = avgGMBolusVelocityMeridional + GMBolusVelocityMeridional - avgVertGMBolusVelocityTop = avgVertGMBolusVelocityTop + vertGMBolusVelocityTop - - nAverage = nAverage + 1 - end subroutine ocn_time_average_accumulate!}}} - - subroutine ocn_time_average_normalize(averagePool)!{{{ - type (mpas_pool_type), intent(inout) :: averagePool - - real (kind=RKIND), pointer :: nAverage - - real (kind=RKIND), dimension(:), pointer :: avgSSH, varSSH - real (kind=RKIND), dimension(:,:), pointer :: & - avgNormalVelocity, avgVelocityZonal, avgVelocityMeridional, avgVertVelocityTop, & - varNormalVelocity, varVelocityZonal, varVelocityMeridional, & - avgNormalTransportVelocity, avgTransportVelocityZonal, avgTransportVelocityMeridional, avgVertTransportVelocityTop, & - avgNormalGMBolusVelocity, avgGMBolusVelocityZonal, avgGMBolusVelocityMeridional, avgVertGMBolusVelocityTop - - call mpas_pool_get_array(averagePool, 'nAverage', nAverage) - call mpas_pool_get_array(averagePool, 'avgSSH', avgSSH) - call mpas_pool_get_array(averagePool, 'varSSH', varSSH) - call mpas_pool_get_array(averagePool, 'avgNormalVelocity', avgNormalVelocity) - call mpas_pool_get_array(averagePool, 'avgVelocityZonal', avgVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgVelocityMeridional', avgVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertVelocityTop', avgVertVelocityTop) - call mpas_pool_get_array(averagePool, 'varNormalVelocity', varNormalVelocity) - call mpas_pool_get_array(averagePool, 'varVelocityZonal', varVelocityZonal) - call mpas_pool_get_array(averagePool, 'varVelocityMeridional', varVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgNormalTransportVelocity', avgNormalTransportVelocity) - call mpas_pool_get_array(averagePool, 'avgTransportVelocityZonal', avgTransportVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgTransportVelocityMeridional', avgTransportVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertTransportVelocityTop', avgVertTransportVelocityTop) - call mpas_pool_get_array(averagePool, 'avgNormalGMBolusVelocity', avgNormalGMBolusVelocity) - call mpas_pool_get_array(averagePool, 'avgGMBolusVelocityZonal', avgGMBolusVelocityZonal) - call mpas_pool_get_array(averagePool, 'avgGMBolusVelocityMeridional', avgGMBolusVelocityMeridional) - call mpas_pool_get_array(averagePool, 'avgVertGMBolusVelocityTop', avgVertGMBolusVelocityTop) - - if(nAverage > 0) then - avgSSH = avgSSH / nAverage - varSSH = varSSH / nAverage - avgNormalVelocity = avgNormalVelocity / nAverage - avgVelocityZonal = avgVelocityZonal / nAverage - avgVelocityMeridional = avgVelocityMeridional / nAverage - avgVertVelocityTop = avgVertVelocityTop / nAverage - varNormalVelocity = varNormalVelocity / nAverage - varVelocityZonal = varVelocityZonal / nAverage - varVelocityMeridional = varVelocityMeridional / nAverage - avgNormalTransportVelocity = avgNormalTransportVelocity / nAverage - avgTransportVelocityZonal = avgTransportVelocityZonal / nAverage - avgTransportVelocityMeridional = avgTransportVelocityMeridional / nAverage - avgVertTransportVelocityTop = avgVertTransportVelocityTop / nAverage - avgNormalGMBolusVelocity = avgNormalGMBolusVelocity / nAverage - avgGMBolusVelocityZonal = avgGMBolusVelocityZonal / nAverage - avgGMBolusVelocityMeridional = avgGMBolusVelocityMeridional / nAverage - avgVertGMBolusVelocityTop = avgVertGMBolusVelocityTop / nAverage - end if - end subroutine ocn_time_average_normalize!}}} - -end module ocn_time_average diff --git a/src/core_ocean/shared/mpas_ocn_time_average_coupled.F b/src/core_ocean/shared/mpas_ocn_time_average_coupled.F index 6df6612234..6e897011d9 100644 --- a/src/core_ocean/shared/mpas_ocn_time_average_coupled.F +++ b/src/core_ocean/shared/mpas_ocn_time_average_coupled.F @@ -24,12 +24,15 @@ module ocn_time_average_coupled use mpas_derived_types use mpas_pool_routines use ocn_constants + use ocn_tracer_ecosys + use ocn_tracer_DMS + use ocn_tracer_MacroMolecules implicit none save public - contains + contains !*********************************************************************** ! @@ -38,28 +41,151 @@ module ocn_time_average_coupled !> \brief Coupled time averager initialization !> \author Doug Jacobsen !> \date 06/08/2013 -!> \details +!> \details !> This routine initializes the coupled time averaging fields ! !----------------------------------------------------------------------- subroutine ocn_time_average_coupled_init(forcingPool)!{{{ type (mpas_pool_type), intent(inout) :: forcingPool - real (kind=RKIND), dimension(:,:), pointer :: avgTracersSurfaceValue, avgSurfaceVelocity, avgSSHGradient + real (kind=RKIND), dimension(:,:), pointer :: avgTracersSurfaceValue, avgSurfaceVelocity, avgSSHGradient, & + avgLandIceBoundaryLayerTracers, avgLandIceTracerTransferVelocities + + real (kind=RKIND), dimension(:), pointer :: avgEffectiveDensityInLandIce + character (len=StrKIND), pointer :: config_land_ice_flux_mode + + integer :: iCell + integer, pointer :: nAccumulatedCoupled, nCells + + logical, pointer :: config_use_ecosysTracers, & + config_use_DMSTracers, & + config_use_MacroMoleculesTracers + + type (mpas_pool_type), pointer :: ecosysAuxiliary + + type (mpas_pool_type), pointer :: ecosysSeaIceCoupling, & + DMSSeaIceCoupling, & + MacroMoleculesSeaIceCoupling + + real (kind=RKIND), dimension(:), pointer :: avgCO2_gas_flux + + real (kind=RKIND), dimension(:), pointer :: avgOceanSurfaceDIC, & + avgOceanSurfaceDON, & + avgOceanSurfaceNO3, & + avgOceanSurfaceSiO3, & + avgOceanSurfaceNH4, & + avgOceanSurfaceDMS, & + avgOceanSurfaceDMSP, & + avgOceanSurfaceDOCr, & + avgOceanSurfaceFeParticulate, & + avgOceanSurfaceFeDissolved - integer, pointer :: nAccumulatedCoupled + real (kind=RKIND), dimension(:,:), pointer :: avgOceanSurfacePhytoC, & + avgOceanSurfaceDOC + + call mpas_pool_get_dimension(forcingPool, 'nCells', nCells) call mpas_pool_get_array(forcingPool, 'avgTracersSurfaceValue', avgTracersSurfaceValue) call mpas_pool_get_array(forcingPool, 'avgSurfaceVelocity', avgSurfaceVelocity) call mpas_pool_get_array(forcingPool, 'avgSSHGradient', avgSSHGradient) call mpas_pool_get_array(forcingPool, 'nAccumulatedCoupled', nAccumulatedCoupled) - avgTracersSurfaceValue(:,:) = 0.0_RKIND - avgSurfaceVelocity(:,:) = 0.0_RKIND - avgSSHGradient(:,:) = 0.0_RKIND + !$omp do schedule(runtime) + do iCell = 1, nCells + avgSurfaceVelocity(:, iCell) = 0.0_RKIND + avgTracersSurfaceValue(:, iCell) = 0.0_RKIND + avgSSHGradient(:, iCell) = 0.0_RKIND + end do + !$omp end do + + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + if(trim(config_land_ice_flux_mode) == 'coupled') then + call mpas_pool_get_array(forcingPool, 'avgLandIceBoundaryLayerTracers', avgLandIceBoundaryLayerTracers) + call mpas_pool_get_array(forcingPool, 'avgLandIceTracerTransferVelocities', avgLandIceTracerTransferVelocities) + call mpas_pool_get_array(forcingPool, 'avgEffectiveDensityInLandIce', avgEffectiveDensityInLandIce) + + !$omp do schedule(runtime) + do iCell = 1, nCells + avgLandIceBoundaryLayerTracers(:, iCell) = 0.0_RKIND + avgLandIceTracerTransferVelocities(:, iCell) = 0.0_RKIND + avgEffectiveDensityInLandIce(iCell) = 0.0_RKIND + end do + !$omp end do + end if + + ! set up BGC coupling fields if necessary + call mpas_pool_get_config(ocnConfigs, 'config_use_ecosysTracers', config_use_ecosysTracers) + call mpas_pool_get_config(ocnConfigs, 'config_use_DMSTracers', config_use_DMSTracers) + call mpas_pool_get_config(ocnConfigs, 'config_use_MacroMoleculesTracers', config_use_MacroMoleculesTracers) + + if (config_use_ecosysTracers) then + + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + call mpas_pool_get_array(ecosysAuxiliary, 'avgCO2_gas_flux', avgCO2_gas_flux) + + call mpas_pool_get_subpool(forcingPool, 'ecosysSeaIceCoupling', ecosysSeaIceCoupling) + + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfacePhytoC', avgOceanSurfacePhytoC) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceDIC', avgOceanSurfaceDIC) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceNO3', avgOceanSurfaceNO3) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceSiO3', avgOceanSurfaceSiO3) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceNH4', avgOceanSurfaceNH4) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceDOCr', avgOceanSurfaceDOCr) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceFeParticulate', avgOceanSurfaceFeParticulate) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceFeDissolved', avgOceanSurfaceFeDissolved) + + !$omp do schedule(runtime) + do iCell = 1, nCells + avgCO2_gas_flux(iCell) = 0.0_RKIND + + avgOceanSurfacePhytoC(:,iCell) = 0.0_RKIND + + avgOceanSurfaceDIC(iCell) = 0.0_RKIND + avgOceanSurfaceNO3(iCell) = 0.0_RKIND + avgOceanSurfaceSiO3(iCell) = 0.0_RKIND + avgOceanSurfaceNH4(iCell) = 0.0_RKIND + avgOceanSurfaceDOCr(iCell) = 0.0_RKIND + avgOceanSurfaceFeParticulate(iCell) = 0.0_RKIND + avgOceanSurfaceFeDissolved(iCell) = 0.0_RKIND + end do + !$omp end do + end if + + if (config_use_DMSTracers) then + call mpas_pool_get_subpool(forcingPool, 'DMSSeaIceCoupling', DMSSeaIceCoupling) + + call mpas_pool_get_array(DMSSeaIceCoupling, 'avgOceanSurfaceDMS', avgOceanSurfaceDMS) + call mpas_pool_get_array(DMSSeaIceCoupling, 'avgOceanSurfaceDMSP', avgOceanSurfaceDMSP) + + !$omp do schedule(runtime) + do iCell = 1, nCells + avgOceanSurfaceDMS(iCell) = 0.0_RKIND + avgOceanSurfaceDMSP(iCell) = 0.0_RKIND + end do + !$omp end do + endif + if (config_use_MacroMoleculesTracers) then + call mpas_pool_get_subpool(forcingPool, 'MacroMoleculesSeaIceCoupling', MacroMoleculesSeaIceCoupling) + + call mpas_pool_get_array(MacroMoleculesSeaIceCoupling, 'avgOceanSurfaceDOC', avgOceanSurfaceDOC) + call mpas_pool_get_array(MacroMoleculesSeaIceCoupling, 'avgOceanSurfaceDON', avgOceanSurfaceDON) + + !$omp do schedule(runtime) + do iCell = 1, nCells + avgOceanSurfaceDOC(:,iCell) = 0.0_RKIND + avgOceanSurfaceDON(iCell) = 0.0_RKIND + end do + !$omp end do + endif + + call mpas_threading_barrier() + !$omp master nAccumulatedCoupled = 0 + !$omp end master + call mpas_threading_barrier() + end subroutine ocn_time_average_coupled_init!}}} !*********************************************************************** @@ -69,19 +195,58 @@ end subroutine ocn_time_average_coupled_init!}}} !> \brief Coupled time averager accumulation !> \author Doug Jacobsen !> \date 06/08/2013 -!> \details +!> \details !> This routine accumulated the coupled time averaging fields ! !----------------------------------------------------------------------- - subroutine ocn_time_average_coupled_accumulate(diagnosticsPool, forcingPool)!{{{ + subroutine ocn_time_average_coupled_accumulate(diagnosticsPool, statePool, forcingPool, timeLevel)!{{{ type (mpas_pool_type), intent(in) :: diagnosticsPool + type (mpas_pool_type), intent(in) :: statePool type (mpas_pool_type), intent(inout) :: forcingPool + integer, intent(in) :: timeLevel real (kind=RKIND), dimension(:,:), pointer :: surfaceVelocity, avgSurfaceVelocity real (kind=RKIND), dimension(:,:), pointer :: tracersSurfaceValue, avgTracersSurfaceValue real (kind=RKIND), dimension(:,:), pointer :: avgSSHGradient - real (kind=RKIND), dimension(:,:), pointer :: gradSSHZonal, gradSSHMeridional - integer, pointer :: index_temperature, index_SSHzonal, index_SSHmeridional, nAccumulatedCoupled + real (kind=RKIND), dimension(:), pointer :: gradSSHZonal, gradSSHMeridional + integer :: iCell + integer, pointer :: index_temperature, index_SSHzonal, index_SSHmeridional, nAccumulatedCoupled, nCells + real (kind=RKIND), dimension(:,:), pointer :: landIceBoundaryLayerTracers, landIceTracerTransferVelocities, & + avgLandIceBoundaryLayerTracers, avgLandIceTracerTransferVelocities + real (kind=RKIND), dimension(:), pointer :: effectiveDensityInLandIce, avgEffectiveDensityInLandIce + character (len=StrKIND), pointer :: config_land_ice_flux_mode + + logical, pointer :: config_use_ecosysTracers, & + config_use_DMSTracers, & + config_use_MacroMoleculesTracers + + type (mpas_pool_type), pointer :: tracersPool + + real (kind=RKIND), dimension(:,:,:), pointer :: & + ecosysTracers, & + DMSTracers, & + MacroMoleculesTracers + + type (mpas_pool_type), pointer :: ecosysSeaIceCoupling, & + DMSSeaIceCoupling, & + MacroMoleculesSeaIceCoupling + + type (mpas_pool_type), pointer :: ecosysAuxiliary + real (kind=RKIND), dimension(:), pointer :: CO2_gas_flux, avgCO2_gas_flux + + real (kind=RKIND), dimension(:), pointer :: avgOceanSurfaceDIC, & + avgOceanSurfaceDON, & + avgOceanSurfaceNO3, & + avgOceanSurfaceSiO3, & + avgOceanSurfaceNH4, & + avgOceanSurfaceDMS, & + avgOceanSurfaceDMSP, & + avgOceanSurfaceDOCr, & + avgOceanSurfaceFeParticulate, & + avgOceanSurfaceFeDissolved + + real (kind=RKIND), dimension(:,:), pointer :: avgOceanSurfacePhytoC, & + avgOceanSurfaceDOC call mpas_pool_get_array(diagnosticsPool, 'tracersSurfaceValue', tracersSurfaceValue) call mpas_pool_get_array(diagnosticsPool, 'surfaceVelocity', surfaceVelocity) @@ -92,53 +257,158 @@ subroutine ocn_time_average_coupled_accumulate(diagnosticsPool, forcingPool)!{{{ call mpas_pool_get_array(forcingPool, 'avgSurfaceVelocity', avgSurfaceVelocity) call mpas_pool_get_array(forcingPool, 'avgSSHGradient', avgSSHGradient) + call mpas_pool_get_dimension(forcingPool, 'nCells', nCells) call mpas_pool_get_dimension(forcingPool, 'index_avgTemperatureSurfaceValue', index_temperature) call mpas_pool_get_dimension(forcingPool, 'index_avgSSHGradientZonal', index_SSHzonal) call mpas_pool_get_dimension(forcingPool, 'index_avgSSHGradientMeridional', index_SSHmeridional) call mpas_pool_get_array(forcingPool, 'nAccumulatedCoupled', nAccumulatedCoupled) - avgTracersSurfaceValue(:,:) = avgTracersSurfaceValue(:,:) * nAccumulatedCoupled + tracersSurfaceValue(:,:) - avgTracersSurfaceValue(index_temperature,:) = avgTracersSurfaceValue(index_temperature,:) + T0_Kelvin - avgTracersSurfaceValue(:,:) = avgTracersSurfaceValue(:,:) / ( nAccumulatedCoupled + 1 ) + !$omp do schedule(runtime) + do iCell = 1, nCells + avgTracersSurfaceValue(:, iCell) = avgTracersSurfaceValue(:, iCell) * nAccumulatedCoupled & + + tracersSurfaceValue(:, iCell) + avgTracersSurfaceValue(index_temperature, iCell) = avgTracersSurfaceValue(index_temperature, iCell) + T0_Kelvin + avgTracersSurfaceValue(:, iCell) = avgTracersSurfaceValue(:, iCell) / ( nAccumulatedCoupled + 1 ) - avgSurfaceVelocity(:,:) = ( avgSurfaceVelocity(:,:) * nAccumulatedCoupled + surfaceVelocity(:,:) ) / ( nAccumulatedCoupled + 1 ) + avgSSHGradient(index_SSHzonal, iCell) = ( avgSSHGradient(index_SSHzonal, iCell) * nAccumulatedCoupled & + + gradSSHZonal(iCell) ) / ( nAccumulatedCoupled + 1 ) + avgSSHGradient(index_SSHmeridional, iCell) = ( avgSSHGradient(index_SSHmeridional, iCell) * nAccumulatedCoupled & + + gradSSHMeridional(iCell) ) / ( nAccumulatedCoupled + 1 ) + avgSurfaceVelocity(:, iCell) = ( avgSurfaceVelocity(:, iCell) * nAccumulatedCoupled + surfaceVelocity(:, iCell) ) & + / ( nAccumulatedCoupled + 1 ) + end do + !$omp end do - avgSSHGradient(index_SSHzonal,:) = ( avgSSHGradient(index_SSHzonal,:) * nAccumulatedCoupled + gradSSHZonal(1,:) ) / ( nAccumulatedCoupled + 1 ) - avgSSHGradient(index_SSHmeridional,:) = ( avgSSHGradient(index_SSHmeridional,:) * nAccumulatedCoupled + gradSSHMeridional(1,:) ) / ( nAccumulatedCoupled + 1 ) + call mpas_pool_get_config(ocnConfigs, 'config_land_ice_flux_mode', config_land_ice_flux_mode) + if(trim(config_land_ice_flux_mode) == 'coupled') then + call mpas_pool_get_array(diagnosticsPool, 'landIceBoundaryLayerTracers', landIceBoundaryLayerTracers) + call mpas_pool_get_array(diagnosticsPool, 'landIceTracerTransferVelocities', landIceTracerTransferVelocities) + call mpas_pool_get_array(statePool, 'effectiveDensityInLandIce', effectiveDensityInLandIce, timeLevel) - nAccumulatedCoupled = nAccumulatedCoupled + 1 + call mpas_pool_get_array(forcingPool, 'avgLandIceBoundaryLayerTracers', avgLandIceBoundaryLayerTracers) + call mpas_pool_get_array(forcingPool, 'avgLandIceTracerTransferVelocities', avgLandIceTracerTransferVelocities) + call mpas_pool_get_array(forcingPool, 'avgEffectiveDensityInLandIce', avgEffectiveDensityInLandIce) - end subroutine ocn_time_average_coupled_accumulate!}}} + !$omp do schedule(runtime) + do iCell = 1, nCells + avgLandIceBoundaryLayerTracers(:, iCell) = ( avgLandIceBoundaryLayerTracers(:, iCell) * nAccumulatedCoupled & + + landIceBoundaryLayerTracers(:, iCell) ) / ( nAccumulatedCoupled + 1 ) + avgLandIceTracerTransferVelocities(:, iCell) = ( avgLandIceTracerTransferVelocities(:, iCell) * nAccumulatedCoupled & + + landIceTracerTransferVelocities(:, iCell) ) / ( nAccumulatedCoupled + 1) + avgEffectiveDensityInLandIce(iCell) = ( avgEffectiveDensityInLandIce(iCell) * nAccumulatedCoupled & + + effectiveDensityInLandIce(iCell) ) / ( nAccumulatedCoupled + 1) + end do + !$omp end do + end if -!*********************************************************************** -! -! routine ocn_time_average_coupled_normalize -! -!> \brief Coupled time averager normalization -!> \author Doug Jacobsen -!> \date 06/08/2013 -!> \details -!> This routine normalizes the coupled time averaging fields -! -!----------------------------------------------------------------------- - subroutine ocn_time_average_coupled_normalize(forcingPool)!{{{ + ! accumulate BGC coupling fields if necessary + call mpas_pool_get_config(ocnConfigs, 'config_use_ecosysTracers', config_use_ecosysTracers) + call mpas_pool_get_config(ocnConfigs, 'config_use_DMSTracers', config_use_DMSTracers) + call mpas_pool_get_config(ocnConfigs, 'config_use_MacroMoleculesTracers', config_use_MacroMoleculesTracers) - type (mpas_pool_type), intent(inout) :: forcingPool + if (config_use_ecosysTracers) then + + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + call mpas_pool_get_array(ecosysAuxiliary, 'CO2_gas_flux', CO2_gas_flux) + call mpas_pool_get_array(ecosysAuxiliary, 'avgCO2_gas_flux', avgCO2_gas_flux) + + call mpas_pool_get_subpool(forcingPool, 'ecosysSeaIceCoupling', ecosysSeaIceCoupling) + + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfacePhytoC', avgOceanSurfacePhytoC) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceDIC', avgOceanSurfaceDIC) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceNO3', avgOceanSurfaceNO3) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceSiO3', avgOceanSurfaceSiO3) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceNH4', avgOceanSurfaceNH4) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceDOCr', avgOceanSurfaceDOCr) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceFeParticulate', avgOceanSurfaceFeParticulate) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'avgOceanSurfaceFeDissolved', avgOceanSurfaceFeDissolved) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'ecosysTracers', ecosysTracers, 1) -! real (kind=RKIND), dimension(:,:), pointer :: avgTracersSurfaceValue, avgSurfaceVelocity, avgSSHGradient + !$omp do schedule(runtime) + do iCell = 1, nCells -! avgTracersSurfaceValue => forcing % avgTracersSurfaceValue % array -! avgSurfaceVelocity => forcing % avgSurfaceVelocity % array -! avgSSHGradient => forcing % avgSSHGradient % array + avgCO2_gas_flux(iCell) = ( avgCO2_gas_flux(iCell) * nAccumulatedCoupled & + + CO2_gas_flux(iCell) ) / ( nAccumulatedCoupled + 1) -! if(forcing % nAccumulatedCoupled % scalar > 0) then -! avgTracersSurfaceValue = avgTracersSurfaceValue / forcing % nAccumulatedCoupled % scalar -! avgSurfaceVelocity = avgSurfaceVelocity / forcing % nAccumulatedCoupled % scalar -! avgSSHGradient = avgSSHGradient / forcing % nAccumulatedCoupled % scalar -! forcing % nAccumulatedCoupled % scalar = 0 -! end if + avgOceanSurfacePhytoC(1,iCell) = ( avgOceanSurfacePhytoC(1,iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%diatC_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfacePhytoC(2,iCell) = ( avgOceanSurfacePhytoC(2,iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%spC_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfacePhytoC(3,iCell) = ( avgOceanSurfacePhytoC(3,iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%phaeoC_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) - end subroutine ocn_time_average_coupled_normalize!}}} + avgOceanSurfaceDIC(iCell) = ( avgOceanSurfaceDIC(iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%dic_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfaceSiO3(iCell) = ( avgOceanSurfaceSiO3(iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%sio3_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfaceNO3(iCell) = ( avgOceanSurfaceNO3(iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%no3_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfaceNH4(iCell) = ( avgOceanSurfaceNH4(iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%nh4_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfaceFeDissolved(iCell) = ( avgOceanSurfaceFeDissolved(iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%fe_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) +!maltrud need to renormalize + avgOceanSurfaceDOCr(iCell) = ( avgOceanSurfaceDOCr(iCell) * nAccumulatedCoupled & + + ecosysTracers(ecosysIndices%donr_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + + avgOceanSurfaceFeParticulate(iCell) = 0.0_RKIND + + end do + !$omp end do + end if + + if (config_use_DMSTracers) then + call mpas_pool_get_subpool(forcingPool, 'DMSSeaIceCoupling', DMSSeaIceCoupling) + + call mpas_pool_get_array(DMSSeaIceCoupling, 'avgOceanSurfaceDMS', avgOceanSurfaceDMS) + call mpas_pool_get_array(DMSSeaIceCoupling, 'avgOceanSurfaceDMSP', avgOceanSurfaceDMSP) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'DMSTracers', DMSTracers, 1) + + !$omp do schedule(runtime) + do iCell = 1, nCells + avgOceanSurfaceDMS(iCell) = ( avgOceanSurfaceDMS(iCell) * nAccumulatedCoupled & + + DMSTracers(dmsIndices%dms_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfaceDMSP(iCell) = ( avgOceanSurfaceDMSP(iCell) * nAccumulatedCoupled & + + DMSTracers(dmsIndices%dmsp_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + end do + !$omp end do + endif + + if (config_use_MacroMoleculesTracers) then + call mpas_pool_get_subpool(forcingPool, 'MacroMoleculesSeaIceCoupling', MacroMoleculesSeaIceCoupling) + + call mpas_pool_get_array(MacroMoleculesSeaIceCoupling, 'avgOceanSurfaceDOC', avgOceanSurfaceDOC) + call mpas_pool_get_array(MacroMoleculesSeaIceCoupling, 'avgOceanSurfaceDON', avgOceanSurfaceDON) + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'MacroMoleculesTracers', MacroMoleculesTracers, 1) + + !$omp do schedule(runtime) + do iCell = 1, nCells + avgOceanSurfaceDOC(1,iCell) = ( avgOceanSurfaceDOC(1,iCell) * nAccumulatedCoupled & + + MacroMoleculesTracers(macrosIndices%poly_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + avgOceanSurfaceDOC(2,iCell) = ( avgOceanSurfaceDOC(2,iCell) * nAccumulatedCoupled & + + MacroMoleculesTracers(macrosIndices%lip_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) +!maltrud need to renormalize + avgOceanSurfaceDON(iCell) = ( avgOceanSurfaceDON(iCell) * nAccumulatedCoupled & + + MacroMoleculesTracers(macrosIndices%prot_ind,1,iCell) ) / ( nAccumulatedCoupled + 1) + end do + !$omp end do + endif + + call mpas_threading_barrier() + !$omp master + + nAccumulatedCoupled = nAccumulatedCoupled + 1 + + !$omp end master + call mpas_threading_barrier() + + end subroutine ocn_time_average_coupled_accumulate!}}} end module ocn_time_average_coupled diff --git a/src/core_ocean/shared/mpas_ocn_tracer_DMS.F b/src/core_ocean/shared/mpas_ocn_tracer_DMS.F new file mode 100644 index 0000000000..5d91827ae5 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_DMS.F @@ -0,0 +1,638 @@ +! copyright (c) 2013, los alamos national security, llc (lans) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_DMS +! +!> \brief MPAS ocean DMS +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This module contains routines for computing tracer forcing due to DMS +! +!----------------------------------------------------------------------- + +module ocn_tracer_DMS + + use mpas_timer + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_timekeeping + use ocn_constants + + use DMS_mod + use DMS_parms + use BGC_mod + use BGC_parms + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_DMS_compute, & + ocn_tracer_DMS_surface_flux_compute, & + ocn_tracer_DMS_init + + integer, public:: & + numColumnsMax + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + ! need dt for surface flux limiting--it is accessible in init routine + + real (kind=RKIND) :: dt + +!----------------------------------------------------------------------- +! name the necessary DMS derived types +! all of these are defined in DMS_mod +!----------------------------------------------------------------------- + + type(DMS_indices_type) , public :: DMS_indices + type(DMS_input_type) , public :: DMS_input + type(DMS_forcing_type) , public :: DMS_forcing + type(DMS_output_type) , public :: DMS_output + type(DMS_diagnostics_type), public :: DMS_diagnostic_fields + type(DMS_flux_diagnostics_type), public :: DMS_flux_diagnostic_fields + +! hold indices in tracer pool corresponding to each tracer array + type(DMS_indices_type), public :: dmsIndices + type(BGC_indices_type) :: ecosysIndices + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_DMS_compute +! +!> \brief computes a tracer tendency due to DMS +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine computes a tracer tendency due to DMS +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_DMS_compute(activeTracers, DMSTracers, nTracersDMS, ecosysTracers, nTracersEcosys, & + forcingPool, nCellsSolve, maxLevelCell, & + nVertLevels, layerThickness, indexTemperature, indexSalinity, DMSTracersTend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! one dimensional arrays + integer, dimension(:), intent(in) :: & + maxLevelCell + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThickness + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + DMSTracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + ecosysTracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + activeTracers + + + type (mpas_pool_type), intent(in) :: forcingPool + + ! scalars + integer, intent(in) :: nTracersDMS, nTracersEcosys, nCellsSolve, nVertLevels + integer, intent(in) :: indexTemperature, indexSalinity + + + ! + ! two dimensional pointers + ! + real (kind=RKIND), dimension(:), pointer :: & + shortWaveHeatFlux + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + DMSTracersTend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + ! source/sink wants cm instead of m + real (kind=RKIND) :: zTop, zBot, convertLengthScale = 100.0_RKIND + + integer :: iCell, iLevel, iTracer, numColumns, column, iLevelSurface + + call mpas_timer_start("DMS source-sink") + + err = 0 + + call mpas_pool_get_array(forcingPool, 'shortWaveHeatFlux', shortWaveHeatFlux) + + numColumns = 1 + column = 1 + iLevelSurface = 1 + + !DWJ 08/05/2016: This loop needs OpenMP added to it. + do iCell=1,nCellsSolve + DMS_input%number_of_active_levels(column) = maxLevelCell(iCell) + + DMS_forcing%ShortWaveFlux_surface(column) = shortWaveHeatFlux(iCell) + DMS_forcing%SST(column) = activeTracers(indexTemperature,iLevelSurface,iCell) + DMS_forcing%SSS(column) = activeTracers(indexSalinity,iLevelSurface,iCell) + + do iLevel=1,maxLevelCell(iCell) + DMS_input%cell_thickness(iLevel,column) = layerThickness(iLevel,iCell)*convertLengthScale + + DMS_input%DMS_tracers(iLevel,column,DMS_indices%dms_ind) = DMSTracers(dmsIndices%dms_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%dmsp_ind) = DMSTracers(dmsIndices%dmsp_ind,iLevel,iCell) + + DMS_input%DMS_tracers(iLevel,column,DMS_indices%no3_ind) = ecosysTracers(ecosysIndices%no3_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%doc_ind) = ecosysTracers(ecosysIndices%doc_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%zooC_ind) = ecosysTracers(ecosysIndices%zooC_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%spC_ind) = ecosysTracers(ecosysIndices%spC_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%spChl_ind) = ecosysTracers(ecosysIndices%spChl_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%spCaCO3_ind) = ecosysTracers(ecosysIndices%spCaCO3_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%diatC_ind) = ecosysTracers(ecosysIndices%diatC_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%diatChl_ind) = ecosysTracers(ecosysIndices%diatChl_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%phaeoC_ind) = ecosysTracers(ecosysIndices%phaeoC_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%phaeoChl_ind) = ecosysTracers(ecosysIndices%phaeoChl_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%diazC_ind) = ecosysTracers(ecosysIndices%diazC_ind,iLevel,iCell) + DMS_input%DMS_tracers(iLevel,column,DMS_indices%diazChl_ind) = ecosysTracers(ecosysIndices%diazChl_ind,iLevel,iCell) + + enddo ! iLevel + + call DMS_SourceSink(DMS_indices, DMS_input, DMS_forcing, & + DMS_output, DMS_diagnostic_fields, nVertLevels, & + numColumnsMax, numColumns) + + do iLevel=1,maxLevelCell(iCell) + + DMSTracersTend(dmsIndices%dms_ind,iLevel,iCell) = DMSTracersTend(dmsIndices%dms_ind,iLevel,iCell) & + + DMS_output%DMS_tendencies(iLevel,column,DMS_indices%dms_ind)*layerThickness(iLevel,iCell) + DMSTracersTend(dmsIndices%dmsp_ind,iLevel,iCell) = DMSTracersTend(dmsIndices%dmsp_ind,iLevel,iCell) & + + DMS_output%DMS_tendencies(iLevel,column,DMS_indices%dmsp_ind)*layerThickness(iLevel,iCell) + + enddo + + enddo ! iCell + + call mpas_timer_stop("DMS source-sink") + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_DMS_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_DMS_surface_flux_compute +! +!> \brief computes a tracer tendency due to DMS +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine computes a tracer tendency due to DMS +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_DMS_surface_flux_compute(activeTracers, DMSTracers, forcingPool, & + nTracers, nCellsSolve, zMid, indexTemperature, indexSalinity, DMSSurfaceFlux, DMSSurfaceFluxRemoved, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + zMid + real (kind=RKIND), dimension(:,:), intent(inout) :: & + DMSSurfaceFlux, & + DMSSurfaceFluxRemoved + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + DMSTracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + activeTracers + + ! scalars + integer, intent(in) :: nTracers, nCellsSolve, indexTemperature, indexSalinity + + type (mpas_pool_type), intent(inout) :: forcingPool + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: ecosysAuxiliary, & + DMSSeaIceCoupling, & + DMSFluxDiagnostics + + integer :: numColumns, column, iCell, iTracer, iLevelSurface + + real (kind=RKIND), dimension(:), pointer :: & + atmosphericPressure, & + iceFraction, & + landIceFraction, & + windSpeedSquared10m, & + iceFluxDMS, & + iceFluxDMSP, & + dms_flux_diag_ifrac, & + dms_flux_diag_xkw, & + dms_flux_diag_atm_press, & + dms_flux_diag_pv, & + dms_flux_diag_schmidt,& + dms_flux_diag_sat, & + dms_flux_diag_surf, & + dms_flux_diag_ws + + real (kind=RKIND) :: & + renormFluxes = 0.01_RKIND, & + PascalsToAtmospheres = 1.0_RKIND/101.325e+3_RKIND, & + mSquared_to_cmSquared = 1.0e+4_RKIND +! PascalsToAtmospheres = 1.0_RKIND, & +! mSquared_to_cmSquared = 1.0_RKIND +! PascalsToAtmospheres = 0.0_RKIND, & +! mSquared_to_cmSquared = 1.0_RKIND + + real (kind=RKIND) :: & + maxAllowedFractionalLoss = 0.5_RKIND + + real (kind=RKIND) :: & + testVal, fractionalLoss, limitedFlux, topLayerThickness + + err = 0 + + call mpas_timer_start("DMS surface flux") + + call mpas_pool_get_array(forcingPool, 'atmosphericPressure', atmosphericPressure) + call mpas_pool_get_array(forcingPool, 'iceFraction', iceFraction) + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + call mpas_pool_get_array(ecosysAuxiliary, 'windSpeedSquared10m', windSpeedSquared10m) + + call mpas_pool_get_subpool(forcingPool, 'DMSSeaIceCoupling', DMSSeaIceCoupling) + call mpas_pool_get_array(DMSSeaIceCoupling, 'iceFluxDMS', iceFluxDMS) + call mpas_pool_get_array(DMSSeaIceCoupling, 'iceFluxDMSP', iceFluxDMSP) + + call mpas_pool_get_subpool(forcingPool, 'DMSFluxDiagnostics', DMSFluxDiagnostics) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_xkw', dms_flux_diag_xkw) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_atm_press', dms_flux_diag_atm_press) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_pv', dms_flux_diag_pv) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_schmidt', dms_flux_diag_schmidt) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_sat', dms_flux_diag_sat) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_surf', dms_flux_diag_surf) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_ws', dms_flux_diag_ws) + call mpas_pool_get_array(DMSFluxDiagnostics, 'dms_flux_diag_ifrac', dms_flux_diag_ifrac) + + DMS_forcing%lcalc_DMS_gas_flux = .true. + + numColumns = 1 + column = 1 + iLevelSurface = 1 + + !DWJ 08/05/2016: This loop needs OpenMP added to it. + do iCell=1,nCellsSolve + + DMS_forcing%surfacePressure(column) = atmosphericPressure(iCell)*PascalsToAtmospheres + DMS_forcing%iceFraction(column) = iceFraction(iCell) +!maltrud assume for now that if there is any land ice, it is all land ice + if (associated(landIceFraction)) then + if (landIceFraction(iCell) > 0.0_RKIND) DMS_forcing%iceFraction(column) = 1.0_RKIND + endif + DMS_forcing%windSpeedSquared10m(column) = windSpeedSquared10m(iCell)*mSquared_to_cmSquared + DMS_forcing%SST(column) = activeTracers(indexTemperature,iLevelSurface,iCell) + DMS_forcing%SSS(column) = activeTracers(indexSalinity,iLevelSurface,iCell) + + DMS_input%DMS_tracers(1,column,DMS_indices%dms_ind) = DMSTracers(dmsIndices%dms_ind,1,iCell) + DMS_input%DMS_tracers(1,column,DMS_indices%dmsp_ind) = DMSTracers(dmsIndices%dmsp_ind,1,iCell) + + call DMS_SurfaceFluxes(DMS_indices, DMS_input, DMS_forcing, & + DMS_flux_diagnostic_fields, & + numColumnsMax, column) + + DMSSurfaceFlux(dmsIndices%dms_ind,iCell) = DMS_forcing%netFlux(column,DMS_indices%dms_ind)*renormFluxes + & + iceFluxDMS(iCell) + DMSSurfaceFlux(dmsIndices%dmsp_ind,iCell) = DMS_forcing%netFlux(column,DMS_indices%dmsp_ind)*renormFluxes + & + iceFluxDMSP(iCell) + + ! flux limitation for DMS (only if flux is out of ocean) + ! use abs(2*zMid) is top layer thickness; remember zMid is negative + if (DMSSurfaceFlux(dmsIndices%dms_ind,iCell) < 0.0_RKIND) then + topLayerThickness = abs(zMid(iLevelSurface,iCell))*2.0_RKIND + testVal = dt/(topLayerThickness*DMSTracers(dmsIndices%dms_ind,1,iCell) + 1.e-20_RKIND) + fractionalLoss = abs(testVal*DMSSurfaceFlux(dmsIndices%dms_ind,iCell)) + if (fractionalLoss > maxAllowedFractionalLoss) then + limitedFlux = -maxAllowedFractionalLoss/testVal + DMSSurfaceFluxRemoved(dmsIndices%dms_ind,iCell) = DMSSurfaceFluxRemoved(dmsIndices%dms_ind,iCell) & + + (DMSSurfaceFlux(dmsIndices%dms_ind,iCell) - limitedFlux) + DMSSurfaceFlux(dmsIndices%dms_ind,iCell) = limitedFlux + endif + endif + + dms_flux_diag_ifrac(iCell) = DMS_flux_diagnostic_fields%diag_DMS_IFRAC(column) + dms_flux_diag_xkw(iCell) = DMS_flux_diagnostic_fields%diag_DMS_XKW(column) + dms_flux_diag_atm_press(iCell) = DMS_flux_diagnostic_fields%diag_DMS_ATM_PRESS(column) + dms_flux_diag_pv(iCell) = DMS_flux_diagnostic_fields%diag_DMS_PV(column) + dms_flux_diag_schmidt(iCell) = DMS_flux_diagnostic_fields%diag_DMS_SCHMIDT(column) + dms_flux_diag_sat(iCell) = DMS_flux_diagnostic_fields%diag_DMS_SAT(column) + dms_flux_diag_surf(iCell) = DMS_flux_diagnostic_fields%diag_DMS_SURF(column) + dms_flux_diag_ws(iCell) = DMS_flux_diagnostic_fields%diag_DMS_WS(column) + + enddo ! iCell + + call mpas_timer_stop("DMS surface flux") + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_DMS_surface_flux_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_DMS_init +! +!> \brief Initializes ocean surface restoring +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine initializes fields required for tracer surface flux restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_DMS_init(domain,err)!{{{ + +!NOTE: called from mpas_ocn_forward_mode.F + + type (domain_type), intent(inout) :: domain !< Input/Output: domain information + + integer, intent(out) :: err !< Output: error flag + + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + + ! three dimensional pointers + real (kind=RKIND), dimension(:,:,:), pointer :: & + DMSTracers + + ! scalars + integer :: nTracers, numColumnsMax + + ! scalar pointers + integer, pointer :: nVertLevels, index_dummy + + type (MPAS_timeInterval_type) :: timeStep + + ! + ! get tracers pools + ! + + err = 0 + + ! + ! Get tracer group so we can get the number of tracers in it + ! + + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'DMSTracers', DMSTracers, 1) + + ! make sure DMS is turned on + + if (associated(DMSTracers)) then + + ! cannot use DMS_tracer_cnt since it has dms, dmsp, and 12 ecosys fields + + nTracers = size(DMSTracers, dim=1) + if (nTracers /= 2) then + err = 1 + return + endif + + ! + ! pull nVertLevels out of the mesh structure + ! + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevels', nVertLevels) + + ! + ! get the timestep value for use in surface DMS flux limiting + ! + + timeStep = mpas_get_clock_timestep(domain % clock, ierr=err) + call mpas_get_timeInterval(timeStep, dt=dt) + +!----------------------------------------------------------------------- +! initialize DMS parameters +!----------------------------------------------------------------------- + + allocate( DMS_indices%short_name(DMS_tracer_cnt) ) + allocate( DMS_indices%long_name(DMS_tracer_cnt) ) + allocate( DMS_indices%units(DMS_tracer_cnt) ) + +! no need to allocate the above fields for dmsIndices (?) + +!----------------------------------------------------------------------- +! sets most of DMS parameters +! sets namelist defaults +!----------------------------------------------------------------------- + + call DMS_parms_init + + ! modify namelist values here.... + + ! + ! for now only do 1 column at a time + ! + numColumnsMax = 1 + + DMS_indices%dms_ind = 1 + DMS_indices%dmsp_ind = 2 + DMS_indices%no3_ind = 3 + DMS_indices%doc_ind = 4 + DMS_indices%zooC_ind = 5 + DMS_indices%spC_ind = 6 + DMS_indices%spCaCO3_ind = 7 + DMS_indices%diatC_ind = 8 + DMS_indices%diazC_ind = 9 + DMS_indices%phaeoC_ind = 10 + DMS_indices%spChl_ind = 11 + DMS_indices%diatChl_ind = 12 + DMS_indices%diazChl_ind = 13 + DMS_indices%phaeoChl_ind = 14 + + call mpas_pool_get_dimension(tracersPool, 'index_DMS', index_dummy) + dmsIndices%dms_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DMSP', index_dummy) + dmsIndices%dmsp_ind = index_dummy + + call mpas_pool_get_dimension(tracersPool, 'index_NO3', index_dummy) + ecosysIndices%no3_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOC', index_dummy) + ecosysIndices%doc_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_zooC', index_dummy) + ecosysIndices%zooC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spChl', index_dummy) + ecosysIndices%spChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spC', index_dummy) + ecosysIndices%spC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spCaCO3', index_dummy) + ecosysIndices%spCaCO3_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatChl', index_dummy) + ecosysIndices%diatChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatC', index_dummy) + ecosysIndices%diatC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazChl', index_dummy) + ecosysIndices%diazChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazC', index_dummy) + ecosysIndices%diazC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoChl', index_dummy) + ecosysIndices%phaeoChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoC', index_dummy) + ecosysIndices%phaeoC_ind = index_dummy + +! DMS_init sets short and long names, units in DMS_indices + + call DMS_init(DMS_indices) + +!NOTES: + +!also check short_name with mpas variable name + +!----------------------------------------------------------------------- +! allocate input, forcing, diagnostic arrays +!----------------------------------------------------------------------- + + allocate ( DMS_input%DMS_tracers(nVertLevels, numColumnsMax, DMS_tracer_cnt) ) + allocate ( DMS_input%cell_thickness(nVertLevels, numColumnsMax) ) + allocate ( DMS_input%number_of_active_levels(numColumnsMax) ) + + allocate ( DMS_forcing%ShortWaveFlux_surface(numColumnsMax) ) + allocate ( DMS_forcing%surfacePressure(numColumnsMax) ) + allocate ( DMS_forcing%iceFraction(numColumnsMax) ) + allocate ( DMS_forcing%windSpeedSquared10m(numColumnsMax) ) + allocate ( DMS_forcing%SST(numColumnsMax) ) + allocate ( DMS_forcing%SSS(numColumnsMax) ) + + allocate ( DMS_forcing%netFlux(numColumnsMax, DMS_tracer_cnt) ) + + allocate ( DMS_output%DMS_tendencies(nVertLevels, numColumnsMax, DMS_tracer_cnt) ) + + !--------------------------------------------------------------------------- + ! allocate flux diagnostic output fields + !--------------------------------------------------------------------------- + + allocate (DMS_flux_diagnostic_fields%diag_DMS_IFRAC(numColumnsMax) ) + allocate (DMS_flux_diagnostic_fields%diag_DMS_XKW(numColumnsMax) ) + allocate (DMS_flux_diagnostic_fields%diag_DMS_ATM_PRESS(numColumnsMax) ) + allocate (DMS_flux_diagnostic_fields%diag_DMS_PV(numColumnsMax) ) + allocate (DMS_flux_diagnostic_fields%diag_DMS_SCHMIDT(numColumnsMax) ) + allocate (DMS_flux_diagnostic_fields%diag_DMS_SAT(numColumnsMax) ) + allocate (DMS_flux_diagnostic_fields%diag_DMS_SURF(numColumnsMax) ) + allocate (DMS_flux_diagnostic_fields%diag_DMS_WS(numColumnsMax) ) + + !--------------------------------------------------------------------------- + ! allocate diagnostic output fields + !--------------------------------------------------------------------------- + + allocate (DMS_diagnostic_fields%diag_DMS_S_DMSP(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMS_S_TOTAL(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMS_R_B(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMS_R_PHOT(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMS_R_BKGND(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMS_R_TOTAL(nVertLevels, numColumnsMax) ) + + allocate (DMS_diagnostic_fields%diag_DMSP_S_PHAEO(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMSP_S_NONPHAEO(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMSP_S_ZOO(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMSP_S_TOTAL(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMSP_R_B(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMSP_R_BKGND(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_DMSP_R_TOTAL(nVertLevels, numColumnsMax) ) + + allocate (DMS_diagnostic_fields%diag_Cyano_frac(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_Cocco_frac(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_Eukar_frac(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_diatS(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_diatN(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_phytoN(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_coccoS(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_cyanoS(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_eukarS(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_diazS(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_phaeoS(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_zooS(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_zooCC(nVertLevels, numColumnsMax) ) + allocate (DMS_diagnostic_fields%diag_RSNzoo(nVertLevels, numColumnsMax) ) + + end if ! associated(DMS_tracers) + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_DMS_init!}}} + +!*********************************************************************** + +end module ocn_tracer_DMS + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_MacroMolecules.F b/src/core_ocean/shared/mpas_ocn_tracer_MacroMolecules.F new file mode 100644 index 0000000000..8597c07258 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_MacroMolecules.F @@ -0,0 +1,448 @@ +! copyright (c) 2013, los alamos national security, llc (lans) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_MacroMolecules +! +!> \brief MPAS ocean MacroMolecules +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This module contains routines for computing tracer forcing due to MacroMolecules +! +!----------------------------------------------------------------------- + +module ocn_tracer_MacroMolecules + + use mpas_timer + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use ocn_constants + + use MACROS_mod + use MACROS_parms + use BGC_mod + use BGC_parms + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_MacroMolecules_compute, & + ocn_tracer_MacroMolecules_surface_flux_compute, & + ocn_tracer_MacroMolecules_init + + integer, public:: & + numColumnsMax + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!----------------------------------------------------------------------- +! name the necessary MacroMolecules derived types +! all of these are defined in MacroMolecules_mod +!----------------------------------------------------------------------- + + type(MACROS_indices_type) , public :: MacroMolecules_indices + type(MACROS_input_type) , public :: MacroMolecules_input + type(MACROS_output_type) , public :: MacroMolecules_output + type(MACROS_diagnostics_type), public :: MacroMolecules_diagnostic_fields + +! hold indices in tracer pool corresponding to each tracer array + type(MACROS_indices_type), public :: macrosIndices + type(BGC_indices_type) :: ecosysIndices + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_MacroMolecules_compute +! +!> \brief computes a tracer tendency due to MacroMolecules +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine computes a tracer tendency due to MacroMolecules +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_MacroMolecules_compute(MacroMoleculesTracers, nTracersMacroMolecules, & + ecosysTracers, nTracersEcosys, forcingPool, & + nCellsSolve, maxLevelCell, nVertLevels, layerThickness, MacroMoleculesTracersTend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! one dimensional arrays + integer, dimension(:), intent(in) :: & + maxLevelCell + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThickness + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + MacroMoleculesTracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + ecosysTracers + + type (mpas_pool_type), intent(in) :: forcingPool + + ! scalars + integer, intent(in) :: nTracersMacroMolecules, nTracersEcosys, nCellsSolve, nVertLevels + + ! + ! two dimensional pointers + ! + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + MacroMoleculesTracersTend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + ! source/sink wants cm instead of m + + real (kind=RKIND) :: zTop, zBot, convertLengthScale = 100.0_RKIND + + integer :: iCell, iLevel, iTracer, numColumns, column + + call mpas_timer_start("MacroMolecules source-sink") + + err = 0 + + numColumns = 1 + column = 1 + !DWJ 08/05/2016: This loop needs OpenMP added to it + do iCell=1,nCellsSolve + MacroMolecules_input%number_of_active_levels(column) = maxLevelCell(iCell) + do iLevel=1,maxLevelCell(iCell) + MacroMolecules_input%cell_thickness(iLevel,column) = layerThickness(iLevel,iCell)*convertLengthScale + + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%prot_ind) = & + MacroMoleculesTracers(macrosIndices%prot_ind,iLevel,iCell) + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%poly_ind) = & + MacroMoleculesTracers(macrosIndices%poly_ind,iLevel,iCell) + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%lip_ind) = & + MacroMoleculesTracers(macrosIndices%lip_ind,iLevel,iCell) + + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%zooC_ind) = & + ecosysTracers(ecosysIndices%zooC_ind,iLevel,iCell) + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%spC_ind) = & + ecosysTracers(ecosysIndices%spC_ind,iLevel,iCell) + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%diatC_ind) = & + ecosysTracers(ecosysIndices%diatC_ind,iLevel,iCell) + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%phaeoC_ind) = & + ecosysTracers(ecosysIndices%phaeoC_ind,iLevel,iCell) + MacroMolecules_input%MACROS_tracers(iLevel,column,MacroMolecules_indices%diazC_ind) = & + ecosysTracers(ecosysIndices%diazC_ind,iLevel,iCell) + + enddo ! iLevel + + call MACROS_SourceSink(MacroMolecules_indices, MacroMolecules_input, & + MacroMolecules_output, MacroMolecules_diagnostic_fields, nVertLevels, & + numColumnsMax, numColumns) + + do iLevel=1,maxLevelCell(iCell) + + MacroMoleculesTracersTend(macrosIndices%prot_ind,iLevel,iCell) = & + MacroMoleculesTracersTend(macrosIndices%prot_ind,iLevel,iCell) & + + MacroMolecules_output%MACROS_tendencies(iLevel,column,MacroMolecules_indices%prot_ind) + MacroMoleculesTracersTend(macrosIndices%poly_ind,iLevel,iCell) = & + MacroMoleculesTracersTend(macrosIndices%poly_ind,iLevel,iCell) & + + MacroMolecules_output%MACROS_tendencies(iLevel,column,MacroMolecules_indices%poly_ind) + MacroMoleculesTracersTend(macrosIndices%lip_ind,iLevel,iCell) = & + MacroMoleculesTracersTend(macrosIndices%lip_ind,iLevel,iCell) & + + MacroMolecules_output%MACROS_tendencies(iLevel,column,MacroMolecules_indices%lip_ind) + + enddo + + enddo ! iCell + + call mpas_timer_stop("MacroMolecules source-sink") + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_MacroMolecules_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_MacroMolecules_surface_flux_compute +! +!> \brief computes a tracer tendency due to MacroMolecules +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine computes a tracer tendency due to MacroMolecules +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_MacroMolecules_surface_flux_compute(activeTracers, MacroMoleculesTracers, forcingPool, & + nTracers, nCellsSolve, zMid, indexTemperature, indexSalinity, MacroMoleculesSurfaceFlux, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + zMid + real (kind=RKIND), dimension(:,:), intent(inout) :: & + MacroMoleculesSurfaceFlux + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + MacroMoleculesTracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + activeTracers + + ! scalars + integer, intent(in) :: nTracers, nCellsSolve, indexTemperature, indexSalinity + + type (mpas_pool_type), intent(inout) :: forcingPool + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell + + call mpas_timer_start("MacroMolecules surface flux") + + err = 0 + + ! fluxes are zero + + !DWJ 08/05/2016: This loop needs OpenMP added to it + do iCell = 1, nCellsSolve + + MacroMoleculesSurfaceFlux(macrosIndices%prot_ind,iCell) = 0.0_RKIND + MacroMoleculesSurfaceFlux(macrosIndices%poly_ind,iCell) = 0.0_RKIND + MacroMoleculesSurfaceFlux(macrosIndices%lip_ind, iCell) = 0.0_RKIND + + enddo ! iCell + + call mpas_timer_stop("MacroMolecules surface flux") + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_MacroMolecules_surface_flux_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_MacroMolecules_init +! +!> \brief Initializes ocean surface restoring +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine initializes fields required for tracer surface flux restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_MacroMolecules_init(domain,err)!{{{ + +!NOTE: called from mpas_ocn_forward_mode.F + + type (domain_type), intent(inout) :: domain !< Input/Output: domain information + + integer, intent(out) :: err !< Output: error flag + + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + + ! three dimensional pointers + real (kind=RKIND), dimension(:,:,:), pointer :: & + MacroMoleculesTracers + + ! scalars + integer :: nTracers, numColumnsMax + + ! scalar pointers + integer, pointer :: nVertLevels, index_dummy + + ! + ! get tracers pools + ! + + err = 0 + + ! + ! Get tracer group so we can get the number of tracers in it + ! + + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'MacroMoleculesTracers', MacroMoleculesTracers, 1) + + ! make sure MacrosMolecules is turned on + + if (associated(MacroMoleculesTracers)) then + + ! cannot use MacroMolecules_tracer_cnt since it has poly, prot, lip and 5 ecosys fields + + nTracers = size(MacroMoleculesTracers, dim=1) + if (nTracers /= 3) then + err = 1 + return + endif + + ! + ! pull nVertLevels out of the mesh structure + ! + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevels', nVertLevels) + +!----------------------------------------------------------------------- +! initialize MacroMolecules parameters +!----------------------------------------------------------------------- + + allocate( MacroMolecules_indices%short_name(MACROS_tracer_cnt) ) + allocate( MacroMolecules_indices%long_name(MACROS_tracer_cnt) ) + allocate( MacroMolecules_indices%units(MACROS_tracer_cnt) ) + +! no need to allocate the above fields for macrosIndices (?) + +!----------------------------------------------------------------------- +! sets most of MacroMolecules parameters +! sets namelist defaults +!----------------------------------------------------------------------- + + call MACROS_parms_init + +! modify namelist values here.... + + ! + ! for now only do 1 column at a time + ! + numColumnsMax = 1 + + MacroMolecules_indices%prot_ind = 1 + MacroMolecules_indices%poly_ind = 2 + MacroMolecules_indices%lip_ind = 3 + MacroMolecules_indices%zooC_ind = 4 + MacroMolecules_indices%spC_ind = 5 + MacroMolecules_indices%diatC_ind = 6 + MacroMolecules_indices%diazC_ind = 7 + MacroMolecules_indices%phaeoC_ind = 8 + + call mpas_pool_get_dimension(tracersPool, 'index_PROT', index_dummy) + macrosIndices%prot_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_POLY', index_dummy) + macrosIndices%poly_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_LIP', index_dummy) + macrosIndices%lip_ind = index_dummy + + call mpas_pool_get_dimension(tracersPool, 'index_zooC', index_dummy) + ecosysIndices%zooC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spC', index_dummy) + ecosysIndices%spC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatC', index_dummy) + ecosysIndices%diatC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazC', index_dummy) + ecosysIndices%diazC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoC', index_dummy) + ecosysIndices%phaeoC_ind = index_dummy + +! MacroMolecules_init sets short and long names, units in MacroMolecules_indices + + call MACROS_init(MacroMolecules_indices) + +!NOTES: + +!also check short_name with mpas variable name + +!----------------------------------------------------------------------- +! allocate input, forcing, diagnostic arrays +!----------------------------------------------------------------------- + + allocate ( MacroMolecules_input%MACROS_tracers(nVertLevels, numColumnsMax, MACROS_tracer_cnt) ) + allocate ( MacroMolecules_input%cell_thickness(nVertLevels, numColumnsMax) ) + allocate ( MacroMolecules_input%number_of_active_levels(numColumnsMax) ) + + allocate ( MacroMolecules_output%MACROS_tendencies(nVertLevels, numColumnsMax, MACROS_tracer_cnt) ) + + !--------------------------------------------------------------------------- + ! allocate diagnostic output fields + !--------------------------------------------------------------------------- + + allocate (MacroMolecules_diagnostic_fields%diag_PROT_S_TOTAL(nVertLevels, numColumnsMax) ) + allocate (MacroMolecules_diagnostic_fields%diag_POLY_S_TOTAL(nVertLevels, numColumnsMax) ) + allocate (MacroMolecules_diagnostic_fields%diag_LIP_S_TOTAL(nVertLevels, numColumnsMax) ) + allocate (MacroMolecules_diagnostic_fields%diag_PROT_R_TOTAL(nVertLevels, numColumnsMax) ) + allocate (MacroMolecules_diagnostic_fields%diag_POLY_R_TOTAL(nVertLevels, numColumnsMax) ) + allocate (MacroMolecules_diagnostic_fields%diag_LIP_R_TOTAL(nVertLevels, numColumnsMax) ) + + end if ! associated(MacroMoleculesTracers) + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_MacroMolecules_init!}}} + +!*********************************************************************** + +end module ocn_tracer_MacroMolecules + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_TTD.F b/src/core_ocean/shared/mpas_ocn_tracer_TTD.F new file mode 100644 index 0000000000..86365b4c1b --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_TTD.F @@ -0,0 +1,160 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_TTD +! +!> \brief MPAS ocean restoring +!> \author Todd Ringler +!> \date 06/08/2015 +!> \details +!> This module contains routines for computing the tracer tendency due to +!> to transit time distribution +! +!----------------------------------------------------------------------- + +module ocn_tracer_TTD + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_TTD_compute, & + ocn_tracer_TTD_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_TTD_compute +! +!> \brief computes a tracer tendency to approximate transit time distribution +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine computes a tracer tendency to approximate transit time distribution +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_TTD_compute(nTracers, nCellsSolve, maxLevelCell, layerThickness, & + TTDMask, tracers, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! one dimensional arrays + integer, dimension(:), intent(in) :: & + maxLevelCell + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThickness, & + TTDMask + + integer, intent(in) :: nTracers, nCellsSolve + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tracers + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, iLevel, iTracer + + !move to ocean constants + real (kind=RKIND), parameter :: c0 = 0.0_RKIND + real (kind=RKIND), parameter :: c1 = 1.0_RKIND + + err = 0 + + ! zero tracers at surface to TTDMask at top-most layer + ! TTDMask should be 1 within region of interest and zero elsewhere + !$omp do schedule(runtime) + do iCell = 1, nCellsSolve + tracers(:, 1, iCell) = TTDMask(:, iCell) + end do + !$omp end do + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_TTD_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_TTD_init +! +!> \brief Initializes ocean ideal age +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine initializes fields required for tracer ideal age +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_TTD_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + + err = 0 + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_TTD_init!}}} + +!*********************************************************************** + +end module ocn_tracer_TTD + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_advection.F b/src/core_ocean/shared/mpas_ocn_tracer_advection.F index 92c78709f2..bcc259242b 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_advection.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_advection.F @@ -10,11 +10,11 @@ ! ocn_tracer_advection ! !> \brief MPAS ocean tracer advection driver -!> \author Doug Jacobsen -!> \date 03/09/12 +!> \author Mark Petersen, David Lee, Doug Jacobsen +!> \date October 2017 !> \details !> This module contains driver routine for tracer advection tendencys -!> as well as the routines for setting up advection coefficients and +!> as well as the routines for setting up advection coefficients and !> initialization of the advection routines. ! !----------------------------------------------------------------------- @@ -26,12 +26,13 @@ module ocn_tracer_advection use mpas_pool_routines use mpas_sort use mpas_hash + use mpas_timer - use mpas_tracer_advection_std - use mpas_tracer_advection_mono + use ocn_tracer_advection_std + use ocn_tracer_advection_mono use ocn_constants - + implicit none private save @@ -49,32 +50,36 @@ module ocn_tracer_advection ! routine ocn_tracer_advection_tend ! !> \brief MPAS ocean tracer advection tendency -!> \author Doug Jacobsen -!> \date 03/09/12 +!> \author Mark Petersen, David Lee, Doug Jacobsen +!> \date October 2017 !> \details !> This routine is the driver routine for computing the tendency for !> advection of tracers. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_advection_tend(tracers, normalThicknessFlux, w, layerThickness, verticalCellSize, dt, meshPool, tend_layerThickness, tend)!{{{ + subroutine ocn_tracer_advection_tend(tracers, normalThicknessFlux, w, layerThickness, dt, meshPool, & !{{{ + scratchPool, diagnosticsPool, tend, tracerGroupName) real (kind=RKIND), dimension(:,:,:), intent(inout) :: tend !< Input/Output: tracer tendency real (kind=RKIND), dimension(:,:,:), intent(in) :: tracers !< Input/Output: tracer values real (kind=RKIND), dimension(:,:), intent(in) :: normalThicknessFlux !< Input: Thickness weighted horizontal velocity real (kind=RKIND), dimension(:,:), intent(in) :: w !< Input: Vertical velocity real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness !< Input: Thickness field - real (kind=RKIND), dimension(:,:), intent(in) :: verticalCellSize !< Input: Distance between vertical interfaces of a cell real (kind=RKIND), intent(in) :: dt !< Input: Time step type (mpas_pool_type), intent(in) :: meshPool !< Input: mesh information - real (kind=RKIND), dimension(:,:), intent(in) :: tend_layerThickness !< Input: Thickness tendency information + type (mpas_pool_type), intent(in) :: scratchPool !< Input: scratch fields + type (mpas_pool_type), intent(in) :: diagnosticsPool !< Input: pool for traceradvection budget term real (kind=RKIND), dimension(:,:), pointer :: advCoefs, advCoefs3rd integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop, nAdvCellsForEdge integer, dimension(:,:), pointer :: highOrderAdvectionMask, edgeSignOnCell, advCellsForEdge + character (len=*), intent(in) :: tracerGroupName ! variable to check for tracer budget if(.not. tracerAdvOn) return + call mpas_timer_start("tracer adv") + call mpas_pool_get_array(meshPool, 'advCoefs', advCoefs) call mpas_pool_get_array(meshPool, 'advCoefs3rd', advCoefs3rd) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) @@ -85,16 +90,18 @@ subroutine ocn_tracer_advection_tend(tracers, normalThicknessFlux, w, layerThick call mpas_pool_get_array(meshPool, 'advCellsForEdge', advCellsForEdge) if(monotonicOn) then - call mpas_tracer_advection_mono_tend(tracers, advCoefs, advCoefs3rd, & + call ocn_tracer_advection_mono_tend(tracers, advCoefs, advCoefs3rd, & nAdvCellsForEdge, advCellsForEdge, normalThicknessFlux, w, layerThickness, & - verticalCellSize, dt, meshPool, tend_layerThickness, tend, maxLevelCell, maxLevelEdgeTop, & - highOrderAdvectionMask, edgeSignOnCell_in = edgeSignOnCell) + dt, meshPool, scratchPool, diagnosticsPool, tend, maxLevelCell, maxLevelEdgeTop, & + highOrderAdvectionMask, edgeSignOnCell, tracerGroupName) else - call mpas_tracer_advection_std_tend(tracers, advCoefs, advCoefs3rd, & + call ocn_tracer_advection_std_tend(tracers, advCoefs, advCoefs3rd, & nAdvCellsForEdge, advCellsForEdge, normalThicknessFlux, w, layerThickness, & - verticalCellSize, dt, meshPool, tend_layerThickness, tend, maxLevelCell, maxLevelEdgeTop, & - highOrderAdvectionMask, edgeSignOnCell_in = edgeSignOnCell) + layerThickness, dt, meshPool, scratchPool, tend, maxLevelCell, maxLevelEdgeTop, & + highOrderAdvectionMask, edgeSignOnCell) endif + + call mpas_timer_stop("tracer adv") end subroutine ocn_tracer_advection_tend!}}} !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -102,10 +109,10 @@ end subroutine ocn_tracer_advection_tend!}}} ! routine ocn_tracer_advection_init ! !> \brief MPAS ocean tracer advection tendency -!> \author Doug Jacobsen -!> \date 03/09/12 +!> \author Mark Petersen, David Lee, Doug Jacobsen +!> \date October 2017 !> \details -!> This routine is the driver routine for initialization of +!> This routine is the driver routine for initialization of !> the tracer advection routines. ! !----------------------------------------------------------------------- @@ -134,8 +141,10 @@ subroutine ocn_tracer_advection_init(err)!{{{ if(config_disable_tr_adv) tracerAdvOn = .false. - call mpas_tracer_advection_std_init(config_horiz_tracer_adv_order, config_vert_tracer_adv_order, config_coef_3rd_order, config_dzdk_positive, config_check_tracer_monotonicity, err_tmp) - call mpas_tracer_advection_mono_init(config_num_halos, config_horiz_tracer_adv_order, config_vert_tracer_adv_order, config_coef_3rd_order, config_dzdk_positive, config_check_tracer_monotonicity, err_tmp) + call ocn_tracer_advection_std_init(config_horiz_tracer_adv_order, config_vert_tracer_adv_order, config_coef_3rd_order, & + config_dzdk_positive, config_check_tracer_monotonicity, err_tmp) + call ocn_tracer_advection_mono_init(config_num_halos, config_horiz_tracer_adv_order, config_vert_tracer_adv_order, & + config_coef_3rd_order, config_dzdk_positive, config_check_tracer_monotonicity, err_tmp) err = ior(err, err_tmp) diff --git a/src/core_ocean/shared/mpas_ocn_tracer_advection_mono.F b/src/core_ocean/shared/mpas_ocn_tracer_advection_mono.F new file mode 100644 index 0000000000..927a5ae348 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_advection_mono.F @@ -0,0 +1,752 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_advection_mono +! +!> \brief MPAS monotonic tracer advection with FCT +!> \author Mark Petersen, David Lee, Doug Jacobsen +!> \date October 2017 +!> \details +!> This module contains routines for monotonic advection of tracers using a FCT +! +!----------------------------------------------------------------------- +module ocn_tracer_advection_mono + +#ifdef _ADV_TIMERS + use mpas_timer +#endif + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_io_units + use mpas_threading + use mpas_tracer_advection_helpers + + use ocn_constants + + implicit none + private + save + + real (kind=RKIND) :: coef_3rd_order + integer :: horizOrder + logical :: vert2ndOrder, vert3rdOrder, vert4thOrder + logical :: positiveDzDk, monotonicityCheck + + public :: ocn_tracer_advection_mono_tend, & + ocn_tracer_advection_mono_init + + contains + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine ocn_tracer_advection_mono_tend +! +!> \brief MPAS monotonic tracer horizontal advection tendency with FCT +!> \author Mark Petersen, David Lee, Doug Jacobsen +!> \date October 2017 +!> \details +!> This routine computes the monotonic tracer horizontal advection tendencity using a FCT. +! +!----------------------------------------------------------------------- + subroutine ocn_tracer_advection_mono_tend(tracers, adv_coefs, adv_coefs_3rd, nAdvCellsForEdge, advCellsForEdge, &!{{{ + normalThicknessFlux, w, layerThickness, dt, meshPool, & + scratchPool, diagnosticsPool, tend, maxLevelCell, maxLevelEdgeTop, & + highOrderAdvectionMask, edgeSignOnCell, tracerGroupName) + + real (kind=RKIND), dimension(:,:,:), intent(in) :: tracers !< Input: current tracer values + real (kind=RKIND), dimension(:,:), intent(in) :: adv_coefs !< Input: Advection coefficients for 2nd order advection + real (kind=RKIND), dimension(:,:), intent(in) :: adv_coefs_3rd !< Input: Advection coeffs for mising 3rd/4th order advection + integer, dimension(:), intent(in) :: nAdvCellsForEdge !< Input: Number of advection cells for each edge + integer, dimension(:,:), intent(in) :: advCellsForEdge !< Input: List of advection cells for each edge + real (kind=RKIND), dimension(:,:), intent(in) :: normalThicknessFlux !< Input: Thichness weighted velocitiy + real (kind=RKIND), dimension(:,:), intent(in) :: w !< Input: Vertical velocity + real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness !< Input: Thickness + real (kind=RKIND), intent(in) :: dt !< Input: Timestep + type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information + type (mpas_pool_type), intent(in) :: scratchPool !< Input: Scratch fields + type (mpas_pool_type), intent(in) :: diagnosticsPool !< Input: pool for traceradvection budget term + real (kind=RKIND), dimension(:,:,:), intent(inout) :: tend !< Input/Output: Tracer tendency + + ! These variables are passed in to match the version of the interface in operators. + integer, dimension(:), pointer :: maxLevelCell !< Input: Index to max level at cell center + integer, dimension(:), pointer :: maxLevelEdgeTop !< Input: Index to max level at edge with non-land cells on both sides + integer, dimension(:,:), pointer :: highOrderAdvectionMask !< Input: Mask for high order advection + integer, dimension(:, :), pointer :: edgeSignOnCell !< Input: Sign for flux from edge on each cell. + + character (len=*), intent(in) :: tracerGroupName ! variable to check for tracer budget + + logical, pointer :: config_compute_active_tracer_budgets + + integer :: i, iCell, iEdge, k, iTracer, cell1, cell2, nVertLevels, num_tracers, nCells, nEdges + integer, pointer :: maxEdges + integer, dimension(:), pointer :: nCellsArray, nEdgesArray + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnEdge, cellsOnCell, edgesOnCell + + real (kind=RKIND) :: signedFactor, tracer_new + real (kind=RKIND) :: flux_upwind, tracer_min_new, tracer_max_new, tracer_upwind_new, scale_factor + real (kind=RKIND) :: flux, tracer_weight, invAreaCell1, invAreaCell2 + real (kind=RKIND) :: verticalWeightK, verticalWeightKm1 + real (kind=RKIND), dimension(:), pointer :: dvEdge, areaCell + real (kind=RKIND), dimension(:,:), pointer :: tracer_cur, work_tend, h_new_inv + real (kind=RKIND), dimension(:,:), pointer :: tracer_max, tracer_min + real (kind=RKIND), dimension(:,:), pointer :: flux_incoming, flux_outgoing + real (kind=RKIND), dimension(:,:), pointer :: high_order_flux + real (kind=RKIND), dimension(:,:), pointer :: h_prov, h_prov_inv + + type (field2DReal), pointer :: tracerCurField, workTendencyField, hNewInvField, tracerMinField, & + tracerMaxField, fluxIncomingField, fluxOutgoingField, & + hProvInvField, hProvField, highOrderFluxField + + real (kind=RKIND), dimension(:,:,:), pointer :: & + activeTracerHorizontalAdvectionTendency, & + activeTracerVerticalAdvectionTendency + + real (kind=RKIND), parameter :: eps = 1.e-10_RKIND + +#ifdef _ADV_TIMERS + call mpas_timer_start('startup') +#endif + ! Get dimensions + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) + nVertLevels = size(tracers,dim=2) + num_tracers = size(tracers,dim=1) + + ! Initialize pointers + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + + call mpas_pool_get_config(ocnConfigs, 'config_compute_active_tracer_budgets', config_compute_active_tracer_budgets) + if (config_compute_active_tracer_budgets) then + call mpas_pool_get_array(diagnosticsPool,'activeTracerHorizontalAdvectionTendency', & + activeTracerHorizontalAdvectionTendency) + call mpas_pool_get_array(diagnosticsPool,'activeTracerVerticalAdvectionTendency', & + activeTracerVerticalAdvectionTendency) + end if + + call mpas_pool_get_field(scratchPool, 'tracerCur', tracerCurField) + call mpas_pool_get_field(scratchPool, 'workTendency', workTendencyField) + call mpas_pool_get_field(scratchPool, 'hNewInv', hNewInvField) + call mpas_pool_get_field(scratchPool, 'tracerMin', tracerMinField) + call mpas_pool_get_field(scratchPool, 'tracerMax', tracerMaxField) + call mpas_pool_get_field(scratchPool, 'fluxIncoming', fluxIncomingField) + call mpas_pool_get_field(scratchPool, 'fluxOutgoing', fluxOutgoingField) + call mpas_pool_get_field(scratchPool, 'hProvInv', hProvInvField) + call mpas_pool_get_field(scratchPool, 'hProv', hProvField) + call mpas_pool_get_field(scratchPool, 'highOrderFlux', highOrderFluxField) + + call mpas_allocate_scratch_field(tracerCurField, .true., .false.) + call mpas_allocate_scratch_field(workTendencyField, .true., .false.) + call mpas_allocate_scratch_field(hNewInvField, .true., .false.) + call mpas_allocate_scratch_field(tracerMinField, .true., .false.) + call mpas_allocate_scratch_field(tracerMaxField, .true., .false.) + call mpas_allocate_scratch_field(fluxIncomingField, .true., .false.) + call mpas_allocate_scratch_field(fluxOutgoingField, .true., .false.) + call mpas_allocate_scratch_field(hProvInvField, .true., .false.) + call mpas_allocate_scratch_field(hProvField, .true., .false.) + call mpas_allocate_scratch_field(highOrderFluxField, .true., .false.) + call mpas_threading_barrier() + + ! allocate nCells arrays + h_prov_inv => hProvInvField % array + h_prov => hProvField % array + h_new_inv => hNewInvField % array + tracer_cur => tracerCurField % array + tracer_max => tracerMaxField % array + tracer_min => tracerMinField % array + work_tend => workTendencyField % array + flux_incoming => fluxIncomingField % array + flux_outgoing => fluxOutgoingField % array + high_order_flux => highOrderFluxField % array + + nCells = nCellsArray( size(nCellsArray) ) + + ! Note: This assumes we are in the first part of the horizontal/ + ! vertical operator splitting, which is true because currently + ! we dont flip order and horizontal is always first. + ! See notes in commit 2cd4a89d. + !$omp do schedule(runtime) private(k, i, iEdge, invAreaCell1) + do iCell = 1, nCells + invAreaCell1 = 1.0_RKIND / areaCell(iCell) + do k = 1, maxLevelCell(iCell) + h_prov(k, iCell) = layerThickness(k, iCell) + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + ! Provisional layer thickness is after horizontal thickness flux only + h_prov(k, iCell) = h_prov(k, iCell) + dt * invAreaCell1 * dvEdge(iEdge) * & + edgeSignOnCell(i, iCell) * normalThicknessFlux(k, iEdge) + end do + h_prov_inv(k, iCell) = 1.0_RKIND / h_prov(k, iCell) + ! New layer thickness is after horizontal and vertical thickness flux + h_new_inv(k, iCell) = 1.0_RKIND / (h_prov(k, iCell) - dt * w(k, iCell) + dt * w(k+1, iCell)) + end do + end do + !$omp end do + +#ifdef _ADV_TIMERS + call mpas_timer_stop('startup') +#endif + + ! Loop over tracers. One tracer is advected at a time. It is copied into a temporary array in order to improve locality + do iTracer = 1, num_tracers +#ifdef _ADV_TIMERS + call mpas_timer_start('cell init') +#endif + nCells = nCellsArray( size(nCellsArray) ) + ! Initialize variables for use in this iTracer iteration + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k=1, nVertLevels + tracer_cur(k,iCell) = tracers(iTracer,k,iCell) + work_tend(k, iCell) = 0.0_RKIND + flux_incoming(k, iCell) = 0.0_RKIND + flux_outgoing(k, iCell) = 0.0_RKIND + end do ! k loop + end do ! iCell loop + !$omp end do +#ifdef _ADV_TIMERS + call mpas_timer_stop('cell init') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('horiz flux') +#endif + nCells = nCellsArray( 2 ) + ! Compute the high and low order vertical fluxes. Also determine bounds on tracer_cur. + !$omp do schedule(runtime) private(k, i) + do iCell = 1, nCells + do k=1, maxLevelCell(iCell) + tracer_min(k,iCell) = tracer_cur(k,iCell) + tracer_max(k,iCell) = tracer_cur(k,iCell) + end do + ! pull tracer_min and tracer_max from the (horizontal) surrounding cells + do i = 1, nEdgesOnCell(iCell) + do k=1, min(maxLevelCell(iCell), maxLevelCell(cellsOnCell(i,iCell))) + tracer_max(k,iCell) = max(tracer_max(k,iCell),tracer_cur(k, cellsOnCell(i,iCell))) + tracer_min(k,iCell) = min(tracer_min(k,iCell),tracer_cur(k, cellsOnCell(i,iCell))) + end do ! k loop + end do ! i loop over nEdgesOnCell + end do + !$omp end do + + ! Need all the edges around the 1 halo cells and owned cells + nEdges = nEdgesArray( 3 ) + ! Compute the high order horizontal flux + !$omp do schedule(runtime) private(cell1, cell2, k, tracer_weight, i, iCell, flux_upwind) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + + do k = 1, nVertLevels + high_order_flux(k, iEdge) = 0.0_RKIND + end do + + ! Compute 3rd or 4th fluxes where requested. + do i = 1, nAdvCellsForEdge(iEdge) + iCell = advCellsForEdge(i,iEdge) + do k = 1, maxLevelCell(iCell) + tracer_weight = highOrderAdvectionMask(k, iEdge) * (adv_coefs(i,iEdge) & + + coef_3rd_order*sign(1.0_RKIND,normalThicknessFlux(k,iEdge))*adv_coefs_3rd(i,iEdge)) + + tracer_weight = normalThicknessFlux(k,iEdge)*tracer_weight + high_order_flux(k,iEdge) = high_order_flux(k,iEdge) + tracer_weight * tracer_cur(k,iCell) + end do ! k loop + end do ! i loop over nAdvCellsForEdge + + ! Compute 2nd order fluxes where needed. + ! Also compute low order upwind horizontal flux (monotonic and diffused) + ! Remove low order flux from the high order flux + ! Store left over high order flux in high_order_flux array + do k = 1, maxLevelEdgeTop(iEdge) + tracer_weight = iand(highOrderAdvectionMask(k, iEdge)+1, 1) * (dvEdge(iEdge) * 0.5_RKIND) & + * normalThicknessFlux(k, iEdge) + + high_order_flux(k, iEdge) = high_order_flux(k, iedge) + tracer_weight * (tracer_cur(k, cell1) & + + tracer_cur(k, cell2)) + + flux_upwind = dvEdge(iEdge) * (max(0.0_RKIND,normalThicknessFlux(k,iEdge))*tracer_cur(k,cell1) & + + min(0.0_RKIND,normalThicknessFlux(k,iEdge))*tracer_cur(k,cell2)) + high_order_flux(k,iEdge) = high_order_flux(k,iEdge) - flux_upwind + end do ! k loop + end do ! iEdge loop + !$omp end do +#ifdef _ADV_TIMERS + call mpas_timer_stop('horiz flux') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('scale factor build') +#endif + ! Need one halo of cells around owned cells + nCells = nCellsArray( 2 ) + !$omp do schedule(runtime) private(k, tracer_max_new, tracer_min_new, tracer_upwind_new, scale_factor, invAreaCell1, i, & + !$omp iEdge, cell1, cell2, flux_upwind, signedFactor) + do iCell = 1, nCells + invAreaCell1 = 1.0_RKIND / areaCell(iCell) + + ! Finish computing the low order horizontal fluxes + ! Upwind fluxes are accumulated in work_tend + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + signedFactor = edgeSignOnCell(i, iCell) * invAreaCell1 + + do k = 1, maxLevelEdgeTop(iEdge) + flux_upwind = dvEdge(iEdge) * (max(0.0_RKIND,normalThicknessFlux(k,iEdge))*tracer_cur(k,cell1) & + + min(0.0_RKIND,normalThicknessFlux(k,iEdge))*tracer_cur(k,cell2)) + + ! Here work_tend is the upwind tendency + work_tend(k,iCell) = work_tend(k,iCell) + signedFactor * flux_upwind + + ! Accumulate remaining high order fluxes + flux_outgoing(k,iCell) = flux_outgoing(k,iCell) + min(0.0_RKIND, signedFactor & + * high_order_flux(k, iEdge)) + flux_incoming(k,iCell) = flux_incoming(k,iCell) + max(0.0_RKIND, signedFactor & + * high_order_flux(k, iEdge)) + end do + end do + + ! Build the factors for the FCT + ! Computed using the bounds that were computed previously, and the bounds on the newly updated value + ! Factors are placed in the flux_incoming and flux_outgoing arrays + do k = 1, maxLevelCell(iCell) + ! Here work_tend is the upwind tendency + tracer_min_new = (tracer_cur(k,iCell)*layerThickness(k,iCell) + dt*(work_tend(k,iCell)+flux_outgoing(k,iCell))) & + * h_prov_inv(k,iCell) + tracer_max_new = (tracer_cur(k,iCell)*layerThickness(k,iCell) + dt*(work_tend(k,iCell)+flux_incoming(k,iCell))) & + * h_prov_inv(k,iCell) + tracer_upwind_new = (tracer_cur(k,iCell)*layerThickness(k,iCell) + dt*work_tend(k,iCell)) * h_prov_inv(k,iCell) + + scale_factor = (tracer_max(k,iCell)-tracer_upwind_new)/(tracer_max_new-tracer_upwind_new+eps) + flux_incoming(k,iCell) = min( 1.0_RKIND, max( 0.0_RKIND, scale_factor) ) + + scale_factor = (tracer_upwind_new-tracer_min(k,iCell))/(tracer_upwind_new-tracer_min_new+eps) + flux_outgoing(k,iCell) = min( 1.0_RKIND, max( 0.0_RKIND, scale_factor) ) + end do ! k loop + end do ! iCell loop + !$omp end do +#ifdef _ADV_TIMERS + call mpas_timer_stop('scale factor build') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('rescale horiz fluxes') +#endif + ! Need all of the edges around owned cells + nEdges = nEdgesArray( 2 ) + ! rescale the high order horizontal fluxes + !$omp do schedule(runtime) private(cell1, cell2, k, flux) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + flux = high_order_flux(k,iEdge) + flux = max(0.0_RKIND,flux) * min(flux_outgoing(k,cell1), flux_incoming(k,cell2)) & + + min(0.0_RKIND,flux) * min(flux_incoming(k,cell1), flux_outgoing(k,cell2)) + high_order_flux(k,iEdge) = flux + end do ! k loop + end do ! iEdge loop + !$omp end do +#ifdef _ADV_TIMERS + call mpas_timer_stop('rescale horiz fluxes') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('flux accumulate') +#endif + + nCells = nCellsArray( 1 ) + ! Accumulate the scaled high order vertical tendencies, and the upwind tendencies + !$omp do schedule(runtime) private(invAreaCell1, signedFactor, i, iEdge, flux, k) + do iCell = 1, nCells + invAreaCell1 = 1.0_RKIND / areaCell(iCell) + + ! Accumulate the scaled high order horizontal tendencies + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + signedFactor = invAreaCell1 * edgeSignOnCell(i, iCell) + do k = 1, maxLevelEdgeTop(iEdge) + ! work_tend on the RHS is the upwind tendency + ! work_tend on the LHS is the total horizontal advection tendency + work_tend(k, iCell) = work_tend(k, iCell) + signedFactor * high_order_flux(k, iEdge) + end do + end do + + do k = 1, maxLevelCell(iCell) + ! work_tend on the RHS is the total horizontal advection tendency + ! tracer_cur on LHS is the provisional tracer after horizontal fluxes only. + tracer_cur(k,iCell) = (tracer_cur(k, iCell)*layerThickness(k, iCell) + dt*work_tend(k, iCell)) & + * h_prov_inv(k, iCell) + tend(iTracer,k,iCell) = tend(iTracer,k,iCell) + work_tend(k,iCell) + end do + + end do ! iCell loop + !$omp end do + + if (config_compute_active_tracer_budgets) then + if (tracerGroupName == 'activeTracers') then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + activeTracerHorizontalAdvectionTendency(iTracer,k,iCell) = work_tend(k,iCell) + end do + end do ! iCell loop + !$omp end do + end if + end if + +#ifdef _ADV_TIMERS + call mpas_timer_stop('flux accumulate') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('monotonic check') +#endif + if (monotonicityCheck) then + nCells = nCellsArray( 1 ) + !build min and max bounds on old and new tracer for check on monotonicity. + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + if(tracer_cur(k,iCell) < tracer_min(k, iCell)-eps) then + call mpas_log_write( & + 'Horizontal minimum out of bounds on tracer: $i $r $r ', & + MPAS_LOG_WARN, intArgs=(/iTracer/), realArgs=(/ tracer_min(k, iCell), tracer_cur(k,iCell) /) ) + end if + + if(tracer_cur(k,iCell) > tracer_max(k,iCell)+eps) then + call mpas_log_write( & + 'Horizontal maximum out of bounds on tracer: $i $r $r ', & + MPAS_LOG_WARN, intArgs=(/iTracer/), realArgs=(/ tracer_max(k, iCell), tracer_cur(k,iCell) /) ) + end if + end do + end do + !$omp end do + end if +#ifdef _ADV_TIMERS + call mpas_timer_stop('monotonic check') +#endif + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! Vertical advection +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +#ifdef _ADV_TIMERS + call mpas_timer_start('cell init') +#endif + nCells = nCellsArray( size(nCellsArray) ) + ! Initialize variables for use in this iTracer iteration + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k=1, nVertLevels + high_order_flux(k, iCell) = 0.0_RKIND + work_tend(k, iCell) = 0.0_RKIND + end do ! k loop + high_order_flux(nVertLevels+1, iCell) = 0.0_RKIND + + end do ! iCell loop + !$omp end do +#ifdef _ADV_TIMERS + call mpas_timer_stop('cell init') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('vertical flux') +#endif + + ! Need all owned and 1 halo cells + nCells = nCellsArray( 2 ) + ! Compute the high and low order vertical fluxes. Also determine bounds on tracer_cur. + !$omp do schedule(runtime) private(k, verticalWeightK, verticalWeightKm1, i, flux_upwind) + do iCell = 1, nCells + + ! Operate on top cell in column + k = 1 + tracer_max(k,iCell) = max(tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + tracer_min(k,iCell) = min(tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + + ! Operate on next-to-top cell in column + k = max(1, min(maxLevelCell(iCell), 2)) + if ( k >= 2 ) then + verticalWeightK = h_prov(k-1, iCell) / (h_prov(k, iCell) + h_prov(k-1, iCell)) + verticalWeightKm1 = h_prov(k, iCell) / (h_prov(k, iCell) + h_prov(k-1, iCell)) + high_order_flux(k,iCell) = w(k,iCell)*(verticalWeightK*tracer_cur(k,iCell)+verticalWeightKm1 & + *tracer_cur(k-1,iCell)) + tracer_max(k,iCell) = max(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + tracer_min(k,iCell) = min(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + end if + + ! Operate on internal cells in column + if ( vert4thOrder ) then + do k=3,maxLevelCell(iCell)-1 + high_order_flux(k, iCell) = mpas_tracer_advection_vflux4( tracer_cur(k-2,iCell),tracer_cur(k-1,iCell), & + tracer_cur(k ,iCell),tracer_cur(k+1,iCell), w(k,iCell)) + + tracer_max(k,iCell) = max(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + tracer_min(k,iCell) = min(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + end do + else if ( vert3rdOrder ) then + do k=3,maxLevelCell(iCell)-1 + high_order_flux(k, iCell) = mpas_tracer_advection_vflux3( tracer_cur(k-2,iCell),tracer_cur(k-1,iCell), & + tracer_cur(k ,iCell),tracer_cur(k+1,iCell), w(k,iCell), coef_3rd_order ) + + tracer_max(k,iCell) = max(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + tracer_min(k,iCell) = min(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + end do + else if ( vert2ndOrder ) then + do k=3,maxLevelCell(iCell)-1 + verticalWeightK = h_prov(k-1, iCell) / (h_prov(k, iCell) + h_prov(k-1, iCell)) + verticalWeightKm1 = h_prov(k, iCell) / (h_prov(k, iCell) + h_prov(k-1, iCell)) + high_order_flux(k,iCell) = w(k,iCell) * (verticalWeightK * tracer_cur(k,iCell) + verticalWeightKm1 & + * tracer_cur(k-1,iCell)) + + tracer_max(k,iCell) = max(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + tracer_min(k,iCell) = min(tracer_cur(k-1,iCell),tracer_cur(k,iCell),tracer_cur(k+1,iCell)) + end do + end if + + ! Operate on deepest vertical cell in column. + k = max(1, maxLevelCell(iCell)) + verticalWeightK = h_prov(k-1, iCell) / (h_prov(k, iCell) + h_prov(k-1, iCell)) + verticalWeightKm1 = h_prov(k, iCell) / (h_prov(k, iCell) + h_prov(k-1, iCell)) + high_order_flux(k,iCell) = w(k,iCell)*(verticalWeightK*tracer_cur(k,iCell)+verticalWeightKm1*tracer_cur(k-1,iCell)) + tracer_max(k,iCell) = max(tracer_cur(k,iCell),tracer_cur(k-1,iCell)) + tracer_min(k,iCell) = min(tracer_cur(k,iCell),tracer_cur(k-1,iCell)) + + ! low order upwind vertical flux (monotonic and diffused) + ! Remove low order flux from the high order flux. + ! Store left over high order flux in high_order_flux array. + ! Upwind fluxes are accumulated in work_tend + do k = 2, maxLevelCell(iCell) + flux_upwind = min(0.0_RKIND,w(k,iCell))*tracer_cur(k-1,iCell) + max(0.0_RKIND,w(k,iCell))*tracer_cur(k,iCell) + work_tend(k-1,iCell) = work_tend(k-1,iCell) + flux_upwind + work_tend(k ,iCell) = work_tend(k ,iCell) - flux_upwind + high_order_flux(k,iCell) = high_order_flux(k,iCell) - flux_upwind + end do ! k loop + + ! flux_incoming contains the total remaining high order flux into iCell + ! it is positive. + ! flux_outgoing contains the total remaining high order flux out of iCell + ! it is negative + do k = 1, maxLevelCell(iCell) + flux_incoming(k, iCell) = max(0.0_RKIND, high_order_flux(k+1, iCell)) & + - min(0.0_RKIND, high_order_flux(k, iCell)) + flux_outgoing(k, iCell) = min(0.0_RKIND, high_order_flux(k+1, iCell)) & + - max(0.0_RKIND, high_order_flux(k, iCell)) + end do ! k Loop + end do ! iCell Loop + !$omp end do +#ifdef _ADV_TIMERS + call mpas_timer_stop('vertical flux') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('scale factor build') +#endif + ! Need one halo of cells around owned cells + nCells = nCellsArray( 2 ) + !$omp do schedule(runtime) private(k, tracer_max_new, tracer_min_new, tracer_upwind_new, scale_factor, i) + do iCell = 1, nCells + + ! Build the factors for the FCT + ! Computed using the bounds that were computed previously, and the bounds on the newly updated value + ! Factors are placed in the flux_incoming and flux_outgoing arrays + do k = 1, maxLevelCell(iCell) + ! work_tend on the RHS is the upwind tendency + tracer_min_new = (tracer_cur(k,iCell)*h_prov(k,iCell) + dt*(work_tend(k,iCell)+flux_outgoing(k,iCell))) & + * h_new_inv(k,iCell) + tracer_max_new = (tracer_cur(k,iCell)*h_prov(k,iCell) + dt*(work_tend(k,iCell)+flux_incoming(k,iCell))) & + * h_new_inv(k,iCell) + tracer_upwind_new = (tracer_cur(k,iCell)*h_prov(k,iCell) + dt*work_tend(k,iCell)) * h_new_inv(k,iCell) + + scale_factor = (tracer_max(k,iCell)-tracer_upwind_new)/(tracer_max_new-tracer_upwind_new+eps) + flux_incoming(k,iCell) = min( 1.0_RKIND, max( 0.0_RKIND, scale_factor) ) + + scale_factor = (tracer_upwind_new-tracer_min(k,iCell))/(tracer_upwind_new-tracer_min_new+eps) + flux_outgoing(k,iCell) = min( 1.0_RKIND, max( 0.0_RKIND, scale_factor) ) + end do ! k loop + end do ! iCell loop + !$omp end do +#ifdef _ADV_TIMERS + call mpas_timer_stop('scale factor build') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('flux accumulate') +#endif + + nCells = nCellsArray( 1 ) + ! Accumulate the scaled high order vertical tendencies, and the upwind tendencies + !$omp do schedule(runtime) private(flux, k) + do iCell = 1, nCells + ! rescale the high order vertical flux + do k = 2, maxLevelCell(iCell) + flux = high_order_flux(k,iCell) + flux = max(0.0_RKIND,flux) * min(flux_outgoing(k ,iCell), flux_incoming(k-1,iCell)) & + + min(0.0_RKIND,flux) * min(flux_outgoing(k-1,iCell), flux_incoming(k ,iCell)) + high_order_flux(k,iCell) = flux + end do ! k loop + + do k = 1,maxLevelCell(iCell) + ! work_tend on the RHS is the upwind tendency + ! work_tend on the LHS is the total vertical advection tendency + work_tend(k, iCell) = work_tend(k, iCell) + (high_order_flux(k+1, iCell) & + - high_order_flux(k, iCell)) + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + work_tend(k, iCell) + end do ! k loop + + end do ! iCell loop + !$omp end do + + if (config_compute_active_tracer_budgets) then + if (tracerGroupName == 'activeTracers') then + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + activeTracerVerticalAdvectionTendency(iTracer,k,iCell) = work_tend(k,iCell) + end do + end do ! iCell loop + !$omp end do + end if + end if +#ifdef _ADV_TIMERS + call mpas_timer_stop('flux accumulate') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('monotonic check') +#endif + if (monotonicityCheck) then + nCells = nCellsArray( 1 ) + ! Check for monotonicity of new tracer value + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 1, maxLevelCell(iCell) + ! work_tend on the RHS is the total vertical advection tendency + tracer_new = (tracer_cur(k, iCell)*h_prov(k, iCell) + dt * work_tend(k, iCell)) * h_new_inv(k, iCell) + + if(tracer_new < tracer_min(k, iCell)-eps) then + call mpas_log_write( & + 'Vertical minimum out of bounds on tracer: $i $i $i $r $r ', & + MPAS_LOG_WARN, intArgs=(/iTracer, k, iCell/), realArgs=(/ tracer_min(k, iCell), tracer_new /) ) + end if + + if(tracer_new > tracer_max(k,iCell)+eps) then + call mpas_log_write( & + 'Vertical maximum out of bounds on tracer: $i $i $i $r $r ', & + MPAS_LOG_WARN, intArgs=(/iTracer, k, iCell/), realArgs=(/ tracer_max(k, iCell), tracer_new /) ) + end if + end do + end do + !$omp end do + end if +#ifdef _ADV_TIMERS + call mpas_timer_stop('monotonic check') +#endif + end do ! iTracer loop + +#ifdef _ADV_TIMERS + call mpas_timer_start('half step') +#endif +#ifdef _ADV_TIMERS + call mpas_timer_stop('half step') +#endif + +#ifdef _ADV_TIMERS + call mpas_timer_start('deallocates') +#endif + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(tracerCurField, .true.) + call mpas_deallocate_scratch_field(workTendencyField, .true.) + call mpas_deallocate_scratch_field(hNewInvField, .true.) + call mpas_deallocate_scratch_field(fluxIncomingField, .true.) + call mpas_deallocate_scratch_field(fluxOutgoingField, .true.) + call mpas_deallocate_scratch_field(hProvInvField, .true.) + call mpas_deallocate_scratch_field(hProvField, .true.) + call mpas_deallocate_scratch_field(highOrderFluxField, .true.) + +#ifdef _ADV_TIMERS + call mpas_timer_stop('deallocates') +#endif + + end subroutine ocn_tracer_advection_mono_tend!}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine ocn_tracer_advection_mono_init +! +!> \brief MPAS initialize monotonic tracer advection tendency with FCT +!> \author Mark Petersen, David Lee, Doug Jacobsen +!> \date October 2017 +!> \details +!> This routine initializes the monotonic tracer advection tendencity using a FCT. +! +!----------------------------------------------------------------------- + subroutine ocn_tracer_advection_mono_init(nHalos, horiz_adv_order, vert_adv_order, coef_3rd_order_in, dzdk_positive, & !{{{ + check_monotonicity, err) + + use mpas_dmpar + integer, intent(in) :: nHalos !< Input: number of halos in current simulation + integer, intent(in) :: horiz_adv_order !< Input: Order for horizontal advection + integer, intent(in) :: vert_adv_order !< Input: Order for vertical advection + real (kind=RKIND), intent(in) :: coef_3rd_order_in !< Input: coefficient for blending advection orders. + logical, intent(in) :: dzdk_positive !< Input: Logical flag determining if dzdk is positive or negative. + logical, intent(in) :: check_monotonicity !< Input: Logical flag determining check on monotonicity of tracers + integer, intent(inout) :: err !< Input/Output: Error Flag + + err = 0 + + vert2ndOrder = .false. + vert3rdOrder = .false. + vert4thOrder = .false. + + if ( horiz_adv_order == 3) then + coef_3rd_order = coef_3rd_order_in + else if(horiz_adv_order == 2 .or. horiz_adv_order == 4) then + coef_3rd_order = 0.0_RKIND + end if + + horizOrder = horiz_adv_order + + if (vert_adv_order == 3) then + vert3rdOrder = .true. + else if (vert_adv_order == 4) then + vert4thOrder = .true. + else + vert2ndOrder = .true. + if(vert_adv_order /= 2) then + call mpas_log_write( & + 'Invalid value for vert_adv_order, defaulting to 2nd order', & + MPAS_LOG_WARN) + end if + end if + + if (nHalos < 3) then + call mpas_log_write( & + 'Monotonic advection cannot be used with less than 3 halos.', & + MPAS_LOG_CRIT) + end if + + positiveDzDk = dzdk_positive + monotonicityCheck = check_monotonicity + + end subroutine ocn_tracer_advection_mono_init!}}} + +end module ocn_tracer_advection_mono + diff --git a/src/core_ocean/shared/mpas_ocn_tracer_advection_std.F b/src/core_ocean/shared/mpas_ocn_tracer_advection_std.F new file mode 100644 index 0000000000..dee1ab1039 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_advection_std.F @@ -0,0 +1,288 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_advection_std +! +!> \brief MPAS standard tracer advection +!> \author Doug Jacobsen +!> \date 03/09/12 +!> \details +!> This module contains routines for standard advection of tracers +! +!----------------------------------------------------------------------- +module ocn_tracer_advection_std + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_io_units + use mpas_threading + + use mpas_tracer_advection_helpers + + implicit none + private + save + + real (kind=RKIND) :: coef_3rd_order + integer :: horizOrder + logical :: vert2ndOrder, vert3rdOrder, vert4thOrder + logical :: positiveDzDk, monotonicityCheck + + public :: ocn_tracer_advection_std_tend, & + ocn_tracer_advection_std_init + + contains + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine ocn_tracer_advection_std_tend +! +!> \brief MPAS standard tracer advection tendency +!> \author Doug Jacobsen +!> \date 03/09/12 +!> \details +!> This routine computes the standard tracer advection tendencity. +!> Both horizontal and vertical. +! +!----------------------------------------------------------------------- + subroutine ocn_tracer_advection_std_tend(tracers, adv_coefs, adv_coefs_3rd, nAdvCellsForEdge, advCellsForEdge, &!{{{ + normalThicknessFlux, w, layerThickness, verticalCellSize, dt, meshPool, & + scratchPool, tend, maxLevelCell, maxLevelEdgeTop, & + highOrderAdvectionMask, edgeSignOnCell) + + real (kind=RKIND), dimension(:,:,:), intent(in) :: tracers !< Input: current tracer values + real (kind=RKIND), dimension(:,:), intent(in) :: adv_coefs !< Input: Advection coefficients for 2nd order advection + real (kind=RKIND), dimension(:,:), intent(in) :: adv_coefs_3rd !< Input: Advection coeffs for blending in 3rd/4th order + integer, dimension(:), intent(in) :: nAdvCellsForEdge !< Input: Number of advection cells for each edge + integer, dimension(:,:), intent(in) :: advCellsForEdge !< Input: List of advection cells for each edge + real (kind=RKIND), dimension(:,:), intent(in) :: normalThicknessFlux !< Input: Thichness weighted velocitiy + real (kind=RKIND), dimension(:,:), intent(in) :: w !< Input: Vertical velocity + real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness !< Input: Thickness + real (kind=RKIND), dimension(:,:), intent(in) :: verticalCellSize !< Input: Distance between vertical interfaces of a cell + real (kind=RKIND), intent(in) :: dt !< Input: Timestep + type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information + type (mpas_pool_type), intent(in) :: scratchPool !< Input: Scratch fields + real (kind=RKIND), dimension(:,:,:), intent(inout) :: tend !< Input/Output: Tracer tendency + integer, dimension(:), pointer :: maxLevelCell !< Input: Index to max level at cell center + integer, dimension(:), pointer :: maxLevelEdgeTop !< Input: Index to max level at edge with non-land cells on both sides + integer, dimension(:,:), pointer :: highOrderAdvectionMask !< Input: Mask for high order advection + integer, dimension(:, :), pointer :: edgeSignOnCell !< Input: Sign for flux from edge on each cell. + + integer :: i, iCell, iEdge, k, iTracer, cell1, cell2 + integer :: nVertLevels, num_tracers + integer, pointer :: nCells, nEdges, nCellsSolve, maxEdges + integer, dimension(:), pointer :: nEdgesOnCell + integer, dimension(:,:), pointer :: cellsOnEdge, cellsOnCell, edgesOnCell + + real (kind=RKIND) :: tracer_weight, invAreaCell1 + real (kind=RKIND) :: verticalWeightK, verticalWeightKm1 + real (kind=RKIND), dimension(:), pointer :: dvEdge, areaCell, verticalDivergenceFactor + real (kind=RKIND), dimension(:,:), pointer :: tracer_cur, high_order_horiz_flux, high_order_vert_flux + + type (field2DReal), pointer :: highOrderHorizFluxField, tracerCurField, highOrderVertFluxField + + real (kind=RKIND), parameter :: eps = 1.e-10_RKIND + + ! Get dimensions + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'maxEdges', maxEdges) + nVertLevels = size(tracers,dim=2) + num_tracers = size(tracers,dim=1) + + ! Initialize pointers + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + + allocate(verticalDivergenceFactor(nVertLevels)) + verticalDivergenceFactor = 1.0_RKIND + + call mpas_pool_get_field(scratchPool, 'highOrderHorizFlux', highOrderHorizFluxField) + call mpas_pool_get_field(scratchPool, 'tracerValue', tracerCurField, 1) + call mpas_pool_get_field(scratchPool, 'highOrderVertFlux', highOrderVertFluxField) + + call mpas_allocate_scratch_field(highOrderHorizFluxField, .true.) + call mpas_allocate_scratch_field(tracerCurField, .true.) + call mpas_allocate_scratch_field(highOrderVertFluxField, .true.) + call mpas_threading_barrier() + + high_order_horiz_flux => highOrderHorizFluxField % array + tracer_cur => tracerCurField % array + high_order_vert_flux => highOrderVertFluxField % array + + ! Loop over tracers. One tracer is advected at a time. It is copied into a temporary array in order to improve locality + do iTracer = 1, num_tracers + ! Initialize variables for use in this iTracer iteration + !$omp do schedule(runtime) + do iCell = 1, nCells + tracer_cur(:, iCell) = tracers(iTracer, :, iCell) + + high_order_vert_flux(:, iCell) = 0.0_RKIND + end do + !$omp end do + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + high_order_horiz_flux(:, iEdge) = 0.0_RKIND + end do + !$omp end do + + ! Compute the high order vertical flux. Also determine bounds on tracer_cur. + !$omp do schedule(runtime) private(k, verticalWeightK, verticalWeightKm1) + do iCell = 1, nCells + k = max(1, min(maxLevelCell(iCell), 2)) + verticalWeightK = verticalCellSize(k-1, iCell) / (verticalCellSize(k, iCell) + verticalCellSize(k-1, iCell)) + verticalWeightKm1 = verticalCellSize(k, iCell) / (verticalCellSize(k, iCell) + verticalCellSize(k-1, iCell)) + high_order_vert_flux(k,iCell) = w(k,iCell)*(verticalWeightK*tracer_cur(k,iCell)+verticalWeightKm1*tracer_cur(k-1,iCell)) + + do k=3,maxLevelCell(iCell)-1 + if(vert4thOrder) then + high_order_vert_flux(k, iCell) = mpas_tracer_advection_vflux4( tracer_cur(k-2,iCell),tracer_cur(k-1,iCell), & + tracer_cur(k ,iCell),tracer_cur(k+1,iCell), w(k,iCell)) + else if(vert3rdOrder) then + high_order_vert_flux(k, iCell) = mpas_tracer_advection_vflux3( tracer_cur(k-2,iCell),tracer_cur(k-1,iCell), & + tracer_cur(k ,iCell),tracer_cur(k+1,iCell), w(k,iCell), coef_3rd_order ) + else if (vert2ndOrder) then + verticalWeightK = verticalCellSize(k-1, iCell) / (verticalCellSize(k, iCell) + verticalCellSize(k-1, iCell)) + verticalWeightKm1 = verticalCellSize(k, iCell) / (verticalCellSize(k, iCell) + verticalCellSize(k-1, iCell)) + high_order_vert_flux(k,iCell) = w(k, iCell) * (verticalWeightK * tracer_cur(k, iCell) & + + verticalWeightKm1 * tracer_cur(k-1, iCell)) + end if + end do + + k = max(1, maxLevelCell(iCell)) + verticalWeightK = verticalCellSize(k-1, iCell) / (verticalCellSize(k, iCell) + verticalCellSize(k-1, iCell)) + verticalWeightKm1 = verticalCellSize(k, iCell) / (verticalCellSize(k, iCell) + verticalCellSize(k-1, iCell)) + high_order_vert_flux(k,iCell) = w(k,iCell)*(verticalWeightK*tracer_cur(k,iCell)+verticalWeightKm1*tracer_cur(k-1,iCell)) + end do ! iCell Loop + !$omp end do + + ! Compute the high order horizontal flux + !$omp do schedule(runtime) private(cell1, cell2, k, tracer_weight, i, iCell) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1, iEdge) + cell2 = cellsOnEdge(2, iEdge) + + ! Compute 2nd order fluxes where needed. + do k = 1, maxLevelEdgeTop(iEdge) + tracer_weight = iand(highOrderAdvectionMask(k, iEdge)+1, 1) * (dvEdge(iEdge) * 0.5_RKIND) & + * normalThicknessFlux(k, iEdge) + + high_order_horiz_flux(k, iEdge) = high_order_horiz_flux(k, iedge) + tracer_weight & + * (tracer_cur(k, cell1) + tracer_cur(k, cell2)) + end do ! k loop + + ! Compute 3rd or 4th fluxes where requested. + do i = 1, nAdvCellsForEdge(iEdge) + iCell = advCellsForEdge(i,iEdge) + do k = 1, maxLevelCell(iCell) + tracer_weight = highOrderAdvectionMask(k, iEdge) * (adv_coefs(i,iEdge) + coef_3rd_order & + * sign(1.0_RKIND,normalThicknessFlux(k,iEdge))*adv_coefs_3rd(i,iEdge)) + + tracer_weight = normalThicknessFlux(k,iEdge)*tracer_weight + high_order_horiz_flux(k,iEdge) = high_order_horiz_flux(k,iEdge) + tracer_weight * tracer_cur(k,iCell) + end do ! k loop + end do ! i loop over nAdvCellsForEdge + end do ! iEdge loop + !$omp end do + + ! Accumulate the scaled high order horizontal tendencies + !$omp do schedule(runtime) private(invAreaCell1, i, iEdge, k) + do iCell = 1, nCells + invAreaCell1 = 1.0_RKIND / areaCell(iCell) + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + do k = 1, maxLevelEdgeTop(iEdge) + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + edgeSignOnCell(i, iCell) * high_order_horiz_flux(k, iEdge) & + * invAreaCell1 + end do + end do + end do + !$omp end do + + ! Accumulate the scaled high order vertical tendencies. + !$omp do schedule(runtime) private(k) + do iCell = 1, nCellsSolve + do k = 1,maxLevelCell(iCell) + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + verticalDivergenceFactor(k) * (high_order_vert_flux(k+1, iCell) & + - high_order_vert_flux(k, iCell)) + end do ! k loop + end do ! iCell loop + !$omp end do + end do ! iTracer loop + + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(highOrderHorizFluxField, .true.) + call mpas_deallocate_scratch_field(tracerCurField, .true.) + call mpas_deallocate_scratch_field(highOrderVertFluxField, .true.) + + deallocate(verticalDivergenceFactor) + + end subroutine ocn_tracer_advection_std_tend!}}} + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! routine ocn_tracer_advection_std_init +! +!> \brief MPAS initialize standard tracer advection tendency. +!> \author Doug Jacobsen +!> \date 03/09/12 +!> \details +!> This routine initializes the standard tracer advection tendencity. +! +!----------------------------------------------------------------------- + subroutine ocn_tracer_advection_std_init(horiz_adv_order, vert_adv_order, coef_3rd_order_in, dzdk_positive, & !{{{ + check_monotonicity, err) + integer, intent(in) :: horiz_adv_order !< Input: Order for horizontal advection + integer, intent(in) :: vert_adv_order !< Input: Order for vertical advection + real (kind=RKIND), intent(in) :: coef_3rd_order_in !< Input: coefficient for blending advection orders. + logical, intent(in) :: dzdk_positive !< Input: Logical flag determining if dzdk is positive or negative. + logical, intent(in) :: check_monotonicity !< Input: Logical flag determining check on monotonicity of tracers + integer, intent(inout) :: err !< Input/Output: Error Flag + + err = 0 + + vert2ndOrder = .false. + vert3rdOrder = .false. + vert4thOrder = .false. + + if ( horiz_adv_order == 3) then + coef_3rd_order = coef_3rd_order_in + else if(horiz_adv_order == 2 .or. horiz_adv_order == 4) then + coef_3rd_order = 0.0_RKIND + end if + + horizOrder = horiz_adv_order + + if (vert_adv_order == 3) then + vert3rdOrder = .true. + else if (vert_adv_order == 4) then + vert4thOrder = .true. + else + vert2ndOrder = .true. + if(vert_adv_order /= 2) then + call mpas_log_write( & + 'Invalid value for vert_adv_order, defaulting to 2nd order', & + MPAS_LOG_WARN) + end if + end if + + positiveDzDk = dzdk_positive + monotonicityCheck = check_monotonicity + + end subroutine ocn_tracer_advection_std_init!}}} + +end module ocn_tracer_advection_std + diff --git a/src/core_ocean/shared/mpas_ocn_tracer_ecosys.F b/src/core_ocean/shared/mpas_ocn_tracer_ecosys.F new file mode 100755 index 0000000000..83480bf508 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_ecosys.F @@ -0,0 +1,2127 @@ +! copyright (c) 2013, los alamos national security, llc (lans) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_ecosys +! +!> \brief MPAS ocean ecosys +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This module contains routines for computing tracer forcing due to ecosys +! +!----------------------------------------------------------------------- + +module ocn_tracer_ecosys + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_timer + use mpas_timekeeping + use mpas_forcing + use mpas_stream_manager + use ocn_constants + use ocn_framework_forcing + +! use BGC_mod +! use BGC_parms + use bgc_mod + use bgc_parms + + implicit none + + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_ecosys_compute, & + ocn_tracer_ecosys_surface_flux_compute, & + ocn_get_ecosysData, & + ocn_ecosys_forcing_write_restart, & + ocn_tracer_ecosys_init + + integer, public:: & + numColumnsMax + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!----------------------------------------------------------------------- +! name the necessary BGC derived types +! all of these are defined in BGC_mod +!----------------------------------------------------------------------- + +! autotroph_cnt comes from BGC_parms module + type(autotroph_type), dimension(autotroph_cnt), public :: autotrophs + type(BGC_indices_type) , public :: BGC_indices + type(BGC_input_type) , public :: BGC_input + type(BGC_forcing_type) , public :: BGC_forcing + type(BGC_output_type) , public :: BGC_output + type(BGC_diagnostics_type), public :: BGC_diagnostic_fields + type(BGC_flux_diagnostics_type), public :: BGC_flux_diagnostic_fields + +! hold indices in tracer pool corresponding to each eco tracer array + type(BGC_indices_type), public :: ecosysIndices + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_ecosys_compute +! +!> \brief computes a tracer tendency due to ecosys +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine computes a tracer tendency due to ecosys +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_ecosys_compute(activeTracers, ecosysTracers, forcingPool, nTracers, nCellsSolve, & + latCell, maxLevelCell, nVertLevels, layerThickness, zMid, indexTemperature, indexSalinity, ecosysTracersTend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! one dimensional arrays + integer, dimension(:), intent(in) :: & + maxLevelCell + real (kind=RKIND), dimension(:), intent(in) :: & + latCell + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + zMid, layerThickness + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + ecosysTracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + activeTracers + + ! scalars + integer, intent(in) :: nTracers, nCellsSolve, nVertLevels, indexTemperature, indexSalinity + + type (mpas_pool_type), intent(inout) :: forcingPool + + ! + ! two dimensional pointers + ! + real (kind=RKIND), dimension(:), pointer :: & + dust_FLUX_IN, PAR_surface, shortWaveHeatFlux + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), pointer :: & + PH_PREV_3D, PH_PREV_ALT_CO2_3D, FESEDFLUX + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + ecosysTracersTend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: ecosysAuxiliary ! additional forcing fields + type (mpas_pool_type), pointer :: ecosysDiagFieldsLevel1, & ! diagnostics + ecosysDiagFieldsLevel2, & + ecosysDiagFieldsLevel3, & + ecosysDiagFieldsLevel4, & + ecosysDiagFieldsLevel5 + + logical, pointer :: & + config_ecosys_atm_alt_co2_use_eco + + logical, pointer :: & + config_ecosysTracers_diagnostic_fields_level1, & + config_ecosysTracers_diagnostic_fields_level2, & + config_ecosysTracers_diagnostic_fields_level3, & + config_ecosysTracers_diagnostic_fields_level4, & + config_ecosysTracers_diagnostic_fields_level5 + + ! + ! level 1 diagnostics + ! + + real (kind=RKIND), dimension(:), pointer :: & + ecosys_diag_photoC_TOT_zint, & + ecosys_diag_photoC_NO3_TOT_zint, & + ecosys_diag_O2_ZMIN, & + ecosys_diag_O2_ZMIN_DEPTH, & + ecosys_diag_Chl_TOT_zint_100m, & + ecosys_diag_Jint_Ctot, & + ecosys_diag_Jint_100m_Ctot, & + ecosys_diag_Jint_Ntot, & + ecosys_diag_Jint_100m_Ntot, & + ecosys_diag_Jint_Ptot, & + ecosys_diag_Jint_100m_Ptot, & + ecosys_diag_Jint_Sitot, & + ecosys_diag_Jint_100m_Sitot + + ! one dimensional arrays for each autotroph (nx,autotroph_cnt) + real (kind=RKIND), dimension(:,:), pointer :: & + ecosys_diag_photoC_zint, & + ecosys_diag_photoC_NO3_zint + + ! two dimensional arrays (nx,nz) + real (kind=RKIND), dimension(:,:), pointer :: & + ecosys_diag_PAR_avg, & + ecosys_diag_POC_FLUX_IN, & + ecosys_diag_CaCO3_FLUX_IN, & + ecosys_diag_auto_graze_TOT, & + ecosys_diag_zoo_loss, & + ecosys_diag_photoC_TOT, & + ecosys_diag_photoC_NO3_TOT, & + ecosys_diag_NITRIF, & + ecosys_diag_DENITRIF, & + ecosys_diag_calcToSed, & + ecosys_diag_pocToSed, & + ecosys_diag_pfeToSed, & + ecosys_diag_SedDenitrif, & + ecosys_diag_tot_Nfix + + ! + ! level 2 diagnostics + ! + + ! two dimensional arrays (nx,nz) + real (kind=RKIND), dimension(:,:), pointer :: & + ecosys_diag_O2_PRODUCTION, & + ecosys_diag_O2_CONSUMPTION, & + ecosys_diag_AOU, & + ecosys_diag_pH_3D, & + ecosys_diag_POC_PROD, & + ecosys_diag_POC_REMIN, & +!maltrud NOT SAVED IN BGC_mod YET + ecosys_diag_POC_ACCUM + + ! two dimensional arrays for each autotroph (nx,nz,autotroph_cnt) + real (BGC_r8), dimension(:,:,:), pointer :: & + ecosys_diag_N_lim, & + ecosys_diag_P_lim, & + ecosys_diag_Fe_lim, & + ecosys_diag_SiO3_lim, & + ecosys_diag_light_lim, & + ecosys_diag_photoC, & + ecosys_diag_photoC_NO3, & + ecosys_diag_photoFe, & + ecosys_diag_photoNO3, & + ecosys_diag_photoNH4, & + ecosys_diag_DOP_uptake, & + ecosys_diag_PO4_uptake, & + ecosys_diag_auto_graze, & + ecosys_diag_auto_loss, & + ecosys_diag_auto_agg, & + ecosys_diag_Nfix + + ! + ! level 3 diagnostics + ! + + ! one dimensional arrays (nx) + real (kind=RKIND), dimension(:), pointer :: & + ecosys_diag_tot_bSi_form + + ! two dimensional arrays (nx,nz) + real (BGC_r8), dimension(:,:), pointer :: & + ecosys_diag_SiO2_FLUX_IN, & + ecosys_diag_SiO2_PROD, & + ecosys_diag_SiO2_REMIN, & + ecosys_diag_dust_FLUX_IN, & + ecosys_diag_dust_REMIN, & + ecosys_diag_P_iron_FLUX_IN, & + ecosys_diag_P_iron_PROD, & + ecosys_diag_P_iron_REMIN, & + ecosys_diag_DOC_prod, & + ecosys_diag_DOC_remin, & + ecosys_diag_DON_prod, & + ecosys_diag_DON_remin, & + ecosys_diag_DOP_prod, & + ecosys_diag_DOP_remin, & + ecosys_diag_DOFe_prod, & + ecosys_diag_DOFe_remin, & + ecosys_diag_Fe_scavenge, & + ecosys_diag_Fe_scavenge_rate, & +!maltrud NOT SAVED IN BGC_mod YET + ecosys_diag_DONr_remin, & + ecosys_diag_DOPr_remin, & + ecosys_diag_ponToSed, & + ecosys_diag_popToSed, & + ecosys_diag_bsiToSed, & + ecosys_diag_dustToSed, & + ecosys_diag_OtherRemin + + ! two dimensional arrays for each autotroph (nx,nz,autotroph_cnt) + real (BGC_r8), dimension(:,:,:), pointer :: & + ecosys_diag_bSi_form + + ! + ! level 4 diagnostics + ! + + ! one dimensional arrays (nx) + real (BGC_r8), dimension(:), pointer :: & + ecosys_diag_tot_CaCO3_form_zint, & + ecosys_diag_zsatcalc, & + ecosys_diag_zsatarag + + ! one dimensional arrays for each autotroph (nx,autotroph_cnt) + real (BGC_r8), dimension(:,:), pointer :: & + ecosys_diag_CaCO3_form_zint ! diag array for CaCO3 formation vertical integral + + ! two dimensional arrays (nx,nz) + real (BGC_r8), dimension(:,:), pointer :: & + ecosys_diag_CaCO3_PROD, & + ecosys_diag_CaCO3_REMIN, & + ecosys_diag_tot_CaCO3_form, & + ecosys_diag_CO3, & + ecosys_diag_HCO3, & + ecosys_diag_H2CO3, & + ecosys_diag_CO3_ALT_CO2, & + ecosys_diag_HCO3_ALT_CO2, & + ecosys_diag_H2CO3_ALT_CO2, & + ecosys_diag_pH_3D_ALT_CO2, & + ecosys_diag_co3_sat_calc, & + ecosys_diag_co3_sat_arag + + ! two dimensional arrays for each autotroph (nx,nz,autotroph_cnt) + real (BGC_r8), dimension(:,:,:), pointer :: & + ecosys_diag_CaCO3_form + + ! + ! + ! level 5 diagnostics + ! + + ! two dimensional arrays (nx,nz) + real (BGC_r8), dimension(:,:), pointer :: & + ecosys_diag_PO4_RESTORE, & + ecosys_diag_NO3_RESTORE, & + ecosys_diag_SiO3_RESTORE + + ! source/sink wants CGS units + ! then convert back to MKS after + real (kind=RKIND) :: convertLengthMKStoCGS = 100.0_RKIND + real (kind=RKIND) :: convertLengthCGStoMKS = 0.01_RKIND + real (kind=RKIND) :: zTop, zBot + + integer :: iCell, iLevel, iTracer, numColumns, column, autotroph + + err = 0 + + call mpas_timer_start("ecosys source-sink") + + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_3D', PH_PREV_3D) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_ALT_CO2_3D', PH_PREV_ALT_CO2_3D) + call mpas_pool_get_array(ecosysAuxiliary, 'FESEDFLUX', FESEDFLUX) + call mpas_pool_get_array(ecosysAuxiliary, 'dust_FLUX_IN', dust_FLUX_IN) + call mpas_pool_get_array(ecosysAuxiliary, 'PAR_surface', PAR_surface) + + call mpas_pool_get_array(forcingPool, 'shortWaveHeatFlux', shortWaveHeatFlux) + + call mpas_pool_get_config(ocnConfigs, 'config_ecosys_atm_alt_co2_use_eco', & + config_ecosys_atm_alt_co2_use_eco) + +!maltrud change to diagnostics pool at some point (needs to be passed in) + + call mpas_pool_get_config(ocnConfigs, 'config_ecosysTracers_diagnostic_fields_level1', & + config_ecosysTracers_diagnostic_fields_level1) + call mpas_pool_get_config(ocnConfigs, 'config_ecosysTracers_diagnostic_fields_level2', & + config_ecosysTracers_diagnostic_fields_level2) + call mpas_pool_get_config(ocnConfigs, 'config_ecosysTracers_diagnostic_fields_level3', & + config_ecosysTracers_diagnostic_fields_level3) + call mpas_pool_get_config(ocnConfigs, 'config_ecosysTracers_diagnostic_fields_level4', & + config_ecosysTracers_diagnostic_fields_level4) + call mpas_pool_get_config(ocnConfigs, 'config_ecosysTracers_diagnostic_fields_level5', & + config_ecosysTracers_diagnostic_fields_level5) + + if (config_ecosysTracers_diagnostic_fields_level1) then + call mpas_pool_get_subpool(forcingPool, 'ecosysDiagFieldsLevel1', ecosysDiagFieldsLevel1) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_photoC_TOT_zint', ecosys_diag_photoC_TOT_zint) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_photoC_NO3_TOT_zint', ecosys_diag_photoC_NO3_TOT_zint) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_O2_ZMIN', ecosys_diag_O2_ZMIN) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_O2_ZMIN_DEPTH', ecosys_diag_O2_ZMIN_DEPTH) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Chl_TOT_zint_100m', ecosys_diag_Chl_TOT_zint_100m) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_Ctot', ecosys_diag_Jint_Ctot) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_100m_Ctot', ecosys_diag_Jint_100m_Ctot) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_Ntot', ecosys_diag_Jint_Ntot) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_100m_Ntot', ecosys_diag_Jint_100m_Ntot) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_Ptot', ecosys_diag_Jint_Ptot) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_100m_Ptot', ecosys_diag_Jint_100m_Ptot) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_Sitot', ecosys_diag_Jint_Sitot) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_Jint_100m_Sitot', ecosys_diag_Jint_100m_Sitot) + + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_photoC_zint', ecosys_diag_photoC_zint) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_photoC_NO3_zint', ecosys_diag_photoC_NO3_zint) + + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_PAR_avg', ecosys_diag_PAR_avg) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_POC_FLUX_IN', ecosys_diag_POC_FLUX_IN) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_CaCO3_FLUX_IN', ecosys_diag_CaCO3_FLUX_IN) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_auto_graze_TOT', ecosys_diag_auto_graze_TOT) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_zoo_loss', ecosys_diag_zoo_loss) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_photoC_TOT', ecosys_diag_photoC_TOT) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_photoC_NO3_TOT', ecosys_diag_photoC_NO3_TOT) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_NITRIF', ecosys_diag_NITRIF) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_DENITRIF', ecosys_diag_DENITRIF) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_calcToSed', ecosys_diag_calcToSed) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_pocToSed', ecosys_diag_pocToSed) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_pfeToSed', ecosys_diag_pfeToSed) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_SedDenitrif', ecosys_diag_SedDenitrif) + call mpas_pool_get_array(ecosysDiagFieldsLevel1, 'ecosys_diag_tot_Nfix', ecosys_diag_tot_Nfix) + + endif + + if (config_ecosysTracers_diagnostic_fields_level2) then + call mpas_pool_get_subpool(forcingPool, 'ecosysDiagFieldsLevel2', ecosysDiagFieldsLevel2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_O2_PRODUCTION', ecosys_diag_O2_PRODUCTION) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_O2_CONSUMPTION', ecosys_diag_O2_CONSUMPTION) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_AOU', ecosys_diag_AOU) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_pH_3D', ecosys_diag_pH_3D) + + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_POC_PROD', ecosys_diag_POC_PROD) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_POC_REMIN', ecosys_diag_POC_REMIN) +!maltrud NOT SAVED IN BGC_mod YET + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_POC_ACCUM', ecosys_diag_POC_ACCUM) + + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_N_lim', ecosys_diag_N_lim) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_P_lim', ecosys_diag_P_lim) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_Fe_lim', ecosys_diag_Fe_lim) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_SiO3_lim', ecosys_diag_SiO3_lim) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_light_lim', ecosys_diag_light_lim) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_photoC', ecosys_diag_photoC) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_photoC_NO3', ecosys_diag_photoC_NO3) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_photoFe', ecosys_diag_photoFe) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_photoNO3', ecosys_diag_photoNO3) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_photoNH4', ecosys_diag_photoNH4) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_DOP_uptake', ecosys_diag_DOP_uptake) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_PO4_uptake', ecosys_diag_PO4_uptake) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_auto_graze', ecosys_diag_auto_graze) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_auto_loss', ecosys_diag_auto_loss) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_auto_agg', ecosys_diag_auto_agg) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_Nfix', ecosys_diag_Nfix) + endif + + if (config_ecosysTracers_diagnostic_fields_level3) then + call mpas_pool_get_subpool(forcingPool, 'ecosysDiagFieldsLevel3', ecosysDiagFieldsLevel3) + + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_tot_bSi_form', ecosys_diag_tot_bSi_form) + + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_SiO2_FLUX_IN', ecosys_diag_SiO2_FLUX_IN) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_SiO2_PROD', ecosys_diag_SiO2_PROD) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_SiO2_REMIN', ecosys_diag_SiO2_REMIN) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_dust_FLUX_IN', ecosys_diag_dust_FLUX_IN) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_dust_REMIN', ecosys_diag_dust_REMIN) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_P_iron_FLUX_IN', ecosys_diag_P_iron_FLUX_IN) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_P_iron_PROD', ecosys_diag_P_iron_PROD) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_P_iron_REMIN', ecosys_diag_P_iron_REMIN) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DOC_prod', ecosys_diag_DOC_prod) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DOC_remin', ecosys_diag_DOC_remin) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DON_prod', ecosys_diag_DON_prod) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DON_remin', ecosys_diag_DON_remin) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DOP_prod', ecosys_diag_DOP_prod) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DOP_remin', ecosys_diag_DOP_remin) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DOFe_prod', ecosys_diag_DOFe_prod) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DOFe_remin', ecosys_diag_DOFe_remin) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_Fe_scavenge', ecosys_diag_Fe_scavenge) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_Fe_scavenge_rate', ecosys_diag_Fe_scavenge_rate) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DONr_remin', ecosys_diag_DONr_remin) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_DOPr_remin', ecosys_diag_DOPr_remin) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_ponToSed', ecosys_diag_ponToSed) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_popToSed', ecosys_diag_popToSed) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_bsiToSed', ecosys_diag_bsiToSed) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_dustToSed', ecosys_diag_dustToSed) + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_OtherRemin', ecosys_diag_OtherRemin) + + call mpas_pool_get_array(ecosysDiagFieldsLevel3, 'ecosys_diag_bSi_form', ecosys_diag_bSi_form) + endif + + if (config_ecosysTracers_diagnostic_fields_level4) then + call mpas_pool_get_subpool(forcingPool, 'ecosysDiagFieldsLevel4', ecosysDiagFieldsLevel4) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_tot_CaCO3_form_zint', ecosys_diag_tot_CaCO3_form_zint) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_zsatcalc', ecosys_diag_zsatcalc) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_zsatarag', ecosys_diag_zsatarag) + + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_CaCO3_form_zint', ecosys_diag_CaCO3_form_zint) + + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_CaCO3_PROD', ecosys_diag_CaCO3_PROD) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_CaCO3_REMIN', ecosys_diag_CaCO3_REMIN) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_tot_CaCO3_form', ecosys_diag_tot_CaCO3_form) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_CaCO3_form', ecosys_diag_CaCO3_form) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_CO3', ecosys_diag_CO3) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_HCO3', ecosys_diag_HCO3) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_H2CO3', ecosys_diag_H2CO3) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_CO3_ALT_CO2', ecosys_diag_CO3_ALT_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_HCO3_ALT_CO2', ecosys_diag_HCO3_ALT_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_H2CO3_ALT_CO2', ecosys_diag_H2CO3_ALT_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_pH_3D_ALT_CO2', ecosys_diag_pH_3D_ALT_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_co3_sat_calc', ecosys_diag_co3_sat_calc) + call mpas_pool_get_array(ecosysDiagFieldsLevel4, 'ecosys_diag_co3_sat_arag', ecosys_diag_co3_sat_arag) + endif + + if (config_ecosysTracers_diagnostic_fields_level5) then + call mpas_pool_get_subpool(forcingPool, 'ecosysDiagFieldsLevel5', ecosysDiagFieldsLevel5) + call mpas_pool_get_array(ecosysDiagFieldsLevel5, 'ecosys_diag_PO4_RESTORE', ecosys_diag_PO4_RESTORE) + call mpas_pool_get_array(ecosysDiagFieldsLevel5, 'ecosys_diag_NO3_RESTORE', ecosys_diag_NO3_RESTORE) + call mpas_pool_get_array(ecosysDiagFieldsLevel5, 'ecosys_diag_SiO3_RESTORE', ecosys_diag_SiO3_RESTORE) + endif + + numColumns = 1 + column = 1 + !DWJ 08/05/2016: This loop needs OpenMP added to it. + do iCell=1,nCellsSolve + BGC_input%number_of_active_levels(column) = maxLevelCell(iCell) + BGC_input%cell_latitude(column) = latCell(iCell) +! convert dust flux from kg/m2/s to g/cm2/s + BGC_forcing%dust_FLUX_IN(column) = dust_FLUX_IN(iCell)*0.1_RKIND + BGC_forcing%ShortWaveFlux_surface(column) = shortWaveHeatFlux(iCell) + zTop = 0.0_RKIND + do iLevel=1,maxLevelCell(iCell) + BGC_input%PotentialTemperature(iLevel,column) = activeTracers(indexTemperature,iLevel,iCell) + BGC_input%Salinity(iLevel,column) = activeTracers(indexSalinity,iLevel,iCell) + BGC_input%cell_center_depth(iLevel,column) = -1.0_RKIND*zMid(iLevel,iCell)*convertLengthMKStoCGS + BGC_input%cell_thickness(iLevel,column) = layerThickness(iLevel,iCell)*convertLengthMKStoCGS + zBot = zTop + layerThickness(iLevel,iCell) + BGC_input%cell_bottom_depth(iLevel,column) = zBot*convertLengthMKStoCGS + zTop = zBot + + BGC_output%PH_PREV_3D(iLevel,column) = PH_PREV_3D(iLevel,iCell) + BGC_output%PH_PREV_ALT_CO2_3D(iLevel,column) = PH_PREV_ALT_CO2_3D(iLevel,iCell) + +!maltrud increase FESEDFLUX by factor of 5 +! BGC_forcing%FESEDFLUX(iLevel,column) = FESEDFLUX(iLevel,iCell)*convertLengthMKStoCGS + BGC_forcing%FESEDFLUX(iLevel,column) = FESEDFLUX(iLevel,iCell)*convertLengthMKStoCGS*5.0_RKIND + BGC_forcing%NUTR_RESTORE_RTAU(iLevel,column) = 0.0_RKIND + BGC_forcing%NO3_CLIM(iLevel,column) = 0.0_RKIND + BGC_forcing%PO4_CLIM(iLevel,column) = 0.0_RKIND + BGC_forcing%SiO3_CLIM(iLevel,column) = 0.0_RKIND + + BGC_input%BGC_tracers(iLevel,column,BGC_indices%po4_ind) = & + ecosysTracers(ecosysIndices%po4_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%no3_ind) = & + ecosysTracers(ecosysIndices%no3_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%sio3_ind) = & + ecosysTracers(ecosysIndices%sio3_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%nh4_ind) = & + ecosysTracers(ecosysIndices%nh4_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%fe_ind) = & + ecosysTracers(ecosysIndices%fe_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%o2_ind) = & + ecosysTracers(ecosysIndices%o2_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%dic_ind) = & + ecosysTracers(ecosysIndices%dic_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%dic_alt_co2_ind) = & + ecosysTracers(ecosysIndices%dic_alt_co2_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%alk_ind) = & + ecosysTracers(ecosysIndices%alk_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%doc_ind) = & + ecosysTracers(ecosysIndices%doc_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%don_ind) = & + ecosysTracers(ecosysIndices%don_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%dofe_ind) = & + ecosysTracers(ecosysIndices%dofe_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%dop_ind) = & + ecosysTracers(ecosysIndices%dop_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%donr_ind) = & + ecosysTracers(ecosysIndices%donr_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%dopr_ind) = & + ecosysTracers(ecosysIndices%dopr_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%zooC_ind) = & + ecosysTracers(ecosysIndices%zooC_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%spC_ind) = & + ecosysTracers(ecosysIndices%spC_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%spChl_ind) = & + ecosysTracers(ecosysIndices%spChl_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%spFe_ind) = & + ecosysTracers(ecosysIndices%spFe_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%spCaCO3_ind) = & + ecosysTracers(ecosysIndices%spCaCO3_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%diatC_ind) = & + ecosysTracers(ecosysIndices%diatC_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%diatChl_ind) = & + ecosysTracers(ecosysIndices%diatChl_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%diatFe_ind) = & + ecosysTracers(ecosysIndices%diatFe_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%diatSi_ind) = & + ecosysTracers(ecosysIndices%diatSi_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%phaeoC_ind) = & + ecosysTracers(ecosysIndices%phaeoC_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%phaeoChl_ind) = & + ecosysTracers(ecosysIndices%phaeoChl_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%phaeoFe_ind) = & + ecosysTracers(ecosysIndices%phaeoFe_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%diazC_ind) = & + ecosysTracers(ecosysIndices%diazC_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%diazChl_ind) = & + ecosysTracers(ecosysIndices%diazChl_ind,iLevel,iCell) + BGC_input%BGC_tracers(iLevel,column,BGC_indices%diazFe_ind) = & + ecosysTracers(ecosysIndices%diazFe_ind,iLevel,iCell) + + enddo ! iLevel + + call BGC_SourceSink(autotrophs, BGC_indices, BGC_input, BGC_forcing, & + BGC_output, BGC_diagnostic_fields, nVertLevels, & + numColumnsMax, numColumns, config_ecosys_atm_alt_co2_use_eco) + + do iLevel=1,maxLevelCell(iCell) + + ecosysTracersTend(ecosysIndices%po4_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%po4_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%po4_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%no3_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%no3_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%no3_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%sio3_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%sio3_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%sio3_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%nh4_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%nh4_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%nh4_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%fe_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%fe_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%fe_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%o2_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%o2_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%o2_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%dic_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%dic_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%dic_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%dic_alt_co2_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%dic_alt_co2_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%dic_alt_co2_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%alk_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%alk_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%alk_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%doc_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%doc_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%doc_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%don_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%don_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%don_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%dofe_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%dofe_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%dofe_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%dop_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%dop_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%dop_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%dopr_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%dopr_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%dopr_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%donr_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%donr_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%donr_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%zooC_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%zooC_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%zooC_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%spC_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%spC_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%spC_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%spChl_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%spChl_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%spChl_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%spFe_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%spFe_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%spFe_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%spCaCO3_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%spCaCO3_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%spCaCO3_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%diatC_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%diatC_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%diatC_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%diatChl_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%diatChl_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%diatChl_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%diatFe_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%diatFe_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%diatFe_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%diatSi_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%diatSi_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%diatSi_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%diazC_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%diazC_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%diazC_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%diazChl_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%diazChl_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%diazChl_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%diazFe_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%diazFe_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%diazFe_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%phaeoC_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%phaeoC_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%phaeoC_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%phaeoChl_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%phaeoChl_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%phaeoChl_ind)*layerThickness(iLevel,iCell) + ecosysTracersTend(ecosysIndices%phaeoFe_ind,iLevel,iCell) = & + ecosysTracersTend(ecosysIndices%phaeoFe_ind,iLevel,iCell) & + + BGC_output%BGC_tendencies(iLevel,column,BGC_indices%phaeoFe_ind)*layerThickness(iLevel,iCell) + + PH_PREV_3D(iLevel,iCell) = BGC_output%PH_PREV_3D(iLevel,column) + PH_PREV_ALT_CO2_3D(iLevel,iCell) = BGC_output%PH_PREV_ALT_CO2_3D(iLevel,column) + + ! + ! level 1 diagnostics + ! + + if (config_ecosysTracers_diagnostic_fields_level1) then + ecosys_diag_PAR_avg(iLevel,iCell) = & + BGC_diagnostic_fields%diag_PAR_avg(iLevel,column) + ecosys_diag_POC_FLUX_IN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_POC_FLUX_IN(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_CaCO3_FLUX_IN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_CaCO3_FLUX_IN(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_auto_graze_TOT(iLevel,iCell) = & + BGC_diagnostic_fields%diag_auto_graze_TOT(iLevel,column) + ecosys_diag_zoo_loss(iLevel,iCell) = & + BGC_diagnostic_fields%diag_zoo_loss(iLevel,column) + ecosys_diag_photoC_TOT(iLevel,iCell) = & + BGC_diagnostic_fields%diag_photoC_TOT(iLevel,column) + ecosys_diag_photoC_NO3_TOT(iLevel,iCell) = & + BGC_diagnostic_fields%diag_photoC_NO3_TOT(iLevel,column) + ecosys_diag_NITRIF(iLevel,iCell) = & + BGC_diagnostic_fields%diag_NITRIF(iLevel,column) + ecosys_diag_DENITRIF(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DENITRIF(iLevel,column) + ecosys_diag_calcToSed(iLevel,iCell) = & + BGC_diagnostic_fields%diag_calcToSed(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_pocToSed(iLevel,iCell) = & + BGC_diagnostic_fields%diag_pocToSed(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_pfeToSed(iLevel,iCell) = & + BGC_diagnostic_fields%diag_pfeToSed(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_SedDenitrif(iLevel,iCell) = & + BGC_diagnostic_fields%diag_SedDenitrif(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_tot_Nfix(iLevel,iCell) = & + BGC_diagnostic_fields%diag_tot_Nfix(iLevel,column) + endif ! config_ecosysTracers_diagnostic_fields_level1 + + ! + ! level 2 diagnostics + ! + if (config_ecosysTracers_diagnostic_fields_level2) then + ecosys_diag_O2_PRODUCTION(iLevel,iCell) = & + BGC_diagnostic_fields%diag_O2_PRODUCTION(iLevel,column) + ecosys_diag_O2_CONSUMPTION(iLevel,iCell) = & + BGC_diagnostic_fields%diag_O2_CONSUMPTION(iLevel,column) + ecosys_diag_AOU(iLevel,iCell) = & + BGC_diagnostic_fields%diag_AOU(iLevel,column) + ecosys_diag_pH_3D(iLevel,iCell) = & + BGC_diagnostic_fields%diag_pH_3D(iLevel,column) + ecosys_diag_POC_PROD(iLevel,iCell) = & + BGC_diagnostic_fields%diag_POC_PROD(iLevel,column) + ecosys_diag_POC_REMIN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_POC_REMIN(iLevel,column) +!maltrud NOT SAVED IN BGC_mod YET +! ecosys_diag_POC_ACCUM(iLevel,iCell) = & +! BGC_diagnostic_fields%diag_POC_ACCUM(iLevel,column) + ecosys_diag_POC_ACCUM(iLevel,iCell) = 0.0_RKIND + + do autotroph = 1, autotroph_cnt + ecosys_diag_N_lim(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_N_lim(iLevel,column,autotroph) + ecosys_diag_P_lim(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_P_lim(iLevel,column,autotroph) + ecosys_diag_Fe_lim(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_Fe_lim(iLevel,column,autotroph) + ecosys_diag_SiO3_lim(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_SiO3_lim(iLevel,column,autotroph) + ecosys_diag_light_lim(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_light_lim(iLevel,column,autotroph) + ecosys_diag_photoC(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_photoC(iLevel,column,autotroph) + ecosys_diag_photoC_NO3(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_photoC_NO3(iLevel,column,autotroph) + ecosys_diag_photoFe(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_photoFe(iLevel,column,autotroph) + ecosys_diag_photoNO3(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_photoNO3(iLevel,column,autotroph) + ecosys_diag_photoNH4(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_photoNH4(iLevel,column,autotroph) + ecosys_diag_DOP_uptake(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOP_uptake(iLevel,column,autotroph) + ecosys_diag_PO4_uptake(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_PO4_uptake(iLevel,column,autotroph) + ecosys_diag_auto_graze(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_auto_graze(iLevel,column,autotroph) + ecosys_diag_auto_loss(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_auto_loss(iLevel,column,autotroph) + ecosys_diag_auto_agg(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_auto_agg(iLevel,column,autotroph) + ecosys_diag_Nfix(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_Nfix(iLevel,column,autotroph) + enddo ! autotroph loop + + endif ! config_ecosysTracers_diagnostic_fields_level2 + + ! + ! level 3 diagnostics + ! + if (config_ecosysTracers_diagnostic_fields_level3) then + ecosys_diag_SiO2_FLUX_IN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_SiO2_FLUX_IN(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_SiO2_PROD(iLevel,iCell) = & + BGC_diagnostic_fields%diag_SiO2_PROD(iLevel,column) + ecosys_diag_SiO2_REMIN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_SiO2_REMIN(iLevel,column) + ecosys_diag_dust_FLUX_IN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_dust_FLUX_IN(iLevel,column) + ecosys_diag_dust_REMIN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_dust_REMIN(iLevel,column) + ecosys_diag_P_iron_FLUX_IN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_P_iron_FLUX_IN(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_P_iron_PROD(iLevel,iCell) = & + BGC_diagnostic_fields%diag_P_iron_PROD(iLevel,column) + ecosys_diag_P_iron_REMIN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_P_iron_REMIN(iLevel,column) + ecosys_diag_DOC_prod(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOC_prod(iLevel,column) + ecosys_diag_DOC_remin(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOC_remin(iLevel,column) + ecosys_diag_DON_prod(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DON_prod(iLevel,column) + ecosys_diag_DON_remin(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DON_remin(iLevel,column) + ecosys_diag_DOP_prod(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOP_prod(iLevel,column) + ecosys_diag_DOP_remin(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOP_remin(iLevel,column) + ecosys_diag_DOFe_prod(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOFe_prod(iLevel,column) + ecosys_diag_DOFe_remin(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOFe_remin(iLevel,column) + ecosys_diag_Fe_scavenge(iLevel,iCell) = & + BGC_diagnostic_fields%diag_Fe_scavenge(iLevel,column) + ecosys_diag_Fe_scavenge_rate(iLevel,iCell) = & + BGC_diagnostic_fields%diag_Fe_scavenge_rate(iLevel,column) + ecosys_diag_DONr_remin(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DONr_remin(iLevel,column) + ecosys_diag_DOPr_remin(iLevel,iCell) = & + BGC_diagnostic_fields%diag_DOPr_remin(iLevel,column) + ecosys_diag_ponToSed(iLevel,iCell) = & + BGC_diagnostic_fields%diag_ponToSed(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_popToSed(iLevel,iCell) = & + BGC_diagnostic_fields%diag_popToSed(iLevel,column)*convertLengthCGStoMKS + ecosys_diag_bsiToSed(iLevel,iCell) = & + BGC_diagnostic_fields%diag_bsiToSed(iLevel,column)*convertLengthCGStoMKS +! convert units from g/cm2/s to kg/m2/s + ecosys_diag_dustToSed(iLevel,iCell) = & + BGC_diagnostic_fields%diag_dustToSed(iLevel,column)*1.e-7_RKIND + ecosys_diag_OtherRemin(iLevel,iCell) = & + BGC_diagnostic_fields%diag_OtherRemin(iLevel,column)*convertLengthCGStoMKS + + do autotroph = 1, autotroph_cnt + ecosys_diag_bSi_form(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_bSi_form(iLevel,column,autotroph) + enddo ! autotroph loop + + endif ! config_ecosysTracers_diagnostic_fields_level3 + + ! + ! level 4 diagnostics + ! + if (config_ecosysTracers_diagnostic_fields_level4) then + ecosys_diag_CaCO3_PROD(iLevel,iCell) = & + BGC_diagnostic_fields%diag_CaCO3_PROD(iLevel,column) + ecosys_diag_CaCO3_REMIN(iLevel,iCell) = & + BGC_diagnostic_fields%diag_CaCO3_REMIN(iLevel,column) + ecosys_diag_tot_CaCO3_form(iLevel,iCell) = & + BGC_diagnostic_fields%diag_tot_CaCO3_form(iLevel,column) + ecosys_diag_CO3(iLevel,iCell) = & + BGC_diagnostic_fields%diag_CO3(iLevel,column) + ecosys_diag_HCO3(iLevel,iCell) = & + BGC_diagnostic_fields%diag_HCO3(iLevel,column) + ecosys_diag_H2CO3(iLevel,iCell) = & + BGC_diagnostic_fields%diag_H2CO3(iLevel,column) + ecosys_diag_CO3_ALT_CO2(iLevel,iCell) = & + BGC_diagnostic_fields%diag_CO3_ALT_CO2(iLevel,column) + ecosys_diag_HCO3_ALT_CO2(iLevel,iCell) = & + BGC_diagnostic_fields%diag_HCO3_ALT_CO2(iLevel,column) + ecosys_diag_H2CO3_ALT_CO2(iLevel,iCell) = & + BGC_diagnostic_fields%diag_H2CO3_ALT_CO2(iLevel,column) + ecosys_diag_pH_3D_ALT_CO2(iLevel,iCell) = & + BGC_diagnostic_fields%diag_pH_3D_ALT_CO2(iLevel,column) + ecosys_diag_co3_sat_calc(iLevel,iCell) = & + BGC_diagnostic_fields%diag_co3_sat_calc(iLevel,column) + ecosys_diag_co3_sat_arag(iLevel,iCell) = & + BGC_diagnostic_fields%diag_co3_sat_arag(iLevel,column) + + do autotroph = 1, autotroph_cnt + ecosys_diag_CaCO3_form(autotroph,iLevel,iCell) = & + BGC_diagnostic_fields%diag_CaCO3_form(iLevel,column,autotroph) + enddo + endif ! config_ecosysTracers_diagnostic_fields_level4 + + ! + ! level 5 diagnostics + ! + + if (config_ecosysTracers_diagnostic_fields_level5) then + ecosys_diag_PO4_RESTORE(iLevel,iCell) = & + BGC_diagnostic_fields%diag_PO4_RESTORE(iLevel,column) + ecosys_diag_NO3_RESTORE(iLevel,iCell) = & + BGC_diagnostic_fields%diag_NO3_RESTORE(iLevel,column) + ecosys_diag_SiO3_RESTORE(iLevel,iCell) = & + BGC_diagnostic_fields%diag_SiO3_RESTORE(iLevel,column) + endif ! config_ecosysTracers_diagnostic_fields_level5 + + enddo ! iLevel + + ! + ! level 1 diagnostics with no depth dependence + ! + if (config_ecosysTracers_diagnostic_fields_level1) then + ecosys_diag_photoC_TOT_zint(iCell) = & + BGC_diagnostic_fields%diag_photoC_TOT_zint(column)*convertLengthCGStoMKS + ecosys_diag_photoC_NO3_TOT_zint(iCell) = & + BGC_diagnostic_fields%diag_photoC_NO3_TOT_zint(column)*convertLengthCGStoMKS + ecosys_diag_O2_ZMIN(iCell) = & + BGC_diagnostic_fields%diag_O2_ZMIN(column) + ecosys_diag_O2_ZMIN_DEPTH(iCell) = & + BGC_diagnostic_fields%diag_O2_ZMIN_DEPTH(column)*convertLengthCGStoMKS + ecosys_diag_Chl_TOT_zint_100m(iCell) = & + BGC_diagnostic_fields%diag_Chl_TOT_zint_100m(column)*convertLengthCGStoMKS + ecosys_diag_Jint_Ctot(iCell) = & + BGC_diagnostic_fields%diag_Jint_Ctot(column)*convertLengthCGStoMKS + ecosys_diag_Jint_100m_Ctot(iCell) = & + BGC_diagnostic_fields%diag_Jint_100m_Ctot(column)*convertLengthCGStoMKS + ecosys_diag_Jint_Ntot(iCell) = & + BGC_diagnostic_fields%diag_Jint_Ntot(column)*convertLengthCGStoMKS + ecosys_diag_Jint_100m_Ntot(iCell) = & + BGC_diagnostic_fields%diag_Jint_100m_Ntot(column)*convertLengthCGStoMKS + ecosys_diag_Jint_Ptot(iCell) = & + BGC_diagnostic_fields%diag_Jint_Ptot(column)*convertLengthCGStoMKS + ecosys_diag_Jint_100m_Ptot(iCell) = & + BGC_diagnostic_fields%diag_Jint_100m_Ptot(column)*convertLengthCGStoMKS + ecosys_diag_Jint_Sitot(iCell) = & + BGC_diagnostic_fields%diag_Jint_Sitot(column)*convertLengthCGStoMKS + ecosys_diag_Jint_100m_Sitot(iCell) = & + BGC_diagnostic_fields%diag_Jint_100m_Sitot(column)*convertLengthCGStoMKS + + do autotroph = 1, autotroph_cnt + ecosys_diag_photoC_zint(autotroph,iCell) = & + BGC_diagnostic_fields%diag_photoC_zint(column,autotroph)*convertLengthCGStoMKS + ecosys_diag_photoC_NO3_zint(autotroph,iCell) = & + BGC_diagnostic_fields%diag_photoC_NO3_zint(column,autotroph)*convertLengthCGStoMKS + enddo + + endif ! config_ecosysTracers_diagnostic_fields_level1 + + ! + ! level 3 diagnostics with no depth dependence + ! + if (config_ecosysTracers_diagnostic_fields_level3) then + ecosys_diag_tot_bSi_form(iCell) = & + BGC_diagnostic_fields%diag_tot_bSi_form(column) + endif ! config_ecosysTracers_diagnostic_fields_level3 + + ! + ! level 4 diagnostics with no depth dependence + ! + if (config_ecosysTracers_diagnostic_fields_level4) then + ecosys_diag_tot_CaCO3_form_zint(iCell) = & + BGC_diagnostic_fields%diag_tot_CaCO3_form_zint(column)*convertLengthCGStoMKS + ecosys_diag_zsatcalc(iCell) = & + BGC_diagnostic_fields%diag_zsatcalc(column)*convertLengthCGStoMKS + ecosys_diag_zsatarag(iCell) = & + BGC_diagnostic_fields%diag_zsatarag(column)*convertLengthCGStoMKS + + do autotroph = 1, autotroph_cnt + ecosys_diag_CaCO3_form_zint(autotroph,iCell) = & + BGC_diagnostic_fields%diag_CaCO3_form_zint(column,autotroph)*convertLengthCGStoMKS + enddo + + endif ! config_ecosysTracers_diagnostic_fields_level4 + + enddo ! iCell + + call mpas_timer_stop("ecosys source-sink") + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_ecosys_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_ecosys_surface_flux_compute +! +!> \brief computes a tracer tendency due to ecosys +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine computes a tracer tendency due to ecosys +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_ecosys_surface_flux_compute(activeTracers, ecosysTracers, forcingPool, & + nTracers, nCellsSolve, zMid, indexTemperature, indexSalinity, ecosysSurfaceFlux, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + zMid + real (kind=RKIND), dimension(:,:), intent(inout) :: & + ecosysSurfaceFlux + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + ecosysTracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + activeTracers + + ! scalars + integer, intent(in) :: nTracers, nCellsSolve, indexTemperature, indexSalinity + + type (mpas_pool_type), intent(inout) :: forcingPool + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), pointer :: ecosysAuxiliary, & ! additional forcing fields + ecosysSeaIceCoupling + + integer :: numColumns, column, iCell, iTracer, iLevelSurface + +! input flux components in forcing pool + real (kind=RKIND), dimension(:), pointer :: & + atmosphericPressure, & + iceFraction, & + landIceFraction + +! input flux components in ecosysAuxiliary + real (kind=RKIND), dimension(:), pointer :: & + windSpeedSquared10m, & + depositionFluxNO3, & + depositionFluxNH4, & + IRON_FLUX_IN, & + riverFluxNO3, & + riverFluxPO4, & + riverFluxDON, & + riverFluxDOP, & + riverFluxSiO3, & + riverFluxFe, & + riverFluxDIC, & + riverFluxALK, & + riverFluxDOC, & + atmosphericCO2, & + atmosphericCO2_ALT_CO2 + +! input flux components in ecosysSeaIceCoupling + real (kind=RKIND), dimension(:), pointer :: & + iceFluxDIC, & + iceFluxDON, & + iceFluxNO3, & + iceFluxSiO3, & + iceFluxNH4, & + iceFluxDOCr, & + iceFluxFeDissolved + + real (kind=RKIND), dimension(:,:), pointer :: iceFluxPhytoC, & + iceFluxDOC + + +! specific output fluxes + real (kind=RKIND), dimension(:), pointer :: & + CO2_gas_flux, & + CO2_alt_gas_flux + +! input/output terms + real (kind=RKIND), dimension(:), pointer :: & + PH_PREV, & + PH_PREV_ALT_CO2 + + ! flux routine wants atmospheres + real (kind=RKIND) :: PascalsToAtmospheres = 1.0_RKIND/101.325e+3_RKIND + ! flux routine wants CGS units + ! then convert back to MKS after + real (kind=RKIND) :: convertLengthMKStoCGS = 100.0_RKIND + real (kind=RKIND) :: convertLengthCGStoMKS = 0.01_RKIND + real (kind=RKIND) :: convertLengthSquaredMKStoCGS = 1.0e4_RKIND + + logical, pointer :: config_use_ecosysTracers_sea_ice_coupling + + ! + ! level 2 diagnostics + ! + + type (mpas_pool_type), pointer :: & + ecosysDiagFieldsLevel2 + + logical, pointer :: & + config_ecosysTracers_diagnostic_fields_level2 + + real (kind=RKIND), dimension(:), pointer :: & + ecosys_diag_pistonVel_O2, & + ecosys_diag_pistonVel_CO2, & + ecosys_diag_Schmidt_O2, & + ecosys_diag_Schmidt_CO2, & + ecosys_diag_O2_saturation, & + ecosys_diag_xkw, & + ecosys_diag_CO2star, & + ecosys_diag_dCO2star, & + ecosys_diag_pCO2surface, & + ecosys_diag_dpCO2, & + ecosys_diag_CO2star_ALT_CO2, & + ecosys_diag_dCO2star_ALT_CO2, & + ecosys_diag_pCO2surface_ALT_CO2, & + ecosys_diag_dpCO2_ALT_CO2 + + + + + + call mpas_timer_start("ecosys surface flux") + err = 0 + + call mpas_pool_get_array(forcingPool, 'atmosphericPressure', atmosphericPressure) + call mpas_pool_get_array(forcingPool, 'iceFraction', iceFraction) + call mpas_pool_get_array(forcingPool, 'landIceFraction', landIceFraction) + + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + + call mpas_pool_get_array(ecosysAuxiliary, 'windSpeedSquared10m', windSpeedSquared10m) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV', PH_PREV) + call mpas_pool_get_array(ecosysAuxiliary, 'PH_PREV_ALT_CO2', PH_PREV_ALT_CO2) + call mpas_pool_get_array(ecosysAuxiliary, 'depositionFluxNO3', depositionFluxNO3) + call mpas_pool_get_array(ecosysAuxiliary, 'depositionFluxNH4', depositionFluxNH4) + call mpas_pool_get_array(ecosysAuxiliary, 'IRON_FLUX_IN', IRON_FLUX_IN) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxNO3', riverFluxNO3) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxPO4', riverFluxPO4) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDON', riverFluxDON) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDOP', riverFluxDOP) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxSiO3', riverFluxSiO3) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxFe', riverFluxFe) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDIC', riverFluxDIC) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxALK', riverFluxALK) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDOC', riverFluxDOC) + call mpas_pool_get_array(ecosysAuxiliary, 'atmosphericCO2', atmosphericCO2) + call mpas_pool_get_array(ecosysAuxiliary, 'atmosphericCO2_ALT_CO2', atmosphericCO2_ALT_CO2) + + call mpas_pool_get_array(ecosysAuxiliary, 'CO2_gas_flux', CO2_gas_flux) + call mpas_pool_get_array(ecosysAuxiliary, 'CO2_alt_gas_flux', CO2_alt_gas_flux) + + call mpas_pool_get_config(ocnConfigs, 'config_use_ecosysTracers_sea_ice_coupling', & + config_use_ecosysTracers_sea_ice_coupling) + + if (config_use_ecosysTracers_sea_ice_coupling) then + + call mpas_pool_get_subpool(forcingPool, 'ecosysSeaIceCoupling', ecosysSeaIceCoupling) + + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxPhytoC', iceFluxPhytoC) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxDIC', iceFluxDIC) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxNO3', iceFluxNO3) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxSiO3', iceFluxSiO3) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxNH4', iceFluxNH4) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxDOCr', iceFluxDOCr) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxDOC', iceFluxDOC) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxDON', iceFluxDON) + call mpas_pool_get_array(ecosysSeaIceCoupling, 'iceFluxFeDissolved', iceFluxFeDissolved) + + endif + + call mpas_pool_get_config(ocnConfigs, 'config_ecosysTracers_diagnostic_fields_level2', & + config_ecosysTracers_diagnostic_fields_level2) + + if (config_ecosysTracers_diagnostic_fields_level2) then + call mpas_pool_get_subpool(forcingPool, 'ecosysDiagFieldsLevel2', ecosysDiagFieldsLevel2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_pistonVel_O2', ecosys_diag_pistonVel_O2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_pistonVel_CO2', ecosys_diag_pistonVel_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_Schmidt_O2', ecosys_diag_Schmidt_O2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_Schmidt_CO2', ecosys_diag_Schmidt_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_O2_saturation', ecosys_diag_O2_saturation) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_xkw', ecosys_diag_xkw) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_CO2star', ecosys_diag_CO2star) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_dCO2star', ecosys_diag_dCO2star) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_pCO2surface', ecosys_diag_pCO2surface) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_dpCO2', ecosys_diag_dpCO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_CO2star_ALT_CO2', ecosys_diag_CO2star_ALT_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_dCO2star_ALT_CO2', ecosys_diag_dCO2star_ALT_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_pCO2surface_ALT_CO2', ecosys_diag_pCO2surface_ALT_CO2) + call mpas_pool_get_array(ecosysDiagFieldsLevel2, 'ecosys_diag_dpCO2_ALT_CO2', ecosys_diag_dpCO2_ALT_CO2) + endif + + BGC_forcing%lcalc_O2_gas_flux = .true. + BGC_forcing%lcalc_CO2_gas_flux = .true. + + numColumns = 1 + column = 1 + iLevelSurface = 1 + !DWJ 08/05/2016: This loop needs OpenMP added to it. + do iCell=1,nCellsSolve + +! BGC_forcing%surfacePressure(column) = atmosphericPressure(iCell)*PascalsToAtmospheres + BGC_forcing%surfacePressure(column) = 1.0_RKIND + BGC_forcing%iceFraction(column) = iceFraction(iCell) +!maltrud assume for now that if there is any land ice, it is all land ice + if (associated(landIceFraction)) then + if (landIceFraction(iCell) > 0.0_RKIND) BGC_forcing%iceFraction(column) = 1.0_RKIND + endif + BGC_forcing%windSpeedSquared10m(column) = windSpeedSquared10m(iCell)*convertLengthSquaredMKStoCGS + BGC_forcing%atmCO2(column) = atmosphericCO2(iCell) + BGC_forcing%atmCO2_ALT_CO2(column) = atmosphericCO2_ALT_CO2(iCell) + BGC_forcing%surface_pH(column) = PH_PREV(iCell) + BGC_forcing%surface_pH_alt_co2(column) = PH_PREV_ALT_CO2(iCell) + BGC_forcing%surfaceDepth(column) = -1.0_RKIND*zMid(iLevelSurface,iCell) + BGC_forcing%SST(column) = activeTracers(indexTemperature,iLevelSurface,iCell) + BGC_forcing%SSS(column) = activeTracers(indexSalinity,iLevelSurface,iCell) + + BGC_input%BGC_tracers(1,column,BGC_indices%dic_ind) = ecosysTracers(ecosysIndices%dic_ind,1,iCell) + BGC_input%BGC_tracers(1,column,BGC_indices%dic_alt_co2_ind) = ecosysTracers(ecosysIndices%dic_alt_co2_ind,1,iCell) + BGC_input%BGC_tracers(1,column,BGC_indices%alk_ind) = ecosysTracers(ecosysIndices%alk_ind,1,iCell) + BGC_input%BGC_tracers(1,column,BGC_indices%po4_ind) = ecosysTracers(ecosysIndices%po4_ind,1,iCell) + BGC_input%BGC_tracers(1,column,BGC_indices%sio3_ind) = ecosysTracers(ecosysIndices%sio3_ind,1,iCell) + BGC_input%BGC_tracers(1,column,BGC_indices%o2_ind) = ecosysTracers(ecosysIndices%o2_ind,1,iCell) + +! NOTE pass in total Fe and mult by parm_Fe_bioavail inside the flux routine +! divide river Fe by bioavail since it is already the available to make it total + + BGC_forcing%depositionFlux(column,BGC_indices%no3_ind) = depositionFluxNO3(iCell)*convertLengthMKStoCGS + BGC_forcing%depositionFlux(column,BGC_indices%nh4_ind) = depositionFluxNH4(iCell)*convertLengthMKStoCGS + BGC_forcing%depositionFlux(column,BGC_indices%fe_ind) = IRON_FLUX_IN(iCell)*convertLengthMKStoCGS + + BGC_forcing%riverFlux(column,BGC_indices%no3_ind) = riverFluxNO3(iCell)*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%po4_ind) = riverFluxPO4(iCell)*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%don_ind) = riverFluxDON(iCell) * 0.9_BGC_r8*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%donr_ind) = riverFluxDON(iCell) * 0.1_BGC_r8*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%dop_ind) = riverFluxDOP(iCell) * 0.975_BGC_r8*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%dopr_ind) = riverFluxDOP(iCell) * 0.025_BGC_r8*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%sio3_ind) = riverFluxSiO3(iCell)*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%fe_ind) = riverFluxFe(iCell)*convertLengthMKStoCGS / parm_Fe_bioavail + BGC_forcing%riverFlux(column,BGC_indices%dic_ind) = riverFluxDIC(iCell)*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%dic_alt_co2_ind) = riverFluxDIC(iCell)*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%alk_ind) = riverFluxALK(iCell)*convertLengthMKStoCGS + BGC_forcing%riverFlux(column,BGC_indices%doc_ind) = riverFluxDOC(iCell)*convertLengthMKStoCGS + + if (config_use_ecosysTracers_sea_ice_coupling) then + +!maltrud sea ice fluxes are already correct MPAS units, so change to POP units + BGC_forcing%seaIceFlux(column,BGC_indices%no3_ind) = iceFluxNO3(iCell)*convertLengthMKStoCGS + BGC_forcing%seaIceFlux(column,BGC_indices%don_ind) = iceFluxDON(iCell)*convertLengthMKStoCGS +!maltrud sea ice BGC doesnt care about C or N--this is just a placeholder--so assume N units + BGC_forcing%seaIceFlux(column,BGC_indices%donr_ind) = iceFluxDOCr(iCell)*convertLengthMKStoCGS + BGC_forcing%seaIceFlux(column,BGC_indices%sio3_ind) = iceFluxSiO3(iCell)*convertLengthMKStoCGS + BGC_forcing%seaIceFlux(column,BGC_indices%nh4_ind) = iceFluxNH4(iCell)*convertLengthMKStoCGS + BGC_forcing%seaIceFlux(column,BGC_indices%fe_ind) = iceFluxFeDissolved(iCell)*convertLengthMKStoCGS / parm_Fe_bioavail + BGC_forcing%seaIceFlux(column,BGC_indices%dic_ind) = iceFluxDIC(iCell)*convertLengthMKStoCGS + BGC_forcing%seaIceFlux(column,BGC_indices%dic_alt_co2_ind) = iceFluxDIC(iCell)*convertLengthMKStoCGS + + BGC_forcing%seaIceFlux(column,BGC_indices%doc_ind) = iceFluxDOC(1,iCell)*convertLengthMKStoCGS + & + iceFluxDOC(2,iCell)*convertLengthMKStoCGS + + BGC_forcing%seaIceFlux(column,BGC_indices%diatC_ind) = iceFluxPhytoC(1,iCell)*convertLengthMKStoCGS + BGC_forcing%seaIceFlux(column,BGC_indices%spC_ind) = iceFluxPhytoC(2,iCell)*convertLengthMKStoCGS + BGC_forcing%seaIceFlux(column,BGC_indices%phaeoC_ind) = iceFluxPhytoC(3,iCell)*convertLengthMKStoCGS + + endif + + call BGC_SurfaceFluxes(BGC_indices, BGC_input, BGC_forcing, & + BGC_flux_diagnostic_fields, & + numColumnsMax, column) + + PH_PREV(iCell) = BGC_forcing%surface_pH(column) + PH_PREV_ALT_CO2(iCell) = BGC_forcing%surface_pH_alt_co2(column) + + ecosysSurfaceFlux(ecosysIndices%no3_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%no3_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%po4_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%po4_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%sio3_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%sio3_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%nh4_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%nh4_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%don_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%don_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%donr_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%donr_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%dop_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%dop_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%dopr_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%dopr_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%fe_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%fe_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%alk_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%alk_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%doc_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%doc_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%o2_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%o2_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%dic_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%dic_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%dic_alt_co2_ind,iCell) = & + BGC_forcing%netFlux(column,BGC_indices%dic_alt_co2_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%diatC_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%diatC_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%spC_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%spC_ind)*convertLengthCGStoMKS + ecosysSurfaceFlux(ecosysIndices%phaeoC_ind,iCell) = BGC_forcing%netFlux(column,BGC_indices%phaeoC_ind)*convertLengthCGStoMKS + +!explicitly set the rest to 0 + ecosysSurfaceFlux(ecosysIndices%dofe_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%zooC_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%spChl_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%spFe_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%spCaCO3_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%diatChl_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%diatFe_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%diatSi_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%diazC_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%diazChl_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%diazFe_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%phaeoChl_ind,iCell) = 0.0_RKIND + ecosysSurfaceFlux(ecosysIndices%phaeoFe_ind,iCell) = 0.0_RKIND + + CO2_gas_flux(iCell) = BGC_forcing%gasFlux(column,BGC_indices%dic_ind)*convertLengthCGStoMKS + CO2_alt_gas_flux(iCell) = BGC_forcing%gasFlux(column,BGC_indices%dic_alt_co2_ind)*convertLengthCGStoMKS + + if (config_ecosysTracers_diagnostic_fields_level2) then + ecosys_diag_pistonVel_O2(iCell) = BGC_flux_diagnostic_fields%pistonVel_O2(column) + ecosys_diag_pistonVel_CO2(iCell) = BGC_flux_diagnostic_fields%pistonVel_CO2(column) + ecosys_diag_Schmidt_O2(iCell) = BGC_flux_diagnostic_fields%SCHMIDT_O2(column) + ecosys_diag_Schmidt_CO2(iCell) = BGC_flux_diagnostic_fields%SCHMIDT_CO2(column) + ecosys_diag_O2_saturation(iCell) = BGC_flux_diagnostic_fields%O2SAT(column) + ecosys_diag_xkw(iCell) = BGC_flux_diagnostic_fields%xkw(column) + ecosys_diag_CO2star(iCell) = BGC_flux_diagnostic_fields%co2star(column) + ecosys_diag_dCO2star(iCell) = BGC_flux_diagnostic_fields%dco2star(column) + ecosys_diag_pCO2surface(iCell) = BGC_flux_diagnostic_fields%pco2surf(column) + ecosys_diag_dpCO2(iCell) = BGC_flux_diagnostic_fields%dpco2(column) + ecosys_diag_CO2star_ALT_CO2(iCell) = BGC_flux_diagnostic_fields%co2star_alt_co2(column) + ecosys_diag_dCO2star_ALT_CO2(iCell) = BGC_flux_diagnostic_fields%dco2star_alt_co2(column) + ecosys_diag_pCO2surface_ALT_CO2(iCell) = BGC_flux_diagnostic_fields%pco2surf_alt_co2(column) + ecosys_diag_dpCO2_ALT_CO2(iCell) = BGC_flux_diagnostic_fields%dpco2_alt_co2(column) + endif + + enddo ! iCell + + call mpas_timer_stop("ecosys surface flux") + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_ecosys_surface_flux_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_ecosys_init +! +!> \brief Initializes ocean surface restoring +!> \author Mathew Maltrud +!> \date 11/01/2015 +!> \details +!> This routine initializes fields required for tracer surface flux restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_ecosys_init(domain,err)!{{{ + +!NOTE: called from mpas_ocn_forward_mode.F + + type (domain_type), intent(inout) :: domain !< Input/Output: domain information + + integer, intent(out) :: err !< Output: error flag + + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: ecosysAuxiliary + type (mpas_pool_type), pointer :: ecosysMonthlyForcing + + ! three dimensional pointers + real (kind=RKIND), dimension(:,:,:), pointer :: & + ecosysTracers + +! input flux components in ecosysAuxiliary + real (kind=RKIND), dimension(:), pointer :: & + depositionFluxNO3, & + depositionFluxNH4, & + IRON_FLUX_IN, & + dust_FLUX_IN, & + riverFluxNO3, & + riverFluxPO4, & + riverFluxDON, & + riverFluxDOP, & + riverFluxSiO3, & + riverFluxFe, & + riverFluxDIC, & + riverFluxALK, & + riverFluxDOC + +! input flux components in ecosysMonthlyForcing + real (kind=RKIND), dimension(:), pointer :: & + depositionFluzNO3, & + depositionFluzNH4, & + IRON_FLUZ_IN, & + dust_FLUZ_IN, & + riverFluzNO3, & + riverFluzPO4, & + riverFluzDON, & + riverFluzDOP, & + riverFluzSiO3, & + riverFluzFe, & + riverFluzDIC, & + riverFluzALK, & + riverFluzDOC + + ! scalars + integer :: nTracers, numColumnsMax + + ! scalar pointers + integer, pointer :: nVertLevels, index_dummy + + character(len=strKIND) :: & + forcingIntervalMonthly, & + forcingReferenceTimeMonthly + + logical, pointer :: & + config_do_restart + + + ! + ! get tracers pools + ! + + err = 0 + + ! + ! Get tracer group so we can get the number of tracers in it + ! + + call mpas_pool_get_subpool(domain % blocklist % structs, 'state', statePool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_array(tracersPool, 'ecosysTracers', ecosysTracers, 1) + + if (associated(ecosysTracers)) then + + nTracers = size(ecosysTracers, dim=1) + if (BGC_tracer_cnt /= nTracers) then + err = 1 + return + endif + + ! + ! pull nVertLevels out of the mesh structure + ! + + call mpas_pool_get_dimension(domain % blocklist % dimensions, 'nVertLevels', nVertLevels) + +!----------------------------------------------------------------------- +! initialize ecosystem parameters +!----------------------------------------------------------------------- + + allocate( BGC_indices%short_name(BGC_tracer_cnt) ) + allocate( BGC_indices%long_name(BGC_tracer_cnt) ) + allocate( BGC_indices%units(BGC_tracer_cnt) ) + +! no need to allocate the above fields for ecosysIndices (?) + +!----------------------------------------------------------------------- +! sets most of BGC parameters +! sets namelist defaults +! sets autotroph sp_ind, diat_ind, diaz_ind, phaeo_ind (swang) +!----------------------------------------------------------------------- + + call BGC_parms_init(BGC_indices, autotrophs) + +! modify autotroph values here.... +! for example to change sp_kFe +! autotrophs(BGC_indices%sp_ind)%kFe = 0.05e-3_BGC_r8 + +!maltrud how to handle this? + T0_Kelvin_BGC = T0_Kelvin + + ! + ! for now only do 1 column at a time + ! + numColumnsMax = 1 + + BGC_indices%po4_ind = 1 + BGC_indices%no3_ind = 2 + BGC_indices%sio3_ind = 3 + BGC_indices%nh4_ind = 4 + BGC_indices%fe_ind = 5 + BGC_indices%o2_ind = 6 + BGC_indices%dic_ind = 7 + BGC_indices%dic_alt_co2_ind = 8 + BGC_indices%alk_ind = 9 + BGC_indices%doc_ind = 10 + BGC_indices%don_ind = 11 + BGC_indices%dofe_ind = 12 + BGC_indices%dop_ind = 13 + BGC_indices%dopr_ind = 14 + BGC_indices%donr_ind = 15 + BGC_indices%zooC_ind = 16 + BGC_indices%spChl_ind = 17 + BGC_indices%spC_ind = 18 + BGC_indices%spFe_ind = 19 + BGC_indices%spCaCO3_ind = 20 + BGC_indices%diatChl_ind = 21 + BGC_indices%diatC_ind = 22 + BGC_indices%diatFe_ind = 23 + BGC_indices%diatSi_ind = 24 + BGC_indices%diazChl_ind = 25 + BGC_indices%diazC_ind = 26 + BGC_indices%diazFe_ind = 27 + BGC_indices%phaeoChl_ind = 28 + BGC_indices%phaeoC_ind = 29 + BGC_indices%phaeoFe_ind = 30 + + call mpas_pool_get_dimension(tracersPool, 'index_PO4', index_dummy) + ecosysIndices%po4_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_NO3', index_dummy) + ecosysIndices%no3_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_SiO3', index_dummy) + ecosysIndices%sio3_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_NH4', index_dummy) + ecosysIndices%nh4_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_Fe', index_dummy) + ecosysIndices%fe_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_O2', index_dummy) + ecosysIndices%o2_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DIC', index_dummy) + ecosysIndices%dic_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DIC_ALT_CO2', index_dummy) + ecosysIndices%dic_alt_co2_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_ALK', index_dummy) + ecosysIndices%alk_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOC', index_dummy) + ecosysIndices%doc_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DON', index_dummy) + ecosysIndices%don_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOFe', index_dummy) + ecosysIndices%dofe_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOP', index_dummy) + ecosysIndices%dop_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DOPr', index_dummy) + ecosysIndices%dopr_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_DONr', index_dummy) + ecosysIndices%donr_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_zooC', index_dummy) + ecosysIndices%zooC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spChl', index_dummy) + ecosysIndices%spChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spC', index_dummy) + ecosysIndices%spC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spFe', index_dummy) + ecosysIndices%spFe_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_spCaCO3', index_dummy) + ecosysIndices%spCaCO3_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatChl', index_dummy) + ecosysIndices%diatChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatC', index_dummy) + ecosysIndices%diatC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatFe', index_dummy) + ecosysIndices%diatFe_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diatSi', index_dummy) + ecosysIndices%diatSi_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazChl', index_dummy) + ecosysIndices%diazChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazC', index_dummy) + ecosysIndices%diazC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_diazFe', index_dummy) + ecosysIndices%diazFe_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoChl', index_dummy) + ecosysIndices%phaeoChl_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoC', index_dummy) + ecosysIndices%phaeoC_ind = index_dummy + call mpas_pool_get_dimension(tracersPool, 'index_phaeoFe', index_dummy) + ecosysIndices%phaeoFe_ind = index_dummy + +! BGC_init sets short and long names, units in BGC_indices +! also sets autotroph indices within the autotroph derived type + + call BGC_init(BGC_indices, autotrophs) + +!NOTES: + +!also check short_name with mpas variable name + +!----------------------------------------------------------------------- +! allocate input, forcing, diagnostic arrays +!----------------------------------------------------------------------- + + allocate ( BGC_input%BGC_tracers(nVertLevels, numColumnsMax, BGC_tracer_cnt) ) + allocate ( BGC_input%PotentialTemperature(nVertLevels, numColumnsMax) ) + allocate ( BGC_input%Salinity(nVertLevels, numColumnsMax) ) + allocate ( BGC_input%cell_center_depth(nVertLevels, numColumnsMax) ) + allocate ( BGC_input%cell_thickness(nVertLevels, numColumnsMax) ) + allocate ( BGC_input%cell_bottom_depth(nVertLevels, numColumnsMax) ) + allocate ( BGC_input%number_of_active_levels(numColumnsMax) ) + allocate ( BGC_input%cell_latitude(numColumnsMax) ) + + allocate ( BGC_forcing%FESEDFLUX(nVertLevels, numColumnsMax) ) + allocate ( BGC_forcing%NUTR_RESTORE_RTAU(nVertLevels, numColumnsMax) ) + allocate ( BGC_forcing%NO3_CLIM(nVertLevels, numColumnsMax) ) + allocate ( BGC_forcing%PO4_CLIM(nVertLevels, numColumnsMax) ) + allocate ( BGC_forcing%SiO3_CLIM(nVertLevels, numColumnsMax) ) + + allocate ( BGC_forcing%dust_FLUX_IN(numColumnsMax) ) + allocate ( BGC_forcing%ShortWaveFlux_surface(numColumnsMax) ) + allocate ( BGC_forcing%surfacePressure(numColumnsMax) ) + allocate ( BGC_forcing%iceFraction(numColumnsMax) ) + allocate ( BGC_forcing%windSpeedSquared10m(numColumnsMax) ) + allocate ( BGC_forcing%atmCO2(numColumnsMax) ) + allocate ( BGC_forcing%atmCO2_ALT_CO2(numColumnsMax) ) + allocate ( BGC_forcing%surface_pH(numColumnsMax) ) + allocate ( BGC_forcing%surface_pH_alt_co2(numColumnsMax) ) + allocate ( BGC_forcing%surfaceDepth(numColumnsMax) ) + allocate ( BGC_forcing%SST(numColumnsMax) ) + allocate ( BGC_forcing%SSS(numColumnsMax) ) + + allocate ( BGC_forcing%depositionFlux(numColumnsMax, BGC_tracer_cnt) ) + allocate ( BGC_forcing%riverFlux(numColumnsMax, BGC_tracer_cnt) ) + allocate ( BGC_forcing%gasFlux(numColumnsMax, BGC_tracer_cnt) ) + allocate ( BGC_forcing%seaIceFlux(numColumnsMax, BGC_tracer_cnt) ) + allocate ( BGC_forcing%netFlux(numColumnsMax, BGC_tracer_cnt) ) + BGC_forcing%depositionFlux = 0.0_RKIND + BGC_forcing%riverFlux = 0.0_RKIND + BGC_forcing%gasFlux = 0.0_RKIND + BGC_forcing%seaIceFlux = 0.0_RKIND + BGC_forcing%netFlux = 0.0_RKIND + + allocate ( BGC_output%BGC_tendencies(nVertLevels, numColumnsMax, BGC_tracer_cnt) ) + allocate ( BGC_output%PH_PREV_3D(nVertLevels, numColumnsMax) ) + allocate ( BGC_output%PH_PREV_ALT_CO2_3D(nVertLevels, numColumnsMax) ) + + !--------------------------------------------------------------------------- + ! allocate flux diagnostic output fields + !--------------------------------------------------------------------------- + + allocate (BGC_flux_diagnostic_fields%pistonVel_O2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%pistonVel_CO2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%SCHMIDT_O2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%SCHMIDT_CO2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%O2SAT(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%xkw(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%co2star(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%dco2star(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%pco2surf(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%dpco2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%co2star_alt_co2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%dco2star_alt_co2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%pco2surf_alt_co2(numColumnsMax) ) + allocate (BGC_flux_diagnostic_fields%dpco2_alt_co2(numColumnsMax) ) + + !--------------------------------------------------------------------------- + ! allocate diagnostic output fields + !--------------------------------------------------------------------------- + + ! 3D stuff + allocate (BGC_diagnostic_fields%diag_tot_Nfix(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_O2_PRODUCTION(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_O2_CONSUMPTION(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_AOU(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_PO4_RESTORE(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_NO3_RESTORE(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_SiO3_RESTORE(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_PAR_avg(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_POC_FLUX_IN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_POC_PROD(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_POC_REMIN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_POC_ACCUM(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_CaCO3_FLUX_IN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_CaCO3_PROD(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_CaCO3_REMIN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_SiO2_FLUX_IN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_SiO2_PROD(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_SiO2_REMIN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_dust_FLUX_IN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_dust_REMIN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_P_iron_FLUX_IN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_P_iron_PROD(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_P_iron_REMIN(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_auto_graze_TOT(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_zoo_loss(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_photoC_TOT(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_photoC_NO3_TOT(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DOC_prod(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DOC_remin(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DON_prod(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DON_remin(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DOFe_prod(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DOFe_remin(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DOP_prod(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DOP_remin(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Fe_scavenge(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Fe_scavenge_rate(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_NITRIF(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DENITRIF(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DONr_remin(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_DOPr_remin(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_CO3(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_HCO3(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_H2CO3(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_pH_3D(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_CO3_ALT_CO2(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_HCO3_ALT_CO2(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_H2CO3_ALT_CO2(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_pH_3D_ALT_CO2(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_co3_sat_calc(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_co3_sat_arag(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_calcToSed(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_pocToSed(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_ponToSed(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_popToSed(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_bsiToSed(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_dustToSed(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_pfeToSed(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_SedDenitrif(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_OtherRemin(nVertLevels, numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_tot_CaCO3_form(nVertLevels, numColumnsMax) ) + +! 3D stuff for each autotroph + allocate (BGC_diagnostic_fields%diag_N_lim(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_P_lim(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_Fe_lim(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_SiO3_lim(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_light_lim(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_photoC(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_photoC_NO3(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_photoFe(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_photoNO3(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_photoNH4(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_DOP_uptake(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_PO4_uptake(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_auto_graze(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_auto_loss(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_auto_agg(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_bSi_form(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_CaCO3_form(nVertLevels, numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_Nfix(nVertLevels, numColumnsMax, autotroph_cnt) ) + +! 2D stuff for each autotroph + allocate (BGC_diagnostic_fields%diag_photoC_zint(numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_photoC_NO3_zint(numColumnsMax, autotroph_cnt) ) + allocate (BGC_diagnostic_fields%diag_CaCO3_form_zint(numColumnsMax, autotroph_cnt) ) + +! 2D vertical integrals for photoC + allocate (BGC_diagnostic_fields%diag_photoC_TOT_zint(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_photoC_NO3_TOT_zint(numColumnsMax) ) + +! 2D vertical integrals for nutrients + allocate (BGC_diagnostic_fields%diag_Chl_TOT_zint_100m(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_Ctot(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_100m_Ctot(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_Ntot(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_100m_Ntot(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_Ptot(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_100m_Ptot(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_Sitot(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_Jint_100m_Sitot(numColumnsMax) ) + +! 2D stuff + allocate (BGC_diagnostic_fields%diag_tot_bSi_form(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_tot_CaCO3_form_zint(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_zsatcalc(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_zsatarag(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_O2_ZMIN(numColumnsMax) ) + allocate (BGC_diagnostic_fields%diag_O2_ZMIN_DEPTH(numColumnsMax) ) + +! initialize monthly forcing to be read from file + + forcingIntervalMonthly = "0000-01-00_00:00:00" + forcingReferenceTimeMonthly = "0000-01-15_00:00:00" + + call MPAS_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) + + call MPAS_forcing_init_group( forcingGroupHead, & + "ecosysMonthlyClimatology", & + domain, & + '0000-01-01_00:00:00', & + '0000-01-01_00:00:00', & + '0001-00-00_00:00:00', & + config_do_restart) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + + call mpas_pool_get_array(ecosysAuxiliary, 'depositionFluxNO3', depositionFluxNO3) + call mpas_pool_get_array(ecosysAuxiliary, 'depositionFluxNH4', depositionFluxNH4) + call mpas_pool_get_array(ecosysAuxiliary, 'IRON_FLUX_IN', IRON_FLUX_IN) + call mpas_pool_get_array(ecosysAuxiliary, 'dust_FLUX_IN', dust_FLUX_IN) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxNO3', riverFluxNO3) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxPO4', riverFluxPO4) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDON', riverFluxDON) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDOP', riverFluxDOP) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxSiO3', riverFluxSiO3) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxFe', riverFluxFe) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDIC', riverFluxDIC) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxALK', riverFluxALK) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDOC', riverFluxDOC) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'ecosysMonthlyForcing', ecosysMonthlyForcing) + + call mpas_pool_get_array(ecosysMonthlyForcing, 'depositionFluzNO3', depositionFluzNO3) + call mpas_pool_get_array(ecosysMonthlyForcing, 'depositionFluzNH4', depositionFluzNH4) + call mpas_pool_get_array(ecosysMonthlyForcing, 'IRON_FLUZ_IN', IRON_FLUZ_IN) + call mpas_pool_get_array(ecosysMonthlyForcing, 'dust_FLUZ_IN', dust_FLUZ_IN) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzNO3', riverFluzNO3) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzPO4', riverFluzPO4) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDON', riverFluzDON) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDOP', riverFluzDOP) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzSiO3', riverFluzSiO3) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzFe', riverFluzFe) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDIC', riverFluzDIC) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzALK', riverFluzALK) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDOC', riverFluzDOC) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'depositionFluzNO3', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'depositionFluzNO3', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'depositionFluzNH4', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'depositionFluzNH4', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'IRON_FLUZ_IN', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'IRON_FLUZ_IN', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'dust_FLUZ_IN', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'dust_FLUZ_IN', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzNO3', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzNO3', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzPO4', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzPO4', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzDON', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzDON', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzDOP', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzDOP', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzSiO3', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzSiO3', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzFe', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzFe', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzDIC', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzDIC', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzALK', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzALK', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'ecosysMonthlyClimatology', & + 'riverFluzDOC', & + 'ecosys_monthly_flux', & + 'ecosysMonthlyForcing', & + 'riverFluzDOC', & + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field_data( forcingGroupHead, & + 'ecosysMonthlyClimatology', & + domain % streamManager, & + config_do_restart, & + .false.) + + end if ! associated(ecosysTracers) + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_ecosys_init!}}} + +!*********************************************************************** + +!*********************************************************************** +! +! routine get_ecosysData +! +!> \brief retrieve data needed to compute ecosys deposition and runoff inputs +!> \author Mathew Maltrud +!> \date 03/07/16 +!> \details +!> This routine calls mpas_forcing routines to acquire needed ecosys forcing data and interpolates +!> between time levels. directly copied from ocn_get_shortWaveData. +! +!----------------------------------------------------------------------- + + subroutine ocn_get_ecosysData( streamManager, & + domain, & + simulationClock, & + firstTimeStep) !{{{ + + type (MPAS_streamManager_type), intent(inout) :: streamManager + + type (domain_type) :: domain + type (MPAS_timeInterval_type) :: timeStepEco + type (MPAS_clock_type) :: simulationClock + + logical, intent(in) :: firstTimeStep + character(len=strKind), pointer :: config_dt + real(kind=RKIND) :: dt + + type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: ecosysAuxiliary + type (mpas_pool_type), pointer :: ecosysMonthlyForcing + + real (kind=RKIND), dimension(:), pointer :: & + depositionFluxNO3, & + depositionFluxNH4, & + IRON_FLUX_IN, & + dust_FLUX_IN, & + riverFluxNO3, & + riverFluxPO4, & + riverFluxDON, & + riverFluxDOP, & + riverFluxSiO3, & + riverFluxFe, & + riverFluxDIC, & + riverFluxALK, & + riverFluxDOC + +! input flux components in ecosysMonthlyForcing + real (kind=RKIND), dimension(:), pointer :: & + depositionFluzNO3, & + depositionFluzNH4, & + IRON_FLUZ_IN, & + dust_FLUZ_IN, & + riverFluzNO3, & + riverFluzPO4, & + riverFluzDON, & + riverFluzDOP, & + riverFluzSiO3, & + riverFluzFe, & + riverFluzDIC, & + riverFluzALK, & + riverFluzDOC + + integer, pointer :: nCells + integer :: iCell + + logical, pointer :: config_do_restart + + call MPAS_pool_get_config(domain%configs, 'config_dt', config_dt) + call MPAS_pool_get_config(domain%configs, 'config_do_restart', config_do_restart) + + call mpas_set_timeInterval(timeStepEco,timeString=config_dt) + call mpas_get_timeInterval(timeStepEco,dt=dt) + +!maltrud debug + if (firstTimeStep .and. config_do_restart) then + call MPAS_forcing_get_forcing(forcingGroupHead, & + 'ecosysMonthlyClimatology', streamManager, 0.0_RKIND) + else + call MPAS_forcing_get_forcing(forcingGroupHead, & + 'ecosysMonthlyClimatology', streamManager, dt) + endif + + call mpas_pool_get_subpool(domain % blocklist % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(domain % blocklist % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(forcingPool, 'ecosysAuxiliary', ecosysAuxiliary) + call mpas_pool_get_subpool(domain % blocklist % structs, 'ecosysMonthlyForcing', ecosysMonthlyForcing) + + call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + + call mpas_pool_get_array(ecosysAuxiliary, 'depositionFluxNO3', depositionFluxNO3) + call mpas_pool_get_array(ecosysAuxiliary, 'depositionFluxNH4', depositionFluxNH4) + call mpas_pool_get_array(ecosysAuxiliary, 'IRON_FLUX_IN', IRON_FLUX_IN) + call mpas_pool_get_array(ecosysAuxiliary, 'dust_FLUX_IN', dust_FLUX_IN) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxNO3', riverFluxNO3) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxPO4', riverFluxPO4) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDON', riverFluxDON) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDOP', riverFluxDOP) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxSiO3', riverFluxSiO3) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxFe', riverFluxFe) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDIC', riverFluxDIC) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxALK', riverFluxALK) + call mpas_pool_get_array(ecosysAuxiliary, 'riverFluxDOC', riverFluxDOC) + + call mpas_pool_get_array(ecosysMonthlyForcing, 'depositionFluzNO3', depositionFluzNO3) + call mpas_pool_get_array(ecosysMonthlyForcing, 'depositionFluzNH4', depositionFluzNH4) + call mpas_pool_get_array(ecosysMonthlyForcing, 'IRON_FLUZ_IN', IRON_FLUZ_IN) + call mpas_pool_get_array(ecosysMonthlyForcing, 'dust_FLUZ_IN', dust_FLUZ_IN) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzNO3', riverFluzNO3) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzPO4', riverFluzPO4) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDON', riverFluzDON) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDOP', riverFluzDOP) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzSiO3', riverFluzSiO3) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzFe', riverFluzFe) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDIC', riverFluzDIC) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzALK', riverFluzALK) + call mpas_pool_get_array(ecosysMonthlyForcing, 'riverFluzDOC', riverFluzDOC) + + do iCell = 1, nCells + depositionFluxNO3(iCell) = depositionFluzNO3(iCell) + depositionFluxNH4(iCell) = depositionFluzNH4(iCell) + IRON_FLUX_IN(iCell) = IRON_FLUZ_IN(iCell) + dust_FLUX_IN(iCell) = dust_FLUZ_IN(iCell) + riverFluxNO3(iCell) = riverFluzNO3(iCell) + riverFluxPO4(iCell) = riverFluzPO4(iCell) + riverFluxDON(iCell) = riverFluzDON(iCell) + riverFluxDOP(iCell) = riverFluzDOP(iCell) + riverFluxSiO3(iCell) = riverFluzSiO3(iCell) + riverFluxFe(iCell) = riverFluzFe(iCell) + riverFluxDIC(iCell) = riverFluzDIC(iCell) + riverFluxALK(iCell) = riverFluzALK(iCell) + riverFluxDOC(iCell) = riverFluzDOC(iCell) + enddo + + end subroutine ocn_get_ecosysData!}}} + +!*********************************************************************** +! +! routine ocn_ecosys_forcing_write_restart +! +!> \brief writes restart timestamp for ecosys data to be read in on future restart +!> \author Mathew Maltrud +!> \date 03/07/2016 + +! +!----------------------------------------------------------------------- + + subroutine ocn_ecosys_forcing_write_restart(domain)!{{{ + + type(domain_type) :: domain + + call MPAS_forcing_write_restart_times(forcingGroupHead) + + end subroutine ocn_ecosys_forcing_write_restart!}}} + +end module ocn_tracer_ecosys + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_exponential_decay.F b/src/core_ocean/shared/mpas_ocn_tracer_exponential_decay.F new file mode 100644 index 0000000000..2c31edc904 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_exponential_decay.F @@ -0,0 +1,166 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_exponential_decay +! +!> \brief MPAS ocean exponential decay +!> \author Todd Ringler +!> \date 06/08/2015 +!> \details +!> This module contains routines for computing tracer forcing due to exponential decay +! +!----------------------------------------------------------------------- + +module ocn_tracer_exponential_decay + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_exponential_decay_compute, & + ocn_tracer_exponential_decay_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_exponential_decay_compute +! +!> \brief computes a tracer tendency due to exponential decay +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine computes a tracer tendency due to exponential decay +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_exponential_decay_compute(nTracers, nCellsSolve, maxLevelCell, layerThickness, tracers, & !{{{ + tracersExponentialDecayRate, tracer_tend, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! one dimensional arrays + integer, dimension(:), intent(in) :: & + maxLevelCell + + real (kind=RKIND), dimension(:), intent(in) :: & + tracersExponentialDecayRate + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThickness + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + tracers + + ! scalars + integer, intent(in) :: nTracers, nCellsSolve + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tracer_tend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, iLevel, iTracer + + err = 0 + + !$omp do schedule(runtime) private(iLevel, iTracer) + do iCell=1,nCellsSolve + do iLevel=1,maxLevelCell(iCell) + do iTracer=1,nTracers + tracer_tend(iTracer,iLevel,iCell) = tracer_tend(iTracer,iLevel,iCell) & + - ( layerThickness(iLevel,iCell) & + * tracers(iTracer,iLevel,iCell) & + * exp(-tracersExponentialDecayRate(iTracer)) ) + enddo + enddo + enddo + !$omp end do + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_exponential_decay_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_exponential_decay_init +! +!> \brief Initializes ocean surface restoring +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine initializes fields required for tracer surface flux restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_exponential_decay_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + + err = 0 + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_exponential_decay_init!}}} + +!*********************************************************************** + +end module ocn_tracer_exponential_decay + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_hmix.F b/src/core_ocean/shared/mpas_ocn_tracer_hmix.F index a7cfbd8b88..436bb0507c 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_hmix.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_hmix.F @@ -13,8 +13,8 @@ !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 !> \details -!> This module contains the main driver routine for computing -!> horizontal mixing tendencies. +!> This module contains the main driver routine for computing +!> horizontal mixing tendencies. !> !> It provides an init and a tend function. Each are described below. ! @@ -22,12 +22,13 @@ module ocn_tracer_hmix + use mpas_timer use mpas_derived_types use mpas_pool_routines - use mpas_timer use ocn_constants use ocn_tracer_hmix_del2 use ocn_tracer_hmix_del4 + use ocn_tracer_hmix_redi implicit none private @@ -67,7 +68,7 @@ module ocn_tracer_hmix !> \brief Computes tendency term for horizontal tracer mixing !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for tracer !> based on current state and user choices of mixing parameterization. !> Multiple parameterizations may be chosen and added together. These @@ -77,7 +78,7 @@ module ocn_tracer_hmix ! !----------------------------------------------------------------------- - subroutine ocn_tracer_hmix_tend(meshPool, scratchPool, layerThickness, layerThicknessEdge, zMid, tracers, & + subroutine ocn_tracer_hmix_tend(meshPool, scratchPool, layerThicknessEdge, zMid, tracers, & relativeSlopeTopOfEdge, relativeSlopeTapering, relativeSlopeTaperingCell, tend, err)!{{{ @@ -90,7 +91,6 @@ subroutine ocn_tracer_hmix_tend(meshPool, scratchPool, layerThickness, layerThic type (mpas_pool_type), intent(in) :: scratchPool !< Input: Scratch information real (kind=RKIND), dimension(:,:), intent(in) :: & - layerThickness, &!< Input: thickness at cell centers layerThicknessEdge, &!< Input: thickness at edge zMid !< Input: Z coordinate at the center of a cell @@ -130,22 +130,22 @@ subroutine ocn_tracer_hmix_tend(meshPool, scratchPool, layerThickness, layerThic !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- if(.not.tracerHmixOn) return - call mpas_timer_start("del2") - call ocn_tracer_hmix_del2_tend(meshPool, scratchPool, layerThickness, layerThicknessEdge, zMid, tracers, & + call mpas_timer_start("tracer hmix") + + call ocn_tracer_hmix_del2_tend(meshPool, layerThicknessEdge, tracers, tend, err1) + call ocn_tracer_hmix_del4_tend(meshPool, scratchPool, layerThicknessEdge, tracers, tend, err2) + call ocn_tracer_hmix_redi_tend(meshPool, scratchPool, layerThicknessEdge, zMid, tracers, & relativeSlopeTopOfEdge, relativeSlopeTapering, relativeSlopeTaperingCell, tend, err1) - call mpas_timer_stop("del2") - call mpas_timer_start("del4") - call ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend, err2) - call mpas_timer_stop("del4") err = ior(err1, err2) + call mpas_timer_stop("tracer hmix") !-------------------------------------------------------------------- @@ -158,11 +158,11 @@ end subroutine ocn_tracer_hmix_tend!}}} !> \brief Initializes ocean tracer horizontal mixing quantities !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> horizontal velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> horizontal velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_tracer_hmix_del2.F b/src/core_ocean/shared/mpas_ocn_tracer_hmix_del2.F index aca1d227a4..558b778ee2 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_hmix_del2.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_hmix_del2.F @@ -13,8 +13,8 @@ !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 !> \details -!> This module contains the main driver routine for computing -!> horizontal mixing tendencies. +!> This module contains the main driver routine for computing +!> horizontal mixing tendencies. !> !> It provides an init and a tend function. Each are described below. ! @@ -22,8 +22,10 @@ module ocn_tracer_hmix_del2 + use mpas_timer use mpas_derived_types use mpas_pool_routines + use mpas_threading use ocn_constants @@ -53,12 +55,7 @@ module ocn_tracer_hmix_del2 !-------------------------------------------------------------------- logical :: del2On - logical, pointer :: config_use_standardGM - logical, pointer :: config_disable_redi_horizontal_term1 - logical, pointer :: config_disable_redi_horizontal_term2 - logical, pointer :: config_disable_redi_horizontal_term3 real (kind=RKIND) :: eddyDiff2 - real (kind=RKIND), pointer :: config_Redi_kappa !*********************************************************************** @@ -72,16 +69,13 @@ module ocn_tracer_hmix_del2 !> \brief Computes Laplacian tendency term for horizontal tracer mixing !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for tracers !> based on current state using a Laplacian parameterization. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_hmix_del2_tend(meshPool, scratchPool, layerThickness, layerThicknessEdge, zMid, tracers, & - relativeSlopeTopOfEdge, relativeSlopeTapering, relativeSlopeTaperingCell, tend, err)!{{{ - - + subroutine ocn_tracer_hmix_del2_tend(meshPool, layerThicknessEdge, tracers, tend, err)!{{{ !----------------------------------------------------------------- ! ! input variables @@ -89,15 +83,9 @@ subroutine ocn_tracer_hmix_del2_tend(meshPool, scratchPool, layerThickness, laye !----------------------------------------------------------------- type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information - type (mpas_pool_type), intent(in) :: scratchPool !< Input: Scratch information real (kind=RKIND), dimension(:,:), intent(in) :: & - layerThickness, &!< Input: thickness at cell centers - layerThicknessEdge, &!< Input: thickness at edge - zMid, &!< Input: Z coordinate at the center of a cell - relativeSlopeTopOfEdge, &!< Input: slope of coordinate relative to neutral surface at edges - relativeSlopeTapering, &!< Input: tapering of slope of coordinate relative to neutral surface at edges - relativeSlopeTaperingCell !< Input: tapering of slope of coordinate relative to neutral surface at cells + layerThicknessEdge !< Input: thickness at edges real (kind=RKIND), dimension(:,:,:), intent(in) :: & tracers !< Input: tracer quantities @@ -126,37 +114,29 @@ subroutine ocn_tracer_hmix_del2_tend(meshPool, scratchPool, layerThickness, laye !----------------------------------------------------------------- integer :: iCell, iEdge, cell1, cell2 - integer :: i, k, iTracer, num_tracers - integer, pointer :: nCells, nVertLevels, nEdges + integer :: i, k, iTracer, num_tracers, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray - integer, dimension(:,:), allocatable :: boundaryMask - - integer, dimension(:), pointer :: maxLevelEdgeTop, nEdgesOnCell, maxLevelCell + integer, dimension(:), pointer :: maxLevelEdgeTop, nEdgesOnCell integer, dimension(:,:), pointer :: cellsOnEdge, edgesOnCell, edgeSignOnCell - real (kind=RKIND) :: invAreaCell1, invAreaCell2, invAreaCell, areaEdge - real (kind=RKIND) :: tracer_turb_flux, flux, s_tmp, r_tmp, h1, h2, s_tmpU, s_tmpD + real (kind=RKIND) :: invAreaCell + real (kind=RKIND) :: tracer_turb_flux, flux, r_tmp real (kind=RKIND), dimension(:), pointer :: areaCell, dvEdge, dcEdge real (kind=RKIND), dimension(:), pointer :: meshScalingDel2 - real (kind=RKIND), dimension(:,:), pointer :: gradTracerEdge, gradTracerTopOfEdge, gradHTracerSlopedTopOfCell, & - dTracerdZTopOfCell, dTracerdZTopOfEdge, areaCellSum - - type (field2DReal), pointer :: gradTracerEdgeField, gradTracerTopOfEdgeField, gradHTracerSlopedTopOfCellField, dTracerdZTopOfCellField, dTracerdZTopOfEdgeField, & - areaCellSumField - err = 0 if (.not.del2On) return - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_timer_start("tracer del2") + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) num_tracers = size(tracers, dim=1) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) - call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) call mpas_pool_get_array(meshPool, 'areaCell', areaCell) call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) @@ -167,18 +147,21 @@ subroutine ocn_tracer_hmix_del2_tend(meshPool, scratchPool, layerThickness, laye call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + nCells = nCellsArray( 1 ) + ! ! compute a boundary mask to enforce insulating boundary conditions in the horizontal ! + !$omp do schedule(runtime) private(invAreaCell, i, iEdge, cell1, cell2, r_tmp, k, iTracer, tracer_turb_flux, flux) do iCell = 1, nCells - invAreaCell = 1.0 / areaCell(iCell) + invAreaCell = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) r_tmp = meshScalingDel2(iEdge) * eddyDiff2 * dvEdge(iEdge) / dcEdge(iEdge) - + do k = 1, maxLevelEdgeTop(iEdge) do iTracer = 1, num_tracers ! \kappa_2 \nabla \phi on edge @@ -193,192 +176,9 @@ subroutine ocn_tracer_hmix_del2_tend(meshPool, scratchPool, layerThickness, laye end do end do + !$omp end do - ! - ! COMPUTE the extra terms arising due to mismatch between the constant coordinate surfaces and the - ! isopycnal surfaces. - ! - ! mrp note: Redi diffusion should be put in a separate subroutine - if (config_use_standardGM) then - - call mpas_pool_get_field(scratchPool, 'gradTracerEdge', gradTracerEdgeField) - call mpas_pool_get_field(scratchPool, 'gradTracerTopOfEdge', gradTracerTopOfEdgeField) - call mpas_pool_get_field(scratchPool, 'gradHTracerSlopedTopOfCell', gradHTracerSlopedTopOfCellField) - call mpas_pool_get_field(scratchPool, 'dTracerdZTopOfCell', dTracerdZTopOfCellField) - call mpas_pool_get_field(scratchPool, 'dTracerdZTopOfEdge', dTracerdZTopOfEdgeField) - call mpas_pool_get_field(scratchPool, 'areaCellSum', areaCellSumField) - - call mpas_allocate_scratch_field(gradTracerEdgeField, .true.) - call mpas_allocate_scratch_field(gradTracerTopOfEdgeField, .true.) - call mpas_allocate_scratch_field(gradHTracerSlopedTopOfCellField, .true.) - call mpas_allocate_scratch_field(dTracerdZTopOfCellField, .true.) - call mpas_allocate_scratch_field(dTracerdZTopOfEdgeField, .true.) - call mpas_allocate_scratch_field(areaCellSumField, .True.) - - gradTracerEdge => gradTracerEdgeField % array - gradTracerTopOfEdge => gradTracerTopOfEdgeField % array - gradHTracerSlopedTopOfCell => gradHTracerSlopedTopOfCellField % array - dTracerdZTopOfCell => dTracerdZTopOfCellField % array - dTracerdZTopOfEdge => dTracerdZTopOfEdgeField % array - areaCellSum => areaCellSumField % array - - gradTracerEdge = 0.0 - gradTracerTopOfEdge = 0.0 - gradHTracerSlopedTopOfCell = 0.0 - dTracerdZTopOfCell = 0.0 - dTracerdZTopOfEdge = 0.0 - - ! this is the "standard" del2 term, but forced to use config_redi_kappa - if(.not.config_disable_redi_horizontal_term1) then - do iCell = 1, nCells - invAreaCell = 1.0 / areaCell(iCell) - do i = 1, nEdgesOnCell(iCell) - iEdge = edgesOnCell(i, iCell) - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - - r_tmp = config_redi_kappa * dvEdge(iEdge) / dcEdge(iEdge) - - do k = 1, maxLevelEdgeTop(iEdge) - - ! this is the tapering of config_redi_kappa where abs(slope) > config_max_relative_slope - s_tmp = relativeSlopeTapering(k,iEdge) - - do iTracer = 1, num_tracers - ! \kappa_2 \nabla \phi on edge - tracer_turb_flux = tracers(iTracer, k, cell2) - tracers(iTracer, k, cell1) - - ! div(h \kappa_2 \nabla \phi) at cell center - flux = layerThicknessEdge(k, iEdge) * tracer_turb_flux * r_tmp * s_tmp - - tend(iTracer, k, iCell) = tend(iTracer, k, iCell) - edgeSignOnCell(i, iCell) * flux * invAreaCell - end do - end do - - end do - end do - endif - - ! Compute vertical derivative of tracers at cell center and top of layer - do iTracer = 1, num_tracers - - do iCell = 1, nCells - do k = 2, maxLevelCell(iCell) - dTracerdZTopOfCell(k,iCell) = (tracers(iTracer,k-1,iCell) - tracers(iTracer,k,iCell)) / (zMid(k-1,iCell) - zMid(k,iCell)) - end do - - ! Approximation of dTracerdZTopOfCell on the top and bottom interfaces through the idea of having - ! ghost cells above the top and below the bottom layers of the same depths and tracer density. - ! Essentially, this enforces the boundary condition (d tracer)/dz = 0 at the top and bottom. - dTracerdZTopOfCell(1,iCell) = 0.0 - dTracerdZTopOfCell(maxLevelCell(iCell)+1,iCell) = 0.0 - end do - - ! Compute tracer gradient (gradTracerEdge) along the constant coordinate surface. - ! The computed variables lives at edge and mid-layer depth - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - - do k=1,maxLevelEdgeTop(iEdge) - gradTracerEdge(k,iEdge) = (tracers(iTracer,k,cell2) - tracers(iTracer,k,cell1)) / dcEdge(iEdge) - end do - end do - - ! Interpolate dTracerdZTopOfCell to edge and top of layer - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - do k = 1, maxLevelEdgeTop(iEdge) - dTracerdZTopOfEdge(k,iEdge) = 0.5 * (dTracerdZTopOfCell(k,cell1) + dTracerdZTopOfCell(k,cell2)) - end do - dTracerdZTopOfEdge(maxLevelEdgeTop(iEdge)+1,iEdge) = 0.0 - end do - - ! Interpolate gradTracerEdge to edge and top of layer - do iEdge = 1, nEdges - do k = 2, maxLevelEdgeTop(iEdge) - h1 = layerThicknessEdge(k-1,iEdge) - h2 = layerThicknessEdge(k,iEdge) - - ! Using second-order interpolation below - gradTracerTopOfEdge(k,iEdge) = (h2 * gradTracerEdge(k-1,iEdge) + h1 * gradTracerEdge(k,iEdge)) / (h1 + h2) - end do - - ! Approximation of values on the top and bottom interfaces through the idea of having ghost cells above - ! the top and below the bottom layers of the same depths and tracer concentration. - gradTracerTopOfEdge(1,iEdge) = gradTracerEdge(1,iEdge) - gradTracerTopOfEdge(maxLevelEdgeTop(iEdge)+1,iEdge) = gradTracerEdge(max(maxLevelEdgeTop(iEdge),1),iEdge) - end do - - ! Compute \nabla\cdot(relativeSlope d\phi/dz) - if(.not.config_disable_redi_horizontal_term2) then - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - invAreaCell1 = 1./areaCell(cell1) - invAreaCell2 = 1./areaCell(cell2) - - do k = 1, maxLevelEdgeTop(iEdge) - s_tmpU = relativeSlopeTapering(k , iEdge) * relativeSlopeTopOfEdge(k,iEdge)*dTracerdZTopOfEdge(k,iEdge) - s_tmpD = relativeSlopeTapering(k+1, iEdge) * relativeSlopeTopOfEdge(k+1,iEdge)*dTracerdZTopOfEdge(k+1,iEdge) - flux = 0.5*dvEdge(iEdge)*(s_tmpU + s_tmpD) - flux = flux * layerThicknessEdge(k, iEdge) - tend(iTracer,k,cell1) = tend(iTracer,k,cell1) + config_Redi_kappa * flux * invAreaCell1 - tend(iTracer,k,cell2) = tend(iTracer,k,cell2) - config_Redi_kappa * flux * invAreaCell2 - end do - - end do - endif - - ! Compute dz * d(relativeSlope\cdot\nabla\phi)/dz (so the dz cancel out) - gradHTracerSlopedTopOfCell = 0.0 - - ! Compute relativeSlope\cdot\nabla\phi (variable gradHTracerSlopedTopOfCell) at non-boundary edges - areaCellSum = 1.0e-34 - do iEdge = 1, nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - ! contribution of cell area from this edge: - areaEdge = 0.25 * dcEdge(iEdge) * dvEdge(iEdge) - - do k = 1, maxLevelEdgeTop(iEdge) - r_tmp = 2.0 * areaEdge * relativeSlopeTopOfEdge(k,iEdge) * gradTracerTopOfEdge(k,iEdge) - gradHTracerSlopedTopOfCell(k,cell1) = gradHTracerSlopedTopOfCell(k,cell1) + r_tmp - gradHTracerSlopedTopOfCell(k,cell2) = gradHTracerSlopedTopOfCell(k,cell2) + r_tmp - - areaCellSum(k,cell1) = areaCellSum(k,cell1) + areaEdge - areaCellSum(k,cell2) = areaCellSum(k,cell2) + areaEdge - - end do - end do - do iCell=1,nCells - do k = 1, maxLevelCell(iCell) - gradHTracerSlopedTopOfCell(k,iCell) = gradHTracerSlopedTopOfCell(k,iCell)/areaCellSum(k,iCell) - end do - end do - - if(.not.config_disable_redi_horizontal_term3) then - do iCell = 1, nCells - ! impose no-flux boundary conditions at top and bottom of column - gradHTracerSlopedTopOfCell(1,iCell) = 0.0 - gradHTracerSlopedTopOfCell(maxLevelCell(iCell)+1,iCell) = 0.0 - do k = 1, maxLevelCell(iCell) - s_tmp = relativeSlopeTaperingCell(k,iCell) - tend(iTracer,k,iCell) = tend(iTracer,k,iCell) + s_tmp * config_Redi_kappa * (gradHTracerSlopedTopOfCell(k,iCell) - gradHTracerSlopedTopOfCell(k+1,iCell)) - end do - end do - endif - - end do ! iTracer - - call mpas_deallocate_scratch_field(gradTracerEdgeField, .true.) - call mpas_deallocate_scratch_field(gradTracerTopOfEdgeField, .true.) - call mpas_deallocate_scratch_field(gradHTracerSlopedTopOfCellField, .true.) - call mpas_deallocate_scratch_field(dTracerdZTopOfCellField, .true.) - call mpas_deallocate_scratch_field(dTracerdZTopOfEdgeField, .true.) - - end if ! config_use_standardGM + call mpas_timer_stop("tracer del2") !-------------------------------------------------------------------- @@ -391,9 +191,9 @@ end subroutine ocn_tracer_hmix_del2_tend!}}} !> \brief Initializes ocean tracer horizontal mixing quantities !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> Laplacian horizontal velocity mixing in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> Laplacian horizontal velocity mixing in the ocean. ! !----------------------------------------------------------------------- @@ -416,28 +216,16 @@ subroutine ocn_tracer_hmix_del2_init(err)!{{{ call mpas_pool_get_config(ocnConfigs, 'config_use_tracer_del2', config_use_tracer_del2) call mpas_pool_get_config(ocnConfigs, 'config_tracer_del2', config_tracer_del2) - call mpas_pool_get_config(ocnConfigs, 'config_use_standardGM',config_use_standardGM) - call mpas_pool_get_config(ocnConfigs, 'config_Redi_kappa',config_Redi_kappa) - call mpas_pool_get_config(ocnConfigs, 'config_disable_redi_horizontal_term1',config_disable_redi_horizontal_term1) - call mpas_pool_get_config(ocnConfigs, 'config_disable_redi_horizontal_term2',config_disable_redi_horizontal_term2) - call mpas_pool_get_config(ocnConfigs, 'config_disable_redi_horizontal_term3',config_disable_redi_horizontal_term3) del2on = .false. if ( config_use_tracer_del2 ) then - if ( config_tracer_del2 > 0.0 ) then - del2On = .true. - eddyDiff2 = config_tracer_del2 - endif + if ( config_tracer_del2 > 0.0_RKIND ) then + del2On = .true. + eddyDiff2 = config_tracer_del2 + endif endif - if ( config_use_standardGM ) then - if ( config_Redi_kappa > 0.0 ) then - del2On = .true. - endif - endif - - !-------------------------------------------------------------------- end subroutine ocn_tracer_hmix_del2_init!}}} diff --git a/src/core_ocean/shared/mpas_ocn_tracer_hmix_del4.F b/src/core_ocean/shared/mpas_ocn_tracer_hmix_del4.F index 7db07db729..db9c02dcfc 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_hmix_del4.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_hmix_del4.F @@ -13,8 +13,8 @@ !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 !> \details -!> This module contains the main driver routine for computing -!> horizontal mixing tendencies. +!> This module contains the main driver routine for computing +!> horizontal mixing tendencies. !> !> It provides an init and a tend function. Each are described below. ! @@ -22,8 +22,10 @@ module ocn_tracer_hmix_del4 + use mpas_timer use mpas_derived_types use mpas_pool_routines + use mpas_threading use ocn_constants implicit none @@ -67,13 +69,13 @@ module ocn_tracer_hmix_del4 !> \brief Computes biharmonic tendency term for horizontal tracer mixing !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for tracers !> based on current state using a biharmonic parameterization. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend, err)!{{{ + subroutine ocn_tracer_hmix_del4_tend(meshPool, scratchPool, layerThicknessEdge, tracers, tend, err)!{{{ !----------------------------------------------------------------- ! @@ -87,6 +89,8 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information + type (mpas_pool_type), intent(in) :: scratchPool !< Input: scratch variables + real (kind=RKIND), dimension(:,:,:), intent(in) :: & tracers !< Input: tracer quantities @@ -113,16 +117,19 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend ! !----------------------------------------------------------------- - integer :: iEdge, num_tracers + integer :: iEdge, num_tracers, nCells, nEdges integer :: iTracer, k, iCell, cell1, cell2, i - integer, pointer :: nEdges, nVertLevels, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray, nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop, maxLevelCell, nEdgesOnCell integer, dimension(:,:), pointer :: edgeMask, cellsOnEdge, edgesOnCell, edgeSignOnCell real (kind=RKIND) :: invAreaCell1, invAreaCell2, tracer_turb_flux, flux, invdcEdge, r_tmp1, r_tmp2 - real (kind=RKIND), dimension(:,:,:), allocatable :: delsq_tracer + !real (kind=RKIND), dimension(:,:,:), allocatable :: delsq_tracer + real (kind=RKIND), dimension(:,:,:), pointer :: delsq_tracer + type (field3DReal), pointer :: delsq_tracerField real (kind=RKIND), dimension(:), pointer :: dcEdge, dvEdge, areaCell, meshScalingDel4 @@ -130,7 +137,7 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- @@ -139,8 +146,10 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend if ( .not. del4On ) return - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_timer_start("tracer del4") + + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) num_tracers = size(tracers, dim=1) @@ -159,13 +168,23 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) - allocate(delsq_tracer(num_tracers,nVertLevels, nCells+1)) + !allocate(delsq_tracer(num_tracers,nVertLevels, nCells+1)) + call mpas_pool_get_field(scratchPool, 'delsq_tracer', delsq_tracerField) + + call mpas_allocate_scratch_field(delsq_tracerField, .true.) + + call mpas_threading_barrier() + + delsq_tracer => delsq_tracerField % array - delsq_tracer(:,:,:) = 0.0 + ! Need 1 halo around owned cells + nCells = nCellsArray( 2 ) ! first del2: div(h \nabla \phi) at cell center + !$omp do schedule(runtime) private(invAreaCell1, i, iEdge, invdcEdge, cell1, cell2, k, iTracer, r_tmp1, r_tmp2) do iCell = 1, nCells - invAreaCell1 = 1.0 / areaCell(iCell) + delsq_tracer(:, :, iCell) = 0.0_RKIND + invAreaCell1 = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) invdcEdge = dvEdge(iEdge) / dcEdge(iEdge) @@ -178,15 +197,21 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend r_tmp1 = invdcEdge * layerThicknessEdge(k, iEdge) * tracers(iTracer, k, cell1) r_tmp2 = invdcEdge * layerThicknessEdge(k, iEdge) * tracers(iTracer, k, cell2) - delsq_tracer(iTracer, k, iCell) = delsq_tracer(iTracer, k, iCell) - edgeSignOnCell(i, iCell) * (r_tmp2 - r_tmp1) * invAreaCell1 + delsq_tracer(iTracer, k, iCell) = delsq_tracer(iTracer, k, iCell) - edgeSignOnCell(i, iCell) & + * (r_tmp2 - r_tmp1) * invAreaCell1 end do end do end do end do + !$omp end do + + ! Only need tendency on owned cells + nCells = nCellsArray( 1 ) ! second del2: div(h \nabla [delsq_tracer]) at cell center + !$omp do schedule(runtime) private(invAreaCell1, i, iEdge, cell1, cell2, invdcEdge, k, iTracer, tracer_turb_flux, flux) do iCell = 1, nCells - invAreaCell1 = 1.0 / areaCell(iCell) + invAreaCell1 = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) cell1 = cellsOnEdge(1, iEdge) @@ -197,7 +222,7 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend do k = 1, maxLevelEdgeTop(iEdge) do iTracer = 1, num_tracers * edgeMask(k, iEdge) tracer_turb_flux = (delsq_tracer(iTracer, k, cell2) - delsq_tracer(iTracer, k, cell1)) - + flux = tracer_turb_flux * invdcEdge tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + edgeSignOnCell(i, iCell) * flux * invAreaCell1 @@ -205,8 +230,13 @@ subroutine ocn_tracer_hmix_del4_tend(meshPool, layerThicknessEdge, tracers, tend end do end do end do + !$omp end do + + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(delsq_tracerField, .true.) + + call mpas_timer_stop("tracer del4") - deallocate(delsq_tracer) !-------------------------------------------------------------------- end subroutine ocn_tracer_hmix_del4_tend!}}} @@ -218,9 +248,9 @@ end subroutine ocn_tracer_hmix_del4_tend!}}} !> \brief Initializes ocean tracer horizontal mixing quantities !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> biharmonic horizontal velocity mixing in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> biharmonic horizontal velocity mixing in the ocean. ! !----------------------------------------------------------------------- @@ -246,7 +276,7 @@ subroutine ocn_tracer_hmix_del4_init(err)!{{{ del4on = .false. - if ( config_tracer_del4 > 0.0 ) then + if ( config_tracer_del4 > 0.0_RKIND ) then del4On = .true. eddyDiff4 = config_tracer_del4 endif diff --git a/src/core_ocean/shared/mpas_ocn_tracer_hmix_redi.F b/src/core_ocean/shared/mpas_ocn_tracer_hmix_redi.F new file mode 100644 index 0000000000..316392b1c7 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_hmix_redi.F @@ -0,0 +1,452 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_hmix_redi +! +!> \brief MPAS ocean horizontal tracer mixing driver +!> \author Doug Jacobsen, Mark Petersen, Todd Ringler +!> \date September 2011 +!> \details +!> This module contains the main driver routine for computing +!> horizontal mixing tendencies. +!> +!> It provides an init and a tend function. Each are described below. +! +!----------------------------------------------------------------------- + +module ocn_tracer_hmix_redi + + use mpas_timer + use mpas_derived_types + use mpas_pool_routines + use mpas_threading + + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_hmix_redi_tend, & + ocn_tracer_hmix_redi_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + logical :: rediOn + logical, pointer :: config_disable_redi_horizontal_term1 + logical, pointer :: config_disable_redi_horizontal_term2 + logical, pointer :: config_disable_redi_horizontal_term3 + real (kind=RKIND), pointer :: config_Redi_kappa + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_hmix_redi_tend +! +!> \brief Computes Laplacian tendency term for horizontal tracer mixing +!> \author Doug Jacobsen, Mark Petersen, Todd Ringler +!> \date September 2011 +!> \details +!> This routine computes the horizontal mixing tendency for tracers +!> based on current state using a Laplacian parameterization. +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_hmix_redi_tend(meshPool, scratchPool, layerThicknessEdge, zMid, tracers, & + relativeSlopeTopOfEdge, relativeSlopeTapering, relativeSlopeTaperingCell, tend, err)!{{{ + + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + type (mpas_pool_type), intent(in) :: meshPool !< Input: Mesh information + type (mpas_pool_type), intent(in) :: scratchPool !< Input: Scratch information + + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThicknessEdge, &!< Input: thickness at edge + zMid, &!< Input: Z coordinate at the center of a cell + relativeSlopeTopOfEdge, &!< Input: slope of coordinate relative to neutral surface at edges + relativeSlopeTapering, &!< Input: tapering of slope of coordinate relative to neutral surface at edges + relativeSlopeTaperingCell !< Input: tapering of slope of coordinate relative to neutral surface at cells + + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + tracers !< Input: tracer quantities + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tend !< Input/Output: velocity tendency + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, iEdge, cell1, cell2 + integer :: i, k, iTracer, num_tracers, nCells, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray, nEdgesArray + + integer, dimension(:,:), allocatable :: boundaryMask + + integer, dimension(:), pointer :: maxLevelEdgeTop, nEdgesOnCell, maxLevelCell + integer, dimension(:,:), pointer :: cellsOnEdge, edgesOnCell, edgeSignOnCell + + real (kind=RKIND) :: invAreaCell1, invAreaCell2, invAreaCell, areaEdge + real (kind=RKIND) :: tracer_turb_flux, flux, s_tmp, r_tmp, h1, h2, s_tmpU, s_tmpD + + real (kind=RKIND), dimension(:), pointer :: areaCell, dvEdge, dcEdge + + real (kind=RKIND), dimension(:,:), pointer :: gradTracerEdge, gradTracerTopOfEdge, gradHTracerSlopedTopOfCell, & + dTracerdZTopOfCell, dTracerdZTopOfEdge, areaCellSum + + type (field2DReal), pointer :: gradTracerEdgeField, gradTracerTopOfEdgeField, gradHTracerSlopedTopOfCellField, & + dTracerdZTopOfCellField, dTracerdZTopOfEdgeField, areaCellSumField + + err = 0 + + if (.not.rediOn) return + + call mpas_timer_start("tracer redi") + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + num_tracers = size(tracers, dim=1) + + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) + + call mpas_pool_get_config(ocnConfigs, 'config_Redi_kappa',config_Redi_kappa) + call mpas_pool_get_config(ocnConfigs, 'config_disable_redi_horizontal_term1',config_disable_redi_horizontal_term1) + call mpas_pool_get_config(ocnConfigs, 'config_disable_redi_horizontal_term2',config_disable_redi_horizontal_term2) + call mpas_pool_get_config(ocnConfigs, 'config_disable_redi_horizontal_term3',config_disable_redi_horizontal_term3) + + ! + ! COMPUTE the extra terms arising due to mismatch between the constant coordinate surfaces and the + ! isopycnal surfaces. + ! + + call mpas_pool_get_field(scratchPool, 'gradTracerEdge', gradTracerEdgeField) + call mpas_pool_get_field(scratchPool, 'gradTracerTopOfEdge', gradTracerTopOfEdgeField) + call mpas_pool_get_field(scratchPool, 'gradHTracerSlopedTopOfCell', gradHTracerSlopedTopOfCellField) + call mpas_pool_get_field(scratchPool, 'dTracerdZTopOfCell', dTracerdZTopOfCellField) + call mpas_pool_get_field(scratchPool, 'dTracerdZTopOfEdge', dTracerdZTopOfEdgeField) + call mpas_pool_get_field(scratchPool, 'areaCellSum', areaCellSumField) + + call mpas_allocate_scratch_field(gradTracerEdgeField, .true., .false.) + call mpas_allocate_scratch_field(gradTracerTopOfEdgeField, .true., .false.) + call mpas_allocate_scratch_field(gradHTracerSlopedTopOfCellField, .true., .false.) + call mpas_allocate_scratch_field(dTracerdZTopOfCellField, .true., .false.) + call mpas_allocate_scratch_field(dTracerdZTopOfEdgeField, .true., .false.) + call mpas_allocate_scratch_field(areaCellSumField, .true., .false.) + call mpas_threading_barrier() + + gradTracerEdge => gradTracerEdgeField % array + gradTracerTopOfEdge => gradTracerTopOfEdgeField % array + gradHTracerSlopedTopOfCell => gradHTracerSlopedTopOfCellField % array + dTracerdZTopOfCell => dTracerdZTopOfCellField % array + dTracerdZTopOfEdge => dTracerdZTopOfEdgeField % array + areaCellSum => areaCellSumField % array + + nCells = nCellsArray( size(nCellsArray) ) + nEdges = nEdgesArray( size(nEdgesArray) ) + + !$omp do schedule(runtime) + do iCell = 1, nCells + gradHTracerSlopedTopOfCell(:, iCell) = 0.0_RKIND + dTracerdZTopOfCell(:, iCell) = 0.0_RKIND + end do + !$omp end do + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + gradTracerEdge(:, iEdge) = 0.0_RKIND + gradTracerTopOfEdge(:, iEdge) = 0.0_RKIND + dTracerdZTopOfEdge(:, iEdge) = 0.0_RKIND + end do + !$omp end do + + ! this is the "standard" del2 term, but forced to use config_redi_kappa + if(.not.config_disable_redi_horizontal_term1) then + + nCells = nCellsArray( 1 ) + !$omp do schedule(runtime) private(invAreaCell, i, iEdge, cell1, cell2, r_tmp, k, s_tmp, iTracer, tracer_turb_flux, flux) + do iCell = 1, nCells + invAreaCell = 1.0_RKIND / areaCell(iCell) + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + + r_tmp = config_redi_kappa * dvEdge(iEdge) / dcEdge(iEdge) + + do k = 1, maxLevelEdgeTop(iEdge) + + do iTracer = 1, num_tracers + ! \kappa_2 \nabla \phi on edge + tracer_turb_flux = tracers(iTracer, k, cell2) - tracers(iTracer, k, cell1) + + ! div(h \kappa_2 \nabla \phi) at cell center + flux = layerThicknessEdge(k, iEdge) * tracer_turb_flux * r_tmp + + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) - edgeSignOnCell(i, iCell) * flux * invAreaCell + + end do + end do + + end do + end do + !$omp end do + + endif + + ! Compute vertical derivative of tracers at cell center and top of layer + do iTracer = 1, num_tracers + ! Sync threads before starting on tracers + call mpas_threading_barrier() + + nCells = nCellsArray( 2 ) + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k = 2, maxLevelCell(iCell) + dTracerdZTopOfCell(k,iCell) = (tracers(iTracer,k-1,iCell) - tracers(iTracer,k,iCell)) & + / (zMid(k-1,iCell) - zMid(k,iCell)) + end do + + ! Approximation of dTracerdZTopOfCell on the top and bottom interfaces through the idea of having + ! ghost cells above the top and below the bottom layers of the same depths and tracer density. + ! Essentially, this enforces the boundary condition (d tracer)/dz = 0 at the top and bottom. + dTracerdZTopOfCell(1,iCell) = 0.0_RKIND + dTracerdZTopOfCell(maxLevelCell(iCell)+1,iCell) = 0.0_RKIND + end do + !$omp end do + + nEdges = nEdgesArray( 2 ) + ! Compute tracer gradient (gradTracerEdge) along the constant coordinate surface. + ! The computed variables lives at edge and mid-layer depth + !$omp do schedule(runtime) private(cell1, cell2, k) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + + do k=1,maxLevelEdgeTop(iEdge) + gradTracerEdge(k,iEdge) = (tracers(iTracer,k,cell2) - tracers(iTracer,k,cell1)) / dcEdge(iEdge) + end do + end do + !$omp end do + + nEdges = nEdgesArray( 2 ) + ! Interpolate dTracerdZTopOfCell to edge and top of layer + !$omp do schedule(runtime) private(cell1, cell2, k) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + dTracerdZTopOfEdge(k,iEdge) = 0.5_RKIND * (dTracerdZTopOfCell(k,cell1) + dTracerdZTopOfCell(k,cell2)) + end do + dTracerdZTopOfEdge(maxLevelEdgeTop(iEdge)+1,iEdge) = 0.0_RKIND + end do + !$omp end do + + nEdges = nEdgesArray( 2 ) + ! Interpolate gradTracerEdge to edge and top of layer + !$omp do schedule(runtime) private(k, h1, h2) + do iEdge = 1, nEdges + do k = 2, maxLevelEdgeTop(iEdge) + h1 = layerThicknessEdge(k-1,iEdge) + h2 = layerThicknessEdge(k,iEdge) + + ! Using second-order interpolation below + gradTracerTopOfEdge(k,iEdge) = (h2 * gradTracerEdge(k-1,iEdge) + h1 * gradTracerEdge(k,iEdge)) / (h1 + h2) + end do + + ! Approximation of values on the top and bottom interfaces through the idea of having ghost cells above + ! the top and below the bottom layers of the same depths and tracer concentration. + gradTracerTopOfEdge(1,iEdge) = gradTracerEdge(2,iEdge) + gradTracerTopOfEdge(maxLevelEdgeTop(iEdge)+1,iEdge) = gradTracerEdge(max(maxLevelEdgeTop(iEdge),1),iEdge) + end do + !$omp end do + + ! Compute \nabla\cdot(relativeSlope d\phi/dz) + if(.not.config_disable_redi_horizontal_term2) then + nCells = nCellsArray( 1 ) + !$omp do schedule(runtime) private(invAreaCell, i, iEdge, k, s_tmpU, s_tmpD, flux) + do iCell = 1, nCells + invAreaCell = 1.0_RKIND / areaCell(iCell) + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + do k = 1, maxLevelEdgeTop(iEdge) + s_tmpU = relativeSlopeTapering(k, iEdge) * relativeSlopeTopOfEdge(k, iEdge) * dTracerdZTopOfEdge(k, iEdge) + s_tmpD = relativeSlopeTapering(k+1, iEdge) * relativeSlopeTopOfEdge(k+1, iEdge) & + * dTracerdZTopOfEdge(k+1, iEdge) + + flux = 0.5 * dvEdge(iEdge) * ( s_tmpU + s_tmpD ) + flux = flux * layerThicknessEdge(k, iEdge) + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + edgeSignOnCell(i, iCell) * config_Redi_kappa * flux & + * invAreaCell + end do + end do + end do + !$omp end do + endif + + ! Compute dz * d(relativeSlope\cdot\nabla\phi)/dz (so the dz cancel out) + + ! Compute relativeSlope\cdot\nabla\phi (variable gradHTracerSlopedTopOfCell) at non-boundary edges + + nCells = nCellsArray( 1 ) + !$omp do schedule(runtime) private(i, iedge, areaEdge, k, r_tmp) + do iCell = 1, nCells + areaCellSum(:, iCell) = 1.0e-34_RKIND + gradHTracerSlopedTopOfCell(:, iCell) = 0.0_RKIND + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + areaEdge = 0.5_RKIND * dcEdge(iEdge) * dvEdge(iEdge) + do k = 1, maxLevelEdgeTop(iEdge) + r_tmp = areaEdge * relativeSlopeTapering(k, iEdge) * relativeSlopeTopOfEdge(k,iEdge) & + * gradTracerTopOfEdge(k,iEdge) + gradHTracerSlopedTopOfCell(k, iCell) = gradHTracerSlopedTopOfCell(k, iCell) + r_tmp + areaCellSum(k, iCell) = areaCellSum(k, iCell) + areaEdge + end do + end do + end do + !$omp end do + + nCells = nCellsArray( 1 ) + !$omp do schedule(runtime) private(k) + do iCell=1,nCells + do k = 1, maxLevelCell(iCell) + gradHTracerSlopedTopOfCell(k,iCell) = gradHTracerSlopedTopOfCell(k,iCell)/areaCellSum(k,iCell) + end do + end do + !$omp end do + + if(.not.config_disable_redi_horizontal_term3) then + nCells = nCellsArray( 1 ) + !$omp do schedule(runtime) private(k, s_tmp) + do iCell = 1, nCells + ! impose no-flux boundary conditions at top and bottom of column + gradHTracerSlopedTopOfCell(1,iCell) = 0.0_RKIND + gradHTracerSlopedTopOfCell(maxLevelCell(iCell)+1,iCell) = 0.0_RKIND + do k = 1, maxLevelCell(iCell) + tend(iTracer,k,iCell) = tend(iTracer,k,iCell) + config_Redi_kappa * & + (gradHTracerSlopedTopOfCell(k,iCell) - gradHTracerSlopedTopOfCell(k+1,iCell)) + end do + end do + !$omp end do + endif + + end do ! iTracer + + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(gradTracerEdgeField, .true.) + call mpas_deallocate_scratch_field(gradTracerTopOfEdgeField, .true.) + call mpas_deallocate_scratch_field(gradHTracerSlopedTopOfCellField, .true.) + call mpas_deallocate_scratch_field(dTracerdZTopOfCellField, .true.) + call mpas_deallocate_scratch_field(dTracerdZTopOfEdgeField, .true.) + + call mpas_timer_stop("tracer redi") + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_hmix_redi_tend!}}} + +!*********************************************************************** +! +! routine ocn_tracer_hmix_redi_init +! +!> \brief Initializes ocean tracer horizontal mixing quantities +!> \author Doug Jacobsen, Mark Petersen, Todd Ringler +!> \date September 2011 +!> \details +!> This routine initializes a variety of quantities related to +!> Laplacian horizontal velocity mixing in the ocean. +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_hmix_redi_init(err)!{{{ + + !-------------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! call individual init routines for each parameterization + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + logical, pointer :: config_use_standardGM + + err = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_use_standardGM', config_use_standardGM) + + rediOn = .false. + + if ( config_use_standardGM ) then + rediOn = .true. + endif + + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_hmix_redi_init!}}} + +!*********************************************************************** + +end module ocn_tracer_hmix_redi + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_ideal_age.F b/src/core_ocean/shared/mpas_ocn_tracer_ideal_age.F new file mode 100644 index 0000000000..fbcd0ae231 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_ideal_age.F @@ -0,0 +1,168 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_ideal_age +! +!> \brief MPAS ocean restoring +!> \author Todd Ringler +!> \date 06/08/2015 +!> \details +!> This module contains routines for computing the tracer tendency due to restoring +! +!----------------------------------------------------------------------- + +module ocn_tracer_ideal_age + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_ideal_age_compute, & + ocn_tracer_ideal_age_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_ideal_age_compute +! +!> \brief computes a tracer tendency to approximate ideal age +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine computes a tracer tendency to approximate ideal age +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_ideal_age_compute(nTracers, nCellsSolve, maxLevelCell, layerThickness, & + idealAgeMask, tracers, tracer_tend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! one dimensional arrays + integer, dimension(:), intent(in) :: & + maxLevelCell + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThickness, & + idealAgeMask + + integer, intent(in) :: nTracers, nCellsSolve + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tracers, & + tracer_tend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, iLevel, iTracer + + !move to ocean constants + real (kind=RKIND), parameter :: c0 = 0.0_RKIND + real (kind=RKIND), parameter :: c1 = 1.0_RKIND + + err = 0 + + !$omp do schedule(runtime) private(iLevel, iTracer) + do iCell=1,nCellsSolve + do iLevel=1,maxLevelCell(iCell) + do iTracer=1,nTracers + ! zero tracers at surface to zero where idealAgeMask == zero + ! idealAgeMask should be equal to 1.0 elsewhere + tracers(iTracer, iLevel, iCell) = idealAgeMask(iTracer, iCell) * tracers(iTracer, iLevel, iCell) + + ! add a tendency increment equivalent to "dt" to entire domain + tracer_tend(iTracer, iLevel, iCell) = tracer_tend(iTracer, iLevel, iCell) + & + layerThickness(iLevel,iCell) * c1 + enddo + enddo + enddo + !$omp end do + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_ideal_age_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_ideal_age_init +! +!> \brief Initializes ocean ideal age +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine initializes fields required for tracer ideal age +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_ideal_age_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + + err = 0 + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_ideal_age_init!}}} + +!*********************************************************************** + +end module ocn_tracer_ideal_age + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_interior_restoring.F b/src/core_ocean/shared/mpas_ocn_tracer_interior_restoring.F new file mode 100644 index 0000000000..6ec9612057 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_interior_restoring.F @@ -0,0 +1,165 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_interior_restoring +! +!> \brief MPAS ocean restoring +!> \author Todd Ringler +!> \date 06/08/2015 +!> \details +!> This module contains routines for computing the tracer tendency due to restoring +! +!----------------------------------------------------------------------- + +module ocn_tracer_interior_restoring + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use ocn_constants + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_interior_restoring_compute, & + ocn_tracer_interior_restoring_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_interior_restoring_compute +! +!> \brief computes a tracer tendency due to interior restoring +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine computes a tracer tendency due to interior restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_interior_restoring_compute(nTracers, nCellsSolve, maxLevelCell, layerThickness, & + tracers, tracersInteriorRestoringRate, tracersInteriorRestoringValue, tracer_tend, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + ! one dimensional arrays + integer, dimension(:), intent(in) :: & + maxLevelCell + + ! two dimensional arrays + real (kind=RKIND), dimension(:,:), intent(in) :: & + layerThickness + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + tracers, & + tracersInteriorRestoringRate, & + tracersInteriorRestoringValue + + ! scalars + integer, intent(in) :: nTracers, nCellsSolve + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tracer_tend + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, iLevel, iTracer + + err = 0 + + !$omp do schedule(runtime) private(iLevel, iTracer) + do iCell=1,nCellsSolve + do iLevel=1,maxLevelCell(iCell) + do iTracer=1,nTracers + tracer_tend(iTracer, iLevel, iCell) = tracer_tend(iTracer, iLevel, iCell) - layerThickness(iLevel,iCell) & + * ( tracers(iTracer, iLevel, iCell) & + - tracersInteriorRestoringValue(iTracer, iLevel, iCell) ) & + * tracersInteriorRestoringRate(iTracer, iLevel, iCell) + enddo + enddo + enddo + !$omp end do + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_interior_restoring_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_interior_restoring_init +! +!> \brief Initializes ocean interior restoring +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine initializes fields required for tracer interior restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_interior_restoring_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + + err = 0 + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_interior_restoring_init!}}} + +!*********************************************************************** + +end module ocn_tracer_interior_restoring + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_nonlocalflux.F b/src/core_ocean/shared/mpas_ocn_tracer_nonlocalflux.F index e44d7512f6..0ceb389dca 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_nonlocalflux.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_nonlocalflux.F @@ -14,13 +14,14 @@ !> \date 11/25/13 !> \version SVN:$Id:$ !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> tracer tendencies due to non-local vertical fluxes computed in CVMix KPP ! !----------------------------------------------------------------------- module ocn_tracer_nonlocalflux + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants @@ -63,7 +64,7 @@ module ocn_tracer_nonlocalflux !> \brief Computes tendency term due to non-local flux transport !> \author Todd Ringler !> \date 11/25/13 -!> \details +!> \details !> This routine computes the tendency for tracers based the vertical divergence of non-local fluxes. ! !----------------------------------------------------------------------- @@ -107,23 +108,27 @@ subroutine ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, surfaceTrace ! !----------------------------------------------------------------- - integer :: iCell, k, iTracer, nTracers - integer, pointer :: nCells, nVertLevels + integer :: iCell, k, iTracer, nTracers, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: maxLevelCell - integer, dimension(:,:), pointer :: cellMask real (kind=RKIND) :: fluxTopOfCell, fluxBottomOfCell err = 0 if (.not. nonLocalFluxOn) return - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_timer_start('non-local flux') + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) nTracers = size(tend, dim=1) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_array(meshPool, 'cellMask', cellMask) + nCells = nCellsArray( 1 ) + + !$omp do schedule(runtime) private(k, iTracer, fluxTopOfCell, fluxBottomOfCell) do iCell = 1, nCells do k = 2, maxLevelCell(iCell)-1 @@ -131,7 +136,7 @@ subroutine ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, surfaceTrace do iTracer = 1, nTracers fluxTopOfCell = surfaceTracerFlux(iTracer, iCell) * vertNonLocalFlux(1, k, iCell) fluxBottomOfCell = surfaceTracerFlux(iTracer, iCell) * vertNonLocalFlux(1, k+1, iCell) - tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + cellMask(k, icell) * (fluxTopOfCell-fluxBottomOfCell) + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + (fluxTopOfCell-fluxBottomOfCell) end do end do @@ -139,19 +144,22 @@ subroutine ocn_tracer_nonlocalflux_tend(meshPool, vertNonLocalFlux, surfaceTrace k = maxLevelCell(iCell) do iTracer = 1, nTracers fluxTopOfCell = surfaceTracerFlux(iTracer, iCell) * vertNonLocalFlux(1, k, iCell) - fluxBottomOfCell = 0.0 - tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + cellMask(k, icell) * (fluxTopOfCell-fluxBottomOfCell) + fluxBottomOfCell = 0.0_RKIND + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + (fluxTopOfCell-fluxBottomOfCell) end do ! enforce boundary conditions at top of column k = 1 do iTracer = 1, nTracers - fluxTopOfCell = 0.0 + fluxTopOfCell = 0.0_RKIND fluxBottomOfCell = surfaceTracerFlux(iTracer, iCell) * vertNonLocalFlux(1, k+1, iCell) - tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + cellMask(k, icell) * (fluxTopOfCell-fluxBottomOfCell) + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + (fluxTopOfCell-fluxBottomOfCell) end do end do + !$omp end do + + call mpas_timer_stop('non-local flux') !-------------------------------------------------------------------- @@ -165,7 +173,7 @@ end subroutine ocn_tracer_nonlocalflux_tend!}}} !> \author Todd Ringler !> \date 11/25/13 !> \version SVN:$Id$ -!> \details +!> \details !> This routine initializes quantities related to nonlocal flux computation ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption.F b/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption.F index bbb7a1a5cd..628ee52661 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption.F @@ -6,17 +6,19 @@ !> \author Doug Jacobsen !> \date 12/17/12 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> short wave tendencies ! !----------------------------------------------------------------------- module ocn_tracer_short_wave_absorption + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants use ocn_tracer_short_wave_absorption_jerlov + use ocn_tracer_short_wave_absorption_variable implicit none private @@ -56,12 +58,13 @@ module ocn_tracer_short_wave_absorption !> \brief Computes tendency term for surface fluxes !> \author Doug Jacobsen !> \date 12/17/12 -!> \details +!> \details !> This routine computes the tendency for tracers based on surface fluxes. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_short_wave_absorption_tend(meshPool, index_temperature, layerThickness, penetrativeTemperatureFlux, tend, err)!{{{ + subroutine ocn_tracer_short_wave_absorption_tend(meshPool, swForcingPool, forcingPool, index_temperature, & !{{{ + layerThickness, penetrativeTemperatureFlux, penetrativeTemperatureFluxOBL, tend, err) !----------------------------------------------------------------- ! @@ -70,11 +73,14 @@ subroutine ocn_tracer_short_wave_absorption_tend(meshPool, index_temperature, la !----------------------------------------------------------------- type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information + meshPool, swForcingPool, forcingPool !< Input: mesh information real (kind=RKIND), dimension(:), intent(in) :: & penetrativeTemperatureFlux !< Input: short wave heat flux + real (kind=RKIND), dimension(:), intent(inout) :: & + penetrativeTemperatureFluxOBL + real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness !< Input: Layer thicknesses integer, intent(in) :: index_temperature @@ -102,12 +108,24 @@ subroutine ocn_tracer_short_wave_absorption_tend(meshPool, index_temperature, la ! !----------------------------------------------------------------- - err = 0 + character(len=strKIND), pointer :: config_sw_absorption_type - if ( useJerlov ) then - call ocn_tracer_short_wave_absorption_jerlov_tend(meshPool, index_temperature, layerThickness, penetrativeTemperatureFlux, tend, err) - end if + call MPAS_pool_get_config(ocnConfigs, 'config_sw_absorption_type', config_sw_absorption_type) + + if (trim(config_sw_absorption_type)=='none') return + + call mpas_timer_start("short wave") + err = 0 + if(useJerlov) then + call ocn_tracer_short_wave_absorption_jerlov_tend(meshPool, forcingPool, index_temperature, layerThickness, & + penetrativeTemperatureFlux, penetrativeTemperatureFluxOBL, tend, err) + else + call ocn_tracer_short_wave_absorption_variable_tend(meshPool,swForcingPool, forcingPool, index_temperature, & + layerThickness, penetrativeTemperatureFlux, penetrativeTemperatureFluxOBL,tend,err) + endif + + call mpas_timer_stop("short wave") !-------------------------------------------------------------------- end subroutine ocn_tracer_short_wave_absorption_tend!}}} @@ -119,32 +137,52 @@ end subroutine ocn_tracer_short_wave_absorption_tend!}}} !> \brief Initializes ocean tracer surface flux quantities !> \author Doug Jacobsen !> \date 12/17/12 -!> \details +!> \details !> This routine initializes quantities related to surface fluxes in the ocean. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_short_wave_absorption_init(err)!{{{ + subroutine ocn_tracer_short_wave_absorption_init(domain,err)!{{{ !-------------------------------------------------------------------- + type (domain_type) :: domain + integer, intent(out) :: err !< Output: error flag + logical, pointer :: config_use_activeTracers_surface_bulk_forcing character (len=StrKind), pointer :: config_sw_absorption_type err = 0 call mpas_pool_get_config(ocnConfigs, 'config_sw_absorption_type', config_sw_absorption_type) - - useJerlov = .false. - - if ( trim( config_sw_absorption_type ) .ne. 'jerlov') then - write(0,*) 'Incorrect option for config_sw_absorption_type. Options are: jerlov' - err = 1 - return - else if ( trim( config_sw_absorption_type ) == 'jerlov') then - useJerlov = .true. - call ocn_tracer_short_wave_absorption_jerlov_init(err) + call MPAS_pool_get_config(ocnConfigs, 'config_use_activeTracers_surface_bulk_forcing', & + config_use_activeTracers_surface_bulk_forcing) + + useJerlov=.false. + + if(.not. config_use_activeTracers_surface_bulk_forcing) then + if(trim(config_sw_absorption_type) .ne. 'none') then + call mpas_log_write( & + 'You have specified bulk_forcing off with shortwave absorption on ' // & + 'either set config_sw_absorption_type to none or enable activeTracers_surface_bulk_forcing', & + MPAS_LOG_CRIT) + err = 1 + endif + return + endif + + if ( trim( config_sw_absorption_type ) == 'jerlov') then + useJerlov=.true. +! call ocn_tracer_short_wave_absorption_jerlov_init(err) + else if ( trim( config_sw_absorption_type ) == 'ohlmann00' ) then + call ocn_tracer_short_wave_absorption_variable_init(domain, err) + else if ( trim( config_sw_absorption_type ) .ne. 'none') then + call mpas_log_write( & + 'Incorrect option for config_sw_absorption_type. Options are: jerlov or ohlmann00 or none', & + MPAS_LOG_CRIT) + err = 1 + return end if @@ -152,6 +190,7 @@ end subroutine ocn_tracer_short_wave_absorption_init!}}} !*********************************************************************** + end module ocn_tracer_short_wave_absorption !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption_jerlov.F b/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption_jerlov.F index 4496f78126..c882b27cfa 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption_jerlov.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption_jerlov.F @@ -6,7 +6,7 @@ !> \author Doug Jacobsen !> \date 12/17/12 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> short wave tendencies using Jerlov ! !----------------------------------------------------------------------- @@ -34,7 +34,6 @@ module ocn_tracer_short_wave_absorption_jerlov !-------------------------------------------------------------------- public :: ocn_tracer_short_wave_absorption_jerlov_tend, & - ocn_tracer_short_wave_absorption_jerlov_init, & ocn_get_jerlov_fraction !-------------------------------------------------------------------- @@ -70,12 +69,13 @@ module ocn_tracer_short_wave_absorption_jerlov !> \brief Computes tendency term for surface fluxes !> \author Doug Jacobsen !> \date 12/17/12 -!> \details +!> \details !> This routine computes the tendency for tracers based on surface fluxes. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_short_wave_absorption_jerlov_tend(meshPool, index_temperature, layerThickness, penetrativeTemperatureFlux, tend, err)!{{{ + subroutine ocn_tracer_short_wave_absorption_jerlov_tend(meshPool, forcingPool, index_temperature, layerThickness, & + penetrativeTemperatureFlux, penetrativeTemperatureFluxOBL, tend, err)!{{{ !----------------------------------------------------------------- ! @@ -84,11 +84,14 @@ subroutine ocn_tracer_short_wave_absorption_jerlov_tend(meshPool, index_temperat !----------------------------------------------------------------- type (mpas_pool_type), intent(in) :: & - meshPool !< Input: mesh information + meshPool, forcingPool !< Input: mesh information real (kind=RKIND), dimension(:), intent(in) :: & penetrativeTemperatureFlux !< Input: penetrative temperature flux through the surface + real (kind=RKIND), dimension(:), intent(out) :: & + penetrativeTemperatureFluxOBL + real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness !< Input: Layer thicknesses integer, intent(in) :: index_temperature @@ -116,92 +119,64 @@ subroutine ocn_tracer_short_wave_absorption_jerlov_tend(meshPool, index_temperat ! !----------------------------------------------------------------- - integer :: iCell, k - integer, pointer :: nCells, nVertLevels + integer :: iCell, k, depLev, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: maxLevelCell real (kind=RKIND) :: depth + real (kind=RKIND), pointer :: config_surface_buoyancy_depth real (kind=RKIND), dimension(:), pointer :: refBottomDepth real (kind=RKIND), dimension(:), allocatable :: weights - logical, pointer :: config_fixed_jerlov_weights - err = 0 - call mpas_pool_get_config(ocnConfigs, 'config_fixed_jerlov_weights', config_fixed_jerlov_weights) - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_config(ocnConfigs, 'config_surface_buoyancy_depth', config_surface_buoyancy_depth) allocate(weights(nVertLevels+1)) weights = 0.0_RKIND weights(1) = 1.0_RKIND - if ( config_fixed_jerlov_weights ) then - do iCell = 1, nCells - depth = 0.0_RKIND - do k = 1, maxLevelCell(iCell) - depth = depth + refBottomDepth(k) + nCells = nCellsArray( 3 ) - call ocn_get_jerlov_fraction(depth, weights(k+1)) - tend(index_temperature, k, iCell) = tend(index_temperature, k, iCell) + penetrativeTemperatureFlux(iCell)*(weights(k) - weights(k+1)) - end do - end do - else - do iCell = 1, nCells - depth = 0.0_RKIND - do k = 1, maxLevelCell(iCell) - depth = depth + layerThickness(k, iCell) - - call ocn_get_jerlov_fraction(depth, weights(k+1)) - tend(index_temperature, k, iCell) = tend(index_temperature, k, iCell) + penetrativeTemperatureFlux(iCell)*(weights(k) - weights(k+1)) - end do + !$omp do schedule(runtime) private(depth, k, depLev) + do iCell = 1, nCells + depth = 0.0_RKIND + do k = 1, maxLevelCell(iCell) + depth = depth + layerThickness(k, iCell) + + call ocn_get_jerlov_fraction(depth, weights(k+1)) + tend(index_temperature, k, iCell) = tend(index_temperature, k, iCell) + penetrativeTemperatureFlux(iCell) & + * (weights(k) - weights(k+1)) end do - end if - deallocate(weights) + depth = 0.0_RKIND + do k=1,maxLevelCell(iCell) + depth = depth + layerThickness(k,iCell) + if(depth > abs(config_surface_buoyancy_depth)) exit + enddo - !-------------------------------------------------------------------- + if(k == maxLevelCell(iCell) .or. k == 1) then + depLev=2 + else + depLev=k + endif + penetrativeTemperatureFluxOBL(iCell)=penetrativeTemperatureFlux(iCell)*weights(depLev) - end subroutine ocn_tracer_short_wave_absorption_jerlov_tend!}}} - -!*********************************************************************** -! -! routine ocn_tracer_short_wave_absorption_jerlov_init -! -!> \brief Initializes ocean tracer surface flux quantities -!> \author Doug Jacobsen -!> \date 12/17/12 -!> \details -!> This routine initializes quantities related to surface fluxes in the ocean. -! -!----------------------------------------------------------------------- + end do + !$omp end do - subroutine ocn_tracer_short_wave_absorption_jerlov_init(err)!{{{ + deallocate(weights) !-------------------------------------------------------------------- - integer, intent(out) :: err !< Output: error flag - - character (len=StrKIND), pointer :: config_sw_absorption_type - - err = 0 - - call mpas_pool_get_config(ocnConfigs, 'config_sw_absorption_type', config_sw_absorption_type) - - if ( trim( config_sw_absorption_type ) .ne. 'jerlov') then - write(0,*) 'Incorrect option for config_sw_absorption_type. Options are: jerlov' - err = 1 - return - end if - - end subroutine ocn_tracer_short_wave_absorption_jerlov_init!}}} - -!*********************************************************************** + end subroutine ocn_tracer_short_wave_absorption_jerlov_tend!}}} !*********************************************************************** ! @@ -210,7 +185,7 @@ end subroutine ocn_tracer_short_wave_absorption_jerlov_init!}}} !> \brief Initializes short wave absorption fractions !> \author Doug Jacobsen !> \date 12/17/12 -!> \details +!> \details !> Computes fraction of solar short-wave flux penetrating to !> specified depth due to exponential decay in Jerlov water type. !> Reference : two band solar absorption model of Simpson and @@ -219,7 +194,7 @@ end subroutine ocn_tracer_short_wave_absorption_jerlov_init!}}} !----------------------------------------------------------------------- subroutine ocn_get_jerlov_fraction(depth, weight)!{{{ ! Note: below 200m the solar penetration gets set to zero, -! otherwise the limit for the exponent ($+/- 5678$) needs to be +! otherwise the limit for the exponent ($+/- 5678$) needs to be ! taken care of. real (kind=RKIND), intent(in) :: depth !< Input: Depth of bottom of cell @@ -231,13 +206,16 @@ subroutine ocn_get_jerlov_fraction(depth, weight)!{{{ ! !----------------------------------------------------------------------- +! integer :: k, nVertLevels integer, parameter :: num_water_types = 5 ! max number of different water types - + + ! I don't understand what the previous two lines are for. They appear unnecessary + real (kind=RKIND), parameter :: depth_cutoff = -200.0_RKIND integer, pointer :: config_jerlov_water_type - + !----------------------------------------------------------------------- ! ! compute absorption fraction diff --git a/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption_variable.F b/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption_variable.F new file mode 100644 index 0000000000..d45d70055e --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_short_wave_absorption_variable.F @@ -0,0 +1,507 @@ +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_short_wave_absorption_jerlov +! +!> \brief MPAS ocean tracer short wave +!> \author Doug Jacobsen +!> \date 12/17/12 +!> \details +!> This module contains the routine for computing +!> short wave tendencies using Jerlov +! +!----------------------------------------------------------------------- + +module ocn_tracer_short_wave_absorption_variable + + use mpas_derived_types + use mpas_pool_routines + use mpas_timekeeping + use mpas_forcing + use mpas_stream_manager + use ocn_constants + use ocn_framework_forcing + + implicit none + + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_short_wave_absorption_variable_tend, & + ocn_tracer_short_wave_absorption_variable_init, & + ocn_get_variable_sw_fraction, & + ocn_get_os00_coeffs, & + ocn_init_shortwave_forcing_ohlmann, & + ocn_get_shortWaveData, & + ocn_shortwave_forcing_write_restart + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_short_wave_absorption_jerlov_tend +! +!> \brief Computes tendency term for surface fluxes +!> \author Luke Van Roekel +!> \date 11/10/2015 +!> \details +!> This routine computes the tendency for tracers based on surface fluxes. +!> This computation is now based on spatially variable chlorophyll, cloud fraction, and zenith angle +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_short_wave_absorption_variable_tend(meshPool, swForcingPool, forcingPool, index_temperature, & !{{{ + layerThickness, penetrativeTemperatureFlux, penetrativeTemperatureFluxOBL, tend, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + + type (mpas_pool_type), intent(in) :: & + meshPool, & !< Input: mesh information + swForcingPool, & !< Input: chlorophyll, cloud, zenith data + forcingPool + + real (kind=RKIND), dimension(:), intent(in) :: & + penetrativeTemperatureFlux !< Input: penetrative temperature flux through the surface + + real (kind=RKIND), dimension(:), intent(out) :: & + penetrativeTemperatureFluxOBL + + real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness !< Input: Layer thicknesses + + integer, intent(in) :: index_temperature + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:,:), intent(inout) :: & + tend !< Input/Output: velocity tendency + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, k, depLev, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray + + integer, dimension(:), pointer :: maxLevelCell + + real (kind=RKIND), pointer :: config_surface_buoyancy_depth + real (kind=RKIND) :: depth + real (kind=RKIND), dimension(:), pointer :: refBottomDepth + real (kind=RKIND), dimension(:), allocatable :: weights + real (kind=RKIND), dimension(:), pointer :: chlorophyllA, zenithAngle, clearSkyRadiation + character (len=StrKIND), pointer :: config_sw_absorption_type + real (kind=RKIND), dimension(4) :: Avals, Kvals + real (kind=RKIND) :: cloudRatio ! cloud Ratio = 1 - incident_sfc_sw_radiation/clearSkyRadiation + + err = 0 + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) + call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) + call mpas_pool_get_config(ocnConfigs, 'config_surface_buoyancy_depth', config_surface_buoyancy_depth) + + allocate(weights(nVertLevels+1)) + weights = 0.0_RKIND + weights(1) = 1.0_RKIND + Avals(:)=0.0_RKIND + Kvals(:)=0.0_RKIND + + call mpas_pool_get_config(ocnConfigs, 'config_sw_absorption_type', config_sw_absorption_type) + call mpas_pool_get_array(swForcingPool,'chlorophyllData',chlorophyllA) + + call mpas_pool_get_array(swForcingPool,'zenithAngle',zenithAngle) + call mpas_pool_get_array(swForcingPool,'clearSkyRadiation',clearSkyRadiation) + + nCells = nCellsArray( 3 ) + + !$omp do schedule(runtime) private(depth, k, cloudRatio, depLev) + do iCell = 1, nCells + depth = 0.0_RKIND + cloudRatio = min(1.0_RKIND, 1.0_RKIND - penetrativeTemperatureFlux(iCell)/(hflux_factor*(1.0E-15_RKIND + & + clearSkyRadiation(iCell)))) + cloudRatio = max(0.0_RKIND, cloudRatio) + + call ocn_get_os00_coeffs(chlorophyllA(iCell),zenithAngle(iCell),cloudRatio,Avals, Kvals) + + do k = 1, maxLevelCell(iCell) + depth = depth + layerThickness(k, iCell) + + call ocn_get_variable_sw_fraction(depth, weights(k+1), Avals, Kvals) + tend(index_temperature, k, iCell) = tend(index_temperature, k, iCell) + penetrativeTemperatureFlux(iCell) & + * (weights(k) - weights(k+1) ) + end do + + depth = 0.0_RKIND + do k=1,maxLevelCell(iCell) + depth = depth + layerThickness(k,iCell) + if(depth > abs(config_surface_buoyancy_depth)) exit + enddo + + if(k == maxLevelCell(iCell) .or. k == 1) then + depLev=2 + else + depLev=k + endif + penetrativeTemperatureFluxOBL(iCell)=penetrativeTemperatureFlux(iCell)*weights(depLev) + + end do + !$omp end do + + deallocate(weights) + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_short_wave_absorption_variable_tend!}}} + +!*********************************************************************** +! +! routine ocn_tracer_short_wave_absorption_variable_init +! +!> \brief Initializes ocean tracer surface flux quantities +!> \author Luke Van Roekel +!> \date 11/10/15 +!> \details +!> This routine initializes quantities related to surface fluxes in the ocean. +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_short_wave_absorption_variable_init(domain,err)!{{{ + + !-------------------------------------------------------------------- + + type(domain_type) :: domain + + integer, intent(out) :: err !< Output: error flag + + character (len=StrKIND), pointer :: config_sw_absorption_type + + err = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_sw_absorption_type', config_sw_absorption_type) + + select case ( trim(config_sw_absorption_type) ) + case ('ohlmann00') + call ocn_init_shortwave_forcing_ohlmann(domain) + case default + call mpas_log_write( & + 'Shortwave parameterization type unknown: config_sw_absortion_type=' // trim(config_sw_absorption_type) // & + ' Options are: jerlov or ohlmann00 or none', & + MPAS_LOG_CRIT) + end select + + + end subroutine ocn_tracer_short_wave_absorption_variable_init!}}} + +!*********************************************************************** + +!*********************************************************************** +! +! routine ocn_init_shortwave_forcing_ohlmann +! +!> \brief Initializes forcing group if parameterization of Ohlmann and Siegel (2000) or Ohlmann(2003) +!> This parameterization only requires chlorophyll-a concentrations, so only add that +!> \author Luke Van Roekel +!> \date 11/10/15 +!> \details +!> This routine initializes forcing stream for Ohlmann and Siegel (2000) or Ohlman (2003) parameterization +! +!----------------------------------------------------------------------- + + subroutine ocn_init_shortwave_forcing_ohlmann(domain)!{{{ + + type(domain_type) :: domain + + logical, pointer :: & + config_do_restart + + character(len=strKIND) :: & + forcingIntervalMonthly, & + forcingReferenceTimeMonthly + + call MPAS_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) + + forcingIntervalMonthly = "0000-01-00_00:00:00" + forcingReferenceTimeMonthly = "0000-01-01_00:00:00" + + call MPAS_forcing_init_group( forcingGroupHead, & + "shortwave_monthly_observations", & + domain, & + '0000-01-01_00:00:00', & + '0000-01-01_00:00:00', & + '0001-00-00_00:00:00', & + config_do_restart) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'shortwave_monthly_observations', & + 'chlorophyllData', & + 'shortwave_forcing_data', & + 'shortwave', & + 'chlorophyllData', & + 'constant', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'shortwave_monthly_observations', & + 'clearSkyRadiation', & + 'shortwave_forcing_data', & + 'shortwave', & + 'clearSkyRadiation', & + 'constant', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'shortwave_monthly_observations', & + 'zenithAngle', & + 'shortwave_forcing_data', & + 'shortwave', & + 'zenithAngle', & + 'constant', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field_data( forcingGroupHead, & + 'shortwave_monthly_observations', & + domain % streamManager, & + config_do_restart, & + .false.) + + end subroutine ocn_init_shortwave_forcing_ohlmann!}}} + +!*********************************************************************** + +!*********************************************************************** +! +! routine get_shortWaveData +! +!> \brief retrieve data needed to compute penetration of shortwave radiation +!> \author Luke Van Roekel +!> \date 11/10/15 +!> \details +!> This routine calls mpas_forcing routines to acquire needed shortwave data and interpolates +!> between time levels +! +!----------------------------------------------------------------------- + + subroutine ocn_get_shortWaveData( streamManager, & + domain, & + simulationClock, & + firstTimeStep) !{{{ + + type (MPAS_streamManager_type), intent(inout) :: streamManager + + type (domain_type) :: domain + type (MPAS_timeInterval_type) :: timeStepSW + type (MPAS_clock_type) :: simulationClock + + logical,pointer :: config_use_activeTracers_surface_bulk_forcing + logical, intent(in) :: firstTimeStep + character(len=strKind), pointer :: config_sw_absorption_type + character(len=strKind), pointer :: config_dt + real(kind=RKIND) :: dt + + + call MPAS_pool_get_config(domain%configs, 'config_use_activeTracers_surface_bulk_forcing', & + config_use_activeTracers_surface_bulk_forcing) + call MPAS_pool_get_config(domain%configs, 'config_sw_absorption_type', config_sw_absorption_type) + call MPAS_pool_get_config(domain%configs, 'config_dt', config_dt) + + call mpas_set_timeInterval(timeStepSW,timeString=config_dt) + call mpas_get_timeInterval(timeStepSW,dt=dt) + + if(trim(config_sw_absorption_type) == 'ohlmann00' .and. config_use_activeTracers_surface_bulk_forcing) then + call MPAS_forcing_get_forcing(forcingGroupHead, & + 'shortwave_monthly_observations', streamManager, dt) + endif + + end subroutine ocn_get_shortWaveData!}}} + + +!*********************************************************************** + + +!*********************************************************************** +! +! routine ocn_get_variable_fractions +! +!> \brief Computes short wave absorption fractions +!> \author Luke Van Roekel +!> \date 11/10/2015 +!> \details +!> Computes fraction of solar short-wave flux penetrating to +!> specified depth due to time and space varying chlorophyll, cloud fraction, and zenith angle +!> based on: +!> Ohlmann and Siegel (2000), Ohlmann (2003), Manizza et al. (2005) + +! +!----------------------------------------------------------------------- + subroutine ocn_get_variable_sw_fraction(depth, weight, Avals, Kvals)!{{{ +! Note: below 200m the solar penetration gets set to zero, +! otherwise the limit for the exponent ($+/- 5678$) needs to be +! taken care of. + + real (kind=RKIND), intent(in) :: depth !< Input: Depth of bottom of cell + real (kind=RKIND), intent(in),dimension(4) :: Avals !< Input: spectral partitioning of radiation + real (kind=RKIND), intent(in),dimension(4) :: Kvals !< Input: extinction coefficients for different radiation bands + real (kind=RKIND), intent(out) :: weight !< Output: Weight for Jerlov absorption + +!----------------------------------------------------------------------- +! +! local variables +! +!----------------------------------------------------------------------- + +! + integer :: k + + real (kind=RKIND), parameter :: depth_cutoff = -200.0_RKIND + +!----------------------------------------------------------------------- +! +! compute absorption fraction +! +!----------------------------------------------------------------------- + + if (-depth < depth_cutoff) then + weight = 0.0_RKIND + else + weight=0.0_RKIND + do k=1,4 + weight = weight + Avals(k)*exp(-depth*Kvals(k)) + enddo + endif + end subroutine ocn_get_variable_sw_fraction!}}} + +!*********************************************************************** + + + +!*********************************************************************** +! +! routine ocn_get_os00_coeffs +! +!> \brief Computes coefficients for spatially varying penetrating shortwave via Ohlmann and Siegel 2000 +!> \author Luke Van Roekel +!> \date 11/10/2015 +!> \details +!> This will fill in coefficients for the penetrating shortwave parameterization +!> A1*exp(-K1*depth) + A2*exp(-K2*depth) + A3*exp(-K3*depth) + A4*exp(-K4*depth) +!> For Ohlmann and Siegel (2000) A4 no approximations are made +!> Here the IR portion of the spectrum is also decomposed. Four exponential terms are used +!> NOTE: other schemes can be easily recovered. For example, Ohlmann (2003) requires a +!> coefficient changes only Use equations 6a - 6d and 7 +! Ohlmann, JC, 2003: Ocean Radiant Heating in Climate Models, J.Clim, v16, 1337-1351 +! To recover the Morel (1988) scheme used in Manizza et al. (2005), use equations 1-4 of +! Manizza, M, C LeQuere, AJ Watson, ET Buitenhuis, 2005: Bio-optical feedbacks among phytoplankton +! upper ocean physics and sea-ice in a global model, Geophys, Res. Lett +! +!----------------------------------------------------------------------- + + subroutine ocn_get_os00_coeffs(chlorophyllA,zenithAngle,cloudFraction,Avals,Kvals)!{{{ + + real(kind=RKIND), intent(in) :: chlorophyllA, zenithAngle, cloudFraction + real(kind=RKIND), intent(out), dimension(4) :: Avals, Kvals + + if(cloudFraction > 0.1_RKIND) then ! cloudy skies + Avals(1) = 0.026_RKIND*chlorophyllA + 0.112_RKIND*cloudFraction + 0.366_RKIND + Avals(2) = -0.009_RKIND*chlorophyllA + 0.034_RKIND*cloudFraction + 0.207_RKIND + Avals(3) = -0.015_RKIND*chlorophyllA -0.006_RKIND*cloudFraction + 0.188_RKIND + Avals(4) = -0.003_RKIND*chlorophyllA -0.131_RKIND*cloudFraction + 0.169_RKIND + Kvals(1) = 0.063_RKIND*chlorophyllA -0.015_RKIND*cloudFraction + 0.082_RKIND + Kvals(2) = 0.278_RKIND*chlorophyllA -0.562_RKIND*cloudFraction + 1.02_RKIND + Kvals(3) = 3.91_RKIND*chlorophyllA -12.91_RKIND*cloudFraction + 16.62_RKIND + Kvals(4) = 16.64_RKIND*chlorophyllA -478.28_RKIND*cloudFraction + 736.56_RKIND + else ! clear skies + Avals(1) = 0.033_RKIND*chlorophyllA -0.025_RKIND*zenithAngle + 0.419_RKIND + Avals(2) = -0.010_RKIND*chlorophyllA -0.007_RKIND*zenithAngle + 0.231_RKIND + Avals(3) = -0.019_RKIND*chlorophyllA -0.003_RKIND*zenithAngle + 0.195_RKIND + Avals(4) = -0.006_RKIND*chlorophyllA -0.004_RKIND*zenithAngle + 0.154_RKIND + Kvals(1) = 0.066_RKIND*chlorophyllA + 0.006_RKIND*zenithAngle + 0.066_RKIND + Kvals(2) = 0.396_RKIND*chlorophyllA -0.027_RKIND*zenithAngle + 0.866_RKIND + Kvals(3) = 7.68_RKIND*chlorophyllA -2.49_RKIND*zenithAngle + 17.81_RKIND + Kvals(4) = 51.27_RKIND*chlorophyllA + 13.14_RKIND*zenithAngle + 665.19_RKIND + endif + + end subroutine ocn_get_os00_coeffs!}}} + +!*********************************************************************** + + +!*********************************************************************** +! +! routine ocn_shortwave_forcing_write_restart +! +!> \brief writes restart timestamp for SW data to be read in on future restart +!> \author Luke Van Roekel +!> \date 11/16/2015 + +! +!----------------------------------------------------------------------- + + subroutine ocn_shortwave_forcing_write_restart(domain)!{{{ + + type(domain_type) :: domain + + character(len=strKind), pointer :: config_sw_absorption_type + + + call MPAS_pool_get_config(domain % configs, "config_sw_absorption_type", config_sw_absorption_type) + + if( trim(config_sw_absorption_type) == 'ohlmann00' ) then + call MPAS_forcing_write_restart_times(forcingGroupHead) + endif + + end subroutine ocn_shortwave_forcing_write_restart!}}} + + +end module ocn_tracer_short_wave_absorption_variable + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_surface_flux.F b/src/core_ocean/shared/mpas_ocn_tracer_surface_flux_to_tend.F similarity index 69% rename from src/core_ocean/shared/mpas_ocn_tracer_surface_flux.F rename to src/core_ocean/shared/mpas_ocn_tracer_surface_flux_to_tend.F index f80dc624fd..f0eb2e3928 100644 --- a/src/core_ocean/shared/mpas_ocn_tracer_surface_flux.F +++ b/src/core_ocean/shared/mpas_ocn_tracer_surface_flux_to_tend.F @@ -7,19 +7,20 @@ ! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! ocn_tracer_surface_flux +! ocn_tracer_surface_flux_to_tend ! !> \brief MPAS ocean tracer surface flux !> \author Doug Jacobsen !> \date 12/17/12 !> \details -!> This module contains the routine for computing -!> surface flux tendencies. +!> This module contains the routine for computing +!> surface flux tendencies. ! !----------------------------------------------------------------------- -module ocn_tracer_surface_flux +module ocn_tracer_surface_flux_to_tend + use mpas_timer use mpas_derived_types use mpas_pool_routines @@ -64,12 +65,13 @@ module ocn_tracer_surface_flux !> \brief Computes tendency term for surface fluxes !> \author Doug Jacobsen !> \date 12/17/12 -!> \details +!> \details !> This routine computes the tendency for tracers based on surface fluxes. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_surface_flux_tend(meshPool, fractionAbsorbed, layerThickness, surfaceTracerFlux, tend, err)!{{{ + subroutine ocn_tracer_surface_flux_tend(meshPool, fractionAbsorbed, fractionAbsorbedRunoff, layerThickness, & + surfaceTracerFlux, surfaceTracerFluxRunoff, tend, err)!{{{ !----------------------------------------------------------------- ! ! input variables @@ -88,6 +90,12 @@ subroutine ocn_tracer_surface_flux_tend(meshPool, fractionAbsorbed, layerThickne real (kind=RKIND), dimension(:,:), intent(in) :: & fractionAbsorbed !< Input: Coefficients for the application of surface fluxes + real (kind=RKIND), dimension(:,:), intent(in), pointer :: & + surfaceTracerFluxRunoff !< Input: surface tracer fluxes from river runoff + + real (kind=RKIND), dimension(:,:), intent(in) :: & + fractionAbsorbedRunoff !< Input: Coefficients for the application of surface fluxes due to river runoff + !----------------------------------------------------------------- ! ! input/output variables @@ -111,10 +119,10 @@ subroutine ocn_tracer_surface_flux_tend(meshPool, fractionAbsorbed, layerThickne ! !----------------------------------------------------------------- - integer :: iCell, k, iTracer, nTracers - integer, pointer :: nCells, nVertLevels + integer :: iCell, k, iTracer, nTracers, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: maxLevelCell - integer, dimension(:,:), pointer :: cellMask real (kind=RKIND) :: remainingFlux @@ -122,29 +130,67 @@ subroutine ocn_tracer_surface_flux_tend(meshPool, fractionAbsorbed, layerThickne if (.not. surfaceTracerFluxOn) return - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_timer_start("surface_tracer_flux") + + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) nTracers = size(tend, dim=1) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - call mpas_pool_get_array(meshPool, 'cellMask', cellMask) + nCells = nCellsArray( 1 ) + + !$omp do schedule(runtime) private(remainingFlux, k, iTracer) do iCell = 1, nCells remainingFlux = 1.0_RKIND do k = 1, maxLevelCell(iCell) remainingFlux = remainingFlux - fractionAbsorbed(k, iCell) do iTracer = 1, nTracers - tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + cellMask(k, icell) * surfaceTracerFlux(iTracer, iCell) * fractionAbsorbed(k, iCell) + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + surfaceTracerFlux(iTracer, iCell) * fractionAbsorbed(k, iCell) end do end do if(maxLevelCell(iCell) > 0 .and. remainingFlux > 0.0_RKIND) then do iTracer = 1, nTracers - tend(iTracer, maxLevelCell(iCell), iCell) = tend(iTracer, maxLevelCell(iCell), iCell) + cellMask(k, iCell) * surfaceTracerFlux(iTracer, iCell) * remainingFlux + tend(iTracer, maxLevelCell(iCell), iCell) = tend(iTracer, maxLevelCell(iCell), iCell) & + + surfaceTracerFlux(iTracer, iCell) * remainingFlux end do end if end do + !$omp end do + + call mpas_timer_stop("surface_tracer_flux") + + ! now do runoff component + + if (associated(surfaceTracerFluxRunoff)) then + call mpas_timer_start("surface_tracer_runoff_flux") + + !$omp do schedule(runtime) private(remainingFlux, k, iTracer) + do iCell = 1, nCells + remainingFlux = 1.0_RKIND + do k = 1, maxLevelCell(iCell) + remainingFlux = remainingFlux - fractionAbsorbedRunoff(k, iCell) + + do iTracer = 1, nTracers + tend(iTracer, k, iCell) = tend(iTracer, k, iCell) + & + surfaceTracerFluxRunoff(iTracer, iCell) * fractionAbsorbedRunoff(k, iCell) + end do + end do + + if(maxLevelCell(iCell) > 0 .and. remainingFlux > 0.0_RKIND) then + do iTracer = 1, nTracers + tend(iTracer, maxLevelCell(iCell), iCell) = tend(iTracer, maxLevelCell(iCell), iCell) & + + surfaceTracerFluxRunoff(iTracer, iCell) * remainingFlux + end do + end if + end do + !$omp end do + + call mpas_timer_stop("surface_tracer_runoff_flux") + end if + !-------------------------------------------------------------------- @@ -157,7 +203,7 @@ end subroutine ocn_tracer_surface_flux_tend!}}} !> \brief Initializes ocean tracer surface flux quantities !> \author Doug Jacobsen !> \date 12/17/12 -!> \details +!> \details !> This routine initializes quantities related to surface fluxes in the ocean. ! !----------------------------------------------------------------------- @@ -169,12 +215,10 @@ subroutine ocn_tracer_surface_flux_init(err)!{{{ integer, intent(out) :: err !< Output: error flag logical, pointer :: config_disable_tr_sflux - character (len=StrKIND), pointer :: config_forcing_type err = 0 call mpas_pool_get_config(ocnConfigs, 'config_disable_tr_sflux', config_disable_tr_sflux) - call mpas_pool_get_config(ocnConfigs, 'config_forcing_type', config_forcing_type) surfaceTracerFluxOn = .true. @@ -182,15 +226,11 @@ subroutine ocn_tracer_surface_flux_init(err)!{{{ surfaceTracerFluxOn = .false. end if - if (config_forcing_type == trim('off')) then - surfaceTracerFluxOn = .false. - end if - end subroutine ocn_tracer_surface_flux_init!}}} !*********************************************************************** -end module ocn_tracer_surface_flux +end module ocn_tracer_surface_flux_to_tend !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_tracer_surface_restoring.F b/src/core_ocean/shared/mpas_ocn_tracer_surface_restoring.F new file mode 100644 index 0000000000..7d13b77c30 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_tracer_surface_restoring.F @@ -0,0 +1,506 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_tracer_surface_restoring +! +!> \brief MPAS ocean restoring +!> \author Todd Ringler +!> \date 06/08/2015 +!> \details +!> This module contains routines for computing the surface tracer flux due to restoring +! +!----------------------------------------------------------------------- + +module ocn_tracer_surface_restoring + + use mpas_kind_types + use mpas_derived_types + use mpas_pool_routines + use mpas_timer + use mpas_timekeeping + use mpas_forcing + use mpas_stream_manager + use ocn_constants + use ocn_framework_forcing + + implicit none + + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_tracer_surface_restoring_compute, & + ocn_get_surfaceSalinityData, & + ocn_salinity_restoring_forcing_write_restart, & + ocn_tracer_surface_restoring_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_tracer_surface_restoring_compute +! +!> \brief computes a surface tracer flux due to surface restoring +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine computes a surface tracer flux due to surface restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_surface_restoring_compute(groupName, nTracers, nCells, tracers, pistonVelocity, & + tracersSurfaceRestoringValue, tracersSurfaceFlux, indexSalinity, & + use_surface_salinity_monthly_restoring, salinity_restoring_constant_piston_velocity, err)!{{{ + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + character (len=*), intent(in) :: groupName !< Input: Name of tracer group + + ! scalars + integer, intent(in) :: & + nTracers, & + indexSalinity, & + nCells + + real (kind=RKIND), intent(in) :: salinity_restoring_constant_piston_velocity + + ! logicals + logical, intent(in) :: & + use_surface_salinity_monthly_restoring + + ! three dimensional arrays + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + tracers + + ! two dimensional ararys + real (kind=RKIND), dimension(:,:), intent(inout) :: & + pistonVelocity, & + tracersSurfaceRestoringValue + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(inout) :: & + tracersSurfaceFlux + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: Error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iCell, iLevel, iTracer + + err = 0 + + iLevel = 1 ! base surface flux restoring on tracer fields in the top layer + + !$omp do schedule(runtime) private(iTracer) + do iCell=1,nCells + do iTracer=1,nTracers + + ! For monthly salinity restoring, tracersSurfaceRestoringValue contains the zero-mean deltaS + if (trim(groupName) == 'activeTracers' & + .and. iTracer == indexSalinity & + .and. use_surface_salinity_monthly_restoring) then + tracersSurfaceFlux(iTracer, iCell) = tracersSurfaceFlux(iTracer, iCell) + & + salinity_restoring_constant_piston_velocity * & + tracersSurfaceRestoringValue(iTracer,iCell) + else + tracersSurfaceFlux(iTracer, iCell) = tracersSurfaceFlux(iTracer, iCell) - & + pistonVelocity(iTracer,iCell) * & + (tracers(iTracer, iLevel, iCell) - tracersSurfaceRestoringValue(iTracer,iCell)) + endif + enddo + enddo + !$omp end do + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_surface_restoring_compute!}}} + +!*********************************************************************** +! +! routine ocn_tracer_surface_restoring_init +! +!> \brief Initializes ocean surface restoring +!> \author Todd Ringler +!> \date 06/09/2015 +!> \details +!> This routine initializes fields required for tracer surface flux restoring +! +!----------------------------------------------------------------------- + + subroutine ocn_tracer_surface_restoring_init(err)!{{{ + + integer, intent(out) :: err !< Output: error flag + + err = 0 + + !-------------------------------------------------------------------- + + end subroutine ocn_tracer_surface_restoring_init!}}} + +!*********************************************************************** + +!*********************************************************************** +! +! routine get_surfaceSalinityData +! +!> \brief retrieve data needed to compute surface salinity restoring using monthly climatology +!> \author Mathew Maltrud +!> \date 09/23/16 +!> \details +!> This routine calls mpas_forcing routines to acquire needed surface salinity forcing data and interpolates +!> between time levels. +! +!----------------------------------------------------------------------- + + subroutine ocn_get_surfaceSalinityData( streamManager, & + domain, & + simulationClock, & + firstTimeStep) !{{{ + + type (MPAS_streamManager_type), intent(inout) :: streamManager + + type (domain_type) :: domain + type (MPAS_timeInterval_type) :: timeStepSurfaceSalinity + type (MPAS_clock_type) :: simulationClock + + type (MPAS_Time_Type) :: currTime + character(len=strKind) :: timeStamp + + logical, intent(in) :: firstTimeStep + character(len=strKind), pointer :: config_dt + real(kind=RKIND) :: dt, sumAreaDeltaS, sumArea, avgDeltaS, deltaS, sumAreaDeltaSGlobal, sumAreaGlobal + real(kind=RKIND) :: avgDeltaS1 + type (block_type), pointer :: block + + type (dm_info) :: dminfo + + type (mpas_pool_type), pointer :: diagnosticsPool + type (mpas_pool_type), pointer :: forcingPool + type (mpas_pool_type), pointer :: meshPool + type (mpas_pool_type), pointer :: statePool + type (mpas_pool_type), pointer :: tracersPool + type (mpas_pool_type), pointer :: surfaceSalinityMonthlyForcing + type (mpas_pool_type), pointer :: tracersSurfaceRestoringFieldsPool + + real (kind=RKIND), pointer :: salinity_restoring_max_difference, salinity_restoring_constant_piston_velocity + + real (kind=RKIND), dimension(:), pointer :: & + surfaceSalinityMonthlyClimatologyValue, iceFraction, areaCell, salinitySurfaceRestoringTendency + + real (kind=RKIND), dimension(:,:), pointer :: & + activeTracersSurfaceRestoringValue + + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers + + integer, pointer :: nCells, nCellsSolve, indexSalinity, indexSalinitySurfaceRestoringValue + integer :: iCell, timeLevel + integer, dimension(:), pointer :: landIceMask + + character(len=strKIND) :: & + forcingIntervalMonthly, & + forcingReferenceTimeMonthly + + integer :: ierr + logical, pointer :: & + config_do_restart, config_salinity_restoring_under_sea_ice + + integer, parameter :: nSums = 2 + real (kind=RKIND), dimension(nSums) :: reductions, sums + + ! initialize monthly forcing to be read from file + + call MPAS_pool_get_config(domain % configs, 'config_do_restart', config_do_restart) + + if (firstTimeStep) then + + currTime = mpas_get_clock_time( simulationClock, MPAS_NOW, ierr) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=ierr) + timeStamp = '0000'//trim(timeStamp(5:)) + + forcingIntervalMonthly = "0000-01-00_00:00:00" + forcingReferenceTimeMonthly = "0000-01-15_00:00:00" + + call MPAS_forcing_init_group( forcingGroupHead, & + "surfaceSalinityMonthlyClimatology", & + domain, & + timeStamp, & + '0000-01-01_00:00:00', & + '0001-00-00_00:00:00', & + config_do_restart) + + call MPAS_forcing_init_field( domain % streamManager, & + forcingGroupHead, & + 'surfaceSalinityMonthlyClimatology', & ! forcing group name + 'surfaceSalinityMonthlyClimatologyValue', & ! array name + 'surface_salinity_monthly_data', & ! stream name + 'surfaceSalinityMonthlyForcing', & ! pool name + 'surfaceSalinityMonthlyClimatologyValue', & ! array name + 'linear', & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field_data( forcingGroupHead, & + 'surfaceSalinityMonthlyClimatology', & + domain % streamManager, & + config_do_restart, & + .false.) + + return + endif ! first timestep + + call MPAS_pool_get_config(domain%configs, 'config_dt', config_dt) + call MPAS_pool_get_config(domain%configs, 'config_salinity_restoring_max_difference', & + salinity_restoring_max_difference) + + call mpas_pool_get_config(domain%configs, 'config_salinity_restoring_under_sea_ice', & + config_salinity_restoring_under_sea_ice) + call MPAS_pool_get_config(domain%configs, 'config_salinity_restoring_constant_piston_velocity', & + salinity_restoring_constant_piston_velocity) + call mpas_set_timeInterval(timeStepSurfaceSalinity,timeString=config_dt) + call mpas_get_timeInterval(timeStepSurfaceSalinity,dt=dt) + + call mpas_pool_get_subpool(domain % blocklist % structs, 'surfaceSalinityMonthlyForcing', & + surfaceSalinityMonthlyForcing) + call mpas_pool_get_array(surfaceSalinityMonthlyForcing, 'surfaceSalinityMonthlyClimatologyValue', & + surfaceSalinityMonthlyClimatologyValue) + + if(firstTimestep .and. config_do_restart) then + call MPAS_forcing_get_forcing(forcingGroupHead, & + 'surfaceSalinityMonthlyClimatology', streamManager, 0.0_RKIND) + else + call MPAS_forcing_get_forcing(forcingGroupHead, & + 'surfaceSalinityMonthlyClimatology', streamManager, dt) + end if + + sumAreaDeltaS = 0.0_RKIND + sumArea = 0.0_RKIND + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_subpool(block % structs, 'mesh', meshPool) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_subpool(block % structs, 'state', statePool) + call mpas_pool_get_subpool(block % structs, 'diagnostics', diagnosticsPool) + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields',tracersSurfaceRestoringFieldsPool) + ! Use time level 1, which is always the new time level + timeLevel = 1 + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) + + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_dimension(block % dimensions, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) + call mpas_pool_get_dimension(tracersSurfaceRestoringFieldsPool, 'index_salinitySurfaceRestoringValue', & + indexSalinitySurfaceRestoringValue) + call mpas_pool_get_subpool(block % structs, 'surfaceSalinityMonthlyForcing', & + surfaceSalinityMonthlyForcing) + call mpas_pool_get_array(surfaceSalinityMonthlyForcing, 'surfaceSalinityMonthlyClimatologyValue', & + surfaceSalinityMonthlyClimatologyValue) + + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersSurfaceRestoringValue', & + activeTracersSurfaceRestoringValue) + + call mpas_pool_get_array(forcingPool, 'iceFraction', iceFraction) + call mpas_pool_get_array(forcingPool, 'landIceMask', landIceMask) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + + ! This is not in a threaded region, so no openMP pragmas are needed. + if ( associated(landIceMask)) then + + if (config_salinity_restoring_under_sea_ice) then + + ! Simulation has landIceMask AND config_salinity_restoring_under_sea_ice=.true. + do iCell = 1, nCells + if (landIceMask(iCell)==1) then + ! Turn off salinity restoring in this cell + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) = 0.0_RKIND + else + ! Turn on salinity restoring in this cell + deltaS = surfaceSalinityMonthlyClimatologyValue(iCell) - activeTracers(indexSalinity,1,iCell) + if (deltaS > salinity_restoring_max_difference) deltaS = salinity_restoring_max_difference + if (deltaS < -salinity_restoring_max_difference) deltaS = -salinity_restoring_max_difference + + ! Salinity restoring below sea ice is always on, regardless of iceFraction value + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) = deltaS + endif + end do + + else ! config_salinity_restoring_under_sea_ice = .false. + + ! Simulation has landIceMask AND config_salinity_restoring_under_sea_ice=.false. (default) + do iCell = 1, nCells + if (landIceMask(iCell)==1) then + ! Turn off salinity restoring in this cell + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) = 0.0_RKIND + else + ! Turn on salinity restoring in this cell + deltaS = surfaceSalinityMonthlyClimatologyValue(iCell) - activeTracers(indexSalinity,1,iCell) + if (deltaS > salinity_restoring_max_difference) deltaS = salinity_restoring_max_difference + if (deltaS < -salinity_restoring_max_difference) deltaS = -salinity_restoring_max_difference + + ! Salinity restoring below sea ice tapers below partial sea ice + ! coverage, from full in the open ocean to zero when iceFraction=1.0 + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) = & + deltaS*(1.0_RKIND - iceFraction(iCell)) + endif + end do + + endif + + else ! associated(landIceMask)) = .false. + + if (config_salinity_restoring_under_sea_ice) then + + ! Simulation has NO landIceMask AND config_salinity_restoring_under_sea_ice=.true. + do iCell = 1, nCells + deltaS = surfaceSalinityMonthlyClimatologyValue(iCell) - activeTracers(indexSalinity,1,iCell) + if (deltaS > salinity_restoring_max_difference) deltaS = salinity_restoring_max_difference + if (deltaS < -salinity_restoring_max_difference) deltaS = -salinity_restoring_max_difference + + ! Salinity restoring below sea ice is always on, regardless of iceFraction value + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) = deltaS + end do + + else ! config_salinity_restoring_under_sea_ice = .false. + + ! Simulation has NO landIceMask AND config_salinity_restoring_under_sea_ice=.false. (default) + do iCell = 1, nCells + deltaS = surfaceSalinityMonthlyClimatologyValue(iCell) - activeTracers(indexSalinity,1,iCell) + if (deltaS > salinity_restoring_max_difference) deltaS = salinity_restoring_max_difference + if (deltaS < -salinity_restoring_max_difference) deltaS = -salinity_restoring_max_difference + + ! Salinity restoring below sea ice tapers below partial sea ice + ! coverage, from full in the open ocean to zero when iceFraction=1.0 + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) = & + deltaS*(1.0_RKIND - iceFraction(iCell)) + end do + + endif + + endif + + do iCell=1,nCellsSolve + deltaS = activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) + if (deltaS .ne. 0.0_RKIND) then + sumAreaDeltaS = sumAreaDeltaS + deltaS*areaCell(iCell) + sumArea = sumArea + areaCell(iCell) + endif + enddo + + block => block % next + end do + + ! Global sum to subtract global mean of deltaS + dminfo = domain % dminfo + sums(1) = sumAreaDeltaS + sums(2) = sumArea + call mpas_dmpar_sum_real_array(dminfo, nSums, sums(1:nSums), reductions(1:nSums)) + sumAreaDeltaSGlobal = reductions(1) + sumAreaGlobal = reductions(2) + avgDeltaS1 = sumAreaDeltaSGlobal/(sumAreaGlobal + 1.e-20_RKIND) + !LPV: There is no guarantee that a global sum of deltaS is BFB across + !decompositions, the next line rounds each value to 10 decimal places to + !ensure BFB results + avgDeltaS = float (int(avgDeltaS1 * 1.0E10_RKIND + 0.5_RKIND)) / 1.0E10_RKIND + + block => domain % blocklist + do while (associated(block)) + + call mpas_pool_get_dimension(block % dimensions, 'nCells', nCells) + call mpas_pool_get_subpool(block % structs, 'forcing', forcingPool) + call mpas_pool_get_array(forcingPool, 'iceFraction', iceFraction) + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceRestoringFields',tracersSurfaceRestoringFieldsPool) + call mpas_pool_get_array(diagnosticsPool,'salinitySurfaceRestoringTendency',salinitySurfaceRestoringTendency) + call mpas_pool_get_array(tracersSurfaceRestoringFieldsPool, 'activeTracersSurfaceRestoringValue', & + activeTracersSurfaceRestoringValue) + call mpas_pool_get_dimension(tracersSurfaceRestoringFieldsPool, 'index_salinitySurfaceRestoringValue', & + indexSalinitySurfaceRestoringValue) + + do iCell = 1, nCells + deltaS = activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) + if (deltaS .ne. 0.0_RKIND) then + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) = & + deltaS - avgDeltaS + salinitySurfaceRestoringTendency(iCell) = & + activeTracersSurfaceRestoringValue(indexSalinitySurfaceRestoringValue,iCell) & + * salinity_restoring_constant_piston_velocity + else + salinitySurfaceRestoringTendency(iCell) = 0.0_RKIND + endif + enddo + + block => block % next + end do + + end subroutine ocn_get_surfaceSalinityData!}}} + +!*********************************************************************** +! +! routine ocn_salinity_restoring_forcing_write_restart +! +!> \brief writes restart timestamp for salinity restoring data to be read in on future restart +!> \author Mathew Maltrud +!> \date 10/17/2016 + +! +!----------------------------------------------------------------------- + + subroutine ocn_salinity_restoring_forcing_write_restart(domain)!{{{ + + type(domain_type) :: domain + + call MPAS_forcing_write_restart_times(forcingGroupHead) + + end subroutine ocn_salinity_restoring_forcing_write_restart!}}} + +!*********************************************************************** + +end module ocn_tracer_surface_restoring + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_vel_coriolis.F b/src/core_ocean/shared/mpas_ocn_vel_coriolis.F index a181b9720f..ca8d97331e 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_coriolis.F +++ b/src/core_ocean/shared/mpas_ocn_vel_coriolis.F @@ -13,14 +13,15 @@ !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 !> \details -!> This module contains the routine for computing -!> tendencies from the coriolis force. +!> This module contains the routine for computing +!> tendencies from the coriolis force. !> ! !----------------------------------------------------------------------- module ocn_vel_coriolis + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants @@ -64,13 +65,14 @@ module ocn_vel_coriolis !> \brief Computes tendency term for coriolis force !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the coriolis tendency for momentum !> based on current state. ! !----------------------------------------------------------------------- - subroutine ocn_vel_coriolis_tend(meshPool, normalizedRelativeVorticityEdge, normalizedPlanetaryVorticityEdge, layerThicknessEdge, normalVelocity, kineticEnergyCell, tend, err)!{{{ + subroutine ocn_vel_coriolis_tend(meshPool, normalizedRelativeVorticityEdge, normalizedPlanetaryVorticityEdge, & + layerThicknessEdge, normalVelocity, kineticEnergyCell, tend, err)!{{{ !----------------------------------------------------------------- ! @@ -117,14 +119,18 @@ subroutine ocn_vel_coriolis_tend(meshPool, normalizedRelativeVorticityEdge, norm real (kind=RKIND), dimension(:), pointer :: dcEdge integer :: j, k - integer :: cell1, cell2, iEdge, eoe - integer, pointer :: nEdgesSolve - real (kind=RKIND) :: workVorticity, q, invLength + integer :: cell1, cell2, iEdge, eoe, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nEdgesArray + real (kind=RKIND) :: workVorticity, invLength, edgeWeight, r_tmp + real (kind=RKIND), dimension(:), allocatable :: qArr err = 0 if ( .not. coriolisOn ) return + call mpas_timer_start("coriolis") + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'nEdgesOnEdge', nEdgesOnEdge) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) @@ -133,29 +139,48 @@ subroutine ocn_vel_coriolis_tend(meshPool, normalizedRelativeVorticityEdge, norm call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + + nEdges = nEdgesArray( 1 ) - do iEdge = 1, nEdgesSolve + allocate( qArr(nVertLevels) ) + + !$omp do schedule(runtime) private(cell1, cell2, invLength, k, j, eoe, workVorticity) + do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - invLength = 1.0 / dcEdge(iEdge) + invLength = 1.0_RKIND / dcEdge(iEdge) do k = 1, maxLevelEdgeTop(iEdge) + qArr(k) = 0.0_RKIND + end do + + do j = 1, nEdgesOnEdge(iEdge) + eoe = edgesOnEdge(j, iEdge) + edgeWeight = weightsOnEdge(j, iEdge) - q = 0.0 - do j = 1,nEdgesOnEdge(iEdge) - eoe = edgesOnEdge(j,iEdge) - workVorticity = 0.5 & - * ( normalizedRelativeVorticityEdge(k,iEdge) + RK4On * normalizedPlanetaryVorticityEdge(k,iEdge) & - + normalizedRelativeVorticityEdge(k,eoe) + RK4On * normalizedPlanetaryVorticityEdge(k,eoe)) - q = q + weightsOnEdge(j,iEdge) * normalVelocity(k,eoe) * workVorticity * layerThicknessEdge(k,eoe) + do k = 1, maxLevelEdgeTop(iEdge) + workVorticity = 0.5_RKIND & + * ( normalizedRelativeVorticityEdge(k, iEdge) + RK4On * normalizedPlanetaryVorticityEdge(k, iEdge) & + + normalizedRelativeVorticityEdge(k, eoe) + RK4On * normalizedPlanetaryVorticityEdge(k, eoe)) + qArr(k) = qArr(k) + edgeWeight * normalVelocity(k, eoe) * workVorticity * layerThicknessEdge(k, eoe) end do - tend(k,iEdge) = tend(k,iEdge) + edgeMask(k, iEdge) * (q - ( kineticEnergyCell(k,cell2) - kineticEnergyCell(k,cell1) ) * invLength ) + end do + do k = 1, maxLevelEdgeTop(iEdge) + tend(k, iEdge) = tend(k, iEdge) + edgeMask(k, iEdge) * ( qArr(k) - ( kineticEnergyCell(k, cell2) & + - kineticEnergyCell(k, cell1) ) * invLength ) end do + end do + !$omp end do + + deallocate( qArr ) + + call mpas_timer_stop("coriolis") !-------------------------------------------------------------------- @@ -168,11 +193,11 @@ end subroutine ocn_vel_coriolis_tend!}}} !> \brief Initializes ocean momentum horizontal mixing quantities !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> horizontal velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> horizontal velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- @@ -205,7 +230,7 @@ subroutine ocn_vel_coriolis_init(err)!{{{ RK4On = 1 elseif ( trim( config_time_integrator ) == 'split_explicit' & .or. trim( config_time_integrator ) == 'unsplit_explicit') then - ! For split explicit, Coriolis tendency uses eta/h because the Coriolis term + ! For split explicit, Coriolis tendency uses eta/h because the Coriolis term ! is added separately to the momentum tendencies. RK4On = 0 end if diff --git a/src/core_ocean/shared/mpas_ocn_vel_forcing.F b/src/core_ocean/shared/mpas_ocn_vel_forcing.F index fced50b619..e9befd1436 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_forcing.F +++ b/src/core_ocean/shared/mpas_ocn_vel_forcing.F @@ -13,8 +13,8 @@ !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 !> \details -!> This module contains the main driver routine for computing -!> tendencies from forcings. +!> This module contains the main driver routine for computing +!> tendencies from forcings. ! !----------------------------------------------------------------------- @@ -25,7 +25,8 @@ module ocn_vel_forcing use ocn_constants use ocn_forcing - use ocn_vel_forcing_windstress + use ocn_vel_forcing_surface_stress + use ocn_vel_forcing_explicit_bottom_drag use ocn_vel_forcing_rayleigh implicit none @@ -65,7 +66,7 @@ module ocn_vel_forcing !> \brief Computes tendency term from forcings !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date 15 September 2011 -!> \details +!> \details !> This routine computes the forcing tendency for momentum !> based on current state and user choices of forcings. !> Multiple forcings may be chosen and added together. These @@ -75,7 +76,8 @@ module ocn_vel_forcing ! !----------------------------------------------------------------------- - subroutine ocn_vel_forcing_tend(meshPool, normalVelocity, surfaceWindStress, layerThicknessEdge, tend, err)!{{{ + subroutine ocn_vel_forcing_tend(meshPool, normalVelocity, surfaceFluxAttenuationCoefficient, & + surfaceStress, kineticEnergyCell, layerThicknessEdge, tend, err)!{{{ !----------------------------------------------------------------- ! @@ -84,10 +86,12 @@ subroutine ocn_vel_forcing_tend(meshPool, normalVelocity, surfaceWindStress, lay !----------------------------------------------------------------- real (kind=RKIND), dimension(:,:), intent(in) :: & - normalVelocity !< Input: Normal velocity at edges + normalVelocity, & !< Input: Normal velocity at edges + kineticEnergyCell !< Input: kinetic energy at cell real (kind=RKIND), dimension(:), intent(in) :: & - surfaceWindStress !< Input: Wind stress at surface of normal velocity at edges + surfaceFluxAttenuationCoefficient, & !< Input: attenuation coefficient for surface fluxes at cell centers + surfaceStress !< Input: surface stress at edges real (kind=RKIND), dimension(:,:), intent(in) :: & layerThicknessEdge !< Input: thickness at edge @@ -118,20 +122,29 @@ subroutine ocn_vel_forcing_tend(meshPool, normalVelocity, surfaceWindStress, lay ! !----------------------------------------------------------------- - integer :: err1, err2 + integer :: err1 !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- - call ocn_vel_forcing_windstress_tend(meshPool, surfaceWindStress, layerThicknessEdge, tend, err1) - call ocn_vel_forcing_rayleigh_tend(meshPool, normalVelocity, tend, err2) + err = 0 - err = ior(err1, err2) + call ocn_vel_forcing_surface_stress_tend(meshPool, surfaceFluxAttenuationCoefficient, & + surfaceStress, layerThicknessEdge, tend, err1) + err = ior(err, err1) + + call ocn_vel_forcing_explicit_bottom_drag_tend(meshPool, normalVelocity, & + kineticEnergyCell, layerThicknessEdge, tend, err1) + + err = ior(err, err1) + + call ocn_vel_forcing_rayleigh_tend(meshPool, normalVelocity, tend, err1) + err = ior(err, err1) !-------------------------------------------------------------------- @@ -144,11 +157,11 @@ end subroutine ocn_vel_forcing_tend!}}} !> \brief Initializes ocean forcings !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes quantities related to forcings -!> in the ocean. Since a multiple forcings are available, +!> \details +!> This routine initializes quantities related to forcings +!> in the ocean. Since a multiple forcings are available, !> this routine primarily calls the -!> individual init routines for each forcing. +!> individual init routines for each forcing. ! !----------------------------------------------------------------------- @@ -166,7 +179,8 @@ subroutine ocn_vel_forcing_init(err)!{{{ integer :: err1, err2 - call ocn_vel_forcing_windstress_init(err1) + call ocn_vel_forcing_surface_stress_init(err1) + call ocn_vel_forcing_explicit_bottom_drag_init(err1) call ocn_vel_forcing_rayleigh_init(err2) err = ior(err1, err2) diff --git a/src/core_ocean/shared/mpas_ocn_vel_forcing_explicit_bottom_drag.F b/src/core_ocean/shared/mpas_ocn_vel_forcing_explicit_bottom_drag.F new file mode 100644 index 0000000000..4eb59b58d7 --- /dev/null +++ b/src/core_ocean/shared/mpas_ocn_vel_forcing_explicit_bottom_drag.F @@ -0,0 +1,210 @@ +! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) +! and the University Corporation for Atmospheric Research (UCAR). +! +! Unless noted otherwise source code is licensed under the BSD license. +! Additional copyright and license information can be found in the LICENSE file +! distributed with this code, or at http://mpas-dev.github.com/license.html +! +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! ocn_vel_forcing_explicit_bottom_drag +! +!> \brief MPAS ocean explicit bottom drag +!> \author Mark Petersen +!> \date August 2017 +!> \details +!> This module contains the routine for computing +!> tendencies from explicit bottom drag. +! +!----------------------------------------------------------------------- + +module ocn_vel_forcing_explicit_bottom_drag + + use mpas_derived_types + use mpas_pool_routines + use mpas_timer + + use ocn_constants + use ocn_forcing + + implicit none + private + save + + !-------------------------------------------------------------------- + ! + ! Public parameters + ! + !-------------------------------------------------------------------- + + !-------------------------------------------------------------------- + ! + ! Public member functions + ! + !-------------------------------------------------------------------- + + public :: ocn_vel_forcing_explicit_bottom_drag_tend, & + ocn_vel_forcing_explicit_bottom_drag_init + + !-------------------------------------------------------------------- + ! + ! Private module variables + ! + !-------------------------------------------------------------------- + + logical :: explicitBottomDragOn + real (kind=RKIND) :: explicitBottomDragCoef + +!*********************************************************************** + +contains + +!*********************************************************************** +! +! routine ocn_vel_forcing_explicit_bottom_drag_tend +! +!> \brief Computes tendency term from explicit bottom drag +!> \author Mark Petersen +!> \date 15 August 2017 +!> \details +!> This routine computes the explicit bottom drag tendency for momentum +!> based on current state. +! +!----------------------------------------------------------------------- + + subroutine ocn_vel_forcing_explicit_bottom_drag_tend(meshPool, normalVelocity, & !{{{ + kineticEnergyCell, layerThicknessEdge, tend, err) + + !----------------------------------------------------------------- + ! + ! input variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(in) :: & + normalVelocity, &!< Input: velocity + kineticEnergyCell, &!< Input: kinetic energy at cell + layerThicknessEdge !< Input: thickness at edge + + type (mpas_pool_type), intent(in) :: & + meshPool !< Input: mesh information + + !----------------------------------------------------------------- + ! + ! input/output variables + ! + !----------------------------------------------------------------- + + real (kind=RKIND), dimension(:,:), intent(inout) :: & + tend !< Input/Output: velocity tendency + + !----------------------------------------------------------------- + ! + ! output variables + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + !----------------------------------------------------------------- + ! + ! local variables + ! + !----------------------------------------------------------------- + + integer :: iEdge, k, cell1, cell2, nEdges + integer, dimension(:), pointer :: nEdgesArray + integer, dimension(:), pointer :: maxLevelEdgeTop + integer, dimension(:,:), pointer :: cellsOnEdge + + err = 0 + + if ( .not. explicitBottomDragOn ) return + + call mpas_timer_start('vel explicit bottom drag') + + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + + nEdges = nEdgesArray( 1 ) + + !$omp do schedule(runtime) private(k, cell1, cell2) + do iEdge = 1, nEdges + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + k = maxLevelEdgeTop(iEdge) + + ! Explicit bottom drag term: + ! du/dt = ... - c |u| u / h + ! appied to bottom layer only. + ! This term comes from the bottom boundary condition in the vertical + ! momentum mixing, and is explicit if both |u| and u are chosen to be at + ! time level n. + + tend(k,iEdge) = tend(k,iEdge) - explicitBottomDragCoef * & + sqrt(kineticEnergyCell(k,cell1) + kineticEnergyCell(k,cell2)) * normalVelocity(k,iEdge) / layerThicknessEdge(k,iEdge) + + enddo + !$omp end do + + call mpas_timer_stop('vel explicit bottom drag') + + !-------------------------------------------------------------------- + + end subroutine ocn_vel_forcing_explicit_bottom_drag_tend!}}} + +!*********************************************************************** +! +! routine ocn_vel_forcing_explicit_bottom_drag_init +! +!> \brief Initializes ocean explicit bottom drag forcing +!> \author Mark Petersen +!> \date August 2017 +!> \details +!> This routine initializes quantities related to explicit bottom drag +!> in the ocean. +! +!----------------------------------------------------------------------- + + subroutine ocn_vel_forcing_explicit_bottom_drag_init(err)!{{{ + + !-------------------------------------------------------------------- + + !----------------------------------------------------------------- + ! + ! call individual init routines for each parameterization + ! + !----------------------------------------------------------------- + + integer, intent(out) :: err !< Output: error flag + + logical, pointer :: config_disable_vel_explicit_bottom_drag + logical, pointer :: config_use_explicit_bottom_drag + real (kind=RKIND), pointer :: config_explicit_bottom_drag_coeff + + err = 0 + + call mpas_pool_get_config(ocnConfigs, 'config_use_explicit_bottom_drag', config_use_explicit_bottom_drag) + call mpas_pool_get_config(ocnConfigs, 'config_explicit_bottom_drag_coeff', config_explicit_bottom_drag_coeff) + call mpas_pool_get_config(ocnConfigs, 'config_disable_vel_explicit_bottom_drag', config_disable_vel_explicit_bottom_drag) + + explicitBottomDragCoef = 0.0_RKIND + + if (config_use_explicit_bottom_drag) then + explicitBottomDragOn = .true. + explicitBottomDragCoef = config_explicit_bottom_drag_coeff + endif + + if (config_disable_vel_explicit_bottom_drag) explicitBottomDragOn = .false. + + !-------------------------------------------------------------------- + + end subroutine ocn_vel_forcing_explicit_bottom_drag_init!}}} + +!*********************************************************************** + +end module ocn_vel_forcing_explicit_bottom_drag + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_vel_forcing_rayleigh.F b/src/core_ocean/shared/mpas_ocn_vel_forcing_rayleigh.F index 529a6e5f5a..07a1f10d46 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_forcing_rayleigh.F +++ b/src/core_ocean/shared/mpas_ocn_vel_forcing_rayleigh.F @@ -13,7 +13,7 @@ !> \author Todd Ringler !> \date 5 January 2012 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> tendencies based on linear Rayleigh friction. ! !----------------------------------------------------------------------- @@ -22,6 +22,7 @@ module ocn_vel_forcing_rayleigh use mpas_derived_types use mpas_pool_routines + use mpas_timer use ocn_constants implicit none @@ -49,8 +50,8 @@ module ocn_vel_forcing_rayleigh ! !-------------------------------------------------------------------- - logical :: rayleighFrictionOn - real (kind=RKIND) :: rayleighDampingCoef + logical :: rayleighFrictionOn, rayleighBottomFrictionOn + real (kind=RKIND) :: rayleighDampingCoef, rayleighBottomDampingCoef !*********************************************************************** @@ -64,7 +65,7 @@ module ocn_vel_forcing_rayleigh !> \brief Computes tendency term from Rayleigh friction !> \author Todd Ringler !> \date 5 January 2012 -!> \details +!> \details !> This routine computes the Rayleigh friction tendency for momentum !> based on current state. ! @@ -79,7 +80,7 @@ subroutine ocn_vel_forcing_rayleigh_tend(meshPool, normalVelocity, tend, err)!{{ !----------------------------------------------------------------- real (kind=RKIND), dimension(:,:), intent(in) :: & - normalVelocity !< Input: velocity + normalVelocity !< Input: velocity type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information @@ -107,14 +108,14 @@ subroutine ocn_vel_forcing_rayleigh_tend(meshPool, normalVelocity, tend, err)!{{ ! !----------------------------------------------------------------- - integer :: iEdge, k - integer, pointer :: nEdgesSolve + integer :: iEdge, k, nEdges + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- @@ -123,17 +124,37 @@ subroutine ocn_vel_forcing_rayleigh_tend(meshPool, normalVelocity, tend, err)!{{ if ( .not. rayleighFrictionOn ) return - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_timer_start('vel rayleigh forcing') + + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) - do iEdge = 1, nEdgesSolve - do k = 1, maxLevelEdgeTop(iEdge) + nEdges = nEdgesArray( 1 ) + + if ( rayleighFrictionOn ) then + !$omp do schedule(runtime) private(k) + do iEdge = 1, nEdges + do k = 1, maxLevelEdgeTop(iEdge) + + tend(k,iEdge) = tend(k,iEdge) - rayleighDampingCoef * normalVelocity(k,iEdge) + + enddo + enddo + !$omp end do + endif + + if ( rayleighBottomFrictionOn ) then + !$omp do schedule(runtime) private(k) + do iEdge = 1, nEdges + k = maxLevelEdgeTop(iEdge) tend(k,iEdge) = tend(k,iEdge) - rayleighDampingCoef * normalVelocity(k,iEdge) - enddo - enddo + enddo + !$omp end do + endif + call mpas_timer_stop('vel rayleigh forcing') !-------------------------------------------------------------------- @@ -146,9 +167,9 @@ end subroutine ocn_vel_forcing_rayleigh_tend!}}} !> \brief Initializes ocean Rayleigh friction !> \author Todd Ringler !> \date 5 January 2012 -!> \details -!> This routine initializes quantities related to -!> in the ocean. +!> \details +!> This routine initializes quantities related to +!> in the ocean. ! !----------------------------------------------------------------------- @@ -164,21 +185,30 @@ subroutine ocn_vel_forcing_rayleigh_init(err)!{{{ integer, intent(out) :: err !< Output: error flag - logical, pointer :: config_Rayleigh_friction - real (kind=RKIND), pointer :: config_Rayleigh_damping_coeff + logical, pointer :: config_Rayleigh_friction, config_Rayleigh_bottom_friction + real (kind=RKIND), pointer :: config_Rayleigh_damping_coeff, config_Rayleigh_bottom_damping_coeff err = 0 call mpas_pool_get_config(ocnConfigs, 'config_Rayleigh_friction', config_Rayleigh_friction) + call mpas_pool_get_config(ocnConfigs, 'config_Rayleigh_bottom_friction', config_Rayleigh_bottom_friction) call mpas_pool_get_config(ocnConfigs, 'config_Rayleigh_damping_coeff', config_Rayleigh_damping_coeff) + call mpas_pool_get_config(ocnConfigs, 'config_Rayleigh_bottom_damping_coeff', config_Rayleigh_bottom_damping_coeff) - rayleighDampingCoef = 0.0 + rayleighDampingCoef = 0.0_RKIND if (config_Rayleigh_friction) then rayleighFrictionOn = .true. rayleighDampingCoef = config_Rayleigh_damping_coeff endif + rayleighBottomDampingCoef = 0.0_RKIND + + if (config_Rayleigh_bottom_friction) then + rayleighBottomFrictionOn = .true. + rayleighBottomDampingCoef = config_Rayleigh_bottom_damping_coeff + endif + !-------------------------------------------------------------------- end subroutine ocn_vel_forcing_rayleigh_init!}}} diff --git a/src/core_ocean/shared/mpas_ocn_vel_forcing_windstress.F b/src/core_ocean/shared/mpas_ocn_vel_forcing_surface_stress.F similarity index 63% rename from src/core_ocean/shared/mpas_ocn_vel_forcing_windstress.F rename to src/core_ocean/shared/mpas_ocn_vel_forcing_surface_stress.F index 1043e9c9f5..b8ce8c54d3 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_forcing_windstress.F +++ b/src/core_ocean/shared/mpas_ocn_vel_forcing_surface_stress.F @@ -7,21 +7,22 @@ ! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! -! ocn_vel_forcing_windstress +! ocn_vel_forcing_surface_stress ! -!> \brief MPAS ocean wind stress +!> \brief MPAS ocean surface stress !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 !> \details -!> This module contains the routine for computing -!> tendencies from wind stress. +!> This module contains the routine for computing +!> tendencies from surface stress. ! !----------------------------------------------------------------------- -module ocn_vel_forcing_windstress +module ocn_vel_forcing_surface_stress use mpas_derived_types use mpas_pool_routines + use mpas_timer use ocn_constants use ocn_forcing @@ -42,8 +43,8 @@ module ocn_vel_forcing_windstress ! !-------------------------------------------------------------------- - public :: ocn_vel_forcing_windstress_tend, & - ocn_vel_forcing_windstress_init + public :: ocn_vel_forcing_surface_stress_tend, & + ocn_vel_forcing_surface_stress_init !-------------------------------------------------------------------- ! @@ -51,7 +52,7 @@ module ocn_vel_forcing_windstress ! !-------------------------------------------------------------------- - logical :: windStressOn + logical :: surfaceStressOn !*********************************************************************** @@ -59,18 +60,19 @@ module ocn_vel_forcing_windstress !*********************************************************************** ! -! routine ocn_vel_forcing_windstress_tend +! routine ocn_vel_forcing_surface_stress_tend ! -!> \brief Computes tendency term from wind stress +!> \brief Computes tendency term from surface stress !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date 15 September 2011 -!> \details -!> This routine computes the wind stress tendency for momentum +!> \details +!> This routine computes the surface stress tendency for momentum !> based on current state. ! !----------------------------------------------------------------------- - subroutine ocn_vel_forcing_windstress_tend(meshPool, surfaceWindStress, layerThicknessEdge, tend, err)!{{{ + subroutine ocn_vel_forcing_surface_stress_tend(meshPool, surfaceFluxAttenuationCoefficient, surfaceStress, & !{{{ + layerThicknessEdge, tend, err) !----------------------------------------------------------------- ! @@ -79,7 +81,8 @@ subroutine ocn_vel_forcing_windstress_tend(meshPool, surfaceWindStress, layerThi !----------------------------------------------------------------- real (kind=RKIND), dimension(:), intent(in) :: & - surfaceWindStress !< Input: Wind stress at surface + surfaceStress, & !< Input: Wind stress at surface + surfaceFluxAttenuationCoefficient !< Input: attenuation coefficient for surface fluxes real (kind=RKIND), dimension(:,:), intent(in) :: & layerThicknessEdge !< Input: thickness at edge @@ -110,47 +113,55 @@ subroutine ocn_vel_forcing_windstress_tend(meshPool, surfaceWindStress, layerThi ! !----------------------------------------------------------------- - integer :: iEdge, k - integer, pointer :: nEdgesSolve + integer :: iEdge, k, cell1, cell2, nEdges + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop - integer, dimension(:,:), pointer :: edgeMask - - real (kind=RKIND) :: transmissionCoeffTop, transmissionCoeffBot, zTop, zBot, remainingStress + integer, dimension(:,:), pointer :: edgeMask, cellsOnEdge - real (kind=RKIND), pointer :: config_density0 + real (kind=RKIND) :: transmissionCoeffTop, transmissionCoeffBot, zTop, zBot, remainingStress, & + attenuationCoeff !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- err = 0 - if ( .not. windStressOn ) return + if ( .not. surfaceStressOn ) return - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) + call mpas_timer_start('vel surface stress') - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) + call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) + + nEdges = nEdgesArray ( 1 ) - do iEdge = 1, nEdgesSolve + !$omp do schedule(runtime) private(zTop, transmissionCoeffBot, remainingStress, k, transmissionCoeffTop, & + !$omp cell1, cell2, attenuationCoeff, zBot) + do iEdge = 1, nEdges zTop = 0.0_RKIND - transmissionCoeffTop = ocn_forcing_transmission(zTop) + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + attenuationCoeff = 0.5_RKIND * (surfaceFluxAttenuationCoefficient(cell1) & + + surfaceFluxAttenuationCoefficient(cell2)) + transmissionCoeffTop = ocn_forcing_transmission(zTop, attenuationCoeff) remainingStress = 1.0_RKIND do k = 1, maxLevelEdgeTop(iEdge) zBot = zTop - layerThicknessEdge(k, iEdge) - transmissionCoeffBot = ocn_forcing_transmission(zBot) + transmissionCoeffBot = ocn_forcing_transmission(zBot, attenuationCoeff) remainingStress = remainingStress - (transmissionCoeffTop - transmissionCoeffBot) - tend(k,iEdge) = tend(k,iEdge) + edgeMask(k, iEdge) * surfaceWindStress(iEdge) & - * (transmissionCoeffTop - transmissionCoeffBot) / config_density0 / layerThicknessEdge(k,iEdge) + tend(k,iEdge) = tend(k,iEdge) + edgeMask(k, iEdge) * surfaceStress(iEdge) & + * (transmissionCoeffTop - transmissionCoeffBot) / rho_sw / layerThicknessEdge(k,iEdge) zTop = zBot transmissionCoeffTop = transmissionCoeffBot @@ -158,30 +169,32 @@ subroutine ocn_vel_forcing_windstress_tend(meshPool, surfaceWindStress, layerThi if ( maxLevelEdgeTop(iEdge) > 0 .and. remainingStress > 0.0_RKIND) then tend(maxLevelEdgeTop(iEdge), iEdge) = tend(maxLevelEdgeTop(iEdge), iEdge) & - + edgeMask(maxLevelEdgeTop(iEdge), iEdge) * surfaceWindStress(iEdge) * remainingStress & - / config_density0 / layerThicknessEdge(maxLevelEdgeTop(iEdge), iEdge) + + edgeMask(maxLevelEdgeTop(iEdge), iEdge) * surfaceStress(iEdge) * remainingStress & + / rho_sw / layerThicknessEdge(maxLevelEdgeTop(iEdge), iEdge) end if enddo + !$omp end do + call mpas_timer_stop('vel surface stress') !-------------------------------------------------------------------- - end subroutine ocn_vel_forcing_windstress_tend!}}} + end subroutine ocn_vel_forcing_surface_stress_tend!}}} !*********************************************************************** ! -! routine ocn_vel_forcing_windstress_init +! routine ocn_vel_forcing_surface_stress_init ! -!> \brief Initializes ocean wind stress forcing +!> \brief Initializes ocean surface stress forcing !> \author Doug Jacobsen, Mark Petersen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes quantities related to wind stress -!> in the ocean. +!> \details +!> This routine initializes quantities related to surface stress +!> in the ocean. ! !----------------------------------------------------------------------- - subroutine ocn_vel_forcing_windstress_init(err)!{{{ + subroutine ocn_vel_forcing_surface_stress_init(err)!{{{ !-------------------------------------------------------------------- @@ -193,30 +206,23 @@ subroutine ocn_vel_forcing_windstress_init(err)!{{{ integer, intent(out) :: err !< Output: error flag - logical, pointer :: config_disable_vel_windstress - character (len=StrKIND), pointer :: config_forcing_type - - call mpas_pool_get_config(ocnConfigs, 'config_disable_vel_windstress', config_disable_vel_windstress) - call mpas_pool_get_config(ocnConfigs, 'config_forcing_type', config_forcing_type) - - windStressOn = .true. + logical, pointer :: config_disable_vel_surface_stress - if(config_disable_vel_windstress) windStressOn = .false. + call mpas_pool_get_config(ocnConfigs, 'config_disable_vel_surface_stress', config_disable_vel_surface_stress) - if (config_forcing_type == trim('off')) then - windStressOn = .false. - end if + surfaceStressOn = .true. + if(config_disable_vel_surface_stress) surfaceStressOn = .false. err = 0 !-------------------------------------------------------------------- - end subroutine ocn_vel_forcing_windstress_init!}}} + end subroutine ocn_vel_forcing_surface_stress_init!}}} !*********************************************************************** -end module ocn_vel_forcing_windstress +end module ocn_vel_forcing_surface_stress !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! vim: foldmethod=marker diff --git a/src/core_ocean/shared/mpas_ocn_vel_hmix.F b/src/core_ocean/shared/mpas_ocn_vel_hmix.F index 86d2e6e258..7aff798ac0 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_hmix.F +++ b/src/core_ocean/shared/mpas_ocn_vel_hmix.F @@ -13,8 +13,8 @@ !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 !> \details -!> This module contains the main driver routine for computing -!> horizontal mixing tendencies. +!> This module contains the main driver routine for computing +!> horizontal mixing tendencies. !> !> It provides an init and a tend function. Each are described below. ! @@ -22,9 +22,10 @@ module ocn_vel_hmix + use mpas_timer use mpas_derived_types use mpas_pool_routines - use mpas_timer + use mpas_threading use ocn_vel_hmix_del2 use ocn_vel_hmix_leith use ocn_vel_hmix_del4 @@ -68,7 +69,7 @@ module ocn_vel_hmix !> \brief Computes tendency term for horizontal momentum mixing !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for momentum !> based on current state and user choices of mixing parameterization. !> Multiple parameterizations may be chosen and added together. These @@ -78,8 +79,8 @@ module ocn_vel_hmix ! !----------------------------------------------------------------------- - subroutine ocn_vel_hmix_tend(meshPool, divergence, relativeVorticity, normalVelocity, tangentialVelocity, viscosity, & - tend, scratchPool, err)!{{{ + subroutine ocn_vel_hmix_tend(meshPool, scratchPool, divergence, relativeVorticity, normalVelocity, tangentialVelocity, & + viscosity, tend, err)!{{{ !----------------------------------------------------------------- ! @@ -90,6 +91,8 @@ subroutine ocn_vel_hmix_tend(meshPool, divergence, relativeVorticity, normalVelo type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information + type (mpas_pool_type), intent(inout) :: scratchPool !< Input: scratch variables + real (kind=RKIND), dimension(:,:), intent(in) :: & divergence !< Input: velocity divergence @@ -114,9 +117,6 @@ subroutine ocn_vel_hmix_tend(meshPool, divergence, relativeVorticity, normalVelo real (kind=RKIND), dimension(:,:), intent(inout) :: & tend !< Input/Output: velocity tendency - type (mpas_pool_type), intent(inout) :: & - scratchPool !< Input: Scratch structure - !----------------------------------------------------------------- ! ! output variables @@ -136,41 +136,35 @@ subroutine ocn_vel_hmix_tend(meshPool, divergence, relativeVorticity, normalVelo !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- if(.not.hmixOn) return - viscosity = 0.0 + call mpas_timer_start("vel hmix") + + viscosity = 0.0_RKIND err = 0 - call mpas_timer_start("del2") call ocn_vel_hmix_del2_tend(meshPool, divergence, relativeVorticity, viscosity, tend, err1) - call mpas_timer_stop("del2") err = ior(err1, err) - call mpas_timer_start("del2_tensor") call ocn_vel_hmix_del2_tensor_tend(meshPool, normalVelocity, tangentialVelocity, viscosity, scratchPool, tend, err1) - call mpas_timer_stop("del2_tensor") err = ior(err1, err) - call mpas_timer_start("leith") call ocn_vel_hmix_leith_tend(meshPool, divergence, relativeVorticity, viscosity, tend, err1) - call mpas_timer_stop("leith") err = ior(err1, err) - call mpas_timer_start("del4") - call ocn_vel_hmix_del4_tend(meshPool, divergence, relativeVorticity, tend, err1) - call mpas_timer_stop("del4") + call ocn_vel_hmix_del4_tend(meshPool, scratchPool, divergence, relativeVorticity, tend, err1) err = ior(err1, err) - call mpas_timer_start("del4_tensor") call ocn_vel_hmix_del4_tensor_tend(meshPool, normalVelocity, tangentialVelocity, viscosity, scratchPool, tend, err1) - call mpas_timer_stop("del4_tensor") err = ior(err1, err) + call mpas_timer_stop("vel hmix") + !-------------------------------------------------------------------- end subroutine ocn_vel_hmix_tend!}}} @@ -182,11 +176,11 @@ end subroutine ocn_vel_hmix_tend!}}} !> \brief Initializes ocean momentum horizontal mixing quantities !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> horizontal velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> horizontal velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_vel_hmix_del2.F b/src/core_ocean/shared/mpas_ocn_vel_hmix_del2.F index 38b9fe317b..3f3a2ba7e9 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_hmix_del2.F +++ b/src/core_ocean/shared/mpas_ocn_vel_hmix_del2.F @@ -9,19 +9,21 @@ ! ! ocn_vel_hmix_del2 ! -!> \brief Ocean horizontal mixing - Laplacian parameterization +!> \brief Ocean horizontal mixing - Laplacian parameterization !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 !> \details -!> This module contains routines for computing horizontal mixing +!> This module contains routines for computing horizontal mixing !> tendencies using a Laplacian formulation. ! !----------------------------------------------------------------------- module ocn_vel_hmix_del2 + use mpas_timer use mpas_derived_types use mpas_pool_routines + use mpas_threading use mpas_vector_operations use mpas_matrix_operations use mpas_tensor_operations @@ -66,7 +68,7 @@ module ocn_vel_hmix_del2 !> \brief Computes tendency term for Laplacian horizontal momentum mixing !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date 22 August 2011 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for momentum !> based on a Laplacian form for the mixing, \f$\nu_2 \nabla^2 u\f$ !> This tendency takes the @@ -92,9 +94,9 @@ subroutine ocn_vel_hmix_del2_tend(meshPool, divergence, relativeVorticity, visco type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information - + !------ ----------------------------------------------------------- - ! + ! ! input /output variables ! !----------------------------------------------------------------- @@ -119,8 +121,8 @@ subroutine ocn_vel_hmix_del2_tend(meshPool, divergence, relativeVorticity, visco ! !----------------------------------------------------------------- - integer :: iEdge, cell1, cell2, vertex1, vertex2, k - integer, pointer :: nEdgesSolve + integer :: iEdge, cell1, cell2, vertex1, vertex2, k, nEdges + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop integer, dimension(:,:), pointer :: cellsOnEdge, verticesOnEdge, edgeMask @@ -140,9 +142,11 @@ subroutine ocn_vel_hmix_del2_tend(meshPool, divergence, relativeVorticity, visco if(.not.hmixDel2On) return + call mpas_timer_start("vel del2") + call mpas_pool_get_config(ocnConfigs, 'config_mom_del2', config_mom_del2) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) @@ -152,19 +156,22 @@ subroutine ocn_vel_hmix_del2_tend(meshPool, divergence, relativeVorticity, visco call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) - do iEdge = 1, nEdgesSolve + nEdges = nEdgesArray( 1 ) + + !$omp do schedule(runtime) private(cell1, cell2, vertex1, vertex2, invLength1, invLength2, k, u_diffusion, visc2) + do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) vertex1 = verticesOnEdge(1,iEdge) vertex2 = verticesOnEdge(2,iEdge) - invLength1 = 1.0 / dcEdge(iEdge) - invLength2 = 1.0 / dvEdge(iEdge) + invLength1 = 1.0_RKIND / dcEdge(iEdge) + invLength2 = 1.0_RKIND / dvEdge(iEdge) do k = 1, maxLevelEdgeTop(iEdge) ! Here -( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1) ) / dvEdge(iEdge) - ! is - \nabla relativeVorticity pointing from vertex 2 to vertex 1, or equivalently + ! is - \nabla relativeVorticity pointing from vertex 2 to vertex 1, or equivalently ! + k \times \nabla relativeVorticity pointing from cell1 to cell2. u_diffusion = ( divergence(k,cell2) - divergence(k,cell1) ) * invLength1 & @@ -178,6 +185,9 @@ subroutine ocn_vel_hmix_del2_tend(meshPool, divergence, relativeVorticity, visco end do end do + !$omp end do + + call mpas_timer_stop("vel del2") !-------------------------------------------------------------------- @@ -190,9 +200,9 @@ end subroutine ocn_vel_hmix_del2_tend!}}} !> \brief Computes tendency term for Laplacian horizontal momentum mixing !> \author Mark Petersen !> \date July 2013 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for momentum -!> using tensor operations, +!> using tensor operations, !> based on a Laplacian form for the mixing, \f$\nabla\cdot( \nu_2 \nabla(u))\f$ !> where \f$\nu_2\f$ is a viscosity. ! @@ -244,8 +254,9 @@ subroutine ocn_vel_hmix_del2_tensor_tend(meshPool, normalVelocity, tangentialVel ! !----------------------------------------------------------------- - integer :: iEdge, k - integer, pointer :: nEdgesSolve, nEdges, nVertLevels + integer :: iEdge, k, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop integer, dimension(:,:), pointer :: edgeMask, edgeSignOnCell @@ -259,7 +270,7 @@ subroutine ocn_vel_hmix_del2_tensor_tend(meshPool, normalVelocity, tangentialVel type (field3DReal), pointer :: strainRateR3CellField, strainRateR3EdgeField, divTensorR3CellField, outerProductEdgeField logical, pointer :: config_use_mom_del2_tensor - real (kind=RKIND), pointer :: config_mom_del2_tensor + real (kind=RKIND), pointer :: config_mom_del2_tensor !----------------------------------------------------------------- ! @@ -272,11 +283,12 @@ subroutine ocn_vel_hmix_del2_tensor_tend(meshPool, normalVelocity, tangentialVel if ( .not. config_use_mom_del2_tensor ) return + call mpas_timer_start("vel del2_tensor") + call mpas_pool_get_config(ocnConfigs, 'config_mom_del2_tensor ', config_mom_del2_tensor ) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'meshScalingDel2', meshScalingDel2) @@ -295,6 +307,7 @@ subroutine ocn_vel_hmix_del2_tensor_tend(meshPool, normalVelocity, tangentialVel call mpas_allocate_scratch_field(divTensorR3CellField, .true.) call mpas_allocate_scratch_field(outerProductEdgeField, .true.) call mpas_allocate_scratch_field(normalVectorEdgeField, .true.) + call mpas_threading_barrier() strainRateR3Cell => strainRateR3CellField % array strainRateR3Edge => strainRateR3EdgeField % array @@ -308,37 +321,50 @@ subroutine ocn_vel_hmix_del2_tensor_tend(meshPool, normalVelocity, tangentialVel call mpas_matrix_cell_to_edge(strainRateR3Cell, meshPool, .true., strainRateR3Edge) + ! Need to compute strain rate and viscosity for all edges. + nEdges = nEdgesArray( size(nEdgesArray) ) + ! The following loop could possibly be reduced to nEdgesSolve - do iEdge = 1, nEdges + !$omp do schedule(runtime) private(visc2, k) + do iEdge = 1, nEdges visc2 = config_mom_del2_tensor * meshScalingDel2(iEdge) do k = 1, maxLevelEdgeTop(iEdge) - strainRateR3Edge(:,k,iEdge) = visc2 * strainRateR3Edge(:,k,iEdge) + strainRateR3Edge(:,k,iEdge) = visc2 * strainRateR3Edge(:,k,iEdge) viscosity(k,iEdge) = viscosity(k,iEdge) + visc2 end do ! Impose zero strain rate at land boundaries do k = maxLevelEdgeTop(iEdge)+1, nVertLevels - strainRateR3Edge(:,k,iEdge) = 0.0 + strainRateR3Edge(:,k,iEdge) = 0.0_RKIND end do end do + !$omp end do ! may change boundaries to false later call mpas_divergence_of_tensor_R3Cell(strainRateR3Edge, meshPool, edgeSignOnCell, .true., divTensorR3Cell) call mpas_vector_R3Cell_to_normalVectorEdge(divTensorR3Cell, meshPool, .true., normalVectorEdge) + ! Only need tendency on owned edges + nEdges = nEdgesArray( 1 ) + ! The following loop could possibly be reduced to nEdgesSolve - do iEdge = 1, nEdges + !$omp do schedule(runtime) private(k) + do iEdge = 1, nEdges do k = 1, maxLevelEdgeTop(iEdge) tend(k,iEdge) = tend(k,iEdge) + edgeMask(k, iEdge) * normalVectorEdge(k,iEdge) end do end do + !$omp end do + call mpas_threading_barrier() call mpas_deallocate_scratch_field(strainRateR3CellField, .true.) call mpas_deallocate_scratch_field(strainRateR3EdgeField, .true.) call mpas_deallocate_scratch_field(divTensorR3CellField, .true.) call mpas_deallocate_scratch_field(outerProductEdgeField, .true.) call mpas_deallocate_scratch_field(normalVectorEdgeField, .true.) + call mpas_timer_stop("vel del2_tensor") + !-------------------------------------------------------------------- end subroutine ocn_vel_hmix_del2_tensor_tend!}}} @@ -350,9 +376,9 @@ end subroutine ocn_vel_hmix_del2_tensor_tend!}}} !> \brief Initializes ocean momentum Laplacian horizontal mixing !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> Laplacian horizontal momentum mixing in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> Laplacian horizontal momentum mixing in the ocean. ! !----------------------------------------------------------------------- @@ -377,7 +403,7 @@ subroutine ocn_vel_hmix_del2_init(err)!{{{ hmixDel2On = .false. - if ( config_mom_del2 > 0.0 ) then + if ( config_mom_del2 > 0.0_RKIND ) then hmixDel2On = .true. endif diff --git a/src/core_ocean/shared/mpas_ocn_vel_hmix_del4.F b/src/core_ocean/shared/mpas_ocn_vel_hmix_del4.F index 88ddb80cef..f33d2ca788 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_hmix_del4.F +++ b/src/core_ocean/shared/mpas_ocn_vel_hmix_del4.F @@ -13,15 +13,17 @@ !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 !> \details -!> This module contains routines and variables for computing -!> horizontal mixing tendencies using a biharmonic formulation. +!> This module contains routines and variables for computing +!> horizontal mixing tendencies using a biharmonic formulation. ! !----------------------------------------------------------------------- module ocn_vel_hmix_del4 + use mpas_timer use mpas_derived_types use mpas_pool_routines + use mpas_threading use mpas_vector_operations use mpas_matrix_operations use mpas_tensor_operations @@ -66,18 +68,18 @@ module ocn_vel_hmix_del4 !> \brief Computes tendency term for biharmonic horizontal momentum mixing !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for momentum !> based on a biharmonic form for the mixing. This mixing tendency !> takes the form \f$-\nu_4 \nabla^4 u\f$ -!> but is computed as +!> but is computed as !> \f$\nabla^2 u = \nabla divergence + k \times \nabla relativeVorticity\f$ !> applied recursively. !> This formulation is only valid for constant \f$\nu_4\f$ . ! !----------------------------------------------------------------------- - subroutine ocn_vel_hmix_del4_tend(meshPool, divergence, relativeVorticity, tend, err)!{{{ + subroutine ocn_vel_hmix_del4_tend(meshPool, scratchPool, divergence, relativeVorticity, tend, err)!{{{ !----------------------------------------------------------------- ! @@ -88,6 +90,8 @@ subroutine ocn_vel_hmix_del4_tend(meshPool, divergence, relativeVorticity, tend, real (kind=RKIND), dimension(:,:), intent(in) :: & divergence !< Input: velocity divergence + type (mpas_pool_type), intent(in) :: scratchPool !< Input: scratch variables + real (kind=RKIND), dimension(:,:), intent(in) :: & relativeVorticity !< Input: relative vorticity @@ -108,7 +112,7 @@ subroutine ocn_vel_hmix_del4_tend(meshPool, divergence, relativeVorticity, tend, ! output variables ! !----------------------------------------------------------------- - + integer, intent(out) :: err !< Output: error flag !----------------------------------------------------------------- @@ -118,12 +122,14 @@ subroutine ocn_vel_hmix_del4_tend(meshPool, divergence, relativeVorticity, tend, !----------------------------------------------------------------- integer :: iEdge, cell1, cell2, vertex1, vertex2, k, i - integer :: iCell, iVertex - integer, pointer :: nVertices, nVertLevels, nCells, nEdges, nEdgesSolve, vertexDegree + integer :: iCell, iVertex, nEdges, nCells, nVertices + integer, pointer :: nVertLevels, vertexDegree + integer, dimension(:), pointer :: nEdgesArray, nCellsArray, nVerticesArray integer, dimension(:), pointer :: maxLevelEdgeTop, maxLevelVertexTop, & maxLevelCell, nEdgesOnCell - integer, dimension(:,:), pointer :: cellsOnEdge, verticesOnEdge, edgeMask, edgesOnVertex, edgesOnCell, edgeSignOnVertex, edgeSignOnCell + integer, dimension(:,:), pointer :: cellsOnEdge, verticesOnEdge, edgeMask, edgesOnVertex, edgesOnCell, edgeSignOnVertex, & + edgeSignOnCell real (kind=RKIND) :: u_diffusion, invAreaCell1, invAreaCell2, invAreaTri1, & @@ -131,21 +137,23 @@ subroutine ocn_vel_hmix_del4_tend(meshPool, divergence, relativeVorticity, tend, real (kind=RKIND), dimension(:), pointer :: dcEdge, dvEdge, areaTriangle, & meshScalingDel4, areaCell - real (kind=RKIND), dimension(:,:), allocatable :: delsq_divergence, & - delsq_circulation, delsq_relativeVorticity, delsq_u + real (kind=RKIND), dimension(:,:), pointer :: delsq_divergence, delsq_relativeVorticity, delsq_u + type (field2DReal), pointer :: delsq_uField, delsq_divergenceField, delsq_relativeVorticityField - real (kind=RKIND), pointer :: config_mom_del4 + real (kind=RKIND), pointer :: config_mom_del4, config_mom_del4_div_factor err = 0 if(.not.hmixDel4On) return + call mpas_timer_start("vel del4") + call mpas_pool_get_config(ocnConfigs, 'config_mom_del4', config_mom_del4) + call mpas_pool_get_config(ocnConfigs, 'config_mom_del4_div_factor', config_mom_del4_div_factor) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) - call mpas_pool_get_dimension(meshPool, 'nVertices', nVertices) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_dimension(meshPool, 'nVerticesArray', nVerticesArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_dimension(meshPool, 'vertexDegree', vertexDegree) @@ -166,77 +174,105 @@ subroutine ocn_vel_hmix_del4_tend(meshPool, divergence, relativeVorticity, tend, call mpas_pool_get_array(meshPool, 'edgeSignOnVertex', edgeSignOnVertex) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) - allocate(delsq_u(nVertLEvels, nEdges+1)) - allocate(delsq_divergence(nVertLevels, nCells+1)) - allocate(delsq_relativeVorticity(nVertLevels, nVertices+1)) + call mpas_pool_get_field(scratchPool, 'delsq_u', delsq_uField) + call mpas_pool_get_field(scratchPool, 'delsq_divergence', delsq_divergenceField) + call mpas_pool_get_field(scratchPool, 'delsq_relativeVorticity', delsq_relativeVorticityField) + call mpas_allocate_scratch_field(delsq_uField, .true., .false.) + call mpas_allocate_scratch_field(delsq_divergenceField, .true., .false.) + call mpas_allocate_scratch_field(delsq_relativeVorticityField, .true., .false.) + call mpas_threading_barrier() + + delsq_u => delsq_uField % array + delsq_divergence => delsq_divergenceField % array + delsq_relativeVorticity => delsq_relativeVorticityField % array - delsq_u(:,:) = 0.0 - delsq_relativeVorticity(:,:) = 0.0 - delsq_divergence(:,:) = 0.0 + nEdges = nEdgesArray( 3 ) !Compute delsq_u + !$omp do schedule(runtime) private(cell1, cell2, vertex1, vertex2, invDcEdge, invDvEdge) do iEdge = 1, nEdges + delsq_u(:, iEdge) = 0.0_RKIND cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) vertex1 = verticesOnEdge(1,iEdge) vertex2 = verticesOnEdge(2,iEdge) - invDcEdge = 1.0 / dcEdge(iEdge) - invDvEdge = 1.0 / dvEdge(iEdge) + invDcEdge = 1.0_RKIND / dcEdge(iEdge) + invDvEdge = 1.0_RKIND / max(dvEdge(iEdge), 0.25_RKIND*dcEdge(iEdge)) do k=1,maxLevelEdgeTop(iEdge) ! Compute \nabla^2 u = \nabla divergence + k \times \nabla relativeVorticity delsq_u(k, iEdge) = ( divergence(k,cell2) - divergence(k,cell1) ) * invDcEdge & - -( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1)) * invDcEdge * sqrt(3.0) + -( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1)) * invDvEdge end do end do + !$omp end do + + nVertices = nVerticesArray( 2 ) ! Compute delsq_relativeVorticity + !$omp do schedule(runtime) private(invAreaTri1, i, iEdge, k) do iVertex = 1, nVertices - invAreaTri1 = 1.0 / areaTriangle(iVertex) + delsq_relativeVorticity(:, iVertex) = 0.0_RKIND + invAreaTri1 = 1.0_RKIND / areaTriangle(iVertex) do i = 1, vertexDegree iEdge = edgesOnVertex(i, iVertex) do k = 1, maxLevelVertexTop(iVertex) - delsq_relativeVorticity(k, iVertex) = delsq_relativeVorticity(k, iVertex) + edgeSignOnVertex(i, iVertex) * dcEdge(iEdge) * delsq_u(k, iEdge) * invAreaTri1 + delsq_relativeVorticity(k, iVertex) = delsq_relativeVorticity(k, iVertex) + edgeSignOnVertex(i, iVertex) & + * dcEdge(iEdge) * delsq_u(k, iEdge) * invAreaTri1 end do end do end do + !$omp end do + + nCells = nCellsArray( 2 ) ! Compute delsq_divergence + !$omp do schedule(runtime) private(invAreaCell1, i, iEdge, k) do iCell = 1, nCells - invAreaCell1 = 1.0 / areaCell(iCell) + delsq_divergence(:, iCell) = 0.0_RKIND + invAreaCell1 = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) do k = 1, maxLevelCell(iCell) - delsq_divergence(k, iCell) = delsq_divergence(k, iCell) - edgeSignOnCell(i, iCell) * dvEdge(iEdge) * delsq_u(k, iEdge) * invAreaCell1 + delsq_divergence(k, iCell) = delsq_divergence(k, iCell) - edgeSignOnCell(i, iCell) * dvEdge(iEdge) & + * delsq_u(k, iEdge) * invAreaCell1 end do end do end do + !$omp end do - ! Compute - \kappa \nabla^4 u + nEdges = nEdgesArray( 1 ) + + ! Compute - \kappa \nabla^4 u ! as \nabla div(\nabla^2 u) + k \times \nabla ( k \cross curl(\nabla^2 u) ) - do iEdge=1,nEdgesSolve + !$omp do schedule(runtime) private(cell1, cell2, vertex1, vertex2, invDcEdge, invDvEdge, r_tmp, u_diffusion, k) + do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) vertex1 = verticesOnEdge(1,iEdge) vertex2 = verticesOnEdge(2,iEdge) - invDcEdge = 1.0 / dcEdge(iEdge) - invDvEdge = 1.0 / dvEdge(iEdge) + invDcEdge = 1.0_RKIND / dcEdge(iEdge) + invDvEdge = 1.0_RKIND / dvEdge(iEdge) r_tmp = config_mom_del4 * meshScalingDel4(iEdge) do k=1,maxLevelEdgeTop(iEdge) - u_diffusion = (delsq_divergence(k,cell2) - delsq_divergence(k,cell1)) * invDcEdge & - - (delsq_relativeVorticity(k,vertex2) - delsq_relativeVorticity(k,vertex1) ) * invDcEdge * sqrt(3.0) + u_diffusion = config_mom_del4_div_factor*(delsq_divergence(k,cell2) - delsq_divergence(k,cell1)) * invDcEdge & + - (delsq_relativeVorticity(k,vertex2) - delsq_relativeVorticity(k,vertex1) ) * invDvEdge tend(k,iEdge) = tend(k,iEdge) - edgeMask(k, iEdge) * u_diffusion * r_tmp end do end do + !$omp end do - deallocate(delsq_u) - deallocate(delsq_divergence) - deallocate(delsq_relativeVorticity) + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(delsq_uField, .true.) + call mpas_deallocate_scratch_field(delsq_divergenceField, .true.) + call mpas_deallocate_scratch_field(delsq_relativeVorticityField, .true.) + + call mpas_timer_stop("vel del4") !-------------------------------------------------------------------- @@ -249,10 +285,10 @@ end subroutine ocn_vel_hmix_del4_tend!}}} !> \brief Computes tendency term for Laplacian horizontal momentum mixing !> \author Mark Petersen !> \date July 2013 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for momentum -!> using tensor operations, -!> based on a Laplacian form for the mixing, +!> using tensor operations, +!> based on a Laplacian form for the mixing, !> \f$-\nabla\cdot( \sqrt{\nu_4} \nabla(\nabla\cdot( \sqrt{\nu_4} \nabla(u))))\f$ !> where \f$\nu_4\f$ is the del4 viscosity. ! @@ -304,8 +340,9 @@ subroutine ocn_vel_hmix_del4_tensor_tend(meshPool, normalVelocity, tangentialVel ! !----------------------------------------------------------------- - integer :: iEdge, k - integer, pointer :: nEdgesSolve, nEdges, nVertLevels + integer :: iEdge, k, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop integer, dimension(:,:), pointer :: edgeMask, edgeSignOnCell @@ -334,11 +371,12 @@ subroutine ocn_vel_hmix_del4_tensor_tend(meshPool, normalVelocity, tangentialVel if(.not.config_use_mom_del4_tensor) return + call mpas_timer_start("vel del4_tensor") + call mpas_pool_get_config(ocnConfigs, 'config_mom_del4_tensor', config_mom_del4_tensor) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'meshScalingDel4', meshScalingDel4) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) @@ -358,6 +396,7 @@ subroutine ocn_vel_hmix_del4_tensor_tend(meshPool, normalVelocity, tangentialVel call mpas_allocate_scratch_field(outerProductEdgeField, .true.) call mpas_allocate_scratch_field(normalVectorEdgeField, .true.) call mpas_allocate_scratch_field(tangentialVectorEdgeField, .true.) + call mpas_threading_barrier() strainRateR3Cell => strainRateR3CellField % array strainRateR3Edge => strainRateR3EdgeField % array @@ -374,22 +413,28 @@ subroutine ocn_vel_hmix_del4_tensor_tend(meshPool, normalVelocity, tangentialVel call mpas_matrix_cell_to_edge(strainRateR3Cell, meshPool, .true., strainRateR3Edge) + ! Need to compute strain rate on all edges + nEdges = nEdgesArray( size(nEdgesArray) ) + ! The following loop could possibly be reduced to nEdgesSolve - do iEdge = 1, nEdges + !$omp do schedule(runtime) private(visc4_sqrt, k) + do iEdge = 1, nEdges visc4_sqrt = sqrt(config_mom_del4_tensor * meshScalingDel4(iEdge)) do k = 1, maxLevelEdgeTop(iEdge) - strainRateR3Edge(:,k,iEdge) = visc4_sqrt * strainRateR3Edge(:,k,iEdge) + strainRateR3Edge(:,k,iEdge) = visc4_sqrt * strainRateR3Edge(:,k,iEdge) end do ! Impose zero strain rate at land boundaries do k = maxLevelEdgeTop(iEdge)+1, nVertLevels - strainRateR3Edge(:,k,iEdge) = 0.0 + strainRateR3Edge(:,k,iEdge) = 0.0_RKIND end do end do + !$omp end do ! may change boundaries to false later call mpas_divergence_of_tensor_R3Cell(strainRateR3Edge, meshPool, edgeSignOnCell, .true., divTensorR3Cell) - call mpas_vector_R3Cell_to_2DEdge(divTensorR3Cell, meshPool, edgeTangentVectors, .true., normalVectorEdge, tangentialVectorEdge) + call mpas_vector_R3Cell_to_2DEdge(divTensorR3Cell, meshPool, edgeTangentVectors, .true., normalVectorEdge, & + tangentialVectorEdge) !!!!!!! second div(grad()) @@ -400,30 +445,38 @@ subroutine ocn_vel_hmix_del4_tensor_tend(meshPool, normalVelocity, tangentialVel call mpas_matrix_cell_to_edge(strainRateR3Cell, meshPool, .true., strainRateR3Edge) ! The following loop could possibly be reduced to nEdgesSolve - do iEdge = 1, nEdges + !$omp do schedule(runtime) private(visc4_sqrt, k) + do iEdge = 1, nEdges visc4_sqrt = sqrt(config_mom_del4_tensor * meshScalingDel4(iEdge)) viscosity(:,iEdge) = viscosity(:,iEdge) + config_mom_del4_tensor * meshScalingDel4(iEdge) do k = 1, maxLevelEdgeTop(iEdge) - strainRateR3Edge(:,k,iEdge) = visc4_sqrt * strainRateR3Edge(:,k,iEdge) + strainRateR3Edge(:,k,iEdge) = visc4_sqrt * strainRateR3Edge(:,k,iEdge) end do ! Impose zero strain rate at land boundaries do k = maxLevelEdgeTop(iEdge)+1, nVertLevels - strainRateR3Edge(:,k,iEdge) = 0.0 + strainRateR3Edge(:,k,iEdge) = 0.0_RKIND end do end do + !$omp end do ! may change boundaries to false later call mpas_divergence_of_tensor_R3Cell(strainRateR3Edge, meshPool, edgeSignOnCell, .true., divTensorR3Cell) call mpas_vector_R3Cell_to_normalVectorEdge(divTensorR3Cell, meshPool, .true., normalVectorEdge) + ! only need to compute tendency on owned edges + nEdges = nEdgesArray( 1 ) + ! The following loop could possibly be reduced to nEdgesSolve + !$omp do schedule(runtime) private(k) do iEdge = 1,nEdges do k = 1,maxLevelEdgeTop(iEdge) tend(k,iEdge) = tend(k,iEdge) - edgeMask(k, iEdge) * normalVectorEdge(k,iEdge) end do end do + !$omp end do + call mpas_threading_barrier() call mpas_deallocate_scratch_field(strainRateR3CellField, .true.) call mpas_deallocate_scratch_field(strainRateR3EdgeField, .true.) call mpas_deallocate_scratch_field(divTensorR3CellField, .true.) @@ -431,6 +484,8 @@ subroutine ocn_vel_hmix_del4_tensor_tend(meshPool, normalVelocity, tangentialVel call mpas_deallocate_scratch_field(normalVectorEdgeField, .true.) call mpas_deallocate_scratch_field(tangentialVectorEdgeField, .true.) + call mpas_timer_stop("vel del4_tensor") + !-------------------------------------------------------------------- end subroutine ocn_vel_hmix_del4_tensor_tend!}}} @@ -442,9 +497,9 @@ end subroutine ocn_vel_hmix_del4_tensor_tend!}}} !> \brief Initializes ocean momentum biharmonic horizontal mixing !> \author Mark Petersen, Doug Jacobsen, Todd Ringler !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> biharmonic horizontal tracer mixing in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> biharmonic horizontal tracer mixing in the ocean. ! !----------------------------------------------------------------------- @@ -468,7 +523,7 @@ subroutine ocn_vel_hmix_del4_init(err)!{{{ hmixDel4On = .false. - if ( config_mom_del4 > 0.0 ) then + if ( config_mom_del4 > 0.0_RKIND ) then hmixDel4On = .true. endif diff --git a/src/core_ocean/shared/mpas_ocn_vel_hmix_leith.F b/src/core_ocean/shared/mpas_ocn_vel_hmix_leith.F index 3ff4ff3af4..e9800218ed 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_hmix_leith.F +++ b/src/core_ocean/shared/mpas_ocn_vel_hmix_leith.F @@ -9,19 +9,21 @@ ! ! ocn_vel_hmix_leith ! -!> \brief Ocean horizontal mixing - Leith parameterization +!> \brief Ocean horizontal mixing - Leith parameterization !> \author Mark Petersen !> \date 22 October 2012 !> \details -!> This module contains routines for computing horizontal mixing +!> This module contains routines for computing horizontal mixing !> tendencies using the Leith parameterization. ! !----------------------------------------------------------------------- module ocn_vel_hmix_leith + use mpas_timer use mpas_derived_types use mpas_pool_routines + use mpas_constants use ocn_constants implicit none @@ -62,7 +64,7 @@ module ocn_vel_hmix_leith !> \brief Computes tendency term for horizontal momentum mixing with Leith parameterization !> \author Mark Petersen, Todd Ringler !> \date 22 October 2012 -!> \details +!> \details !> This routine computes the horizontal mixing tendency for momentum !> based on the Leith closure. The Leith closure is the !> enstrophy-cascade analogy to the Smagorinsky (1963) energy-cascade @@ -70,10 +72,10 @@ module ocn_vel_hmix_leith !> moving toward the mesh scale. The assumption of an enstrophy cascade !> and dimensional analysis produces right-hand-side dissipation, !> $\bf{D}$, of velocity of the form -!> $ {\bf D} = \nabla \cdot \left( \nu_\ast \nabla {\bf u} \right) -!> = \nabla \cdot \left( \gamma \left| \nabla \omega \right| +!> $ {\bf D} = \nabla \cdot \left( \nu_\ast \nabla {\bf u} \right) +!> = \nabla \cdot \left( \gamma \left| \nabla \omega \right| !> \left( \Delta x \right)^3 \nabla \bf{u} \right) -!> where $\omega$ is the relative vorticity and $\gamma$ is a non-dimensional, +!> where $\omega$ is the relative vorticity and $\gamma$ is a non-dimensional, !> $O(1)$ parameter. We set $\gamma=1$. ! @@ -122,12 +124,12 @@ subroutine ocn_vel_hmix_leith_tend(meshPool, divergence, relativeVorticity, visc ! !----------------------------------------------------------------- - integer :: iEdge, cell1, cell2, vertex1, vertex2, k - integer, pointer :: nEdgesSolve + integer :: iEdge, cell1, cell2, vertex1, vertex2, k, nEdges + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop integer, dimension(:,:), pointer :: cellsOnEdge, verticesOnEdge, edgeMask - real (kind=RKIND) :: u_diffusion, invLength1, invLength2, visc2 + real (kind=RKIND) :: u_diffusion, invLength_dc, invLength_dv, visc2 real (kind=RKIND), dimension(:), pointer :: meshScaling, & dcEdge, dvEdge @@ -143,11 +145,13 @@ subroutine ocn_vel_hmix_leith_tend(meshPool, divergence, relativeVorticity, visc if(.not.hmixLeithOn) return + call mpas_timer_start("vel leith") + call mpas_pool_get_config(ocnConfigs, 'config_Leith_parameter', config_leith_parameter) call mpas_pool_get_config(ocnConfigs, 'config_Leith_dx', config_leith_dx) call mpas_pool_get_config(ocnConfigs, 'config_Leith_visc2_max', config_leith_visc2_max) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) @@ -157,29 +161,32 @@ subroutine ocn_vel_hmix_leith_tend(meshPool, divergence, relativeVorticity, visc call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) - do iEdge = 1, nEdgesSolve + nEdges = nEdgesArray( 1 ) + + !$omp do schedule(runtime) private(cell1, cell2, vertex1, vertex2, invLength_dc, invLength_dv, k, u_diffusion, visc2) + do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) vertex1 = verticesOnEdge(1,iEdge) vertex2 = verticesOnEdge(2,iEdge) - invLength1 = 1.0 / dcEdge(iEdge) - invLength2 = 1.0 / dvEdge(iEdge) + invLength_dc = 1.0_RKIND / dcEdge(iEdge) + invLength_dv = 1.0_RKIND / dvEdge(iEdge) do k = 1, maxLevelEdgeTop(iEdge) ! Here -( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1) ) / dvEdge(iEdge) - ! is - \nabla relativeVorticity pointing from vertex 2 to vertex 1, or equivalently + ! is - \nabla relativeVorticity pointing from vertex 2 to vertex 1, or equivalently ! + k \times \nabla relativeVorticity pointing from cell1 to cell2. - u_diffusion = ( divergence(k,cell2) - divergence(k,cell1) ) * invLength1 & - -( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1) ) * invLength2 + u_diffusion = ( divergence(k,cell2) - divergence(k,cell1) ) * invLength_dc & + -( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1) ) * invLength_dv ! Here the first line is (\delta x)^3 ! the second line is |\nabla \omega| ! and u_diffusion is \nabla^2 u (see formula for $\bf{D}$ above). - visc2 = ( config_leith_parameter * config_leith_dx * meshScaling(iEdge) / 3.14)**3 & - * abs( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1) ) * invLength1 * sqrt(3.0) + visc2 = ( config_leith_parameter * config_leith_dx * meshScaling(iEdge) / pii)**3 & + * abs( relativeVorticity(k,vertex2) - relativeVorticity(k,vertex1) ) * invLength_dc * sqrt(3.0_RKIND) visc2 = min(visc2, config_leith_visc2_max) tend(k,iEdge) = tend(k,iEdge) + edgeMask(k, iEdge) * visc2 * u_diffusion @@ -188,6 +195,9 @@ subroutine ocn_vel_hmix_leith_tend(meshPool, divergence, relativeVorticity, visc end do end do + !$omp end do + + call mpas_timer_stop("vel leith") !-------------------------------------------------------------------- @@ -200,9 +210,9 @@ end subroutine ocn_vel_hmix_leith_tend!}}} !> \brief Initializes ocean momentum horizontal mixing with Leith parameterization !> \author Mark Petersen !> \date 22 October 2012 -!> \details -!> This routine initializes a variety of quantities related to -!> Leith parameterization for horizontal momentum mixing in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> Leith parameterization for horizontal momentum mixing in the ocean. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_vel_pressure_grad.F b/src/core_ocean/shared/mpas_ocn_vel_pressure_grad.F index 4810ce58f2..01c61a0373 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_pressure_grad.F +++ b/src/core_ocean/shared/mpas_ocn_vel_pressure_grad.F @@ -13,7 +13,7 @@ !> \author Mark Petersen !> \date September 2011 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> tendencie from the horizontal pressure gradient. !> ! @@ -21,6 +21,7 @@ module ocn_vel_pressure_grad + use mpas_timer use mpas_derived_types use mpas_pool_routines use mpas_constants @@ -70,7 +71,7 @@ module ocn_vel_pressure_grad !> \brief Computes tendency term for horizontal pressure gradient !> \author Mark Petersen !> \date February 2014 -!> \details +!> \details !> This routine computes the pressure gradient tendency for momentum !> based on current state. ! @@ -95,7 +96,7 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z potentialDensity !< Input: potentialDensity real (kind=RKIND), dimension(:,:), intent(in), optional :: & - inSituThermalExpansionCoeff, & + inSituThermalExpansionCoeff, & inSituSalineContractionCoeff real (kind=RKIND), dimension(:,:,:), intent(in) :: tracers @@ -126,8 +127,9 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z ! !----------------------------------------------------------------- - integer :: iEdge, k, cell1, cell2, iCell, kMax - integer, pointer :: nVertLevels, nCells, nEdgesSolve + integer :: iEdge, k, cell1, cell2, iCell, kMax, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop, maxLevelCell integer, dimension(:,:), pointer :: cellsOnEdge, edgeMask @@ -140,47 +142,54 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z if (.not. pgradOn) return + call mpas_timer_start("pressure grad") + call mpas_pool_get_dimension(meshPool, 'nVertLevels',nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nCells',nCells) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) + nEdges = nEdgesArray( 1 ) + if (config_pressure_gradient_type.eq.'pressure_and_zmid') then ! pressure for generalized coordinates ! -1/density_0 (grad p_k + density g grad z_k^{mid}) - do iEdge=1,nEdgesSolve + !$omp do schedule(runtime) private(cell1, cell2, invdcEdge, k) + do iEdge=1,nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - invdcEdge = 1.0 / dcEdge(iEdge) + invdcEdge = 1.0_RKIND / dcEdge(iEdge) do k=1,maxLevelEdgeTop(iEdge) tend(k,iEdge) = tend(k,iEdge) + edgeMask(k,iEdge) * invdcEdge * ( & - density0Inv * ( pressure(k,cell2) - pressure(k,cell1) ) & - - gdensity0Inv * 0.5*(density(k,cell1)+density(k,cell2)) * ( zMid(k,cell2) - zMid(k,cell1) ) ) + - gdensity0Inv * 0.5_RKIND*(density(k,cell1)+density(k,cell2)) * ( zMid(k,cell2) - zMid(k,cell1) ) ) end do end do + !$omp end do elseif (config_pressure_gradient_type.eq.'MontgomeryPotential') then - ! For pure isopycnal coordinates, this is just grad(M), + ! For pure isopycnal coordinates, this is just grad(M), ! the gradient of Montgomery Potential - do iEdge=1,nEdgesSolve + !$omp do schedule(runtime) private(cell1, cell2, invdcEdge, k) + do iEdge=1,nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - invdcEdge = 1.0 / dcEdge(iEdge) + invdcEdge = 1.0_RKIND / dcEdge(iEdge) do k=1,maxLevelEdgeTop(iEdge) tend(k,iEdge) = tend(k,iEdge) + edgeMask(k,iEdge) * invdcEdge * ( & - ( montgomeryPotential(k,cell2) - montgomeryPotential(k,cell1) ) ) end do end do + !$omp end do elseif (config_pressure_gradient_type.eq.'MontgomeryPotential_and_density') then @@ -190,26 +199,30 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z ! Where rho is the potential density. ! See Bleck (2002) equation 1, and last equation in Appendix A. - do iEdge=1,nEdgesSolve + !$omp do schedule(runtime) private(cell1, cell2, invdcEdge, k) + do iEdge=1,nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - invdcEdge = 1.0 / dcEdge(iEdge) + invdcEdge = 1.0_RKIND / dcEdge(iEdge) do k=1,maxLevelEdgeTop(iEdge) tend(k,iEdge) = tend(k,iEdge) + edgeMask(k,iEdge) * invdcEdge * ( & - ( montgomeryPotential(k,cell2) - montgomeryPotential(k,cell1) ) & - + 0.5*(pressure(k,cell1)+pressure(k,cell2)) * ( 1.0/potentialDensity(k,cell2) - 1.0/potentialDensity(k,cell1) ) ) + + 0.5_RKIND*(pressure(k,cell1)+pressure(k,cell2)) * ( 1.0_RKIND/potentialDensity(k,cell2) & + - 1.0_RKIND/potentialDensity(k,cell1) ) ) end do end do + !$omp end do elseif (config_pressure_gradient_type.eq.'Jacobian_from_density') then allocate(JacobianDxDs(nVertLevels)) - do iEdge=1,nEdgesSolve + !$omp do schedule(runtime) private(cell1, cell2, invdcEdge, k, pGrad) + do iEdge=1,nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - invdcEdge = 1.0 / dcEdge(iEdge) + invdcEdge = 1.0_RKIND / dcEdge(iEdge) call pGrad_Jacobian_common_level(density(:,cell1),density(:,cell2),zMid(:,cell1),zMid(:,cell2), & maxLevelEdgeTop(iEdge), config_common_level_weight, JacobianDxDs) @@ -219,7 +232,7 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z k = 1 pGrad = edgeMask(k,iEdge) * invdcEdge * ( & - density0Inv * ( pressure(k,cell2) - pressure(k,cell1) ) & - - gdensity0Inv * 0.5*(density(k,cell1)+density(k,cell2)) * ( zMid(k,cell2) - zMid(k,cell1) ) ) + - gdensity0Inv * 0.5_RKIND*(density(k,cell1)+density(k,cell2)) * ( zMid(k,cell2) - zMid(k,cell1) ) ) tend(k,iEdge) = tend(k,iEdge) + pGrad @@ -234,17 +247,20 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z end do end do + !$omp end do deallocate(JacobianDxDs) elseif (config_pressure_gradient_type.eq.'Jacobian_from_TS') then - allocate(JacobianDxDs(nVertLevels),JacobianTz(nVertLevels),JacobianSz(nVertLevels), T1(nVertLevels), T2(nVertLevels), S1(nVertLevels), S2(nVertLevels)) + allocate(JacobianDxDs(nVertLevels),JacobianTz(nVertLevels),JacobianSz(nVertLevels), T1(nVertLevels)) + allocate(T2(nVertLevels), S1(nVertLevels), S2(nVertLevels)) - do iEdge=1,nEdgesSolve + !$omp do schedule(runtime) private(cell1, cell2, invdcEdge, kMax, k, pGrad, alpha, beta) + do iEdge=1,nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) - invdcEdge = 1.0 / dcEdge(iEdge) + invdcEdge = 1.0_RKIND / dcEdge(iEdge) kMax = maxLevelEdgeTop(iEdge) ! copy T and S to local column arrays @@ -262,7 +278,7 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z k = 1 pGrad = edgeMask(k,iEdge) * invdcEdge * ( & - density0Inv * ( pressure(k,cell2) - pressure(k,cell1) ) & - - gdensity0Inv * 0.5*(density(k,cell1)+density(k,cell2)) * ( zMid(k,cell2) - zMid(k,cell1) ) ) + - gdensity0Inv * 0.5_RKIND*(density(k,cell1)+density(k,cell2)) * ( zMid(k,cell2) - zMid(k,cell1) ) ) tend(k,iEdge) = tend(k,iEdge) + pGrad @@ -271,10 +287,14 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z ! Average alpha and beta over four data points of the Jacobian cell. ! Note that inSituThermalExpansionCoeff and inSituSalineContractionCoeff include a 1/density factor, ! so must multiply by density here. - alpha = 0.25*( density(k,cell1)*inSituThermalExpansionCoeff (k,cell1) + density(k-1,cell1)*inSituThermalExpansionCoeff (k-1,cell1) & - + density(k,cell2)*inSituThermalExpansionCoeff (k,cell2) + density(k-1,cell2)*inSituThermalExpansionCoeff (k-1,cell2) ) - beta = 0.25*( density(k,cell1)*inSituSalineContractionCoeff(k,cell1) + density(k-1,cell1)*inSituSalineContractionCoeff(k-1,cell1) & - + density(k,cell2)*inSituSalineContractionCoeff(k,cell2) + density(k-1,cell2)*inSituSalineContractionCoeff(k-1,cell2) ) + alpha = 0.25_RKIND*( density(k,cell1)*inSituThermalExpansionCoeff (k,cell1) & + + density(k-1,cell1)*inSituThermalExpansionCoeff (k-1,cell1) & + + density(k,cell2)*inSituThermalExpansionCoeff (k,cell2) & + + density(k-1,cell2)*inSituThermalExpansionCoeff (k-1,cell2) ) + beta = 0.25_RKIND*( density(k,cell1)*inSituSalineContractionCoeff(k,cell1) & + + density(k-1,cell1)*inSituSalineContractionCoeff(k-1,cell1) & + + density(k,cell2)*inSituSalineContractionCoeff(k,cell2) & + + density(k-1,cell2)*inSituSalineContractionCoeff(k-1,cell2) ) ! Shchepetkin and McWilliams (2003) (7.16) JacobianDxDs(k) = -alpha*JacobianTz(k) + beta*JacobianSz(k) @@ -288,6 +308,7 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z end do end do + !$omp end do deallocate(JacobianDxDs,JacobianTz,JacobianSz, T1, T2, S1, S2) @@ -299,6 +320,8 @@ subroutine ocn_vel_pressure_grad_tend(meshPool, pressure, montgomeryPotential, z endif + call mpas_timer_stop("pressure grad") + !-------------------------------------------------------------------- end subroutine ocn_vel_pressure_grad_tend!}}} @@ -310,7 +333,7 @@ end subroutine ocn_vel_pressure_grad_tend!}}} !> \brief Computes density-Jacobian !> \author Mark Petersen !> \date February 2014 -!> \details +!> \details !> This routine computes the density-Jacobian in common_level form. !> See Shchepetkin and McWilliams (2003) Ocean Modeling, sections 2-4 ! @@ -325,10 +348,10 @@ subroutine pGrad_Jacobian_common_level(rho1,rho2,z1,z2,kMax,gamma,JacobianDxDs) !----------------------------------------------------------------- real (kind=RKIND), dimension(:), intent(in) :: & - rho1, & ! density of column 1 + rho1, & ! density of column 1 rho2, & ! density of column 2 - z1, & ! z-coordinate at middle of cell, column 1 - z2 ! z-coordinate at middle of cell, column 2 + z1, & ! z-coordinate at middle of cell, column 1 + z2 ! z-coordinate at middle of cell, column 2 real (kind=RKIND), intent(in) :: & gamma ! weight between zStar (original Jacobian) and z_C (weighted Jacobian) @@ -360,23 +383,23 @@ subroutine pGrad_Jacobian_common_level(rho1,rho2,z1,z2,kMax,gamma,JacobianDxDs) integer :: k real (kind=RKIND) :: Area, zStar, rhoL, rhoR, zC, zGamma - JacobianDxDs = 0.0 + JacobianDxDs = 0.0_RKIND do k=2,kMax ! eqn 2.7 in Shchepetkin and McWilliams (2003) ! Note delta x was removed. It must be an error in the paper, ! as it makes the units incorrect. - Area = 0.5*(z1(k-1) - z1(k) + z2(k-1) - z2(k) ) + Area = 0.5_RKIND*(z1(k-1) - z1(k) + z2(k-1) - z2(k) ) ! eqn 2.8 zStar = ( z2(k-1)*z1(k-1) - z2(k)*z1(k) )/(z2(k-1)-z2(k) + z1(k-1)-z1(k)) ! eqn 3.2 - zC = 0.25*( z1(k) + z1(k-1) + z2(k) + z2(k-1) ) + zC = 0.25_RKIND*( z1(k) + z1(k-1) + z2(k) + z2(k-1) ) ! eqn 4.1 - zGamma = (1.0 - gamma)*zStar + gamma*zC + zGamma = (1.0_RKIND - gamma)*zStar + gamma*zC rhoL = (rho1(k)*(z1(k-1)-zGamma) + rho1(k-1)*(zGamma-z1(k)))/(z1(k-1) - z1(k)) rhoR = (rho2(k)*(z2(k-1)-zGamma) + rho2(k-1)*(zGamma-z2(k)))/(z2(k-1) - z2(k)) @@ -394,7 +417,7 @@ end subroutine pGrad_Jacobian_common_level !> \brief Computes density-Jacobian !> \author Mark Petersen !> \date February 2014 -!> \details +!> \details !> This routine computes the density-Jacobian in POM/SCRUM form. !> See Shchepetkin and McWilliams (2003) Ocean Modeling, section 2. ! @@ -409,10 +432,10 @@ subroutine pGrad_Jacobian_POM_SCRUM(rho1,rho2,z1,z2,kMax,JacobianDxDs) !----------------------------------------------------------------- real (kind=RKIND), dimension(:), intent(in) :: & - rho1, & ! density of column 1 + rho1, & ! density of column 1 rho2, & ! density of column 2 - z1, & ! z-coordinate at middle of cell, column 1 - z2 ! z-coordinate at middle of cell, column 2 + z1, & ! z-coordinate at middle of cell, column 1 + z2 ! z-coordinate at middle of cell, column 2 integer, intent(in) :: & kMax ! maximum level @@ -440,13 +463,13 @@ subroutine pGrad_Jacobian_POM_SCRUM(rho1,rho2,z1,z2,kMax,JacobianDxDs) integer :: k - JacobianDxDs = 0.0 + JacobianDxDs = 0.0_RKIND do k=2,kMax ! eqn 2.3 in Shchepetkin and McWilliams (2003) - JacobianDxDs(k) = 0.25*(& - (rho1(k) + rho1(k-1) - rho2(k) - rho2(k-1) )*(z1(k-1) - z1(k) + z2(k-1) - z2(k) ) & + JacobianDxDs(k) = 0.25_RKIND*(& + (rho1(k) + rho1(k-1) - rho2(k) - rho2(k-1) )*(z1(k-1) - z1(k) + z2(k-1) - z2(k) ) & - (rho1(k-1) - rho1(k) + rho2(k-1) - rho2(k) )*(z1(k) + z1(k-1) - z2(k) - z2(k-1) ) ) end do @@ -459,7 +482,7 @@ end subroutine pGrad_Jacobian_POM_SCRUM !> \brief Computes density-Jacobian !> \author Mark Petersen !> \date February 2014 -!> \details +!> \details !> This routine computes the density-Jacobian in diagonal form. !> See Shchepetkin and McWilliams (2003) Ocean Modeling, section 2. ! @@ -474,10 +497,10 @@ subroutine pGrad_Jacobian_diagonal(rho1,rho2,z1,z2,kMax,JacobianDxDs) !----------------------------------------------------------------- real (kind=RKIND), dimension(:), intent(in) :: & - rho1, & ! density of column 1 + rho1, & ! density of column 1 rho2, & ! density of column 2 - z1, & ! z-coordinate at middle of cell, column 1 - z2 ! z-coordinate at middle of cell, column 2 + z1, & ! z-coordinate at middle of cell, column 1 + z2 ! z-coordinate at middle of cell, column 2 integer, intent(in) :: & kMax @@ -505,13 +528,13 @@ subroutine pGrad_Jacobian_diagonal(rho1,rho2,z1,z2,kMax,JacobianDxDs) integer :: k - JacobianDxDs = 0.0 + JacobianDxDs = 0.0_RKIND do k=2,kMax ! eqn 2.5 in Shchepetkin and McWilliams (2003) - JacobianDxDs(k) = 0.5*( & - (rho1(k-1) - rho2(k))*(z2(k-1) - z1(k) ) & + JacobianDxDs(k) = 0.5_RKIND*( & + (rho1(k-1) - rho2(k))*(z2(k-1) - z1(k) ) & + (rho1(k) - rho2(k-1))*(z1(k-1) - z2(k)) ) end do @@ -524,7 +547,7 @@ end subroutine pGrad_Jacobian_diagonal !> \brief Computes density-Jacobian !> \author Mark Petersen !> \date February 2014 -!> \details +!> \details !> This routine computes the density-Jacobian in pseudo_flux form. !> See Shchepetkin and McWilliams (2003) Ocean Modeling, section 2. ! @@ -539,10 +562,10 @@ subroutine pGrad_Jacobian_pseudo_flux(rho1,rho2,z1,z2,kMax,JacobianDxDs) !----------------------------------------------------------------- real (kind=RKIND), dimension(:), intent(in) :: & - rho1, & ! density of column 1 + rho1, & ! density of column 1 rho2, & ! density of column 2 - z1, & ! z-coordinate at middle of cell, column 1 - z2 ! z-coordinate at middle of cell, column 2 + z1, & ! z-coordinate at middle of cell, column 1 + z2 ! z-coordinate at middle of cell, column 2 integer, intent(in) :: & kMax @@ -569,19 +592,19 @@ subroutine pGrad_Jacobian_pseudo_flux(rho1,rho2,z1,z2,kMax,JacobianDxDs) !----------------------------------------------------------------- integer :: k - real (kind=RKIND) :: FLeft, FTop, FRight, FBottom + real (kind=RKIND) :: FLeft, FTop, FRight, FBottom - JacobianDxDs = 0.0 + JacobianDxDs = 0.0_RKIND do k=2,kMax - FLeft = 0.5*( rho1(k) + rho1(k-1) ) * (z1(k-1) - z1(k)) - FTop = 0.5*( rho1(k-1) + rho2(k-1) ) * (z2(k-1) - z1(k-1)) - FRight = 0.5*( rho2(k) + rho2(k-1) ) * (z2(k-1) - z2(k)) - FBottom = 0.5*( rho1(k) + rho2(k) ) * (z2(k) - z1(k)) + FLeft = 0.5_RKIND*( rho1(k) + rho1(k-1) ) * (z1(k-1) - z1(k)) + FTop = 0.5_RKIND*( rho1(k-1) + rho2(k-1) ) * (z2(k-1) - z1(k-1)) + FRight = 0.5_RKIND*( rho2(k) + rho2(k-1) ) * (z2(k-1) - z2(k)) + FBottom = 0.5_RKIND*( rho1(k) + rho2(k) ) * (z2(k) - z1(k)) ! eqn 2.11 in Shchepetkin and McWilliams (2003) - JacobianDxDs(k) = FLeft + FTop - FRight - FBottom + JacobianDxDs(k) = FLeft + FTop - FRight - FBottom end do end subroutine pGrad_Jacobian_pseudo_flux @@ -593,7 +616,7 @@ end subroutine pGrad_Jacobian_pseudo_flux !> \brief Initializes ocean momentum horizontal pressure gradient !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine initializes parameters required for the computation of the !> horizontal pressure gradient. ! @@ -618,21 +641,19 @@ subroutine ocn_vel_pressure_grad_init(err)!{{{ ! call individual init routines for each parameterization ! !----------------------------------------------------------------- - real (kind=RKIND), pointer :: config_density0 logical, pointer :: config_disable_vel_pgrad err = 0 call mpas_pool_get_config(ocnConfigs, 'config_pressure_gradient_type', config_pressure_gradient_type) call mpas_pool_get_config(ocnConfigs, 'config_common_level_weight', config_common_level_weight) - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) call mpas_pool_get_config(ocnConfigs, 'config_disable_vel_pgrad', config_disable_vel_pgrad) pgradOn = .true. - density0Inv = 1.0/config_density0 - gdensity0Inv = gravity/config_density0 - inv12 = 1.0/12.0 + density0Inv = 1.0_RKIND / rho_sw + gdensity0Inv = gravity / rho_sw + inv12 = 1.0_RKIND / 12.0_RKIND if (config_disable_vel_pgrad) pgradOn = .false. diff --git a/src/core_ocean/shared/mpas_ocn_vel_vadv.F b/src/core_ocean/shared/mpas_ocn_vel_vadv.F index b39ead11b0..7ce207035b 100644 --- a/src/core_ocean/shared/mpas_ocn_vel_vadv.F +++ b/src/core_ocean/shared/mpas_ocn_vel_vadv.F @@ -9,11 +9,11 @@ ! ! ocn_vel_vadv ! -!> \brief MPAS ocean vertical advection +!> \brief MPAS ocean vertical advection !> \author Mark Petersen !> \date September 2011 !> \details -!> This module contains the routine for computing +!> This module contains the routine for computing !> tendencies for vertical advection. !> ! @@ -21,6 +21,7 @@ module ocn_vel_vadv + use mpas_timer use mpas_derived_types use mpas_pool_routines use ocn_constants @@ -64,7 +65,7 @@ module ocn_vel_vadv !> \brief Computes tendency term for vertical advection !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the vertical advection tendency for momentum !> based on current state. ! @@ -110,8 +111,9 @@ subroutine ocn_vel_vadv_tend(meshPool, normalVelocity, layerThicknessEdge, vertA ! !----------------------------------------------------------------- - integer :: iEdge, cell1, cell2, k - integer, pointer :: nEdgesSolve, nVertLevels + integer :: iEdge, cell1, cell2, k, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop integer, dimension(:,:), pointer :: cellsOnEdge, edgeMask @@ -120,37 +122,47 @@ subroutine ocn_vel_vadv_tend(meshPool, normalVelocity, layerThicknessEdge, vertA if (.not. velVadvOn) return + call mpas_timer_start("vel vadv") + err = 0 call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) call mpas_pool_get_array(meshPool, 'edgeMask', edgeMask) allocate(w_dudzTopEdge(nVertLevels+1)) - w_dudzTopEdge = 0.0 - do iEdge = 1, nEdgesSolve + w_dudzTopEdge = 0.0_RKIND + + nEdges = nEdgesArray( 1 ) + + !$omp do schedule(runtime) private(cell1, cell2, k, vertAleTransportTopEdge) + do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) do k = 2, maxLevelEdgeTop(iEdge) ! Average w from cell center to edge - vertAleTransportTopEdge = 0.5*(vertAleTransportTop(k,cell1) + vertAleTransportTop(k,cell2)) + vertAleTransportTopEdge = 0.5_RKIND*(vertAleTransportTop(k,cell1) + vertAleTransportTop(k,cell2)) ! compute dudz at vertical interface with first order derivative. w_dudzTopEdge(k) = vertAleTransportTopEdge * (normalVelocity(k-1,iEdge)-normalVelocity(k,iEdge)) & - / (0.5*(layerThicknessEdge(k-1,iEdge) + layerThicknessEdge(k,iEdge))) + / (0.5_RKIND*(layerThicknessEdge(k-1,iEdge) + layerThicknessEdge(k,iEdge))) end do - w_dudzTopEdge(maxLevelEdgeTop(iEdge)+1) = 0.0 + w_dudzTopEdge(maxLevelEdgeTop(iEdge)+1) = 0.0_RKIND ! Average w*du/dz from vertical interface to vertical middle of cell do k = 1, maxLevelEdgeTop(iEdge) tend(k,iEdge) = tend(k,iEdge) - edgeMask(k, iEdge) * 0.5 * (w_dudzTopEdge(k) + w_dudzTopEdge(k+1)) enddo enddo + !$omp end do + deallocate(w_dudzTopEdge) + call mpas_timer_stop("vel vadv") + !-------------------------------------------------------------------- end subroutine ocn_vel_vadv_tend!}}} @@ -162,9 +174,9 @@ end subroutine ocn_vel_vadv_tend!}}} !> \brief Initializes ocean momentum vertical advection !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> vertical velocity advection in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> vertical velocity advection in the ocean. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_vmix.F b/src/core_ocean/shared/mpas_ocn_vmix.F index 710ed6fc1e..e8d7019db4 100644 --- a/src/core_ocean/shared/mpas_ocn_vmix.F +++ b/src/core_ocean/shared/mpas_ocn_vmix.F @@ -13,8 +13,8 @@ !> \author Mark Petersen !> \date September 2011 !> \details -!> This module is the main driver for -!> vertical mixing in the ocean. +!> This module is the main driver for +!> vertical mixing in the ocean. !> ! !----------------------------------------------------------------------- @@ -56,7 +56,8 @@ module ocn_vmix ocn_vel_vmix_tend_implicit, & ocn_tracer_vmix_tend_implicit, & ocn_vmix_init, & - ocn_vmix_implicit + ocn_vmix_implicit, & + ocn_compute_kpp_rhs !-------------------------------------------------------------------- ! @@ -65,6 +66,7 @@ module ocn_vmix !-------------------------------------------------------------------- logical :: velVmixOn, tracerVmixOn + real (kind=RKIND) :: implicitBottomDragCoef !*********************************************************************** @@ -77,13 +79,13 @@ module ocn_vmix !> \brief Computes coefficients for vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the vertical mixing coefficients for momentum !> and tracers based user choices of mixing parameterization. ! !----------------------------------------------------------------------- - subroutine ocn_vmix_coefs(meshPool, statePool, diagnosticsPool, err, timeLevelIn)!{{{ + subroutine ocn_vmix_coefs(meshPool, statePool, forcingPool, diagnosticsPool, scratchPool, err, timeLevelIn)!{{{ !----------------------------------------------------------------- ! @@ -93,7 +95,9 @@ subroutine ocn_vmix_coefs(meshPool, statePool, diagnosticsPool, err, timeLevelIn type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information - + + type (mpas_pool_type), intent(in) :: scratchPool !< Input/Output: Scratch structure + integer, intent(in), optional :: timeLevelIn !< Input: Time level for state pool !----------------------------------------------------------------- @@ -105,6 +109,9 @@ subroutine ocn_vmix_coefs(meshPool, statePool, diagnosticsPool, err, timeLevelIn type (mpas_pool_type), intent(inout) :: & statePool !< Input/Output: state information + type (mpas_pool_type), intent(inout) :: & + forcingPool !< Input/Output: forcing information + type (mpas_pool_type), intent(inout) :: & diagnosticsPool !< Input/Output: diagnostic information @@ -125,6 +132,9 @@ subroutine ocn_vmix_coefs(meshPool, statePool, diagnosticsPool, err, timeLevelIn integer :: err1, err2, err3, err4, err5 integer :: timeLevel + integer :: iEdge, iCell, nEdges, nCells + integer, dimension(:), pointer :: nEdgesArray, nCellsArray + real (kind=RKIND), dimension(:,:), pointer :: vertViscTopOfEdge, vertDiffTopOfCell !----------------------------------------------------------------- @@ -141,15 +151,32 @@ subroutine ocn_vmix_coefs(meshPool, statePool, diagnosticsPool, err, timeLevelIn timeLevel = 1 end if + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + call mpas_pool_get_array(diagnosticsPool, 'vertViscTopOfEdge', vertViscTopOfEdge) call mpas_pool_get_array(diagnosticsPool, 'vertDiffTopOfCell', vertDiffTopOfCell) - vertViscTopOfEdge = 0.0_RKIND - vertDiffTopOfCell = 0.0_RKIND + + nEdges = nEdgesArray( 2 ) + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + vertViscTopOfEdge(:, iEdge) = 0.0_RKIND + end do + !$omp end do + + nCells = nCellsArray( 2 ) + + !$omp do schedule(runtime) + do iCell = 1, nCells + vertDiffTopOfCell(:, iCell) = 0.0_RKIND + end do + !$omp end do call ocn_vmix_coefs_const_build(meshPool, statePool, diagnosticsPool, err1, timeLevel) call ocn_vmix_coefs_tanh_build(meshPool, statePool, diagnosticsPool, err2, timeLevel) - call ocn_vmix_coefs_rich_build(meshPool, statePool, diagnosticsPool, err3, timeLevel) - call ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err4, timeLevel) + call ocn_vmix_coefs_rich_build(meshPool, statePool, diagnosticsPool, scratchPool, err3, timeLevel) + call ocn_vmix_coefs_cvmix_build(meshPool, statePool, forcingPool, diagnosticsPool, err4, timeLevel) call ocn_vmix_coefs_redi_build(meshPool, statePool, diagnosticsPool, err5, timeLevel) err = ior(ior(ior(err1, ior(err2, err3)), err4), err5) @@ -165,13 +192,14 @@ end subroutine ocn_vmix_coefs!}}} !> \brief Computes tendencies for implicit momentum vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the tendencies for implicit vertical mixing for momentum !> using computed coefficients. ! !----------------------------------------------------------------------- - subroutine ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscTopOfEdge, layerThickness, layerThicknessEdge, normalVelocity, err)!{{{ + subroutine ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscTopOfEdge, layerThickness, & !{{{ + layerThicknessEdge, normalVelocity, err) !----------------------------------------------------------------- ! @@ -220,8 +248,9 @@ subroutine ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscT ! !----------------------------------------------------------------- - integer :: iEdge, k, cell1, cell2, N - integer, pointer :: nEdges, nVertLevels + integer :: iEdge, k, cell1, cell2, N, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nEdgesArray integer, dimension(:), pointer :: maxLevelEdgeTop @@ -229,23 +258,22 @@ subroutine ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscT real (kind=RKIND), dimension(:), allocatable :: A, B, C, velTemp - real (kind=RKIND), pointer :: config_bottom_drag_coeff - err = 0 if(.not.velVmixOn) return - call mpas_pool_get_config(ocnConfigs, 'config_bottom_drag_coeff', config_bottom_drag_coeff) - - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - allocate(A(nVertLevels),B(nVertLevels),C(nVertLevels),velTemp(nVertLevels)) + nEdges = nEdgesArray( 1 ) + + allocate(A(nVertLevels),B(nVertLevels),C(nVertLevels),velTemp(nVertLevels)) A(1)=0 + !$omp do schedule(runtime) private(N, cell1, cell2, k) do iEdge = 1, nEdges N = maxLevelEdgeTop(iEdge) if (N .gt. 0) then @@ -256,19 +284,19 @@ subroutine ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscT cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) do k = 1, N - layerThicknessEdge(k,iEdge) = 0.5 * (layerThickness(k,cell1) + layerThickness(k,cell2)) + layerThicknessEdge(k,iEdge) = 0.5_RKIND * (layerThickness(k,cell1) + layerThickness(k,cell2)) end do ! A is lower diagonal term do k = 2, N - A(k) = -2.0*dt*vertViscTopOfEdge(k,iEdge) & + A(k) = -2.0_RKIND*dt*vertViscTopOfEdge(k,iEdge) & / (layerThicknessEdge(k-1,iEdge) + layerThicknessEdge(k,iEdge)) & / layerThicknessEdge(k,iEdge) enddo ! C is upper diagonal term do k = 1, N-1 - C(k) = -2.0*dt*vertViscTopOfEdge(k+1,iEdge) & + C(k) = -2.0_RKIND*dt*vertViscTopOfEdge(k+1,iEdge) & / (layerThicknessEdge(k,iEdge) + layerThicknessEdge(k+1,iEdge)) & / layerThicknessEdge(k,iEdge) enddo @@ -281,16 +309,17 @@ subroutine ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscT ! Apply bottom drag boundary condition on the viscous term ! second line uses sqrt(2.0*kineticEnergyEdge(k,iEdge)) - B(N) = 1 - A(N) + dt*config_bottom_drag_coeff & + B(N) = 1 - A(N) + dt*implicitBottomDragCoef & * sqrt(kineticEnergyCell(k,cell1) + kineticEnergyCell(k,cell2)) / layerThicknessEdge(k,iEdge) call tridiagonal_solve(A(2:N),B,C(1:N-1),normalVelocity(:,iEdge),velTemp,N) normalVelocity(1:N,iEdge) = velTemp(1:N) - normalVelocity(N+1:nVertLevels,iEdge) = 0.0 + normalVelocity(N+1:nVertLevels,iEdge) = 0.0_RKIND end if end do + !$omp end do deallocate(A,B,C,velTemp) @@ -305,13 +334,15 @@ end subroutine ocn_vel_vmix_tend_implicit!}}} !> \brief Computes tendencies for implicit tracer vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the tendencies for implicit vertical mixing for !> tracers using computed coefficients. ! !----------------------------------------------------------------------- - subroutine ocn_tracer_vmix_tend_implicit(meshPool, dt, vertDiffTopOfCell, layerThickness, tracers, err)!{{{ + subroutine ocn_tracer_vmix_tend_implicit(meshPool, dt, vertDiffTopOfCell, layerThickness, tracers, & + vertNonLocalFlux, tracerGroupSurfaceFlux, config_cvmix_kpp_nonlocal_with_implicit_mix, & + err)!{{{ !----------------------------------------------------------------- ! @@ -329,8 +360,13 @@ subroutine ocn_tracer_vmix_tend_implicit(meshPool, dt, vertDiffTopOfCell, layerT dt !< Input: time step real (kind=RKIND), dimension(:,:), intent(in) :: & - layerThickness !< Input: thickness at cell center + layerThickness, & !< Input: thickness at cell center + tracerGroupSurfaceFlux !< Input: surface flux for tracers nonlocal computation + real (kind=RKIND), dimension(:,:,:), intent(in) :: & + vertNonLocalFlux !non local flux at interfaces + + logical, intent(in) :: config_cvmix_kpp_nonlocal_with_implicit_mix !----------------------------------------------------------------- ! ! input/output variables @@ -354,26 +390,32 @@ subroutine ocn_tracer_vmix_tend_implicit(meshPool, dt, vertDiffTopOfCell, layerT ! !----------------------------------------------------------------- - integer :: iCell, k, num_tracers, N - integer, pointer :: nCells, nVertLevels + integer :: iCell, k, num_tracers, N, nCells + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray integer, dimension(:), pointer :: maxLevelCell real (kind=RKIND), dimension(:), allocatable :: A,B,C - real (kind=RKIND), dimension(:,:), allocatable :: tracersTemp + real (kind=RKIND), dimension(:,:), allocatable :: tracersTemp, rhs err = 0 if(.not.tracerVmixOn) return - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) num_tracers = size(tracers, dim=1) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) allocate(A(nVertLevels),B(nVertLevels),C(nVertLevels),tracersTemp(num_tracers,nVertLevels)) + allocate(rhs(num_tracers,nVertLevels)) + + nCells = nCellsArray( 1 ) + call mpas_timer_start('vmix tracers tend imp loop', .false.) + !$omp do schedule(runtime) private(N, k) do iCell = 1, nCells ! Compute A(k), B(k), C(k) for tracers N = maxLevelCell(iCell) @@ -381,30 +423,40 @@ subroutine ocn_tracer_vmix_tend_implicit(meshPool, dt, vertDiffTopOfCell, layerT ! A is lower diagonal term A(1)=0 do k = 2, N - A(k) = -2.0*dt*vertDiffTopOfCell(k,iCell) & + A(k) = -2.0_RKIND*dt*vertDiffTopOfCell(k,iCell) & / (layerThickness(k-1,iCell) + layerThickness(k,iCell)) / layerThickness(k,iCell) enddo ! C is upper diagonal term do k = 1, N-1 - C(k) = -2.0*dt*vertDiffTopOfCell(k+1,iCell) & + C(k) = -2.0_RKIND*dt*vertDiffTopOfCell(k+1,iCell) & / (layerThickness(k,iCell) + layerThickness(k+1,iCell)) / layerThickness(k,iCell) enddo - C(N) = 0.0 + C(N) = 0.0_RKIND ! B is diagonal term do k = 1, N B(k) = 1 - A(k) - C(k) enddo - call tridiagonal_solve_mult(A(2:N),B,C(1:N-1),tracers(:,:,iCell), & - tracersTemp, N, nVertLevels,num_tracers) + if ( config_cvmix_kpp_nonlocal_with_implicit_mix ) then + call ocn_compute_kpp_rhs(tracers(:,:,iCell), rhs(:,:), dt, N, num_tracers, & + layerThickness(:,iCell), vertNonLocalFlux(:,:,iCell), & + tracerGroupSurfaceFlux(:,iCell)) + else + rhs(:,:) = tracers(:,:,iCell) + endif + + call tridiagonal_solve_mult(A(2:N), B, C(1:N-1), rhs(:,:), & + tracersTemp, N, nVertLevels, num_tracers) tracers(:,1:N,iCell) = tracersTemp(:,1:N) tracers(:,N+1:nVertLevels,iCell) = -1e34 end do + !$omp end do + call mpas_timer_stop('vmix tracers tend imp loop') - deallocate(A,B,C,tracersTemp) + deallocate(A, B, C, tracersTemp, rhs) !-------------------------------------------------------------------- @@ -417,42 +469,61 @@ end subroutine ocn_tracer_vmix_tend_implicit!}}} !> \brief Driver for implicit vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine is a driver for handling implicit vertical mixing !> of both momentum and tracers for a block. It's intended to reduce !> redundant code. ! !----------------------------------------------------------------------- - subroutine ocn_vmix_implicit(dt, meshPool, diagnosticsPool, statePool, err, timeLevelIn)!{{{ + subroutine ocn_vmix_implicit(dt, meshPool, diagnosticsPool, statePool, forcingPool, scratchPool, err, timeLevelIn)!{{{ real (kind=RKIND), intent(in) :: dt type (mpas_pool_type), intent(in) :: meshPool type (mpas_pool_type), intent(inout) :: diagnosticsPool type (mpas_pool_type), intent(inout) :: statePool + type (mpas_pool_type), intent(inout) :: forcingPool + type (mpas_pool_type), intent(in) :: scratchPool !< Input/Output: Scratch structure integer, intent(out) :: err integer, intent(in), optional :: timeLevelIn - integer :: timeLevel, k, cell1, cell2, iEdge - integer, pointer :: nCells, nEdges - real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, layerThickness, layerThicknessEdge, vertViscTopOfEdge, vertDiffTopOfCell, kineticEnergyCell - real (kind=RKIND), dimension(:,:), pointer :: vertViscTopOfCell - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + type (mpas_pool_type), pointer :: tracersPool, tracersSurfaceFluxPool + + integer :: iCell, timeLevel, k, cell1, cell2, iEdge, nCells, nEdges + integer, dimension(:), pointer :: nCellsArray, nEdgesArray + real (kind=RKIND), dimension(:,:), pointer :: normalVelocity, layerThickness, layerThicknessEdge, vertViscTopOfEdge, & + vertDiffTopOfCell, kineticEnergyCell + real (kind=RKIND), dimension(:,:), pointer :: vertViscTopOfCell, nonLocalSurfaceTracerFlux, tracerGroupSurfaceFlux + real (kind=RKIND), dimension(:,:,:), pointer :: tracersGroup, vertNonLocalFlux + real (kind=RKIND), dimension(:,:,:), allocatable :: nonLocalFluxTend + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracerVertMixTendency integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop integer, dimension(:,:), pointer :: cellsOnEdge - logical, pointer :: config_use_cvmix + logical, pointer :: config_use_cvmix,config_compute_active_tracer_budgets + logical, pointer :: config_cvmix_kpp_nonlocal_with_implicit_mix + + type (mpas_pool_iterator_type) :: groupItr + character (len=StrKIND) :: modifiedGroupName + integer, pointer :: indexTempFlux, indexSaltFlux, nVertLevels err = 0 + call mpas_timer_start('vmix imp') + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + if (present(timeLevelIn)) then timeLevel = timeLevelIn else timeLevel = 1 end if + call mpas_pool_get_subpool(forcingPool, 'tracersSurfaceFlux', tracersSurfaceFluxPool) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix', config_use_cvmix) - + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_nonlocal_with_implicit_mix', & + config_cvmix_kpp_nonlocal_with_implicit_mix) + call mpas_pool_get_config(ocnConfigs, 'config_compute_active_tracer_budgets', & + config_compute_active_tracer_budgets) call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) call mpas_pool_get_array(diagnosticsPool, 'kineticEnergyCell', kineticEnergyCell) @@ -460,38 +531,102 @@ subroutine ocn_vmix_implicit(dt, meshPool, diagnosticsPool, statePool, err, time call mpas_pool_get_array(diagnosticsPool, 'vertViscTopOfEdge', vertViscTopOfEdge) call mpas_pool_get_array(diagnosticsPool, 'vertDiffTopOfCell', vertDiffTopOfCell) call mpas_pool_get_array(diagnosticsPool, 'vertViscTopOfCell', vertViscTopOfCell) + call mpas_pool_get_array(diagnosticsPool, & + 'activeTracerVertMixTendency',activeTracerVertMixTendency) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) call mpas_pool_get_array(meshPool, 'cellsOnEdge', cellsOnEdge) - - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) - call ocn_vmix_coefs(meshPool, statePool, diagnosticsPool, err, timeLevel) + call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) + + call mpas_timer_start('vmix coefs', .false.) + call ocn_vmix_coefs(meshPool, statePool, forcingPool, diagnosticsPool, scratchPool, err, timeLevel) + call mpas_timer_stop('vmix coefs') + nCells = nCellsArray(1) + call mpas_pool_get_array(diagnosticsPool, 'vertNonLocalFlux', vertNonLocalFlux) ! if using CVMix, then viscosity has to be averaged from cell centers to cell edges if ( config_use_cvmix ) then - vertViscTopOfEdge(:,:) = 0.0 - do iEdge=1,nEdges - cell1 = cellsOnEdge(1,iEdge) - cell2 = cellsOnEdge(2,iEdge) - do k=1,maxLevelEdgeTop(iEdge) - vertViscTopOfEdge(k,iEdge) = 0.5*(vertViscTopOfCell(k,cell2)+vertViscTopOfCell(k,cell1)) - enddo - enddo + + nEdges = nEdgesArray( 1 ) + call mpas_timer_start('CVMix avg', .false.) + !$omp do schedule(runtime) private(cell1, cell2, k) + do iEdge=1,nEdges + vertViscTopOfEdge(:, iEdge) = 0.0_RKIND + cell1 = cellsOnEdge(1,iEdge) + cell2 = cellsOnEdge(2,iEdge) + do k=1,maxLevelEdgeTop(iEdge) + vertViscTopOfEdge(k,iEdge) = 0.5_RKIND*(vertViscTopOfCell(k,cell2)+vertViscTopOfCell(k,cell1)) + end do + end do + !$omp end do + call mpas_timer_stop('CVMix avg') endif ! ! Implicit vertical solve for momentum ! - call ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscTopOfEdge, layerThickness, layerThicknessEdge, normalVelocity, err) - - ! - ! Implicit vertical solve for tracers - ! + call mpas_timer_start('vmix solve momentum', .false.) + call ocn_vel_vmix_tend_implicit(meshPool, dt, kineticEnergyCell, vertViscTopOfEdge, layerThickness, layerThicknessEdge, & + normalVelocity, err) + call mpas_timer_stop('vmix solve momentum') + + ! + ! Implicit vertical solve for all tracers + ! + + call mpas_timer_start('vmix solve tracers', .false.) + call mpas_pool_begin_iteration(tracersPool) + do while ( mpas_pool_get_next_member(tracersPool, groupItr) ) + + if ( groupItr % memberType == MPAS_POOL_FIELD ) then + call mpas_pool_get_array(tracersPool, groupItr % memberName, tracersGroup, timeLevel) + ! store tracers + if (trim(groupItr % memberName) == 'activeTracers') then + if (config_compute_active_tracer_budgets) then + !$omp do schedule(runtime) + do iCell = 1, nCells + activeTracerVertMixTendency(:,:,iCell)=tracersGroup(:,:,iCell) + end do + !$omp end do + endif + endif + + if ( associated(tracersGroup) ) then + if (trim(groupItr % memberName) == 'activeTracers') then + call mpas_pool_get_array(tracersSurfaceFluxPool, 'nonLocalSurfaceTracerFlux', & + tracerGroupSurfaceFlux) + else + modifiedGroupName = trim(groupItr % memberName) // "SurfaceFlux" + call mpas_pool_get_array(tracersSurfaceFluxPool, trim(modifiedGroupName), & + tracerGroupSurfaceFlux) + endif + + call ocn_tracer_vmix_tend_implicit(meshPool, dt, vertDiffTopOfCell, layerThickness, tracersGroup, & + vertNonLocalFlux, tracerGroupSurfaceFlux, & + config_cvmix_kpp_nonlocal_with_implicit_mix, err) + end if + + ! difference tracers to compute influence of vertical mixing and divide by dt + if (trim(groupItr % memberName) == 'activeTracers') then + if (config_compute_active_tracer_budgets) then + !$omp do schedule(runtime) + do iCell = 1, nCells + activeTracerVertMixTendency(:,:,iCell) = & + (tracersGroup(:,:,iCell) - activeTracerVertMixTendency(:,:,iCell)) / dt + end do + !$omp end do + endif + endif + + end if + end do + call mpas_timer_stop('vmix solve tracers') - call ocn_tracer_vmix_tend_implicit(meshPool, dt, vertDiffTopOfCell, layerThickness, tracers, err) + call mpas_timer_stop('vmix imp') end subroutine ocn_vmix_implicit!}}} @@ -502,9 +637,9 @@ end subroutine ocn_vmix_implicit!}}} !> \brief Initializes ocean vertical mixing quantities !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> vertical mixing in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> vertical mixing in the ocean. ! !----------------------------------------------------------------------- @@ -524,11 +659,15 @@ subroutine ocn_vmix_init(domain, err)!{{{ integer :: err_tmp logical, pointer :: config_disable_vel_vmix, config_disable_tr_vmix + logical, pointer :: config_use_implicit_bottom_drag + real (kind=RKIND), pointer :: config_implicit_bottom_drag_coeff err = 0 call mpas_pool_get_config(ocnConfigs, 'config_disable_vel_vmix', config_disable_vel_vmix) call mpas_pool_get_config(ocnConfigs, 'config_disable_tr_vmix', config_disable_tr_vmix) + call mpas_pool_get_config(ocnConfigs, 'config_use_implicit_bottom_drag', config_use_implicit_bottom_drag) + call mpas_pool_get_config(ocnConfigs, 'config_implicit_bottom_drag_coeff', config_implicit_bottom_drag_coeff) velVmixOn = .true. tracerVmixOn = .true. @@ -536,6 +675,12 @@ subroutine ocn_vmix_init(domain, err)!{{{ if(config_disable_vel_vmix) velVmixOn = .false. if(config_disable_tr_vmix) tracerVmixOn = .false. + implicitBottomDragCoef = 0.0_RKIND + + if (config_use_implicit_bottom_drag) then + implicitBottomDragCoef = config_implicit_bottom_drag_coeff + endif + call ocn_vmix_coefs_const_init(err_tmp) err = ior(err, err_tmp) call ocn_vmix_coefs_tanh_init(err_tmp) @@ -558,7 +703,7 @@ end subroutine ocn_vmix_init!}}} !> \brief Solve the matrix equation Ax=r for x, where A is tridiagonal. !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> Solve the matrix equation Ax=r for x, where A is tridiagonal. !> A is an nxn matrix, with: !> a sub-diagonal, filled from 1:n-1 (a(1) appears on row 2) @@ -567,7 +712,7 @@ end subroutine ocn_vmix_init!}}} ! !----------------------------------------------------------------------- subroutine tridiagonal_solve(a,b,c,r,x,n) !{{{ - + !----------------------------------------------------------------- ! ! input variables @@ -598,14 +743,14 @@ subroutine tridiagonal_solve(a,b,c,r,x,n) !{{{ ! Use work variables for b and r bTemp(1) = b(1) rTemp(1) = r(1) - + ! First pass: set the coefficients do i = 2,n m = a(i-1)/bTemp(i-1) bTemp(i) = b(i) - m*c(i-1) rTemp(i) = r(i) - m*rTemp(i-1) - end do - + end do + x(n) = rTemp(n)/bTemp(n) ! Second pass: back-substition do i = n-1, 1, -1 @@ -621,7 +766,7 @@ end subroutine tridiagonal_solve !}}} !> \brief Solve multiple matrix equations Ax=r for x, where A is tridiagonal. !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> Solve the matrix equation Ax=r for x, where A is tridiagonal. !> A is an nxn matrix, with: !> a sub-diagonal, filled from 1:n-1 (a(1) appears on row 2) @@ -639,22 +784,22 @@ subroutine tridiagonal_solve_mult(a,b,c,r,x,n,nDim,nSystems)!{{{ real (KIND=RKIND), dimension(nSystems,n) :: rTemp real (KIND=RKIND) :: m integer i,j - + ! Use work variables for b and r bTemp(1) = b(1) do j = 1,nSystems rTemp(j,1) = r(j,1) end do - + ! First pass: set the coefficients do i = 2,n m = a(i-1)/bTemp(i-1) bTemp(i) = b(i) - m*c(i-1) do j = 1,nSystems rTemp(j,i) = r(j,i) - m*rTemp(j,i-1) - end do - end do - + end do + end do + do j = 1,nSystems x(j,n) = rTemp(j,n)/bTemp(n) end do @@ -664,9 +809,53 @@ subroutine tridiagonal_solve_mult(a,b,c,r,x,n,nDim,nSystems)!{{{ x(j,i) = (rTemp(j,i) - c(i)*x(j,i+1))/bTemp(i) end do end do - + end subroutine tridiagonal_solve_mult!}}} +!*********************************************************************** +! +! subroutine ocn_compute_kpp_rhs +! +!> \brief Computes the non local flux tendency for KPP +!> \author Luke Van Roekel +!> \date October 2017 +!> \details +!> Computes non local flux tendency from KPP when +!> config_cvmix_kpp_nonlocal_with_implicit_mix = .true. +!> otherwise this term is computed in ocn_tend_tracer +! +!----------------------------------------------------------------------- + +subroutine ocn_compute_kpp_rhs(tracers, rhs, dt, maxLevelCell, nTracers, & + layerThickness, vertNonLocalFlux, tracerGroupSurfaceFlux)!{{{ + + real (kind=RKIND), intent(in) :: dt + real (kind=RKIND), dimension(:,:), intent(in) :: vertNonLocalFlux, tracers + real (kind=RKIND), dimension(:), intent(in) :: layerThickness, tracerGroupSurfaceFlux + real (kind=RKIND), dimension(:,:), intent(inout) :: rhs + integer, intent(in) :: maxLevelCell, nTracers + integer :: iTracer, k + + do k=2,maxLevelCell-1 + do iTracer=1,nTracers + rhs(iTracer, k) = tracers(iTracer,k) + dt * tracerGroupSurfaceFlux(iTracer) * & + (vertNonLocalFlux(1,k) - vertNonLocalFlux(1,k+1)) / layerThickness(k) + enddo + enddo + + k=1 + do iTracer=1,nTracers + rhs(iTracer, k) = tracers(iTracer,k) + dt * tracerGroupSurfaceFlux(iTracer) * & + (-vertNonLocalFlux(1,k+1) )/ layerThickness(k) + enddo + + k=maxLevelCell + do iTracer=1,nTracers + rhs(iTracer,k) = tracers(iTracer,k) + dt * tracerGroupSurfaceFlux(iTracer) * & + vertNonLocalFlux(1,k) / layerThickness(k) + enddo + +end subroutine ocn_compute_kpp_rhs!}}} !*********************************************************************** end module ocn_vmix diff --git a/src/core_ocean/shared/mpas_ocn_vmix_coefs_const.F b/src/core_ocean/shared/mpas_ocn_vmix_coefs_const.F index f11ba0e409..13de5791d6 100644 --- a/src/core_ocean/shared/mpas_ocn_vmix_coefs_const.F +++ b/src/core_ocean/shared/mpas_ocn_vmix_coefs_const.F @@ -13,8 +13,8 @@ !> \author Mark Petersen !> \date September 2011 !> \details -!> This module contains the routines for computing -!> constant vertical mixing coefficients. +!> This module contains the routines for computing +!> constant vertical mixing coefficients. !> ! !----------------------------------------------------------------------- @@ -70,7 +70,7 @@ module ocn_vmix_coefs_const !> \brief Computes coefficients for vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the vertical mixing coefficients for momentum !> and tracers based user choices of mixing parameterization. ! @@ -125,7 +125,7 @@ subroutine ocn_vmix_coefs_const_build(meshPool, statePool, diagnosticsPool, err, !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- @@ -157,7 +157,7 @@ end subroutine ocn_vmix_coefs_const_build!}}} !> \brief Computes coefficients for vertical momentum mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the constant vertical mixing coefficients for momentum ! !----------------------------------------------------------------------- @@ -187,6 +187,9 @@ subroutine ocn_vel_vmix_coefs_const(meshPool, vertViscTopOfEdge, err)!{{{ real (kind=RKIND), dimension(:,:), intent(out) :: vertViscTopOfEdge !< Output: vertical viscosity + integer :: iEdge, nEdges + integer, pointer :: nEdgesSolve + integer, intent(out) :: err !< Output: error flag !----------------------------------------------------------------- @@ -199,7 +202,18 @@ subroutine ocn_vel_vmix_coefs_const(meshPool, vertViscTopOfEdge, err)!{{{ if ( .not. constViscOn ) return - vertViscTopOfEdge = vertViscTopOfEdge + constVisc + call mpas_timer_start('vel const coef') + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) + + nEdges = nEdgesSolve + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + vertViscTopOfEdge(:, iEdge) = vertViscTopOfEdge(:, iEdge) + constVisc + end do + !$omp end do + + call mpas_timer_stop('vel const coef') !-------------------------------------------------------------------- @@ -212,7 +226,7 @@ end subroutine ocn_vel_vmix_coefs_const!}}} !> \brief Computes coefficients for vertical tracer mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the constant vertical mixing coefficients for tracers ! !----------------------------------------------------------------------- @@ -242,6 +256,9 @@ subroutine ocn_tracer_vmix_coefs_const(meshPool, vertDiffTopOfCell, err)!{{{ real (kind=RKIND), dimension(:,:), intent(out) :: vertDiffTopOfCell !< Output: Vertical diffusion + integer :: iCell, nCells + integer, pointer :: nCellsSolve + integer, intent(out) :: err !< Output: error flag !----------------------------------------------------------------- @@ -254,7 +271,19 @@ subroutine ocn_tracer_vmix_coefs_const(meshPool, vertDiffTopOfCell, err)!{{{ if ( .not. constDiffOn ) return - vertDiffTopOfCell = vertDiffTopOfCell + constDiff + call mpas_timer_start('tracer const coef') + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + nCells = nCellsSolve + + !$omp do schedule(runtime) + do iCell = 1, nCells + vertDiffTopOfCell(:, iCell) = vertDiffTopOfCell(:, iCell) + constDiff + end do + !$omp end do + + call mpas_timer_stop('tracer const coef') !-------------------------------------------------------------------- @@ -267,11 +296,11 @@ end subroutine ocn_tracer_vmix_coefs_const!}}} !> \brief Initializes ocean momentum vertical mixing quantities !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> vertical velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> vertical velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_vmix_coefs_redi.F b/src/core_ocean/shared/mpas_ocn_vmix_coefs_redi.F index 5b1495c8a4..f6e5ab4555 100644 --- a/src/core_ocean/shared/mpas_ocn_vmix_coefs_redi.F +++ b/src/core_ocean/shared/mpas_ocn_vmix_coefs_redi.F @@ -8,7 +8,7 @@ !> \version SVN:$Id:$ !> \details !> This module contains the routines for compounding -!> the Redi vertical mixing coefficients. +!> the Redi vertical mixing coefficients. !> ! !----------------------------------------------------------------------- @@ -64,7 +64,7 @@ module ocn_vmix_coefs_redi !> \author Doug Jacobsen !> \date 19 September 2011 !> \version SVN:$Id$ -!> \details +!> \details !> This routine computes the vertical mixing coefficients for momentum !> and tracers based user choices of mixing parameterization. ! @@ -115,7 +115,7 @@ subroutine ocn_vmix_coefs_redi_build(meshPool, statePool, diagnosticsPool, err, !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- @@ -141,7 +141,7 @@ end subroutine ocn_vmix_coefs_redi_build!}}} !> \author Doug Jacobsen !> \date 19 September 2011 !> \version SVN:$Id$ -!> \details +!> \details !> This routine computes the rediant vertical mixing coefficients for tracers ! !----------------------------------------------------------------------- @@ -173,6 +173,9 @@ subroutine ocn_tracer_vmix_coefs_redi(meshPool, vertDiffTopOfCell, vertRediDiff, real (kind=RKIND), dimension(:,:), intent(in) :: vertRediDiff !< Output: Vertical diffusion + integer :: iCell, nCells + integer, pointer :: nCellsSolve + integer, intent(out) :: err !< Output: error flag !----------------------------------------------------------------- @@ -185,7 +188,19 @@ subroutine ocn_tracer_vmix_coefs_redi(meshPool, vertDiffTopOfCell, vertRediDiff, if(.not.rediDiffOn) return - vertDiffTopOfCell = vertDiffTopOfCell + vertRediDiff + call mpas_timer_start('tracer redi coef') + + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + + nCells = nCellsSolve + + !$omp do schedule(runtime) + do iCell = 1, nCells + vertDiffTopOfCell(:, iCell) = vertDiffTopOfCell(:, iCell) + vertRediDiff(:, iCell) + end do + !$omp end do + + call mpas_timer_stop('tracer redi coef') !-------------------------------------------------------------------- @@ -200,11 +215,11 @@ end subroutine ocn_tracer_vmix_coefs_redi!}}} !> \author Doug Jacobsen !> \date 19 September 2011 !> \version SVN:$Id$ -!> \details -!> This routine initializes a variety of quantities related to -!> vertical velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> vertical velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_vmix_coefs_rich.F b/src/core_ocean/shared/mpas_ocn_vmix_coefs_rich.F index 1185fcd305..252090f502 100644 --- a/src/core_ocean/shared/mpas_ocn_vmix_coefs_rich.F +++ b/src/core_ocean/shared/mpas_ocn_vmix_coefs_rich.F @@ -13,8 +13,8 @@ !> \author Mark Petersen !> \date September 2011 !> \details -!> This module contains the routines for computing -!> richardson vertical mixing coefficients. +!> This module contains the routines for computing +!> richardson vertical mixing coefficients. !> ! !----------------------------------------------------------------------- @@ -25,6 +25,7 @@ module ocn_vmix_coefs_rich use mpas_pool_routines use mpas_constants use mpas_timer + use mpas_threading use ocn_constants use ocn_equation_of_state @@ -67,12 +68,12 @@ module ocn_vmix_coefs_rich !> \brief Computes coefficients for vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the vertical mixing coefficients for momentum -!> and tracers based user choices of mixing parameterization. +!> and activeTracers based user choices of mixing parameterization. ! !----------------------------------------------------------------------- - subroutine ocn_vmix_coefs_rich_build(meshPool, statePool, diagnosticsPool, err, timeLevelIn)!{{{ + subroutine ocn_vmix_coefs_rich_build(meshPool, statePool, diagnosticsPool, scratchPool, err, timeLevelIn)!{{{ !----------------------------------------------------------------- ! @@ -83,6 +84,8 @@ subroutine ocn_vmix_coefs_rich_build(meshPool, statePool, diagnosticsPool, err, type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information + type (mpas_pool_type), intent(in) :: scratchPool !< Input/Output: Scratch structure + integer, intent(in), optional :: timeLevelIn !< Input: Time level for state pool !----------------------------------------------------------------- @@ -111,34 +114,43 @@ subroutine ocn_vmix_coefs_rich_build(meshPool, statePool, diagnosticsPool, err, ! !----------------------------------------------------------------- - integer :: err1, err2, err3, timeLevel - integer, pointer :: indexT, indexS + integer :: err1, err2, err3, timeLevel, nCells + integer, pointer :: indexTemperature, indexSalinity, nCellsSolve + + type (mpas_pool_type), pointer :: tracersPool real (kind=RKIND), dimension(:,:), pointer :: & vertViscTopOfEdge, vertDiffTopOfCell, normalVelocity, layerThickness, layerThicknessEdge, density, displacedDensity real (kind=RKIND), dimension(:,:), pointer :: RiTopOfEdge, RiTopOfCell - real (kind=RKIND), dimension(:,:,:), pointer :: tracers + real (kind=RKIND), dimension(:,:,:), pointer :: activeTracers !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- err = 0 + if ( .not. richViscOn .and. .not. richDiffOn ) then + return + end if + + call mpas_pool_get_subpool(statePool, 'tracers', tracersPool) + if (present(timeLevelIn)) then timeLevel = timeLevelIn else timeLevel = 1 end if - call mpas_pool_get_dimension(statePool, 'index_temperature', indexT) - call mpas_pool_get_dimension(statePool, 'index_salinity', indexS) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) + call mpas_pool_get_dimension(tracersPool, 'index_temperature', indexTemperature) + call mpas_pool_get_dimension(tracersPool, 'index_salinity', indexSalinity) call mpas_pool_get_array(diagnosticsPool, 'vertViscTopOfEdge', vertViscTopOfEdge) call mpas_pool_get_array(diagnosticsPool, 'vertDiffTopOfCell', vertDiffTopOfCell) @@ -150,21 +162,21 @@ subroutine ocn_vmix_coefs_rich_build(meshPool, statePool, diagnosticsPool, err, call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) call mpas_pool_get_array(statePool, 'layerThickness', layerThickness, timeLevel) - call mpas_pool_get_array(statePool, 'tracers', tracers, timeLevel) + call mpas_pool_get_array(tracersPool, 'activeTracers', activeTracers, timeLevel) - call mpas_timer_start("eos rich") + nCells = nCellsSolve ! compute in-place density - call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, 0, 'relative', density, err, timeLevelIn=timeLevel) + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, nCells, 0, 'relative', density, err, & + timeLevelIn=timeLevel) - ! compute displacedDensity, density displaced adiabatically to the mid-depth one layer deeper. + ! compute displacedDensity, density displaced adiabatically to the mid-depth one layer deeper. ! That is, layer k has been displaced to the depth of layer k+1. - call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, 1, 'relative', displacedDensity, err, timeLevelIn=timeLevel) - - call mpas_timer_stop("eos rich") + call ocn_equation_of_state_density(statePool, diagnosticsPool, meshPool, scratchPool, nCells, 1, 'relative', & + displacedDensity, err, timeLevelIn=timeLevel) - call ocn_vmix_get_rich_numbers(meshPool, indexT, indexS, normalVelocity, layerThickness, layerThicknessEdge, & - density, displacedDensity, tracers, RiTopOfEdge, RiTopOfCell, err1) + call ocn_vmix_get_rich_numbers(meshPool, scratchPool, indexTemperature, indexSalinity, normalVelocity, layerThickness, & + layerThicknessEdge, density, displacedDensity, activeTracers, RiTopOfEdge, RiTopOfCell, err1) call ocn_vel_vmix_coefs_rich(meshPool, RiTopOfEdge, layerThicknessEdge, vertViscTopOfEdge, err2) call ocn_tracer_vmix_coefs_rich(meshPool, RiTopOfCell, layerThickness, vertDiffTopOfCell, err3) @@ -182,7 +194,7 @@ end subroutine ocn_vmix_coefs_rich_build!}}} !> \brief Computes coefficients for vertical momentum mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the richardson vertical mixing coefficients for momentum ! !----------------------------------------------------------------------- @@ -226,8 +238,8 @@ subroutine ocn_vel_vmix_coefs_rich(meshPool, RiTopOfEdge, layerThicknessEdge, ve ! !----------------------------------------------------------------- - integer :: iEdge, k - integer, pointer :: nEdges + integer :: iEdge, k, nEdges + integer, pointer :: nEdgesSolve integer, dimension(:), pointer :: maxLevelEdgeTop @@ -237,21 +249,26 @@ subroutine ocn_vel_vmix_coefs_rich(meshPool, RiTopOfEdge, layerThicknessEdge, ve if(.not.richViscOn) return + call mpas_timer_start('vel rich coef') + call mpas_pool_get_config(ocnConfigs, 'config_rich_mix', config_rich_mix) call mpas_pool_get_config(ocnConfigs, 'config_bkrd_vert_visc', config_bkrd_vert_visc) call mpas_pool_get_config(ocnConfigs, 'config_convective_visc', config_convective_visc) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) + nEdges = nEdgesSolve + + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges do k = 2, maxLevelEdgeTop(iEdge) ! efficiency note: these if statements are inside iEdge and k loops. ! Perhaps there is a more efficient way to do this. - if (RiTopOfEdge(k,iEdge)>0.0) then + if (RiTopOfEdge(k,iEdge)>0.0_RKIND) then vertViscTopOfEdge(k,iEdge) = vertViscTopOfEdge(k, iEdge) + config_bkrd_vert_visc & - + config_rich_mix / (1.0 + 5.0*RiTopOfEdge(k,iEdge))**2 + + config_rich_mix / (1.0_RKIND + 5.0_RKIND*RiTopOfEdge(k,iEdge))**2 if (vertViscTopOfEdge(k,iEdge) > config_convective_visc) then vertViscTopOfEdge(k,iEdge) = config_convective_visc end if @@ -261,6 +278,9 @@ subroutine ocn_vel_vmix_coefs_rich(meshPool, RiTopOfEdge, layerThicknessEdge, ve end if end do end do + !$omp end do + + call mpas_timer_stop('vel rich coef') !-------------------------------------------------------------------- @@ -274,8 +294,8 @@ end subroutine ocn_vel_vmix_coefs_rich!}}} !> \brief Computes coefficients for vertical tracer mixing !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine computes the richardson vertical mixing coefficients for tracers +!> \details +!> This routine computes the richardson vertical mixing coefficients for activeTracers ! !----------------------------------------------------------------------- @@ -318,38 +338,42 @@ subroutine ocn_tracer_vmix_coefs_rich(meshPool, RiTopOfCell, layerThickness, ver ! !----------------------------------------------------------------- - integer :: iCell, k - integer, pointer :: nCells + integer :: iCell, k, nCells + integer, pointer :: nCellsSolve integer, dimension(:), pointer :: maxLevelCell real (kind=RKIND) :: coef - real (kind=RKIND), pointer :: config_density0, config_bkrd_vert_diff, config_bkrd_vert_visc, config_rich_mix, config_convective_diff + real (kind=RKIND), pointer :: config_bkrd_vert_diff, config_bkrd_vert_visc, config_rich_mix, config_convective_diff err = 0 if(.not.richDiffOn) return - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) + call mpas_timer_start('tracer rich coef') + call mpas_pool_get_config(ocnConfigs, 'config_bkrd_vert_diff', config_bkrd_vert_diff) call mpas_pool_get_config(ocnConfigs, 'config_bkrd_vert_visc', config_bkrd_vert_visc) call mpas_pool_get_config(ocnConfigs, 'config_rich_mix', config_rich_mix) call mpas_pool_get_config(ocnConfigs, 'config_convective_diff', config_convective_diff) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) - coef = -gravity / config_density0 / 2.0 + nCells = nCellsSolve + + coef = -gravity / rho_sw / 2.0_RKIND + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 2, maxLevelCell(iCell) ! efficiency note: these if statements are inside iEdge and k loops. ! Perhaps there is a more efficient way to do this. - if (RiTopOfCell(k,iCell)>0.0) then + if (RiTopOfCell(k,iCell)>0.0_RKIND) then vertDiffTopOfCell(k,iCell) = vertDiffTopOfCell(k, iCell) + config_bkrd_vert_diff & - + (config_bkrd_vert_visc & - + config_rich_mix / (1.0 + 5.0*RiTopOfCell(k,iCell))**2) & - / (1.0 + 5.0*RiTopOfCell(k,iCell)) + + (config_bkrd_vert_visc & + + config_rich_mix / (1.0_RKIND + 5.0_RKIND*RiTopOfCell(k,iCell))**2) & + / (1.0_RKIND + 5.0_RKIND*RiTopOfCell(k,iCell)) if (vertDiffTopOfCell(k,iCell) > config_convective_diff) then vertDiffTopOfCell(k,iCell) = config_convective_diff end if @@ -359,7 +383,9 @@ subroutine ocn_tracer_vmix_coefs_rich(meshPool, RiTopOfCell, layerThickness, ver end if end do end do + !$omp end do + call mpas_timer_stop('tracer rich coef') !-------------------------------------------------------------------- @@ -372,14 +398,15 @@ end subroutine ocn_tracer_vmix_coefs_rich!}}} !> \brief Build richardson numbers for vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine builds the arrays needed for richardson number vertical !> mixing coefficients. ! !----------------------------------------------------------------------- - subroutine ocn_vmix_get_rich_numbers(meshPool, indexT, indexS, normalVelocity, layerThickness, layerThicknessEdge, & !{{{ - density, displacedDensity, tracers, RiTopOfEdge, RiTopOfCell, err) + subroutine ocn_vmix_get_rich_numbers(meshPool, scratchPool, indexTemperature, indexSalinity, normalVelocity, & !{{{ + layerThickness, layerThicknessEdge, density, displacedDensity, activeTracers, & + RiTopOfEdge, RiTopOfCell, err) !----------------------------------------------------------------- ! @@ -390,14 +417,16 @@ subroutine ocn_vmix_get_rich_numbers(meshPool, indexT, indexS, normalVelocity, l type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information - integer, intent(in) :: indexT !< Input: index for temperature - integer, intent(in) :: indexS !< Input: index for salinity + type (mpas_pool_type), intent(in) :: scratchPool !< Input: scratch variables + + integer, intent(in) :: indexTemperature !< Input: index for temperature + integer, intent(in) :: indexSalinity !< Input: index for salinity real (kind=RKIND), dimension(:,:), intent(in) :: normalVelocity !< Input: horizontal velocity real (kind=RKIND), dimension(:,:), intent(in) :: layerThickness !< Input: thickness - real (kind=RKIND), dimension(:,:), intent(in) :: layerThicknessEdge !< Input: thickness at edge + real (kind=RKIND), dimension(:,:), intent(in) :: layerThicknessEdge !< Input: thickness at edge - real (kind=RKIND), dimension(:,:,:), intent(in) :: tracers !< Input: tracers + real (kind=RKIND), dimension(:,:,:), intent(in) :: activeTracers !< Input: activeTracers !----------------------------------------------------------------- ! @@ -425,28 +454,29 @@ subroutine ocn_vmix_get_rich_numbers(meshPool, indexT, indexS, normalVelocity, l !----------------------------------------------------------------- integer :: iCell, iEdge, k, i - integer :: cell1, cell2 - integer, pointer :: nVertLevels, nCells, nEdges + integer :: cell1, cell2, nCells, nEdges + integer, pointer :: nVertLevels + integer, dimension(:), pointer :: nCellsArray, nEdgesArray integer, dimension(:), pointer :: maxLevelCell, maxLevelEdgeTop, maxLevelEdgeBot, nEdgesOnCell integer, dimension(:,:), pointer :: cellsOnEdge, edgesOncell, edgeSignOnCell real (kind=RKIND) :: coef, invAreaCell real (kind=RKIND), dimension(:), pointer :: dcEdge, dvEdge, areaCell - real (kind=RKIND), dimension(:,:), allocatable :: ddensityTopOfCell, du2TopOfCell, & - ddensityTopOfEdge, du2TopOfEdge - - real (kind=RKIND), pointer :: config_density0 + real (kind=RKIND), dimension(:,:), pointer :: ddensityTopOfCell, du2TopOfCell, & + ddensityTopOfEdge, du2TopOfEdge + type (field2DReal), pointer :: ddensityTopOfCellField, du2TopOfCellField, & + ddensityTopOfEdgeField, du2TopOfEdgeField err = 0 if ( ( .not. richViscOn ) .and. ( .not. richDiffOn ) ) return - call mpas_pool_get_config(ocnConfigs, 'config_density0', config_density0) + call mpas_timer_start('get rich nums') call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) - call mpas_pool_get_dimension(meshPool, 'nEdges', nEdges) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_dimension(meshPool, 'nEdgesArray', nEdgesArray) call mpas_pool_get_array(meshPool, 'maxLevelCell', maxLevelCell) call mpas_pool_get_array(meshPool, 'maxLevelEdgeTop', maxLevelEdgeTop) @@ -459,76 +489,124 @@ subroutine ocn_vmix_get_rich_numbers(meshPool, indexT, indexS, normalVelocity, l call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) call mpas_pool_get_array(meshPool, 'edgeSignOnCell', edgeSignOnCell) - allocate( & - ddensityTopOfCell(nVertLevels+1,nCells+1), ddensityTopOfEdge(nVertLevels+1,nEdges), & - du2TopOfCell(nVertLevels+1,nCells+1), du2TopOfEdge(nVertLevels+1,nEdges)) + call mpas_pool_get_field(scratchPool, 'ddensityTopOfCell', ddensityTopOfCellField) + call mpas_pool_get_field(scratchPool, 'ddensityTopOfEdge', ddensityTopOfEdgeField) + call mpas_pool_get_field(scratchPool, 'du2TopOfCell', du2TopOfCellField) + call mpas_pool_get_field(scratchPool, 'du2TopOfEdge', du2TopOfEdgeField) + call mpas_allocate_scratch_field(ddensityTopOfCellField, .true., .false.) + call mpas_allocate_scratch_field(ddensityTopOfEdgeField, .true., .false.) + call mpas_allocate_scratch_field(du2TopOfCellField, .true., .false.) + call mpas_allocate_scratch_field(du2TopOfEdgeField, .true., .false.) + call mpas_threading_barrier() + + ddensityTopOfCell => ddensityTopOfCellField % array + ddensityTopOfEdge => ddensityTopOfEdgeField % array + du2TopOfCell => du2TopOfCellField % array + du2TopOfEdge => du2TopOfEdgeField % array + + nCells = nCellsArray( size(nCellsArray) ) ! ddensityTopOfCell(k) = $\rho^*_{k-1}-\rho_k$, where $\rho^*$ has been adiabatically displaced to level k. - ddensityTopOfCell = 0.0 + !$omp do schedule(runtime) + do iCell = 1, nCells + ddensityTopOfCell(:, iCell) = 0.0_RKIND + du2TopOfCell(:, iCell) = 0.0_RKIND + RiTopOfCell(:, iCell) = 0.0_RKIND + end do + !$omp end do + + nEdges = nEdgesArray( size(nEdgesArray) ) + + !$omp do schedule(runtime) + do iEdge = 1, nEdges + ddensityTopOfEdge(:, iEdge) = 0.0_RKIND + du2TopOfEdge(:, iEdge) = 0.0_RKIND + RiTopOfEdge(:, iEdge) = 0.0_RKIND + end do + !$omp end do + + nCells = nCellsArray( 2 ) + + !$omp do schedule(runtime) private(k) do iCell = 1, nCells do k = 2, maxLevelCell(iCell) ddensityTopOfCell(k,iCell) = displacedDensity(k-1,iCell) - density(k,iCell) end do end do + !$omp end do + + nEdges = nEdgesArray( 2 ) - ! interpolate ddensityTopOfCell to ddensityTopOfEdge - ddensityTopOfEdge = 0.0 + !$omp do schedule(runtime) private(cell1, cell2, k) do iEdge = 1, nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) do k = 2, maxLevelEdgeTop(iEdge) + ! interpolate ddensityTopOfCell to ddensityTopOfEdge ddensityTopOfEdge(k,iEdge) = & (ddensityTopOfCell(k,cell1) + & - ddensityTopOfCell(k,cell2))/2 - end do - end do + ddensityTopOfCell(k,cell2))/2 - ! du2TopOfEdge(k) = $u_{k-1}-u_k$ - du2TopOfEdge=0.0 - do iEdge = 1, nEdges - do k = 2, maxLevelEdgeTop(iEdge) + ! du2TopOfEdge(k) = $u_{k-1}-u_k$ du2TopOfEdge(k,iEdge) = (normalVelocity(k-1,iEdge) - normalVelocity(k,iEdge))**2 end do - end do + end do + !$omp end do + + nCells = nCellsArray( 1 ) ! interpolate du2TopOfEdge to du2TopOfCell - du2TopOfCell = 0.0 + !$omp do schedule(runtime) private(invAreaCell, i, iEdge, k) do iCell = 1, nCells - invAreaCell = 1.0 / areaCell(iCell) + invAreaCell = 1.0_RKIND / areaCell(iCell) do i = 1, nEdgesOnCell(iCell) iEdge = edgesOnCell(i, iCell) do k = 2, maxLevelEdgeBot(iEdge) - du2TopOfCell(k, iCell) = du2TopOfCell(k, iCell) + 0.5 * dcEdge(iEdge) * dvEdge(iEdge) * du2TopOfEdge(k, iEdge) * invAreaCell + du2TopOfCell(k, iCell) = du2TopOfCell(k, iCell) + 0.5_RKIND * dcEdge(iEdge) * dvEdge(iEdge) & + * du2TopOfEdge(k, iEdge) * invAreaCell end do end do end do + !$omp end do ! compute RiTopOfEdge using ddensityTopOfEdge and du2TopOfEdge ! coef = -g/density_0/2 - RiTopOfEdge = 0.0 - coef = -gravity / config_density0 / 2.0 + coef = -gravity / rho_sw / 2.0_RKIND + + nEdges = nEdgesArray( 2 ) + + !$omp do schedule(runtime) private(k) do iEdge = 1, nEdges do k = 2, maxLevelEdgeTop(iEdge) RiTopOfEdge(k,iEdge) = coef * ddensityTopOfEdge(k,iEdge) & * ( layerThicknessEdge(k-1,iEdge) + layerThicknessEdge(k,iEdge) ) & - / ( du2TopOfEdge(k,iEdge) + 1e-20 ) + / ( du2TopOfEdge(k,iEdge) + 1e-20_RKIND ) end do end do + !$omp end do + + nCells = nCellsArray( 1 ) ! compute RiTopOfCell using ddensityTopOfCell and du2TopOfCell ! coef = -g/density_0/2 - RiTopOfCell = 0.0 + !$omp do schedule(runtime) private(k) do iCell = 1,nCells do k = 2,maxLevelCell(iCell) RiTopOfCell(k,iCell) = coef * ddensityTopOfCell(k,iCell) & * (layerThickness(k-1,iCell) + layerThickness(k,iCell)) & - / (du2TopOfCell(k,iCell) + 1e-20) + / (du2TopOfCell(k,iCell) + 1e-20_RKIND) end do end do + !$omp end do - deallocate(ddensityTopOfCell, ddensityTopOfEdge, & - du2TopOfCell, du2TopOfEdge) + call mpas_threading_barrier() + call mpas_deallocate_scratch_field(ddensityTopOfCellField, .true.) + call mpas_deallocate_scratch_field(ddensityTopOfEdgeField, .true.) + call mpas_deallocate_scratch_field(du2TopOfCellField, .true.) + call mpas_deallocate_scratch_field(du2TopOfEdgeField, .true.) + + call mpas_timer_stop('get rich nums') !-------------------------------------------------------------------- @@ -541,11 +619,11 @@ end subroutine ocn_vmix_get_rich_numbers!}}} !> \brief Initializes ocean momentum vertical mixing quantities !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> vertical velocity mixing in the ocean. Since a variety of +!> \details +!> This routine initializes a variety of quantities related to +!> vertical velocity mixing in the ocean. Since a variety of !> parameterizations are available, this routine primarily calls the -!> individual init routines for each parameterization. +!> individual init routines for each parameterization. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_vmix_coefs_tanh.F b/src/core_ocean/shared/mpas_ocn_vmix_coefs_tanh.F index a6f8c733ae..733a538c07 100644 --- a/src/core_ocean/shared/mpas_ocn_vmix_coefs_tanh.F +++ b/src/core_ocean/shared/mpas_ocn_vmix_coefs_tanh.F @@ -13,8 +13,8 @@ !> \author Mark Petersen !> \date September 2011 !> \details -!> This module contains the routines for computing -!> tanhant vertical mixing coefficients. +!> This module contains the routines for computing +!> tanhant vertical mixing coefficients. !> ! !----------------------------------------------------------------------- @@ -64,7 +64,7 @@ module ocn_vmix_coefs_tanh !> \brief Computes coefficients for vertical mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the vertical mixing coefficients for momentum !> and tracers based user choices of mixing parameterization. ! @@ -117,7 +117,7 @@ subroutine ocn_vmix_coefs_tanh_build(meshPool, statePool, diagnosticsPool, err, !----------------------------------------------------------------- ! ! call relevant routines for computing tendencies - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! tendencies will be added together ! !----------------------------------------------------------------- @@ -149,7 +149,7 @@ end subroutine ocn_vmix_coefs_tanh_build!}}} !> \brief Computes coefficients for vertical momentum mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the tanh vertical mixing coefficients for momentum ! !----------------------------------------------------------------------- @@ -187,34 +187,45 @@ subroutine ocn_vel_vmix_coefs_tanh(meshPool, vertViscTopOfEdge, err)!{{{ ! !----------------------------------------------------------------- - integer :: k - integer, pointer :: nVertLevels + integer :: k, iEdge, nEdges + integer, pointer :: nVertLevels, nEdgesSolve real (kind=RKIND), dimension(:), pointer :: refBottomDepth - real (kind=RKIND), pointer :: config_max_visc_tanh, config_min_visc_tanh, config_ZMid_tanh + real (kind=RKIND), pointer :: config_max_visc_tanh, config_min_visc_tanh, config_zMid_tanh real (kind=RKIND), pointer :: config_zWidth_tanh err = 0 if(.not.tanhViscOn) return + call mpas_timer_start('vel tanh coef') + call mpas_pool_get_config(ocnConfigs, 'config_max_visc_tanh', config_max_visc_tanh) call mpas_pool_get_config(ocnConfigs, 'config_min_visc_tanh', config_min_visc_tanh) - call mpas_pool_get_config(ocnConfigs, 'config_ZMid_tanh', config_ZMid_tanh) + call mpas_pool_get_config(ocnConfigs, 'config_zMid_tanh', config_zMid_tanh) call mpas_pool_get_config(ocnConfigs, 'config_zWidth_tanh', config_zWidth_tanh) + call mpas_pool_get_dimension(meshPool, 'nEdgesSolve', nEdgesSolve) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) - ! refBottomDepth is used here for simplicity. Using zMid and h, which + nEdges = nEdgesSolve + + ! refBottomDepth is used here for simplicity. Using zMid and h, which ! vary in time, would give the exact location of the top, but it ! would only change the diffusion value very slightly. - do k = 2, nVertLevels - vertViscTopOfEdge(k,:) = vertViscTopOfEdge(k,:) - (config_max_visc_tanh - config_min_visc_tanh) / 2.0 & - * tanh((refBottomDepth(k-1) + config_ZMid_tanh) & - / config_zWidth_tanh) & - + (config_max_visc_tanh + config_min_visc_tanh) / 2 + !$omp do schedule(runtime) private(k) + do iEdge = 1, nEdges + do k = 2, nVertLevels + vertViscTopOfEdge(k, iEdge) = vertViscTopOfEdge(k, iEdge) - (config_max_visc_tanh - config_min_visc_tanh) / 2.0_RKIND & + * tanh((refBottomDepth(k-1) + config_zMid_tanh) & + / config_zWidth_tanh) & + + (config_max_visc_tanh + config_min_visc_tanh) / 2 + end do end do + !$omp end do + + call mpas_timer_stop('vel tanh coef') !-------------------------------------------------------------------- @@ -228,7 +239,7 @@ end subroutine ocn_vel_vmix_coefs_tanh!}}} !> \brief Computes coefficients for vertical tracer mixing !> \author Mark Petersen !> \date September 2011 -!> \details +!> \details !> This routine computes the tanh vertical mixing coefficients for tracers ! !----------------------------------------------------------------------- @@ -266,35 +277,46 @@ subroutine ocn_tracer_vmix_coefs_tanh(meshPool, vertDiffTopOfCell, err)!{{{ ! !----------------------------------------------------------------- - integer :: k - integer, pointer :: nVertLevels + integer :: k, iCell, nCells + integer, pointer :: nVertLevels, nCellsSolve real (kind=RKIND), dimension(:), pointer :: refBottomDepth - real (kind=RKIND), pointer :: config_max_diff_tanh, config_min_diff_tanh, config_ZMid_tanh + real (kind=RKIND), pointer :: config_max_diff_tanh, config_min_diff_tanh, config_zMid_tanh real (kind=RKIND), pointer :: config_zWidth_tanh err = 0 if ( .not. tanhDiffOn ) return + call mpas_timer_start('tracer tanh coef') + call mpas_pool_get_config(ocnConfigs, 'config_max_diff_tanh', config_max_diff_tanh) call mpas_pool_get_config(ocnConfigs, 'config_min_diff_tanh', config_min_diff_tanh) - call mpas_pool_get_config(ocnConfigs, 'config_ZMid_tanh', config_ZMid_tanh) + call mpas_pool_get_config(ocnConfigs, 'config_zMid_tanh', config_zMid_tanh) call mpas_pool_get_config(ocnConfigs, 'config_zWidth_tanh', config_zWidth_tanh) + call mpas_pool_get_dimension(meshPool, 'nCellsSolve', nCellsSolve) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) call mpas_pool_get_array(meshPool, 'refBottomDepth', refBottomDepth) - ! refBottomDepth is used here for simplicity. Using zMid and h, which + nCells = nCellsSolve + + ! refBottomDepth is used here for simplicity. Using zMid and h, which ! vary in time, would give the exact location of the top, but it ! would only change the diffusion value very slightly. - do k=2,nVertLevels - vertDiffTopOfCell(k,:) = vertDiffTopOfCell(k,:) - (config_max_diff_tanh - config_min_diff_tanh) / 2.0 & - * tanh((refBottomDepth(k-1) + config_ZMid_tanh) & - / config_zWidth_tanh) & - + (config_max_diff_tanh + config_min_diff_tanh) / 2 + !$omp do schedule(runtime) private(k) + do iCell = 1, nCells + do k=2,nVertLevels + vertDiffTopOfCell(k, iCell) = vertDiffTopOfCell(k, iCell) - (config_max_diff_tanh - config_min_diff_tanh) / 2.0_RKIND & + * tanh((refBottomDepth(k-1) + config_zMid_tanh) & + / config_zWidth_tanh) & + + (config_max_diff_tanh + config_min_diff_tanh) / 2 + end do end do + !$omp end do + + call mpas_timer_stop('tracer tanh coef') !-------------------------------------------------------------------- @@ -309,9 +331,9 @@ end subroutine ocn_tracer_vmix_coefs_tanh!}}} !> \brief Initializes ocean vertical mixing quantities !> \author Mark Petersen !> \date September 2011 -!> \details -!> This routine initializes a variety of quantities related to -!> tanh vertical mixing in the ocean. +!> \details +!> This routine initializes a variety of quantities related to +!> tanh vertical mixing in the ocean. ! !----------------------------------------------------------------------- diff --git a/src/core_ocean/shared/mpas_ocn_vmix_cvmix.F b/src/core_ocean/shared/mpas_ocn_vmix_cvmix.F index 93682b5587..5ad7d37b64 100644 --- a/src/core_ocean/shared/mpas_ocn_vmix_cvmix.F +++ b/src/core_ocean/shared/mpas_ocn_vmix_cvmix.F @@ -16,6 +16,7 @@ module ocn_vmix_cvmix use mpas_derived_types use mpas_pool_routines use mpas_timer + use mpas_constants use mpas_log use ocn_constants @@ -58,7 +59,6 @@ module ocn_vmix_cvmix type(cvmix_bkgnd_params_type) :: cvmix_background_params type(cvmix_shear_params_type) :: cvmix_shear_params type(cvmix_tidal_params_type) :: cvmix_tidal_params - type(cvmix_data_type) :: cvmix_variables logical :: cvmixOn, cvmixBackgroundOn, cvmixConvectionOn, cvmixKPPOn real (kind=RKIND) :: backgroundVisc, backgroundDiff @@ -75,13 +75,13 @@ module ocn_vmix_cvmix !> \brief Computes mixing coefficients using CVMix !> \author Todd Ringler !> \date 04 February 2013 -!> \details +!> \details !> This routine computes the vertical mixing coefficients for momentum !> and tracers by calling CVMix routines. ! !----------------------------------------------------------------------- - subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, timeLevelIn)!{{{ + subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, forcingPool, diagnosticsPool, err, timeLevelIn)!{{{ !----------------------------------------------------------------- ! @@ -91,7 +91,7 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, type (mpas_pool_type), intent(in) :: & meshPool !< Input: mesh information - + integer, intent(in), optional :: timeLevelIn !< Input: time level for state pool !----------------------------------------------------------------- @@ -106,6 +106,9 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, type (mpas_pool_type), intent(inout) :: & diagnosticsPool !< Input/Output: diagnostic information + type (mpas_pool_type), intent(inout) :: & + forcingPool !< Input/Output: forcing information + !----------------------------------------------------------------- ! ! output variables @@ -120,33 +123,46 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, ! !----------------------------------------------------------------- + type(cvmix_data_type) :: cvmix_variables + integer, dimension(:), pointer :: & - maxLevelCell + maxLevelCell, nEdgesOnCell real (kind=RKIND), dimension(:), pointer :: & latCell, lonCell, bottomDepth, surfaceBuoyancyForcing, surfaceFrictionVelocity, fCell, & - boundaryLayerDepth, ssh, indexBoundaryLayerDepth - + boundaryLayerDepth, ssh, indexBoundaryLayerDepth, dcEdge, dvEdge, areaCell, iceFraction, & + boundaryLayerDepthSmooth + real (kind=RKIND), dimension(:,:), pointer :: & vertViscTopOfCell, vertDiffTopOfCell, layerThickness, & zMid, zTop, density, displacedDensity, potentialDensity, & bulkRichardsonNumber, RiTopOfCell, BruntVaisalaFreqTop, & - bulkRichardsonNumberBuoy, bulkRichardsonNumberShear, unresolvedShear + bulkRichardsonNumberBuoy, bulkRichardsonNumberShear, unresolvedShear, normalVelocity real (kind=RKIND), dimension(:,:,:), pointer :: vertNonLocalFlux - integer, pointer :: index_vertNonLocalFluxTemp + integer, pointer :: index_vertNonLocalFluxTemp, config_cvmix_num_ri_smooth_loops + integer, dimension(:,:), pointer :: edgesOnCell, cellsOnCell, cellMask logical, pointer :: config_use_cvmix_shear, config_use_cvmix_convection, config_use_cvmix_kpp - logical, pointer :: config_use_cvmix_fixed_boundary_layer + logical, pointer :: config_use_cvmix_fixed_boundary_layer, config_cvmix_use_BLD_smoothing real (kind=RKIND), pointer :: config_cvmix_kpp_stop_OBL_search, config_cvmix_kpp_criticalBulkRichardsonNumber - real (kind=RKIND), pointer :: config_cvmix_kpp_boundary_layer_depth + real (kind=RKIND), pointer :: config_cvmix_kpp_boundary_layer_depth, config_cvmix_kpp_surface_layer_extent + real (kind=RKIND), pointer :: configure_cvmix_kpp_minimum_OBL_under_sea_ice character (len=StrKIND), pointer :: config_cvmix_shear_mixing_scheme, config_cvmix_kpp_matching - integer :: k, iCell, jCell, iNeighbor, iter, timeLevel, kIndexOBL - integer, pointer :: nVertLevels, nCells - real (kind=RKIND) :: r, layerSum, bulkRichardsonNumberStop - real (kind=RKIND), dimension(:), allocatable :: sigma, Nsqr_iface, turbulentScalarVelocityScale, tmp - real (kind=RKIND), dimension(:), allocatable, target :: RiSmoothed, BVFSmoothed + integer :: k, i, iCell, jCell, iNeighbor, iter, timeLevel, kIndexOBL, kav, iEdge, nCells + integer :: edgeCount, nEdges, topIndex, nsmooth, kpp_stage + integer, pointer :: nVertLevels, nVertLevelsP1 + integer, dimension(:), pointer :: nCellsArray + integer, dimension(:), allocatable :: surfaceAverageIndex + + real (kind=RKIND) :: r, layerSum, bulkRichardsonNumberStop, sfc_layer_depth, invAreaCell + real (kind=RKIND) :: normalVelocityAv, factor, delU2, areaSum, blTemp + real (kind=RKIND) :: sigma, turbulentScalarVelocityScalePoint + real (kind=RKIND), dimension(:), allocatable :: Nsqr_iface, turbulentScalarVelocityScale, & + deltaVelocitySquared, normalVelocitySum, & + potentialDensitySum, RiTemp + real (kind=RKIND), dimension(:), allocatable, target :: RiSmoothed, BVFSmoothed, OBLDepths, interfaceForcings logical :: bulkRichardsonFlag real (kind=RKIND), pointer :: config_cvmix_background_viscosity, config_cvmix_background_diffusion @@ -154,11 +170,11 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, !----------------------------------------------------------------- ! ! call relevant routines for computing mixing-related fields - ! note that the user can choose multiple options and the + ! note that the user can choose multiple options and the ! mixing fields have to be added/merged together ! !----------------------------------------------------------------- - + ! ! assume no errors during initialization and set to 1 when error is encountered ! @@ -181,7 +197,8 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, call mpas_pool_get_config(ocnConfigs, 'config_cvmix_background_viscosity', config_cvmix_background_viscosity) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_background_diffusion', config_cvmix_background_diffusion) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_stop_OBL_search', config_cvmix_kpp_stop_OBL_search) - call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_criticalBulkRichardsonNumber', config_cvmix_kpp_criticalBulkRichardsonNumber) + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_criticalBulkRichardsonNumber', & + config_cvmix_kpp_criticalBulkRichardsonNumber) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_shear', config_use_cvmix_shear) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_convection', config_use_cvmix_convection) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_kpp', config_use_cvmix_kpp) @@ -189,9 +206,22 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_matching', config_cvmix_kpp_matching) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_fixed_boundary_layer', config_use_cvmix_fixed_boundary_layer) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_boundary_layer_depth', config_cvmix_kpp_boundary_layer_depth) - + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_surface_layer_extent', config_cvmix_kpp_surface_layer_extent) + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_num_ri_smooth_loops', config_cvmix_num_ri_smooth_loops) + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_use_BLD_smoothing', config_cvmix_use_BLD_smoothing) + call mpas_pool_get_config(ocnConfigs, 'configure_cvmix_kpp_minimum_OBL_under_sea_ice', & + configure_cvmix_kpp_minimum_OBL_under_sea_ice) call mpas_pool_get_dimension(meshPool, 'nVertLevels', nVertLevels) - call mpas_pool_get_dimension(meshPool, 'nCells', nCells) + call mpas_pool_get_dimension(meshPool, 'nVertLevelsP1', nVertLevelsP1) + call mpas_pool_get_dimension(meshPool, 'nCellsArray', nCellsArray) + call mpas_pool_get_array(meshPool, 'dcEdge', dcEdge) + call mpas_pool_get_array(meshPool, 'dvEdge', dvEdge) + call mpas_pool_get_array(meshPool, 'nEdgesOnCell', nEdgesOnCell) + call mpas_pool_get_array(meshPool, 'areaCell', areaCell) + call mpas_pool_get_array(statePool, 'normalVelocity', normalVelocity, timeLevel) + call mpas_pool_get_array(meshPool, 'edgesOnCell', edgesOnCell) + call mpas_pool_get_array(meshPool, 'cellsOnCell', cellsOnCell) + call mpas_pool_get_array(meshPool, 'cellMask', cellMask) ! ! set pointers for fields related to position on sphere @@ -221,12 +251,18 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, call mpas_pool_get_array(diagnosticsPool, 'bulkRichardsonNumber', bulkRichardsonNumber) call mpas_pool_get_array(diagnosticsPool, 'unresolvedShear', unresolvedShear) call mpas_pool_get_array(diagnosticsPool, 'boundaryLayerDepth', boundaryLayerDepth) + call mpas_pool_get_array(diagnosticsPool, 'boundaryLayerDepthSmooth', boundaryLayerDepthSmooth) call mpas_pool_get_array(diagnosticsPool, 'RiTopOfCell', RiTopOfCell) call mpas_pool_get_array(diagnosticsPool, 'BruntVaisalaFreqTop',BruntVaisalaFreqTop) call mpas_pool_get_array(diagnosticsPool, 'bulkRichardsonNumberBuoy',bulkRichardsonNumberBuoy) call mpas_pool_get_array(diagnosticsPool, 'bulkRichardsonNumberShear',bulkRichardsonNumberShear) call mpas_pool_get_array(diagnosticsPool, 'indexBoundaryLayerDepth',indexBoundaryLayerDepth) + ! + ! set pointers for fields related to ocean forcing state + ! + call mpas_pool_get_array(forcingPool, 'iceFraction', iceFraction) + ! ! set pointers for fields related forcing at ocean surface ! @@ -239,8 +275,6 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, call mpas_pool_get_array(diagnosticsPool, 'vertViscTopOfCell', vertViscTopOfCell) call mpas_pool_get_array(diagnosticsPool, 'vertDiffTopOfCell', vertDiffTopOfCell) - vertViscTopOfCell = 0.0 - vertDiffTopOfCell = 0.0 ! ! set pointers for nonlocal flux and intialize to zero @@ -248,14 +282,30 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, call mpas_pool_get_array(diagnosticsPool, 'vertNonLocalFlux', vertNonLocalFlux) call mpas_pool_get_dimension(diagnosticsPool, 'index_vertNonLocalFluxTemp', index_vertNonLocalFluxTemp) - vertNonLocalFlux = 0.0 + nCells = nCellsArray( size(nCellsArray) ) + + !$omp do schedule(runtime) + do iCell = 1, nCells + vertViscTopOfCell(:, iCell) = 0.0_RKIND + vertDiffTopOfCell(:, iCell) = 0.0_RKIND + vertNonLocalFlux(:, :, iCell) = 0.0_RKIND + end do + !$omp end do + + nCells = nCellsArray( 3 ) ! ! start by adding the mininum background values to the visocity/diffusivity arrays ! if (cvmixBackgroundOn) then - vertViscTopOfCell(:,:) = vertViscTopOfCell(:,:) + config_cvmix_background_viscosity - vertDiffTopOfCell(:,:) = vertDiffTopOfCell(:,:) + config_cvmix_background_diffusion + !$omp do schedule(runtime) + do iCell = 1, nCells + do k = 1, nVertLevelsP1 + vertViscTopOfCell(k, iCell) = vertViscTopOfCell(k, iCell) + config_cvmix_background_viscosity + vertDiffTopOfCell(k, iCell) = vertDiffTopOfCell(k, iCell) + config_cvmix_background_diffusion + end do + end do + !$omp end do endif ! @@ -271,32 +321,53 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, allocate(cvmix_variables % dzt(nVertLevels)) allocate(cvmix_variables % kpp_Tnonlocal_iface(nVertLevels+1)) allocate(cvmix_variables % kpp_Snonlocal_iface(nVertLevels+1)) - allocate(cvmix_variables % BulkRichardson_cntr(nVertLevels)) - allocate(sigma(nVertLevels)) + ! Initialize some of the cvmix variables that are not set later. + cvmix_variables % Mdiff_iface(1:nVertLevels+1) = 0.0_RKIND + cvmix_variables % Tdiff_iface(1:nVertLevels+1) = 0.0_RKIND + cvmix_variables % Sdiff_iface(1:nVertLevels+1) = 0.0_RKIND + + allocate(OBLDepths(nVertLevels)) + allocate(interfaceForcings(nVertLevels)) + allocate(Nsqr_iface(nVertLevels+1)) allocate(turbulentScalarVelocityScale(nVertLevels)) - allocate(tmp(nVertLevels+1)) allocate(RiSmoothed(nVertLevels+1)) allocate(BVFSmoothed(nVertLevels+1)) - + allocate(RiTemp(nVertLevels+1)) + + allocate(normalVelocitySum(nVertLevels)) + allocate(potentialDensitySum(nVertLevels)) + allocate(surfaceAverageIndex(nVertLevels)) + allocate(deltaVelocitySquared(nVertLevels)) + + do k = 1, nVertLevels + Nsqr_iface(k) = 0.0_RKIND + turbulentScalarVelocityScale(k) = 0.0_RKIND + end do + Nsqr_iface(nVertLevelsP1) = 0.0_RKIND + + call mpas_timer_start('cvmix cell loop', .false.) + do kpp_stage = 1,2 + !$omp do schedule(runtime) private(k, bulkRichardsonNumberStop, kIndexOBL, bulkRichardsonFlag) do iCell = 1, nCells + invAreaCell = 1.0_RKIND / areaCell(iCell) ! specify geometry/location cvmix_variables % SeaSurfaceHeight = ssh(iCell) cvmix_variables % Coriolis = fCell(iCell) - cvmix_variables % lat = latCell(iCell) * 180.0 / 3.14 - cvmix_variables % lon = lonCell(iCell) * 180.0 / 3.14 + cvmix_variables % lat = latCell(iCell) * 180.0_RKIND / 3.14_RKIND + cvmix_variables % lon = lonCell(iCell) * 180.0_RKIND / 3.14_RKIND ! fill vertical position of column ! CVMix assume top of ocean is at z=0, so building all z-coordinate data based on layerThickness - cvmix_variables % zw_iface(1) = 0.0 - cvmix_variables % dzw(1) = layerThickness(1,iCell)/2.0 - cvmix_variables % zt_cntr(1) = -layerThickness(1,iCell)/2.0 + cvmix_variables % zw_iface(1) = 0.0_RKIND + cvmix_variables % dzw(1) = layerThickness(1,iCell)/2.0_RKIND + cvmix_variables % zt_cntr(1) = -layerThickness(1,iCell)/2.0_RKIND do k=2,maxLevelCell(iCell) cvmix_variables % zw_iface(k) = cvmix_variables % zw_iface(k-1) - layerThickness(k-1,iCell) - cvmix_variables % zt_cntr(k) = cvmix_variables % zw_iface(k) - layerThickness(k,iCell)/2.0 - cvmix_variables % dzw(k) = cvmix_variables % zt_cntr(k-1) - cvmix_variables % zt_cntr(k) + cvmix_variables % zt_cntr(k) = cvmix_variables % zw_iface(k) - layerThickness(k,iCell)/2.0_RKIND + cvmix_variables % dzw(k) = cvmix_variables % zt_cntr(k-1) - cvmix_variables % zt_cntr(k) cvmix_variables % dzt(k) = layerThickness(k,iCell) enddo k = maxLevelCell(iCell)+1 @@ -305,43 +376,82 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, do k = maxLevelCell(iCell) + 1, nVertLevels cvmix_variables % zw_iface(k+1) = cvmix_variables % zw_iface(maxLevelCell(iCell)+1) cvmix_variables % zt_cntr(k) = cvmix_variables % zw_iface(maxLevelCell(iCell)+1) - cvmix_variables % dzw(k+1) = 0.0 - cvmix_variables % dzt(k) = 0.0 + cvmix_variables % dzw(k+1) = 0.0_RKIND + cvmix_variables % dzt(k) = 0.0_RKIND enddo ! fill the intent(in) convective adjustment cvmix_variables % nlev = maxLevelCell(iCell) cvmix_variables % OceanDepth = bottomDepth(iCell) - cvmix_variables % WaterDensity_cntr => density(:,iCell) - cvmix_variables % AdiabWaterDensity_cntr => displacedDensity(:,iCell) + cvmix_variables % WaterDensity_cntr => density(1:maxLevelCell(iCell), iCell) + cvmix_variables % AdiabWaterDensity_cntr => displacedDensity(1:maxLevelCell(iCell), iCell) ! fill Ri - RiSmoothed(1:nVertLevels) = RiTopOfCell(1:nVertLevels,iCell) - RiSmoothed(nVertLevels+1) = RiSmoothed(nVertLevels) - do k=2,maxLevelCell(iCell) - ! For input to cvmix, Richardson number should be positive. - RiSmoothed(k)=max(0.0_RKIND, RiSmoothed(k)) + if (kpp_stage == 2) then + RiSmoothed(1:maxLevelCell(iCell)) = RiTopOfCell(1:maxLevelCell(iCell),iCell) + RiSmoothed(maxLevelCell(iCell)+1) = RiSmoothed(maxLevelCell(iCell)) + RiTemp(1:maxLevelCell(iCell)+1) = RiSmoothed(1:maxLevelCell(iCell)+1) + + ! Use a 1-2-1 filter to remove 2 dz noise in RiTopOfCell + do nsmooth=1,config_cvmix_num_ri_smooth_loops + do k=2,maxLevelCell(iCell) + RiSmoothed(k) = (RiTemp(k-1) + 2.0_RKIND*RiTemp(k) + RiTemp(k+1)) / 4.0_RKIND + enddo + RiTemp(1:maxLevelCell(iCell)) = RiSmoothed(1:maxLevelCell(iCell)) enddo - cvmix_variables%ShearRichardson_iface => RiSmoothed + + cvmix_variables%ShearRichardson_iface => RiSmoothed(1:maxLevelCell(iCell)+1) + + endif ! fill BVF - BVFSmoothed(1:nVertLevels) = BruntVaisalaFreqTop(1:nVertLevels,iCell) - BVFSmoothed(nVertLevels+1) = BVFSmoothed(nVertLevels) - cvmix_variables%SqrBuoyancyFreq_iface => BVFSmoothed + BVFSmoothed(1:maxLevelCell(iCell)) = max(0.0_RKIND,BruntVaisalaFreqTop(1:maxLevelCell(iCell),iCell)) + BVFSmoothed(maxLevelCell(iCell)+1) = max(0.0_RKIND,BVFSmoothed(maxLevelCell(iCell))) + cvmix_variables%SqrBuoyancyFreq_iface => BVFSmoothed(1:maxLevelCell(iCell)+1) ! fill the intent(in) KPP cvmix_variables % SurfaceFriction = surfaceFrictionVelocity(iCell) cvmix_variables % SurfaceBuoyancyForcing = surfaceBuoyancyForcing(iCell) + cvmix_variables % BulkRichardson_cntr => bulkRichardsonNumber(1:maxLevelCell(iCell), iCell) + + if (kpp_stage == 2) then + if (config_use_cvmix_shear) then + + do k = 1, maxLevelCell(iCell) + 1 + cvmix_variables % Mdiff_iface(k) = 0.0_RKIND + cvmix_variables % Tdiff_iface(k) = 0.0_RKIND + end do + call cvmix_coeffs_shear( & + cvmix_variables, & + cvmix_shear_params) + ! add shear mixing to vertical viscosity/diffusivity + ! at present, shear mixing adds in background values when using PP, but background is + ! accounted for seperately. so remove bac kground from shear mixing values + if(config_cvmix_shear_mixing_scheme=='PP') then + do k = 1, maxLevelCell(iCell) + 1 + cvmix_variables % Mdiff_iface(k) = cvmix_variables % Mdiff_iface(k) - config_cvmix_background_viscosity + cvmix_variables % Tdiff_iface(k) = cvmix_variables % Tdiff_iface(k) - config_cvmix_background_diffusion + end do + endif + + do k = 1, maxLevelCell(iCell) + vertViscTopOfCell(k, iCell) = vertViscTopOfCell(k, iCell) + cvmix_variables % Mdiff_iface(k) + vertDiffTopOfCell(k, iCell) = vertDiffTopOfCell(k, iCell) + cvmix_variables % Tdiff_iface(k) + end do + + endif ! if (config_use_cvmix_shear) + endif ! stage 2 shear compute ! call kpp ocean mixed layer scheme if (cvmixKPPOn) then - ! copy data into cvmix_variables - cvmix_variables % Mdiff_iface(:)=vertViscTopOfCell(:,iCell) - cvmix_variables % Tdiff_iface(:)=vertDiffTopOfCell(:,iCell) - + if (kpp_stage==1) then if (config_use_cvmix_fixed_boundary_layer) then cvmix_variables % BoundaryLayerDepth = config_cvmix_kpp_boundary_layer_depth + cvmix_variables % kOBL_depth = cvmix_kpp_compute_kOBL_depth( & + zw_iface = cvmix_variables%zw_iface(1:maxLevelCell(iCell)+1), & + zt_cntr = cvmix_variables%zt_cntr(1:maxLevelCell(iCell)), & + OBL_depth = cvmix_variables % BoundaryLayerDepth ) else @@ -350,118 +460,197 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, Nsqr_iface(k) = BVFSmoothed(k) enddo k=min(maxLevelCell(iCell)+1,nVertLevels) - Nsqr_iface(k:nVertLevels+1) = Nsqr_iface(k-1) + Nsqr_iface(k:maxLevelCell(iCell)+1) = Nsqr_iface(k-1) ! compute bulk Richardson number ! assume boundary layer depth is at bottom of every kIndexOBL cell bulkRichardsonNumberStop = config_cvmix_kpp_stop_OBL_search * config_cvmix_kpp_criticalBulkRichardsonNumber - bulkRichardsonNumber(:,iCell) = bulkRichardsonNumberStop - 1.0 - kIndexOBL=1 bulkRichardsonFlag = .false. - do while (.not.bulkRichardsonFlag) - - ! set OBL at bottome of kIndexOBL cell for computation of bulk Richardson number - cvmix_variables % BoundaryLayerDepth = cvmix_variables % zw_iface(kIndexOBL+1) - - ! define sigma based on assumption of where OBL bottom resides - do k=1,maxLevelCell(iCell) - sigma(k) = -cvmix_variables % zt_cntr(k) / cvmix_variables % BoundaryLayerDepth - enddo - do k=maxLevelCell(iCell)+1,nVertLevels - sigma(k) = sigma(maxLevelCell(iCell)) + topIndex = 1 +! call mpas_timer_start('Bulk Richardson kIndexOBL loops') + ! compute the index of the last cell in the KPP defined surface layer + ! this index is used for the necessary surface layer averages of buoyancy and momentum + do kIndexOBL = 1, maxLevelCell(iCell) + + ! Reset deltaVelocitySquared and bulkRichardsonNumber at this layer for the later computation + deltaVelocitySquared(kIndexOBL) = 0.0_RKIND + bulkRichardsonNumber(kIndexOBL, iCell) = bulkRichardsonNumberStop - 1.0_RKIND + + ! set OBL at bottom of kIndexOBL cell for computation of bulk Richardson number + cvmix_variables % BoundaryLayerDepth = abs(cvmix_variables % zw_iface(kIndexOBL+1)) + sigma = -cvmix_variables % zt_cntr(kIndexOBL) / cvmix_variables % BoundaryLayerDepth + + OBLDepths(kIndexOBL) = abs(cvmix_variables % zw_iface(kIndexOBL+1)) + interfaceForcings(kIndexOBL) = cvmix_variables % SurfaceBuoyancyForcing + + ! initialize the surfaceAverageIndex for cases when the if statement below is not true + surfaceAverageIndex(kIndexOBL) = 1 + ! move progressively downward to find the bottom most layer within the surface layer + sfc_layer_depth = cvmix_variables % BoundaryLayerDepth * config_cvmix_kpp_surface_layer_extent + do kav=topIndex,kIndexOBL + if(cvmix_variables%zw_iface(kav+1) < -sfc_layer_depth) then + surfaceAverageIndex(kIndexOBL) = kav + exit + end if enddo - - ! compute the turbulent scales in order to compute the bulk Richardson number - call cvmix_kpp_compute_turbulent_scales( & - sigma_coord = sigma(1:nVertLevels), & - OBL_depth = cvmix_variables % BoundaryLayerDepth, & - surf_buoy_force = cvmix_variables % SurfaceBuoyancyForcing, & - surf_fric_vel = cvmix_variables % SurfaceFriction, & - w_s = turbulentScalarVelocityScale(1:nVertLevels)) - - cvmix_variables % BulkRichardson_cntr = cvmix_kpp_compute_bulk_Richardson( & - zt_cntr = cvmix_variables % zt_cntr(1:nVertLevels), & - delta_buoy_cntr = bulkRichardsonNumberBuoy(1:nVertLevels,iCell), & - delta_Vsqr_cntr = bulkRichardsonNumberShear(1:nVertLevels,iCell), & - ws_cntr = turbulentScalarVelocityScale(:), & - Nsqr_iface = Nsqr_iface(1:nVertLevels+1) ) - - unresolvedShear(:,iCell) = cvmix_kpp_compute_unresolved_shear( & - zt_cntr = cvmix_variables % zt_cntr(1:nVertLevels), & - ws_cntr = turbulentScalarVelocityScale(1:nVertLevels), & - Nsqr_iface = Nsqr_iface(1:nVertLevels+1)) - - ! each level of bulk Richardson is computed as if OBL resided at bottom of that level - bulkRichardsonNumber(kIndexOBL,iCell) = cvmix_variables % BulkRichardson_cntr(kIndexOBL) - - ! test to see if search should be ended - if(kIndexOBL.eq.maxLevelCell(iCell)) bulkRichardsonFlag=.true. - if(bulkRichardsonNumber(kIndexOBL,iCell).gt.bulkRichardsonNumberStop) bulkRichardsonFlag=.true. - - ! move downward one level - kIndexOBL = kIndexOBL + 1 - - enddo ! do while (.not.bulkRichardsonFlag) - - call cvmix_kpp_compute_OBL_depth( & - Ri_bulk = bulkRichardsonNumber(1:nVertLevels,iCell), & - zw_iface = cvmix_variables % zw_iface(1:nVertLevels+1), & - OBL_depth = cvmix_variables % BoundaryLayerDepth, & - kOBL_depth = cvmix_variables % kOBL_depth, & - zt_cntr = cvmix_variables % zt_cntr(1:nVertLevels), & - surf_fric = cvmix_variables % SurfaceFriction, & - surf_buoy = cvmix_variables % SurfaceBuoyancyForcing, & - Coriolis = cvmix_variables % Coriolis) + topIndex = max(1, surfaceAverageIndex(kIndexOBL) - 1) + end do + + ! compute the turbulent scales in order to compute the bulk Richardson number + call cvmix_kpp_compute_turbulent_scales( & + sigma_coord = config_cvmix_kpp_surface_layer_extent, & + OBL_depth = OBLDepths(1:maxLevelCell(iCell)), & + surf_buoy_force = interfaceForcings(1:maxLevelCell(iCell)), & + surf_fric_vel = cvmix_variables % SurfaceFriction, & + w_s = turbulentScalarVelocityScale(1:maxLevelCell(iCell))) + + ! averaging over a surface layer assuming that BLdepth is cell bottom + ! Build deltaVelocitySquared + do i = 1, nEdgesOnCell(iCell) + iEdge = edgesOnCell(i, iCell) + factor = 0.5_RKIND * dcEdge(iEdge) * dvEdge(iEdge) * invAreaCell + + deltaVelocitySquared(1) = 0.0_RKIND + + normalVelocitySum(1) = normalVelocity(1, iEdge) + + do kIndexOBL = 2, maxLevelCell(iCell) + normalVelocitySum(kIndexOBL) = normalVelocitySum(kIndexOBL-1) + normalVelocity(kIndexOBL, iEdge) + end do + + do kIndexOBL = 1, maxLevelCell(iCell) + normalVelocityAv = normalVelocitySum(surfaceAverageIndex(kIndexOBL )) / & + real( surfaceAverageIndex(kIndexOBL), kind=RKIND) + delU2 = ( normalVelocityAv - normalVelocity(kIndexOBL, iEdge) )**2 + deltaVelocitySquared(kIndexOBL) = deltaVelocitySquared(kIndexOBL) + factor * delU2 + end do + end do + + potentialDensitySum(1) = potentialDensity(1, iCell) + do kIndexOBL = 2, maxLevelCell(iCell) + potentialDensitySum(kIndexOBL) = potentialDensitySum(kIndexOBL-1) + potentialDensity(kIndexOBL, iCell) + end do + + do kIndexOBL = 1, maxLevelCell(iCell) + ! !compute shear contribution assuming BLdepth is cell bottom + bulkRichardsonNumberShear(kIndexOBL,iCell) = max(deltaVelocitySquared(kIndexOBL), 1.0e-15_RKIND) + + bulkRichardsonNumberBuoy(kIndexOBL,iCell) = gravity * (potentialDensity(kIndexOBL, iCell) & + - potentialDensitySum(surfaceAverageIndex(kIndexOBL)) & + / real(surfaceAverageIndex(kIndexOBL), kind=RKIND)) / rho_sw + end do ! do kIndexOBL +! call mpas_timer_stop('Bulk Richardson kIndexOBL loops') + + cvmix_variables % bulkRichardson_cntr(:) = cvmix_kpp_compute_bulk_Richardson( & + zt_cntr = cvmix_variables % zt_cntr(1:maxLevelCell(iCell)), & + delta_buoy_cntr = bulkRichardsonNumberBuoy(1:maxLevelCell(iCell),iCell), & + delta_Vsqr_cntr = bulkRichardsonNumberShear(1:maxLevelCell(iCell),iCell), & + ws_cntr = turbulentScalarVelocityScale(:), & + Nsqr_iface = Nsqr_iface(1:maxLevelCell(iCell)+1) ) + + ! each level of bulk Richardson is computed as if OBL resided at bottom of that level + + call cvmix_kpp_compute_OBL_depth( & + Ri_bulk = bulkRichardsonNumber(1:maxLevelCell(iCell),iCell), & + zw_iface = cvmix_variables % zw_iface(1:maxLevelCell(iCell)+1), & + OBL_depth = cvmix_variables % BoundaryLayerDepth, & + kOBL_depth = cvmix_variables % kOBL_depth, & + zt_cntr = cvmix_variables % zt_cntr(1:maxLevelCell(iCell)), & + surf_fric = cvmix_variables % SurfaceFriction, & + surf_buoy = cvmix_variables % SurfaceBuoyancyForcing, & + Coriolis = cvmix_variables % Coriolis) endif ! if (config_use_cvmix_fixed_boundary_layer) then ! apply minimum limit to OBL - if(cvmix_variables % BoundaryLayerDepth .lt. layerThickness(1,iCell)/2.0) then - cvmix_variables % BoundaryLayerDepth = layerThickness(1,iCell)/2.0 + if(cvmix_variables % BoundaryLayerDepth .lt. layerThickness(1,iCell)/2.0_RKIND) then + cvmix_variables % BoundaryLayerDepth = layerThickness(1,iCell)/2.0_RKIND + cvmix_variables % kOBL_depth = cvmix_kpp_compute_kOBL_depth( & + zw_iface = cvmix_variables%zw_iface(1:maxLevelCell(iCell)+1),& + zt_cntr = cvmix_variables%zt_cntr(1:maxLevelCell(iCell)), & + OBL_depth = cvmix_variables % BoundaryLayerDepth ) + endif + + ! apply minimum limit to OBL under sea-ice + if(iceFraction(iCell).gt.0.15_RKIND) then + if(cvmix_variables % BoundaryLayerDepth .lt. configure_cvmix_kpp_minimum_OBL_under_sea_ice) then + cvmix_variables % BoundaryLayerDepth = configure_cvmix_kpp_minimum_OBL_under_sea_ice + cvmix_variables % kOBL_depth = cvmix_kpp_compute_kOBL_depth( & + zw_iface = cvmix_variables%zw_iface(1:maxLevelCell(iCell)+1),& + zt_cntr = cvmix_variables%zt_cntr(1:maxLevelCell(iCell)), & + OBL_depth = cvmix_variables % BoundaryLayerDepth ) + endif endif ! apply maximum limit to OBL if(cvmix_variables % BoundaryLayerDepth .gt. abs(cvmix_variables%zt_cntr(maxLevelCell(iCell)))) then cvmix_variables % BoundaryLayerDepth = abs(cvmix_variables%zt_cntr(maxLevelCell(iCell))) + cvmix_variables % kOBL_depth = cvmix_kpp_compute_kOBL_depth( & + zw_iface = cvmix_variables%zw_iface(1:maxLevelCell(iCell)+1), & + zt_cntr = cvmix_variables%zt_cntr(1:maxLevelCell(iCell)), & + OBL_depth = cvmix_variables % BoundaryLayerDepth ) + endif + boundaryLayerDepth(iCell) = cvmix_variables % BoundaryLayerDepth + endif !kpp stage 1 -- boundary layer compute + + if (kpp_stage == 2) then + ! copy data into cvmix_variables + do k = 1, maxLevelCell(iCell) + 1 + cvmix_variables % Mdiff_iface(k) = vertViscTopOfCell(k, iCell) + cvmix_variables % Tdiff_iface(k) = vertDiffTopOfCell(k, iCell) + end do + + !must reapply max and min limits to boundaryLayerDepth + blTemp = max(boundaryLayerDepth(iCell), layerThickness(1,iCell)/2.0_RKIND) + boundaryLayerDepth(iCell) = min(blTemp, abs(cvmix_variables%zt_cntr(maxLevelCell(iCell)))) + cvmix_variables % kOBL_depth = cvmix_kpp_compute_kOBL_depth( & - zw_iface = cvmix_variables%zw_iface(1:nVertLevels+1), & - zt_cntr = cvmix_variables%zt_cntr(1:nVertLevels), & - OBL_depth = cvmix_variables % BoundaryLayerDepth ) + zw_iface = cvmix_variables%zw_iface(1:maxLevelCell(iCell)+1), & + zt_cntr = cvmix_variables%zt_cntr(1:maxLevelCell(iCell)), & + OBL_depth = boundaryLayerDepth(iCell) ) + + ! call mpas_timer_start('cvmix coeffs kpp', .false.) call cvmix_coeffs_kpp( & - Mdiff_out = cvmix_variables % Mdiff_iface(1:nVertLevels+1), & - Tdiff_out = cvmix_variables % Tdiff_iface(1:nVertLevels+1), & - Sdiff_out = cvmix_variables % Sdiff_iface(1:nVertLevels+1), & - zw = cvmix_variables%zw_iface(1:nVertLevels+1), & - zt = cvmix_variables%zt_cntr(1:nVertLevels), & - old_Mdiff = cvmix_variables%Mdiff_iface(1:nVertLevels+1), & - old_Tdiff = cvmix_variables%Tdiff_iface(1:nVertLevels+1), & - old_Sdiff = cvmix_variables%Sdiff_iface(1:nVertLevels+1), & - OBL_depth = cvmix_variables%BoundaryLayerDepth, & + Mdiff_out = cvmix_variables % Mdiff_iface(1:maxLevelCell(iCell)+1), & + Tdiff_out = cvmix_variables % Tdiff_iface(1:maxLevelCell(iCell)+1), & + Sdiff_out = cvmix_variables % Sdiff_iface(1:maxLevelCell(iCell)+1), & + zw = cvmix_variables%zw_iface(1:maxLevelCell(iCell)+1), & + zt = cvmix_variables%zt_cntr(1:maxLevelCell(iCell)), & + old_Mdiff = cvmix_variables%Mdiff_iface(1:maxLevelCell(iCell)+1), & + old_Tdiff = cvmix_variables%Tdiff_iface(1:maxLevelCell(iCell)+1), & + old_Sdiff = cvmix_variables%Sdiff_iface(1:maxLevelCell(iCell)+1), & + OBL_depth = boundaryLayerDepth(iCell), & kOBL_depth = cvmix_variables%kOBL_depth, & - Tnonlocal = cvmix_variables%kpp_Tnonlocal_iface(1:nVertLevels+1), & - Snonlocal = cvmix_variables%kpp_Snonlocal_iface(1:nVertLevels+1), & + Tnonlocal = cvmix_variables%kpp_Tnonlocal_iface(1:maxLevelCell(iCell)+1), & + Snonlocal = cvmix_variables%kpp_Snonlocal_iface(1:maxLevelCell(iCell)+1), & surf_fric = cvmix_variables%SurfaceFriction, & surf_buoy = cvmix_variables%SurfaceBuoyancyForcing, & nlev = maxLevelCell(iCell), & max_nlev = nVertLevels) +! call mpas_timer_stop('cvmix coeffs kpp') ! intent out of BoundaryLayerDepth is boundary layer depth measured in meters and vertical index - boundaryLayerDepth(iCell) = cvmix_variables % BoundaryLayerDepth indexBoundaryLayerDepth(iCell) = cvmix_variables % kOBL_depth - ! if using KPP with "MatchBoth" matching, then the output from KPP is the full viscosity/diffusivity - ! if using KPP with "SimpleShape" matching, then the output from KPP needs to be added to current viscosity/diffusivity - if(config_cvmix_kpp_matching.eq."MatchBoth") then - vertViscTopOfCell(:,iCell) = cvmix_variables % Mdiff_iface(:) - vertDiffTopOfCell(:,iCell) = cvmix_variables % Tdiff_iface(:) - elseif(config_cvmix_kpp_matching.eq."SimpleShapes") then - vertViscTopOfCell(:,iCell) = vertViscTopOfCell(:,iCell) + cvmix_variables % Mdiff_iface(:) - vertDiffTopOfCell(:,iCell) = vertDiffTopOfCell(:,iCell) + cvmix_variables % Tdiff_iface(:) + + + if(config_cvmix_kpp_matching .eq. 'SimpleShapes') then + do k = 1, int(indexBoundaryLayerDepth(iCell)) + vertViscTopOfCell(k,iCell) = vertViscTopOfCell(k,iCell) + cvmix_variables % Mdiff_iface(k) + vertDiffTopOfCell(k,iCell) = vertDiffTopOfCell(k,iCell) + cvmix_variables % Tdiff_iface(k) + end do + do k = int(indexBoundaryLayerDepth(iCell))+1, maxLevelCell(iCell)+1 + vertViscTopOfCell(k,iCell) = cvmix_variables % Mdiff_iface(k) + vertDiffTopOfCell(k,iCell) = cvmix_variables % Tdiff_iface(k) + enddo else - stop + do k = 1, maxLevelCell(iCell) + 1 + vertViscTopOfCell(k, iCell) = cvmix_variables % Mdiff_iface(k) + vertDiffTopOfCell(k, iCell) = cvmix_variables % Tdiff_iface(k) + end do endif ! store non-local flux terms @@ -470,12 +659,16 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, ! both of these operations are done in ocn_tracer_nonlocalflux_tend routine vertNonLocalFlux(index_vertNonLocalFluxTemp,:,iCell) = cvmix_variables % kpp_Tnonlocal_iface(:) + endif ! kpp stage 2 endif !if (config_use_cvmix_kpp) + if ( kpp_stage == 2) then ! call convective mixing scheme if (config_use_cvmix_convection) then - cvmix_variables % Mdiff_iface(:)=0.0 - cvmix_variables % Tdiff_iface(:)=0.0 + do k = 1, maxLevelCell(iCell) + 1 + cvmix_variables % Mdiff_iface(k) = 0.0_RKIND + cvmix_variables % Tdiff_iface(k) = 0.0_RKIND + end do call cvmix_coeffs_conv( CVmix_vars = cvmix_variables ) ! add convective mixing to vertical viscosity/diffusivity @@ -486,38 +679,13 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, vertDiffTopOfCell(k,iCell) = vertDiffTopOfCell(k,iCell) + cvmix_variables % Tdiff_iface(k) enddo else - vertViscTopOfCell(:,iCell) = vertViscTopOfCell(:,iCell) + cvmix_variables % Mdiff_iface(:) - vertDiffTopOfCell(:,iCell) = vertDiffTopOfCell(:,iCell) + cvmix_variables % Tdiff_iface(:) + do k = 1, maxLevelCell(iCell) + 1 + vertViscTopOfCell(k, iCell) = vertViscTopOfCell(k, iCell) + cvmix_variables % Mdiff_iface(k) + vertDiffTopOfCell(k, iCell) = vertDiffTopOfCell(k, iCell) + cvmix_variables % Tdiff_iface(k) + end do endif endif ! if (config_use_cvmix_convection) - ! call shear-based mixing scheme - if (config_use_cvmix_shear) then - cvmix_variables % Mdiff_iface(:)=0.0 - cvmix_variables % Tdiff_iface(:)=0.0 - call cvmix_coeffs_shear( & - cvmix_variables, & - cvmix_shear_params) - - ! add shear mixing to vertical viscosity/diffusivity - ! at present, shear mixing adds in background values when using PP, but background is accounted for seperately. so remove background from shear mixing values - if(config_cvmix_shear_mixing_scheme=='PP') then - cvmix_variables % Mdiff_iface(:) = cvmix_variables % Mdiff_iface(:) - config_cvmix_background_viscosity - cvmix_variables % Tdiff_iface(:) = cvmix_variables % Tdiff_iface(:) - config_cvmix_background_diffusion - endif - - if(config_use_cvmix_kpp) then - do k = int(indexBoundaryLayerDepth(iCell)) + 1, maxLevelCell(iCell) - vertViscTopOfCell(k,iCell) = vertViscTopOfCell(k,iCell) + cvmix_variables % Mdiff_iface(k) - vertDiffTopOfCell(k,iCell) = vertDiffTopOfCell(k,iCell) + cvmix_variables % Tdiff_iface(k) - enddo - else - vertViscTopOfCell(:,iCell) = vertViscTopOfCell(:,iCell) + cvmix_variables % Mdiff_iface(:) - vertDiffTopOfCell(:,iCell) = vertDiffTopOfCell(:,iCell) + cvmix_variables % Tdiff_iface(:) - endif - - endif ! if (config_use_cvmix_shear) - ! ! put tidal mixing here ! @@ -529,29 +697,73 @@ subroutine ocn_vmix_coefs_cvmix_build(meshPool, statePool, diagnosticsPool, err, ! computation of viscosity/diffusivity complete ! impose no-flux boundary conditions at top and bottom by zero viscosity/diffusivity - vertViscTopOfCell(1,iCell) = 0.0 - vertDiffTopOfCell(1,iCell) = 0.0 - vertViscTopOfCell(maxLevelCell(iCell)+1:nVertLevels,iCell)=0.0 - vertDiffTopOfCell(maxLevelCell(iCell)+1:nVertLevels,iCell)=0.0 - + vertViscTopOfCell(1, iCell) = 0.0_RKIND + vertDiffTopOfCell(1, iCell) = 0.0_RKIND + do k = maxLevelCell(iCell)+1, nVertLevelsP1 + vertViscTopOfCell(k, iCell)=0.0_RKIND + vertDiffTopOfCell(k, iCell)=0.0_RKIND + end do + + endif ! kpp stage 2 convection calc end do ! do iCell=1,mesh%nCells + !$omp end do + if (kpp_stage == 1 .and. config_cvmix_use_BLD_smoothing) then ! smooth boundary layer + nCells = nCellsArray(2) + + !$omp do schedule(runtime) private(nEdges, areaSum, edgeCount, iEdge, iNeighbor) + do iCell=1,nCells + nEdges = nEdgesOnCell(iCell) + boundaryLayerDepthSmooth(iCell) = 0.0_RKIND + areaSum = 0.0_RKIND + edgeCount = 0 + + do iEdge = 1,nEdges + iNeighbor = cellsOnCell(iEdge,iCell) + boundaryLayerDepthSmooth(iCell) = boundaryLayerDepthSmooth(iCell) + & + 2.0_RKIND * cellMask(1,iNeighbor) * areaCell(iNeighbor) & + * boundaryLayerDepth(iNeighbor) + areaSum = areaSum + 2.0_RKIND * areaCell(iNeighbor) * cellMask(1,iNeighbor) + edgeCount = edgeCount + cellMask(1,iNeighbor) + end do + areaSum = areaSum + edgeCount * areaCell(iCell) + boundaryLayerDepthSmooth(iCell) = boundaryLayerDepthSmooth(iCell) + & + boundaryLayerDepth(iCell) * edgeCount * areaCell(iCell) + boundaryLayerDepthSmooth(iCell) = boundaryLayerDepthSmooth(iCell) / areaSum + end do + !$omp end do + + !$omp do schedule(runtime) + do iCell=1, nCells + boundaryLayerDepth(iCell) = boundaryLayerDepthSmooth(iCell) + enddo + !$omp end do + + endif !stage 1 BLD smoothing + end do ! kpp_stage + call mpas_timer_stop('cvmix cell loop') ! dellocate cmvix variables deallocate(cvmix_variables % Mdiff_iface) deallocate(cvmix_variables % Tdiff_iface) + deallocate(cvmix_variables % Sdiff_iface) deallocate(cvmix_variables % zw_iface) deallocate(cvmix_variables % dzw) deallocate(cvmix_variables % zt_cntr) deallocate(cvmix_variables % dzt) deallocate(cvmix_variables % kpp_Tnonlocal_iface) - deallocate(cvmix_variables % BulkRichardson_cntr) + deallocate(cvmix_variables % kpp_Snonlocal_iface) - deallocate(sigma) deallocate(Nsqr_iface) deallocate(turbulentScalarVelocityScale) - deallocate(tmp) deallocate(RiSmoothed) deallocate(BVFSmoothed) + deallocate(normalVelocitySum) + deallocate(potentialDensitySum) + deallocate(surfaceAverageIndex) + deallocate(deltaVelocitySquared) + + deallocate(OBLDepths) + deallocate(interfaceForcings) !-------------------------------------------------------------------- @@ -565,8 +777,8 @@ end subroutine ocn_vmix_coefs_cvmix_build!}}} !> \ get and puts into CVMix !> \author Todd Ringler !> \date 04 February 2013 -!> \details -!> This routine initializes a variety of quantities related to +!> \details +!> This routine initializes a variety of quantities related to !> vertical mixing in the ocean. Parameters are set by calling into CVMix ! !----------------------------------------------------------------------- @@ -607,8 +819,9 @@ subroutine ocn_vmix_cvmix_init(domain,err)!{{{ ! Convection configs logical, pointer :: config_use_cvmix_convection - real (kind=RKIND), pointer :: config_cvmix_convective_diffusion, config_cvmix_convective_viscosity, config_cvmix_convective_triggerBVF - logical, pointer :: config_cvmix_convective_basedOnBVF + real (kind=RKIND), pointer :: config_cvmix_convective_diffusion, config_cvmix_convective_viscosity, & + config_cvmix_convective_triggerBVF + logical, pointer :: config_cvmix_convective_basedOnBVF, config_cvmix_kpp_use_enhanced_diff ! Tidal mixing logical, pointer :: config_use_cvmix_tidal_mixing @@ -646,7 +859,8 @@ subroutine ocn_vmix_cvmix_init(domain,err)!{{{ call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_tidal_mixing', config_use_cvmix_tidal_mixing) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_double_diffusion', config_use_cvmix_double_diffusion) call mpas_pool_get_config(ocnConfigs, 'config_use_cvmix_kpp', config_use_cvmix_kpp) - call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_criticalBulkRichardsonNumber', config_cvmix_kpp_criticalBulkRichardsonNumber) + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_criticalBulkRichardsonNumber', & + config_cvmix_kpp_criticalBulkRichardsonNumber) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_stop_OBL_search', config_cvmix_kpp_stop_OBL_search) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_interpolationOMLType', config_cvmix_kpp_interpolationOMLType) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_interpolationOMLType', config_cvmix_kpp_interpolationOMLType) @@ -657,7 +871,7 @@ subroutine ocn_vmix_cvmix_init(domain,err)!{{{ call mpas_pool_get_config(ocnConfigs, 'config_cvmix_prandtl_number', config_cvmix_prandtl_number) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_convective_diffusion', config_cvmix_convective_diffusion) call mpas_pool_get_config(ocnConfigs, 'config_cvmix_convective_viscosity', config_cvmix_convective_viscosity) - + call mpas_pool_get_config(ocnConfigs, 'config_cvmix_kpp_use_enhanced_diff', config_cvmix_kpp_use_enhanced_diff) cvmixOn = config_use_cvmix cvmixBackgroundOn = config_use_cvmix_background backgroundVisc = config_cvmix_background_viscosity @@ -702,8 +916,8 @@ subroutine ocn_vmix_cvmix_init(domain,err)!{{{ ! if (config_use_cvmix_shear) then if (.not. config_use_cvmix_background .and. trim(config_cvmix_shear_mixing_scheme) == 'PP') then - call mpas_log_write("ERROR: config_use_cvmix_shear cannot be used with with config_cvmix_shear_mixing_scheme = 'PP'") - call mpas_log_write(" without config_use_cvmix_background = .true.") + call mpas_log_write("config_use_cvmix_shear cannot be used with with config_cvmix_shear_mixing_scheme = 'PP'" & + // " without config_use_cvmix_background = .true.", MPAS_LOG_CRIT) err = 1 return end if @@ -725,7 +939,7 @@ subroutine ocn_vmix_cvmix_init(domain,err)!{{{ ! config_cvmix_convective_basedOnBVF is not supported at this time if (.not.config_cvmix_convective_basedOnBVF) then - call mpas_log_write("ERROR: config_cvmix_convective_basedOnBVF = .false. is not supported. Change to true.") + call mpas_log_write("config_cvmix_convective_basedOnBVF = .false. is not supported. Change to true.", MPAS_LOG_CRIT) err = 1 return endif @@ -757,6 +971,19 @@ subroutine ocn_vmix_cvmix_init(domain,err)!{{{ ! initialize KPP boundary layer scheme ! if (config_use_cvmix_kpp) then + if(config_cvmix_kpp_matching.eq."MatchBoth") then + call mpas_log_write( & + "Use of option MatchBoth is discouraged, use SimpleShapes instead", & + MPAS_LOG_WARN) + elseif(.not. config_cvmix_kpp_matching.eq."SimpleShapes") then + call mpas_log_write( & + "Unknown value for config_cvmix_kpp_matching., supported values are:" // & + " SimpleShapes or MatchBoth", & + MPAS_LOG_CRIT) + err = 1 + return + endif + call cvmix_init_kpp ( & ri_crit = config_cvmix_kpp_criticalBulkRichardsonNumber, & interp_type = config_cvmix_kpp_interpolationOMLType, & @@ -764,7 +991,8 @@ subroutine ocn_vmix_cvmix_init(domain,err)!{{{ lEkman = config_cvmix_kpp_EkmanOBL, & lMonOb = config_cvmix_kpp_MonObOBL, & MatchTechnique = config_cvmix_kpp_matching, & - surf_layer_ext = config_cvmix_kpp_surface_layer_extent) + surf_layer_ext = config_cvmix_kpp_surface_layer_extent, & + lenhanced_diff = config_cvmix_kpp_use_enhanced_diff) endif diff --git a/src/core_ocean/tracer_groups/Registry_DMS.xml b/src/core_ocean/tracer_groups/Registry_DMS.xml new file mode 100644 index 0000000000..c34f132ccc --- /dev/null +++ b/src/core_ocean/tracer_groups/Registry_DMS.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/tracer_groups/Registry_MacroMolecules.xml b/src/core_ocean/tracer_groups/Registry_MacroMolecules.xml new file mode 100644 index 0000000000..e4b6c3ac68 --- /dev/null +++ b/src/core_ocean/tracer_groups/Registry_MacroMolecules.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/tracer_groups/Registry_TEMPLATEGRP.xml b/src/core_ocean/tracer_groups/Registry_TEMPLATEGRP.xml new file mode 100644 index 0000000000..c397d6c63b --- /dev/null +++ b/src/core_ocean/tracer_groups/Registry_TEMPLATEGRP.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/tracer_groups/Registry_activeTracers.xml b/src/core_ocean/tracer_groups/Registry_activeTracers.xml new file mode 100644 index 0000000000..66e2dba813 --- /dev/null +++ b/src/core_ocean/tracer_groups/Registry_activeTracers.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/tracer_groups/Registry_debugTracers.xml b/src/core_ocean/tracer_groups/Registry_debugTracers.xml new file mode 100644 index 0000000000..e2d6e3af17 --- /dev/null +++ b/src/core_ocean/tracer_groups/Registry_debugTracers.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/tracer_groups/Registry_ecosys.xml b/src/core_ocean/tracer_groups/Registry_ecosys.xml new file mode 100644 index 0000000000..d245c275cf --- /dev/null +++ b/src/core_ocean/tracer_groups/Registry_ecosys.xml @@ -0,0 +1,1755 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core_ocean/tracer_groups/Registry_tracers.xml b/src/core_ocean/tracer_groups/Registry_tracers.xml new file mode 100644 index 0000000000..ce11f75662 --- /dev/null +++ b/src/core_ocean/tracer_groups/Registry_tracers.xml @@ -0,0 +1,6 @@ +#include "Registry_activeTracers.xml" +#include "Registry_debugTracers.xml" +#include "Registry_ecosys.xml" +#include "Registry_DMS.xml" +#include "Registry_MacroMolecules.xml" +//#include "Registry_TEMPLATEGRP.xml" diff --git a/src/core_seaice/LICENSE b/src/core_seaice/LICENSE new file mode 100644 index 0000000000..0bf5064708 --- /dev/null +++ b/src/core_seaice/LICENSE @@ -0,0 +1,19 @@ +This software is open source software available under the BSD-3 license. + +Copyright (c) 2018 Los Alamos National Security, LLC. + +All rights reserved. + +Copyright 2018. Los Alamos National Security, LLC. This software was produced under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National Laboratory (LANL), which is operated by Los Alamos National Security, LLC for the U.S. Department of Energy. The U.S. Government has rights to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce derivative works, such modified software should be clearly marked, so as not to confuse it with the version available from LANL. + +Additionally, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +- Neither the name of Los Alamos National Security, LLC, Los Alamos National Laboratory, LANL, the U.S. Government, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + + +THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/core_seaice/Registry.xml b/src/core_seaice/Registry.xml index 47d9040b38..92d40a723d 100644 --- a/src/core_seaice/Registry.xml +++ b/src/core_seaice/Registry.xml @@ -1,5 +1,5 @@ - + + + + @@ -781,7 +792,7 @@ possible_values="positive real number less than 1" icepack_name="grid_o_t" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -1947,6 +1966,15 @@ + + + + + + + + + + + + + + + + + + + + + @@ -3571,6 +3622,11 @@ + + + + + @@ -3829,6 +3885,23 @@ + + + + + + + + + + + + + + + + + \brief Setup packages for MPAS-Ocean analysis driver +!> \brief Setup packages for MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details @@ -358,12 +351,12 @@ end subroutine seaice_analysis_bootstrap!}}} ! ! routine seaice_analysis_init ! -!> \brief Initialize MPAS-Ocean analysis driver +!> \brief Initialize MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all initializations required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -500,12 +493,12 @@ end subroutine seaice_analysis_init!}}} ! ! routine seaice_analysis_compute_startup ! -!> \brief Driver for MPAS-Ocean analysis computations +!> \brief Driver for MPAS-Seaice analysis computations !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all computation subroutines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -599,7 +592,7 @@ end subroutine seaice_analysis_compute_startup!}}} ! routine seaice_analysis_precompute ! !> \brief Driver for MPAS-Seaice analysis computations -!> \author MPAS-Ocean/Seaice development team +!> \author MPAS-Seaice/Seaice development team !> \date November 2013 !> \details !> This routine calls all pre timestep computation subroutines @@ -692,12 +685,12 @@ end subroutine seaice_analysis_precompute!}}} ! ! routine seaice_analysis_compute ! -!> \brief Driver for MPAS-Ocean analysis computations +!> \brief Driver for MPAS-Seaice analysis computations !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all computation subroutines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -788,12 +781,12 @@ end subroutine seaice_analysis_compute!}}} ! ! routine seaice_analysis_restart ! -!> \brief Save restart for MPAS-Ocean analysis driver +!> \brief Save restart for MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all subroutines required to prepare to save -!> the restart state for the MPAS-Ocean analysis driver. +!> the restart state for the MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- @@ -862,12 +855,12 @@ end subroutine seaice_analysis_restart!}}} ! ! routine seaice_analysis_write ! -!> \brief Driver for MPAS-Ocean analysis output +!> \brief Driver for MPAS-Seaice analysis output !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all output writing subroutines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. !> At this time this is just a stub, and all analysis output is written !> to the output file specified by config_output_name. ! @@ -985,12 +978,12 @@ end subroutine seaice_analysis_write!}}} ! ! routine seaice_analysis_finalize ! -!> \brief Finalize MPAS-Ocean analysis driver +!> \brief Finalize MPAS-Seaice analysis driver !> \author Mark Petersen !> \date November 2013 !> \details !> This routine calls all finalize routines required for the -!> MPAS-Ocean analysis driver. +!> MPAS-Seaice analysis driver. ! !----------------------------------------------------------------------- diff --git a/src/core_seaice/analysis_members/mpas_seaice_area_variables.F b/src/core_seaice/analysis_members/mpas_seaice_area_variables.F index f5e2b9da29..b69518518d 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_area_variables.F +++ b/src/core_seaice/analysis_members/mpas_seaice_area_variables.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_area_variables diff --git a/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F b/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F index b1ce104434..2a3f9f6261 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F +++ b/src/core_seaice/analysis_members/mpas_seaice_conservation_check.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_conservation_check @@ -448,6 +441,8 @@ subroutine energy_conservation(domain, err) character(len=17) :: & formatString + err = 0 + call MPAS_pool_get_config(domain % blocklist % configs, "config_dt", dt) call MPAS_pool_get_config(domain % blocklist % configs, "config_AM_conservationCheck_write_to_logfile", & config_AM_conservationCheck_write_to_logfile) @@ -714,6 +709,8 @@ subroutine mass_conservation(domain, err) character(len=17) :: & formatString + err = 0 + call MPAS_pool_get_config(domain % blocklist % configs, "config_dt", dt) call MPAS_pool_get_config(domain % blocklist % configs, "config_AM_conservationCheck_write_to_logfile", & config_AM_conservationCheck_write_to_logfile) @@ -956,6 +953,8 @@ subroutine salt_conservation(domain, err) character(len=17) :: & formatString + err = 0 + call MPAS_pool_get_config(domain % blocklist % configs, "config_dt", dt) call MPAS_pool_get_config(domain % blocklist % configs, "config_AM_conservationCheck_write_to_logfile", & config_AM_conservationCheck_write_to_logfile) diff --git a/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F b/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F index 3b202cdecb..63859a1e39 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F +++ b/src/core_seaice/analysis_members/mpas_seaice_deactivate_unneeded_fields.F @@ -1,9 +1,3 @@ -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_deactivate_unneeded_fields diff --git a/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F b/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F index e8a7cbed80..b7dc9c79d8 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F +++ b/src/core_seaice/analysis_members/mpas_seaice_geographical_vectors.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_geographical_vectors diff --git a/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F b/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F index bc23a2ce3e..b116060633 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F +++ b/src/core_seaice/analysis_members/mpas_seaice_high_frequency_output.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_high_frequency_output diff --git a/src/core_seaice/analysis_members/mpas_seaice_ice_present.F b/src/core_seaice/analysis_members/mpas_seaice_ice_present.F index 0c1e78399e..6f146235e4 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_ice_present.F +++ b/src/core_seaice/analysis_members/mpas_seaice_ice_present.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_ice_present diff --git a/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F b/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F index bfb28705af..2a72d0c19c 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F +++ b/src/core_seaice/analysis_members/mpas_seaice_ice_shelves.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_ice_shelves @@ -282,6 +275,8 @@ subroutine seaice_compute_ice_shelves(domain, instance, timeLevel, err)!{{{ integer :: & iCell + err = 0 + iceAreaOverIceShelvesThisProc = 0.0_RKIND block => domain % blocklist diff --git a/src/core_seaice/analysis_members/mpas_seaice_load_balance.F b/src/core_seaice/analysis_members/mpas_seaice_load_balance.F index e3d9138a4b..2ef0138895 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_load_balance.F +++ b/src/core_seaice/analysis_members/mpas_seaice_load_balance.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_load_balance diff --git a/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F b/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F index 119983876d..fdea4d5629 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F +++ b/src/core_seaice/analysis_members/mpas_seaice_maximum_ice_presence.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_maximum_ice_presence diff --git a/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F b/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F index 6ed36c9932..9575834577 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F +++ b/src/core_seaice/analysis_members/mpas_seaice_miscellaneous.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_miscellaneous diff --git a/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F b/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F index 7efd727227..17fffe6f10 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F +++ b/src/core_seaice/analysis_members/mpas_seaice_pointwise_stats.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_pointwise_stats diff --git a/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F b/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F index 371b89a2b5..006c48bc8e 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F +++ b/src/core_seaice/analysis_members/mpas_seaice_pond_diagnostics.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_pond_diagnostics diff --git a/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F b/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F index 41d1bdd81b..187d2af187 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F +++ b/src/core_seaice/analysis_members/mpas_seaice_regional_statistics.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_regional_statistics diff --git a/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F b/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F index fd12e97a98..911bf4582a 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F +++ b/src/core_seaice/analysis_members/mpas_seaice_ridging_diagnostics.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_ridging_diagnostics diff --git a/src/core_seaice/analysis_members/mpas_seaice_temperatures.F b/src/core_seaice/analysis_members/mpas_seaice_temperatures.F index 412a00962f..587b78bbe6 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_temperatures.F +++ b/src/core_seaice/analysis_members/mpas_seaice_temperatures.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_temperatures diff --git a/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F b/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F index 442494b979..31d108e478 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F +++ b/src/core_seaice/analysis_members/mpas_seaice_time_series_stats.F @@ -1,11 +1,3 @@ -! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! - !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! seaice_time_series_stats ! @@ -161,7 +153,7 @@ module seaice_time_series_stats 'repeat_interval' character (len=StrKIND), parameter :: RESET_INTERVAL_TOKEN = 'reset_interval' - character (len=StrKIND), parameter :: CURRENT_CORE_NAME = 'MPAS-Ocean' + character (len=StrKIND), parameter :: CURRENT_CORE_NAME = 'MPAS-Seaice' character (len=4), parameter :: NONE_TOKEN = 'none' !*********************************************************************** @@ -214,12 +206,12 @@ end subroutine seaice_bootstrap_time_series_stats!}}} !*********************************************************************** ! routine seaice_init_time_series_stats ! -!> \brief Initialize MPAS-Ocean analysis member +!> \brief Initialize MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts all initializations required for the -!> MPAS-Ocean analysis member. +!> MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_init_time_series_stats(domain, instance, err)!{{{ ! input variables @@ -276,12 +268,12 @@ end subroutine seaice_init_time_series_stats!}}} !*********************************************************************** ! routine seaice_compute_time_series_stats ! -!> \brief Compute MPAS-Ocean analysis member +!> \brief Compute MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts all computation required for this -!> MPAS-Ocean analysis member. +!> MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_compute_time_series_stats(domain, timeLevel, instance, err)!{{{ ! input variables @@ -356,12 +348,12 @@ end subroutine seaice_compute_time_series_stats!}}} !*********************************************************************** ! routine seaice_restart_time_series_stats ! -!> \brief Save restart for MPAS-Ocean analysis member +!> \brief Save restart for MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts computation required to save a restart state -!> for the MPAS-Ocean analysis member. +!> for the MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_restart_time_series_stats(domain, instance, err)!{{{ ! input variables @@ -385,12 +377,12 @@ end subroutine seaice_restart_time_series_stats!}}} !*********************************************************************** ! routine seaice_finalize_time_series_stats ! -!> \brief Finalize MPAS-Ocean analysis member +!> \brief Finalize MPAS-Seaice analysis member !> \author Jon Woodring !> \date September 1, 2015 !> \details !> This routine conducts all finalizations required for this -!> MPAS-Ocean analysis member. +!> MPAS-Seaice analysis member. !----------------------------------------------------------------------- subroutine seaice_finalize_time_series_stats(domain, instance, err)!{{{ ! input variables diff --git a/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F b/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F index a28e7e620d..97e69ca262 100644 --- a/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F +++ b/src/core_seaice/analysis_members/mpas_seaice_unit_conversion.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! seaice_unit_conversion @@ -289,6 +282,8 @@ subroutine seaice_compute_unit_conversion(domain, instance, timeLevel, err)!{{{ real(kind=RKIND) :: & meltGrowthRateConversionFactor + err = 0 + block => domain % blocklist do while(associated(block)) diff --git a/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 b/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 index c8adacab52..1a46fc063d 100644 --- a/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 +++ b/src/core_seaice/column/constants/cesm/ice_constants_colpkg.F90 @@ -83,9 +83,10 @@ module ice_constants_colpkg snowpatch = 0.005_dbl_kind ! parameter for fractional snow area (m) !tcx note cice snowpatch = 0.02 - integer (kind=int_kind), parameter :: & - nspint = 3 ! number of solar spectral intervals - + integer (kind=int_kind), parameter, public :: & + nspint = 3 ,& ! number of solar spectral intervals + nspint_5bd = 5 ! number of solar spectral intervals with config_use_snicar + ! weights for albedos ! 4 Jan 2007 BPB Following are appropriate for complete cloud ! in a summer polar atmosphere with 1.5m bare sea ice surface: diff --git a/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 b/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 index 0a6c705164..6dd06f9451 100644 --- a/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 +++ b/src/core_seaice/column/constants/cice/ice_constants_colpkg.F90 @@ -75,8 +75,9 @@ module ice_constants_colpkg snowpatch = 0.02_dbl_kind ! parameter for fractional snow area (m) integer (kind=int_kind), parameter, public :: & - nspint = 3 ! number of solar spectral intervals - + nspint = 3 ,& ! number of solar spectral intervals + nspint_5bd = 5 ! number of solar spectral intervals used in SNICAR + ! weights for albedos ! 4 Jan 2007 BPB Following are appropriate for complete cloud ! in a summer polar atmosphere with 1.5m bare sea ice surface: diff --git a/src/core_seaice/column/ice_algae.F90 b/src/core_seaice/column/ice_algae.F90 index 429fd5e268..cb63dddf7a 100644 --- a/src/core_seaice/column/ice_algae.F90 +++ b/src/core_seaice/column/ice_algae.F90 @@ -1278,7 +1278,7 @@ subroutine z_biogeochemistry (n_cat, dt, & call add_warning(warning) write(warning,*)'Category,mm:',n_cat,mm call add_warning(warning) - l_stop = .true. +! l_stop = .true. stop_label = 'zbgc FCT tracer solution failed' endif if (l_stop) return @@ -1351,7 +1351,7 @@ subroutine z_biogeochemistry (n_cat, dt, & call add_warning(warning) write(warning, *) react(k,m),iphin_N(k),biomat_brine(k,m) call add_warning(warning) - l_stop = .true. +! l_stop = .true. stop_label = 'very large bgc value' elseif (bio_tmp < c0) then write(warning, *) 'negative bgc' @@ -2036,7 +2036,8 @@ subroutine algal_dyn (dt, & enddo endif endif !Docin(1) > c0 - elseif (tr_bgc_Fe) then + endif + if (tr_bgc_Fe) then do n = 1,n_fed Fed_r(n) = Fed_r(n) + rFed(n)*Fed_tot_r ! scavenging + uptake enddo diff --git a/src/core_seaice/column/ice_colpkg.F90 b/src/core_seaice/column/ice_colpkg.F90 index 2951fb8156..1cb257579d 100644 --- a/src/core_seaice/column/ice_colpkg.F90 +++ b/src/core_seaice/column/ice_colpkg.F90 @@ -259,6 +259,10 @@ subroutine colpkg_init_itd(ncat, hin_max, l_stop, stop_label) endif ! kcatbound + if (kitd == 1) then + hin_max(ncat) = 999.9_dbl_kind ! arbitrary big number + endif + end subroutine colpkg_init_itd !======================================================================= @@ -2329,7 +2333,7 @@ subroutine colpkg_step_therm2 (dt, ncat, n_aero, nltrcr, & logical (kind=log_kind), intent(in) :: & update_ocn_f ! if true, update fresh water and salt fluxes - real (kind=dbl_kind), dimension(0:ncat), intent(inout) :: & + real (kind=dbl_kind), dimension(0:ncat), intent(in) :: & hin_max ! category boundaries (m) real (kind=dbl_kind), intent(in) :: & @@ -2688,8 +2692,21 @@ subroutine colpkg_step_radiation (dt, ncat, & albpndn, apeffn, & snowfracn, & dhsn, ffracn, & - l_print_point, & - initonly) + l_print_point, & + initonly, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) use ice_constants_colpkg, only: c0, puny use ice_shortwave, only: run_dEdd, shortwave_ccsm3, compute_shortwave_trcr @@ -2804,6 +2821,30 @@ subroutine colpkg_step_radiation (dt, ncat, & logical (kind=log_kind), optional :: & initonly ! flag to indicate init only, default is false + + ! snow grain single-scattering properties for + ! direct (drc) and diffuse (dfs) shortwave incidents + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc, & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs, & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc, & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs, & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc, & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + ! local variables integer (kind=int_kind) :: & @@ -2908,7 +2949,21 @@ subroutine colpkg_step_radiation (dt, ncat, & snowfracn, & dhsn, ffracn, & l_print_point, & - linitonly) + linitonly, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) else ! .not. dEdd @@ -3474,6 +3529,7 @@ subroutine colpkg_atm_boundary(sfctype, & if (present(uvel)) then worku = uvel endif + ! should this be for vvel,workv? if (present(uvel)) then worku = uvel endif @@ -3626,6 +3682,7 @@ subroutine colpkg_init_parameters(& phi_c_slow_mode_in, & phi_i_mushy_in, & shortwave_in, & + use_snicar_in, & albedo_type_in, & albicev_in, & albicei_in, & @@ -3809,6 +3866,7 @@ subroutine colpkg_init_parameters(& phi_c_slow_mode, & phi_i_mushy, & shortwave, & + use_snicar, & albedo_type, & albicev, & albicei, & @@ -4037,6 +4095,12 @@ subroutine colpkg_init_parameters(& rsnw_mlt_in , & ! maximum melting snow grain radius (10^-6 m) kalg_in ! algae absorption coefficient for 0.5 m thick layer + ! snicar 5 band system, set in namelist + logical (kind=log_kind), intent(in) :: & + use_snicar_in ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice + !----------------------------------------------------------------------- ! Parameters for ridging and strength !----------------------------------------------------------------------- @@ -4286,6 +4350,7 @@ subroutine colpkg_init_parameters(& phi_c_slow_mode = phi_c_slow_mode_in phi_i_mushy = phi_i_mushy_in shortwave = shortwave_in + use_snicar = use_snicar_in albedo_type = albedo_type_in albicev = albicev_in albicei = albicei_in diff --git a/src/core_seaice/column/ice_colpkg_shared.F90 b/src/core_seaice/column/ice_colpkg_shared.F90 index 5e49c598c1..25fbf67a0f 100644 --- a/src/core_seaice/column/ice_colpkg_shared.F90 +++ b/src/core_seaice/column/ice_colpkg_shared.F90 @@ -91,6 +91,12 @@ module ice_colpkg_shared hi_ssl = 0.050_dbl_kind, & ! ice surface scattering layer thickness (m) hs_ssl = 0.040_dbl_kind ! snow surface scattering layer thickness (m) + ! snicar 5 band system, set in namelist + logical (kind=log_kind), public :: & + use_snicar ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice + !----------------------------------------------------------------------- ! Parameters for ridging and strength !----------------------------------------------------------------------- diff --git a/src/core_seaice/column/ice_shortwave.F90 b/src/core_seaice/column/ice_shortwave.F90 index 5c1d1a1593..f4c0d1e891 100644 --- a/src/core_seaice/column/ice_shortwave.F90 +++ b/src/core_seaice/column/ice_shortwave.F90 @@ -1,3 +1,4 @@ + ! SVN:$Id: ice_shortwave.F90 1182 2017-03-16 19:29:26Z njeffery $ !======================================================================= ! @@ -45,7 +46,7 @@ module ice_shortwave use ice_constants_colpkg, only: c0, c1, c1p5, c2, c3, c4, c10, & p01, p1, p15, p25, p5, p75, puny, & albocn, Timelt, snowpatch, awtvdr, awtidr, awtvdf, awtidf, & - kappav, hs_min, rhofresh, rhos, nspint + kappav, hs_min, rhofresh, rhos, nspint, nspint_5bd use ice_colpkg_shared, only: hi_ssl, hs_ssl, modal_aero, max_aero use ice_warnings, only: add_warning @@ -716,7 +717,21 @@ subroutine run_dEdd(dt, tr_aero, & snowfracn, & dhsn, ffracn, & l_print_point, & - initonly) + initonly, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) use ice_orbital, only: compute_coszen @@ -765,6 +780,27 @@ subroutine run_dEdd(dt, tr_aero, & waer_bc_tab, & ! aerosol single scatter albedo (fraction) gaer_bc_tab ! aerosol asymmetry parameter (cos(theta)) + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc, & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs, & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc, & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs, & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc, & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment bcenh ! BC absorption enhancement factor @@ -829,6 +865,11 @@ subroutine run_dEdd(dt, tr_aero, & Iswabsn , & ! SW radiation absorbed in ice layers (W m-2) fswpenln ! visible SW entering ice layers (W m-2) + logical (kind=log_kind), intent(in) :: & + use_snicar ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice + logical (kind=log_kind), intent(in) :: & l_print_point @@ -1048,7 +1089,21 @@ subroutine run_dEdd(dt, tr_aero, & albicen(n), & albsnon(n), albpndn(n), & fswpenln(:,n), zbion(:,n), & - l_print_point) + l_print_point, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) endif ! aicen > puny @@ -1113,7 +1168,21 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & Iswabs, albice, & albsno, albpnd, & fswpenl, zbio, & - l_print_point) + l_print_point, & + use_snicar, & + asm_prm_ice_drc, & + asm_prm_ice_dfs, & + ss_alb_ice_drc, & + ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, & + ext_cff_mss_ice_dfs, & + kaer_tab_5bd, & + waer_tab_5bd, & + gaer_tab_5bd, & + kaer_bc_tab_5bd, & + waer_bc_tab_5bd, & + gaer_bc_tab_5bd, & + bcenh_5bd) integer (kind=int_kind), intent(in) :: & nilyr , & ! number of ice layers @@ -1189,6 +1258,11 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & albsno , & ! snow albedo, for history albpnd ! pond albedo, for history + logical (kind=log_kind), intent(in) :: & + use_snicar ! if true, use 5-band snicar IOPs for + ! shortwave radiative calculation of + ! snow-coverd sea ice + logical (kind=log_kind) , intent(in) :: & l_print_point @@ -1236,6 +1310,29 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & character(len=char_len_long) :: & warning ! warning message + ! snow grain single-scattering properties for + ! direct (drc) and diffuse (dfs) shortwave incidents + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc, & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs, & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc, & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs, & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc, & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd, & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd, & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + !----------------------------------------------------------------------- klev = nslyr + nilyr + 1 ! number of radiation layers - 1 @@ -1257,6 +1354,7 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & fswsfc = c0 fswint = c0 fswthru = c0 + ! compute fraction of nir down direct to total over all points: fnidr = c0 if( swidr + swidf > puny ) then @@ -1339,22 +1437,52 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & ! calculate snow covered sea ice srftyp = 1 - call compute_dEdd(nilyr, nslyr, klev, klevp, & - n_zaero, zbio, dEdd_algae, & - nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & - tr_zaero, & - heat_capacity, fnidr, coszen, & - n_aero, tr_aero, R_ice, R_pnd, & - kaer_tab, waer_tab, gaer_tab, & - kaer_bc_tab, waer_bc_tab, gaer_bc_tab, & - bcenh, modal_aero, kalg, & - swvdr, swvdf, swidr, swidf, srftyp, & - hs, rhosnw, rsnw, hi, hp, & - fs, aero_mp, avdrl, avdfl, & - aidrl, aidfl, & - fswsfc, fswint, & - fswthru, Sswabs, & - Iswabs, fswpenl) + if (use_snicar) then ! use 5-band snicar IOPs for snow + call compute_dEdd_5bd(nilyr, nslyr, klev, klevp, & + n_zaero, zbio, dEdd_algae, & + nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & + tr_zaero, & + heat_capacity, fnidr, coszen, & + n_aero, tr_aero, R_ice, R_pnd, & + kaer_tab_5bd, waer_tab_5bd, gaer_tab_5bd, & + kaer_bc_tab_5bd, waer_bc_tab_5bd, gaer_bc_tab_5bd,& + bcenh_5bd, modal_aero, kalg, & + swvdr, swvdf, swidr, swidf, srftyp, & + hs, rhosnw, rsnw, hi, hp, & + fs, aero_mp, avdrl, avdfl, & + aidrl, aidfl, & + fswsfc, fswint, & + fswthru, Sswabs, & + Iswabs, fswpenl, & + asm_prm_ice_drc, asm_prm_ice_dfs, & + ss_alb_ice_drc, ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, ext_cff_mss_ice_dfs) + + alvdr = alvdr + avdrl *fs + alvdf = alvdf + avdfl *fs + alidr = alidr + aidrl *fs + alidf = alidf + aidfl *fs + ! for history + albsno = albsno & + + awtvdr*avdrl + awtidr*aidrl & + + awtvdf*avdfl + awtidf*aidfl + else ! use 3 band IOPs for snow + call compute_dEdd(nilyr, nslyr, klev, klevp, & + n_zaero, zbio, dEdd_algae, & + nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & + tr_zaero, & + heat_capacity, fnidr, coszen, & + n_aero, tr_aero, R_ice, R_pnd, & + kaer_tab, waer_tab, gaer_tab, & + kaer_bc_tab, waer_bc_tab, gaer_bc_tab, & + bcenh, modal_aero, kalg, & + swvdr, swvdf, swidr, swidf, srftyp, & + hs, rhosnw, rsnw, hi, hp, & + fs, aero_mp, avdrl, avdfl, & + aidrl, aidfl, & + fswsfc, fswint, & + fswthru, Sswabs, & + Iswabs, fswpenl) alvdr = alvdr + avdrl *fs alvdf = alvdf + avdfl *fs @@ -1364,9 +1492,11 @@ subroutine shortwave_dEdd (n_aero, n_zaero, & albsno = albsno & + awtvdr*avdrl + awtidr*aidrl & + awtvdf*avdfl + awtidf*aidfl + endif ! end if using 5band snicar subroutine + endif endif - + hi = c0 ! sea ice points with sun above horizon @@ -3728,6 +3858,1494 @@ subroutine compute_shortwave_trcr(n_algae, nslyr, & endif end subroutine compute_shortwave_trcr + +!======================================================================= +! --- Begin 5 band dEdd subroutine --- +! Evaluate snow/ice/ponded ice inherent optical properties (IOPs), and +! then calculate the multiple scattering solution by calling solution_dEdd. +! +! author: Bruce P. Briegleb, NCAR +! 2013: E Hunke merged with NCAR version +! 2018: Cheng Dang merged with SNICAR 5-band snow and aersols IOPs, UC Irvine +! +! Note by Cheng Dang 2018: +! This subroutine kept the existing delta-eddington adding-doubling +! method, snow and sea ice layer sturcture, and most of the code structures +! of subroutine compute_dEdd, with major changeds listed below to merge +! current snow treatments in SNICAR Model +! 1. The shortwave radiative transfer properties of snow-covered sea ice are +! calcualted for 5 bands (1 visible and 4 near-IR) defined in SNICAR +! 2. The reflection/absorption/transmission of direct and diffuse shortwave +! incidents are calculated seperately to remove the snow grain adjustment +! in subroutine compute_dEdd +! 3. The albedo and absorption of snow-covered sea ice are adjusted when solar +! zenith angle is above 75 degree +! 4. Comments given in subroutine compute_dEdd are all kepted in this subroutine +! with modifications at where above changes applies to. +! 5. This subroutine can be modified and merged with subroutine compute_dEdd +! to compute shortwave properties of bare and ponded sea ice if requested. +! For now, these two subroutines are seperated for testing new features. +! +! The justification and explaination for above changes can be find in paper: +! Dang et al., 2018, in prep, cdang5@uci.edu +! + + subroutine compute_dEdd_5bd (nilyr, nslyr, klev, klevp, & + n_zaero, zbio, dEdd_algae, & + nlt_chl_sw,nlt_zaero_sw, tr_bgc_N, & + tr_zaero, & + heat_capacity, fnidr, coszen, & + n_aero, tr_aero, R_ice, R_pnd, & + kaer_tab_5bd, waer_tab_5bd, gaer_tab_5bd, & + kaer_bc_tab_5bd, waer_bc_tab_5bd, & + gaer_bc_tab_5bd, bcenh_5bd, modal_aero, kalg, & + swvdr, swvdf, swidr, swidf, srftyp, & + hs, rhosnw, rsnw, hi, hp, & + fi, aero_mp, alvdr, alvdf, & + alidr, alidf, & + fswsfc, fswint, & + fswthru, Sswabs, & + Iswabs, fswpenl, & + asm_prm_ice_drc, asm_prm_ice_dfs, & + ss_alb_ice_drc, ss_alb_ice_dfs, & + ext_cff_mss_ice_drc, ext_cff_mss_ice_dfs) + + integer (kind=int_kind), intent(in) :: & + nilyr , & ! number of ice layers + nslyr , & ! number of snow layers + n_aero , & ! number of aerosol tracers + n_zaero , & ! number of zaerosol tracers in use + nlt_chl_sw , & ! index for chla + klev , & ! number of radiation layers - 1 + klevp ! number of radiation interfaces - 1 + ! (0 layer is included also) + + integer (kind=int_kind), dimension(:), intent(in) :: & + nlt_zaero_sw ! index for zaerosols + + logical (kind=log_kind), intent(in) :: & + heat_capacity , & ! if true, ice has nonzero heat capacity + tr_aero , & ! if .true., use aerosol tracers + dEdd_algae , & ! .true. use prognostic chla in dEdd + tr_bgc_N , & ! .true. active bgc (skl or z) + tr_zaero , & ! .true. use zaerosols + modal_aero ! .true. use modal aerosol treatment + + ! dEdd tuning parameters, set in namelist + real (kind=dbl_kind), intent(in) :: & + R_ice , & ! sea ice tuning parameter; +1 > 1sig increase in albedo + R_pnd ! ponded ice tuning parameter; +1 > 1sig increase in albedo + + real (kind=dbl_kind), intent(in) :: & + kalg , & ! algae absorption coefficient + fnidr , & ! fraction of direct to total down flux in nir + coszen , & ! cosine solar zenith angle + swvdr , & ! shortwave down at surface, visible, direct (W/m^2) + swvdf , & ! shortwave down at surface, visible, diffuse (W/m^2) + swidr , & ! shortwave down at surface, near IR, direct (W/m^2) + swidf ! shortwave down at surface, near IR, diffuse (W/m^2) + + integer (kind=int_kind), intent(in) :: & + srftyp ! surface type over ice: (0=air, 1=snow, 2=pond) + + real (kind=dbl_kind), intent(in) :: & + hs ! snow thickness (m) + + real (kind=dbl_kind), dimension (:), intent(in) :: & + rhosnw , & ! snow density in snow layer (kg/m3) + rsnw , & ! snow grain radius in snow layer (m) + zbio , & ! zaerosol + chla shortwave tracers kg/m^3 + aero_mp ! aerosol mass path in kg/m2 + + real (kind=dbl_kind), intent(in) :: & + hi , & ! ice thickness (m) + hp , & ! pond depth (m) + fi ! snow/bare ice fractional coverage (0 to 1) + + real (kind=dbl_kind), intent(inout) :: & + alvdr , & ! visible, direct, albedo (fraction) + alvdf , & ! visible, diffuse, albedo (fraction) + alidr , & ! near-ir, direct, albedo (fraction) + alidf , & ! near-ir, diffuse, albedo (fraction) + fswsfc , & ! SW absorbed at snow/bare ice/pondedi ice surface (W m-2) + fswint , & ! SW interior absorption (below surface, above ocean,W m-2) + fswthru ! SW through snow/bare ice/ponded ice into ocean (W m-2) + + real (kind=dbl_kind), dimension (:), intent(inout) :: & + fswpenl , & ! visible SW entering ice layers (W m-2) + Sswabs , & ! SW absorbed in snow layer (W m-2) + Iswabs ! SW absorbed in ice layer (W m-2) + + + ! snow grain single-scattering properties for + ! direct (drc) and diffuse (dfs) shortwave incidents + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Model SNICAR snow SSP + asm_prm_ice_drc , & ! snow asymmetry factor (cos(theta)) + asm_prm_ice_dfs , & ! snow asymmetry factor (cos(theta)) + ss_alb_ice_drc , & ! snow single scatter albedo (fraction) + ss_alb_ice_dfs , & ! snow single scatter albedo (fraction) + ext_cff_mss_ice_drc , & ! snow mass extinction cross section (m2/kg) + ext_cff_mss_ice_dfs ! snow mass extinction cross section (m2/kg) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & + kaer_tab_5bd , & ! aerosol mass extinction cross section (m2/kg) + waer_tab_5bd , & ! aerosol single scatter albedo (fraction) + gaer_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:), intent(in) :: & ! Modal aerosol treatment + kaer_bc_tab_5bd , & ! aerosol mass extinction cross section (m2/kg) + waer_bc_tab_5bd , & ! aerosol single scatter albedo (fraction) + gaer_bc_tab_5bd ! aerosol asymmetry parameter (cos(theta)) + + real (kind=dbl_kind), dimension(:,:,:), intent(in) :: & ! Modal aerosol treatment + bcenh_5bd ! BC absorption enhancement factor + +!----------------------------------------------------------------------- +! Set up optical property profiles, based on snow, sea ice and ponded +! ice IOPs from: +! +! Briegleb, B. P., and B. Light (2007): A Delta-Eddington Multiple +! Scattering Parameterization for Solar Radiation in the Sea Ice +! Component of the Community Climate System Model, NCAR Technical +! Note NCAR/TN-472+STR February 2007 +! +! Computes column Delta-Eddington radiation solution for specific +! surface type: either snow over sea ice, bare sea ice, or ponded sea ice. +! +! Divides solar spectrum into 3 intervals: 0.2-0.7, 0.7-1.19, and +! 1.19-5.0 micro-meters. The latter two are added (using an assumed +! partition of incident shortwave in the 0.7-5.0 micro-meter band between +! the 0.7-1.19 and 1.19-5.0 micro-meter band) to give the final output +! of 0.2-0.7 visible and 0.7-5.0 near-infrared albedos and fluxes. +! +! Specifies vertical layer optical properties based on input snow depth, +! density and grain radius, along with ice and pond depths, then computes +! layer by layer Delta-Eddington reflectivity, transmissivity and combines +! layers (done by calling routine). Finally, surface albedos +! and internal fluxes/flux divergences are evaluated. +! +! Description of the level and layer index conventions. This is +! for the standard case of one snow layer and four sea ice layers. +! +! Please read the following; otherwise, there is 99.9% chance you +! will be confused about indices at some point in time........ :) +! +! CICE4.0 snow treatment has one snow layer above the sea ice. This +! snow layer has finite heat capacity, so that surface absorption must +! be distinguished from internal. The Delta-Eddington solar radiation +! thus adds extra surface scattering layers to both snow and sea ice. +! Note that in the following, we assume a fixed vertical layer structure +! for the radiation calculation. In other words, we always have the +! structure shown below for one snow and four sea ice layers, but for +! ponded ice the pond fills "snow" layer 1 over the sea ice, and for +! bare sea ice the top layers over sea ice are treated as transparent air. +! +! SSL = surface scattering layer for either snow or sea ice +! DL = drained layer for sea ice immediately under sea ice SSL +! INT = interior layers for sea ice below the drained layer. +! +! Notice that the radiation level starts with 0 at the top. Thus, +! the total number radiation layers is klev+1, where klev is the +! sum of nslyr, the number of CCSM snow layers, and nilyr, the +! number of CCSM sea ice layers, plus the sea ice SSL: +! klev = 1 + nslyr + nilyr +! +! For the standard case illustrated below, nslyr=1, nilyr=4, +! and klev=6, with the number of layer interfaces klevp=klev+1. +! Layer interfaces are the surfaces on which reflectivities, +! transmissivities and fluxes are evaluated. +! +! CCSM3 Sea Ice Model Delta-Eddington Solar Radiation +! Layers and Interfaces +! Layer Index Interface Index +! --------------------- --------------------- 0 +! 0 \\\ snow SSL \\\ +! snow layer 1 --------------------- 1 +! 1 rest of snow layer +! +++++++++++++++++++++ +++++++++++++++++++++ 2 +! 2 \\\ sea ice SSL \\\ +! sea ice layer 1 --------------------- 3 +! 3 sea ice DL +! --------------------- --------------------- 4 +! +! sea ice layer 2 4 sea ice INT +! +! --------------------- --------------------- 5 +! +! sea ice layer 3 5 sea ice INT +! +! --------------------- --------------------- 6 +! +! sea ice layer 4 6 sea ice INT +! +! --------------------- --------------------- 7 +! +! When snow lies over sea ice, the radiation absorbed in the +! snow SSL is used for surface heating, and that in the rest +! of the snow layer for its internal heating. For sea ice in +! this case, all of the radiant heat absorbed in both the +! sea ice SSL and the DL are used for sea ice layer 1 heating. +! +! When pond lies over sea ice, and for bare sea ice, all of the +! radiant heat absorbed within and above the sea ice SSL is used +! for surface heating, and that absorbed in the sea ice DL is +! used for sea ice layer 1 heating. +! +! Basically, vertical profiles of the layer extinction optical depth (tau), +! single scattering albedo (w0) and asymmetry parameter (g) are required over +! the klev+1 layers, where klev+1 = 2 + nslyr + nilyr. All of the surface type +! information and snow/ice iop properties are evaulated in this routine, so +! the tau,w0,g profiles can be passed to solution_dEdd for multiple scattering +! evaluation. Snow, bare ice and ponded ice iops are contained in data arrays +! in this routine. +! +!----------------------------------------------------------------------- + + ! local variables + + integer (kind=int_kind) :: & + k , & ! level index + ns , & ! spectral index + nr , & ! index for grain radius tables + ki , & ! index for internal absorption + km , & ! k starting index for snow, sea ice internal absorption + kp , & ! k+1 or k+2 index for snow, sea ice internal absorption + ksrf , & ! level index for surface absorption + ksnow , & ! level index for snow density and grain size + kii ! level starting index for sea ice (nslyr+1) + + integer (kind=int_kind), parameter :: & + nmbrad = 32 ! number of snow grain radii in tables + + real (kind=dbl_kind) :: & + avdr , & ! visible albedo, direct (fraction) + avdf , & ! visible albedo, diffuse (fraction) + aidr , & ! near-ir albedo, direct (fraction) + aidf ! near-ir albedo, diffuse (fraction) + + real (kind=dbl_kind) :: & + fsfc , & ! shortwave absorbed at snow/bare ice/ponded ice surface (W m-2) + fint , & ! shortwave absorbed in interior (W m-2) + fthru ! shortwave through snow/bare ice/ponded ice to ocean (W/m^2) + + real (kind=dbl_kind), dimension(nslyr) :: & + Sabs ! shortwave absorbed in snow layer (W m-2) + + real (kind=dbl_kind), dimension(nilyr) :: & + Iabs ! shortwave absorbed in ice layer (W m-2) + + real (kind=dbl_kind), dimension(nilyr+1) :: & + fthrul ! shortwave through to ice layers (W m-2) + + real (kind=dbl_kind), dimension (nspint) :: & + wghtns ! spectral weights + + real (kind=dbl_kind), parameter :: & + cp67 = 0.67_dbl_kind , & ! nir band weight parameter + cp33 = 0.33_dbl_kind , & ! nir band weight parameter + cp78 = 0.78_dbl_kind , & ! nir band weight parameter + cp22 = 0.22_dbl_kind , & ! nir band weight parameter + cp01 = 0.01_dbl_kind ! for ocean visible albedo + + real (kind=dbl_kind), dimension (0:klev) :: & + tau , & ! layer extinction optical depth + w0 , & ! layer single scattering albedo + g ! layer asymmetry parameter + + ! following arrays are defined at model interfaces; 0 is the top of the + ! layer above the sea ice; klevp is the sea ice/ocean interface. + real (kind=dbl_kind), dimension (0:klevp) :: & + trndir , & ! solar beam down transmission from top + trntdr , & ! total transmission to direct beam for layers above + trndif , & ! diffuse transmission to diffuse beam for layers above + rupdir , & ! reflectivity to direct radiation for layers below + rupdif , & ! reflectivity to diffuse radiation for layers below + rdndif ! reflectivity to diffuse radiation for layers above + + real (kind=dbl_kind), dimension (0:klevp) :: & + dfdir , & ! down-up flux at interface due to direct beam at top surface + dfdif ! down-up flux at interface due to diffuse beam at top surface + + real (kind=dbl_kind) :: & + refk , & ! interface k multiple scattering term + delr , & ! snow grain radius interpolation parameter + ! inherent optical properties (iop) for snow + Qs , & ! Snow extinction efficiency + ks , & ! Snow extinction coefficient (/m) + ws , & ! Snow single scattering albedo + gs ! Snow asymmetry parameter + + real (kind=dbl_kind), dimension(nslyr) :: & + frsnw ! snow grain radius in snow layer * adjustment factor (m) + + ! actual used ice and ponded ice IOPs, allowing for tuning + ! modifications of the above "_mn" value + real (kind=dbl_kind), dimension (nspint) :: & + ki_ssl , & ! Surface-scattering-layer ice extinction coefficient (/m) + wi_ssl , & ! Surface-scattering-layer ice single scattering albedo + gi_ssl , & ! Surface-scattering-layer ice asymmetry parameter + ki_dl , & ! Drained-layer ice extinction coefficient (/m) + wi_dl , & ! Drained-layer ice single scattering albedo + gi_dl , & ! Drained-layer ice asymmetry parameter + ki_int , & ! Interior-layer ice extinction coefficient (/m) + wi_int , & ! Interior-layer ice single scattering albedo + gi_int , & ! Interior-layer ice asymmetry parameter + ki_p_ssl , & ! Ice under pond srf scat layer extinction coefficient (/m) + wi_p_ssl , & ! Ice under pond srf scat layer single scattering albedo + gi_p_ssl , & ! Ice under pond srf scat layer asymmetry parameter + ki_p_int , & ! Ice under pond extinction coefficient (/m) + wi_p_int , & ! Ice under pond single scattering albedo + gi_p_int ! Ice under pond asymmetry parameter + + real (kind=dbl_kind), dimension(0:klev) :: & + dzk ! layer thickness + + real (kind=dbl_kind) :: & + dz , & ! snow, sea ice or pond water layer thickness + dz_ssl , & ! snow or sea ice surface scattering layer thickness + fs ! scaling factor to reduce (nilyr<4) or increase (nilyr>4) DL + ! extinction coefficient to maintain DL optical depth constant + ! with changing number of sea ice layers, to approximately + ! conserve computed albedo for constant physical depth of sea + ! ice when the number of sea ice layers vary + real (kind=dbl_kind) :: & + sig , & ! scattering coefficient for tuning + kabs , & ! absorption coefficient for tuning + sigp ! modified scattering coefficient for tuning + + ! real (kind=dbl_kind), dimension(nspint, 0:klev) :: & + ! kabs_chl , & ! absorption coefficient for chlorophyll (/m) + ! tzaer , & ! total aerosol extinction optical depth + ! wzaer , & ! total aerosol single scatter albedo + ! gzaer ! total aerosol asymmetry parameter + + real (kind=dbl_kind) :: & + albodr , & ! spectral ocean albedo to direct rad + albodf ! spectral ocean albedo to diffuse rad + + ! for melt pond transition to bare sea ice for small pond depths + real (kind=dbl_kind) :: & + sig_i , & ! ice scattering coefficient (/m) + sig_p , & ! pond scattering coefficient (/m) + kext ! weighted extinction coefficient (/m) + + ! aerosol optical properties from Mark Flanner, 26 June 2008 + ! order assumed: hydrophobic black carbon, hydrophilic black carbon, + ! four dust aerosols by particle size range: + ! dust1(.05-0.5 micron), dust2(0.5-1.25 micron), + ! dust3(1.25-2.5 micron), dust4(2.5-5.0 micron) + ! spectral bands same as snow/sea ice: (0.3-0.7 micron, 0.7-1.19 micron + ! and 1.19-5.0 micron in wavelength) + + integer (kind=int_kind) :: & + na , n ! aerosol index + + real (kind=dbl_kind) :: & + taer , & ! total aerosol extinction optical depth + waer , & ! total aerosol single scatter albedo + gaer , & ! total aerosol asymmetry parameter + swdr , & ! shortwave down at surface, direct (W/m^2) + swdf , & ! shortwave down at surface, diffuse (W/m^2) + rnilyr , & ! real(nilyr) + rnslyr , & ! real(nslyr) + rns , & ! real(ns) + tmp_0, tmp_ks, tmp_kl ! temp variables + + integer(kind=int_kind), dimension(0:klev) :: & + k_bcini , & + k_bcins , & + k_bcexs + + real(kind=dbl_kind):: & + tmp_gs, tmp1 ! temp variables + + ! inherent optical property (iop) arrays for ice and ponded ice + ! mn = specified mean (or base) value + ! ki = extinction coefficient (/m) + ! wi = single scattering albedo + ! gi = asymmetry parameter + + ! ice surface scattering layer (ssl) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_ssl_mn = (/ 1000.1_dbl_kind, 1003.7_dbl_kind, 7042._dbl_kind/), & + wi_ssl_mn = (/ .9999_dbl_kind, .9963_dbl_kind, .9088_dbl_kind/), & + gi_ssl_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind/) + + ! ice drained layer (dl) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_dl_mn = (/ 100.2_dbl_kind, 107.7_dbl_kind, 1309._dbl_kind /), & + wi_dl_mn = (/ .9980_dbl_kind, .9287_dbl_kind, .0305_dbl_kind /), & + gi_dl_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! ice interior layer (int) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_int_mn = (/ 20.2_dbl_kind, 27.7_dbl_kind, 1445._dbl_kind /), & + wi_int_mn = (/ .9901_dbl_kind, .7223_dbl_kind, .0277_dbl_kind /), & + gi_int_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! ponded ice surface scattering layer (ssl) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_p_ssl_mn = (/ 70.2_dbl_kind, 77.7_dbl_kind, 1309._dbl_kind/), & + wi_p_ssl_mn = (/ .9972_dbl_kind, .9009_dbl_kind, .0305_dbl_kind/), & + gi_p_ssl_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! ponded ice interior layer (int) iops + real (kind=dbl_kind), dimension (nspint), parameter :: & + ki_p_int_mn = (/ 20.2_dbl_kind, 27.7_dbl_kind, 1445._dbl_kind/), & + wi_p_int_mn = (/ .9901_dbl_kind, .7223_dbl_kind, .0277_dbl_kind/), & + gi_p_int_mn = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind /) + + ! inherent optical property (iop) arrays for pond water and underlying ocean + ! kw = Pond water extinction coefficient (/m) + ! ww = Pond water single scattering albedo + ! gw = Pond water asymmetry parameter + real (kind=dbl_kind), dimension (nspint), parameter :: & + kw = (/ 0.20_dbl_kind, 12.0_dbl_kind, 729._dbl_kind /), & + ww = (/ 0.00_dbl_kind, 0.00_dbl_kind, 0.00_dbl_kind /), & + gw = (/ 0.00_dbl_kind, 0.00_dbl_kind, 0.00_dbl_kind /) + + real (kind=dbl_kind), parameter :: & + rhoi = 917.0_dbl_kind, & ! pure ice mass density (kg/m3) + fr_max = 1.00_dbl_kind, & ! snow grain adjustment factor max + fr_min = 0.80_dbl_kind, & ! snow grain adjustment factor min + ! tuning parameters + ! ice and pond scat coeff fractional change for +- one-sigma in albedo + fp_ice = 0.15_dbl_kind, & ! ice fraction of scat coeff for + stn dev in alb + fm_ice = 0.15_dbl_kind, & ! ice fraction of scat coeff for - stn dev in alb + fp_pnd = 2.00_dbl_kind, & ! ponded ice fraction of scat coeff for + stn dev in alb + fm_pnd = 0.50_dbl_kind ! ponded ice fraction of scat coeff for - stn dev in alb + + real (kind=dbl_kind), parameter :: & !chla-specific absorption coefficient + kchl_tab = 0.01 !0.0023-0.0029 Perovich 1993, also 0.0067 m^2 (mg Chl)^-1 + ! found values of 0.006 to 0.023 m^2/ mg (676 nm) Neukermans 2014 + ! and averages over the 300-700nm of 0.0075 m^2/mg in ice Fritsen (2011) + ! at 440nm values as high as 0.2 m^2/mg in under ice bloom (Balch 2014) + ! Grenfell 1991 uses 0.004 (m^2/mg) which is (0.0078 * spectral weighting) + !chlorophyll mass extinction cross section (m^2/mg chla) + + character(len=char_len_long) :: & + warning ! warning message + + ! SNICAR + ! new inputs + integer (kind=int_kind), parameter :: & + nmbrad_snicar = 1471 , &! number of snow grain radii in SNICAR + ! snow iops table + rsnw_snicar_max = 1500 , & + rsnw_snicar_min = 30 + + real (kind=dbl_kind), dimension (nspint_5bd) :: & + wghtns_5bd_dfs, & ! spectral weights for diffuse incident + wghtns_5bd_drc ! spectral weights for direct incident + + ! FUTURE-WORK: update 5-band sea ice iops when avalible + real (kind=dbl_kind), dimension (nspint_5bd) :: & ! for ice only + ki_ssl_5bd , & ! Surface-scattering-layer ice extinction coefficient (/m) + wi_ssl_5bd , & ! Surface-scattering-layer ice single scattering albedo + gi_ssl_5bd , & ! Surface-scattering-layer ice asymmetry parameter + ki_dl_5bd , & ! Drained-layer ice extinction coefficient (/m) + wi_dl_5bd , & ! Drained-layer ice single scattering albedo + gi_dl_5bd , & ! Drained-layer ice asymmetry parameter + ki_int_5bd , & ! Interior-layer ice extinction coefficient (/m) + wi_int_5bd , & ! Interior-layer ice single scattering albedo + gi_int_5bd ! Interior-layer ice asymmetry parameter + + ! 5-band aersol data + real (kind=dbl_kind), dimension(nspint_5bd, 0:klev) :: & + kabs_chl_5bd , & ! absorption coefficient for chlorophyll (/m) + tzaer_5bd , & ! total aerosol extinction optical depth + wzaer_5bd , & ! total aerosol single scatter albedo + gzaer_5bd ! total aerosol asymmetry parameter + + ! index + integer (kind=int_kind) :: & + nsky !sky = 1 (2) for direct (diffuse) downward SW incident + + ! temporary variables used to assign variables for direct/diffuse incident + ! based on snicar 5 band IOPs + real (kind=dbl_kind), dimension (0:klevp) :: & + dfdir_snicar , & ! down-up flux at interface due to direct beam at top surface + dfdif_snicar , & ! down-up flux at interface due to diffuse beam at top surface + rupdir_snicar , & ! reflectivity to direct radiation for layers below + rupdif_snicar ! reflectivity to diffuse radiation for layers above + + ! solar zenith angle parameterizations + real (kind=dbl_kind), parameter :: & + sza_a0 = 0.085730_dbl_kind , & + sza_a1 = -0.630883_dbl_kind , & + sza_a2 = 1.303723_dbl_kind , & + sza_b0 = 1.467291_dbl_kind , & + sza_b1 = -3.338043_dbl_kind , & + sza_b2 = 6.807489_dbl_kind , & + mu_75 = 0.2588_dbl_kind ! cosine of 75 degree + + real (kind=dbl_kind) :: & + sza_c1 , & ! spectral ocean albedo to direct rad + sza_c0 , & ! spectral ocean albedo to diffuse rad + sza_factor , & + mu0 + + ! 5-bands ice surface scattering layer (ssl) iops to match SNICAR calculations + ! note by Cheng Dang: + ! for now these data are not needed since the sea ice layer IOPs can be directly + ! assigned based on the 3 bands data after adjustment based on tuning parameter R_ice + ! In the future, when 5-band sea ice IOPs are available, these data shall be updated + ! and the sea ice layer IOPs shall be calculated based on updated 5band iops* + ! + ! The 5band data given in this section are based on CICE and SNICAR band choice: + ! SNICAR band 1 = CICE band 1 + ! SNICAR band 2 = SNICAR band 3 = CICE band 2 + ! SNICAR band 4 = SNICAR band 5 = CICE band 3 + + ! ice surface scattering layer (ssl) iops + real (kind=dbl_kind), dimension (nspint_5bd), parameter :: & + ki_ssl_mn_5bd = (/ 1000.1_dbl_kind, 1003.7_dbl_kind, 1003.7_dbl_kind, & + 7042._dbl_kind, 7042._dbl_kind /), & + wi_ssl_mn_5bd = (/ .9999_dbl_kind, .9963_dbl_kind, .9963_dbl_kind, & + .9088_dbl_kind, .9088_dbl_kind /), & + gi_ssl_mn_5bd = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind, & + .94_dbl_kind, .94_dbl_kind /) + + ! ice drained layer (dl) iops + real (kind=dbl_kind), dimension (nspint_5bd), parameter :: & + ki_dl_mn_5bd = (/ 100.2_dbl_kind, 107.7_dbl_kind, 107.7_dbl_kind, & + 1309._dbl_kind, 1309._dbl_kind /), & + wi_dl_mn_5bd = (/ .9980_dbl_kind, .9287_dbl_kind, .9287_dbl_kind, & + .0305_dbl_kind, .0305_dbl_kind /), & + gi_dl_mn_5bd = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind, & + .94_dbl_kind, .94_dbl_kind /) + + ! ice interior layer (int) iops + real (kind=dbl_kind), dimension (nspint_5bd), parameter :: & + ki_int_mn_5bd = (/ 20.2_dbl_kind, 27.7_dbl_kind, 27.7_dbl_kind, & + 1445._dbl_kind, 1445._dbl_kind/), & + wi_int_mn_5bd = (/ .9901_dbl_kind, .7223_dbl_kind, .7223_dbl_kind, & + .0277_dbl_kind, .0277_dbl_kind /), & + gi_int_mn_5bd = (/ .94_dbl_kind, .94_dbl_kind, .94_dbl_kind, & + .94_dbl_kind, .94_dbl_kind /) + +!----------------------------------------------------------------------- +! Initialize and tune bare ice/ponded ice iops + + k_bcini(:) = c0 + k_bcins(:) = c0 + k_bcexs(:) = c0 + + rnilyr = c1/real(nilyr,kind=dbl_kind) + rnslyr = c1/real(nslyr,kind=dbl_kind) + kii = nslyr + 1 + + ! initialize albedos and fluxes to 0 + fthrul = c0 + Iabs = c0 + kabs_chl_5bd(:,:) = c0 + tzaer_5bd(:,:) = c0 + wzaer_5bd(:,:) = c0 + gzaer_5bd(:,:) = c0 + + avdr = c0 + avdf = c0 + aidr = c0 + aidf = c0 + fsfc = c0 + fint = c0 + fthru = c0 + + + ! spectral weights - 3 bands + ! this section of code is kept for future mearge between 5band and 3 band + ! subroutines + ! weights 2 (0.7-1.19 micro-meters) and 3 (1.19-5.0 micro-meters) + ! are chosen based on 1D calculations using ratio of direct to total + ! near-infrared solar (0.7-5.0 micro-meter) which indicates clear/cloudy + ! conditions: more cloud, the less 1.19-5.0 relative to the + ! 0.7-1.19 micro-meter due to cloud absorption. + !wghtns(1) = c1 + !wghtns(2) = cp67 + (cp78-cp67)*(c1-fnidr) +! wghtns(3) = cp33 + (cp22-cp33)*(c1-fnidr) + !wghtns(3) = c1 - wghtns(2) + + ! spectral weights - 5 bands + ! direct beam incident + ! add-local-variable + wghtns_5bd_drc(1) = 1._dbl_kind + wghtns_5bd_drc(2) = 0.49352158521175_dbl_kind!0.49352_dbl_kind!0.50_dbl_kind + wghtns_5bd_drc(3) = 0.18099494230665_dbl_kind!0.18100_dbl_kind!0.18_dbl_kind + wghtns_5bd_drc(4) = 0.12094898498813_dbl_kind!0.12095_dbl_kind!0.12_dbl_kind ! + wghtns_5bd_drc(5) = c1-(wghtns_5bd_drc(2)+wghtns_5bd_drc(3)+wghtns_5bd_drc(4)) + !wghtns_5bd_drc(5) = 0.20453448749347_dbl_kind!0.20453_dbl_kind!0.20_dbl_kind ! + + ! diffuse incident + wghtns_5bd_dfs(1) = 1._dbl_kind + wghtns_5bd_dfs(2) = 0.58581507618433_dbl_kind!0.58582_dbl_kind!0.59_dbl_kind ! + wghtns_5bd_dfs(3) = 0.20156903770812_dbl_kind!0.20157_dbl_kind!0.20_dbl_kind ! + wghtns_5bd_dfs(4) = 0.10917889346386_dbl_kind!0.10918_dbl_kind!0.11_dbl_kind ! + wghtns_5bd_dfs(5) = c1-(wghtns_5bd_dfs(2)+wghtns_5bd_dfs(3)+wghtns_5bd_dfs(4)) + !wghtns_5bd_dfs(5) = 0.10343699264369_dbl_kind!0.10343_dbl_kind!0.10_dbl_kind ! + + + do k = 1, nslyr + !frsnw(k) = (fr_max*fnidr + fr_min*(c1-fnidr))*rsnw(k) + Sabs(k) = c0 + enddo + + ! layer thicknesses + ! snow + dz = hs*rnslyr + ! for small enough snow thickness, ssl thickness half of top snow layer +!ech: note this is highly resolution dependent! + dzk(0) = min(hs_ssl, dz/c2) + dzk(1) = dz - dzk(0) + if (nslyr > 1) then + do k = 2, nslyr + dzk(k) = dz + enddo + endif + + ! ice + dz = hi*rnilyr + ! empirical reduction in sea ice ssl thickness for ice thinner than 1.5m; + ! factor of 30 gives best albedo comparison with limited observations + dz_ssl = hi_ssl +!ech: note hardwired parameters +! if( hi < 1.5_dbl_kind ) dz_ssl = hi/30._dbl_kind + dz_ssl = min(hi_ssl, hi/30._dbl_kind) + ! set sea ice ssl thickness to half top layer if sea ice thin enough +!ech: note this is highly resolution dependent! + dz_ssl = min(dz_ssl, dz/c2) + + dzk(kii) = dz_ssl + dzk(kii+1) = dz - dz_ssl + if (kii+2 <= klev) then + do k = kii+2, klev + dzk(k) = dz + enddo + endif + + ! adjust sea ice iops with tuning parameters; tune only the + ! scattering coefficient by factors of R_ice, R_pnd, where + ! R values of +1 correspond approximately to +1 sigma changes in albedo, and + ! R values of -1 correspond approximately to -1 sigma changes in albedo + ! Note: the albedo change becomes non-linear for R values > +1 or < -1 + if( R_ice >= c0 ) then + do ns = 1, nspint_5bd + sigp = ki_ssl_mn_5bd(ns)*wi_ssl_mn_5bd(ns)*(c1+fp_ice*R_ice) + ki_ssl_5bd(ns) = sigp+ki_ssl_mn_5bd(ns)*(c1-wi_ssl_mn_5bd(ns)) + wi_ssl_5bd(ns) = sigp/ki_ssl_5bd(ns) + gi_ssl_5bd(ns) = gi_ssl_mn_5bd(ns) + + sigp = ki_dl_mn_5bd(ns)*wi_dl_mn_5bd(ns)*(c1+fp_ice*R_ice) + ki_dl_5bd(ns) = sigp+ki_dl_mn_5bd(ns)*(c1-wi_dl_mn_5bd(ns)) + wi_dl_5bd(ns) = sigp/ki_dl_5bd(ns) + gi_dl_5bd(ns) = gi_dl_mn_5bd(ns) + + sigp = ki_int_mn_5bd(ns)*wi_int_mn_5bd(ns)*(c1+fp_ice*R_ice) + ki_int_5bd(ns) = sigp+ki_int_mn_5bd(ns)*(c1-wi_int_mn_5bd(ns)) + wi_int_5bd(ns) = sigp/ki_int_5bd(ns) + gi_int_5bd(ns) = gi_int_mn_5bd(ns) + enddo + else !if( R_ice < c0 ) then + do ns = 1, nspint + sigp = ki_ssl_mn_5bd(ns)*wi_ssl_mn_5bd(ns)*(c1+fm_ice*R_ice) + sigp = max(sigp, c0) + ki_ssl_5bd(ns) = sigp+ki_ssl_mn_5bd(ns)*(c1-wi_ssl_mn_5bd(ns)) + wi_ssl_5bd(ns) = sigp/ki_ssl_5bd(ns) + gi_ssl_5bd(ns) = gi_ssl_mn_5bd(ns) + + sigp = ki_dl_mn_5bd(ns)*wi_dl_mn_5bd(ns)*(c1+fm_ice*R_ice) + sigp = max(sigp, c0) + ki_dl_5bd(ns) = sigp+ki_dl_mn_5bd(ns)*(c1-wi_dl_mn_5bd(ns)) + wi_dl_5bd(ns) = sigp/ki_dl_5bd(ns) + gi_dl_5bd(ns) = gi_dl_mn_5bd(ns) + + sigp = ki_int_mn_5bd(ns)*wi_int_mn_5bd(ns)*(c1+fm_ice*R_ice) + sigp = max(sigp, c0) + ki_int_5bd(ns) = sigp+ki_int_mn_5bd(ns)*(c1-wi_int_mn_5bd(ns)) + wi_int_5bd(ns) = sigp/ki_int_5bd(ns) + gi_int_5bd(ns) = gi_int_mn_5bd(ns) + enddo + endif ! adjust ice iops + + ! use srftyp to determine interface index of surface absorption + ksrf = 1 ! snow covered sea ice + + if (tr_bgc_N .and. dEdd_algae) then ! compute kabs_chl for chlorophyll + do k = 0, klev + kabs_chl_5bd(1,k) = kchl_tab*zbio(nlt_chl_sw+k) + enddo + else + k = klev + kabs_chl_5bd(1,k) = kalg*(0.50_dbl_kind/dzk(k)) + endif + +!mgf++ + if (modal_aero) then + do k=0,klev + if (k < nslyr+1) then ! define indices for snow layer + ! use top rsnw, rhosnw for snow ssl and rest of top layer + ! Cheng: note that aerosol IOPs are related to snow grain radius. + ! CICE adjusted snow grain radius rsnw to frsnw, while for + ! SNICAR there is no need, the tmp_gs is therefore calculated + ! differently from code in subroutine compute_dEdd + ksnow = k - min(k-1,0) + tmp_gs = rsnw(ksnow) ! use rsnw not frsnw + + ! get grain size index: + ! works for 25 < snw_rds < 1625 um: + if (tmp_gs < 125) then + tmp1 = tmp_gs/50 + k_bcini(k) = nint(tmp1) + elseif (tmp_gs < 175) then + k_bcini(k) = 2 + else + tmp1 = (tmp_gs/250)+2 + k_bcini(k) = nint(tmp1) + endif + else ! use the largest snow grain size for ice + k_bcini(k) = 8 + endif + ! Set index corresponding to BC effective radius. Here, + ! asssume constant BC effective radius of 100nm + ! (corresponding to index 2) + k_bcins(k) = 2 + k_bcexs(k) = 2 + + ! check bounds: + if (k_bcini(k) < 1) k_bcini(k) = 1 + if (k_bcini(k) > 8) k_bcini(k) = 8 + if (k_bcins(k) < 1) k_bcins(k) = 1 + if (k_bcins(k) > 10) k_bcins(k) = 10 + if (k_bcexs(k) < 1) k_bcexs(k) = 1 + if (k_bcexs(k) > 10) k_bcexs(k) = 10 + + ! print ice radius index: + ! write(warning,*) "MGFICE2:k, ice index= ",k, k_bcini(k) + ! call add_warning(warning) + enddo ! k + ! assign the aerosol index + + if (tr_zaero .and. dEdd_algae) then ! compute kzaero for chlorophyll + do n = 1,n_zaero + if (n == 1) then ! interstitial BC + do k = 0, klev + do ns = 1,nspint_5bd ! not weighted by aice + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcexs(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))* & + gaer_bc_tab_5bd(ns,k_bcexs(k))*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + elseif (n==2) then ! within-ice BC + do k = 0, klev + do ns = 1,nspint_5bd + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcins(k)) * & + bcenh_5bd(ns,k_bcins(k),k_bcini(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))* & + gaer_bc_tab_5bd(ns,k_bcins(k))*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + else ! dust + do k = 0, klev + do ns = 1,nspint_5bd ! not weighted by aice + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + gaer_tab_5bd(ns,n)*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + endif !(n=1) + enddo ! n_zaero + endif ! tr_zaero and dEdd_algae + else ! Bulk aerosol treatment + if (tr_zaero .and. dEdd_algae) then ! compute kzaero for chlorophyll + do n = 1,n_zaero ! multiply by aice? + do k = 0, klev + do ns = 1,nspint_5bd ! not weighted by aice + tzaer_5bd(ns,k) = tzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + zbio(nlt_zaero_sw(n)+k)*dzk(k) + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)+kaer_tab_5bd(ns,n)*waer_tab_5bd(ns,n)* & + gaer_tab_5bd(ns,n)*zbio(nlt_zaero_sw(n)+k)*dzk(k) + enddo ! nspint + enddo + enddo + endif !tr_zaero + endif ! modal_aero + + +!----------------------------------------------------------------------- + ! begin spectral loop + ! for snow-covered sea ice, comput 5 bands + if( srftyp == 1 ) then + + ! note: major changeds: + ! 1. loop through 5bands: do ns = 1, nspint_5bd + ! 2. use snow grain size rsnow, not scaled frsnw + ! 3. replace $IOPs_tab with $IOPs_snicar + ! 4. replace wghtns with wghtns_5bd + do ns = 1, nspint_5bd + do nsky = 1,2 ! loop for both direct beam and diffuse beam + if (nsky == 1) then ! direc incident + do k=0,nslyr + ! use top rsnw, rhosnw for snow ssl and rest of top layer + ksnow = k - min(k-1,0) + if (rsnw(ksnow) <= rsnw_snicar_min) then + ks = ext_cff_mss_ice_drc(ns,1) + ws = ss_alb_ice_drc(ns,1) + gs = asm_prm_ice_drc(ns,1) + elseif (rsnw(ksnow) >= rsnw_snicar_max) then + ks = ext_cff_mss_ice_drc(ns,nmbrad_snicar) + ws = ss_alb_ice_drc(ns,nmbrad_snicar) + gs = asm_prm_ice_drc(ns,nmbrad_snicar) + elseif (ceiling(rsnw(ksnow)) - rsnw(ksnow) < 1.0e-3_dbl_kind) then + nr = ceiling(rsnw(ksnow)) - 30 + 1 + ks = ext_cff_mss_ice_drc(ns,nr) + ws = ss_alb_ice_drc(ns,nr) + gs = asm_prm_ice_drc(ns,nr) + else ! linear interpolation in rsnw + ! radius = 30 --> nr = 1 in SNICAR table + nr = ceiling(rsnw(ksnow)) - 30 + 1 + delr = (rsnw(ksnow) - floor(rsnw(ksnow))) / & + (ceiling(rsnw(ksnow)) - floor(rsnw(ksnow))) + ks = ext_cff_mss_ice_drc(ns,nr-1)*(delr) + & + ext_cff_mss_ice_drc(ns,nr)*(c1-delr) + ws = ss_alb_ice_drc(ns,nr-1)*(delr) + & + ss_alb_ice_drc(ns,nr)*(c1-delr) + gs = asm_prm_ice_drc(ns,nr-1)*(delr) + & + asm_prm_ice_drc(ns,nr)*(c1-delr) + endif + ! ks = Qs*((rhosnw(ksnow)/rhoi)*3._dbl_kind / & + ! (4._dbl_kind*rsnw(ksnow)*1.0e-6_dbl_kind)) + tau(k) = (ks*rhosnw(ksnow) + kabs_chl_5bd(ns,k))*dzk(k) + w0(k) = ks/(ks + kabs_chl_5bd(ns,k))*ws + g(k) = gs + enddo ! k + elseif (nsky == 2) then ! diffuse incident + do k=0,nslyr + ! use top rsnw, rhosnw for snow ssl and rest of top layer + ksnow = k - min(k-1,0) + if (rsnw(ksnow) < rsnw_snicar_min) then + ks = ext_cff_mss_ice_dfs(ns,1) + ws = ss_alb_ice_dfs(ns,1) + gs = asm_prm_ice_dfs(ns,1) + elseif (rsnw(ksnow) > rsnw_snicar_max) then + ks = ext_cff_mss_ice_dfs(ns,nmbrad_snicar) + ws = ss_alb_ice_dfs(ns,nmbrad_snicar) + gs = asm_prm_ice_dfs(ns,nmbrad_snicar) + elseif (ceiling(rsnw(ksnow)) - rsnw(ksnow) < 1.0e-3_dbl_kind) then + nr = ceiling(rsnw(ksnow)) - 30 + 1 + ks = ext_cff_mss_ice_dfs(ns,nr) + ws = ss_alb_ice_dfs(ns,nr) + gs = asm_prm_ice_dfs(ns,nr) + else ! linear interpolation in rsnw + ! radius = 30 --> nr = 1 in SNICAR table + nr = ceiling(rsnw(ksnow)) - 30 + 1 + delr = (rsnw(ksnow) - floor(rsnw(ksnow))) / & + (ceiling(rsnw(ksnow)) - floor(rsnw(ksnow))) + ks = ext_cff_mss_ice_dfs(ns,nr-1)*(c1-delr) + & + ext_cff_mss_ice_dfs(ns,nr)*delr + ws = ss_alb_ice_dfs(ns,nr-1)*(c1-delr) + & + ss_alb_ice_dfs(ns,nr)*delr + gs = asm_prm_ice_dfs(ns,nr-1)*(c1-delr) + & + asm_prm_ice_dfs(ns,nr)*delr + endif + ! ks = Qs*((rhosnw(ksnow)/rhoi)*3._dbl_kind / & + ! (4._dbl_kind*rsnw(ksnow)*1.0e-6_dbl_kind)) + tau(k) = (ks*rhosnw(ksnow) + kabs_chl_5bd(ns,k))*dzk(k) + w0(k) = ks/(ks + kabs_chl_5bd(ns,k)) *ws + g(k) = gs + enddo ! k + endif ! end if nsky for snow IOPs assignment +! ------------------------------------------------------------------------------ + !aerosol in snow + if (tr_zaero .and. dEdd_algae) then + do k = 0,nslyr + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)/(wzaer_5bd(ns,k)+puny) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)/(tzaer_5bd(ns,k)+puny) + g(k) = (g(k)*w0(k)*tau(k) + gzaer_5bd(ns,k)*wzaer_5bd(ns,k)*tzaer_5bd(ns,k)) / & + (w0(k)*tau(k) + wzaer_5bd(ns,k)*tzaer_5bd(ns,k)) + w0(k) = (w0(k)*tau(k) + wzaer_5bd(ns,k)*tzaer_5bd(ns,k)) / & + (tau(k) + tzaer_5bd(ns,k)) + tau(k) = tau(k) + tzaer_5bd(ns,k) + enddo + + elseif (tr_aero) then + k = 0 ! snow SSL + taer = c0 + waer = c0 + gaer = c0 + + do na=1,4*n_aero,4 +! mgf++ + if (modal_aero) then + ! if na + if (na == 1) then + !interstitial BC + taer = taer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na == 5)then + !within-ice BC + taer = taer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + aero_mp(na)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + else + ! other species (dust) + taer = taer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif ! if na = 1 + ! end if na + else + taer = taer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif !modal_aero +!mgf-- + enddo ! na + + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + do k=1,nslyr + taer = c0 + waer = c0 + gaer = c0 + do na=1,4*n_aero,4 + if (modal_aero) then +!mgf++ + if (na==1) then + ! interstitial BC + taer = taer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na==5) then + ! within-ice BC + taer = taer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcins(k))*& + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + (aero_mp(na+1)/rnslyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + + else + ! other species (dust) + taer = taer + & + (aero_mp(na+1)/rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+1)/rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+1)/rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif !(na==1) + + else + taer = taer + & + (aero_mp(na+1)*rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+1)*rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+1)*rnslyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif ! modal_aero +!mgf-- + enddo ! na + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + g(k) = (g(k)*w0(k)*tau(k) + gaer*waer*taer) / & + (w0(k)*tau(k) + waer*taer) + w0(k) = (w0(k)*tau(k) + waer*taer) / & + (tau(k) + taer) + tau(k) = tau(k) + taer + enddo ! k + endif ! tr_aero + +! --- sea ice layers under snowpack ---- +! if( srftyp == 1 ) then + ! ssl + k = kii + tau(k) = (ki_ssl_5bd(ns)+kabs_chl_5bd(ns,k))*dzk(k) + w0(k) = ki_ssl_5bd(ns)/(ki_ssl_5bd(ns) + kabs_chl_5bd(ns,k))*wi_ssl_5bd(ns) + g(k) = gi_ssl_5bd(ns) + ! dl + k = kii + 1 + ! scale dz for dl relative to 4 even-layer-thickness 1.5m case + fs = p25/rnilyr + tau(k) = (ki_dl_5bd(ns) + kabs_chl_5bd(ns,k)) *dzk(k)*fs + w0(k) = ki_dl_5bd(ns)/(ki_dl_5bd(ns) + kabs_chl_5bd(ns,k)) *wi_dl_5bd(ns) + g(k) = gi_dl_5bd(ns) + ! int above lowest layer + if (kii+2 <= klev-1) then + do k = kii+2, klev-1 + tau(k) = (ki_int_5bd(ns) + kabs_chl_5bd(ns,k))*dzk(k) + w0(k) = ki_int_5bd(ns)/(ki_int_5bd(ns) + kabs_chl_5bd(ns,k)) *wi_int_5bd(ns) + g(k) = gi_int_5bd(ns) + enddo + endif + ! lowest layer + k = klev + ! add algae to lowest sea ice layer, visible only: + kabs = ki_int_5bd(ns)*(c1-wi_int_5bd(ns)) + if( ns == 1 ) then + ! total layer absorption optical depth fixed at value + ! of kalg*0.50m, independent of actual layer thickness + kabs = kabs + kabs_chl_5bd(ns,k) + endif + sig = ki_int_5bd(ns)*wi_int_5bd(ns) + tau(k) = (kabs+sig)*dzk(k) + w0(k) = (sig/(sig+kabs)) + g(k) = gi_int_5bd(ns) + + + ! aerosol in sea ice + if (tr_zaero .and. dEdd_algae) then + do k = kii, klev + gzaer_5bd(ns,k) = gzaer_5bd(ns,k)/(wzaer_5bd(ns,k)+puny) + wzaer_5bd(ns,k) = wzaer_5bd(ns,k)/(tzaer_5bd(ns,k)+puny) + g(k) = (g(k)*w0(k)*tau(k) + gzaer_5bd(ns,k)*wzaer_5bd(ns,k)*tzaer_5bd(ns,k)) / & + (w0(k)*tau(k) + wzaer_5bd(ns,k)*tzaer_5bd(ns,k)) + w0(k) = (w0(k)*tau(k) + wzaer_5bd(ns,k)*tzaer_5bd(ns,k)) / & + (tau(k) + tzaer_5bd(ns,k)) + tau(k) = tau(k) + tzaer_5bd(ns,k) + enddo + elseif (tr_aero) then + k = kii ! sea ice SSL + taer = c0 + waer = c0 + gaer = c0 + do na=1,4*n_aero,4 +!mgf++ + if (modal_aero) then + if (na==1) then + ! interstitial BC + taer = taer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na==5) then + ! within-ice BC + taer = taer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + aero_mp(na+2)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + else + ! other species (dust) + taer = taer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif + else !bulk + taer = taer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + aero_mp(na+2)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif ! modal_aero +!mgf-- + enddo ! na + + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + g(k) = (g(k)*w0(k)*tau(k) + gaer*waer*taer) / & + (w0(k)*tau(k) + waer*taer) + w0(k) = (w0(k)*tau(k) + waer*taer) / & + (tau(k) + taer) + tau(k) = tau(k) + taer + do k = kii+1, klev + taer = c0 + waer = c0 + gaer = c0 + do na=1,4*n_aero,4 +!mgf++ + if (modal_aero) then + if (na==1) then + ! interstitial BC + taer = taer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcexs(k)) + waer = waer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k)) + gaer = gaer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcexs(k))* & + waer_bc_tab_5bd(ns,k_bcexs(k))*gaer_bc_tab_5bd(ns,k_bcexs(k)) + elseif (na==5) then + ! within-ice BC + taer = taer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + bcenh_5bd(ns,k_bcins(k),k_bcini(k)) + waer = waer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k)) + gaer = gaer + & + (aero_mp(na+3)/rnilyr)*kaer_bc_tab_5bd(ns,k_bcins(k))* & + waer_bc_tab_5bd(ns,k_bcins(k))*gaer_bc_tab_5bd(ns,k_bcins(k)) + + else + ! other species (dust) + taer = taer + & + (aero_mp(na+3)/rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+3)/rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+3)/rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif + else !bulk + + taer = taer + & + (aero_mp(na+3)*rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4)) + waer = waer + & + (aero_mp(na+3)*rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4)) + gaer = gaer + & + (aero_mp(na+3)*rnilyr)*kaer_tab_5bd(ns,(1+(na-1)/4))* & + waer_tab_5bd(ns,(1+(na-1)/4))*gaer_tab_5bd(ns,(1+(na-1)/4)) + endif ! modal_aero +!mgf-- + enddo ! na + gaer = gaer/(waer+puny) + waer = waer/(taer+puny) + g(k) = (g(k)*w0(k)*tau(k) + gaer*waer*taer) / & + (w0(k)*tau(k) + waer*taer) + w0(k) = (w0(k)*tau(k) + waer*taer) / & + (tau(k) + taer) + tau(k) = tau(k) + taer + enddo ! k + endif ! tr_aero + +! --------------------------------------------------------------------------- + + ! set reflectivities for ocean underlying sea ice + ! if ns == 1 (visible), albedo is 0.1, else, albedo is zero + rns = real(ns-1, kind=dbl_kind) + albodr = cp01 * (c1 - min(rns, c1)) + albodf = cp01 * (c1 - min(rns, c1)) + + ! layer input properties now completely specified: tau, w0, g, + ! albodr, albodf; now compute the Delta-Eddington solution + ! reflectivities and transmissivities for each layer; then, + ! combine the layers going downwards accounting for multiple + ! scattering between layers, and finally start from the + ! underlying ocean and combine successive layers upwards to + ! the surface; see comments in solution_dEdd for more details. + call solution_dEdd & + (coszen, srftyp, klev, klevp, nslyr, & + tau, w0, g, albodr, albodf, & + trndir, trntdr, trndif, rupdir, rupdif, & + rdndif) + + ! the interface reflectivities and transmissivities required + ! to evaluate interface fluxes are returned from solution_dEdd; + ! now compute up and down fluxes for each interface, using the + ! combined layer properties at each interface: + ! + ! layers interface + ! + ! --------------------- k + ! k + ! --------------------- + + do k = 0, klevp + ! interface scattering + refk = c1/(c1 - rdndif(k)*rupdif(k)) + ! dir tran ref from below times interface scattering, plus diff + ! tran and ref from below times interface scattering + ! fdirup(k) = (trndir(k)*rupdir(k) + & + ! (trntdr(k)-trndir(k)) & + ! *rupdif(k))*refk + ! dir tran plus total diff trans times interface scattering plus + ! dir tran with up dir ref and down dif ref times interface scattering + ! fdirdn(k) = trndir(k) + (trntdr(k) & + ! - trndir(k) + trndir(k) & + ! *rupdir(k)*rdndif(k))*refk + ! diffuse tran ref from below times interface scattering + ! fdifup(k) = trndif(k)*rupdif(k)*refk + ! diffuse tran times interface scattering + ! fdifdn(k) = trndif(k)*refk + + ! dfdir = fdirdn - fdirup + dfdir(k) = trndir(k) & + + (trntdr(k)-trndir(k)) * (c1 - rupdif(k)) * refk & + - trndir(k)*rupdir(k) * (c1 - rdndif(k)) * refk + if (dfdir(k) < puny) dfdir(k) = c0 !echmod necessary? + ! dfdif = fdifdn - fdifup + dfdif(k) = trndif(k) * (c1 - rupdif(k)) * refk + if (dfdif(k) < puny) dfdif(k) = c0 !echmod necessary? + enddo ! k + + ! note that because the snow IOPs for diffuse and direct incidents + ! are different, the snow albedo needs to be calculated twice for + ! direct incident and diffuse incident respectively + if (nsky == 1) then ! direc beam (keep the direct beam results) + do k = 0, klevp + dfdir_snicar(k) = dfdir(k) + rupdir_snicar(k) = rupdir(k) + enddo + elseif (nsky == 2) then ! diffuse (keep the diffuse incident results) + do k = 0, klevp + dfdif_snicar(k) = dfdif(k) + rupdif_snicar(k) = rupdif(k) + enddo + endif + enddo ! end direct/diffuse incident nsky + + ! calculate final surface albedos and fluxes- + ! all absorbed flux above ksrf is included in surface absorption + if( ns == 1) then ! visible + swdr = swvdr + swdf = swvdf + avdr = rupdir_snicar(0) + avdf = rupdif_snicar(0) + tmp_0 = dfdir_snicar(0 )*swdr + dfdif_snicar(0 )*swdf + tmp_ks = dfdir_snicar(ksrf )*swdr + dfdif_snicar(ksrf )*swdf + tmp_kl = dfdir_snicar(klevp)*swdr + dfdif_snicar(klevp)*swdf + + ! for layer biology: save visible only + do k = nslyr+2, klevp ! Start at DL layer of ice after SSL scattering + fthrul(k-nslyr-1) = dfdir_snicar(k)*swdr + dfdif_snicar(k)*swdf + enddo + + fsfc = fsfc + tmp_0 - tmp_ks + fint = fint + tmp_ks - tmp_kl + fthru = fthru + tmp_kl + + ! if snow covered ice, set snow internal absorption; else, Sabs=0 + if( srftyp == 1 ) then + ki = 0 + do k=1,nslyr + ! skip snow SSL, since SSL absorption included in the surface + ! absorption fsfc above + km = k + kp = km + 1 + ki = ki + 1 + Sabs(ki) = Sabs(ki) & + + dfdir_snicar(km)*swdr + dfdif_snicar(km)*swdf & + - (dfdir_snicar(kp)*swdr + dfdif_snicar(kp)*swdf) + enddo ! k + endif + + ! complex indexing to insure proper absorptions for sea ice + ki = 0 + do k=nslyr+2,nslyr+1+nilyr + ! for bare ice, DL absorption for sea ice layer 1 + km = k + kp = km + 1 + ! modify for top sea ice layer for snow over sea ice + if( srftyp == 1 ) then + ! must add SSL and DL absorption for sea ice layer 1 + if( k == nslyr+2 ) then + km = k - 1 + kp = km + 2 + endif + endif + ki = ki + 1 + Iabs(ki) = Iabs(ki) & + + dfdir_snicar(km)*swdr + dfdif_snicar(km)*swdf & + - (dfdir_snicar(kp)*swdr + dfdif_snicar(kp)*swdf) + enddo ! k + + else !if(ns > 1) then ! near IR + + swdr = swidr + swdf = swidf + + + ! let fr2(3,4,5) = alb_2(3,4,5)*swd*wght2(3,4,5) + ! the ns=2(3,4,5) reflected fluxes respectively, + ! where alb_2(3,4,5) are the band + ! albedos, swd = nir incident shortwave flux, and wght2(3,4,5) are + ! the 2(3,4,5) band weights. thus, the total reflected flux is: + ! fr = fr2 + fr3 + fr4 + fr5 + ! = alb_2*swd*wght2 + alb_3*swd*wght3 + alb_4*swd*wght4 + alb_5*swd*wght5 + ! hence, the 2,3,4,5 nir band albedo is + ! alb = fr/swd = alb_2*wght2 + alb_3*wght3 + alb_4*wght4 + alb_5*wght5 + + aidr = aidr + rupdir_snicar(0)*wghtns_5bd_drc(ns) + aidf = aidf + rupdif_snicar(0)*wghtns_5bd_dfs(ns) + + tmp_0 = dfdir_snicar(0 )*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(0 )*swdf*wghtns_5bd_dfs(ns) + tmp_ks = dfdir_snicar(ksrf )*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(ksrf )*swdf*wghtns_5bd_dfs(ns) + tmp_kl = dfdir_snicar(klevp)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(klevp)*swdf*wghtns_5bd_dfs(ns) + + fsfc = fsfc + tmp_0 - tmp_ks + fint = fint + tmp_ks - tmp_kl + fthru = fthru + tmp_kl + + ! if snow covered ice, set snow internal absorption; else, Sabs=0 + if( srftyp == 1 ) then + ki = 0 + do k=1,nslyr + ! skip snow SSL, since SSL absorption included in the surface + ! absorption fsfc above + km = k + kp = km + 1 + ki = ki + 1 + Sabs(ki) = Sabs(ki) & + + dfdir_snicar(km)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(km)*swdf*wghtns_5bd_dfs(ns) & + -(dfdir_snicar(kp)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(kp)*swdf*wghtns_5bd_dfs(ns)) + + enddo ! k + endif + + ! complex indexing to insure proper absorptions for sea ice + ki = 0 + do k=nslyr+2,nslyr+1+nilyr + ! for bare ice, DL absorption for sea ice layer 1 + km = k + kp = km + 1 + ! modify for top sea ice layer for snow over sea ice + if( srftyp == 1 ) then + ! must add SSL and DL absorption for sea ice layer 1 + if( k == nslyr+2 ) then + km = k - 1 + kp = km + 2 + endif + endif + ki = ki + 1 + Iabs(ki) = Iabs(ki) & + + dfdir_snicar(km)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(km)*swdf*wghtns_5bd_dfs(ns) & + -(dfdir_snicar(kp)*swdr*wghtns_5bd_drc(ns) & + + dfdif_snicar(kp)*swdf*wghtns_5bd_dfs(ns)) + enddo ! k + endif ! ns = 1, ns > 1 check which spectra + + enddo ! end spectral loop ns + + endif !srf_type + + + ! accumulate fluxes over bare sea ice + + ! solar zenith angle parameterization + ! calculate the scaling factor for NIR direct albedo if SZA>75 degree + sza_factor = c1 + if( srftyp == 1 ) then + mu0 = max(coszen,p01) + if (mu0 < mu_75) then + sza_c1 = sza_a0 + sza_a1 * mu0 + sza_a2 * mu0**2 + sza_c0 = sza_b0 + sza_b1 * mu0 + sza_b2 * mu0**2 + sza_factor = sza_c1 * log10 (rsnw(1)) + sza_c0 + endif + endif + + alvdr = avdr + alvdf = avdf + alidr = aidr * sza_factor !sza factor is always larger or equal than 1 + alidf = aidf + + ! note that we assume the reduced NIR energy absorption + ! due to corrected snow albedo are absorbed by the snow single + ! scattering layer only - this is generally true if snow SSL >= 2 cm + ! by the default model set up: + ! if snow_depth >= 8 cm, SSL = 4 cm, satisify + ! esle if snow_depth >= 4 cm, SSL = snow_depth/2 >= 2 cm, satisfy + ! esle snow_depth < 4 cm, SSL = snow_depth/2, may overheat SSL layer + fswsfc = fswsfc - (sza_factor-c1)*aidr*swidr + fsfc*fi + fswint = fswint + fint *fi + fswthru = fswthru + fthru*fi + + + do k = 1, nslyr + Sswabs(k) = Sswabs(k) + Sabs(k)*fi + enddo ! k + + do k = 1, nilyr + Iswabs(k) = Iswabs(k) + Iabs(k)*fi + + ! bgc layer + fswpenl(k) = fswpenl(k) + fthrul(k)* fi + + if (k == nilyr) then + fswpenl(k+1) = fswpenl(k+1) + fthrul(k+1)*fi + endif + enddo ! k + + !---------------------------------------------------------------- + ! if ice has zero heat capacity, no SW can be absorbed + ! in the ice/snow interior, so add to surface absorption. + ! Note: nilyr = nslyr = 1 for this case + !---------------------------------------------------------------- + + if (.not. heat_capacity) then + + ! SW absorbed at snow/ice surface + fswsfc = fswsfc + Iswabs(1) + Sswabs(1) + + ! SW absorbed in ice interior + fswint = c0 + Iswabs(1) = c0 + Sswabs(1) = c0 + + endif ! heat_capacity + + end subroutine compute_dEdd_5bd + !======================================================================= end module ice_shortwave diff --git a/src/core_seaice/column/ice_therm_itd.F90 b/src/core_seaice/column/ice_therm_itd.F90 index d9b617841d..62059aa129 100644 --- a/src/core_seaice/column/ice_therm_itd.F90 +++ b/src/core_seaice/column/ice_therm_itd.F90 @@ -90,7 +90,7 @@ subroutine linear_itd (ncat, hin_max, & nslyr , & ! number of snow layers ntrcr ! number of tracers in use - real (kind=dbl_kind), dimension(0:ncat), intent(inout) :: & + real (kind=dbl_kind), dimension(0:ncat), intent(in) :: & hin_max ! category boundaries (m) integer (kind=int_kind), dimension (:), intent(in) :: & @@ -197,8 +197,6 @@ subroutine linear_itd (ncat, hin_max, & l_stop = .false. - hin_max(ncat) = 999.9_dbl_kind ! arbitrary big number - do n = 1, ncat donor(n) = 0 daice(n) = c0 diff --git a/src/core_seaice/column/ice_therm_mushy.F90 b/src/core_seaice/column/ice_therm_mushy.F90 index 78401ca716..e7108b3166 100644 --- a/src/core_seaice/column/ice_therm_mushy.F90 +++ b/src/core_seaice/column/ice_therm_mushy.F90 @@ -523,7 +523,7 @@ subroutine two_stage_solver_snow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return @@ -576,7 +576,7 @@ subroutine two_stage_solver_snow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return @@ -842,7 +842,7 @@ subroutine two_stage_solver_nosnow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return @@ -895,7 +895,7 @@ subroutine two_stage_solver_nosnow(nilyr, nslyr, & ! check if solution is consistent ! surface conductive heat flux should be less than ! incoming surface heat flux - if (fcondtop - fsurfn < ferrmax) then + if (fcondtop - fsurfn < 0.9_dbl_kind*ferrmax) then ! solution is consistent - have solution so finish return diff --git a/src/core_seaice/column/ice_therm_vertical.F90 b/src/core_seaice/column/ice_therm_vertical.F90 index f28d24962c..e61e1b49f9 100644 --- a/src/core_seaice/column/ice_therm_vertical.F90 +++ b/src/core_seaice/column/ice_therm_vertical.F90 @@ -1837,7 +1837,8 @@ subroutine conservation_check_vthermo(dt, & real (kind=dbl_kind) :: & einp , & ! energy input during timestep (J m-2) - ferr ! energy conservation error (W m-2) + ferr , & ! energy conservation error (W m-2) + ftop ! surface flux error: fcondtopn-fsurfn character(len=char_len_long) :: & warning ! warning message @@ -1883,6 +1884,10 @@ subroutine conservation_check_vthermo(dt, & call add_warning(warning) write(warning,*) fbot,fcondbot call add_warning(warning) + write(warning,*) 'fsurfn,fcondtopn:' + call add_warning(warning) + write(warning,*) fsurfn,fcondtopn + call add_warning(warning) ! if (ktherm == 2) then write(warning,*) 'Intermediate energy =', einter @@ -1893,11 +1898,15 @@ subroutine conservation_check_vthermo(dt, & write(warning,*) 'einter - einit =', & einter-einit call add_warning(warning) + ftop = c0 + if (ktherm == 2) then + if (fcondtopn > fsurfn) ftop = (fcondtopn-fsurfn) + end if write(warning,*) 'Conduction Error =', (einter-einit) & - - (fcondtopn*dt - fcondbot*dt + fswint*dt) + - (fcondtopn*dt - fcondbot*dt + fswint*dt) + ftop*dt call add_warning(warning) write(warning,*) 'Melt/Growth Error =', (einter-einit) & - + ferr*dt - (fcondtopn*dt - fcondbot*dt + fswint*dt) + + ferr*dt - (fcondtopn*dt - fcondbot*dt + fswint*dt)-ftop*dt call add_warning(warning) write(warning,*) 'Advection Error =', fadvocn*dt call add_warning(warning) diff --git a/src/core_seaice/column/ice_warnings.F90 b/src/core_seaice/column/ice_warnings.F90 index 15ab61abac..6747269623 100644 --- a/src/core_seaice/column/ice_warnings.F90 +++ b/src/core_seaice/column/ice_warnings.F90 @@ -33,7 +33,7 @@ subroutine add_warning(warning) ! number of array elements to increase size of warnings array if that array has run out of space integer, parameter :: & - nWarningsBuffer = 10 + nWarningsBuffer = 100 ! temporary array to store previous warnings while warning array is increased in size character(len=char_len_long), dimension(:), allocatable :: & diff --git a/src/core_seaice/model_forward/mpas_seaice_core.F b/src/core_seaice/model_forward/mpas_seaice_core.F index 2dceb8a9cb..5be282baeb 100644 --- a/src/core_seaice/model_forward/mpas_seaice_core.F +++ b/src/core_seaice/model_forward/mpas_seaice_core.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! module seaice_core use mpas_framework @@ -391,8 +384,7 @@ end function seaice_core_run subroutine mpas_timestep(domain, itimestep, timeStamp) use mpas_derived_types - use seaice_time_integration - use seaice_error, only: seaice_check_critical_error + use seaice_time_integration, only: seaice_timestep implicit none @@ -400,12 +392,7 @@ subroutine mpas_timestep(domain, itimestep, timeStamp) integer, intent(in) :: itimestep character(len=*), intent(in) :: timeStamp - integer :: ierr - - ierr = 0 - call seaice_timestep(domain, clock, itimestep, ierr) - - call seaice_check_critical_error(domain, ierr) + call seaice_timestep(domain, clock, itimestep) end subroutine mpas_timestep diff --git a/src/core_seaice/model_forward/mpas_seaice_core_interface.F b/src/core_seaice/model_forward/mpas_seaice_core_interface.F index fbca56ab5a..43d52702ea 100644 --- a/src/core_seaice/model_forward/mpas_seaice_core_interface.F +++ b/src/core_seaice/model_forward/mpas_seaice_core_interface.F @@ -1,10 +1,3 @@ -! Copyright (c) 2013, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! module seaice_core_interface use mpas_derived_types @@ -552,7 +545,9 @@ subroutine setup_packages_bergs(configPool, packagePool, ierr)!{{{ ! icebergs package logical, pointer :: & - config_use_bergs + config_use_forcing, & + config_use_bergs, & + config_use_data_icebergs logical, pointer :: & pkgBergsActive @@ -563,9 +558,16 @@ subroutine setup_packages_bergs(configPool, packagePool, ierr)!{{{ ! iceberg routines !----------------------------------------------------------------------- + call MPAS_pool_get_config(configPool, "config_use_forcing", config_use_forcing) call MPAS_pool_get_config(configPool, "config_use_bergs", config_use_bergs) + call MPAS_pool_get_config(configPool, "config_use_data_icebergs", config_use_data_icebergs) call MPAS_pool_get_package(packagePool, "pkgBergsActive", pkgBergsActive) - pkgBergsActive = config_use_bergs + + if (config_use_forcing .or. config_use_bergs .or. config_use_data_icebergs) then + + pkgBergsActive = .true. + + endif end subroutine setup_packages_bergs!}}} @@ -589,20 +591,28 @@ subroutine setup_packages_other(configPool, packagePool, ierr)!{{{ logical, pointer :: & config_use_forcing, & - config_testing_system_test + config_use_data_icebergs, & + config_testing_system_test, & + config_use_snicar logical, pointer :: & pkgForcingActive, & - pkgTestingSystemTestActive + pkgTestingSystemTestActive, & + pkgSnicarActive + + ierr = 0 + + ierr = 0 ! pkgForcing call MPAS_pool_get_config(configPool, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(configPool, "config_use_data_icebergs", config_use_data_icebergs) call MPAS_pool_get_package(packagePool, "pkgForcingActive", pkgForcingActive) ! see if we are using the forcing system - if (config_use_forcing) then + if (config_use_forcing .or. config_use_data_icebergs) then pkgForcingActive = .true. @@ -616,11 +626,15 @@ subroutine setup_packages_other(configPool, packagePool, ierr)!{{{ ! see if we are testing the testing system if (config_testing_system_test) then - pkgTestingSystemTestActive = .true. - endif + ! pkgSnicar + call MPAS_pool_get_config(configPool, "config_use_snicar", config_use_snicar) + call MPAS_pool_get_package(packagePool, "pkgSnicarActive", pkgSnicarActive) + ! see if we are using the snicar system + pkgSnicarActive = .true. + end subroutine setup_packages_other!}}} !*********************************************************************** diff --git a/src/core_seaice/shared/mpas_seaice_advection.F b/src/core_seaice/shared/mpas_seaice_advection.F index 13f3233a43..37bed39e51 100644 --- a/src/core_seaice/shared/mpas_seaice_advection.F +++ b/src/core_seaice/shared/mpas_seaice_advection.F @@ -87,7 +87,7 @@ end subroutine seaice_init_advection ! !----------------------------------------------------------------------- - subroutine seaice_run_advection(domain, clock, ierr) + subroutine seaice_run_advection(domain, clock) use seaice_advection_upwind, only: & seaice_run_advection_upwind @@ -104,9 +104,6 @@ subroutine seaice_run_advection(domain, clock, ierr) type (MPAS_Clock_type), intent(in) :: & clock !< Input: - integer, intent(inout) :: & - ierr !< Input/Output: - logical, pointer :: & config_use_advection @@ -130,7 +127,7 @@ subroutine seaice_run_advection(domain, clock, ierr) ! (Later, change volume to thickness throughout code?) call mpas_timer_start("advection incr remap") - call seaice_run_advection_incremental_remap(domain, iceTracersHead, clock, ierr) + call seaice_run_advection_incremental_remap(domain, iceTracersHead, clock) call mpas_timer_stop("advection incr remap") !TODO WHL - Convert ice/snow thickness to volume here diff --git a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F index 814df8fcec..f53aac0f81 100644 --- a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F +++ b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap.F @@ -1,10 +1,3 @@ -! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! mpas_seaice_advection_incremental_remap @@ -45,20 +38,20 @@ module seaice_advection_incremental_remap ! variables private to this module type :: geometric_avg_cell_type - real(kind=RKIND), dimension(:), pointer :: x => null() ! grid cell average of x - real(kind=RKIND), dimension(:), pointer :: y => null() ! grid cell average of y - real(kind=RKIND), dimension(:), pointer :: xx => null() ! grid cell average of x^2 - real(kind=RKIND), dimension(:), pointer :: xy => null() ! grid cell average of x*y - real(kind=RKIND), dimension(:), pointer :: yy => null() ! grid cell average of y^2 - real(kind=RKIND), dimension(:), pointer :: xxx => null() ! grid cell average of x^3 - real(kind=RKIND), dimension(:), pointer :: xxy => null() ! grid cell average of x^2*y - real(kind=RKIND), dimension(:), pointer :: xyy => null() ! grid cell average of x*y^2 - real(kind=RKIND), dimension(:), pointer :: yyy => null() ! grid cell average of y^3 - real(kind=RKIND), dimension(:), pointer :: xxxx => null() ! grid cell average of x^4 - real(kind=RKIND), dimension(:), pointer :: xxxy => null() ! grid cell average of x^3*y - real(kind=RKIND), dimension(:), pointer :: xxyy => null() ! grid cell average of x^2*y^2 - real(kind=RKIND), dimension(:), pointer :: xyyy => null() ! grid cell average of x*y^3 - real(kind=RKIND), dimension(:), pointer :: yyyy => null() ! grid cell average of y^4 + real(kind=RKIND), dimension(:), contiguous, pointer :: x => null() ! grid cell average of x + real(kind=RKIND), dimension(:), contiguous, pointer :: y => null() ! grid cell average of y + real(kind=RKIND), dimension(:), contiguous, pointer :: xx => null() ! grid cell average of x^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: xy => null() ! grid cell average of x*y + real(kind=RKIND), dimension(:), contiguous, pointer :: yy => null() ! grid cell average of y^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxx => null() ! grid cell average of x^3 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxy => null() ! grid cell average of x^2*y + real(kind=RKIND), dimension(:), contiguous, pointer :: xyy => null() ! grid cell average of x*y^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: yyy => null() ! grid cell average of y^3 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxxx => null() ! grid cell average of x^4 + real(kind=RKIND), dimension(:), contiguous, pointer :: xxxy => null() ! grid cell average of x^3*y + real(kind=RKIND), dimension(:), contiguous, pointer :: xxyy => null() ! grid cell average of x^2*y^2 + real(kind=RKIND), dimension(:), contiguous, pointer :: xyyy => null() ! grid cell average of x*y^3 + real(kind=RKIND), dimension(:), contiguous, pointer :: yyyy => null() ! grid cell average of y^4 end type geometric_avg_cell_type ! parameters private to this module @@ -2346,7 +2339,6 @@ subroutine seaice_run_advection_incremental_remap(& domain, & tracersHead, & clock, & - ierr, & timeLevelIn, & updateHaloInitIn, & updateHaloFinalIn) @@ -2365,9 +2357,6 @@ subroutine seaice_run_advection_incremental_remap(& type (MPAS_Clock_type), intent(in) :: & clock !< Input: clock - integer, intent(inout) :: & - ierr !< Input/Output: error code - integer, intent(in), optional :: & timeLevelIn !< Input: time level of input fields (1 or 2) @@ -2408,6 +2397,9 @@ subroutine seaice_run_advection_incremental_remap(& real(kind=RKIND), pointer :: & advectionTimeStep + logical, pointer :: & + abortFlag ! abort flag + ! assign pointers dminfo => domain % dminfo @@ -2457,6 +2449,7 @@ subroutine seaice_run_advection_incremental_remap(& ! open to improvement call MPAS_dmpar_field_halo_exch(domain, 'u' // trim(tracersHead % rootName) // 'Velocity') call MPAS_dmpar_field_halo_exch(domain, 'v' // trim(tracersHead % rootName) // 'Velocity') + call MPAS_pool_get_config(domain % configs, "config_use_halo_exch", config_use_halo_exch) if (config_use_halo_exch) then @@ -2476,8 +2469,8 @@ subroutine seaice_run_advection_incremental_remap(& else ! with reuse - call mpas_dmpar_exch_group_reuse_halo_exch(domain, 'velocityHaloExchangeGroup', iErr=ierr) - if (ierr /= MPAS_DMPAR_NOERR) then + call mpas_dmpar_exch_group_reuse_halo_exch(domain, 'velocityHaloExchangeGroup', iErr=ierrHalo) + if (ierrHalo /= MPAS_DMPAR_NOERR) then call MPAS_log_write("failure to perform reuse halo exchange for velocityHaloExchangeGroup", MPAS_LOG_CRIT) endif @@ -2574,15 +2567,12 @@ subroutine seaice_run_advection_incremental_remap(& domain, & block, & advectionTimeStep, & - tracersHead, & - ierr) - if (ierr > 0) exit + tracersHead) block => block % next enddo ! associated(block) call mpas_timer_stop("incr remap blocks") - if (ierr > 0) return ! Optional check for conservation of mass and mass*tracer ! Note: This check must be done outside the block loop because it requires global sums @@ -2593,9 +2583,9 @@ subroutine seaice_run_advection_incremental_remap(& if (verboseRun) call mpas_log_write('Check conservation') call mpas_timer_start("incr remap tracer cons check") - call check_tracer_conservation(dminfo, tracersHead, ierr) + call check_tracer_conservation(dminfo, tracersHead, abortFlag) call mpas_timer_stop("incr remap tracer cons check") - if (ierr > 0) return + call seaice_check_critical_error(domain, abortFlag) endif @@ -2609,9 +2599,9 @@ subroutine seaice_run_advection_incremental_remap(& if (verboseRun) call mpas_log_write('Check monotonicity') call mpas_timer_start("incr remap tracer mono check") - call check_tracer_monotonicity(domain, tracersHead, ierr) + call check_tracer_monotonicity(domain, tracersHead, abortFlag) call mpas_timer_stop("incr remap tracer mono check") - if (ierr > 0) return + call seaice_check_critical_error(domain, abortFlag) endif @@ -2758,7 +2748,6 @@ subroutine incremental_remap_block(& block, & dt, & tracersHead, & - ierr, & timeLevelIn) ! in/out arguments @@ -2771,9 +2760,6 @@ subroutine incremental_remap_block(& real(kind=RKIND), intent(in) :: & dt !< Input: time step - integer, intent(inout) :: & - ierr !< Input/Output: error code - type(tracer_type), intent(inout), pointer :: & tracersHead !< Input/output: pointer to first element of linked list of tracers ! The pointer stays attached to the first tracer, but all tracers are updated @@ -2896,6 +2882,9 @@ subroutine incremental_remap_block(& integer :: n, m, iEdge, iCat, iLayer, iCell integer :: nCategories, nLayers, count + logical :: & + abortFlag ! flag if code aborting + logical, pointer :: & configConservationCheck, & ! namelist configuration whether perform conservation check configMonotonicityCheck, & ! namelist configuration whether perform monotonicity check @@ -3183,6 +3172,7 @@ subroutine incremental_remap_block(& !------------------------------------------------------------------- call mpas_timer_start("incr remap integrate fluxes") + abortFlag = .false. call integrate_fluxes_over_triangles(& tracersHead, & nCells, & @@ -3195,12 +3185,10 @@ subroutine incremental_remap_block(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) call mpas_timer_stop("incr remap integrate fluxes") - if (ierr > 0) then - call seaice_critical_error_write_block(domain, block) - return - endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) if (verboseFluxes .and. etestOnProc .and. block % localBlockID == etestBlockID) then iEdge = etest @@ -3295,6 +3283,7 @@ subroutine incremental_remap_block(& !------------------------------------------------------------------- call mpas_timer_start("incr remap update tracers") + abortFlag = .false. call update_mass_and_tracers(& nCellsSolve, & nEdgesOnCell, & @@ -3305,12 +3294,10 @@ subroutine incremental_remap_block(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) call mpas_timer_stop("incr remap update tracers") - if (ierr > 0) then - call seaice_critical_error_write_block(domain, block) - return - endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) !------------------------------------------------------------------- ! Compute final sums of mass*tracer over the locally owned cells @@ -4279,7 +4266,7 @@ subroutine compute_gradient_2d(& mask !< Input: integer mask for parent tracer; ! = 1 where field values for this tracer are physically meaningful, else = 0 - real(kind=RKIND), dimension(:,:), intent(out) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(out) :: & xGrad, yGrad !< Output: x and y components of the gradient integer, dimension(:), intent(in) :: & @@ -4296,29 +4283,28 @@ subroutine compute_gradient_2d(& integer :: iCell, iEdge, iEdgeOnCell, iCellNeighbor real(kind=RKIND) :: & - signGradient ! = 1 or -1, depending on which direction is taken as positive at a given edge + signGradient, & ! = 1 or -1, depending on which direction is taken as positive at a given edge + tempGrad real(kind=RKIND), dimension(:,:), allocatable :: & normalGrad, & ! normal components of the gradient, defined on cell edges - globalGrad, & ! gradient at cell center, in global x/y/z coordinates - globalGradRotate ! rotated globalGrad vector + globalGrad ! gradient at cell center, in global x/y/z coordinates - real(kind=RKIND), dimension(:), allocatable :: & - zGrad ! diagnostic only; should be much smaller than xGrad and yGrad + !real(kind=RKIND), dimension(:), allocatable :: & + ! zGrad ! diagnostic only; should be much smaller than xGrad and yGrad ! find dimensions and allocate arrays nCategories = size(field,1) - allocate(zGrad(nCategories)) + !allocate(zGrad(nCategories)) allocate(normalGrad(nCategories,maxEdges)) allocate(globalGrad(nCategories,3)) - if (config_rotate_cartesian_grid) allocate(globalGradRotate(nCategories,3)) ! initialize the gradient xGrad(:,:) = 0.0_RKIND yGrad(:,:) = 0.0_RKIND - zGrad(:) = 0.0_RKIND ! diagnostic only + !zGrad(:) = 0.0_RKIND ! diagnostic only - ! loop over cells + !$omp parallel do default(shared) private(normalGrad,globalGrad,iEdgeOnCell,iCellNeighbor,iEdge,iCat,signGradient,tempGrad) do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present in the cell @@ -4350,19 +4336,10 @@ subroutine compute_gradient_2d(& normalGrad(iCat,iEdgeOnCell) = signGradient * (field(iCat,iCellNeighbor) - field(iCat,iCell)) / dcEdge(iEdge) - else ! either or both field values do not have physical meaning; set gradient component = 0 - - normalGrad(iCat,iEdgeOnCell) = 0.0_RKIND - endif enddo ! iCat - else ! there is no cell neighbor on this edge - - ! set gradient component = 0 - normalGrad(:,iEdgeOnCell) = 0.0_RKIND - endif ! add the contribution of this normal component to the reconstructed @@ -4380,10 +4357,11 @@ subroutine compute_gradient_2d(& !TODO - Rotate the gradient vector if on a plane? if (config_rotate_cartesian_grid .and. on_a_sphere) then - globalGradRotate(:,1) = -globalGrad(:,3) ! xR = -z - globalGradRotate(:,2) = globalGrad(:,2) ! yR = y - globalGradRotate(:,3) = globalGrad(:,1) ! zR = x - globalGrad(:,:) = globalGradRotate(:,:) + do iCat = 1, nCategories + tempGrad = globalGrad(iCat,1) + globalGrad(iCat,1) = -globalGrad(iCat,3) ! xR = -z + globalGrad(iCat,3) = tempGrad ! zR = x + enddo ! iCat endif ! transform from global x/y/z coordinates to local east/west coordinates @@ -4399,10 +4377,9 @@ subroutine compute_gradient_2d(& + transGlobalToCell(2,3,iCell) * globalGrad(:,3) ! Note: The zGrad component is never used; it is simply computed as a diagnostic - !TODO - Comment out zGrad computation? - zGrad(:) = transGlobalToCell(3,1,iCell) * globalGrad(:,1) & - + transGlobalToCell(3,2,iCell) * globalGrad(:,2) & - + transGlobalToCell(3,3,iCell) * globalGrad(:,3) + !zGrad(:) = transGlobalToCell(3,1,iCell) * globalGrad(:,1) & + ! + transGlobalToCell(3,2,iCell) * globalGrad(:,2) & + ! + transGlobalToCell(3,3,iCell) * globalGrad(:,3) else ! on a plane; do a simple copy @@ -4426,18 +4403,19 @@ subroutine compute_gradient_2d(& realArgs=(/field(iCatTest,iCellNeighbor), normalGrad(iCatTest,iEdgeOnCell)/)) enddo call mpas_log_write(' ') - call mpas_log_write('Unlimited center gradient: $r $r $r', & - realArgs=(/xGrad(iCatTest,iCell), yGrad(iCatTest,iCell), zGrad(iCatTest)/)) + call mpas_log_write('Unlimited center gradient: $r $r', & + realArgs=(/xGrad(iCatTest,iCell), yGrad(iCatTest,iCell)/)) + !call mpas_log_write('Unlimited center gradient: $r $r $r', & + ! realArgs=(/xGrad(iCatTest,iCell), yGrad(iCatTest,iCell), zGrad(iCatTest)/)) endif endif enddo ! iCell ! cleanup - deallocate(zGrad) + !deallocate(zGrad) deallocate(normalGrad) deallocate(globalGrad) - if (config_rotate_cartesian_grid) deallocate(globalGradRotate) end subroutine compute_gradient_2d @@ -4511,7 +4489,7 @@ subroutine compute_gradient_3d(& mask !< Input: integer mask for parent tracer; ! = 1 where field values for this tracer are physically meaningful, else = 0 - real(kind=RKIND), dimension(:,:,:), intent(out) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(out) :: & xGrad, yGrad !< Output: x and y components of the gradient integer, dimension(:), intent(in) :: & @@ -4529,29 +4507,27 @@ subroutine compute_gradient_3d(& real(kind=RKIND), dimension(:,:,:), allocatable :: & normalGrad, & ! normal components of the gradient, defined on cell edges - globalGrad, & ! gradient at cell center, in global x/y/z coordinates - globalGradRotate ! rotated globalGrad vector + globalGrad ! gradient at cell center, in global x/y/z coordinates real(kind=RKIND) :: & signGradient ! = 1 or -1, depending on which direction is taken as positive at a given edge - real(kind=RKIND), dimension(:,:), allocatable :: & - zGrad ! diagnostic only; should be much smaller than xGrad and yGrad + !real(kind=RKIND), dimension(:,:), allocatable :: & + ! zGrad ! diagnostic only; should be much smaller than xGrad and yGrad ! find dimensions and allocate arrays nLayers = size(field,1) nCategories = size(field,2) - allocate(zGrad(nLayers,nCategories)) + !allocate(zGrad(nLayers,nCategories)) allocate(normalGrad(nLayers,nCategories,maxEdges)) allocate(globalGrad(nLayers,nCategories,3)) - if (config_rotate_cartesian_grid) allocate(globalGradRotate(nLayers,nCategories,3)) ! initialize the gradient xGrad(:,:,:) = 0.0_RKIND yGrad(:,:,:) = 0.0_RKIND - zGrad(:,:) = 0.0_RKIND ! diagnostic only + !zGrad(:,:) = 0.0_RKIND ! diagnostic only - ! loop over cells + !$omp parallel do default(shared) private(normalGrad,globalGrad,iEdgeOnCell,iCellNeighbor,iEdge,iCat,iLayer,signGradient) do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present in the cell @@ -4586,20 +4562,11 @@ subroutine compute_gradient_3d(& normalGrad(iLayer,iCat,iEdgeOnCell) = & signGradient * (field(iLayer,iCat,iCellNeighbor) - field(iLayer,iCat,iCell)) / dcEdge(iEdge) - else ! either or both field values do not have physical meaning; set gradient component = 0 - - normalGrad(iLayer,iCat,iEdgeOnCell) = 0.0_RKIND - endif enddo ! iCat enddo ! iLayer - else ! there is no cell neighbor on this edge - - ! set gradient component = 0 - normalGrad(:,:,iEdgeOnCell) = 0.0_RKIND - endif ! add the contribution of this normal component to the reconstructed gradient @@ -4617,10 +4584,10 @@ subroutine compute_gradient_3d(& !TODO - Rotate the gradient vector if on a plane? if (config_rotate_cartesian_grid .and. on_a_sphere) then - globalGradRotate(:,:,1) = -globalGrad(:,:,3) ! xR = -z - globalGradRotate(:,:,2) = globalGrad(:,:,2) ! yR = y - globalGradRotate(:,:,3) = globalGrad(:,:,1) ! zR = x - globalGrad(:,:,:) = globalGradRotate(:,:,:) + ! reusing normalGrad as a temporary variable to rotate globalGrad + normalGrad(:,:,1) = globalGrad(:,:,1) ! temp + globalGrad(:,:,1) = -globalGrad(:,:,3) ! xR = -z + globalGrad(:,:,3) = normalGrad(:,:,1) ! zR = x endif ! transform from global x/y/z coordinates to local east/west coordinates @@ -4636,10 +4603,9 @@ subroutine compute_gradient_3d(& + transGlobalToCell(2,3,iCell) * globalGrad(:,:,3) ! Note: The zGrad component is never used; it is simply computed as a diagnostic - !TODO - Comment out zGrad computation? - zGrad(:,:) = transGlobalToCell(3,1,iCell) * globalGrad(:,:,1) & - + transGlobalToCell(3,2,iCell) * globalGrad(:,:,2) & - + transGlobalToCell(3,3,iCell) * globalGrad(:,:,3) + !zGrad(:,:) = transGlobalToCell(3,1,iCell) * globalGrad(:,:,1) & + ! + transGlobalToCell(3,2,iCell) * globalGrad(:,:,2) & + ! + transGlobalToCell(3,3,iCell) * globalGrad(:,:,3) else ! on a plane; do a simple copy @@ -4663,18 +4629,19 @@ subroutine compute_gradient_3d(& realArgs=(/field(iLayerTest,iCatTest,iCellNeighbor), normalGrad(iLayerTest,iCatTest,iEdgeOnCell)/)) enddo call mpas_log_write(' ') - call mpas_log_write('Unlimited center gradient: $r $r $r', & - realArgs=(/xGrad(iLayerTest,iCatTest,iCell), yGrad(iLayerTest,iCatTest,iCell), zGrad(iLayerTest,iCatTest)/)) + call mpas_log_write('Unlimited center gradient: $r $r', & + realArgs=(/xGrad(iLayerTest,iCatTest,iCell), yGrad(iLayerTest,iCatTest,iCell)/)) + !call mpas_log_write('Unlimited center gradient: $r $r $r', & + ! realArgs=(/xGrad(iLayerTest,iCatTest,iCell), yGrad(iLayerTest,iCatTest,iCell), zGrad(iLayerTest,iCatTest)/)) endif endif enddo ! iCell ! cleanup - deallocate(zGrad) + !deallocate(zGrad) deallocate(normalGrad) deallocate(globalGrad) - if (config_rotate_cartesian_grid) deallocate(globalGradRotate) end subroutine compute_gradient_3d @@ -4691,6 +4658,7 @@ end subroutine compute_gradient_3d ! !----------------------------------------------------------------------- +!DIR$ ATTRIBUTES FORCEINLINE :: compute_barycenter_coordinates subroutine compute_barycenter_coordinates(& geomAvgCell, & iCell, & @@ -4849,30 +4817,30 @@ subroutine limit_tracer_gradient_2d(& integer, intent(in) :: & nCells !< Input: number of cells - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & nEdgesOnCell !< Input: number of edges per cell - integer, dimension(:,:), intent(in) :: & + integer, dimension(:,:), contiguous, intent(in) :: & cellsOnCell !< Input: cell index for each edge neighbor of a given cell - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & field !< Input: 2d field for which we are limiting the gradient - integer, dimension(:,:), intent(in) :: & + integer, dimension(:,:), contiguous, intent(in) :: & fieldMask !< Input: mask = 1 where field value is physically meaningful, = 0 elsewhere - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & xVertexOnCell, & !< Input: x (east) coordinate of vertex relative to cell center in local tangent plane yVertexOnCell !< Input: y (north) coordinate of vertex relative to cell center in local tangent plane - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & maskCell !< Input: = 1 for cells with ice, else = 0 - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & xBarycenter, & !< Input: x (east) coordinate of barycenter of this tracer's parent yBarycenter !< Input: y (north) coordinate of barycenter of this tracer's parent - real(kind=RKIND), dimension(:,:), intent(inout) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(inout) :: & xGrad, & !< Input/output: x (east) component of gradient vector yGrad !< Input/output: y (north) coordinate of barycenter of this tracer's parent ! Gradient components are unlimited on input, limited on output @@ -4908,7 +4876,8 @@ subroutine limit_tracer_gradient_2d(& allocate(maxLocal(nCategories)) allocate(minLocal(nCategories)) - ! loop over cells + !$omp parallel do default(shared) private(maxNeighbor,minNeighbor,iEdgeOnCell,iCellNeighbor,& + !$omp& iCat,maxLocal,minLocal,iVertex,deviationAtVertex,gradFactor,gradFactor1,gradFactor2) do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present @@ -5045,35 +5014,35 @@ subroutine limit_tracer_gradient_3d(& integer, intent(in) :: & nCells !< Input: number of cells - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & nEdgesOnCell !< Input: number of edges per cell - integer, dimension(:,:), intent(in) :: & + integer, dimension(:,:), contiguous, intent(in) :: & cellsOnCell !< Input: cell index for each edge neighbor of a given cell - real(kind=RKIND), dimension(:,:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in) :: & field !< Input: 3d field for which we are limiting the gradient - integer, dimension(:,:,:), intent(in) :: & + integer, dimension(:,:,:), contiguous, intent(in) :: & fieldMask !< Input: mask = 1 where field value is physically meaningful, = 0 elsewhere - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & xVertexOnCell, & !< Input: x (east) coordinate of vertex relative to cell center in local tangent plane yVertexOnCell !< Input: y (north) coordinate of vertex relative to cell center in local tangent plane - integer, dimension(:), intent(in) :: & + integer, dimension(:), contiguous, intent(in) :: & maskCell !< Input: = 1 for cells with ice, else = 0 - real(kind=RKIND), dimension(:,:,:), intent(inout) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(inout) :: & xGrad, & !< Input/output: x (east) component of gradient vector yGrad !< Input/output: y (north) coordinate of barycenter of this tracer's parent ! Gradient components are unlimited on input, limited on output - real(kind=RKIND), dimension(:,:), intent(in), optional :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in), optional :: & xBarycenter2D, & !< Input: x (east) coordinate of barycenter of this tracer's 2Dparent yBarycenter2D !< Input: y (north) coordinate of barycenter of this tracer's 2D parent - real(kind=RKIND), dimension(:,:,:), intent(in), optional :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in), optional :: & xBarycenter3D, & !< Input: x (east) coordinate of barycenter of this tracer's 3D parent yBarycenter3D !< Input: y (north) coordinate of barycenter of this tracer's 3D parent @@ -5120,7 +5089,8 @@ subroutine limit_tracer_gradient_3d(& allocate(maxLocal(nLayers,nCategories)) allocate(minLocal(nLayers,nCategories)) - ! loop over cells + !$omp parallel do default(shared) private(maxNeighbor,minNeighbor,iEdgeOnCell,iCellNeighbor,& + !$omp& iCat,iLayer,maxLocal,minLocal,iVertex,deviationAtVertex,gradFactor,gradFactor1,gradFactor2) do iCell = 1, nCells if (maskCell(iCell) == 1) then ! ice is present @@ -6705,7 +6675,7 @@ subroutine integrate_fluxes_over_triangles(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) type(tracer_type), intent(inout), pointer :: & tracersHead !< Input/output: pointer to first element of linked list of tracers @@ -6738,8 +6708,8 @@ subroutine integrate_fluxes_over_triangles(& type(block_type), intent(in) :: & block !< Input: local block (diagnostic only) - integer, intent(inout) :: & - ierr !< Input: error code + logical, intent(inout) :: & + abortFlag !< Input: error code ! local variables @@ -6749,12 +6719,10 @@ subroutine integrate_fluxes_over_triangles(& dummyTracer ! dummy tracer with value of 1 everywhere integer :: & - iEdge, iCell, iCat, iLayer, iTri, iqp - - integer :: & nTriPerEdgeRemap, & ! number of triangles per edge nCategories, & ! number of ice thickness categories - nLayers ! number of layers + nLayers, & ! number of layers + iEdge, iCell, iCat, iLayer, iTri, iqp real(kind=RKIND), dimension(:), allocatable :: & tracerIntegral2D ! integral over a triangle of mass, mass*tracer, etc. @@ -6822,13 +6790,10 @@ subroutine integrate_fluxes_over_triangles(& else ! nParents = 1, 2 or 3 parentTracer => thisTracer % parent - - endif - - if (verboseFluxes) then - if (thisTracer % nParents > 0) then + if (verboseFluxes) then call mpas_log_write('Parent: '//trim(parentTracer % tracerName)) endif + endif ! Integrate the fluxes of this tracer (for each category and layer) over each triangle of each edge. @@ -6836,6 +6801,7 @@ subroutine integrate_fluxes_over_triangles(& if (thisTracer % ndims == 2) then + !$omp parallel do default(shared) private(iTri,iCell,tracerIntegral2D,iqp,iCat) do iEdge = 1, nEdges if (maskEdge(iEdge) == 1) then @@ -6843,86 +6809,42 @@ subroutine integrate_fluxes_over_triangles(& if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - ! In the following arrays: - ! 1st index of triangleValue2D = category; 2nd index = QP - ! 1st index of center/xGrad/yGrad = category - ! 1st index of xTriangle/yTriangle = QP - ! 1st index of tracerIntegral2D = category - ! 1st index of edgeFlux2D = category - ! identify the cell where the triangle is located iCell = iCellTriangle(iTri,iEdge) - ! evaluate the tracer at each quadrature point - do iqp = 1, nQuadPoints - thisTracer % triangleValue2D(:,iqp,iTri,iEdge) = & - thisTracer % center2D(:,iCell) & - + thisTracer % xGrad2D(:,iCell) * xTriangle(iqp,iTri,iEdge) & - + thisTracer % yGrad2D(:,iCell) * yTriangle(iqp,iTri,iEdge) - enddo - - ! evaluate the product mass*tracer at each quadrature point (using parent tracer info computed already) - ! In nParents = 0, this is mass - ! If nParents = 1, this is mass*tracer1 - ! If nParents = 2, this is mass*tracer1*tracer2 - ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 - ! Note: Parent tracer must have ndims = 2 - thisTracer % triangleValue2D(:,:,iTri,iEdge) = parentTracer % triangleValue2D(:,:,iTri,iEdge) & - * thisTracer % triangleValue2D(:,:,iTri,iEdge) - - ! integrate over the triangle by summing over quadrature points tracerIntegral2D(:) = 0.0_RKIND + ! evaluate the tracer at each quadrature point do iqp = 1, nQuadPoints - tracerIntegral2D(:) = tracerIntegral2D(:) & - + weightQuadPoint(iqp) * thisTracer % triangleValue2D(:,iqp,iTri,iEdge) - enddo - + do iCat = 1, nCategories + ! eval the product mass*tracer at each quadrature point (using parent tracer info computed already) + ! In nParents = 0, this is mass + ! If nParents = 1, this is mass*tracer1 + ! If nParents = 2, this is mass*tracer1*tracer2 + ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 + ! Note: Parent tracer must have ndims = 2 + thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) = & + parentTracer % triangleValue2D(iCat,iqp,iTri,iEdge) * & + ( thisTracer % center2D(iCat,iCell) & + + thisTracer % xGrad2D(iCat,iCell) * xTriangle(iqp,iTri,iEdge) & + + thisTracer % yGrad2D(iCat,iCell) * yTriangle(iqp,iTri,iEdge) & + ) + ! integrate over the triangle by summing over quadrature points + tracerIntegral2D(iCat) = tracerIntegral2D(iCat) & + + weightQuadPoint(iqp) * thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) + enddo ! iCat + enddo ! iqp ! increment the area-weighted flux across the edge thisTracer % edgeFlux2D(:,iEdge) = thisTracer % edgeFlux2D(:,iEdge) & + triangleArea(iTri,iEdge) * tracerIntegral2D(:) - endif ! triangleArea /= 0 - enddo ! nTriPerEdgeRemap - endif ! maskEdge = 1 enddo ! nEdges - - ! Check for negative reconstructed ice area - if (trim(thisTracer % tracerName) == 'iceAreaCategory') then - do iEdge = 1, nEdges - do iTri = 1, nTriPerEdgeRemap - if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - iCell = iCellTriangle(iTri,iEdge) - do iqp = 1, nQuadPoints - do iCat = 1, nCategories - if (thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) < 0.0_RKIND) then - call mpas_log_write('Negative reconstructed ice area', MPAS_LOG_ERR) - call mpas_log_write('nCells = $i', MPAS_LOG_ERR, intArgs=(/nCells/)) - call mpas_log_write('iCat, iCell, global iCell: $i $i $i', MPAS_LOG_ERR, & - intArgs=(/iCat, iCell, indexToCellID(iCell)/)) - call mpas_log_write('iEdge, global iEdge, iTri, iqp: $i $i $i $i', MPAS_LOG_ERR, & - intArgs=(/iEdge, indexToEdgeID(iEdge), iTri, iqp/)) - call mpas_log_write('triangle area: $r', MPAS_LOG_ERR, realArgs=(/triangleArea(iTri,iEdge)/)) - call mpas_log_write('tracer val: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge)/)) - call mpas_log_write('center val: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % center2D(iCat,iCell)/)) - call mpas_log_write('x gradient: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % xGrad2D(iCat,iCell)/)) - call mpas_log_write('y gradient: $r', MPAS_LOG_ERR, realArgs=(/thisTracer % yGrad2D(iCat,iCell)/)) - call mpas_log_write('IR negative reconstructed ice area (nDims == 2)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_NEG_AREA - return - endif ! negative area - enddo ! iCat - enddo ! iqp - endif ! triangleArea > 0 - enddo ! iTri - enddo ! iEdge - endif ! iceAreaCategory - deallocate(tracerIntegral2D) elseif (thisTracer % ndims == 3) then + !$omp parallel do default(shared) private(iTri,iCell,tracerIntegral3D,iqp,iCat,iLayer) do iEdge = 1, nEdges if (maskEdge(iEdge) == 1) then @@ -6930,106 +6852,104 @@ subroutine integrate_fluxes_over_triangles(& if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - ! In the following arrays: - ! 1st index of triangleValue3D = layer; 2nd index = category; 3rd index = QP - ! 1st index of center/xGrad/yGrad = layer; 2nd index = category - ! 1st index of xTriangle/yTriangle = QP - ! 1st index of tracerIntegral = layer; 2nd index = category - ! 1st index of edgeFlux = layer; 2nd index = category - ! identify the cell where the triangle is located iCell = iCellTriangle(iTri,iEdge) - ! evaluate the tracer at each quadrature point - do iqp = 1, nQuadPoints - thisTracer % triangleValue3D(:,:,iqp,iTri,iEdge) = & - thisTracer % center3D(:,:,iCell) & - + thisTracer % xGrad3D(:,:,iCell) * xTriangle(iqp,iTri,iEdge) & - + thisTracer % yGrad3D(:,:,iCell) * yTriangle(iqp,iTri,iEdge) - enddo - - ! evaluate the product mass*tracer at each quadrature point (using parent tracer info computed already) - ! In nParents = 0, this is mass - ! If nParents = 1, this is mass*tracer1 - ! If nParents = 2, this is mass*tracer1*tracer2 - ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 - ! Note: Parent tracer can have ndims = 2 or 3 - if (parentTracer % ndims == 2) then - do iLayer = 1, nLayers - thisTracer % triangleValue3D(iLayer,:,:,iTri,iEdge) = & - parentTracer % triangleValue2D(:,:,iTri,iEdge) * & - thisTracer % triangleValue3D(iLayer,:,:,iTri,iEdge) - enddo - else ! parents has ndims = 3 - thisTracer % triangleValue3D(:,:,:,iTri,iEdge) = parentTracer % triangleValue3D(:,:,:,iTri,iEdge) & - * thisTracer % triangleValue3D(:,:,:,iTri,iEdge) - endif - - ! integrate over the triangle by summing over quadrature points tracerIntegral3D(:,:) = 0.0_RKIND + ! evaluate the tracer at each quadrature point do iqp = 1, nQuadPoints - tracerIntegral3D(:,:) = tracerIntegral3D(:,:) & - + weightQuadPoint(iqp) * thisTracer % triangleValue3D(:,:,iqp,iTri,iEdge) - enddo - + do iCat = 1, nCategories + do iLayer = 1, nLayers + if (parentTracer % ndims == 2) then + ! eval the product mass*tracer at each quadrature point (using parent tracer info + ! computed already) + ! In nParents = 0, this is mass + ! If nParents = 1, this is mass*tracer1 + ! If nParents = 2, this is mass*tracer1*tracer2 + ! If nParents = 3, this is mass*tracer1*tracer2*tracer3 + ! Note: Parent tracer can have ndims = 2 or 3 + thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) = & + parentTracer % triangleValue2D(iCat,iqp,iTri,iEdge) * & + ( thisTracer % center3D(iLayer,iCat,iCell) & + + thisTracer % xGrad3D(iLayer,iCat,iCell) * xTriangle(iqp,iTri,iEdge) & + + thisTracer % yGrad3D(iLayer,iCat,iCell) * yTriangle(iqp,iTri,iEdge) & + ) + else ! parents has ndims = 3 + thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) = & + parentTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) * & + ( thisTracer % center3D(iLayer,iCat,iCell) & + + thisTracer % xGrad3D(iLayer,iCat,iCell) * xTriangle(iqp,iTri,iEdge) & + + thisTracer % yGrad3D(iLayer,iCat,iCell) * yTriangle(iqp,iTri,iEdge) & + ) + endif + ! integrate over the triangle by summing over quadrature points + tracerIntegral3D(iLayer,iCat) = tracerIntegral3D(iLayer,iCat) & + + weightQuadPoint(iqp) * thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) + enddo ! iLayer + enddo ! iCat + enddo ! iqp ! increment the area-weighted flux across the edge thisTracer % edgeFlux3D(:,:,iEdge) = thisTracer % edgeFlux3D(:,:,iEdge) & + triangleArea(iTri,iEdge) * tracerIntegral3D(:,:) - endif ! triangleArea /= 0 - enddo ! nTriPerEdgeRemap - endif ! maskEdge = 1 enddo ! nEdges + deallocate(tracerIntegral3D) - ! Check for negative reconstructed ice area - if (trim(thisTracer % tracerName) == 'iceAreaCategory') then - do iEdge = 1, nEdges - do iTri = 1, nTriPerEdgeRemap - if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then - iCell = iCellTriangle(iTri,iEdge) - do iqp = 1, nQuadPoints - do iCat = 1, nCategories + endif ! ndims + + ! Check for negative reconstructed ice area + if (trim(thisTracer % tracerName) == 'iceAreaCategory') then + do iEdge = 1, nEdges + do iTri = 1, nTriPerEdgeRemap + if (triangleArea(iTri,iEdge) /= 0.0_RKIND) then + iCell = iCellTriangle(iTri,iEdge) + do iqp = 1, nQuadPoints + do iCat = 1, nCategories + if (thisTracer % ndims == 2) then + if (thisTracer % triangleValue2D(iCat,iqp,iTri,iEdge) < 0.0_RKIND) then + call mpas_log_write('MPAS-seaice: IR negative reconstructed ice area', messageType=MPAS_LOG_ERR) + call mpas_log_write('nCells, iCat, iCell, global iCell: $i $i $i $i', messageType=MPAS_LOG_ERR, & + intArgs=(/nCells, iCat, iCell, indexToCellID(iCell)/)) + call mpas_log_write('iEdge, global iEdge, iTri, iqp: $i $i $i $i', messageType=MPAS_LOG_ERR, & + intArgs=(/iEdge, indexToEdgeID(iEdge), iTri, iqp/)) + call mpas_log_write('triangle area, tracer, center, xGrad2D, yGrad2D: $r $r $r $r $r', & + messageType=MPAS_LOG_CRIT, & ! abort + realArgs=(/triangleArea(iTri,iEdge), & + thisTracer%triangleValue2D(iCat,iqp,iTri,iEdge), & + thisTracer%center2D(iCat,iCell), & + thisTracer% xGrad2D(iCat,iCell), & + thisTracer% yGrad2D(iCat,iCell) /)) + endif ! negative 2D area + elseif (thisTracer % ndims == 3) then do iLayer = 1, nLayers if (thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge) < 0.0_RKIND) then - call mpas_log_write('Negative reconstructed ice area', messageType=MPAS_LOG_ERR) - call mpas_log_write('nCells = $i', messageType=MPAS_LOG_ERR, intArgs=(/nCells/)) - call mpas_log_write('iLayer, iCat, iCell, global iCell: $i $i $i $i', & + call mpas_log_write('MPAS-seaice: IR negative reconstructed ice area', messageType=MPAS_LOG_ERR) + call mpas_log_write('nCells, iLayer, iCat, iCell, global iCell: $i $i $i $i $i', & messageType=MPAS_LOG_ERR, & - intArgs=(/iLayer, iCat, iCell, indexToCellID(iCell)/)) + intArgs=(/nCells, iLayer, iCat, iCell, indexToCellID(iCell)/)) call mpas_log_write('iEdge, global iEdge, iTri, iqp: $i $i $i $i', & messageType=MPAS_LOG_ERR, & intArgs=(/iEdge, indexToEdgeID(iEdge), iTri, iqp/)) - call mpas_log_write('triangle area: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/triangleArea(iTri,iEdge)/)) - call mpas_log_write('tracer val: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % triangleValue3D(iLayer,iCat,iqp,iTri,iEdge)/)) - call mpas_log_write('center val: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % center3D(iLayer,iCat,iCell)/)) - call mpas_log_write('x gradient: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % xGrad3D(iLayer,iCat,iCell)/)) - call mpas_log_write('y gradient: $r', messageType=MPAS_LOG_ERR, & - realArgs=(/thisTracer % yGrad3D(iLayer,iCat,iCell)/)) - call mpas_log_write('MPAS-seaice: IR negative reconstructed ice area (nDims == 3)', & - messageType=MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_NEG_AREA - return - endif ! negative area + call mpas_log_write('triangle area, tracer, center, xGrad3D, yGrad3D: $r $r $r $r $r', & + messageType=MPAS_LOG_CRIT, & ! abort + realArgs=(/triangleArea(iTri,iEdge), & + thisTracer%triangleValue3D(iLayer,iCat,iqp,iTri,iEdge), & + thisTracer%center3D(iLayer,iCat,iCell), & + thisTracer% xGrad3D(iLayer,iCat,iCell), & + thisTracer% yGrad3D(iLayer,iCat,iCell) /)) + endif ! negative 3D area enddo ! iLayer - enddo ! iCat - enddo ! iqp - endif ! triangleArea > 0 - enddo ! iTri - enddo ! iEdge - endif ! iceAreaCategory - - deallocate(tracerIntegral3D) - - endif ! ndims + endif ! ndims + enddo ! iCat + enddo ! iqp + endif ! triangleArea > 0 + enddo ! iTri + enddo ! iEdge + endif ! iceAreaCategory ! clean up - if (associated(dummyTracer)) then if (dummyTracer % ndims == 2) then if (associated(dummyTracer % triangleValue2D)) deallocate(dummyTracer % triangleValue2D) @@ -7211,7 +7131,7 @@ subroutine update_mass_and_tracers(& indexToCellID, & indexToEdgeID, & block, & - ierr) + abortFlag) integer, intent(in) :: & nCellsSolve !< Input: number of locally owned cells to be updated @@ -7237,8 +7157,8 @@ subroutine update_mass_and_tracers(& type(block_type), intent(in) :: & block !< Input: local block (diagnostic only) - integer, intent(inout) :: & - ierr !< Input/Output: error code + logical, intent(inout) :: & + abortFlag !< Input/Output: error flag ! local variables @@ -7339,6 +7259,7 @@ subroutine update_mass_and_tracers(& allocate (fluxFromCell2D(nCategories)) + !$omp parallel do default(shared) private(fluxFromCell2D,iEdgeOnCell,iEdge,edgeSignOnCell,iCat) do iCell = 1, nCellsSolve fluxFromCell2D = 0.0_RKIND @@ -7420,6 +7341,7 @@ subroutine update_mass_and_tracers(& allocate (fluxFromCell3D(nLayers,nCategories)) + !$omp parallel do default(shared) private(fluxFromCell3D,iEdgeOnCell,iEdge,edgeSignOnCell,iCat,iLayer) do iCell = 1, nCellsSolve fluxFromCell3D = 0.0_RKIND @@ -7547,7 +7469,7 @@ subroutine update_mass_and_tracers(& messageType=MPAS_LOG_ERR, & intArgs=(/iCat, iCell, indexToCellID(iCell)/), & realArgs=(/thisTracer % array2D(iCat,iCell)/)) - ierr = SEAICE_ERROR_IR_NEG_MASS + abortFlag = .true. return endif enddo @@ -7563,7 +7485,7 @@ subroutine update_mass_and_tracers(& messageType=MPAS_LOG_ERR, & intArgs=(/iLayer, iCat, iCell, indexToCellID(iCell)/), & realArgs=(/thisTracer % array3D(iLayer,iCat,iCell)/)) - ierr = SEAICE_ERROR_IR_NEG_MASS + abortFlag = .true. return endif enddo @@ -8191,7 +8113,7 @@ end subroutine sum_tracers ! !----------------------------------------------------------------------- - subroutine check_tracer_conservation(dminfo, tracersHead, ierr) + subroutine check_tracer_conservation(dminfo, tracersHead, abortFlag) type (dm_info), intent(in) :: dminfo !< Input: domain info @@ -8199,8 +8121,8 @@ subroutine check_tracer_conservation(dminfo, tracersHead, ierr) tracersHead !< Input/output: pointer to first element of linked list of tracers ! The pointer stays attached to the first tracer, but tracer sums are updated - integer, intent(inout) :: & - ierr ! thisTracer % localMax2D(iCat,iCell) + toleranceMax) then @@ -8657,8 +8579,8 @@ subroutine check_tracer_monotonicity(domain, tracersHead, ierr) call mpas_log_write('Tolerance, difference: $r $r', MPAS_LOG_ERR, & realArgs=(/toleranceMax, thisTracer % array2D(iCat,iCell) - thisTracer % localMax2D(iCat,iCell)/)) call mpas_log_write('IR advection, monotonicity violation (max, nDims == 2)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_MONO - call seaice_critical_error_write_block(domain, block) + abortFlag = .true. + call seaice_critical_error_write_block(domain, block, abortFlag) return endif @@ -8772,8 +8694,8 @@ subroutine check_tracer_monotonicity(domain, tracersHead, ierr) call mpas_log_write('Tolerance, difference: $r $r', MPAS_LOG_ERR, & realArgs=(/toleranceMin, thisTracer % localMin3D(iLayer,iCat,iCell) - thisTracer % array3D(iLayer,iCat,iCell)/)) call mpas_log_write('IR advection, monotonicity violation (min, nDims == 3)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_MONO - call seaice_critical_error_write_block(domain, block) + abortFlag = .true. + call seaice_critical_error_write_block(domain, block, abortFlag) return elseif (thisTracer % array3D(iLayer,iCat,iCell) > & @@ -8791,8 +8713,8 @@ subroutine check_tracer_monotonicity(domain, tracersHead, ierr) call mpas_log_write('Tolerance, difference: $r $r', MPAS_LOG_ERR, & realArgs=(/toleranceMax, thisTracer % array3D(iLayer,iCat,iCell) - thisTracer % localMax3D(iLayer,iCat,iCell)/)) call mpas_log_write('IR advection, monotonicity violation (max, nDims == 3)', MPAS_LOG_ERR) - ierr = SEAICE_ERROR_IR_MONO - call seaice_critical_error_write_block(domain, block) + abortFlag = .true. + call seaice_critical_error_write_block(domain, block, abortFlag) return endif diff --git a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F index f0d5b5b6b1..356e693a29 100644 --- a/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F +++ b/src/core_seaice/shared/mpas_seaice_advection_incremental_remap_tracers.F @@ -1,10 +1,3 @@ -! Copyright (c) 2015, Los Alamos National Security, LLC (LANS) -! and the University Corporation for Atmospheric Research (UCAR). -! -! Unless noted otherwise source code is licensed under the BSD license. -! Additional copyright and license information can be found in the LICENSE file -! distributed with this code, or at http://mpas-dev.github.com/license.html -! !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ! ! mpas_seaice_advection_incremental_remap_tracers @@ -50,16 +43,16 @@ module seaice_advection_incremental_remap_tracers ! (nLayer1, nLayer2, nCategories, nCells) for 3D ! tracer arrays - real(kind=RKIND), dimension(:,:), pointer :: array2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: array3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: array2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: array3D => null() ! mass*tracer products ! = mass for mass field ! = mass*tracer1 for tracers with one parent ! = mass*tracer1*tracer2 for tracers with two parents ! = mass*tracer1*tracer2*tracer3 for tracers with three parents - real(kind=RKIND), dimension(:,:), pointer :: massTracerProduct2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: massTracerProduct3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: massTracerProduct2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: massTracerProduct3D => null() !TODO - Replace with one field, 2 time levels? ! global sums (over all cells) @@ -82,14 +75,14 @@ module seaice_advection_incremental_remap_tracers ! fluxes across cell edges ! (nCategories, nEdges) for 2D ! (nLayers, nCategories, nEdges) for 3D - real(kind=RKIND), dimension(:,:), pointer :: edgeFlux2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: edgeFlux3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: edgeFlux2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: edgeFlux3D => null() ! tracer values at each quadrature point of departure triangles ! (nCategories, nQuadPoints, nTriPerEdge, nEdges) for 2D ! (nLayers,nCategories, nQuadPoints, nTriPerEdge, nEdges) for 3D - real(kind=RKIND), dimension(:,:,:,:), pointer :: triangleValue2D => null() - real(kind=RKIND), dimension(:,:,:,:,:), pointer :: triangleValue3D => null() + real(kind=RKIND), dimension(:,:,:,:), contiguous, pointer :: triangleValue2D => null() + real(kind=RKIND), dimension(:,:,:,:,:), contiguous, pointer :: triangleValue3D => null() ! coordinates of barycenter associated with this tracer ! The term 'barycenter' refers to a center of mass or related quantity, as distinct from the geometric center. @@ -100,25 +93,25 @@ module seaice_advection_incremental_remap_tracers ! The barycenter for this tracer is the location where the child tracer value (array2D or array3D) is located. ! Only mass-type fields (nParents = 0) are located at the geometric cell center. - real(kind=RKIND), dimension(:,:), pointer :: xBarycenter2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: xBarycenter3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: xBarycenter2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: xBarycenter3D => null() - real(kind=RKIND), dimension(:,:), pointer :: yBarycenter2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: yBarycenter3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: yBarycenter2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: yBarycenter3D => null() ! quantities needed for linear reconstruction (value at cell center plus x and y gradients) ! Note: The center value is the value at the geometric cell center and generally is ! difference from the value at the barycenter. ! xGrad and yGrad are the gradient components defined at the cell center - real(kind=RKIND), dimension(:,:), pointer :: center2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: center3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: center2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: center3D => null() - real(kind=RKIND), dimension(:,:), pointer :: xGrad2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: xGrad3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: xGrad2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: xGrad3D => null() - real(kind=RKIND), dimension(:,:), pointer :: yGrad2D => null() - real(kind=RKIND), dimension(:,:,:), pointer :: yGrad3D => null() + real(kind=RKIND), dimension(:,:), contiguous, pointer :: yGrad2D => null() + real(kind=RKIND), dimension(:,:,:), contiguous, pointer :: yGrad3D => null() real(kind=RKIND), dimension(:), pointer :: uVelocity => null() real(kind=RKIND), dimension(:), pointer :: vVelocity => null() diff --git a/src/core_seaice/shared/mpas_seaice_berg_advection.F b/src/core_seaice/shared/mpas_seaice_berg_advection.F index 40d2dea2b0..86dfb72576 100644 --- a/src/core_seaice/shared/mpas_seaice_berg_advection.F +++ b/src/core_seaice/shared/mpas_seaice_berg_advection.F @@ -91,7 +91,7 @@ end subroutine seaice_init_berg_tracers ! !----------------------------------------------------------------------- - subroutine seaice_run_berg_advection(domain, clock, ierr) + subroutine seaice_run_berg_advection(domain, clock) use seaice_advection_incremental_remap, only: & seaice_run_advection_incremental_remap @@ -105,9 +105,6 @@ subroutine seaice_run_berg_advection(domain, clock, ierr) type(MPAS_Clock_type), intent(in) :: & clock !< Input: - integer, intent(inout) :: & - ierr !< Input/Output: - integer, pointer :: & nBergCategories @@ -120,7 +117,6 @@ subroutine seaice_run_berg_advection(domain, clock, ierr) call seaice_run_advection_incremental_remap(domain, & bergTracersHeadArray(iCategory) % tracerTypePtr, & clock, & - ierr, & updateHaloInitIn = .true., & updateHaloFinalIn = .true.) diff --git a/src/core_seaice/shared/mpas_seaice_berg_decay.F b/src/core_seaice/shared/mpas_seaice_berg_decay.F index 32ed9265ae..39e9f5c220 100644 --- a/src/core_seaice/shared/mpas_seaice_berg_decay.F +++ b/src/core_seaice/shared/mpas_seaice_berg_decay.F @@ -65,6 +65,26 @@ subroutine seaice_run_berg_decay(domain, clock)!{{{ logical, pointer :: & config_berg_allow_rollover ! flag to turn on / off berg rollover due to instability +!!! DC need iceberg velocity halo exchange before basal melting + + type(MPAS_pool_type), pointer :: & + bergVelocitySolverPool + + real(kind=RKIND), dimension(:,:), pointer :: & + uBergVelocity, & ! berg velocity + vBergVelocity + + call MPAS_pool_get_subpool(domain % blocklist % structs, "berg_velocity_solver", bergVelocitySolverPool) + + call MPAS_pool_get_array(bergVelocitySolverPool, "uBergVelocity", uBergVelocity) + call MPAS_pool_get_array(bergVelocitySolverPool, "vBergVelocity", vBergVelocity) + + ! halo exchange + call mpas_timer_start("Berg velocity solver halo") + call MPAS_dmpar_field_halo_exch(domain, 'uBergVelocity') + call MPAS_dmpar_field_halo_exch(domain, 'vBergVelocity') + call mpas_timer_stop("Berg velocity solver halo") + call MPAS_pool_get_config(domain % configs, "config_berg_allow_rollover", config_berg_allow_rollover) block => domain % blocklist diff --git a/src/core_seaice/shared/mpas_seaice_bergs.F b/src/core_seaice/shared/mpas_seaice_bergs.F index bfaa170948..f81d14ce7e 100644 --- a/src/core_seaice/shared/mpas_seaice_bergs.F +++ b/src/core_seaice/shared/mpas_seaice_bergs.F @@ -137,7 +137,7 @@ end subroutine seaice_run_berg_predynamics ! !----------------------------------------------------------------------- - subroutine seaice_run_berg_dynamics(domain, clock, ierr) + subroutine seaice_run_berg_dynamics(domain, clock) use seaice_berg_velocity_solver, only: & seaice_run_berg_velocity_solver @@ -151,9 +151,6 @@ subroutine seaice_run_berg_dynamics(domain, clock, ierr) type(MPAS_Clock_type), intent(in) :: & clock !< Input: - integer, intent(inout) :: & - ierr !< Input/Output: - logical, pointer :: & config_use_berg_velocity_solver, & ! indicates if berg velocity solver is used config_use_berg_advection, & @@ -184,7 +181,7 @@ subroutine seaice_run_berg_dynamics(domain, clock, ierr) ! advection by incremental remapping call mpas_timer_start("Berg advection") - if (config_use_berg_advection) call seaice_run_berg_advection(domain, clock, ierr) + if (config_use_berg_advection) call seaice_run_berg_advection(domain, clock) call mpas_timer_stop("Berg advection") else ! advection inside subcycling @@ -198,7 +195,7 @@ subroutine seaice_run_berg_dynamics(domain, clock, ierr) ! advection by incremental remapping call mpas_timer_start("Berg advection") - if (config_use_berg_advection) call seaice_run_berg_advection(domain, clock, ierr) + if (config_use_berg_advection) call seaice_run_berg_advection(domain, clock) call mpas_timer_stop("Berg advection") enddo ! iBergSubcycle diff --git a/src/core_seaice/shared/mpas_seaice_column.F b/src/core_seaice/shared/mpas_seaice_column.F index 064ae259c2..ac668249aa 100644 --- a/src/core_seaice/shared/mpas_seaice_column.F +++ b/src/core_seaice/shared/mpas_seaice_column.F @@ -60,12 +60,6 @@ module seaice_column ! maximum number of ancestor tracers integer :: nMaxAncestorTracers = 2 - ! category tracer array - real(kind=RKIND), dimension(:,:), allocatable :: tracerArrayCategory ! trcrn - - ! cell tracer array - real(kind=RKIND), dimension(:), allocatable :: tracerArrayCell ! trcr - ! index of the parent tracer integer, dimension(:), allocatable :: parentIndex ! trcr_depend @@ -176,6 +170,13 @@ module seaice_column type(ciceTracerObjectType), private :: ciceTracerObject + real(kind=RKIND), dimension(:,:), allocatable :: & + tracerArrayCategory +!$omp threadprivate(tracerArrayCategory) + + real(kind=RKIND), dimension(:), allocatable :: & + tracerArrayCell + ! warnings string kind integer, parameter :: strKINDWarnings = char_len_long @@ -436,7 +437,6 @@ subroutine seaice_init_column_shortwave(domain, clock) use ice_colpkg, only: & colpkg_init_orbit, & - colpkg_get_warnings, & colpkg_clear_warnings use seaice_constants, only: & @@ -506,13 +506,12 @@ subroutine seaice_init_column_shortwave(domain, clock) config_shortwave_type logical, pointer :: & - config_do_restart - - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings + config_do_restart, & + config_use_snicar call MPAS_pool_get_config(domain % configs, "config_shortwave_type", config_shortwave_type) call MPAS_pool_get_config(domain % configs, "config_do_restart", config_do_restart) + call MPAS_pool_get_config(domain % configs, "config_use_snicar", config_use_snicar) if (trim(config_shortwave_type) == "dEdd") then @@ -523,8 +522,7 @@ subroutine seaice_init_column_shortwave(domain, clock) call colpkg_init_orbit(& abortFlag, & abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + call column_write_warnings(abortFlag) if (abortFlag) then call mpas_log_write("colpkg_init_orbit: "//trim(abortMessage), messageType=MPAS_LOG_CRIT) @@ -834,14 +832,12 @@ end subroutine seaice_column_finalize ! !----------------------------------------------------------------------- - subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) + subroutine seaice_column_predynamics_time_integration(domain, clock) type(domain_type), intent(inout) :: domain type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - logical, pointer :: & config_use_column_package, & config_use_column_shortwave, & @@ -883,9 +879,8 @@ subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column vertical thermodynamics") if (config_use_column_vertical_thermodynamics) & - call column_vertical_thermodynamics(domain, clock, ierr) + call column_vertical_thermodynamics(domain, clock) call mpas_timer_stop("Column vertical thermodynamics") - if (config_use_column_vertical_thermodynamics .and. ierr > 0) return !----------------------------------------------------------------- ! Biogeochemistry @@ -893,9 +888,8 @@ subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column biogeochemistry") if (config_use_column_biogeochemistry) & - call column_biogeochemistry(domain, ierr) + call column_biogeochemistry(domain) call mpas_timer_stop("Column biogeochemistry") - if (config_use_column_biogeochemistry .and. ierr > 0) return !----------------------------------------------------------------- ! ITD thermodynamics @@ -903,9 +897,8 @@ subroutine seaice_column_predynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column ITD thermodynamics") if (config_use_column_itd_thermodynamics) & - call column_itd_thermodynamics(domain, clock, ierr) + call column_itd_thermodynamics(domain, clock) call mpas_timer_stop("Column ITD thermodynamics") - if (config_use_column_itd_thermodynamics .and. ierr > 0) return !----------------------------------------------------------------- ! Update the aggregated state variables @@ -940,14 +933,12 @@ end subroutine seaice_column_predynamics_time_integration ! !----------------------------------------------------------------------- - subroutine seaice_column_dynamics_time_integration(domain, clock, ierr) + subroutine seaice_column_dynamics_time_integration(domain, clock) type(domain_type), intent(inout) :: domain type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - logical, pointer :: & config_use_column_package, & config_use_column_ridging, & @@ -984,9 +975,8 @@ subroutine seaice_column_dynamics_time_integration(domain, clock, ierr) call mpas_timer_start("Column ridging") if (config_use_column_ridging) & - call column_ridging(domain, ierr) + call column_ridging(domain) call mpas_timer_stop("Column ridging") - if (config_use_column_ridging .and. ierr > 0) return !----------------------------------------------------------------- ! Update the aggregated state variables @@ -1061,12 +1051,11 @@ end subroutine seaice_column_postdynamics_time_integration ! !----------------------------------------------------------------------- - subroutine column_vertical_thermodynamics(domain, clock, ierr) + subroutine column_vertical_thermodynamics(domain, clock) use ice_colpkg, only: & colpkg_step_therm1, & - colpkg_clear_warnings, & - colpkg_get_warnings + colpkg_clear_warnings use seaice_constants, only: & seaicePuny @@ -1075,8 +1064,6 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -1261,9 +1248,6 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) real(kind=RKIND) :: & dayOfYear - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - ! day of year call get_day_of_year(clock, dayOfYear) @@ -1447,7 +1431,12 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) endif - ! loop over cells + ! code abort + abortFlag = .false. + abortMessage = "" + + !$omp parallel do default(shared) private(iCategory,iAerosol,northernHemisphereMask,& + !$omp& specificSnowAerosol,specificIceAerosol,abortMessage) reduction(.or.:abortFlag) do iCell = 1, nCellsSolve ! initial state values @@ -1489,9 +1478,6 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) northernHemisphereMask = .false. endif - abortFlag = .false. - abortMessage = "" - call colpkg_clear_warnings() call colpkg_step_therm1(& config_dt, & @@ -1617,16 +1603,12 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) dayOfYear, & abortFlag, & abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + call column_write_warnings(abortFlag) - ! code abort + ! cell-specific abort message if (abortFlag) then call mpas_log_write("column_vertical_thermodynamics: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_VERT_THERM - call seaice_critical_error_write_block(domain, block) - return endif ! aerosol @@ -1656,6 +1638,10 @@ subroutine column_vertical_thermodynamics(domain, clock, ierr) enddo ! iCell + ! error-checking + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + ! aerosols deallocate(specificSnowAerosol) deallocate(specificIceAerosol) @@ -1677,19 +1663,16 @@ end subroutine column_vertical_thermodynamics ! !----------------------------------------------------------------------- - subroutine column_itd_thermodynamics(domain, clock, ierr) + subroutine column_itd_thermodynamics(domain, clock) use ice_colpkg, only: & colpkg_step_therm2, & - colpkg_get_warnings, & colpkg_clear_warnings type(domain_type), intent(inout) :: domain type(MPAS_clock_type), intent(in) :: clock - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -1791,9 +1774,6 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) real(kind=RKIND) :: & dayOfYear - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - ! day of year call get_day_of_year(clock, dayOfYear) @@ -1876,6 +1856,12 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry + ! code abort + abortFlag = .false. + abortMessage = "" + + !$omp parallel do default(shared) private(iCategory,newlyFormedIceLogical,iBioTracers,iBioData,& + !$omp& oceanBioConcentrationsUsed,abortMessage) reduction(.or.:abortFlag) do iCell = 1, nCellsSolve ! newly formed ice @@ -1890,33 +1876,31 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) enddo ! iBioTracers ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) - - abortFlag = .false. - abortMessage = "" + call set_cice_tracer_array_category(block, ciceTracerObject,& + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_clear_warnings() call colpkg_step_therm2(& config_dt, & nCategories, & nAerosols, & - ciceTracerObject % nBioTracersLayer, & + ciceTracerObject % nBioTracersLayer, & !nltrcr, intent(in) nIcelayers, & nSnowLayers, & - categoryThicknessLimits(:), & + categoryThicknessLimits(:), & !hin_max, intent(inout), dimension(0:ncat) nBioLayers, & iceAreaCategory(1,:,iCell), & iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & iceAreaCategoryInitial(:,iCell), & iceVolumeCategoryInitial(:,iCell), & - ciceTracerObject % tracerArrayCategory, & !trcrn + tracerArrayCategory, & !trcrn, intent(inout) openWaterArea(iCell), & iceAreaCell(iCell), & - ciceTracerObject % parentIndex, & !trcr_depend - ciceTracerObject % firstAncestorMask, & !trcr_base - ciceTracerObject % ancestorNumber, & !n_trcr_strata - ciceTracerObject % ancestorIndices, & !nt_strata + ciceTracerObject % parentIndex, & !trcr_depend, intent(in) + ciceTracerObject % firstAncestorMask, & !trcr_base, intent(in) + ciceTracerObject % ancestorNumber, & !n_trcr_strata,intent(in) + ciceTracerObject % ancestorIndices, & !nt_strata, intent(in) seaFreezingTemperature(iCell), & seaSurfaceSalinity(iCell), & initialSalinityProfile(:,iCell), & @@ -1929,22 +1913,21 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) oceanFreshWaterFlux(iCell), & oceanSaltFlux(iCell), & oceanHeatFlux(iCell), & - config_update_ocean_fluxes, & - biologyGrid(:), & - verticalGrid(:), & - interfaceBiologyGrid(:), & + config_update_ocean_fluxes, & !update_ocn_f, intent(in) + biologyGrid(:), & !bgrid, intent(in) + verticalGrid(:), & !cgrid, intent(in) + interfaceBiologyGrid(:), & !igrid, intent(in) oceanAerosolFlux(:,iCell), & - newlyFormedIceLogical(:), & + newlyFormedIceLogical(:), & !first_ice, intent(inout) zSalinityFlux(iCell), & oceanBioFluxes(:,iCell), & - oceanBioConcentrationsUsed(:), & + oceanBioConcentrationsUsed(:), & !ocean_bio, intent(in) abortFlag, & abortMessage, & frazilGrowthDiagnostic(iCell), & freezeOnset(iCell), & dayOfYear) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + call column_write_warnings(abortFlag) ! update do iCategory = 1, nCategories @@ -1953,19 +1936,21 @@ subroutine column_itd_thermodynamics(domain, clock, ierr) enddo ! iCategory ! get category tracer array - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) - ! code abort + ! cell-specific abort message if (abortFlag) then call mpas_log_write("column_itd_thermodynamics: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_ITD_THERM - call seaice_critical_error_write_block(domain, block) - return endif enddo ! iCell + ! error checking + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + ! newly formed ice deallocate(newlyFormedIceLogical) deallocate(oceanBioConcentrationsUsed) @@ -2119,7 +2104,6 @@ subroutine column_radiation(domain, clock, lInitialization) use ice_colpkg, only: & colpkg_step_radiation, & - colpkg_get_warnings, & colpkg_clear_warnings use seaice_constants, only: & @@ -2141,7 +2125,8 @@ subroutine column_radiation(domain, clock, lInitialization) shortwave, & ponds, & aerosols, & - biogeochemistry + biogeochemistry, & + snicar ! configs real(kind=RKIND), pointer :: & @@ -2199,7 +2184,19 @@ subroutine column_radiation(domain, clock, lInitialization) albedoVisibleDiffuseCategory, & albedoIRDirectCategory, & albedoIRDiffuseCategory, & - snowFractionCategory + snowFractionCategory, & + iceAsymmetryParameterDirect, & + iceAsymmetryParameterDiffuse, & + iceSingleScatterAlbedoDirect, & + iceSingleScatterAlbedoDiffuse, & + iceMassExtinctionCrossSectionDirect, & + iceMassExtinctionCrossSectionDiffuse, & + aerosolAsymmetryParameter5band, & + aerosolMassExtinctionCrossSection5band, & + aerosolSingleScatterAlbedo5band, & + modalAsymmetryParameter5band, & + modalMassExtinctionCrossSection5band, & + modalSingleScatterAlbedo5band real(kind=RKIND), dimension(:,:,:), pointer :: & iceAreaCategory, & @@ -2219,7 +2216,9 @@ subroutine column_radiation(domain, clock, lInitialization) iceBodyAerosol, & brineFraction, & modalBCabsorptionParameter, & - bioTracerShortwave + bioTracerShortwave, & + modalBCabsorptionParameter5band + real(kind=RKIND), pointer :: & dayOfNextShortwaveCalculation ! needed for CESM like coupled simulations @@ -2256,9 +2255,6 @@ subroutine column_radiation(domain, clock, lInitialization) setGetPhysicsTracers, & setGetBGCTracers - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - ! day of year call get_day_of_year(clock, dayOfYear) @@ -2284,6 +2280,7 @@ subroutine column_radiation(domain, clock, lInitialization) call MPAS_pool_get_subpool(block % structs, "ponds", ponds) call MPAS_pool_get_subpool(block % structs, "aerosols", aerosols) call MPAS_pool_get_subpool(block % structs, "biogeochemistry", biogeochemistry) + call MPAS_pool_get_subpool(block % structs, "snicar", snicar) call MPAS_pool_get_config(block % configs, "config_dt", config_dt) @@ -2353,6 +2350,21 @@ subroutine column_radiation(domain, clock, lInitialization) call MPAS_pool_get_array(biogeochemistry, "verticalShortwaveGrid", verticalShortwaveGrid) call MPAS_pool_get_array(biogeochemistry, "verticalGrid", verticalGrid) + ! snicar 5-band snow IOPs + call MPAS_pool_get_array(snicar, "iceAsymmetryParameterDirect", iceAsymmetryParameterDirect) + call MPAS_pool_get_array(snicar, "iceAsymmetryParameterDiffuse", iceAsymmetryParameterDiffuse) + call MPAS_pool_get_array(snicar, "iceSingleScatterAlbedoDirect", iceSingleScatterAlbedoDirect) + call MPAS_pool_get_array(snicar, "iceSingleScatterAlbedoDiffuse", iceSingleScatterAlbedoDiffuse) + call MPAS_pool_get_array(snicar, "iceMassExtinctionCrossSectionDirect", iceMassExtinctionCrossSectionDirect) + call MPAS_pool_get_array(snicar, "iceMassExtinctionCrossSectionDiffuse", iceMassExtinctionCrossSectionDiffuse) + call MPAS_pool_get_array(snicar, "aerosolMassExtinctionCrossSection5band", aerosolMassExtinctionCrossSection5band) + call MPAS_pool_get_array(snicar, "aerosolSingleScatterAlbedo5band", aerosolSingleScatterAlbedo5band) + call MPAS_pool_get_array(snicar, "aerosolAsymmetryParameter5band", aerosolAsymmetryParameter5band) + call MPAS_pool_get_array(snicar, "modalMassExtinctionCrossSection5band", modalMassExtinctionCrossSection5band) + call MPAS_pool_get_array(snicar, "modalSingleScatterAlbedo5band", modalSingleScatterAlbedo5band) + call MPAS_pool_get_array(snicar, "modalAsymmetryParameter5band", modalAsymmetryParameter5band) + call MPAS_pool_get_array(snicar, "modalBCabsorptionParameter5band", modalBCabsorptionParameter5band) + ! calendar type call MPAS_pool_get_config(block % configs, "config_calendar_type", config_calendar_type) if (trim(config_calendar_type) == "gregorian") then @@ -2364,6 +2376,7 @@ subroutine column_radiation(domain, clock, lInitialization) ! aerosols array allocate(aerosolsArray(4*nAerosols,nCategories)) allocate(index_shortwaveAerosol(maxAerosolType)) + if (.not. config_use_column_biogeochemistry) then index_shortwaveAerosol(1:maxAerosolType) = 1 else @@ -2375,6 +2388,7 @@ subroutine column_radiation(domain, clock, lInitialization) setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry + !$omp parallel do default(shared) private(iCategory,iAerosol,aerosolsArray,lonCellColumn) do iCell = 1, nCellsSolve ! set aerosols array @@ -2393,7 +2407,8 @@ subroutine column_radiation(domain, clock, lInitialization) if (lonCellColumn > pii) lonCellColumn = lonCellColumn - 2.0_RKIND * pii ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_clear_warnings() call colpkg_step_radiation(& @@ -2411,9 +2426,9 @@ subroutine column_radiation(domain, clock, lInitialization) nzAerosols, & config_use_shortwave_bioabsorption, & ciceTracerObject % index_chlorophyllShortwave, & - index_shortwaveAerosol, & - verticalShortwaveGrid(:), & - verticalGrid(:), & + index_shortwaveAerosol, & ! nlt_zaero_sw, dimension(:), intent(in) + verticalShortwaveGrid(:), & ! swgrid, dimension (:), intent(in) + verticalGrid(:), & ! igrid, dimension (:), intent(in) brineFraction(1,:,iCell), & iceAreaCategory(1,:,iCell), & iceVolumeCategory(1,:,iCell), & @@ -2423,9 +2438,9 @@ subroutine column_radiation(domain, clock, lInitialization) pondArea(1,:,iCell), & pondDepth(1,:,iCell), & pondLidThickness(1,:,iCell), & - aerosolsArray, & + aerosolsArray, & ! aeron, dimension(:,:), intent(in) bioTracerShortwave(:,:,iCell), & - ciceTracerObject % tracerArrayCategory(:,:), & + tracerArrayCategory, & ! trcrn, dimension(:,:), intent(in) latCell(iCell), & lonCellColumn, & calendarType, & @@ -2433,13 +2448,13 @@ subroutine column_radiation(domain, clock, lInitialization) dayOfNextShortwaveCalculation, & dayOfYear, & secondsIntoDay, & - aerosolMassExtinctionCrossSection(:,:), & - aerosolSingleScatterAlbedo(:,:), & - aerosolAsymmetryParameter(:,:), & - modalMassExtinctionCrossSection(:,:), & - modalSingleScatterAlbedo(:,:), & - modalAsymmetryParameter(:,:), & - modalBCabsorptionParameter(:,:,:), & + aerosolMassExtinctionCrossSection(:,:), & ! kaer_tab, dimension(:,:), intent(in) + aerosolSingleScatterAlbedo(:,:), & ! waer_tab, dimension(:,:), intent(in) + aerosolAsymmetryParameter(:,:), & ! gaer_tab, dimension(:,:), intent(in) + modalMassExtinctionCrossSection(:,:), & ! kaer_bc_tab, dimension(:,:), intent(in) + modalSingleScatterAlbedo(:,:), & ! waer_bc_tab, dimension(:,:), intent(in) + modalAsymmetryParameter(:,:), & ! gaer_bc_tab, dimension(:,:), intent(in) + modalBCabsorptionParameter(:,:,:), & ! bcenh, dimension(:,:,:), intent(in) config_use_modal_aerosols, & shortwaveVisibleDirectDown(iCell), & shortwaveVisibleDiffuseDown(iCell), & @@ -2465,12 +2480,26 @@ subroutine column_radiation(domain, clock, lInitialization) pondSnowDepthDifference(:,iCell), & pondLidMeltFluxFraction(:,iCell), & .false., & - lInitialization) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + lInitialization, & + iceAsymmetryParameterDirect(:,:), & + iceAsymmetryParameterDiffuse(:,:), & + iceSingleScatterAlbedoDirect(:,:), & + iceSingleScatterAlbedoDiffuse(:,:), & + iceMassExtinctionCrossSectionDirect(:,:), & + iceMassExtinctionCrossSectionDiffuse(:,:), & + aerosolMassExtinctionCrossSection5band(:,:), & + aerosolSingleScatterAlbedo5band(:,:), & + aerosolAsymmetryParameter5band(:,:), & + modalMassExtinctionCrossSection5band(:,:), & + modalSingleScatterAlbedo5band(:,:), & + modalAsymmetryParameter5band(:,:), & + modalBCabsorptionParameter5band(:,:,:)) + + call column_write_warnings(.false.) ! set the category tracer array - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) enddo ! iCell @@ -2495,17 +2524,14 @@ end subroutine column_radiation ! !----------------------------------------------------------------------- - subroutine column_ridging(domain, ierr) + subroutine column_ridging(domain) use ice_colpkg, only: & colpkg_step_ridge, & - colpkg_get_warnings, & colpkg_clear_warnings type(domain_type), intent(inout) :: domain - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -2601,9 +2627,6 @@ subroutine column_ridging(domain, ierr) abortMessage, & abortLocation - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - block => domain % blocklist do while (associated(block)) @@ -2677,6 +2700,10 @@ subroutine column_ridging(domain, ierr) setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry + ! code abort + abortFlag = .false. + abortMessage = "" + do iCell = 1, nCellsSolve ! newly formed ice @@ -2685,10 +2712,8 @@ subroutine column_ridging(domain, ierr) enddo ! iCategory ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) - - abortFlag = .false. - abortMessage = "" + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_clear_warnings() call colpkg_step_ridge(& @@ -2698,11 +2723,11 @@ subroutine column_ridging(domain, ierr) nSnowLayers, & nBioLayers, & nCategories, & - categoryThicknessLimits, & + categoryThicknessLimits, & ! hin_max, dimension(0:ncat), intent(inout) ridgeConvergence(iCell), & ridgeShear(iCell), & iceAreaCategory(1,:,iCell), & - ciceTracerObject % tracerArrayCategory, & ! trcrn + tracerArrayCategory, & ! trcrn, dimension(:,:), intent(inout) iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & openWaterArea(iCell), & @@ -2735,8 +2760,7 @@ subroutine column_ridging(domain, ierr) oceanBioFluxes(:,iCell), & abortFlag, & abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + call column_write_warnings(abortFlag) ! update do iCategory = 1, nCategories @@ -2745,19 +2769,22 @@ subroutine column_ridging(domain, ierr) enddo ! iCategory ! get category tracer array - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) ! code abort - if (abortFlag) then - call mpas_log_write("column_ridging: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) - call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_RIDGING - call seaice_critical_error_write_block(domain, block) - return - endif + if (abortFlag) exit enddo ! iCell + ! code abort + if (abortFlag) then + call mpas_log_write("column_ridging: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) + call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) + endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + ! newly formed ice deallocate(newlyFormedIceLogical) @@ -2778,12 +2805,11 @@ end subroutine column_ridging ! !----------------------------------------------------------------------- - subroutine column_biogeochemistry(domain, ierr) + subroutine column_biogeochemistry(domain) use ice_colpkg, only: & colpkg_biogeochemistry, & colpkg_init_OceanConcArray, & - colpkg_get_warnings, & colpkg_clear_warnings use seaice_constants, only: & @@ -2791,8 +2817,6 @@ subroutine column_biogeochemistry(domain, ierr) type(domain_type), intent(inout) :: domain - integer, intent(inout) :: ierr - type(block_type), pointer :: block type(MPAS_pool_type), pointer :: & @@ -2955,9 +2979,6 @@ subroutine column_biogeochemistry(domain, ierr) abortMessage, & abortLocation - character(len=strKINDWarnings), dimension(:), allocatable :: & - warnings - block => domain % blocklist do while (associated(block)) @@ -2994,7 +3015,7 @@ subroutine column_biogeochemistry(domain, ierr) call MPAS_pool_get_dimension(mesh, "maxBCType", maxBCType) call MPAS_pool_get_dimension(mesh, "maxDustType", maxDustType) - call MPAS_pool_get_dimension(mesh, "indexToCellID", indexToCellID) + call MPAS_pool_get_array(mesh, "indexToCellID", indexToCellID) call MPAS_pool_get_config(block % configs, "config_dt", config_dt) call MPAS_pool_get_config(block % configs, "config_use_brine", config_use_brine) @@ -3088,6 +3109,10 @@ subroutine column_biogeochemistry(domain, ierr) setGetPhysicsTracers = .true. setGetBGCTracers = config_use_column_biogeochemistry + ! code abort + abortFlag = .false. + abortMessage = "" + do iCell = 1, nCellsSolve ! newly formed ice @@ -3146,7 +3171,8 @@ subroutine column_biogeochemistry(domain, ierr) abortFlag = .false. - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_clear_warnings() call colpkg_biogeochemistry(& @@ -3215,26 +3241,20 @@ subroutine column_biogeochemistry(domain, ierr) iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & openWaterArea(iCell), & - ciceTracerObject % tracerArrayCategory(:,:), & + tracerArrayCategory, & snowVolumeCategoryInitial(:,iCell), & config_use_skeletal_biochemistry, & maxAlgaeType, & nZBGCTracers, & abortFlag, & abortMessage) - call colpkg_get_warnings(warnings) - call column_write_warnings(warnings) + call column_write_warnings(abortFlag) ! code abort - if (abortFlag) then - call mpas_log_write("column_biogeochemistry: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) - call mpas_log_write("iCell: $i", messageType=MPAS_LOG_CRIT, intArgs=(/indexToCellID(iCell)/)) - ierr = SEAICE_ERROR_COL_BGC - call seaice_critical_error_write_block(domain, block) - return - endif + if (abortFlag) exit - call get_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) ! update totalSkeletalAlgae(iCell) = 0.0_RKIND @@ -3263,6 +3283,14 @@ subroutine column_biogeochemistry(domain, ierr) enddo ! iCell + ! code abort + if (abortFlag) then + call mpas_log_write("column_biogeochemistry: "//trim(abortMessage) , messageType=MPAS_LOG_ERR) + call mpas_log_write("iCell: $i", messageType=MPAS_LOG_ERR, intArgs=(/indexToCellID(iCell)/)) + endif + call seaice_critical_error_write_block(domain, block, abortFlag) + call seaice_check_critical_error(domain, abortFlag) + deallocate(newlyFormedIceLogical) deallocate(oceanBioConcentrationsUsed) @@ -3590,16 +3618,17 @@ subroutine seaice_column_aggregate(domain) do iCell = 1, nCellsSolve ! set the category tracer array - call set_cice_tracer_array_category(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, ciceTracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) call colpkg_aggregate(& nCategories, & iceAreaCategory(1,:,iCell), & - ciceTracerObject % tracerArrayCategory, & ! trcrn + tracerArrayCategory, & ! trcrn iceVolumeCategory(1,:,iCell), & snowVolumeCategory(1,:,iCell), & iceAreaCell(iCell), & - ciceTracerObject % tracerArrayCell, & ! trcr + tracerArrayCell, & ! trcr iceVolumeCell(iCell), & snowVolumeCell(iCell), & openWaterArea(iCell), & @@ -3610,7 +3639,8 @@ subroutine seaice_column_aggregate(domain) ciceTracerObject % ancestorIndices) ! nt_strata ! set the cell tracer array - call get_cice_tracer_array_cell(block, ciceTracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_cell(block, ciceTracerObject, & + tracerArrayCell, iCell, setGetPhysicsTracers, setGetBGCTracers) enddo ! iCell @@ -4581,12 +4611,6 @@ subroutine init_column_tracer_object(domain, tracerObject) ! get the number of CICE tracers in trcrn call init_column_tracer_object_tracer_number(domain, tracerObject) - ! allocate the category tracer array - allocate(tracerObject % tracerArrayCategory(tracerObject % nTracers,nCategories)) - - ! allocate the cell tracer array - allocate(tracerObject % tracerArrayCell(tracerObject % nTracers)) - ! allocate other arrays allocate(tracerObject % parentIndex(tracerObject % nTracers)) allocate(tracerObject % firstAncestorMask(tracerObject % nTracers, tracerObject % nBaseTracers)) @@ -4616,6 +4640,13 @@ subroutine init_column_tracer_object(domain, tracerObject) endif + ! allocate tracer arrays + !$omp parallel + allocate(tracerArrayCategory(tracerObject % nTracers, nCategories)) + !$omp end parallel + + allocate(tracerArrayCell(tracerObject % nTracers)) + end subroutine init_column_tracer_object !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -5284,14 +5315,17 @@ end subroutine init_column_tracer_object_ancestor_indices ! !----------------------------------------------------------------------- - subroutine set_cice_tracer_array_category(block, tracerObject, iCell, setPhysicsTracers, setBGCTracers) + subroutine set_cice_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell, setPhysicsTracers, setBGCTracers) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(inout) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -5301,11 +5335,11 @@ subroutine set_cice_tracer_array_category(block, tracerObject, iCell, setPhysics ! get physics tracers if (setPhysicsTracers) & - call set_cice_physics_tracer_array_category(block, tracerObject, iCell) + call set_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) ! get BGC tracers if (setBGCTracers) & - call set_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + call set_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) end subroutine set_cice_tracer_array_category @@ -5321,7 +5355,7 @@ end subroutine set_cice_tracer_array_category ! !----------------------------------------------------------------------- - subroutine get_cice_tracer_array_category(block, tracerObject, iCell, getPhysicsTracers, getBGCTracers) + subroutine get_cice_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell, getPhysicsTracers, getBGCTracers) type(block_type), intent(inout) :: & block @@ -5329,6 +5363,9 @@ subroutine get_cice_tracer_array_category(block, tracerObject, iCell, getPhysics type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(in) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -5338,11 +5375,11 @@ subroutine get_cice_tracer_array_category(block, tracerObject, iCell, getPhysics ! get physics tracers if (getPhysicsTracers) & - call get_cice_physics_tracer_array_category(block, tracerObject, iCell) + call get_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) ! get BGC tracers if (getBGCTracers) & - call get_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + call get_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) end subroutine get_cice_tracer_array_category @@ -5358,14 +5395,17 @@ end subroutine get_cice_tracer_array_category ! !----------------------------------------------------------------------- - subroutine set_cice_tracer_array_cell(block, tracerObject, iCell, setPhysicsTracers, setBGCTracers) + subroutine set_cice_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell, setPhysicsTracers, setBGCTracers) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(inout) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -5375,11 +5415,11 @@ subroutine set_cice_tracer_array_cell(block, tracerObject, iCell, setPhysicsTrac ! get physics tracers if (setPhysicsTracers) & - call set_cice_physics_tracer_array_cell(block, tracerObject, iCell) + call set_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) ! get BGC tracers if (setBGCTracers) & - call set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + call set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) end subroutine set_cice_tracer_array_cell @@ -5395,7 +5435,7 @@ end subroutine set_cice_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine get_cice_tracer_array_cell(block, tracerObject, iCell, getPhysicsTracers, getBGCTracers) + subroutine get_cice_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell, getPhysicsTracers, getBGCTracers) type(block_type), intent(inout) :: & block @@ -5403,6 +5443,9 @@ subroutine get_cice_tracer_array_cell(block, tracerObject, iCell, getPhysicsTrac type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(in) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -5412,11 +5455,11 @@ subroutine get_cice_tracer_array_cell(block, tracerObject, iCell, getPhysicsTrac ! get physics tracers if (getPhysicsTracers) & - call get_cice_physics_tracer_array_cell(block, tracerObject, iCell) + call get_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) ! get BGC tracers if (getBGCTracers) & - call get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + call get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) end subroutine get_cice_tracer_array_cell @@ -5432,13 +5475,13 @@ end subroutine get_cice_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) + subroutine set_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & - tracerObject + real(kind=RKIND), dimension(:,:), intent(inout) :: & + tracerArrayCategory integer, intent(in) :: & iCell @@ -5514,38 +5557,38 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) nTracers = 1 ! surfaceTemperature - tracerObject % tracerArrayCategory(nTracers,:) = surfaceTemperature(1,:,iCell) + tracerArrayCategory(nTracers,:) = surfaceTemperature(1,:,iCell) nTracers = nTracers + 1 ! iceEnthalpy - tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceEnthalpy(:,:,iCell) + tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceEnthalpy(:,:,iCell) nTracers = nTracers + nIceLayers ! snowEnthalpy - tracerObject % tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowEnthalpy(:,:,iCell) + tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) = snowEnthalpy(:,:,iCell) nTracers = nTracers + nSnowLayers ! ice Salinity - tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceSalinity(:,:,iCell) + tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) = iceSalinity(:,:,iCell) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - tracerObject % tracerArrayCategory(nTracers,:) = iceAge(1,:,iCell) + tracerArrayCategory(nTracers,:) = iceAge(1,:,iCell) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - tracerObject % tracerArrayCategory(nTracers,:) = firstYearIceArea(1,:,iCell) + tracerArrayCategory(nTracers,:) = firstYearIceArea(1,:,iCell) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - tracerObject % tracerArrayCategory(nTracers,:) = levelIceArea(1,:,iCell) + tracerArrayCategory(nTracers,:) = levelIceArea(1,:,iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCategory(nTracers,:) = levelIceVolume(1,:,iCell) + tracerArrayCategory(nTracers,:) = levelIceVolume(1,:,iCell) nTracers = nTracers + 1 endif @@ -5553,16 +5596,16 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCategory(nTracers,:) = pondArea(1,:,iCell) + tracerArrayCategory(nTracers,:) = pondArea(1,:,iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCategory(nTracers,:) = pondDepth(1,:,iCell) + tracerArrayCategory(nTracers,:) = pondDepth(1,:,iCell) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCategory(nTracers,:) = pondLidThickness(1,:,iCell) + tracerArrayCategory(nTracers,:) = pondLidThickness(1,:,iCell) nTracers = nTracers + 1 end if @@ -5570,10 +5613,10 @@ subroutine set_cice_physics_tracer_array_category(block, tracerObject, iCell) if (config_use_aerosols) then do iAerosol = 1, nAerosols - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) = snowScatteringAerosol(iAerosol,:,iCell) - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) = snowBodyAerosol(iAerosol,:,iCell) - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) = iceScatteringAerosol(iAerosol,:,iCell) - tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) = iceBodyAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) = snowScatteringAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) = snowBodyAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) = iceScatteringAerosol(iAerosol,:,iCell) + tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) = iceBodyAerosol(iAerosol,:,iCell) enddo ! iAerosol endif @@ -5592,13 +5635,13 @@ end subroutine set_cice_physics_tracer_array_category ! !----------------------------------------------------------------------- - subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) + subroutine get_cice_physics_tracer_array_category(block, tracerArrayCategory, iCell) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(in) :: & - tracerObject + real(kind=RKIND), dimension(:,:), intent(in) :: & + tracerArrayCategory integer, intent(in) :: & iCell @@ -5674,38 +5717,38 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) nTracers = 1 ! surfaceTemperature - surfaceTemperature(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + surfaceTemperature(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 ! iceEnthalpy - iceEnthalpy(:,:,iCell) = tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) + iceEnthalpy(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) nTracers = nTracers + nIceLayers ! snowEnthalpy - snowEnthalpy(:,:,iCell) = tracerObject % tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) + snowEnthalpy(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nSnowLayers-1,:) nTracers = nTracers + nSnowLayers ! ice Salinity - iceSalinity(:,:,iCell) = tracerObject % tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) + iceSalinity(:,:,iCell) = tracerArrayCategory(nTracers:nTracers+nIceLayers-1,:) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - iceAge(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + iceAge(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - firstYearIceArea(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + firstYearIceArea(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - levelIceArea(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + levelIceArea(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 - levelIceVolume(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + levelIceVolume(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif @@ -5713,16 +5756,16 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondArea(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + pondArea(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 - pondDepth(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + pondDepth(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondLidThickness(1,:,iCell) = tracerObject % tracerArrayCategory(nTracers,:) + pondLidThickness(1,:,iCell) = tracerArrayCategory(nTracers,:) nTracers = nTracers + 1 end if @@ -5730,10 +5773,10 @@ subroutine get_cice_physics_tracer_array_category(block, tracerObject, iCell) if (config_use_aerosols) then do iAerosol = 1, nAerosols - snowScatteringAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) - snowBodyAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) - iceScatteringAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) - iceBodyAerosol(iAerosol,:,iCell) = tracerObject % tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) + snowScatteringAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1) ,:) + snowBodyAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1)+1,:) + iceScatteringAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1)+2,:) + iceBodyAerosol(iAerosol,:,iCell) = tracerArrayCategory(nTracers+4*(iAerosol-1)+3,:) enddo ! iAerosol endif @@ -5752,13 +5795,13 @@ end subroutine get_cice_physics_tracer_array_category ! !----------------------------------------------------------------------- - subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) + subroutine set_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & - tracerObject + real(kind=RKIND), dimension(:), intent(inout) :: & + tracerArrayCell integer, intent(in) :: & iCell @@ -5836,38 +5879,38 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) nTracers = 1 ! surfaceTemperature - tracerObject % tracerArrayCell(nTracers) = surfaceTemperatureCell(iCell) + tracerArrayCell(nTracers) = surfaceTemperatureCell(iCell) nTracers = nTracers + 1 ! iceEnthalpy - tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceEnthalpyCell(:,iCell) + tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceEnthalpyCell(:,iCell) nTracers = nTracers + nIceLayers ! snowEnthalpy - tracerObject % tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowEnthalpyCell(:,iCell) + tracerArrayCell(nTracers:nTracers+nSnowLayers-1) = snowEnthalpyCell(:,iCell) nTracers = nTracers + nSnowLayers ! ice Salinity - tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceSalinityCell(:,iCell) + tracerArrayCell(nTracers:nTracers+nIceLayers-1) = iceSalinityCell(:,iCell) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - tracerObject % tracerArrayCell(nTracers) = iceAgeCell(iCell) + tracerArrayCell(nTracers) = iceAgeCell(iCell) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - tracerObject % tracerArrayCell(nTracers) = firstYearIceAreaCell(iCell) + tracerArrayCell(nTracers) = firstYearIceAreaCell(iCell) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - tracerObject % tracerArrayCell(nTracers) = levelIceAreaCell(iCell) + tracerArrayCell(nTracers) = levelIceAreaCell(iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCell(nTracers) = levelIceVolumeCell(iCell) + tracerArrayCell(nTracers) = levelIceVolumeCell(iCell) nTracers = nTracers + 1 endif @@ -5875,16 +5918,16 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCell(nTracers) = pondAreaCell(iCell) + tracerArrayCell(nTracers) = pondAreaCell(iCell) nTracers = nTracers + 1 - tracerObject % tracerArrayCell(nTracers) = pondDepthCell(iCell) + tracerArrayCell(nTracers) = pondDepthCell(iCell) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - tracerObject % tracerArrayCell(nTracers) = pondLidThicknessCell(iCell) + tracerArrayCell(nTracers) = pondLidThicknessCell(iCell) nTracers = nTracers + 1 end if @@ -5892,10 +5935,10 @@ subroutine set_cice_physics_tracer_array_cell(block, tracerObject, iCell) if (config_use_aerosols) then do iAerosol = 1, nAerosols - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1) ) = snowScatteringAerosolCell(iAerosol,iCell) - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+1) = snowBodyAerosolCell(iAerosol,iCell) - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+2) = iceScatteringAerosolCell(iAerosol,iCell) - tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+3) = iceBodyAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1) ) = snowScatteringAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1)+1) = snowBodyAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1)+2) = iceScatteringAerosolCell(iAerosol,iCell) + tracerArrayCell(nTracers+4*(iAerosol-1)+3) = iceBodyAerosolCell(iAerosol,iCell) enddo ! iAerosol endif @@ -5914,13 +5957,13 @@ end subroutine set_cice_physics_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) + subroutine get_cice_physics_tracer_array_cell(block, tracerArrayCell, iCell) type(block_type), intent(inout) :: & block - type(ciceTracerObjectType), intent(in) :: & - tracerObject + real(kind=RKIND), dimension(:), intent(in) :: & + tracerArrayCell integer, intent(in) :: & iCell @@ -5998,38 +6041,38 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) nTracers = 1 ! surfaceTemperature - surfaceTemperatureCell(iCell) = tracerObject % tracerArrayCell(nTracers) + surfaceTemperatureCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 ! iceEnthalpy - iceEnthalpyCell(:,iCell) = tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) + iceEnthalpyCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nIceLayers-1) nTracers = nTracers + nIceLayers ! snowEnthalpy - snowEnthalpyCell(:,iCell) = tracerObject % tracerArrayCell(nTracers:nTracers+nSnowLayers-1) + snowEnthalpyCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nSnowLayers-1) nTracers = nTracers + nSnowLayers ! ice Salinity - iceSalinityCell(:,iCell) = tracerObject % tracerArrayCell(nTracers:nTracers+nIceLayers-1) + iceSalinityCell(:,iCell) = tracerArrayCell(nTracers:nTracers+nIceLayers-1) nTracers = nTracers + nIceLayers ! iceAge if (config_use_ice_age) then - iceAgeCell(iCell) = tracerObject % tracerArrayCell(nTracers) + iceAgeCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif ! firstYearIceArea if (config_use_first_year_ice) then - firstYearIceAreaCell(iCell) = tracerObject % tracerArrayCell(nTracers) + firstYearIceAreaCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif ! level ice tracers if (config_use_level_ice) then - levelIceAreaCell(iCell) = tracerObject % tracerArrayCell(nTracers) + levelIceAreaCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 - levelIceVolumeCell(iCell) = tracerObject % tracerArrayCell(nTracers) + levelIceVolumeCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif @@ -6037,16 +6080,16 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) if (config_use_cesm_meltponds .or. & config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondAreaCell(iCell) = tracerObject % tracerArrayCell(nTracers) + pondAreaCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 - pondDepthCell(iCell) = tracerObject % tracerArrayCell(nTracers) + pondDepthCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 endif ! level or topo ponds if (config_use_level_meltponds .or. & config_use_topo_meltponds) then - pondLidThicknessCell(iCell) = tracerObject % tracerArrayCell(nTracers) + pondLidThicknessCell(iCell) = tracerArrayCell(nTracers) nTracers = nTracers + 1 end if @@ -6054,10 +6097,10 @@ subroutine get_cice_physics_tracer_array_cell(block, tracerObject, iCell) if (config_use_aerosols) then do iAerosol = 1, nAerosols - snowScatteringAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1) ) - snowBodyAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+1) - iceScatteringAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+2) - iceBodyAerosolCell(iAerosol,iCell) = tracerObject % tracerArrayCell(nTracers+4*(iAerosol-1)+3) + snowScatteringAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1) ) + snowBodyAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1)+1) + iceScatteringAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1)+2) + iceBodyAerosolCell(iAerosol,iCell) = tracerArrayCell(nTracers+4*(iAerosol-1)+3) enddo ! iAerosol endif @@ -6076,14 +6119,17 @@ end subroutine get_cice_physics_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(inout) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -6222,69 +6268,69 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! brine height fraction if (config_use_brine) & - tracerObject % tracerArrayCategory(tracerObject % index_brineFraction,:) = brineFraction(1,:,iCell) + tracerArrayCategory(tracerObject % index_brineFraction,:) = brineFraction(1,:,iCell) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) = & + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) = & skeletalAlgaeConc(iBioTracers,:,iCell) enddo ! nitrate if (config_use_nitrate) & - tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc,:) = skeletalNitrateConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_nitrateConc,:) = skeletalNitrateConc(1,:,iCell) ! DOC if (config_use_carbon) then do iBioTracers = 1, nDOC - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) = skeletalDOCConc(iBioTracers,:,iCell) + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) = skeletalDOCConc(iBioTracers,:,iCell) enddo ! DIC do iBioTracers = 1, nDIC - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) = skeletalDICConc(iBioTracers,:,iCell) + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) = skeletalDICConc(iBioTracers,:,iCell) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) = skeletalDONConc(iBioTracers,:,iCell) + tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) = skeletalDONConc(iBioTracers,:,iCell) enddo endif ! ammonium if (config_use_ammonium) & - tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc,:) = skeletalAmmoniumConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_ammoniumConc,:) = skeletalAmmoniumConc(1,:,iCell) ! silicate if (config_use_silicate) & - tracerObject % tracerArrayCategory(tracerObject % index_silicateConc,:) = skeletalSilicateConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_silicateConc,:) = skeletalSilicateConc(1,:,iCell) ! DMS, DMSPp, DMSPd if (config_use_DMS) then - tracerObject % tracerArrayCategory(tracerObject % index_DMSConc,:) = skeletalDMSConc(1,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc,:) = skeletalDMSPpConc(1,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc,:) = skeletalDMSPdConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_DMSConc,:) = skeletalDMSConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPpConc,:) = skeletalDMSPpConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPdConc,:) = skeletalDMSPdConc(1,:,iCell) endif ! nonreactive mobile tracer if (config_use_nonreactive) & - tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc,:) = skeletalNonreactiveConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_nonreactiveConc,:) = skeletalNonreactiveConc(1,:,iCell) ! humic material if (config_use_humics) & - tracerObject % tracerArrayCategory(tracerObject % index_humicsConc,:) = skeletalHumicsConc(1,:,iCell) + tracerArrayCategory(tracerObject % index_humicsConc,:) = skeletalHumicsConc(1,:,iCell) ! Particulate and dissovled Iron if (config_use_iron) then do iBioTracers = 1, nParticulateIron - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) = & + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) = & skeletalParticulateIronConc(iBioTracers,:,iCell) enddo do iBioTracers = 1, nDissolvedIron - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) = & + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) = & skeletalDissolvedIronConc(iBioTracers,:,iCell) enddo endif @@ -6293,7 +6339,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! Fraction of biogeochemical tracer in the mobile phase do iLayers = 1, tracerObject % nBioTracers - tracerObject % tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) = mobileFraction(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) = mobileFraction(iLayers,:,iCell) enddo ! algal nitrogen @@ -6302,7 +6348,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nAlgae do iLayers = 1,nBioLayersP3 iBiocount = iBiocount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) = & verticalAlgaeConc(iBioCount,:,iCell) enddo enddo @@ -6311,7 +6357,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! nitrate if (config_use_nitrate) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) = & verticalNitrateConc(iLayers,:,iCell) enddo endif @@ -6322,7 +6368,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDOC do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) = & verticalDOCConc(iBioCount,:,iCell) enddo enddo @@ -6332,7 +6378,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDIC do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) = & verticalDICConc(iBioCount,:,iCell) enddo enddo @@ -6344,7 +6390,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDON do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) = & verticalDONConc(iBioCount,:,iCell) enddo enddo @@ -6353,7 +6399,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! ammonium if (config_use_ammonium) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) = & verticalAmmoniumConc(iLayers,:,iCell) enddo endif @@ -6361,7 +6407,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! silicate if (config_use_silicate) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) = & verticalSilicateConc(iLayers,:,iCell) enddo endif @@ -6369,16 +6415,16 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) = verticalDMSConc(iLayers,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) = verticalDMSPpConc(iLayers,:,iCell) - tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) = verticalDMSPdConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) = verticalDMSConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) = verticalDMSPpConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) = verticalDMSPdConc(iLayers,:,iCell) enddo endif ! nonreactive purely mobile tracers if (config_use_nonreactive) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) = & verticalNonreactiveConc(iLayers,:,iCell) enddo endif @@ -6386,7 +6432,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP3 - tracerObject % tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) = verticalHumicsConc(iLayers,:,iCell) + tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) = verticalHumicsConc(iLayers,:,iCell) enddo endif @@ -6396,7 +6442,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nParticulateIron do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) = & verticalParticulateIronConc(iBioCount,:,iCell) enddo enddo @@ -6404,7 +6450,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nDissolvedIron do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) = & verticalDissolvedIronConc(iBioCount,:,iCell) enddo enddo @@ -6416,7 +6462,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iBioTracers = 1, nzAerosols do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) = & verticalAerosolsConc(iBioCount,:,iCell) enddo enddo @@ -6425,7 +6471,7 @@ subroutine set_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! salinity used with BL99 thermodynamics if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers - tracerObject % tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) = & + tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) = & verticalSalinity(iLayers,:,iCell) enddo endif @@ -6445,7 +6491,7 @@ end subroutine set_cice_biogeochemistry_tracer_array_category ! !----------------------------------------------------------------------- - subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, iCell) + subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, tracerArrayCategory, iCell) type(block_type), intent(inout) :: & block @@ -6453,6 +6499,9 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:,:), intent(in) :: & + tracerArrayCategory + integer, intent(in) :: & iCell @@ -6590,74 +6639,74 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! biogeochemistry ! brine height fraction if (config_use_brine) & - brineFraction(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_brineFraction,:) + brineFraction(1,:,iCell) = tracerArrayCategory(tracerObject % index_brineFraction,:) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae skeletalAlgaeConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers),:) enddo ! nitrate if (config_use_nitrate) & - skeletalNitrateConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc,:) + skeletalNitrateConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_nitrateConc,:) if (config_use_carbon) then ! DOC do iBioTracers = 1, nDOC skeletalDOCConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers),:) enddo ! DIC do iBioTracers = 1, nDIC skeletalDICConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers),:) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - skeletalDONConc(iBioTracers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) + skeletalDONConc(iBioTracers,:,iCell) = tracerArrayCategory(tracerObject % index_DONConc(iBioTracers),:) enddo endif ! ammonium if (config_use_ammonium) & - skeletalAmmoniumConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc,:) + skeletalAmmoniumConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_ammoniumConc,:) ! silicate if (config_use_silicate) & - skeletalSilicateConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_silicateConc,:) + skeletalSilicateConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_silicateConc,:) ! DNS, DMSPp, DMSPd if (config_use_DMS) then - skeletalDMSConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSConc,:) - skeletalDMSPpConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc,:) - skeletalDMSPdConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc,:) + skeletalDMSConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_DMSConc,:) + skeletalDMSPpConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPpConc,:) + skeletalDMSPdConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPdConc,:) endif ! nonreactive tracer if (config_use_nonreactive) & - skeletalNonreactiveConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc,:) + skeletalNonreactiveConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_nonreactiveConc,:) ! humic material if (config_use_humics) & - skeletalHumicsConc(1,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_humicsConc,:) + skeletalHumicsConc(1,:,iCell) = tracerArrayCategory(tracerObject % index_humicsConc,:) if (config_use_iron) then ! Particulate Iron do iBioTracers = 1, nParticulateIron skeletalParticulateIronConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers),:) enddo ! Dissolved Iron do iBioTracers = 1, nDissolvedIron skeletalDissolvedIronConc(iBioTracers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers),:) enddo endif @@ -6665,7 +6714,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i ! fraction of biogeochemical tracer in the mobile phase do iLayers = 1, tracerObject % nBioTracers - mobileFraction(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) + mobileFraction(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_mobileFraction+iLayers-1,:) enddo if (config_use_vertical_biochemistry) then @@ -6676,7 +6725,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBiocount = iBiocount + 1 verticalAlgaeConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_algaeConc(iBioTracers)+iLayers-1,:) enddo enddo endif @@ -6685,7 +6734,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_nitrate) then do iLayers = 1, nBioLayersP3 verticalNitrateConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) + tracerArrayCategory(tracerObject % index_nitrateConc + iLayers-1,:) enddo endif @@ -6697,7 +6746,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDOCConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) + tracerArrayCategory(tracerObject % index_DOCConc(iBioTracers) + iLayers-1,:) enddo enddo iBioCount = 0 @@ -6707,7 +6756,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDICConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) + tracerArrayCategory(tracerObject % index_DICConc(iBioTracers) + iLayers-1,:) enddo enddo endif @@ -6719,7 +6768,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDONConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) + tracerArrayCategory(tracerObject % index_DONConc(iBioTracers) + iLayers-1,:) enddo enddo endif @@ -6728,7 +6777,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_ammonium) then do iLayers = 1, nBioLayersP3 verticalAmmoniumConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) + tracerArrayCategory(tracerObject % index_ammoniumConc + iLayers-1,:) enddo endif @@ -6736,16 +6785,16 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_silicate) then do iLayers = 1, nBioLayersP3 verticalSilicateConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) + tracerArrayCategory(tracerObject % index_silicateConc+iLayers-1,:) enddo endif ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP3 - verticalDMSConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) - verticalDMSPpConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) - verticalDMSPdConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) + verticalDMSConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_DMSConc+iLayers-1,:) + verticalDMSPpConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPpConc+iLayers-1,:) + verticalDMSPdConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_DMSPdConc+iLayers-1,:) enddo endif @@ -6753,14 +6802,14 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_nonreactive) then do iLayers = 1, nBioLayersP3 verticalNonreactiveConc(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) + tracerArrayCategory(tracerObject % index_nonreactiveConc+iLayers-1,:) enddo endif ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP3 - verticalHumicsConc(iLayers,:,iCell) = tracerObject % tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) + verticalHumicsConc(iLayers,:,iCell) = tracerArrayCategory(tracerObject % index_humicsConc+iLayers-1,:) enddo endif @@ -6772,7 +6821,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalParticulateIronConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1,:) enddo enddo iBioCount = 0 @@ -6782,7 +6831,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDissolvedIronConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1,:) enddo enddo endif @@ -6794,7 +6843,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i do iLayers = 1,nBioLayersP3 iBioCount = iBioCount + 1 verticalAerosolsConc(iBioCount,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) + tracerArrayCategory(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1,:) enddo enddo endif @@ -6803,7 +6852,7 @@ subroutine get_cice_biogeochemistry_tracer_array_category(block, tracerObject, i if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers verticalSalinity(iLayers,:,iCell) = & - tracerObject % tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) + tracerArrayCategory(tracerObject % index_verticalSalinity+iLayers-1,:) enddo endif endif @@ -6822,14 +6871,17 @@ end subroutine get_cice_biogeochemistry_tracer_array_category ! !----------------------------------------------------------------------- - subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) type(block_type), intent(in) :: & block - type(ciceTracerObjectType), intent(inout) :: & + type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(inout) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -7002,72 +7054,72 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! biogeochemistry ! brine height fraction if (config_use_brine) & - tracerObject % tracerArrayCell(tracerObject % index_brineFraction) = brineFractionCell(iCell) + tracerArrayCell(tracerObject % index_brineFraction) = brineFractionCell(iCell) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) = skeletalAlgaeConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) = skeletalAlgaeConcCell(iBioTracers,iCell) enddo ! nitrate if (config_use_nitrate) & - tracerObject % tracerArrayCell(tracerObject % index_nitrateConc) = skeletalNitrateConcCell(1,iCell) + tracerArrayCell(tracerObject % index_nitrateConc) = skeletalNitrateConcCell(1,iCell) if (config_use_carbon) then ! DOC do iBioTracers = 1, nDOC - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) = skeletalDOCConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) = skeletalDOCConcCell(iBioTracers,iCell) enddo ! DIC do iBioTracers = 1, nDIC - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) = skeletalDICConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) = skeletalDICConcCell(iBioTracers,iCell) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) = skeletalDONConcCell(iBioTracers,iCell) + tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) = skeletalDONConcCell(iBioTracers,iCell) enddo endif ! ammonium if (config_use_ammonium) & - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc) = skeletalAmmoniumConcCell(1,iCell) + tracerArrayCell(tracerObject % index_ammoniumConc) = skeletalAmmoniumConcCell(1,iCell) ! silicate if (config_use_silicate) & - tracerObject % tracerArrayCell(tracerObject % index_silicateConc) = skeletalSilicateConcCell(1,iCell) + tracerArrayCell(tracerObject % index_silicateConc) = skeletalSilicateConcCell(1,iCell) ! DMS, DMSPp, DMSPd if (config_use_DMS) then - tracerObject % tracerArrayCell(tracerObject % index_DMSConc) = skeletalDMSConcCell(1,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc) = skeletalDMSPpConcCell(1,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc) = skeletalDMSPdConcCell(1,iCell) + tracerArrayCell(tracerObject % index_DMSConc) = skeletalDMSConcCell(1,iCell) + tracerArrayCell(tracerObject % index_DMSPpConc) = skeletalDMSPpConcCell(1,iCell) + tracerArrayCell(tracerObject % index_DMSPdConc) = skeletalDMSPdConcCell(1,iCell) endif ! nonreactive tracer if (config_use_nonreactive) & - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc) = skeletalNonreactiveConcCell(1,iCell) + tracerArrayCell(tracerObject % index_nonreactiveConc) = skeletalNonreactiveConcCell(1,iCell) ! humic material if (config_use_humics) & - tracerObject % tracerArrayCell(tracerObject % index_humicsConc) = skeletalHumicsConcCell(1,iCell) + tracerArrayCell(tracerObject % index_humicsConc) = skeletalHumicsConcCell(1,iCell) if (config_use_iron) then ! particulate iron do iBioTracers = 1, nParticulateIron - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) = & + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) = & skeletalParticulateIronConcCell(iBioTracers,iCell) enddo ! dissolved iron do iBioTracers = 1, nDissolvedIron - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) = & + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) = & skeletalDissolvedIronConcCell(iBioTracers,iCell) enddo endif @@ -7083,13 +7135,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBiocount = iBiocount + 1 - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & verticalAlgaeConcCell(iBioCount,iCell) verticalAlgaeIceCell(iLayers+iIceCount,iCell) = verticalAlgaeConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBiocount = iBiocount + 1 - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) = & verticalAlgaeConcCell(iBioCount,iCell) enddo enddo @@ -7098,11 +7150,11 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! nitrate if (config_use_nitrate) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) verticalNitrateIceCell(iLayers,iCell) = verticalNitrateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) = verticalNitrateConcCell(iLayers,iCell) enddo endif @@ -7115,13 +7167,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & verticalDOCConcCell(iBioCount,iCell) verticalDOCIceCell(iLayers+iIceCount,iCell) = verticalDOCConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) = & verticalDOCConcCell(iBioCount,iCell) enddo enddo @@ -7133,13 +7185,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & verticalDICConcCell(iBioCount,iCell) verticalDICIceCell(iLayers+iIceCount,iCell) = verticalDICConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) = & verticalDICConcCell(iBioCount,iCell) enddo enddo @@ -7153,13 +7205,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & verticalDONConcCell(iBioCount,iCell) verticalDONIceCell(iLayers+iIceCount,iCell) = verticalDONConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) = & verticalDONConcCell(iBioCount,iCell) enddo enddo @@ -7168,12 +7220,12 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! ammonium if (config_use_ammonium) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & verticalAmmoniumConcCell(iLayers,iCell) verticalAmmoniumIceCell(iLayers,iCell) = verticalAmmoniumConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) = & verticalAmmoniumConcCell(iLayers,iCell) enddo endif @@ -7181,40 +7233,40 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! silicate if (config_use_silicate) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) verticalSilicateIceCell(iLayers,iCell) = verticalSilicateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) = verticalSilicateConcCell(iLayers,iCell) enddo endif ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) verticalDMSIceCell(iLayers,iCell) = verticalDMSConcCell(iLayers,iCell) verticalDMSPpIceCell(iLayers,iCell) = verticalDMSPpConcCell(iLayers,iCell) verticalDMSPdIceCell(iLayers,iCell) = verticalDMSPdConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) - tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) = verticalDMSConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) = verticalDMSPpConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) = verticalDMSPdConcCell(iLayers,iCell) enddo endif ! nonreactive if (config_use_nonreactive) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & verticalNonreactiveConcCell(iLayers,iCell) verticalNonreactiveIceCell(iLayers,iCell) = verticalNonreactiveConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) = & verticalNonreactiveConcCell(iLayers,iCell) enddo endif @@ -7222,11 +7274,11 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP1 - tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) verticalHumicsIceCell(iLayers,iCell) = verticalHumicsConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) = verticalHumicsConcCell(iLayers,iCell) enddo endif @@ -7239,13 +7291,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & verticalParticulateIronConcCell(iBioCount,iCell) verticalParticulateIronIceCell(iLayers+iIceCount,iCell) = verticalParticulateIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) = & verticalParticulateIronConcCell(iBioCount,iCell) enddo enddo @@ -7257,13 +7309,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & verticalDissolvedIronConcCell(iBioCount,iCell) verticalDissolvedIronIceCell(iLayers+iIceCount,iCell) = verticalDissolvedIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) = & verticalDissolvedIronConcCell(iBioCount,iCell) enddo enddo @@ -7277,13 +7329,13 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & verticalAerosolsConcCell(iBioCount,iCell) verticalAerosolsIceCell(iLayers+iIceCount,iCell) = verticalAerosolsConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) = & verticalAerosolsConcCell(iBioCount,iCell) enddo enddo @@ -7292,7 +7344,7 @@ subroutine set_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! salinity for use with BL99 thermodynamics if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers - tracerObject % tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) = verticalSalinityCell(iLayers,iCell) + tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) = verticalSalinityCell(iLayers,iCell) enddo endif endif @@ -7311,7 +7363,7 @@ end subroutine set_cice_biogeochemistry_tracer_array_cell ! !----------------------------------------------------------------------- - subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell) + subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, tracerArrayCell, iCell) type(block_type), intent(inout) :: & block @@ -7319,6 +7371,9 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell type(ciceTracerObjectType), intent(in) :: & tracerObject + real(kind=RKIND), dimension(:), intent(in) :: & + tracerArrayCell + integer, intent(in) :: & iCell @@ -7489,73 +7544,73 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! biogeochemistry ! brine height fraction if (config_use_brine) & - brineFractionCell(iCell) = tracerObject % tracerArrayCell(tracerObject % index_brineFraction) + brineFractionCell(iCell) = tracerArrayCell(tracerObject % index_brineFraction) if (config_use_skeletal_biochemistry) then ! algal nitrogen do iBioTracers = 1, nAlgae - skeletalAlgaeConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) + skeletalAlgaeConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)) enddo ! nitrate if (config_use_nitrate) & - skeletalNitrateConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nitrateConc) + skeletalNitrateConcCell(1,iCell) = tracerArrayCell(tracerObject % index_nitrateConc) if (config_use_carbon) then ! DOC do iBioTracers = 1, nDOC - skeletalDOCConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) + skeletalDOCConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_DOCConc(iBioTracers)) enddo ! DIC do iBioTracers = 1, nDIC - skeletalDICConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) + skeletalDICConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_DICConc(iBioTracers)) enddo endif ! DON if (config_use_DON) then do iBioTracers = 1, nDON - skeletalDONConcCell(iBioTracers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) + skeletalDONConcCell(iBioTracers,iCell) = tracerArrayCell(tracerObject % index_DONConc(iBioTracers)) enddo endif ! ammonium if (config_use_ammonium) & - skeletalAmmoniumConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc) + skeletalAmmoniumConcCell(1,iCell) = tracerArrayCell(tracerObject % index_ammoniumConc) ! silicate if (config_use_silicate) & - skeletalSilicateConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_silicateConc) + skeletalSilicateConcCell(1,iCell) = tracerArrayCell(tracerObject % index_silicateConc) ! DMS if (config_use_DMS) then - skeletalDMSConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSConc) - skeletalDMSPpConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc) - skeletalDMSPdConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc) + skeletalDMSConcCell(1,iCell) = tracerArrayCell(tracerObject % index_DMSConc) + skeletalDMSPpConcCell(1,iCell) = tracerArrayCell(tracerObject % index_DMSPpConc) + skeletalDMSPdConcCell(1,iCell) = tracerArrayCell(tracerObject % index_DMSPdConc) endif ! nonreactive tracer if (config_use_nonreactive) & - skeletalNonreactiveConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc) + skeletalNonreactiveConcCell(1,iCell) = tracerArrayCell(tracerObject % index_nonreactiveConc) ! humic material if (config_use_humics) & - skeletalHumicsConcCell(1,iCell) = tracerObject % tracerArrayCell(tracerObject % index_humicsConc) + skeletalHumicsConcCell(1,iCell) = tracerArrayCell(tracerObject % index_humicsConc) if (config_use_iron) then ! particulate iron do iBioTracers = 1, nParticulateIron skeletalParticulateIronConcCell(iBioTracers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)) enddo ! dissolved iron do iBioTracers = 1, nDissolvedIron skeletalDissolvedIronConcCell(iBioTracers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)) enddo endif @@ -7571,13 +7626,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBiocount = iBiocount + 1 verticalAlgaeConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) verticalAlgaeIceCell(iLayers+iIceCount,iCell) = verticalAlgaeConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBiocount = iBiocount + 1 verticalAlgaeConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_algaeConc(iBioTracers)+iLayers-1) enddo enddo endif @@ -7585,11 +7640,11 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! nitrate if (config_use_nitrate) then do iLayers = 1, nBioLayersP1 - verticalNitrateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) + verticalNitrateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) verticalNitrateIceCell(iLayers,iCell) = verticalNitrateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalNitrateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) + verticalNitrateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_nitrateConc + iLayers-1) enddo endif @@ -7603,13 +7658,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDOCConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) verticalDOCIceCell(iLayers+iIceCount,iCell) = verticalDOCConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDOCConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DOCConc(iBioTracers) + iLayers-1) enddo enddo iBioCount = 0 @@ -7621,13 +7676,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDICConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) verticalDICIceCell(iLayers+iIceCount,iCell) = verticalDICConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDICConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DICConc(iBioTracers) + iLayers-1) enddo enddo endif @@ -7641,13 +7696,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDONConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) verticalDONIceCell(iLayers+iIceCount,iCell) = verticalDONConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDONConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) + tracerArrayCell(tracerObject % index_DONConc(iBioTracers) + iLayers-1) enddo enddo endif @@ -7656,40 +7711,40 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell if (config_use_ammonium) then do iLayers = 1, nBioLayersP1 verticalAmmoniumConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) verticalAmmoniumIceCell(iLayers,iCell) = verticalAmmoniumConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 verticalAmmoniumConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) + tracerArrayCell(tracerObject % index_ammoniumConc + iLayers-1) enddo endif ! silicate if (config_use_silicate) then do iLayers = 1, nBioLayersP1 - verticalSilicateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) + verticalSilicateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) verticalSilicateIceCell(iLayers,iCell) = verticalSilicateConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalSilicateConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) + verticalSilicateConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_silicateConc+iLayers-1) enddo endif ! DMS, DMSPp, DMSPd if (config_use_DMS) then do iLayers = 1, nBioLayersP1 - verticalDMSConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) - verticalDMSPpConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) - verticalDMSPdConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) + verticalDMSConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) + verticalDMSPpConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) + verticalDMSPdConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) verticalDMSIceCell(iLayers,iCell) = verticalDMSConcCell(iLayers,iCell) verticalDMSPpIceCell(iLayers,iCell) = verticalDMSPpConcCell(iLayers,iCell) verticalDMSPdIceCell(iLayers,iCell) = verticalDMSPdConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalDMSConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) - verticalDMSPpConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) - verticalDMSPdConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) + verticalDMSConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSConc+iLayers-1) + verticalDMSPpConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPpConc+iLayers-1) + verticalDMSPdConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_DMSPdConc+iLayers-1) enddo endif @@ -7697,23 +7752,23 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell if (config_use_nonreactive) then do iLayers = 1, nBioLayersP1 verticalNonreactiveConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) verticalNonreactiveIceCell(iLayers,iCell) = verticalNonreactiveConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 verticalNonreactiveConcCell(iLayers,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) + tracerArrayCell(tracerObject % index_nonreactiveConc+iLayers-1) enddo endif ! humic material if (config_use_humics) then do iLayers = 1, nBioLayersP1 - verticalHumicsConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) + verticalHumicsConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) verticalHumicsIceCell(iLayers,iCell) = verticalHumicsConcCell(iLayers,iCell) enddo do iLayers = nBioLayersP1+1, nBioLayersP3 - verticalHumicsConcCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) + verticalHumicsConcCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_humicsConc+iLayers-1) enddo endif @@ -7727,13 +7782,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalParticulateIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) verticalDissolvedIronIceCell(iLayers+iIceCount,iCell) = verticalDissolvedIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalParticulateIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_particulateIronConc(iBioTracers)+iLayers-1) enddo enddo iBioCount = 0 @@ -7745,13 +7800,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalDissolvedIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) verticalDissolvedIronIceCell(iLayers+iIceCount,iCell) = verticalDissolvedIronConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalDissolvedIronConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_dissolvedIronConc(iBioTracers)+iLayers-1) enddo enddo endif @@ -7765,13 +7820,13 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell do iLayers = 1,nBioLayersP1 iBioCount = iBioCount + 1 verticalAerosolsConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) verticalAerosolsIceCell(iLayers+iIceCount,iCell) = verticalAerosolsConcCell(iBioCount,iCell) enddo do iLayers = nBioLayersP1+1,nBioLayersP3 iBioCount = iBioCount + 1 verticalAerosolsConcCell(iBioCount,iCell) = & - tracerObject % tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) + tracerArrayCell(tracerObject % index_verticalAerosolsConc(iBioTracers)+iLayers-1) enddo enddo endif @@ -7779,7 +7834,7 @@ subroutine get_cice_biogeochemistry_tracer_array_cell(block, tracerObject, iCell ! salinity for use with BL99 thermodynamics if (config_use_vertical_zsalinity) then do iLayers = 1, nBioLayers - verticalSalinityCell(iLayers,iCell) = tracerObject % tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) + verticalSalinityCell(iLayers,iCell) = tracerArrayCell(tracerObject % index_verticalSalinity+iLayers-1) enddo endif endif @@ -8812,7 +8867,8 @@ subroutine init_column_package_configs(domain) config_use_shortwave_bioabsorption, & config_use_skeletal_biochemistry, & config_use_vertical_zsalinity, & - config_use_modal_aerosols + config_use_modal_aerosols, & + config_use_snicar real(kind=RKIND), pointer :: & config_min_friction_velocity, & @@ -8980,6 +9036,7 @@ subroutine init_column_package_configs(domain) call MPAS_pool_get_config(domain % configs, "config_slow_mode_critical_porosity", config_slow_mode_critical_porosity) call MPAS_pool_get_config(domain % configs, "config_congelation_ice_porosity", config_congelation_ice_porosity) call MPAS_pool_get_config(domain % configs, "config_shortwave_type", config_shortwave_type) + call MPAS_pool_get_config(domain % configs, "config_use_snicar", config_use_snicar) call MPAS_pool_get_config(domain % configs, "config_albedo_type", config_albedo_type) call MPAS_pool_get_config(domain % configs, "config_visible_ice_albedo", config_visible_ice_albedo) call MPAS_pool_get_config(domain % configs, "config_infrared_ice_albedo", config_infrared_ice_albedo) @@ -9171,6 +9228,7 @@ subroutine init_column_package_configs(domain) config_slow_mode_critical_porosity, & config_congelation_ice_porosity, & config_shortwave_type, & + config_use_snicar, & config_albedo_type, & config_visible_ice_albedo, & config_infrared_ice_albedo, & @@ -11905,7 +11963,8 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) endif ! set the category tracer array - call set_cice_tracer_array_category(block, tracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call set_cice_tracer_array_category(block, tracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) if (config_use_vertical_zsalinity) then call colpkg_init_zsalinity(& @@ -11914,7 +11973,7 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) config_do_restart_zsalinity, & rayleighCriteria, & rayleighCriteriaReal(iCell), & - tracerObject % tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & + tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & tracerObject % index_verticalSalinity, & nCategories, & seaSurfaceSalinity(iCell)) @@ -11933,7 +11992,7 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) tracerObject % nTracers, & tracerObject % nBiotracers, & iceSalinity(:,:,iCell), & - tracerObject % tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & + tracerArrayCategory(tracerObject % nTracersNotBio+1:tracerObject % nTracers,:), & seaSurfaceSalinity(iCell), & oceanNitrateConc(iCell), & oceanAmmoniumConc(iCell), & @@ -11967,7 +12026,8 @@ subroutine init_column_biogeochemistry_profiles(domain, tracerObject) endif ! biogeochemistry ! get the category tracer array - call get_cice_tracer_array_category(block, tracerObject, iCell, setGetPhysicsTracers, setGetBGCTracers) + call get_cice_tracer_array_category(block, tracerObject, & + tracerArrayCategory, iCell, setGetPhysicsTracers, setGetBGCTracers) enddo ! iCell @@ -13269,19 +13329,30 @@ end subroutine column_combine_snow_ice_tracers ! Warning messages !----------------------------------------------------------------------- - subroutine column_write_warnings(warnings) + subroutine column_write_warnings(logAsErrors) + + use ice_colpkg, only: colpkg_get_warnings - character(len=strKINDWarnings), dimension(:), intent(in) :: & + character(len=strKINDWarnings), dimension(:), allocatable :: & warnings + logical, intent(in) :: & + logAsErrors + integer :: & iWarning - do iWarning = 1, size(warnings) - - call mpas_log_write(trim(warnings(iWarning)), messageType=MPAS_LOG_WARN) + call colpkg_get_warnings(warnings) - enddo ! iWarning + if (logAsErrors) then + do iWarning = 1, size(warnings) + call mpas_log_write(trim(warnings(iWarning)), messageType=MPAS_LOG_ERR) + enddo ! iWarning + else + do iWarning = 1, size(warnings) + call mpas_log_write(trim(warnings(iWarning)), messageType=MPAS_LOG_WARN) + enddo ! iWarning + endif end subroutine column_write_warnings diff --git a/src/core_seaice/shared/mpas_seaice_error.F b/src/core_seaice/shared/mpas_seaice_error.F index ea61e7ed3b..1a21bfde52 100644 --- a/src/core_seaice/shared/mpas_seaice_error.F +++ b/src/core_seaice/shared/mpas_seaice_error.F @@ -22,20 +22,6 @@ module seaice_error private save - ! column critical error codes - integer, parameter, public :: & - SEAICE_ERROR_COL_VERT_THERM = 10, & - SEAICE_ERROR_COL_ITD_THERM = 11, & - SEAICE_ERROR_COL_RIDGING = 12, & - SEAICE_ERROR_COL_BGC = 13 - - ! incremental remap critical error codes - integer, parameter, public :: & - SEAICE_ERROR_IR_NEG_AREA = 20, & - SEAICE_ERROR_IR_NEG_MASS = 21, & - SEAICE_ERROR_IR_MONO = 22, & - SEAICE_ERROR_IR_CONS = 23 - ! public routines public :: & seaice_critical_error_write_block, & @@ -55,7 +41,7 @@ module seaice_error ! !----------------------------------------------------------------------- - subroutine seaice_critical_error_write_block(domain, block) + subroutine seaice_critical_error_write_block(domain, block, abortFlag) type(domain_type), intent(in) :: & domain @@ -63,12 +49,19 @@ subroutine seaice_critical_error_write_block(domain, block) type(block_type), intent(in) :: & block - ! write out block streams - call mpas_stream_mgr_block_write(& - domain % streamManager, & - writeBlock=block, & - streamID='abort_block', & - forceWriteNow=.true.) + logical, intent(in) :: & + abortFlag + + if (abortFlag) then + + ! write out block streams + call mpas_stream_mgr_block_write(& + domain % streamManager, & + writeBlock=block, & + streamID='abort_block', & + forceWriteNow=.true.) + + endif ! abortFlag end subroutine seaice_critical_error_write_block @@ -84,27 +77,29 @@ end subroutine seaice_critical_error_write_block ! !----------------------------------------------------------------------- - subroutine seaice_check_critical_error(domain, ierr) + subroutine seaice_check_critical_error(domain, abortFlag) use mpas_dmpar, only: mpas_dmpar_max_int - type(domain_type), intent(inout) :: & + type(domain_type), intent(in) :: & domain !< Input/Output: - integer, intent(in) :: & - ierr + logical, intent(in) :: & + abortFlag logical, pointer :: & config_full_abort_write integer :: & + ierr, & ierrmax call MPAS_pool_get_config(domain % configs, "config_full_abort_write", config_full_abort_write) - if (config_full_abort_write) then ! find if anyone failed + ierr = 0 + if (abortFlag) ierr = 1 call mpas_dmpar_max_int(domain % dminfo, ierr, ierrmax) if (ierrmax > 0) then @@ -115,47 +110,10 @@ subroutine seaice_check_critical_error(domain, ierr) endif ! check if a critical error occured - if (ierr > 0) then - - ! kill the model - call mpas_log_write("Runtime error $i: "//trim(error_string(ierr)), & - messageType=MPAS_LOG_CRIT, intArgs=(/ierr/)) - - endif + if (abortFlag) call mpas_log_write("Runtime error", messageType=MPAS_LOG_CRIT) end subroutine seaice_check_critical_error !----------------------------------------------------------------------- - function error_string(ierr) result(errorStr) - - character(len=strKIND) :: errorStr - - integer, intent(in) :: ierr - - select case(ierr) - case(SEAICE_ERROR_COL_VERT_THERM) - errorStr = "Column: Vertical Thermodynamics" - case(SEAICE_ERROR_COL_ITD_THERM) - errorStr = "Column: ITD thermodynamics" - case(SEAICE_ERROR_COL_RIDGING) - errorStr = "Column: Ridging" - case(SEAICE_ERROR_COL_BGC) - errorStr = "Column: BGC" - case(SEAICE_ERROR_IR_NEG_AREA) - errorStr = "IR: Negative area" - case(SEAICE_ERROR_IR_NEG_MASS) - errorStr = "IR: Negative mass" - case(SEAICE_ERROR_IR_MONO) - errorStr = "IR: Monotonicity violation" - case(SEAICE_ERROR_IR_CONS) - errorStr = "IR: Conservation violation" - case default - errorStr = "Unknown error code" - end select - - end function error_string - - !----------------------------------------------------------------------- - end module seaice_error diff --git a/src/core_seaice/shared/mpas_seaice_forcing.F b/src/core_seaice/shared/mpas_seaice_forcing.F index 1f95cbfc1c..95f72e4958 100644 --- a/src/core_seaice/shared/mpas_seaice_forcing.F +++ b/src/core_seaice/shared/mpas_seaice_forcing.F @@ -13,6 +13,8 @@ module seaice_forcing + use mpas_kind_types + use mpas_timer use mpas_derived_types use mpas_pool_routines use mpas_timekeeping @@ -63,14 +65,18 @@ module seaice_forcing ! !----------------------------------------------------------------------- - subroutine seaice_forcing_init(domain) + subroutine seaice_forcing_init(domain, clock) type (domain_type) :: domain + type (MPAS_Clock_type) :: clock + logical, pointer :: & - config_use_forcing + config_use_forcing, & + config_use_data_icebergs call MPAS_pool_get_config(domain % configs, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(domain % configs, "config_use_data_icebergs", config_use_data_icebergs) if (config_use_forcing) then @@ -82,6 +88,9 @@ subroutine seaice_forcing_init(domain) endif + ! init the data iceberg forcing + if (config_use_data_icebergs) call init_data_iceberg_forcing(domain, clock) + end subroutine seaice_forcing_init !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -310,9 +319,11 @@ subroutine seaice_forcing_get(& firstTimeStep logical, pointer :: & - config_use_forcing + config_use_forcing, & + config_use_data_icebergs call MPAS_pool_get_config(domain % configs, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(domain % configs, "config_use_data_icebergs", config_use_data_icebergs) if (config_use_forcing) then @@ -329,6 +340,15 @@ subroutine seaice_forcing_get(& endif + ! data iceberg forcing + if (config_use_data_icebergs) then + + call data_iceberg_forcing(& + streamManager, & + domain) + + endif + end subroutine seaice_forcing_get !||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| @@ -1590,6 +1610,203 @@ subroutine post_oceanic_coupling(block) end subroutine post_oceanic_coupling +!----------------------------------------------------------------------- +! data iceberg forcing +!----------------------------------------------------------------------- + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! init_data_iceberg_forcing +! +!> \brief +!> \author Darin Comeau, LANL +!> \date 27th August 2018 +!> \details This initializes the data iceberg forcing group. +!> +!----------------------------------------------------------------------- + + subroutine init_data_iceberg_forcing(domain, clock) + + type (domain_type) :: domain + + type (MPAS_Clock_type) :: clock + + logical, pointer :: & + config_do_restart + + character(len=strKIND) :: & + forcingIntervalMonthly, & + forcingReferenceTimeMonthly + + type (MPAS_Time_Type) :: currTime + character(len=strKIND) :: timeStamp + integer :: ierr + + ! get configuration options + call MPAS_pool_get_config(domain % configs, "config_do_restart", config_do_restart) + + forcingIntervalMonthly = "00-01-00_00:00:00" + forcingReferenceTimeMonthly = "0001-01-15_00:00:00" + + currTime = mpas_get_clock_time(clock, MPAS_NOW, ierr) + call mpas_get_time(curr_time=currTime, dateTimeString=timeStamp, ierr=ierr) + timeStamp = '0000'//trim(timeStamp(5:)) + + ! create own data iceberg forcing group + call MPAS_forcing_init_group(& + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + domain, & + timeStamp, & + '0000-01-01_00:00:00', & + '0001-00-00_00:00:00', & + config_do_restart) + + ! iceberg freshwater fluxes + call MPAS_forcing_init_field(& + domain % streamManager, & + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + "bergFreshwaterFluxData", & + "dataIcebergForcing", & + "berg_forcing", & + "bergFreshwaterFluxData", & + "linear", & + forcingReferenceTimeMonthly, & + forcingIntervalMonthly) + + call MPAS_forcing_init_field_data(& + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + domain % streamManager, & + config_do_restart, & + .false.) + + end subroutine init_data_iceberg_forcing + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! data_iceberg_forcing +! +!> \brief +!> \author Darin Comeau, LANL +!> \date 27th Aug 2018 +!> \details This routine is the timestep for getting and setting fluxes +!> from data icebergs. +! +!----------------------------------------------------------------------- + + subroutine data_iceberg_forcing(& + streamManager, & + domain) + + type (MPAS_streamManager_type), intent(inout) :: streamManager + + type (domain_type) :: domain + + ! Arguments for verbose debugging + ! type (MPAS_time_type) :: currentForcingTime + ! character(len=strKIND) :: currentForcingTimeStr + + real(kind=RKIND), pointer :: & + config_dt + + call mpas_pool_get_config(domain % configs, 'config_dt', config_dt) + + ! For verbose debugging. + ! Uncomment the lines below and the arguments above to print the forcing time + ! to the log file, to ensure the forcing times match the simulation times. + + ! call MPAS_forcing_get_forcing_time(& + ! seaiceForcingGroups, & + ! "seaice_data_iceberg_forcing_monthly", & + ! currentForcingTime) + + ! call MPAS_get_time(currentForcingTime, dateTimeString=currentForcingTimeStr) + ! call MPAS_log_write('Get Data icebergs at: '//trim(currentForcingTimeStr)) + + ! use the forcing layer to get + call MPAS_forcing_get_forcing(& + seaiceForcingGroups, & + "seaice_data_iceberg_forcing_monthly", & + streamManager, & + config_dt) + + call get_data_iceberg_fluxes(domain) + + end subroutine data_iceberg_forcing + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! get_data_iceberg_fluxes +! +!> \brief Initialize with icebergs calving mass +!> \author Darin Comeau, LANL +!> \date 20 Aug 2018 +!> \details This routine is intended to initialize the set data iceberg +!> meltwater and latent heat fluxes from a forcing file of monthly +!> climatologies. +! +!----------------------------------------------------------------------- + + subroutine get_data_iceberg_fluxes(domain) + + use seaice_constants, only: & + seaiceLatentHeatMelting ! latent heat of melting of fresh ice (J/kg) + + type(domain_type), intent(inout) :: & + domain + + type(block_type), pointer :: & + block + + type(MPAS_pool_type), pointer :: & + mesh, & + berg_forcing, & + berg_fluxes + + integer, pointer :: & + nCellsSolve + + real(kind=RKIND), dimension(:), pointer :: & + bergFreshwaterFluxData, & ! iceberg freshwater flux read in from file (kg/m^2/s) + bergFreshwaterFlux, & ! iceberg freshwater flux for ocean (kg/m^2/s) + bergLatentHeatFlux ! iceberg latent heat flux for ocean (J/m^2/s) + + integer :: & + iCell + + ! dc including as parameters here so as not to create new namelist options + real(kind=RKIND), parameter :: & + specificHeatFreshIce = 2106.0_RKIND, & ! specific heat of fresh ice J * kg^-1 * K^-1 + bergTemperature = -4.0_RKIND ! iceberg temperature, assumed constant + + block => domain % blocklist + do while (associated(block)) + + call MPAS_pool_get_subpool(block % structs, "mesh", mesh) + call MPAS_pool_get_subpool(block % structs, "berg_forcing", berg_forcing) + call MPAS_pool_get_subpool(block % structs, "berg_fluxes", berg_fluxes) + + call MPAS_pool_get_dimension(mesh, "nCellsSolve", nCellsSolve) + + call MPAS_pool_get_array(berg_forcing, "bergFreshwaterFluxData", bergFreshwaterFluxData) + call MPAS_pool_get_array(berg_fluxes, "bergFreshwaterFlux", bergFreshwaterFlux) + call MPAS_pool_get_array(berg_fluxes, "bergLatentHeatFlux", bergLatentHeatFlux) + + do iCell = 1, nCellsSolve + + bergFreshwaterFlux(iCell) = bergFreshwaterFluxData(iCell) + bergLatentHeatFlux(iCell) = bergFreshwaterFluxData(iCell) * & + (seaiceLatentHeatMelting - specificHeatFreshIce*bergTemperature) + + enddo + + block => block % next + enddo + + end subroutine get_data_iceberg_fluxes + !----------------------------------------------------------------------- ! coupler fluxes initialization !----------------------------------------------------------------------- @@ -1748,6 +1965,7 @@ subroutine reset_ocean_coupler_fluxes(domain) type(MPAS_pool_type), pointer :: & oceanFluxes, & + icebergFluxes, & biogeochemistry real(kind=RKIND), dimension(:), pointer :: & @@ -1756,6 +1974,10 @@ subroutine reset_ocean_coupler_fluxes(domain) oceanHeatFlux, & oceanShortwaveFlux + real(kind=RKIND), dimension(:), pointer :: & + bergFreshwaterFlux, & + bergLatentHeatFlux + real(kind=RKIND), dimension(:), pointer :: & oceanNitrateFlux, & oceanSilicateFlux, & @@ -1776,7 +1998,8 @@ subroutine reset_ocean_coupler_fluxes(domain) oceanDissolvedIronFlux logical, pointer :: & - config_use_column_biogeochemistry + config_use_column_biogeochemistry, & + config_use_data_icebergs block => domain % blocklist do while (associated(block)) @@ -1834,6 +2057,21 @@ subroutine reset_ocean_coupler_fluxes(domain) endif + ! data icebergs + call MPAS_pool_get_config(block % configs, "config_use_data_icebergs", config_use_data_icebergs) + + if (config_use_data_icebergs) then + + call MPAS_pool_get_subpool(block % structs, "berg_fluxes", icebergFluxes) + + call MPAS_pool_get_array(icebergFluxes, "bergFreshwaterFlux", bergFreshwaterFlux) + call MPAS_pool_get_array(icebergFluxes, "bergLatentHeatFlux", bergLatentHeatFlux) + + bergFreshwaterFlux = 0.0_RKIND + bergLatentHeatFlux = 0.0_RKIND + + endif + block => block % next end do @@ -1859,11 +2097,13 @@ subroutine seaice_forcing_write_restart_times(domain) type(domain_type) :: domain logical, pointer :: & - config_use_forcing + config_use_forcing, & + config_use_data_icebergs call MPAS_pool_get_config(domain % configs, "config_use_forcing", config_use_forcing) + call MPAS_pool_get_config(domain % configs, "config_use_data_icebergs", config_use_data_icebergs) - if (config_use_forcing) then + if (config_use_forcing .or. config_use_data_icebergs) then call MPAS_forcing_write_restart_times(seaiceForcingGroups) diff --git a/src/core_seaice/shared/mpas_seaice_initialize.F b/src/core_seaice/shared/mpas_seaice_initialize.F index c3e138e09e..f335bcff2f 100644 --- a/src/core_seaice/shared/mpas_seaice_initialize.F +++ b/src/core_seaice/shared/mpas_seaice_initialize.F @@ -97,7 +97,7 @@ subroutine seaice_init(& ! initialize forcing call mpas_log_write(" Initialize forcing...") - call seaice_forcing_init(domain) + call seaice_forcing_init(domain, clock) ! init dynamics call mpas_log_write(" Initialize velocity solver...") diff --git a/src/core_seaice/shared/mpas_seaice_time_integration.F b/src/core_seaice/shared/mpas_seaice_time_integration.F index 8270b847c1..08af497112 100644 --- a/src/core_seaice/shared/mpas_seaice_time_integration.F +++ b/src/core_seaice/shared/mpas_seaice_time_integration.F @@ -42,8 +42,7 @@ module seaice_time_integration subroutine seaice_timestep(& domain, & clock, & - itimestep, & - ierr)!{{{ + itimestep)!{{{ use mpas_timer @@ -84,9 +83,6 @@ subroutine seaice_timestep(& integer, intent(in) :: & itimestep !< Input: - integer, intent(inout) :: & - ierr !< Input - type(block_type), pointer :: & block @@ -144,8 +140,7 @@ subroutine seaice_timestep(& ! pre dynamics column physics call mpas_timer_start("Column pre-dynamics") - call seaice_column_predynamics_time_integration(domain, clock, ierr) - if (ierr > 0) return + call seaice_column_predynamics_time_integration(domain, clock) call mpas_timer_stop("Column pre-dynamics") ! loop of dynamcis subcycle @@ -165,20 +160,18 @@ subroutine seaice_timestep(& ! advection call mpas_timer_start("Advection") if (config_use_advection) & - call seaice_run_advection(domain, clock, ierr) + call seaice_run_advection(domain, clock) call mpas_timer_stop("Advection") - if (ierr > 0) return ! ridging call mpas_timer_start("Column") - call seaice_column_dynamics_time_integration(domain, clock, ierr) + call seaice_column_dynamics_time_integration(domain, clock) call mpas_timer_stop("Column") - if (ierr > 0) return ! berg dynamics if (config_use_bergs) then call mpas_timer_start("Berg dynamics") - call seaice_run_berg_dynamics(domain, clock, ierr) + call seaice_run_berg_dynamics(domain, clock) call mpas_timer_stop("Berg dynamics") endif diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F index 27c04b5506..23f188fe0c 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_constitutive_relation.F @@ -158,6 +158,7 @@ subroutine seaice_evp_constitutive_relation(& replacementPressure, & areaCell, & dtElastic) +!$omp declare simd(seaice_evp_constitutive_relation) real(kind=RKIND), intent(inout) :: & stress11, & !< Input/Output: @@ -237,6 +238,7 @@ subroutine seaice_evp_constitutive_relation_revised(& icePressure, & replacementPressure, & areaCell) +!$omp declare simd(seaice_evp_constitutive_relation_revised) real(kind=RKIND), intent(inout) :: & stress11, & !< Input/Output: @@ -312,6 +314,7 @@ subroutine seaice_linear_constitutive_relation(& strain11, & strain22, & strain12) +!$omp declare simd(seaice_linear_constitutive_relation) real(kind=RKIND), intent(out) :: & stress11, & !< Output: diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F index 42eadea3ca..a9c3b48a0e 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_pwl.F @@ -527,366 +527,6 @@ function pwl_basis_gradient(& end function pwl_basis_gradient!}}} -!----------------------------------------------------------------------- -! Hex specific plotting stuff -!----------------------------------------------------------------------- - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! plot_pwl -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 28 October 2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine plot_pwl(& - mesh, & - iCell, & - nEdgesOnCell, & - iVertex, & - subBasisGradientU, & - subBasisGradientV, & - subBasisConstant, & - xLocal, & - yLocal)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - integer, intent(in) :: & - iCell, & !< Input: - nEdgesOnCell, & !< Input: - iVertex !< Input: - - real(kind=RKIND), dimension(:,:), intent(in) :: & - subBasisGradientU, & - subBasisGradientV, & - subBasisConstant - - real(kind=RKIND), dimension(:), intent(in) :: & - xLocal, & !< Input: - yLocal !< Input: - - real(kind=RKIND) :: & - xmin, xmax, ymin, ymax - - real(kind=RKIND), dimension(2,2) :: & - mapping - - integer :: & - iSubTriangle, & - i1, & - i2, & - iVertexOnCell, & - iVertex2 - - integer, dimension(:,:), pointer :: & - verticesOnCell - - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex, & - xCell, & - yCell - - ! init variables - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - call MPAS_pool_get_array(mesh, "xCell", xCell) - call MPAS_pool_get_array(mesh, "yCell", yCell) - - xmin = 1e30 - xmax = -1e30 - ymin = 1e30 - ymax = -1e30 - - do iVertexOnCell = 1, nEdgesOnCell - - iVertex2 = verticesOnCell(iVertexOnCell,iCell) - - xmin = min(xmin, xVertex(iVertex2)) - 1000.0_RKIND - xmax = max(xmax, xVertex(iVertex2)) + 1000.0_RKIND - ymin = min(ymin, yVertex(iVertex2)) - 1000.0_RKIND - ymax = max(ymax, yVertex(iVertex2)) + 1000.0_RKIND - - open(55,file='test.txt') - if (iVertexOnCell == iVertex) then - write(55,*) xVertex(iVertex2), yVertex(iVertex2) - endif - close(55) - - enddo ! iVertex - - xmin = xCell(iCell) - 11000.0_RKIND - xmax = xCell(iCell) + 11000.0_RKIND - ymin = yCell(iCell) - 11000.0_RKIND - ymax = yCell(iCell) + 11000.0_RKIND - - open(55,file="pwl.txt") - - write(55,fmt='(a,f10.2,a,f10.2,a)') "set xrange [",xmin,":",xmax,"]" - write(55,fmt='(a,f10.2,a,f10.2,a)') "set yrange [",ymin,":",ymax,"]" - - ! loop over subtriangles - do iSubTriangle = 1, nEdgesOnCell - - i1 = iSubTriangle - i2 = iSubTriangle + 1 - if (i2 > nEdgesOnCell) i2 = i2 - nEdgesOnCell - - ! get the triangle mapping - call get_triangle_mapping(mapping, & - 1.0_RKIND,0.0_RKIND,& - 0.0_RKIND,1.0_RKIND,& - xLocal(i1),yLocal(i1),& - xLocal(i2),yLocal(i2)) - - ! plot from subtriangle - call plot_subtriangle(& - nEdgesOnCell, & - iSubTriangle, & - iVertex, & - subBasisGradientU, & - subBasisGradientV, & - subBasisConstant, & - mapping, & - xCell(iCell), & - yCell(iCell)) - - enddo ! iSubTriangle - - close(55) - - end subroutine plot_pwl!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! plot_subtriangle -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 28 October 2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine plot_subtriangle(& - nEdgesOnCell, & - iSubTriangle, & - iVertex, & - subBasisGradientU, & - subBasisGradientV, & - subBasisConstant, & - mapping, & - x0, & - y0)!{{{ - - use seaice_velocity_solver_variational_shared, only: & - seaice_wrapped_index - - integer, intent(in) :: & - nEdgesOnCell, & !< Input: - iSubTriangle, & !< Input: - iVertex !< Input: - - real(kind=RKIND), dimension(:,:), intent(in) :: & - subBasisGradientU, & !< Input: - subBasisGradientV, & !< Input: - subBasisConstant !< Input: - - real(kind=RKIND), dimension(2,2), intent(in) :: & - mapping !< Input: - - real(kind=RKIND), intent(in) :: & - x0, & !< Input: - y0 !< Input: - - real(kind=RKIND) :: & - u, v, & - x, y, & - pwl, & - alpha - - integer :: & - i, j, iObject - - real(kind=RKIND) :: & - x1, x2, x3, x4, x5, & - y1, y2, y3, y4, y5, & - d, & - dx, dy - - integer, parameter :: n = 50 - - logical, parameter :: & - lfinitedifference = .false. - - iObject = ((iSubTriangle - 1) * (n+1) * (n+2)) / 2 - - d = 1.0_RKIND / real(n, RKIND) - - do i = 0, n - do j = 0, n - - if (i<=n-j-1) then - - iObject = iObject + 1 - - u = real(i,RKIND) / real(n,RKIND) - v = real(j,RKIND) / real(n,RKIND) - - call use_triangle_mapping(x, y, u, v, mapping) - - alpha = 1.0_RKIND / real(nEdgesOnCell,RKIND) - - pwl = alpha * (subBasisGradientU(iSubTriangle,3) * x + & - subBasisGradientV(iSubTriangle,3) * y + & - subBasisConstant(iSubTriangle,3)) - - write(*,*) iVertex, nEdgesOnCell, seaice_wrapped_index(iVertex - 1, nEdgesOnCell) - - if (iSubTriangle == iVertex) then - - pwl = pwl + & - (subBasisGradientU(iSubTriangle,1) * x + & - subBasisGradientV(iSubTriangle,1) * y + & - subBasisConstant(iSubTriangle,1)) - - else if (iSubTriangle == seaice_wrapped_index(iVertex - 1, nEdgesOnCell)) then - - pwl = pwl + & - (subBasisGradientU(iSubTriangle,2) * x + & - subBasisGradientV(iSubTriangle,2) * y + & - subBasisConstant(iSubTriangle,2)) - - endif - - if (i \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine get_triangle_mapping(& - mapping, & - x1, y1, & - x2, y2, & - u1, v1, & - u2, v2)!{{{ - - real(kind=RKIND), dimension(2,2), intent(out) :: & - mapping !< Output: - - real(kind=RKIND), intent(in) :: & - x1, & !< Input: - y1, & !< Input: - x2, & !< Input: - y2, & !< Input: - u1, & !< Input: - v1, & !< Input: - u2, & !< Input: - v2 !< Input: - - mapping(1,1) = (u2*y1 - u1*y2) / (x2*y1 - x1*y2) - mapping(1,2) = (u1*x2 - u2*x1) / (y1*x2 - y2*x1) - - mapping(2,1) = (v2*y1 - v1*y2) / (x2*y1 - x1*y2) - mapping(2,2) = (v1*x2 - v2*x1) / (y1*x2 - y2*x1) - - end subroutine get_triangle_mapping!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! use_triangle_mapping -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine use_triangle_mapping(& - u, v, & - x, y, & - mapping)!{{{ - - real(kind=RKIND), intent(out) :: & - u, v !< Output: - - real(kind=RKIND), intent(in) :: & - x, y !< Input: - - real(kind=RKIND), dimension(2,2), intent(in) :: & - mapping !< Input: - - u = mapping(1,1) * x + mapping(1,2) * y - v = mapping(2,1) * x + mapping(2,2) * y - - end subroutine use_triangle_mapping!}}} - !----------------------------------------------------------------------- end module seaice_velocity_solver_pwl diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F index f090cb73b5..b5017b08f0 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_variational.F @@ -293,7 +293,7 @@ subroutine seaice_strain_tensor_variational(& vVelocity, & !< Input: tanLatVertexRotatedOverRadius !< Input: - real(kind=RKIND), dimension(:,:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in) :: & basisGradientU, & !< Input: basisGradientV !< Input: @@ -307,13 +307,18 @@ subroutine seaice_strain_tensor_variational(& iVertex, & jVertex + real(kind=RKIND) :: & + strain11Tmp, & + strain22Tmp, & + strain12Tmp + integer, pointer :: & nCells integer, dimension(:), pointer :: & nEdgesOnCell - integer, dimension(:,:), pointer :: & + integer, dimension(:,:), contiguous, pointer :: & verticesOnCell ! init variables @@ -323,43 +328,38 @@ subroutine seaice_strain_tensor_variational(& call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) ! loop over cells - !$omp parallel do default(shared) private(iCell, iGradientVertex, iBasisVertex, iVertex, jVertex) + !$omp parallel do default(shared) private(iCell, iGradientVertex, iBasisVertex, iVertex, jVertex,& + !$omp& strain11Tmp, strain22Tmp, strain12Tmp) do iCell = 1, nCells if (solveStress(iCell) == 1) then - strain11(:,iCell) = 0.0_RKIND - strain22(:,iCell) = 0.0_RKIND - strain12(:,iCell) = 0.0_RKIND - ! loop over velocity points surrounding cell - location of stress and derivative do iGradientVertex = 1, nEdgesOnCell(iCell) + strain11Tmp = 0.0_RKIND + strain22Tmp = 0.0_RKIND + strain12Tmp = 0.0_RKIND + ! loop over basis functions do iBasisVertex = 1, nEdgesOnCell(iCell) iVertex = verticesOnCell(iBasisVertex,iCell) - strain11(iGradientVertex,iCell) = strain11(iGradientVertex,iCell) + & - uVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell) - - strain22(iGradientVertex,iCell) = strain22(iGradientVertex,iCell) + & - vVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) - - strain12(iGradientVertex,iCell) = strain12(iGradientVertex,iCell) + 0.5_RKIND * (& - uVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) + & - vVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell)) + strain11Tmp = strain11Tmp + uVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell) + strain22Tmp = strain22Tmp + vVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) + strain12Tmp = strain12Tmp + 0.5_RKIND * (& + uVelocity(iVertex) * basisGradientV(iBasisVertex,iGradientVertex,iCell) + & + vVelocity(iVertex) * basisGradientU(iBasisVertex,iGradientVertex,iCell)) enddo ! iVertexOnCell ! metric terms jVertex = verticesOnCell(iGradientVertex,iCell) - strain11(iGradientVertex,iCell) = strain11(iGradientVertex,iCell) - & - vVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) - - strain12(iGradientVertex,iCell) = strain12(iGradientVertex,iCell) + & - uVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) * 0.5_RKIND + strain11(iGradientVertex,iCell) = strain11Tmp - vVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) + strain12(iGradientVertex,iCell) = strain12Tmp + uVelocity(jVertex) * tanLatVertexRotatedOverRadius(jVertex) * 0.5_RKIND + strain22(iGradientVertex,iCell) = strain22Tmp enddo ! jVertexOnCell @@ -402,15 +402,15 @@ subroutine seaice_stress_tensor_variational(& type(MPAS_pool_type), pointer, intent(in) :: & mesh !< Input: - real(kind=RKIND), dimension(:,:), intent(inout) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(inout) :: & stress11, & !< Input/Output: stress22, & !< Input/Output: stress12 !< Input/Output: - real(kind=RKIND), dimension(:,:), intent(out) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(out) :: & replacementPressure !< Output: - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & strain11, & !< Input: strain22, & !< Input: strain12 !< Input: @@ -432,8 +432,7 @@ subroutine seaice_stress_tensor_variational(& iVertexOnCell integer, pointer :: & - nCells, & - maxEdges + nCells integer, dimension(:), pointer :: & nEdgesOnCell @@ -443,7 +442,6 @@ subroutine seaice_stress_tensor_variational(& ! init variables call MPAS_pool_get_dimension(mesh, "nCells", nCells) - call MPAS_pool_get_dimension(mesh, "maxEdges", maxEdges) call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) call MPAS_pool_get_array(mesh, "areaCell", areaCell) @@ -457,6 +455,7 @@ subroutine seaice_stress_tensor_variational(& if (solveStress(iCell) == 1) then + !$omp simd do iVertexOnCell = 1, nEdgesOnCell(iCell) call seaice_evp_constitutive_relation(& @@ -479,10 +478,12 @@ subroutine seaice_stress_tensor_variational(& else + !$omp parallel do default(shared) private(iCell, iVertexOnCell) do iCell = 1, nCells if (solveStress(iCell) == 1) then + !$omp simd do iVertexOnCell = 1, nEdgesOnCell(iCell) call seaice_evp_constitutive_relation_revised(& @@ -539,12 +540,12 @@ subroutine seaice_stress_divergence_variational(& stressDivergenceU, & !< Output: stressDivergenceV !< Output: - real(kind=RKIND), dimension(:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:), contiguous, intent(in) :: & stress11, & !< Input: stress22, & !< Input: stress12 !< Input: - real(kind=RKIND), dimension(:,:,:), intent(in) :: & + real(kind=RKIND), dimension(:,:,:), contiguous, intent(in) :: & basisIntegralsU, & !< Input: basisIntegralsV, & !< Input: basisIntegralsMetric !< Input: @@ -577,8 +578,7 @@ subroutine seaice_stress_divergence_variational(& nEdgesOnCell integer, dimension(:,:), pointer :: & - cellsOnVertex, & - verticesOnCell + cellsOnVertex real(kind=RKIND), dimension(:), pointer :: & areaTriangle @@ -589,7 +589,6 @@ subroutine seaice_stress_divergence_variational(& call MPAS_pool_get_array(mesh, "nEdgesOnCell", nEdgesOnCell) call MPAS_pool_get_array(mesh, "cellsOnVertex", cellsOnVertex) - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) call MPAS_pool_get_array(mesh, "areaTriangle", areaTriangle) ! loop over velocity positions @@ -616,21 +615,16 @@ subroutine seaice_stress_divergence_variational(& ! loop over the vertices of the surrounding cell do iStressVertex = 1, nEdgesOnCell(iCell) - ! normal terms + ! normal & metric terms stressDivergenceUCell = stressDivergenceUCell - & stress11(iStressVertex,iCell) * basisIntegralsU(iStressVertex,iVelocityVertex,iCell) - & - stress12(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - - stressDivergenceVCell = stressDivergenceVCell - & - stress22(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - & - stress12(iStressVertex,iCell) * basisIntegralsU(iStressVertex,iVelocityVertex,iCell) - - ! metric terms - stressDivergenceUCell = stressDivergenceUCell - & + stress12(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - & stress12(iStressVertex,iCell) * basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell) * & tanLatVertexRotatedOverRadius(iVertex) - stressDivergenceVCell = stressDivergenceVCell + & + stressDivergenceVCell = stressDivergenceVCell - & + stress22(iStressVertex,iCell) * basisIntegralsV(iStressVertex,iVelocityVertex,iCell) - & + stress12(iStressVertex,iCell) * basisIntegralsU(iStressVertex,iVelocityVertex,iCell) + & stress11(iStressVertex,iCell) * basisIntegralsMetric(iStressVertex,iVelocityVertex,iCell) * & tanLatVertexRotatedOverRadius(iVertex) diff --git a/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F b/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F index 80a06ee556..0e63e9c991 100644 --- a/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F +++ b/src/core_seaice/shared/mpas_seaice_velocity_solver_wachspress.F @@ -2065,847 +2065,6 @@ subroutine integrate_wachspress_polygon(& end subroutine integrate_wachspress_polygon!}}} -!----------------------------------------------------------------------- -! Hex specific plotting stuff -!----------------------------------------------------------------------- - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! plot_wachpress -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine plot_wachpress(& - mesh, & - iCell, & - nEdgesOnCell, & - iVertex, & - wachspressKappa, & - wachspressA, & - wachspressB, & - xLocal, & - yLocal)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - integer, intent(in) :: & - iCell, & !< Input: - nEdgesOnCell, & !< Input: - iVertex !< Input: - - real(kind=RKIND), dimension(:,:), intent(out) :: & - wachspressKappa !< Output: - - real(kind=RKIND), dimension(:), intent(out) :: & - wachspressA, & !< Output: - wachspressB !< Output: - - real(kind=RKIND), dimension(:), intent(in) :: & - xLocal, & !< Input: - yLocal !< Input: - - real(kind=RKIND) :: & - xmin, xmax, ymin, ymax - - real(kind=RKIND) :: & - jacobian - - real(kind=RKIND), dimension(2,2) :: & - mapping - - integer :: & - iSubTriangle, & - i1, & - i2, & - iVertexOnCell, & - iVertex2, & - iObject - - integer, dimension(:,:), pointer :: & - verticesOnCell - - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex, & - xCell, & - yCell - - ! init variables - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - call MPAS_pool_get_array(mesh, "xCell", xCell) - call MPAS_pool_get_array(mesh, "yCell", yCell) - - xmin = 1e30 - xmax = -1e30 - ymin = 1e30 - ymax = -1e30 - - iObject = 1 - - do iVertexOnCell = 1, nEdgesOnCell - - iVertex2 = verticesOnCell(iVertexOnCell,iCell) - - xmin = min(xmin, xVertex(iVertex2)) - 1000.0_RKIND - xmax = max(xmax, xVertex(iVertex2)) + 1000.0_RKIND - ymin = min(ymin, yVertex(iVertex2)) - 1000.0_RKIND - ymax = max(ymax, yVertex(iVertex2)) + 1000.0_RKIND - - open(55,file='test.txt') - if (iVertexOnCell == iVertex) then - write(55,*) xVertex(iVertex2), yVertex(iVertex2) - endif - close(55) - - enddo ! iVertex - - open(55,file="wachspress.txt") - - write(55,fmt='(a,f10.2,a,f10.2,a)') "set xrange [",xmin,":",xmax,"]" - write(55,fmt='(a,f10.2,a,f10.2,a)') "set yrange [",ymin,":",ymax,"]" - - ! loop over subtriangles - do iSubTriangle = 1, nEdgesOnCell - - i1 = iSubTriangle - i2 = iSubTriangle + 1 - if (i2 > nEdgesOnCell) i2 = i2 - nEdgesOnCell - - ! get the triangle mapping - call get_triangle_mapping(mapping, & - jacobian, & - 1.0_RKIND,0.0_RKIND,& - 0.0_RKIND,1.0_RKIND,& - xLocal(i1),yLocal(i1),& - xLocal(i2),yLocal(i2)) - - ! plot from subtriangle - call plot_subtriangle_2(nEdgesOnCell, iSubTriangle, iVertex, & - wachspressKappa, wachspressA, wachspressB, & - mapping, xCell(iCell), yCell(iCell), iObject) - - enddo ! iSubTriangle - - close(55) - - end subroutine plot_wachpress!}}} - - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! plot_subtriangle -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine plot_subtriangle(& - nEdgesOnCell, & - iSubTriangle, & - iVertex, & - wachspressKappa, & - wachspressA, & - wachspressB, & - mapping, & - x0, & - y0, & - iObject)!{{{ - - integer, intent(in) :: & - nEdgesOnCell, & !< Input: - iSubTriangle, & !< Input: - iVertex !< Input: - - integer, intent(inout) :: & - iObject !< Input: - - real(kind=RKIND), dimension(:,:), intent(in) :: & - wachspressKappa !< Input: - - real(kind=RKIND), dimension(:), intent(in) :: & - wachspressA, & !< Input: - wachspressB !< Input: - - real(kind=RKIND), dimension(2,2), intent(in) :: & - mapping !< Input: - - real(kind=RKIND), intent(in) :: & - x0, & !< Input: - y0 !< Input: - - real(kind=RKIND) :: & - u, v, & - x, y, & - wachspress, & - wachspress1 - - integer :: & - i, j - - real(kind=RKIND) :: & - x1, x2, x3, x4, x5, & - y1, y2, y3, y4, y5, & - d, & - dx, dy - - integer, dimension(:), allocatable :: & - nEdgesOnCellSubset - - integer, dimension(:,:), allocatable :: & - vertexIndexSubset - - integer, parameter :: n = 5 - - logical, parameter :: & - lfinitedifference = .false. - - real(kind=RKIND), dimension(1) :: & - xbasis, ybasis, var - - allocate(nEdgesOnCellSubset(nEdgesOnCell)) - allocate(vertexIndexSubset(nEdgesOnCell,nEdgesOnCell)) - - call wachspress_indexes(& - nEdgesOnCell, & - nEdgesOnCellSubset, & - vertexIndexSubset) - - d = 1.0_RKIND / real(n, RKIND) - - do i = 0, n - do j = 0, n - - if (i<=n-j-1) then - - iObject = iObject + 1 - - u = real(i,RKIND) / real(n,RKIND) - v = real(j,RKIND) / real(n,RKIND) - - call use_triangle_mapping(x, y, u, v, mapping) - - if (lfinitedifference) then - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - wachspress = var(1) - - dx = 0.0_RKIND - dy = 100.0_RKIND - - x1 = x + dx - y1 = y + dy - - xbasis(1) = x1 ; ybasis(1) = y1 - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - wachspress1 = var(1) - - wachspress = (wachspress1 - wachspress) / dy - - else - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - wachspress = var(1) - - endif - - if (i \brief -!> \author Adrian K. Turner, LANL -!> \date 31 October 2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine plot_subtriangle_2(& - nEdgesOnCell, & - iSubTriangle, & - iVertex, & - wachspressKappa, & - wachspressA, & - wachspressB, & - mapping, & - x0, & - y0, & - iObject)!{{{ - - integer, intent(in) :: & - nEdgesOnCell, & !< Input: - iSubTriangle, & !< Input: - iVertex !< Input: - - integer, intent(inout) :: & - iObject !< Input: - - real(kind=RKIND), dimension(:,:), intent(in) :: & - wachspressKappa !< Input: - - real(kind=RKIND), dimension(:), intent(in) :: & - wachspressA, & !< Input: - wachspressB !< Input: - - real(kind=RKIND), dimension(2,2), intent(in) :: & - mapping !< Input: - - real(kind=RKIND), intent(in) :: & - x0, & !< Input: - y0 !< Input: - - integer :: & - i, j, & - ii, jj - - real(kind=RKIND) :: & - u, v, & - uu0, vv0, & - uu1, vv1, & - uu2, vv2, & - xx0, yy0, & - xx1, yy1, & - xx2, yy2, & - f0, f1, f2, & - f_avg, & - d, & - u0, u1, u2, & - v0, v1, v2, & - uuu, vvv, & - x, y - - integer, dimension(:), allocatable :: & - nEdgesOnCellSubset - - integer, dimension(:,:), allocatable :: & - vertexIndexSubset - - integer, parameter :: n = 50 - integer, parameter :: nn = 1 - - logical, parameter :: & - lfinitedifference = .false. - - real(kind=RKIND), dimension(1) :: & - xbasis, ybasis, var - - d = (1.0_RKIND / real(n,RKIND)) - - allocate(nEdgesOnCellSubset(nEdgesOnCell)) - allocate(vertexIndexSubset(nEdgesOnCell,nEdgesOnCell)) - - call wachspress_indexes(& - nEdgesOnCell, & - nEdgesOnCellSubset, & - vertexIndexSubset) - - ! loop over integration segments - do i = 0, n - do j = 0, n - - ! forward integration triangle - if (i+j<=n-1) then - - ! subtriangle starting point - u0 = real(i,RKIND) / real(n,RKIND) - v0 = real(j,RKIND) / real(n,RKIND) - call use_triangle_mapping(x, y, u0, v0, mapping) - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - f0 = var(1) - - u1 = real(i+1,RKIND) / real(n,RKIND) - v1 = real(j ,RKIND) / real(n,RKIND) - call use_triangle_mapping(x, y, u1, v1, mapping) - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - f1 = var(1) - - u2 = real(i ,RKIND) / real(n,RKIND) - v2 = real(j+1,RKIND) / real(n,RKIND) - call use_triangle_mapping(x, y, u2, v2, mapping) - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - f2 = var(1) - - u = u0 - v = v0 - - ! break up the forward triangle for sub-plotting - do ii = 0, nn - do jj = 0, nn - - ! forward triangle - if (ii+jj<=nn-1) then - - uu0 = real(ii ,RKIND) / real(nn,RKIND) - vv0 = real(jj ,RKIND) / real(nn,RKIND) - - uu1 = real(ii+1,RKIND) / real(nn,RKIND) - vv1 = real(jj ,RKIND) / real(nn,RKIND) - - uu2 = real(ii ,RKIND) / real(nn,RKIND) - vv2 = real(jj+1,RKIND) / real(nn,RKIND) - - ! subtriangle location in canonical coordinates - uuu = (uu0 + uu1 + uu2) / 3.0_RKIND - vvv = (vv0 + vv1 + vv2) / 3.0_RKIND - - ! linear basis - f_avg = f0 * (-uuu-vvv+1) + & - f1 * uuu + & - f2 * vvv - - uu0 = u + uu0 * d - vv0 = v + vv0 * d - call use_triangle_mapping(xx0, yy0, uu0, vv0, mapping) - - uu1 = u + uu1 * d - vv1 = v + vv1 * d - call use_triangle_mapping(xx1, yy1, uu1, vv1, mapping) - - uu2 = u + uu2 * d - vv2 = v + vv2 * d - call use_triangle_mapping(xx2, yy2, uu2, vv2, mapping) - - xx0 = x0 + xx0 ; yy0 = y0 + yy0 - xx1 = x0 + xx1 ; yy1 = y0 + yy1 - xx2 = x0 + xx2 ; yy2 = y0 + yy2 - - write(55,fmt='(a,i5,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2)') & - "set object ",iObject," polygon from ",& - xx0,",",yy0," to ",xx1,",",yy1," to ",xx2,",",yy2," to ",xx0,",",yy0 - - write(55,fmt='(a,i5,a,e14.6,a)') "set object ",iObject,' fc palette cb ', f_avg, ' fillstyle solid' - - iObject = iObject + 1 - - endif - - ! backward triangle - if (ii+jj<=nn .and. ii/=0 .and. jj/=0) then - - uu0 = real(ii ,RKIND) / real(nn,RKIND) - vv0 = real(jj ,RKIND) / real(nn,RKIND) - - uu1 = real(ii-1,RKIND) / real(nn,RKIND) - vv1 = real(jj ,RKIND) / real(nn,RKIND) - - uu2 = real(ii ,RKIND) / real(nn,RKIND) - vv2 = real(jj-1,RKIND) / real(nn,RKIND) - - ! subtriangle location in canonical coordinates - uuu = (uu0 + uu1 + uu2) / 3.0_RKIND - vvv = (vv0 + vv1 + vv2) / 3.0_RKIND - - ! linear basis - f_avg = f0 * (-uuu-vvv+1) + & - f1 * uuu + & - f2 * vvv - - uu0 = u + uu0 * d - vv0 = v + vv0 * d - call use_triangle_mapping(xx0, yy0, uu0, vv0, mapping) - - uu1 = u + uu1 * d - vv1 = v + vv1 * d - call use_triangle_mapping(xx1, yy1, uu1, vv1, mapping) - - uu2 = u + uu2 * d - vv2 = v + vv2 * d - call use_triangle_mapping(xx2, yy2, uu2, vv2, mapping) - - xx0 = x0 + xx0 ; yy0 = y0 + yy0 - xx1 = x0 + xx1 ; yy1 = y0 + yy1 - xx2 = x0 + xx2 ; yy2 = y0 + yy2 - - write(55,fmt='(a,i5,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2)') & - "set object ",iObject," polygon from ",& - xx0,",",yy0," to ",xx1,",",yy1," to ",xx2,",",yy2," to ",xx0,",",yy0 - - write(55,fmt='(a,i5,a,e14.6,a)') "set object ",iObject,' fc palette cb ', f_avg, ' fillstyle solid' - - iObject = iObject + 1 - - endif - - enddo ! jj - enddo ! ii - - endif - - ! backward integration triangle - if (i+j<=n .and. i/=0 .and. j/=0) then - - ! subtriangle starting point - u0 = real(i,RKIND) / real(n,RKIND) - v0 = real(j,RKIND) / real(n,RKIND) - call use_triangle_mapping(x, y, u0, v0, mapping) - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - f0 = var(1) - - u1 = real(i-1,RKIND) / real(n,RKIND) - v1 = real(j ,RKIND) / real(n,RKIND) - call use_triangle_mapping(x, y, u1, v1, mapping) - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - f1 = var(1) - - u2 = real(i ,RKIND) / real(n,RKIND) - v2 = real(j-1,RKIND) / real(n,RKIND) - call use_triangle_mapping(x, y, u2, v2, mapping) - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_function(& - nEdgesOnCell, iVertex, xbasis, ybasis, & - wachspressKappa, wachspressA, wachspressB, & - nEdgesOnCellSubset, vertexIndexSubset, var) - f2 = var(1) - - u = u0 - d - v = v0 - d - - ! break up the backward triangle for sub-plotting - do ii = 0, nn - do jj = 0, nn - - ! forward triangle - if (ii+jj>=nn .and. ii/=nn .and. jj/=nn) then - - uu0 = real(ii ,RKIND) / real(nn,RKIND) - vv0 = real(jj ,RKIND) / real(nn,RKIND) - - uu1 = real(ii+1,RKIND) / real(nn,RKIND) - vv1 = real(jj ,RKIND) / real(nn,RKIND) - - uu2 = real(ii ,RKIND) / real(nn,RKIND) - vv2 = real(jj+1,RKIND) / real(nn,RKIND) - - ! subtriangle location in canonical coordinates - uuu = (uu0 + uu1 + uu2) / 3.0_RKIND - vvv = (vv0 + vv1 + vv2) / 3.0_RKIND - - uuu = 1.0_RKIND - uuu - vvv = 1.0_RKIND - vvv - - ! linear basis - f_avg = f0 * (-uuu-vvv+1) + & - f1 * uuu + & - f2 * vvv - - uu0 = u + uu0 * d - vv0 = v + vv0 * d - call use_triangle_mapping(xx0, yy0, uu0, vv0, mapping) - - uu1 = u + uu1 * d - vv1 = v + vv1 * d - call use_triangle_mapping(xx1, yy1, uu1, vv1, mapping) - - uu2 = u + uu2 * d - vv2 = v + vv2 * d - call use_triangle_mapping(xx2, yy2, uu2, vv2, mapping) - - xx0 = x0 + xx0 ; yy0 = y0 + yy0 - xx1 = x0 + xx1 ; yy1 = y0 + yy1 - xx2 = x0 + xx2 ; yy2 = y0 + yy2 - - write(55,fmt='(a,i5,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2)') & - "set object ",iObject," polygon from ",& - xx0,",",yy0," to ",xx1,",",yy1," to ",xx2,",",yy2," to ",xx0,",",yy0 - - write(55,fmt='(a,i5,a,e14.6,a)') "set object ",iObject,' fc palette cb ', f_avg, ' fillstyle solid' - - iObject = iObject + 1 - - endif - - ! backward triangle - if (ii+jj>=nn+1) then - - uu0 = real(ii ,RKIND) / real(nn,RKIND) - vv0 = real(jj ,RKIND) / real(nn,RKIND) - - uu1 = real(ii-1,RKIND) / real(nn,RKIND) - vv1 = real(jj ,RKIND) / real(nn,RKIND) - - uu2 = real(ii ,RKIND) / real(nn,RKIND) - vv2 = real(jj-1,RKIND) / real(nn,RKIND) - - ! subtriangle location in canonical coordinates - uuu = (uu0 + uu1 + uu2) / 3.0_RKIND - vvv = (vv0 + vv1 + vv2) / 3.0_RKIND - - uuu = 1.0_RKIND - uuu - vvv = 1.0_RKIND - vvv - - ! linear basis - f_avg = f0 * (-uuu-vvv+1) + & - f1 * uuu + & - f2 * vvv - - uu0 = u + uu0 * d - vv0 = v + vv0 * d - call use_triangle_mapping(xx0, yy0, uu0, vv0, mapping) - - uu1 = u + uu1 * d - vv1 = v + vv1 * d - call use_triangle_mapping(xx1, yy1, uu1, vv1, mapping) - - uu2 = u + uu2 * d - vv2 = v + vv2 * d - call use_triangle_mapping(xx2, yy2, uu2, vv2, mapping) - - xx0 = x0 + xx0 ; yy0 = y0 + yy0 - xx1 = x0 + xx1 ; yy1 = y0 + yy1 - xx2 = x0 + xx2 ; yy2 = y0 + yy2 - - write(55,fmt='(a,i5,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2,a,f10.2)') & - "set object ",iObject," polygon from ",& - xx0,",",yy0," to ",xx1,",",yy1," to ",xx2,",",yy2," to ",xx0,",",yy0 - - write(55,fmt='(a,i5,a,e14.6,a)') "set object ",iObject,' fc palette cb ', f_avg, ' fillstyle solid' - - iObject = iObject + 1 - - endif - - enddo ! jj - enddo ! ii - - endif - - enddo ! j - enddo ! i - - deallocate(nEdgesOnCellSubset) - deallocate(vertexIndexSubset) - - end subroutine plot_subtriangle_2!}}} - -!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -! -! plot_wachspress_square -! -!> \brief -!> \author Adrian K. Turner, LANL -!> \date 2013-2014 -!> \details -!> -! -!----------------------------------------------------------------------- - - subroutine plot_wachspress_square(& - mesh, & - wachspressKappa, & - wachspressA, & - wachspressB)!{{{ - - type(MPAS_pool_type), pointer, intent(in) :: & - mesh !< Input: - - real(kind=RKIND), dimension(:,:,:), intent(in) :: & - wachspressKappa !< Input: - - real(kind=RKIND), dimension(:,:), intent(in) :: & - wachspressA, & !< Input: - wachspressB !< Input: - - integer :: & - iCell, & - iVertex, & - iVertexOnCell - - real(kind=RKIND) :: & - x, y, x0, y0, dx, dy, wachspress - - integer :: i, j - - integer, parameter :: ni = 100 - integer, parameter :: nj = 100 - - integer, dimension(:), pointer :: & - nEdgesOnCell - - integer, dimension(:,:), pointer :: & - verticesOnCell - - real(kind=RKIND), dimension(:), pointer :: & - xVertex, & - yVertex - - integer, dimension(:), allocatable :: & - nEdgesOnCellSubset - - integer, dimension(:,:), allocatable :: & - vertexIndexSubset - - real(kind=RKIND), dimension(1) :: & - xbasis, ybasis - - real(kind=RKIND), dimension(1) :: & - varU, varV - - ! init variables - call MPAS_pool_get_array(mesh, "verticesOnCell", verticesOnCell) - call MPAS_pool_get_array(mesh, "nEdgesOnCell", nedgesOnCell) - call MPAS_pool_get_array(mesh, "xVertex", xVertex) - call MPAS_pool_get_array(mesh, "yVertex", yVertex) - - iCell = 1 - - x0 = 1e30 - y0 = 1e30 - - do iVertexOnCell = 1, nEdgesOnCell(iCell) - - iVertex = verticesOnCell(iVertexOnCell,iCell) - - x0 = min(x0,xVertex(iVertex)) - y0 = min(y0,yVertex(iVertex)) - - enddo ! - - dx = 16000.0_RKIND / real(ni,RKIND) - dy = 16000.0_RKIND / real(nj,RKIND) - - iVertexOnCell = 1 - - allocate(nEdgesOnCellSubset(nEdgesOnCell(iCell))) - allocate(vertexIndexSubset(nEdgesOnCell(iCell),nEdgesOnCell(iCell))) - - call wachspress_indexes(& - nEdgesOnCell(iCell), & - nEdgesOnCellSubset, & - vertexIndexSubset) - - do i = 1, ni - do j = 1, nj - - x = x0 + (real(i,RKIND) - 0.5_RKIND) * dx - 24000.0_RKIND - y = x0 + (real(j,RKIND) - 0.5_RKIND) * dy - 24000.0_RKIND - - xbasis(1) = x ; ybasis(1) = y - call wachspress_basis_derivative(& - nEdgesOnCell(iCell), & - iVertexOnCell, & - xbasis, & - ybasis, & - wachspressKappa(:,:,iCell), & - wachspressA(:,iCell), & - wachspressB(:,iCell), & - nEdgesOnCellSubset, & - vertexIndexSubset, & - varU, & - varV) - wachspress = varU(1) - - write(55,*) x+0.5_RKIND*dx,x-0.5_RKIND*dx,y+0.5_RKIND*dy,y-0.5_RKIND*dy, wachspress - - enddo ! j - enddo ! i - - deallocate(nEdgesOnCellSubset) - deallocate(vertexIndexSubset) - - end subroutine plot_wachspress_square!}}} - !----------------------------------------------------------------------- end module seaice_velocity_solver_wachspress diff --git a/src/core_sw/Registry.xml b/src/core_sw/Registry.xml index e072aba279..60a200c314 100644 --- a/src/core_sw/Registry.xml +++ b/src/core_sw/Registry.xml @@ -1,5 +1,5 @@ - + diff --git a/src/core_test/Registry.xml b/src/core_test/Registry.xml index 33640eb50f..b87e86b88a 100644 --- a/src/core_test/Registry.xml +++ b/src/core_test/Registry.xml @@ -1,5 +1,5 @@ - + diff --git a/src/framework/Makefile b/src/framework/Makefile index 4e635bbbc9..d19cd78677 100644 --- a/src/framework/Makefile +++ b/src/framework/Makefile @@ -94,7 +94,7 @@ mpas_bootstrapping.o: mpas_derived_types.o mpas_dmpar.o mpas_block_decomp.o mpas mpas_io_units.o: mpas_kind_types.o -mpas_threading.o: mpas_kind_types.o mpas_log.o +mpas_threading.o: mpas_kind_types.o mpas_stream_list.o: mpas_derived_types.o mpas_kind_types.o mpas_io_streams.o mpas_timekeeping.o regex_matching.o mpas_log.o diff --git a/src/framework/mpas_block_decomp.F b/src/framework/mpas_block_decomp.F index 8aef81bf02..8a5c75ce9c 100644 --- a/src/framework/mpas_block_decomp.F +++ b/src/framework/mpas_block_decomp.F @@ -120,7 +120,7 @@ subroutine mpas_block_decomp_cells_for_proc(dminfo, partial_global_graph_info, l open(unit=iunit, file=trim(filename), form='formatted', status='old', iostat=istatus) if (istatus /= 0) then - call mpas_log_write('Could not open block decomposition file for %i blocks.', MPAS_LOG_ERR, intArgs=(/dminfo % total_blocks/) ) + call mpas_log_write('Could not open block decomposition file for $i blocks.', MPAS_LOG_ERR, intArgs=(/dminfo % total_blocks/) ) call mpas_log_write('Filename: '//trim(filename), MPAS_LOG_CRIT) end if diff --git a/src/framework/mpas_dmpar.F b/src/framework/mpas_dmpar.F index ef82d27081..490687d095 100644 --- a/src/framework/mpas_dmpar.F +++ b/src/framework/mpas_dmpar.F @@ -6800,8 +6800,6 @@ subroutine mpas_dmpar_exch_group_add_field(domain, groupName, fieldName, timeLev do while (associated(exchGroupPtr)) if ( nLen == exchGroupPtr % nLen) then if ( groupName(1:nLen) == exchGroupPtr % groupName(1:exchGroupPtr % nLen) ) then - call mpas_pool_add_config(exchGroupPtr % fieldPool, fieldName, timeLevelLocal) - ! Add field to exchange group's fieldList if ( associated(exchGroupPtr % fieldList) ) then ! If the field is already added, find it. @@ -6923,9 +6921,6 @@ subroutine mpas_dmpar_exch_group_remove_field(domain, groupName, fieldName, iErr do while (associated(exchGroupPtr)) if ( nLen == exchGroupPtr % nLen) then if ( groupName(1:nLen) == exchGroupPtr % groupName(1:exchGroupPtr % nLen) ) then - ! Remove after the transition to exchange_field_lists is complete - call mpas_pool_remove_config(exchGroupPtr % fieldPool, fieldName) - if ( associated(exchGroupPtr % fieldList) ) then exchFieldListPtr => exchGroupPtr % fieldList nullify(prevFieldListPtr) @@ -7320,8 +7315,6 @@ subroutine mpas_dmpar_exch_group_reuse_halo_exch(domain, groupName, timeLevel, h else exchFieldListPtr % haloLayers(:) = .true. end if - call mpas_pool_remove_config(exchGroupPtr % fieldPool, exchFieldListPtr % fieldName) - call mpas_pool_add_config(exchGroupPtr % fieldPool, exchFieldListPtr % fieldName, timeLevelLocal) exchFieldListPtr => exchFieldListPtr % next end do end if diff --git a/src/framework/mpas_io.F b/src/framework/mpas_io.F index d38f206024..2c17d3c661 100644 --- a/src/framework/mpas_io.F +++ b/src/framework/mpas_io.F @@ -308,9 +308,11 @@ type (MPAS_IO_Handle_type) function MPAS_io_open(filename, mode, ioformat, ioCon MPAS_io_open % preexisting_file = .true. else pio_ierr = PIO_createfile(ioContext % pio_iosystem, MPAS_io_open % pio_file, pio_iotype, trim(filename), pio_mode) +#ifdef MPAS_DEBUG if (exists) then call mpas_log_write('MPAS I/O: Truncating existing data in output file '//trim(filename), MPAS_LOG_WARN) end if +#endif end if else inquire(file=trim(filename), exist=exists) diff --git a/src/operators/Makefile b/src/operators/Makefile index d697b2d1b0..b4d7085e90 100644 --- a/src/operators/Makefile +++ b/src/operators/Makefile @@ -9,7 +9,7 @@ OBJS = mpas_vector_operations.o \ mpas_tracer_advection_helpers.o \ mpas_tracer_advection_mono.o \ mpas_tracer_advection_std.o \ - mpas_geometry_utils.o + mpas_geometry_utils.o DEPS := $(shell find ../core_$(CORE)/ -type f -name "*.xml" ! -name "*processed.xml") @@ -27,7 +27,7 @@ mpas_spline_interpolation: mpas_tracer_advection_helpers.o: mpas_geometry_utils.o $(DEPS) mpas_tracer_advection_mono.o: mpas_tracer_advection_helpers.o mpas_tracer_advection_std.o: mpas_tracer_advection_helpers.o -mpas_geometry_utils.o: mpas_matrix_operations.o +mpas_geometry_utils.o: mpas_vector_operations.o mpas_matrix_operations.o clean: $(RM) *.o *.mod *.f90 libops.a diff --git a/src/operators/mpas_vector_operations.F b/src/operators/mpas_vector_operations.F index 3b49d790a4..099e4bf1d3 100644 --- a/src/operators/mpas_vector_operations.F +++ b/src/operators/mpas_vector_operations.F @@ -707,21 +707,21 @@ subroutine mpas_initialize_vectors(meshPool)!{{{ ! Initialize normal unit vectors at each edge ! These vectors point from cell to cell. - ! At boundaries, one cell does not exist, so it points from cell to edge. + ! At boundaries, one cell does not exist, so it points from cell to edge or from edge to cell. do iEdge = 1,nEdges cell1 = cellsOnEdge(1,iEdge) cell2 = cellsOnEdge(2,iEdge) if (cell1 == nCells+1) then ! this is a boundary edge - ! the normal points from the cell location to the edge location + ! the normal points from the edge location to the cell location if (is_periodic) then - edgeNormalVectors(1,iEdge) = mpas_fix_periodicity(xEdge(iEdge), xCell(cell2), x_period) - xCell(cell2) - edgeNormalVectors(2,iEdge) = mpas_fix_periodicity(yEdge(iEdge), yCell(cell2), y_period) - yCell(cell2) - edgeNormalVectors(3,iEdge) = zEdge(iEdge) - zCell(cell2) + edgeNormalVectors(1,iEdge) = xCell(cell2) - mpas_fix_periodicity(xEdge(iEdge), xCell(cell2), x_period) + edgeNormalVectors(2,iEdge) = yCell(cell2) - mpas_fix_periodicity(yEdge(iEdge), yCell(cell2), y_period) + edgeNormalVectors(3,iEdge) = zCell(cell2) - zEdge(iEdge) else - edgeNormalVectors(1,iEdge) = xEdge(iEdge) - xCell(cell2) - edgeNormalVectors(2,iEdge) = yEdge(iEdge) - yCell(cell2) - edgeNormalVectors(3,iEdge) = zEdge(iEdge) - zCell(cell2) + edgeNormalVectors(1,iEdge) = xCell(cell2) - xEdge(iEdge) + edgeNormalVectors(2,iEdge) = yCell(cell2) - yEdge(iEdge) + edgeNormalVectors(3,iEdge) = zCell(cell2) - zEdge(iEdge) end if else if (cell2 == nCells+1) then ! this is a boundary edge diff --git a/testing_and_setup/compass/clean_testcase.py b/testing_and_setup/compass/clean_testcase.py index 0f6588065c..499c94f645 100755 --- a/testing_and_setup/compass/clean_testcase.py +++ b/testing_and_setup/compass/clean_testcase.py @@ -6,163 +6,207 @@ It will remove directories / driver scripts that were generated as part of setting up a test case. """ - -import sys, os, shutil, fnmatch, re +import sys +import os +import shutil +import fnmatch +import re import argparse import subprocess import xml.etree.ElementTree as ET if __name__ == "__main__": - # Define and process input arguments - parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter) - - parser.add_argument("-o", "--core", dest="core", help="Core that conatins configurations to clean", metavar="CORE") - parser.add_argument("-c", "--configuration", dest="configuration", help="Configuration to clean", metavar="CONFIG") - parser.add_argument("-r", "--resolution", dest="resolution", help="Resolution of configuration to clean", metavar="RES") - parser.add_argument("-t", "--test", dest="test", help="Test name within a resolution to clean", metavar="TEST") - parser.add_argument("-n", "--case_number", dest="case_num", help="Case number to clean, as listed from list_testcases.py. Can be a comma delimited list of case numbers.", metavar="NUM") - parser.add_argument("-q", "--quiet", dest="quiet", help="If set, script will not write a command_history file", action="store_true") - parser.add_argument("-a", "--all", dest="clean_all", help="Is set, the script will clean all test cases in the work_dir.", action="store_true") - parser.add_argument("--work_dir", dest="work_dir", help="If set, script will clean case directories in work_dir rather than the current directory.", metavar="PATH") - - args = parser.parse_args() - - if not args.case_num and ( not args.core and not args.configuration and not args.resolution and not args.test) and not args.clean_all: - print 'Must be run with either the --case_number argument, the --all argument, or all of the core, configuration, resolution, and test arguments.' - parser.error(' Invalid configuration. Exiting...') - - if args.case_num and args.core and args.configuration and args.resoltuion and args.test and args.clean_all: - print 'Can only be configured with either --case_number (-n), --all (-a), or all of --core (-o), --configuration (-c), --resolution (-r), and --test (-t).' - parser.error(' Invalid configuration. Too many options used. Exiting...') - - if not args.clean_all: - if args.case_num: - use_case_list = True - case_list = args.case_num.split(',') - else: - use_case_list = False - case_list = list() - case_list.append(0) - else: - use_case_list = True - valid_case = 1 - case_num = 1 - case_list = list() - - regex = re.compile('(\d):') - core_configuration = subprocess.check_output(['./list_testcases.py']) - for line in core_configuration.split('\n'): - if not regex.search(line) == None: - conf_arr = line.replace(":", " ").split() - case_num = int(conf_arr[0]) - del conf_arr - case_list.append(case_num) - del core_configuration - del regex - - if not args.work_dir: - args.work_dir = os.getcwd() - - args.work_dir = os.path.abspath(args.work_dir) - - # Build variables for history output - old_dir = os.getcwd() - os.chdir( os.path.dirname( os.path.realpath(__file__) ) ) - git_version = subprocess.check_output(['git', 'describe', '--tags', '--dirty']) - git_version = git_version.strip('\n') - os.chdir(old_dir) - calling_command = "" - write_history = False - for arg in sys.argv: - calling_command = "%s%s "%(calling_command, arg) - - # Iterate over all cases in the case_list. - # There is only one if the (-o, -c, -r) options were used in place of (-n) - for case_num in case_list: - # If we're using a case_list, determine the core, configuration, and - # resolution for the current test case. - if use_case_list: - core_configuration = subprocess.check_output(['./list_testcases.py', '-n', '%d'%(int(case_num))]) - config_options = core_configuration.strip('\n').split(' ') - args.core = config_options[1] - args.configuration = config_options[3] - args.resolution = config_options[5] - args.test = config_options[7] - - # Setup each xml file in the configuration directory: - test_path = '%s/%s/%s/%s'%(args.core, args.configuration, args.resolution, args.test) - work_dir = '%s/%s'%(args.work_dir, test_path) - - # Only write history if we did something... - write_history = False - - # Loop over all files in test_path that have the .xml extension. - for file in os.listdir('%s'%(test_path)): - if fnmatch.fnmatch(file, '*.xml'): - # Build full file name - config_file = '%s/%s'%(test_path, file) - - # Parse file - config_tree = ET.parse(config_file) - config_root = config_tree.getroot() - - # Process files - if config_root.tag == 'config': - case_dir = config_root.attrib['case'] - - case_paths = case_dir.split('/') - # Determine the base directory in the case path, to delete - case_base = case_paths[0] - - # Delete the top level directory that was created, if it exists. - if os.path.exists('%s/%s'%(work_dir, case_base)): - if os.path.isdir('%s/%s'%(work_dir, case_base)): - shutil.rmtree('%s/%s'%(work_dir, case_base)) - write_history = True - print ' -- Removed case %s/%s'%(work_dir, case_base) - - # Process files - elif config_root.tag == 'driver_script': - script_name = config_root.attrib['name'] - - # Delete script if it exists - if os.path.exists('%s/%s'%(work_dir, script_name)): - os.remove('%s/%s'%(work_dir, script_name)) - write_history = True - print ' -- Removed driver script %s/%s'%(work_dir, script_name) - - del config_tree - del config_root - - # Write the history of this command to the command_history file, for - # provenance. - if write_history and not args.quiet: - history_file_path = '%s/command_history'%(args.work_dir) - if os.path.exists(history_file_path): - history_file = open(history_file_path, 'a') - history_file.write('\n') - else: - history_file = open(history_file_path, 'w') - - history_file.write('***********************************************************************\n') - history_file.write('git_version: %s\n'%(git_version)) - history_file.write('command: %s\n'%(calling_command)) - history_file.write('setup the following cases:\n') - if use_case_list: - for case_num in case_list: - core_configuration = subprocess.check_output(['./list_testcases.py', '-n', '%d'%(int(case_num))]) - config_options = core_configuration.strip('\n').split(' ') - history_file.write('\n') - history_file.write('\tcore: %s\n'%(config_options[1])) - history_file.write('\tconfiguration: %s\n'%(config_options[3])) - history_file.write('\tresolution: %s\n'%(config_options[5])) - history_file.write('\ttest: %s\n'%(config_options[7])) - else: - history_file.write('core: %s\n'%(args.core)) - history_file.write('configuration: %s\n'%(args.configuration)) - history_file.write('resolution: %s\n'%(args.resolution)) - history_file.write('test: %s\n'%(args.test)) - - history_file.write('***********************************************************************\n') - history_file.close() - + # Define and process input arguments + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument("-o", "--core", dest="core", + help="Core that conatins configurations to clean", + metavar="CORE") + parser.add_argument("-c", "--configuration", dest="configuration", + help="Configuration to clean", metavar="CONFIG") + parser.add_argument("-r", "--resolution", dest="resolution", + help="Resolution of configuration to clean", + metavar="RES") + parser.add_argument("-t", "--test", dest="test", + help="Test name within a resolution to clean", + metavar="TEST") + parser.add_argument("-n", "--case_number", dest="case_num", + help="Case number to clean, as listed from " + "list_testcases.py. Can be a comma delimited " + "list of case numbers.", metavar="NUM") + parser.add_argument("-q", "--quiet", dest="quiet", + help="If set, script will not write a command_history " + "file", action="store_true") + parser.add_argument("-a", "--all", dest="clean_all", + help="Is set, the script will clean all test cases " + "in the work_dir.", action="store_true") + parser.add_argument("--work_dir", dest="work_dir", + help="If set, script will clean case directories in " + "work_dir rather than the current directory.", + metavar="PATH") + + args = parser.parse_args() + + if not args.case_num and (not args.core and not args.configuration and + not args.resolution and not args.test) \ + and not args.clean_all: + print 'Must be run with either the --case_number argument, the ' \ + '--all argument, or all of the core, configuration, ' \ + 'resolution, and test arguments.' + parser.error(' Invalid configuration. Exiting...') + + if args.case_num and args.core and args.configuration and args.resoltuion \ + and args.test and args.clean_all: + print 'Can only be configured with either --case_number (-n), --all ' \ + '(-a), or all of --core (-o), --configuration (-c), ' \ + '--resolution (-r), and --test (-t).' + parser.error(' Invalid configuration. Too many options used. ' + 'Exiting...') + + if not args.clean_all: + if args.case_num: + use_case_list = True + case_list = args.case_num.split(',') + else: + use_case_list = False + case_list = list() + case_list.append(0) + else: + use_case_list = True + valid_case = 1 + case_num = 1 + case_list = list() + + regex = re.compile('(\d):') + core_configuration = subprocess.check_output(['./list_testcases.py']) + for line in core_configuration.split('\n'): + if regex.search(line) is not None: + conf_arr = line.replace(":", " ").split() + case_num = int(conf_arr[0]) + del conf_arr + case_list.append(case_num) + del core_configuration + del regex + + if not args.work_dir: + args.work_dir = os.getcwd() + + args.work_dir = os.path.abspath(args.work_dir) + + # Build variables for history output + old_dir = os.getcwd() + os.chdir(os.path.dirname(os.path.realpath(__file__))) + git_version = subprocess.check_output(['git', 'describe', '--tags', + '--dirty']) + git_version = git_version.strip('\n') + os.chdir(old_dir) + calling_command = "" + write_history = False + for arg in sys.argv: + calling_command = "{}{} ".format(calling_command, arg) + + # Iterate over all cases in the case_list. + # There is only one if the (-o, -c, -r) options were used in place of (-n) + for case_num in case_list: + # If we're using a case_list, determine the core, configuration, and + # resolution for the current test case. + if use_case_list: + core_configuration = subprocess.check_output( + ['./list_testcases.py', '-n', + '{:d}'.format(int(case_num))]) + config_options = core_configuration.strip('\n').split(' ') + args.core = config_options[1] + args.configuration = config_options[3] + args.resolution = config_options[5] + args.test = config_options[7] + + # Setup each xml file in the configuration directory: + test_path = '{}/{}/{}/{}'.format(args.core, args.configuration, + args.resolution, args.test) + work_dir = '{}/{}'.format(args.work_dir, test_path) + + # Only write history if we did something... + write_history = False + + # Loop over all files in test_path that have the .xml extension. + for file in os.listdir('{}'.format(test_path)): + if fnmatch.fnmatch(file, '*.xml'): + # Build full file name + config_file = '{}/{}'.format(test_path, file) + + # Parse file + config_tree = ET.parse(config_file) + config_root = config_tree.getroot() + + # Process files + if config_root.tag == 'config': + case_dir = config_root.attrib['case'] + + case_paths = case_dir.split('/') + # Determine the base directory in the case path, to delete + case_base = case_paths[0] + + # Delete the top level directory that was created, if it + # exists. + if os.path.exists('{}/{}'.format(work_dir, case_base)): + if os.path.isdir('{}/{}'.format(work_dir, case_base)): + shutil.rmtree('{}/{}'.format(work_dir, case_base)) + write_history = True + print ' -- Removed case {}/{}'.format(work_dir, + case_base) + + # Process files + elif config_root.tag == 'driver_script': + script_name = config_root.attrib['name'] + + # Delete script if it exists + if os.path.exists('{}/{}'.format(work_dir, script_name)): + os.remove('{}/{}'.format(work_dir, script_name)) + write_history = True + print ' -- Removed driver script ' \ + '{}/{}'.format(work_dir, script_name) + + del config_tree + del config_root + + # Write the history of this command to the command_history file, for + # provenance. + if write_history and not args.quiet: + history_file_path = '{}/command_history'.format(args.work_dir) + if os.path.exists(history_file_path): + history_file = open(history_file_path, 'a') + history_file.write('\n') + else: + history_file = open(history_file_path, 'w') + + history_file.write('*************************************************' + '**********************\n') + history_file.write('git_version: {}\n'.format(git_version)) + history_file.write('command: {}\n'.format(calling_command)) + history_file.write('setup the following cases:\n') + if use_case_list: + for case_num in case_list: + core_configuration = subprocess.check_output( + ['./list_testcases.py', '-n', + '{:d}'.format(int(case_num))]) + config_options = core_configuration.strip('\n').split(' ') + history_file.write('\n') + history_file.write(' core: {}\n'.format(config_options[1])) + history_file.write(' configuration: {}\n'.format( + config_options[3])) + history_file.write(' resolution: {}\n'.format( + config_options[5])) + history_file.write(' test: {}\n'.format(config_options[7])) + else: + history_file.write('core: {}\n'.format(args.core)) + history_file.write('configuration: {}\n'.format( + args.configuration)) + history_file.write('resolution: {}\n'.format(args.resolution)) + history_file.write('test: {}\n'.format(args.test)) + + history_file.write('*************************************************' + '**********************\n') + history_file.close() + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/testing_and_setup/compass/general.config.landice b/testing_and_setup/compass/general.config.landice new file mode 100644 index 0000000000..9c53cdca7e --- /dev/null +++ b/testing_and_setup/compass/general.config.landice @@ -0,0 +1,44 @@ +# This file is the landice core's configuration file. It is specific to the landice +# core, and a specific machine. Each machine will configure this file +# differently, but it can be used to point on version of the testing +# infrastructure at a different version of the model. + + +# The namelists section describes required namelist templates that are used to +# generate specific namelist files. In the landice core, there is only a 'forward' +# mode template, which typically would point to the namelist.landice generated in +# default_inputs after a successful build of the model. +[namelists] +forward = FULL_PATH_TO_FORWARD_TEMPLATE_NAMELIST + + +# The streams section describes required streams file templates that are used to +# generate specific streams files. In the landice core, there is only a 'forward' +# mode template, which generally points to the streams.landice generated in +# default_inputs after a successful build of the model. +[streams] +forward = FULL_PATH_TO_FORWARD_TEMPLATE_STREAMS + +# The executables section defines paths to required executables. These +# executables are provided for use by specific test cases. +# Full paths should be provided in order to access the executables from +# anywhere on the machine. +[executables] +model = FULL_PATH_TO_MODEL +mesh_converter = FULL_PATH_TO_MESH_CONVERTER +cell_culler = FULL_PATH_TO_CELL_CULLER +metis = FULL_PATH_TO_METIS +grid_to_li_grid = FULL_PATH_TO_create_landice_grid_from_generic_MPAS_grid.py +periodic_hex = FULL_PATH_TO_PERIODIC_HEX + + +# The paths section describes paths that are used within the landice core test +# cases. +[paths] +# The mesh_database and the initial_condition_database are locations where +# meshes / initial conditions might be found on a specific machine. They can be +# the same directory, or different directory. Additionally, if they are empty +# some test cases might download data into them, which will then be reused if +# the test case is run again later. +mesh_database = FULL_PATH_TO_LOCAL_MESH_DATABASE +initial_condition_database = NOT_CURRENTLY_USED diff --git a/testing_and_setup/compass/general.config.ocean b/testing_and_setup/compass/general.config.ocean index c537fe6936..6a8342cb18 100644 --- a/testing_and_setup/compass/general.config.ocean +++ b/testing_and_setup/compass/general.config.ocean @@ -30,7 +30,9 @@ init = FULL_PATH_TO_INIT_TEMPLATE_STREAMS model = FULL_PATH_TO_MODEL mesh_converter = FULL_PATH_TO_MESH_CONVERTER cell_culler = FULL_PATH_TO_CELL_CULLER +mask_creator = FULL_PATH_TO_MASK_CREATOR metis = FULL_PATH_TO_METIS +mesh_generator = FULL_PATH_TO_EXECUTABLE_MESH_GENERATION_SCRIPT # The paths section describes paths that are used within the ocean core test @@ -44,3 +46,5 @@ metis = FULL_PATH_TO_METIS # the test case is run again later. mesh_database = FULL_PATH_TO_LOCAL_MESH_DATABASE initial_condition_database = FULL_PATH_TO_LOCAL_INITIAL_CONDITION_DATABASE +geometric_features = FULL_PATH_TO_LOCAL_CHECKOUT_OF_GEOMETRIC_FEATURES_DATABASE +mesh_scripts = FULL_PATH_TO_LOCAL_CHECKOUT_OF_MESH_GENERATION_SCRIPTS diff --git a/testing_and_setup/compass/landice/EISMINT1/50000m/EISMINT-1_50000m_template.xml b/testing_and_setup/compass/landice/EISMINT1/50000m/EISMINT-1_50000m_template.xml new file mode 100644 index 0000000000..c1fa8b9500 --- /dev/null +++ b/testing_and_setup/compass/landice/EISMINT1/50000m/EISMINT-1_50000m_template.xml @@ -0,0 +1,58 @@ + + diff --git a/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_driver.xml b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_driver.xml new file mode 100644 index 0000000000..0c49af0119 --- /dev/null +++ b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_driver.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_run_model_step.xml b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_run_model_step.xml new file mode 100644 index 0000000000..eb4d2c0773 --- /dev/null +++ b/testing_and_setup/compass/landice/EISMINT1/50000m/MovingMargin1/config_run_model_step.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + +