Forward Models
==============

.. _`Choosing_a_BRDF_Model`:

Choosing a BRDF Model
---------------------

You can import specific BRDF models directly::
    from pydirectional import RPVBRDF
    model = RPVBRDF(*args, **kwargs)

Or you can access them via the BRDF model factory class::

    from pydirectional import BRDFModelFactory 
    model = BRDFModelFactory('RPV', *args, **kwargs)

Instantiating a BRDF Model
--------------------------

When you instantiate a BRDF model using either of the 
methods outlined in :ref:`choosing_a_brdf_model`, the required arguments are:

* vza
* sza
* vaa and saa OR raa

The angle inputs must all have the same shape,
for example if you want to return quantities 
for multiple viewing angles and the same solar
angle, use `sza = sza_value * np.ones_like(vza)` to create a sza array
of the same shape as the vza.

If you want to calculate HCRF or other values that
require the direct to diffuse ratio, then 
`direct_to_diffuse_irr` must be set to a value.

Models are instantiated using the geometry of the scenario::

    model = RTLSBRDF(vza = 20,
                     sza = 30
                     vaa = 210,
                     saa = 50,
                     direct_to_diffuse_irr = 0.5)
    
Models can be instantiated for arrays of angles, but these must be the same size::

    vza_array = np.linspace(0, 10, 9)
    raa_array = np.linspace(0, 360, 18)
    vza_mesh, raa_mesh = np.meshgrid(vza_array, raa_array)
    model = RTLSBRDF(vza = vza_mesh,
                     sza = 30 * np.ones_like(vza_mesh),
                     raa = raa_mesh)

.. note:: 
    The default angle unit is set as "degrees", if your
    inputs are in radians, set `angle_unit = "radians"`.

Finetuning Models
^^^^^^^^^^^^^^^^^
Some models take further parameters that can be used to finetune the model. For example
the :math:`HB` and :math:`BR` parameters in the RTLS model. More information can be found
in the class documentation of each model. Functionality also exists to use different
kernel types, for example Li-Dense and Ross-Thin, similarly information can be found in the
class documentation.


Calculating Quantities
----------------------

All the quantities defined in the Theory section
can be calulated for each BRDF model.

The quantities and corresponding functions are:

* BRF - `model.return_BRF(*args)`
* HCRF - `model.return_HCRF(*args)`
* Black-Sky Albedo (or DHR) - `model.return_black_sky_albedo(*args)`
* White-Sky Albedo (or BHR) - `model.return_white_sky_albedo(*args)`
* Blue-Sky Albedo - `model.return_blue_sky_albedo(*args)`

.. note:: 
    BRDF cannot be directly calculated,
    if this is required calculate BRF then use the
    equation from the Theory section (:math:`BRDF = \frac{BRF}{\pi}`).


.. note:: 
    To calculate HDRF, use ``return_HCRF``. This is valid if HDRF if constant over the sensor's 
    field of view (FOV), hence this assumption is also valid for small sensor FOV.

The arguments for all of the functions used to
calculate quantities are the parameters for the 
chosen BRDF model. If the parameter names are not known
they can be retrieved using `model.get_coefficient_names()`.

The following example uses the RPV model to calculate BRF and HCRF::

    model = RPVBRDF(vza = 20,
                     sza = 30
                     vaa = 210,
                     saa = 50,
                     direct_to_diffuse_irr = 0.5)

    BRF = model.return_BRF(rho_0 = 0.3,
                           k = 0.4
                           theta = 0.8
                           rho_c = 0.2)

    HCRF = model.return_HCRF(rho_0 = 0.3,
                             k = 0.4
                             theta = 0.8
                             rho_c = 0.2)

All quantities except BRF are calculated by integrating over the viewing
and/or solar hemispheres. To speed up this process look-up tables (LUTs)
of the results of these integrations have been created; the default 
processing within the return_QUANTITY functions is to use these LUTs
with the ``use_fast`` keyword argument set to ``True``. Manual integrations
can be performed by setting ``use_fast = False`` however note this will 
increase processing time. HCRF and black-sky albedo LUTs are accurate 
to within 1% of manual integration, while white-sky and blue-sky albedo LUTs (where multiple
integrations take place) are accurate to within 1.5%. 

Averaging Quantities
^^^^^^^^^^^^^^^^^^^^^

Averages of BRF or HCRF over a given sensor field of view (up to the whole hemisphere) can be 
calculated.

The average BRF over the whole viewing or solar hemispheres is caluclated using 
`model.average_BRF_hemispherical`.
The average BRF or HCRF over a given sensor field of view is calculated using
`model.average_BRF_FOV` and `model.average_HCRF_FOV`, respectively.

The hemisphere over which the integrations are performed is set using the `integrating_hemisphere`
function input, this must be `'solar'` or `'viewing'` and the angular grid step is set in degrees
using `grid_step`::

    average_solar_BRF = model.average_BRF_hemispherical(rho_0 = 0.3,
                                         k = 0.4
                                         theta = 0.8
                                         rho_c = 0.2,
                                         integrating_hemisphere = "solar"
                                         )

For the FOV functions, the zenith and azimuthal ranges are set using `min_vza`, `max_vza`, `min_raa`,
and `max_raa` regardless of integrating hemisphere. The angular response function can also be set
by providing a Python function (with `vza` and `raa` as inputs in degrees) as the `angular_response` input::

    def angular_response_function(vza, raa):
        return np.abs(np.cos(np.radians(vza))*np.sin(np.radians(raa)))
    
    averaged_BRF = model.average_BRF_FOV(rho_0 = 0.3,
                                         k = 0.4
                                         theta = 0.8
                                         rho_c = 0.2
                                         min_vza = 0,
                                         max_vza = 10,
                                         grid_step = 1,
                                         angular_response = angular_response_function
                                         )

.. note::

    Any angular response function must return positive values.