diff --git a/schemes/chemistry/prescribed_aerosol_deposition_flux.F90 b/schemes/chemistry/prescribed_aerosol_deposition_flux.F90
new file mode 100644
index 00000000..181aa283
--- /dev/null
+++ b/schemes/chemistry/prescribed_aerosol_deposition_flux.F90
@@ -0,0 +1,331 @@
+! Manages reading and interpolation of prescribed aerosol deposition
+! fluxes. These are the deposition fluxes sent to the surface model.
+!
+! Note: this module currently only implements bulk aerosol deposition fluxes.
+!
+! This scheme requires the concurrent use of prescribed_aerosols scheme
+! to provide prescribed aerosol concentrations.
+!
+! Based on original CAM version from: Francis Vitt
+module prescribed_aerosol_deposition_flux
+ ! CAM-SIMA host model dependency to read aerosol data.
+ use tracer_data, only: trfile ! data information and file read state.
+ use tracer_data, only: trfld ! tracer data container.
+
+ implicit none
+ private
+
+ ! public CCPP-compliant subroutines
+ public :: prescribed_aerosol_deposition_flux_init
+ public :: prescribed_aerosol_deposition_flux_run
+
+ ! fields to store tracer_data state and information.
+ type(trfld), pointer :: tracer_data_fields(:)
+ type(trfile) :: tracer_data_file
+
+ ! is this module active?
+ logical :: has_aerodep_flx = .false.
+
+ ! for bulk aerosol fluxes
+ integer, parameter :: N_BULK = 14
+ logical :: is_bulk = .false.
+
+ ! list of bulk aerosol flux field names
+ ! these and the index fields below are explicitly enumerated
+ ! because they are explicitly enumerated in the cam_out coupler data structure.
+ ! no runtime configurability is achievable here if cam_out hardcodes these.
+ character(len=12), parameter :: bulk_names(N_BULK) = [&
+ 'BCDEPWET ', 'BCPHODRY ', 'BCPHIDRY ', &
+ 'OCDEPWET ', 'OCPHODRY ', 'OCPHIDRY ', &
+ 'DSTX01DD ', 'DSTX02DD ', 'DSTX03DD ', 'DSTX04DD ', &
+ 'DSTX01WD ', 'DSTX02WD ', 'DSTX03WD ', 'DSTX04WD ']
+ integer :: index_bulk_map(N_BULK)
+ integer :: ibcphiwet, ibcphidry, ibcphodry
+ integer :: iocphiwet, iocphidry, iocphodry
+ integer :: idst1dry, idst2dry, idst3dry, idst4dry
+ integer :: idst1wet, idst2wet, idst3wet, idst4wet
+
+ ! for modal aerosol fluxes (unavailable)
+ integer, parameter :: N_MODAL = 22
+ logical :: is_modal = .false.
+
+contains
+!> \section arg_table_prescribed_aerosol_deposition_flux_init Argument Table
+!! \htmlinclude prescribed_aerosol_deposition_flux_init.html
+ subroutine prescribed_aerosol_deposition_flux_init( &
+ amIRoot, iulog, &
+ specifier, &
+ filename, filelist, datapath, &
+ data_type, &
+ cycle_yr, fixed_ymd, fixed_tod, &
+ prescribed_aero_model, &
+ errmsg, errflg)
+
+ !
+ use tracer_data, only: trcdata_init
+
+ ! host model dependency for history diagnostics
+ use cam_history, only: history_add_field
+ use cam_history_support, only: horiz_only
+
+ logical, intent(in) :: amIRoot
+ integer, intent(in) :: iulog
+ character(len=*), intent(in) :: specifier(:) ! field specifiers for tracer data
+ character(len=*), intent(in) :: filename ! input filename
+ character(len=*), intent(in) :: filelist ! input filelist
+ character(len=*), intent(in) :: datapath ! input datapath
+ character(len=*), intent(in) :: data_type ! data type
+ integer, intent(in) :: cycle_yr ! cycle year
+ integer, intent(in) :: fixed_ymd ! fixed year-month-day (YYYYMMDD) [1]
+ integer, intent(in) :: fixed_tod ! fixed time of day [s]
+ character(len=*), intent(in) :: prescribed_aero_model ! type of aerosol representation
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ integer :: i, ndx, number_flds
+ character(len=*), parameter :: subname = 'prescribed_aerosol_deposition_flux_init'
+
+ errmsg = ''
+ errflg = 0
+
+ ! check if user has specified an input dataset
+ if (filename /= 'UNSET' .and. len_trim(filename) > 0) then
+ has_aerodep_flx = .true.
+
+ if (amIRoot) then
+ write(iulog,*) subname//': aerosol deposition fluxes prescribed in: '//trim(filename)
+ write(iulog,*) subname//': aerosol representation is: '//trim(prescribed_aero_model)
+ end if
+ else
+ return
+ end if
+
+ select case(prescribed_aero_model)
+ case('bulk')
+ is_bulk = .true.
+ case('modal')
+ is_modal = .true.
+ errflg = 1
+ errmsg = subname//': modal aerosol mode not yet implemented'
+ return
+ case default
+ errflg = 1
+ errmsg = subname//': ERROR: unknown aerosol representation: "'//trim(prescribed_aero_model)//'"'
+ end select
+
+ ! count number of specifiers
+ number_flds = 0
+ specifier_count_loop: do i = 1, size(specifier)
+ ! remove empty or unset specifiers
+ if(len_trim(specifier(i)) > 0 .and. trim(specifier(i)) /= 'UNSET') then
+ number_flds = number_flds + 1
+ else
+ ! Assume all remaining specifier entries are also not set.
+ exit specifier_count_loop
+ end if
+ end do specifier_count_loop
+
+ ! initialize dataset in tracer_data module.
+ call trcdata_init( &
+ specifier = specifier(:number_flds), &
+ filename = filename, &
+ filelist = filelist, &
+ datapath = datapath, &
+ flds = tracer_data_fields, & ! ptr
+ file = tracer_data_file, &
+ data_cycle_yr = cycle_yr, &
+ data_fixed_ymd = fixed_ymd, &
+ data_fixed_tod = fixed_tod, &
+ data_type = data_type)
+
+ number_flds = 0
+ if (associated(tracer_data_fields)) number_flds = size(tracer_data_fields)
+
+ if (number_flds < 1) then
+ has_aerodep_flx = .false.
+ if (amIRoot) then
+ write (iulog, *) subname//': no aerosol deposition fluxes have been specified'
+ end if
+ return
+ end if
+
+ ! map field names to indices
+ index_bulk_map(:) = -1
+
+ do i = 1, number_flds
+ ! match specifier field name to known bulk aerosol names
+ ndx = get_ndx(tracer_data_fields(i)%fldnam, bulk_names)
+ if (ndx > 0) then
+ index_bulk_map(ndx) = i
+
+ ! add history field for diagnostics
+ call history_add_field(trim(tracer_data_fields(i)%fldnam)//'_D', &
+ 'prescribed aero deposition flux ' // trim(tracer_data_fields(i)%fldnam), &
+ horiz_only, 'avg', &
+ tracer_data_fields(i)%units)
+ else
+ ! when modal aerosols are available can match against modal names.
+
+ ! if all else fails
+ errflg = 1
+ errmsg = 'prescribed_aerosol_deposition_flux_init: aerosol flux name not recognized: "' &
+ //trim(tracer_data_fields(i)%fldnam)//'"'
+ return
+ end if
+ end do
+
+ ! set up index pointers for bulk fluxes
+ ! these correspond exactly to the order in bulk_names
+ ibcphiwet = index_bulk_map(1)
+ ibcphodry = index_bulk_map(2)
+ ibcphidry = index_bulk_map(3)
+ iocphiwet = index_bulk_map(4)
+ iocphodry = index_bulk_map(5)
+ iocphidry = index_bulk_map(6)
+ idst1dry = index_bulk_map(7)
+ idst2dry = index_bulk_map(8)
+ idst3dry = index_bulk_map(9)
+ idst4dry = index_bulk_map(10)
+ idst1wet = index_bulk_map(11)
+ idst2wet = index_bulk_map(12)
+ idst3wet = index_bulk_map(13)
+ idst4wet = index_bulk_map(14)
+
+ end subroutine prescribed_aerosol_deposition_flux_init
+
+!> \section arg_table_prescribed_aerosol_deposition_flux_run Argument Table
+!! \htmlinclude prescribed_aerosol_deposition_flux_run.html
+ subroutine prescribed_aerosol_deposition_flux_run( &
+ ncol, &
+ pmid, pint, phis, zi, & ! necessary fields for trcdata read.
+ bcphiwet, bcphidry, bcphodry, &
+ ocphiwet, ocphidry, ocphodry, &
+ dst1dry, dst2dry, dst3dry, dst4dry, &
+ dst1wet, dst2wet, dst3wet, dst4wet, &
+ errmsg, errflg)
+
+ use ccpp_kinds, only: kind_phys
+
+ use tracer_data, only: advance_trcdata
+ use cam_history, only: history_out_field
+
+ ! Input arguments:
+ integer, intent(in) :: ncol
+ real(kind_phys), intent(in) :: pmid(:, :) ! air pressure at centers [Pa]
+ real(kind_phys), intent(in) :: pint(:, :) ! air pressure at interfaces [Pa]
+ real(kind_phys), intent(in) :: phis(:) ! surface geopotential [m2 s-2]
+ real(kind_phys), intent(in) :: zi(:, :) ! height above surface, interfaces [m]
+
+ ! Output arguments to coupler:
+ real(kind_phys), intent(out) :: bcphiwet(:) ! wet deposition of hydrophilic black carbon [kg m-2 s-1]
+ real(kind_phys), intent(out) :: bcphidry(:) ! dry deposition of hydrophilic black carbon [kg m-2 s-1]
+ real(kind_phys), intent(out) :: bcphodry(:) ! dry deposition of hydrophobic black carbon [kg m-2 s-1]
+ real(kind_phys), intent(out) :: ocphiwet(:) ! wet deposition of hydrophilic organic carbon [kg m-2 s-1]
+ real(kind_phys), intent(out) :: ocphidry(:) ! dry deposition of hydrophilic organic carbon [kg m-2 s-1]
+ real(kind_phys), intent(out) :: ocphodry(:) ! dry deposition of hydrophobic organic carbon [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst1dry(:) ! dry deposition of dust bin 1 [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst2dry(:) ! dry deposition of dust bin 2 [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst3dry(:) ! dry deposition of dust bin 3 [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst4dry(:) ! dry deposition of dust bin 4 [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst1wet(:) ! wet deposition of dust bin 1 [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst2wet(:) ! wet deposition of dust bin 2 [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst3wet(:) ! wet deposition of dust bin 3 [kg m-2 s-1]
+ real(kind_phys), intent(out) :: dst4wet(:) ! wet deposition of dust bin 4 [kg m-2 s-1]
+
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ errmsg = ''
+ errflg = 0
+
+ ! initialize outputs to zero
+ bcphiwet(:) = 0._kind_phys
+ bcphidry(:) = 0._kind_phys
+ bcphodry(:) = 0._kind_phys
+ ocphiwet(:) = 0._kind_phys
+ ocphidry(:) = 0._kind_phys
+ ocphodry(:) = 0._kind_phys
+ dst1dry (:) = 0._kind_phys
+ dst2dry (:) = 0._kind_phys
+ dst3dry (:) = 0._kind_phys
+ dst4dry (:) = 0._kind_phys
+ dst1wet (:) = 0._kind_phys
+ dst2wet (:) = 0._kind_phys
+ dst3wet (:) = 0._kind_phys
+ dst4wet (:) = 0._kind_phys
+
+ if (.not. has_aerodep_flx) return
+
+ if (is_modal) then
+ ! modal aerosol mode not yet implemented
+ errflg = 1
+ errmsg = 'prescribed_aerosol_deposition_flux_run: modal aerosol mode not yet implemented'
+ return
+ end if
+
+ ! advance data in tracer_data to current time.
+ call advance_trcdata(tracer_data_fields, tracer_data_file, &
+ pmid, pint, phis, zi)
+
+ if(is_bulk) then
+ ! set bulk aerosol fluxes
+ call set_fluxes(bcphiwet, ibcphiwet)
+ call set_fluxes(bcphidry, ibcphidry)
+ call set_fluxes(bcphodry, ibcphodry)
+ call set_fluxes(ocphiwet, iocphiwet)
+ call set_fluxes(ocphidry, iocphidry)
+ call set_fluxes(ocphodry, iocphodry)
+ call set_fluxes(dst1dry, idst1dry)
+ call set_fluxes(dst2dry, idst2dry)
+ call set_fluxes(dst3dry, idst3dry)
+ call set_fluxes(dst4dry, idst4dry)
+ call set_fluxes(dst1wet, idst1wet)
+ call set_fluxes(dst2wet, idst2wet)
+ call set_fluxes(dst3wet, idst3wet)
+ call set_fluxes(dst4wet, idst4wet)
+ end if
+
+ end subroutine prescribed_aerosol_deposition_flux_run
+
+ ! helper subroutine to set fluxes based on target (hardcoded) array and tracer data index
+ subroutine set_fluxes(fluxes, fld_idx)
+ use ccpp_kinds, only: kind_phys
+
+ ! host model dependency for history output
+ use cam_history, only: history_out_field
+
+ real(kind_phys), intent(inout) :: fluxes(:)
+ integer, intent(in) :: fld_idx
+
+ integer :: i
+
+ if (fld_idx < 1) return
+
+ ! copy from tracer_data container (surface field only)
+ fluxes(:) = tracer_data_fields(fld_idx)%data(:, 1)
+
+ ! output diagnostic
+ call history_out_field(trim(tracer_data_fields(fld_idx)%fldnam)//'_D', &
+ fluxes(:))
+
+ end subroutine set_fluxes
+
+ pure integer function get_ndx(name, list)
+ character(len=*), intent(in) :: name
+ character(len=*), intent(in) :: list(:)
+
+ integer :: i
+ integer :: maxnum
+
+ maxnum = size(list)
+
+ get_ndx = -1
+ do i = 1, maxnum
+ if (trim(name) == trim(list(i))) then
+ get_ndx = i
+ return
+ end if
+ end do
+
+ end function get_ndx
+end module prescribed_aerosol_deposition_flux
diff --git a/schemes/chemistry/prescribed_aerosol_deposition_flux.meta b/schemes/chemistry/prescribed_aerosol_deposition_flux.meta
new file mode 100644
index 00000000..7f932c58
--- /dev/null
+++ b/schemes/chemistry/prescribed_aerosol_deposition_flux.meta
@@ -0,0 +1,215 @@
+[ccpp-table-properties]
+ name = prescribed_aerosol_deposition_flux
+ type = scheme
+
+[ccpp-arg-table]
+ name = prescribed_aerosol_deposition_flux_init
+ type = scheme
+[ amIRoot ]
+ standard_name = flag_for_mpi_root
+ units = flag
+ type = logical
+ dimensions = ()
+ intent = in
+[ iulog ]
+ standard_name = log_output_unit
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ specifier ]
+ standard_name = specifier_for_prescribed_aerosol_deposition_fluxes
+ units = none
+ type = character | kind = len=*
+ dimensions = (aerodep_flx_specifier_dimension)
+ intent = in
+[ filename ]
+ standard_name = filename_for_prescribed_aerosol_deposition_fluxes
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ filelist ]
+ standard_name = filename_of_file_list_for_prescribed_aerosol_deposition_fluxes
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ datapath ]
+ standard_name = datapath_for_prescribed_aerosol_deposition_fluxes
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ data_type ]
+ standard_name = time_interpolation_method_for_prescribed_aerosol_deposition_fluxes
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ cycle_yr ]
+ standard_name = cycle_year_for_prescribed_aerosol_deposition_fluxes
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ fixed_ymd ]
+ standard_name = fixed_date_for_prescribed_aerosol_deposition_fluxes
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ fixed_tod ]
+ standard_name = fixed_time_of_day_for_prescribed_aerosol_deposition_fluxes
+ units = s
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_model ]
+ standard_name = representation_type_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
+
+[ccpp-arg-table]
+ name = prescribed_aerosol_deposition_flux_run
+ type = scheme
+[ ncol ]
+ standard_name = horizontal_loop_extent
+ units = count
+ type = integer
+ dimensions = ()
+ intent = in
+[ pmid ]
+ standard_name = air_pressure
+ units = Pa
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_layer_dimension)
+ intent = in
+[ pint ]
+ standard_name = air_pressure_at_interface
+ units = Pa
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_interface_dimension)
+ intent = in
+[ phis ]
+ standard_name = surface_geopotential
+ units = m2 s-2
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = in
+[ zi ]
+ standard_name = geopotential_height_wrt_surface_at_interface
+ units = m
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_interface_dimension)
+ intent = in
+[ bcphiwet ]
+ standard_name = wet_deposition_flux_of_hydrophilic_black_carbon_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ bcphidry ]
+ standard_name = dry_deposition_flux_of_hydrophilic_black_carbon_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ bcphodry ]
+ standard_name = dry_deposition_flux_of_hydrophobic_black_carbon_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ ocphiwet ]
+ standard_name = wet_deposition_flux_of_hydrophilic_organic_carbon_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ ocphidry ]
+ standard_name = dry_deposition_flux_of_hydrophilic_organic_carbon_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ ocphodry ]
+ standard_name = dry_deposition_flux_of_hydrophobic_organic_carbon_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst1dry ]
+ standard_name = dry_deposition_of_dust_bin1_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst2dry ]
+ standard_name = dry_deposition_of_dust_bin2_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst3dry ]
+ standard_name = dry_deposition_of_dust_bin3_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst4dry ]
+ standard_name = dry_deposition_of_dust_bin4_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst1wet ]
+ standard_name = wet_deposition_of_dust_bin1_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst2wet ]
+ standard_name = wet_deposition_of_dust_bin2_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst3wet ]
+ standard_name = wet_deposition_of_dust_bin3_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ dst4wet ]
+ standard_name = wet_deposition_of_dust_bin4_at_surface_to_coupler
+ units = kg m-2 s-1
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = out
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
diff --git a/schemes/chemistry/prescribed_aerosol_deposition_flux_namelist.xml b/schemes/chemistry/prescribed_aerosol_deposition_flux_namelist.xml
new file mode 100644
index 00000000..3e421355
--- /dev/null
+++ b/schemes/chemistry/prescribed_aerosol_deposition_flux_namelist.xml
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+
+ char*256
+ chemistry
+ aerodep_flx_nl
+ datapath_for_prescribed_aerosol_deposition_fluxes
+ none
+
+ Full pathname of the directory that contains the files specified in aerodep_flx_filelist.
+
+
+ ${DIN_LOC_ROOT}/atm/cam/chem/trop_mozart_aero/aero
+
+
+
+
+ char*256
+ chemistry
+ aerodep_flx_nl
+ filename_for_prescribed_aerosol_deposition_fluxes
+ none
+
+ Filename of dataset for prescribed aerosol deposition fluxes.
+
+
+ aerosoldep_monthly_1849-2006_1.9x2.5_c090803.nc
+
+
+
+
+ char*256
+ chemistry
+ aerodep_flx_nl
+ filename_of_file_list_for_prescribed_aerosol_deposition_fluxes
+ none
+
+ Filename of file that contains a sequence of filenames for prescribed aerosol deposition fluxes. The filenames in this file are relative to the directory specified by aerodep_flx_datapath.
+
+
+ UNSET
+
+
+
+
+ char*32(22)
+ chemistry
+ aerodep_flx_nl
+ specifier_for_prescribed_aerosol_deposition_fluxes
+ none
+
+ A list of variable names of the aerosol deposition flux fields in the prescribed datasets and corresponding internal name separated by colons. For example:
+
+ aerodep_flx_specifier = 'internal_name1:ncdf_fld_name1','internal_name2:ncdf_fld_name2', ...
+
+ If there is no colon separator then the specified name is used as both the internal_name and ncdf_fld_name.
+
+
+ 'BCDEPWET','BCPHODRY','BCPHIDRY','OCDEPWET','OCPHODRY', 'OCPHIDRY','DSTX01DD','DSTX02DD','DSTX03DD','DSTX04DD','DSTX01WD','DSTX02WD','DSTX03WD','DSTX04WD'
+
+
+
+
+ char*32
+ chemistry
+ aerodep_flx_nl
+ time_interpolation_method_for_prescribed_aerosol_deposition_fluxes
+ none
+ CYCLICAL,SERIAL,INTERP_MISSING_MONTHS,FIXED
+
+ Type of time interpolation for data in aerodep_flx files.
+ Can be set to 'CYCLICAL', 'SERIAL', 'INTERP_MISSING_MONTHS', or 'FIXED'.
+ Default: 'SERIAL'
+
+
+ SERIAL
+
+
+
+
+ integer
+ chemistry
+ aerodep_flx_nl
+ cycle_year_for_prescribed_aerosol_deposition_fluxes
+ 1
+
+ The cycle year of the prescribed aerosol deposition flux data if aerodep_flx_type is 'CYCLICAL'.
+ Format: YYYY
+ Default: 2000
+
+
+ 2000
+
+
+
+
+ integer
+ chemistry
+ aerodep_flx_nl
+ fixed_date_for_prescribed_aerosol_deposition_fluxes
+ 1
+
+ The date at which the prescribed aerosol deposition flux data is fixed if aerodep_flx_type is 'FIXED'.
+ Format: YYYYMMDD
+ Default: 0
+
+
+ 0
+
+
+
+
+ integer
+ chemistry
+ aerodep_flx_nl
+ fixed_time_of_day_for_prescribed_aerosol_deposition_fluxes
+ s
+
+ The time of day (seconds) corresponding to aerodep_flx_fixed_ymd at which the prescribed aerosol deposition flux data is fixed if aerodep_flx_type is 'FIXED'.
+ Default: 0 seconds
+
+
+ 0
+
+
+
+
diff --git a/schemes/chemistry/prescribed_aerosols.F90 b/schemes/chemistry/prescribed_aerosols.F90
new file mode 100644
index 00000000..4c474b6d
--- /dev/null
+++ b/schemes/chemistry/prescribed_aerosols.F90
@@ -0,0 +1,655 @@
+! Manages reading and interpolation of prescribed aerosol concentrations.
+!
+! This module uses CCPP constituents (non-advected) to store prescribed aerosol fields
+! for access by radiation and other schemes.
+!
+! The prescribed_aero_specifier namelist variable specifies a list of
+! variable names of the concentration fields in the netCDF dataset (ncdf_fld_name)
+! and the corresponding constituent names, both of which are arbitrary:
+!
+! prescribed_aero_specifier = 'constituent_name1:ncdf_fld_name1','constituent_name2:ncdf_fld_name2', ...
+!
+! If there is no ":" then the specified name is used as both the
+! constituent_name and ncdf_fld_name
+!
+! Based on original CAM version from: Francis Vitt
+module prescribed_aerosols
+
+ use ccpp_kinds, only: kind_phys
+
+ ! CAM-SIMA host model dependency to read aerosol data
+ use tracer_data, only: trfile ! data information and file read state
+ use tracer_data, only: trfld ! tracer data container
+
+ implicit none
+ private
+
+ ! public CCPP-compliant subroutines
+ public :: prescribed_aerosols_register
+ public :: prescribed_aerosols_init
+ public :: prescribed_aerosols_run
+
+ ! fields to store tracer_data state and information
+ type(trfld), pointer :: tracer_data_fields(:)
+ type(trfile) :: tracer_data_file
+
+ ! local derived type to store constituent name to
+ ! netCDF field name and tracer_data read index mapping:
+ type :: aero_constituent_map
+ character(len=64) :: constituent_name ! CCPP constituent standard name
+
+ ! if modal aerosols and is an interstitial (_a) species, then the species
+ ! is diagnosed from log-mean (_logm) and log-variance (_logv) with a randomized
+ ! log-normal distribution;
+ ! in this case, field_index is used for log-mean, and a separate field for variance
+ ! is stored here.
+ logical :: is_modal_aero_interstitial
+ character(len=64) :: trcdata_field_name_logv! tracer_data field name (fldnam, not netCDF variable name) (log-variance)
+ integer :: field_index_logv ! index into tracer_data_fields array (log-variance)
+
+ ! all other cases, including modal log-mean:
+ character(len=64) :: trcdata_field_name ! tracer_data field name (fldnam, not netCDF variable name)
+ integer :: field_index ! index into tracer_data_fields array
+ end type aero_constituent_map
+
+ type(aero_constituent_map), allocatable :: aero_map_list(:)
+
+ ! module state variables
+ logical :: has_prescribed_aerosols = .false.
+ logical :: clim_modal_aero = .false.
+ integer :: aero_count ! # of aerosol constituents
+ integer :: aero_count_c ! # of cloud-borne species (for modal aerosols only)
+
+ ! maximum of specified aerosol fields (as counted from namelist entries)
+ integer :: n_aero_max = -1
+
+ ! Normal random number which persists from one timestep to the next
+ ! (used for modal aerosol sampling)
+ real(kind_phys) :: randn_persists = 0.0_kind_phys
+
+ ! TODO: infrastructure for writing (and reading) randn_persists to persist during restart runs.
+ ! see CAM/prescribed_aero::{read,write}_prescribed_aero_restart
+ ! !!! Restarts will not be bit-for-bit without this !!!
+ ! TODO when SIMA implements restarts.
+
+contains
+
+ ! Register prescribed aerosols in constituents object.
+!> \section arg_table_prescribed_aerosols_register Argument Table
+!! \htmlinclude prescribed_aerosols_register.html
+ subroutine prescribed_aerosols_register( &
+ amIRoot, iulog, &
+ prescribed_aero_specifier, &
+ prescribed_aero_file, &
+ prescribed_aero_filelist, &
+ prescribed_aero_datapath, &
+ prescribed_aero_type, &
+ prescribed_aero_cycle_yr, &
+ prescribed_aero_fixed_ymd, &
+ prescribed_aero_fixed_tod, &
+ prescribed_aero_model, &
+ aerosol_constituents, &
+ errmsg, errflg)
+
+ use ccpp_constituent_prop_mod, only: ccpp_constituent_properties_t
+
+ use ccpp_chem_utils, only: chem_constituent_qmin
+
+ ! Input arguments:
+ logical, intent(in) :: amIRoot
+ integer, intent(in) :: iulog
+ character(len=*), intent(in) :: prescribed_aero_specifier(:) ! aerosol specifiers from namelist
+ character(len=*), intent(in) :: prescribed_aero_file ! input filename from namelist
+ character(len=*), intent(in) :: prescribed_aero_filelist ! input filelist from namelist
+ character(len=*), intent(in) :: prescribed_aero_datapath ! input datapath from namelist
+ character(len=*), intent(in) :: prescribed_aero_type ! data type from namelist
+ integer, intent(in) :: prescribed_aero_cycle_yr ! cycle year from namelist [1]
+ integer, intent(in) :: prescribed_aero_fixed_ymd ! fixed year-month-day from namelist (YYYYMMDD) [1]
+ integer, intent(in) :: prescribed_aero_fixed_tod ! fixed time of day from namelist [s]
+ character(len=*), intent(in) :: prescribed_aero_model ! type of aerosol representation [none]
+
+ ! Output arguments:
+ type(ccpp_constituent_properties_t), allocatable, intent(out) :: aerosol_constituents(:) ! prescribed aero runtime CCPP constituents
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ ! Local variables:
+ character(len=256) :: tmpstr
+ character(len=64) :: constituent_name
+ character(len=64) :: ncdf_fld_name
+ character(len=64) :: unit_name
+ integer :: strlen
+ integer :: idx, idx2
+ integer :: aero_idx
+ integer :: i, j
+ logical :: is_modal_aero_interstitial
+ logical :: skip_spec
+ real(kind_phys) :: qmin
+
+ character(len=*), parameter :: subname = 'prescribed_aerosols_register'
+
+ errmsg = ''
+ errflg = 0
+
+ ! Store module-level settings
+ clim_modal_aero = (trim(prescribed_aero_model) == "modal")
+
+ ! Check if prescribed aerosols are enabled
+ if (prescribed_aero_file == 'UNSET' .or. &
+ len_trim(prescribed_aero_file) == 0) then
+ has_prescribed_aerosols = .false.
+ if (amIRoot) then
+ write(iulog,*) subname//': No prescribed aerosols specified'
+ end if
+ return
+ end if
+
+ ! Parse the aerosol format specifier from namelist into mapping ddt.
+ ! We need two scans. First to determine the count, the second to populate
+ ! the information into the ddt.
+ !
+ ! Determine number of non-empty/non-UNSET elements in the namelist specifier
+ n_aero_max = count((prescribed_aero_specifier /= 'UNSET') .and. &
+ (len_trim(prescribed_aero_specifier) > 0))
+
+ aero_count = 0
+ aero_count_c = 0 ! cloud borne species count
+ count_loop: do i = 1, n_aero_max
+ skip_spec = .false.
+ if(clim_modal_aero) then
+ ! For modal aerosols, interstitial species (*_a) are diagnosed from
+ ! their *_logm and *_logv counterparts (e.g. soa_a1 is diagnosed from
+ ! soa_a1_logm and soa_a1_logv). Therefore, only *_logm and *_logv and cloud
+ ! borne (*_c) species are specified in the build-namelist.
+
+ ! In the following count_loop, we will count the cloud borne species and *_logm species
+ ! (in lieu of *_a species). We will skip *_logv species.
+ ! This will ensure that aero_count variable is the sum of cloud borne and
+ ! interstitial species (which we will manually add in the names to the ddt later).
+ ! We are also counting cloud borne (*_c) species which will help
+ ! adding the same number of interstitial species to the ddt.
+ !
+ ! For modal aerosols, skip counting species ending with *_logv
+ if(index(prescribed_aero_specifier(i),'_c') >= 1) aero_count_c = aero_count_c + 1
+ if(index(prescribed_aero_specifier(i),'_logv') >= 1) skip_spec = .true.
+ end if
+
+ if(.not. skip_spec) aero_count = aero_count + 1
+ end do count_loop
+
+ if(aero_count == 0) then
+ has_prescribed_aerosols = .false.
+ return
+ end if
+
+ has_prescribed_aerosols = .true.
+
+ ! Allocate mapping list of ddt
+ allocate(aero_map_list(aero_count), stat=errflg, errmsg=errmsg)
+ if(errflg /= 0) then
+ errmsg = subname // ": " // trim(errmsg)
+ return
+ end if
+
+ ! Now populate the information into the ddt.
+ !
+ ! In CAM modal aerosols, the interstitial species (_a) are added at the end of the cloud borne (_c) species. This ordering is not needed in SIMA.
+
+ aero_idx = 1 ! pointer for current aero_map_list index.
+ ddt_loop: do i = 1, n_aero_max
+ ! Parse specifier
+ tmpstr = trim(adjustl(prescribed_aero_specifier(i)))
+
+ idx = index(tmpstr, ':')
+ if(idx > 0) then
+ ! format as constituent_name:ncdf_name
+ constituent_name = tmpstr(:idx-1)
+ ncdf_fld_name = tmpstr(idx+1:)
+ else
+ ! format as single_name_for_both
+ constituent_name = tmpstr
+ ncdf_fld_name = tmpstr
+ end if
+
+ is_modal_aero_interstitial = .false.
+
+ ! For modal aerosols only,
+ ! skip the _logv species; remove the _logm suffix to directly get the interstitial (_a) name
+ ! e.g. in loop: num_c1, num_a1_logm, num_a1_logv, ... --> num_c1, num_a1, (skip), ...
+ if(clim_modal_aero) then
+ if(index(constituent_name,'_logv') >= 1) cycle ddt_loop
+ idx = index(constituent_name,'_logm')
+ if(idx > 0) then
+ ! use this as the proxy for interstitial species and initialize it here
+ is_modal_aero_interstitial = .true.
+ aero_map_list(aero_idx)%is_modal_aero_interstitial = .true.
+
+ aero_map_list(aero_idx)%constituent_name = constituent_name(:idx-1) ! removed _logm
+ aero_map_list(aero_idx)%trcdata_field_name = constituent_name ! log-mean specifier name
+
+ ! construct log-variance specifier name by changing _logm with _logv
+ aero_map_list(aero_idx)%trcdata_field_name_logv = constituent_name(:idx-1) // '_logv'
+
+ ! The tracer data specifier index will be rescanned once tracer_data_fields is initialized
+ ! at the init phase.
+ aero_map_list(aero_idx)%field_index = -1
+ aero_map_list(aero_idx)%field_index_logv = -1
+ end if ! logm
+ end if ! clim_modal_aero
+
+ if(.not. is_modal_aero_interstitial) then
+ ! Store mapping for all other cases except modal aerosol interstitial species.
+ aero_map_list(aero_idx)%is_modal_aero_interstitial = .false.
+
+ aero_map_list(aero_idx)%constituent_name = constituent_name
+ aero_map_list(aero_idx)%trcdata_field_name = constituent_name
+
+ ! The tracer data specifier index will be rescanned once tracer_data_fields is initialized
+ ! at the init phase.
+ aero_map_list(aero_idx)%field_index = -1
+ end if
+
+ ! We added a new aero_map_list entry, so advance the pointer.
+ aero_idx = aero_idx + 1
+ end do ddt_loop
+
+ ! Sanity check
+ if(aero_idx /= aero_count+1) then
+ errflg = 1
+ write(errmsg,*) subname//': consistency check 1 failure; at the end of ddt allocation, aero_idx is not aero_count+1', aero_idx, aero_count
+ return
+ end if
+
+ ! Allocate CCPP dynamic constituents object for prescribed aerosols.
+ allocate(aerosol_constituents(aero_count), stat=errflg, errmsg=errmsg)
+ if (errflg /= 0) then
+ errmsg = subname // ": " // trim(errmsg)
+ return
+ end if
+
+ ! Now register constituents in the CCPP constituent properties object.
+ reg_loop: do i = 1, aero_count
+ ! check units. at this point, we do not know the units from file
+ ! because tracer_data has not read any data yet.
+ ! number concentrations are units of (1) kg-1; all others are kg kg-1
+ if(index(aero_map_list(i)%constituent_name, 'num_') == 1) then
+ unit_name = 'kg-1'
+ else
+ unit_name = 'kg kg-1'
+ end if
+
+ qmin = chem_constituent_qmin(aero_map_list(i)%constituent_name)
+
+ call aerosol_constituents(i)%instantiate( &
+ std_name = trim(aero_map_list(i)%constituent_name), &
+ long_name = 'prescribed aerosol '//trim(aero_map_list(i)%constituent_name), &
+ units = unit_name, &
+ vertical_dim = 'vertical_layer_dimension', &
+ min_value = qmin, &
+ advected = .false., &
+ water_species = .false., &
+ mixing_ratio_type = 'dry', &
+ errcode = errflg, &
+ errmsg = errmsg)
+ if(errflg /= 0) return
+ end do reg_loop
+
+ if (amIRoot) then
+ write(iulog,*) trim(subname)//': Registered ', aero_count, ' prescribed aerosol constituents'
+ end if
+
+ end subroutine prescribed_aerosols_register
+
+ ! Initialize prescribed aerosol reading via tracer_data.
+!> \section arg_table_prescribed_aerosols_init Argument Table
+!! \htmlinclude prescribed_aerosols_init.html
+ subroutine prescribed_aerosols_init( &
+ amIRoot, iulog, &
+ prescribed_aero_specifier, &
+ prescribed_aero_file, &
+ prescribed_aero_filelist, &
+ prescribed_aero_datapath, &
+ prescribed_aero_type, &
+ prescribed_aero_cycle_yr, &
+ prescribed_aero_fixed_ymd, &
+ prescribed_aero_fixed_tod, &
+ errmsg, errflg)
+
+ ! host model dependency for tracer_data read utility
+ use tracer_data, only: trcdata_init
+
+ ! host model dependency for history output
+ use cam_history, only: history_add_field
+
+ ! Input arguments:
+ logical, intent(in) :: amIRoot
+ integer, intent(in) :: iulog
+ character(len=*), intent(in) :: prescribed_aero_specifier(:) ! aerosol specifier from namelist
+ character(len=*), intent(in) :: prescribed_aero_file ! input filename from namelist
+ character(len=*), intent(in) :: prescribed_aero_filelist ! input filelist from namelist
+ character(len=*), intent(in) :: prescribed_aero_datapath ! input datapath from namelist
+ character(len=*), intent(in) :: prescribed_aero_type ! data type from namelist
+ integer, intent(in) :: prescribed_aero_cycle_yr ! cycle year from namelist [1]
+ integer, intent(in) :: prescribed_aero_fixed_ymd ! fixed year-month-day from namelist (YYYYMMDD) [1]
+ integer, intent(in) :: prescribed_aero_fixed_tod ! fixed time of day from namelist [s]
+
+ ! Output arguments:
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ ! Local variables:
+ integer :: i
+ integer :: idx
+ character(len=64) :: unit_name
+
+ ! Local parameters:
+ character(len=*), parameter :: subname = 'prescribed_aerosols_init'
+
+ ! Initialize output arguments:
+ errmsg = ''
+ errflg = 0
+
+ if (.not. has_prescribed_aerosols) return
+
+ if (amIRoot) then
+ write(iulog,*) trim(subname)//': aerosols are prescribed in: '//trim(prescribed_aero_file)
+ end if
+
+ ! Initialize tracer_data module with file and field information
+ call trcdata_init( &
+ specifier = prescribed_aero_specifier(:aero_count), &
+ filename = prescribed_aero_file, &
+ filelist = prescribed_aero_filelist, &
+ datapath = prescribed_aero_datapath, &
+ flds = tracer_data_fields, &
+ file = tracer_data_file, &
+ data_cycle_yr = prescribed_aero_cycle_yr, &
+ data_fixed_ymd = prescribed_aero_fixed_ymd, &
+ data_fixed_tod = prescribed_aero_fixed_tod, &
+ data_type = prescribed_aero_type)
+
+ ! Verify tracer_data is correctly initialized
+ if (.not. associated(tracer_data_fields)) then
+ errflg = 1
+ errmsg = subname//': tracer_data_fields not associated after trcdata_init'
+ return
+ end if
+
+ ! Note: because in modal aerosols, interstitial fields are derived from the
+ ! log-mean and log-variance (logm, logv) fields, the number of tracer_data
+ ! fields is not equal to aero_count(= a + c), but (logm + logv) + c.
+
+ ! Based on aero_map_list, scan the tracer data fields to populate correct field_index
+ do i = 1, aero_count
+ ! Find the matching field in tracer_data_fields for the primary field
+ do idx = 1, size(tracer_data_fields)
+ if (trim(tracer_data_fields(idx)%fldnam) == trim(aero_map_list(i)%trcdata_field_name)) then
+ aero_map_list(i)%field_index = idx
+ exit
+ end if
+ end do
+
+ ! For modal aerosol interstitial species, also find the logv field
+ if (aero_map_list(i)%is_modal_aero_interstitial) then
+ do idx = 1, size(tracer_data_fields)
+ if (trim(tracer_data_fields(idx)%fldnam) == trim(aero_map_list(i)%trcdata_field_name_logv)) then
+ aero_map_list(i)%field_index_logv = idx
+ exit
+ end if
+ end do
+ end if
+ end do
+
+ ! Check aero_map_list for any unpopulated field indices (consistency check),
+ ! Register history field, and
+ ! Print out each aero_map_list field information
+ do i = 1, aero_count
+ if (aero_map_list(i)%field_index <= 0) then
+ errflg = 1
+ write(errmsg, '(3a)') trim(subname), ': Field not found in tracer_data for constituent: ', &
+ trim(aero_map_list(i)%constituent_name)
+ return
+ end if
+
+ ! Add history field
+ ! Check units. at this point, we do not know the units from file
+ ! because tracer_data has not read any data yet.
+ ! number concentrations are units of (1) kg-1; all others are kg kg-1
+ if(index(aero_map_list(i)%constituent_name, 'num_') == 1) then
+ unit_name = 'kg-1'
+ else
+ unit_name = 'kg kg-1'
+ end if
+ call history_add_field(trim(aero_map_list(i)%constituent_name) // '_D', &
+ 'prescribed aero ' // trim(aero_map_list(i)%constituent_name), &
+ 'lev', 'avg', unit_name)
+
+ ! Informational printout
+ if (amIRoot) then
+ if (aero_map_list(i)%is_modal_aero_interstitial) then
+ if (aero_map_list(i)%field_index_logv <= 0) then
+ errflg = 1
+ write(errmsg, '(3a)') trim(subname), ': logv field not found for interstitial constituent: ', &
+ trim(aero_map_list(i)%constituent_name)
+ return
+ end if
+
+ write(iulog, '(a,i3,2a)') ' ', i, ': ', trim(aero_map_list(i)%constituent_name)
+ write(iulog, '(3a,i3)') ' log-mean field: ', trim(aero_map_list(i)%trcdata_field_name), &
+ ' (trcdata index ', aero_map_list(i)%field_index, ')'
+ write(iulog, '(3a,i3)') ' log-variance field: ', trim(aero_map_list(i)%trcdata_field_name_logv), &
+ ' (trcdata index ', aero_map_list(i)%field_index_logv, ')'
+ else
+ write(iulog, '(a,i3,5a,i3,a)') ' ', i, ': ', trim(aero_map_list(i)%constituent_name), &
+ ' from ', trim(aero_map_list(i)%trcdata_field_name), ' (trcdata index ', aero_map_list(i)%field_index, ')'
+ end if
+ end if
+ end do
+
+ if (amIRoot) then
+ write(iulog,*) trim(subname)//': Initialized ', aero_count, ' aerosol fields'
+ end if
+
+ end subroutine prescribed_aerosols_init
+
+!> \section arg_table_prescribed_aerosols_run Argument Table
+!! \htmlinclude prescribed_aerosols_run.html
+ subroutine prescribed_aerosols_run( &
+ ncol, pver, pcnst, &
+ const_props, &
+ pi, &
+ pmid, pint, phis, zi, & ! necessary fields for trcdata read.
+ constituents, &
+ errmsg, errflg)
+
+ ! host model dependency for tracer_data
+ use tracer_data, only: advance_trcdata
+
+ ! host model dependency for history output
+ use cam_history, only: history_out_field
+
+ ! framework dependency for const_props
+ use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t
+
+ ! dependency to get constituent index
+ use ccpp_const_utils, only: ccpp_const_get_idx
+
+ integer, intent(in) :: ncol
+ integer, intent(in) :: pver
+ integer, intent(in) :: pcnst ! # of CCPP constituents [count]
+ type(ccpp_constituent_prop_ptr_t), &
+ intent(in) :: const_props(:) ! CCPP constituent properties pointer
+ real(kind_phys), intent(in) :: pi
+ real(kind_phys), intent(in) :: pmid(:,:) ! air pressure [Pa]
+ real(kind_phys), intent(in) :: pint(:,:) ! air pressure at interfaces [Pa]
+ real(kind_phys), intent(in) :: phis(:) ! surface geopotential [m2 s-2]
+ real(kind_phys), intent(in) :: zi(:,:) ! height above surface, interfaces [m]
+
+ real(kind_phys), intent(inout) :: constituents(:,:,:) ! constituent array (ncol, pver, pcnst)
+
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ integer :: i
+ integer :: const_idx
+
+ character(len=*), parameter :: subname = 'prescribed_aerosols_run'
+
+ ! Initialize output arguments:
+ errmsg = ''
+ errflg = 0
+
+ if (.not. has_prescribed_aerosols) return
+
+ ! Advance tracer_data to current time
+ call advance_trcdata(tracer_data_fields, tracer_data_file, &
+ pmid, pint, phis, zi)
+
+ ! Copy the prescribed aerosol data to constituent array
+ ! Loop over aero_map_list to retrieve data from tracer_data_fields.
+ ! For most species (non-modal; cloud borne) just retrieve data and save to constituent.
+ ! For interstitial species (is_modal_aero_interstitial) construct mixing ratio based on
+ ! the logv and logm values read from tracer_data (port of rand_sample_prescribed_aero)
+ do i = 1, aero_count
+ ! Get constituent index
+ call ccpp_const_get_idx(const_props, &
+ trim(aero_map_list(i)%constituent_name), &
+ const_idx, errmsg, errflg)
+ if (errflg /= 0) return
+
+ if (.not. aero_map_list(i)%is_modal_aero_interstitial) then
+ ! For non-interstitial species (cloud-borne or bulk aerosols),
+ ! directly copy field data from tracer_data container
+ constituents(:ncol, :pver, const_idx) = &
+ tracer_data_fields(aero_map_list(i)%field_index)%data(:ncol,:pver)
+ else
+ ! For modal aerosol interstitial species (_a), compute from log-normal distribution
+ ! using log-mean (logm) and log-variance (logv)
+ call compute_modal_aero_interstitial( &
+ ncol, pver, &
+ pi, &
+ tracer_data_fields(aero_map_list(i)%field_index)%data(:ncol,:pver), &
+ tracer_data_fields(aero_map_list(i)%field_index_logv)%data(:ncol,:pver), &
+ constituents(:ncol, :pver, const_idx))
+ end if
+
+ ! History output
+ call history_out_field(trim(aero_map_list(i)%constituent_name) // '_D', &
+ constituents(:ncol, :pver, const_idx))
+ end do
+
+ end subroutine prescribed_aerosols_run
+
+ ! Compute modal aerosol interstitial species from log-normal distribution
+ ! Port of rand_sample_prescribed_aero from CAM prescribed_aero.F90
+ !
+ ! Original authors: Balwinder Singh (12/14/2012) adapted from Jin-Ho Yoon
+ subroutine compute_modal_aero_interstitial(ncol, pver, pi, logm_data, logv_data, mixing_ratio)
+
+ ! Input arguments
+ integer, intent(in) :: ncol
+ integer, intent(in) :: pver
+ real(kind_phys), intent(in) :: pi
+ real(kind_phys), intent(in) :: logm_data(:, :) ! Log-mean of aerosol distribution [ln(kg kg-1)]
+ real(kind_phys), intent(in) :: logv_data(:, :) ! Log-variance of aerosol distribution [ln(kg kg-1)^2]
+
+ ! Output arguments
+ real(kind_phys), intent(out) :: mixing_ratio(:, :) ! Computed aerosol mixing ratio [kg kg-1]
+
+ ! Local variables
+ integer :: i, k
+ real(kind_phys) :: logm2 ! Log-mean squared [ln(kg kg-1)^2]
+ real(kind_phys) :: variance ! Variance [(ln(kg kg-1))^2]
+ real(kind_phys) :: std ! Standard deviation [ln(kg kg-1)]
+ real(kind_phys) :: mean_max ! Maximum mean value [kg kg-1]
+ real(kind_phys) :: std_max ! Maximum std value [kg kg-1]
+ real(kind_phys) :: randn ! Normal random number [1]
+
+ ! Local parameters
+ real(kind_phys), parameter :: mean_max_val = 5.0_kind_phys
+ real(kind_phys), parameter :: std_max_val = 3.0_kind_phys
+
+ ! Generate/use persisting random number
+ randn = randn_prescribed_aero(pi)
+
+ ! This loop logic is a good candidate for making into a pure elemental function
+ ! despite the impurity of the RNG dependency above.
+ do k = 1, pver
+ do i = 1, ncol
+ logm2 = logm_data(i, k) * logm_data(i, k)
+
+ ! Compute (non-negative) variance
+ variance = max(0.0_kind_phys, (logv_data(i, k) - logm2))
+
+ ! Standard deviation
+ std = sqrt(variance)
+
+ ! Bounds to keep mixing ratios from going unphysical
+ mean_max = exp(logm_data(i, k)) * mean_max_val
+ std_max = exp(logm_data(i, k) + std_max_val * std)
+
+ ! Compute mixing ratio with random sampling
+ mixing_ratio(i, k) = min(exp(logm_data(i, k) + randn * std), mean_max, std_max)
+ end do
+ end do
+ end subroutine compute_modal_aero_interstitial
+
+ ! Generate normally distributed random number that persists for entire day
+ ! Port of randn_prescribed_aero and boxMuller from CAM prescribed_aero.F90
+ !
+ ! NOTE: I think this can be moved into the host model to be used as a
+ ! common RNG for physics schemes to avoid having dependency on time_manager.
+ function randn_prescribed_aero(pi) result(randn)
+ use time_manager, only: is_end_curr_day, is_first_step, get_nstep
+
+ real(kind_phys), intent(in) :: pi
+
+ ! Local variables
+ integer :: i, seed_size, nstep
+ integer, allocatable :: seed(:)
+ real(kind_phys) :: randu1, randu2 ! Uniform random numbers [1]
+ real(kind_phys) :: randn ! Normal random number [1]
+ real(kind_phys) :: ur, theta ! Box-Muller variables [1]
+
+ ! Local parameters for random seed generation
+ integer, parameter :: rconst1_1 = 5000000
+ integer, parameter :: rconst1_2 = 50
+ integer, parameter :: rconst2_1 = 10000000
+ integer, parameter :: rconst2_2 = 10
+
+ ! Use same random number for the entire day; generate new one at start of new day
+ if (is_first_step() .or. is_end_curr_day()) then
+ ! Generate two uniformly distributed random numbers (between 0 and 1)
+ call random_seed(size=seed_size)
+ allocate(seed(seed_size))
+
+ ! Using nstep as a seed to generate same sequence
+ nstep = get_nstep()
+ do i = 1, seed_size
+ seed(i) = rconst1_1 * nstep + rconst1_2 * (i - 1)
+ end do
+ call random_seed(put=seed)
+ call random_number(randu1)
+
+ do i = 1, seed_size
+ seed(i) = rconst2_1 * nstep + rconst2_2 * (i - 1)
+ end do
+ call random_seed(put=seed)
+ call random_number(randu2)
+ deallocate(seed)
+
+ ! Box-Muller method: convert uniform to normal distribution (mean=0, std=1)
+ ur = sqrt(-2.0_kind_phys * log(randu1))
+ theta = 2.0_kind_phys * pi * randu2
+ randn = ur * cos(theta)
+
+ ! Store for use throughout the day
+ randn_persists = randn
+ else
+ ! Use the previously generated random number
+ randn = randn_persists
+ end if
+
+ end function randn_prescribed_aero
+
+end module prescribed_aerosols
diff --git a/schemes/chemistry/prescribed_aerosols.meta b/schemes/chemistry/prescribed_aerosols.meta
new file mode 100644
index 00000000..eec2debe
--- /dev/null
+++ b/schemes/chemistry/prescribed_aerosols.meta
@@ -0,0 +1,246 @@
+[ccpp-table-properties]
+ name = prescribed_aerosols
+ type = scheme
+ dependencies = ../../to_be_ccppized/ccpp_chem_utils.F90
+
+[ccpp-arg-table]
+ name = prescribed_aerosols_register
+ type = scheme
+[ amIRoot ]
+ standard_name = flag_for_mpi_root
+ units = flag
+ type = logical
+ dimensions = ()
+ intent = in
+[ iulog ]
+ standard_name = log_output_unit
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_specifier ]
+ standard_name = specifier_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = (prescribed_aero_specifier_dimension)
+ intent = in
+[ prescribed_aero_file ]
+ standard_name = filename_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_filelist ]
+ standard_name = filename_of_file_list_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_datapath ]
+ standard_name = datapath_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_type ]
+ standard_name = time_interpolation_method_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_cycle_yr ]
+ standard_name = cycle_year_for_prescribed_aerosols
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_fixed_ymd ]
+ standard_name = fixed_date_for_prescribed_aerosols
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_fixed_tod ]
+ standard_name = fixed_time_of_day_for_prescribed_aerosols
+ units = s
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_model ]
+ standard_name = representation_type_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ aerosol_constituents ]
+ # or can this just be the ccpp_constituent_properties?
+ standard_name = prescribed_aerosols_constituents
+ units = none
+ type = ccpp_constituent_properties_t
+ allocatable = True
+ dimensions = (:)
+ intent = out
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
+
+[ccpp-arg-table]
+ name = prescribed_aerosols_init
+ type = scheme
+[ amIRoot ]
+ standard_name = flag_for_mpi_root
+ units = flag
+ type = logical
+ dimensions = ()
+ intent = in
+[ iulog ]
+ standard_name = log_output_unit
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_specifier ]
+ standard_name = specifier_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = (prescribed_aero_specifier_dimension)
+ intent = in
+[ prescribed_aero_file ]
+ standard_name = filename_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_filelist ]
+ standard_name = filename_of_file_list_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_datapath ]
+ standard_name = datapath_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_type ]
+ standard_name = time_interpolation_method_for_prescribed_aerosols
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ prescribed_aero_cycle_yr ]
+ standard_name = cycle_year_for_prescribed_aerosols
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_fixed_ymd ]
+ standard_name = fixed_date_for_prescribed_aerosols
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ prescribed_aero_fixed_tod ]
+ standard_name = fixed_time_of_day_for_prescribed_aerosols
+ units = s
+ type = integer
+ dimensions = ()
+ intent = in
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
+
+[ccpp-arg-table]
+ name = prescribed_aerosols_run
+ type = scheme
+[ ncol ]
+ standard_name = horizontal_loop_extent
+ units = count
+ type = integer
+ dimensions = ()
+ intent = in
+[ pver ]
+ standard_name = vertical_layer_dimension
+ units = count
+ type = integer
+ dimensions = ()
+ intent = in
+[ pcnst ]
+ standard_name = number_of_ccpp_constituents
+ units = count
+ type = integer
+ dimensions = ()
+ intent = in
+[ const_props ]
+ standard_name = ccpp_constituent_properties
+ units = none
+ type = ccpp_constituent_prop_ptr_t
+ dimensions = (number_of_ccpp_constituents)
+ intent = in
+[ pi ]
+ standard_name = pi_constant
+ units = 1
+ type = real | kind = kind_phys
+ dimensions = ()
+ intent = in
+[ pmid ]
+ standard_name = air_pressure
+ units = Pa
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_layer_dimension)
+ intent = in
+[ pint ]
+ standard_name = air_pressure_at_interface
+ units = Pa
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_interface_dimension)
+ intent = in
+[ phis ]
+ standard_name = surface_geopotential
+ units = m2 s-2
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = in
+[ zi ]
+ standard_name = geopotential_height_wrt_surface_at_interface
+ units = m
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_interface_dimension)
+ intent = in
+[ constituents ]
+ standard_name = ccpp_constituents
+ units = none
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_layer_dimension, number_of_ccpp_constituents)
+ intent = inout
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
diff --git a/schemes/chemistry/prescribed_aerosols_namelist.xml b/schemes/chemistry/prescribed_aerosols_namelist.xml
new file mode 100644
index 00000000..878043b6
--- /dev/null
+++ b/schemes/chemistry/prescribed_aerosols_namelist.xml
@@ -0,0 +1,230 @@
+
+
+
+
+
+
+
+
+ char*256
+ chemistry
+ prescribed_aero_nl
+ datapath_for_prescribed_aerosols
+ none
+
+ Full pathname of the directory that contains the files specified in prescribed_aero_filelist.
+
+
+
+
+ ${DIN_LOC_ROOT}/atm/cam/chem/trop_mozart_aero/aero
+
+
+
+
+
+ char*5
+ chemistry
+ prescribed_aero_nl
+ representation_type_for_prescribed_aerosols
+ none
+ bulk,modal
+
+ Switch used to indicate which type of aerosols are prescribed -- bulk or modal.
+
+ TODO: This is used to set the default prescribed_aero_specifier and aerodep_flx_specifier namelist variables.
+
+
+ bulk
+
+
+
+
+ char*256
+ chemistry
+ prescribed_aero_nl
+ filename_for_prescribed_aerosols
+ none
+
+ Filename of dataset for prescribed aerosols.
+
+
+ aero_1.9x2.5_L26_2000clim_c091112.nc
+
+
+
+
+
+ char*256
+ chemistry
+ prescribed_aero_nl
+ filename_of_file_list_for_prescribed_aerosols
+ none
+
+ Filename of file that contains a sequence of filenames for prescribed aerosols. The filenames in this file are relative to the directory specified by prescribed_aero_datapath.
+
+
+ aero_1.9x2.5_L26_list_c070514.txt
+
+
+
+
+
+ char*32(50)
+ chemistry
+ prescribed_aero_nl
+ specifier_for_prescribed_aerosols
+ none
+
+ A list of variable names of the concentration fields in the prescribed aerosol datasets and corresponding standard name stored in the constituents properties object and constituent value array seperated by colons. For example:
+
+ prescribed_aero_specifier = 'pconstituent_standard_name1:ncdf_fld_name1','constituent_standard_name2:ncdf_fld_name2', ...
+
+ If there is no colon separator then the specified name is used as both the constituent_standard_name and ncdf_fld_name.
+
+
+ 'sulf:SO4','bcar1:CB1','bcar2:CB2','ocar1:OC1','ocar2:OC2','sslt1:SSLT01','sslt2:SSLT02','sslt3:SSLT03','sslt4:SSLT04','dust1:DST01','dust2:DST02','dust3:DST03','dust4:DST04'
+
+
+
+
+
+ char*32
+ chemistry
+ prescribed_aero_nl
+ time_interpolation_method_for_prescribed_aerosols
+ none
+ CYCLICAL,SERIAL,INTERP_MISSING_MONTHS,FIXED
+
+ Type of time interpolation for data in prescribed_aero files.
+ Can be set to 'CYCLICAL', 'SERIAL', 'INTERP_MISSING_MONTHS', or 'FIXED'.
+ Default: 'CYCLICAL'
+
+
+ CYCLICAL
+
+
+
+
+ integer
+ chemistry
+ prescribed_aero_nl
+ cycle_year_for_prescribed_aerosols
+ 1
+
+ The cycle year of the prescribed aerosol data if prescribed_aero_type is 'CYCLICAL'.
+ Format: YYYY
+ Default: 2000
+
+
+ 2000
+
+
+
+
+ integer
+ chemistry
+ prescribed_aero_nl
+ fixed_date_for_prescribed_aerosols
+ 1
+
+ The date at which the prescribed aerosol data is fixed if prescribed_aero_type is 'FIXED'.
+ Format: YYYYMMDD
+ Default: 0
+
+
+ 0
+
+
+
+
+ integer
+ chemistry
+ prescribed_aero_nl
+ fixed_time_of_day_for_prescribed_aerosols
+ s
+
+ The time of day (seconds) corresponding to prescribed_aero_fixed_ymd at which the prescribed aerosol data is fixed if prescribed_aero_type is 'FIXED'.
+ Default: 0 seconds
+
+
+ 0
+
+
+
diff --git a/schemes/chemistry/prescribed_ozone.F90 b/schemes/chemistry/prescribed_ozone.F90
new file mode 100644
index 00000000..21c4fd7d
--- /dev/null
+++ b/schemes/chemistry/prescribed_ozone.F90
@@ -0,0 +1,259 @@
+! read and provide prescribed ozone for radiation
+! this is a simple example of a CCPP scheme using the CAM-SIMA tracer_data utility.
+!
+! Based on original CAM version from: Francis Vitt
+module prescribed_ozone
+ use ccpp_kinds, only: kind_phys
+
+ ! CAM-SIMA host model dependency to read chemistry data.
+ use tracer_data, only: trfile ! data information and file read state.
+ use tracer_data, only: trfld ! tracer data container.
+
+ implicit none
+ private
+
+ ! public CCPP-compliant subroutines
+ public :: prescribed_ozone_register
+ public :: prescribed_ozone_init
+ public :: prescribed_ozone_run
+
+ ! fields to store tracer_data state and information.
+ type(trfld), pointer :: tracer_data_fields(:)
+ type(trfile) :: tracer_data_file
+
+ ! parameters for ozone
+ ! TODO - this should use a centralized chemistry species database
+ real(kind_phys), parameter :: ozone_mw = 47.9981995_kind_phys
+
+ ! namelist options
+ logical :: has_prescribed_ozone = .false.
+ character(len=8), parameter :: ozone_name = 'O3' ! standard name of the output field
+
+contains
+
+ ! Register the prescribed ozone constituent in the CCPP constituent properties object.
+!> \section arg_table_prescribed_ozone_register Argument Table
+!! \htmlinclude prescribed_ozone_register.html
+ subroutine prescribed_ozone_register( &
+ amIRoot, iulog, &
+ filename, &
+ ozone_constituents, &
+ errmsg, errflg)
+
+ use ccpp_constituent_prop_mod, only: ccpp_constituent_properties_t
+
+ use ccpp_chem_utils, only: chem_constituent_qmin
+
+ logical, intent(in) :: amIRoot
+ integer, intent(in) :: iulog
+ character(len=*), intent(in) :: filename ! input filename
+
+ ! prescribed ozone runtime CCPP constituent
+ type(ccpp_constituent_properties_t), allocatable, intent(out) :: ozone_constituents(:)
+
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ errmsg = ''
+ errflg = 0
+
+ ! check if user has specified an input dataset
+ if(filename /= 'UNSET' .and. len_trim(filename) > 0) then
+ has_prescribed_ozone = .true.
+
+ if(amIRoot) then
+ write(iulog,*) 'prescribed_ozone_register: ozone is prescribed in: ' // trim(filename)
+ end if
+ else
+ return
+ end if
+
+ ! allocate CCPP dynamic constituents object for prescribed ozone.
+ ! if we are prescribing ozone, this module is responsible for registering the constituent.
+ allocate(ozone_constituents(1), stat=errflg, errmsg=errmsg)
+ if (errflg /= 0) then
+ errmsg = "prescribed_ozone_register: " // trim(errmsg)
+ return
+ end if
+
+ call ozone_constituents(1)%instantiate( &
+ std_name = trim(ozone_name), &
+ long_name = 'prescribed ozone (O3)', &
+ units = 'kg kg-1', &
+ vertical_dim = 'vertical_layer_dimension', &
+ min_value = chem_constituent_qmin(trim(ozone_name)), &
+ advected = .false., &
+ water_species = .false., &
+ mixing_ratio_type = 'dry', &
+ errcode = errflg, &
+ errmsg = errmsg)
+ if(errflg /= 0) return
+
+ end subroutine prescribed_ozone_register
+
+!> \section arg_table_prescribed_ozone_init Argument Table
+!! \htmlinclude prescribed_ozone_init.html
+ subroutine prescribed_ozone_init( &
+ amIRoot, iulog, &
+ fld_name, filename, filelist, datapath, &
+ data_type, &
+ cycle_yr, fixed_ymd, fixed_tod, &
+ errmsg, errflg)
+
+ use cam_history, only: history_add_field
+ use tracer_data, only: trcdata_init
+
+ logical, intent(in) :: amIRoot
+ integer, intent(in) :: iulog
+
+ ! input fields from namelist to initialize tracer_data
+ character(len=*), intent(in) :: fld_name ! field name
+ character(len=*), intent(in) :: filename ! input filename
+ character(len=*), intent(in) :: filelist ! input filelist
+ character(len=*), intent(in) :: datapath ! input datapath
+ character(len=*), intent(in) :: data_type ! data type
+ integer, intent(in) :: cycle_yr ! cycle year
+ integer, intent(in) :: fixed_ymd ! fixed year-month-day (YYYYMMDD) [1]
+ integer, intent(in) :: fixed_tod ! fixed time of day [s]
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ character(len=32) :: tracer_data_specifier(1)
+
+ errmsg = ''
+ errflg = 0
+
+ if(.not. has_prescribed_ozone) return
+
+ ! initialize dataset in tracer_data module.
+ ! construct field specifier - one field
+ ! format is (internal field name):(netCDF name)
+ ! the latter is namelist-configurable together with the file source.
+ tracer_data_specifier(1) = trim(ozone_name)//':'//trim(fld_name)
+
+ call trcdata_init( &
+ specifier = tracer_data_specifier(:), &
+ filename = filename, &
+ filelist = filelist, &
+ datapath = datapath, &
+ flds = tracer_data_fields, & ! ptr
+ file = tracer_data_file, &
+ data_cycle_yr = cycle_yr, &
+ data_fixed_ymd = fixed_ymd, &
+ data_fixed_tod = fixed_tod, &
+ data_type = data_type)
+
+ ! add history field for diagnostic purposes
+ call history_add_field('ozone', 'prescribed_ozone', 'lev', 'inst', 'mol mol-1')
+
+ end subroutine prescribed_ozone_init
+
+!> \section arg_table_prescribed_ozone_run Argument Table
+!! \htmlinclude prescribed_ozone_run.html
+ subroutine prescribed_ozone_run( &
+ ncol, pver, &
+ const_props, &
+ mwdry, boltz, &
+ t, pmiddry, &
+ pmid, pint, phis, zi, & ! necessary fields for trcdata read.
+ constituents, &
+ errmsg, errflg)
+
+ ! host model dependency for tracer_data
+ use tracer_data, only: advance_trcdata
+
+ ! host model dependency for history output
+ use cam_history, only: history_out_field
+
+ ! framework dependency for const_props
+ use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t
+
+ ! dependency to get constituent index
+ use ccpp_const_utils, only: ccpp_const_get_idx
+
+ integer, intent(in) :: ncol
+ integer, intent(in) :: pver
+ type(ccpp_constituent_prop_ptr_t), &
+ intent(in) :: const_props(:) ! CCPP constituent properties pointer
+ real(kind_phys), intent(in) :: mwdry ! molecular_weight_of_dry_air [g mol-1]
+ real(kind_phys), intent(in) :: boltz ! boltzmann_constant [J K-1]
+ real(kind_phys), intent(in) :: t(:,:) ! air temperature [K]
+ real(kind_phys), intent(in) :: pmiddry(:,:) ! dry air pressure [Pa]
+ real(kind_phys), intent(in) :: pmid(:,:) ! air pressure [Pa]
+ real(kind_phys), intent(in) :: pint(:,:) ! air pressure at interfaces [Pa]
+ real(kind_phys), intent(in) :: phis(:) ! surface geopotential [m2 s-2]
+ real(kind_phys), intent(in) :: zi(:,:) ! geopotential height above surface, interfaces [m]
+
+ real(kind_phys), intent(inout) :: constituents(:,:,:) ! constituent array (ncol, pver, pcnst)
+
+ character(len=*), intent(out) :: errmsg
+ integer, intent(out) :: errflg
+
+ ! conversion factor to mass mixing ratio (kg kg-1 dry)
+ real(kind_phys) :: to_mmr(ncol, pver)
+
+ ! prescribed ozone mass mixing ratio [kg kg-1 dry]
+ real(kind_phys) :: prescribed_ozone(ncol, pver)
+
+ ! units from file
+ character(len=32) :: units_str
+
+ integer :: id_o3
+
+ errmsg = ''
+ errflg = 0
+
+ if(.not. has_prescribed_ozone) then
+ return
+ end if
+
+ ! check for 'O3' constituent where prescribed ozone will be written to
+ ! which will be read by radiation.
+ call ccpp_const_get_idx(const_props, &
+ trim(ozone_name), &
+ id_o3, errmsg, errflg)
+ if (errflg /= 0) return
+
+ ! could not find the constituent, but the specifier is active.
+ ! throw an error.
+ if (id_o3 < 0) then
+ errmsg = 'prescribed_ozone: could not find constituent ' // trim(ozone_name)
+ errflg = 1
+ return
+ end if
+
+ ! advance data in tracer_data to current time.
+ call advance_trcdata(tracer_data_fields, tracer_data_file, &
+ pmid, pint, phis, zi)
+ units_str = trim(tracer_data_fields(1)%units)
+
+ ! copy field from tracer_data container.
+ prescribed_ozone(:ncol,:pver) = tracer_data_fields(1)%data(:ncol, :pver)
+
+ ! unit conversions needed for this field to be in kg kg-1 dry?
+ select case(units_str)
+ case('kg/kg', 'mmr')
+ to_mmr = 1._kind_phys
+ case('mol/mol', 'mole/mole', 'vmr', 'fraction')
+ to_mmr = ozone_mw/mwdry
+ case('molec/cm3', '/cm3', 'molecules/cm3', 'cm^-3', 'cm**-3')
+ to_mmr(:ncol,:pver) = (ozone_mw*1e6_kind_phys*boltz*t(:ncol,:pver))/&
+ (mwdry*pmiddry(:ncol,:pver))
+ case default
+ errflg = 1
+ errmsg = 'prescribed_ozone_run: unit' // units_str //' are not recognized'
+ return
+ end select
+
+ ! convert to kg kg-1 (dry)
+ prescribed_ozone = to_mmr * prescribed_ozone
+
+ ! write to constituent array
+ constituents(:ncol, :pver, id_o3) = prescribed_ozone
+
+ ! convert to mol mol-1 (dry) only for diagnostic output
+ call history_out_field('ozone', prescribed_ozone(:ncol,:pver)*(mwdry/ozone_mw))
+
+ end subroutine prescribed_ozone_run
+
+end module prescribed_ozone
diff --git a/schemes/chemistry/prescribed_ozone.meta b/schemes/chemistry/prescribed_ozone.meta
new file mode 100644
index 00000000..3e0b9d84
--- /dev/null
+++ b/schemes/chemistry/prescribed_ozone.meta
@@ -0,0 +1,210 @@
+[ccpp-table-properties]
+ name = prescribed_ozone
+ type = scheme
+ dependencies = ../../to_be_ccppized/ccpp_chem_utils.F90
+
+[ccpp-arg-table]
+ name = prescribed_ozone_register
+ type = scheme
+[ amIRoot ]
+ standard_name = flag_for_mpi_root
+ units = flag
+ type = logical
+ dimensions = ()
+ intent = in
+[ iulog ]
+ standard_name = log_output_unit
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ filename ]
+ standard_name = filename_of_prescribed_ozone
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ ozone_constituents ]
+ # or can this just be the ccpp_constituent_properties?
+ standard_name = prescribed_ozone_constituents
+ units = none
+ type = ccpp_constituent_properties_t
+ allocatable = True
+ dimensions = (:)
+ intent = out
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
+
+[ccpp-arg-table]
+ name = prescribed_ozone_init
+ type = scheme
+[ amIRoot ]
+ standard_name = flag_for_mpi_root
+ units = flag
+ type = logical
+ dimensions = ()
+ intent = in
+[ iulog ]
+ standard_name = log_output_unit
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ fld_name ]
+ standard_name = variable_name_of_ozone_in_file_for_prescribed_ozone
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ filename ]
+ standard_name = filename_of_prescribed_ozone
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ filelist ]
+ standard_name = filename_of_file_list_for_prescribed_ozone
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ datapath ]
+ standard_name = datapath_for_prescribed_ozone
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ data_type ]
+ standard_name = time_interpolation_method_for_prescribed_ozone
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = in
+[ cycle_yr ]
+ standard_name = cycle_year_for_prescribed_ozone
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ fixed_ymd ]
+ standard_name = fixed_date_for_prescribed_ozone
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = in
+[ fixed_tod ]
+ standard_name = fixed_time_of_day_for_prescribed_ozone
+ units = s
+ type = integer
+ dimensions = ()
+ intent = in
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
+
+[ccpp-arg-table]
+ name = prescribed_ozone_run
+ type = scheme
+[ ncol ]
+ standard_name = horizontal_loop_extent
+ units = count
+ type = integer
+ dimensions = ()
+ intent = in
+[ pver ]
+ standard_name = vertical_layer_dimension
+ units = count
+ type = integer
+ dimensions = ()
+ intent = in
+[ const_props ]
+ standard_name = ccpp_constituent_properties
+ units = none
+ type = ccpp_constituent_prop_ptr_t
+ dimensions = (number_of_ccpp_constituents)
+ intent = in
+[ mwdry ]
+ standard_name = molecular_weight_of_dry_air
+ units = g mol-1
+ type = real | kind = kind_phys
+ dimensions = ()
+ intent = in
+[ boltz ]
+ standard_name = boltzmann_constant
+ units = J K-1
+ type = real | kind = kind_phys
+ dimensions = ()
+ intent = in
+[ t ]
+ standard_name = air_temperature
+ units = K
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_layer_dimension)
+ intent = in
+[ pmiddry ]
+ standard_name = air_pressure_of_dry_air
+ units = Pa
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_layer_dimension)
+ intent = in
+[ pmid ]
+ standard_name = air_pressure
+ units = Pa
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_layer_dimension)
+ intent = in
+[ pint ]
+ standard_name = air_pressure_at_interface
+ units = Pa
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_interface_dimension)
+ intent = in
+[ phis ]
+ standard_name = surface_geopotential
+ units = m2 s-2
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent)
+ intent = in
+[ zi ]
+ standard_name = geopotential_height_wrt_surface_at_interface
+ units = m
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_interface_dimension)
+ intent = in
+[ constituents ]
+ standard_name = ccpp_constituents
+ units = none
+ type = real | kind = kind_phys
+ dimensions = (horizontal_loop_extent, vertical_layer_dimension, number_of_ccpp_constituents)
+ intent = inout
+[ errmsg ]
+ standard_name = ccpp_error_message
+ units = none
+ type = character | kind = len=*
+ dimensions = ()
+ intent = out
+[ errflg ]
+ standard_name = ccpp_error_code
+ units = 1
+ type = integer
+ dimensions = ()
+ intent = out
diff --git a/schemes/chemistry/prescribed_ozone_namelist.xml b/schemes/chemistry/prescribed_ozone_namelist.xml
new file mode 100644
index 00000000..835c0123
--- /dev/null
+++ b/schemes/chemistry/prescribed_ozone_namelist.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+ char*256
+ chemistry
+ prescribed_ozone_nl
+ datapath_for_prescribed_ozone
+ none
+
+ Full pathname of the directory that contains the files specified in prescribed_ozone_filelist.
+
+
+ ${DIN_LOC_ROOT}/atm/cam/ozone
+
+
+
+
+ char*256
+ chemistry
+ prescribed_ozone_nl
+ filename_of_prescribed_ozone
+ none
+
+ Filename of dataset for prescribed ozone.
+
+
+ ozone_1.9x2.5_L26_2000clim_c091112.nc
+
+
+
+
+ char*256
+ chemistry
+ prescribed_ozone_nl
+ filename_of_file_list_for_prescribed_ozone
+ none
+
+ Filename of file that contains a sequence of filenames for prescribed ozone. The filenames in this file are relative to the directory specified by prescribed_ozone_datapath.
+
+
+ UNSET
+
+
+
+
+ char*16
+ chemistry
+ prescribed_ozone_nl
+ variable_name_of_ozone_in_file_for_prescribed_ozone
+ none
+
+ Name of variable containing ozone data in the prescribed ozone datasets. Default: 'O3'
+
+
+ O3
+
+
+
+
+ char*32
+ chemistry
+ prescribed_ozone_nl
+ time_interpolation_method_for_prescribed_ozone
+ none
+ CYCLICAL,SERIAL,INTERP_MISSING_MONTHS,FIXED
+
+ Type of time interpolation for data in prescribed_ozone files. Can be set to 'CYCLICAL', 'SERIAL', 'INTERP_MISSING_MONTHS', or 'FIXED'. Default: 'CYCLICAL'
+
+
+ CYCLICAL
+
+
+
+
+ integer
+ chemistry
+ prescribed_ozone_nl
+ cycle_year_for_prescribed_ozone
+ 1
+
+ The cycle year of the prescribed ozone data if prescribed_ozone_type is 'CYCLICAL'. Format: YYYY
+ Default: 2000
+
+
+ 2000
+
+
+
+
+ integer
+ chemistry
+ prescribed_ozone_nl
+ fixed_date_for_prescribed_ozone
+ 1
+
+ The date at which the prescribed ozone data is fixed if prescribed_ozone_type is 'FIXED'. Format: YYYYMMDD
+ Default: 0
+
+
+ 0
+
+
+
+
+ integer
+ chemistry
+ prescribed_ozone_nl
+ fixed_time_of_day_for_prescribed_ozone
+ s
+
+ The time of day (seconds) corresponding to prescribed_ozone_fixed_ymd at which the prescribed ozone data is fixed if prescribed_ozone_type is 'FIXED'. Default: 0 seconds
+
+
+ 0
+
+
+
diff --git a/test/test_suites/suite_tracer_data_test.xml b/test/test_suites/suite_tracer_data_test.xml
new file mode 100644
index 00000000..c7f7319d
--- /dev/null
+++ b/test/test_suites/suite_tracer_data_test.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ prescribed_ozone
+
+
+ prescribed_aerosols
+
+
+ prescribed_aerosol_deposition_flux
+
+
diff --git a/to_be_ccppized/ccpp_chem_utils.F90 b/to_be_ccppized/ccpp_chem_utils.F90
new file mode 100644
index 00000000..74a1024b
--- /dev/null
+++ b/to_be_ccppized/ccpp_chem_utils.F90
@@ -0,0 +1,44 @@
+! Various utilities used in CAM-SIMA chemistry.
+module ccpp_chem_utils
+
+ implicit none
+ private
+
+ public :: chem_constituent_qmin
+
+contains
+
+ ! Returns the minimum mixing ratio for a given constituent
+ ! Used to set appropriate minimum value for various chemical species at register phase.
+ function chem_constituent_qmin(constituent_name) result(qmin)
+ use ccpp_kinds, only: kind_phys
+
+ use string_utils, only: to_lower
+
+ character(len=*), intent(in) :: constituent_name ! Name of the chemical constituent
+ real(kind_phys) :: qmin ! Minimum mixing ratio
+
+ character(len=len(constituent_name)) :: name_lower
+
+ ! Convert to lowercase for case-insensitive comparison
+ name_lower = to_lower(constituent_name) ! impure
+
+ ! Default minimum mixing ratio for chemistry species.
+ qmin = 1.e-36_kind_phys
+
+ if (index(name_lower, 'num_a') == 1) then
+ ! Aerosol number density.
+ qmin = 1.e-5_kind_phys
+ else if (trim(name_lower) == 'o3') then
+ qmin = 1.e-12_kind_phys
+ else if (trim(name_lower) == 'ch4') then
+ qmin = 1.e-12_kind_phys
+ else if (trim(name_lower) == 'n2o') then
+ qmin = 1.e-15_kind_phys
+ else if (trim(name_lower) == 'cfc11' .or. trim(name_lower) == 'cfc12') then
+ qmin = 1.e-20_kind_phys
+ end if
+
+ end function chem_constituent_qmin
+
+end module ccpp_chem_utils