!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! !! !! GNU General Public License !! !! !! !! This file is part of the Flexible Modeling System (FMS). !! !! !! !! FMS is free software; you can redistribute it and/or modify !! !! it and are expected to follow the terms of the GNU General Public !! !! License as published by the Free Software Foundation. !! !! !! !! FMS is distributed in the hope that it will be useful, !! !! but WITHOUT ANY WARRANTY; without even the implied warranty of !! !! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !! !! GNU General Public License for more details. !! !! !! !! You should have received a copy of the GNU General Public License !! !! along with FMS; if not, write to: !! !! Free Software Foundation, Inc. !! !! 59 Temple Place, Suite 330 !! !! Boston, MA 02111-1307 USA !! !! or see: !! !! http://www.gnu.org/licenses/gpl.txt !! !! !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! module atmosphere_mod ! ! ! B.L. Samuels ! ! ! Zhi Liang ! ! ! interface for spherical grid dynamical core and physics ! for the energy balance model (EBM) ! ! ! ! The Energy Balance Model is a simple spectral atmospheric model ! using diffusion and radiative balance. The EBM solves two ! prognostic equations for atmospheric temperature and specific ! humidity ! ! ! ! The Energy Balance Model was originally developed at GFDL ! by I. Held and extensively modified by J. Russell to be ! MOM4 compatible. Zhi Liang made the appropriate modifications ! for the code to be fms_io compatible and meet RTS standards. ! No current reference of this code exists. ! ! ! Zhang,Rong and Geoffrey Vallis, " The Role of the Deep Western Boundary Current in the ! Separation of the Gulf Stream", Journal of Physical Oceanography,submitted 2004 ! see Appendix A ! ! ! Note by J. Russell: the number of processors you use must be less than the number of ! atmospheric latitude boxes and must divide into that number equally ! ! !----------------------------------------------------------------------- ! ! interface for spherical grid dynamical core and physics ! for the energy balance model (EBM) ! !----------------------------------------------------------------------- !---------------- m o d u l e i n f o r m a t i o n ------------------ use mpp_mod, only: mpp_pe, stdout, stdlog, mpp_error use fms_mod, only: file_exist, close_file, read_data, write_data use fms_mod, only: error_mesg, FATAL, check_nml_error, open_namelist_file use fms_mod, only: write_version_number use constants_mod, only: radius, rdgas, rvgas, cp_air, hlv, hlf, tfreeze, stefan, PI use transforms_mod, only: transforms_init, grid_domain, spectral_domain use transforms_mod, only: trans_grid_to_spherical, trans_spherical_to_grid use transforms_mod, only: get_deg_lat, get_eigen_laplacian, compute_gradient_cos use transforms_mod, only: compute_div, get_grid_boundaries, divide_by_cos2 use transforms_mod, only: triangular_truncation, get_grid_domain, get_spec_domain use transforms_mod, only: get_wts_lat use time_manager_mod, only: time_type, get_time, get_date, operator(+) use ebm_diagnostics_mod, only: ebm_diagnostics_init, ebm_diagnostics_up, ebm_diagnostics_down use astronomy_mod, only: astronomy_init, daily_mean_solar, annual_mean_solar use sat_vapor_pres_mod, only: escomp, descomp use mpp_domains_mod, only: domain2D use tracer_manager_mod, only: get_number_tracers use xgrid_mod, only: grid_box_type !================================================================================== implicit none private !================================================================================== ! version information character(len=128), parameter :: version = & '$Id: atmosphere.F90,v 17.0 2009/07/21 02:52:53 fms Exp $' character(len=128), parameter :: tagname = & '$Name: mom4p1_pubrel_dec2009_nnz $' !================================================================================== ! public interfaces public :: atmosphere_init, atmosphere_down, atmosphere_up public :: atmosphere_end, atmosphere_resolution, atmosphere_boundary public :: get_bottom_mass, get_bottom_wind, get_atmosphere_axes public :: surf_diff_type, radiation, precipitation public :: diffusion, atmosphere_domain public :: get_stock_pe, atmosphere_cell_area public :: atmosphere_restart !================================================================================== ! module variables ! type surf_diff_type real, pointer, dimension(:,:) :: dtmass => NULL() ! dt/mass, where dt = atmospheric time step (sec) ! mass = mass of lowest atmospheric layer (Kg/m2) real, pointer, dimension(:,:) :: dflux_t => NULL() ! derivative of the temperature flux at the top of the lowest ! atmospheric layer with respect to the temperature ! of that layer (J/(m2 K)) real, pointer, dimension(:,:,:) :: dflux_tr => NULL() ! derivative of the flux of specific humidity ! at the top of the lowest atmospheric layer with respect to ! the specific humidity of that layer (--/(m2 K)) real, pointer, dimension(:,:) :: delta_t => NULL() ! the increment in temperature in the lowest atmospheric ! layer (((i+1)-(i-1) if atmos model is leapfrog) (K) ! (defined in gcm_vert_diff_down as the increment computed up ! to this point in model, including effect of vertical ! diffusive flux at top of lowest model level, presumed ! to be modified to include effects of surface fluxes ! outside of this module, then used to start upward ! tridiagonal sweep, real, pointer, dimension(:,:,:):: delta_tr => NULL() ! similarly for the increment in specific humidity ! (non-dimensional = Kg/Kg) real, pointer, dimension(:,:) :: delta_u => NULL() real, pointer, dimension(:,:) :: delta_v => NULL() real, pointer, dimension(:,:) :: sst_miz => NULL() end type surf_diff_type ! complex, allocatable, dimension(:,:) :: ts ! atmospheric temperature deg_k complex, allocatable, dimension(:,:) :: qs ! specific humidity no units complex, allocatable, dimension(:,:) :: dt_ts ! rate of change in atmospheric temperature (degK/s) complex, allocatable, dimension(:,:) :: dt_qs ! rate of change in specific humidity (1/s) real , allocatable, dimension(:,:) :: tg ! atmospheric temperature ( deg_k ) real , allocatable, dimension(:,:) :: qg ! specific humidity ( no units ) real , allocatable, dimension(:,:) :: dt_tg ! rate of change in atmospheric temperature (degK/s) real , allocatable, dimension(:,:) :: dt_qg ! rate of change in specific humidity (1/s) real , allocatable, dimension(:,:) :: u_bot ! zonal wind component at lowest model level (m/s) real , allocatable, dimension(:,:) :: v_bot ! meridional wind component at lowest model level (m/s) real , allocatable, dimension(:,:) :: t_bot ! temperature at lowest model level (deg k) real , allocatable, dimension(:,:) :: q_bot ! specific humidity at lowest model level (kg/kg) real , allocatable, dimension(:) :: deg_lat, deg_lon, rad_lat, annual_solar, annual_cosz integer, dimension(4) :: axis_id ! axes identifiers provided by ebm_diagnostics_mod real :: dt ! time step (secs) type(time_type) :: Time, Time_next, Time_step integer :: is,ie,js,je,ms,me,ns,ne real, parameter :: d622 = rdgas/rvgas real, parameter :: d378 = 1.-d622 logical :: module_is_initialized =.false. integer :: q_ind = 1 !----------------------------------------------------------------------- !---- namelist (saved in file input.nml) ---- ! ! namelist parameters with default values ! Default values are based on ebm run with 1p5 ocean model ! 120 x 65 as run by Joellen Russell integer :: lon_max = 180 integer :: lat_max = 120 integer :: num_fourier = 42 real :: diff = 1.e04 real :: nu = 4.0e16 real :: solar_constant = 1360.0 real :: atm_abs = 0.15 real :: atm_ref = 0.05 real :: mass = 8.e03 real :: rh = 0.8 real :: t_bot_atm = 30.0 logical :: seasonal_solar = .true. logical :: diff_is_uniform = .false. real :: tg_init = 273.0 real :: gustiness = 1.0 logical :: debug_ebm_atm = .false. ! ! namelist/atmosphere_nml/ lon_max, lat_max, num_fourier, & ! mass, rh, t_bot_atm, & ! solar_constant, atm_abs, atm_ref, seasonal_solar, & ! diff_is_uniform, nu, diff, tg_init, debug_ebm_atm ! ! This is the longitude of the spectral atmospheric grid ! ! ! This is the latitude of the spectral atmospheric grid ! Note that the number of processors you use must be less than the number of ! atmospheric latitude boxes and must divide into that number equally ! ! ! num_fourier is used along with lon_max, lat_max in the spectral transforms of grids ! ! ! background atmospheric lateral eddy diffusion coefficient for the EBM ! ! ! scaling factor for spherical grid analysis used in diffusion subroutine ! ! ! standard value for the annual mean solar flux at the top of atmosphere. ! Default value if seasonal_solar=.false. ! ! ! atmospheric absorption (fraction) ! ! ! atmosphere reflectance (fraction) ! ! ! mass = mass of lowest atmospheric layer (Kg/m2) ! ! ! relative humidity (fraction) ! ! ! temperature at the bottom of the atmosphere ! ! ! If .true., seasonal solar value is computed ! ! ! ! ! constant value for inital atmospheric temperature (degK) ! ! ! For debugging purposes. ! namelist/atmosphere_nml/ lon_max, lat_max, num_fourier, mass, rh, t_bot_atm, & solar_constant, atm_abs, atm_ref, seasonal_solar, & diff_is_uniform, nu, diff, tg_init, debug_ebm_atm, gustiness ! !================================================================================== contains ! ================================================================================== ! ================================================================================== ! ! ! ! public routine required for atmospheric components of coupled models ! Read in restart files and initialize arrays ! ! ! ! public routine required for atmospheric components of coupled models ! Read in restart files and initialize arrays ! ! ! ! ! ! The base (or initial) time of the experiment. ! ! ! The current time. ! ! ! The atmospheric model/physics time step. ! ! ! The surface terms for vertical diffusion that are exchanged ! with other component models. On input fields have not been ! allocated, on output all arrays are allocated. ! ! ! Contains information about the grid cells ! subroutine atmosphere_init(Time_init, Time_in, Time_step_in, Surf_diff, Grid_box) type(time_type), intent(in) :: Time_init, Time_in, Time_step_in type(surf_diff_type), intent(inout) :: Surf_diff type(grid_box_type), intent(inout) :: Grid_box integer :: ierr, io, unit, seconds, days, m, n character(len=64) :: filename real, allocatable, dimension(:,:) :: real_part, imag_part !----------------------------------------------------------------------------------------- ! managing time Time = Time_in Time_step = Time_step_in call get_time(Time_step, seconds, days) dt = float(86400*days + seconds) !----------------------------------------------------------------------------------------- ! read namelist and copy to logfile unit = open_namelist_file() read (unit, nml=atmosphere_nml,iostat=io) write (stdout(),'(/)') write (stdout(), atmosphere_nml) write (stdlog(), atmosphere_nml) ierr = check_nml_error(io, 'atmosphere_nml') call close_file(unit) !--- write out version information call write_version_number( version, tagname ) !----------------------------------------------------------------------------------------- ! Initialize spherical fourier call transforms_init(radius, lat_max, lon_max, num_fourier, 1, num_fourier+1) !----------------------------------------------------------------------------------------- call get_grid_domain(is,ie,js,je) call get_spec_domain(ms,me,ns,ne) ! allocate module variables allocate ( tg (is:ie, js:je) ) allocate ( qg (is:ie, js:je) ) allocate ( dt_tg (is:ie, js:je) ) allocate ( dt_qg (is:ie, js:je) ) allocate ( u_bot (is:ie, js:je) ) allocate ( v_bot (is:ie, js:je) ) allocate ( t_bot (is:ie, js:je) ) allocate ( q_bot (is:ie, js:je) ) allocate ( ts (ms:me, ns:ne) ) allocate ( qs (ms:me, ns:ne) ) allocate ( dt_ts (ms:me, ns:ne) ) allocate ( dt_qs (ms:me, ns:ne) ) allocate ( deg_lon (is:ie) ) allocate ( deg_lat (js:je) ) allocate ( rad_lat (js:je) ) allocate ( annual_cosz (js:je) ) allocate ( annual_solar (js:je) ) allocate (real_part( ms:me, ns:ne) ) allocate (imag_part( ms:me, ns:ne) ) ! hack: in case EBM use more than 1 tracer other than water vapor, revamp this code. ! fil allocate (Surf_diff%dtmass (is:ie, js:je) ) allocate (Surf_diff%dflux_t (is:ie, js:je) ) allocate (Surf_diff%dflux_tr (is:ie, js:je, q_ind) ) allocate (Surf_diff%delta_t (is:ie, js:je) ) allocate (Surf_diff%delta_tr (is:ie, js:je, q_ind) ) allocate (Surf_diff%delta_u (is:ie, js:je) ) allocate (Surf_diff%delta_v (is:ie, js:je) ) Surf_diff%dtmass = 0.0 Surf_diff%dflux_t = 0.0 Surf_diff%dflux_tr = 0.0 Surf_diff%delta_t = 0.0 Surf_diff%delta_tr = 0.0 Surf_diff%delta_u = 0.0 Surf_diff%delta_v = 0.0 !----------------------------------------------------------------------------------------- ! these elements of Surf_diff are static Surf_diff%dtmass = dt/mass Surf_diff%dflux_t = 0.0 Surf_diff%dflux_tr = 0.0 !----------------------------------------------------------------------------------------- ! read restart filename = 'INPUT/atmosphere.res.nc' if(file_exist(filename)) then call read_data(filename, 'ts_real', real_part, spectral_domain) call read_data(filename, 'ts_imag', imag_part, spectral_domain) do n = ns, ne do m = ms, me ts(m,n) = cmplx(real_part(m,n),imag_part(m,n)) enddo enddo call read_data(filename, 'qs_real', real_part, spectral_domain) call read_data(filename, 'qs_imag', imag_part, spectral_domain) do n = ns, ne do m = ms, me qs(m,n) = cmplx(real_part(m,n),imag_part(m,n)) enddo enddo call read_data(filename, 'tg', tg, grid_domain) call read_data(filename, 'qg', qg, grid_domain) else ! tg-initial atmospheric temperature default value 273 deg Kelvin ! qg-initial atmospheric specific humidity tg = tg_init qg = rh*sat_mixing_ratio(tg) call trans_grid_to_spherical(tg, ts) call trans_grid_to_spherical(qg, qs) endif v_bot = 0.0 t_bot = tg + t_bot_atm q_bot = rh*sat_mixing_ratio(t_bot) ! initalize other modules call ebm_diagnostics_init(Time, lon_max, lat_max, axis_id) call astronomy_init() ! get latitudes of grid points in degrees and radians call get_deg_lat(deg_lat) rad_lat = deg_lat*PI/180. ! if annual mean option is used, compute solar radiation here ! Use version for spectral 2-layer model if(.not.seasonal_solar) call annual_mean_solar ( rad_lat, annual_cosz, annual_solar ) module_is_initialized = .true. return end subroutine atmosphere_init ! ! ================================================================================== ! ! ! public routine required for atmospheric components of coupled models ! ! ! atmosphere_down ! This routine calls the dynamical core and the ! "downward pass" of the atmospheric physics. ! It should only be called once per time step and before ! calling atmosphere_up. ! ! ! ! time at the current time level (tau) ! ! ! fraction (0. to 1.) of underlying surface which covered by land ! ! ! surface (skin) temperature (in deg k) ! ! ! surface albedo ! ! ! momentum roughness length (units=m) ! ! ! friction velocity ! ! ! buoyancy scale ! ! ! moisture scale ! ! ! derivative of zonal wind stress w.r.t. the lowest level wind speed ! ! ! derivative of meridional wind stress w.r.t. the lowest level wind speed ! ! ! wind gustiness ! ! ! cosine of the zenith angle ! ! ! net shortwave surface flux (down minus up) (in watts/m**2) ! ! ! downward longwave surface flux (in watts/m**2) ! ! ! Surface diffusion terms computed by the vertical diffusion scheme ! subroutine atmosphere_down(Time, frac_land, t_surf, albedo, albedo_vis_dir, & albedo_nir_dir, albedo_vis_dif, albedo_nir_dif, & rough_mom, u_star, b_star, q_star, dtau_du, dtau_dv, & tau_x, tau_y, gust, coszen, net_surf_sw_down, & flux_sw_dir, flux_sw_dif, flux_sw_down_vis_dir, & flux_sw_down_vis_dif, flux_sw_down_total_dir, & flux_sw_down_total_dif, flux_sw_vis, & flux_sw_vis_dir, flux_sw_vis_dif,surf_lw_down, & Surf_diff) ! public routine required for atmospheric components of coupled models type(time_type), intent(in) :: Time real, intent(in), dimension(is:ie,js:je) :: frac_land, t_surf, albedo, rough_mom real, intent(in), dimension(is:ie,js:je) :: albedo_vis_dir, albedo_nir_dir real, intent(in), dimension(is:ie,js:je) :: albedo_vis_dif, albedo_nir_dif real, intent(in), dimension(is:ie,js:je) :: u_star, b_star, q_star, dtau_du, dtau_dv real, intent(inout), dimension(is:ie,js:je) :: tau_x, tau_y real, intent(out), dimension(is:ie,js:je) :: gust, coszen, net_surf_sw_down, surf_lw_down real, intent(out), dimension(is:ie,js:je) :: flux_sw_dir, flux_sw_dif, flux_sw_down_vis_dir real, intent(out), dimension(is:ie,js:je) :: flux_sw_down_vis_dif, flux_sw_down_total_dir real, intent(out), dimension(is:ie,js:je) :: flux_sw_down_total_dif, flux_sw_vis real, intent(out), dimension(is:ie,js:je) :: flux_sw_vis_dir, flux_sw_vis_dif type(surf_diff_type), intent(inout) :: Surf_diff ! initialize tendencies of temperature and mixing ratio to zero dt_tg = 0.0 dt_qg = 0.0 ! add temperature tendency due to radiation, and compute radiative fluxes and ! zenith angle needed by flux module call radiation (Time, t_surf, albedo, coszen, net_surf_sw_down, surf_lw_down) ! ! VIS NIR VIS+NIR !----------------------------------------------------- ! | | | ! DIR |flux_sw_vis_dir | | flux_sw_dir ! | | | !----------------------------------------------------- ! | | | ! DIF |flux_sw_vis_dif | | flux_sw_dif !----------------------------------------------------- ! | | | !DIR+DIF|flux_sw_vis | | flux_sw ! | | |(diagnostic only) !----------------------------------------------------- ! bls-total Short wave is equally divided into VIS and NIR ! flux_sw_dif - total Diffusive Short wave : VIS + NIR flux_sw_dif = .5*net_surf_sw_down ! flux_sw_dir - total Direct Short wave : VIS + NIR flux_sw_dir = net_surf_sw_down - flux_sw_dif ! bls- Visible Direct and Diffusive short wave will be assumed to be ! half of the total VIS+DIR components flux_sw_vis_dir = .5*flux_sw_dir flux_sw_vis_dif = .5*flux_sw_dif ! Total Visible short wave (DIR + DIFFUSE) flux_sw_vis = flux_sw_vis_dir + flux_sw_vis_dif ! NIR components are determined in the ice model ! for surface flux computation gust = gustiness v_bot = 0.0 t_bot = tg + t_bot_atm q_bot = rh*sat_mixing_ratio(t_bot) Surf_diff%delta_t = dt*dt_tg ! needed in this form for surface models Surf_diff%delta_tr(:,:,q_ind) = dt*dt_qg return end subroutine atmosphere_down ! ! ================================================================================== ! ! ! public routine required for atmospheric components of coupled models ! ! ! ! atmosphere_up ! This routine calls the "upward pass" of the atmospheric physics, ! spherical diagnostics, and time differencing. The prognostic ! variables are advanced to the next time step. It should only be ! called once per time step and after calling atmosphere_down. ! ! ! ! time at the current time level (tau) ! ! ! fraction (0. to 1.) of underlying surface which covered by land ! ! ! liquid precipitation rate (rain) in kg/m2/s ! ! ! frozen precipitation rate (snow) in kg/m2/s ! ! ! wind gustiness ! ! ! urface diffusion terms computed by the vertical diffusion scheme ! ! ! NOT USED in this routine. Dummy argument added for compilation success. ! ! ! NOT USED in this routine. Dummy argument added for compilation success. ! ! ! NOT USED in this routine. Dummy argument added for compilation success. ! subroutine atmosphere_up(Time, frac_land, Surf_diff, lprec, fprec, gust, u_star, b_star, q_star ) type(time_type), intent(in) :: Time real, intent(in), dimension(is:ie,js:je) :: frac_land type(surf_diff_type), intent(in) :: Surf_diff real, intent(out), dimension(is:ie,js:je) :: lprec, fprec, gust real, intent(in), dimension(:,:) :: u_star, b_star, q_star complex, dimension(ms:me,ns:ne) :: lap, spec real, dimension(ms:me,ns:ne) :: eigen integer :: year, month, day, hour, minute, second dt_tg = Surf_diff%delta_t/dt dt_qg = Surf_diff%delta_tr(:,:,q_ind)/dt !initialize arrays to zero gust = gustiness call precipitation(lprec, fprec) call diffusion Time_next = Time + Time_step if(debug_ebm_atm) then call get_date(Time_next, year, month, day, hour, minute, second) if (mpp_pe() == 0 .and. hour+minute == 0 ) then print *,"atmosphere_up:Current time: year =",year," month = ",month," day = ",day write (stdout(),1000) tg(ie,je), qg(ie,je), lprec(ie,je), fprec(ie,je) endif endif call ebm_diagnostics_up(Time_next, tg, qg, lprec, fprec, dt_qg) if(debug_ebm_atm) then ! print out a few things for degbugging if (mpp_pe() == 0 .and. hour+minute == 0 ) then print *,"atmosphere_up aft diag:Current time: year =",year," month = ",month," day = ",day write (stdout(),1000) tg(ie,je), qg(ie,je), lprec(ie,je), fprec(ie,je) endif 1000 format(4(1x,e11.4)) endif return end subroutine atmosphere_up ! ! ================================================================================== ! ! ! radiation ! Radiation module for prognostic equation of atmospheric temperature ! The atmospheric tendency term (dt_tg) includes the balance of shortwave ! and longwave radiation terms at the surface ! ! ! ! ! time at the current time level (tau) ! ! ! surface (skin) temperature (in deg k) ! ! ! surface albedo ! ! ! cosine of the zenith angle ! ! ! net shortwave surface flux (down minus up) (in watts/m**2) ! ! ! downward longwave surface flux (in watts/m**2) ! subroutine radiation (Time, t_surf, albedo, coszen, net_surf_sw_down, surf_lw_down) ! type(time_type), intent(in) :: Time real, intent(in), dimension(is:ie,js:je) :: t_surf, albedo real, intent(out), dimension(is:ie,js:je) :: coszen, net_surf_sw_down, surf_lw_down integer :: j real, dimension(is:ie,js:je) :: net_top_sw_down, top_lw_up, surf_lw_up real, dimension(js:je) :: cosz, solar, incident_solar integer :: year, month, day, hour, minute, second if(seasonal_solar) then ! Use spectral 2-layer model version call daily_mean_solar (rad_lat, Time, cosz, solar) else cosz = annual_cosz solar = annual_solar endif incident_solar = solar_constant*solar if(debug_ebm_atm) then call get_date(Time, year, month, day, hour, minute, second) endif do j = js, je net_top_sw_down (:,j) = incident_solar(j)*(1.0 - atm_ref - (1.0 - atm_ref - atm_abs)*albedo(:,j)) net_surf_sw_down (:,j) = incident_solar(j)*(1.0 - atm_ref- atm_abs)*(1. - albedo(:,j)) top_lw_up (:,j) = stefan*( tg (:,j) **4) surf_lw_down (:,j) = stefan*((tg (:,j)+20.0)**4) surf_lw_up (:,j) = stefan*( t_surf (:,j) **4) dt_tg (:,j) = dt_tg(:,j) + & ( + surf_lw_up (:,j) & - top_lw_up (:,j) - surf_lw_down (:,j) & + net_top_sw_down(:,j) - net_surf_sw_down(:,j) )/(cp_air*mass) coszen (:,j) = cosz(j) if(debug_ebm_atm) then if (mpp_pe() == 0 .and. hour+minute == 0 ) then print *,"radiation:Current time: year =",year," month = ",month," day = ",day, " j=",j write (stdout(),1000) incident_solar(j),albedo(ie,j),atm_ref,atm_abs write (stdout(),1000) tg(ie,j), qg(ie,j), net_top_sw_down(ie,j), net_surf_sw_down(ie,j) write (stdout(),1000) top_lw_up(ie,j), surf_lw_down(ie,j), surf_lw_up(ie,j), dt_tg(ie,j) endif 1000 format(4(1x,e11.4)) endif enddo call ebm_diagnostics_down(Time, top_lw_up, surf_lw_down, surf_lw_up, net_top_sw_down, net_surf_sw_down) if(debug_ebm_atm) then if (mpp_pe() == 0 .and. hour+minute == 0 ) then print *,"radiation aft diag:Current time: year =",year," month = ",month," day = ",day, " j=",j-1 write (stdout(),1000) incident_solar(j-1),albedo(ie,j-1),atm_ref,atm_abs write (stdout(),1000) tg(ie,j-1), qg(ie,j-1), net_top_sw_down(ie,j-1), net_surf_sw_down(ie,j-1) write (stdout(),1000) top_lw_up(ie,j-1), surf_lw_down(ie,j-1), surf_lw_up(ie,j-1), dt_tg(ie,j-1) endif endif return end subroutine radiation ! ! ================================================================================== ! ! ! ! Precipiatation module for prognostic equation of atmospheric temperature ! and specific humidity ! ! ! Precipiatation module for prognostic equation of atmospheric temperature ! and specific humidity ! The atmospheric tendency term (dt_tg) includes the latent heat flux released ! during precipitation (tdel) ! The specific humidity is the balance of evaporation and precipitation (dt_qg) ! ! ! ! liquid precipitation rate (rain) in kg/m2/s ! ! ! frozen precipitation rate (snow) in kg/m2/s ! subroutine precipitation(lprec, fprec) real, intent(out), dimension(is:ie,js:je) :: lprec, fprec real, dimension(is:ie,js:je) :: qsat, dqsat, tdel, qdel, & tg_temp, qg_temp real :: hlcp, hfcp integer :: i,j integer :: year, month, day, hour, minute, second tg_temp = tg + dt_tg*dt + t_bot_atm qg_temp = qg + dt_qg*dt qsat = sat_mixing_ratio(tg_temp) dqsat = deriv_sat_mixing_ratio(tg_temp) ! tfreeze - temperature where fresh water freezes 273.16 degK ! hlv - Latent heat of evaporation J/kg ! hlf - latent heat of fusion J/kg condensate ! cp_air - specific heat of air at J/kg air/K hlcp = hlv /cp_air hfcp = (hlv + hlf)/cp_air ! initialize precipiation to zero lprec = 0.0 fprec = 0.0 where (qg_temp > qsat .and. tg_temp > tfreeze) qdel = (qsat - qg_temp)/(1.0 + hlcp*dqsat) tdel = -hlcp*qdel lprec = -qdel*mass/dt elsewhere (qg_temp > qsat .and. tg_temp <= tfreeze) qdel = (qsat - qg_temp)/(1.0 + hfcp*dqsat) tdel = -hfcp*qdel fprec = -qdel*mass/dt elsewhere qdel = 0.0 tdel = 0.0 endwhere dt_tg = dt_tg + tdel/dt dt_qg = dt_qg + qdel/dt if(debug_ebm_atm) then if (mpp_pe() == 0 ) then print *,"in precip:" write (stdout(),1000) qg_temp(ie,je), qsat(ie,je), tg_temp(ie,je), dqsat(ie,je) write (stdout(),1000) tg(ie,je), qg(ie,je), lprec(ie,je), fprec(ie,je) write (stdout(),1000) dt_tg(ie,je), dt_qg(ie,je), tdel(ie,je), qdel(ie,je) endif 1000 format(4(1x,e11.4)) endif return end subroutine precipitation ! ! ================================================================================== ! ! ! ! Lateral eddy diffusion module for prognostic equation of atmospheric temperature ! and specific humidity ! ! ! Lateral eddy diffusion module for prognostic equation of atmospheric temperature ! and specific humidity ! There are two options: uniform or variable diffusion ! ! subroutine diffusion real , dimension (ms:me,ns:ne) :: eigen, denom complex, dimension (ms:me,ns:ne) :: dxs, dys, div real , dimension (is:ie,js:je) :: dxg, dyg, d integer :: m, n ! real :: nu = 4.0e16 call trans_grid_to_spherical(dt_tg, dt_ts) call trans_grid_to_spherical(dt_qg, dt_qs) call get_eigen_laplacian(eigen) if(diff_is_uniform) then dt_ts = dt_ts - diff*eigen*ts dt_qs = dt_qs - diff*eigen*qs else d = diffusivity() ! temperature call compute_gradient_cos(ts, dxs, dys) call trans_spherical_to_grid(dxs,dxg) call trans_spherical_to_grid(dys,dyg) dxg = d*dxg dyg = d*dyg call divide_by_cos2(dxg) call divide_by_cos2(dyg) call trans_grid_to_spherical(dxg,dxs) call trans_grid_to_spherical(dyg,dys) div = compute_div(dxs, dys) call triangular_truncation(div) dt_ts = dt_ts + div ! water vapor call compute_gradient_cos(qs, dxs, dys) call trans_spherical_to_grid(dxs,dxg) call trans_spherical_to_grid(dys,dyg) dxg = d*dxg dyg = d*dyg call divide_by_cos2(dxg) call divide_by_cos2(dyg) call trans_grid_to_spherical(dxg,dxs) call trans_grid_to_spherical(dyg,dys) div = compute_div(dxs, dys) call triangular_truncation(div) dt_qs = dt_qs + div endif dt_ts = dt_ts - nu*eigen*eigen*ts dt_qs = dt_qs - nu*eigen*eigen*qs denom = 1./(1.0 + (diff*eigen + nu*eigen*eigen)*dt) ts = ts + denom*dt*dt_ts qs = qs + denom*dt*dt_qs call trans_spherical_to_grid(ts,tg) call trans_spherical_to_grid(qs,qg) if(debug_ebm_atm) then if (mpp_pe() == 0 ) then print *,"diffusion:" write (stdout(),1000) tg(ie,je), qg(ie,je), denom(me,ne), eigen(me,ne) write (stdout(),1000) ts(me,ne), qs(me,ne), dt_qs(me,ne), dt_ts(me,ne) endif 1000 format(4(1x,e11.4)) endif return end subroutine diffusion ! ! ================================================================================== ! ! ! ! For the a given scalar diffusion coefficient the routine returns an array ! ! ! For the a given scalar diffusion coefficient the routine returns an array ! ! function diffusivity() result(d) real, dimension(is:ie,js:je) :: d d = diff return end function diffusivity ! !####################################################################### ! ! ! ! For the given temperatures routine returns the saturation mixing ratio ! esat - saturation vapor pressure ! ! ! ! For the given temperatures routine returns the saturation mixing ratio ! esat - saturation vapor pressure ! function sat_mixing_ratio(t) result(q) real, dimension(is:ie,js:je) :: t, q, esat call escomp (t, esat) q = d622*esat/1.e05 return end function sat_mixing_ratio ! ! ================================================================================== ! ! ! ! For the given temperatures, routine returns the derivative of the saturation mixing ratio ! ! ! For the given temperatures, routine returns the derivative of the saturation mixing ratio ! ! function deriv_sat_mixing_ratio(t) result(q) real, dimension(is:ie,js:je) :: t, q, desat call descomp (t, desat) q = d622*desat/1.e05 return end function deriv_sat_mixing_ratio ! ! ================================================================================== ! ! ! ! returns temp, sphum, pres, height at the lowest model level ! and surface pressure ! ! ! public routine required for atmospheric components of coupled models ! returns temp, sphum, pres, height at the lowest model level ! and surface pressure ! ! ! ! near surface temperature in degrees Kelvin ! ! ! near surface mixing ratio ! ! ! pressure at which atmos near usrface values are assumed to be defined ! ! ! height at which atmos near usrface values are assumed to be defined ! ! ! surface pressure ! subroutine get_bottom_mass (t_bot_out, tr_bot_out, p_bot, z_bot, p_surf, slp) real, intent(out), dimension(is:ie,js:je) :: t_bot_out, p_bot, z_bot, p_surf, slp real, intent(out), dimension(:,:,:) :: tr_bot_out p_surf = 1000.0e02 ! surface pressure p_bot = 990.0e02 ! pressure at which atmos near usrface values are assumed to be defined z_bot = 100.0 ! height at which atmos near surface values are assumed to be defined t_bot_out = t_bot(is:ie,js:je) ! near surface temperature tr_bot_out(:,:,1) = q_bot(is:ie,js:je) ! near surface mixing ratio slp = p_surf return end subroutine get_bottom_mass ! ! ================================================================================== ! ! ! ! returns u and v on the mass grid at the lowest model level ! ! ! public routine required for atmospheric components of coupled models ! returns u and v on the mass grid at the lowest model level ! ! ! ! near surface zonal wind ! ! ! near surface meridional wind ! subroutine get_bottom_wind (u_bot_out, v_bot_out) real, intent(out), dimension(is:ie,js:je) :: u_bot_out, v_bot_out u_bot_out = u_bot(is:ie,js:je) ! near surface zonal wind v_bot_out = v_bot(is:ie,js:je) ! near surface meridional wind return end subroutine get_bottom_wind ! ! ================================================================================== ! ! ! returns the number of longitude and latitude grid points ! for either the local PEs grid (default) or the global grid ! ! ! public routine required for atmospheric components of coupled models ! returns the number of longitude and latitude grid points ! for either the local PEs grid (default) or the global grid ! ! ! ! The number of longitude points in the compute domain. ! ! ! The number of latitude points in the compute domain. ! ! ! Flag that specifies whether the returned compute domain size is ! for the global grid (TRUE) or for the current processor (FALSE). ! subroutine atmosphere_resolution(num_lon_out, num_lat_out, global) integer, intent(out) :: num_lon_out, num_lat_out logical, intent(in), optional :: global logical :: global_tmp if (present(global)) then global_tmp = global else global_tmp = .false. endif if(global_tmp) then num_lon_out = lon_max num_lat_out = lat_max else num_lon_out = ie+1-is num_lat_out = je+1-js endif return end subroutine atmosphere_resolution ! ! ================================================================================== ! ! ! ! returns the axis indices associated with the coupling grid ! ! ! public routine required for atmospheric components of coupled models ! returns the axis indices associated with the coupling grid ! ! ! ! The axis identifiers for the atmospheric grids. ! The size of axes must be least 1 but not greater than 4. ! The axes are returned in the order (/ x, y, p_full, p_half /) ! subroutine get_atmosphere_axes(axes_out) integer, intent(out), dimension(:) :: axes_out axes_out = axis_id return end subroutine get_atmosphere_axes ! ! ================================================================================== ! ! ! returns the longitude and latitude grid box edges ! for either the local PEs grid (default) or the global grid ! ! ! public routine required for atmospheric components of coupled models ! returns the longitude and latitude grid box edges ! for either the local PEs grid (default) or the global grid ! ! ! ! The west-to-east longitude edges of grid boxes (in radians). ! ! ! The south-to-north latitude edges of grid boxes (in radians). ! ! ! Flag that specifies whether the returned grid box edges are ! for the global grid (TRUE) or for the current processor (FALSE). ! subroutine atmosphere_boundary(lon_boundaries, lat_boundaries, global) real, intent(out), dimension(:,:) :: lon_boundaries, lat_boundaries logical, intent(in), optional :: global real, dimension(size(lon_boundaries,1)) :: tmpx real, dimension(size(lon_boundaries,2)) :: tmpy integer :: i logical :: global_tmp if(present(global)) then global_tmp = global else global_tmp = .false. endif call get_grid_boundaries(tmpx, tmpy, global_tmp) do i = 1, size(lon_boundaries,2) lon_boundaries(:,i) = tmpx(:) end do do i = 1, size(lon_boundaries,1) lat_boundaries(i,:) = tmpy(:) end do return end subroutine atmosphere_boundary ! ! ================================================================================== ! ! ! ! returns grid cell areas for the domain ! ! ! ! returns grid cell areas for the domain ! subroutine atmosphere_cell_area(area_out) real, dimension(:,:), intent(out) :: area_out integer :: xsize, ysize, j real, dimension(size(area_out,2)) :: wts_lat xsize = ie-is+1 ysize = je-js+1 if(any(shape(area_out) /= (/xsize,ysize/))) then call mpp_error(FATAL,'atmosphere_cell_area: argument has wrong shape. Its shape is ', & shape(area_out),' It should be ',(/xsize,ysize/)) endif call get_wts_lat(wts_lat) do j=1,ysize area_out(:,j) = 2*PI*radius*radius*wts_lat(j)/lon_max enddo end subroutine atmosphere_cell_area ! ! ================================================================================== ! ! ! ! returns the domain2d variable associated with the coupling grid ! ! ! ! public routine required for atmospheric components of coupled models ! returns the domain2d variable associated with the coupling grid ! note: coupling is done using the mass/temperature grid with no halos ! !OUTPUT ! Domain The domain2d variable describing the grid used for coupling. ! For the B-grid, this corresponds to the temperature grid ! without halos. ! ! ! ! The domain2d variable describing the grid used for coupling. ! For the B-grid, this corresponds to the temperature grid ! without halos. ! subroutine atmosphere_domain(domain) type (domain2d), intent(inout) :: domain domain = grid_domain; end subroutine atmosphere_domain ! ! ===================================================================== ! ! ! ! write out restart file ! Termination routine for atmosphere_mod. ! ! ! public routine required for atmospheric components of coupled models ! write out restart file ! Termination routine for atmosphere_mod. ! ! ! ! time at the current time level (tau) ! ! ! Contains information about the grid cells ! subroutine atmosphere_end(Time, Grid_box) type(time_type), intent(in) :: Time type(grid_box_type), intent(inout) :: Grid_box integer :: time_level,seconds,days,unit character(len=64) :: filename if(.not.module_is_initialized) then call error_mesg('atmosphere_end',' atmosphere_init has not been called.', FATAL) end if !--- write out restart file filename = 'RESTART/atmosphere.res.nc' call write_data(filename, 'ts_real', real(ts), spectral_domain) call write_data(filename, 'ts_imag', aimag(ts), spectral_domain) call write_data(filename, 'qs_real', real(qs), spectral_domain) call write_data(filename, 'qs_imag', aimag(qs), spectral_domain) call write_data(filename, 'tg', tg, grid_domain) call write_data(filename, 'qg', qg, grid_domain) return end subroutine atmosphere_end ! !####################################################################### ! ! ! dummy routine. ! subroutine atmosphere_restart(timestamp) character(len=*), intent(in) :: timestamp call error_mesg ('atmosphere_restart in atmosphere_mod', & 'intermediate restart capability is not implemented for this model', FATAL) end subroutine atmosphere_restart ! ! ===================================================================== ! ! ! ! Not implemented properly. Puts value=0 ! ! ! This is a stub routine needed for compilation of mom4_test6. ! It puts value=0 for any index. ! ! ! ! ! ! subroutine get_stock_pe(index, value) use stock_constants_mod, only : ISTOCK_WATER, ISTOCK_HEAT integer, intent(in) :: index real, intent(out) :: value select case (index) case (ISTOCK_WATER) ! don't know how to compute water stock value = 0 case (ISTOCK_HEAT) ! don't know how to compute water stock value = 0 case default value = 0.0 end select end subroutine get_stock_pe ! ================================================================================== ! ================================================================================== end module atmosphere_mod