Skip to main content Link Search Menu Expand Document (external link)

Radiative Transfer

Table of contents
  1. Code
    1. Main program
    2. Aux. programs
  2. Output
    1. Figures
    2. Text

Code

Main program

sp_14_03.m | View on GitHub
% Supplemental program 14.3

% --------------------------------------------
% Calculate and graph light profiles in canopy
% --------------------------------------------

% --- Model parameters

params.numrad = 2;         % Number of wavebands (visible, near-infrared)
params.vis = 1;            % Array index for visible waveband
params.nir = 2;            % Array index for near-infrared waveband
params.sun = 1;            % Array index for sunlit leaf
params.sha = 2;            % Array index for shaded leaf
params.npts = 1;           % Number of grid points to process

% --- Model options

  light = 'Norman';        % Use Norman radiative transfer
% light = 'Goudriaan';     % Use Goudriaan radiative transfer
% light = 'TwoStream';     % Use two-stream approximation radiative transfer

  canopy_type = 'dense';   % High leaf area index
% canopy_type = 'sparse';  % Low leaf area index

% --- Define plant canopy

for p = 1:params.npts

   % Set canopy LAI, layer LAI increment, and number of layers

   switch canopy_type
   case 'dense'
      lai_inc = 0.1;                                % Leaf area index for each layer
      canopy.lai(p) = 6;                            % Leaf area index of canopy (m2/m2)
   case 'sparse'
      lai_inc = 0.05;                               % Leaf area index for each layer
      canopy.lai(p) = 1;                            % Leaf area index of canopy (m2/m2)
   end
   canopy.nveg(p) = round(canopy.lai(p) / lai_inc); % Number of leaf layers in canopy

   % Minimum number of layers for Norman radiation

   switch light
   case 'Norman'
      if (canopy.nveg(p) < 10)
         canopy.nveg(p) = 10;
         lai_inc = canopy.lai(p) / canopy.nveg(p);
      end
   end

   % Set array indices for canopy layers

   canopy.nsoi(p) = 1;                                     % First layer is soil
   canopy.nbot(p) = canopy.nsoi(p) + 1;                    % Bottom leaf layer
   canopy.ntop(p) = canopy.nbot(p) + canopy.nveg(p) - 1;   % Top leaf layer

   % Set LAI of each layer

   for iv = canopy.nbot(p):canopy.ntop(p)
      canopy.dlai(p,iv) = lai_inc;
   end

   % Cumulative leaf area index (from canopy top) at mid-layer

   for iv = canopy.ntop(p): -1: canopy.nbot(p)
      if (iv == canopy.ntop(p))
         canopy.sumlai(p,iv) = 0.5 * canopy.dlai(p,iv);
      else
         canopy.sumlai(p,iv) = canopy.sumlai(p,iv+1) + canopy.dlai(p,iv);
      end
   end

   % Clumping index

   canopy.clumpfac(p) = 1;

end

% --- Atmospheric solar radiation. Solar radiation is given as a unit of visible radiation
% and a unit of near-infrared radiation, both split into direct and diffuse components.

for p = 1:params.npts
   atmos.solar_zenith(p) = 30 * (pi / 180);     % Solar zentih angle (radians)
   atmos.swskyb(p,params.vis) = 0.8;            % Direct beam solar radiation for visible waveband (W/m2)
   atmos.swskyd(p,params.vis) = 0.2;            % Diffuse solar radiation for visible waveband (W/m2)
   atmos.swskyb(p,params.nir) = 0.8;            % Direct beam solar radiation for near-infrared waveband (W/m2)
   atmos.swskyd(p,params.nir) = 0.2;            % Diffuse solar radiation for near-infrared waveband (W/m2)
end

% --- Leaf optical properties

for p = 1:params.npts
   rho(p,params.vis) = 0.10;                    % Leaf reflectance (visible)
   rho(p,params.nir) = 0.45;                    % Leaf reflectance (near-infrared)
   tau(p,params.vis) = 0.05;                    % Leaf transmittance (visible)
   tau(p,params.nir) = 0.25;                    % Leaf transmittance (near-infrared)
   for ib = 1:params.numrad
      omega(p,ib) = rho(p,ib) + tau(p,ib);      % Leaf scattering coefficient for canopy
   end
end

% --- Soil albedo

for p = 1:params.npts
   flux.albsoib(p,params.vis) = 0.1;                         % Direct beam albedo of ground (visible)
   flux.albsoid(p,params.vis) = flux.albsoib(p,params.vis);  % Diffuse albedo of ground (visible)
   flux.albsoib(p,params.nir) = 0.2;                         % Direct beam albedo of ground (near-infrared)
   flux.albsoid(p,params.nir) = flux.albsoib(p,params.nir);  % Diffuse albedo of ground near-infrared)
end

% --- Direct beam extinction coefficient

% xl - departure of leaf angle from spherical orientation

xl = 0.00;

% Kb - direct beam extinction coefficient for canopy

for p = 1:params.npts

   % -0.4 <= xl <= 0.6

   chil(p) = min(max(xl, -0.4), 0.6);

  % Prevent near-zero xl for two-stream radiation

   switch light
   case 'TwoStream'
      if (abs(chil(p)) <= 0.01)
         chil(p) = 0.01;
      end
   end

   % Terms in Ross-Goudriaan function for gdir

   phi1(p) = 0.5 - 0.633 * chil(p) - 0.330 * chil(p)*chil(p);
   phi2(p) = 0.877 * (1 - 2 * phi1(p));

   % Relative projected area of leaf in the direction of solar beam

   cosz = cos(atmos.solar_zenith(p));
   gdir(p) = phi1(p) + phi2(p) * cosz;

   % Direct beam extinction coefficient

   Kb(p) = gdir(p) / cosz;

   % Prevent large Kb at low sun angle

   Kb(p) = min(Kb(p), 20);

end

% --- Sunlit and shaded portions of canopy

for p = 1:params.npts

   % Sunlit and shaded fraction of leaf layer

   for iv = canopy.nbot(p):canopy.ntop(p)
      flux.fracsun(p,iv) = canopy.clumpfac(p) * exp(-Kb(p) * canopy.sumlai(p,iv) * canopy.clumpfac(p));
      flux.fracsha(p,iv) = 1 - flux.fracsun(p,iv);
   end

   % Sunlit and shaded leaf area index for canopy

   laisun(p) = (1 - exp(-Kb(p) * canopy.lai(p) * canopy.clumpfac(p))) / Kb(p);
   laisha(p) = canopy.lai(p) - laisun(p);

