Пример #1
0
def calc_L_n_Campbell(T_C, T_S, L_dn, lai, emisVeg, emisGrd, x_LAD=1):
    ''' Net longwave radiation for soil and canopy layers

    Estimates the net longwave radiation for soil and canopy layers unisg based on equation 2a
    from [Kustas1999]_ and incorporated the effect of the Leaf Angle Distribution based on [Campbell1998]_

    Parameters
    ----------
    T_C : float
        Canopy temperature (K).
    T_S : float
        Soil temperature (K).
    L_dn : float
        Downwelling atmospheric longwave radiation (w m-2).
    lai : float
        Effective Leaf (Plant) Area Index.
    emisVeg : float
        Broadband emissivity of vegetation cover.
    emisGrd : float
        Broadband emissivity of soil.
    x_LAD: float, optional
        x parameter for the ellipsoidal Leaf Angle Distribution function,
        use x_LAD=1 for a spherical LAD.

    Returns
    -------
    L_nC : float
        Net longwave radiation of canopy (W m-2).
    L_nS : float
        Net longwave radiation of soil (W m-2).

    References
    ----------
    .. [Kustas1999] Kustas and Norman (1999) Evaluation of soil and vegetation heat
        flux predictions using a simple two-source model with radiometric temperatures for
        partial canopy cover, Agricultural and Forest Meteorology, Volume 94, Issue 1,
        Pages 13-29, http://dx.doi.org/10.1016/S0168-1923(99)00005-2.
    '''

    # calculate long wave emissions from canopy, soil and sky
    L_C = emisVeg * met.calc_stephan_boltzmann(T_C)
    L_C[np.isnan(L_C)] = 0
    L_S = emisGrd * met.calc_stephan_boltzmann(T_S)
    L_S[np.isnan(L_S)] = 0
    # Calculate the canopy spectral properties
    _, albl, _, taudl = calc_spectra_Cambpell(lai,
                                              np.zeros(emisVeg.shape),
                                              1.0 - emisVeg,
                                              np.zeros(emisVeg.shape),
                                              1.0 - emisGrd,
                                              x_lad=x_LAD,
                                              lai_eff=None)

    # calculate net longwave radiation divergence of the soil
    L_nS = emisGrd * taudl * L_dn + emisGrd * (1.0 - taudl) * L_C - L_S
    L_nC = (1 - albl) * (1.0 - taudl) * (L_dn + L_S) - 2.0 * (1.0 -
                                                              taudl) * L_C
    L_nC[np.isnan(L_nC)] = 0
    L_nS[np.isnan(L_nS)] = 0
    return np.asarray(L_nC), np.asarray(L_nS)
Пример #2
0
def calc_L_n_Kustas(T_C, T_S, L_dn, LAI, emisVeg, emisGrd, x_LAD=1):
    ''' Net longwave radiation for soil and canopy layers

    Estimates the net longwave radiation for soil and canopy layers unisg based on equation 2a
    from [Kustas1999]_ and incorporated the effect of the Leaf Angle Distribution based on [Campbell1998]_

    Parameters
    ----------
    T_C : float
        Canopy temperature (K).
    T_S : float
        Soil temperature (K).
    L_dn : float
        Downwelling atmospheric longwave radiation (w m-2).
    LAI : float
        Effective Leaf (Plant) Area Index.
    emisVeg : float
        Broadband emissivity of vegetation cover.
    emisGrd : float
        Broadband emissivity of soil.
    x_LAD: float, optional
        x parameter for the ellipsoidal Leaf Angle Distribution function,
        use x_LAD=1 for a spherical LAD.

    Returns
    -------
    L_nC : float
        Net longwave radiation of canopy (W m-2).
    L_nS : float
        Net longwave radiation of soil (W m-2).

    References
    ----------
    .. [Kustas1999] Kustas and Norman (1999) Evaluation of soil and vegetation heat
        flux predictions using a simple two-source model with radiometric temperatures for
        partial canopy cover, Agricultural and Forest Meteorology, Volume 94, Issue 1,
        Pages 13-29, http://dx.doi.org/10.1016/S0168-1923(99)00005-2.
    '''

    # Integrate to get the diffuse transmitance
    taud = 0
    for angle in range(0, 90, 5):
        akd = calc_K_be_Campbell(angle, x_LAD)  # Eq. 15.4
        taub = np.exp(-akd * LAI)
        taud = taud + taub * np.cos(np.radians(angle)) * \
            np.sin(np.radians(angle)) * np.radians(5)
    taud = 2.0 * taud
    # D I F F U S E   C O M P O N E N T S
    # Diffuse light canopy reflection coefficients  for a deep canopy
    akd = -np.log(taud) / LAI
    ameanl = np.asarray(emisVeg)
    taudl = np.exp(-np.sqrt(ameanl) * akd * LAI)  # Eq 15.6
    # calculate long wave emissions from canopy, soil and sky
    L_C = emisVeg * met.calc_stephan_boltzmann(T_C)
    L_S = emisGrd * met.calc_stephan_boltzmann(T_S)
    # calculate net longwave radiation divergence of the soil
    L_nS = taudl * L_dn + (1.0 - taudl) * L_C - L_S
    L_nC = (1.0 - taudl) * (L_dn + L_S - 2.0 * L_C)
    return np.asarray(L_nC), np.asarray(L_nS)
