Esempio n. 1
0
def calc_r_r(p, ea, t_k):
    """ Calculates the resistance to radiative transfer

    Parameters
    ----------
    p : float or array
        Surface atmospheric pressure (mb)
    ea : float or array
        Vapour pressure (mb).
    t_k : float or array
        surface temperature (K)

    Returns
    -------
    r_r : float or array
        Resistance to radiative transfer (s m-1)

    References
    ----------
    .. [Monteith2008] Monteith, JL, Unsworth MH, Principles of Environmental
    Physics, 2008. ISBN 978-0-12-505103-5

    """

    rho = met.calc_rho(p, ea, t_k)
    cp = met.calc_c_p(p, ea)
    r_r = rho * cp / (4. * rad.SB * t_k**3)  # Eq. 4.7 of [Monteith2008]_
    return r_r
Esempio n. 2
0
def ESVEP(Tr_K,
          vza,
          T_A_K,
          u,
          ea,
          p,
          Sn_C,
          Sn_S,
          L_dn,
          LAI,
          emis_C,
          emis_S,
          z_0M,
          d_0,
          z_u,
          z_T,
          z0_soil=0.001,
          x_LAD=1,
          f_c=1.0,
          f_g=1.0,
          w_C=1.0,
          calcG_params=[[1], 0.35],
          UseL=False):
    '''ESVEP

    Calculates soil and vegetation energy fluxes using Soil and Vegetation Energy Patrtitioning
    `ESVEP` model and a single observation of composite radiometric temperature.

    Parameters
    ----------
    Tr_K : float
        Radiometric composite temperature (Kelvin).
    vza : float
        View Zenith Angle (degrees).
    T_A_K : float
        Air temperature (Kelvin).
    u : float
        Wind speed above the canopy (m s-1).
    ea : float
        Water vapour pressure above the canopy (mb).
    p : float
        Atmospheric pressure (mb), use 1013 mb by default.
    Sn_C : float
        Canopy net shortwave radiation (W m-2).
    Sn_S : float
        Soil net shortwave radiation (W m-2).
    L_dn : float
        Downwelling longwave radiation (W m-2).
    LAI : float
        Effective Leaf Area Index (m2 m-2).
    emis_C : float
        Leaf emissivity.
    emis_S : flaot
        Soil emissivity.
    z_0M : float
        Aerodynamic surface roughness length for momentum transfer (m).
    d_0 : float
        Zero-plane displacement height (m).
    z_u : float
        Height of measurement of windspeed (m).
    z_T : float
        Height of measurement of air temperature (m).
    z0_soil : float, optional
        bare soil aerodynamic roughness length (m).
    x_LAD : float, optional
        Campbell 1990 leaf inclination distribution function chi parameter.
    f_c : float, optional
        Fractional cover.
    f_g : float, optional
        Fraction of vegetation that is green.
    w_C : float, optional
        Canopy width to height ratio.
    calcG_params : list[list,float or array], optional
        Method to calculate soil heat flux,parameters.
            * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
            * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
            * [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with G_param list of parameters (see :func:`~TSEB.calc_G_time_diff`).
    UseL : float or None, optional
        If included, its value will be used to force the Moning-Obukhov stability length.

    Returns
    -------
    flag : int
        Quality flag, see Appendix for description.
    T_S : float
        Soil temperature  (Kelvin).
    T_C : float
        Canopy temperature  (Kelvin).
    T_sd: float
        End-member temperature of dry soil (Kelvin)
    T_vd: float
        End-member temperature of dry vegetation (Kelvin)
    T_sw: float
        End-member temperature of saturated soil (Kelvin)
    T_vw: float
        End-member temperature of well-watered vegetation (Kelvin)
    T_star: float
        Critical surface temperature (Kelvin)
    L_nS : float
        Soil net longwave radiation (W m-2)
    L_nC : float
        Canopy net longwave radiation (W m-2)
    LE_C : float
        Canopy latent heat flux (W m-2).
    H_C : float
        Canopy sensible heat flux (W m-2).
    LE_S : float
        Soil latent heat flux (W m-2).
    H_S : float
        Soil sensible heat flux (W m-2).
    G : float
        Soil heat flux (W m-2).
    r_vw: float
        Canopy resistance to heat transport of well-watered vegetation (s m-1)
    r_vd: float
        Canopy resistance to heat transport of vegetation with zero soil water avaiability (s m-1)
    r_av: float
        Aerodynamic resistance to heat transport of the vegetation (s m-1)
    r_as: float
        Aerodynamic resistance to heat transport of the soil (s m-1)
    L : float
        Monin-Obuhkov length (m).
    n_iterations : int
        number of iterations until convergence of L.

    References
    ----------
    .. [Tang2017] Tang, R., and Z. L. Li. An End-Member-Based Two-Source Approach for Estimating  
        Land Surface Evapotranspiration From Remote Sensing Data. IEEE Transactions on Geoscience 
        and Remote Sensing 55, no. 10 (October 2017): 5818–32. 
        https://doi.org/10.1109/TGRS.2017.2715361.
    '''

    # Convert input float scalars to arrays and check parameters size
    Tr_K = np.asarray(Tr_K)
    (vza, T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, z_0M, d_0,
     z_u, z_T, z0_soil, x_LAD, f_c, f_g, w_C,
     calcG_array) = map(tseb._check_default_parameter_size, [
         vza, T_A_K, u, ea, p, Sn_C, Sn_S, L_dn, LAI, emis_C, emis_S, z_0M,
         d_0, z_u, z_T, z0_soil, x_LAD, f_c, f_g, w_C, calcG_params[1]
     ], [Tr_K] * 21)

    # Create the output variables
    [
        flag, T_S, T_C, T_sd, T_vd, T_sw, T_vw, T_star, Ln_S, Ln_C, LE_C, H_C,
        LE_S, H_S, G, r_vw, r_vd, r_av, r_as, iterations
    ] = [np.zeros(Tr_K.shape) + np.NaN for i in range(20)]

    # iteration of the Monin-Obukhov length
    if isinstance(UseL, bool):
        # Initially assume stable atmospheric conditions and set variables for
        L = np.asarray(np.zeros(T_S.shape) + np.inf)
        max_iterations = ITERATIONS
    else:  # We force Monin-Obukhov lenght to the provided array/value
        L = np.asarray(np.ones(T_S.shape) * UseL)
        max_iterations = 1  # No iteration

    # Calculate the general parameters
    rho = met.calc_rho(p, ea, T_A_K)  # Air density
    c_p = met.calc_c_p(p, ea)  # Heat capacity of air
    z_0H = res.calc_z_0H(z_0M, kB=kB)  # Roughness length for heat transport
    z_0H_soil = res.calc_z_0H(z0_soil,
                              kB=kB)  # Roughness length for heat transport
    s = met.calc_delta_vapor_pressure(
        T_A_K) * 10  # slope of the saturation pressure curve (mb C-1)
    lbd = met.calc_lambda(T_A_K)  # latent heat of vaporisation (MJ./kg)
    gama = met.calc_psicr(p, lbd)  # psychrometric constant (mb C-1)
    vpd = met.calc_vapor_pressure(T_A_K) - ea  # vapor pressure deficit (mb)

    # Calculate LAI dependent parameters for dataset where LAI > 0
    omega0 = CI.calc_omega0_Kustas(LAI, f_c, x_LAD=x_LAD, isLAIeff=True)
    F = np.asarray(LAI / f_c)  # Real LAI
    # Fraction of vegetation observed by the sensor
    f_theta = tseb.calc_F_theta_campbell(vza,
                                         F,
                                         w_C=w_C,
                                         Omega0=omega0,
                                         x_LAD=x_LAD)

    # Initially assume stable atmospheric conditions and set variables for
    # iteration of the Monin-Obukhov length
    u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
    u_friction = np.asarray(np.maximum(u_friction_min, u_friction))
    u_friction_s = MO.calc_u_star(u, z_u, L, np.zeros(d_0.shape), z0_soil)
    u_friction_s = np.asarray(np.maximum(u_friction_min, u_friction_s))
    L_old = np.ones(Tr_K.shape)
    L_diff = np.asarray(np.ones(Tr_K.shape) * float('inf'))

    # First assume that canopy temperature equals the minumum of Air or
    # radiometric T
    T_C = np.asarray(np.minimum(Tr_K, T_A_K))
    flag, T_S = tseb.calc_T_S(Tr_K, T_C, f_theta)

    # Loop for estimating stability.
    # Stops when difference in consecutives L is below a given threshold
    for n_iterations in range(max_iterations):
        i = flag != 255
        if np.all(L_diff[i] < L_thres):
            if L_diff[i].size == 0:
                print("Finished iterations with no valid solution")
            else:
                print("Finished interations with a max. L diff: " +
                      str(np.max(L_diff[i])))
            break
        i = np.logical_and(L_diff >= L_thres, flag != 255)
        print("Iteration " + str(n_iterations) + ", max. L diff: " +
              str(np.max(L_diff[i])))
        iterations[i] = n_iterations

        # Calculate net longwave radiation with current values of T_C and T_S
        Ln_C[i], Ln_S[i] = rad.calc_L_n_Kustas(T_C[i], T_S[i], L_dn[i], LAI[i],
                                               emis_C[i], emis_S[i])
        Rn_C = Sn_C + Ln_C
        Rn_S = Sn_S + Ln_S

        # Compute Soil Heat Flux
        G[i] = tseb.calc_G([calcG_params[0], calcG_array], Rn_S, i)

        # Calculate aerodynamic resistances
        r_vw[i] = 100.0 / LAI[i]
        r_vd[i] = np.zeros(LAI[i].shape) + 2000.0
        r_av_params = {
            "z_T": z_T[i],
            "u_friction": u_friction[i],
            "L": L[i],
            "d_0": d_0[i],
            "z_0H": z_0H[i]
        }
        r_av[i] = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999,
                                        {"R_A": r_av_params})[0]
        r_as_params = {
            "z_T": z_T[i],
            "u_friction": u_friction_s[i],
            "L": L[i],
            "d_0": np.zeros(d_0[i].shape),
            "z_0H": z_0H_soil[i]
        }
        r_as[i] = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999,
                                        {"R_A": r_as_params})[0]

        # Estimate the surface temperatures of the end-members
        # Eq 8a
        T_sd[i] = r_as[i] * (Rn_S[i] - G[i]) / (rho[i] * c_p[i]) + T_A_K[i]
        # Eq 8b
        T_vd[i] = r_av[i] * Rn_C[i] / (rho[i] * c_p[i]) *\
                  gama[i] * (1 + r_vd[i] / r_av[i]) / (s[i] + gama[i] * (1 + r_vd[i] / r_av[i])) -\
                  vpd[i] / (s[i] + gama[i] * (1 + r_vd[i] / r_av[i])) + T_A_K[i]
        # Eq 8c
        T_sw[i] = r_as[i] * (Rn_S[i] - G[i]) / (rho[i] + c_p[i]) *\
                  gama[i] / (s[i] + gama[i]) - vpd[i] / (s[i] + gama[i]) + T_A_K[i]
        # Eq 8d
        T_vw[i] = r_av[i] * Rn_C[i] / (rho[i] * c_p[i]) *\
                  gama[i] * (1 + r_vw[i] / r_av[i]) / (s[i] + gama[i] * (1 + r_vw[i] / r_av[i])) -\
                  vpd[i] / (s[i] + gama[i] * (1 + r_vw[i] / r_av[i])) + T_A_K[i]

        # Estimate critical surface temperature - eq 10
        T_star[i] = (T_sd[i]**4 * (1 - f_theta[i]) +
                     T_vw[i]**4 * f_theta[i])**0.25

        # Estimate latent heat fluxes when water in the top-soil is avaiable for evaporation
        j = np.logical_and(Tr_K <= T_star, i)
        # Eq 12a
        T_C[j] = T_vw[j]
        # Eq 12b
        T_S[j] = ((Tr_K[j]**4 - f_theta[j] * T_C[j]**4) /
                  (1 - f_theta[j]))**0.25
        # Eq 13a
        LE_C[j] = (s[j] * Rn_C[j] + rho[j] * c_p[j] * vpd[j] / r_av[j]) /\
                  (s[j] + gama[j] * (1 + r_vw[j] / r_av[j]))
        # Eq 13b
        LE_S[j] = (T_sd[j] - T_S[j]) / (T_sd[j] - T_sw[j]) *\
                  ((s[j] * (Rn_S[j] - G[j]) + rho[j] * c_p[j] * vpd[j] / r_as[j]) /
                   (s[j] + gama[j]))

        # Estimate latent heat fluxes when no water in the top-soil is avaiable for evaporation
        j = np.logical_and(Tr_K > T_star, i)
        # Eq 14a
        T_S[j] = T_sd[j]
        # Eq 14b
        T_C[j] = ((Tr_K[j]**4 - (1 - f_theta[j]) * T_S[j]**4) /
                  f_theta[j])**0.25
        # Eq 15a
        LE_C[j] = (T_vd[j] - T_C[j]) / (T_vd[j] - T_vw[j]) *\
                  ((s[j] * Rn_C[j] + rho[j] * c_p[j] * vpd [j] / r_av[j]) /
                   (s[j] + gama[j] * (1 + r_vw[j] / r_av[j])))
        # Eq 15b
        LE_S[j] = 0

        # Estimate sensible heat fluxes as residuals
        H_C[i] = Rn_C[i] - LE_C[i]
        H_S[i] = Rn_S[i] - G[i] - LE_S[i]

        # Calculate total fluxes
        H = np.asarray(H_C + H_S)
        LE = np.asarray(LE_C + LE_S)

        # Now L can be recalculated and the difference between iterations
        # derived
        if isinstance(UseL, bool):
            L[i] = MO.calc_L(u_friction[i], T_A_K[i], rho[i], c_p[i], H[i],
                             LE[i])
            L_diff = np.asarray(np.fabs(L - L_old) / np.fabs(L_old))
            L_diff[np.isnan(L_diff)] = float('inf')
            L_old = np.array(L)
            L_old[L_old == 0] = 1e-36
            # Calculate again the friction velocity with the new stability
            # correctios
            u_friction[i] = MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i])
            u_friction = np.asarray(np.maximum(u_friction_min, u_friction))
            u_friction_s[i] = MO.calc_u_star(u[i], z_u[i], L[i],
                                             np.zeros(d_0[i].shape),
                                             np.zeros(z_0M[i].shape) + 0.005)
            u_friction_s = np.asarray(np.maximum(u_friction_min, u_friction_s))

    return [
        flag, T_S, T_C, T_sd, T_vd, T_sw, T_vw, T_star, Ln_S, Ln_C, LE_C, H_C,
        LE_S, H_S, G, r_vw, r_vd, r_av, r_as, L, n_iterations
    ]
