! Copyright 2008-2021 Earth Sciences Department, BSC-CNS
!
! This file is part of MONARCH.
!
! MONARCH is free software: you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! MONARCH is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
! GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License
! along with MONARCH. If not, see <http://www.gnu.org/licenses/>.

!> \file
!> The monarch_dry_deposition_t object and related functions

!> \page config_dry_deposition Dry deposition configuration file
!!
!! Dry deposition files are in \c json format and structured as follows:
!!
!! \code{.json}
!! {
!!   "dry-dep-data": {
!!     "calculate" : true,
!!     "output rates" : true,
!!   }
!! }
!! \endcode
!!
!! The \c dry-dep-data and \c calculate key value pairs are required. When
!! \c calculate is set to \c false, no dry deposition calculations are
!! performed, any dry deposition reactions in PartMC will retain their
!! default values (zero unless otherwise specified).
!!
!! The dry deposition module will find reactions in the PartMC \c phlex_core_t
!! object's mechanisms that include the key-value pair:
!! \code{.json}
!!   "loss type" : "dry dep"
!! \endcode
!! It attaches update cookies to these reactions and updates their base rates
!! during calls to monarch_dry_deposition::update_rxn_rates.
!!
!! The \c output \c rates key-value pair is optional and indicates whether
!! calculated dry deposition rates are output to the history file. It defaults
!! to \c false.

!> Functions to calculate dry deposition
module monarch_dry_deposition
! MONARCH always has MPI
#define MONARCH_USE_MPI

#ifdef DO_PARTMC

! PartMC always has JSON when used in MONARCH
#define PMC_USE_JSON

! Prefix to use in labeling PMC dry deposition rxns for updates
!( This can be any integer, but must be unique to dry deposition )
#define PMC_DRY_DEP_PREFIX 24601

  use monarch_constants,                    only : mint, mflt, mdbl
  use monarch_state_changer,                only : monarch_state_array_ptr
  use monarch_util,                         only : string_t
  use pmc_phlex_core,                       only : phlex_core_t
  use pmc_property,                         only : property_t
  use pmc_rxn_first_order_loss,             only :                           &
                                      rxn_update_data_first_order_loss_rate_t

  implicit none
  private

  public :: monarch_dry_deposition_t

  !> Dry deposition seasons
  !> Mid-Summer with lush vegitation
  integer(kind=mint), parameter, public :: PMC_DRYDEP_SEASON_SUMMER     = 1
  !> Autumn with unharvested cropland
  integer(kind=mint), parameter, public :: PMC_DRYDEP_SEASON_AUTUMN     = 2
  !> Late autumn with frost, but no snow
  integer(kind=mint), parameter, public :: PMC_DRYDEP_SEASON_LATEAUTUMN = 3
  !> Winter, snow on the ground and sub-freezing
  integer(kind=mint), parameter, public :: PMC_DRYDEP_SEASON_WINTER     = 4
  !> Transitional spring with partially green short annuals
  integer(kind=mint), parameter, public :: PMC_DRYDEP_SEASON_SPRING     = 5

  !> PartMC dry deposition
  !!
  !! This type provides functions to calculate dry deposition rates and update
  !! the PartMC-phlex first-order loss reactions accordingly
  type monarch_dry_deposition_t
    private
    !> Phlex-chem core
    type(phlex_core_t), pointer :: phlex_core => null()
    !> Number of dry deposition species
    integer(kind=mint) :: n_species
    !> Index for NH3 in the TRACERS array
    integer(kind=mint) :: NH3_index = 0
    !> Index for SO2 in the TRACERS array
    integer(kind=mint) :: SO2_index = 0
    !> Index for O3 in the TRACERS array
    integer(kind=mint) :: O3_index = 0
    !> Index for NH3 in PMC-phlex state array
    integer(kind=mint) :: NH3_phlex_index = 0
    !> Index for SO2 in PMC-phlex state array
    integer(kind=mint) :: SO2_phlex_index = 0
    !> Dry deposition rate update nugget
    type(rxn_update_data_first_order_loss_rate_t) :: rate_update
    !> Henry's Law constant at 298 K [mol L-1 atm-1]
    real(kind=mdbl), allocatable :: hlc_298K( : )
    !> Henry's Law exponential factor [K]
    real(kind=mdbl), allocatable :: hlc_exp_factor( : )
    !> Reactivity factor [unitless fraction]
    real(kind=mdbl), allocatable :: reactivity_factor( : )
    !> Diffusion coefficient [m2 s-1]
    real(kind=mdbl), allocatable :: diffusion_coeff( : )
    !> Surface resistance [s m-1]
    real(kind=mflt), allocatable :: surface_resistance( : )
    !> Deposition rate constant [s-1]
    real(kind=mflt), allocatable :: loss_rate_constant( : )
    !> Property set
    type(property_t), pointer :: property_set => null()
    !> Flag indicating whether to do dry deposition
    logical :: do_dry_deposition = .false.
    !> Flag indicating to output dry deposition velocities
    logical :: output_rates = .false.
    !> Dry deposition labels for output
    type(string_t), allocatable :: dry_dep_rxn_labels( : )
    !> State array for deposition velocities
    type(monarch_state_array_ptr) :: dry_dep_velocity_output
  contains
    !> Update deposition rates in PartMC-phlex for one grid cell
    procedure :: update_rxn_rates
    !> Get the dry-deposition season for a single column
    procedure :: get_season
    !> Determine the number of bytes required to pack the given object
    procedure :: pack_size
    !> Packs the given object in the buffer, advancing position
    procedure :: bin_pack
    !> Unpacks the given object from the buffer, advancing position
    procedure :: bin_unpack
    !> Print the dry deposition data
    procedure :: print => do_print
    !> Load dry deposition configuration
    procedure, private :: load
    !> Initialize the dry deposition module
    procedure, private :: initialize
    !> Calculate the dry deposition rates for one vertical column
    procedure, private :: calc_rates
    !> Set up output arrays
    procedure, private :: setup_output_arrays
    !> Finalize the dry deposition object
    final :: finalize
  end type monarch_dry_deposition_t

  !> PartMC dry deposition constructor
  interface monarch_dry_deposition_t
    procedure :: constructor
  end interface monarch_dry_deposition_t

