! 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 abstract monarch_emission_model_t object and related functions

!> Abstract type definition for emission model drivers
module monarch_emission_model
#ifdef DO_PARTMC

  use monarch_constants,                    only : mint
  use pmc_phlex_core
  use pmc_rxn_emission,                     only :                           &
                                            rxn_update_data_emission_rate_t

  implicit none
  private

  public :: monarch_emission_model_t

  !> Abstract emission model driver
  !!
  !! Definition of functionality for emission modle drivers to calculate
  !! emission rates for chemical species
  type, abstract :: monarch_emission_model_t
    !> Phlex-chem core
    type(phlex_core_t), pointer :: phlex_core
    !> Number of emission reaction that this model will update
    integer(kind=mint) :: n_rxns
    !> Starting rxn id in PartMC-phlex
    integer(kind=mint) :: starting_rxn_id
    !> Emissions rate update nugget
    type(rxn_update_data_emission_rate_t) :: rate_update
  contains
    !> Recalculate the emission rates for this model
    procedure(calc_rates), deferred :: calc_rates
    !> Update the emissions rates for this emission model for one grid cell
    procedure :: update_rxn_rates
    !> Get the current emission rate for a particular rxn and grid cell
    !! (PRIVATE)
    procedure(get_rxn_rate), deferred :: get_rxn_rate
    !> Initialize the model driver on each process
    procedure :: initialize
    !> Initialize the model (PRIVATE)
    procedure(initialize_model), deferred :: initialize_model
    !> Determine the number of bytes required to pack the given object
    procedure(pack_size), deferred :: pack_size
    !> Packs the given object in the buffer, advancing position
    procedure(bin_pack), deferred :: bin_pack
    !> Unpacks the given object from the buffer, advancing position
    procedure(bin_unpack), deferred :: bin_unpack
    !> Print the emission model data
    procedure(do_print), deferred :: print
  end type monarch_emission_model_t

abstract interface
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Recalculate the emission rates for this model
  subroutine calc_rates( this, diag )
    use monarch_diagnostics
    import :: monarch_emission_model_t

    !> Emission model driver
    class(monarch_emission_model_t), intent(inout) :: this
    !> Diagnostics object attached to current internal state
    type(monarch_diagnostics_t), intent(in) ::diag

  end subroutine calc_rates

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

  !> Get the current emission rate for a particular rxn and grid cell
  !! (PRIVATE)
  pure function get_rxn_rate( this, indices, rxn_id ) result( rate )
    use monarch_constants
    use monarch_util,                         only : monarch_point_t
    import :: monarch_emission_model_t

    !> Emission rate [ppm s-1] or [ug m-3 s-1]
    real(kind=mdbl) :: rate
    !> Emission model driver
    class(monarch_emission_model_t), intent(in) :: this
    !> Indices for the grid cell
    type(monarch_point_t), intent(in) :: indices
    !> Internal index for the emission reaction
    integer(kind=mint) , intent(in)  :: rxn_id

  end function get_rxn_rate

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

  !> Initialize the model (PRIVATE)
  subroutine initialize_model( this, diag, species_map )
    use monarch_diagnostics
    use monarch_species_map
    import :: monarch_emission_model_t

    !> Emission model driver
    class(monarch_emission_model_t), intent(inout) :: this
    !> Diagnostics object
    type(monarch_diagnostics_t), intent(in) :: diag
    !> PartMC-Phlex <-> MONARCH species map
    type(monarch_species_map_t), target, intent(in) :: species_map

  end subroutine initialize_model

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

  !> Determine the number of bytes required to pack the given object
  function pack_size( this, comm )
    use monarch_constants
    import :: monarch_emission_model_t

    !> Number of bytes need to pack the object
    integer(kind=mint) :: pack_size
    !> Emission model driver
    class(monarch_emission_model_t), intent(in) :: this
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

  end function pack_size

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

  !> Packs the given object in the buffer, advancing position
  subroutine bin_pack( this, buffer, pos, comm )
    use monarch_constants
    import :: monarch_emission_model_t

    !> Emission model driver
    class(monarch_emission_model_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

  end subroutine bin_pack

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

  !> Unpacks the given object from the buffer, advancing position
  subroutine bin_unpack( this, buffer, pos, comm )
    use monarch_constants
    import :: monarch_emission_model_t

    !> Emission model driver
    class(monarch_emission_model_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

  end subroutine bin_unpack

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

  !> Print the emission model data
  subroutine do_print( this, file_unit )
    use monarch_constants
    import :: monarch_emission_model_t

    !> Emission model driver
    class(monarch_emission_model_t), intent(in) :: this
    !> File unit to write to
    integer(kind=mint), intent(in) :: file_unit

  end subroutine do_print

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
end interface

contains

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

  !> Update the emissions rates for this emission model for one grid cell
  subroutine update_rxn_rates( this, indices )

    use monarch_util,                       only : monarch_point_t

    !> Emission model driver
    class(monarch_emission_model_t), intent(inout) :: this
    !> Grid cell indices
    type(monarch_point_t), intent(in) :: indices

    integer(kind=mint) :: i_rxn

    ! Update each emission reaction
    do i_rxn = 1, this%n_rxns
      call this%rate_update%set_rate( this%starting_rxn_id + i_rxn - 1,      &
                this%get_rxn_rate( indices, i_rxn ) )
      call this%phlex_core%update_rxn_data( this%rate_update )
    end do

  end subroutine update_rxn_rates

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

  !> Initialize the model driver on each process
  subroutine initialize( this, diag, species_map )

    use monarch_diagnostics,                only : monarch_diagnostics_t
    use monarch_species_map,                only : monarch_species_map_t
    use pmc_rxn_factory,                    only : rxn_factory_t

    !> Emission model driver
    class(monarch_emission_model_t), intent(inout) :: this
    !> Diagnostics object
    class(monarch_diagnostics_t), intent(in) :: diag
    !> PartMC-Phlex <-> MONARCH species map
    type(monarch_species_map_t), target, intent(in) :: species_map

    type(rxn_factory_t) :: rxn_factory

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

    ! Initialize the emissions model
    call this%initialize_model( diag, species_map )

  end subroutine initialize

#endif
end module monarch_emission_model