Esempio n. 3
0
def calc_stomatal_resistance_TSEB(
        LE_C,
        LE,
        R_A,
        R_x,
        e_a,
        T_A,
        T_C,
        F,
        p=1013.0,
        leaf_type=1,
        f_g=1,
        f_dry=1):
    ''' TSEB Stomatal conductace

    Estimates the effective Stomatal conductace by inverting the
    resistance-based canopy latent heat flux from a Two source perspective

    Parameters
    ----------
    LE_C : float
        Canopy latent heat flux (W m-2).
    LE : float
        Surface (bulk) latent heat flux (W m-2).
    R_A : float
        Aerodynamic resistance to heat transport (s m-1).
    R_x : float
        Bulk aerodynamic resistance to heat transport at the canopy boundary layer (s m-1).
    e_a : float
        Water vapour pressure at the reference height (mb).
    T_A : float
        Air temperature at the reference height (K).
    T_C : float
        Canopy (leaf) temperature (K).
    F : float
        local Leaf Area Index.
    p : float, optional
        Atmospheric pressure (mb) use 1013.0 as default.
    leaf_type : int, optional
        type of leaf regarding stomata distribution.

            1=HYPOSTOMATOUS stomata in the lower surface of the leaf (default).
            2=AMPHISTOMATOUS, stomata in both surfaces of the leaf.
    f_g : float, optional
        Fraction of green leaves.
    f_dry : float, optional
        Fraction of dry (non-wet) leaves.

    Returns
    -------
    G_s : float
        effective leaf stomata conductance (m s-1).

    References
    ----------
    .. [Anderson2000] M.C. Anderson, J.M. Norman, T.P. Meyers, G.R. Diak, An analytical
        model for estimating canopy transpiration and carbon assimilation fluxes based on
        canopy light-use efficiency, Agricultural and Forest Meteorology, Volume 101,
        Issue 4, 12 April 2000, Pages 265-289, ISSN 0168-1923,
        http://dx.doi.org/10.1016/S0168-1923(99)00170-7.'''

    # Convert input scalars to numpy arrays
    LE_C, LE, R_A, R_x, e_a, T_A, T_C, F, p, leaf_type, f_g, f_dry = map(
        np.asarray, (LE_C, LE, R_A, R_x, e_a, T_A, T_C, F, p, leaf_type, f_g, f_dry))

    # Invert the bulk SW to obtain eb (vapor pressure at the canopy interface)
    rho = met.calc_rho(p, e_a, T_A)
    Cp = met.calc_c_p(p, e_a)
    Lambda = met.calc_lambda(T_A)
    psicr = met.calc_psicr(Cp, p, Lambda)
    e_ac = e_a + LE * R_A * psicr / (rho * Cp)
    # Calculate the saturation vapour pressure in the leaf in mb
    e_star = met.calc_vapor_pressure(T_C)
    # Calculate the boundary layer canopy resisitance to water vapour (Anderson et al. 2000)
    # Invert the SW LE_S equation to calculate the bulk stomatal resistance
    R_c = np.asarray((rho * Cp * (e_star - e_ac) / (LE_C * psicr)) - R_x)
    K_c = np.asarray(f_dry * f_g * leaf_type)
    # Get the mean stomatal resistance (here LAI comes in as stomatal resistances
    # are in parallel: 1/Rc=sum(1/R_st)=LAI/Rst
    r_st = R_c * K_c * F
    return np.asarray(r_st)