end

% --- Unique parameters for Norman radiative transfer

% tb - exponential transmittance of direct beam radiation through a single leaf layer

for p = 1:params.npts
   for iv = canopy.nbot(p):canopy.ntop(p)
      tb(p,iv) = exp(-Kb(p) * canopy.dlai(p,iv) * canopy.clumpfac(p));
   end
end

% td - exponential transmittance of diffuse radiation through a single leaf layer
% with thickness dlai, estimated for nine sky angles in increments of 10 degrees

for p = 1:params.npts
   for iv = canopy.nbot(p):canopy.ntop(p)
      td(p,iv) = 0;

      for j = 1:9

         % Sky angles (5, 15, 25, 35, 45, 55, 65, 75, 85)

         angle = (5 + (j - 1) * 10) * pi / 180;

         % Relative projected area of leaf in the direction of sky angle

         gdirj = phi1(p) + phi2(p) * cos(angle);

         % Sum transmittance

         td(p,iv) = td(p,iv) ...
         + exp(-gdirj / cos(angle) * canopy.dlai(p,iv) * canopy.clumpfac(p)) * sin(angle) * cos(angle);

      end
      td(p,iv) = td(p,iv) * 2 * (10 * pi / 180);

   end
end

% tbcum - direct beam transmittance uses cumulative lai above layer i to
% give unscattered direct beam onto layer i

for p = 1:params.npts
   cumlai = 0;
   iv = canopy.ntop(p);
   tbcum(p,iv) = 1;
   for iv = canopy.ntop(p): -1: canopy.nbot(p)
      cumlai = cumlai + canopy.dlai(p,iv);
      tbcum(p,iv-1) = exp(-Kb(p) * cumlai * canopy.clumpfac(p));
   end
end

% --- Unique parameters for Goudriaan radiative transfer

% Kd - diffuse extinction coefficient for canopy, estimated for nine sky angles
% in increments of 10 degrees

for p = 1:params.npts
   Kd(p) = 0;

   for j = 1:9

      % Sky angles (5, 15, 25, 35, 45, 55, 65, 75, 85)

      angle = (5 + (j - 1) * 10) * pi / 180;

      % Relative projected area of leaf in the direction of sky angle

      gdirj = phi1(p) + phi2(p) * cos(angle);

      % Sum transmittance

      Kd(p) = Kd(p) + exp(-gdirj / cos(angle) * canopy.lai(p) * canopy.clumpfac(p)) * sin(angle) * cos(angle);

   end

   Kd(p) = Kd(p) * 2 * (10 * pi / 180);

   % Convert transmittance to extinction coefficient

   if (canopy.lai(p) > 0)
      Kd(p) = -log(Kd(p)) / (canopy.lai(p) * canopy.clumpfac(p));
   else
      Kd(p) = 0;
   end

end

for ib = 1:params.numrad
   for p = 1:params.npts

      % Adjust Kb and Kd for scattering

      Kbm(p,ib) = Kb(p) * sqrt(1 - omega(p,ib));
      Kdm(p,ib) = Kd(p) * sqrt(1 - omega(p,ib));

      % albvegh - vegetation albedo for horizontal leaves

      albvegh = (1 - sqrt(1 - omega(p,ib))) / (1 + sqrt(1 - omega(p,ib)));

      % albvegb - direct beam vegetation albedo for non-horizontal leaves
      
      albvegb = 2 * Kb(p) / (Kb(p) + Kd(p)) * albvegh;

      % albvegd - diffuse vegetation albedo for non-horizontal leaves, calculated by summing albedo over 9 sky angles

      albvegd = 0;
      for j = 1:9

         % Sky angles (5, 15, 25, 35, 45, 55, 65, 75, 85)

         angle = (5 + (j - 1) * 10) * pi / 180;

         % Relative projected area of leaf in the direction of sky angle

         gdirj = phi1(p) + phi2(p) * cos(angle);

         % Kb for sky angle j

         Kbj = gdirj / cos(angle);

         % Direct beam albedo for sky angle j

         albvegbj = 2 * Kbj / (Kbj + Kd(p)) * albvegh;

         % Sum albedo

         albvegd = albvegd + albvegbj * sin(angle) * cos(angle);

      end
      albvegd = albvegd * 2 * (10 * pi / 180);
   
      % Effective canopy albedo, including soil
      % albcanb - direct beam albedo above canopy
      % albcand - diffuse albedo above canopy
      
      albcanb(p,ib) = albvegb ...
      + (flux.albsoib(p,ib) - albvegb) * exp(-2 * Kbm(p,ib) * canopy.lai(p) * canopy.clumpfac(p));
      albcand(p,ib) = albvegd ...
      + (flux.albsoid(p,ib) - albvegd) * exp(-2 * Kdm(p,ib) * canopy.lai(p) * canopy.clumpfac(p));

   end
end

% --- Two-stream parameters

for p = 1:params.npts

   % avmu - average inverse diffuse optical depth per unit leaf area

   avmu(p) = ( 1 - phi1(p)/phi2(p) * log((phi1(p)+phi2(p))/phi1(p)) ) / phi2(p);

   % Upscatter parameters

   for ib = 1:params.numrad

      % betad - upscatter parameter for diffuse radiation

      betad(p,ib) = 0.5 / omega(p,ib) * ( rho(p,ib) + tau(p,ib) + (rho(p,ib)-tau(p,ib)) * ((1+chil(p))/2)^2 );

      % betab - upscatter parameter for direct beam radiation

      cosz = cos(atmos.solar_zenith(p));
      tmp0 = gdir(p) + phi2(p) * cosz;
      tmp1 = phi1(p) * cosz;
      tmp2 = 1 - tmp1/tmp0 * log((tmp1+tmp0)/tmp1);
      asu = 0.5 * omega(p,ib) * gdir(p) / tmp0 * tmp2;
      betab(p,ib) = (1 + avmu(p)*Kb(p)) / (omega(p,ib)*avmu(p)*Kb(p)) * asu;

   end
end

% --- Light profile through canopy

switch light
   case 'Norman'
   [flux] = NormanRadiation (rho, tau, omega, td, tb, tbcum, params, canopy, atmos, flux);
   case 'Goudriaan'
   [flux] = GoudriaanRadiation (omega, Kb, Kbm, Kdm, albcanb, albcand, params, canopy, atmos, flux);
   case 'TwoStream'
   [flux] = TwoStreamRadiation (omega, avmu, betad, betab, Kb, params, canopy, atmos, flux);