contains

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Create a new monarch_dry_deposition_t object
  !!
  !! Create a new monarch_dry_deposition_t object at the beginning of the model
  !! run for each process. This constructor should be called with an already-
  !! initialized phlex_core_t object. This should be called with the
  !! initialize flag set to true on the primary process, and false on the
  !! other processes.
  function constructor( phlex_core, diag, state_changer, config_file )       &
      result( new_obj )

    use monarch_diagnostics,                only : monarch_diagnostics_t
    use monarch_state_changer,              only : monarch_state_changer_t
    use monarch_util,                       only : assert_msg

    !> A new PartMC dry deposition object
    type(monarch_dry_deposition_t), pointer :: new_obj
    !> Phlex core
    type(phlex_core_t), target, intent(inout) :: phlex_core
    !> Diagnostics object attached to internal state with model dimensions
    !! Should only be included in calls from the primary process
    type(monarch_diagnostics_t), intent(in), optional :: diag
    !> State changer object with registered state arrays
    !! Should only be included in calls from the primary process
    type(monarch_state_changer_t), intent(inout), optional :: state_changer
    !> Path to the dry deposition configuration file
    !! Should only be included in calls from the primary process
    character(len=:), allocatable, optional, intent(in) :: config_file

    ! Create a new dry deposition object
    allocate( new_obj )

    ! Attach to the phlex_core_t object
    new_obj%phlex_core => phlex_core

    ! Find the depositing species properties on the primary process
    if( present( diag ) ) then

      call assert_msg( 618664274, present( state_changer ),                  &
                     "Missing state changer in dry deposition set up" )

      call assert_msg( 583911286, present( config_file ),                    &
                       "Missing configuration file for dry deposition" )

      ! Load the dry deposition configuration, species and their properties
      ! from PartMC
      call new_obj%load( config_file )

      ! Initialize the dry deposition module on the primary process
      call new_obj%initialize( )

      ! Set the output arrays if needed
      call new_obj%setup_output_arrays( state_changer )

    end if

  end function constructor

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Update dry deposition reaction rates for a single grid cell
  subroutine update_rxn_rates( this, state_changer, column, phlex_state,     &
      point )

    use monarch_column,                     only : monarch_column_t
    use monarch_state_changer,              only : monarch_state_changer_t
    use monarch_util,                       only : monarch_point_t
    use pmc_phlex_state,                    only : phlex_state_t

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(inout) :: this
    !> State changer object
    type(monarch_state_changer_t), intent(inout) :: state_changer
    !> State object for column to calculate dry deposition for
    type(monarch_column_t), intent(in) :: column
    !> Phlex state for the surface layer
    type(phlex_state_t), intent(in) :: phlex_state
    !> Grid cell to update
    type(monarch_point_t), intent(in) :: point

    integer(kind=mint) :: i_rate

    ! Do nothing if not flagged to calculate dry deposition
    if( .not. this%do_dry_deposition ) return

    ! Set dry deposition loss rates to zero except at the surface
    if( point%vertical_idx( ) .eq. 1 ) then

      ! Recalculate the rates
      call this%calc_rates( column, phlex_state, state_changer, point )

      ! Update each dry deposition reaction
      do i_rate = 1, size( this%loss_rate_constant )
        call this%rate_update%set_rate( PMC_DRY_DEP_PREFIX + i_rate,         &
                  real( this%loss_rate_constant( i_rate ) , kind=mdbl ) )
        call this%phlex_core%update_rxn_data( this%rate_update )
      end do

    else

      ! Update each dry deposition reaction
      do i_rate = 1, size( this%loss_rate_constant )
        call this%rate_update%set_rate( PMC_DRY_DEP_PREFIX + i_rate,         &
                  real( 0.0, kind=mdbl ) )
        call this%phlex_core%update_rxn_data( this%rate_update )
      end do

    end if

  end subroutine update_rxn_rates

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Get the dry-deposition season for a single vertical column
  integer(kind=mint)function get_season( this, column ) result( season )

    use monarch_column,                     only : monarch_column_t

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(in) :: this
    !> Column state
    type(monarch_column_t), intent(in) :: column

    real(kind=mdbl) :: lat, long, jul_day

    lat     = column%latitude__deg( )
    jul_day = column%julian_day

    !> \todo Spring and late autumn seasons are never selected in dry
    !!       deposition. Check why.

    ! Poles
    if( lat.gt.80.0 .or. lat.lt.-80.0 ) then
      season = PMC_DRYDEP_SEASON_WINTER

    ! Northern Hemisphere mid-latitudes
    else if( lat.ge.20.0 ) then
      if( jul_day.lt.90.0 .or. jul_day.gt.270.0 ) then
        season = PMC_DRYDEP_SEASON_AUTUMN
      else
        season = PMC_DRYDEP_SEASON_SUMMER
      end if

    ! Southern Hemisphere mid-latitudes
    else if( lat.le.-20.0 ) then
      if( jul_day.lt.90.0 .or. jul_day.gt.270.0 ) then
        season = PMC_DRYDEP_SEASON_SUMMER
      else
        season = PMC_DRYDEP_SEASON_AUTUMN
      endif

    ! Equator
    else
      season = PMC_DRYDEP_SEASON_SUMMER
    end if

  end function get_season

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Determine the size of a binary required to pack the PMC dry deposition
  !! module
  integer(kind=mint)function pack_size( this, comm )
    use monarch_mpi,                    only : mnrc_mpi_pack_size_integer,   &
                                               mnrc_mpi_pack_size_logical,   &
                                               mnrc_mpi_pack_size_double_array

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(in) :: this
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

    pack_size =                                                              &
        mnrc_mpi_pack_size_integer(      this%n_species        , comm )    + &
        mnrc_mpi_pack_size_integer(      this%NH3_index        , comm )    + &
        mnrc_mpi_pack_size_integer(      this%SO2_index        , comm )    + &
        mnrc_mpi_pack_size_integer(      this%O3_index         , comm )    + &
        mnrc_mpi_pack_size_integer(      this%NH3_phlex_index  , comm )    + &
        mnrc_mpi_pack_size_integer(      this%SO2_phlex_index  , comm )    + &
        mnrc_mpi_pack_size_logical(      this%do_dry_deposition, comm )    + &
        mnrc_mpi_pack_size_logical(      this%output_rates     , comm )    + &
        mnrc_mpi_pack_size_double_array( this%hlc_298K         , comm )    + &
        mnrc_mpi_pack_size_double_array( this%hlc_exp_factor   , comm )    + &
        mnrc_mpi_pack_size_double_array( this%reactivity_factor, comm )    + &
        mnrc_mpi_pack_size_double_array( this%diffusion_coeff  , comm )

  end function pack_size

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Pack the given object to the buffer, advancing position
  subroutine bin_pack( this, buffer, pos, comm )