Esempio n. 4
0
def pet_fao56(T_A_K,
              u,
              ea,
              p,
              Sdn,
              z_u,
              z_T,
              f_cd=1,
              reference=SHORT_REFERENCE):
    '''Calcultaes the latent heat flux for well irrigated and cold pixel using
    FAO56 potential ET from a short (grass) crop

    Parameters
    ----------
    T_A_K : float or array
        Air temperature (Kelvin).
    u : float or array
        Wind speed above the canopy (m s-1).
    ea : float or array
        Water vapour pressure above the canopy (mb).
    p : float or array
        Atmospheric pressure (mb), use 1013 mb by default.
    Sdn : float or array
        Solar irradiance (W m-2).
    z_u : float or array
        Height of measurement of windspeed (m).
    z_T : float or array
        Height of measurement of air temperature (m).
    f_cd : float or array
        cloudiness factor, default = 1
    reference : bool
        If true, reference ET is for a tall canopy (i.e. alfalfa)

    '''
    # Atmospheric constants
    delta = 10. * met.calc_delta_vapor_pressure(
        T_A_K)  # slope of saturation water vapour pressure in mb K-1
    lambda_ = met.calc_lambda(T_A_K)  # latent heat of vaporization MJ kg-1
    c_p = met.calc_c_p(p, ea)  # Heat capacity of air
    psicr = met.calc_psicr(c_p, p, lambda_)  # Psicrometric constant (mb K-1)
    es = met.calc_vapor_pressure(
        T_A_K)  # saturation water vapour pressure in mb
    rho = met.calc_rho(p, ea, T_A_K)

    # Net shortwave radiation
    # Sdn = Sdn * 3600 / 1e6 # W m-2 to MJ m-2 h-1
    albedo = 0.23
    Sn = Sdn * (1.0 - albedo)
    # Net longwave radiation
    Ln = calc_Ln(T_A_K, ea, f_cd=f_cd)
    # Net radiation
    Rn = Sn + Ln

    if reference == TALL_REFERENCE:
        G_ratio = 0.04
        h_c = 0.5
    else:
        G_ratio = 0.1
        h_c = 0.12

    R_c = 70.0

    # Soil heat flux
    G = G_ratio * Rn
    # Windspeed at 2m height
    z_0M = h_c * 0.123
    d = h_c * 2. / 3.
    u_2 = wind_profile(u, z_u, z_0M, d, 2.0)
    R_a = 208. / u_2

    LE = (delta * (Rn - G) + rho * c_p * (es - ea) / R_a) / (delta + psicr *
                                                             (1.0 + R_c / R_a))

    return LE