end

% Absorbed PAR per unit sunlit and shaded leaf area (umol photon/m2 leaf/s)

for p = 1:params.npts
   for iv = canopy.nbot(p):canopy.ntop(p)
      flux.apar(p,iv,params.sun) = flux.swleaf(p,iv,params.sun,params.vis) * 4.6;
      flux.apar(p,iv,params.sha) = flux.swleaf(p,iv,params.sha,params.vis) * 4.6;
   end
end

% --- Write formatted output to file, from top layer to bottom layer

p = 1;
for iv = canopy.ntop(p): -1: canopy.nbot(p)
   j = iv - 1;
   k = canopy.nveg(p) - j + 2;
   a1(j) = j;
   a2(j) = canopy.sumlai(p,k);
   a3(j) = flux.swleaf(p,k,params.sun,params.vis);
   a4(j) = flux.swleaf(p,k,params.sha,params.vis);
   a5(j) = flux.swleaf(p,k,params.sun,params.nir);
   a6(j) = flux.swleaf(p,k,params.sha,params.nir);
end

A = [a1; a2; a3; a4; a5; a6];

fileID = fopen('data.txt','w');
fprintf(fileID,'%12s %12s %12s %12s %12s %12s\n','layer','lai','sun','sha','sun','sha');
fprintf(fileID,'%12.0f %12.4f %12.6f %12.6f %12.6f %12.6f\n', A);
fclose(fileID);

% --- Make graph

plot(a3,a2,'b-',a4,a2,'r-',a5,a2,'b--',a6,a2,'r--')
set(gca,'YDir','reverse')
title(light)
xlabel('Fraction absorbed')
ylabel('Cumulative leaf area index')
legend('sun, vis','sha, vis','sun, nir','sha, nir','Location','best')

Aux. programs

GoudriaanRadiation.m | View on GitHub
function [flux] = GoudriaanRadiation (omega, Kb, Kbm, Kdm, albcanb, albcand, params, canopy, atmos, flux)

% Compute solar radiation transfer through canopy using Goudriaan parameterization.
% Radiative transfer of Goudriaan (1977), as described by Goudriaan and van Laar
% (1994) and implemented in the plant canopy models of Spitters (1986),
% de Pury and Farquhar (1997), Wang and Leuning (1998), and  Wang (2003).
% This uses the multilayer form of the equations, with constant optical
% properties through the canopy.

% -----------------------------------------------------------------------
% Input
% omega           ! Leaf scattering coefficient for canopy
% Kb              ! Direct beam extinction coefficient for canopy
% Kbm             ! Direct beam extinction coefficient for canopy adjusted for scattering
% Kdm             ! Diffuse extinction coefficient for canopy adjusted for scattering
% albcanb         ! Direct beam albedo above canopy
% albcand         ! Diffuse albedo above canopy
% params.numrad   ! Number of wavebands
% params.npts     ! Number of grid points to process
% params.sun      ! Index for sunlit leaf
% params.sha      ! Index for shaded leaf
% canopy.ntop     ! Index for top leaf layer
% canopy.nbot     ! Index for bottom leaf layer
% canopy.dlai     ! Layer leaf area index (m2/m2)
% canopy.lai      ! Leaf area index of canopy (m2/m2)
% canopy.sumlai   ! Cumulative leaf area index for canopy layer (m2/m2)
% canopy.clumpfac ! Leaf clumping index
% atmos.swskyb    ! Atmospheric direct beam solar radiation (W/m2)
% atmos.swskyd    ! Atmospheric diffuse solar radiation (W/m2)
% flux.fracsun    ! Sunlit fraction of canopy layer
% flux.fracsha    ! Shaded fraction of canopy layer
%
% Output
% flux.swleaf     ! Leaf absorbed solar radiation (W/m2 leaf)
% flux.swveg      ! Absorbed solar radiation, vegetation (W/m2)
% flux.swvegsun   ! Absorbed solar radiation, sunlit canopy (W/m2)
% flux.swvegsha   ! Absorbed solar radiation, shaded canopy (W/m2)
% flux.swsoi      ! Absorbed solar radiation, ground (W/m2)
% flux.albcan     ! Albedo above canopy
% -----------------------------------------------------------------------

% --- Process each waveband (ib) for each grid point (p)

