Radiative Transfer Code for a Plane Parallel Multiple Scattering/ Absorbing Atmosphere


/**************************************************************************************
*   These C/C++ routines calculate the source function produced by multiple scattering  and absorption 
*   in a plane-parallel atmosphere, and the intensities on a given line in the medium (in both directions).
*   The former program is based on an iterative solution of the radiative transfer equation, the latter
*   is a straightforward integration based on the known optical depth and source function vectors
*   (see the page https://www.plasmaphysics.org.uk/radiative_transfer.htm for more).
*
*  Input parameters for the routine plansource are:
*    mode:  'c' for continuum- (monochromatic), 'l' for line-scattering
*    N:  number of layers (with the source function assumed constant in each)
*    tau:  vertical optical depth of scatterers (vector of length N+1)
*    t:  vertical optical depth of absorbers (vector of length N+1)
*    S0:  initial source function (vector of length N)
*    xd:  base interval for Gaussian Quadrature (recommended =0.5) (for the kernel function routine)
*    epsi:  required relative accuracy (see also the notes for the kernel function routine)
*
*  Output parameters for plansource are:
*    ST:  total source function (vector of length N)
*    ni:  number of iterations used
*
*
*  Input parameters for the routine intens are:
*    mode:  'c' for continuum- (monochromatic), 'l' for line-scattering
*    N:  number of layers/intervals (with the source function assumed constant in each)
*    tau:  line-of-sight optical depth of scatterers (vector of length N+1)
*    t:  line-of-sight optical depth of absorbers (vector of length N+1)
*    S:  source function (vector of length N)
*    xd:  base interval for Gaussian Quadrature (recommended =0.5) (for the kernel function routine)
*    epsi:  required relative accuracy (see also the notes for the kernel function routine)
*
*  Output parameters for intens are:
*    Iu:  up-looking intensities (vector of length N)    (referred to bottom of each layer/interval)
*    Id:  down-looking intensities (vector of length N)  (referred to top of each layer/interval)
*
*   (due to its design, intens is not restricted to a plane-parallel atmosphere, but can
*   be used for arbitray geometries as long as the optical depth and source function
*   along the line of sight is specified).
*
*   All variables are of double precision floating point type, apart from N and ni (integer) and 'mode'.
*
*   Required here are additonally the routines for the calculation of the kernel functions, which for
*   the case of continuum- (monochromatic) or spectral line transfer with a Gaussian profile 
*   can be found under https://www.plasmaphysics.org.uk/programs/kernels.htm .
*   The code should work with different kernel functions (i.e. different line profiles) as well 
*   (provided these are properly normalized), but this has not been tested.
*
*
*   In case of any questions or problems, please let me know
*   (for contact details see  https://www.plasmaphysics.org.uk/feedback.htm ).
*
*    Thomas Smid, June 2008
*                 July 2008 (added 'mode' option for line/continuum scattering,
*                            changed Neumann series iteration to direct iteration
*                            because of better convergence if absorption is significant)
*                 August 2008 (split the program into two different routines
*                             for the source functions and intensities
*                             for better flexibility and efficiency)
*****************************************************************************************/


void plansource(char mode, int N, double tau[], double t[], double S0[], double xd, double epsi,
               double S[], int& ni)  {

   double S1[N],KS[N][N],al[N],q[N];
   double convgc,ddtau,ddt,jktau,jkt,jktau1,jkt1,dev,devmax;
   int j,k;


/* calculate absorbing/scattering optical depth ratio, and initialize source function */

   for (j=0; j<=N-1; j=j+1)  {
      al[j]=(t[j+1]-t[j])/(tau[j+1]-tau[j]) ;
      S[j]=S0[j];
   }


/* assign the kernel function coefficients  */

   for (j=0; j<=N-1; j=j+1)  {  // KS referred to midpoint of layer
      for (k=0; k<=N-1; k=k+1)  {
        if (k>=j) {ddtau=-(tau[k+1]-tau[k])/2; ddt=-(t[k+1]-t[k])/2; } 
         else {ddtau=(tau[k+1]-tau[k])/2; ddt=(t[k+1]-t[k])/2; }
        jktau=fabs(tau[j]-tau[k])+ddtau; jkt=fabs(t[j]-t[k])+ddt;
        if (k==j) {jktau=0; jkt=0; }
        jktau1=fabs(tau[j]-tau[k+1])+ddtau; jkt1=fabs(t[j]-t[k+1])+ddt;
        KS[j][k]= K2(mode,jktau,jkt,al[k],xd,epsi)- K2(mode,jktau1,jkt1,al[k],xd,epsi) ;
        if (KS[j][k]<0) {KS[j][k]=-KS[j][k];}
      }
       KS[j][j]=2*KS[j][j];
   }



/* iteration loop  */

   convgc=1; ni=0;

   while (convgc >= epsi) {

      for (j=0; j<=N-1; j=j+1)  {
        S1[j]=S[j];
      }

      for (j=0; j<=N-1; j=j+1)  {
        S[j]=0;
        for (k=0; k<=N-1; k=k+1)  {
         if (k!=j) { S[j]=S[j]+ S1[k]*KS[j][k]; }
        }
        S[j]=(S[j]+S0[j])/(1-KS[j][j]);
      }


      devmax=0;
      for (j=0; j<=N-1; j=j+1)  {
       if (S1[j]!=0) { dev=fabs(S[j]/S1[j]-1.0); if (dev>devmax) devmax=dev; }
      }
      ni=ni+1;
      convgc=devmax*ni;  // *ni in order to assure actual convergence

   }     // end iteration loop



}





void intens(char mode, int N, double tau[], double t[], double S[], double xd, double epsi,
               double Iu[], double Id[])  {

   double KI[N][N],al[N];
   double ddtau,ddt,jktau,jkt,jktau1,jkt1;
   int j,k;

   
/* calculate absorbing/scattering optical depth ratio */

   for (j=0; j<=N-1; j=j+1)  {
      al[j]=(t[j+1]-t[j])/(tau[j+1]-tau[j]) ;
   }

 
/* calculate transmission function matrix */

  for (j=0; j<=N-1; j=j+1)  {  // KI referred to top/bottom of layer
      for (k=0; k<=N-1; k=k+1)  {
        if (k>=j) {jktau=fabs(tau[k]-tau[j]); jkt=fabs(t[k]-t[j]);
                   jktau1=fabs(tau[k+1]-tau[j]); jkt1=fabs(t[k+1]-t[j]);
                  }
             else {jktau=fabs(tau[j+1]-tau[k]); jkt=fabs(t[j+1]-t[k]);
                   jktau1=fabs(tau[j+1]-tau[k+1]); jkt1=fabs(t[j+1]-t[k+1]);
                  }

        KI[j][k]= Z(mode,jktau,jkt,al[k],xd,epsi)- Z(mode,jktau1,jkt1,al[k],xd,epsi) ;
        if (KI[j][k]<0) {KI[j][k]=-KI[j][k];}
      }
   }



/* calculate up- and down looking intensities, 
    Iu is referred here to the bottom of each layer/interval, Id to the top,
    (with 'top to bottom' defined as being in the direction of increasing j  ) */

   for (j=0; j<=N-1; j=j+1)  {
     Iu[j]=0; Id[j]=0;
     for (k=0; k<=N-1; k=k+1)  {
      if (k>=j) {Id[j]=Id[j]+S[k]*KI[j][k]; }
      if (k<=j) {Iu[j]=Iu[j]+S[k]*KI[j][k]; }
     }
   }


}


(Back To) Description

Programs Home

Home