#ifdef MONARCH_USE_MPI
    use monarch_mpi,                        only : mnrc_mpi_pack_integer,    &
                                                   mnrc_mpi_pack_logical,    &
                                                   mnrc_mpi_pack_double_array
    use monarch_util,                       only : assert
#endif

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(in) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer(kind=mint), intent(inout) :: pos
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

#ifdef MONARCH_USE_MPI
    integer(kind=mint) :: prev_position

    prev_position = pos
    call mnrc_mpi_pack_integer(      buffer, pos, this%n_species      , comm )
    call mnrc_mpi_pack_integer(      buffer, pos, this%NH3_index      , comm )
    call mnrc_mpi_pack_integer(      buffer, pos, this%SO2_index      , comm )
    call mnrc_mpi_pack_integer(      buffer, pos, this%O3_index       , comm )
    call mnrc_mpi_pack_integer(      buffer, pos, this%NH3_phlex_index, comm )
    call mnrc_mpi_pack_integer(      buffer, pos, this%SO2_phlex_index, comm )
    call mnrc_mpi_pack_logical(      buffer, pos, this%do_dry_deposition,    &
                                                                        comm )
    call mnrc_mpi_pack_logical(      buffer, pos, this%output_rates   , comm )
    call mnrc_mpi_pack_double_array( buffer, pos, this%hlc_298K       , comm )
    call mnrc_mpi_pack_double_array( buffer, pos, this%hlc_exp_factor , comm )
    call mnrc_mpi_pack_double_array( buffer, pos, this%reactivity_factor,    &
                                                                        comm )
    call mnrc_mpi_pack_double_array( buffer, pos, this%diffusion_coeff, comm )
    call assert( 450628052, &
         pos - prev_position <= this%pack_size( comm ) )

#endif

  end subroutine bin_pack

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Unpack the given object to the buffer, advancing position
  subroutine bin_unpack( this, buffer, pos, comm, state_changer )
    use monarch_state_changer,              only : monarch_state_changer_t