for ib = 1:params.numrad
   for p = 1:params.npts

      % Zero terms that are summed over all layers

      icsun = 0;
      icsha = 0;
      icshad = 0;
      icshabs = 0;
      icsund = 0;
      icsunbs = 0;
      icsunb = 0;

      % Process each canopy layer (iv)

      for iv = canopy.nbot(p):canopy.ntop(p)

         % --- Calculate leaf fluxes. Fluxes are per unit leaf area (W/m2 leaf)
         
         % ild - absorbed diffuse flux per unit leaf area at cumulative LAI, 
         % average for all leaves (J / m2 leaf / s)

         ild = (1 - albcand(p,ib)) * atmos.swskyd(p,ib) * Kdm(p,ib) * canopy.clumpfac(p) ...
             * exp(-Kdm(p,ib) * canopy.sumlai(p,iv) * canopy.clumpfac(p));

         % ilb - absorbed direct beam flux (total with scattering) per unit leaf area 
         % at cumulative LAI, average for all leaves (J / m2 leaf / s)

         ilb = (1 - albcanb(p,ib)) * atmos.swskyb(p,ib) * Kbm(p,ib) * canopy.clumpfac(p) ...
             * exp(-Kbm(p,ib) * canopy.sumlai(p,iv) * canopy.clumpfac(p));

         % ilbb - absorbed direct beam flux (unscattered direct component) per unit leaf area 
         % at cumulative LAI, average for all leaves (J / m2 leaf / s)

         ilbb = (1 - omega(p,ib)) * atmos.swskyb(p,ib) * Kb(p) * canopy.clumpfac(p) ...
              * exp(-Kb(p) * canopy.sumlai(p,iv) * canopy.clumpfac(p));

         % ilbs - absorbed direct beam flux (scattered direct component) per unit leaf area 
         % at cumulative LAI, average for all leaves (J / m2 leaf / s)

         ilbs = ilb - ilbb;

         % ilsha - total absorbed flux (shaded leaves) per unit shaded leaf area 
         % at cumulative LAI (J / m2 leaf / s)

         ilsha = ild + ilbs;

         % ilsun - total absorbed flux (sunlit leaves) per unit sunlit leaf area 
         % at cumulative LAI (J / m2 leaf / s)

         ilsun = ilsha + Kb(p) * (1 - omega(p,ib)) * atmos.swskyb(p,ib);

         % Save solar radiation absorbed by sunlit and shaded leaves

         flux.swleaf(p,iv,params.sun,ib) = ilsun;
         flux.swleaf(p,iv,params.sha,ib) = ilsha;

         % --- Canopy summation and soil absorption. Fluxes are per unit ground area (W/m2 ground area)

         % icsun - absorbed solar radiation, sunlit canopy (W/m2)
         % icsha - absorbed solar radiation, shaded canopy (W/m2)

         icsun = icsun + ilsun * flux.fracsun(p,iv) * canopy.dlai(p,iv);
         icsha = icsha + ilsha * flux.fracsha(p,iv) * canopy.dlai(p,iv);

         % icshad  - diffuse radiation absorbed by shaded leaves (W/m2)
         % icshabs - scattered direct beam radiation absorbed by shaded leaves (W/m2)

         icshad = icshad + ild * flux.fracsha(p,iv) * canopy.dlai(p,iv);
         icshabs = icshabs + ilbs * flux.fracsha(p,iv) * canopy.dlai(p,iv);

         % icsund  - diffuse radiation absorbed by sunlit leaves (W/m2)
         % icsunbs - scattered direct beam radiation absorbed by sunlit leaves (W/m2)
         % icsunb  - direct beam radiation absorbed by sunlit leaves (W/m2)

         icsund = icsund + ild * flux.fracsun(p,iv) * canopy.dlai(p,iv);
         icsunbs = icsunbs + ilbs * flux.fracsun(p,iv) * canopy.dlai(p,iv);
         icsunb = icsunb + Kb(p) * (1 - omega(p,ib)) * atmos.swskyb(p,ib) * flux.fracsun(p,iv) * canopy.dlai(p,iv);

      end

      % Solar radiation absorbed by vegetation (W/m2)

      sabv = icsun + icsha;

      % Solar radiation absorbed by ground (W/m2)

      sabg = atmos.swskyb(p,ib) * (1 - albcanb(p,ib)) * exp(-Kbm(p,ib)*canopy.lai(p)*canopy.clumpfac(p)) + ...
             atmos.swskyd(p,ib) * (1 - albcand(p,ib)) * exp(-Kdm(p,ib)*canopy.lai(p)*canopy.clumpfac(p));

      % Conservation check: absorbed = incoming - outgoing
      % This is not valid, because the numerical integration of leaf fluxes does not equal the analytical
      % solution (unless dlai is very small)

      suminc = atmos.swskyb(p,ib) + atmos.swskyd(p,ib);
      sumabs = sabv + sabg;
      sumref = atmos.swskyb(p,ib) * albcanb(p,ib) + atmos.swskyd(p,ib) * albcand(p,ib);

      err = suminc - (sumref + sumabs);
      err = 0;
      if (abs(err) > 1e-03)
         fprintf('err = %15.5f\n',err)
         fprintf('suminc = %15.5f\n',suminc)
         fprintf('sumref = %15.5f\n',sumref)
         fprintf('sumabs = %15.5f\n',sumabs)
         error ('GoudriaanRadiation: Solar radiation conservation error')
      end

      % --- Save necessary radiative fluxes

      % Albedo

      suminc = atmos.swskyb(p,ib) + atmos.swskyd(p,ib);
      sumref = atmos.swskyb(p,ib) * albcanb(p,ib) + atmos.swskyd(p,ib) * albcand(p,ib);
      if (suminc > 0)
         flux.albcan(p,ib) = sumref / suminc;
      else
         flux.albcan(p,ib) = 0;
      end

      % Solar radiation absorbed by canopy

      flux.swveg(p,ib) = sabv;
      flux.swvegsun(p,ib) = icsun;
      flux.swvegsha(p,ib) = icsha;

      % Solar radiation absorbed by ground (soil)

      flux.swsoi(p,ib) = sabg;

   end
end
NormanRadiation.m | View on GitHub
function [flux] = NormanRadiation (rho, tau, omega, td, tb, tbcum, params, canopy, atmos, flux)

% Compute solar radiation transfer through canopy using Norman (1979)

% -----------------------------------------------------------------------
% Input
% rho            ! Leaf reflectance
% tau            ! Leaf transmittance
% omega          ! Leaf scattering coefficient
% td             ! Exponential transmittance of diffuse radiation through a single leaf layer
% tb             ! Exponential transmittance of direct beam radiation through a single leaf layer
% tbcum          ! Cumulative exponential transmittance of direct beam onto a canopy layer
% params.numrad  ! Number of wavebands
% params.npts    ! Number of grid points to process
% params.sun     ! Index for sunlit leaf
% params.sha     ! Index for shaded leaf
% canopy.ntop    ! Index for top leaf layer
% canopy.nbot    ! Index for bottom leaf layer
% canopy.nsoi    ! First canopy layer is soil
% canopy.dlai    ! Layer leaf area index (m2/m2)
% atmos.swskyb   ! Atmospheric direct beam solar radiation (W/m2)
% atmos.swskyd   ! Atmospheric diffuse solar radiation (W/m2)
% flux.fracsun   ! Sunlit fraction of canopy layer
% flux.fracsha   ! Shaded fraction of canopy layer
% flux.albsoib   ! Direct beam albedo of ground (soil)
% flux.albsoid   ! Diffuse albedo of ground (soil)
%
% Output
% flux.swleaf    ! Leaf absorbed solar radiation (W/m2 leaf)
% flux.swveg     ! Absorbed solar radiation, vegetation (W/m2)
% flux.swvegsun  ! Absorbed solar radiation, sunlit canopy (W/m2)
% flux.swvegsha  ! Absorbed solar radiation, shaded canopy (W/m2)
% flux.swsoi     ! Absorbed solar radiation, ground (W/m2)
% flux.albcan    ! Albedo above canopy
% -----------------------------------------------------------------------

% --- Set up tridiagonal matrix

