<HTML> <BODY BGCOLOR=#ccccdd LINK=#0000aa VLINK=#0000ff ALINK=#ff0000 ><BASE TARGET="bottom_target"><PRE>
!
! CRTM_AOD_Module
!
! Module containing the CRTM Aerosol Optical Depth (AOD) at nadir
! functions.
!
!
! CREATION HISTORY:
!       Written by:     Quanhua Liu, 29-Jun-2010
!                       Quanhua.Liu@noaa.gov
!

<A NAME='CRTM_AOD_MODULE'><A href='../../html_code/crtm/CRTM_AOD_Module.f90.html#CRTM_AOD_MODULE' TARGET='top_target'><IMG SRC="../../gif/bar_purple.gif" border=0></A>

MODULE CRTM_AOD_Module 1


  ! ------------
  ! Module usage
  ! ------------
  USE Message_Handler,          ONLY: SUCCESS, FAILURE, Display_Message
  USE CRTM_Parameters,          ONLY: ZERO, &amp;
                                      MAX_N_PHASE_ELEMENTS, &amp;
                                      MAX_N_LEGENDRE_TERMS
  USE CRTM_Atmosphere_Define,   ONLY: CRTM_Atmosphere_type, &amp;
                                      CRTM_Atmosphere_IsValid
  USE CRTM_ChannelInfo_Define,  ONLY: CRTM_ChannelInfo_type, &amp;
                                      CRTM_ChannelInfo_n_Channels
  USE CRTM_Options_Define,      ONLY: CRTM_Options_type
  USE CRTM_AtmOptics_Define,    ONLY: CRTM_AtmOptics_type      , &amp;
                                      CRTM_AtmOptics_Associated, &amp;
                                      CRTM_AtmOptics_Create    , &amp;
                                      CRTM_AtmOptics_Destroy   , &amp;
                                      CRTM_AtmOptics_Zero
  USE CRTM_AerosolScatter,      ONLY: CRTM_Compute_AerosolScatter, &amp;
                                      CRTM_Compute_AerosolScatter_TL, &amp;
                                      CRTM_Compute_AerosolScatter_AD
  USE CRTM_RTSolution_Define,   ONLY: CRTM_RTSolution_type, &amp;
                                      CRTM_RTSolution_Associated
  USE CRTM_AerosolCoeff,        ONLY: CRTM_AerosolCoeff_IsLoaded

  ! Internal variable definition modules
  ! ...AerosolScatter
  USE ASvar_Define, ONLY: ASvar_type, &amp;
                          ASvar_Associated, &amp;
                          ASvar_Destroy   , &amp;
                          ASvar_Create    


  ! -----------------------
  ! Disable implicit typing
  ! -----------------------
  IMPLICIT NONE


  ! ------------
  ! Visibilities
  ! ------------
  ! Everything private by default
  PRIVATE
  ! Public procedures
  PUBLIC :: CRTM_AOD
  PUBLIC :: CRTM_AOD_TL
  PUBLIC :: CRTM_AOD_AD
  PUBLIC :: CRTM_AOD_K
  PUBLIC :: CRTM_AOD_Version


  ! -----------------
  ! Module parameters
  ! -----------------
  ! Version Id for the module
  CHARACTER(*), PARAMETER :: MODULE_VERSION_ID = &amp;
  '$Id: CRTM_AOD_Module.f90 29405 2013-06-20 20:19:52Z paul.vandelst@noaa.gov $'
  ! Message string length
  INTEGER, PARAMETER :: ML = 256
 

CONTAINS


!--------------------------------------------------------------------------------
!:sdoc+:
!
! NAME:
!       CRTM_AOD
!
! PURPOSE:
!       Function that calculates layer total optical depth profile at nadir.
!
! CALLING SEQUENCE:
!       Error_Status = CRTM_AOD( Atmosphere       , &amp;
!                                ChannelInfo      , &amp;
!                                RTSolution       , &amp;
!                                Options = Options  )
!
! INPUTS:
!       Atmosphere:     Structure containing the Atmosphere data.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Atmosphere_type
!                       DIMENSION:  Rank-1 (n_Profiles)
!                       ATTRIBUTES: INTENT(IN)
!
!       ChannelInfo:    Structure returned from the CRTM_Init() function
!                       that contains the satellite/sensor channel index
!                       information.
!                       UNITS:      N/A
!                       TYPE:       CRTM_ChannelInfo_type
!                       DIMENSION:  Rank-1 (n_Sensors)
!                       ATTRIBUTES: INTENT(IN)
!
! OUTPUTS:
!       RTSolution:     Structure containing the layer aerosol optical
!                       profile for the given inputs.
!                       UNITS:      N/A
!                       TYPE:       CRTM_RTSolution_type
!                       DIMENSION:  Rank-2 (n_Channels x n_Profiles)
!                       ATTRIBUTES: INTENT(IN OUT)
!
! OPTIONAL INPUTS:
!       Options:        Options structure containing the optional arguments
!                       for the CRTM.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Options_type
!                       DIMENSION:  Same as input Atmosphere structure
!                       ATTRIBUTES: INTENT(IN), OPTIONAL
!
! FUNCTION RESULT:
!       Error_Status:   The return value is an integer defining the error status.
!                       The error codes are defined in the Message_Handler module.
!                       If == SUCCESS the computation was sucessful
!                          == FAILURE an unrecoverable error occurred
!                       UNITS:      N/A
!                       TYPE:       INTEGER
!                       DIMENSION:  Scalar
!
! COMMENTS:
!       - Many of the components of the Options optional input structure
!         are not used in this function. Consult the CRTM User Guide for
!         which Options components are usable for AOD calculations.
!
!:sdoc-:
!--------------------------------------------------------------------------------

