module cam3_aero_data 4,13
!-----------------------------------------------------------------------
!
! Purposes:
! read, store, interpolate, and return fields
! of aerosols to CAM. The initialization
! file (mass.nc) is assumed to be a monthly climatology
! of aerosols from MATCH (on a sigma pressure
! coordinate system).
! also provide a "background" aerosol field to correct
! for any deficiencies in the physical parameterizations
! This fields is a "tuning" parameter.
! Public methods:
! (1) - initialization
! read aerosol masses from external file
! also pressure coordinates
! convert from monthly average values to mid-month values
! (2) - interpolation (time and vertical)
! interpolate onto pressure levels of CAM
! interpolate to time step of CAM
! return mass of aerosols
!
!-----------------------------------------------------------------------
use shr_kind_mod
, only: r8 => shr_kind_r8
use shr_scam_mod
, only: shr_scam_GetCloseLatLon
use spmd_utils
, only: masterproc
use ppgrid
, only: pcols, pver, pverp, begchunk, endchunk
use phys_grid
, only: get_ncols_p, scatter_field_to_chunk
use time_manager
, only: get_curr_calday
use infnan
, only: inf, bigint
use abortutils
, only: endrun
use scamMod
, only: scmlon,scmlat,single_column
use error_messages
, only: handle_ncerr
use physics_types
, only: physics_state
use boundarydata
, only: boundarydata_init, boundarydata_type
use perf_mod, only: t_startf, t_stopf
use cam_logfile
, only: iulog
use netcdf
implicit none
private
save
public :: &
cam3_aero_data_readnl, & ! read namelist
cam3_aero_data_register, & ! register these aerosols with pbuf
cam3_aero_data_init, & ! read from file, interpolate onto horiz grid
cam3_aero_data_timestep_init ! update data-aerosols to this timestep
! namelist variables
logical, public :: cam3_aero_data_on = .false.
character(len=256) :: bndtvaer = 'bndtvaer' ! full pathname for time-variant aerosol mass climatology dataset
! naer is number of species in climatology
integer, parameter :: naer = 11
real, parameter :: wgt_sscm = 6.0 / 7.0 ! Fraction of total seasalt mass in coarse mode
! indices to aerosol array (species portion)
integer, parameter :: &
idxSUL = 1, &
idxSSLTA = 2, & ! accumulation mode
idxSSLTC = 3, & ! coarse mode
idxOCPHO = 8, &
idxBCPHO = 9, &
idxOCPHI = 10, &
idxBCPHI = 11
! indices to sections of array that represent
! groups of aerosols
integer, parameter :: &
idxSSLTfirst = 2, numSSLT = 2, &
idxDUSTfirst = 4, &
numDUST = 4, &
idxCARBONfirst = 8, &
numCARBON = 4
! names of aerosols are they are represented in
! the climatology file.
! Appended '_V' indicates field has been vertically summed.
character(len=8), parameter :: aerosol_name(naer) = &
(/"MSUL_V "&
,"MSSLTA_V"&
,"MSSLTC_V"&
,"MDUST1_V"&
,"MDUST2_V"&
,"MDUST3_V"&
,"MDUST4_V"&
,"MOCPHO_V"&
,"MBCPHO_V"&
,"MOCPHI_V"&
,"MBCPHI_V"/)
! number of different "groups" of aerosols
integer, parameter :: num_aer_groups=4
! which group does each bin belong to?
integer, dimension(naer), parameter :: &
group =(/1,2,2,3,3,3,3,4,4,4,4/)
! name of each group
character(len=10), dimension(num_aer_groups), parameter :: &
aerosol_names = (/'sul ','sslt ','dust ','car '/)
! this boundarydata_type is used for datasets in the ncols format only.
type(boundarydata_type) :: aerosol_datan
integer :: aernid = -1 ! netcdf id for aerosol file (init to invalid)
integer :: species_id(naer) = -1 ! netcdf_id of each aerosol species (init to invalid)
integer :: Mpsid ! netcdf id for MATCH PS
integer :: nm = 1 ! index to prv month in array. init to 1 and toggle between 1 and 2
integer :: np = 2 ! index to nxt month in array. init to 2 and toggle between 1 and 2
integer :: mo_nxt = bigint ! index to nxt month in file
real(r8) :: cdaym = inf ! calendar day of prv month
real(r8) :: cdayp = inf ! calendar day of next month
! aerosol mass
real(r8), allocatable :: aer_mass(:, :, :, :)
! Days into year for mid month date
! This variable is dumb, the dates are in the dataset to be read in but they are
! slightly different than this so getting rid of it causes a change which
! exceeds roundoff.
real(r8) :: Mid(12) = (/16.5_r8, 46.0_r8, 75.5_r8, 106.0_r8, 136.5_r8, 167.0_r8, &
197.5_r8, 228.5_r8, 259.0_r8, 289.5_r8, 320.0_r8, 350.5_r8 /)
! values read from file and temporary values used for interpolation
!
! aerosolc is:
! Cumulative Mass at midpoint of each month
! on CAM's horizontal grid (col)
! on MATCH's levels (lev)
! aerosolc
integer, parameter :: paerlev = 28 ! number of levels for aerosol fields (MUST = naerlev)
integer :: naerlev ! size of level dimension in MATCH data
integer :: naerlon
integer :: naerlat
real(r8), pointer :: M_hybi(:) ! MATCH hybi
real(r8), pointer :: M_ps(:,:) ! surface pressure from MATCH file
real(r8), pointer :: aerosolc(:,:,:,:,:) ! Aerosol cumulative mass from MATCH
real(r8), pointer :: M_ps_cam_col(:,:,:) ! PS from MATCH on Cam Columns
! indices for fields in the physics buffer
integer :: cam3_sul_idx, cam3_ssam_idx, cam3_sscm_idx, &
cam3_dust1_idx, cam3_dust2_idx, cam3_dust3_idx, cam3_dust4_idx,&
cam3_ocpho_idx, cam3_bcpho_idx, cam3_ocphi_idx, cam3_bcphi_idx
!================================================================================================
contains
!================================================================================================
subroutine cam3_aero_data_readnl(nlfile) 1,9
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
character(len=*), parameter :: subname = 'cam3_aero_data_readnl'
namelist /cam3_aero_data_nl/ cam3_aero_data_on, bndtvaer
!-----------------------------------------------------------------------------
if (masterproc) then
unitn = getunit
()
open( unitn, file=trim(nlfile), status='old' )
call find_group_name
(unitn, 'cam3_aero_data_nl', status=ierr)
if (ierr == 0) then
read(unitn, cam3_aero_data_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
(cam3_aero_data_on, 1, mpilog, 0, mpicom)
call mpibcast
(bndtvaer, len(bndtvaer), mpichar, 0, mpicom)
#endif
end subroutine cam3_aero_data_readnl
!================================================================================================
subroutine cam3_aero_data_register 1,12
! register old prescribed aerosols with physics buffer
use phys_buffer
, only: pbuf_add
call pbuf_add
('cam3_sul', 'physpkg', 1, pver, 1, cam3_sul_idx)
call pbuf_add
('cam3_ssam', 'physpkg', 1, pver, 1, cam3_ssam_idx)
call pbuf_add
('cam3_sscm', 'physpkg', 1, pver, 1, cam3_sscm_idx)
call pbuf_add
('cam3_dust1', 'physpkg', 1, pver, 1, cam3_dust1_idx)
call pbuf_add
('cam3_dust2', 'physpkg', 1, pver, 1, cam3_dust2_idx)
call pbuf_add
('cam3_dust3', 'physpkg', 1, pver, 1, cam3_dust3_idx)
call pbuf_add
('cam3_dust4', 'physpkg', 1, pver, 1, cam3_dust4_idx)
call pbuf_add
('cam3_ocpho', 'physpkg', 1, pver, 1, cam3_ocpho_idx)
call pbuf_add
('cam3_bcpho', 'physpkg', 1, pver, 1, cam3_bcpho_idx)
call pbuf_add
('cam3_ocphi', 'physpkg', 1, pver, 1, cam3_ocphi_idx)
call pbuf_add
('cam3_bcphi', 'physpkg', 1, pver, 1, cam3_bcphi_idx)
end subroutine cam3_aero_data_register
!================================================================================================
subroutine cam3_aero_data_init(phys_state) 1,38
!------------------------------------------------------------------
! Reads in:
! file from which to read aerosol Masses on CAM grid. Currently
! assumed to be MATCH ncep runs, averaged by month.
! NOTE (Data have been externally interpolated onto CAM grid
! and backsolved to provide Mid-month values)
!
! Populates:
! module variables:
! aerosolc(pcols,paerlev+1,begchunk:endchunk,naer,2))
! aerosolc( column_index
! , level_index (match levels)
! , chunk_index
! , species_index
! , month = 1:2 )
! M_hybi(level_index = Lev_MATCH) = pressure at mid-level.
! M_ps_cam_col(column,chunk,month) ! PS from MATCH on Cam Columns
!
! Method:
! read data from file
! allocate memory for storage of aerosol data on CAM horizontal grid
! distribute data to remote nodes
! populates the module variables
!
!------------------------------------------------------------------
use ioFileMod
, only: getfil
#if ( defined SPMD )
use mpishorthand
#endif
type(physics_state), intent(in) :: phys_state(begchunk:endchunk)
! local variables
integer :: naerlev
integer dateid ! netcdf id for date variable
integer secid ! netcdf id for seconds variable
integer londimid ! netcdf id for longitude dimension
integer latdimid ! netcdf id for latitude dimension
integer levdimid ! netcdf id for level dimension
integer timesiz ! number of time samples (=12) in netcdf file
integer latid ! netcdf id for latitude variable
integer Mhybiid ! netcdf id for MATCH hybi
integer timeid ! netcdf id for time variable
integer dimids(nf90_max_var_dims) ! variable shape
integer :: start(4) ! start vector for netcdf calls
integer :: kount(4) ! count vector for netcdf calls
integer mo ! month index
integer m ! constituent index
integer :: n ! loop index
integer :: i,j,k ! spatial indices
integer :: date_aer(12) ! Date on aerosol dataset (YYYYMMDD)
integer :: attnum ! attribute number
integer :: ierr ! netcdf return code
real(r8) :: coldata(paerlev) ! aerosol field read in from dataset
integer :: ret
integer mo_prv ! index to previous month
integer latidx,lonidx
character(len=8) :: aname ! temporary aerosol name
character(len=8) :: tmp_aero_name(naer) ! name for input to boundary data
character(len=256) :: locfn ! netcdf local filename to open
!
! aerosol_data will be read in from the aerosol boundary dataset, then scattered to chunks
! after filling in the bottom level with zeros
!
real(r8), allocatable :: aerosol_data(:,:,:) ! aerosol field read in from dataset
real(r8), allocatable :: aerosol_field(:,:,:) ! (plon,paerlev+1,plat) aerosol field to be scattered
real(r8) :: caldayloc ! calendar day of current timestep
real(r8) :: closelat,closelon
character(len=*), parameter :: subname = 'cam3_aero_data_init'
!------------------------------------------------------------------
call t_startf(subname)
allocate (aer_mass(pcols, pver, naer, begchunk:endchunk) )
! set new aerosol names because input file has 1 seasalt bin
do m = 1, naer
tmp_aero_name(m)=aerosol_name(m)
if (aerosol_name(m)=='MSSLTA_V') tmp_aero_name(m) = 'MSSLT_V'
if (aerosol_name(m)=='MSSLTC_V') tmp_aero_name(m) = 'MSSLT_V'
end do
allocate (aerosolc(pcols,paerlev+1,begchunk:endchunk,naer,2))
aerosolc(:,:,:,:,:) = 0._r8
caldayloc = get_curr_calday
()
if (caldayloc < Mid(1)) then
mo_prv = 12
mo_nxt = 1
else if (caldayloc >= Mid(12)) then
mo_prv = 12
mo_nxt = 1
else
do i = 2 , 12
if (caldayloc < Mid(i)) then
mo_prv = i-1
mo_nxt = i
exit
end if
end do
end if
! Set initial calendar day values
cdaym = Mid(mo_prv)
cdayp = Mid(mo_nxt)
if (masterproc) &
write(iulog,*) subname//': CAM3 prescribed aerosol dataset is: ', trim(bndtvaer)
call getfil
(bndtvaer, locfn, 0)
call handle_ncerr
( nf90_open (locfn, 0, aernid),&
subname, __LINE__)
if (single_column) &
call shr_scam_GetCloseLatLon
(aernid,scmlat,scmlon,closelat,closelon,latidx,lonidx)
! Check to see if this dataset is in ncol format.
aerosol_datan%isncol=.false.
ierr = nf90_inq_dimid( aernid, 'ncol', londimid )
if ( ierr==NF90_NOERR ) then
aerosol_datan%isncol=.true.
call handle_ncerr
(nf90_close(aernid),subname, __LINE__)
call boundarydata_init
(bndtvaer, phys_state, tmp_aero_name, naer, &
aerosol_datan, 3)
aerosolc(:,1:paerlev,:,:,:)=aerosol_datan%fields
M_ps_cam_col=>aerosol_datan%ps
M_hybi=>aerosol_datan%hybi
else
! Allocate memory for dynamic arrays local to this module
allocate (M_ps_cam_col(pcols,begchunk:endchunk,2))
allocate (M_hybi(paerlev+1))
! TBH: HACK to avoid use of uninitialized values when ncols < pcols
M_ps_cam_col(:,:,:) = 0._r8
if (masterproc) then
! First ensure dataset is CAM-ready
call handle_ncerr
(nf90_inquire_attribute (aernid, nf90_global, 'cam-ready', attnum=attnum),&
subname//': interpaerosols needs to be run to create a cam-ready aerosol dataset')
! Get and check dimension info
call handle_ncerr
( nf90_inq_dimid( aernid, 'lon', londimid ),&
subname, __LINE__)
call handle_ncerr
( nf90_inq_dimid( aernid, 'lev', levdimid ),&
subname, __LINE__)
call handle_ncerr
( nf90_inq_dimid( aernid, 'time', timeid ),&
subname, __LINE__)
call handle_ncerr
( nf90_inq_dimid( aernid, 'lat', latdimid ),&
subname, __LINE__)
call handle_ncerr
( nf90_inquire_dimension( aernid, londimid, len=naerlon ),&
subname, __LINE__)
call handle_ncerr
( nf90_inquire_dimension( aernid, levdimid, len=naerlev ),&
subname, __LINE__)
call handle_ncerr
( nf90_inquire_dimension( aernid, latdimid, len=naerlat ),&
subname, __LINE__)
call handle_ncerr
( nf90_inquire_dimension( aernid, timeid, len=timesiz ),&
subname, __LINE__)
call handle_ncerr
( nf90_inq_varid( aernid, 'date', dateid ),&
subname, __LINE__)
call handle_ncerr
( nf90_inq_varid( aernid, 'datesec', secid ),&
subname, __LINE__)
do m = 1, naer
aname=aerosol_name(m)
! rename because file has only one seasalt field
if (aname=='MSSLTA_V') aname = 'MSSLT_V'
if (aname=='MSSLTC_V') aname = 'MSSLT_V'
call handle_ncerr
( nf90_inq_varid( aernid, TRIM(aname), species_id(m)), &
subname, __LINE__)
end do
call handle_ncerr
( nf90_inq_varid( aernid, 'lat', latid ),&
subname, __LINE__)
! quick sanity check on one field
call handle_ncerr
( nf90_inquire_variable (aernid, species_id(1), dimids=dimids),&
subname, __LINE__)
if ( (dimids(4) /= timeid) .or. &
(dimids(3) /= levdimid) .or. &
(dimids(2) /= latdimid) .or. &
(dimids(1) /= londimid) ) then
write(iulog,*) subname//': Data must be ordered time, lev, lat, lon'
write(iulog,*) 'data are ordered as', dimids(4), dimids(3), dimids(2), dimids(1)
write(iulog,*) 'data should be ordered as', timeid, levdimid, latdimid, londimid
call endrun
()
end if
! use hybi,PS from MATCH
call handle_ncerr
( nf90_inq_varid( aernid, 'hybi', Mhybiid ),&
subname, __LINE__)
call handle_ncerr
( nf90_inq_varid( aernid, 'PS', Mpsid ),&
subname, __LINE__)
! check dimension order for MATCH's surface pressure
call handle_ncerr
( nf90_inquire_variable (aernid, Mpsid, dimids=dimids),&
subname, __LINE__)
if ( (dimids(3) /= timeid) .or. &
(dimids(2) /= latdimid) .or. &
(dimids(1) /= londimid) ) then
write(iulog,*) subname//': Pressure must be ordered time, lat, lon'
write(iulog,*) 'data are ordered as', dimids(3), dimids(2), dimids(1)
write(iulog,*) 'data should be ordered as', timeid, levdimid, latdimid, londimid
call endrun
()
end if
! read in hybi from MATCH
call handle_ncerr
( nf90_get_var (aernid, Mhybiid, M_hybi),&
subname, __LINE__)
! Retrieve date and sec variables.
call handle_ncerr
( nf90_get_var (aernid, dateid, date_aer),&
subname, __LINE__)
if (timesiz < 12) then
write(iulog,*) subname//': When cycling aerosols, dataset must have 12 consecutive ', &
'months of data starting with Jan'
write(iulog,*) 'Current dataset has only ',timesiz,' months'
call endrun
()
end if
do mo = 1,12
if (mod(date_aer(mo),10000)/100 /= mo) then
write(iulog,*) subname//': When cycling aerosols, dataset must have 12 consecutive ', &
'months of data starting with Jan'
write(iulog,*)'Month ',mo,' of dataset says date=',date_aer(mo)
call endrun
()
end if
end do
if (single_column) then
naerlat=1
naerlon=1
endif
kount(:) = (/naerlon,naerlat,paerlev,1/)
end if ! masterproc
! broadcast hybi to nodes
#if ( defined SPMD )
call mpibcast
(M_hybi, paerlev+1, mpir8, 0, mpicom)
call mpibcast
(kount, 3, mpiint, 0, mpicom)
naerlon = kount(1)
naerlat = kount(2)
#endif
allocate(aerosol_field(kount(1),kount(3)+1,kount(2)))
allocate(M_ps(kount(1),kount(2)))
if (masterproc) allocate(aerosol_data(kount(1),kount(2),kount(3)))
! Retrieve Aerosol Masses (kg/m^2 in each layer), transpose to model order (lon,lev,lat),
! then scatter to slaves.
if (nm /= 1 .or. np /= 2) call endrun
(subname//': bad nm or np value')
do n=nm,np
if (n == 1) then
mo = mo_prv
else
mo = mo_nxt
end if
do m=1,naer
if (masterproc) then
if (single_column) then
start(:) = (/lonidx,latidx,1,mo/)
else
start(:) = (/1,1,1,mo/)
endif
kount(:) = (/naerlon,naerlat,paerlev,1/)
call handle_ncerr
( nf90_get_var (aernid, species_id(m),aerosol_data, start, kount),&
subname, __LINE__)
do j=1,naerlat
do k=1,paerlev
aerosol_field(:,k,j) = aerosol_data(:,j,k)
end do
aerosol_field(:,paerlev+1,j) = 0._r8 ! value at bottom
end do
end if
call scatter_field_to_chunk
(1, paerlev+1, 1, naerlon, aerosol_field, &
aerosolc(:,:,:,m,n))
end do
! Retrieve PS from Match
if (masterproc) then
if (single_column) then
start(:) = (/lonidx,latidx,mo,-1/)
else
start(:) = (/1,1,mo,-1/)
endif
kount(:) = (/naerlon,naerlat,1,-1/)
call handle_ncerr
( nf90_get_var(aernid, Mpsid, M_ps,start,kount),&
subname, __LINE__)
end if
call scatter_field_to_chunk
(1, 1, 1, naerlon, M_ps(:,:), M_ps_cam_col(:,:,n))
end do ! n=nm,np (=1,2)
if(masterproc) deallocate(aerosol_data)
deallocate(aerosol_field)
end if ! Check to see if this dataset is in ncol format.
call t_stopf(subname)
end subroutine cam3_aero_data_init
!================================================================================================
subroutine cam3_aero_data_timestep_init(pbuf, phys_state) 1,12
!------------------------------------------------------------------
!
! Input:
! time at which aerosol masses are needed (get_curr_calday())
! chunk index
! CAM's vertical grid (pint)
!
! Output:
! values for Aerosol Mass at time specified by get_curr_calday
! on vertical grid specified by pint (aer_mass) :: aerosol at time t
!
! Method:
! first determine which indexs of aerosols are the bounding data sets
! interpolate both onto vertical grid aerm(),aerp().
! from those two, interpolate in time.
!
!------------------------------------------------------------------
use interpolate_data
, only: get_timeinterp_factors
use phys_buffer
, only: pbuf_size_max, pbuf_fld
use cam_logfile
, only: iulog
use ppgrid
, only: begchunk,endchunk
use physconst
, only: gravit
!
! aerosol fields interpolated to current time step
! on pressure levels of this time step.
! these should be made read-only for other modules
! Is allocation done correctly here?
!
type(pbuf_fld), intent(inout), dimension(pbuf_size_max) :: pbuf
type(physics_state), intent(in), dimension(begchunk:endchunk) :: phys_state
!
! Local workspace
!
real(r8) :: pint(pcols,pverp) ! interface pres.
integer :: c ! chunk index
real(r8) caldayloc ! calendar day of current timestep
real(r8) fact1, fact2 ! time interpolation factors
integer i, k, j ! spatial indices
integer m ! constituent index
integer lats(pcols),lons(pcols) ! latitude and longitudes of column
integer ncol ! number of columns
real(r8) speciesmin(naer) ! minimal value for each species
!
! values before current time step "the minus month"
! aerosolm(pcols,pver) is value of preceeding month's aerosol masses
! aerosolp(pcols,pver) is value of next month's aerosol masses
! (think minus and plus or values to left and right of point to be interpolated)
!
real(r8) aerosolm(pcols,pver,naer,begchunk:endchunk) ! aerosol mass from MATCH in column,level at previous (minus) month
!
! values beyond (or at) current time step "the plus month"
!
real(r8) aerosolp(pcols,pver,naer,begchunk:endchunk) ! aerosol mass from MATCH in column,level at next (plus) month
real(r8) :: mass_to_mmr(pcols,pver)
character(len=*), parameter :: subname = 'cam3_aero_data_timestep_init'
logical error_found
!------------------------------------------------------------------
call aerint
(phys_state)
caldayloc = get_curr_calday
()
! Determine time interpolation factors. 1st arg says we are cycling 1 year of data
call get_timeinterp_factors
(.true., mo_nxt, cdaym, cdayp, caldayloc, &
fact1, fact2, 'GET_AEROSOL:')
! interpolate (prv and nxt month) bounding datasets onto cam vertical grid.
! compute mass mixing ratios on CAMS's pressure coordinate
! for both the "minus" and "plus" months
!
! This loop over chunk could probably be removed by working with the whole
! begchunk:endchunk group at once. It would require a slight generalization
! in vert_interpolate.
do c = begchunk,endchunk
pint = phys_state(c)%pint
ncol = get_ncols_p
(c)
call vert_interpolate
(M_ps_cam_col(:,c,nm), pint, nm, aerosolm(:,:,:,c), ncol, c)
call vert_interpolate
(M_ps_cam_col(:,c,np), pint, np, aerosolp(:,:,:,c), ncol, c)
! Time interpolate.
do m=1,naer
do k=1,pver
do i=1,ncol
aer_mass(i,k,m,c) = aerosolm(i,k,m,c)*fact1 + aerosolp(i,k,m,c)*fact2
end do
end do
! Partition seasalt aerosol mass
if (m .eq. idxSSLTA) then
aer_mass(:ncol,:,m,c) = (1.-wgt_sscm)*aer_mass(:ncol,:,m,c) ! fraction of seasalt mass in accumulation mode
elseif (m .eq. idxSSLTC) then
aer_mass(:ncol,:,m,c) = wgt_sscm*aer_mass(:ncol,:,m,c) ! fraction of seasalt mass in coarse mode
endif
end do
! exit if mass is negative (we have previously set
! cumulative mass to be a decreasing function.)
speciesmin(:) = 0._r8 ! speciesmin(m) = 0 is minimum mass for each species
error_found = .false.
do m=1,naer
do k=1,pver
do i=1,ncol
if (aer_mass(i, k, m,c) < speciesmin(m)) error_found = .true.
end do
end do
end do
if (error_found) then
do m=1,naer
do k=1,pver
do i=1,ncol
if (aer_mass(i, k, m,c) < speciesmin(m)) then
write(iulog,*) subname//': negative mass mixing ratio, exiting'
write(iulog,*) 'm, column, pver',m, i, k ,aer_mass(i, k, m,c)
call endrun
()
end if
end do
end do
end do
end if
do k = 1, pver
mass_to_mmr(1:ncol,k) = gravit/(pint(1:ncol,k+1)-pint(1:ncol,k))
enddo
pbuf(cam3_sul_idx )%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxSUL,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_ssam_idx )%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxSSLTA,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_sscm_idx )%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxSSLTC,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_dust1_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxDUSTfirst,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_dust2_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:,idxDUSTfirst+1,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_dust3_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:,idxDUSTfirst+2,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_dust4_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:,idxDUSTfirst+3,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_ocpho_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxOCPHO,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_bcpho_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxBCPHO,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_ocphi_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxOCPHI,c)*mass_to_mmr(:ncol,:)
pbuf(cam3_bcphi_idx)%fld_ptr(1,1:ncol,:,c,1)=aer_mass(1:ncol,:, idxBCPHI,c)*mass_to_mmr(:ncol,:)
enddo ! c = begchunk:endchunk
end subroutine cam3_aero_data_timestep_init
!================================================================================================
subroutine vert_interpolate (Match_ps, pint, n, aerosol_mass, ncol, c) 2,2
!--------------------------------------------------------------------
! Input: match surface pressure, cam interface pressure,
! month index, number of columns, chunk index
!
! Output: Aerosol mass mixing ratio (aerosol_mass)
!
! Method:
! interpolate column mass (cumulative) from match onto
! cam's vertical grid (pressure coordinate)
! convert back to mass mixing ratio
!
!--------------------------------------------------------------------
real(r8), intent(out) :: aerosol_mass(pcols,pver,naer) ! aerosol mass from MATCH
real(r8), intent(in) :: Match_ps(pcols) ! surface pressure at a particular month
real(r8), intent(in) :: pint(pcols,pverp) ! interface pressure from CAM
integer, intent(in) :: ncol,c ! chunk index and number of columns
integer, intent(in) :: n ! prv or nxt month index
!
! Local workspace
!
integer m ! index to aerosol species
integer kupper(pcols) ! last upper bound for interpolation
integer i, k, kk, kkstart, kount ! loop vars for interpolation
integer isv, ksv, msv ! loop indices to save
logical bad ! indicates a bad point found
logical lev_interp_comp ! interpolation completed for a level
logical error_found
real(r8) aerosol(pcols,pverp,naer) ! cumulative mass of aerosol in column beneath upper
! interface of level in column at particular month
real(r8) dpl, dpu ! lower and upper intepolation factors
real(r8) v_coord ! vertical coordinate
real(r8) AER_diff ! temp var for difference between aerosol masses
character(len=*), parameter :: subname = 'cam3_aero_data.vert_interpolate'
!-----------------------------------------------------------------------
call t_startf ('vert_interpolate')
!
! Initialize index array
!
do i=1,ncol
kupper(i) = 1
end do
!
! assign total mass to topmost level
!
aerosol(:,1,:) = aerosolc(:,1,c,:,n)
!
! At every pressure level, interpolate onto that pressure level
!
do k=2,pver
!
! Top level we need to start looking is the top level for the previous k
! for all longitude points
!
kkstart = paerlev+1
do i=1,ncol
kkstart = min0(kkstart,kupper(i))
end do
kount = 0
!
! Store level indices for interpolation
!
! for the pressure interpolation should be comparing
! pint(column,lev) with M_hybi(lev)*M_ps_cam_col(month,column,chunk)
!
lev_interp_comp = .false.
do kk=kkstart,paerlev
if(.not.lev_interp_comp) then
do i=1,ncol
v_coord = pint(i,k)
if (M_hybi(kk)*Match_ps(i) .lt. v_coord .and. v_coord .le. M_hybi(kk+1)*Match_ps(i)) 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
!
! Interpolate in pressure.
!
if (kount.eq.ncol) then
do m=1,naer
do i=1,ncol
dpu = pint(i,k) - M_hybi(kupper(i))*Match_ps(i)
dpl = M_hybi(kupper(i)+1)*Match_ps(i) - pint(i,k)
aerosol(i,k,m) = &
(aerosolc(i,kupper(i) ,c,m,n)*dpl + &
aerosolc(i,kupper(i)+1,c,m,n)*dpu)/(dpl + dpu)
enddo !i
end do
lev_interp_comp = .true.
end if
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 pressure level for at least some
! of the longitude points.
!
if(.not.lev_interp_comp) then
do m=1,naer
do i=1,ncol
if (pint(i,k) .lt. M_hybi(1)*Match_ps(i)) then
aerosol(i,k,m) = aerosolc(i,1,c,m,n)
else if (pint(i,k) .gt. M_hybi(paerlev+1)*Match_ps(i)) then
aerosol(i,k,m) = 0.0_r8
else
dpu = pint(i,k) - M_hybi(kupper(i))*Match_ps(i)
dpl = M_hybi(kupper(i)+1)*Match_ps(i) - pint(i,k)
aerosol(i,k,m) = &
(aerosolc(i,kupper(i) ,c,m,n)*dpl + &
aerosolc(i,kupper(i)+1,c,m,n)*dpu)/(dpl + dpu)
end if
end do
end do
if (kount.gt.ncol) then
call endrun
(subname//': Bad data: non-monotonicity suspected in dependent variable')
end if
end if
end do
! call t_startf ('vi_checks')
!
! aerosol mass beneath lowest interface (pverp) must be 0
!
aerosol(1:ncol,pverp,:) = 0._r8
!
! Set mass in layer to zero whenever it is less than
! 1.e-40 kg/m^2 in the layer
!
do m = 1, naer
do k = 1, pver
do i = 1, ncol
if (aerosol(i,k,m) < 1.e-40_r8) aerosol(i,k,m) = 0._r8
end do
end do
end do
!
! Set mass in layer to zero whenever it is less than
! 10^-15 relative to column total mass
!
error_found = .false.
do m = 1, naer
do k = 1, pver
do i = 1, ncol
AER_diff = aerosol(i,k,m) - aerosol(i,k+1,m)
if( abs(AER_diff) < 1e-15_r8*aerosol(i,1,m)) then
AER_diff = 0._r8
end if
aerosol_mass(i,k,m)= AER_diff
if (aerosol_mass(i,k,m) < 0) error_found = .true.
end do
end do
end do
if (error_found) then
do m = 1, naer
do k = 1, pver
do i = 1, ncol
if (aerosol_mass(i,k,m) < 0) then
write(iulog,*) subname//': mass < 0, m, col, lev, mass',m, i, k, aerosol_mass(i,k,m)
write(iulog,*) subname//': aerosol(k),(k+1)',aerosol(i,k,m),aerosol(i,k+1,m)
write(iulog,*) subname//': pint(k+1),(k)',pint(i,k+1),pint(i,k)
write(iulog,*)'n,c',n,c
call endrun
()
end if
end do
end do
end do
end if
call t_stopf ('vert_interpolate')
return
end subroutine vert_interpolate
!================================================================================================
subroutine aerint (phys_state) 1,9
type(physics_state), intent(in) :: phys_state(begchunk:endchunk)
integer :: ntmp ! used in index swapping
integer :: start(4) ! start vector for netcdf calls
integer :: kount(4) ! count vector for netcdf calls
integer :: i,j,k ! spatial indices
integer :: m ! constituent index
integer :: cols, cole
integer :: lchnk, ncol
real(r8) :: caldayloc ! calendar day of current timestep
real(r8) :: aerosol_data(naerlon,naerlat,paerlev) ! aerosol field read in from dataset
real(r8) :: aerosol_field(naerlon,paerlev+1,naerlat) ! aerosol field to be scattered
integer latidx,lonidx
real(r8) closelat,closelon
character(len=*), parameter :: subname = 'cam3_aero_data.aerint'
!-----------------------------------------------------------------------
if (single_column) &
call shr_scam_GetCloseLatLon
(aernid,scmlat,scmlon,closelat,closelon,latidx,lonidx)
!
! determine if need to read in next month data
! also determine time interpolation factors
!
caldayloc = get_curr_calday
()
!
! If model time is past current forward timeslice, then
! masterproc reads in the next timeslice for time interpolation. Messy logic is
! for interpolation between December and January (mo_nxt == 1). Just like
! ozone_data_timestep_init, sstint.
!
if (caldayloc > cdayp .and. .not. (mo_nxt == 1 .and. caldayloc >= cdaym)) then
mo_nxt = mod(mo_nxt,12) + 1
cdaym = cdayp
cdayp = Mid(mo_nxt)
!
! Check for valid date info
!
if (.not. (mo_nxt == 1 .or. caldayloc <= cdayp)) then
call endrun
(subname//': Non-monotonicity suspected in input aerosol data')
end if
ntmp = nm
nm = np
np = ntmp
if(aerosol_datan%isncol) then
do lchnk=begchunk,endchunk
ncol=phys_state(lchnk)%ncol
cols=1
cole=cols+aerosol_datan%count(cols,lchnk)-1
do while(cole<=ncol)
start=(/aerosol_datan%start(cols,lchnk),mo_nxt,1,-1/)
kount=(/aerosol_datan%count(cols,lchnk),1,-1,-1/)
call handle_ncerr
( nf90_get_var(aerosol_datan%ncid, aerosol_datan%psid , &
aerosol_datan%ps(cols:cole,lchnk,np), start(1:2), &
kount(1:2)),&
subname, __LINE__)
start(2)=1
start(3)=mo_nxt
kount(2)=paerlev
kount(3)=1
do m=1,naer
call handle_ncerr
( nf90_get_var(aerosol_datan%ncid, aerosol_datan%dataid(m) , &
aerosol_datan%fields(cols:cole,:,lchnk,m,np), &
start(1:3), kount(1:3)),&
subname, __LINE__)
end do
if(cols==ncol) exit
cols=cols+aerosol_datan%count(cols,lchnk)
cole=cols+aerosol_datan%count(cols,lchnk)-1
end do
end do
aerosolc(:,1:paerlev,:,:,np)=aerosol_datan%fields(:,:,:,:,np)
else
do m=1,naer
if (masterproc) then
if (single_column) then
naerlon=1
naerlat=1
start(:) = (/lonidx,latidx,1,mo_nxt/)
else
start(:) = (/1,1,1,mo_nxt/)
endif
kount(:) = (/naerlon,naerlat,paerlev,1/)
call handle_ncerr
( nf90_get_var (aernid, species_id(m), aerosol_data, start, kount),&
subname, __LINE__)
do j=1,naerlat
do k=1,paerlev
aerosol_field(:,k,j) = aerosol_data(:,j,k)
end do
aerosol_field(:,paerlev+1,j) = 0._r8 ! value at bottom
end do
end if
call scatter_field_to_chunk
(1, paerlev+1, 1, naerlon, aerosol_field, &
aerosolc(:,:,:,m,np))
end do
!
! Retrieve PS from Match
!
if (masterproc) then
if (single_column) then
naerlon=1
naerlat=1
start(:) = (/lonidx,latidx,mo_nxt,-1/)
else
start(:) = (/1,1,mo_nxt,-1/)
endif
kount(:) = (/naerlon,naerlat,1,-1/)
call handle_ncerr
( nf90_get_var (aernid, Mpsid, M_ps, start, kount),&
subname, __LINE__)
write(iulog,*) subname//': Read aerosols data for julian day', Mid(mo_nxt)
end if
call scatter_field_to_chunk
(1, 1, 1, naerlon, M_ps(:,:), M_ps_cam_col(:,:,np))
end if
end if
end subroutine aerint
end module cam3_aero_data