for ib = 1:params.numrad   % Process each waveband
   for p = 1:params.npts   % Process each grid point

      iv = canopy.nsoi(p);
      swup(iv) = 0;
      swdn(iv) = 0;
      for iv = canopy.nbot(p):canopy.ntop(p)
         swup(iv) = 0;
         swdn(iv) = 0;
      end

      % There are two equations for each canopy layer and the soil. The first
      % equation is the upward flux and the second equation is the downward flux. 

      m = 0; % Initialize equation index for tridiagonal matrix

      % Soil: upward flux

      iv = canopy.nsoi(p);
      m = m + 1;
      a(m) = 0;
      b(m) = 1;
      c(m) = -flux.albsoid(p,ib);
      d(m) = atmos.swskyb(p,ib) * tbcum(p,iv) * flux.albsoib(p,ib);

      % Soil: downward flux

      refld = (1 - td(p,iv+1)) * rho(p,ib);
      trand = (1 - td(p,iv+1)) * tau(p,ib) + td(p,iv+1);
      aiv = refld - trand * trand / refld;
      biv = trand / refld;

      m = m + 1;
      a(m) = -aiv;
      b(m) = 1;
      c(m) = -biv;
      d(m) = atmos.swskyb(p,ib) * tbcum(p,iv+1) * (1 - tb(p,iv+1)) * (tau(p,ib) - rho(p,ib) * biv);

      % Leaf layers, excluding top layer

      for iv = canopy.nbot(p):canopy.ntop(p)-1

         % Upward flux

         refld = (1 - td(p,iv)) * rho(p,ib);
         trand = (1 - td(p,iv)) * tau(p,ib) + td(p,iv);
         fiv = refld - trand * trand / refld;
         eiv = trand / refld;

         m = m + 1;
         a(m) = -eiv;
         b(m) = 1;
         c(m) = -fiv;
         d(m) = atmos.swskyb(p,ib) * tbcum(p,iv) * (1 - tb(p,iv)) * (rho(p,ib) - tau(p,ib) * eiv);

         % Downward flux

         refld = (1 - td(p,iv+1)) * rho(p,ib);
         trand = (1 - td(p,iv+1)) * tau(p,ib) + td(p,iv+1);
         aiv = refld - trand * trand / refld;
         biv = trand / refld;

         m = m + 1;
         a(m) = -aiv;
         b(m) = 1;
         c(m) = -biv;
         d(m) = atmos.swskyb(p,ib) * tbcum(p,iv+1) * (1 - tb(p,iv+1)) * (tau(p,ib) - rho(p,ib) * biv);

      end

      % Top canopy layer: upward flux

      iv = canopy.ntop(p);
      refld = (1 - td(p,iv)) * rho(p,ib);
      trand = (1 - td(p,iv)) * tau(p,ib) + td(p,iv);
      fiv = refld - trand * trand / refld;
      eiv = trand / refld;

      m = m + 1;
      a(m) = -eiv;
      b(m) = 1;
      c(m) = -fiv;
      d(m) = atmos.swskyb(p,ib) * tbcum(p,iv) * (1 - tb(p,iv)) * (rho(p,ib) - tau(p,ib) * eiv);

      % Top canopy layer: downward flux

      m = m + 1;
      a(m) = 0;
      b(m) = 1;
      c(m) = 0;
      d(m) = atmos.swskyd(p,ib);

      % --- Solve tridiagonal equations for fluxes

      [u] = tridiagonal_solver (a, b, c, d, m);

      % Now copy the solution (u) to the upward (swup) and downward (swdn) fluxes for each layer
      % swup - Upward diffuse solar flux above layer
      % swdn - Downward diffuse solar flux onto layer

      m = 0;

      % Soil fluxes

      iv = canopy.nsoi(p);
      m = m + 1;
      swup(iv) = u(m);
      m = m + 1;
      swdn(iv) = u(m);

      % Leaf layer fluxes

      for iv = canopy.nbot(p):canopy.ntop(p)
         m = m + 1;
         swup(iv) = u(m);
         m = m + 1;
         swdn(iv) = u(m);
      end

      % --- Compute flux densities

      % Absorbed direct beam and diffuse for ground (soil)

      iv = canopy.nsoi(p);
      direct = atmos.swskyb(p,ib) * tbcum(p,iv) * (1 - flux.albsoib(p,ib));
      diffuse = swdn(iv) * (1 - flux.albsoid(p,ib));
      flux.swsoi(p,ib) = direct + diffuse;

      % Absorbed direct beam and diffuse for each leaf layer and sum
      % for all leaf layers

      flux.swveg(p,ib) = 0;
      flux.swvegsun(p,ib) = 0;
      flux.swvegsha(p,ib) = 0;

      for iv = canopy.nbot(p):canopy.ntop(p)

         % Per unit ground area (W/m2 ground)

         direct = atmos.swskyb(p,ib) * tbcum(p,iv) * (1 - tb(p,iv)) * (1 - omega(p,ib));
         diffuse = (swdn(iv) + swup(iv-1)) * (1 - td(p,iv)) * (1 - omega(p,ib));

         % Absorbed solar radiation for shaded and sunlit portions of leaf layer
         % per unit ground area (W/m2 ground)

         sun = diffuse * flux.fracsun(p,iv) + direct;
         shade = diffuse * flux.fracsha(p,iv);

         % Convert to per unit sunlit and shaded leaf area (W/m2 leaf)

         flux.swleaf(p,iv,params.sun,ib) = sun / (flux.fracsun(p,iv) * canopy.dlai(p,iv));
         flux.swleaf(p,iv,params.sha,ib) = shade / (flux.fracsha(p,iv) * canopy.dlai(p,iv));

         % Sum fluxes over all leaf layers

         flux.swveg(p,ib) = flux.swveg(p,ib) + (direct + diffuse);
         flux.swvegsun(p,ib) = flux.swvegsun(p,ib) + sun;
         flux.swvegsha(p,ib) = flux.swvegsha(p,ib) + shade;

      end

      % --- Albedo

      incoming = atmos.swskyb(p,ib) + atmos.swskyd(p,ib);
      reflected = swup(canopy.ntop(p));
      if (incoming > 0)
         flux.albcan(p,ib) = reflected / incoming;
      else
         flux.albcan(p,ib) = 0;
      end

      % --- Conservation check

      % Total radiation balance: absorbed = incoming - outgoing

      suminc = atmos.swskyb(p,ib) + atmos.swskyd(p,ib);
      sumref = flux.albcan(p,ib) * (atmos.swskyb(p,ib) + atmos.swskyd(p,ib));
      sumabs = suminc - sumref;

      err = sumabs - (flux.swveg(p,ib) + flux.swsoi(p,ib));
      if (abs(err) > 1e-03)
         fprintf('err = %15.5f\n',err)
         fprintf('sumabs = %15.5f\n',sumabs)
         fprintf('swveg = %15.5f\n',flux.swveg(p,ib))
         fprintf('swsoi = %15.5f\n',flux.swsoi(p,ib))
         error ('NormanRadiation: Total solar conservation error')
      end

      % Sunlit and shaded absorption

      err = (flux.swvegsun(p,ib) + flux.swvegsha(p,ib)) - flux.swveg(p,ib);
      if (abs(err) > 1e-03)
         fprintf('err = %15.5f\n',err)
         fprintf('swveg = %15.5f\n',flux.swveg(p,ib))
         fprintf('swvegsun = %15.5f\n',flux.swvegsun(p,ib))
         fprintf('swvegsha = %15.5f\n',flux.swvegsha(p,ib))
         error ('NormanRadiation: Sunlit/shade solar conservation error')
      end

   end   % End grid point loop