Пример #3
0
def calc_longwave_irradiance(ea, t_a_k, p=1013.25, z_T=2.0, h_C=2.0):
    '''Longwave irradiance

    Estimates longwave atmospheric irradiance from clear sky.
    By default there is no lapse rate correction unless air temperature
    measurement height is considerably different than canopy height, (e.g. when
    using NWP gridded meteo data at blending height)

    Parameters
    ----------
    ea : float
        atmospheric vapour pressure (mb).
    t_a_k : float
        air temperature (K).
    p : float
        air pressure (mb)
    z_T: float
        air temperature measurement height (m), default 2 m.
    h_C: float
        canopy height (m), default 2 m,

    Returns
    -------
    L_dn : float
        Longwave atmospheric irradiance (W m-2) above the canopy
    '''

    lapse_rate = met.calc_lapse_rate_moist(t_a_k, ea, p)
    t_a_surface = t_a_k - lapse_rate * (h_C - z_T)
    emisAtm = calc_emiss_atm(ea, t_a_surface)
    L_dn = emisAtm * met.calc_stephan_boltzmann(t_a_surface)
    return np.asarray(L_dn)
Пример #4
0
def calc_longwave_irradiance(ea, T_A_K, p=1013.25, z_T=2.0):
    '''Longwave irradiance

    Estimates longwave atmospheric irradiance from clear sky.

    Parameters
    ----------
    ea : float
        atmospheric vapour pressure (mb).
    T_A_K : float
        air temperature (K).
    p : float
        air pressure (mb)
    z_T: float
        air temperature measurement height (m), default 2 m.

    Returns
    -------
    L_dn : float
        Longwave atmospheric irradiance (W m-2)
    '''

    lapse_rate = met.calc_lapse_rate_moist(T_A_K, ea, p)
    T_A_surface = T_A_K - z_T * lapse_rate
    emisAtm = calc_emiss_atm(ea, T_A_surface)
    L_dn = emisAtm * met.calc_stephan_boltzmann(T_A_surface)
    return np.asarray(L_dn)
Пример #5
0
def calc_longwave_irradiance(ea, T_A_K, z_T=2.0):
    '''Longwave irradiance

    Estimates longwave atmospheric irradiance from clear sky.

    Parameters
    ----------
    ea : float
        atmospheric vapour pressure (mb).
    T_A_K : float
        air temperature (K).
    z_T: float
        air temperature measurement height (m), default 2 m.

    Returns
    -------
    L_dn : float
        Longwave atmospheric irradiance (W m-2)
    '''

    # Assume dry adiabatic lapse rate of air temperature.
    T_A_surface = T_A_K - z_T * 0.0098
    emisAtm = calc_emiss_atm(ea, T_A_surface)
    L_dn = emisAtm * met.calc_stephan_boltzmann(T_A_surface)
    return np.asarray(L_dn)
