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

!> \page module_aero_rep Abstract aerosol representations
!!
!! MONARCH provides the abstract
!! \c monarch_aerosol_representation::monarch_aerosol_representation_t type to
!! ease the tight coupling between particular aerosol microphysical schemes
!! and other MONARCH modules.
!!
!! The general usage of a \c monarch_aerosol_representation is as follows:
!!
!!  -# An aerosol microphysical scheme is selected during the model
!!     initialization:
!!     \code{.f90}
!!     module solver_grid_component
!!       ...
!!       type(solver_internal_state), pointer :: int_state
!!       type(monarch_aerosol_representation_ptr) :: aero_rep_ptr
!!       ...
!!     contains
!!       ...
!!       subroutine solver_initialize( )
!!         ...
!!         ! Choose the aerosol representation here
!!         aero_rep_ptr%val => monarch_aero_rep_fixed_t( int_state )
!!         ...
!!       end subroutine solver_initialize
!!       ...
!!     end module solver_grid_component
!!   \endcode
!!   This structure allows many options for aerosol representations that are
!!   accessible throughout the model run from `aero_rep_ptr%%val`. Consider the
!!   following possibilities:
!!   \code{.f90}
!!     ! Choose a 2-moment aerosol representation that sets itself up from a
!!     ! configuration text file and allocates memory in the internal state
!!     ! to store aerosol state data.
!!     aero_rep_ptr%val => monarch_aero_rep_two_moment( int_state,           &
!!                                                      "configure.conf" )
!!   \endcode
!!   or
!!   \code{.f90}
!!     ! Choose a single-particle representation that runs on specific processes
!!     ! with an MPI communicator for talking to the MONARCH compute tasks.
!!     aero_rep_ptr%val => monarch_aero_rep_partmc( int_state, MPI_PARTMC_COMM, &
!!                                                  "partmc_config.json" )
!!   \endcode
!!
!!  -# During the model run, the \c monarch_aerosol_representation can be asked
!!     for specific aerosol-related information or to make changes to the
!!     aerosol state. This is the most useful feature of the abstract aerosol
!!     design. The
!!     \c monarch_aerosol_representation_pointer::monarch_aerosol_representation_ptr
!!     you set up in the previous step can be used at any point in the model
!!     run regardless of the micropysical scheme you chose during
!!     initialization.
!!     (See here \ref using_diagnostics "here" for how to setup and use the
!!     \c monarch_util::monarch_point_t indices.)
!!     \code{.f90}
!!     subroutine solver_run( )
!!       ...
!!       real(kind=mdbl) :: aero_conc, emit_rate
!!       type(monarch_point_t) :: indices
!!
!!       ! Get the total aerosol number concentration for a grid cell
!!       aero_conc = aero_rep_ptr%val%num_particles__m3( indices )
!!       ...
!!     end subroutine solver_run
!!     \endcode
!!
!!  -# In practice, specific subsets of the total aerosol population are often
!!     needed to perform model calculations. This complicates the situation
!!     because, for example, you may be intereseted in dust aerosols, or
!!     PM10, or hydrophobic SOA, or black carbon from biomass burning. However,
!!     these will be represented very differently in a mass-only binned
!!     representation than they are in a single-particle representation.
!!
!!     Enter \c monarch_aerosol_representation::monarch_aerosol_group_collection_t.
!!     This abstract type is the final piece in the abstracted aerosol
!!     representation puzzle. Each module that includes a type that extends
!!     the abstract
!!     \c monarch_aerosol_representation::monarch_aerosol_representation_t
!!     type should also include a type that extends the abstract
!!     \c monarch_aerosol_representation::monarch_aerosol_group_collection_t
!!     type. This group collection allows, for example, certain people to work
!!     only with dust aerosols to the complete exclusion of all the other
!!     interesting types of aerosols present in the model. And they can do so
!!     with minimal ties to the particular aerosol microphysical scheme used
!!     in MONARCH.
!!
!!     The steps to use aerosol groups are as follows:
!!
!!      -# Set up the groups you are intereseted in during model
!!         initialization. This will be the only part of the process that
!!         requires some knowledge of the aerosol scheme, because each scheme
!!         will have unique functions for specifying particular subsets of the
!!         total aerosol population.
!!
!!         After your \c monarch_aerosol_representation_ptr object has been
!!         set up, find the groups you will be working with during the model
!!         run:
!!         \code{.f90}
!!         ! A group that will be related to dust aerosols
!!         class(monarch_aerosol_group_collection_t), pointer :: dust_grp
!!
!!         ! Set up a total dust group
!!         associate( aero_rep => aero_rep_ptr%val )
!!         select type( aero_rep )
!!            ! ... if using the fixed mass-based modal/binned model
!!            type is( monarch_aero_rep_fixed_t )
!!              dust_grp => aero_rep%group_by_type( "DUST" )                   !<-- this function is specific to the fixed rep
!!            ! ... if using a 2-moment model
!!            type is( monarch_aero_rep_two_moment_t )
!!              dust_grp => aero_rep%group_by_type( "DUST" )                   !<-- this function is specific to the 2-moment rep
!!            ! ... if using a single particle scheme
!!            type is( monarch_aero_rep_partmc_t )
!!              dust_grp => aero_rep%group_by_source( "DUST" )                 !<-- this function is specific to the PartMC rep
!!            ! Include a default failure to help identify functions
!!            ! that need updated when including a new aerosol representation
!!            ! in the MONARCH
!!            class default
!!              call die_msg( 134259706, "I'm not ready for this yet!!!" )
!!         end select
!!         end associate
!!         \endcode
!!
!!         Now that your group has been set up, you no longer have to care
!!         about the particular aerosol representation of the model:
!!         \code{.f90}
!!           ! Get the total dust aerosol number concentration for a grid cell
!!           aero_conc = aero_rep_ptr%val%num_particles__m3( indices, dust_grp )
!!         \endcode
!!         This call will return the total dust aerosol number concentration
!!         for the grid cell from any of the aerosol representations.
!!
!!         (<em>Coming soon!</em>) If you want to treat subsets of aerosols
!!         in your group according to, for example, their dry-mass radius,
!!         you can use one of the built-in iterators:
!!         \code{.f90}
!!         real(kind=mdbl) :: aero_conc
!!
!!         call dust_grp%iter_by_dry_radius( )
!!         do while( dust_grp%iter_next( ) )
!!           aero_conc = aero_rep_ptr%val%num_particles__m3( indices, dust_grp )
!!           ...
!!         end do
!!         \endcode