end      % End waveband loop
tridiagonal_solver.m | View on GitHub
function [u] = tridiagonal_solver (a, b, c, d, n)

% Solve for U given the set of equations R * U = D, where U is a vector
% of length N, D is a vector of length N, and R is an N x N tridiagonal
% matrix defined by the vectors A, B, C each of length N. A(1) and
% C(N) are undefined and are not referenced.
%
%     |B(1) C(1) ...  ...  ...                     |
%     |A(2) B(2) C(2) ...  ...                     |
% R = |     A(3) B(3) C(3) ...                     |
%     |                    ... A(N-1) B(N-1) C(N-1)|
%     |                    ... ...    A(N)   B(N)  |
%
% The system of equations is written as:
%
%    A_i * U_i-1 + B_i * U_i + C_i * U_i+1 = D_i
%
% for i = 1 to N. The solution is found by rewriting the
% equations so that:
%
%    U_i = F_i - E_i * U_i+1

% --- Forward sweep (1 -> N) to get E and F

e(1) = c(1) / b(1);

for i = 2: 1: n-1
   e(i) = c(i) / (b(i) - a(i) * e(i-1));
end

f(1) = d(1) / b(1);

for i = 2: 1: n
   f(i) = (d(i) - a(i) * f(i-1)) / (b(i) - a(i) * e(i-1));
end

% --- Backward substitution (N -> 1) to solve for U

u(n) = f(n);

for i = n-1: -1: 1
   u(i) = f(i) - e(i) * u(i+1);
end
TwoStreamRadiation.m | View on GitHub
function [flux] = TwoStreamRadiation (omega, avmu, betad, betab, Kb, params, canopy, atmos, flux)

% Compute solar radiation transfer through canopy using the two-stream approximation

% -----------------------------------------------------------------------
% Input
% omega           ! Leaf scattering coefficient for canopy
% avmu            ! Average inverse diffuse optical depth per unit leaf area
% betad           ! Upscatter parameter for diffuse radiation
% betab           ! Upscatter parameter for direct beam radiation
% Kb              ! Optical depth of direct beam per unit leaf area (direct beam extinction coefficient for canopy)
% params.numrad   ! Number of wavebands
% params.npts     ! Number of grid points to process
% params.sun      ! Index for sunlit leaf
% params.sha      ! Index for shaded leaf
% canopy.ntop     ! Index for top leaf layer
% canopy.nbot     ! Index for bottom leaf layer
% canopy.lai      ! Leaf area index of canopy (m2/m2)
% canopy.sumlai   ! Cumulative leaf area index for canopy layer (m2/m2)
% canopy.dlai     ! Layer leaf area index (m2/m2)
% canopy.clumpfac ! Leaf clumping index
% atmos.swskyb    ! Atmospheric direct beam solar radiation (W/m2)
% atmos.swskyd    ! Atmospheric diffuse solar radiation (W/m2)
% flux.fracsun    ! Sunlit fraction of canopy layer
% flux.fracsha    ! Shaded fraction of canopy layer
% flux.albsoib    ! Direct beam albedo of ground (soil)
% flux.albsoid    ! Diffuse albedo of ground (soil)
%
% Output
% flux.swleaf     ! Leaf absorbed solar radiation (W/m2 leaf)
% flux.swveg      ! Absorbed solar radiation, vegetation (W/m2)
% flux.swvegsun   ! Absorbed solar radiation, sunlit canopy (W/m2)
% flux.swvegsha   ! Absorbed solar radiation, shaded canopy (W/m2)
% flux.swsoi      ! Absorbed solar radiation, ground (W/m2)
% flux.albcan     ! Albedo above canopy
% -----------------------------------------------------------------------

% --- Process each waveband for each grid point