Esempio n. 5
0
def METRIC(Tr_K,
           T_A_K,
           u,
           ea,
           p,
           Sn,
           L_dn,
           emis,
           z_0M,
           d_0,
           z_u,
           z_T,
           cold_pixel,
           hot_pixel,
           LE_cold,
           LE_hot=0,
           use_METRIC_resistance=True,
           calcG_params=[[1], 0.35],
           UseL=False,
           UseDEM=False):
    '''Calulates bulk fluxes using METRIC model

    Parameters
    ----------
    Tr_K : float
        Radiometric composite temperature (Kelvin).
    T_A_K : float
        Air temperature (Kelvin).
    u : float
        Wind speed above the canopy (m s-1).
    ea : float
        Water vapour pressure above the canopy (mb).
    p : float
        Atmospheric pressure (mb), use 1013 mb by default.
    S_n : float
        Solar irradiance (W m-2).
    L_dn : float
        Downwelling longwave radiation (W m-2)
    emis : float
        Surface emissivity.
    z_0M : float
        Aerodynamic surface roughness length for momentum transfer (m).
    d_0 : float
        Zero-plane displacement height (m).
    z_u : float
        Height of measurement of windspeed (m).
    z_T : float
        Height of measurement of air temperature (m).
    cold_pixel : tuple
        pixel coordinates (row, col) for the cold endmember
    hot_pixel : tuple
        pixel coordinates (row, col) for the hot endmember
    calcG_params : list[list,float or array], optional
        Method to calculate soil heat flux,parameters.

            * [[1],G_ratio]: default, estimate G as a ratio of Rn_S, default Gratio=0.35.
            * [[0],G_constant] : Use a constant G, usually use 0 to ignore the computation of G.
            * [[2,Amplitude,phase_shift,shape],time] : estimate G from Santanello and Friedl with G_param list of parameters (see :func:`~TSEB.calc_G_time_diff`).
    UseL : Optional[float]
        If included, its value will be used to force the Moning-Obukhov stability length.

    Returns
    -------
    flag : int
        Quality flag, see Appendix for description.
    Ln : float
        Net longwave radiation (W m-2)
    LE : float
        Latent heat flux (W m-2).
    H : float
        Sensible heat flux (W m-2).
    G : float
        Soil heat flux (W m-2).
    R_A : float
        Aerodynamic resistance to heat transport (s m-1).
    u_friction : float
        Friction velocity (m s-1).
    L : float
        Monin-Obuhkov length (m).
    n_iterations : int
        number of iterations until convergence of L.

    References
    ----------

    '''

    # Convert input scalars to numpy arrays and check parameters size
    Tr_K = np.asarray(Tr_K)
    (T_A_K, u, ea, p, Sn, L_dn, emis, z_0M, d_0, z_u, z_T, LE_cold, LE_hot,
     calcG_array) = map(tseb._check_default_parameter_size, [
         T_A_K, u, ea, p, Sn, L_dn, emis, z_0M, d_0, z_u, z_T, LE_cold, LE_hot,
         calcG_params[1]
     ], [Tr_K] * 14)

    # Create the output variables
    [Ln, LE, H, G, R_A,
     iterations] = [np.zeros(Tr_K.shape) + np.NaN for i in range(6)]
    flag = np.zeros(Tr_K.shape, dtype=np.byte)
    # iteration of the Monin-Obukhov length
    if isinstance(UseL, bool):
        # Initially assume stable atmospheric conditions and set variables for
        L = np.zeros(Tr_K.shape) + np.inf
        max_iterations = ITERATIONS
    else:  # We force Monin-Obukhov lenght to the provided array/value
        L = np.ones(Tr_K.shape) * UseL
        max_iterations = 1  # No iteration

    if isinstance(UseDEM, bool):
        Tr_datum = np.asarray(Tr_K)
        Ta_datum = np.asarray(T_A_K)
    else:
        gamma_w = met.calc_lapse_rate_moist(T_A_K, ea, p)
        Tr_datum = Tr_K + gamma_w * UseDEM
        Ta_datum = T_A_K + gamma_w * UseDEM

    # Calculate the general parameters
    rho = met.calc_rho(p, ea, T_A_K)  # Air density
    c_p = met.calc_c_p(p, ea)  # Heat capacity of air
    rho_datum = met.calc_rho(p, ea, Ta_datum)  # Air density

    # Calc initial Monin Obukhov variables
    u_friction = MO.calc_u_star(u, z_u, L, d_0, z_0M)
    u_friction = np.maximum(u_friction_min, u_friction)

    z_0H = res.calc_z_0H(z_0M, kB=kB)

    # Calculate Net radiation
    Ln = emis * L_dn - emis * met.calc_stephan_boltzmann(Tr_K)
    Rn = np.asarray(Sn + Ln)

    # Compute Soil Heat Flux
    i = np.ones(Rn.shape, dtype=bool)
    G[i] = tseb.calc_G([calcG_params[0], calcG_array], Rn, i)

    # Get cold and hot variables
    Rn_endmembers = np.array([Rn[cold_pixel], Rn[hot_pixel]])
    G_endmembers = np.array([G[cold_pixel], G[hot_pixel]])
    LE_endmembers = np.array([LE_cold[cold_pixel], LE_hot[hot_pixel]])
    u_friction_endmembers = np.array(
        [u_friction[cold_pixel], u_friction[hot_pixel]])
    u_endmembers = np.array([u[cold_pixel], u[hot_pixel]])
    z_u_endmembers = np.array([z_u[cold_pixel], z_u[hot_pixel]])
    Ta_datum_endmembers = np.array([Ta_datum[cold_pixel], Ta_datum[hot_pixel]])
    z_T_endmembers = np.array([z_T[cold_pixel], z_T[hot_pixel]])
    rho_datum_endmembers = np.array(
        [rho_datum[cold_pixel], rho_datum[hot_pixel]])
    c_p_endmembers = np.array([c_p[cold_pixel], c_p[hot_pixel]])
    d_0_endmembers = np.array([d_0[cold_pixel], d_0[hot_pixel]])
    z_0M_endmembers = np.array([z_0M[cold_pixel], z_0M[hot_pixel]])
    z_0H_endmembers = np.array([z_0H[cold_pixel], z_0H[hot_pixel]])

    H_endmembers = calc_H_residual(Rn_endmembers,
                                   G_endmembers,
                                   LE=LE_endmembers)

    # ==============================================================================
    #     HOT and COLD PIXEL ITERATIONS FOR MONIN-OBUKHOV LENGTH TO CONVERGE
    # ==============================================================================
    # Initially assume stable atmospheric conditions and set variables for
    L_old = np.ones(2)
    L_diff = np.ones(2) * float('inf')
    for iteration in range(max_iterations):
        if np.all(L_diff < L_thres):
            break

        if isinstance(UseL, bool):
            # Recaulculate L and the difference between iterations
            L_endmembers = MO.calc_L(u_friction_endmembers,
                                     Ta_datum_endmembers, rho_datum_endmembers,
                                     c_p_endmembers, H_endmembers,
                                     LE_endmembers)

            L_diff = np.fabs(L_endmembers - L_old) / np.fabs(L_old)
            L_old = np.array(L_endmembers)
            L_old[np.fabs(L_old) == 0] = 1e-36

            u_friction_endmembers = MO.calc_u_star(u_endmembers,
                                                   z_u_endmembers,
                                                   L_endmembers,
                                                   d_0_endmembers,
                                                   z_0M_endmembers)

            u_friction_endmembers = np.maximum(u_friction_min,
                                               u_friction_endmembers)

    # Hot and Cold aerodynamic resistances
    if use_METRIC_resistance is True:
        R_A_params = {
            "z_T": np.array([2.0, 2.0]),
            "u_friction": u_friction_endmembers,
            "L": L_endmembers,
            "d_0": np.array([0.0, 0.0]),
            "z_0H": np.array([0.1, 0.1])
        }
    else:
        R_A_params = {
            "z_T": z_T_endmembers,
            "u_friction": u_friction_endmembers,
            "L": L_endmembers,
            "d_0": d_0_endmembers,
            "z_0H": z_0H_endmembers
        }

    R_A_endmembers, _, _ = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999,
                                                 {"R_A": R_A_params})

    # Calculate the temperature gradients
    dT_endmembers = calc_dT(H_endmembers, R_A_endmembers, rho_datum_endmembers,
                            c_p_endmembers)

    # dT constants
    # Note: the equations for a and b in the Allen 2007 paper (eq 50 and 51) appear to be wrong.
    dT_b = (dT_endmembers[1] - dT_endmembers[0]) / (Tr_datum[hot_pixel] -
                                                    Tr_datum[cold_pixel])
    dT_a = dT_endmembers[1] - dT_b * Tr_datum[hot_pixel]

    # Apply the constant to the whole image
    dT = dT_a + dT_b * Tr_datum  # Allen 2007 eq. 29

    # ==============================================================================
    #     ITERATIONS FOR MONIN-OBUKHOV LENGTH AND H TO CONVERGE
    # ==============================================================================
    # Initially assume stable atmospheric conditions and set variables for
    L_queue = deque([np.ones(dT.shape)], 6)
    L_converged = np.asarray(np.zeros(Tr_K.shape)).astype(bool)
    L_diff_max = np.inf
    i = np.ones(dT.shape, dtype=bool)
    start_time = time.time()
    loop_time = time.time()

    for n_iterations in range(max_iterations):

        iterations[i] = n_iterations
        if np.all(L_converged):
            break
        current_time = time.time()
        loop_duration = current_time - loop_time
        loop_time = current_time
        total_duration = loop_time - start_time
        print(
            "Iteration: %d, non-converged pixels: %d, max L diff: %f, total time: %f, loop time: %f"
            % (n_iterations, np.sum(~L_converged[i]), L_diff_max,
               total_duration, loop_duration))

        i = ~L_converged

        if use_METRIC_resistance is True:
            R_A_params = {
                "z_T": np.array([2.0, 2.0]),
                "u_friction": u_friction[i],
                "L": L[i],
                "d_0": np.array([0.0, 0.0]),
                "z_0H": np.array([0.1, 0.1])
            }
        else:
            R_A_params = {
                "z_T": z_T[i],
                "u_friction": u_friction[i],
                "L": L[i],
                "d_0": d_0[i],
                "z_0H": z_0H[i]
            }

            R_A[i], _, _ = tseb.calc_resistances(tseb.KUSTAS_NORMAN_1999,
                                                 {"R_A": R_A_params})

        H[i] = calc_H(dT[i], rho[i], c_p[i], R_A[i])
        LE[i] = Rn[i] - G[i] - H[i]

        if isinstance(UseL, bool):
            # Now L can be recalculated and the difference between iterations
            # derived
            L[i] = MO.calc_L(u_friction[i], T_A_K[i], rho[i], c_p[i], H[i],
                             LE[i])

            u_friction[i] = MO.calc_u_star(u[i], z_u[i], L[i], d_0[i], z_0M[i])
            u_friction[i] = np.asarray(
                np.maximum(u_friction_min, u_friction[i]))

            # We check convergence against the value of L from previous iteration but as well
            # against values from 2 or 3 iterations back. This is to catch situations (not
            # infrequent) where L oscillates between 2 or 3 steady state values.
            L_new = np.array(L)
            L_new[L_new == 0] = 1e-36
            L_queue.appendleft(L_new)
            i = ~L_converged
            L_converged[i] = _L_diff(L_queue[0][i], L_queue[1][i]) < L_thres
            L_diff_max = np.max(_L_diff(L_queue[0][i], L_queue[1][i]))
            if len(L_queue) >= 4:
                i = ~L_converged
                L_converged[i] = np.logical_and(
                    _L_diff(L_queue[0][i], L_queue[2][i]) < L_thres,
                    _L_diff(L_queue[1][i], L_queue[3][i]) < L_thres)
            if len(L_queue) == 6:
                i = ~L_converged
                L_converged[i] = np.logical_and.reduce(
                    (_L_diff(L_queue[0][i], L_queue[3][i]) < L_thres,
                     _L_diff(L_queue[1][i], L_queue[4][i]) < L_thres,
                     _L_diff(L_queue[2][i], L_queue[5][i]) < L_thres))

    flag, Ln, LE, H, G, R_A, u_friction, L, iterations = map(
        np.asarray, (flag, Ln, LE, H, G, R_A, u_friction, L, iterations))

    return flag, Ln, LE, H, G, R_A, u_friction, L, iterations