Пример #6
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
Пример #7
0
    def run_TSEB_local_image(self):
        ''' Runs TSEB for all the pixel in an image'''

        #======================================
        # Process the input

        # Create an input dictionary
        in_data = dict()

        if 'subset' in self.p:
            subset = ast.literal_eval(self.p['subset'])
        else:
            subset = []

        # Open the LST data according to the model
        try:
            fid = gdal.Open(self.p['T_R1'], gdal.GA_ReadOnly)
            prj = fid.GetProjection()
            geo = fid.GetGeoTransform()
            if subset:
                T_R1 = fid.GetRasterBand(1).ReadAsArray(
                    subset[0], subset[1], subset[2], subset[3])
                geo = [
                    geo[0] + subset[0] * geo[1], geo[1], geo[2],
                    geo[3] + subset[1] * geo[5], geo[4], geo[5]
                ]
            else:
                T_R1 = fid.GetRasterBand(1).ReadAsArray()
            dims = np.shape(T_R1)

            # In case of TSEB_PT or DTD models save noon temperature
            if self.model_type == 'TSEB_PT' or self.model_type == 'DTD':
                in_data['T_R1'] = T_R1

            # In case of TSEB_2T save both component temperatures
            if self.model_type == 'TSEB_2T':
                in_data['T_C'] = T_R1
                if subset:
                    in_data['T_S'] = fid.GetRasterBand(2).ReadAsArray(
                        subset[0], subset[1], subset[2], subset[3])
                else:
                    in_data['T_S'] = fid.GetRasterBand(2).ReadAsArray()

            fid = None
        except:
            print('Error reading sunrise LST file ' + str(self.p['T_R0']))
            fid = None
            return

        # In case of DTD also need to read the sunrise/night LST
        if self.model_type == 'DTD':
            success, in_data['T_R0'] = self._open_GDAL_image(
                self.p['T_R0'], dims, 'Sunrise LST', subset)
            if not success:
                return

        # Read the image mosaic and get the LAI
        success, in_data['LAI'] = self._open_GDAL_image(
            self.p['LAI'], dims, 'Leaf Area Index', subset)
        if not success:
            return
        # Read the image View Zenith Angle
        success, in_data['VZA'] = self._open_GDAL_image(
            self.p['VZA'], dims, 'View Zenith Angle', subset)
        if not success:
            return
        # Read the fractional cover data
        success, in_data['f_c'] = self._open_GDAL_image(
            self.p['f_c'], dims, 'Fractional Cover', subset)
        if not success:
            return
        # Read the Canopy Height data
        success, in_data['h_C'] = self._open_GDAL_image(
            self.p['h_C'], dims, 'Canopy Height', subset)
        if not success:
            return
        # Read the canopy witdth ratio
        success, in_data['w_C'] = self._open_GDAL_image(
            self.p['w_C'], dims, 'Canopy Width Ratio', subset)
        if not success:
            return
        # Read the Green fraction
        success, in_data['f_g'] = self._open_GDAL_image(
            self.p['f_g'], dims, 'Green Fraction', subset)
        if not success:
            return
        # Read landcover
        success, in_data['landcover'] = self._open_GDAL_image(
            self.p['landcover'], dims, 'Landcover', subset)
        if not success:
            return
        # Read leaf angle distribution
        success, in_data['x_LAD'] = self._open_GDAL_image(
            self.p['x_LAD'], dims, 'Leaf Angle Distribution', subset)
        if not success:
            return
        # Read initial alpha_PT
        success, in_data['alpha_PT'] = self._open_GDAL_image(
            self.p['alpha_PT'], dims, 'Initial alpha_PT', subset)
        if not success:
            return

        # Read spectral properties
        success, in_data['rho_vis_C'] = self._open_GDAL_image(
            self.p['rho_vis_C'], dims, 'Leaf PAR Reflectance', subset)
        if not success:
            return
        success, in_data['tau_vis_C'] = self._open_GDAL_image(
            self.p['tau_vis_C'], dims, 'Leaf PAR Transmitance', subset)
        if not success:
            return
        success, in_data['rho_nir_C'] = self._open_GDAL_image(
            self.p['rho_nir_C'], dims, 'Leaf NIR Reflectance', subset)
        if not success:
            return
        success, in_data['tau_nir_C'] = self._open_GDAL_image(
            self.p['tau_nir_C'], dims, 'Leaf NIR Transmitance', subset)
        if not success:
            return
        success, in_data['rho_vis_S'] = self._open_GDAL_image(
            self.p['rho_vis_S'], dims, 'Soil PAR Reflectance', subset)
        if not success:
            return
        success, in_data['rho_nir_S'] = self._open_GDAL_image(
            self.p['rho_nir_S'], dims, 'Soil NIR Reflectance', subset)
        if not success:
            return
        success, in_data['emis_C'] = self._open_GDAL_image(
            self.p['emis_C'], dims, 'Leaf Emissivity', subset)
        if not success:
            return
        success, in_data['emis_S'] = self._open_GDAL_image(
            self.p['emis_S'], dims, 'Soil Emissivity', subset)
        if not success:
            return

        # Calculate illumination conditions
        success, lat = self._open_GDAL_image(self.p['lat'], dims, 'Latitude',
                                             subset)
        if not success:
            return
        success, lon = self._open_GDAL_image(self.p['lon'], dims, 'Longitude',
                                             subset)
        if not success:
            return
        success, stdlon = self._open_GDAL_image(self.p['stdlon'], dims,
                                                'Standard Longitude', subset)
        if not success:
            return
        success, in_data['time'] = self._open_GDAL_image(
            self.p['time'], dims, 'Time', subset)
        if not success:
            return
        success, doy = self._open_GDAL_image(self.p['DOY'], dims, 'DOY',
                                             subset)
        if not success:
            return
        in_data['SZA'], in_data['SAA'] = met.calc_sun_angles(
            lat, lon, stdlon, doy, in_data['time'])
        # Wind speed
        success, in_data['u'] = self._open_GDAL_image(self.p['u'], dims,
                                                      'Wind speed', subset)
        if not success:
            return
        # Vapour pressure
        success, in_data['ea'] = self._open_GDAL_image(self.p['ea'], dims,
                                                       'Vapour pressure',
                                                       subset)
        if not success:
            return
        # Air pressure
        success, in_data['p'] = self._open_GDAL_image(self.p['p'], dims,
                                                      'Pressure', subset)
        # If pressure was not provided then estimate it based of altitude
        if not success:
            success, alt = self._open_GDAL_image(self.p['alt'], dims,
                                                 'Altitude', subset)
            if success:
                in_data['p'] = met.calc_pressure(alt)
            else:
                return
        success, in_data['S_dn'] = self._open_GDAL_image(
            self.p['S_dn'], dims, 'Shortwave irradiance', subset)
        if not success:
            return
        # Wind speed measurement height
        success, in_data['z_u'] = self._open_GDAL_image(
            self.p['z_u'], dims, 'Wind speed height', subset)
        if not success:
            return
        # Air temperature mesurement height
        success, in_data['z_T'] = self._open_GDAL_image(
            self.p['z_T'], dims, 'Air temperature height', subset)
        if not success:
            return
        # Leaf width
        success, in_data['leaf_width'] = self._open_GDAL_image(
            self.p['leaf_width'], dims, 'Leaf Width', subset)
        if not success:
            return
        # Soil roughness
        success, in_data['z0_soil'] = self._open_GDAL_image(
            self.p['z0_soil'], dims, 'Soil Roughness', subset)
        if not success:
            return

        # Incoming long wave radiation
        success, in_data['T_A1'] = self._open_GDAL_image(
            self.p['T_A1'], dims, 'Air Temperature', subset)
        if not success:
            return
        if self.model_type == 'DTD':
            success, in_data['T_A0'] = self._open_GDAL_image(
                self.p['T_A0'], dims, 'Air Temperature at time 0', subset)
        if not success:
            return
        success, in_data['L_dn'] = self._open_GDAL_image(
            self.p['L_dn'], dims, 'Longwave irradiance', subset)
        # If longwave irradiance was not provided then estimate it based on air
        # temperature and humidity
        if not success:
            emisAtm = rad.calc_emiss_atm(in_data['ea'], in_data['T_A1'])
            in_data['L_dn'] = emisAtm * met.calc_stephan_boltzmann(
                in_data['T_A1'])

        # Open the processing maks and get the id for the cells to process
        if self.p['input_mask'] != '0':
            success, mask = self._open_GDAL_image(self.p['input_mask'], dims,
                                                  'input mask', subset)
            if not success:
                print(
                    "Please set input_mask=0 for processing the whole image.")
                return
        # Otherwise create mask from landcover array
        else:
            mask = np.ones(dims)
            mask[np.logical_or.reduce((in_data['landcover'] == res.WATER,
                                       in_data['landcover'] == res.URBAN,
                                       in_data['landcover'] == res.SNOW))] = 0

        # Get the Soil Heat flux if G_form includes the option of measured G
        # Constant G or constant ratio of soil reaching radiation
        if self.G_form[0][0] == 0 or self.G_form[0][0] == 1:
            success, self.G_form[1] = self._open_GDAL_image(
                self.G_form[1], dims, 'G', subset)
            if not success:
                return
        # Santanello and Friedls G
        elif self.G_form[0][0] == 2:
            # Set the time in the G_form flag to compute the Santanello and
            # Friedl G
            self.G_form[1] = in_data['time']

        # Set the Kustas and Norman resistance parameters
        if self.resistance_form == 0:
            success, self.res_params['KN_b'] = self._open_GDAL_image(
                self.p['KN_b'], dims, 'Resistance parameter b', subset)
            if not success:
                return
            success, self.res_params['KN_c'] = self._open_GDAL_image(
                self.p['KN_c'], dims, 'Resistance parameter c', subset)
            if not success:
                return
            success, self.res_params['KN_C_dash'] = self._open_GDAL_image(
                self.p['KN_C_dash'], dims, 'Resistance parameter C\'', subset)
            if not success:
                return

        #======================================
        # Run the chosen model

        out_data = self.run_TSEB(in_data, mask)

        #======================================
        # Save output files

        # Output variables saved in images
        self.fields = ('H1', 'LE1', 'R_n1', 'G1')
        # Ancillary output variables
        self.anc_fields = ('H_C1', 'LE_C1', 'LE_partition', 'T_C1', 'T_S1',
                           'R_ns1', 'R_nl1', 'delta_R_n1', 'u_friction', 'L',
                           'R_S1', 'R_x1', 'R_A1', 'flag')
        outdir = dirname(self.p['output_file'])
        if not exists(outdir):
            mkdir(outdir)
        self._write_raster_output(self.p['output_file'], out_data, geo, prj,
                                  self.fields)
        outputfile = splitext(self.p['output_file'])[0] + '_ancillary' + \
                     splitext(self.p['output_file'])[1]
        self._write_raster_output(outputfile, out_data, geo, prj,
                                  self.anc_fields)
        print('Saved Files')

        return in_data, out_data