for ib = 1:params.numrad
   for p = 1:params.npts

      % --- Canopy fluxes using total canopy lai

      % Common terms

      b = (1 - (1 - betad(p,ib)) * omega(p,ib)) / avmu(p);
      c = betad(p,ib) * omega(p,ib) / avmu(p);
      h = sqrt(b*b - c*c);
      u = (h - b - c) / (2 * h);
      v = (h + b + c) / (2 * h);
      d = omega(p,ib) * Kb(p) * atmos.swskyb(p,ib) / (h*h - Kb(p)*Kb(p));
      g1 = (betab(p,ib) * Kb(p) - b * betab(p,ib) - c * (1 - betab(p,ib))) * d;
      g2 = ((1 - betab(p,ib)) * Kb(p) + c * betab(p,ib) + b * (1 - betab(p,ib))) * d;
      s1 = exp(-h * canopy.lai(p) * canopy.clumpfac(p));
      s2 = exp(-Kb(p) * canopy.lai(p) * canopy.clumpfac(p));

      % Direct beam radiation

      num1 = v * (g1 + g2 * flux.albsoid(p,ib) + flux.albsoib(p,ib) * atmos.swskyb(p,ib)) * s2;
      num2 = g2 * (u + v * flux.albsoid(p,ib)) * s1;
      den1 = v * (v + u * flux.albsoid(p,ib)) / s1;
      den2 = u * (u + v * flux.albsoid(p,ib)) * s1;
      n2b = (num1 - num2) / (den1 - den2);
      n1b = (g2 - n2b * u) / v;

      a1b = -g1 *      (1 - s2*s2) / (2 * Kb(p)) + ...
             n1b * u * (1 - s2*s1) / (Kb(p) + h) + n2b * v * (1 - s2/s1) / (Kb(p) - h);
      a2b =  g2 *      (1 - s2*s2) / (2 * Kb(p)) - ...
             n1b * v * (1 - s2*s1) / (Kb(p) + h) - n2b * u * (1 - s2/s1) / (Kb(p) - h);

      % iupwb0    - Direct beam flux scattered upward (reflected) above canopy (W/m2)
      % iupwb     - Direct beam flux scattered upward at the canopy depth (W/m2)
      % idwnb     - Direct beam flux scattered downward below canopy (W/m2)
      % iabsb     - Direct beam flux absorbed by canopy (W/m2)
      % iabsb_sun - Direct beam flux absorbed by sunlit canopy (W/m2)
      % iabsb_sha - Direct beam flux absorbed by shaded canopy (W/m2)

      iupwb0 = -g1 + n1b * u + n2b * v;
      iupwb = -g1 * s2 + n1b * u * s1 + n2b * v / s1;
      idwnb = g2 * s2 - n1b * v * s1 - n2b * u / s1;
      iabsb = atmos.swskyb(p,ib) * (1 - s2) - iupwb0 + iupwb - idwnb;
      iabsb_sun = (1 - omega(p,ib)) ...
         * ((1 - s2) * atmos.swskyb(p,ib) + 1 / avmu(p) * (a1b + a2b) * canopy.clumpfac(p));
      iabsb_sha = iabsb - iabsb_sun;

      % Diffuse radiation
 
      num = atmos.swskyd(p,ib) * (u + v * flux.albsoid(p,ib)) * s1;
      den1 = v * (v + u * flux.albsoid(p,ib)) / s1;
      den2 = u * (u + v * flux.albsoid(p,ib)) * s1;
      n2d = num / (den1 - den2);
      n1d = -(atmos.swskyd(p,ib) + n2d * u) / v;

      a1d =  n1d * u * (1 - s2*s1) / (Kb(p) + h) + n2d * v * (1 - s2/s1) / (Kb(p) - h);
      a2d = -n1d * v * (1 - s2*s1) / (Kb(p) + h) - n2d * u * (1 - s2/s1) / (Kb(p) - h);

      % iupwd0    - Diffuse flux scattered upward (reflected) above canopy (W/m2)
      % iupwd     - Diffuse flux scattered upward at the canopy depth (W/m2)
      % idwnd     - Diffuse flux scattered downward below canopy (W/m2)
      % iabsd     - Diffuse flux absorbed by canopy (W/m2)
      % iabsd_sun - Diffuse flux absorbed by sunlit canopy (W/m2)
      % iabsd_sha - Diffuse flux absorbed by shaded canopy (W/m2)

      iupwd0 = n1d * u + n2d * v;
      iupwd = n1d * u * s1 + n2d * v / s1;
      idwnd = -n1d * v * s1 - n2d * u / s1;
      iabsd = atmos.swskyd(p,ib) - iupwd0 + iupwd - idwnd;
      iabsd_sun = (1 - omega(p,ib)) / avmu(p) * (a1d + a2d) * canopy.clumpfac(p);
      iabsd_sha = iabsd - iabsd_sun;

      % --- Save necessary radiative fluxes

      % Albedo

      suminc = atmos.swskyb(p,ib) + atmos.swskyd(p,ib);
      sumref = iupwb0 + iupwd0;
      if (suminc > 0)
         flux.albcan(p,ib) = sumref / suminc;
      else
         flux.albcan(p,ib) = 0;
      end

      % Solar radiation absorbed by canopy

      flux.swveg(p,ib) = iabsb +  iabsd;
      flux.swvegsun(p,ib) = iabsb_sun + iabsd_sun;
      flux.swvegsha(p,ib) = iabsb_sha + iabsd_sha;

      % Solar radiation absorbed by ground (soil)

      dir = atmos.swskyb(p,ib) * s2 * (1 - flux.albsoib(p,ib));
      dif = (idwnb + idwnd) * (1 - flux.albsoid(p,ib));
      flux.swsoi(p,ib) = dir + dif;

      % --- Conservation check: total incident = total reflected + total absorbed

      suminc = atmos.swskyb(p,ib) + atmos.swskyd(p,ib);
      sumref = iupwb0 + iupwd0;
      sumabs = flux.swveg(p,ib) + flux.swsoi(p,ib);

      err = suminc - (sumabs + sumref);
      if (abs(err) > 1e-06)
         fprintf('suminc = %15.5f\n',suminc)
         fprintf('sumref = %15.5f\n',sumref)
         fprintf('sumabs = %15.5f\n',sumabs)
         error ('TwoStreamRadiation: Total solar radiation conservation error')
      end

      % --- Repeat two-stream calculations for each leaf layer to calculate leaf fluxes

      icsun(ib) = 0;
      icsha(ib) = 0;

      for iv = canopy.nbot(p):canopy.ntop(p)

         % s1 and s2 depend on cumulative lai

         s1 = exp(-h * canopy.sumlai(p,iv) * canopy.clumpfac(p));
         s2 = exp(-Kb(p) * canopy.sumlai(p,iv) * canopy.clumpfac(p));

         % ilbb - absorbed direct beam flux (unscattered direct component) per unit leaf area
         % at cumulative LAI, average for all leaves (J / m2 leaf / s)

         ilbb = (1 - omega(p,ib)) * Kb(p) * atmos.swskyb(p,ib) * s2;

         % ilbs - absorbed direct beam flux (scattered direct component) per unit leaf area
         % at cumulative LAI, average for all leaves (J / m2 leaf / s)

         diupwb = Kb(p) * g1 * s2 - h * n1b * u * s1 + h * n2b * v / s1;
         didwnb = -Kb(p) * g2 * s2 + h * n1b * v * s1 - h * n2b * u / s1;
         ilbs = (omega(p,ib) * Kb(p) * atmos.swskyb(p,ib) * s2 + (diupwb - didwnb)) * canopy.clumpfac(p);

         % ild - absorbed diffuse flux per unit leaf area at cumulative LAI,
         % average for all leaves (J / m2 leaf / s)

         diupwd = -h * n1d * u * s1 + h * n2d * v / s1;
         didwnd = h * n1d * v * s1 - h * n2d * u / s1;
         ild = (diupwd - didwnd) * canopy.clumpfac(p);

         % Save leaf fluxes per unit sunlit and shaded leaf area (W/m2 leaf)

         flux.swleaf(p,iv,params.sun,ib) = ilbb / flux.fracsun(p,iv) + (ilbs + ild);
         flux.swleaf(p,iv,params.sha,ib) = ilbs + ild;

         icsun(ib) = icsun(ib) + (ilbb + (ilbs + ild)*flux.fracsun(p,iv)) * canopy.dlai(p,iv);
         icsha(ib) = icsha(ib) + (ilbs + ild)*flux.fracsha(p,iv) * canopy.dlai(p,iv);

      end   % end canopy loop
   end      % end grid point loop
