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

!> Interface for the Fast-J photolysis model and PartMC-phlex
module monarch_fastJ_interface
! 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 photolysis rxns for Fast-J updates
!( This can be any integer, but must be unique to Fast-J updates )
#define PMC_FASTJ_PREFIX 90210

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! Ids for the version of Fast-J to be used !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

! No Fast-J connection
#define FASTJ_VERSION_NONE 0

! Original Fast-J updated for use with WRF-chem
! * Fast, J.D., W.I. Gustafson Jr., R.C. Easter, R.A. Zaveri, J.C. Barnard,
!   E.G. Chapman, G.A. Grell, and S.E. Peckham( 2005 ), Evolution of ozone,
!   particulates, and aerosol direct radiative forcing in the vicinity of
!   Houston using a fully-coupled meteorology-chemistry-aerosol model,
!   Submitted to J. Geophys. Res.
! and further modified to work with CB05 in MONARCH
#define FASTJ_VERSION_2005_CB05 1

  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_photolysis,                   only :                           &
                                            rxn_update_data_photolysis_rate_t

  implicit none
  private

  public :: monarch_fastJ_interface_t

  !> PartMC <-> Fast-J interface
  !!
  !! Contains all data required initialize and run Fast-J, and update
  !! PartMC-phlex photolysis reactions accordingly
  type monarch_fastJ_interface_t
  private
    !> Phlex-chem core
    type(phlex_core_t), pointer :: phlex_core => null()
    !> Version of FastJ to connect to( if any )
    integer(kind=mint) :: version
    !> Number of vertical layers in MONARCH
    integer(kind=mint) :: n_layers
    !> Number of vertical layers in FastJ
    integer(kind=mint) :: n_photo_layers
    !> PartMC <-> Fast-J photolysis rate map
    integer(kind=mint), allocatable :: map_fastJ_id( : )
    !> Photolysis rate update nugget
    type(rxn_update_data_photolysis_rate_t) :: rate_update
    !> Property set
    type(property_t), pointer :: property_set => null()
    !> Pressure at layer intersections [mb]
    !! Fast-J assumes the top of the highest layer is 0 Pa. Thus, this array
    !! is of length n_layers( excludes top of highest layer ) .
    real(kind=mflt), allocatable :: btw_layer_pressure__mb( : )
    !> Temperature [K]
    real(kind=mflt), allocatable :: temperature( : )
    !> Cloud optical depth [unitless]
    real(kind=mflt), allocatable :: cloud_optical_depth( : )
    !> Ozone volume mixing ratios by layer [m3 m-3]
    real(kind=mflt), allocatable :: ozone_mix_ratio( : )
    !> Photolysis rates for one vertical column [s-1]
    real(kind=mdbl), allocatable :: photo_rates( :,: )
    !> Solar zenith angle [decimal degrees]
    real(kind=mflt) :: solar_zenith_angle
    !> Flag indicating to output emission rates
    logical :: output_rates = .false.
    !> Emission reaction labels for output
    type(string_t), allocatable :: photo_rxn_labels( : )
    !> State array for emission rates
    type(monarch_state_array_ptr) :: photo_rates_output
  contains
    !> Run Fast-J to calculate photolysis rates for one vertical column
    procedure :: calc_rates
    !> Update photolysis rates in PartMC-phlex for one grid cell
    procedure :: update_rxn_rates
    !> 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 interface data
    procedure :: print => do_print
    !> Load the interface data from a configuration file
    procedure, private :: load
    !> Initialize the Fast-J model
    procedure, private :: initialize
    !> Create a map between Fast-J photolysis rates and PartMC-phlex reactions
    procedure, private :: create_map
    !> Set up output arrays
    procedure, private :: setup_output_arrays
    !> Finalize the interface
    final :: finalize
  end type monarch_fastJ_interface_t

  !> PartMC <-> Fast-J interface constructor
  interface monarch_fastJ_interface_t
    procedure :: constructor
  end interface monarch_fastJ_interface_t

