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