<A NAME='CRTM_AOD'><A href='../../html_code/crtm/CRTM_AOD_Module.f90.html#CRTM_AOD' TARGET='top_target'><IMG SRC="../../gif/bar_green.gif" border=0></A>

  FUNCTION CRTM_AOD( &amp;,14
    Atmosphere , &amp;  ! Input, M
    ChannelInfo, &amp;  ! Input, M
    RTSolution , &amp;  ! Output, L x M 
    Options    ) &amp;  ! Optional input, M
  RESULT( Error_Status )
    ! Arguments
    TYPE(CRTM_Atmosphere_type),        INTENT(IN)     :: Atmosphere(:)     ! M
    TYPE(CRTM_ChannelInfo_type),       INTENT(IN)     :: ChannelInfo(:)    ! n_Sensors 
    TYPE(CRTM_RTSolution_type),        INTENT(IN OUT) :: RTSolution(:,:)   ! L x M
    TYPE(CRTM_Options_type), OPTIONAL, INTENT(IN)     :: Options(:)        ! M
    ! Function result
    INTEGER :: Error_Status
    ! Local parameters
    CHARACTER(*), PARAMETER :: ROUTINE_NAME = 'CRTM_AOD'
    ! Local variables
    CHARACTER(ML) :: Message
    LOGICAL :: Options_Present
    LOGICAL :: Check_Input
    INTEGER :: n, n_Sensors,  SensorIndex
    INTEGER :: l, n_Channels, ChannelIndex
    INTEGER :: m, n_Profiles
    INTEGER :: ln
    ! Component variables
    TYPE(CRTM_AtmOptics_type) :: AtmOptics 
    TYPE(ASVar_type) :: ASvar


    ! ------
    ! SET UP
    ! ------
    Error_Status = SUCCESS


    ! If no sensors or channels, simply return
    n_Sensors  = SIZE(ChannelInfo)
    n_Channels = SUM(CRTM_ChannelInfo_n_Channels(ChannelInfo))
    IF ( n_Sensors == 0 .OR. n_Channels == 0 ) RETURN


    ! Check the number of channels
    IF ( SIZE(RTSolution,DIM=1) &lt; n_Channels ) THEN
      Error_Status = FAILURE
      WRITE( Message,'("Output RTSolution structure array too small (",i0,&amp;
             &amp;") to hold results for the number of requested channels (",i0,")")') &amp;
             SIZE(RTSolution,DIM=1), n_Channels
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! Check the number of profiles
    ! ...Number of atmospheric profiles.
    n_Profiles = SIZE(Atmosphere)
    ! ...Check the profile dimensionality of the other mandatory arguments
    IF ( SIZE(RTSolution,DIM=2) /= n_Profiles ) THEN
      Error_Status = FAILURE
      Message = 'Inconsistent profile dimensionality for RTSolution argument.'
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF
    ! ...Check the profile dimensionality of the other optional arguments
    Options_Present = PRESENT(Options)
    IF ( Options_Present ) THEN
      IF ( SIZE(Options) /= n_Profiles ) THEN
        Error_Status = FAILURE
        Message = 'Inconsistent profile dimensionality for Options optional input argument.'
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
    END IF

    
    ! Check the RTSolution structure has been allocated
    IF ( ANY(.NOT. CRTM_RTSolution_Associated(RTSolution)) ) THEN
      Error_Status = FAILURE
      Message = 'RTSolution output structure components have not been allocated'
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! ------------
    ! PROFILE LOOP
    ! ------------
    Profile_Loop: DO m = 1, n_Profiles
    
    
      ! Check the aerosol coeff. data for cases with aerosols
      IF( Atmosphere(m)%n_Aerosols &gt; 0 .AND. .NOT. CRTM_AerosolCoeff_IsLoaded() )THEN
         Error_Status = FAILURE                                                                 
         WRITE( Message,'("The AerosolCoeff data must be loaded (with CRTM_Init routine) ", &amp;   
                &amp;"for the aerosol case profile #",i0)' ) m                                      
         CALL Display_Message( ROUTINE_NAME, Message, Error_Status )                      
         RETURN                                                                                 
      END IF

      
      ! Check the optional Options structure argument
      Check_Input = .TRUE.
      IF (Options_Present) THEN
        Check_Input = Options(m)%Check_Input
      END IF


      ! Check the input atmosphere if required
      IF ( Check_Input ) THEN
        IF ( .NOT. CRTM_Atmosphere_IsValid( Atmosphere(m) ) ) THEN
          Error_Status = FAILURE
          WRITE( Message,'("Input data check failed for profile #",i0)' ) m
          CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
          RETURN
        END IF
      END IF


      ! Check the RTSolution layer dimension
      IF ( ANY(RTSolution(:,m)%n_Layers &lt; Atmosphere(m)%n_Layers) ) THEN
        Error_Status=FAILURE
        WRITE( Message,'("Number of RTSolution layers &lt; Atmosphere for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN      
      END IF
      
      
      ! Allocate AtmOptics based on Atmosphere dimension
      CALL CRTM_AtmOptics_Create( AtmOptics, &amp;
                                  Atmosphere(m)%n_Layers, &amp;
                                  MAX_N_LEGENDRE_TERMS, &amp;
                                  MAX_N_PHASE_ELEMENTS  )
      IF ( .NOT. CRTM_AtmOptics_Associated( Atmoptics ) ) THEN
        Error_Status = FAILURE
        WRITE( Message,'("Error allocating AtmOptics data structure for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
      ! ...Set default number of streams
      AtmOptics%n_Legendre_Terms = 4


      ! Allocate the aerosol scattering internal variable if necessary
      IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
        CALL ASvar_Create( ASvar, &amp;
                           MAX_N_LEGENDRE_TERMS    , &amp;
                           MAX_N_PHASE_ELEMENTS    , &amp;
                           Atmosphere(m)%n_Layers  , &amp;
                           Atmosphere(m)%n_Aerosols  )
      END IF


      ! -----------
      ! SENSOR LOOP
      ! -----------
      ! Initialise channel counter for channel(l)/sensor(n) count
      ln = 0
      
      Sensor_Loop: DO n = 1, n_Sensors

        ! Shorter name
        SensorIndex = ChannelInfo(n)%Sensor_Index


        ! ------------
        ! CHANNEL LOOP
        ! ------------
        Channel_Loop: DO l = 1, ChannelInfo(n)%n_Channels
         
          ! Channel setup
          ! ...Skip channel if requested
          IF ( .NOT. ChannelInfo(n)%Process_Channel(l) ) CYCLE Channel_Loop
          ! ...Shorter name
          ChannelIndex = ChannelInfo(n)%Channel_Index(l)
          ! ...Increment the processed channel counter
          ln = ln + 1
          ! ...Assign sensor+channel information to output
          RTSolution(ln,m)%Sensor_Id        = ChannelInfo(n)%Sensor_Id
          RTSolution(ln,m)%WMO_Satellite_Id = ChannelInfo(n)%WMO_Satellite_Id
          RTSolution(ln,m)%WMO_Sensor_Id    = ChannelInfo(n)%WMO_Sensor_Id   
          RTSolution(ln,m)%Sensor_Channel   = ChannelInfo(n)%Sensor_Channel(l)

          
          ! Initialisations
          CALL CRTM_AtmOptics_Zero( AtmOptics )


          ! Compute the aerosol absorption/scattering properties
          IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
            Error_Status = CRTM_Compute_AerosolScatter( Atmosphere(m), &amp;  ! Input
                                                        SensorIndex  , &amp;  ! Input
                                                        ChannelIndex , &amp;  ! Input
                                                        AtmOptics    , &amp;  ! In/Output
                                                        ASVar          )  ! Internal variable output
            IF ( Error_Status /= SUCCESS ) THEN
              WRITE( Message,'("Error computing AerosolScatter for ",a,&amp;
                     &amp;", channel ",i0,", profile #",i0)' ) &amp;
                     TRIM(ChannelInfo(n)%Sensor_ID), ChannelInfo(n)%Sensor_Channel(l), m
              CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
              RETURN
            END IF
          END IF


          ! Save the nadir optical depth
          RTSolution(ln,m)%Layer_Optical_Depth(1:Atmosphere(m)%n_Layers) = AtmOptics%Optical_Depth

        END DO Channel_Loop

      END DO Sensor_Loop
      

      ! Deallocate local sensor independent data structures
      CALL CRTM_AtmOptics_Destroy( AtmOptics )

    END DO Profile_Loop

  END FUNCTION CRTM_AOD


!--------------------------------------------------------------------------------
!:sdoc+:
!
! NAME:
!       CRTM_AOD_TL
!
! PURPOSE:
!       Function that calculates tangent-linear layer total optical depth.
!
! CALLING SEQUENCE:
!       Error_Status = CRTM_AOD_TL( Atmosphere       , &amp;
!                                   Atmosphere_TL    , &amp;
!                                   ChannelInfo      , &amp;
!                                   RTSolution       , &amp;
!                                   RTSolution_TL    , &amp;
!                                   Options = Options  )
!
! INPUTS:
!       Atmosphere:     Structure containing the Atmosphere data.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Atmosphere_type
!                       DIMENSION:  Rank-1 (n_Profiles)
!                       ATTRIBUTES: INTENT(IN)
!
!       Atmosphere_TL:  Structure containing the tangent-linear Atmosphere data.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Atmosphere_type
!                       DIMENSION:  Same as input Atmosphere structure
!                       ATTRIBUTES: INTENT(IN)
!
!       ChannelInfo:    Structure returned from the CRTM_Init() function
!                       that contains the satellite/sensor channel index
!                       information.
!                       UNITS:      N/A
!                       TYPE:       CRTM_ChannelInfo_type
!                       DIMENSION:  Rank-1 (n_Sensors)
!                       ATTRIBUTES: INTENT(IN)
!
! OUTPUTS:
!       RTSolution:     Structure containing the layer aerosol optical
!                       profile for the given inputs.
!                       UNITS:      N/A
!                       TYPE:       CRTM_RTSolution_type
!                       DIMENSION:  Rank-2 (n_Channels x n_Profiles)
!                       ATTRIBUTES: INTENT(IN OUT)
!
!       RTSolution_TL:  Structure containing the tangent-linear aerosol
!                       optical depth profile for the given inputs.
!                       UNITS:      N/A
!                       TYPE:       CRTM_RTSolution_type
!                       DIMENSION:  Same as RTSolution output
!                       ATTRIBUTES: INTENT(IN OUT)
!
! OPTIONAL INPUTS:
!       Options:        Options structure containing the optional arguments
!                       for the CRTM.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Options_type
!                       DIMENSION:  Same as input Atmosphere structure
!                       ATTRIBUTES: INTENT(IN), OPTIONAL
!
! FUNCTION RESULT:
!       Error_Status:   The return value is an integer defining the error status.
!                       The error codes are defined in the Message_Handler module.
!                       If == SUCCESS the computation was sucessful
!                          == FAILURE an unrecoverable error occurred
!                       UNITS:      N/A
!                       TYPE:       INTEGER
!                       DIMENSION:  Scalar
!
! COMMENTS:
!       - Many of the components of the Options optional input structure
!         are not used in this function. Consult the CRTM User Guide for
!         which Options components are usable for AOD calculations.
!
!:sdoc-:
!--------------------------------------------------------------------------------

<A NAME='CRTM_AOD_TL'><A href='../../html_code/crtm/CRTM_AOD_Module.f90.html#CRTM_AOD_TL' TARGET='top_target'><IMG SRC="../../gif/bar_green.gif" border=0></A>

  FUNCTION CRTM_AOD_TL( &amp;,18
    Atmosphere   , &amp;  ! Input, M
    Atmosphere_TL, &amp;  ! Input, M
    ChannelInfo  , &amp;  ! Input, M
    RTSolution   , &amp;  ! Output, L x M 
    RTSolution_TL, &amp;  ! Output, L x M 
    Options      ) &amp;  ! Optional FWD input, M
  RESULT( Error_Status )
    ! Arguments
    TYPE(CRTM_Atmosphere_type),        INTENT(IN)     :: Atmosphere(:)      ! M
    TYPE(CRTM_Atmosphere_type),        INTENT(IN)     :: Atmosphere_TL(:)   ! M
    TYPE(CRTM_ChannelInfo_type),       INTENT(IN)     :: ChannelInfo(:)     ! n_Sensors 
    TYPE(CRTM_RTSolution_type),        INTENT(IN OUT) :: RTSolution(:,:)    ! L x M
    TYPE(CRTM_RTSolution_type),        INTENT(IN OUT) :: RTSolution_TL(:,:) ! L x M
    TYPE(CRTM_Options_type), OPTIONAL, INTENT(IN)     :: Options(:)         ! M
    ! Function result
    INTEGER :: Error_Status
    ! Local parameters
    CHARACTER(*), PARAMETER :: ROUTINE_NAME = 'CRTM_AOD_TL'
    ! Local variables
    CHARACTER(ML) :: Message
    LOGICAL :: Options_Present
    LOGICAL :: Check_Input
    INTEGER :: Status_FWD, Status_TL
    INTEGER :: n, n_Sensors,  SensorIndex
    INTEGER :: l, n_Channels, ChannelIndex
    INTEGER :: m, n_Profiles
    INTEGER :: ln
    ! Component variables
    TYPE(CRTM_AtmOptics_type)   :: AtmOptics, AtmOptics_TL
    TYPE(ASVar_type) :: ASvar


    ! ------
    ! SET UP
    ! ------
    Error_Status = SUCCESS


    ! If no sensors or channels, simply return
    n_Sensors  = SIZE(ChannelInfo)
    n_Channels = SUM(CRTM_ChannelInfo_n_Channels(ChannelInfo))
    IF ( n_Sensors == 0 .OR. n_Channels == 0 ) RETURN


    ! Check the number of channels
    IF ( SIZE(RTSolution,   DIM=1) &lt; n_Channels .OR. &amp;
         SIZE(RTSolution_TL,DIM=1) &lt; n_Channels      ) THEN
      Error_Status = FAILURE
      WRITE( Message,'("Output RTSolution structure arrays too small (",i0,&amp;
             &amp;") to hold results for the number of requested channels (",i0,")")') &amp;
             SIZE(RTSolution,DIM=1), n_Channels
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! Check the number of profiles
    n_Profiles = SIZE(Atmosphere)
    ! ...Check the profile dimensionality of the other mandatory arguments
    IF ( SIZE(Atmosphere_TL)       /= n_Profiles .OR. &amp;
         SIZE(RTSolution,   DIM=2) /= n_Profiles .OR. &amp;
         SIZE(RTSolution_TL,DIM=2) /= n_Profiles      ) THEN
      Error_Status = FAILURE
      Message = 'Inconsistent profile dimensionality for input arguments.'
      CALL Display_Message( ROUTINE_NAME, TRIM(Message), Error_Status )
      RETURN
    END IF
    ! ...Check the profile dimensionality of the other optional arguments
    Options_Present = PRESENT(Options)
    IF ( Options_Present ) THEN
      IF ( SIZE(Options) /= n_Profiles ) THEN
        Error_Status = FAILURE
        Message = 'Inconsistent profile dimensionality for Options optional input argument.'
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
    END IF

    
    ! Check the RTSolution structures have been allocated
    IF ( ANY(.NOT. CRTM_RTSolution_Associated(RTSolution)) .OR. &amp;
         ANY(.NOT. CRTM_RTSolution_Associated(RTSolution_TL)) ) THEN
      Error_Status = FAILURE
      Message = 'RTSolution output structure components have not been allocated'
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! ------------
    ! PROFILE LOOP
    ! ------------
    Profile_Loop: DO m = 1, n_Profiles
    
    
      ! Check the aerosol coeff. data for cases with aerosols
      IF( Atmosphere(m)%n_Aerosols &gt; 0 .AND. .NOT. CRTM_AerosolCoeff_IsLoaded() )THEN
         Error_Status = FAILURE                                                                 
         WRITE( Message,'("The AerosolCoeff data must be loaded (with CRTM_Init routine) ", &amp;   
                &amp;"for the aerosol case profile #",i0)' ) m                                      
         CALL Display_Message( ROUTINE_NAME, Message, Error_Status )                      
         RETURN                                                                                 
      END IF

      
      ! Check the optional Options structure argument
      Check_Input = .TRUE.
      IF (Options_Present) THEN
        Check_Input = Options(m)%Check_Input
      END IF


      ! Check the input atmosphere if required
      IF ( Check_Input ) THEN
        IF ( .NOT. CRTM_Atmosphere_IsValid( Atmosphere(m) ) ) THEN
          Error_Status = FAILURE
          WRITE( Message,'("Input data check failed for profile #",i0)' ) m
          CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
          RETURN
        END IF
      END IF


      ! Check the RTSolution layer dimensions
      IF ( ANY(RTSolution(:,m)%n_Layers    &lt; Atmosphere(m)%n_Layers) .OR. &amp;
           ANY(RTSolution_TL(:,m)%n_Layers &lt; Atmosphere(m)%n_Layers) ) THEN
        Error_Status=FAILURE
        WRITE( Message,'("Number of RTSolution layers &lt; Atmosphere for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN      
      END IF
      

      ! Allocate AtmOptics based on Atmosphere dimensions
      CALL CRTM_AtmOptics_Create( AtmOptics, &amp;
                                  Atmosphere(m)%n_Layers, &amp;
                                  MAX_N_LEGENDRE_TERMS  , &amp;
                                  MAX_N_PHASE_ELEMENTS    )
      CALL CRTM_AtmOptics_Create( AtmOptics_TL, &amp;
                                  Atmosphere(m)%n_Layers, &amp;
                                  MAX_N_LEGENDRE_TERMS  , &amp;
                                  MAX_N_PHASE_ELEMENTS    )
      IF ( .NOT. CRTM_AtmOptics_Associated( Atmoptics ) .OR. &amp;
           .NOT. CRTM_AtmOptics_Associated( Atmoptics_TL ) ) THEN
        Error_Status = FAILURE
        WRITE( Message,'("Error allocating AtmOptics data structures for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
      ! ...Set default number of streams
      AtmOptics%n_Legendre_Terms    = 4
      AtmOptics_TL%n_Legendre_Terms = AtmOptics%n_Legendre_Terms


      ! Allocate the aerosol scattering internal variable if necessary
      IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
        CALL ASvar_Create( ASvar, &amp;
                           MAX_N_LEGENDRE_TERMS    , &amp;
                           MAX_N_PHASE_ELEMENTS    , &amp;
                           Atmosphere(m)%n_Layers  , &amp;
                           Atmosphere(m)%n_Aerosols  )
      END IF


      ! -----------
      ! SENSOR LOOP
      ! -----------
      ! Initialise channel counter for channel(l)/sensor(n) count
      ln = 0
      
      Sensor_Loop: DO n = 1, n_Sensors

        ! Shorter name
        SensorIndex = ChannelInfo(n)%Sensor_Index


        ! ------------
        ! CHANNEL LOOP
        ! ------------
        Channel_Loop: DO l = 1, ChannelInfo(n)%n_Channels
         
          ! Channel setup
          ! ...Skip channel if requested
          IF ( .NOT. ChannelInfo(n)%Process_Channel(l) ) CYCLE Channel_Loop
          ! ...Shorter name
          ChannelIndex = ChannelInfo(n)%Channel_Index(l)
          ! ...Increment the processed channel counter
          ln = ln + 1
          ! ...Assign sensor+channel information to output
          RTSolution(ln,m)%Sensor_Id        = ChannelInfo(n)%Sensor_Id
          RTSolution(ln,m)%WMO_Satellite_Id = ChannelInfo(n)%WMO_Satellite_Id
          RTSolution(ln,m)%WMO_Sensor_Id    = ChannelInfo(n)%WMO_Sensor_Id   
          RTSolution(ln,m)%Sensor_Channel   = ChannelInfo(n)%Sensor_Channel(l)
          RTSolution_TL(ln,m)%Sensor_Id        = RTSolution(ln,m)%Sensor_Id       
          RTSolution_TL(ln,m)%WMO_Satellite_Id = RTSolution(ln,m)%WMO_Satellite_Id
          RTSolution_TL(ln,m)%WMO_Sensor_Id    = RTSolution(ln,m)%WMO_Sensor_Id   
          RTSolution_TL(ln,m)%Sensor_Channel   = RTSolution(ln,m)%Sensor_Channel  

          
          ! Initialisations
          CALL CRTM_AtmOptics_Zero( AtmOptics )
          CALL CRTM_AtmOptics_Zero( AtmOptics_TL )


          ! Compute the aerosol absorption/scattering properties
          IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
            Status_FWD = CRTM_Compute_AerosolScatter( Atmosphere(m), &amp;  ! Input
                                                      SensorIndex  , &amp;  ! Input
                                                      ChannelIndex , &amp;  ! Input
                                                      AtmOptics    , &amp;  ! In/Output
                                                      ASVar          )  ! Internal variable output
            Status_TL = CRTM_Compute_AerosolScatter_TL( Atmosphere(m)   , &amp;  ! FWD Input
                                                        AtmOptics       , &amp;  ! FWD Input 
                                                        Atmosphere_TL(m), &amp;  ! TL  Input
                                                        SensorIndex     , &amp;  ! Input
                                                        ChannelIndex    , &amp;  ! Input
                                                        AtmOptics_TL    , &amp;  ! TL  Output  
                                                        ASVar             )  ! Internal variable 
            IF ( Status_FWD /= SUCCESS .OR. Status_TL /= SUCCESS) THEN
              Error_Status = FAILURE
              WRITE( Message,'("Error computing AerosolScatter for ",a,&amp;
                     &amp;", channel ",i0,", profile #",i0)' ) &amp;
                     TRIM(ChannelInfo(n)%Sensor_ID), ChannelInfo(n)%Sensor_Channel(l), m
              CALL Display_Message( ROUTINE_NAME, TRIM(Message), Error_Status )
              RETURN
            END IF

          END IF

          ! Save the nadir optical depths
          RTSolution(ln,m)%Layer_Optical_Depth(1:Atmosphere(m)%n_Layers) = AtmOptics%Optical_Depth
          RTSolution_TL(ln,m)%Layer_Optical_Depth(1:Atmosphere(m)%n_Layers) = AtmOptics_TL%Optical_Depth
        
        END DO Channel_Loop

      END DO Sensor_Loop
      

      ! Deallocate local sensor independent data structures
      CALL CRTM_AtmOptics_Destroy( AtmOptics )
      CALL CRTM_AtmOptics_Destroy( AtmOptics_TL )

    END DO Profile_Loop

  END FUNCTION CRTM_AOD_TL


!--------------------------------------------------------------------------------
!:sdoc+:
!
! NAME:
!       CRTM_AOD_AD
!
! PURPOSE:
!       Function that calculates the adjoint nadir aerosol optical depth.
!
! CALLING SEQUENCE:
!       Error_Status = CRTM_AOD_AD( Atmosphere       , &amp;
!                                   RTSolution_AD    , &amp;
!                                   ChannelInfo      , &amp;
!                                   RTSolution       , &amp;
!                                   Atmosphere_AD    , &amp;
!                                   Options = Options  )
!
! INPUTS:
!       Atmosphere:     Structure containing the Atmosphere data.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Atmosphere_type
!                       DIMENSION:  Rank-1 (n_Profiles)
!                       ATTRIBUTES: INTENT(IN)
!
!       RTSolution_AD:  Structure containing the RT solution adjoint inputs.
!                       **NOTE: On EXIT from this function, the contents of
!                               this structure may be modified (e.g. set to
!                               zero.)
!                       UNITS:      N/A
!                       TYPE:       CRTM_RTSolution_type
!                       DIMENSION:  Rank-2 (n_Channels x n_Profiles)
!                       ATTRIBUTES: INTENT(IN OUT)
!
!       ChannelInfo:    Structure returned from the CRTM_Init() function
!                       that contains the satellite/sensor channel index
!                       information.
!                       UNITS:      N/A
!                       TYPE:       CRTM_ChannelInfo_type
!                       DIMENSION:  Rank-1 (n_Sensors)
!                       ATTRIBUTES: INTENT(IN)
!
! OUTPUTS:
!       RTSolution:     Structure containing the soluition to the RT equation
!                       for the given inputs.
!                       UNITS:      N/A
!                       TYPE:       CRTM_RTSolution_type
!                       DIMENSION:  Rank-2 (n_Channels x n_Profiles)
!                       ATTRIBUTES: INTENT(IN OUT)
!
!       Atmosphere_AD:  Structure containing the adjoint Atmosphere data.
!                       **NOTE: On ENTRY to this function, the contents of
!                               this structure should be defined (e.g.
!                               initialized to some value based on the
!                               position of this function in the call chain.)
!                       UNITS:      N/A
!                       TYPE:       CRTM_Atmosphere_type
!                       DIMENSION:  Same as input Atmosphere argument
!                       ATTRIBUTES: INTENT(IN OUT)
!
! OPTIONAL INPUTS:
!       Options:        Options structure containing the optional arguments
!                       for the CRTM.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Options_type
!                       DIMENSION:  Same as input Atmosphere structure
!                       ATTRIBUTES: INTENT(IN), OPTIONAL
!
! FUNCTION RESULT:
!       Error_Status:   The return value is an integer defining the error status.
!                       The error codes are defined in the Message_Handler module.
!                       If == SUCCESS the computation was sucessful
!                          == FAILURE an unrecoverable error occurred
!                       UNITS:      N/A
!                       TYPE:       INTEGER
!                       DIMENSION:  Scalar
!
! COMMENTS:
!       - Many of the components of the Options optional input structure
!         are not used in this function. Consult the CRTM User Guide for
!         which Options components are usable for AOD calculations.
!
!:sdoc-:
!--------------------------------------------------------------------------------

<A NAME='CRTM_AOD_AD'><A href='../../html_code/crtm/CRTM_AOD_Module.f90.html#CRTM_AOD_AD' TARGET='top_target'><IMG SRC="../../gif/bar_green.gif" border=0></A>

  FUNCTION CRTM_AOD_AD( &amp;,18
    Atmosphere   , &amp;  ! Input, M
    RTSolution_AD, &amp;  ! Input, M
    ChannelInfo  , &amp;  ! Input, M
    RTSolution   , &amp;  ! Output, L x M 
    Atmosphere_AD, &amp;  ! Output, L x M 
    Options      ) &amp;  ! Optional input, M
  RESULT( Error_Status )
    ! Arguments
    TYPE(CRTM_Atmosphere_type),        INTENT(IN)     :: Atmosphere(:)      ! M
    TYPE(CRTM_RTSolution_type),        INTENT(IN OUT) :: RTSolution_AD(:,:) ! L x M
    TYPE(CRTM_ChannelInfo_type),       INTENT(IN)     :: ChannelInfo(:)     ! n_Sensors
    TYPE(CRTM_RTSolution_type),        INTENT(IN OUT) :: RTSolution(:,:)    ! L x M
    TYPE(CRTM_Atmosphere_type),        INTENT(IN OUT) :: Atmosphere_AD(:)   ! M
    TYPE(CRTM_Options_type), OPTIONAL, INTENT(IN)     :: Options(:)         ! M
    ! Function result
    INTEGER :: Error_Status
    ! Local parameters
    CHARACTER(*), PARAMETER :: ROUTINE_NAME = 'CRTM_AOD_AD'
    ! Local variables
    CHARACTER(ML) :: Message
    LOGICAL :: Options_Present
    LOGICAL :: Check_Input
    INTEGER :: Status_FWD, Status_AD
    INTEGER :: n, n_Sensors,  SensorIndex
    INTEGER :: l, n_Channels, ChannelIndex
    INTEGER :: m, n_Profiles
    INTEGER :: na
    INTEGER :: ln
    ! Component variables
    TYPE(CRTM_AtmOptics_type) :: AtmOptics, AtmOptics_AD
    TYPE(ASVar_type) :: ASvar


    ! ------
    ! SET UP
    ! ------
    Error_Status = SUCCESS


    ! If no sensors or channels, simply return
    n_Sensors  = SIZE(ChannelInfo)
    n_Channels = SUM(CRTM_ChannelInfo_n_Channels(ChannelInfo))
    IF ( n_Sensors == 0 .OR. n_Channels == 0 ) RETURN


    ! Check the number of channels
    IF ( SIZE(RTSolution,   DIM=1) &lt; n_Channels .OR. &amp;
         SIZE(RTSolution_AD,DIM=1) &lt; n_Channels      ) THEN
      Error_Status = FAILURE
      WRITE( Message,'("Output RTSolution structure arrays too small (",i0,&amp;
             &amp;") to hold results for the number of requested channels (",i0,")")') &amp;
             SIZE(RTSolution,DIM=1), n_Channels
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! Check the number of profiles
    n_Profiles = SIZE(Atmosphere)
    ! ...Check the profile dimensionality of the other mandatory arguments
    IF ( SIZE(Atmosphere_AD)       /= n_Profiles .OR. &amp;
         SIZE(RTSolution,   DIM=2) /= n_Profiles .OR. &amp;
         SIZE(RTSolution_AD,DIM=2) /= n_Profiles      ) THEN
      Error_Status = FAILURE
      Message = 'Inconsistent profile dimensionality for input arguments.'
      CALL Display_Message( ROUTINE_NAME, TRIM(Message), Error_Status )
      RETURN
    END IF
    ! ...Check the profile dimensionality of the other optional arguments
    Options_Present = PRESENT(Options)
    IF ( Options_Present ) THEN
      IF ( SIZE(Options) /= n_Profiles ) THEN
        Error_Status = FAILURE
        Message = 'Inconsistent profile dimensionality for Options optional input argument.'
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
    END IF

    
    ! Check the RTSolution structures have been allocated
    IF ( ANY(.NOT. CRTM_RTSolution_Associated(RTSolution)) .OR. &amp;
         ANY(.NOT. CRTM_RTSolution_Associated(RTSolution_AD)) ) THEN
      Error_Status = FAILURE
      Message = 'RTSolution output structure components have not been allocated'
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! ------------
    ! PROFILE LOOP
    ! ------------
    Profile_Loop: DO m = 1, n_Profiles
    
    
      ! Check the aerosol coeff. data for cases with aerosols
      IF( Atmosphere(m)%n_Aerosols &gt; 0 .AND. .NOT. CRTM_AerosolCoeff_IsLoaded() )THEN
         Error_Status = FAILURE                                                                 
         WRITE( Message,'("The AerosolCoeff data must be loaded (with CRTM_Init routine) ", &amp;   
                &amp;"for the aerosol case profile #",i0)' ) m                                      
         CALL Display_Message( ROUTINE_NAME, Message, Error_Status )                      
         RETURN                                                                                 
      END IF

      
      ! Check the optional Options structure argument
      Check_Input = .TRUE.
      IF (Options_Present) THEN
        Check_Input = Options(m)%Check_Input
      END IF


      ! Check the input atmosphere if required
      IF ( Check_Input ) THEN
        IF ( .NOT. CRTM_Atmosphere_IsValid( Atmosphere(m) ) ) THEN
          Error_Status = FAILURE
          WRITE( Message,'("Input data check failed for profile #",i0)' ) m
          CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
          RETURN
        END IF
      END IF


      ! Check the RTSolution layer dimensions
      IF ( ANY(RTSolution(:,m)%n_Layers    &lt; Atmosphere(m)%n_Layers) .OR. &amp;
           ANY(RTSolution_AD(:,m)%n_Layers &lt; Atmosphere(m)%n_Layers) ) THEN
        Error_Status=FAILURE
        WRITE( Message,'("Number of RTSolution layers &lt; Atmosphere for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN      
      END IF
      

      ! Allocate AtmOptics based on Atmosphere dimensions
      CALL CRTM_AtmOptics_Create( AtmOptics, &amp;
                                  Atmosphere(m)%n_Layers, &amp;
                                  MAX_N_LEGENDRE_TERMS  , &amp;
                                  MAX_N_PHASE_ELEMENTS    )
      CALL CRTM_AtmOptics_Create( AtmOptics_AD, &amp;
                                  Atmosphere(m)%n_Layers, &amp;
                                  MAX_N_LEGENDRE_TERMS  , &amp;
                                  MAX_N_PHASE_ELEMENTS    )
      IF ( .NOT. CRTM_AtmOptics_Associated( Atmoptics ) .OR. &amp;
           .NOT. CRTM_AtmOptics_Associated( Atmoptics_AD ) ) THEN
        Error_Status = FAILURE
        WRITE( Message,'("Error allocating AtmOptics data structures for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
      ! ...Set default number of streams
      AtmOptics%n_Legendre_Terms    = 4
      AtmOptics_AD%n_Legendre_Terms = AtmOptics%n_Legendre_Terms


      ! Allocate the aerosol scattering internal variable if necessary
      IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
        CALL ASvar_Create( ASvar, &amp;
                           MAX_N_LEGENDRE_TERMS    , &amp;
                           MAX_N_PHASE_ELEMENTS    , &amp;
                           Atmosphere(m)%n_Layers  , &amp;
                           Atmosphere(m)%n_Aerosols  )
      END IF


      ! Copy over atmosphere info to adjoint output
      ! ...Climatology
      Atmosphere_AD(m)%Climatology = Atmosphere(m)%Climatology
      ! ...Absorber info
      Atmosphere_AD(m)%Absorber_Id    = Atmosphere(m)%Absorber_Id   
      Atmosphere_AD(m)%Absorber_Units = Atmosphere(m)%Absorber_Units
      ! ...Aerosol info
      DO na = 1, Atmosphere(m)%n_Aerosols
        Atmosphere_AD(m)%Aerosol(na)%Type = Atmosphere(m)%Aerosol(na)%Type      
      END DO


      ! -----------
      ! SENSOR LOOP
      ! -----------
      ! Initialise channel counter for sensor(n)/channel(l) count
      ln = 0
    
      Sensor_Loop: DO n = 1, n_Sensors


        ! Shorter name
        SensorIndex = ChannelInfo(n)%Sensor_Index


        ! ------------
        ! CHANNEL LOOP
        ! ------------
        Channel_Loop: DO l = 1, ChannelInfo(n)%n_Channels
         
          ! Channel setup
          ! ...Skip channel if requested
          IF ( .NOT. ChannelInfo(n)%Process_Channel(l) ) CYCLE Channel_Loop
          ! ...Shorter name
          ChannelIndex = ChannelInfo(n)%Channel_Index(l)
          ! ...Increment the processed channel counter
          ln = ln + 1
          ! ...Assign sensor+channel information to output
          RTSolution(ln,m)%Sensor_Id        = ChannelInfo(n)%Sensor_Id
          RTSolution(ln,m)%WMO_Satellite_Id = ChannelInfo(n)%WMO_Satellite_Id
          RTSolution(ln,m)%WMO_Sensor_Id    = ChannelInfo(n)%WMO_Sensor_Id   
          RTSolution(ln,m)%Sensor_Channel   = ChannelInfo(n)%Sensor_Channel(l)
          RTSolution_AD(ln,m)%Sensor_Id        = RTSolution(ln,m)%Sensor_Id       
          RTSolution_AD(ln,m)%WMO_Satellite_Id = RTSolution(ln,m)%WMO_Satellite_Id
          RTSolution_AD(ln,m)%WMO_Sensor_Id    = RTSolution(ln,m)%WMO_Sensor_Id   
          RTSolution_AD(ln,m)%Sensor_Channel   = RTSolution(ln,m)%Sensor_Channel  

          
          ! Initialisations
          CALL CRTM_AtmOptics_Zero( AtmOptics )
          CALL CRTM_AtmOptics_Zero( AtmOptics_AD )
          AtmOptics_AD%Optical_Depth = RTSolution_AD(ln,m)%Layer_Optical_Depth(1:Atmosphere(m)%n_Layers)


          ! Compute the aerosol absorption/scattering properties
          IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
            Status_FWD = CRTM_Compute_AerosolScatter( Atmosphere(m), &amp;  ! Input
                                                      SensorIndex  , &amp;  ! Input
                                                      ChannelIndex , &amp;  ! Input
                                                      AtmOptics    , &amp;  ! In/Output
                                                      ASVar          )  ! Internal variable output
            Status_AD = CRTM_Compute_AerosolScatter_AD( Atmosphere(m)   , &amp;  ! FWD Input
                                                        AtmOptics       , &amp;  ! FWD Input
                                                        AtmOptics_AD    , &amp;  ! AD  Input
                                                        SensorIndex     , &amp;  ! Input
                                                        ChannelIndex    , &amp;  ! Input
                                                        Atmosphere_AD(m), &amp;  ! AD  Output
                                                        ASVar             )  ! Internal variable input
            IF ( Status_FWD /= SUCCESS .OR. Status_AD /= SUCCESS) THEN
              Error_Status = FAILURE
              WRITE( Message,'("Error computing AerosolScatter for ",a,&amp;
                     &amp;", channel ",i0,", profile #",i0)' ) &amp;
                     TRIM(ChannelInfo(n)%Sensor_ID), ChannelInfo(n)%Sensor_Channel(l), m
              CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
              RETURN
            END IF
          END IF


          ! Save the nadir optical depths
          RTSolution(ln,m)%Layer_Optical_Depth(1:Atmosphere(m)%n_Layers) = AtmOptics%Optical_Depth
         
        END DO Channel_Loop

      END DO Sensor_Loop
      

      ! Deallocate local sensor independent data structures
      CALL CRTM_AtmOptics_Destroy( AtmOptics )
      CALL CRTM_AtmOptics_Destroy( AtmOptics_AD )

    END DO Profile_Loop

  END FUNCTION CRTM_AOD_AD


!--------------------------------------------------------------------------------
!:sdoc+:
!
! NAME:
!       CRTM_AOD_K
!
! PURPOSE:
!       Function that calculates the K-matrix nadir aerosol optical depth.
!
! CALLING SEQUENCE:
!       Error_Status = CRTM_AOD_K( Atmosphere        , &amp;
!                                  RTSolution_K      , &amp;
!                                  ChannelInfo       , &amp;
!                                  RTSolution        , &amp;
!                                  Atmosphere_K      , &amp;
!                                  Opttions = Options  )
!
! INPUTS:
!       Atmosphere:     Structure containing the Atmosphere data.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Atmosphere_type
!                       DIMENSION:  Rank-1 (n_Profiles)
!                       ATTRIBUTES: INTENT(IN)
!
!       RTSolution_K:   Structure containing the aerosol optical depth
!                       profile K-matrix input.
!                       **NOTE: On EXIT from this function, the contents of
!                               this structure may be modified (e.g. set to
!                               zero.)
!                       UNITS:      N/A
!                       TYPE:       CRTM_RTSolution_type
!                       DIMENSION:  Rank-2 (n_Channels x n_Profiles)
!                       ATTRIBUTES: INTENT(IN OUT)
!
!       ChannelInfo:    Structure returned from the CRTM_Init() function
!                       that contains the satellite/sensor channel index
!                       information.
!                       UNITS:      N/A
!                       TYPE:       CRTM_ChannelInfo_type
!                       DIMENSION:  Rank-1 (n_Sensors)
!                       ATTRIBUTES: INTENT(IN)
!
! OUTPUTS:
!       RTSolution:     Structure containing the layer aerosol optical 
!                       depth profile for the given inputs.
!                       UNITS:      N/A
!                       TYPE:       CRTM_RTSolution_type
!                       DIMENSION:  Rank-2 (n_Channels x n_Profiles)
!                       ATTRIBUTES: INTENT(IN OUT)
!
!       Atmosphere_K:   Structure containing the K-matrix Atmosphere data.
!                       **NOTE: On ENTRY to this function, the contents of
!                               this structure should be defined (e.g.
!                               initialized to some value based on the
!                               position of this function in the call chain.)
!                       UNITS:      N/A
!                       TYPE:       CRTM_Atmosphere_type
!                       DIMENSION:  Same as input RTSolution_K argument
!                       ATTRIBUTES: INTENT(IN OUT)
!
! OPTIONAL INPUTS:
!       Options:        Options structure containing the optional arguments
!                       for the CRTM.
!                       UNITS:      N/A
!                       TYPE:       CRTM_Options_type
!                       DIMENSION:  Same as input Atmosphere structure
!                       ATTRIBUTES: INTENT(IN), OPTIONAL
!
! FUNCTION RESULT:
!       Error_Status:   The return value is an integer defining the error status.
!                       The error codes are defined in the Message_Handler module.
!                       If == SUCCESS the computation was sucessful
!                          == FAILURE an unrecoverable error occurred
!                       UNITS:      N/A
!                       TYPE:       INTEGER
!                       DIMENSION:  Scalar
!
! COMMENTS:
!       - Many of the components of the Options optional input structure
!         are not used in this function. Consult the CRTM User Guide for
!         which Options components are usable for AOD calculations.
!
!:sdoc-:
!--------------------------------------------------------------------------------

<A NAME='CRTM_AOD_K'><A href='../../html_code/crtm/CRTM_AOD_Module.f90.html#CRTM_AOD_K' TARGET='top_target'><IMG SRC="../../gif/bar_green.gif" border=0></A>

  FUNCTION CRTM_AOD_K ( &amp;,18
    Atmosphere  , &amp;  ! Input, M
    RTSolution_K, &amp;  ! Input, M
    ChannelInfo , &amp;  ! Input, M
    RTSolution  , &amp;  ! Output, L x M 
    Atmosphere_K, &amp;  ! Output, L x M 
    Options     ) &amp;  ! Optional input, M
  RESULT( Error_Status )
    ! Arguments
    TYPE(CRTM_Atmosphere_type),        INTENT(IN)     :: Atmosphere(:)      ! M
    TYPE(CRTM_RTSolution_type),        INTENT(IN OUT) :: RTSolution_K(:,:)  ! L x M
    TYPE(CRTM_ChannelInfo_type),       INTENT(IN)     :: ChannelInfo(:)     ! n_Sensors 
    TYPE(CRTM_RTSolution_type),        INTENT(IN OUT) :: RTSolution(:,:)    ! L x M
    TYPE(CRTM_Atmosphere_type),        INTENT(IN OUT) :: Atmosphere_K(:,:)  ! L x M
    TYPE(CRTM_Options_type), OPTIONAL, INTENT(IN)     :: Options(:)         ! M
    ! Function result
    INTEGER :: Error_Status
    ! Local parameters
    CHARACTER(*), PARAMETER :: ROUTINE_NAME = 'CRTM_AOD_K'
    ! Local variables
    CHARACTER(ML) :: Message
    LOGICAL :: Options_Present
    LOGICAL :: Check_Input
    INTEGER :: Status_FWD, Status_K
    INTEGER :: n, n_Sensors,  SensorIndex
    INTEGER :: l, n_Channels, ChannelIndex
    INTEGER :: m, n_Profiles
    INTEGER :: na
    INTEGER :: ln
    ! Component variables
    TYPE(CRTM_AtmOptics_type) :: AtmOptics, AtmOptics_K
    TYPE(ASVar_type) :: ASvar


    ! ------
    ! SET UP
    ! ------
    Error_Status = SUCCESS


    ! If no sensors or channels, simply return
    n_Sensors  = SIZE(ChannelInfo)
    n_Channels = SUM(CRTM_ChannelInfo_n_Channels(ChannelInfo))
    IF ( n_Sensors == 0 .OR. n_Channels == 0 ) RETURN


    ! Check spectral arrays
    IF ( SIZE(RTSolution  ,DIM=1) &lt; n_Channels .OR. &amp;
         SIZE(Atmosphere_K,DIM=1) &lt; n_Channels .OR. &amp;
         SIZE(RTSolution_K,DIM=1) &lt; n_Channels      ) THEN
      Error_Status = FAILURE
      WRITE( Message,'("RTSolution and K-matrix (Atm,RT) structure arrays too small (",&amp;
             &amp;2(i0,","),i0,") for the number of requested channels (",i0,")")') &amp;
             SIZE(RTSolution  ,DIM=1), &amp;
             SIZE(Atmosphere_K,DIM=1), &amp;
             SIZE(RTSolution_K,DIM=1), &amp;
             n_Channels
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! Check the number of profiles
    ! ...Number of atmospheric profiles.
    n_Profiles = SIZE(Atmosphere)
    ! ...Check the profile dimensionality of the other mandatory arguments
    IF ( SIZE(RTSolution_K,DIM=2) /= n_Profiles .OR. &amp;
         SIZE(Atmosphere_K,DIM=2) /= n_Profiles .OR. &amp;
         SIZE(RTSolution  ,DIM=2) /= n_Profiles      ) THEN
      Error_Status = FAILURE
      Message = 'Inconsistent profile dimensionality for input arguments.'
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF
    ! ...Check the profile dimensionality of the other optional arguments
    Options_Present = PRESENT(Options)
    IF ( Options_Present ) THEN
      IF ( SIZE( Options ) /= n_Profiles ) THEN
        Error_Status = FAILURE
        Message = 'Inconsistent profile dimensionality for Options optional input argument.'
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
    END IF


    ! Check the RTSolution structures have been allocated
    IF ( ANY(.NOT. CRTM_RTSolution_Associated(RTSolution)) .OR. &amp;
         ANY(.NOT. CRTM_RTSolution_Associated(RTSolution_K)) ) THEN
      Error_Status = FAILURE
      Message = 'RTSolution output structure components have not been allocated'
      CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
      RETURN
    END IF


    ! ------------
    ! PROFILE LOOP
    ! ------------
    Profile_Loop: DO m = 1, n_Profiles


      ! Check the aerosol coeff. data for cases with aerosols
      IF( Atmosphere(m)%n_Aerosols &gt; 0 .AND. .NOT. CRTM_AerosolCoeff_IsLoaded() )THEN
         Error_Status = FAILURE
         WRITE( Message,'("The AerosolCoeff data must be loaded (with CRTM_Init routine) ", &amp;
                &amp;"for the aerosol case profile #",i0)' ) m
         CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
         RETURN
      END IF
      
      
      ! Check the optional Options structure argument
      Check_Input = .TRUE.
      IF (Options_Present) THEN
        Check_Input = Options(m)%Check_Input
      END IF


      ! Check the input atmosphere if required
      IF ( Check_Input ) THEN
        IF ( .NOT. CRTM_Atmosphere_IsValid( Atmosphere(m) ) ) THEN
          Error_Status = FAILURE
          WRITE( Message,'("Input data check failed for profile #",i0)' ) m
          CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
          RETURN
        END IF
      END IF


      ! Check the RTSolution layer dimensions
      IF ( ANY(RTSolution(:,m)%n_Layers   &lt; Atmosphere(m)%n_Layers) .OR. &amp;
           ANY(RTSolution_K(:,m)%n_Layers &lt; Atmosphere(m)%n_Layers) ) THEN
        Error_Status=FAILURE
        WRITE( Message,'("Number of RTSolution layers &lt; Atmosphere for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF


      ! Allocate AtmOptics based on Atmosphere dimensions
      CALL CRTM_AtmOptics_Create( AtmOptics, &amp;
                                  Atmosphere(m)%n_Layers, &amp;
                                  MAX_N_LEGENDRE_TERMS  , &amp;
                                  MAX_N_PHASE_ELEMENTS    )
      CALL CRTM_AtmOptics_Create( AtmOptics_K, &amp;
                                  Atmosphere(m)%n_Layers, &amp;
                                  MAX_N_LEGENDRE_TERMS  , &amp;
                                  MAX_N_PHASE_ELEMENTS    )
      IF ( .NOT. CRTM_AtmOptics_Associated( Atmoptics ) .OR. &amp;
           .NOT. CRTM_AtmOptics_Associated( Atmoptics_K ) ) THEN
        Error_Status = FAILURE
        WRITE( Message,'("Error allocating AtmOptics data structures for profile #",i0)' ) m
        CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
        RETURN
      END IF
      ! ...Set default number of streams
      AtmOptics%n_Legendre_Terms    = 4
      AtmOptics_K%n_Legendre_Terms = AtmOptics%n_Legendre_Terms


      ! Allocate the aerosol scattering internal variable if necessary
      IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
        CALL ASvar_Create( ASvar, &amp;
                           MAX_N_LEGENDRE_TERMS    , &amp;
                           MAX_N_PHASE_ELEMENTS    , &amp;
                           Atmosphere(m)%n_Layers  , &amp;
                           Atmosphere(m)%n_Aerosols  )
      END IF


      ! -----------
      ! SENSOR LOOP
      ! -----------
      ! Initialise channel counter for sensor(n)/channel(l) count
      ln = 0
    
      Sensor_Loop: DO n = 1, n_Sensors


        ! Shorter name
        SensorIndex = ChannelInfo(n)%Sensor_Index


        ! ------------
        ! CHANNEL LOOP
        ! ------------
        Channel_Loop: DO l = 1, ChannelInfo(n)%n_Channels
         
          ! Channel setup
          ! ...Skip channel if requested
          IF ( .NOT. ChannelInfo(n)%Process_Channel(l) ) CYCLE Channel_Loop
          ! ...Shorter name
          ChannelIndex = ChannelInfo(n)%Channel_Index(l)
          ! ...Increment the processed channel counter
          ln = ln + 1
          ! ...Assign sensor+channel information to output
          RTSolution(ln,m)%Sensor_Id        = ChannelInfo(n)%Sensor_Id
          RTSolution(ln,m)%WMO_Satellite_Id = ChannelInfo(n)%WMO_Satellite_Id
          RTSolution(ln,m)%WMO_Sensor_Id    = ChannelInfo(n)%WMO_Sensor_Id   
          RTSolution(ln,m)%Sensor_Channel   = ChannelInfo(n)%Sensor_Channel(l)
          RTSolution_K(ln,m)%Sensor_Id        = RTSolution(ln,m)%Sensor_Id       
          RTSolution_K(ln,m)%WMO_Satellite_Id = RTSolution(ln,m)%WMO_Satellite_Id
          RTSolution_K(ln,m)%WMO_Sensor_Id    = RTSolution(ln,m)%WMO_Sensor_Id   
          RTSolution_K(ln,m)%Sensor_Channel   = RTSolution(ln,m)%Sensor_Channel  


          ! Copy over atmosphere info to k-matrix output
          ! ...Climatology
          Atmosphere_K(ln,m)%Climatology = Atmosphere(m)%Climatology
          ! ...Absorber info
          Atmosphere_K(ln,m)%Absorber_Id    = Atmosphere(m)%Absorber_Id   
          Atmosphere_K(ln,m)%Absorber_Units = Atmosphere(m)%Absorber_Units
          ! ...Aerosol info
          DO na = 1, Atmosphere(m)%n_Aerosols
            Atmosphere_K(ln,m)%Aerosol(na)%Type = Atmosphere(m)%Aerosol(na)%Type      
          END DO

          
          ! Initialisations
          CALL CRTM_AtmOptics_Zero( AtmOptics )
          CALL CRTM_AtmOptics_Zero( AtmOptics_K )
          AtmOptics_K%Optical_Depth = RTSolution_K(ln,m)%Layer_Optical_Depth(1:Atmosphere(m)%n_Layers)


          ! Compute the aerosol absorption/scattering properties
          IF ( Atmosphere(m)%n_Aerosols &gt; 0 ) THEN
            Status_FWD = CRTM_Compute_AerosolScatter( Atmosphere(m), &amp;  ! Input
                                                      SensorIndex  , &amp;  ! Input
                                                      ChannelIndex , &amp;  ! Input
                                                      AtmOptics    , &amp;  ! In/Output
                                                      ASVar          )  ! Internal variable output
            Status_K = CRTM_Compute_AerosolScatter_AD( Atmosphere(m)     , &amp;  ! FWD Input
                                                       AtmOptics         , &amp;  ! FWD Input
                                                       AtmOptics_K       , &amp;  ! AD  Input
                                                       SensorIndex       , &amp;  ! Input
                                                       ChannelIndex      , &amp;  ! Input
                                                       Atmosphere_K(ln,m), &amp;  ! AD  Output
                                                       ASVar               )  ! Internal variable input
            IF ( Status_FWD /= SUCCESS .OR. Status_K /= SUCCESS) THEN
              Error_Status = FAILURE
              WRITE( Message,'("Error computing AerosolScatter for ",a,&amp;
                     &amp;", channel ",i0,", profile #",i0)' ) &amp;
                     TRIM(ChannelInfo(n)%Sensor_ID), ChannelInfo(n)%Sensor_Channel(l), m
              CALL Display_Message( ROUTINE_NAME, Message, Error_Status )
              RETURN
            END IF
          END IF


          ! Save the nadir optical depths
          RTSolution(ln,m)%Layer_Optical_Depth(1:Atmosphere(m)%n_Layers) = AtmOptics%Optical_Depth
         
        END DO Channel_Loop

      END DO Sensor_Loop
      

      ! Deallocate local sensor independent data structures
      CALL CRTM_AtmOptics_Destroy( AtmOptics )
      CALL CRTM_AtmOptics_Destroy( AtmOptics_K )

    END DO Profile_Loop

  END FUNCTION CRTM_AOD_K


!--------------------------------------------------------------------------------
!:sdoc+:
!
! NAME:
!       CRTM_AOD_Version
!
! PURPOSE:
!       Subroutine to return the module version information.
!
! CALLING SEQUENCE:
!       CALL CRTM_AOD_Version( Id )
!
! OUTPUTS:
!       Id:            Character string containing the version Id information
!                      for the module.
!                      UNITS:      N/A
!                      TYPE:       CHARACTER(*)
!                      DIMENSION:  Scalar
!                      ATTRIBUTES: INTENT(OUT)
!
!:sdoc-:
!--------------------------------------------------------------------------------

<A NAME='CRTM_AOD_VERSION'><A href='../../html_code/crtm/CRTM_AOD_Module.f90.html#CRTM_AOD_VERSION' TARGET='top_target'><IMG SRC="../../gif/bar_red.gif" border=0></A>

  SUBROUTINE CRTM_AOD_Version( Id )
    CHARACTER(*), INTENT(OUT) :: Id
    Id = MODULE_VERSION_ID
  END SUBROUTINE CRTM_AOD_Version

END MODULE CRTM_AOD_Module