!> Abstract type definition for aerosol representations
!! Provides functionality for aerosol-dependent calculations
module monarch_aerosol_representation

  implicit none
  private

  public :: monarch_aerosol_representation_t,                                &
            monarch_aerosol_group_collection_t

  !> Abstract aerosol representation
  !!
  !! Defines functionality provided to aerosol-dependent calculations by any
  !! aerosol representation used by MONARCH.
  type, abstract :: monarch_aerosol_representation_t
  contains
    !> Get a collection of pointers to the aerosol groups in this
    !! representation
    procedure(groups), deferred :: groups
    !> Get the number of aerosol particles [# m-3]
    procedure(num_particles__m3), deferred :: num_particles__m3
    !> Get the dry aerosol mass concentration [kg m-3]
    procedure(dry_mass_conc__kgm3), deferred :: dry_mass_conc__kgm3
    !> Get the aerosol extinction [m-1]
    procedure(extinction__m), deferred :: extinction__m
    !> Determine the number of bytes required to pack the given object
    procedure(pack_size), deferred :: pack_size
    !> Pack the given object to the buffer, advancing position
    procedure(bin_pack), deferred :: bin_pack
    !> Unpack the given object from the buffer, advancing position
    procedure(bin_unpack), deferred :: bin_unpack
    !> Print out the configuration of the aerosol representation
    procedure(do_print), deferred :: print
  end type monarch_aerosol_representation_t

  !> Abstract aerosol group collection
  !!
  !! These can be, for example, bins, modes, single particles, etc. They can
  !! be passed to the functions of \c monarch_aerosol_representation_t
  !! extending types to return aerosol properties for specific groups of
  !! aerosols. The only required functionality for the pointers is related to
  !! iterating through the collection.
  type, abstract :: monarch_aerosol_group_collection_t
  contains
    !> Dupicate the group in its current state
    procedure(duplicate), deferred :: duplicate
    !> Determine the number of bytes required to pack the given object
    procedure(group_pack_size), deferred :: pack_size
    !> Pack the given object to the buffer, advancing position
    procedure(group_bin_pack), deferred :: bin_pack
    !> Unpack the given object from the buffer, advancing position
    procedure(group_bin_unpack), deferred :: bin_unpack
  end type monarch_aerosol_group_collection_t

abstract interface

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

  !> Get a collection of aerosol groups in this representation
  function groups( this )
    import :: monarch_aerosol_representation_t
    import :: monarch_aerosol_group_collection_t

    !> Aerosol group collection
    class(monarch_aerosol_group_collection_t), pointer :: groups
    !> Aerosol representation
    class(monarch_aerosol_representation_t), target, intent(in) :: this

  end function groups

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

  !> Get the number of particles [# m-3]
  !! If a group collection is provided, the number of particles of the
  !! currently selected group is returned
  function num_particles__m3( this, cell, group )
    use monarch_constants,                    only : mdbl
    use monarch_util,                         only : monarch_point_t
    import :: monarch_aerosol_representation_t
    import :: monarch_aerosol_group_collection_t

    !> Number of particles [# m-3]
    real(kind=mdbl) :: num_particles__m3
    !> Aerosol representation
    class(monarch_aerosol_representation_t), intent(in) :: this
    !> Indices for the cell to get the number concentration for
    type(monarch_point_t), intent(in) :: cell
    !> Aerosol group collection
    class(monarch_aerosol_group_collection_t), target, optional,             &
        intent(in) :: group

  end function num_particles__m3

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

  !> Get the dry aerosol mass concentration [kg m-3]
  !! If a group collection is provided, the mass concentration of the
  !! currently selected group is returned
  function dry_mass_conc__kgm3( this, cell, group )
    use monarch_constants,                    only : mdbl
    use monarch_util,                         only : monarch_point_t
    import :: monarch_aerosol_representation_t
    import :: monarch_aerosol_group_collection_t

    !> Dry mass concentration [kg m-3]
    real(kind=mdbl) :: dry_mass_conc__kgm3
    !> Aerosol representation
    class(monarch_aerosol_representation_t), intent(in) :: this
    !> Indices for the cell to get the mass concentration for
    type(monarch_point_t), intent(in) :: cell
    !> Aerosol group collection
    class(monarch_aerosol_group_collection_t), target, optional,             &
        intent(in) :: group

  end function dry_mass_conc__kgm3

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

  !> Get the aerosol extinction [m-1]
  !! If a group collection is provided, the extinction for the currently
  !! selected group is returned, otherwise return the total aerosol extinction
  function extinction__m( this, cell, rel_humid, group )
    use monarch_constants,                    only : mdbl
    use monarch_util,                         only : monarch_point_t
    import :: monarch_aerosol_representation_t
    import :: monarch_aerosol_group_collection_t

    !> Aerosol extinction [m-1]
    real(kind=mdbl) :: extinction__m
    !> Aerosol representation
    class(monarch_aerosol_representation_t), intent(in) :: this
    !> Indices for the cell to get the aerosol extinction for
    type(monarch_point_t), intent(in) :: cell
    !> Relative humidity as a decimal [0...1]
    real(kind=mdbl), intent(in) :: rel_humid
    !> Aerosol group collection
    class(monarch_aerosol_group_collection_t), target, optional,             &
        intent(in) :: group

  end function extinction__m

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

  !> Print out the configuration of the aerosol representation
  subroutine do_print( this, file_unit )
    use monarch_constants,                     only : mint
    import :: monarch_aerosol_representation_t

    !> Aerosol representation
    class(monarch_aerosol_representation_t), intent(in) :: this
    !> File unit
    integer(kind=mint), intent(in) :: file_unit

  end subroutine do_print

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

  !> Determine the number of bytes required to pack the given object
  integer(kind=mint) function pack_size( this, comm )
    use monarch_constants,                  only : mint
    import :: monarch_aerosol_representation_t

    !> Output diagnostics
    class(monarch_aerosol_representation_t), intent(in) :: this
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

  end function pack_size

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

  !> Pack the given object to the buffer, advancing position
  subroutine bin_pack( this, buffer, pos, comm )
    use monarch_constants,                  only : mint
    import :: monarch_aerosol_representation_t

    !> Output diagnostics
    class(monarch_aerosol_representation_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

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

  !> Unpack the given object from the buffer, advancing position
  subroutine bin_unpack( this, buffer, pos, comm )
    use monarch_constants,                  only : mint
    import :: monarch_aerosol_representation_t

    !> Output diagnostics
    class(monarch_aerosol_representation_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

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

  !> Duplicate the aerosol group in its current state
  function duplicate( this ) result( new_obj )
    import :: monarch_aerosol_group_collection_t

    !> New aerosol group collection
    class(monarch_aerosol_group_collection_t), pointer :: new_obj
    !> Aerosol group collection
    class(monarch_aerosol_group_collection_t), intent(in) :: this

  end function duplicate

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

  !> Determine the number of bytes required to pack the given object
  integer(kind=mint) function group_pack_size( this, comm )
    use monarch_constants,                  only : mint
    import :: monarch_aerosol_group_collection_t

    !> Output diagnostics
    class(monarch_aerosol_group_collection_t), intent(in) :: this
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

  end function group_pack_size

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

  !> Pack the given object to the buffer, advancing position
  subroutine group_bin_pack( this, buffer, pos, comm )
    use monarch_constants,                  only : mint
    import :: monarch_aerosol_group_collection_t

    !> Output diagnostics
    class(monarch_aerosol_group_collection_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 group_bin_pack

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

  !> Unpack the given object from the buffer, advancing position
  subroutine group_bin_unpack( this, buffer, pos, comm )
    use monarch_constants,                  only : mint
    import :: monarch_aerosol_group_collection_t

    !> Output diagnostics
    class(monarch_aerosol_group_collection_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 group_bin_unpack

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

end module monarch_aerosol_representation