Пример #8
0
    def run_TSEB_point_series_array(self):
        ''' Runs TSEB for all the dates in point time-series'''
        def compose_date(years,
                         months=1,
                         days=1,
                         weeks=None,
                         hours=None,
                         minutes=None,
                         seconds=None,
                         milliseconds=None,
                         microseconds=None,
                         nanoseconds=None):
            ''' Taken from http://stackoverflow.com/questions/34258892/converting-year-and-day-of-year-into-datetime-index-in-pandas'''
            years = np.asarray(years) - 1970
            months = np.asarray(months) - 1
            days = np.asarray(days) - 1
            types = ('<M8[Y]', '<m8[M]', '<m8[D]', '<m8[W]', '<m8[h]',
                     '<m8[m]', '<m8[s]', '<m8[ms]', '<m8[us]', '<m8[ns]')
            vals = (years, months, days, weeks, hours, minutes, seconds,
                    milliseconds, microseconds, nanoseconds)
            return sum(
                np.asarray(v, dtype=t) for t, v in zip(types, vals)
                if v is not None)

        #======================================
        # Process the input

        # Read input data from CSV file
        in_data = pd.read_csv(self.p['input_file'], delim_whitespace=True)
        in_data.index = compose_date(years=in_data['year'],
                                     days=in_data['DOY'],
                                     hours=in_data['time'],
                                     minutes=in_data['time'] % 1 * 60)

        # Check if all the required columns are present
        if not self._required_data_present(in_data):
            return None, None

        # Fill in data fields which might not be in the input file
        if 'SZA' not in in_data.columns:
            sza, _ = met.calc_sun_angles(self.p['lat'], self.p['lon'],
                                         self.p['stdlon'], in_data['DOY'],
                                         in_data['time'])
            in_data['SZA'] = sza
        if 'SAA' not in in_data.columns:
            _, saa = met.calc_sun_angles(self.p['lat'], self.p['lon'],
                                         self.p['stdlon'], in_data['DOY'],
                                         in_data['time'])
            in_data['SAA'] = saa
        if 'p' not in in_data.columns:  # Estimate barometric pressure from the altitude if not included in the table
            in_data['p'] = met.calc_pressure(self.p['alt'])
        if 'f_c' not in in_data.columns:  # Fractional cover
            in_data['f_c'] = self.p['f_c']  # Use default value
        if 'w_C' not in in_data.columns:  # Canopy width to height ratio
            in_data['w_C'] = self.p['w_C']  # Use default value
        if 'f_g' not in in_data.columns:  # Green fraction
            in_data['f_g'] = self.p['f_g']  # Use default value
        if 'rho_vis_C' not in in_data.columns:
            in_data['rho_vis_C'] = self.p['rho_vis_C']
        if 'tau_vis_C' not in in_data.columns:
            in_data['tau_vis_C'] = self.p['tau_vis_C']
        if 'rho_nir_C' not in in_data.columns:
            in_data['rho_nir_C'] = self.p['rho_nir_C']
        if 'tau_nir_C' not in in_data.columns:
            in_data['tau_nir_C'] = self.p['tau_nir_C']
        if 'rho_vis_S' not in in_data.columns:
            in_data['rho_vis_S'] = self.p['rho_vis_S']
        if 'rho_nir_S' not in in_data.columns:
            in_data['rho_nir_S'] = self.p['rho_nir_S']
        if 'emis_C' not in in_data.columns:
            in_data['emis_C'] = self.p['emis_C']
        if 'emis_S' not in in_data.columns:
            in_data['emis_S'] = self.p['emis_S']

        # Fill in other data fields from the parameter file
        in_data['landcover'] = self.p['landcover']
        in_data['z_u'] = self.p['z_u']
        in_data['z_T'] = self.p['z_T']
        in_data['leaf_width'] = self.p['leaf_width']
        in_data['z0_soil'] = self.p['z0_soil']
        in_data['alpha_PT'] = self.p['alpha_PT']
        in_data['x_LAD'] = self.p['x_LAD']

        # Incoming long wave radiation
        if 'L_dn' not in in_data.columns:
            # If longwave irradiance was not provided then estimate it based on air
            # temperature and humidity
            emisAtm = rad.calc_emiss_atm(in_data['ea'], in_data['T_A1'])
            in_data['L_dn'] = emisAtm * met.calc_stephan_boltzmann(
                in_data['T_A1'])

        # Get the Soil Heat flux if G_form includes the option of measured G
        dims = in_data['LAI'].shape
        if self.G_form[0][0] == 0:  # Constant G
            if 'G' in in_data.columns:
                self.G_form[1] = in_data['G']
        elif self.G_form[0][0] == 1:
            self.G_form[1] = np.ones(dims) * self.G_form[1]
        elif self.G_form[0][0] == 2:  # Santanello and Friedls G
            # Set the time in the G_form flag to compute the Santanello and
            # Friedl G
            self.G_form[1] = in_data['time']

        # Set the Kustas and Norman resistance parameters
        if self.resistance_form == 0:
            self.res_params['KN_b'] = np.ones(dims) * self.p['KN_b']
            self.res_params['KN_c'] = np.ones(dims) * self.p['KN_c']
            self.res_params['KN_C_dash'] = np.ones(dims) * self.p['KN_C_dash']

        #======================================
        # Run the chosen model

        out_data = self.run_TSEB(in_data)
        out_data = pd.DataFrame(data=np.stack(out_data.values()).T,
                                index=in_data.index,
                                columns=out_data.keys())

        #======================================
        # Save output file

        # Output Headers
        outputTxtFieldNames = [
            'Year', 'DOY', 'Time', 'LAI', 'f_g', 'VZA', 'SZA', 'SAA', 'L_dn',
            'Rn_model', 'Rn_sw_veg', 'Rn_sw_soil', 'Rn_lw_veg', 'Rn_lw_soil',
            'T_C', 'T_S', 'T_AC', 'LE_model', 'H_model', 'LE_C', 'H_C', 'LE_S',
            'H_S', 'G_model', 'R_S', 'R_x', 'R_A', 'u_friction', 'L', 'Skyl',
            'z_0M', 'd_0', 'flag'
        ]

        # Create the ouput directory if it doesn't exist
        outdir = dirname(self.p['output_file'])
        if not exists(outdir):
            mkdir(outdir)

        # Write the data
        csvData = pd.concat([
            in_data[[
                'year', 'DOY', 'time', 'LAI', 'f_g', 'VZA', 'SZA', 'SAA',
                'L_dn'
            ]], out_data[[
                'R_n1', 'Sn_C1', 'Sn_S1', 'Ln_C1', 'Ln_S1', 'T_C1', 'T_S1',
                'T_AC1', 'LE1', 'H1', 'LE_C1', 'H_C1', 'LE_S1', 'H_S1', 'G1',
                'R_S1', 'R_x1', 'R_A1', 'u_friction', 'L', 'Skyl', 'z_0M',
                'd_0', 'flag'
            ]]
        ],
                            axis=1)
        csvData.to_csv(self.p['output_file'],
                       sep='\t',
                       index=False,
                       header=outputTxtFieldNames)

        print('Done')

        return in_data, out_data