#ifdef MONARCH_USE_MPI
    use monarch_mpi,                        only : mnrc_mpi_unpack_integer,  &
                                            mnrc_mpi_unpack_logical,         &
                                            mnrc_mpi_unpack_double_array
    use monarch_util,                       only : assert
#endif

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(inout) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer(kind=mint), intent(inout) :: pos
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm
    !> State changer object
    type(monarch_state_changer_t), intent(inout) :: state_changer

#ifdef MONARCH_USE_MPI
    integer(kind=mint) :: prev_position

    prev_position = pos
    call mnrc_mpi_unpack_integer(      buffer, pos, this%n_species    , comm )
    call mnrc_mpi_unpack_integer(      buffer, pos, this%NH3_index    , comm )
    call mnrc_mpi_unpack_integer(      buffer, pos, this%SO2_index    , comm )
    call mnrc_mpi_unpack_integer(      buffer, pos, this%O3_index     , comm )
    call mnrc_mpi_unpack_integer(      buffer, pos, this%NH3_phlex_index,    &
                                                                        comm )
    call mnrc_mpi_unpack_integer(      buffer, pos, this%SO2_phlex_index,    &
                                                                        comm )
    call mnrc_mpi_unpack_logical(      buffer, pos, this%do_dry_deposition,  &
                                                                        comm )
    call mnrc_mpi_unpack_logical(      buffer, pos, this%output_rates , comm )
    call mnrc_mpi_unpack_double_array( buffer, pos, this%hlc_298K     , comm )
    call mnrc_mpi_unpack_double_array( buffer, pos, this%hlc_exp_factor,comm )
    call mnrc_mpi_unpack_double_array( buffer, pos, this%reactivity_factor,  &
                                                                        comm )
    call mnrc_mpi_unpack_double_array( buffer, pos, this%diffusion_coeff,    &
                                                                        comm )
    call assert( 893183886, &
         pos - prev_position <= this%pack_size( comm ) )

    ! Initialize the dry deposition object on this process
    call this%initialize( )

    ! Setup the output arrays if needed
    call this%setup_output_arrays( state_changer )
#endif

  end subroutine bin_unpack

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Set up output arrays
  subroutine setup_output_arrays( this, state_changer )

    use monarch_state_changer,         only : monarch_state_changer_t
    use monarch_util,                  only : to_string

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(inout) :: this
    !> State changer oject
    type(monarch_state_changer_t), intent(inout) :: state_changer

    integer(kind=mint) :: i_rxn, rxn_id

    ! Register a state array to hold dry deposition velocities
    if( this%n_species .eq. 0 ) this%output_rates = .false.
    if( this%output_rates ) then
      allocate( this%dry_dep_rxn_labels( this%n_species ) )
      do i_rxn = 1, size( this%dry_dep_rxn_labels )
        rxn_id = PMC_DRY_DEP_PREFIX + i_rxn
        this%dry_dep_rxn_labels( i_rxn )%string = to_string( i_rxn )
      end do
      this%dry_dep_velocity_output = state_changer%register_per_column_real( &
                                            "dry_deposition_velocity",       & !<-- unique name for the array
                                            .true.,                          & !<-- output to history file
                                            size( this%dry_dep_rxn_labels ), & !<-- number of array elements
                                            this%dry_dep_rxn_labels )          !<-- label for each element
      call this%dry_dep_velocity_output%set_netcdf_long_name(                &
                                            "Dry deposition velocity" )
      call this%dry_dep_velocity_output%set_netcdf_units( "m s-1" )
    end if

  end subroutine setup_output_arrays

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Print the dry deposition data
  subroutine do_print( this, file_unit )

    use monarch_util,                       only : to_string

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(in) :: this
    !> File unit to write to
    integer(kind=mint), intent(in) :: file_unit

    integer(kind=mint) :: i_spec, i_dry_dep_rxn
    character(len=*), parameter :: fmt_blank_line = "('')"
    character(len=*), parameter :: fmt1 = "(' | ',A13,' | ',A13,' | ',A13"// &
                                   ",' | ',A13,' | ',A13,' |')"
    character(len=*), parameter :: fmt2 = "(' | ',I13,' | ',ES13.3,' | ',"// &
                                   "E13.3,' | ',ES13.3,' | ',ES13.3,' |')"

    write(file_unit,fmt_blank_line)
    write(file_unit,*) "*****************************"
    write(file_unit,*) "** PMC Dry Deposition Data **"
    write(file_unit,*) "*****************************"
    write(file_unit,fmt_blank_line)
    if( this%do_dry_deposition ) then
      write(file_unit,*) "Number of species:", this%n_species
      write(file_unit,fmt1) "PartMC rxn id", "HLC( 298K )", "HLC exp fact",  &
                            "React fact", "diff coeff"
      do i_spec = 1, this%n_species
        i_dry_dep_rxn = PMC_DRY_DEP_PREFIX + i_spec
        write(file_unit,fmt2) i_dry_dep_rxn, this%hlc_298K( i_spec ),        &
                              this%hlc_exp_factor( i_spec ),                 &
                              this%reactivity_factor( i_spec ),              &
                              this%diffusion_coeff( i_spec )
      end do
      write(file_unit,fmt_blank_line)
      write(file_unit,*) "Output dry deposition velocities?", this%output_rates
    else
      write(file_unit,*) "Dry deposition not calculated - defaults rates "// &
                         "specified in reaction parameters will be used."
    endif
    write(file_unit,fmt_blank_line)
    write(file_unit,*) "*********************************"
    write(file_unit,*) "** End PMC Dry Deposition Data **"
    write(file_unit,*) "*********************************"
    write(file_unit,fmt_blank_line)

  end subroutine do_print

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Finalize the dry deposition object
  subroutine finalize( this )

    !> Dry deposition object
    type(monarch_dry_deposition_t), intent(inout) :: this

    ! The PartMC <-> MONARCH interface owns the phlex_core object
    if( associated( this%phlex_core         ) ) &
        this%phlex_core => null( )
    if( allocated( this%hlc_298K            ) ) &
        deallocate( this%hlc_298K             )
    if( allocated( this%hlc_exp_factor      ) ) &
        deallocate( this%hlc_exp_factor       )
    if( allocated( this%reactivity_factor   ) ) &
        deallocate( this%reactivity_factor    )
    if( allocated( this%diffusion_coeff     ) ) &
        deallocate( this%diffusion_coeff      )
    if( allocated( this%surface_resistance  ) ) &
        deallocate( this%surface_resistance   )
    if( allocated( this%loss_rate_constant  ) ) &
        deallocate( this%loss_rate_constant   )
    if( associated( this%property_set       ) ) &
        deallocate( this%property_set         )

  end subroutine finalize

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Load dry deposition configuration
  !!
  !! The dry deposition species are found in the PartMC mechanism and numbered
  !! in order of being found to create the set. Required properties are loaded
  !! from PartMC input data for chemical species.
  subroutine load( this, config_file )