contains

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

  !> Create and initialize a new monarch_fastJ_interface_t object
  !!
  !! Create a new monarch_fastJ_interface_t object at the beginning of the
  !! model run for each node. This constructor should be called with an
  !! already-initialized phlex_core_t object. It can be called on the primary
  !! node with the path to the interface configuration file before passing the
  !! interface to the other nodes, or on the other nodes before unpacking the
  !! interface from an MPI message.
  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 : die_msg, assert_msg,      &
                                                   monarch_dimensions_t

    !> A new PMC <-> Fast-J interface
    type(monarch_fastJ_interface_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 Fast-J configuration file
    !! Should only be included in calls from the primary process
    character(len=:), allocatable, optional, intent(in) :: config_file

    ! model domain dimensions
    type(monarch_dimensions_t) :: model_domain

    ! Create a new interface instance
    allocate( new_obj )

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

    ! Assume no connection to Fast-J until one is loaded
    new_obj%version = FASTJ_VERSION_NONE

    ! Return if not loading Fast-J configuration
    if( .not.present( config_file ) ) then
      new_obj%n_layers = 0
      allocate( new_obj%map_fastJ_id( 0 ) )
      return
    end if

    ! Set the number of vertical layers
    if( present( diag ) ) then
      model_domain = diag%total_domain_dimensions( )
      new_obj%n_layers = model_domain%upper%vertical_idx( )
      new_obj%n_photo_layers = new_obj%n_layers - 2
    else
      call die_msg( 192778295,                                               &
                   "Missing diagnostics object for MONARCH for FastJ" )
    endif

    ! Load the interface data
    call new_obj%load( config_file )

    call assert_msg( 324147640, present( state_changer ),                    &
                     "Missing state changer in FastJ interface set up" )

    ! Initialize Fast-J
    call new_obj%initialize( )

    ! Map Fast-J photolysis rates to PartMC-phlex reactions
    call new_obj%create_map( )

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

  end function constructor

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

  !> Run Fast-J to calculate photolysis rates for a given set of conditions,
  !! for one vertical column
  subroutine calc_rates( this, column, state_changer )

    use module_phot_fastj,	   only : v2005cb05_calc  => fastj,          &
                                          v2005cb05_set_mie => set_common_mie
    use module_fastj_data,         only : v2005cb05_jppj  => jppjmax,        &
                                          v2005cb05_nb    => nb,             &
                                          v2005cb05_nc    => nc
    use module_data_mosaic_other,  only : v2005cb05_kmaxd => kmaxd
    use monarch_column,            only : monarch_column_t
    use monarch_state_changer,     only : monarch_state_changer_t
    use monarch_util,              only : monarch_point_t

    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(inout) :: this
    !> Column state
    type(monarch_column_t), intent(inout) :: column
    !> State changer
    type(monarch_state_changer_t), intent(inout) :: state_changer

    integer, parameter :: nspint = 4
    real, dimension( nspint, v2005cb05_kmaxd+1 ) :: sizeaer, extaer, waer,   &
                                                  gaer, tauaer, l2, l3, l4,  &
                                                  l5, l6, l7
    type(monarch_point_t) :: cell
    integer(kind=mint) :: i_layer, i_photo_rate, julian_day
    real(kind=mflt) :: surface_pressure__mb, surface_albedo, UTC__hr
    real(kind=mflt) :: aerosol_optical_depth

    ! Skip the calculation if no photolysis reactions were found
    if( size( this%map_fastJ_id ) .eq. 0 ) return

    !!!!!!!!!!!!!!!!
    !! v2005-CB05 !!
    !!!!!!!!!!!!!!!!

    if( this%version.eq.FASTJ_VERSION_2005_CB05 ) then

      !> \bug Determine what pressures should be send to Fast-J (interfacial or
      !!      mid-layer; hydro- or nonhydrostatic; etc.). During evaluation of
      !!      the PartMC<->Fast-J interface, it was found that Fast-J
      !!      photolysis rates depend strongly on the pressure of at the top-
      !!      most layer. The exact meaning of the various parameters related
      !!      to the number of vertical layers (NB, jpnl, lpar, etc.) should
      !!      be reviewed and documented, the correct pressures chosen, and
      !!      O3 and AOD from the model state used, if appropriate.

      ! Update Fast-J specific arrays
      this%btw_layer_pressure__mb(        1:this%n_photo_layers + 1 ) =      &
          column%btw_layer_pressure__Pa(  1:this%n_photo_layers + 1 ) * 0.01
      this%btw_layer_pressure__mb(          this%n_photo_layers + 2 ) = 0.0
      this%temperature(                   1:this%n_photo_layers     ) =      &
          column%temperature__K(          1:this%n_photo_layers     )
      this%temperature(                     this%n_photo_layers + 1 ) = 0.0
      forall( i_layer = 1 : this%n_photo_layers )
        this%cloud_optical_depth(           i_layer ) =                      &
            column%cloud_optical_thickness( i_layer )
      end forall
      this%cloud_optical_depth(             this%n_photo_layers     ) = 0.0
      this%ozone_mix_ratio(     : ) = 0.0

      ! Update  FastJ grid parameters
      v2005cb05_nb = this%n_photo_layers + 1
      v2005cb05_nc = v2005cb05_nb * 2

      ! Get some FastJ input conditions
      surface_pressure__mb  = real( column%surface_pressure__Pa( ) * 0.01 )
      !> \todo send Fast-J surface albedos from model state instead of a
      !!       constant
      surface_albedo        = 0.055 ! real( column%surface_albedo( ) )
      julian_day            = ceiling( column%julian_day )
      UTC__hr               = real( column%mid_time_step_UTC__s / 3600.0 )
      !> \todo send Fast-J calculated aerosol optical depths instead of a
      !!       constant
      aerosol_optical_depth = 0.05 ! real( column%aerosol_optical_depth( ) )

      ! Initialize all photo rates
      this%photo_rates( :,: ) = 0.0

      ! Mie calculations are not currently used. This function sets
      ! Mie-related arrays to zero.
      !> \todo Determine whether and how to use the Fast-J Mie calculations
      call v2005cb05_set_mie( this%n_photo_layers, sizeaer, extaer, waer,    &
                              gaer, tauaer, l2, l3, l4, l5, l6, l7 )

      ! Run Fast-J
      call v2005cb05_calc(                                                   &
           1,                                                                & ! [in] not used
           1,                                                                & ! [in] not used
           column%latitude__deg( ),                                          & ! [in] Column latitude [decimal degrees]
           column%longitude__deg( ),                                         & ! [in] Column longitude [decimal degrees]
           surface_pressure__mb,                                             & ! [in] Surface pressure [mb]
           surface_albedo,                                                   & ! [in] Surface albedo [unitless fraction]
           julian_day,                                                       & ! [in] Column Julian day [integer]
           UTC__hr,                                                          & ! [in] UTC [hours]
           this%btw_layer_pressure__mb,                                      & ! [in] Pressure at layer intersections [mb]
           this%temperature,                                                 & ! [in] Mid-layer temperatures [K]
           this%cloud_optical_depth,                                         & ! [in] Cloud optical depths [unitless]
           this%ozone_mix_ratio,                                             & ! [in] Ozone volume mixing ratio [m3 m-3]
           0,                                                                & ! [in] Layer above which to use climatological
                                                                               !      values for O3 mixing ratios
           aerosol_optical_depth,                                            & ! [in] Aerosol optical thickness [unitless]
           this%photo_rates,                                                 & ! [out] Photolysis rates [s-1]( 1:n_layers,1:jppj )
           this%solar_zenith_angle,                                          & ! [out] Solar zenith angle [decimal degrees]
           this%n_photo_layers,                                              & ! [in] Number of layers
           column%num_tropospheric_layers,                                   & ! [in] Top layer to do chemistry in
           sizeaer, extaer, waer, gaer,                                      & ! [who knows] Mie-related arrays
           tauaer, l2, l3, l4, l5, l6, l7,                                   & !     that are all set to zero
           v2005cb05_jppj                                                    & ! [in] Number of Fast-J photolysis rates
           )

    endif

    ! Save the photolysis rates if flagged to do so
    if( this%output_rates ) then
      cell = column%indices
      do i_layer = 1, this%n_photo_layers
        call cell%set_vertical_idx( i_layer )
        do i_photo_rate = 1, size( this%map_fastJ_id )
          call state_changer%save_property( this%photo_rates_output,         & !<-- photolysis rate state array
                     cell,                                                   & !<-- grid cell indices
                     i_photo_rate,                                           & !<-- photolysis rate index
                     real( this%photo_rates( i_layer,                        & !<-- photolysis rate [s-1]
                            this%map_fastJ_id( i_photo_rate ) ), kind=mflt ) )
        end do
      end do
    end if

  end subroutine calc_rates

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

  !> Update reaction rates in PartMC-phlex for a single grid cell in the last
  !! column calculated by Fast-J
  subroutine update_rxn_rates( this, indices )

    use monarch_util,                       only : monarch_point_t

    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(inout) :: this
    !> Indices containing layer to update photolysis rates for
    type(monarch_point_t), intent(in) :: indices

    real(kind=mdbl) :: new_rate
    integer(kind=mint) :: i_photo_rate

    ! Only update rates if there is a Fast-J connection
    if( this%version.eq.FASTJ_VERSION_NONE ) return

    ! Update each photolysis reaction
    do i_photo_rate = 1, size( this%map_fastJ_id )
      if( indices%vertical_idx( ) .gt. this%n_photo_layers ) then
        new_rate = 0.0
      else
        new_rate = this%photo_rates( indices%vertical_idx( ),                &
                                     this%map_fastJ_id( i_photo_rate ) )
      end if
      call this%rate_update%set_rate( PMC_FASTJ_PREFIX + i_photo_rate,       &
                   new_rate )
      call this%phlex_core%update_rxn_data( this%rate_update )
    end do

  end subroutine update_rxn_rates

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

  !> Determine the size of a binary required to pack the PMC <-> Fast-J
  !! interface data
  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_integer_array


    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(in) :: this
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

    pack_size = mnrc_mpi_pack_size_integer(    this%version       , comm ) + &
                mnrc_mpi_pack_size_integer(    this%n_layers      , comm ) + &
                mnrc_mpi_pack_size_integer(    this%n_photo_layers, comm ) + &
                mnrc_mpi_pack_size_logical(       this%output_rates,comm ) + &
                mnrc_mpi_pack_size_integer_array( this%map_fastJ_id,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_integer_array
    use monarch_util,                only : assert
#endif

    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(in) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer, intent(inout) :: pos
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

#ifdef MONARCH_USE_MPI
    integer :: prev_position

    prev_position = pos
    call mnrc_mpi_pack_integer(       buffer, pos, this%version       , comm )
    call mnrc_mpi_pack_integer(       buffer, pos, this%n_layers      , comm )
    call mnrc_mpi_pack_integer(       buffer, pos, this%n_photo_layers, comm )
    call mnrc_mpi_pack_logical(       buffer, pos, this%output_rates  , comm )
    call mnrc_mpi_pack_integer_array( buffer, pos, this%map_fastJ_id  , comm )
    call assert( 802633674, &
         pos - prev_position <= this%pack_size( comm ) )

#endif

  end subroutine bin_pack

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

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

    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(inout) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer, 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 :: prev_position

    if( allocated( this%map_fastJ_id ) ) deallocate( this%map_fastJ_id )

    prev_position = pos
    call mnrc_mpi_unpack_integer(       buffer, pos, this%version     , comm )
    call mnrc_mpi_unpack_integer(       buffer, pos, this%n_layers    , comm )
    call mnrc_mpi_unpack_integer(       buffer, pos, this%n_photo_layers,comm)
    call mnrc_mpi_unpack_logical(       buffer, pos, this%output_rates, comm )
    call mnrc_mpi_unpack_integer_array( buffer, pos, this%map_fastJ_id, comm )
    call assert( 600342279, &
         pos - prev_position <= this%pack_size( comm ) )

    ! Initialize Fast-J on this process
    call this%initialize( )

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

  end subroutine bin_unpack

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

  !> Load the PMC <-> Fast-J interface configuration data
  subroutine load( this, config_file )

    use monarch_util,                       only : assert_msg, die_msg
#ifdef PMC_USE_JSON
    use json_module,                        only : json_core, json_file,     &
                                                   json_value, json_ck
#endif

    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(inout) :: this
    !> Path to the PMC <-> Fast-J 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
    character(len=:), allocatable :: prop_key, str_val, owner_name
    integer(kind=mint) :: var_type
    logical :: found

    ! 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( 709113499, allocated( config_file ),                    &
              "Received non-allocated string for file path" )
    call assert_msg( 653180474, trim( config_file ) .ne."",                  &
              "Received empty string for file path" )
    inquire( file=config_file, exist=found )
    call assert_msg( 986776736, found, "Cannot find file: "//                &
              config_file )
    call j_file%load_file( filename = config_file )

    ! Find the interface data
    call j_file%get( 'fastJ-data( 1 ) ', j_obj )

    ! Load the data to the property set
    do while( associated( j_obj ) )

      owner_name = "Fast-J interface"
      call this%property_set%load( json, j_obj, .true., owner_name )
      j_next => j_obj
      call json%get_next( j_next, j_obj )

    end do

    ! Get the version of Fast-J to use
    prop_key = "version"
    call assert_msg( 882654773,                                              &
            this%property_set%get_string( prop_key, str_val ),               &
            "Missing version for Fast-J" )

    ! Initialize the requested version of Fast-J
    if( trim( str_val ) .eq."2005-CB05" ) then
      this%version = FASTJ_VERSION_2005_CB05
    else
      call die_msg( 360202208, "Invalid version of Fast-J requested - "//    &
            trim( str_val ) )
    end if

    ! Look for a flag to output emission rates
    prop_key = "output rates"
    if( this%property_set%get_string( prop_key, str_val ) ) then
      if( trim( str_val ) .eq. "ALL" ) then
        this%output_rates = .true.
      else if( trim( str_val ) .eq. "NONE" ) then
        this%output_rates = .false.
      else
        call die_msg( 353662776, "Unknown output flag in FastJ setup: "//    &
                                 trim( str_val ) )
      end if
    end if

    ! Clean up the json objects
    call j_file%destroy( )
    call json%destroy( )
    deallocate( json )
#else
    call die_msg( 106868795, "No JSON support for input Fast-J input files." )
#endif
  end subroutine load

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

  !> Initialize the Fast-J model
  subroutine initialize( this )

    use module_fastj_data,             only : v2005cb05_jppj  => jppjmax
    use monarch_util,                  only : assert_msg, die_msg
    use pmc_rxn_factory,               only : rxn_factory_t

    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(inout) :: this

    type(rxn_factory_t) :: rxn_factory

    ! Verify the phlex core is initialized
    call assert_msg( 430640569, this%phlex_core%is_initialized( ),           &
                    "Trying to initialize a PartMC <-> Fast-J interface "//  &
                    "with an uninitialized phlex_core_t object." )

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

    if( this%version.eq.FASTJ_VERSION_NONE ) return

    ! Allocate space for Fast-J related arrays
    allocate( this%btw_layer_pressure__mb( this%n_photo_layers + 2 ) )
    allocate( this%temperature(            this%n_photo_layers + 1 ) )
    allocate( this%cloud_optical_depth(    this%n_photo_layers + 1 ) )
    allocate( this%ozone_mix_ratio(        this%n_photo_layers + 1 ) )

    ! Set up a photo rates array
    if( this%version.eq.FASTJ_VERSION_2005_CB05 ) then
      allocate( this%photo_rates( this%n_photo_layers, v2005cb05_jppj )  )
    else
      call die_msg( 656927710, "Unknown FastJ version" )
    endif

  end subroutine initialize

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

  !> Create the map between PMC and Fast-J photolysis rates
  subroutine create_map( this )

    use module_fastj_data,             only : J_LABEL_2005_CB05 => jlabel,   &
                                              NUM_J_2005_CB05   => jppjmax
    use monarch_util,                  only : assert, die_msg
    use pmc_mechanism_data,            only : mechanism_data_t
    use pmc_rxn_data,                  only : rxn_data_t
    use pmc_rxn_photolysis,            only : rxn_photolysis_t

    !> PMC <-> Fast-J Interface
    class(monarch_fastJ_interface_t), intent(inout) :: this

    integer(kind=mint) :: i_rxn, i_rate, n_rate, i_fastJ_rate
    type(mechanism_data_t), pointer :: mechanism
    class(rxn_data_t), pointer :: rxn
    character(len=:), allocatable :: key, str_val

    !> \todo Allow MONARCH to access more than one PartMC mechanism at a time
    mechanism => this%phlex_core%mechanism( 1 ) %val

    ! label for Fast-J photolysis rates in PMC photolysis reactions
    key = "Fast-J id"

    ! Count the number of photolysis rates with Fast-J rate labels
    n_rate = 0
    do i_rxn = 1, mechanism%size( )
      rxn => mechanism%get_rxn( i_rxn )
      select type( rxn_photo => rxn )
        class is( rxn_photolysis_t )
          if( rxn%property_set%get_string( key, str_val ) ) then
            n_rate = n_rate + 1
          end if
      end select
    end do

    ! Allocate space for the Fast-J map
    allocate( this%map_fastJ_id( n_rate ) )

    ! Set the PMC and Fast-J ids
    i_rate = 1
    do i_rxn = 1, mechanism%size( )
      rxn => mechanism%get_rxn( i_rxn )
      select type( rxn_photo => rxn )
        class is( rxn_photolysis_t )
          if( rxn%property_set%get_string( key, str_val ) ) then

            ! Find the photolysis rate by label in Fast-J
            if( this%version.eq.FASTJ_VERSION_2005_CB05 ) then
              do i_fastJ_rate = 1, NUM_J_2005_CB05
                if( trim( str_val ) .eq. &
                    trim( J_LABEL_2005_CB05( i_fastJ_rate ) )  ) exit
                if( i_fastJ_rate .eq. NUM_J_2005_CB05 ) then
                  call die_msg( 392373886, "Could not find Fast-J "//        &
                               "photolysis rate '"//trim( str_val ) )
                end if
              end do
            else
              call die_msg( 712810589, "Internal error." )
            end if

            ! Save the Fast-J id and set the PartMC-phlex reaction id
            this%map_fastJ_id( i_rate ) = i_fastJ_rate
            call rxn_photo%set_photo_id( PMC_FASTJ_PREFIX + i_rate )

            i_rate = i_rate + 1
          end if
      end select
    end do

    ! Make sure nothing strange happened
    call assert( 410333867, n_rate .eq. i_rate - 1 )

  end subroutine create_map

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

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

    use module_fastj_data,             only : J_LABEL_2005_CB05 => jlabel
    use monarch_state_changer,         only : monarch_state_changer_t
    use monarch_util,                  only : to_string

    !> PartMC <-> Fast-J interface
    class(monarch_fastJ_interface_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 photolysis rates
    if( size( this%map_fastJ_id ) .eq. 0 ) this%output_rates = .false.
    if( this%output_rates ) then
      allocate( this%photo_rxn_labels( size( this%map_fastJ_id ) ) )
      do i_rxn = 1, size( this%photo_rxn_labels )
        rxn_id = PMC_FASTJ_PREFIX + i_rxn
        this%photo_rxn_labels( i_rxn )%string = to_string( i_rxn )//"-"//    &
                       trim( J_LABEL_2005_CB05( this%map_fastJ_id( i_rxn ) ) )
      end do
      this%photo_rates_output = state_changer%register_per_cell_real(        &
                                              "photolysis_rates",            & !<-- unique name for the array
                                              .true.,                        & !<-- output to history file
                                              size( this%photo_rxn_labels ), & !<-- number of array elements
                                              this%photo_rxn_labels )          !<-- label for each element
      call this%photo_rates_output%set_netcdf_long_name( "Photolysis rates" )
      call this%photo_rates_output%set_netcdf_units( "s-1" )
    end if

  end subroutine setup_output_arrays

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

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

    use module_fastj_data,             only : J_LABEL_2005_CB05 => jlabel
    use monarch_util,                  only : to_string

    !> PartMC <-> Fast-J interface
    class(monarch_fastJ_interface_t), intent(in) :: this
    !> File unit to write to
    integer(kind=mint), intent(in) :: file_unit

    integer(kind=mint) :: i_rxn, i_photo_id
    character(len=*), parameter :: fmt_blank_line = "('')"
    character(len=*), parameter :: fmt1 = "(' | ',A15,' | ',A12,' | ',A20"// &
                                          ",' |')"
    character(len=*), parameter :: fmt2 = "(' | ',I15,' | ',I12,' | ',A20"// &
                                          ",' |')"

    write(file_unit,fmt_blank_line)
    write(file_unit,*) "*******************"
    write(file_unit,*) "** Fast-J Driver **"
    write(file_unit,*) "*******************"
    write(file_unit,fmt_blank_line)
    if( this%version.eq.FASTJ_VERSION_NONE ) then
      write(file_unit,*) " -- Fast-J module loaded --"
    else if( this%version.eq.FASTJ_VERSION_2005_CB05 ) then
      write(file_unit,*) " -- Fast-J 2005 mod CB05 --"
      write(file_unit,*) "Number of vertical layers:      ", this%n_layers
      write(file_unit,*) "Number of layers for photolysis:",                 &
                         this%n_photo_layers
      write(file_unit,fmt_blank_line)
      write(file_unit,*) "PartMC <-> Fast-J rxn map"
      write(file_unit,fmt1) "PartMC photo id", "Fast-J index", "Label"
      do i_rxn = 1, size( this%map_fastJ_id )
        i_photo_id = PMC_FASTJ_PREFIX+i_rxn
        write(file_unit,fmt2) i_photo_id, this%map_fastJ_id( i_rxn ),        &
                         J_LABEL_2005_CB05( this%map_fastJ_id( i_rxn ) )
      end do
    endif
    write(file_unit,fmt_blank_line)
    if( this%output_rates ) then
      write(file_unit,*) "Output rates to netCDF history file"
    else
      write(file_unit,*) "No Output of rates to netCDF history file"
    end if
    write(file_unit,fmt_blank_line)
    write(file_unit,*) "***********************"
    write(file_unit,*) "** End Fast-J Driver **"
    write(file_unit,*) "***********************"
    write(file_unit,fmt_blank_line)

  end subroutine do_print

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

  !> Finalize the interface
  elemental subroutine finalize( this )

    !> PartMC <-> Fast-J interface
    type(monarch_fastJ_interface_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%map_fastJ_id            ) ) &
         deallocate( this%map_fastJ_id            )
    if( associated( this%property_set           ) ) &
         deallocate( this%property_set            )
    if( allocated( this%btw_layer_pressure__mb  ) ) &
         deallocate( this%btw_layer_pressure__mb  )
    if( allocated( this%temperature             ) ) &
         deallocate( this%temperature             )
    if( allocated( this%cloud_optical_depth     ) ) &
         deallocate( this%cloud_optical_depth     )
    if( allocated( this%ozone_mix_ratio         ) ) &
         deallocate( this%ozone_mix_ratio         )
    if( allocated( this%photo_rates             ) ) &
         deallocate( this%photo_rates             )

  end subroutine finalize

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#endif // DO_PARTMC
end module monarch_fastJ_interface