Пример #9
0
    def process_local_image(self):
        ''' Runs pyMETRIC for all the pixel in an image'''

        #======================================
        # Process the input

        # Create an input dictionary
        in_data = dict()

        if 'subset' in self.p:
            subset = ast.literal_eval(self.p['subset'])
        else:
            subset = []

        # Open the LST data according to the model
        try:
            fid = gdal.Open(self.p['T_R1'], gdal.GA_ReadOnly)
            prj = fid.GetProjection()
            geo = fid.GetGeoTransform()
            if subset:
                in_data['T_R1'] = fid.GetRasterBand(1).ReadAsArray(
                    subset[0], subset[1], subset[2], subset[3])
                geo = [
                    geo[0] + subset[0] * geo[1], geo[1], geo[2],
                    geo[3] + subset[1] * geo[5], geo[4], geo[5]
                ]
            else:
                in_data['T_R1'] = fid.GetRasterBand(1).ReadAsArray()
            dims = np.shape(in_data['T_R1'])

        except:
            print('Error reading LST file ' + str(self.p['T_R1']))
            fid = None
            return

        # Read the image mosaic and get the LAI
        success, in_data['LAI'] = self._open_GDAL_image(
            self.p['LAI'], dims, 'Leaf Area Index', subset)
        if not success:
            return
        # Read the image mosaic and get the Vegetation Index
        success, in_data['VI'] = self._open_GDAL_image(self.p['VI'], dims,
                                                       'Vegetation Index',
                                                       subset)
        if not success:
            return

        # Read the fractional cover data
        success, in_data['f_c'] = self._open_GDAL_image(
            self.p['f_c'], dims, 'Fractional Cover', subset)
        if not success:
            return
        # Read the Canopy Height data
        success, in_data['h_C'] = self._open_GDAL_image(
            self.p['h_C'], dims, 'Canopy Height', subset)
        if not success:
            return
        # Read the canopy witdth ratio
        success, in_data['w_C'] = self._open_GDAL_image(
            self.p['w_C'], dims, 'Canopy Width Ratio', subset)
        if not success:
            return
        # Read landcover
        success, in_data['landcover'] = self._open_GDAL_image(
            self.p['landcover'], dims, 'Landcover', subset)
        if not success:
            return
        # Read leaf angle distribution
        success, in_data['x_LAD'] = self._open_GDAL_image(
            self.p['x_LAD'], dims, 'Leaf Angle Distribution', subset)
        if not success:
            return
        # Read digital terrain model
        success, in_data['alt'] = self._open_GDAL_image(
            self.p['alt'], dims, 'Digital Terrain Model', subset)
        if not success:
            return

        # Read spectral properties
        success, in_data['rho_vis_C'] = self._open_GDAL_image(
            self.p['rho_vis_C'], dims, 'Leaf PAR Reflectance', subset)
        if not success:
            return
        success, in_data['tau_vis_C'] = self._open_GDAL_image(
            self.p['tau_vis_C'], dims, 'Leaf PAR Transmitance', subset)
        if not success:
            return
        success, in_data['rho_nir_C'] = self._open_GDAL_image(
            self.p['rho_nir_C'], dims, 'Leaf NIR Reflectance', subset)
        if not success:
            return
        success, in_data['tau_nir_C'] = self._open_GDAL_image(
            self.p['tau_nir_C'], dims, 'Leaf NIR Transmitance', subset)
        if not success:
            return
        success, in_data['rho_vis_S'] = self._open_GDAL_image(
            self.p['rho_vis_S'], dims, 'Soil PAR Reflectance', subset)
        if not success:
            return
        success, in_data['rho_nir_S'] = self._open_GDAL_image(
            self.p['rho_nir_S'], dims, 'Soil NIR Reflectance', subset)
        if not success:
            return
        success, in_data['emis_C'] = self._open_GDAL_image(
            self.p['emis_C'], dims, 'Leaf Emissivity', subset)
        if not success:
            return
        success, in_data['emis_S'] = self._open_GDAL_image(
            self.p['emis_S'], dims, 'Soil Emissivity', subset)
        if not success:
            return

        # Calculate illumination conditions
        success, lat = self._open_GDAL_image(self.p['lat'], dims, 'Latitude',
                                             subset)
        if not success:
            return
        success, lon = self._open_GDAL_image(self.p['lon'], dims, 'Longitude',
                                             subset)
        if not success:
            return
        success, stdlon = self._open_GDAL_image(self.p['stdlon'], dims,
                                                'Standard Longitude', subset)
        if not success:
            return
        success, in_data['time'] = self._open_GDAL_image(
            self.p['time'], dims, 'Time', subset)
        if not success:
            return
        success, doy = self._open_GDAL_image(self.p['DOY'], dims, 'DOY',
                                             subset)
        if not success:
            return
        in_data['SZA'], in_data['SAA'] = met.calc_sun_angles(
            lat, lon, stdlon, doy, in_data['time'])

        del lat, lon, stdlon, doy

        # Wind speed
        success, in_data['u'] = self._open_GDAL_image(self.p['u'], dims,
                                                      'Wind speed', subset)
        if not success:
            return
        # Vapour pressure
        success, in_data['ea'] = self._open_GDAL_image(self.p['ea'], dims,
                                                       'Vapour pressure',
                                                       subset)
        if not success:
            return
        # Air pressure
        success, in_data['p'] = self._open_GDAL_image(self.p['p'], dims,
                                                      'Pressure', subset)
        # If pressure was not provided then estimate it based of altitude
        if not success:
            success, alt = self._open_GDAL_image(self.p['alt'], dims,
                                                 'Altitude', subset)
            if success:
                in_data['p'] = met.calc_pressure(alt)
            else:
                return
        success, in_data['S_dn'] = self._open_GDAL_image(
            self.p['S_dn'], dims, 'Shortwave irradiance', subset)
        if not success:
            return
        # Wind speed measurement height
        success, in_data['z_u'] = self._open_GDAL_image(
            self.p['z_u'], dims, 'Wind speed height', subset)
        if not success:
            return
        # Air temperature mesurement height
        success, in_data['z_T'] = self._open_GDAL_image(
            self.p['z_T'], dims, 'Air temperature height', subset)
        if not success:
            return
        # Soil roughness
        success, in_data['z0_soil'] = self._open_GDAL_image(
            self.p['z0_soil'], dims, 'Soil Roughness', subset)
        if not success:
            return

        # Incoming long wave radiation
        success, in_data['T_A1'] = self._open_GDAL_image(
            self.p['T_A1'], dims, 'Air Temperature', subset)
        if not success:
            return
        success, in_data['L_dn'] = self._open_GDAL_image(
            self.p['L_dn'], dims, 'Longwave irradiance', subset)
        # If longwave irradiance was not provided then estimate it based on air
        # temperature and humidity
        if not success:
            emisAtm = rad.calc_emiss_atm(in_data['ea'], in_data['T_A1'])
            in_data['L_dn'] = emisAtm * met.calc_stephan_boltzmann(
                in_data['T_A1'])

        # Open the processing maks and get the id for the cells to process
        if self.p['input_mask'] != '0':
            success, mask = self._open_GDAL_image(self.p['input_mask'], dims,
                                                  'input mask', subset)
            if not success:
                print(
                    "Please set input_mask=0 for processing the whole image.")
                return
        # Otherwise create mask from landcover array
        else:
            mask = np.ones(dims)
            mask[np.logical_or.reduce((in_data['landcover'] == res.WATER,
                                       in_data['landcover'] == res.URBAN,
                                       in_data['landcover'] == res.SNOW))] = 0

        mask[np.logical_or(~np.isfinite(in_data['VI']),
                           ~np.isfinite(in_data['T_R1']))] = 0
        # Open the bare soil ET
        if str(self.p['ET_bare_soil']) != '0':
            success, in_data['ET_bare_soil'] = self._open_GDAL_image(
                self.p['ET_bare_soil'], dims, 'ET_bare_soil', subset)
            if not success:
                print(
                    "Please set ET_bare_soil=0 for assuming zero ET for bare soil."
                )
                return
        # Otherwise assume ET soil = 0
        else:
            in_data['ET_bare_soil'] = np.zeros(dims)

        # Get the Soil Heat flux if G_form includes the option of measured G
        # Constant G or constant ratio of soil reaching radiation
        if self.G_form[0][0] == 0 or self.G_form[0][0] == 1:
            success, self.G_form[1] = self._open_GDAL_image(
                self.G_form[1], dims, 'G', subset)
            if not success:
                return
        # Santanello and Friedls G
        elif self.G_form[0][0] == 2:
            # Set the time in the G_form flag to compute the Santanello and
            # Friedl G
            self.G_form[1] = in_data['time']
        # ASCE G ratios
        elif self.G_form[0][0] == 4:
            self.G_form[1] = (0.04, 0.10)

        elif self.G_form[0][0] == 5:
            self.G_form[1] = (0.04, 0.10)

        del in_data['time']

        #======================================
        # Run the chosen model

        out_data = self.run_METRIC(in_data, mask)

        #======================================
        # Save output files

        # Output variables saved in images
        self.fields = ('R_n1', 'H1', 'LE1', 'G1')
        # Ancillary output variables
        self.anc_fields = ('R_ns1', 'R_nl1', 'u_friction', 'L', 'R_A1', 'flag',
                           'ET0_datum')

        outdir = dirname(self.p['output_file'])
        if not exists(outdir):
            mkdir(outdir)

        geo_father = []
        if 'subset_output' in self.p:
            geo_father = geo
            subset, geo = self._get_subset(self.p["subset_output"], prj, geo)
            dims = (subset[3], subset[2])

            for field in self.fields:
                out_data[field] = out_data[field][subset[1]:subset[1] +
                                                  subset[3],
                                                  subset[0]:subset[0] +
                                                  subset[2]]
            for field in self.anc_fields:
                out_data[field] = out_data[field][subset[1]:subset[1] +
                                                  subset[3],
                                                  subset[0]:subset[0] +
                                                  subset[2]]

        if dims[0] <= 0 or dims[1] <= 0:
            print('No valid extent for creating ouput')
            return in_data, out_data

        self._write_raster_output(self.p['output_file'],
                                  out_data,
                                  geo,
                                  prj,
                                  self.fields,
                                  geo_father=geo_father)

        outputfile = splitext(self.p['output_file'])[0] + '_ancillary' + \
                     splitext(self.p['output_file'])[1]
        self._write_raster_output(outputfile,
                                  out_data,
                                  geo,
                                  prj,
                                  self.anc_fields,
                                  geo_father=geo_father)

        print('Saved Files')

        return in_data, out_data