#ifdef PMC_USE_JSON
    use json_module,                        only : json_core, json_file,     &
                                                   json_value, json_ck
#endif
    use monarch_util,                       only : string_t, assert_msg,     &
                                                   die_msg, warn_assert_msg
    use pmc_chem_spec_data,                 only : chem_spec_data_t
    use pmc_mechanism_data,                 only : mechanism_data_t
    use pmc_property,                       only : property_t
    use pmc_rxn_data,                       only : rxn_data_t
    use pmc_rxn_first_order_loss,           only : rxn_first_order_loss_t

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(inout) :: this
    !> Path to the PMC <-> dry deposition interface configuration file
    character(len=:), allocatable, intent(in) :: config_file
#ifdef PMC_USE_JSON
    type(json_core), pointer :: json
    type(json_file) :: j_file
    type(json_value), pointer :: j_obj, j_next, j_child
    character(kind=json_ck, len=:), allocatable :: key, unicode_str_val
    integer(kind=mint) :: i_rxn, i_spec, n_spec
    type(string_t), allocatable :: spec_names( : ) , tmp_spec_names( : )
    type(mechanism_data_t), pointer :: mechanism
    class(rxn_data_t), pointer :: rxn
    type(chem_spec_data_t), pointer :: chem_spec_data
    type(property_t), pointer :: spec_props
    character(len=:), allocatable :: spec_key, type_key, prop_key, str_val
    character(len=:), allocatable :: owner_name
    real(kind=mdbl) :: real_val
    integer(kind=mint), parameter :: realloc_size = 100
    logical :: found, logical_val

    ! Initialize the property set
    this%property_set => property_t( )

    ! Get a new json core
    allocate( json )

    ! Initialize the json objects
    j_obj  => null( )
    j_next => null( )

    ! Initialize the json file
    call j_file%initialize( )
    call j_file%get_core( json )
    call assert_msg( 899272034, allocated( config_file ),                    &
                     "Received non-allocated string for file path" )
    call assert_msg( 843339009, trim( config_file ) .ne. "",                 &
                     "Received empty string for file path" )
    inquire( file=config_file, exist=found )
    call assert_msg( 235489477, found, "Could not find file: "//             &
                     config_file )
    call j_file%load_file( filename = config_file )

    ! Find the interface data
    call j_file%get( 'dry-dep-data', j_obj )

    ! Load the data to the property set
    call assert_msg( 906681062, associated( j_obj ),                         &
                     "Missing 'dry-dep-data' element in dry deposition "//   &
                     "configuration data file" )
    owner_name = "dry deposition interface"
    call this%property_set%load( json, j_obj, .true., owner_name )

    ! Determine whether to calculate dry deposition
    prop_key = "calculate"
    call assert_msg( 992942174,                                              &
                     this%property_set%get_logical( prop_key, logical_val ), &
                     "Missing dry deposition 'calculate' flag" )
    this%do_dry_deposition = logical_val

    ! Determine whether to output the dry deposition rates
    if( this%do_dry_deposition ) then
      prop_key = "output rates"
      if( this%property_set%get_logical( prop_key, logical_val ) ) then
        this%output_rates = logical_val
      end if
    end if

    ! Clean up the json objects
    call j_file%destroy( )
    call json%destroy( )
    deallocate( json )

    ! Verify the phlex core is initialized
    call assert_msg( 923502065, this%phlex_core%is_initialized( ) , &
                    "Trying to load species properties for "// &
                    "monarch_dry_deposition_t with an "// &
                    "uninitialized phlex_core_t object" )

    ! FIXME This assumes MONARCH only uses one PMC mechanism at a time
    mechanism => this%phlex_core%mechanism( 1 ) %val

    ! Allocate space for species names
    allocate( spec_names( realloc_size ) )
    allocate( tmp_spec_names( realloc_size ) )

    ! Count and get the names of species involved in deposition reactions
    n_spec = 0
    spec_key = "species"
    type_key = "loss type"
    do i_rxn = 1, mechanism%size( )

      ! Find all first-order loss reactions
      rxn => mechanism%get_rxn( i_rxn )
      select type( rxn_dry_dep => rxn )
        class is( rxn_first_order_loss_t )

          ! Find dry deposition reactions
          if( rxn%property_set%get_string( type_key, str_val ) ) then
            if( str_val.eq."dry dep" ) then

              ! Find the species name
              call assert_msg( 343923921, &
                   rxn%property_set%get_string( spec_key, str_val ) , &
                   "Missing species in dry deposition reaction." )

              ! Try to find the species in the list
              found = .false.
              do i_spec = 1, n_spec
                if( spec_names( i_spec ) %string.eq.str_val ) then
                  found = .true.
                  exit
                end if
              end do

              ! Add new species to the list for dry deposition
              if( .not.found ) then
                n_spec = n_spec + 1

                ! Increase the size of the species names list if necessary
                if( n_spec.gt.size( spec_names ) ) then
                  tmp_spec_names( : ) = spec_names( : )
                  deallocate( spec_names )
                  allocate( spec_names( n_spec+realloc_size ) )
                  spec_names( 1:size( tmp_spec_names ) ) = tmp_spec_names( : )
                end if

                ! Save the species name for looking up properties
                spec_names( n_spec ) %string = str_val

                ! Save the dry deposition id for rate updates
                call rxn_dry_dep%set_rxn_id( PMC_DRY_DEP_PREFIX + n_spec )
              end if
            end if
          end if
      end select
    end do

    ! Allocate space for the species properties
    this%n_species = n_spec
    allocate( this%hlc_298K( n_spec ) )
    allocate( this%hlc_exp_factor( n_spec ) )
    allocate( this%reactivity_factor( n_spec ) )
    allocate( this%diffusion_coeff( n_spec ) )

    ! Get the species properties required for dry deposition calculations
    call assert_msg( 219674917,                                              &
        this%phlex_core%get_chem_spec_data( chem_spec_data ) ,               &
        "Error getting chemical species data for dry deposition." )
    do i_spec = 1, n_spec

      ! Get the species property set
      call assert_msg( 767831550,                                            &
           chem_spec_data%get_property_set( spec_names( i_spec )%string,     &
                                            spec_props ),                    &
           "Error getting property set for dry deposition species "//        &
           spec_names( i_spec )%string )

      !!! Get the deposition parameters for the current species !!!

      ! Henry's Law constant @ 298 K( HLC ) [mol L-1 atm-1]
      ! (convert from [mol L-1 Pa-1] in input file to [mol L-1 atm-1] used
      !  in the dry deposition module.)
      !> \todo Convert all units of [mol L-1] in input files, including those
      !!       for PartMC, to SI units [mol m-3]
      prop_key = "HLC(298K) [M Pa-1]"
      call assert_msg( 240566973, spec_props%get_real( prop_key, real_val ), &
           "Cannot find Henry's Law constant @ 298 K for dry deposition "//  &
           "species "//spec_names( i_spec )%string )
      this%hlc_298K( i_spec ) = real_val * 101325.0

      ! Henry's Law constant exponential factor( dhr ) [K]
      ! HLC( T ) = HLC( 298K ) * exp( dhr *( 1/T - 1/298 ) )
      prop_key = "HLC exp factor [K]"
      call assert_msg( 130441285, spec_props%get_real( prop_key, real_val ), &
           "Cannot find Henry's Law constant exponential factor for dry "//  &
           "deposition species "//spec_names( i_spec ) %string )
      this%hlc_exp_factor( i_spec ) = real_val

      ! Reactivity factor [unitless fraction]
      prop_key = "dry dep reactivity factor [unitless]"
      call assert_msg( 234588845, spec_props%get_real( prop_key, real_val ), &
           "Cannot find dry deposition reactivity factor for species "//     &
           spec_names( i_spec )%string )
      this%reactivity_factor( i_spec ) = real_val

      ! Diffusion coeffcient [m2 s-1]
      prop_key = "diffusion coeff [m2 s-1]"
      call assert_msg( 615947347, spec_props%get_real( prop_key, real_val ), &
           "Cannot find diffusion coefficient for dry deposition species "// &
           spec_names( i_spec )%string )
      call assert_msg( 180871332, real_val.gt.0.0,                           &
           "Trying to do dry deposition without a positive diffusion "//     &
           "coefficient for species "//                                      &
           spec_names( i_spec )%string )
      this%diffusion_coeff( i_spec ) = real_val

      ! Check for O3, SO2 and NH3
      prop_key = "special dry deposition species"
      if( spec_props%get_string( prop_key, str_val ) ) then
        if( str_val.eq."O3" ) then
          this%O3_index = i_spec
        else if( str_val.eq."SO2" ) then
          this%SO2_index = i_spec
          this%SO2_phlex_index = chem_spec_data%gas_state_id(                &
                                   spec_names( i_spec )%string )
        else if( str_val.eq."NH3" ) then
          this%NH3_index = i_spec
          this%NH3_phlex_index = chem_spec_data%gas_state_id(                &
                                   spec_names( i_spec )%string )
        else
          call die_msg( 897874771, "Received unknown special dry "//         &
                       "deposition species: "//str_val )
        end if
      end if

    end do

    ! Send a warning if special species O3, SO2 and NH3 were not labelled in
    ! the PMC-phlex input data
    call warn_assert_msg( 230255549, this%O3_index.gt.0,                     &
                         "No O3 species found for dry deposition" )
    call warn_assert_msg( 285187630, this%SO2_index.gt.0,                    &
                         "No SO2 species found for dry deposition" )
    call warn_assert_msg( 957192166, this%NH3_index.gt.0,                    &
                         "No NH3 species found for dry deposition" )

    deallocate( spec_names )
    deallocate( tmp_spec_names )
#else
    call die_msg( 562655316, "No JSON support for dry deposition "//         &
                             "configuration files" )
#endif

  end subroutine load

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Initialize the dry deposition module
  subroutine initialize( this )

    use module_drydepwesely,            only : dry_dep_species_t,            &
                                               dep_init
    use monarch_util,                   only : assert_msg
    use pmc_rxn_factory,                only : rxn_factory_t

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(inout) :: this

    type(rxn_factory_t) :: rxn_factory
    type(dry_dep_species_t), allocatable :: spec_props( : )
    integer(kind=mint) :: i_spec

    ! Verify the phlex core is initialized
    call assert_msg( 147713025, this%phlex_core%is_initialized( ),           &
                    "Trying to initialize monarch_dry_deposition_t with "//  &
                    "an uninitialized phlex_core_t object" )

    ! Initialize the rate update nugget
    call rxn_factory%initialize_update_data( this%rate_update )

    allocate( spec_props( this%n_species ) )

    ! Load the species properties into the dry deposition species objects
    forall( i_spec = 1 : this%n_species )
      spec_props( i_spec ) %hstar = this%hlc_298K( i_spec )                         ! <-- Henry's Law constant at 298 K [mol L-1 atm-1]
      spec_props( i_spec ) %dhr   = this%hlc_exp_factor( i_spec )                   ! <-- Henry's Law constant exponential factor [K]
      spec_props( i_spec ) %f0    = this%reactivity_factor( i_spec )                ! <-- Reactivity factor [unitless fraction]
      spec_props( i_spec ) %dvj   = this%diffusion_coeff( i_spec ) * 100.0**2       ! <-- Diffusion coefficient [cm2 s-1]
    end forall

    ! Initialize the dry deposition module on this process
    call dep_init( this%n_species, spec_props )

    ! Set up working arrays for this process
    allocate( this%surface_resistance( this%n_species ) )
    allocate( this%loss_rate_constant( this%n_species ) )

  end subroutine initialize

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Run the dry deposition module for a given set of conditions
  subroutine calc_rates( this, column, phlex_state, state_changer, point )

    use module_DRYDEPWESELY,                only : rc, deppart, landusevg
    use monarch_column,                     only : monarch_column_t
    use monarch_state_changer,              only : monarch_state_changer_t
    use monarch_util,                       only : monarch_point_t
    use pmc_phlex_state,                    only : phlex_state_t

    !> Dry deposition object
    class(monarch_dry_deposition_t), intent(inout) :: this
    !> State object for column to calculate dry deposition for
    type(monarch_column_t), intent(in) :: column
    !> Phlex state for the surface layer
    type(phlex_state_t), intent(in) :: phlex_state
    !> State changer object
    type(monarch_state_changer_t), intent(inout) :: state_changer
    !> Grid cell to update
    type(monarch_point_t), intent(in) :: point

    real(kind=mflt) :: part_dep_vel, fog_dep_vel, aero_resis
    logical :: is_wet, is_high_nh3
    integer(kind=mint) :: i_rate

    ! Do nothing if not flagged to calculate dry deposition
    if( .not. this%do_dry_deposition ) return

    ! Set up dry deposition flags
    is_wet = column%relative_humidity__dec( 1 ) > 0.95
    if( this%nh3_phlex_index.gt.0 .and. this%so2_phlex_index.gt.0 ) then
      is_high_nh3 = phlex_state%state_var( this%nh3_phlex_index ) .gt.       &
                    2.0 * phlex_state%state_var( this%so2_phlex_index )
    else
      is_high_nh3 = .false.
    end if

    ! Calculate surface resistances
    call rc(                                                                 &
         this%surface_resistance( : )                                        & ! <-- [out] surface resistances [s m-1]
         ,real(column%skin_temperature__K(), kind=mflt )                     & ! <-- [in] skin temperature [K]
         ,real(column%surface_downward_shortwave_radiation__Wm2(), kind=mflt)& ! <-- [in] shortwave radiation at the surface [W m-2]
         ,real( column%relative_humidity__dec( 1 ) * 100.0, kind=mflt )      & ! <-- [in] relative humidity at the surface [percent]
         ,column%USGS_land_use_type_id( )                                    & ! <-- [in] USGS land-use type id
         ,this%get_season( column )                                          & ! <-- [in] season flag
         ,this%n_species                                                     & ! <-- [in] number of dry-deposition species
         ,is_wet                                                             & ! <-- [in] wetness flag
         ,column%is_raining( 1 )                                             & ! <-- [in] rain flag
         ,is_high_nh3                                                        & ! <-- [in] high nh3 flag
         ,0                                                                  & ! <-- [in] iprt FIXME what is this??? appears unused
         ,this%O3_index                                                      & ! <-- [in] O3 index in dry deposition species
         ,this%SO2_index                                                     & ! <-- [in] SO2 index in dry deposition species
         ,this%NH3_index                                                     & ! <-- [in] NH3 index in dry deposition species
         )

    ! Calculate surface deposition veolicities for aerosols and fog droplets
    ! FIXME The results of this calculation appear to be unused
    if (.false.) then
    call deppart(                                                            &
           real(column%recip_Monin_Obukhov_length__m(), kind=mflt )          & ! <-- [in] reciprocal of the Monin-Obukhov length [m-1]
           ,real(column%friction_velocity__ms(), kind=mflt )                 & ! <-- [in] friction velocity at the surface [m s-1]
           ,real( column%relative_humidity__dec( 1 ) * 100.0, kind=mflt )    & ! <-- [in] relative humidity at the surface [percent]
           ,real( column%cloud_water_mix_rat__kgkg( 1 ) , kind=mflt )        & ! <-- [in] cloud water in the surface layer [kg kg-1]
                                                                               ! FIXME cloud water has a factor of 1/0.622 from
                                                                               ! PREP_DRIVER( ) - see if it should be used here too
           ,column%USGS_land_use_type_id( )                                  & ! <-- [in] USGS land-use type id
           ,part_dep_vel                                                     & ! <-- [out] Fine particle deposition velocity [m s-1]
           ,fog_dep_vel                                                      & ! <-- [out] Fog droplet deposition velocity [m s-1]
           )
    end if

    ! Calculate species-specific deposition velocities as a function of land
    ! use and local meteorology
    call landusevg(                                                          &
            this%loss_rate_constant( : )                                     & ! <-- [out] dry deposition velocity [m s-1]
           ,real(column%friction_velocity__ms(), kind=mflt )                 & ! <-- [in] friction velocity at the surface [m s-1]
           ,real(column%recip_Monin_Obukhov_length__m(), kind=mflt )         & ! <-- [in] reciprocal of the Monin-Obukhov length [m-1]
           ,real(column%roughness_length__m(), kind=mflt )                   & ! <-- [in] roughness length [m]
           ,real( column%layer_thickness__m( 1 ) , kind=mflt )               & ! <-- [in] thickness of the surface layer [m]
           ,part_dep_vel                                                     & ! <-- [out] Fine particle deposition velocity [m s-1]
           ,column%USGS_land_use_type_id( )                                  & ! <-- [in] USGS land-use type id
           ,this%n_species                                                   & ! <-- [in] number of dry-deposition species
           ,this%surface_resistance( : )                                     & ! <-- [out] surface resistances [s m-1]
           ,aero_resis                                                       & ! <-- [out] aerodynamic resistance FIXME units? unused?
           ,0                                                                & ! <-- [in] index for particulate sulfate FIXME unused?
           )

    ! Save the deposition velocities [m s-1] if flagged to do so
    if( this%output_rates ) then
      do i_rate = 1, size( this%loss_rate_constant )
        call state_changer%save_property( this%dry_dep_velocity_output,      & !<-- dry deposition state array
                   point,                                                    & !<-- grid cell indices
                   i_rate,                                                   & !<-- dry deposition rate index
                   real( this%loss_rate_constant( i_rate ), kind=mflt ) )      !<-- dry deposition velocity [m s-1]
      end do
    end if

    ! Convert dry deposition velocity [m s-1] to loss rate constant for
    ! gases [s-1]
    this%loss_rate_constant( : ) =                                           &
           this%loss_rate_constant( : ) /                                    & ! dry deposition velocity [m s-1]
           column%layer_thickness__m( 1 )                                      ! height of the surface layer [m]

  end subroutine calc_rates

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#endif // DO_PARTMC
end module monarch_dry_deposition
