module chem_surfvals 8,6
!-----------------------------------------------------------------------------------
! Purpose: Provides greenhouse gas (ghg) values at the Earth's surface.
! These values may be time dependent.
!
! Author: Brian Eaton (assembled module from existing scattered code pieces)
!-----------------------------------------------------------------------------------
use shr_kind_mod
, only: r8=>shr_kind_r8
use spmd_utils
, only: masterproc
use time_manager
, only: get_curr_date, get_start_date, is_end_curr_day, &
timemgr_datediff, get_curr_calday
use abortutils
, only: endrun
use netcdf
use error_messages
, only: handle_ncerr
use cam_logfile
, only: iulog
!-----------------------------------------------------------------------
!- module boilerplate --------------------------------------------------
!-----------------------------------------------------------------------
implicit none
private ! Make default access private
save
! Public methods
public ::&
chem_surfvals_readnl, &! read namelist input
chem_surfvals_init, &! initialize options that depend on namelist input
chem_surfvals_set, &! set ghg surface values when ramp option is on
chem_surfvals_get, &! return surface values for: CO2VMR, CO2MMR, CH4VMR
! N2OVMR, F11VMR, and F12VMR
chem_surfvals_co2_rad ! return co2 for radiation
! Private module data
! Default values for namelist variables -- now set by build-namelist
real(r8) :: o2mmr = .23143_r8 ! o2 mass mixing ratio
real(r8) :: co2vmr_rad = -1.0_r8 ! co2 vmr override for radiation
real(r8) :: co2vmr = -1.0_r8 ! co2 volume mixing ratio
real(r8) :: n2ovmr = -1.0_r8 ! n2o volume mixing ratio
real(r8) :: ch4vmr = -1.0_r8 ! ch4 volume mixing ratio
real(r8) :: f11vmr = -1.0_r8 ! cfc11 volume mixing ratio
real(r8) :: f12vmr = -1.0_r8 ! cfc12 volume mixing ratio
character(len=16) :: scenario_ghg = 'FIXED' ! 'FIXED','RAMPED' or 'RAMP_CO2_ONLY'
integer :: rampYear_ghg = 0 ! ramped gases fixed at this year (if > 0)
character(len=256) :: bndtvghg ! filename for ramped data
integer :: ramp_co2_start_ymd = 0 ! start date for co2 ramping (yyyymmdd)
real(r8) :: ramp_co2_annual_rate = 1.0_r8 ! % amount of co2 ramping per yr; default is 1%
real(r8) :: ramp_co2_cap = -9999.0_r8 ! co2 ramp cap if rate>0, floor otherwise
! as multiple or fraction of inital value
! ex. 4.0 => cap at 4x initial co2 setting
integer :: ghg_yearStart_model = 0 ! model start year
integer :: ghg_yearStart_data = 0 ! data start year
logical :: ghg_use_calendar ! true => data year = model year
logical :: doRamp_ghg ! true => turn on ramping for ghg
logical :: ramp_just_co2 ! true => ramping to be done just for co2 and not other ghg's
integer :: fixYear_ghg ! year at which Ramped gases are fixed
integer :: co2_start ! date at which co2 begins ramping
real(r8) :: co2_daily_factor ! daily multiplier to achieve annual rate of co2 ramp
real(r8) :: co2_limit ! value of co2vmr where ramping ends
real(r8) :: co2_base ! initial co2 volume mixing ratio, before any ramping
integer :: ntim = -1 ! number of yearly data values
integer, allocatable :: yrdata(:) ! yearly data values
real(r8), allocatable :: co2(:) ! co2 mixing ratios in ppmv
real(r8), allocatable :: ch4(:) ! ppbv
real(r8), allocatable :: n2o(:) ! ppbv
real(r8), allocatable :: f11(:) ! pptv
real(r8), allocatable :: f12(:) ! pptv
real(r8), allocatable :: adj(:) ! unitless adjustment factor for f11 & f12
!=========================================================================================
contains
!=========================================================================================
subroutine chem_surfvals_readnl(nlfile) 1,21
! Read chem_surfvals_nl namelist group.
use namelist_utils
, only: find_group_name
use units
, only: getunit, freeunit
use mpishorthand
character(len=*), intent(in) :: nlfile ! filepath for file containing namelist input
! Local variables
integer :: unitn, ierr, i
character(len=*), parameter :: subname = 'chem_surfvals_readnl'
namelist /chem_surfvals_nl/ co2vmr, n2ovmr, ch4vmr, f11vmr, f12vmr, &
co2vmr_rad, scenario_ghg, rampyear_ghg, bndtvghg, &
ramp_co2_start_ymd, ramp_co2_annual_rate, ramp_co2_cap, &
ghg_yearStart_model, ghg_yearStart_data
!-----------------------------------------------------------------------------
if (masterproc) then
unitn = getunit
()
open( unitn, file=trim(nlfile), status='old' )
call find_group_name
(unitn, 'chem_surfvals_nl', status=ierr)
if (ierr == 0) then
read(unitn, chem_surfvals_nl, iostat=ierr)
if (ierr /= 0) then
call endrun
(subname // ':: ERROR reading namelist')
end if
end if
close(unitn)
call freeunit
(unitn)
end if
#ifdef SPMD
! Broadcast namelist variables
call mpibcast
(co2vmr, 1, mpir8, 0, mpicom)
call mpibcast
(n2ovmr, 1, mpir8, 0, mpicom)
call mpibcast
(ch4vmr, 1, mpir8, 0, mpicom)
call mpibcast
(f11vmr, 1, mpir8, 0, mpicom)
call mpibcast
(f12vmr, 1, mpir8, 0, mpicom)
call mpibcast
(co2vmr_rad, 1, mpir8, 0, mpicom)
call mpibcast
(scenario_ghg, len(scenario_ghg), mpichar, 0, mpicom)
call mpibcast
(rampyear_ghg, 1, mpiint, 0, mpicom)
call mpibcast
(bndtvghg, len(bndtvghg), mpichar, 0, mpicom)
call mpibcast
(ramp_co2_start_ymd, 1, mpiint, 0, mpicom)
call mpibcast
(ramp_co2_annual_rate, 1, mpir8, 0, mpicom)
call mpibcast
(ramp_co2_cap, 1, mpir8, 0, mpicom)
call mpibcast
(ghg_yearstart_model, 1, mpiint, 0, mpicom)
call mpibcast
(ghg_yearstart_data, 1, mpiint, 0, mpicom)
#endif
if (co2vmr_rad > 0._r8) then
if (masterproc) &
write(iulog,*) trim(subname)//': co2vmr_rad override is set to ', co2vmr_rad
end if
end subroutine chem_surfvals_readnl
!================================================================================================
subroutine chem_surfvals_init() 2,8
!-----------------------------------------------------------------------
!
! Purpose:
! Initialize the ramp options that are controlled by namelist input.
! Set surface values at initial time.
! N.B. This routine must be called after the time manager has been initialized
! since chem_surfvals_set calls time manager methods.
!
! Author: B. Eaton - merged code from parse_namelist and rampnl_ghg.
!
!-----------------------------------------------------------------------
use infnan
, only: inf
!---------------------------Local variables-----------------------------
integer :: yr, mon, day, ncsec
!-----------------------------------------------------------------------
if (scenario_ghg == 'FIXED') then
doRamp_ghg = .false.
ramp_just_co2 = .false.
if (masterproc) &
write(iulog,*)'chem_surfvals_init: ghg surface values are fixed as follows'
else if (scenario_ghg == 'RAMPED') then
doRamp_ghg = .true.
ramp_just_co2 = .false.
call ghg_ramp_read
fixYear_ghg = rampYear_ghg ! set private member to namelist var
if (masterproc) then
if ( fixYear_ghg > 0 ) then
write(iulog,*) ' FIXED values from year ',fixYear_ghg
else
write(iulog,*) ' RAMPED values initialized to'
end if
end if
call chem_surfvals_set
()
else if (scenario_ghg == 'RAMP_CO2_ONLY') then
if(ramp_co2_start_ymd == 0) then
! by default start the ramp at the initial run time
call get_start_date
(yr, mon, day, ncsec)
ramp_co2_start_ymd = yr*10000 + mon*100 + day
end if
co2_start = ramp_co2_start_ymd
if(ramp_co2_annual_rate <= -100.0_r8) then
write(iulog,*) 'RAMP_CO2: invalid ramp_co2_annual_rate= ',ramp_co2_annual_rate
call endrun
('chem_surfvals_init: RAMP_CO2_ANNUAL_RATE must be greater than -100.0')
end if
doRamp_ghg = .true.
ramp_just_co2 = .true.
co2_base = co2vmr ! save initial setting
if (masterproc) &
write(iulog,*) ' RAMPED values initialized to'
co2_daily_factor = (ramp_co2_annual_rate*0.01_r8+1.0_r8)**(1.0_r8/365.0_r8)
if(ramp_co2_cap > 0.0_r8) then
co2_limit = ramp_co2_cap * co2_base
else ! if no cap/floor specified, provide default
if(ramp_co2_annual_rate < 0.0_r8) then
co2_limit = 0.0_r8
else
co2_limit = inf
end if
end if
if((ramp_co2_annual_rate<0.0_r8 .and. co2_limit>co2_base) .or. &
(ramp_co2_annual_rate>0.0_r8 .and. co2_limit<co2_base)) then
write(iulog,*) 'RAMP_CO2: ramp_co2_cap is unreachable'
write(iulog,*) 'RAMP_CO2: ramp_co2_annual_rate= ',ramp_co2_annual_rate,' ramp_co2_cap= ',ramp_co2_cap
call endrun
('chem_surfvals_init: ramp_co2_annual_rate and ramp_co2_cap incompatible')
end if
call chem_surfvals_set
()
else
call endrun
('chem_surfvals_init: input namelist SCENARIO_GHG must be set to either FIXED, RAMPED or RAMP_CO2_ONLY')
endif
if (masterproc) then
write(iulog,*) ' co2 volume mixing ratio = ',co2vmr
write(iulog,*) ' ch4 volume mixing ratio = ',ch4vmr
write(iulog,*) ' n2o volume mixing ratio = ',n2ovmr
write(iulog,*) ' f11 volume mixing ratio = ',f11vmr
write(iulog,*) ' f12 volume mixing ratio = ',f12vmr
end if
end subroutine chem_surfvals_init
!=========================================================================================
subroutine ghg_ramp_read() 1,30
!-----------------------------------------------------------------------
!
! Purpose:
! Read ramped greenhouse gas surface data.
!
! Author: T. Henderson
!
!-----------------------------------------------------------------------
use ioFileMod
, only: getfil
#if ( defined SPMD )
use mpishorthand
, only: mpicom, mpiint, mpir8
#endif
!---------------------------Local variables-----------------------------
integer :: ncid
integer :: co2_id
integer :: ch4_id
integer :: n2o_id
integer :: f11_id
integer :: f12_id
integer :: adj_id
integer :: date_id
integer :: time_id
integer :: ierror
character(len=256) :: locfn ! netcdf local filename to open
if (masterproc) then
call getfil
(bndtvghg, locfn, 0)
call handle_ncerr
( nf90_open (trim(locfn), NF90_NOWRITE, ncid),&
'chem_surfvals.F90:351')
write(iulog,*)'GHG_RAMP_READ: reading ramped greenhouse gas surface data from file ',trim(locfn)
call handle_ncerr
( nf90_inq_varid( ncid, 'date', date_id ),&
'chem_surfvals.F90:354')
call handle_ncerr
( nf90_inq_varid( ncid, 'CO2', co2_id ),&
'chem_surfvals.F90:356')
call handle_ncerr
( nf90_inq_varid( ncid, 'CH4', ch4_id ),&
'chem_surfvals.F90:358')
call handle_ncerr
( nf90_inq_varid( ncid, 'N2O', n2o_id ),&
'chem_surfvals.F90:360')
call handle_ncerr
( nf90_inq_varid( ncid, 'f11', f11_id ),&
'chem_surfvals.F90:362')
call handle_ncerr
( nf90_inq_varid( ncid, 'f12', f12_id ),&
'chem_surfvals.F90:364')
call handle_ncerr
( nf90_inq_varid( ncid, 'adj', adj_id ),&
'chem_surfvals.F90:366')
call handle_ncerr
( nf90_inq_dimid( ncid, 'time', time_id ),&
'chem_surfvals.F90:368')
call handle_ncerr
( nf90_inquire_dimension( ncid, time_id, len=ntim ),&
'chem_surfvals.F90:370')
endif
#if (defined SPMD )
call mpibcast
(ntim, 1, mpiint, 0, mpicom)
#endif
! these arrays are never deallocated
allocate ( yrdata(ntim), co2(ntim), ch4(ntim), n2o(ntim), &
f11(ntim), f12(ntim), adj(ntim), stat=ierror )
if (ierror /= 0) then
write(iulog,*)'GHG_RAMP_READ: ERROR, allocate() failed!'
call endrun
endif
if (masterproc) then
call handle_ncerr
( nf90_get_var (ncid, date_id, yrdata ),&
'chem_surfvals.F90:384')
yrdata = yrdata / 10000
call handle_ncerr
( nf90_get_var (ncid, co2_id, co2 ),&
'chem_surfvals.F90:387')
call handle_ncerr
( nf90_get_var (ncid, ch4_id, ch4 ),&
'chem_surfvals.F90:389')
call handle_ncerr
( nf90_get_var (ncid, n2o_id, n2o ),&
'chem_surfvals.F90:391')
call handle_ncerr
( nf90_get_var (ncid, f11_id, f11 ),&
'chem_surfvals.F90:393')
call handle_ncerr
( nf90_get_var (ncid, f12_id, f12 ),&
'chem_surfvals.F90:395')
call handle_ncerr
( nf90_get_var (ncid, adj_id, adj ),&
'chem_surfvals.F90:397')
call handle_ncerr
( nf90_close (ncid),&
'chem_surfvals.F90:399')
write(iulog,*)'GHG_RAMP_READ: successfully read ramped greenhouse gas surface data from years ',&
yrdata(1),' through ',yrdata(ntim)
endif
#if (defined SPMD )
call mpibcast
(co2, ntim, mpir8, 0, mpicom)
call mpibcast
(ch4, ntim, mpir8, 0, mpicom)
call mpibcast
(n2o, ntim, mpir8, 0, mpicom)
call mpibcast
(f11, ntim, mpir8, 0, mpicom)
call mpibcast
(f12, ntim, mpir8, 0, mpicom)
call mpibcast
(adj, ntim, mpir8, 0, mpicom)
call mpibcast
(yrdata, ntim, mpiint, 0, mpicom)
#endif
return
end subroutine ghg_ramp_read
!=========================================================================================
function chem_surfvals_get(name) 14,2
use physconst
, only: mwdry, mwco2
character(len=*), intent(in) :: name
real(r8) :: rmwco2
real(r8) :: chem_surfvals_get
rmwco2 = mwco2/mwdry ! ratio of molecular weights of co2 to dry air
select case (name)
case ('CO2VMR')
chem_surfvals_get = co2vmr
case ('CO2MMR')
chem_surfvals_get = rmwco2 * co2vmr
case ('N2OVMR')
chem_surfvals_get = n2ovmr
case ('CH4VMR')
chem_surfvals_get = ch4vmr
case ('F11VMR')
chem_surfvals_get = f11vmr
case ('F12VMR')
chem_surfvals_get = f12vmr
case ('O2MMR')
chem_surfvals_get = o2mmr
case default
call endrun
('chem_surfvals_get does not know name')
end select
end function chem_surfvals_get
!=========================================================================================
function chem_surfvals_co2_rad(vmr_in) 2,1
! Return the value of CO2 (as mmr) that is radiatively active.
! This method is used by ghg_data to set the prescribed value of CO2 in
! the physics buffer. If the user has set the co2vmr_rad namelist
! variable then that value will override either the value set by the
! co2vmr namelist variable, or the values time interpolated from a
! dataset.
! This method is also used by cam_history to write the radiatively active
! CO2 to the history file. The optional argument allows returning the
! value as vmr.
use physconst
, only: mwdry, mwco2
! Arguments
logical, intent(in), optional :: vmr_in ! return CO2 as vmr
! Return value
real(r8) :: chem_surfvals_co2_rad
! Local variables
real(r8) :: convert_vmr ! convert vmr to desired output
!-----------------------------------------------------------------------
! by default convert vmr to mmr
convert_vmr = mwco2/mwdry ! ratio of molecular weights of co2 to dry air
if (present(vmr_in)) then
! if request return vmr
if (vmr_in) convert_vmr = 1.0_r8
end if
if (co2vmr_rad > 0._r8) then
chem_surfvals_co2_rad = convert_vmr * co2vmr_rad
else
chem_surfvals_co2_rad = convert_vmr * co2vmr
end if
end function chem_surfvals_co2_rad
!=========================================================================================
subroutine chem_surfvals_set( phys_state ) 3,5
! phys_state argument is unused in this version
use ppgrid
, only: begchunk, endchunk
use physics_types
, only: physics_state
type(physics_state), intent(inout), dimension(begchunk:endchunk), optional :: phys_state
!---------------------------Local variables-----------------------------
integer :: yr, mon, day, ncsec ! components of a date
integer :: ncdate ! current date in integer format [yyyymmdd]
if (.not. doRamp_ghg) return
if(ramp_just_co2) then
call chem_surfvals_set_co2
()
else
call chem_surfvals_set_all
()
end if
if (masterproc .and. is_end_curr_day()) then
call get_curr_date
(yr, mon, day, ncsec)
ncdate = yr*10000 + mon*100 + day
write(iulog,*) 'chem_surfvals_set: ncdate= ',ncdate,' co2vmr=',co2vmr
if (.not. ramp_just_co2 .and. mon==1 .and. day==1) then
write(iulog,*) 'chem_surfvals_set: ch4vmr=', ch4vmr, ' n2ovmr=', n2ovmr, &
' f11vmr=', f11vmr, ' f12vmr=', f12vmr
end if
end if
return
end subroutine chem_surfvals_set
!=========================================================================================
subroutine chem_surfvals_set_all() 1,7
!-----------------------------------------------------------------------
!
! Purpose:
! Computes greenhouse gas volume mixing ratios via interpolation of
! yearly input data.
!
! Author: B. Eaton - updated ramp_ghg for use in chem_surfvals module
!
!-----------------------------------------------------------------------
use interpolate_data
, only: get_timeinterp_factors
!---------------------------Local variables-----------------------------
integer yrmodel ! model year
integer nyrm ! year index
integer nyrp ! year index
integer :: yr, mon, day ! components of a date
integer :: ncdate ! current date in integer format [yyyymmdd]
integer :: ncsec ! current time of day [seconds]
real(r8) :: calday ! current calendar day
real(r8) doymodel ! model day of year
real(r8) doydatam ! day of year for input data yrdata(nyrm)
real(r8) doydatap ! day or year for input data yrdata(nyrp)
real(r8) deltat ! delta time
real(r8) fact1, fact2 ! time interpolation factors
real(r8) cfcscl ! cfc scale factor for f11
integer yearRan_model ! model ran year
!
! ---------------------------------------------------------------------
!
calday = get_curr_calday
()
call get_curr_date
(yr, mon, day, ncsec)
ncdate = yr*10000 + mon*100 + day
!
! determine ghg_use_calendar
!
if ( ghg_yearStart_model > 0 .and. ghg_yearStart_data > 0 ) then
ghg_use_calendar = .false.
else
ghg_use_calendar = .true.
end if
!
! determine index into input data
!
if ( fixYear_ghg > 0) then
yrmodel = fixYear_ghg
nyrm = fixYear_ghg - yrdata(1) + 1
else
if ( ghg_use_calendar) then
yrmodel = yr
nyrm = yr - yrdata(1) + 1
else
yearRan_model = yr - ghg_yearStart_model
if ( yearRan_model < 0 ) then
call endrun
('chem_surfvals_set_all: incorrect ghg_yearStart_model')
endif
yrmodel = yearRan_model + ghg_yearStart_data
nyrm = ghg_yearStart_data + yearRan_model - yrdata(1) + 1
end if
end if
nyrp = nyrm + 1
!
! if current date is before yrdata(1), quit
!
if (nyrm < 1) then
write(iulog,*)'chem_surfvals_set_all: data time index is out of bounds'
write(iulog,*)'nyrm = ',nyrm,' nyrp= ',nyrp, ' ncdate= ', ncdate
call endrun
endif
!
! if current date later than yrdata(ntim), call endrun.
! if want to use ntim values - uncomment the following lines
! below and comment the call to endrun and previous write
!
if (nyrp > ntim) then
call endrun
('chem_surfvals_set_all: error - current date is past the end of valid data')
! write(iulog,*)'chem_surfvals_set_all: using ghg data for ',yrdata(ntim)
! co2vmr = co2(ntim)*1.e-06
! ch4vmr = ch4(ntim)*1.e-09
! n2ovmr = n2o(ntim)*1.e-09
! f11vmr = f11(ntim)*1.e-12*(1.+cfcscl)
! f12vmr = f12(ntim)*1.e-12
! co2mmr = rmwco2 * co2vmr
! return
endif
!
! determine time interpolation factors, check sanity
! of interpolation factors to within 32-bit roundoff
! assume that day of year is 1 for all input data
!
doymodel = yrmodel*365._r8 + calday
doydatam = yrdata(nyrm)*365._r8 + 1._r8
doydatap = yrdata(nyrp)*365._r8 + 1._r8
call get_timeinterp_factors
(.false.,2,doydatam,doydatap, doymodel, &
fact1, fact2,'chem_surfvals')
!
! do time interpolation:
! co2 in ppmv
! n2o,ch4 in ppbv
! f11,f12 in pptv
!
co2vmr = (co2(nyrm)*fact1 + co2(nyrp)*fact2)*1.e-06_r8
ch4vmr = (ch4(nyrm)*fact1 + ch4(nyrp)*fact2)*1.e-09_r8
n2ovmr = (n2o(nyrm)*fact1 + n2o(nyrp)*fact2)*1.e-09_r8
cfcscl = (adj(nyrm)*fact1 + adj(nyrp)*fact2)
f11vmr = (f11(nyrm)*fact1 + f11(nyrp)*fact2)*1.e-12_r8*(1._r8+cfcscl)
f12vmr = (f12(nyrm)*fact1 + f12(nyrp)*fact2)*1.e-12_r8
return
end subroutine chem_surfvals_set_all
!=========================================================================================
subroutine chem_surfvals_set_co2() 1,3
!-----------------------------------------------------------------------
!
! Purpose:
! Computes co2 greenhouse gas volume mixing ratio via ramping info
! provided in namelist var's
!
! Author: B. Eaton - updated ramp_ghg for use in chem_surfvals module
!
!-----------------------------------------------------------------------
use shr_kind_mod
, only: r8 => shr_kind_r8
!---------------------------Local variables-----------------------------
real(r8) :: daydiff ! number of days of co2 ramping
integer :: yr, mon, day, ncsec ! components of a date
integer :: ncdate ! current date in integer format [yyyymmdd]
!-----------------------------------------------------------------------
call get_curr_date
(yr, mon, day, ncsec)
ncdate = yr*10000 + mon*100 + day
call timemgr_datediff
(co2_start, 0, ncdate, ncsec, daydiff)
if (daydiff > 0.0_r8) then
co2vmr = co2_base*(co2_daily_factor)**daydiff
if(co2_daily_factor < 1.0_r8) then
co2vmr = max(co2vmr,co2_limit)
else
co2vmr = min(co2vmr,co2_limit)
end if
end if
return
end subroutine chem_surfvals_set_co2
!=========================================================================================
end module chem_surfvals