Setting the Surface BRDF#

import sasktran2 as sk

SASKTRAN2 has the capability to include surface reflectance in the form of a Bi-directional Reflectance Distribution Function (BRDF). The simplest BRDF is a Lambertian surface where the outgoing radiation is the same in all directions.

Let’s start by setting up a calculation where we view the ground at a variety of different viewing zenith angles.

import numpy as np
import matplotlib.pyplot as plt

config = sk.Config()
#config.multiple_scatter_source = sk.MultipleScatterSource.DiscreteOrdinates
config.num_streams = 4

model_geometry = sk.Geometry1D(cos_sza=0.6,
                                solar_azimuth=0,
                                earth_radius_m=6372000,
                                altitude_grid_m=np.arange(0, 65001, 1000),
                                interpolation_method=sk.InterpolationMethod.LinearInterpolation,
                                geometry_type=sk.GeometryType.Spherical)

viewing_geo = sk.ViewingGeometry()

cos_viewing_angle = np.arange(0.01, 0.99, 0.05)

for mu in cos_viewing_angle:
    ray = sk.GroundViewingSolar(0.6, np.pi, mu, 100000)
    viewing_geo.add_ray(ray)

for mu in cos_viewing_angle[::-1]:
    ray = sk.GroundViewingSolar(0.6, 0, mu, 100000)
    viewing_geo.add_ray(ray)

wavel = np.array([600])

atmosphere = sk.Atmosphere(model_geometry, config, wavelengths_nm=wavel)

sk.climatology.us76.add_us76_standard_atmosphere(atmosphere)

atmosphere['rayleigh'] = sk.constituent.Rayleigh()
atmosphere['ozone'] = sk.climatology.mipas.constituent("O3", sk.optical.O3DBM())

engine = sk.Engine(config, model_geometry, viewing_geo)

BRDFs are added to the atmosphere the same way any constituent is. Let’s add a Lambertian surface with a constant surface albedo of 1.0

atmosphere['brdf'] = sk.constituent.LambertianSurface(1.0)

And calculate the radiance

radiance = engine.calculate_radiance(atmosphere)

radiance["radiance"].isel(stokes=0).sel(wavelength=600).plot()
[<matplotlib.lines.Line2D at 0x7b34f972c890>]
../_images/5ba5f5d1be2ebcda82cd038e68f2a4beb2e42c0a4f1d949d608734d94f92ac61.png

We can change the surface to a snow surface and compare the results,

atmosphere['brdf'] = sk.constituent.SnowKokhanovsky()

radiance_snow = engine.calculate_radiance(atmosphere)

radiance["radiance"].isel(stokes=0).sel(wavelength=600).plot()
radiance_snow["radiance"].isel(stokes=0).sel(wavelength=600).plot()
[<matplotlib.lines.Line2D at 0x7b34f97bacf0>]
../_images/badb2c342e4b2e0e8d42a0ec204c9a3367018a4dbdd8fc14b84d938edd66c188.png

And we see a shifting of radiance values towards the later lines of sight, which have relative azimuth 0 and are in the glint region.

Surface BRDF Linearization#

Each BRDF provides linearizations with respect to it’s input parameters. If we look at the radiance calculated with the Lambertian surface,

print(radiance)
<xarray.Dataset> Size: 80kB
Dimensions:               (wavelength: 1, los: 40, stokes: 1, altitude: 66,
                           ozone_altitude: 50, brdf_wavelength: 1)
Coordinates:
  * wavelength            (wavelength) float64 8B 600.0
  * stokes                (stokes) <U1 4B 'I'
Dimensions without coordinates: los, altitude, ozone_altitude, brdf_wavelength
Data variables:
    radiance              (wavelength, los, stokes) float64 320B 0.05523 ... ...
    wf_temperature_k      (altitude, wavelength, los, stokes) float64 21kB 6....
    wf_ozone_vmr          (ozone_altitude, wavelength, los, stokes) float64 16kB ...
    wf_pressure_pa        (altitude, wavelength, los, stokes) float64 21kB -1...
    wf_specific_humidity  (altitude, wavelength, los, stokes) float64 21kB 0....
    wf_brdf_albedo        (brdf_wavelength, wavelength, los, stokes) float64 320B ...

We see there is a field wf_brdf_albedo that represents the derivative of the observations with respect to our input albedo value.

Looking at the snow calculation,

print(radiance_snow)
<xarray.Dataset> Size: 81kB
Dimensions:               (wavelength: 1, los: 40, stokes: 1, altitude: 66,
                           ozone_altitude: 50, brdf_wavelength: 1)
Coordinates:
  * wavelength            (wavelength) float64 8B 600.0
  * stokes                (stokes) <U1 4B 'I'
Dimensions without coordinates: los, altitude, ozone_altitude, brdf_wavelength
Data variables:
    radiance              (wavelength, los, stokes) float64 320B 0.05351 ... ...
    wf_temperature_k      (altitude, wavelength, los, stokes) float64 21kB 4....
    wf_pressure_pa        (altitude, wavelength, los, stokes) float64 21kB -1...
    wf_specific_humidity  (altitude, wavelength, los, stokes) float64 21kB 0....
    wf_ozone_vmr          (ozone_altitude, wavelength, los, stokes) float64 16kB ...
    wf_brdf_L             (brdf_wavelength, wavelength, los, stokes) float64 320B ...
    wf_brdf_albedo        (brdf_wavelength, wavelength, los, stokes) float64 320B ...
    wf_brdf_M             (brdf_wavelength, wavelength, los, stokes) float64 320B ...

We see two parameters, wf_brdf_L and wf_brdf_M. These are the derivatives with respect to the input parameters L and M of the Kokhanovsky model.

Available BRDFs#

A full list of available surface parameterizations can be found at BRDFs.