end         % end waveband loop

% --- Adjust leaf fluxes as needed. The sum of the fluxes for sunlit and shaded
% leaves should equal the total absorbed by the canopy, but may not because of
% inaccuracies in the flux derivatives (this is a small error if the dlai increment
% is small). Normalize these fluxes to sum to the canopy absorption.

for ib = 1:params.numrad
   for p = 1:params.npts

      % Sum canopy absorption (W/m2 ground) using leaf fluxes per unit sunlit
      % and shaded leaf area (W/m2 leaf)

      sumabs = 0;
      sumabs_sun = 0;
      sumabs_sha = 0;
      for iv = canopy.nbot(p):canopy.ntop(p)
         sun = flux.swleaf(p,iv,params.sun,ib) * flux.fracsun(p,iv) * canopy.dlai(p,iv);
         sha = flux.swleaf(p,iv,params.sha,ib) * flux.fracsha(p,iv) * canopy.dlai(p,iv);
         sumabs = sumabs + sun + sha;
         sumabs_sun = sumabs_sun + sun;
         sumabs_sha = sumabs_sha + sha;
      end

      % Normalize profile

      if (sumabs > 0)
         for iv = canopy.nbot(p):canopy.ntop(p)
            flux.swleaf(p,iv,params.sun,ib) = flux.swleaf(p,iv,params.sun,ib) * flux.swveg(p,ib) / sumabs;
            flux.swleaf(p,iv,params.sha,ib) = flux.swleaf(p,iv,params.sha,ib) * flux.swveg(p,ib) / sumabs;
         end
      end

   end

end

Output

Figures

Figure 1

Text

data.txt | View on GitHub | View raw
layer          lai          sun          sha          sun          sha
           1       0.0500     0.571710     0.179057     0.264662     0.126079
           2       0.1500     0.559613     0.166960     0.264655     0.126072
           3       0.2500     0.548363     0.155710     0.264276     0.125692
           4       0.3500     0.537898     0.145246     0.263565     0.124982
           5       0.4500     0.528163     0.135510     0.262560     0.123977
           6       0.5500     0.519105     0.126452     0.261294     0.122711
           7       0.6500     0.510674     0.118021     0.259800     0.121216
           8       0.7500     0.502826     0.110173     0.258104     0.119520
           9       0.8500     0.495519     0.102866     0.256233     0.117650
          10       0.9500     0.488715     0.096062     0.254212     0.115628
          11       1.0500     0.482378     0.089725     0.252061     0.113477
          12       1.1500     0.476474     0.083821     0.249800     0.111217
          13       1.2500     0.470973     0.078321     0.247449     0.108865
          14       1.3500     0.465847     0.073195     0.245022     0.106439
          15       1.4500     0.461069     0.068417     0.242535     0.103952
          16       1.5500     0.456615     0.063962     0.240002     0.101418
          17       1.6500     0.452462     0.059809     0.237434     0.098850
          18       1.7500     0.448589     0.055936     0.234842     0.096259
          19       1.8500     0.444976     0.052324     0.232237     0.093654
          20       1.9500     0.441606     0.048953     0.229628     0.091044
          21       2.0500     0.438462     0.045809     0.227021     0.088438
          22       2.1500     0.435527     0.042874     0.224425     0.085841
          23       2.2500     0.432788     0.040135     0.221845     0.083262
          24       2.3500     0.430232     0.037579     0.219287     0.080704
          25       2.4500     0.427845     0.035192     0.216756     0.078173
          26       2.5500     0.425616     0.032963     0.214257     0.075674
          27       2.6500     0.423535     0.030882     0.211793     0.073209
          28       2.7500     0.421591     0.028938     0.209366     0.070783
          29       2.8500     0.419775     0.027122     0.206981     0.068397
          30       2.9500     0.418079     0.025426     0.204639     0.066055
          31       3.0500     0.416495     0.023842     0.202341     0.063758
          32       3.1500     0.415015     0.022362     0.200091     0.061508
          33       3.2500     0.413632     0.020979     0.197888     0.059305
          34       3.3500     0.412340     0.019687     0.195735     0.057151
          35       3.4500     0.411133     0.018480     0.193630     0.055047
          36       3.5500     0.410005     0.017352     0.191576     0.052993
          37       3.6500     0.408952     0.016299     0.189572     0.050989
          38       3.7500     0.407968     0.015316     0.187618     0.049034
          39       3.8500     0.407050     0.014397     0.185713     0.047130
          40       3.9500     0.406193     0.013540     0.183859     0.045275
          41       4.0500     0.405393     0.012740     0.182053     0.043470
          42       4.1500     0.404646     0.011994     0.180296     0.041713
          43       4.2500     0.403950     0.011298     0.178586     0.040003
          44       4.3500     0.403302     0.010649     0.176924     0.038341
          45       4.4500     0.402699     0.010046     0.175307     0.036724
          46       4.5500     0.402137     0.009485     0.173735     0.035152
          47       4.6500     0.401616     0.008963     0.172207     0.033624
          48       4.7500     0.401132     0.008480     0.170722     0.032139
          49       4.8500     0.400684     0.008032     0.169278     0.030695
          50       4.9500     0.400270     0.007617     0.167874     0.029291
          51       5.0500     0.399888     0.007236     0.166509     0.027926
          52       5.1500     0.399537     0.006885     0.165181     0.026598
          53       5.2500     0.399216     0.006563     0.163890     0.025306
          54       5.3500     0.398922     0.006270     0.162632     0.024049
          55       5.4500     0.398656     0.006004     0.161408     0.022825
          56       5.5500     0.398417     0.005764     0.160216     0.021633
          57       5.6500     0.398203     0.005550     0.159054     0.020470
          58       5.7500     0.398014     0.005361     0.157920     0.019336
          59       5.8500     0.397849     0.005196     0.156813     0.018230
          60       5.9500     0.397708     0.005056     0.155731     0.017148